Dashboard › opencode › Distillation
f4546629-cb04-426a-9f21-1ac67305ec44["lore_tm_v1_A2eEo0AX_vmTM6Wnw_NFlwAQZAAapdIIHWvvXOO4aMs","lore_tm_v1__PDBvCErdtbFUUB2m1-O5tN4XEC1sdT_WDB1rrL6Vkg","lore_tm_v1_80hzE3NaX1IYH9k4CFl7aGccHk1L8Y6QbmwdNx4BK_E","lore_tm_v1_EqUT4ZGfNLkDU_ChLvZH2vik31WaMMhr0VjJrzmrCJg","lore_tm_v1_1ykBcq3bQ5P9zPBcvtliy7BEdaX99lpSqxmyP5hUZEI","lore_tm_v1_J31nJ1x23tWWIf4HDA2GCUE6RrARZTDW_fb5Uvny-9Y","lore_tm_v1__r8C0_7CEn_7CrWxTXMuFaD8sSmf9d_zSGfep-d9svM","lore_tm_v1_yzmxZ1AfKxBCH5E8Gf3iNHr2boTGp8fI7Qccf21t1iI","lore_tm_v1_4R5cxzUfr5GltO51_ABSQRuoHuraaIDA63VrHwgxBh8"]
/home/byk/Code/opencode-lore-v2/packages/core/src/fetch-interceptor.ts implements fetch-level interception by wrapping globalThis.fetch, allowing provider SDKs to construct requests with their original authentication, headers, and URLs before transparent rerouting through the Lore gateway.FetchInterceptorConfig contains gatewayBase: string, dynamic per-request getHeaders: () => Record<string, string>, and optional onRequestHeaders?: (headers: Headers) => void.LLM_API_PATH_PATTERNS recognizes /v1/..., /api/v1/..., /api/..., /openai/v1/..., /anthropic/v1/..., and /codex/responses, limited to messages, chat/completions, or responses endpoint suffixes. The permissive prefixes prevent recurrence of the Onur “lore-config” bug for providers using non-standard path prefixes.NON_STANDARD_PATH_REWRITES maps "/codex/responses" to "/v1/codex/responses" for ChatGPT’s Codex Responses API."anthropic", "openai", and "openai-responses"; PROTOCOL_GATEWAY_PATHS maps them respectively to /v1/messages, /v1/chat/completions, and /v1/responses.LLM_ENDPOINT_SUFFIXES is ordered longest-first as: 1. /chat/completions, 2. /codex/responses, 3. /responses, 4. /messages.extractBodyString() synchronously inspects only RequestInit.body values that are strings, ArrayBuffer, or typed-array views, decoding binary bodies with TextDecoder; it does not consume ReadableStream bodies or bodies available only through a Request, and returns undefined on unsupported bodies or decoding failures.detectProtocolFromBody() requires a parsed non-null object with model. It detects OpenAI Responses first via array-valued input, max_output_tokens, or previous_response_id; Anthropic via top-level system plus array-valued messages; and OpenAI Chat as the fallback for array-valued messages.system—it embeds system messages inside the messages array.store and instructions are intentionally not used for protocol detection because some Chat Completions extensions also use them.Rewrite contains gatewayUrl, upstreamBase, and upstreamPath; upstreamPath preserves the client’s full original endpoint pathname so the gateway can reconstruct origin(base) + pathname rather than synthesizing /v1/.... This supports GitHub Copilot’s /chat/completions endpoint from issue #1052 and providers with non-standard prefixes.interceptUrl() first extracts the final /v1/ portion, preserving the query in gatewayUrl and treating the preceding origin/path as upstreamBase; it then tries NON_STANDARD_PATH_REWRITES, and returns null if URL-only mapping is impossible.interceptUrlForProtocol() handles body-detected non-standard endpoints such as /v2/chat/completions and /llm/messages: the detected protocol selects the canonical gateway path, while the first matching suffix in LLM_ENDPOINT_SUFFIXES is removed to derive upstreamBase; absent a known suffix, it falls back to the upstream origin.rewriteRequest() never reads a Request body, allowing known paths to carry streaming bodies safely.isLocalHost() treats localhost, 127.0.0.1, 0.0.0.0, ::1, and [::1] as local and therefore ineligible for gateway routing.shouldIntercept(url, gatewayBase) returns false for malformed URLs, direct gateway URLs, and local hosts; otherwise it only returns true when matchesLLMApiPath(parsed.pathname) succeeds.rewriteRequestForProtocol() observes headers on direct gateway requests without modifying them, leaves local and unmappable requests unchanged, and routes recognized requests by constructing new Request(rewrite.gatewayUrl, request), calling applyGatewayHeaders(), and invoking observeRequestHeaders().applyGatewayHeaders() preserves original headers, sets lk= No, it sets x-lore-upstream-url to the upstream base and sets x-lore-upstream-path only for a saneE-supported absolute? Actually exact: only sane absolute path starts "/". Dynamic headers are added only when absent, so caller-set values win; failures from dynamic-header generation are logged as fetch-interceptor: getHeaders() failed:.observeRequestHeaders() is best effort: callback failures are caught and logged as fetch-interceptor: onRequestHeaders() failed:.installFetchInterceptor() uses process-global Symbol.for("lore.fetchInterceptor.originalFetch") as a cross-copy double-install guard. This was chosen over a module-scoped handle because separate bundled copies would each install an interceptor, stack wrappers, and create the gateway → interceptor → gateway infinite-loop shape associated with issue #1027.installFetchInterceptor() calls return a no-op cleanup. The installing copy captures globalThis.fetch, patches it with Object.assign(interceptor, originalFetch) to preserve properties such as Node’s preconnect, and cleanup restores the original fetch and writes null to the shared slot.mayBeLLMRequest() as a fast string check for /messages, /completions, or /responses, parses the URL once, bypasses local requests, and attempts body-shape detection only when the path looks LLM-like but does not match a known pattern.${upstream.host}${upstream.pathname} through the module-level warnedPaths set, using the warning matched no LLM API pattern — request bypassing Lore gateway. Add a pattern in fetch-interceptor.ts if this is an LLM endpoint./home/byk/Code/opencode-lore-v2/packages/opencode/src/server.ts defines the lore OpenCode plugin and configures 3 hidden subagents in subagent mode, in exact order: 1. lore-distill — “Lore memory distillation worker”; 2. lore-curator — “Lore knowledge curator worker”; 3. lore-query-expand — “Lore query expansion worker”.acquireServerRuntime(ctx.location.project.directory) and returns without registering anything when the runtime is unavailable.model.request hook fetches the session by event.sessionID, tolerates lookup failure, builds headers using runtime, session ID, optional session.parentID, agent, and event.model.providerID, assigns them into event.headers, and changes event.baseURL to ${runtime.gatewayBase}/v1 only when x-lore-upstream-url exists.http.request hook replaces event.request with await rewriteRequest(event.request, runtime.gatewayBase, {}).Promise.allSettled() before rethrowing. Normal cleanup is idempotent via cleaned, settles all registration disposals plus runtime.release(), and throws AggregateError(failures, "Lore plugin cleanup failed") if any cleanup operation rejects./home/byk/Code/opencode-lore-v2/packages/opencode/src/server-runtime.ts uses gateway package @loreai/gateway, known local ports [3207, 5673], and a process-level gatewayLeasePromise with reference-counted GatewayLease.LoreServerRuntime exposes gatewayBase, projectPath, gitRemote, gatewayHeaders, and idempotent asynchronous release(). ServerRuntimeDependencies allows injectable resolveGateway() and gitRemote(path) implementations.resetServerRuntimeForTest() throws Server runtime reset is test-only unless NODE_ENV === "test"; it clears gatewayLeasePromise and invokes the prior lease’s optional shutdown.acquireServerRuntime() is inactive during tests or when an argument includes .test. unless LORE_OPENCODE_FORCE_ACTIVE === "1". It is also inactive when LORE_DISABLED is "1" or "true".acquireServerRuntime() obtains a gateway lease, resolves the git remote with injected gitRemote or getGitRemote, normalizes a missing remote to "", and releases the acquired lease before rethrowing if git-remote resolution fails.acquireGatewayLease() shares an existing initialization promise, retries after any lease’s closing promise finishes, increments lease.refs, and clears gatewayLeasePromise when initialization fails.createGatewayLease() uses an already discoverable gateway when available. Otherwise it logs No Lore gateway found, starting in-process…, dynamically imports @loreai/gateway, calls startGateway({ quiet: true, local: true }), and uses http://127.0.0.1:${handle.port}; shutdown is retained only when handle.owned is true.LORE_REMOTE_URL, with unreachable remotes logged before falling through; 2. normalized LORE_GATEWAY_URL; 3. a port from @loreai/gateway’s readPortFile() if available; 4. known ports 3207 and 5673. Each candidate is accepted only when probeGateway(url) succeeds.releaseGatewayLease() decrements refs; shutdown begins only when references reach 0. The lease’s closing promise serializes shutdown, and gatewayLeasePromise is cleared after shutdown only if it still points to that active lease.buildServerHeaders() creates x-lore-session-id, x-lore-agent, x-lore-project, x-lore-git-remote, and x-lore-provider, merges runtime gateway-access headers, and adds x-parent-session-id when parentID exists.buildServerHeaders() resolves a provider-specific upstream from environment variable LORE_UPSTREAM_${providerID.toUpperCase().replace(/-/g, "_")} and places it in x-lore-upstream-url; parsed LORE_UPSTREAM_EXTRA_HEADERS entries are included only when shouldForwardUpstreamExtraHeader(name) permits them.AgentEditor.list(), get(id), default(id), update(id, update), and remove(id); AgentDomain extends AgentApi and exposes transform: Transform<AgentEditor> plus reload(): Promise<void>.Registration.dispose(): Promise<void>, optional ModelHookOptions.providerID, generic asynchronous Hooks<Spec> and ModelHooks<Spec>, and Transform<Input> returning a Promise<Registration>.2914 insertions(+), 206 deletions(-): packages/core/src/fetch-interceptor.ts 135/115; packages/core/src/index.ts 3/0; packages/core/test/fetch-interceptor-request.test.ts 163/0; packages/opencode/package.json 15/2; packages/opencode/script/build.ts 25/0; packages/opencode/src/index.ts 7/15; packages/opencode/src/internal.ts 13/0; packages/opencode/src/server-core.ts 4/0; packages/opencode/src/server-runtime.ts 222/0; packages/opencode/src/server.ts 79/0; packages/opencode/test/internal.test.ts 10/0; packages/opencode/test/package.test.ts 62/0; packages/opencode/test/server-runtime.test.ts 159/0; packages/opencode/test/server.test.ts 316/0; pnpm-lock.yaml 1701/74./home/byk/Code/opencode-lore-v2/packages/core/test/fetch-interceptor-request.test.ts adds 10 effective rewriteRequest cases: 1. routes OpenRouter /api/v1/chat/completions?stream=true&trace=abc to ${GATEWAY}/v1/chat/completions?stream=true&trace=abc while preserving POST body, Bearer sk-test, api-key-test, dynamic headers, upstream base https://openrouter.ai/api, and original path /api/v1/chat/completions; 2. preserves caller-set x-lore-session-id: request-session; 3. leaves direct gateway requests unchanged while observing their headers; 4–7. leaves localhost, 127.0.0.1, 0.0.0.0, and [::1] requests unchanged; 8. leaves a non-LLM npm registry request unchanged; 9. routes a known-path streaming body without consuming the original; 10. neither reads nor routes a streaming body on unknown /v2/chat/completions?stream=true./home/byk/Code/opencode-lore-v2/packages/gateway/src/config.ts defines extractUpstreamUrlHeader(): it strips control characters, trims, enforces MAX_UPSTREAM_URL_LENGTH, accepts only credential-free HTTP(S) URLs, strips trailing slashes and a final /v1, and returns undefined for absent or invalid input.normalizeUpstreamBase() repeatedly decodes up to 4 times and rejects backslashes, control bytes, encoded control/dot/slash/backslash bytes, literal . or .. path segments, credentials, query strings, fragments, and non-HTTP(S) protocols before returning a normalized origin plus path boundary.isUpstreamWithinBase() uses segment-safe containment so /tenant-a does not match /tenant-ab. extraHeadersForUpstream() supplies gateway-admin upstreamExtraHeaders only when the destination falls within a configured trusted upstream base, preventing arbitrary caller URL overrides from receiving gateway-global credentials.extractUpstreamPathHeader() limits x-lore-upstream-path to 512 characters, strips control characters and trims, requires exactly an absolute single-leading-slash path, and rejects protocol-relative //host, whitespace, and any ...origin(effectiveUpstreamBase) + upstreamPath over a reconstructed canonical URL only when both a validated header-derived upstream base and preserved upstream path are present; the path cannot change the destination host.