From d3fb9b664adb2239a96076201e0da9e84b499e73 Mon Sep 17 00:00:00 2001 From: devilarch Date: Thu, 10 Sep 2026 01:50:25 +0530 Subject: [PATCH 1/2] fix(core): break filesystem/search circular import that crashed compiled binary filesystem.ts and filesystem/search.ts imported each other. ESM dev mode resolves the cycle, but the compiled binary evaluates one side first and FileSystemSearch.node was undefined when filesystem.ts built its deps array, crashing the layer graph walk (TypeError: undefined is not an object, evaluating 'a.name' at layer-node.ts) before any LLM request. Import the schema structs (Entry/FindInput/Match) directly from @pentestcode/schema/filesystem and take GlobInput/GrepInput as a type-only import so search.ts no longer needs the runtime back-edge. --- packages/core/src/filesystem/search.ts | 25 +++++++++++++------------ 1 file changed, 13 insertions(+), 12 deletions(-) diff --git a/packages/core/src/filesystem/search.ts b/packages/core/src/filesystem/search.ts index c5a8565e..ba3dff7f 100644 --- a/packages/core/src/filesystem/search.ts +++ b/packages/core/src/filesystem/search.ts @@ -5,7 +5,8 @@ import path from "path" import { Context, Effect, Layer, Scope } from "effect" import { Fff } from "#fff" import fuzzysort from "fuzzysort" -import { FileSystem } from "../filesystem" +import { Entry, FindInput, Match } from "@pentestcode/schema/filesystem" +import type { GlobInput, GrepInput } from "../filesystem" import { FSUtil } from "../fs-util" import { Location } from "../location" import { Ripgrep } from "../ripgrep" @@ -13,9 +14,9 @@ import { RelativePath } from "../schema" import { Flag } from "../flag/flag" export interface Interface { - readonly find: (input: FileSystem.FindInput) => Effect.Effect - readonly glob: (input: FileSystem.GlobInput) => Effect.Effect - readonly grep: (input: FileSystem.GrepInput) => Effect.Effect + readonly find: (input: FindInput) => Effect.Effect + readonly glob: (input: GlobInput) => Effect.Effect + readonly grep: (input: GrepInput) => Effect.Effect } export class Service extends Context.Service()("@pentestcode/v2/FileSystem/Search") {} @@ -61,7 +62,7 @@ export const ripgrepLayer = Layer.effect( .pipe( Effect.map((result) => result.map((entry) => - FileSystem.Entry.make({ + Entry.make({ ...entry, path: RelativePath.make(path.relative(location.directory, path.resolve(cwd, entry.path))), }), @@ -86,9 +87,9 @@ export const ripgrepLayer = Layer.effect( .pipe( Effect.map((result) => result.map((match) => - FileSystem.Match.make({ + Match.make({ ...match, - entry: FileSystem.Entry.make({ + entry: Entry.make({ ...match.entry, path: RelativePath.make(path.relative(location.directory, path.resolve(cwd, match.entry.path))), }), @@ -109,7 +110,7 @@ export const ripgrepLayer = Layer.effect( return fuzzysort.go(input.query, items, { limit: input.limit ?? 50 }).map((item) => { const relative = item.target const type = relative.endsWith(path.sep) ? ("directory" as const) : ("file" as const) - return FileSystem.Entry.make({ + return Entry.make({ path: RelativePath.make(relative), type, }) @@ -152,7 +153,7 @@ export const fffLayer = Layer.effect( }) if (!found.ok) throw found.error return found.value.items.map((item) => - FileSystem.Entry.make({ + Entry.make({ path: RelativePath.make(item.relativePath.replaceAll("\\", "/")), type: "file", }), @@ -170,8 +171,8 @@ export const fffLayer = Layer.effect( if (!found.ok) throw found.error return found.value.items.map((match) => { const bytes = Buffer.from(match.lineContent) - return FileSystem.Match.make({ - entry: FileSystem.Entry.make({ + return Match.make({ + entry: Entry.make({ path: RelativePath.make(match.relativePath.replaceAll("\\", "/")), type: "file", }), @@ -220,7 +221,7 @@ export const fffLayer = Layer.effect( .sort((a, b) => b.score - a.score || a.path.length - b.path.length) .map((item) => { const relative = item.path.replaceAll("\\", "/").replace(/\/$/, "") - return FileSystem.Entry.make({ + return Entry.make({ path: RelativePath.make(relative + (item.type === "directory" ? path.sep : "")), type: item.type, }) From a891db05376550b3e701c63d5aca06dcef2f67b3 Mon Sep 17 00:00:00 2001 From: devilarch Date: Thu, 10 Sep 2026 01:50:28 +0530 Subject: [PATCH 2/2] feat(opencode): send x-opencode-session header for gateway session affinity OpenCode Go started requiring the literal x-opencode-session header on 2026-09-06 for session affinity and prompt caching; requests missing it can fail with 400 Model is unavailable. The opencode-gateway header block here only sent the rebranded x-pentestcode-session name, which the gateway does not recognize. Add x-opencode-session alongside the existing headers; the value stays the stable per-session sessionID and flows to all transports through the single prepare() choke point. --- packages/opencode/src/session/llm/request.ts | 1 + 1 file changed, 1 insertion(+) diff --git a/packages/opencode/src/session/llm/request.ts b/packages/opencode/src/session/llm/request.ts index a52219ce..d130a32a 100644 --- a/packages/opencode/src/session/llm/request.ts +++ b/packages/opencode/src/session/llm/request.ts @@ -196,6 +196,7 @@ export const prepare = Effect.fn("LLMRequestPrep.prepare")(function* (input: Pre ...(input.model.providerID.startsWith("opencode") ? { ...(opencodeProjectID ? { "x-pentestcode-project": opencodeProjectID } : {}), + "x-opencode-session": input.sessionID, "x-pentestcode-session": input.sessionID, "x-pentestcode-request": input.user.id, "x-pentestcode-client": input.flags.client,