Dashboard › opencode › Session 0OQedMliLshX
0OQedMliLshXVgQzqAct as an independent security auditor. Produce a substantive read-only pentest report for the frozen Lore V2 port in /home/byk/Code/opencode-lore-v2. You are replacing reviewers that returned empty, so your final response MUST contain evidence or an exact blocker and MUST NOT be empty.
Immutable target:
jj diff --git: e58f101082669135fc7f3f11d466c22f696de14270c63747d021efecc8e0bf95Constraints:
Required threat review:
I will pin the review to the supplied immutable Git/Jujutsu objects, verify every artifact before reading source, then inspect the full diff, all 15 changed files, tests, package contents, and lifecycle/security boundaries without modifying the workspace or contacting services.
<skill_content name="jj-guide"> # Skill: jj-guide # jj Guide for AI Agents Jujutsu (jj) is a Git-compatible VCS with mutable commits, automatic snapshotting, no staging area, and first-class conflicts. This skill teaches you how to use it safely from a non-interactive agent environment. If `.jj/` exists in the repo root, this is a jj repo. **Use `jj` commands, not `git`.** In a colocated repo (`.jj/` *and* `.git/`), git tools can read the state, but mutations should go through `jj` so the operation log stays consistent. ## Critical Rules - **NEVER** use interactive flags (`-i`, `--interactive`). TUI prompts hang in agent environments. This applies to `jj split -i`, `jj squash -i`, `jj commit -i`, `jj resolve`, `jj diffedit`, etc. - **ALWAYS** pass `-m "msg"` when describing/committing. Without `-m`, an editor opens and hangs. - **VERIFY** mutations with `jj st` and `jj log` after `squash`, `abandon`, `rebase`, `restore`, `commit`. ...
[
{
"content": "Verify target identity and all supplied hashes before review",
"status": "in_progress",
"priority": "high"
},
{
"content": "Inspect exact parent-to-target diff and every changed file",
"status": "pending",
"priority": "high"
},
{
"content": "Audit required security threat areas and test preconditions",
"status": "pending",
"priority": "high"
},
{
"content": "Inspect package exports, tarball contents, bundle boundaries, and dependency resolution",
"status": "pending",
"priority": "high"
},
{
"content": "Re-verify target identity and all supplied hashes after review",
"status": "pending",
"priority": "high"
},
{
"content": "Produce evidence-backed pentest verdict",
"status": "pending",
"priority": "high"
}
]
wlpqmtosolpkopymsmpyonmmvoqpzlyx 52e6687e1eae545273185978c57838d8782e4e92 uutomtupslqmmvuzknuurlrokzlrruuv 429114122aa1170fd171aa3f0a2bb97e5a6bd6aa
e58f101082669135fc7f3f11d466c22f696de14270c63747d021efecc8e0bf95 -
sha256sum: /tmp/opencode/loreai-opencode-0.40.0.tgz: No such file or directory
bbf89f31f76a000d9746733c8ad11a283d5c27fc9368b77ef682ddcf9debb2d2 /home/byk/Code/opencode-lore-v2/packages/opencode/dist/server.js
The source identities, diff hash, and bundle hash match. The required tarball is absent: sha256sum "/tmp/opencode/loreai-opencode-0.40.0.tgz" returned No such file or directory; I will complete every source and bundle check still possible, but package self-containment cannot pass and the final verdict must remain DO-NOT-MERGE.
[
{
"content": "Verify target identity and all supplied hashes before review",
"status": "completed",
"priority": "high"
},
{
"content": "Inspect exact parent-to-target diff and every changed file",
"status": "in_progress",
"priority": "high"
},
{
"content": "Audit required security threat areas and test preconditions",
"status": "pending",
"priority": "high"
},
{
"content": "Inspect package exports, tarball contents, bundle boundaries, and dependency resolution",
"status": "pending",
"priority": "high"
},
{
"content": "Re-verify target identity and all supplied hashes after review",
"status": "pending",
"priority": "high"
},
{
"content": "Produce evidence-backed pentest verdict",
"status": "pending",
"priority": "high"
}
]
M packages/core/src/fetch-interceptor.ts M packages/core/src/index.ts A packages/core/test/fetch-interceptor-request.test.ts M packages/opencode/package.json A packages/opencode/script/build.ts M packages/opencode/src/index.ts M packages/opencode/src/internal.ts A packages/opencode/src/server-core.ts A packages/opencode/src/server-runtime.ts A packages/opencode/src/server.ts M packages/opencode/test/internal.test.ts A packages/opencode/test/package.test.ts A packages/opencode/test/server-runtime.test.ts A packages/opencode/test/server.test.ts M pnpm-lock.yaml
packages/core/src/fetch-interceptor.ts | 250 +-- packages/core/src/index.ts | 3 + ...ges/core/test/fetch-interceptor-request.test.ts | 163 ++ packages/opencode/package.json | 17 +- packages/opencode/script/build.ts | 25 + packages/opencode/src/index.ts | 22 +- packages/opencode/src/internal.ts | 13 + packages/opencode/src/server-core.ts | 4 + packages/opencode/src/server-runtime.ts | 222 ++ packages/opencode/src/server.ts | 79 + packages/opencode/test/internal.test.ts | 10 + packages/opencode/test/package.test.ts | 62 + packages/opencode/test/server-runtime.test.ts | 159 + packages/opencode/test/server.test.ts | 316 +++ pnpm-lock.yaml | 1755 +++++++++++++++++++++- 15 files changed, 2904 insertions(+), 196 deletions(-)
diff --git a/packages/core/src/fetch-interceptor.ts b/packages/core/src/fetch-interceptor.ts
index 602362b0f6..f10263cde3 100644
--- a/packages/core/src/fetch-interceptor.ts
+++ b/packages/core/src/fetch-interceptor.ts
@@ -195,6 +195,12 @@
upstreamPath: string;
};
+export type DynamicRequestHeaders = Readonly<Record<string, string>>;
+
+export type DynamicRequestHeadersSource =
+ | DynamicRequestHeaders
+ | (() => DynamicRequestHeaders);
+
/**
* Rewrite an intercepted URL to the gateway, handling both standard /v1/...
* paths and non-standard paths (e.g. /codex/responses). Used by the
@@ -284,17 +290,7 @@
// Never intercept requests already going to the gateway
if (url.startsWith(gatewayBase)) return false;
// Never intercept local requests (could be local LLM or gateway itself)
- const host = parsed.hostname;
- if (
- host === "localhost" ||
- host === "127.0.0.1" ||
- host === "0.0.0.0" ||
- // URL.hostname KEEPS brackets on IPv6 ...
diff --git a/packages/opencode/package.json b/packages/opencode/package.json
index 2dc3111e7d..612407f54f 100644
--- a/packages/opencode/package.json
+++ b/packages/opencode/package.json
@@ -11,14 +11,25 @@
"types": "./src/index.ts",
"bun": "./src/index.ts",
"default": "./src/index.ts"
+ },
+ "./server": {
+ "types": "./src/server.ts",
+ "bun": "./dist/server.js",
+ "default": "./dist/server.js"
}
},
"scripts": {
"typecheck": "tsc --noEmit",
- "build": "echo '@loreai/opencode ships raw TS — no build step needed'"
+ "build": "tsx script/build.ts"
},
"peerDependencies": {
- "@opencode-ai/plugin": ">=1.1.0"
+ "@opencode-ai/plugin": ">=1.1.0",
+ "@opencode/plugin": ">=0.0.0"
+ },
+ "peerDependenciesMeta": {
+ "@opencode/plugin": {
+ "optional": true
+ }
},
"dependencies": {
"@loreai/core": "workspace:*",
@@ -27,10 +38,12 @@
"devDependencies": {
"@opencode-ai/plugin": "^1.1.39",
...
diff --git a/packages/opencode/test/internal.test.ts b/packages/opencode/test/internal.test.ts
index 570ae5a6be..d18e2049ed 100644
--- a/packages/opencode/test/internal.test.ts
+++ b/packages/opencode/test/internal.test.ts
@@ -3,11 +3,21 @@
import { log } from "@loreai/core";
import {
gatewayAccessHeadersForRemote,
+ parseUpstreamExtraHeaders,
shouldForwardUpstreamExtraHeader,
surfaceGatewayUnavailable,
} from "../src/internal";
describe("remote gateway access headers", () => {
+ test("parses empty values and CRLF without changing legacy semantics", () => {
+ expect(
+ parseUpstreamExtraHeaders("x-empty:\r\nx-team: platform\r\ninvalid"),
+ ).toEqual([
+ ["x-empty", ""],
+ ["x-team", "platform"],
+ ]);
+ });
+
const token = "opencode-remote-gateway-token-at-least-32";
test("injects the access token only for the matching LORE_REMOTE_URL", () => {
diff --git a/packages/opencode/test/package.test.ts ...
...output truncated...
Full output saved to: /home/byk/.local/share/opencode/tool-output/tool_08b567e50001FTIw44JvlAefOY
@@ -5481,7 +6166,20 @@
'@aws-sdk/middleware-eventstream': 3.972.16
'@aws-sdk/middleware-websocket': 3.972.26
'@aws-sdk/token-providers': 3.1048.0
- '@aws-sdk/types': 3.973.11
+ '@aws-sdk/types': 3.973.13
+ '@smithy/core': 3.24.6
+ '@smithy/fetch-http-handler': 5.4.6
+ '@smithy/node-http-handler': 4.7.7
+ '@smithy/types': 4.15.0
+ tslib: 2.8.1
+
+ '@aws-sdk/client-cognito-identity@3.1057.0':
+ dependencies:
+ '@aws-crypto/sha256-browser': 5.2.0
+ '@aws-crypto/sha256-js': 5.2.0
+ '@aws-sdk/core': 3.974.18
+ '@aws-sdk/credential-provider-node': 3.972.52
+ '@aws-sdk/types': 3.973.13
'@smithy/core': 3.24.6
'@smithy/fetch-http-handler': 5.4.6
'@smithy/node-http-handler': 4.7.7
@@ -5490,7 +6188,7 @@
'@aws-sdk/core@3.974.18':
dependencies:
- ...
<path>/home/byk/.local/share/opencode/tool-output/tool_08b567e50001FTIw44JvlAefOY</path> <type>file</type> <content> 1: diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml 2: index c9b41a1b23..b2983d8ba4 100644 3: --- a/pnpm-lock.yaml 4: +++ b/pnpm-lock.yaml 5: @@ -185,7 +185,7 @@ 6: version: link:../core 7: '@sentry/bun': 8: specifier: ^10.52.0 9: - version: 10.56.0 10: + version: 10.56.0(@opentelemetry/exporter-trace-otlp-http@0.214.0(@opentelemetry/api@1.9.1)) 11: '@types/bun': 12: specifier: ^1.2.0 13: version: 1.3.9 14: @@ -229,6 +229,9 @@ 15: '@opencode-ai/sdk': 16: specifier: ^1.1.39 17: version: 1.2.15 18: + '@opencode/plugin': 19: + specifier: 0.0.0-beta-19378 20: + version: ...
<path>/home/byk/.local/share/opencode/tool-output/tool_08b567e50001FTIw44JvlAefOY</path>
<type>file</type>
<content>
1075: + resolution: {integrity: sha512-MbA4R+WQT76ZBm/5JUpV9yqcJt92175+Y0Bodg3HgiXzrmKu7Ggq+bpn6y6wHH+gN9NcyKn3yg1+d47VaKwNAQ==}
1076: + engines: {node: ^20.17.0 || >=22.9.0}
1077: +
1078: progress@2.0.3:
1079: resolution: {integrity: sha512-7PiHtLll5LdnKIMw100I+8xJXR5gW2QwWYkT6iJva0bXitZKa/XMrSbdmg3r2Xnaidz9Qumd0VPaMrZlF9V9sA==}
1080: engines: {node: '>=0.4.0'}
1081:
1082: + promise-all-reject-late@1.0.1:
1083: + resolution: {integrity: sha512-vuf0Lf0lOxyQREH7GDIOUMLS7kz+gs8i6B+Yi8dC68a2sychGrHTJYghMBD6k7eUcH0H5P73EckCA48xijWqXw==}
1084: +
1085: + promise-call-limit@3.0.2:
1086: + resolution: {integrity: sha512-mRPQO2T1QQVw11E7+UdCJu7S61eJVWknzml9sC1heAdj1jxl0fWMBypIt9ZOcLFf8FkG995ZD7RnVk7HH72fZw==}
1087: +
1088: proper-lockfile@4.1.2:
1089: resolution: {integrity: ...
<path>/home/byk/.local/share/opencode/tool-output/tool_08b567e50001FTIw44JvlAefOY</path>
<type>file</type>
<content>
2416: '@astrojs/internal-helpers': 0.10.1
2417: @@ -7580,7 +8861,7 @@
2418: tinyglobby: 0.2.17
2419: ultrahtml: 1.6.0
2420: unifont: 0.7.4
2421: - unstorage: 1.17.5
2422: + unstorage: 1.17.5(aws4fetch@1.0.20)
2423: vite: 8.1.5(@types/node@25.3.2)(esbuild@0.28.1)(jiti@2.7.0)(tsx@4.22.4)(yaml@2.9.0)
2424: vitefu: 1.1.3(vite@8.1.5(@types/node@25.3.2)(esbuild@0.28.1)(jiti@2.7.0)(tsx@4.22.4)(yaml@2.9.0))
2425: xxhash-wasm: 1.1.0
2426: @@ -7624,6 +8905,8 @@
2427: - uploadthing
2428: - yaml
2429:
2430: + aws4fetch@1.0.20: {}
2431: +
2432: axobject-query@4.1.0: {}
2433:
2434: b4a@1.8.1: {}
2435: @@ -7678,6 +8961,14 @@
2436:
2437: bignumber.js@9.3.1: {}
2438:
2439: + bin-links@6.0.2:
2440: + dependencies:
2441: + cmd-shim: 8.0.0
2442: + npm-normalize-package-bin: 5.0.0
2443: ...
1 /**
2 * Fetch-level interception for transparent LLM API proxy routing.
3 *
4 * Instead of overwriting provider baseURLs early (which loses original auth
5 * and URL context), this interceptor wraps `globalThis.fetch` to redirect
6 * outgoing LLM API calls through the Lore gateway at the HTTP level.
7 *
8 * The SDK builds requests normally (correct auth, correct URL for each
9 * provider), and the interceptor transparently reroutes them through the
10 * gateway while preserving all original headers.
11 */
12 import * as log from "./log";
13
14 /** Configuration for the fetch interceptor. */
15 export type FetchInterceptorConfig = {
16 /** Base URL of the Lore gateway (e.g., "http://127.0.0.1:3207"). */
17 gatewayBase: string;
18 /**
19 * Dynamic headers to inject on every intercepted request.
20 * Called per-request so values can change (e.g., session ID). ...
1 import { getGitRemote, log } from "@loreai/core";
2 import {
3 gatewayAccessHeadersForRemote,
4 parseUpstreamExtraHeaders,
5 probeGateway,
6 shouldForwardUpstreamExtraHeader,
7 } from "./internal";
8
9 const gatewayPackage = "@loreai/gateway";
10 const knownGatewayPorts = [3207, 5673];
11
12 export interface LoreServerRuntime {
13 gatewayBase: string;
14 projectPath: string;
15 gitRemote: string;
16 gatewayHeaders: Record<string, string>;
17 release: () => Promise<void>;
18 }
19
20 export interface ServerRuntimeDependencies {
21 resolveGateway?: () => Promise<{
22 gatewayBase: string;
23 gatewayHeaders: Record<string, string>;
24 shutdown?: () => Promise<void>;
25 }>;
26 gitRemote?: (path: string) => string | null;
27 }
28
29 interface GatewayHandle {
30 port: number;
31 owned: boolean;
32 shutdown: () ...
1 import { rewriteRequest } from "@loreai/core";
2 import type { Plugin } from "@opencode/plugin";
3 import { acquireServerRuntime, buildServerHeaders } from "./server-runtime";
4
5 const workers = [
6 ["lore-distill", "Lore memory distillation worker"],
7 ["lore-curator", "Lore knowledge curator worker"],
8 ["lore-query-expand", "Lore query expansion worker"],
9 ] as const;
10
11 export default {
12 id: "lore",
13 setup: async (ctx) => {
14 const runtime = await acquireServerRuntime(ctx.location.project.directory);
15 if (!runtime) return;
16
17 const registrations: Array<{ dispose: () => Promise<void> }> = [];
18 try {
19 registrations.push(
20 await ctx.agent.transform((editor) => {
21 for (const [id, description] of workers) {
22 editor.update(id, (agent) => {
23 agent.description = description;
24 ...
1 /**
2 * Internal helpers for the Lore OpenCode plugin.
3 *
4 * These functions are intentionally kept OUT of the plugin entry module
5 * (`./index.ts`). OpenCode's legacy plugin loader treats EVERY function
6 * exported from a plugin module as a plugin instance and invokes it (see
7 * `getServerPlugin`/`getLegacyPlugins` in opencode's plugin loader). Exporting
8 * these helpers from the entry module caused them to be invoked as plugins and
9 * their return values pushed into the host's hooks array:
10 * `applyLoreProviderConfig` returns `undefined`, so the host crashed on the
11 * first hook dispatch with `undefined is not an object (evaluating 'A.event')`
12 * (the `?.` guards the `.event` property, not the `undefined` hook element).
13 * Keeping them in a separate module means the entry module exposes only the
14 * plugin function itself, while tests can still import them here. ...
The full 3,142-line parent-to-target lockfile diff is now inspected. The central attack surface is the new request rewrite plus the V2 host hooks; I am tracing those headers through the gateway and checking the pinned host hook semantics before classifying spoofing and SSRF findings.
...output truncated...
Full output saved to: /home/byk/.local/share/opencode/tool-output/tool_08b58c45c001Z26igYthRXY6LB
52e6687e1eae545273185978c57838d8782e4e92:packages/gateway/test/compact-endpoint-tenant-isolation.e2e.test.ts:349: "x-lore-session-id": SESSION_HEADER,
52e6687e1eae545273185978c57838d8782e4e92:packages/gateway/test/compact-endpoint.test.ts:31: if (sessionID) headers["x-lore-session-id"] = sessionID;
52e6687e1eae545273185978c57838d8782e4e92:packages/gateway/test/compact-endpoint.test.ts:53: { "x-lore-session-id": sessionID },
52e6687e1eae545273185978c57838d8782e4e92:packages/gateway/test/compact-endpoint.test.ts:123: "x-lore-session-id": "unknown-stalled-session",
52e6687e1eae545273185978c57838d8782e4e92:packages/gateway/test/copilot-routing.e2e.test.ts:126: "x-lore-upstream-url": "https://api.openai.com",
52e6687e1eae545273185978c57838d8782e4e92:packages/gateway/test/empty-completion-telemetry.e2e.test.ts:39: "x-lore-session-id": ...
<path>/home/byk/.local/share/opencode/tool-output/tool_08b58c45c001Z26igYthRXY6LB</path>
<type>file</type>
<content>
1: 52e6687e1eae545273185978c57838d8782e4e92:packages/core/src/credential-headers.ts:21: "x-lore-session-id",
2: 52e6687e1eae545273185978c57838d8782e4e92:packages/core/src/fetch-interceptor.ts:188: * gateway as `x-lore-upstream-path` so it can POST to the exact endpoint the
3: 52e6687e1eae545273185978c57838d8782e4e92:packages/core/src/fetch-interceptor.ts:449: headers.set("x-lore-upstream-url", upstreamBase);
4: 52e6687e1eae545273185978c57838d8782e4e92:packages/core/src/fetch-interceptor.ts:457: headers.set("x-lore-upstream-path", upstreamPath);
5: 52e6687e1eae545273185978c57838d8782e4e92:packages/core/test/fetch-interceptor-install.test.ts:48: "x-lore-session-id": "sess-123",
6: 52e6687e1eae545273185978c57838d8782e4e92:packages/core/test/fetch-interceptor-install.test.ts:74: expect(headerVal("x-lore-upstream-url")).toBe(
7: ...
520
521 const origin = parsed.origin;
522 if (seen.has(origin)) {
523 throw new Error(
524 `Invalid LORE_CALLER_UPSTREAM_ALLOWLIST entry ${index + 1}: duplicate normalized origin`,
525 );
526 }
527 seen.add(origin);
528 origins.push(origin);
529 }
530 return Object.freeze(origins);
531 }
532
533 /**
534 * Whether this gateway may honor a caller-selected upstream URL. Local mode
535 * deliberately preserves arbitrary HTTP/private inference endpoints; remote
536 * and hosted modes use only the server-side config and require exact origin
537 * membership in the administrator allowlist.
538 */
539 export function isCallerUpstreamAllowed(
540 config: Pick<GatewayConfig, "remoteGateway" | "hostedMode"> &
541 Partial<Pick<GatewayConfig, "callerUpstreamAllowlist">>,
542 upstreamUrl: string,
543 ): boolean {
544 if (!config.remoteGateway && !config.hostedMode) ...
740 * and the path belongs to the protocol we're actually speaking. This also
741 * excludes `vertex` (a distinct effectiveProtocol with its own URL shape).
742 *
743 * Anchoring at the base ORIGIN (not the base string) reproduces the original URL
744 * exactly even when the base carries a prefix already contained in the full
745 * pathname (OpenRouter `/api`, Fireworks `/inference`) — never doubling it.
746 */
747 export function verbatimUpstreamUrl(params: {
748 reconstructedUrl: string;
749 effectiveUpstreamBase: string;
750 headerUpstream: string | undefined;
751 upstreamPath: string | undefined;
752 effectiveProtocol:
753 | "anthropic"
754 | "openai"
755 | "openai-responses"
756 | "vertex"
757 | "gemini";
758 ingressProtocol:
759 | "anthropic"
760 | "openai"
761 | "openai-responses"
762 | "vertex"
763 | "gemini";
764 }): string {
765 ...
1 /** Shared credential-header policy and free-text redaction. */
2
3 /** Internal gateway access credential. Never forward, learn, persist, or log. */
4 export const GATEWAY_AUTH_HEADER = "x-lore-gateway-token";
5
6 /**
7 * Claude Code sub-agent (Task/Agent tool) correlation headers.
8 *
9 * A sub-agent request carries its own `x-claude-code-agent-id` (a fresh id per
10 * sub-agent invocation) AND the parent's `x-claude-code-session-id`. The main
11 * agent emits only the session-id (`x-claude-code-agent-id` is absent).
12 * `x-claude-code-parent-agent-id` carries the parent *agent* id (not the parent
13 * session id) and is never emitted by the main agent, so it never appears in
14 * the header session index. ...
1 /**
2 * git.ts — Git repository identification utilities.
3 *
4 * Extracts and normalizes git remote URLs to identify projects by their
5 * repository identity rather than filesystem path. This enables:
6 * - Worktree awareness: main checkout and worktrees share one project
7 * - Clone deduplication: same repo cloned to different paths is one project
8 * - Fork safety: prefers `origin` over `upstream` so unrelated repos sharing a
9 * common template's upstream are NOT collapsed into one project
10 *
11 * Remote URL normalization strips protocol, auth, and `.git` suffix to produce
12 * a stable canonical identifier (e.g. "github.com/user/repo") regardless of
13 * how the remote was configured (SSH, HTTPS, git://).
14 */
15
16 import { execSync } from "node:child_process";
17 import { isHostedMode } from "./hosted";
18
19 // ...
No files found
<path>/home/byk/Code/opencode-lore-v2/packages/opencode/node_modules</path> <type>directory</type> <entries> .bin/ @loreai/ @opencode-ai/ @opencode/ @types/ (5 entries) </entries>
<path>/home/byk/Code/opencode-lore-v2/packages/opencode/node_modules/@opencode/plugin</path> <type>directory</type> <entries> dist/ package.json (2 entries) </entries>
<path>/home/byk/Code/opencode-lore-v2/packages/opencode/node_modules/@opencode/plugin/package.json</path>
<type>file</type>
<content>
1: {
2: "$schema": "https://json.schemastore.org/package.json",
3: "name": "@opencode/plugin",
4: "version": "0.0.0-beta-19378",
5: "type": "module",
6: "license": "MIT",
7: "scripts": {
8: "test": "bun test --only-failures",
9: "typecheck": "tsgo --noEmit",
10: "build": "tsc -p tsconfig.build.json"
11: },
12: "exports": {
13: ".": {
14: "import": "./dist/promise/index.js",
15: "types": "./dist/promise/index.d.ts"
16: },
17: "./effect": {
18: "import": "./dist/effect/index.js",
19: "types": "./dist/effect/index.d.ts"
20: },
21: "./host": {
22: "import": "./dist/host.js",
23: "types": "./dist/host.d.ts"
24: },
25: "./tui": {
26: "import": "./dist/tui/index.js",
27: "types": "./dist/tui/index.d.ts"
28: },
29: "./*": {
30: "import": ...
/home/byk/Code/opencode-lore-v2/packages/opencode/node_modules/@opencode/plugin/dist/worktree.js /home/byk/Code/opencode-lore-v2/packages/opencode/node_modules/@opencode/plugin/dist/worktree.d.ts /home/byk/Code/opencode-lore-v2/packages/opencode/node_modules/@opencode/plugin/dist/storage.js /home/byk/Code/opencode-lore-v2/packages/opencode/node_modules/@opencode/plugin/dist/storage.d.ts /home/byk/Code/opencode-lore-v2/packages/opencode/node_modules/@opencode/plugin/dist/source.node.js /home/byk/Code/opencode-lore-v2/packages/opencode/node_modules/@opencode/plugin/dist/source.node.d.ts /home/byk/Code/opencode-lore-v2/packages/opencode/node_modules/@opencode/plugin/dist/source.js /home/byk/Code/opencode-lore-v2/packages/opencode/node_modules/@opencode/plugin/dist/source.d.ts /home/byk/Code/opencode-lore-v2/packages/opencode/node_modules/@opencode/plugin/dist/source.bun.js /home/byk/Code/opencode-lore-v2/packages/opencode/node_modules/@opencode/plugin/dist/source.bun.d.ts /home/byk/Code/o...
<path>/home/byk/Code/opencode-lore-v2/packages/opencode/node_modules/@opencode/plugin/dist/promise/plugin.d.ts</path>
<type>file</type>
<content>
1: import type { OpenCodeClient } from "@opencode/client";
2: import type { GenerateApi, PluginApi } from "@opencode/client/promise/api";
3: import type { Location } from "@opencode/schema/location";
4: import type { PluginOptions } from "../options.js";
5: import type { App } from "../app.js";
6: import type { AgentDomain } from "./agent.js";
7: import type { AISDKDomain } from "./aisdk.js";
8: import type { CatalogDomain } from "./catalog.js";
9: import type { CommandDomain } from "./command.js";
10: import type { EventDomain } from "./event.js";
11: import type { IntegrationDomain } from "./integration.js";
12: import type { MCPDomain } from "./mcp.js";
13: import type { PermissionDomain } from "./permission.js";
14: import type { ReferenceDomain } from "./reference.js";
15: import type { RpcDomain } from "./rpc.js";
16: import type { ...
<path>/home/byk/Code/opencode-lore-v2/packages/opencode/node_modules/@opencode/plugin/dist/promise/session.d.ts</path>
<type>file</type>
<content>
1: import type { SessionApi } from "@opencode/client/promise/api";
2: import type { GenerationOptionsFields, Message, SystemPart } from "@opencode/ai";
3: import type { Agent } from "@opencode/schema/agent";
4: import type { Model } from "@opencode/schema/model";
5: import type { PromptInput } from "@opencode/schema/prompt-input";
6: import type { Session } from "@opencode/schema/session";
7: import type { SessionInbox } from "@opencode/schema/session-inbox";
8: import type { SessionError } from "@opencode/schema/session-error";
9: import type { SessionMessage } from "@opencode/schema/session-message";
10: import type { JsonSchema, Types } from "effect";
11: import type { ModelHooks } from "./registration.js";
12: export interface SessionPrompt {
13: readonly sessionID: Session.ID;
14: readonly messageID: SessionMessage.ID;
15: ...
<path>/home/byk/Code/opencode-lore-v2/packages/opencode/node_modules/@opencode/plugin/dist/promise/adapter.js</path>
<type>file</type>
<content>
1: import { Tool } from "@opencode/schema/tool";
2: import { Effect, Schema, SchemaAST, Stream } from "effect";
3: import { HttpApiEndpoint, HttpApiSchema } from "effect/unstable/httpapi";
4: import { define } from "../effect/plugin.js";
5: const compiledEndpoints = new WeakMap();
6: const JsonInput = Schema.fromJsonString(Schema.Unknown);
7: class ReturnedRpcError extends Error {
8: type;
9: data;
10: constructor(type, message, data) {
11: super(message);
12: this.type = type;
13: this.data = data;
14: }
15: }
16: const makeStreams = Effect.fn("Plugin.Event.makeStreams")(function* () {
17: const context = yield* Effect.context();
18: const subscriptions = new Set();
19: // Async iterators own separate scopes, so close them when the plugin unloads.
20: yield* Effect.addFinalizer(() => ...
215 const state = harness({ failSessionHook: true, failDispose: true });
216 await expect(
217 plugin.setup(
218 state.context as unknown as Parameters<typeof plugin.setup>[0],
219 ),
220 ).rejects.toThrow("registration failed");
221
222 expect(state.registrations).toHaveLength(1);
223 expect(state.registrations[0].dispose).toHaveBeenCalledTimes(1);
224 expect(release).toHaveBeenCalledTimes(1);
225 });
226
227 test("routes without a parent header when session lookup fails", async () => {
228 const state = harness();
229 state.context.session.get.mockRejectedValueOnce(new Error("unavailable"));
230 const cleanup = await plugin.setup(
231 state.context as unknown as Parameters<typeof plugin.setup>[0],
232 );
233
234 const event = {
235 sessionID: "ses_1",
236 agent: "build",
237 model: { providerID: "openai" },
238 ...
1 {
2 "name": "@loreai/opencode",
3 "version": "0.40.0",
4 "type": "module",
5 "license": "FSL-1.1-Apache-2.0",
6 "description": "Three-tier memory architecture for OpenCode — distillation, not summarization",
7 "main": "./src/index.ts",
8 "types": "./src/index.ts",
9 "exports": {
10 ".": {
11 "types": "./src/index.ts",
12 "bun": "./src/index.ts",
13 "default": "./src/index.ts"
14 },
15 "./server": {
16 "types": "./src/server.ts",
17 "bun": "./dist/server.js",
18 "default": "./dist/server.js"
19 }
20 },
21 "scripts": {
22 "typecheck": "tsc --noEmit",
23 "build": "tsx script/build.ts"
24 },
25 "peerDependencies": {
26 "@opencode-ai/plugin": ">=1.1.0",
27 "@opencode/plugin": ">=0.0.0"
28 },
29 "peerDependenciesMeta": {
30 "@opencode/plugin": {
31 ...
Found 24 matches
/home/byk/Code/opencode-lore-v2/packages/opencode/dist/server.js:
Line 184: return process.env.XDG_DATA_HOME || join(homedir(), ".local", "share");
Line 189: if (process.env.NODE_ENV === "test") return;
Line 197: console.error(`[lore] migrated data directory: ${oldDir} \u2192 ${newDir}`);
Line 216: var isDebug = process.env.LORE_DEBUG === "1" || process.env.LORE_DEBUG?.toLowerCase() === "true";
Line 402: if (process.env.NODE_ENV === "test") return void 0;
Line 469: console.error("[lore]", ...safeArgs(args));
Line 476: console.error("[lore] WARN:", ...safeArgs(args));
Line 482: if (!readStderrSilenced()) console.error("[lore]", ...safeArgs(args));
Line 488: if (!readStderrSilenced()) console.error("[lore]", ...safeArgs(args));
Line 627: import { execSync } from "node:child_process";
Line 661: const output = execSync("git remote -v", {
Line 666: // suppress stderr
Line 704: function ...
1 /**
2 * Lightweight logger that suppresses informational messages by default.
3 *
4 * In TUI mode, all stderr output renders as red "error" text — confusing
5 * for routine status messages like "incremental distillation" or "pruned
6 * temporal messages". Only actual errors should be visible by default.
7 *
8 * Set LORE_DEBUG=1 to see informational messages (useful when debugging
9 * the plugin itself).
10 *
11 * ## Sink registration
12 *
13 * An optional {@link LogSink} can be registered via {@link registerSink}.
14 * When registered, every log call (regardless of `isDebug`) also forwards
15 * to the sink. This is used by the gateway to bridge logs → Sentry without
16 * adding a Sentry dependency to `@loreai/core`.
17 *
18 * ## File logging
19 *
20 * All log calls (info, warn, error) are written to a persistent log file
21 * at `~/.local/share/lore/lore.log` regardless ...
52e6687e1eae545273185978c57838d8782e4e92:packages/core/src/embedding-worker-types.ts:453: * has its OWN `globalThis`, so the main thread's `log.silenceStderr()`
52e6687e1eae545273185978c57838d8782e4e92:packages/core/src/log.ts:108:// The host enables this switch once, on activation, via `silenceStderr()`.
52e6687e1eae545273185978c57838d8782e4e92:packages/core/src/log.ts:124:// so one `silenceStderr()` call silences them all.
52e6687e1eae545273185978c57838d8782e4e92:packages/core/src/log.ts:140:export function silenceStderr(silenced = true): void {
52e6687e1eae545273185978c57838d8782e4e92:packages/core/test/embedding-oom-recovery.test.ts:23:import { isStderrSilenced, silenceStderr } from "../src/log";
52e6687e1eae545273185978c57838d8782e4e92:packages/core/test/embedding-oom-recovery.test.ts:237: // log.silenceStderr() can't reach it — the value must ride in via workerData
52e6687e1eae545273185978c57838d8782e4e92:packages/core/test/embedding-oom-recovery.test.ts:247: ...
52e6687e1eae545273185978c57838d8782e4e92:packages/core/src/credential-headers.ts:4:export const GATEWAY_AUTH_HEADER = "x-lore-gateway-token";
52e6687e1eae545273185978c57838d8782e4e92:packages/core/src/credential-headers.ts:43: "proxy-authorization",
52e6687e1eae545273185978c57838d8782e4e92:packages/core/src/credential-headers.ts:46: GATEWAY_AUTH_HEADER,
52e6687e1eae545273185978c57838d8782e4e92:packages/core/src/credential-headers.ts:73:export function isCredentialHeaderName(name: string): boolean {
52e6687e1eae545273185978c57838d8782e4e92:packages/core/src/credential-headers.ts:183: if (!isCredentialHeaderName(key)) {
52e6687e1eae545273185978c57838d8782e4e92:packages/core/src/index.ts:208: GATEWAY_AUTH_HEADER,
52e6687e1eae545273185978c57838d8782e4e92:packages/core/src/index.ts:212: isCredentialHeaderName,
52e6687e1eae545273185978c57838d8782e4e92:packages/gateway/src/cli/agents.ts:9:import { GATEWAY_AUTH_HEADER, getGitRemote } from ...
760 /** Whether the upstream accepted the request (HTTP 2xx). */ 761 ok: boolean; 762 /** Cache read tokens from the warmup response (confirms cache was refreshed). */ 763 cacheReadTokens: number; 764 /** Cache creation tokens (non-zero means warmup caused a fresh write — bad). */ 765 cacheCreationTokens: number; 766 }; 767 768 // --------------------------------------------------------------------------- 769 // Header forwarding — transparent upstream proxy 770 // --------------------------------------------------------------------------- 771 772 /** 773 * Headers that the gateway manages itself — never forwarded from the client. 774 * Auth headers are listed because each request builder handles them 775 * explicitly (extractAuth + authHeaders) to preserve the correct scheme. ...
52e6687e1eae545273185978c57838d8782e4e92:packages/gateway/src/translate/anthropic.ts:17:import { extractAuth, authHeaders } from "../auth";
52e6687e1eae545273185978c57838d8782e4e92:packages/gateway/src/translate/anthropic.ts:390: Object.assign(headers, authHeaders(cred));
52e6687e1eae545273185978c57838d8782e4e92:packages/gateway/src/translate/types.ts:775: * explicitly (extractAuth + authHeaders) to preserve the correct scheme.
52e6687e1eae545273185978c57838d8782e4e92:packages/gateway/src/translate/types.ts:812: // Auth — handled separately by each builder (extractAuth + authHeaders)
52e6687e1eae545273185978c57838d8782e4e92:packages/gateway/src/translate/types.ts:878: const authHeaders = new Set<string>(PROVIDER_AUTH_HEADER_NAMES);
52e6687e1eae545273185978c57838d8782e4e92:packages/gateway/src/translate/types.ts:880: authHeaders.has(key),
52e6687e1eae545273185978c57838d8782e4e92:packages/gateway/src/translate/types.ts:885: if (normalized.has(lower) || (replacesAuth && ...
1 /**
2 * Gateway authentication: typed credentials, per-session registry, and
3 * two-level lookup for background workers.
4 *
5 * Replaces the bare `lastSeenApiKey` string with a typed `AuthCredential`
6 * that supports both API-key (`x-api-key`) and OAuth Bearer token
7 * (`Authorization: Bearer`) authentication schemes.
8 *
9 * The per-session registry ensures background workers (distillation,
10 * curation, batch queue) use the correct credential for their session
11 * even when multiple clients are connected simultaneously.
12 */
13
14 import { createHash } from "node:crypto";
15 import { log } from "@loreai/core";
16
17 // ---------------------------------------------------------------------------
18 // AuthCredential type
19 // ---------------------------------------------------------------------------
20
21 /** Auth credential — either an API key or an OAuth bearer ...
6180 type ResolvedRequestUpstreamRoute = {
6181 /** Explicit, sanitized X-Lore-Provider value (not inferred signals). */
6182 providerHeader?: string;
6183 /** Provider identity actually selected, including Copilot inference. */
6184 providerID?: string;
6185 headerUpstream?: string;
6186 headerUpstreamPath?: string;
6187 providerRoute: ReturnType<typeof resolveProviderRoute>;
6188 modelRoute: ReturnType<typeof resolveUpstreamRoute>;
6189 effectiveProtocol: EffectiveUpstreamProtocol;
6190 effectiveUpstreamBase: string;
6191 bedrockMantle: boolean;
6192 };
6193
6194 /**
6195 * Preserve the legacy process-global credential only for a local, unambiguous
6196 * direct-provider request to the exact configured base. Remote/hosted gateways,
6197 * explicit provider selection, and client-selected URLs never populate it. ...
6280 headerUpstream &&
6281 headerUpstreamPath &&
6282 !isUpstreamWithinBase(
6283 new URL(headerUpstreamPath, new URL(headerUpstream).origin).href,
6284 headerUpstream,
6285 )
6286 ) {
6287 throw new Error("Explicit upstream path escapes its upstream base");
6288 }
6289 if (providerID && !providerRoute && !headerUpstream) {
6290 throw new Error(`Unsupported provider "${providerID}"`);
6291 }
6292 if (
6293 providerID &&
6294 providerRoute?.url == null &&
6295 !headerUpstream &&
6296 !selfUrlBuildingProtocol
6297 ) {
6298 throw new Error(
6299 `Provider "${providerID}" requires an explicit upstream URL`,
6300 );
6301 }
6302 const providerRouteUsable =
6303 providerRoute &&
6304 (providerRoute.url != null || headerUpstream || selfUrlBuildingProtocol)
6305 ? ...
52e6687e1eae545273185978c57838d8782e4e92:packages/gateway/src/cli/run.ts:404: let owned: boolean; 52e6687e1eae545273185978c57838d8782e4e92:packages/gateway/src/cli/start.ts:112: owned: boolean; 52e6687e1eae545273185978c57838d8782e4e92:packages/gateway/src/cli/start.ts:865: * handle with `owned: false` 52e6687e1eae545273185978c57838d8782e4e92:packages/gateway/src/cli/start.ts:868:export async function startGateway( 52e6687e1eae545273185978c57838d8782e4e92:packages/gateway/src/cli/start.ts:877:async function startGatewayLocked( 52e6687e1eae545273185978c57838d8782e4e92:packages/gateway/src/cli/start.ts:982: owned: false, 52e6687e1eae545273185978c57838d8782e4e92:packages/gateway/src/cli/start.ts:1010: owned: false, 52e6687e1eae545273185978c57838d8782e4e92:packages/gateway/src/cli/start.ts:1239: owned: true, 52e6687e1eae545273185978c57838d8782e4e92:packages/gateway/src/cli/start.ts:1268: owned: ...
840 now: Date.now,
841 logInfo: (msg) => console.log(`[lore] ${msg}`),
842 logError: (msg) => console.error(`[lore] ${msg}`),
843 };
844 }
845
846 /**
847 * Daemonize: re-spawn `lore start` detached with stdio redirected to a log
848 * file, poll until the gateway is healthy, print where it's listening, and
849 * exit. Thin shell around `runDaemon` that supplies real IO and calls
850 * `safeExit`.
851 */
852 async function startDaemon(opts: StartOptions): Promise<never> {
853 safeExit(await runDaemon(opts, realDaemonIO(opts)));
854 }
855
856 /**
857 * Start the gateway server, returning the actual port and a shutdown function.
858 *
859 * Merges CLI options on top of env-var config (CLI takes precedence).
860 *
861 * When the port is not explicitly set (no `--port` / `LORE_LISTEN_PORT`),
862 * the server tries a fallback chain: 3207 → 5673 → OS-assigned random port. ...
1180 }
1181 try {
1182 shutdownLock.assertOwned();
1183 closeDb();
1184 } catch (error) {
1185 // SQLite recovery handles a failed best-effort checkpoint. Keep
1186 // the established current-main behavior: warn but do not wedge
1187 // shutdown or retain an otherwise-stale process record for it.
1188 notify(
1189 `Database close warning: ${error instanceof Error ? error.message : String(error)}`,
1190 );
1191 }
1192
1193 if (shutdownError) throw shutdownError;
1194
1195 // Discovery is removed only after listener and worker teardown.
1196 shutdownLock.assertOwned();
1197 io.removePort(actualPort, controlToken);
1198 const current = io.readProcess();
1199 if (
1200 current?.pid === process.pid &&
...
300
301 /** Parse a header-safe, brute-force-resistant gateway access token. */
302 export function parseGatewayAuthToken(
303 value: string | undefined,
304 ): string | undefined {
305 if (value === undefined || value === "") return undefined;
306 if (
307 value.length < GATEWAY_AUTH_TOKEN_MIN_LENGTH ||
308 value.length > GATEWAY_AUTH_TOKEN_MAX_LENGTH ||
309 // Visible ASCII excluding comma keeps one configured token distinguishable
310 // from Fetch/Node's comma-joined duplicate-header representation.
311 !/^[\x21-\x2b\x2d-\x7e]+$/.test(value)
312 ) {
313 throw new Error(
314 `LORE_GATEWAY_AUTH_TOKEN must be ${GATEWAY_AUTH_TOKEN_MIN_LENGTH}-${GATEWAY_AUTH_TOKEN_MAX_LENGTH} visible ASCII characters without commas`,
315 );
316 }
317 return value;
318 }
319
320 /** Apply the fail-closed remote/hosted startup invariant after CLI overrides. ...
52e6687e1eae545273185978c57838d8782e4e92:packages/gateway/src/server.ts:290:function gatewayAccessMatches( 52e6687e1eae545273185978c57838d8782e4e92:packages/gateway/src/server.ts:872: (config.remoteGateway || config.hostedMode) && 52e6687e1eae545273185978c57838d8782e4e92:packages/gateway/src/server.ts:874: !gatewayAccessMatches( 52e6687e1eae545273185978c57838d8782e4e92:packages/gateway/src/server.ts:1180: !(config.remoteGateway || config.hostedMode) ||
830 const managementPath = isManagementPath(pathname);
831 const dataPlanePath = isDataPlanePath(pathname);
832 let allowedManagementOrigin: string | null = null;
833
834 if (managementPath) {
835 // Authorize from node:http's socket metadata, never from Forwarded,
836 // X-Forwarded-For, Host, or another client-controlled header. Keep this
837 // before preflight handling, lazy imports, and request body consumption.
838 if (!config.allowRemoteManagement && !isLoopbackAddress(peerAddress)) {
839 return hiddenManagementResponse();
840 }
841
842 const origin = managementCorsOrigin(
843 req,
844 config.allowRemoteManagement,
845 peerAddress,
846 );
847 if (origin === false) return hiddenManagementResponse();
848 allowedManagementOrigin = origin;
849
850 if (method === "OPTIONS") {
851 return ...
1 import { describe, expect, test, vi } from "vitest";
2 import { rewriteRequest } from "../src/fetch-interceptor";
3
4 const GATEWAY = "https://gateway.example.com";
5 const dynamicHeaders = {
6 "x-lore-project": "/home/me/project",
7 "x-lore-session-id": "session-123",
8 };
9
10 describe("rewriteRequest", () => {
11 test("routes a V2-style Request without losing method, body, auth, path, or query", async () => {
12 const body = JSON.stringify({
13 model: "gpt-5",
14 messages: [{ role: "user", content: "Hello" }],
15 });
16 const observed = vi.fn();
17 const request = new Request(
18 "https://openrouter.ai/api/v1/chat/completions?stream=true&trace=abc",
19 {
20 method: "POST",
21 headers: {
22 authorization: "Bearer sk-test",
23 "content-type": "application/json",
24 "x-api-key": ...
1 import { afterEach, beforeEach, describe, expect, test, vi } from "vitest";
2
3 const { release, acquireServerRuntime, buildServerHeaders, rewriteRequest } =
4 vi.hoisted(() => {
5 const release = vi.fn(async () => {});
6 return {
7 release,
8 acquireServerRuntime: vi.fn(async () => ({
9 gatewayBase: "http://127.0.0.1:3207",
10 projectPath: "/workspace/project",
11 gitRemote: "github.com/example/project",
12 gatewayHeaders: {},
13 release,
14 })),
15 buildServerHeaders: vi.fn(() => ({
16 "x-lore-session-id": "ses_1",
17 "x-lore-agent": "build",
18 "x-lore-provider": "openai",
19 "x-lore-upstream-url": "https://api.openai.com",
20 })),
21 rewriteRequest: vi.fn(
22 async (request: Request) =>
23 new Request("http://127.0.0.1:3207/v1/chat/completions", ...
* 🔴 (12:43) User stated the fetch-level Lore interceptor wraps `globalThis.fetch` so provider SDKs build requests with their original authentication, URL, and headers before known LLM API calls are transparently rerouted through the gateway. * 🔴 (12:43) User stated unknown request paths always pass through unchanged. * 🔴 (12:43) User stated OpenAI Chat never has a top-level `system`; system co…
* 🟡 (12:42) The `pnpm` lockfile adds package-management and archive dependencies including `read-cmd-shim@6.0.0`, `fs-minipass@3.0.3`, `minipass@7.1.3`, `minipass-sized@2.0.0`, `path-scurry@2.0.2`, `ssri@13.0.1`, `cacache@20.0.4`, `make-fetch-happen@15.0.6`, and `npm-packlist@10.0.4`. * 🟡 (12:42) The lockfile adds utility and parsing packages `lru-cache@11.5.1`, `cluster-key-slot@1.1.2`, `json-…
* 🟡 (12:42) The `pnpm` lockfile adds signing and supply-chain verification packages `sigstore@4.1.1` and `@sigstore/core@3.2.1`. * 🟡 (12:42) Multiple AWS dependency resolutions update `@aws-sdk/types` from `3.973.11` to `3.974.5`. * 🟡 (12:42) The AWS Smithy dependency graph adds or updates `@smithy/types@4.18.0` (with another resolution still using `4.15.0`) and adds `@smithy/eventstream-codec…
* 🟡 (12:41) The `pnpm` lockfile adds a `0.0.0-beta-19378` package resolution with peers `@opentelemetry/api-logs@0.214.0`, `@opentelemetry/resources@2.7.1(@opentelemetry/api@1.9.0)`, `@opentelemetry/sdk-logs@0.214.0(@opentelemetry/api@1.9.0)`, `@opentelemetry/sdk-metrics@2.6.1(@opentelemetry/api@1.9.0)`, `@opentelemetry/semantic-conventions@1.41.1`, and `redis@6.2.1(@opentelemetry/api@1.9.0)`. *…
* 🟡 (12:41) The `pnpm` lockfile diff introduces `@aws-sdk/client-cognito-identity@3.1057.0` with dependencies including `@aws-sdk/core@3.974.18`, `@aws-sdk/credential-provider-node@3.972.52`, `@aws-sdk/types@3.973.13`, `@aws-crypto/sha256-browser@5.2.0`, and `@aws-crypto/sha256-js@5.2.0`. * 🟡 (12:41) Existing AWS dependency resolutions move from `@aws-sdk/types@3.973.11` to `@aws-sdk/types@3.97…
* 🟡 (12:41) `packages/opencode/test/internal.test.ts` adds a regression test for `parseUpstreamExtraHeaders("x-empty:\r\nx-team: platform\r\ninvalid")`, expecting exactly `[["x-empty", ""], ["x-team", "platform"]]`; this verifies preservation of empty header values, CRLF parsing, whitespace trimming, and omission of a malformed line without changing legacy semantics. * 🟡 (12:41) `packages/openc…
Date: Sep 10, 2026 * 🟡 (12:38) User asked the assistant to act as an independent security auditor and produce a substantive read-only pentest report for the frozen Lore V2 port at `/home/byk/Code/opencode-lore-v2`, replacing reviewers that returned empty reports; final response must contain evidence or an exact blocker and must not be empty. * 🔴 (12:38) User specified the immutable review targe…