Dashboard › opencode › Distillation
ec963a4c-4312-48e7-a7d2-adc994298ae9["lore_tm_v1_qchMRu7XSCjBNbiPHvTcla37SvYnnPTy2M_CmqGcwiw","lore_tm_v1_joO09lYUesxR5Rooe__XPALLlEEoCjSP71U7aUCZaQ8","lore_tm_v1_WE0CDx1th8aI1Q8rJxfI1PPDlxI37rqt5pAuMKQwGmU"]
/home/byk/Code/opencode-lore/packages/gateway/src/cli/start.ts, opts.local === true always disables remote mode by setting config.remoteGateway = false and config.remoteGatewayAutoDetected = false; this mirrors hosted-mode opt-out behavior./home/byk/Code/opencode-lore/packages/gateway/src/cli/start.ts, when opts.local === undefined and neither LORE_REMOTE_GATEWAY nor LORE_HOSTED_MODE exists in process.env, lore start preserves bind-address auto-detection or defaults to remote mode by setting config.remoteGateway = true and config.remoteGatewayCommandDefault = true.assertGatewayAccessConfigured(config) runs only after CLI/env/default mode precedence is fully applied, ensuring --local can opt out before the invariant is checked.startGateway() is serialized with withLifecycleLock("gateway-start", ...); startGatewayLocked() merges realStartGatewayIO with ioOverrides.log.warn; non-quiet lore start callers receive visible [lore] messages through console.error, because stdout/stderr writes corrupt the host agentβs full-screen TUI.startGatewayLocked() applies explicit port, hosts, debug, and allowRemoteManagement over loadConfig(); an explicit port also sets config.portExplicit = true./home/byk/Code/opencode-lore/packages/gateway/src/cli/start.ts is: opts.local first, then LORE_HOSTED_MODE, then caller default. lore start defaults hosted mode on, while lore run, lore import, OpenCode, and Pi pass local: true to keep it off.local: true because hosted mode is a process-wide @loreai/core flag that disables filesystem operations; enabling it in an embedded gateway breaks getGitRemote(), .lore.md import, config loading, and file watching.3207 β 5673 β 0; port 0 is OS-assigned and always succeeds or throws a non-EADDRINUSE error.controlToken encoded with "base64url" and a processIdentity from currentProcessIdentity() or fallback unverified:${controlToken}; the unverified Windows identity supports discovery while destructive stop fails closed.startGatewayLocked() first reads the process record and reuses the gateway only if io.authenticate(record) succeeds; reused handles have owned: false, the persisted managementToken, and a no-op shutdown.0. For other candidates, only an authenticated owner process record establishes Lore identity; public /health never establishes identity and only classifies an unauthenticated occupant as foreign."127.0.0.1", config.hosts, and matching-record hosts. A foreign occupant on an explicit port throws an error directing users to --port / LORE_LISTEN_PORT; on default ports it logs the fallback and continues.io.startServer(config, ...) runs inside withoutLifecycleLock() after lifecycleLock.assertOwned(). The await is inside the try because startServer() awaits OS binds internally and may directly reject with EADDRINUSE.remoteShutdown is installed is refused with "Remote shutdown was requested before gateway publication; refusing." and sets process.exitCode = 1.config.hosts is replaced with server.hosts when nonempty so logs and probes do not advertise dropped/unbindable interfaces such as stale Tailscale addresses that produced EADDRNOTAVAIL.io.writePort(actualPort, controlToken), then writes a version-2 GatewayProcessRecord containing pid, port, hosts, token, and processIdentity.AggregateError is thrown with both publicationError and closeError.pid, token, and processIdentity all match the current process generation.shutdownPromise and runs under withLifecycleLock("gateway-shutdown", ...) with timeoutMs: Math.max(1, Math.floor(SHUTDOWN_DEADLINE_MS / 2)).boundServer.stop() so new work is rejected; call io.resetPipelineState({ fast: true }); await listener closure; stop and settle temporalEmbeddingQueue with EMBED_DRAIN_DEADLINE_MS; settle document embeds with the same deadline; call embedding.resetProvider(); await shutdownVectorPoolAsync(VECTOR_POOL_SHUTDOWN_DEADLINE_MS); then call closeDb().closeDb() failure emits Database close warning: ... but does not wedge shutdown or preserve an otherwise stale process record; SQLite recovery handles a failed best-effort checkpoint.actualPort plus controlToken; process-record removal again requires matching pid, token, and processIdentity.EADDRINUSE, startGatewayLocked() stops any partially bound multi-host server, rereads and authenticates the exact persisted owner, reuses it if authenticated, and otherwise falls back or fails. Non-port-conflict errors are rethrown without retry.commandStart() sets processBoundary: true; if it finds an existing authenticated gateway, it reports its addresses/dashboard, notes that hosted-mode status belongs to the running instance, and exits successfully.spawnDetachedGateway(opts) redirects stdin to "ignore" and stdout/stderr to daemonLogPath(), closes the parentβs log FD in finally, calls child.unref(), and returns child.pid./home/byk/Code/opencode-lore/packages/opencode/src/index.ts imports helper functions from ./internal rather than exporting them from the plugin entry because OpenCodeβs legacy loader invokes every exported function as a plugin; leaked helper exports inserted undefined into the hooks array and crashed event dispatch with undefined is not an object (evaluating 'A.event').LORE_REMOTE_URL β LORE_GATEWAY_URL β port file β known ports 3207 and 5673; each candidate is probed, trailing slashes are removed from env URLs, and an unreachable remote URL falls through to local discovery.resolveGatewayUrl() dynamically imports readPortFile from @loreai/gateway using /* webpackIgnore: true */; inability to load the gateway package merely skips the port file.startInProcess() dynamically imports startGateway from @loreai/gateway and calls startGateway({ quiet: true, local: true }); it returns http://127.0.0.1:${handle.port}, logs reuse when handle.owned is false, and stores failures in lastGatewayStartError.processInitDone, processLoreActive, processLoreBase, and memoized loreInitPromise, preventing concurrent plugin calls from racing on gateway probe/start.projectState, keyed by ctx.project.id, with exact fields { projectPath, gitRemote, lastSeenAt }; this prevents concurrent projects or worktrees from overwriting one anotherβs path/remote attribution.currentProject stores { path, gitRemote } together so the pair is always self-consistent: a path is never emitted with a remote resolved for a different project on an earlier plugin call. This prevents non-repository directories from becoming a βgit-remote magnet.βprojectState and sessionParent use SESSION_STATE_TTL_MS = 24 * 60 * 60 * 1000 (24 hours). Stale entries are reaped opportunistically, and the documented estimate is approximately 200 bytes per project, or 20 KB for 100 projects.resolveParentSession(client, sessionID) caches { parentID, lastSeenAt } for successful SDK lookups, including null for primary sessions; non-null parent IDs are forwarded as x-parent-session-id so the gateway recognizes OpenCode Task child sessions as sub-agents and sizes LTM injection accordingly.isInertTestEnv() keeps the OpenCode plugin inert under NODE_ENV=test or any argv entry containing .test., unless LORE_OPENCODE_FORCE_ACTIVE === "1"; forced-active tests retain NODE_ENV=test and use LORE_GATEWAY_URL for controlled routing.log.silenceStderr() is called before initialization so neither the plugin nor its bundled in-process gateway writes to the full-screen TUI; logs still go to the log file and Sentry sink exposed through lore logs.surfaceGatewayUnavailable(ctx.client, msg) to show a TUI-safe notification. Errors matching not found, not exported, or cannot find module are labeled as likely stale builds and recommend pnpm --filter @loreai/gateway run bundle or run build in a development checkout.discoverWorkspaceRoot(ctx.worktree || ctx.directory) and reuses a cached git remote only when the cached projectPath still equals the newly computed path; otherwise it calls getGitRemote(thisProjectPath) ?? "".config hook sets cfg.compaction = { auto: false, prune: false } and registers "lore-distill" and "lore-curator" with mode: "subagent", hidden: true, and descriptions "Lore memory distillation worker" and "Lore knowledge curator worker" respectively.mode: "subagent" for hidden: true to take effect; otherwise OpenCode defaults them to mode: "all" and exposes them in both the primary Tab picker and the @-mention/skill list.