Dashboard › opencode › Distillation
2732cac6-825d-48d4-9540-6d0fad82b4cb["lore_tm_v1_jzuxErMB_cx5L066Vydszn7w-gbnzRYYDW0PegsVJoM","lore_tm_v1_MF4dLnqRk6MtMuJ3vVym7HHoByyPscf5bCLMTJjw72c","lore_tm_v1_7W6PmQTF7ChLYhOLkebYLHEhswseE2P9xyxT-3t-5fw","lore_tm_v1_8lIhONpeVDpsmIbCsVydHz8hER1qPaEAjUYt50H5Wng","lore_tm_v1_CxoNajt-3S9ZVXINVWG83Fzt5-ZaDO5kc2dWVzJgn1o","lore_tm_v1_y_k4pBbTzoRH5uw6n22DFC7SPxTrqGs4ISActlG0Ncw"]
SessionModelRequest implementation, Permission.DeclinedError and QuestionTool.CancelledError deliberately travel through the defect tunnel beneath blanket mapError, then declineDefect() resurfaces them exactly once as typed ExecuteError failures at the runner execution seam.IMAGE_BYTES_TRIGGER = 25 * 1024 * 1024 (25 MiB), IMAGE_BYTES_TARGET = 15 * 1024 * 1024 (15 MiB), and replacement text IMAGE_REMOVED; boundImages() removes the oldest encountered image media/tool-result files until the estimated request image size is at or below the target, while requests at or below the trigger remain unchanged.ExecuteError = Tool.Error | Permission.DeclinedError | QuestionTool.CancelledError; Prepared contains request: LLMRequest, options: StreamOptions, retry(event), and executeTool(input), while PrepareInput carries request kind, session/agent/model scope, optional contextAgentID, optional tools, transcript, optional toolChoice, optional contextHooks?: false, and optional webSocket?: "session".baseTranscript() selects providerMetadataKey from input.model.model.route.providerMetadataKey ?? input.model.model.provider, uses the agent’s explicit system prompt or SessionSystemPrompt.make() over tool names, appends the initial prompt when nonempty, wraps system parts with SystemPart.make, and converts history through toLLMMessages(input.messages, input.model.ref, providerMetadataKey).unsupportedParts() converts unsupported image/audio/video/PDF inputs—including files nested inside content-style tool results—to text errors of the form ERROR: Cannot read ... (this model does not support ... input). Inform the user."x-session-affinity", "X-Session-Id", optional "x-parent-session-id", "User-Agent", "x-opencode-project", "x-opencode-session", and "x-opencode-client"; promptCacheKey() strips the ses_ prefix only for IDs matching /^ses_[0-9a-f]{64}$/.applyModelHooks() triggers hooks.trigger("session", "model.request", ...), allowing plugins to rewrite the model route baseURL and HTTP headers while preserving the existing HTTP body and query.httpMiddleware() exposes each exchange through web-standard Request/Response values to "session" hooks "http.request" and "http.response"; request bodies are cloned into a Uint8Array, statuses 204, 205, and 304 produce null response bodies, and non-Error failures are normalized with new Error(String(cause)).SessionModelRequest.prepare() lets "session.context" hooks mutate system messages, messages, generation settings, provider options, and tool definitions. Surviving tool definitions are resolved by definition-object identity first and original key second; renamed definitions still execute their canonical underlying tool, removed tools disappear, and definitions invented by hooks are dropped.SessionModelRequest.prepare() constructs the request with session headers, cache lineage from session.fork?.sessionID ?? session.id, boundImages(unsupportedParts(...)), hooked tools, optional generation/provider options, and then applies model-request hooks. It defects with Provider context is incompatible with the route selected by model request hooks if an existing compaction window is incompatible with the hook-selected deployment.resolved.capabilities.responsesWebsockets === true, provider-specific config flag OPENCODE_EXPERIMENTAL_${providerID.replace(/[^a-zA-Z0-9]+/g, "_").toUpperCase()}_RESPONSES_WEBSOCKET, input.webSocket === "session", and no "http.request" or "http.response" hooks. Otherwise HTTP middleware is installed when either HTTP hook exists.Context.Service identifier "@opencode/SessionModelRequest" and its location node depends on PluginHooks.node, SessionModelTransport.node, and App.node./home/byk/Code/opencode-lore-v2/packages/opencode/src/internal.ts keeps helper functions outside ./index.ts because OpenCode’s legacy getServerPlugin/getLegacyPlugins loader invokes every exported function as a plugin. Root cause of the prior crash: exporting applyLoreProviderConfig from the entry module caused its undefined return value to enter the hooks array, then the first dispatch failed with undefined is not an object (evaluating 'A.event'); fix: entry module exposes only the plugin function while tests import helpers from internal.ts.gatewayAccessHeadersForRemote() only injects { [GATEWAY_AUTH_HEADER]: LORE_GATEWAY_AUTH_TOKEN } when normalized LORE_REMOTE_URL exactly matches the selected gateway base. shouldForwardUpstreamExtraHeader() blocks all x-lore-* headers plus x-api-key, x-goog-api-key, and authorization; parseUpstreamExtraHeaders() parses newline-delimited name:value entries and drops malformed/empty-name lines.resolveSDK() “always passes options.baseURL” to the @ai-sdk factory, so @ai-sdk’s loadOptionalSetting() only consults an environment variable when baseURL is undefined.applyLoreProviderConfig() in /home/byk/Code/opencode-lore-v2/packages/opencode/src/internal.ts universally pins every configured provider’s options.baseURL to ${gatewayBase}/v1, because relying on OPENAI_BASE_URL/ANTHROPIC_BASE_URL is insufficient and providers such as Google, Mistral, Groq, Cohere, xAI, Perplexity, Together AI, Vercel, Alibaba, DeepInfra, Gateway, OpenRouter, and Cerebras have no base-URL environment variable. The function deep-merges provider settings, sets headerTimeout: false for openai and openai-codex, and creates an openai provider with those options if absent.applyLoreProviderConfig() was that OpenCode could derive Anthropic’s base URL from OPENAI_BASE_URL while stripping /v1, causing requests to hit http://host/messages; the Lore gateway only routes /v1/messages, and its fetch interceptor skips 127.0.0.1 to prevent loops, resulting in a bare /messages 404.probeGateway(baseURL, timeoutMs = 1500) requests ${baseURL}/health, uses native http/https for loopback hosts (localhost, localhost., ::1, or 127.x.x.x) and fetch otherwise, aborts at the timeout, returns false on every exception, and always clears the timer.surfaceGatewayUnavailable() logs to file/Sentry and calls client?.tui?.showToast() with { title: "Lore", message, variant: "error" }, replacing direct process.stderr.write, which could corrupt OpenCode’s full-screen TUI.surfaceGatewayUnavailable() is fire-and-forget, defensively swallows rejected toast promises with .catch?.(() => {}), and catches synchronous showToast failures so missing/headless TUIs or older servers without /tui/show-toast cannot crash a degraded-but-running session.getGitRemote(path) and getGitUser(path) in /home/byk/Code/opencode-lore-v2/packages/core/src/git.ts return immediately in hosted mode (null and { name: null, email: null }, respectively).normalizeRemoteUrl() canonicalizes Git remotes by trimming, converting SSH shorthand such as git@github.com:user/repo.git, stripping protocol/auth/.git/trailing slashes, and lowercasing only the host. Examples normalize SSH, HTTPS, ssh://, git://, and credential-bearing HTTPS URLs to forms such as github.com/user/repo.getGitRemote() caches per absolute path in gitRemoteCache, runs git remote -v once with cwd: path, UTF-8 encoding, timeout: 5000, and piped stdio, parses fetch remotes only, and deliberately chooses origin before upstream before any other remote. Origin-first avoids falsely merging unrelated repositories that share a common template upstream; incorrect merging is considered worse than failing to unify a genuine fork.getGitUser() independently runs git config user.name and git config user.email with timeout: 5000, caches nullable results in gitUserCache, and tolerates missing Git, non-repositories, and unset values. clearGitRemoteCache() and clearGitUserCache() provide test isolation./home/byk/Code/opencode-lore-v2/packages/gateway/src/compaction.ts defines LORE_AGENT_HEADER = "x-lore-agent" as the authoritative OpenCode-plugin signal for meta-request detection. Primary agents are "coder" and "code" (always normal turns); meta agents are "title", "summary", "summarize", "categorize", "label", and "classify" (always passthrough); unknown agents fall through to heuristics.isMetaRequest() first excludes compaction requests, then applies weighted signals: tools ≤ 2 adds 3; messages ≤ 2 adds 3; system length < 500 adds 2; maxTokens in 1..300 adds 3; and one matching meta keyword in a system prompt shorter than 2000 adds 2. The threshold is 8, preserving the old 3 + 3 + 2 behavior."generate a title", 2. "generate a short title", 3. "title for the conversation", 4. "title for this conversation", 5. "summarize this conversation", 6. "summarize the conversation", 7. "conversation summary", 8. "categorize", 9. "classify this", 10. "label this". Detection intentionally favors false negatives over false positives so a real conversation turn is not mistakenly skipped.assembleOfflineCompaction() is documented to deterministically concatenate prior compacted summary, ordered Lore distillation observations, pending messages, and long-term knowledge without an LLM call. It intentionally does not emulate COMPACT_SUMMARY_TEMPLATE; the raw assembly is accepted as sufficient compressed history and returns null only when all four sources are absent.