diff --git a/integrations/claude-code-plugin/specbridge/dist/checksums.json b/integrations/claude-code-plugin/specbridge/dist/checksums.json index 049c58b..ec219a4 100644 --- a/integrations/claude-code-plugin/specbridge/dist/checksums.json +++ b/integrations/claude-code-plugin/specbridge/dist/checksums.json @@ -7,12 +7,12 @@ "bytes": 155994 }, "cli.cjs": { - "sha256": "db1ed5fb7eacf11662e1206e27b74004cb31f1df88c4ee30b65ce092eb78e377", - "bytes": 5858878 + "sha256": "7d741451a5008788fe716220e57fd96c38f7d15ae27db32ba863e02ad56748db", + "bytes": 5863147 }, "mcp-server.cjs": { - "sha256": "090a7686c19a94495baa75b230899f15a8e0b6036cfb09f73d4482686b621a3e", - "bytes": 3777757 + "sha256": "6568a89c0c530f599038bc4aa7eb63de37b05c3c7e464c8771fa6e620d9ff61f", + "bytes": 3786242 } } } diff --git a/integrations/claude-code-plugin/specbridge/dist/cli.cjs b/integrations/claude-code-plugin/specbridge/dist/cli.cjs index c194ec4..75dd9b5 100644 --- a/integrations/claude-code-plugin/specbridge/dist/cli.cjs +++ b/integrations/claude-code-plugin/specbridge/dist/cli.cjs @@ -39182,6 +39182,85 @@ var ClaudeCodeRunner = class { const stageReport = report; return { ...rest, ...stageReport !== void 0 ? { report: stageReport } : {} }; } + async invokeStructured(input, execution) { + const started = Date.now(); + const probe = await this.probe(); + const unavailable = this.unavailableResult(probe, started); + if (unavailable !== void 0) { + const { report: _report, ...rest } = unavailable; + return rest; + } + const plan = buildClaudeInvocation({ + config: this.config, + probe, + prompt: input.prompt, + toolPolicy: input.toolPolicy, + outputJsonSchema: input.outputJsonSchema, + execution + }); + try { + const processResult = await runClaudeInvocation(plan, this.config, execution); + const parsed = parseClaudeEnvelope(processResult.stdout); + const usage = usageFromEnvelope(parsed.envelope, processResult.observation.durationMs); + const cost = costFromEnvelope(parsed.envelope); + const base = { + runner: this.name, + rawStdout: processResult.stdout, + rawStderr: processResult.stderr, + process: processResult.observation, + durationMs: Math.max(0, Date.now() - started), + warnings: plan.skippedFlags.map( + (flag) => `flag ${flag} is unsupported by this Claude Code version and was skipped` + ), + ...parsed.envelope?.session_id !== void 0 ? { sessionId: parsed.envelope.session_id } : {}, + ...usage !== void 0 ? { usage } : {}, + ...cost !== void 0 ? { cost } : {} + }; + switch (processResult.status) { + case "timeout": + return { ...base, outcome: "timed-out", failureReason: processResult.failureReason ?? "timeout" }; + case "cancelled": + return { ...base, outcome: "cancelled", failureReason: processResult.failureReason ?? "cancelled" }; + case "output-limit": + case "spawn-failed": + return { + ...base, + outcome: "failed", + failureReason: processResult.failureReason ?? processResult.status + }; + case "ok": + case "nonzero-exit": + break; + } + if (this.looksPermissionDenied(processResult, parsed.envelope?.subtype, parsed.envelope)) { + return { + ...base, + outcome: "permission-denied", + failureReason: "Claude Code reported a permission denial." + }; + } + if (processResult.status === "nonzero-exit" || parsed.envelope?.is_error === true) { + return { + ...base, + outcome: "malformed-output", + failureReason: processResult.status === "nonzero-exit" ? claudeFailureProblem(parsed.problem ?? "the runner produced no output", processResult) : `Claude Code reported an error result${parsed.envelope?.subtype !== void 0 ? ` (${parsed.envelope.subtype})` : ""}`, + ...parsed.reportText !== void 0 ? { invalidStructuredOutput: parsed.reportText } : {} + }; + } + const text15 = parsed.structuredResult !== void 0 ? JSON.stringify(parsed.structuredResult) : parsed.reportText; + if (text15 === void 0 || safeJsonParse(text15) === void 0) { + return { + ...base, + outcome: "malformed-output", + failureReason: parsed.problem ?? "the runner returned no valid JSON document", + ...text15 !== void 0 ? { invalidStructuredOutput: text15 } : {} + }; + } + return { ...base, outcome: "completed", text: text15.trim() }; + } finally { + cleanupTempFiles(plan); + } + } async executeTask(input, execution) { return this.runTask(input.prompt, execution, { ...input.sessionId !== void 0 ? { sessionId: input.sessionId } : {} @@ -40218,6 +40297,115 @@ var CodexCliRunner = class { const stageReport = report; return { ...rest, ...stageReport !== void 0 ? { report: stageReport } : {} }; } + async invokeStructured(input, execution) { + const started = Date.now(); + const probe = await this.probe(); + const unavailable = this.unavailableResult(probe, started); + if (unavailable !== void 0) { + const { report: _report, ...rest } = unavailable; + return rest; + } + const plan = buildCodexInvocation({ + config: this.config, + probe, + prompt: input.prompt, + toolPolicy: input.toolPolicy, + outputJsonSchema: input.outputJsonSchema, + execution + }); + try { + const processResult = await runCodexInvocation(plan, this.config, execution); + const stream = parseCodexEventStream(processResult.stdout); + const warnings = plan.skippedFlags.map( + (flag) => `flag ${flag} is unsupported by this Codex CLI version and was skipped` + ); + const normalizedEvents = normalizeCodexEvents( + stream, + { + runner: this.name, + profile: this.name, + runId: "pending", + attemptId: "pending" + }, + () => (/* @__PURE__ */ new Date()).toISOString() + ); + const usage = usageFromStream(stream, processResult.observation.durationMs, this.config.model); + const base = { + runner: this.name, + rawStdout: redactCodexStdoutForRetention(processResult.stdout), + rawStderr: processResult.stderr, + process: processResult.observation, + durationMs: Math.max(0, Date.now() - started), + warnings, + normalizedEvents, + ...usage !== void 0 ? { usage } : {}, + ...stream.threadId !== void 0 ? { sessionId: stream.threadId } : {} + }; + switch (processResult.status) { + case "timeout": + return { + ...base, + outcome: "timed-out", + failureReason: processResult.failureReason ?? "timeout", + error: runnerError({ code: "timed_out", message: "The Codex process timed out." }) + }; + case "cancelled": + return { + ...base, + outcome: "cancelled", + failureReason: processResult.failureReason ?? "cancelled", + error: runnerError({ code: "cancelled", message: "The Codex process was cancelled." }) + }; + case "output-limit": + return { + ...base, + outcome: "failed", + failureReason: processResult.failureReason ?? "output limit exceeded", + error: runnerError({ + code: "output_limit_exceeded", + message: "The Codex process exceeded its output limit." + }) + }; + case "spawn-failed": + return { + ...base, + outcome: "failed", + failureReason: processResult.failureReason ?? "spawn failed", + error: runnerError({ + code: "executable_not_found", + message: "The Codex CLI could not be started." + }) + }; + case "ok": + break; + case "nonzero-exit": { + const error2 = classifyCodexFailure(processResult.stderr, stream.errors); + return { + ...base, + outcome: error2.code === "permission_denied" ? "permission-denied" : "failed", + failureReason: error2.message, + error: error2 + }; + } + } + const finalText = readLastMessage(plan) ?? stream.lastAgentMessage; + if (finalText === void 0 || strictJsonParse(finalText) === void 0) { + return { + ...base, + outcome: "malformed-output", + failureReason: finalText === void 0 ? "the runner returned no final structured result" : "the final Codex message is not a bare JSON document", + error: runnerError({ + code: "structured_output_invalid", + message: "The Codex orchestration response was not a valid JSON document." + }), + ...finalText !== void 0 ? { invalidStructuredOutput: finalText } : {} + }; + } + return { ...base, outcome: "completed", text: finalText.trim() }; + } finally { + cleanupCodexTempFiles(plan); + } + } async executeTask(input, execution) { return this.runTask(input.prompt, execution, {}); } @@ -54035,8 +54223,6 @@ var import_fs42 = require("fs"); var import_path44 = __toESM(require("path"), 1); var import_fs43 = require("fs"); var import_path45 = __toESM(require("path"), 1); -var import_fs44 = require("fs"); -var import_path46 = __toESM(require("path"), 1); // ../../packages/mission/dist/index.js var import_fs30 = require("fs"); @@ -56386,28 +56572,27 @@ function observeSpecApproval(deps4, missionId) { } // ../../packages/orchestration/dist/index.js +var import_path46 = __toESM(require("path"), 1); +var import_fs44 = require("fs"); var import_path47 = __toESM(require("path"), 1); +var import_crypto23 = require("crypto"); var import_fs45 = require("fs"); var import_path48 = __toESM(require("path"), 1); -var import_crypto23 = require("crypto"); -var import_fs46 = require("fs"); var import_path49 = __toESM(require("path"), 1); +var import_fs46 = require("fs"); var import_path50 = __toESM(require("path"), 1); -var import_path51 = __toESM(require("path"), 1); var import_fs47 = require("fs"); -var import_path52 = __toESM(require("path"), 1); +var import_path51 = __toESM(require("path"), 1); var import_fs48 = require("fs"); -var import_path53 = __toESM(require("path"), 1); +var import_path52 = __toESM(require("path"), 1); var import_fs49 = require("fs"); -var import_path54 = __toESM(require("path"), 1); +var import_path53 = __toESM(require("path"), 1); var import_fs50 = require("fs"); -var import_path55 = __toESM(require("path"), 1); +var import_path54 = __toESM(require("path"), 1); var import_fs51 = require("fs"); -var import_path56 = __toESM(require("path"), 1); +var import_path55 = __toESM(require("path"), 1); var import_fs52 = require("fs"); -var import_path57 = __toESM(require("path"), 1); -var import_fs53 = require("fs"); -var import_path58 = __toESM(require("path"), 1); +var import_path56 = __toESM(require("path"), 1); var import_crypto24 = require("crypto"); var ORCHESTRATION_PHASES = [ /** The run exists; no intent has been assessed yet. */ @@ -60550,7 +60735,6 @@ function assessCompletion(gate, jobId) { } } var LOCAL_WORKER_ID = "local-llamacpp"; -var CLAUDE_WORKER_ID = "claude-code"; function resolveWorkers(config2) { const workers = []; const local = config2.localInference; @@ -60575,7 +60759,7 @@ function resolveWorkers(config2) { }); } workers.push({ - workerId: CLAUDE_WORKER_ID, + workerId: config2.defaultRunner, runnerProfile: config2.defaultRunner, roles: [ "CLASSIFIER", @@ -60649,7 +60833,7 @@ function selectWorker(input) { ); if (writer === void 0) { throw new OrchestrationError("SBO034", `No repository-writing worker is available for ${role}.`, { - remediation: ["Check the Claude Code runner with `specbridge runner doctor claude-code`."], + remediation: ["Check the configured default runner with `specbridge runner doctor`."], failureCategory: "CAPABILITY_UNAVAILABLE" }); } @@ -68913,10 +69097,10 @@ function assessContextMiss(input) { for (const symbol of extractSymbolReferences(input.workerReportedText ?? "")) { const declaring = input.index?.declaring(symbol) ?? []; if (declaring.length === 0) continue; - if (declaring.some((path272) => provided.has(path272))) continue; + if (declaring.some((path252) => provided.has(path252))) continue; signals2.add("UNKNOWN_SYMBOL_REFERENCE"); if (!missingSymbols.includes(symbol)) missingSymbols.push(symbol); - for (const path272 of declaring) if (!missingPaths.includes(path272)) missingPaths.push(path272); + for (const path252 of declaring) if (!missingPaths.includes(path252)) missingPaths.push(path252); } for (const candidate of extractPathReferences2(input.failureText ?? "")) { if (provided.has(candidate)) continue; @@ -68924,7 +69108,7 @@ function assessContextMiss(input) { signals2.add("FAILURE_IN_UNSELECTED_FILE"); if (!missingPaths.includes(candidate)) missingPaths.push(candidate); } - const staleSelected = (input.refreshedPaths ?? []).filter((path272) => provided.has(path272)); + const staleSelected = (input.refreshedPaths ?? []).filter((path252) => provided.has(path252)); if (staleSelected.length > 0) signals2.add("SELECTED_ARTIFACT_STALE"); const droppedMandatory = (input.plan?.excludedCandidates ?? []).filter( (entry2) => entry2.reason === "BUDGET_EXHAUSTED" || entry2.reason === "TOO_LARGE" @@ -69028,7 +69212,7 @@ function runCriterionCheck(check22, evidence) { case "changed-within": { const prefix = normalizePath2(check22.value); const outside = evidence.changedPaths.filter( - (path272) => !normalizePath2(path272).startsWith(prefix) + (path252) => !normalizePath2(path252).startsWith(prefix) ); return outside.length === 0 ? { outcome: "PASSED", detail: `every change is inside ${check22.value}` } : { outcome: "FAILED", @@ -69044,8 +69228,8 @@ function runCriterionCheck(check22, evidence) { } } } -function normalizePath2(path272) { - return path272.replace(/\\/g, "/").replace(/^\.\//, ""); +function normalizePath2(path252) { + return path252.replace(/\\/g, "/").replace(/^\.\//, ""); } function inferLevel(name) { return /test|spec|e2e|integration|regression|contract/i.test(name) ? "TESTS" : "BUILD_STATIC"; @@ -73062,17 +73246,6 @@ var AUTH_FAILURE_PATTERN = new RegExp( String.raw`\b(401|403|unauthorized|unauthenticated|failed to authenticate` + String.raw`|re-?authenticate|oauth[^.]{0,40}\bexpired\b|token has expired` + String.raw`|expired token|invalid api key|api key not found|please log ?in` + String.raw`|credentials? (are )?(invalid|missing|expired))\b`, "i" ); -var AUTH_FAILURE_MAX_CHARS = 2e3; -function looksLikeAuthenticationFailure(text93) { - const collapsed = text93.trim(); - if (collapsed.length === 0 || collapsed.length > AUTH_FAILURE_MAX_CHARS) return false; - try { - JSON.parse(collapsed); - return false; - } catch { - } - return AUTH_FAILURE_PATTERN.test(collapsed); -} function observedExcerpt(text93) { return text93.replace(/\s+/g, " ").trim().slice(0, OBSERVED_OUTPUT_EXCERPT_CHARS); } @@ -73179,23 +73352,22 @@ ${correctionMessage(invocation.role, validated.problem)}`; }; } async function runLargeRole(invocation) { - const profile = invocation.config.runnerProfiles[invocation.runnerProfile]; - if (profile === void 0 || profile.runner !== "claude-code") { + const registry2 = invocation.registry ?? createDefaultRunnerRegistry(invocation.config); + let profile; + try { + profile = registry2.getProfile(invocation.runnerProfile); + } catch (cause) { return { ok: false, kind: "worker-unavailable", - problem: `Runner profile "${invocation.runnerProfile}" is not a Claude Code profile.` + problem: cause instanceof Error ? cause.message : `Runner profile "${invocation.runnerProfile}" is unavailable.` }; } - const claudeProfile = profile; - const probe = invocation.cachedProbe ?? await probeClaude(claudeProfile, { - ...invocation.signal !== void 0 ? { signal: invocation.signal } : {} - }); - if (!probe.found || probe.status === "unavailable" || probe.status === "error") { + if (profile.config.enabled !== true || profile.runner.invokeStructured === void 0) { return { ok: false, kind: "worker-unavailable", - problem: `The Claude Code CLI is not available (status ${probe.status}).` + problem: profile.config.enabled !== true ? `Runner profile "${invocation.runnerProfile}" is disabled.` : `Runner profile "${invocation.runnerProfile}" does not support structured orchestration roles.` }; } const prompt = [ @@ -73205,89 +73377,49 @@ async function runLargeRole(invocation) { "", invocation.packet ].join("\n"); - const plan = buildClaudeInvocation({ - config: claudeProfile, - probe, + const result = await profile.runner.invokeStructured({ prompt, toolPolicy: "inspect-only", - outputJsonSchema: AGENT_OUTPUT_JSON_SCHEMAS[invocation.role], - execution: { - workspaceRoot: invocation.workspace.rootDir, - runDir: invocation.scratchDir, - timeoutMs: invocation.timeoutMs - } + schemaName: invocation.role, + outputJsonSchema: AGENT_OUTPUT_JSON_SCHEMAS[invocation.role] + }, { + workspaceRoot: invocation.workspace.rootDir, + runDir: invocation.scratchDir, + timeoutMs: invocation.timeoutMs, + ...invocation.signal !== void 0 ? { signal: invocation.signal } : {} }); - try { - const processResult = await runSafeProcess({ - executable: plan.executable, - argv: plan.argv, - cwd: invocation.workspace.rootDir, - timeoutMs: invocation.timeoutMs, - stdin: plan.stdin, - ...invocation.signal !== void 0 ? { signal: invocation.signal } : {} - }); - if (processResult.status === "cancelled") { - return { ok: false, kind: "cancelled", problem: "The role invocation was cancelled." }; - } - if (processResult.status !== "ok" && processResult.status !== "nonzero-exit") { - return { - ok: false, - kind: "worker-unavailable", - problem: processResult.failureReason ?? `the runner process ended with status ${processResult.status}` - }; - } - const parsed = parseClaudeEnvelope(processResult.stdout); - if (parsed.problem !== void 0) { - return { - ok: false, - kind: "invalid-output", - problem: claudeFailureProblem(parsed.problem, processResult), - probe - }; - } - const text93 = parsed.structuredResult !== void 0 ? JSON.stringify(parsed.structuredResult) : parsed.reportText ?? ""; - const validated = validateAgentOutput(invocation.role, text93); - if (!validated.ok) { - if (looksLikeAuthenticationFailure(text93)) { - return { - ok: false, - // NOT invalid-output: the worker is unusable, not incoherent, and - // the two need different answers from a person. - kind: "worker-unavailable", - problem: `The ${invocation.role} worker is not authenticated: ${observedExcerpt(text93)}`, - observed: observedExcerpt(text93), - probe - }; - } - return { - ok: false, - kind: "invalid-output", - problem: validated.problem, - observed: observedExcerpt(text93), - probe - }; - } - const usage = usageFromEnvelope(parsed.envelope, 0); - const cost = costFromEnvelope(parsed.envelope); + if (result.outcome === "cancelled") { + return { ok: false, kind: "cancelled", problem: result.failureReason ?? "The role invocation was cancelled." }; + } + if (result.outcome !== "completed" || result.text === void 0) { + const observed = result.invalidStructuredOutput; return { - ok: true, - output: validated.output, - raw: text93, - usage: { - inputTokens: usage?.inputTokens ?? null, - outputTokens: usage?.outputTokens ?? null, - // Only provider-reported USD amounts count; nothing is fabricated. - costUsd: cost !== null && cost !== void 0 && cost.currency === "USD" ? cost.amount : null - }, - corrected: false, - probe + ok: false, + kind: result.outcome === "malformed-output" ? "invalid-output" : "worker-unavailable", + problem: result.failureReason ?? result.error?.message ?? `Runner profile "${invocation.runnerProfile}" ended with ${result.outcome}.`, + ...observed !== void 0 ? { observed: observedExcerpt(observed) } : {} }; - } finally { - try { - (0, import_fs43.rmSync)(import_path45.default.join(invocation.scratchDir, "tmp"), { recursive: true, force: true }); - } catch { - } } + const validated = validateAgentOutput(invocation.role, result.text); + if (!validated.ok) { + return { + ok: false, + kind: "invalid-output", + problem: validated.problem, + observed: observedExcerpt(result.text) + }; + } + return { + ok: true, + output: validated.output, + raw: result.text, + usage: { + inputTokens: result.usage?.inputTokens ?? null, + outputTokens: result.usage?.outputTokens ?? null, + costUsd: result.cost?.currency === "USD" ? result.cost.amount : null + }, + corrected: false + }; } function createLocalManager(config2, onEvent) { if (!config2.localInference.enabled) return void 0; @@ -73703,13 +73835,13 @@ function findResearchReuse(records, request) { var RESEARCH_DIR_NAME = "research"; var ID_PATTERN7 = /^[A-Za-z0-9][A-Za-z0-9._-]{0,127}$/; function researchRootDir(workspace) { - return assertInsideWorkspace(workspace.rootDir, import_path48.default.join(workspace.sidecarDir, RESEARCH_DIR_NAME)); + return assertInsideWorkspace(workspace.rootDir, import_path47.default.join(workspace.sidecarDir, RESEARCH_DIR_NAME)); } function researchRecordsDir(workspace) { - return assertInsideWorkspace(workspace.rootDir, import_path48.default.join(researchRootDir(workspace), "records")); + return assertInsideWorkspace(workspace.rootDir, import_path47.default.join(researchRootDir(workspace), "records")); } function researchUsesDir(workspace) { - return assertInsideWorkspace(workspace.rootDir, import_path48.default.join(researchRootDir(workspace), "uses")); + return assertInsideWorkspace(workspace.rootDir, import_path47.default.join(researchRootDir(workspace), "uses")); } function assertResearchId(researchId) { if (!ID_PATTERN7.test(researchId)) throw new Error(`Invalid research id "${researchId}".`); @@ -73719,7 +73851,7 @@ function researchRecordFile(workspace, researchId) { assertResearchId(researchId); return assertInsideWorkspace( workspace.rootDir, - import_path48.default.join(researchRecordsDir(workspace), `${researchId}.json`) + import_path47.default.join(researchRecordsDir(workspace), `${researchId}.json`) ); } function majorOf3(value) { @@ -73727,10 +73859,10 @@ function majorOf3(value) { } function readResearchRecord(workspace, researchId) { const file = researchRecordFile(workspace, researchId); - if (!(0, import_fs45.existsSync)(file)) return { kind: "missing" }; + if (!(0, import_fs44.existsSync)(file)) return { kind: "missing" }; let value; try { - value = JSON.parse((0, import_fs45.readFileSync)(file, "utf8")); + value = JSON.parse((0, import_fs44.readFileSync)(file, "utf8")); } catch (cause) { return { kind: "corrupt", problem: cause instanceof Error ? cause.message : String(cause), file }; } @@ -73752,31 +73884,31 @@ function readResearchRecord(workspace, researchId) { function writeResearchRecord(workspace, value) { const record32 = researchRecordSchema.parse(value); const file = researchRecordFile(workspace, record32.researchId); - (0, import_fs45.mkdirSync)(import_path48.default.dirname(file), { recursive: true }); + (0, import_fs44.mkdirSync)(import_path47.default.dirname(file), { recursive: true }); writeFileAtomic(file, `${JSON.stringify(record32, null, 2)} `); return record32; } function researchUseFile(workspace, useId) { - return assertInsideWorkspace(workspace.rootDir, import_path48.default.join(researchUsesDir(workspace), `${useId}.json`)); + return assertInsideWorkspace(workspace.rootDir, import_path47.default.join(researchUsesDir(workspace), `${useId}.json`)); } function writeResearchUseRecord(workspace, value) { const record32 = researchUseRecordSchema.parse(value); const file = researchUseFile(workspace, record32.useId); - if ((0, import_fs45.existsSync)(file)) throw new Error(`research use id ${record32.useId} already exists`); - (0, import_fs45.mkdirSync)(import_path48.default.dirname(file), { recursive: true }); + if ((0, import_fs44.existsSync)(file)) throw new Error(`research use id ${record32.useId} already exists`); + (0, import_fs44.mkdirSync)(import_path47.default.dirname(file), { recursive: true }); writeFileAtomic(file, `${JSON.stringify(record32, null, 2)} `); return record32; } function listResearchUseRecords(workspace) { const dir = researchUsesDir(workspace); - if (!(0, import_fs45.existsSync)(dir)) return []; + if (!(0, import_fs44.existsSync)(dir)) return []; const records = []; - for (const entry2 of (0, import_fs45.readdirSync)(dir, { withFileTypes: true })) { + for (const entry2 of (0, import_fs44.readdirSync)(dir, { withFileTypes: true })) { if (!entry2.isFile() || !entry2.name.endsWith(".json")) continue; try { - const value = JSON.parse((0, import_fs45.readFileSync)(import_path48.default.join(dir, entry2.name), "utf8")); + const value = JSON.parse((0, import_fs44.readFileSync)(import_path47.default.join(dir, entry2.name), "utf8")); const version2 = value !== null && typeof value === "object" && typeof value.schemaVersion === "string" ? value.schemaVersion : ""; if (majorOf3(version2) !== majorOf3(RESEARCH_USE_SCHEMA_VERSION)) continue; const parsed = researchUseRecordSchema.safeParse(value); @@ -73788,10 +73920,10 @@ function listResearchUseRecords(workspace) { } function listResearchRecords(workspace) { const dir = researchRecordsDir(workspace); - if (!(0, import_fs45.existsSync)(dir)) return { records: [], diagnostics: [] }; + if (!(0, import_fs44.existsSync)(dir)) return { records: [], diagnostics: [] }; const records = []; const diagnostics = []; - for (const entry2 of (0, import_fs45.readdirSync)(dir, { withFileTypes: true })) { + for (const entry2 of (0, import_fs44.readdirSync)(dir, { withFileTypes: true })) { if (!entry2.isFile() || !entry2.name.endsWith(".json")) continue; const researchId = entry2.name.slice(0, -5); if (!ID_PATTERN7.test(researchId)) continue; @@ -74390,13 +74522,13 @@ function emptyResearchTelemetry(now52) { }; } function researchTelemetryFile(workspace) { - return assertInsideWorkspace(workspace.rootDir, import_path49.default.join(researchRootDir(workspace), "telemetry.json")); + return assertInsideWorkspace(workspace.rootDir, import_path48.default.join(researchRootDir(workspace), "telemetry.json")); } function readResearchTelemetry(workspace, now52 = /* @__PURE__ */ new Date()) { const file = researchTelemetryFile(workspace); - if (!(0, import_fs46.existsSync)(file)) return { telemetry: emptyResearchTelemetry(now52) }; + if (!(0, import_fs45.existsSync)(file)) return { telemetry: emptyResearchTelemetry(now52) }; try { - const parsed = researchTelemetrySchema.safeParse(JSON.parse((0, import_fs46.readFileSync)(file, "utf8"))); + const parsed = researchTelemetrySchema.safeParse(JSON.parse((0, import_fs45.readFileSync)(file, "utf8"))); return parsed.success ? { telemetry: parsed.data } : { telemetry: emptyResearchTelemetry(now52), diagnostic: "research telemetry is schema-invalid" }; } catch { return { telemetry: emptyResearchTelemetry(now52), diagnostic: "research telemetry is unreadable" }; @@ -74405,7 +74537,7 @@ function readResearchTelemetry(workspace, now52 = /* @__PURE__ */ new Date()) { function writeTelemetry(workspace, value) { const telemetry = researchTelemetrySchema.parse(value); const file = researchTelemetryFile(workspace); - (0, import_fs46.mkdirSync)(import_path49.default.dirname(file), { recursive: true }); + (0, import_fs45.mkdirSync)(import_path48.default.dirname(file), { recursive: true }); writeFileAtomic(file, `${JSON.stringify(telemetry, null, 2)} `); return telemetry; @@ -75692,23 +75824,22 @@ ${correctionMessage(invocation.role, validated.problem)}`; }; } async function runLargeObjectiveRole(invocation) { - const profile = invocation.config.runnerProfiles[invocation.runnerProfile]; - if (profile === void 0 || profile.runner !== "claude-code") { + const registry2 = invocation.registry ?? createDefaultRunnerRegistry(invocation.config); + let profile; + try { + profile = registry2.getProfile(invocation.runnerProfile); + } catch (cause) { return { ok: false, kind: "worker-unavailable", - problem: `Runner profile "${invocation.runnerProfile}" is not a Claude Code profile.` + problem: cause instanceof Error ? cause.message : `Runner profile "${invocation.runnerProfile}" is unavailable.` }; } - const claudeProfile = profile; - const probe = invocation.cachedProbe ?? await probeClaude(claudeProfile, { - ...invocation.signal !== void 0 ? { signal: invocation.signal } : {} - }); - if (!probe.found || probe.status === "unavailable" || probe.status === "error") { + if (profile.config.enabled !== true || profile.runner.invokeStructured === void 0) { return { ok: false, kind: "worker-unavailable", - problem: `The Claude Code CLI is not available (status ${probe.status}).` + problem: profile.config.enabled !== true ? `Runner profile "${invocation.runnerProfile}" is disabled.` : `Runner profile "${invocation.runnerProfile}" does not support structured orchestration roles.` }; } const prompt = [ @@ -75718,73 +75849,41 @@ async function runLargeObjectiveRole(invocation) { "", invocation.packet ].join("\n"); - const plan = buildClaudeInvocation({ - config: claudeProfile, - probe, + const result = await profile.runner.invokeStructured({ prompt, toolPolicy: invocation.role === "BUILDER" ? "implementation" : "inspect-only", - outputJsonSchema: OBJECTIVE_OUTPUT_JSON_SCHEMAS[invocation.role], - execution: { - workspaceRoot: invocation.cwd, - runDir: invocation.scratchDir, - timeoutMs: invocation.timeoutMs - } + schemaName: invocation.role, + outputJsonSchema: OBJECTIVE_OUTPUT_JSON_SCHEMAS[invocation.role] + }, { + workspaceRoot: invocation.cwd, + runDir: invocation.scratchDir, + timeoutMs: invocation.timeoutMs, + ...invocation.signal !== void 0 ? { signal: invocation.signal } : {} }); - try { - const processResult = await runSafeProcess({ - executable: plan.executable, - argv: plan.argv, - cwd: invocation.cwd, - timeoutMs: invocation.timeoutMs, - stdin: plan.stdin, - ...invocation.signal !== void 0 ? { signal: invocation.signal } : {} - }); - if (processResult.status === "cancelled") { - return { ok: false, kind: "cancelled", problem: "The worker invocation was cancelled.", probe }; - } - if (processResult.status !== "ok" && processResult.status !== "nonzero-exit") { - return { - ok: false, - kind: "worker-unavailable", - problem: processResult.failureReason ?? `the worker process ended with status ${processResult.status}`, - probe - }; - } - const parsed = parseClaudeEnvelope(processResult.stdout); - if (parsed.problem !== void 0) { - return { - ok: false, - kind: "invalid-output", - problem: claudeFailureProblem(parsed.problem, processResult), - probe - }; - } - const text93 = parsed.structuredResult !== void 0 ? JSON.stringify(parsed.structuredResult) : parsed.reportText ?? ""; - const validated = validateObjectiveOutput(invocation.role, text93); - if (!validated.ok) { - return { ok: false, kind: "invalid-output", problem: validated.problem, probe }; - } - const usage = usageFromEnvelope(parsed.envelope, 0); - const cost = costFromEnvelope(parsed.envelope); + if (result.outcome === "cancelled") { + return { ok: false, kind: "cancelled", problem: result.failureReason ?? "The worker invocation was cancelled." }; + } + if (result.outcome !== "completed" || result.text === void 0) { return { - ok: true, - output: validated.output, - raw: text93, - usage: { - inputTokens: usage?.inputTokens ?? null, - outputTokens: usage?.outputTokens ?? null, - costUsd: cost !== null && cost !== void 0 && cost.currency === "USD" ? cost.amount : null - }, - probe + ok: false, + kind: result.outcome === "malformed-output" ? "invalid-output" : "worker-unavailable", + problem: result.failureReason ?? result.error?.message ?? `Runner profile "${invocation.runnerProfile}" ended with ${result.outcome}.` }; - } finally { - cleanupTempFiles(plan); - try { - const { rmSync: rmSync82 } = await import("fs"); - rmSync82(import_path51.default.join(invocation.scratchDir, "tmp"), { recursive: true, force: true }); - } catch { - } } + const validated = validateObjectiveOutput(invocation.role, result.text); + if (!validated.ok) { + return { ok: false, kind: "invalid-output", problem: validated.problem }; + } + return { + ok: true, + output: validated.output, + raw: result.text, + usage: { + inputTokens: result.usage?.inputTokens ?? null, + outputTokens: result.usage?.outputTokens ?? null, + costUsd: result.cost?.currency === "USD" ? result.cost.amount : null + } + }; } async function applyPatch(workspaceRoot, patch) { const result = await runSafeProcess({ @@ -75890,14 +75989,14 @@ async function integrateObjective(input) { const reconcile = await runLargeObjectiveRole({ workspace: input.workspace, config: input.config, + registry: input.registry, runnerProfile: input.runnerProfile, role: "BUILDER", packet, cwd: input.workspace.rootDir, - scratchDir: import_path50.default.join(jobDir(input.workspace, input.jobId), "scratch"), + scratchDir: import_path49.default.join(jobDir(input.workspace, input.jobId), "scratch"), timeoutMs: input.reconcileTimeoutMs ?? 6e5, - ...input.signal !== void 0 ? { signal: input.signal } : {}, - ...input.cachedProbe !== void 0 ? { cachedProbe: input.cachedProbe } : {} + ...input.signal !== void 0 ? { signal: input.signal } : {} }); if (!reconcile.ok || reconcile.output.outcome !== "CANDIDATE_COMPLETE") { await abort(`reconciliation of ${entry2.unit.workUnitId} failed`); @@ -76347,7 +76446,7 @@ async function git3(cwd, argv2, timeoutMs = GIT_TIMEOUT_MS3) { return { ok: result.status === "ok", stdout: result.stdout, stderr: result.stderr }; } function worktreesRootDir(workspace, jobId) { - return import_path52.default.join(jobDir(workspace, jobId), "worktrees"); + return import_path50.default.join(jobDir(workspace, jobId), "worktrees"); } async function readCanonicalHead(workspace) { const head = await git3(workspace.rootDir, ["rev-parse", "HEAD"]); @@ -76366,13 +76465,13 @@ async function createWorkerWorktree(input) { } const dir = assertInsideWorkspace( input.workspace.rootDir, - import_path52.default.join(worktreesRootDir(input.workspace, input.jobId), name) + import_path50.default.join(worktreesRootDir(input.workspace, input.jobId), name) ); const baselineCommit = await readCanonicalHead(input.workspace); - if ((0, import_fs47.existsSync)(dir)) { + if ((0, import_fs46.existsSync)(dir)) { await removeWorkerWorktree(input.workspace, input.jobId, { dir }); } - (0, import_fs47.mkdirSync)(import_path52.default.dirname(dir), { recursive: true }); + (0, import_fs46.mkdirSync)(import_path50.default.dirname(dir), { recursive: true }); const added = await git3(input.workspace.rootDir, ["worktree", "add", "--detach", dir, baselineCommit], 18e4); if (!added.ok) { throw new OrchestrationError("SBO048", `git worktree add failed: ${added.stderr.slice(0, 500)}`, { @@ -76447,7 +76546,7 @@ async function runWorktreeVerification(handle, commands, signal) { async function removeWorkerWorktree(workspace, jobId, handle) { await git3(workspace.rootDir, ["worktree", "remove", "--force", handle.dir], 12e4); try { - (0, import_fs47.rmSync)(handle.dir, { recursive: true, force: true }); + (0, import_fs46.rmSync)(handle.dir, { recursive: true, force: true }); } catch { } await git3(workspace.rootDir, ["worktree", "prune"]); @@ -76456,14 +76555,14 @@ async function removeWorkerWorktree(workspace, jobId, handle) { async function pruneWorktrees(workspace, jobId) { const removed = []; const root = worktreesRootDir(workspace, jobId); - if ((0, import_fs47.existsSync)(root)) { + if ((0, import_fs46.existsSync)(root)) { const { readdirSync: readdirSync112 } = await import("fs"); for (const entry2 of readdirSync112(root, { withFileTypes: true })) { if (!entry2.isDirectory()) continue; - const dir = import_path52.default.join(root, entry2.name); + const dir = import_path50.default.join(root, entry2.name); await git3(workspace.rootDir, ["worktree", "remove", "--force", dir], 12e4); try { - (0, import_fs47.rmSync)(dir, { recursive: true, force: true }); + (0, import_fs46.rmSync)(dir, { recursive: true, force: true }); } catch { } removed.push(entry2.name); @@ -77248,16 +77347,15 @@ async function decomposeObjective(input, truth, relevantContractIds, acceptance) const large = await runLargeObjectiveRole({ workspace: input.workspace, config: input.config, + registry: input.registry, runnerProfile: selection.worker.runnerProfile ?? input.config.defaultRunner, role: "DECOMPOSER", packet, cwd: input.workspace.rootDir, - scratchDir: import_path47.default.join(jobDir(input.workspace, input.jobId), "scratch"), + scratchDir: import_path46.default.join(jobDir(input.workspace, input.jobId), "scratch"), timeoutMs: 6e5, - signal: input.signal, - cachedProbe: input.probeCache.probe + signal: input.signal }); - if (large.probe !== void 0) input.probeCache.probe = large.probe; return large; })(); input.countWorkerRun({ @@ -78269,18 +78367,18 @@ async function executeBuilder(context, prepared) { const reconcile = await runLargeObjectiveRole({ workspace: input.workspace, config: input.config, + registry: input.registry, runnerProfile: input.runnerProfile ?? input.config.defaultRunner, role: "BUILDER", packet: packet2, cwd: worktree.dir, - scratchDir: import_path47.default.join( + scratchDir: import_path46.default.join( jobDir(input.workspace, input.jobId), "scratch", `${prepared.unitId}-a${prepared.attempt}-depfix` ), timeoutMs: input.policy.objectives.builderTimeoutMs, - signal: input.signal, - cachedProbe: input.probeCache.probe + signal: input.signal }); if (!reconcile.ok || reconcile.output.outcome !== "CANDIDATE_COMPLETE") { const why = !reconcile.ok ? `${reconcile.kind}: ${reconcile.problem.slice(0, 400)}` : `worker outcome ${reconcile.output.outcome}: ${(reconcile.output.summary ?? "").slice(0, 300)}`; @@ -78293,7 +78391,6 @@ async function executeBuilder(context, prepared) { } }; } - if (reconcile.probe !== void 0) input.probeCache.probe = reconcile.probe; } if (prepared.priorCandidatePatch !== void 0 && prepared.priorCandidatePatch.trim().length > 0) { try { @@ -78384,20 +78481,19 @@ async function executeBuilder(context, prepared) { const result = await runLargeObjectiveRole({ workspace: input.workspace, config: input.config, + registry: input.registry, runnerProfile: input.runnerProfile ?? input.config.defaultRunner, role: "BUILDER", packet, cwd: worktree.dir, - scratchDir: import_path47.default.join( + scratchDir: import_path46.default.join( jobDir(input.workspace, input.jobId), "scratch", `${prepared.unitId}-a${prepared.attempt}` ), timeoutMs: input.policy.objectives.builderTimeoutMs, - signal: input.signal, - cachedProbe: input.probeCache.probe + signal: input.signal }); - if (result.probe !== void 0) input.probeCache.probe = result.probe; if (!result.ok && isStrongQuotaFailure(result.problem)) { const resource = quotaFailureResource({ observedAt: nowIso3(input), @@ -79080,16 +79176,15 @@ async function runSemanticEvaluation(context, graph, unitId) { const large = await runLargeObjectiveRole({ workspace: input.workspace, config: input.config, + registry: input.registry, runnerProfile: selection.worker.runnerProfile ?? input.config.defaultRunner, role: "EVALUATOR", packet: packetOverride ?? packet, cwd: input.workspace.rootDir, - scratchDir: import_path47.default.join(jobDir(input.workspace, input.jobId), "scratch"), + scratchDir: import_path46.default.join(jobDir(input.workspace, input.jobId), "scratch"), timeoutMs: 6e5, - signal: input.signal, - cachedProbe: input.probeCache.probe + signal: input.signal }); - if (large.probe !== void 0) input.probeCache.probe = large.probe; return large; }; const ranLocally = selection.worker.reasoningTier === "LOCAL_SMALL" && input.localManager !== void 0; @@ -79721,16 +79816,15 @@ async function maybeAggregateSemantically(context, graph) { const large = await runLargeObjectiveRole({ workspace: input.workspace, config: input.config, + registry: input.registry, runnerProfile: selection.worker.runnerProfile ?? input.config.defaultRunner, role: "AGGREGATOR", packet, cwd: input.workspace.rootDir, - scratchDir: import_path47.default.join(jobDir(input.workspace, input.jobId), "scratch"), + scratchDir: import_path46.default.join(jobDir(input.workspace, input.jobId), "scratch"), timeoutMs: 6e5, - signal: input.signal, - cachedProbe: input.probeCache.probe + signal: input.signal }); - if (large.probe !== void 0) input.probeCache.probe = large.probe; return large; })(); input.countWorkerRun({ @@ -79851,6 +79945,7 @@ async function integrateVerifiedCandidates(input, graph) { const result = await integrateObjective({ workspace: input.workspace, config: input.config, + registry: input.registry, jobId: input.jobId, // Reconciling a conflicting candidate is a BUILD-sized job, not a // question-sized one: the worker reads the conflict, understands two @@ -79869,7 +79964,6 @@ async function integrateVerifiedCandidates(input, graph) { clock: input.clock, idFactory: input.idFactory, signal: input.signal, - cachedProbe: input.probeCache.probe, onProgress: input.onProgress }); if (!result.ok) { @@ -80072,37 +80166,37 @@ var schedulingDecisionSchema = external_exports.object({ createdAt: shortText15 }).passthrough(); function schedulingDir(workspace, jobId) { - return assertInsideWorkspace(workspace.rootDir, import_path53.default.join(jobDir(workspace, jobId), "scheduling")); + return assertInsideWorkspace(workspace.rootDir, import_path51.default.join(jobDir(workspace, jobId), "scheduling")); } function decisionsFile2(workspace, jobId) { return assertInsideWorkspace( workspace.rootDir, - import_path53.default.join(schedulingDir(workspace, jobId), "decisions.jsonl") + import_path51.default.join(schedulingDir(workspace, jobId), "decisions.jsonl") ); } function appendSchedulingDecision(workspace, record32, options) { const validated = schedulingDecisionSchema.parse(record32); const dir = schedulingDir(workspace, record32.jobId); - (0, import_fs48.mkdirSync)(dir, { recursive: true }); + (0, import_fs47.mkdirSync)(dir, { recursive: true }); const file = decisionsFile2(workspace, record32.jobId); const line = `${JSON.stringify(validated)} `; - const existing = (0, import_fs48.existsSync)(file) ? (0, import_fs48.readFileSync)(file, "utf8") : ""; + const existing = (0, import_fs47.existsSync)(file) ? (0, import_fs47.readFileSync)(file, "utf8") : ""; const lines = existing.split("\n").filter((entry2) => entry2.length > 0); if (lines.length + 1 > options.maxRecords) { const retained = [...lines, line.trimEnd()].slice(-options.maxRecords); writeFileAtomic(file, `${retained.join("\n")} `); } else { - (0, import_fs48.appendFileSync)(file, line, "utf8"); + (0, import_fs47.appendFileSync)(file, line, "utf8"); } return validated; } function readSchedulingDecisions(workspace, jobId, options = {}) { const file = decisionsFile2(workspace, jobId); - if (!(0, import_fs48.existsSync)(file)) return []; + if (!(0, import_fs47.existsSync)(file)) return []; const records = []; - for (const line of (0, import_fs48.readFileSync)(file, "utf8").split("\n")) { + for (const line of (0, import_fs47.readFileSync)(file, "utf8").split("\n")) { if (line.length === 0) continue; try { const parsed = schedulingDecisionSchema.safeParse(JSON.parse(line)); @@ -80665,7 +80759,7 @@ var ID_PATTERN8 = /^[A-Za-z0-9][A-Za-z0-9._-]{0,63}$/; function approvalsDir(workspace, jobId) { return assertInsideWorkspace( workspace.rootDir, - import_path54.default.join(jobDir(workspace, jobId), "api-approvals") + import_path52.default.join(jobDir(workspace, jobId), "api-approvals") ); } function approvalFile(workspace, jobId, approvalId) { @@ -80674,26 +80768,26 @@ function approvalFile(workspace, jobId, approvalId) { } return assertInsideWorkspace( workspace.rootDir, - import_path54.default.join(approvalsDir(workspace, jobId), `${approvalId}.json`) + import_path52.default.join(approvalsDir(workspace, jobId), `${approvalId}.json`) ); } function writeApiSpendApproval(workspace, approval) { const validated = apiSpendApprovalSchema.parse(approval); const file = approvalFile(workspace, validated.jobId, validated.approvalId); - (0, import_fs49.mkdirSync)(import_path54.default.dirname(file), { recursive: true }); + (0, import_fs48.mkdirSync)(import_path52.default.dirname(file), { recursive: true }); writeFileAtomic(file, `${JSON.stringify(validated, null, 2)} `); return validated; } function listApiSpendApprovals(workspace, jobId, options = {}) { const dir = approvalsDir(workspace, jobId); - if (!(0, import_fs49.existsSync)(dir)) return []; + if (!(0, import_fs48.existsSync)(dir)) return []; const approvals = []; - for (const name of (0, import_fs49.readdirSync)(dir).sort()) { + for (const name of (0, import_fs48.readdirSync)(dir).sort()) { if (!name.endsWith(".json")) continue; try { const parsed = apiSpendApprovalSchema.safeParse( - JSON.parse((0, import_fs49.readFileSync)(import_path54.default.join(dir, name), "utf8")) + JSON.parse((0, import_fs48.readFileSync)(import_path52.default.join(dir, name), "utf8")) ); if (parsed.success) approvals.push(parsed.data); } catch { @@ -80704,8 +80798,8 @@ function listApiSpendApprovals(workspace, jobId, options = {}) { } function readApiSpendApproval(workspace, jobId, approvalId) { const file = approvalFile(workspace, jobId, approvalId); - if (!(0, import_fs49.existsSync)(file)) return void 0; - const parsed = apiSpendApprovalSchema.safeParse(JSON.parse((0, import_fs49.readFileSync)(file, "utf8"))); + if (!(0, import_fs48.existsSync)(file)) return void 0; + const parsed = apiSpendApprovalSchema.safeParse(JSON.parse((0, import_fs48.readFileSync)(file, "utf8"))); return parsed.success ? parsed.data : void 0; } function requestApiSpendApproval(input) { @@ -80853,14 +80947,14 @@ var MANUAL_TELEMETRY_SOURCE = "manual-file"; function quotaTelemetryFilePath(workspace) { return assertInsideWorkspace( workspace.rootDir, - import_path55.default.join(workspace.sidecarDir, QUOTA_TELEMETRY_FILE_NAME) + import_path53.default.join(workspace.sidecarDir, QUOTA_TELEMETRY_FILE_NAME) ); } function readQuotaTelemetryFile(workspace) { const file = quotaTelemetryFilePath(workspace); - if (!(0, import_fs50.existsSync)(file)) return quotaTelemetryFileSchema.parse({}); + if (!(0, import_fs49.existsSync)(file)) return quotaTelemetryFileSchema.parse({}); try { - const parsed = quotaTelemetryFileSchema.safeParse(JSON.parse((0, import_fs50.readFileSync)(file, "utf8"))); + const parsed = quotaTelemetryFileSchema.safeParse(JSON.parse((0, import_fs49.readFileSync)(file, "utf8"))); return parsed.success ? parsed.data : quotaTelemetryFileSchema.parse({}); } catch { return quotaTelemetryFileSchema.parse({}); @@ -82895,7 +82989,7 @@ function specExcerptFor(workspace, specName, maxChars) { if (file === void 0) continue; try { parts.push(`--- ${kind} --- -${(0, import_fs44.readFileSync)(file.path, "utf8")}`); +${(0, import_fs43.readFileSync)(file.path, "utf8")}`); } catch { } } @@ -82936,7 +83030,6 @@ async function driveJob(deps4, jobId, options = {}) { return { stop: { kind: "final", status: job.status }, job }; } } - const probeCache = { probe: void 0 }; const localManager = createLocalManager(deps4.config, (event) => { emit22("local-model", `${event.type}: ${event.detail}`); if (event.type === "ready") { @@ -83023,7 +83116,6 @@ async function driveJob(deps4, jobId, options = {}) { case "RUN_ROLE": { const outcome = await handleRoleDecision(deps4, jobId, decision, { localManager, - probeCache, signal, emit: emit22 }); @@ -83422,6 +83514,7 @@ async function driveJob(deps4, jobId, options = {}) { }) : mission !== void 0 ? await driveObjective({ workspace: deps4.workspace, config: deps4.config, + registry: deps4.registry, jobId, specName: job.specName, node, @@ -83431,7 +83524,6 @@ async function driveJob(deps4, jobId, options = {}) { allowDirty, runnerProfile: decision.worker.runnerProfile, localManager, - probeCache, ...deps4.clock !== void 0 ? { clock: deps4.clock } : {}, ...deps4.idFactory !== void 0 ? { idFactory: deps4.idFactory } : {}, ...signal !== void 0 ? { signal } : {}, @@ -83863,7 +83955,7 @@ function buildCriteriaEvidence(input) { const normalized = input.changedPaths.map((entry2) => entry2.replaceAll("\\", "/")); const existing = /* @__PURE__ */ new Set(); for (const changed of normalized) { - if ((0, import_fs44.existsSync)(import_path46.default.join(input.workspaceRoot, changed))) existing.add(changed); + if ((0, import_fs43.existsSync)(import_path45.default.join(input.workspaceRoot, changed))) existing.add(changed); } return { existingPaths: existing, @@ -84473,7 +84565,7 @@ async function handleRoleDecision(deps4, jobId, decision, runtime) { code: "LARGE_WORKER_FAILED", message: `The large-agent ${role} failed twice: ${result.problem.slice(0, 500)}`, remediation: [ - "Check the Claude Code installation with `specbridge runner doctor claude-code`.", + `Check runner profile "${decision.worker.runnerProfile ?? deps4.config.defaultRunner}" with \`specbridge runner doctor ${decision.worker.runnerProfile ?? deps4.config.defaultRunner}\`.`, // The excerpt is the whole point of the remediation. A job blocked // on "the response is not a single valid JSON document" with // nothing retained leaves an operator a message and no evidence, @@ -84608,15 +84700,14 @@ async function runRole(deps4, jobId, role, decision, packet, runtime) { const result = await runLargeRole({ workspace: deps4.workspace, config: deps4.config, + registry: deps4.registry, runnerProfile: decision.worker.runnerProfile ?? deps4.config.defaultRunner, role, packet, - scratchDir: import_path46.default.join(jobDir(deps4.workspace, jobId), "scratch"), + scratchDir: import_path45.default.join(jobDir(deps4.workspace, jobId), "scratch"), timeoutMs: 6e5, - signal: runtime.signal, - cachedProbe: runtime.probeCache.probe + signal: runtime.signal }); - if (result.probe !== void 0) runtime.probeCache.probe = result.probe; return result; } async function applyRoleOutput(deps4, jobId, role, result, context, node, activePlan) { @@ -84954,17 +85045,17 @@ async function git22(cwd, argv2, timeoutMs = GIT_TIMEOUT_MS22) { return { ok: result.status === "ok", stdout: result.stdout, stderr: result.stderr }; } function seedSidecar(source, targetRoot, specNames) { - const sidecar = import_path56.default.join(targetRoot, ".specbridge"); - (0, import_fs51.mkdirSync)(sidecar, { recursive: true }); - const config2 = import_path56.default.join(source.sidecarDir, "config.json"); - if ((0, import_fs51.existsSync)(config2)) (0, import_fs51.copyFileSync)(config2, import_path56.default.join(sidecar, "config.json")); - const stateDir = import_path56.default.join(source.sidecarDir, "state", "specs"); - if (!(0, import_fs51.existsSync)(stateDir)) return; - const targetState = import_path56.default.join(sidecar, "state", "specs"); - (0, import_fs51.mkdirSync)(targetState, { recursive: true }); + const sidecar = import_path54.default.join(targetRoot, ".specbridge"); + (0, import_fs50.mkdirSync)(sidecar, { recursive: true }); + const config2 = import_path54.default.join(source.sidecarDir, "config.json"); + if ((0, import_fs50.existsSync)(config2)) (0, import_fs50.copyFileSync)(config2, import_path54.default.join(sidecar, "config.json")); + const stateDir = import_path54.default.join(source.sidecarDir, "state", "specs"); + if (!(0, import_fs50.existsSync)(stateDir)) return; + const targetState = import_path54.default.join(sidecar, "state", "specs"); + (0, import_fs50.mkdirSync)(targetState, { recursive: true }); for (const name of new Set(specNames)) { - const file = import_path56.default.join(stateDir, `${name}.json`); - if ((0, import_fs51.existsSync)(file)) (0, import_fs51.copyFileSync)(file, import_path56.default.join(targetState, `${name}.json`)); + const file = import_path54.default.join(stateDir, `${name}.json`); + if ((0, import_fs50.existsSync)(file)) (0, import_fs50.copyFileSync)(file, import_path54.default.join(targetState, `${name}.json`)); } } function syntheticNode(evaluationCase) { @@ -84992,8 +85083,8 @@ async function evaluateLocalRuntime(input) { const modes = input.modes ?? ["DIRECT_MODEL", "HARNESS"]; const binding = resolveLocalHarnessBinding(input.config); const harnessProfile = input.harnessProfile ?? binding.profileName ?? void 0; - const workRoot = input.workRoot ?? import_path56.default.join(input.workspace.sidecarDir, "local-runtime-eval"); - (0, import_fs51.mkdirSync)(workRoot, { recursive: true }); + const workRoot = input.workRoot ?? import_path54.default.join(input.workspace.sidecarDir, "local-runtime-eval"); + (0, import_fs50.mkdirSync)(workRoot, { recursive: true }); const head = await git22(input.workspace.rootDir, ["rev-parse", "HEAD"]); if (!head.ok) { throw new OrchestrationError( @@ -85052,7 +85143,7 @@ async function evaluateLocalRuntime(input) { } async function runArm(options) { const { input, evaluationCase, mode, workRoot } = options; - const armDir = import_path56.default.join( + const armDir = import_path54.default.join( workRoot, `${evaluationCase.caseId}-${mode === "HARNESS" ? "harness" : "direct"}`.replace( /[^A-Za-z0-9._-]/g, @@ -85083,9 +85174,9 @@ async function runArm(options) { if (mode === "HARNESS" && options.harnessProfile === void 0) { return unavailable("no harness profile is bound or configured for the harness arm"); } - if ((0, import_fs51.existsSync)(armDir)) { + if ((0, import_fs50.existsSync)(armDir)) { await git22(input.workspace.rootDir, ["worktree", "remove", "--force", armDir]); - (0, import_fs51.rmSync)(armDir, { recursive: true, force: true }); + (0, import_fs50.rmSync)(armDir, { recursive: true, force: true }); } const added = await git22( input.workspace.rootDir, @@ -85161,7 +85252,7 @@ async function runArm(options) { if (input.keepWorktrees !== true) { await git22(input.workspace.rootDir, ["worktree", "remove", "--force", armDir]); try { - (0, import_fs51.rmSync)(armDir, { recursive: true, force: true }); + (0, import_fs50.rmSync)(armDir, { recursive: true, force: true }); } catch { } await git22(input.workspace.rootDir, ["worktree", "prune"]); @@ -85879,46 +85970,46 @@ function assertRecordId4(kind, id) { function qualificationDir(workspace) { return assertInsideWorkspace( workspace.rootDir, - import_path57.default.join(workspace.rootDir, ".specbridge", "qualification") + import_path55.default.join(workspace.rootDir, ".specbridge", "qualification") ); } function dogfoodRunDir(workspace, runId) { assertRecordId4("dogfood run", runId); - return assertInsideWorkspace(workspace.rootDir, import_path57.default.join(qualificationDir(workspace), runId)); + return assertInsideWorkspace(workspace.rootDir, import_path55.default.join(qualificationDir(workspace), runId)); } function runFile(workspace, runId) { - return assertInsideWorkspace(workspace.rootDir, import_path57.default.join(dogfoodRunDir(workspace, runId), "run.json")); + return assertInsideWorkspace(workspace.rootDir, import_path55.default.join(dogfoodRunDir(workspace, runId), "run.json")); } function recordDir2(workspace, runId, kind) { - return assertInsideWorkspace(workspace.rootDir, import_path57.default.join(dogfoodRunDir(workspace, runId), kind)); + return assertInsideWorkspace(workspace.rootDir, import_path55.default.join(dogfoodRunDir(workspace, runId), kind)); } function recordFile2(workspace, runId, kind, id) { assertRecordId4(kind, id); return assertInsideWorkspace( workspace.rootDir, - import_path57.default.join(recordDir2(workspace, runId, kind), `${id}.json`) + import_path55.default.join(recordDir2(workspace, runId, kind), `${id}.json`) ); } function writeRecord2(file, value) { - (0, import_fs52.mkdirSync)(import_path57.default.dirname(file), { recursive: true }); + (0, import_fs51.mkdirSync)(import_path55.default.dirname(file), { recursive: true }); writeFileAtomic(file, `${JSON.stringify(value, null, 2)} `); } function readRecord2(file, parse3) { - if (!(0, import_fs52.existsSync)(file)) return void 0; + if (!(0, import_fs51.existsSync)(file)) return void 0; try { - return parse3(JSON.parse((0, import_fs52.readFileSync)(file, "utf8"))); + return parse3(JSON.parse((0, import_fs51.readFileSync)(file, "utf8"))); } catch { return void 0; } } function listRecords2(workspace, runId, kind, parse3) { const dir = recordDir2(workspace, runId, kind); - if (!(0, import_fs52.existsSync)(dir)) return []; + if (!(0, import_fs51.existsSync)(dir)) return []; const records = []; - for (const entry2 of (0, import_fs52.readdirSync)(dir).sort()) { + for (const entry2 of (0, import_fs51.readdirSync)(dir).sort()) { if (!entry2.endsWith(".json")) continue; - const record32 = readRecord2(import_path57.default.join(dir, entry2), parse3); + const record32 = readRecord2(import_path55.default.join(dir, entry2), parse3); if (record32 !== void 0) records.push(record32); } return records; @@ -85945,9 +86036,9 @@ function requireDogfoodRun(workspace, runId) { } function listDogfoodRuns(workspace) { const dir = qualificationDir(workspace); - if (!(0, import_fs52.existsSync)(dir)) return []; + if (!(0, import_fs51.existsSync)(dir)) return []; const runs = []; - for (const entry2 of (0, import_fs52.readdirSync)(dir, { withFileTypes: true })) { + for (const entry2 of (0, import_fs51.readdirSync)(dir, { withFileTypes: true })) { if (!entry2.isDirectory()) continue; if (!ID_PATTERN9.test(entry2.name)) continue; const run = readDogfoodRun(workspace, entry2.name); @@ -85984,9 +86075,9 @@ function writeQualificationArtifact(workspace, runId, name, contents) { } const file = assertInsideWorkspace( workspace.rootDir, - import_path57.default.join(recordDir2(workspace, runId, "reports"), name) + import_path55.default.join(recordDir2(workspace, runId, "reports"), name) ); - (0, import_fs52.mkdirSync)(import_path57.default.dirname(file), { recursive: true }); + (0, import_fs51.mkdirSync)(import_path55.default.dirname(file), { recursive: true }); writeFileAtomic(file, contents); return file; } @@ -85996,7 +86087,7 @@ function qualificationArtifactPath(workspace, runId, name) { } return assertInsideWorkspace( workspace.rootDir, - import_path57.default.join(recordDir2(workspace, runId, "reports"), name) + import_path55.default.join(recordDir2(workspace, runId, "reports"), name) ); } var PROFILE_ORDER = ["offline", "local", "subscription", "full"]; @@ -86682,7 +86773,7 @@ function runPreflight(input) { "Offline qualification does not need a target: run it with --profile offline." ]) ); - } else if (!(0, import_fs53.existsSync)(target.repositoryPath) || !(0, import_fs53.statSync)(target.repositoryPath).isDirectory()) { + } else if (!(0, import_fs52.existsSync)(target.repositoryPath) || !(0, import_fs52.statSync)(target.repositoryPath).isDirectory()) { findings2.push( refuse2( "target.repository", @@ -86878,7 +86969,7 @@ function normalizeTargetPath(value) { if (value === null || value === void 0) return null; const trimmed = value.trim(); if (trimmed.length === 0) return null; - return import_path58.default.resolve(trimmed); + return import_path56.default.resolve(trimmed); } function add(current, reported) { if (reported === null || reported === void 0) return current; @@ -91000,20 +91091,20 @@ var import_node_fs6 = require("fs"); var import_node_path8 = __toESM(require("path"), 1); // ../../packages/drift/dist/index.js -var import_fs54 = require("fs"); -var import_path59 = __toESM(require("path"), 1); +var import_fs53 = require("fs"); +var import_path57 = __toESM(require("path"), 1); var import_picomatch = __toESM(require_picomatch2(), 1); +var import_fs54 = require("fs"); +var import_path58 = __toESM(require("path"), 1); var import_fs55 = require("fs"); -var import_path60 = __toESM(require("path"), 1); +var import_path59 = __toESM(require("path"), 1); var import_fs56 = require("fs"); -var import_path61 = __toESM(require("path"), 1); +var import_path60 = __toESM(require("path"), 1); var import_fs57 = require("fs"); -var import_path62 = __toESM(require("path"), 1); +var import_path61 = __toESM(require("path"), 1); var import_fs58 = require("fs"); -var import_path63 = __toESM(require("path"), 1); -var import_fs59 = require("fs"); var import_crypto25 = require("crypto"); -var import_path64 = __toESM(require("path"), 1); +var import_path62 = __toESM(require("path"), 1); var taskEvidenceSchema = external_exports.object({ taskId: external_exports.string().min(1), status: external_exports.enum(["recorded", "verified", "rejected"]), @@ -91114,24 +91205,24 @@ var verificationPolicySchema = external_exports.object({ } }); function policyDir(workspace) { - return import_path59.default.join(workspace.sidecarDir, "policies"); + return import_path57.default.join(workspace.sidecarDir, "policies"); } function policyPath(workspace, specName) { - const resolved2 = import_path59.default.resolve(policyDir(workspace), `${specName}.json`); - const relative = import_path59.default.relative(workspace.rootDir, resolved2); - if (relative.startsWith("..") || import_path59.default.isAbsolute(relative)) { - return import_path59.default.join(policyDir(workspace), "invalid-spec-name.json"); + const resolved2 = import_path57.default.resolve(policyDir(workspace), `${specName}.json`); + const relative = import_path57.default.relative(workspace.rootDir, resolved2); + if (relative.startsWith("..") || import_path57.default.isAbsolute(relative)) { + return import_path57.default.join(policyDir(workspace), "invalid-spec-name.json"); } return resolved2; } function readVerificationPolicy(workspace, specName, explicitPath) { - const filePath = explicitPath !== void 0 ? import_path59.default.resolve(workspace.rootDir, explicitPath) : policyPath(workspace, specName); - if (!(0, import_fs54.existsSync)(filePath)) { + const filePath = explicitPath !== void 0 ? import_path57.default.resolve(workspace.rootDir, explicitPath) : policyPath(workspace, specName); + if (!(0, import_fs53.existsSync)(filePath)) { return { path: filePath, exists: false, diagnostics: [] }; } let parsed; try { - parsed = JSON.parse((0, import_fs54.readFileSync)(filePath, "utf8")); + parsed = JSON.parse((0, import_fs53.readFileSync)(filePath, "utf8")); } catch (cause) { return { path: filePath, @@ -91194,7 +91285,7 @@ function resolveEffectivePolicy(workspace, specName, options = {}) { const storedMode = policy?.mode ?? "advisory"; const strictFromCli = options.strict === true && storedMode !== "strict"; const mode = options.strict === true ? "strict" : storedMode; - const workspaceRelativePolicyPath = import_path59.default.relative(workspace.rootDir, read.path).split(import_path59.default.sep).join("/"); + const workspaceRelativePolicyPath = import_path57.default.relative(workspace.rootDir, read.path).split(import_path57.default.sep).join("/"); return { specName, mode, @@ -91355,33 +91446,33 @@ function mergeNumstat(files, stats) { function sniffBinary(absolutePath) { let fd; try { - fd = (0, import_fs55.openSync)(absolutePath, "r"); + fd = (0, import_fs54.openSync)(absolutePath, "r"); const buffer = Buffer.alloc(8e3); - const bytesRead = (0, import_fs55.readSync)(fd, buffer, 0, buffer.length, 0); + const bytesRead = (0, import_fs54.readSync)(fd, buffer, 0, buffer.length, 0); return buffer.subarray(0, bytesRead).includes(0); } catch { return false; } finally { - if (fd !== void 0) (0, import_fs55.closeSync)(fd); + if (fd !== void 0) (0, import_fs54.closeSync)(fd); } } function flagSymlinkEscapes(repoRoot, files) { const resolvedRoot = (() => { try { - return (0, import_fs55.realpathSync)(repoRoot); + return (0, import_fs54.realpathSync)(repoRoot); } catch { - return import_path60.default.resolve(repoRoot); + return import_path58.default.resolve(repoRoot); } })(); for (const file of files) { if (file.changeType === "deleted") continue; - const absolute = import_path60.default.join(repoRoot, file.path.split("/").join(import_path60.default.sep)); + const absolute = import_path58.default.join(repoRoot, file.path.split("/").join(import_path58.default.sep)); try { - const stats = (0, import_fs55.lstatSync)(absolute); + const stats = (0, import_fs54.lstatSync)(absolute); if (!stats.isSymbolicLink()) continue; - const target = (0, import_fs55.realpathSync)(absolute); - const relative = import_path60.default.relative(resolvedRoot, target); - if (relative.startsWith("..") || import_path60.default.isAbsolute(relative)) { + const target = (0, import_fs54.realpathSync)(absolute); + const relative = import_path58.default.relative(resolvedRoot, target); + if (relative.startsWith("..") || import_path58.default.isAbsolute(relative)) { file.symlinkOutsideRepository = true; } } catch { @@ -91509,7 +91600,7 @@ async function resolveComparison(repoRoot, request, options = {}) { const known = new Set(files.map((file) => file.path)); for (const token of untracked.stdout.split("\0")) { if (token.length === 0 || known.has(token)) continue; - const absolute = import_path60.default.join(repoRoot, token.split("/").join(import_path60.default.sep)); + const absolute = import_path58.default.join(repoRoot, token.split("/").join(import_path58.default.sep)); files.push({ path: token, changeType: "untracked", @@ -91589,9 +91680,9 @@ function specMatchReasons(specName, policy, validEvidencePaths, designPathRefere function readSpecEvidenceRecords(workspace, specName) { const byTask = /* @__PURE__ */ new Map(); let invalidRecordCount = 0; - const specDir = import_path61.default.join(workspace.sidecarDir, "evidence", specName); - if ((0, import_fs56.existsSync)(specDir)) { - const taskDirs = (0, import_fs56.readdirSync)(specDir, { withFileTypes: true }).filter((entry2) => entry2.isDirectory()).map((entry2) => entry2.name).sort((a2, b) => a2.localeCompare(b, "en")); + const specDir = import_path59.default.join(workspace.sidecarDir, "evidence", specName); + if ((0, import_fs55.existsSync)(specDir)) { + const taskDirs = (0, import_fs55.readdirSync)(specDir, { withFileTypes: true }).filter((entry2) => entry2.isDirectory()).map((entry2) => entry2.name).sort((a2, b) => a2.localeCompare(b, "en")); for (const taskDir of taskDirs) { const { records, diagnostics } = listTaskEvidence(workspace, specName, taskDir); invalidRecordCount += diagnostics.length; @@ -91638,7 +91729,7 @@ async function buildSpecVerificationContext(options) { } if (effective("tasks") && tasksStage !== void 0) { const planHash2 = typeof tasksStage.approvedPlanHash === "string" ? tasksStage.approvedPlanHash : tryTaskPlanHashOfFile( - import_path61.default.join(workspace.rootDir, tasksStage.file.split("/").join(import_path61.default.sep)) + import_path59.default.join(workspace.rootDir, tasksStage.file.split("/").join(import_path59.default.sep)) ); if (planHash2 !== void 0) approved.tasksPlanHash = planHash2; } @@ -91866,7 +91957,7 @@ async function evaluateGlobalRules(rules, context) { return { diagnostics, disabledRules }; } function repoRelative(workspace, absolutePath) { - return import_path62.default.relative(workspace.rootDir, absolutePath).split(import_path62.default.sep).join("/"); + return import_path60.default.relative(workspace.rootDir, absolutePath).split(import_path60.default.sep).join("/"); } function isSpecInfraPath(candidate) { return candidate === ".git" || candidate.startsWith(".git/") || candidate.startsWith(".kiro/") || candidate.startsWith(".specbridge/"); @@ -92547,14 +92638,14 @@ var sbv018 = { if (designDocument === void 0) return []; const designFile = designDocument.filePath; const designRepoPath = designFile !== void 0 ? repoRelative(context.workspace, designFile) : void 0; - const specDir = import_path62.default.join(context.workspace.rootDir, ".kiro", "specs", context.specName); + const specDir = import_path60.default.join(context.workspace.rootDir, ".kiro", "specs", context.specName); return context.traceability.designPathReferences.filter((reference) => !reference.isGlob).filter((reference) => { - const fromRoot = import_path62.default.join( + const fromRoot = import_path60.default.join( context.workspace.rootDir, - reference.path.split("/").join(import_path62.default.sep) + reference.path.split("/").join(import_path60.default.sep) ); - const fromSpecDir = import_path62.default.join(specDir, reference.path.split("/").join(import_path62.default.sep)); - return !(0, import_fs57.existsSync)(fromRoot) && !(0, import_fs57.existsSync)(fromSpecDir); + const fromSpecDir = import_path60.default.join(specDir, reference.path.split("/").join(import_path60.default.sep)); + return !(0, import_fs56.existsSync)(fromRoot) && !(0, import_fs56.existsSync)(fromSpecDir); }).map( (reference) => makeDiagnostic({ rule: this, @@ -92801,9 +92892,9 @@ function loadSpecMatchingInfo(workspace, folder, options) { } } const evidencePaths = /* @__PURE__ */ new Set(); - const evidenceDir2 = import_path63.default.join(workspace.sidecarDir, "evidence", folder.name); - if ((0, import_fs58.existsSync)(evidenceDir2)) { - for (const entry2 of (0, import_fs59.readdirSync)(evidenceDir2, { withFileTypes: true })) { + const evidenceDir2 = import_path61.default.join(workspace.sidecarDir, "evidence", folder.name); + if ((0, import_fs57.existsSync)(evidenceDir2)) { + for (const entry2 of (0, import_fs58.readdirSync)(evidenceDir2, { withFileTypes: true })) { if (!entry2.isDirectory()) continue; const { records } = listTaskEvidence(workspace, folder.name, entry2.name); for (const record5 of records) { @@ -92912,8 +93003,8 @@ async function verifySpecs(request) { let artifactsDir; const ensureArtifactsDir = () => { if (artifactsDir === void 0) { - const base = request.reportsDir ?? import_path64.default.join(workspace.sidecarDir, "reports"); - artifactsDir = import_path64.default.join(base, verificationId); + const base = request.reportsDir ?? import_path62.default.join(workspace.sidecarDir, "reports"); + artifactsDir = import_path62.default.join(base, verificationId); } return artifactsDir; }; @@ -92936,8 +93027,8 @@ async function verifySpecs(request) { onCommandFinished: (result, stdout, stderr) => { const dir = ensureArtifactsDir(); const safeName = result.name.replace(/[^A-Za-z0-9._-]+/g, "-"); - writeFileAtomic(import_path64.default.join(dir, "commands", `${safeName}.stdout.log`), stdout); - writeFileAtomic(import_path64.default.join(dir, "commands", `${safeName}.stderr.log`), stderr); + writeFileAtomic(import_path62.default.join(dir, "commands", `${safeName}.stdout.log`), stdout); + writeFileAtomic(import_path62.default.join(dir, "commands", `${safeName}.stderr.log`), stderr); } } : {} }) : { mode: "none", commands: [], missingRequired: [] }; @@ -93088,7 +93179,7 @@ async function verifySpecs(request) { verificationReportSchema.parse(report); if (persistArtifacts && artifactsDir !== void 0) { writeFileAtomic( - import_path64.default.join(artifactsDir, "report.json"), + import_path62.default.join(artifactsDir, "report.json"), `${JSON.stringify(report, null, 2)} ` ); @@ -93197,18 +93288,18 @@ function resolveExitCode(report, comparison, commands, failOn) { } // ../../packages/templates/dist/index.js +var import_fs59 = require("fs"); +var import_path63 = __toESM(require("path"), 1); var import_fs60 = require("fs"); -var import_path65 = __toESM(require("path"), 1); +var import_path64 = __toESM(require("path"), 1); var import_fs61 = require("fs"); +var import_path65 = __toESM(require("path"), 1); var import_path66 = __toESM(require("path"), 1); var import_fs62 = require("fs"); var import_path67 = __toESM(require("path"), 1); -var import_path68 = __toESM(require("path"), 1); var import_fs63 = require("fs"); -var import_path69 = __toESM(require("path"), 1); -var import_fs64 = require("fs"); var import_os = require("os"); -var import_path70 = __toESM(require("path"), 1); +var import_path68 = __toESM(require("path"), 1); var SPECBRIDGE_VERSION = "1.0.0"; var TEMPLATE_ERROR_CODES = { SBT001: "template not found", @@ -94037,11 +94128,11 @@ function readTemplatePackDirectory(dir) { { path: currentDir } ); } - const entries = (0, import_fs60.readdirSync)(currentDir, { withFileTypes: true }).sort( + const entries = (0, import_fs59.readdirSync)(currentDir, { withFileTypes: true }).sort( (a2, b) => a2.name.localeCompare(b.name, "en") ); for (const entry2 of entries) { - const entryPath = import_path65.default.join(currentDir, entry2.name); + const entryPath = import_path63.default.join(currentDir, entry2.name); const entryRelative = relative === "" ? entry2.name : `${relative}/${entry2.name}`; const stat = statNoFollow(entryPath); if (stat.isSymbolicLink()) { @@ -94093,7 +94184,7 @@ function readTemplatePackDirectory(dir) { { path: dir } ); } - const buffer = (0, import_fs60.readFileSync)(entryPath); + const buffer = (0, import_fs59.readFileSync)(entryPath); const text15 = buffer.toString("utf8"); if (!Buffer.from(text15, "utf8").equals(buffer)) { throw new TemplateError( @@ -94119,7 +94210,7 @@ function readTemplatePackDirectory(dir) { } function statNoFollow(target) { try { - return (0, import_fs60.lstatSync)(target); + return (0, import_fs59.lstatSync)(target); } catch (cause) { throw new TemplateError( "SBT007", @@ -94483,7 +94574,7 @@ var BUILTIN_TEMPLATE_PACKS = [ } ]; function projectTemplatesDir(workspace) { - return import_path66.default.join(workspace.sidecarDir, "templates"); + return import_path64.default.join(workspace.sidecarDir, "templates"); } function builtinEntries(options) { const entries = []; @@ -94508,11 +94599,11 @@ function builtinEntries(options) { function projectEntries(workspace, options, diagnostics) { if (workspace === void 0) return []; const dir = projectTemplatesDir(workspace); - if (!(0, import_fs61.existsSync)(dir)) return []; + if (!(0, import_fs60.existsSync)(dir)) return []; const entries = []; let names; try { - names = (0, import_fs61.readdirSync)(dir, { withFileTypes: true }).filter((entry2) => entry2.isDirectory() && !entry2.isSymbolicLink()).map((entry2) => entry2.name).sort((a2, b) => a2.localeCompare(b, "en")); + names = (0, import_fs60.readdirSync)(dir, { withFileTypes: true }).filter((entry2) => entry2.isDirectory() && !entry2.isSymbolicLink()).map((entry2) => entry2.name).sort((a2, b) => a2.localeCompare(b, "en")); } catch (cause) { diagnostics.push({ severity: "warning", @@ -94522,7 +94613,7 @@ function projectEntries(workspace, options, diagnostics) { return []; } for (const name of names) { - const packDir = import_path66.default.join(dir, name); + const packDir = import_path64.default.join(dir, name); let pack; try { const data = readTemplatePackDirectory(packDir); @@ -94766,7 +94857,7 @@ var templateRecordSchema = external_exports.discriminatedUnion("type", [ templateScaffoldRecordSchema ]); function templateRecordsPath(workspace) { - return import_path67.default.join(workspace.sidecarDir, TEMPLATE_RECORDS_FILE_NAME); + return import_path65.default.join(workspace.sidecarDir, TEMPLATE_RECORDS_FILE_NAME); } var recordCounter = 0; function newTemplateRecordId(clock = systemClock) { @@ -94777,8 +94868,8 @@ function appendTemplateRecord(workspace, record5) { const validated = templateRecordSchema.parse(record5); const filePath = templateRecordsPath(workspace); try { - (0, import_fs62.mkdirSync)(workspace.sidecarDir, { recursive: true }); - (0, import_fs62.appendFileSync)(filePath, `${JSON.stringify(validated)} + (0, import_fs61.mkdirSync)(workspace.sidecarDir, { recursive: true }); + (0, import_fs61.appendFileSync)(filePath, `${JSON.stringify(validated)} `, "utf8"); } catch (cause) { throw ioError("append template record to", filePath, cause); @@ -94787,10 +94878,10 @@ function appendTemplateRecord(workspace, record5) { function readTemplateRecords(workspace) { const filePath = templateRecordsPath(workspace); const diagnostics = []; - if (!(0, import_fs62.existsSync)(filePath)) return { records: [], diagnostics }; + if (!(0, import_fs61.existsSync)(filePath)) return { records: [], diagnostics }; let text15; try { - text15 = (0, import_fs62.readFileSync)(filePath, "utf8"); + text15 = (0, import_fs61.readFileSync)(filePath, "utf8"); } catch (cause) { diagnostics.push({ severity: "warning", @@ -94989,7 +95080,7 @@ function planTemplateApplication(workspace, catalog, request, clock = systemCloc }; } function toPosix2(relative) { - return relative.split(import_path68.default.sep).join("/"); + return relative.split(import_path66.default.sep).join("/"); } function executeTemplateApplication(workspace, plan, clock = systemClock, recordId) { let creation; @@ -95019,15 +95110,15 @@ function executeTemplateApplication(workspace, plan, clock = systemClock, record })), variableNames: plan.variableNames, createdPaths: [ - ...creation.writtenFiles.map((file) => toPosix2(import_path68.default.relative(workspace.rootDir, file))), - toPosix2(import_path68.default.relative(workspace.rootDir, creation.statePath)) + ...creation.writtenFiles.map((file) => toPosix2(import_path66.default.relative(workspace.rootDir, file))), + toPosix2(import_path66.default.relative(workspace.rootDir, creation.statePath)) ] }; appendTemplateRecord(workspace, record5); return { plan, creation, recordId: id }; } function planTemplateInstall(workspace, catalog, request) { - const sourceDir = import_path69.default.resolve(request.cwd ?? workspace.rootDir, request.sourcePath); + const sourceDir = import_path67.default.resolve(request.cwd ?? workspace.rootDir, request.sourcePath); try { assertInsideWorkspace(workspace.rootDir, sourceDir); } catch (cause) { @@ -95053,8 +95144,8 @@ function planTemplateInstall(workspace, catalog, request) { ); } const templateId = pack.manifest.id; - const targetDir = import_path69.default.join(projectTemplatesDir(workspace), templateId); - if ((0, import_fs63.existsSync)(targetDir)) { + const targetDir = import_path67.default.join(projectTemplatesDir(workspace), templateId); + if ((0, import_fs62.existsSync)(targetDir)) { throw new TemplateError( "SBT021", `Template "project:${templateId}" is already installed at ${targetDir}.`, @@ -95080,16 +95171,16 @@ function planTemplateInstall(workspace, catalog, request) { }; } function executeTemplateInstall(workspace, plan, clock = systemClock, recordId) { - const tmpParent = import_path69.default.join(workspace.sidecarDir, "tmp"); - const tempDir = import_path69.default.join( + const tmpParent = import_path67.default.join(workspace.sidecarDir, "tmp"); + const tempDir = import_path67.default.join( tmpParent, `template-install-${plan.templateId}-${process.pid}-${Math.random().toString(36).slice(2, 8)}` ); try { - (0, import_fs63.mkdirSync)(tempDir, { recursive: true }); + (0, import_fs62.mkdirSync)(tempDir, { recursive: true }); for (const [relative, content] of plan.pack.files) { - const target = import_path69.default.join(tempDir, relative); - (0, import_fs63.mkdirSync)(import_path69.default.dirname(target), { recursive: true }); + const target = import_path67.default.join(tempDir, relative); + (0, import_fs62.mkdirSync)(import_path67.default.dirname(target), { recursive: true }); writeFileAtomic(target, content); } const copied = loadTemplatePack(readTemplatePackDirectory(tempDir)); @@ -95101,8 +95192,8 @@ function executeTemplateInstall(workspace, plan, clock = systemClock, recordId) { path: plan.sourceDir } ); } - (0, import_fs63.mkdirSync)(import_path69.default.dirname(plan.targetDir), { recursive: true }); - if ((0, import_fs63.existsSync)(plan.targetDir)) { + (0, import_fs62.mkdirSync)(import_path67.default.dirname(plan.targetDir), { recursive: true }); + if ((0, import_fs62.existsSync)(plan.targetDir)) { throw new TemplateError( "SBT021", `Template "project:${plan.templateId}" was installed by another process.`, @@ -95110,11 +95201,11 @@ function executeTemplateInstall(workspace, plan, clock = systemClock, recordId) { path: plan.targetDir } ); } - (0, import_fs63.renameSync)(tempDir, plan.targetDir); + (0, import_fs62.renameSync)(tempDir, plan.targetDir); } finally { - (0, import_fs63.rmSync)(tempDir, { recursive: true, force: true }); + (0, import_fs62.rmSync)(tempDir, { recursive: true, force: true }); try { - (0, import_fs63.rmdirSync)(tmpParent); + (0, import_fs62.rmdirSync)(tmpParent); } catch { } } @@ -95129,8 +95220,8 @@ function executeTemplateInstall(workspace, plan, clock = systemClock, recordId) templateId: plan.templateId, templateVersion: plan.templateVersion, manifestHash: plan.manifestHash, - sourcePath: import_path69.default.relative(workspace.rootDir, plan.sourceDir).split(import_path69.default.sep).join("/"), - installedPath: import_path69.default.relative(workspace.rootDir, plan.targetDir).split(import_path69.default.sep).join("/") + sourcePath: import_path67.default.relative(workspace.rootDir, plan.sourceDir).split(import_path67.default.sep).join("/"), + installedPath: import_path67.default.relative(workspace.rootDir, plan.targetDir).split(import_path67.default.sep).join("/") }); return { plan, installedPath: plan.targetDir, recordId: id }; } @@ -95160,10 +95251,10 @@ function planTemplateUninstall(workspace, rawReference) { { reference: rawReference } ); } - const dir = import_path69.default.join(projectTemplatesDir(workspace), reference.id); + const dir = import_path67.default.join(projectTemplatesDir(workspace), reference.id); let stat; try { - stat = (0, import_fs63.lstatSync)(dir); + stat = (0, import_fs62.lstatSync)(dir); } catch { throw new TemplateError( "SBT001", @@ -95183,18 +95274,18 @@ function planTemplateUninstall(workspace, rawReference) { return { templateId: reference.id, ref: `project:${reference.id}`, dir }; } function executeTemplateUninstall(workspace, plan, clock = systemClock, recordId) { - const tmpParent = import_path69.default.join(workspace.sidecarDir, "tmp"); - const tempDir = import_path69.default.join( + const tmpParent = import_path67.default.join(workspace.sidecarDir, "tmp"); + const tempDir = import_path67.default.join( tmpParent, `template-uninstall-${plan.templateId}-${process.pid}-${Math.random().toString(36).slice(2, 8)}` ); - (0, import_fs63.mkdirSync)(tmpParent, { recursive: true }); - (0, import_fs63.renameSync)(plan.dir, tempDir); + (0, import_fs62.mkdirSync)(tmpParent, { recursive: true }); + (0, import_fs62.renameSync)(plan.dir, tempDir); try { - (0, import_fs63.rmSync)(tempDir, { recursive: true, force: true }); + (0, import_fs62.rmSync)(tempDir, { recursive: true, force: true }); } finally { try { - (0, import_fs63.rmdirSync)(tmpParent); + (0, import_fs62.rmdirSync)(tmpParent); } catch { } } @@ -95207,7 +95298,7 @@ function executeTemplateUninstall(workspace, plan, clock = systemClock, recordId result: "ok", templateRef: plan.ref, templateId: plan.templateId, - uninstalledPath: import_path69.default.relative(workspace.rootDir, plan.dir).split(import_path69.default.sep).join("/") + uninstalledPath: import_path67.default.relative(workspace.rootDir, plan.dir).split(import_path67.default.sep).join("/") }); return { plan, recordId: id }; } @@ -95293,10 +95384,10 @@ The built-in variables \`specName\`, \`title\`, \`description\`, \`kind\`, and \`\`\`bash # From the directory containing this template pack: -specbridge template validate ./${import_path70.default.basename(request.outputPath)} +specbridge template validate ./${import_path68.default.basename(request.outputPath)} # Then install it into a project for a real preview: -specbridge template install ./${import_path70.default.basename(request.outputPath)} +specbridge template install ./${import_path68.default.basename(request.outputPath)} specbridge template preview project:${request.templateId} --name example-spec \`\`\` @@ -95516,9 +95607,9 @@ ${idCheck.problems.map((p) => ` - ${p}`).join("\n")}`, if (new Set(modes).size !== modes.length) { throw new TemplateError("SBT015", "--modes contains duplicates.", "List each mode once.", {}); } - const outputDir = import_path70.default.resolve(request.cwd, request.outputPath); - const relative = import_path70.default.relative(import_path70.default.resolve(request.cwd), outputDir); - if (relative.startsWith("..") || import_path70.default.isAbsolute(relative)) { + const outputDir = import_path68.default.resolve(request.cwd, request.outputPath); + const relative = import_path68.default.relative(import_path68.default.resolve(request.cwd), outputDir); + if (relative.startsWith("..") || import_path68.default.isAbsolute(relative)) { throw new TemplateError( "SBT007", `Scaffold output ${outputDir} is outside the current directory.`, @@ -95526,7 +95617,7 @@ ${idCheck.problems.map((p) => ` - ${p}`).join("\n")}`, { path: outputDir } ); } - if ((0, import_fs64.existsSync)(outputDir)) { + if ((0, import_fs63.existsSync)(outputDir)) { throw new TemplateError( "SBT025", `Scaffold output directory already exists: ${outputDir}.`, @@ -95558,21 +95649,21 @@ ${idCheck.problems.map((p) => ` - ${p}`).join("\n")}`, return { templateId: request.templateId, kind: request.kind, outputDir, files }; } function executeTemplateScaffold(plan, workspace, clock = systemClock, recordId) { - const tmpParent = workspace !== void 0 ? import_path70.default.join(workspace.sidecarDir, "tmp") : import_path70.default.join((0, import_os.tmpdir)(), "specbridge-scaffold"); - const tempDir = import_path70.default.join( + const tmpParent = workspace !== void 0 ? import_path68.default.join(workspace.sidecarDir, "tmp") : import_path68.default.join((0, import_os.tmpdir)(), "specbridge-scaffold"); + const tempDir = import_path68.default.join( tmpParent, `template-scaffold-${plan.templateId}-${process.pid}-${Math.random().toString(36).slice(2, 8)}` ); const writtenFiles = []; try { - (0, import_fs64.mkdirSync)(tempDir, { recursive: true }); + (0, import_fs63.mkdirSync)(tempDir, { recursive: true }); for (const [relative, content] of plan.files) { - const target = import_path70.default.join(tempDir, relative); - (0, import_fs64.mkdirSync)(import_path70.default.dirname(target), { recursive: true }); + const target = import_path68.default.join(tempDir, relative); + (0, import_fs63.mkdirSync)(import_path68.default.dirname(target), { recursive: true }); writeFileAtomic(target, content); } - (0, import_fs64.mkdirSync)(import_path70.default.dirname(plan.outputDir), { recursive: true }); - if ((0, import_fs64.existsSync)(plan.outputDir)) { + (0, import_fs63.mkdirSync)(import_path68.default.dirname(plan.outputDir), { recursive: true }); + if ((0, import_fs63.existsSync)(plan.outputDir)) { throw new TemplateError( "SBT025", `Scaffold output directory was created by another process: ${plan.outputDir}.`, @@ -95580,14 +95671,14 @@ function executeTemplateScaffold(plan, workspace, clock = systemClock, recordId) { path: plan.outputDir } ); } - (0, import_fs64.renameSync)(tempDir, plan.outputDir); + (0, import_fs63.renameSync)(tempDir, plan.outputDir); for (const relative of plan.files.keys()) { - writtenFiles.push(import_path70.default.join(plan.outputDir, relative)); + writtenFiles.push(import_path68.default.join(plan.outputDir, relative)); } } finally { - (0, import_fs64.rmSync)(tempDir, { recursive: true, force: true }); + (0, import_fs63.rmSync)(tempDir, { recursive: true, force: true }); try { - (0, import_fs64.rmdirSync)(tmpParent); + (0, import_fs63.rmdirSync)(tmpParent); } catch { } } @@ -95602,7 +95693,7 @@ function executeTemplateScaffold(plan, workspace, clock = systemClock, recordId) result: "ok", templateId: plan.templateId, kind: plan.kind, - outputPath: import_path70.default.relative(workspace.rootDir, plan.outputDir).split(import_path70.default.sep).join("/") + outputPath: import_path68.default.relative(workspace.rootDir, plan.outputDir).split(import_path68.default.sep).join("/") }); } return { plan, writtenFiles, recordId: id }; @@ -96519,26 +96610,26 @@ var TEMPLATE_PROVIDER_TEMPLATES_DIR = "templates"; var MAX_TEMPLATE_PROVIDER_PACKS = 20; // ../../packages/extensions/dist/index.js -var import_fs65 = require("fs"); -var import_path71 = __toESM(require("path"), 1); +var import_fs64 = require("fs"); +var import_path69 = __toESM(require("path"), 1); var import_crypto27 = require("crypto"); -var import_fs66 = require("fs"); -var import_path72 = __toESM(require("path"), 1); +var import_fs65 = require("fs"); +var import_path70 = __toESM(require("path"), 1); var import_child_process2 = require("child_process"); +var import_fs66 = require("fs"); +var import_path71 = __toESM(require("path"), 1); var import_fs67 = require("fs"); -var import_path73 = __toESM(require("path"), 1); +var import_path72 = __toESM(require("path"), 1); var import_fs68 = require("fs"); -var import_path74 = __toESM(require("path"), 1); +var import_path73 = __toESM(require("path"), 1); var import_fs69 = require("fs"); -var import_path75 = __toESM(require("path"), 1); +var import_path74 = __toESM(require("path"), 1); var import_fs70 = require("fs"); -var import_path76 = __toESM(require("path"), 1); +var import_path75 = __toESM(require("path"), 1); var import_fs71 = require("fs"); -var import_path77 = __toESM(require("path"), 1); +var import_path76 = __toESM(require("path"), 1); var import_fs72 = require("fs"); -var import_path78 = __toESM(require("path"), 1); -var import_fs73 = require("fs"); -var import_path79 = __toESM(require("path"), 1); +var import_path77 = __toESM(require("path"), 1); var ExtensionError = class extends SpecBridgeError { extensionCode; /** Actionable next step, always present. */ @@ -97040,7 +97131,7 @@ var FORBIDDEN_LIFECYCLE_SCRIPTS = [ "postuninstall" ]; function readExtensionPackageDirectory(dir) { - const rootStat = (0, import_fs65.lstatSync)(dir, { throwIfNoEntry: false }); + const rootStat = (0, import_fs64.lstatSync)(dir, { throwIfNoEntry: false }); if (rootStat === void 0 || !rootStat.isDirectory()) { throw new ExtensionError( "SBE008", @@ -97065,7 +97156,7 @@ function readExtensionPackageDirectory(dir) { "Flatten the package layout." ); } - for (const entry2 of (0, import_fs65.readdirSync)(currentDir, { withFileTypes: true })) { + for (const entry2 of (0, import_fs64.readdirSync)(currentDir, { withFileTypes: true })) { const relativePath = relativePrefix === "" ? entry2.name : `${relativePrefix}/${entry2.name}`; if (entry2.isSymbolicLink()) { throw new ExtensionError( @@ -97091,7 +97182,7 @@ function readExtensionPackageDirectory(dir) { "Remove the directory before validating or packaging." ); } - walk(import_path71.default.join(currentDir, entry2.name), relativePath, depth + 1); + walk(import_path69.default.join(currentDir, entry2.name), relativePath, depth + 1); continue; } if (!entry2.isFile()) { @@ -97108,7 +97199,7 @@ function readExtensionPackageDirectory(dir) { "Reduce the package contents." ); } - const content = (0, import_fs65.readFileSync)(import_path71.default.join(currentDir, entry2.name)); + const content = (0, import_fs64.readFileSync)(import_path69.default.join(currentDir, entry2.name)); totalBytes += content.length; if (totalBytes > EXTENSION_LIMITS.maxExtractedTotalBytes) { throw new ExtensionError( @@ -97383,10 +97474,10 @@ var EXTENSION_RECORDS_FILE_NAME = "records.jsonl"; var EXTENSION_STATE_SCHEMA_VERSION = "1.0.0"; var systemClock2 = () => /* @__PURE__ */ new Date(); function extensionsDir(workspace) { - return import_path72.default.join(workspace.sidecarDir, EXTENSIONS_DIR_NAME); + return import_path70.default.join(workspace.sidecarDir, EXTENSIONS_DIR_NAME); } function installedRootDir(workspace) { - return import_path72.default.join(extensionsDir(workspace), "installed"); + return import_path70.default.join(extensionsDir(workspace), "installed"); } function installedVersionDir(workspace, id, version2) { if (!validateExtensionId(id).valid || parseSemver2(version2) === void 0) { @@ -97396,7 +97487,7 @@ function installedVersionDir(workspace, id, version2) { "Use a valid extension ID and X.Y.Z version." ); } - const dir = import_path72.default.join(installedRootDir(workspace), id, version2); + const dir = import_path70.default.join(installedRootDir(workspace), id, version2); assertInsideWorkspace(workspace.rootDir, dir); return dir; } @@ -97440,12 +97531,12 @@ function emptyPermissionGrants() { return { schemaVersion: EXTENSION_STATE_SCHEMA_VERSION, grants: {} }; } function readValidatedJson(filePath, schema, empty, label) { - if (!(0, import_fs66.existsSync)(filePath)) { + if (!(0, import_fs65.existsSync)(filePath)) { return { value: empty, diagnostics: [], exists: false }; } let text15; try { - text15 = (0, import_fs66.readFileSync)(filePath, "utf8"); + text15 = (0, import_fs65.readFileSync)(filePath, "utf8"); } catch (cause) { return { value: empty, @@ -97495,13 +97586,13 @@ function readValidatedJson(filePath, schema, empty, label) { return { value: result.data, diagnostics: [], exists: true }; } function extensionStatePath(workspace) { - return import_path72.default.join(extensionsDir(workspace), EXTENSION_STATE_FILE_NAME); + return import_path70.default.join(extensionsDir(workspace), EXTENSION_STATE_FILE_NAME); } function permissionGrantsPath(workspace) { - return import_path72.default.join(extensionsDir(workspace), EXTENSION_GRANTS_FILE_NAME); + return import_path70.default.join(extensionsDir(workspace), EXTENSION_GRANTS_FILE_NAME); } function extensionRecordsPath(workspace) { - return import_path72.default.join(extensionsDir(workspace), EXTENSION_RECORDS_FILE_NAME); + return import_path70.default.join(extensionsDir(workspace), EXTENSION_RECORDS_FILE_NAME); } function readExtensionState(workspace) { const { value, diagnostics, exists } = readValidatedJson( @@ -97552,8 +97643,8 @@ function appendExtensionRecord(workspace, record5) { const filePath = extensionRecordsPath(workspace); assertInsideWorkspace(workspace.rootDir, filePath); try { - (0, import_fs66.mkdirSync)(extensionsDir(workspace), { recursive: true }); - (0, import_fs66.appendFileSync)(filePath, `${JSON.stringify(validated)} + (0, import_fs65.mkdirSync)(extensionsDir(workspace), { recursive: true }); + (0, import_fs65.appendFileSync)(filePath, `${JSON.stringify(validated)} `, "utf8"); } catch (cause) { throw ioError("append extension record to", filePath, cause); @@ -97786,9 +97877,9 @@ function resolveEntrypoint(installedDir, entrypoint) { if (problem !== void 0) { throw new ExtensionError("SBE012", `entrypoint "${entrypoint}": ${problem}.`, "Fix the extension manifest."); } - const resolved2 = import_path73.default.join(installedDir, ...entrypoint.split("/")); - const relative = import_path73.default.relative(installedDir, resolved2); - if (relative.startsWith("..") || import_path73.default.isAbsolute(relative)) { + const resolved2 = import_path71.default.join(installedDir, ...entrypoint.split("/")); + const relative = import_path71.default.relative(installedDir, resolved2); + if (relative.startsWith("..") || import_path71.default.isAbsolute(relative)) { throw new ExtensionError( "SBE012", `entrypoint "${entrypoint}" escapes the installed extension directory.`, @@ -97796,9 +97887,9 @@ function resolveEntrypoint(installedDir, entrypoint) { ); } let current = installedDir; - for (const segment of relative.split(import_path73.default.sep)) { - current = import_path73.default.join(current, segment); - const stat = (0, import_fs67.lstatSync)(current, { throwIfNoEntry: false }); + for (const segment of relative.split(import_path71.default.sep)) { + current = import_path71.default.join(current, segment); + const stat = (0, import_fs66.lstatSync)(current, { throwIfNoEntry: false }); if (stat === void 0) { throw new ExtensionError( "SBE012", @@ -97814,7 +97905,7 @@ function resolveEntrypoint(installedDir, entrypoint) { ); } } - const finalStat = (0, import_fs67.lstatSync)(resolved2, { throwIfNoEntry: false }); + const finalStat = (0, import_fs66.lstatSync)(resolved2, { throwIfNoEntry: false }); if (finalStat === void 0 || !finalStat.isFile()) { throw new ExtensionError( "SBE012", @@ -98395,14 +98486,14 @@ async function runAnalyzerExtension(workspace, extensionId, input, options = {}) } function compatibilityOf(workspace, record5, specbridgeVersion) { try { - const manifestPath = import_path74.default.join( + const manifestPath = import_path72.default.join( installedVersionDir(workspace, record5.id, record5.version), EXTENSION_MANIFEST_FILE_NAME ); - if (!(0, import_fs68.existsSync)(manifestPath)) { + if (!(0, import_fs67.existsSync)(manifestPath)) { return { compatibility: "unknown", deprecated: false }; } - const parsed = parseExtensionManifest((0, import_fs68.readFileSync)(manifestPath, "utf8")); + const parsed = parseExtensionManifest((0, import_fs67.readFileSync)(manifestPath, "utf8")); if (parsed.manifest === void 0) { return { compatibility: "unknown", deprecated: false }; } @@ -98681,8 +98772,8 @@ async function runExporterExtension(workspace, extensionId, input, options = {}) }; } function validateExportTargets(outputDir, files) { - const resolvedRoot = import_path75.default.resolve(outputDir); - const rootStat = (0, import_fs69.lstatSync)(resolvedRoot, { throwIfNoEntry: false }); + const resolvedRoot = import_path73.default.resolve(outputDir); + const rootStat = (0, import_fs68.lstatSync)(resolvedRoot, { throwIfNoEntry: false }); if (rootStat !== void 0 && rootStat.isSymbolicLink()) { throw new ExtensionError( "SBE011", @@ -98701,9 +98792,9 @@ function validateExportTargets(outputDir, files) { "Report this to the extension author; nothing was written." ); } - const target = import_path75.default.resolve(resolvedRoot, ...file.path.split("/")); - const relative = import_path75.default.relative(resolvedRoot, target); - if (relative.startsWith("..") || import_path75.default.isAbsolute(relative)) { + const target = import_path73.default.resolve(resolvedRoot, ...file.path.split("/")); + const relative = import_path73.default.relative(resolvedRoot, target); + if (relative.startsWith("..") || import_path73.default.isAbsolute(relative)) { throw new ExtensionError( "SBE030", `exporter output path "${file.path}" escapes the output directory.`, @@ -98719,9 +98810,9 @@ function validateExportTargets(outputDir, files) { } seen.add(target.toLowerCase()); let current = resolvedRoot; - for (const segment of relative.split(import_path75.default.sep)) { - current = import_path75.default.join(current, segment); - const stat = (0, import_fs69.lstatSync)(current, { throwIfNoEntry: false }); + for (const segment of relative.split(import_path73.default.sep)) { + current = import_path73.default.join(current, segment); + const stat = (0, import_fs68.lstatSync)(current, { throwIfNoEntry: false }); if (stat?.isSymbolicLink() === true) { throw new ExtensionError( "SBE011", @@ -98730,7 +98821,7 @@ function validateExportTargets(outputDir, files) { ); } } - if ((0, import_fs69.existsSync)(target)) { + if ((0, import_fs68.existsSync)(target)) { throw new ExtensionError( "SBE030", `export target "${file.path}" already exists in the output directory.`, @@ -98750,7 +98841,7 @@ function writeExportFiles(workspace, extensionId, extensionVersion, specName, ou if (target === void 0 || file === void 0) { continue; } - (0, import_fs69.mkdirSync)(import_path75.default.dirname(target.target), { recursive: true }); + (0, import_fs68.mkdirSync)(import_path73.default.dirname(target.target), { recursive: true }); writeFileAtomic(target.target, file.content); written.push(target.relative); } @@ -98837,19 +98928,19 @@ function installExtensionPackage(files, options, archiveSha256) { return { ...base, dryRun: true }; } const recordId = newExtensionRecordId(clock); - const stagingDir = import_path76.default.join(extensionsDir(workspace), `tmp-install-${recordId}`); + const stagingDir = import_path74.default.join(extensionsDir(workspace), `tmp-install-${recordId}`); assertInsideWorkspace(workspace.rootDir, stagingDir); try { for (const [name, content] of files) { - const target = import_path76.default.join(stagingDir, ...name.split("/")); + const target = import_path74.default.join(stagingDir, ...name.split("/")); assertInsideWorkspace(workspace.rootDir, target); - (0, import_fs70.mkdirSync)(import_path76.default.dirname(target), { recursive: true }); + (0, import_fs69.mkdirSync)(import_path74.default.dirname(target), { recursive: true }); writeFileAtomic(target, content); } - (0, import_fs70.mkdirSync)(import_path76.default.dirname(targetDir), { recursive: true }); - (0, import_fs70.renameSync)(stagingDir, targetDir); + (0, import_fs69.mkdirSync)(import_path74.default.dirname(targetDir), { recursive: true }); + (0, import_fs69.renameSync)(stagingDir, targetDir); } catch (cause) { - (0, import_fs70.rmSync)(stagingDir, { recursive: true, force: true }); + (0, import_fs69.rmSync)(stagingDir, { recursive: true, force: true }); if (cause instanceof ExtensionError) { throw cause; } @@ -98902,7 +98993,7 @@ function installExtensionPackage(files, options, archiveSha256) { } }); } catch (cause) { - (0, import_fs70.rmSync)(targetDir, { recursive: true, force: true }); + (0, import_fs69.rmSync)(targetDir, { recursive: true, force: true }); if (cause instanceof ExtensionError) { throw cause; } @@ -98957,8 +99048,8 @@ function buildExtensionArchive(sourceDir, options = {}) { const manifest = validation.manifest; const archive = createDeterministicZip(runtimeFiles); const archiveSha256 = sha256HexOf(archive); - const outputDir = options.outputDir ?? import_path77.default.join(sourceDir, "dist"); - const archivePath = import_path77.default.join( + const outputDir = options.outputDir ?? import_path75.default.join(sourceDir, "dist"); + const archivePath = import_path75.default.join( outputDir, `${manifest.id}-${manifest.version}${EXTENSION_ARCHIVE_SUFFIX}` ); @@ -98972,7 +99063,7 @@ function buildExtensionArchive(sourceDir, options = {}) { ); } if (options.dryRun !== true) { - (0, import_fs71.mkdirSync)(outputDir, { recursive: true }); + (0, import_fs70.mkdirSync)(outputDir, { recursive: true }); writeFileAtomic(archivePath, archive); } return { @@ -99770,7 +99861,7 @@ function scaffoldExtension(options) { ); } const outputDir = options.outputDir; - if ((0, import_fs72.existsSync)(outputDir) && (0, import_fs72.readdirSync)(outputDir).length > 0) { + if ((0, import_fs71.existsSync)(outputDir) && (0, import_fs71.readdirSync)(outputDir).length > 0) { throw new ExtensionError( "SBE030", `output directory "${outputDir}" already exists and is not empty.`, @@ -99830,8 +99921,8 @@ function scaffoldExtension(options) { }; } for (const [name, content] of files) { - const target = import_path78.default.join(outputDir, ...name.split("/")); - (0, import_fs72.mkdirSync)(import_path78.default.dirname(target), { recursive: true }); + const target = import_path76.default.join(outputDir, ...name.split("/")); + (0, import_fs71.mkdirSync)(import_path76.default.dirname(target), { recursive: true }); writeFileAtomic(target, content); } return { @@ -99944,7 +100035,7 @@ function uninstallExtension(options) { ); } const installedDir = installedVersionDir(workspace, options.id, version2); - const stat = (0, import_fs73.lstatSync)(installedDir, { throwIfNoEntry: false }); + const stat = (0, import_fs72.lstatSync)(installedDir, { throwIfNoEntry: false }); if (stat !== void 0 && stat.isSymbolicLink()) { throw new ExtensionError( "SBE011", @@ -99958,11 +100049,11 @@ function uninstallExtension(options) { const recordId = newExtensionRecordId(clock); let trashPath; if (stat !== void 0) { - const trashDir = import_path79.default.join(extensionsDir(workspace), "trash"); - trashPath = import_path79.default.join(trashDir, `${options.id}-${version2}-${recordId}`); + const trashDir = import_path77.default.join(extensionsDir(workspace), "trash"); + trashPath = import_path77.default.join(trashDir, `${options.id}-${version2}-${recordId}`); assertInsideWorkspace(workspace.rootDir, trashPath); - (0, import_fs73.mkdirSync)(trashDir, { recursive: true }); - (0, import_fs73.renameSync)(installedDir, trashPath); + (0, import_fs72.mkdirSync)(trashDir, { recursive: true }); + (0, import_fs72.renameSync)(installedDir, trashPath); } writeExtensionState(workspace, { ...state, @@ -100076,11 +100167,11 @@ function createExtensionVerifierHook(workspace, options = {}) { } // ../../packages/registry/dist/index.js -var import_fs74 = require("fs"); -var import_path80 = __toESM(require("path"), 1); +var import_fs73 = require("fs"); +var import_path78 = __toESM(require("path"), 1); var import_crypto28 = require("crypto"); -var import_fs75 = require("fs"); -var import_path81 = __toESM(require("path"), 1); +var import_fs74 = require("fs"); +var import_path79 = __toESM(require("path"), 1); var BUILTIN_REGISTRY_INDEX_JSON = '{\n "schemaVersion": "1.0.0",\n "name": "specbridge-examples",\n "updatedAt": "2026-01-01T00:00:00.000Z",\n "extensions": [\n {\n "id": "example-analyzer",\n "displayName": "example-analyzer",\n "description": "Deterministic spec diagnostics contributed by the example-analyzer analyzer extension.",\n "kind": "analyzer",\n "latestVersion": "1.0.0",\n "versions": [\n {\n "version": "1.0.0",\n "archiveUrl": "https://example.invalid/specbridge-extensions/example-analyzer-1.0.0.specbridge-extension.zip",\n "sha256": "e6e0948a315b09e53bd18997dce21888af9adbb3997fbf82955399dcf3252a19",\n "manifest": {\n "protocolVersion": "1.0.0",\n "compatibility": {\n "specbridge": ">=0.7.1 <2.0.0"\n },\n "permissions": {\n "specRead": true,\n "repositoryRead": false,\n "repositoryWrite": false,\n "network": false,\n "childProcess": false,\n "environmentVariables": []\n }\n }\n }\n ],\n "repository": "https://github.com/HelloThisWorld/specbridge",\n "license": "MIT",\n "keywords": [\n "analyzer",\n "specbridge-extension"\n ]\n },\n {\n "id": "example-exporter",\n "displayName": "example-exporter",\n "description": "Candidate export files produced by the example-exporter exporter extension.",\n "kind": "exporter",\n "latestVersion": "1.0.0",\n "versions": [\n {\n "version": "1.0.0",\n "archiveUrl": "https://example.invalid/specbridge-extensions/example-exporter-1.0.0.specbridge-extension.zip",\n "sha256": "68f42755a4e56d0e318012ec8c0e3b093e44429182ca93b02d9fb4ce2ec308a3",\n "manifest": {\n "protocolVersion": "1.0.0",\n "compatibility": {\n "specbridge": ">=0.7.1 <2.0.0"\n },\n "permissions": {\n "specRead": true,\n "repositoryRead": false,\n "repositoryWrite": false,\n "network": false,\n "childProcess": false,\n "environmentVariables": []\n }\n }\n }\n ],\n "repository": "https://github.com/HelloThisWorld/specbridge",\n "license": "MIT",\n "keywords": [\n "exporter",\n "specbridge-extension"\n ]\n },\n {\n "id": "example-runner",\n "displayName": "example-runner",\n "description": "An out-of-process runner adapter provided by the example-runner extension.",\n "kind": "runner",\n "latestVersion": "1.0.0",\n "versions": [\n {\n "version": "1.0.0",\n "archiveUrl": "https://example.invalid/specbridge-extensions/example-runner-1.0.0.specbridge-extension.zip",\n "sha256": "5ef3db937d872bfe09495695e9ecb0a3cf3beaf9e006fabdc2972ef55ace80ef",\n "manifest": {\n "protocolVersion": "1.0.0",\n "compatibility": {\n "specbridge": ">=0.7.1 <2.0.0"\n },\n "permissions": {\n "specRead": true,\n "repositoryRead": true,\n "repositoryWrite": true,\n "network": false,\n "childProcess": false,\n "environmentVariables": []\n }\n }\n }\n ],\n "repository": "https://github.com/HelloThisWorld/specbridge",\n "license": "MIT",\n "keywords": [\n "runner",\n "specbridge-extension"\n ]\n },\n {\n "id": "example-template-provider",\n "displayName": "example-template-provider",\n "description": "Spec template packs contributed by the example-template-provider template-provider extension.",\n "kind": "template-provider",\n "latestVersion": "1.0.0",\n "versions": [\n {\n "version": "1.0.0",\n "archiveUrl": "https://example.invalid/specbridge-extensions/example-template-provider-1.0.0.specbridge-extension.zip",\n "sha256": "f7caa11a13473f0891cc8d237ec4f9f2962a2dd1bd2baba4e9d01570de29044b",\n "manifest": {\n "protocolVersion": "1.0.0",\n "compatibility": {\n "specbridge": ">=0.7.1 <2.0.0"\n },\n "permissions": {\n "specRead": false,\n "repositoryRead": false,\n "repositoryWrite": false,\n "network": false,\n "childProcess": false,\n "environmentVariables": []\n }\n }\n }\n ],\n "repository": "https://github.com/HelloThisWorld/specbridge",\n "license": "MIT",\n "keywords": [\n "template-provider",\n "specbridge-extension"\n ]\n },\n {\n "id": "example-verifier",\n "displayName": "example-verifier",\n "description": "Verification diagnostics contributed by the example-verifier verifier extension.",\n "kind": "verifier",\n "latestVersion": "1.0.0",\n "versions": [\n {\n "version": "1.0.0",\n "archiveUrl": "https://example.invalid/specbridge-extensions/example-verifier-1.0.0.specbridge-extension.zip",\n "sha256": "d531c9078fcbeef6573a95773eefafd409d798bac1223c83748e0229ae0225bf",\n "manifest": {\n "protocolVersion": "1.0.0",\n "compatibility": {\n "specbridge": ">=0.7.1 <2.0.0"\n },\n "permissions": {\n "specRead": true,\n "repositoryRead": false,\n "repositoryWrite": false,\n "network": false,\n "childProcess": false,\n "environmentVariables": []\n }\n }\n }\n ],\n "repository": "https://github.com/HelloThisWorld/specbridge",\n "license": "MIT",\n "keywords": [\n "verifier",\n "specbridge-extension"\n ]\n }\n ]\n}\n'; var REGISTRY_ERROR_CODES = { SBR001: "registry not found", @@ -100237,20 +100328,20 @@ var cachedRegistrySchema = external_exports.object({ index: registryIndexSchema }).passthrough(); function registryCacheDir(workspace) { - return import_path80.default.join(workspace.sidecarDir, REGISTRY_CACHE_DIR_NAME); + return import_path78.default.join(workspace.sidecarDir, REGISTRY_CACHE_DIR_NAME); } function registryCachePath(workspace, name) { - const target = import_path80.default.join(registryCacheDir(workspace), `${name}.json`); + const target = import_path78.default.join(registryCacheDir(workspace), `${name}.json`); assertInsideWorkspace(workspace.rootDir, target); return target; } function readRegistryCache(workspace, name) { const filePath = registryCachePath(workspace, name); - if (!(0, import_fs74.existsSync)(filePath)) { + if (!(0, import_fs73.existsSync)(filePath)) { return { diagnostics: [] }; } try { - const parsed = cachedRegistrySchema.safeParse(JSON.parse((0, import_fs74.readFileSync)(filePath, "utf8"))); + const parsed = cachedRegistrySchema.safeParse(JSON.parse((0, import_fs73.readFileSync)(filePath, "utf8"))); if (!parsed.success) { return { diagnostics: [ @@ -100303,9 +100394,9 @@ function resolveRegistryIndex(workspace, source) { return { sourceName: source.name, index: parsed.index, origin: "builtin", diagnostics: [] }; } if (source.type === "local-file") { - const filePath = import_path80.default.resolve(workspace.rootDir, source.file); + const filePath = import_path78.default.resolve(workspace.rootDir, source.file); assertInsideWorkspace(workspace.rootDir, filePath); - if (!(0, import_fs74.existsSync)(filePath)) { + if (!(0, import_fs73.existsSync)(filePath)) { return { sourceName: source.name, index: { schemaVersion: "1.0.0", name: source.name, updatedAt: "unknown", extensions: [] }, @@ -100320,7 +100411,7 @@ function resolveRegistryIndex(workspace, source) { ] }; } - const text15 = (0, import_fs74.readFileSync)(filePath, "utf8"); + const text15 = (0, import_fs73.readFileSync)(filePath, "utf8"); const parsed = parseRegistryIndex(text15); if (parsed.index === void 0) { throw new RegistryError( @@ -100563,7 +100654,7 @@ var registriesConfigSchema = external_exports.object({ registries: external_exports.array(registrySourceSchema).max(20) }).passthrough(); function registriesConfigPath(workspace) { - return import_path81.default.join(workspace.sidecarDir, REGISTRIES_FILE_NAME); + return import_path79.default.join(workspace.sidecarDir, REGISTRIES_FILE_NAME); } function defaultRegistriesConfig() { return { @@ -100573,12 +100664,12 @@ function defaultRegistriesConfig() { } function readRegistriesConfig(workspace) { const filePath = registriesConfigPath(workspace); - if (!(0, import_fs75.existsSync)(filePath)) { + if (!(0, import_fs74.existsSync)(filePath)) { return { config: defaultRegistriesConfig(), diagnostics: [], exists: false }; } let parsed; try { - parsed = JSON.parse((0, import_fs75.readFileSync)(filePath, "utf8")); + parsed = JSON.parse((0, import_fs74.readFileSync)(filePath, "utf8")); } catch (cause) { return { config: defaultRegistriesConfig(), @@ -103195,30 +103286,30 @@ var import_node_fs9 = require("fs"); // ../../packages/intake/dist/index.js var import_crypto30 = require("crypto"); +var import_fs81 = require("fs"); +var import_path89 = __toESM(require("path"), 1); var import_fs82 = require("fs"); -var import_path91 = __toESM(require("path"), 1); -var import_fs83 = require("fs"); -var import_path92 = __toESM(require("path"), 1); +var import_path90 = __toESM(require("path"), 1); // ../../packages/autonomy/dist/index.js var import_crypto29 = require("crypto"); +var import_fs75 = require("fs"); +var import_path80 = __toESM(require("path"), 1); +var import_os2 = __toESM(require("os"), 1); var import_fs76 = require("fs"); +var import_path81 = __toESM(require("path"), 1); var import_path82 = __toESM(require("path"), 1); -var import_os2 = __toESM(require("os"), 1); -var import_fs77 = require("fs"); var import_path83 = __toESM(require("path"), 1); +var import_net2 = require("net"); +var import_fs77 = require("fs"); var import_path84 = __toESM(require("path"), 1); var import_path85 = __toESM(require("path"), 1); -var import_net2 = require("net"); var import_fs78 = require("fs"); var import_path86 = __toESM(require("path"), 1); -var import_path87 = __toESM(require("path"), 1); var import_fs79 = require("fs"); -var import_path88 = __toESM(require("path"), 1); +var import_path87 = __toESM(require("path"), 1); var import_fs80 = require("fs"); -var import_path89 = __toESM(require("path"), 1); -var import_fs81 = require("fs"); -var import_path90 = __toESM(require("path"), 1); +var import_path88 = __toESM(require("path"), 1); var SEAL_STATUSES = [ /** Drafted from mission state; not yet authorized by a human. */ "DRAFT", @@ -103740,43 +103831,43 @@ function assertAutonomyId(kind, id) { function autonomyDir(workspace) { return assertInsideWorkspace( workspace.rootDir, - import_path82.default.join(workspace.rootDir, ".specbridge", "autonomy") + import_path80.default.join(workspace.rootDir, ".specbridge", "autonomy") ); } function autonomyPath(workspace, ...segments) { - return assertInsideWorkspace(workspace.rootDir, import_path82.default.join(autonomyDir(workspace), ...segments)); + return assertInsideWorkspace(workspace.rootDir, import_path80.default.join(autonomyDir(workspace), ...segments)); } function writeJsonRecord(file, value) { - (0, import_fs76.mkdirSync)(import_path82.default.dirname(file), { recursive: true }); + (0, import_fs75.mkdirSync)(import_path80.default.dirname(file), { recursive: true }); writeFileAtomic(file, `${JSON.stringify(value, null, 2)} `); } function readJsonRecord(file, parse3) { - if (!(0, import_fs76.existsSync)(file)) return void 0; + if (!(0, import_fs75.existsSync)(file)) return void 0; try { - return parse3(JSON.parse((0, import_fs76.readFileSync)(file, "utf8"))); + return parse3(JSON.parse((0, import_fs75.readFileSync)(file, "utf8"))); } catch { return void 0; } } function listJsonRecords(dir, parse3) { - if (!(0, import_fs76.existsSync)(dir)) return []; + if (!(0, import_fs75.existsSync)(dir)) return []; const out = []; - for (const entry2 of (0, import_fs76.readdirSync)(dir).sort()) { + for (const entry2 of (0, import_fs75.readdirSync)(dir).sort()) { if (!entry2.endsWith(".json")) continue; - const value = readJsonRecord(import_path82.default.join(dir, entry2), parse3); + const value = readJsonRecord(import_path80.default.join(dir, entry2), parse3); if (value !== void 0) out.push(value); } return out; } function appendJsonl2(file, value) { - (0, import_fs76.mkdirSync)(import_path82.default.dirname(file), { recursive: true }); - (0, import_fs76.appendFileSync)(file, `${JSON.stringify(value)} + (0, import_fs75.mkdirSync)(import_path80.default.dirname(file), { recursive: true }); + (0, import_fs75.appendFileSync)(file, `${JSON.stringify(value)} `, "utf8"); } function readJsonl2(file, parse3, limit = 5e3) { - if (!(0, import_fs76.existsSync)(file)) return { entries: [], skipped: 0 }; - const lines = (0, import_fs76.readFileSync)(file, "utf8").split("\n").filter((line) => line.trim().length > 0); + if (!(0, import_fs75.existsSync)(file)) return { entries: [], skipped: 0 }; + const lines = (0, import_fs75.readFileSync)(file, "utf8").split("\n").filter((line) => line.trim().length > 0); const slice = lines.slice(-limit); const entries = []; let skipped = 0; @@ -103790,12 +103881,12 @@ function readJsonl2(file, parse3, limit = 5e3) { return { entries, skipped }; } function writeImmutableRecord(file, value, kind) { - if ((0, import_fs76.existsSync)(file)) { + if ((0, import_fs75.existsSync)(file)) { throw new AutonomyError("SBA024", `A ${kind} already exists at this identity and is immutable.`, { remediation: [ `Create a new ${kind} that supersedes the existing one instead of rewriting history.` ], - details: { file: import_path82.default.basename(file), kind } + details: { file: import_path80.default.basename(file), kind } }); } writeJsonRecord(file, value); @@ -105364,7 +105455,7 @@ function createProcessProbeRunner(cwd) { } function isWritableDirectory(dir) { try { - (0, import_fs77.accessSync)(dir, import_fs77.constants.W_OK); + (0, import_fs76.accessSync)(dir, import_fs76.constants.W_OK); return true; } catch { return false; @@ -105372,7 +105463,7 @@ function isWritableDirectory(dir) { } function freeDiskBytes(target) { try { - const stats = (0, import_fs77.statfsSync)(target); + const stats = (0, import_fs76.statfsSync)(target); return Number(stats.bavail) * Number(stats.bsize); } catch { return null; @@ -105380,7 +105471,7 @@ function freeDiskBytes(target) { } function pathExists(target) { try { - return (0, import_fs77.existsSync)(target); + return (0, import_fs76.existsSync)(target); } catch { return false; } @@ -105418,10 +105509,10 @@ async function probeCompose(run) { }; } function detectPackageManager(projectDir) { - const manifest = import_path83.default.join(projectDir, "package.json"); + const manifest = import_path81.default.join(projectDir, "package.json"); if (pathExists(manifest)) { try { - const raw = JSON.parse((0, import_fs77.readFileSync)(manifest, "utf8")); + const raw = JSON.parse((0, import_fs76.readFileSync)(manifest, "utf8")); if (typeof raw.packageManager === "string" && raw.packageManager.length > 0) { return raw.packageManager.split("@")[0] ?? null; } @@ -105434,7 +105525,7 @@ function detectPackageManager(projectDir) { ["package-lock.json", "npm"], ["bun.lockb", "bun"] ]) { - if (pathExists(import_path83.default.join(projectDir, lockfile))) return manager; + if (pathExists(import_path81.default.join(projectDir, lockfile))) return manager; } return null; } @@ -105448,7 +105539,7 @@ function detectBuildTool(projectDir) { ["Cargo.toml", "cargo"], ["go.mod", "go"] ]) { - if (pathExists(import_path83.default.join(projectDir, marker))) return tool; + if (pathExists(import_path81.default.join(projectDir, marker))) return tool; } return null; } @@ -105772,7 +105863,7 @@ function assertOvernightReady(report) { { remediation: [ ...report.checks.filter((check6) => check6.outcome === "HUMAN_REQUIRED" || check6.outcome === "UNKNOWN").flatMap((check6) => check6.remediation).slice(0, 10), - `Full report: ${import_path84.default.posix.join(".specbridge", "autonomy", "preflight", `${report.reportId}.json`)}` + `Full report: ${import_path82.default.posix.join(".specbridge", "autonomy", "preflight", `${report.reportId}.json`)}` ], details: { verdict: report.verdict, reportId: report.reportId } } @@ -105933,19 +106024,19 @@ function decideToolsmithRequest(request, context) { }; } function assertInsideWorkspaceBoundary(target, context) { - if (import_path85.default.isAbsolute(target)) { - const resolved2 = import_path85.default.resolve(target); - const root = import_path85.default.resolve(context.workspaceRoot); - if (resolved2 !== root && !resolved2.startsWith(root + import_path85.default.sep)) { + if (import_path83.default.isAbsolute(target)) { + const resolved2 = import_path83.default.resolve(target); + const root = import_path83.default.resolve(context.workspaceRoot); + if (resolved2 !== root && !resolved2.startsWith(root + import_path83.default.sep)) { return { granted: false, reason: "TARGET_OUTSIDE_WORKSPACE", detail: `"${target}" is outside the workspace. Project tooling lives in the project.` }; } - return matchesProtected(import_path85.default.relative(root, resolved2), context); + return matchesProtected(import_path83.default.relative(root, resolved2), context); } - const normalized = import_path85.default.normalize(target).replace(/\\/g, "/"); + const normalized = import_path83.default.normalize(target).replace(/\\/g, "/"); if (normalized.startsWith("../") || normalized === "..") { return { granted: false, @@ -106625,7 +106716,7 @@ async function finishFailed(deps4, options, plan, instance, failure3) { return failed; } function retainLog(deps4, instanceId, serviceId, text142) { - const relative = import_path86.default.posix.join( + const relative = import_path84.default.posix.join( ".specbridge", "autonomy", "environments", @@ -106634,8 +106725,8 @@ function retainLog(deps4, instanceId, serviceId, text142) { `${serviceId}.log` ); const absolute = autonomyPath(deps4.workspace, "environments", "logs", instanceId, `${serviceId}.log`); - (0, import_fs78.mkdirSync)(import_path86.default.dirname(absolute), { recursive: true }); - (0, import_fs78.writeFileSync)(absolute, text142, "utf8"); + (0, import_fs77.mkdirSync)(import_path84.default.dirname(absolute), { recursive: true }); + (0, import_fs77.writeFileSync)(absolute, text142, "utf8"); return relative; } async function teardownEnvironment(deps4, input) { @@ -106714,7 +106805,7 @@ function createComposeRuntime(options) { const composeArgs = (plan, rest) => { const args = ["compose"]; if (plan.composeFile !== void 0) { - args.push("-f", import_path87.default.resolve(options.cwd, plan.composeFile)); + args.push("-f", import_path85.default.resolve(options.cwd, plan.composeFile)); } args.push("--project-name", plan.projectName ?? plan.planId); args.push(...rest); @@ -107120,9 +107211,9 @@ function writeEvidenceFile(deps4, resultId, name, extension, data) { resultId, `${safe}.${extension}` ); - (0, import_fs79.mkdirSync)(import_path88.default.dirname(absolute), { recursive: true }); - (0, import_fs79.writeFileSync)(absolute, data); - return import_path88.default.posix.join( + (0, import_fs78.mkdirSync)(import_path86.default.dirname(absolute), { recursive: true }); + (0, import_fs78.writeFileSync)(absolute, data); + return import_path86.default.posix.join( ".specbridge", "autonomy", "browser", @@ -108644,7 +108735,7 @@ async function runReproducibilityPhase(deps4, options) { } const runId = newRecordId(deps4, "rp"); const checkoutPath = autonomyPath(deps4.workspace, "reproducibility", "checkouts", runId); - (0, import_fs80.mkdirSync)(import_path89.default.dirname(checkoutPath), { recursive: true }); + (0, import_fs79.mkdirSync)(import_path87.default.dirname(checkoutPath), { recursive: true }); const head = await runSafeProcess({ executable: "git", argv: ["rev-parse", "HEAD"], @@ -108712,9 +108803,9 @@ async function runReproducibilityPhase(deps4, options) { } function detectNodeInstaller(workspace) { const root = workspace.rootDir; - if ((0, import_fs80.existsSync)(import_path89.default.join(root, "pnpm-lock.yaml"))) return ["pnpm", "install", "--frozen-lockfile"]; - if ((0, import_fs80.existsSync)(import_path89.default.join(root, "package-lock.json"))) return ["npm", "ci"]; - if ((0, import_fs80.existsSync)(import_path89.default.join(root, "yarn.lock"))) return ["yarn", "install", "--frozen-lockfile"]; + if ((0, import_fs79.existsSync)(import_path87.default.join(root, "pnpm-lock.yaml"))) return ["pnpm", "install", "--frozen-lockfile"]; + if ((0, import_fs79.existsSync)(import_path87.default.join(root, "package-lock.json"))) return ["npm", "ci"]; + if ((0, import_fs79.existsSync)(import_path87.default.join(root, "yarn.lock"))) return ["yarn", "install", "--frozen-lockfile"]; return void 0; } async function removeCheckout(workspace, checkoutPath) { @@ -108796,12 +108887,12 @@ async function runGapRepairs(deps4, options) { fail(`the trusted suite failed in the repair worktree: ${verification.requiredFailed.join(", ").slice(0, 200)}`); continue; } - const patchFile = import_path89.default.join( + const patchFile = import_path87.default.join( autonomyPath(deps4.workspace, "closure", options.jobId, "scratch", item.gapId), "repair.patch" ); - (0, import_fs80.mkdirSync)(import_path89.default.dirname(patchFile), { recursive: true }); - (0, import_fs80.writeFileSync)(patchFile, collected.patch, "utf8"); + (0, import_fs79.mkdirSync)(import_path87.default.dirname(patchFile), { recursive: true }); + (0, import_fs79.writeFileSync)(patchFile, collected.patch, "utf8"); const applied = await runSafeProcess({ executable: "git", argv: ["apply", "--3way", patchFile], @@ -110439,13 +110530,13 @@ function executionTelemetryReportFile(workspace, jobId) { } return assertInsideWorkspace( workspace.rootDir, - import_path90.default.join(workspace.sidecarDir, "reports", `job-${jobId}-telemetry.json`) + import_path88.default.join(workspace.sidecarDir, "reports", `job-${jobId}-telemetry.json`) ); } function persistExecutionTelemetryReport(workspace, report) { const validated = executionTelemetryReportSchema.parse(report); const file = executionTelemetryReportFile(workspace, validated.jobId); - (0, import_fs81.mkdirSync)(import_path90.default.dirname(file), { recursive: true }); + (0, import_fs80.mkdirSync)(import_path88.default.dirname(file), { recursive: true }); writeFileAtomic(file, `${JSON.stringify(validated, null, 2)} `); return file; @@ -111109,18 +111200,18 @@ function listCertificationRuns(workspace) { } // ../../packages/intake/dist/index.js +var import_fs83 = require("fs"); +var import_path91 = __toESM(require("path"), 1); var import_fs84 = require("fs"); -var import_path93 = __toESM(require("path"), 1); +var import_path92 = __toESM(require("path"), 1); var import_fs85 = require("fs"); -var import_path94 = __toESM(require("path"), 1); +var import_path93 = __toESM(require("path"), 1); var import_fs86 = require("fs"); -var import_path95 = __toESM(require("path"), 1); +var import_path94 = __toESM(require("path"), 1); var import_fs87 = require("fs"); -var import_path96 = __toESM(require("path"), 1); +var import_path95 = __toESM(require("path"), 1); var import_fs88 = require("fs"); -var import_path97 = __toESM(require("path"), 1); -var import_fs89 = require("fs"); -var import_path98 = __toESM(require("path"), 1); +var import_path96 = __toESM(require("path"), 1); var INTAKE_STATUSES = [ /** The source specification is ingested; discovery has not run. */ "INGESTED", @@ -111845,41 +111936,41 @@ function assertIntakeId(id) { function intakeRootDir(workspace) { return assertInsideWorkspace( workspace.rootDir, - import_path91.default.join(workspace.rootDir, ".specbridge", INTAKE_DIR_NAME) + import_path89.default.join(workspace.rootDir, ".specbridge", INTAKE_DIR_NAME) ); } function intakeDir(workspace, intakeId) { assertIntakeId(intakeId); - return assertInsideWorkspace(workspace.rootDir, import_path91.default.join(intakeRootDir(workspace), intakeId)); + return assertInsideWorkspace(workspace.rootDir, import_path89.default.join(intakeRootDir(workspace), intakeId)); } function intakePath(workspace, intakeId, ...segments) { return assertInsideWorkspace( workspace.rootDir, - import_path91.default.join(intakeDir(workspace, intakeId), ...segments) + import_path89.default.join(intakeDir(workspace, intakeId), ...segments) ); } function writeJson(file, value) { - (0, import_fs82.mkdirSync)(import_path91.default.dirname(file), { recursive: true }); + (0, import_fs81.mkdirSync)(import_path89.default.dirname(file), { recursive: true }); writeFileAtomic(file, `${JSON.stringify(value, null, 2)} `); } function readJson2(file, parse3) { - if (!(0, import_fs82.existsSync)(file)) return void 0; + if (!(0, import_fs81.existsSync)(file)) return void 0; try { - return parse3(JSON.parse((0, import_fs82.readFileSync)(file, "utf8"))); + return parse3(JSON.parse((0, import_fs81.readFileSync)(file, "utf8"))); } catch { return void 0; } } function appendJsonl3(file, value) { - (0, import_fs82.mkdirSync)(import_path91.default.dirname(file), { recursive: true }); - (0, import_fs82.appendFileSync)(file, `${JSON.stringify(value)} + (0, import_fs81.mkdirSync)(import_path89.default.dirname(file), { recursive: true }); + (0, import_fs81.appendFileSync)(file, `${JSON.stringify(value)} `, "utf8"); } function readFolded(file, key, parse3) { - if (!(0, import_fs82.existsSync)(file)) return []; + if (!(0, import_fs81.existsSync)(file)) return []; const folded = /* @__PURE__ */ new Map(); - for (const line of (0, import_fs82.readFileSync)(file, "utf8").split("\n")) { + for (const line of (0, import_fs81.readFileSync)(file, "utf8").split("\n")) { if (line.trim().length === 0) continue; try { const value = parse3(JSON.parse(line)); @@ -111912,16 +112003,16 @@ function writeIntakeState(workspace, state) { } function listIntakes(workspace) { const root = intakeRootDir(workspace); - if (!(0, import_fs82.existsSync)(root)) return { intakes: [], diagnostics: [] }; + if (!(0, import_fs81.existsSync)(root)) return { intakes: [], diagnostics: [] }; const intakes = []; const diagnostics = []; - for (const entry2 of (0, import_fs82.readdirSync)(root, { withFileTypes: true })) { + for (const entry2 of (0, import_fs81.readdirSync)(root, { withFileTypes: true })) { if (!entry2.isDirectory()) continue; if (!ID_PATTERN11.test(entry2.name)) continue; - const file = import_path91.default.join(root, entry2.name, "intake.json"); - if (!(0, import_fs82.existsSync)(file)) continue; + const file = import_path89.default.join(root, entry2.name, "intake.json"); + if (!(0, import_fs81.existsSync)(file)) continue; try { - intakes.push(specIntakeStateSchema.parse(JSON.parse((0, import_fs82.readFileSync)(file, "utf8")))); + intakes.push(specIntakeStateSchema.parse(JSON.parse((0, import_fs81.readFileSync)(file, "utf8")))); } catch (cause) { diagnostics.push({ intakeId: entry2.name, @@ -111951,8 +112042,8 @@ function sourceFile(workspace, intakeId, contentHash) { } function storeSourceText(workspace, intakeId, contentHash, content) { const file = sourceFile(workspace, intakeId, contentHash); - if (!(0, import_fs82.existsSync)(file)) { - (0, import_fs82.mkdirSync)(import_path91.default.dirname(file), { recursive: true }); + if (!(0, import_fs81.existsSync)(file)) { + (0, import_fs81.mkdirSync)(import_path89.default.dirname(file), { recursive: true }); writeFileAtomic(file, content); } return file; @@ -112037,7 +112128,7 @@ function approvalFile2(workspace, intakeId) { function writeApproval(workspace, approval) { const validated = intakeApprovalSchema.parse(approval); const file = approvalFile2(workspace, validated.intakeId); - if ((0, import_fs82.existsSync)(file)) { + if ((0, import_fs81.existsSync)(file)) { throw new IntakeError( "SBI017", `Spec intake "${validated.intakeId}" is already approved; an approval is immutable.`, @@ -112096,7 +112187,7 @@ function appendIntakeEvent(workspace, intakeId, event) { function baselineFile(workspace) { return assertInsideWorkspace( workspace.rootDir, - import_path91.default.join(intakeRootDir(workspace), "baseline.json") + import_path89.default.join(intakeRootDir(workspace), "baseline.json") ); } function readProductBaseline(workspace) { @@ -112711,7 +112802,7 @@ var BUILD_MARKERS = [ ]; function detectBuildSystem(rootDir) { for (const marker of BUILD_MARKERS) { - if ((0, import_fs83.existsSync)(import_path92.default.join(rootDir, marker.file))) return marker.system; + if ((0, import_fs82.existsSync)(import_path90.default.join(rootDir, marker.file))) return marker.system; } return null; } @@ -112748,33 +112839,33 @@ var PUBLIC_INTERFACE_PATTERNS = [ var TEST_DIR_PATTERN = /^(tests?|spec|specs|__tests__|it|integration-tests?|e2e)$/i; function readGitHead(rootDir) { try { - const dotGit = import_path92.default.join(rootDir, ".git"); - if (!(0, import_fs83.existsSync)(dotGit)) return null; + const dotGit = import_path90.default.join(rootDir, ".git"); + if (!(0, import_fs82.existsSync)(dotGit)) return null; let gitDir = dotGit; - if ((0, import_fs83.statSync)(dotGit).isFile()) { - const pointer = (0, import_fs83.readFileSync)(dotGit, "utf8").trim(); + if ((0, import_fs82.statSync)(dotGit).isFile()) { + const pointer = (0, import_fs82.readFileSync)(dotGit, "utf8").trim(); const match = /^gitdir:\s*(.+)$/.exec(pointer); if (match === null) return null; const target = match[1] ?? ""; - gitDir = import_path92.default.isAbsolute(target) ? target : import_path92.default.resolve(rootDir, target); + gitDir = import_path90.default.isAbsolute(target) ? target : import_path90.default.resolve(rootDir, target); } - const headFile = import_path92.default.join(gitDir, "HEAD"); - if (!(0, import_fs83.existsSync)(headFile)) return null; - const head = (0, import_fs83.readFileSync)(headFile, "utf8").trim(); + const headFile = import_path90.default.join(gitDir, "HEAD"); + if (!(0, import_fs82.existsSync)(headFile)) return null; + const head = (0, import_fs82.readFileSync)(headFile, "utf8").trim(); if (/^[0-9a-f]{40}$/i.test(head)) return head.toLowerCase(); const refMatch = /^ref:\s*(.+)$/.exec(head); if (refMatch === null) return null; const ref = (refMatch[1] ?? "").trim(); for (const dir of refDirsFor(gitDir)) { - const refFile = import_path92.default.join(dir, ...ref.split("/")); - if (!(0, import_fs83.existsSync)(refFile)) continue; - const sha = (0, import_fs83.readFileSync)(refFile, "utf8").trim(); + const refFile = import_path90.default.join(dir, ...ref.split("/")); + if (!(0, import_fs82.existsSync)(refFile)) continue; + const sha = (0, import_fs82.readFileSync)(refFile, "utf8").trim(); if (/^[0-9a-f]{40}$/i.test(sha)) return sha.toLowerCase(); } for (const dir of refDirsFor(gitDir)) { - const packed = import_path92.default.join(dir, "packed-refs"); - if (!(0, import_fs83.existsSync)(packed)) continue; - for (const line of (0, import_fs83.readFileSync)(packed, "utf8").split("\n")) { + const packed = import_path90.default.join(dir, "packed-refs"); + if (!(0, import_fs82.existsSync)(packed)) continue; + for (const line of (0, import_fs82.readFileSync)(packed, "utf8").split("\n")) { const entry2 = /^([0-9a-f]{40})\s+(.+)$/.exec(line.trim()); if (entry2 !== null && entry2[2] === ref) return (entry2[1] ?? "").toLowerCase(); } @@ -112786,12 +112877,12 @@ function readGitHead(rootDir) { } function refDirsFor(gitDir) { const dirs = [gitDir]; - const commonFile = import_path92.default.join(gitDir, "commondir"); - if ((0, import_fs83.existsSync)(commonFile)) { + const commonFile = import_path90.default.join(gitDir, "commondir"); + if ((0, import_fs82.existsSync)(commonFile)) { try { - const target = (0, import_fs83.readFileSync)(commonFile, "utf8").trim(); + const target = (0, import_fs82.readFileSync)(commonFile, "utf8").trim(); if (target.length > 0) { - dirs.push(import_path92.default.isAbsolute(target) ? target : import_path92.default.resolve(gitDir, target)); + dirs.push(import_path90.default.isAbsolute(target) ? target : import_path90.default.resolve(gitDir, target)); } } catch { } @@ -112843,7 +112934,7 @@ function groundInRepository(deps4, request) { summary: `existing Kiro spec with ${folder.files.length} document(s)`, authoritative: false, topics: [], - path: import_path92.default.posix.join(".kiro", "specs", folder.name) + path: import_path90.default.posix.join(".kiro", "specs", folder.name) }); } for (const steering of safeSteering(workspace, notes)) { @@ -112854,7 +112945,7 @@ function groundInRepository(deps4, request) { summary: `steering document (${steering.inclusion})`, authoritative: false, topics: [], - path: import_path92.default.posix.join(".kiro", "steering", steering.fileName) + path: import_path90.default.posix.join(".kiro", "steering", steering.fileName) }); } const buildSystem = detectBuildSystem(workspace.rootDir); @@ -112898,7 +112989,7 @@ function groundInRepository(deps4, request) { }); } for (const container of modules.slice(0, 40)) { - const dir = import_path92.default.join(workspace.rootDir, container); + const dir = import_path90.default.join(workspace.rootDir, container); for (const entry2 of safeReaddir(dir, notes)) { if (!entry2.isDirectory()) continue; if (MODULE_DENYLIST.has(entry2.name) || entry2.name.startsWith(".")) continue; @@ -113050,7 +113141,7 @@ function safeSteering(workspace, notes) { } function safeReaddir(dir, notes) { try { - return (0, import_fs83.readdirSync)(dir, { withFileTypes: true }); + return (0, import_fs82.readdirSync)(dir, { withFileTypes: true }); } catch (cause) { notes.push(`Directory ${dir} could not be listed: ${message(cause)}.`); return []; @@ -113863,14 +113954,14 @@ function emptyProjectionMap() { function mapFile(workspace, intakeId) { return assertInsideWorkspace( workspace.rootDir, - import_path93.default.join(workspace.rootDir, ".specbridge", "intake", intakeId, "mission-map.json") + import_path91.default.join(workspace.rootDir, ".specbridge", "intake", intakeId, "mission-map.json") ); } function readProjectionMap(workspace, intakeId) { const file = mapFile(workspace, intakeId); - if (!(0, import_fs84.existsSync)(file)) return emptyProjectionMap(); + if (!(0, import_fs83.existsSync)(file)) return emptyProjectionMap(); try { - const raw = JSON.parse((0, import_fs84.readFileSync)(file, "utf8")); + const raw = JSON.parse((0, import_fs83.readFileSync)(file, "utf8")); return { itemContracts: raw.itemContracts ?? {}, itemDecisions: raw.itemDecisions ?? {}, @@ -113886,7 +113977,7 @@ function readProjectionMap(workspace, intakeId) { } function writeProjectionMap(workspace, intakeId, map) { const file = mapFile(workspace, intakeId); - (0, import_fs84.mkdirSync)(import_path93.default.dirname(file), { recursive: true }); + (0, import_fs83.mkdirSync)(import_path91.default.dirname(file), { recursive: true }); writeFileAtomic(file, `${JSON.stringify(map, null, 2)} `); } @@ -114546,8 +114637,8 @@ function checkProjectionEquivalence(request) { let checked = 0; let traced = 0; for (const stage of stages) { - const file = import_path94.default.join(folder.dir, `${stage}.md`); - if (!(0, import_fs85.existsSync)(file)) { + const file = import_path92.default.join(folder.dir, `${stage}.md`); + if (!(0, import_fs84.existsSync)(file)) { divergences.push({ kind: "UNRELATED_ARTIFACT", stage, @@ -114555,7 +114646,7 @@ function checkProjectionEquivalence(request) { }); continue; } - const content = (0, import_fs85.readFileSync)(file, "utf8"); + const content = (0, import_fs84.readFileSync)(file, "utf8"); artifactHashes[stage] = sha256Hex(content); for (const statement of extractNormativeStatements(stage, content)) { checked += 1; @@ -115298,7 +115389,7 @@ function startSpecIntake(deps4, request) { receivedVia: hostOf2(deps4), byteLength, contentHash, - storedAt: import_path95.default.posix.join( + storedAt: import_path93.default.posix.join( ".specbridge", "intake", intakeId, @@ -115354,20 +115445,20 @@ function startSpecIntake(deps4, request) { return { intake, source, mission }; } function startSpecIntakeFromFile(deps4, request) { - const resolved2 = import_path95.default.resolve(request.file); - if (!(0, import_fs86.existsSync)(resolved2)) { + const resolved2 = import_path93.default.resolve(request.file); + if (!(0, import_fs85.existsSync)(resolved2)) { throw new IntakeError("SBI007", `No specification file at ${request.file}.`, { remediation: ["Check the path, or pass the specification text with --text."] }); } - const size = (0, import_fs86.statSync)(resolved2).size; + const size = (0, import_fs85.statSync)(resolved2).size; if (size > INTAKE_LIMITS.maxSourceBytes) { throw new IntakeError( "SBI006", `${request.file} is ${size} bytes, over the ${INTAKE_LIMITS.maxSourceBytes}-byte bound.` ); } - const content = (0, import_fs86.readFileSync)(resolved2, "utf8"); + const content = (0, import_fs85.readFileSync)(resolved2, "utf8"); return startSpecIntake(deps4, { ...request, kind: "file", @@ -116092,7 +116183,7 @@ var repositoryManifestSchema = external_exports.object({ function repositoryManifestFile(workspace) { return assertInsideWorkspace( workspace.rootDir, - import_path96.default.join(workspace.sidecarDir, "repositories.json") + import_path94.default.join(workspace.sidecarDir, "repositories.json") ); } var DETECTION_DENYLIST = /* @__PURE__ */ new Set([ @@ -116109,10 +116200,10 @@ var DETECTION_DENYLIST = /* @__PURE__ */ new Set([ ]); function readRepositoryManifest(workspace) { const file = repositoryManifestFile(workspace); - if (!(0, import_fs87.existsSync)(file)) return void 0; + if (!(0, import_fs86.existsSync)(file)) return void 0; let raw; try { - raw = JSON.parse((0, import_fs87.readFileSync)(file, "utf8")); + raw = JSON.parse((0, import_fs86.readFileSync)(file, "utf8")); } catch (cause) { throw new IntakeError("SBI018", `The repository manifest at ${file} is not valid JSON.`, { remediation: ["Fix or delete .specbridge/repositories.json; without it the workspace root is the repository."], @@ -116132,7 +116223,7 @@ function resolveRepositories(workspace) { } seen.add(entry2.id); const absDir = assertInsideWorkspace(workspace.rootDir, entry2.path); - if (!(0, import_fs87.existsSync)(absDir) || !(0, import_fs87.statSync)(absDir).isDirectory()) { + if (!(0, import_fs86.existsSync)(absDir) || !(0, import_fs86.statSync)(absDir).isDirectory()) { throw new IntakeError( "SBI018", `The repository manifest names "${entry2.id}" at ${entry2.path}, which is not a directory.`, @@ -116149,11 +116240,11 @@ function resolveRepositories(workspace) { } const children = []; try { - for (const entry2 of (0, import_fs87.readdirSync)(workspace.rootDir, { withFileTypes: true })) { + for (const entry2 of (0, import_fs86.readdirSync)(workspace.rootDir, { withFileTypes: true })) { if (!entry2.isDirectory()) continue; if (DETECTION_DENYLIST.has(entry2.name) || entry2.name.startsWith(".")) continue; - const absDir = import_path96.default.join(workspace.rootDir, entry2.name); - if (!(0, import_fs87.existsSync)(import_path96.default.join(absDir, ".git"))) continue; + const absDir = import_path94.default.join(workspace.rootDir, entry2.name); + if (!(0, import_fs86.existsSync)(import_path94.default.join(absDir, ".git"))) continue; if (children.length >= BOOTSTRAP_LIMITS.maxRepositories) { notes.push("More child repositories exist than the bootstrap bound; declare a manifest to choose."); break; @@ -116164,7 +116255,7 @@ function resolveRepositories(workspace) { notes.push(`The workspace root could not be listed: ${cause instanceof Error ? cause.message : String(cause)}.`); } if (children.length > 0) { - const rootIsRepo = (0, import_fs87.existsSync)(import_path96.default.join(workspace.rootDir, ".git")); + const rootIsRepo = (0, import_fs86.existsSync)(import_path94.default.join(workspace.rootDir, ".git")); const repositories = rootIsRepo ? [resolved(workspace, rootRepositoryId(workspace), workspace.rootDir, void 0), ...children] : children; return { repositories: repositories.slice(0, BOOTSTRAP_LIMITS.maxRepositories), @@ -116179,18 +116270,18 @@ function resolveRepositories(workspace) { }; } function rootRepositoryId(workspace) { - const base = import_path96.default.basename(workspace.rootDir).replace(/[^A-Za-z0-9._-]/g, "-").replace(/^[^A-Za-z0-9]+/, ""); + const base = import_path94.default.basename(workspace.rootDir).replace(/[^A-Za-z0-9._-]/g, "-").replace(/^[^A-Za-z0-9]+/, ""); return base.length > 0 ? base.slice(0, 64) : "workspace"; } function resolved(workspace, repositoryId, absDir, role) { - const relPath2 = import_path96.default.relative(workspace.rootDir, absDir).replace(/\\/g, "/"); + const relPath2 = import_path94.default.relative(workspace.rootDir, absDir).replace(/\\/g, "/"); return { repositoryId, relPath: relPath2, ...role !== void 0 ? { role } : {}, absDir, gitHead: readGitHead(absDir), - isGitRepository: (0, import_fs87.existsSync)(import_path96.default.join(absDir, ".git")) + isGitRepository: (0, import_fs86.existsSync)(import_path94.default.join(absDir, ".git")) }; } function repositoryOfPath(repositories, workspaceRelativePath) { @@ -116383,7 +116474,7 @@ function synthesizeSystemFindings(input) { }); } const manifestEntries = entries.filter( - (entry2) => MANIFEST_BASENAMES.has(import_path97.default.posix.basename(entry2.path).toLowerCase()) + (entry2) => MANIFEST_BASENAMES.has(import_path95.default.posix.basename(entry2.path).toLowerCase()) ); const architectureLabels = /* @__PURE__ */ new Map(); for (const entry2 of manifestEntries.slice(0, 40)) { @@ -116428,7 +116519,7 @@ function synthesizeSystemFindings(input) { architecture.push({ findingId: ids("arc"), class: "OBSERVED_IMPLEMENTATION", - statement: clip3(`${label} (declared by ${import_path97.default.posix.basename(entry2.path)}).`), + statement: clip3(`${label} (declared by ${import_path95.default.posix.basename(entry2.path)}).`), evidence: [fileRef(entry2)] }); } @@ -116573,7 +116664,7 @@ function synthesizeSystemFindings(input) { findingId: ids("con"), class: "OBSERVED_IMPLEMENTATION", statement: clip3( - `Repository "${repo.repositoryId}" builds with ${import_path97.default.posix.basename(marker.path)}.` + `Repository "${repo.repositoryId}" builds with ${import_path95.default.posix.basename(marker.path)}.` ), evidence: [fileRef(marker)] }); @@ -116645,9 +116736,9 @@ function clip3(value) { } function boundedRead(workspace, relPath2) { try { - const abs = import_path97.default.join(workspace.rootDir, relPath2); - if (!(0, import_fs88.existsSync)(abs)) return void 0; - const body = (0, import_fs88.readFileSync)(abs, "utf8"); + const abs = import_path95.default.join(workspace.rootDir, relPath2); + if (!(0, import_fs87.existsSync)(abs)) return void 0; + const body = (0, import_fs87.readFileSync)(abs, "utf8"); return body.length > MAX_MANIFEST_READ_BYTES ? body.slice(0, MAX_MANIFEST_READ_BYTES) : body; } catch { return void 0; @@ -116688,25 +116779,25 @@ function safeSeals(workspace) { } } function bootstrapDir(workspace) { - return assertInsideWorkspace(workspace.rootDir, import_path98.default.join(workspace.sidecarDir, "bootstrap")); + return assertInsideWorkspace(workspace.rootDir, import_path96.default.join(workspace.sidecarDir, "bootstrap")); } function snapshotFile(workspace) { return assertInsideWorkspace( workspace.rootDir, - import_path98.default.join(bootstrapDir(workspace), "current-system-snapshot.json") + import_path96.default.join(bootstrapDir(workspace), "current-system-snapshot.json") ); } function readCurrentSystemSnapshot(workspace) { const file = snapshotFile(workspace); - if (!(0, import_fs89.existsSync)(file)) return void 0; + if (!(0, import_fs88.existsSync)(file)) return void 0; try { - return currentSystemSnapshotSchema.parse(JSON.parse((0, import_fs89.readFileSync)(file, "utf8"))); + return currentSystemSnapshotSchema.parse(JSON.parse((0, import_fs88.readFileSync)(file, "utf8"))); } catch { return void 0; } } function persistSnapshot(workspace, snapshot2) { - (0, import_fs89.mkdirSync)(bootstrapDir(workspace), { recursive: true }); + (0, import_fs88.mkdirSync)(bootstrapDir(workspace), { recursive: true }); writeFileAtomic(snapshotFile(workspace), `${JSON.stringify(snapshot2, null, 2)} `); } @@ -116879,7 +116970,7 @@ function inspectWorkspace(deps4, options) { } let body; try { - body = (0, import_fs89.readFileSync)( + body = (0, import_fs88.readFileSync)( assertInsideWorkspace(workspace.rootDir, entry2.path), "utf8" ); @@ -121519,10 +121610,10 @@ Examples: // ../../packages/mcp-server/dist/chunk-XJ3HVTHJ.js var import_buffer7 = require("buffer"); -var import_fs90 = require("fs"); -var import_path99 = __toESM(require("path"), 1); +var import_fs89 = require("fs"); +var import_path97 = __toESM(require("path"), 1); var import_crypto31 = require("crypto"); -var import_path100 = __toESM(require("path"), 1); +var import_path98 = __toESM(require("path"), 1); // ../../node_modules/.pnpm/zod@3.25.76/node_modules/zod/v4/core/core.js var NEVER2 = Object.freeze({ @@ -131850,12 +131941,12 @@ var EMPTY_COMPLETION_RESULT = { }; // ../../packages/mcp-server/dist/chunk-XJ3HVTHJ.js +var import_fs90 = require("fs"); var import_fs91 = require("fs"); +var import_path99 = __toESM(require("path"), 1); var import_fs92 = require("fs"); -var import_path101 = __toESM(require("path"), 1); -var import_fs93 = require("fs"); var import_os3 = __toESM(require("os"), 1); -var import_path102 = __toESM(require("path"), 1); +var import_path100 = __toESM(require("path"), 1); // ../../node_modules/.pnpm/@modelcontextprotocol+sdk@1.29.0_zod@3.25.76/node_modules/@modelcontextprotocol/sdk/dist/esm/server/stdio.js var import_node_process11 = __toESM(require("process"), 1); @@ -132314,10 +132405,10 @@ function validateProjectRoot(value, source, cwd) { remediation: ["Pass a plain filesystem path as --project-root."] }; } - const resolved2 = import_path99.default.resolve(cwd, value); + const resolved2 = import_path97.default.resolve(cwd, value); let canonical; try { - canonical = (0, import_fs90.realpathSync)(resolved2); + canonical = (0, import_fs89.realpathSync)(resolved2); } catch { return { ok: false, @@ -132330,7 +132421,7 @@ function validateProjectRoot(value, source, cwd) { } let stats; try { - stats = (0, import_fs90.statSync)(canonical); + stats = (0, import_fs89.statSync)(canonical); } catch { return { ok: false, @@ -132587,8 +132678,8 @@ var paginationShape = external_exports.object({ nextCursor: external_exports.string().optional() }); function repoRelative2(workspace, target) { - const relative = import_path100.default.isAbsolute(target) ? import_path100.default.relative(workspace.rootDir, target) : target; - const posix = relative.split(import_path100.default.sep).join("/"); + const relative = import_path98.default.isAbsolute(target) ? import_path98.default.relative(workspace.rootDir, target) : target; + const posix = relative.split(import_path98.default.sep).join("/"); return posix === "" ? "." : posix; } function toDiagnosticView(workspace, diagnostic) { @@ -133088,7 +133179,7 @@ function registerRunResources(server, context) { throw resourceNotFound(`Run "${runId}"`, "List runs with the run_list tool."); } const directory = runDir(workspace, record5.runId); - const artifactNames = (0, import_fs91.existsSync)(directory) ? (0, import_fs91.readdirSync)(directory).filter((name) => !REDACTED_ARTIFACTS.has(name)).sort((a2, b) => a2.localeCompare(b, "en")) : []; + const artifactNames = (0, import_fs90.existsSync)(directory) ? (0, import_fs90.readdirSync)(directory).filter((name) => !REDACTED_ARTIFACTS.has(name)).sort((a2, b) => a2.localeCompare(b, "en")) : []; return jsonContents(context, uri.href, buildRunDetail(workspace, record5, artifactNames)); } ); @@ -134904,7 +134995,7 @@ function registerRunReadTool(server, context) { }); } const directory = runDir(workspace, record5.runId); - const artifactNames = (0, import_fs92.existsSync)(directory) ? (0, import_fs92.readdirSync)(directory).filter((name) => !REDACTED_ARTIFACTS2.has(name)).sort((a2, b) => a2.localeCompare(b, "en")) : []; + const artifactNames = (0, import_fs91.existsSync)(directory) ? (0, import_fs91.readdirSync)(directory).filter((name) => !REDACTED_ARTIFACTS2.has(name)).sort((a2, b) => a2.localeCompare(b, "en")) : []; const detail = buildRunDetail(workspace, record5, artifactNames); const lines = [ `Run ${detail.summary.runId} \u2014 ${detail.summary.runType} for spec "${detail.summary.specName}"${detail.summary.taskId !== void 0 ? `, task ${detail.summary.taskId}` : ""}.`, @@ -135264,7 +135355,7 @@ function registerSpecRunVerificationTool(server, context) { durationMs: command.durationMs, timedOut: command.timedOut })); - const reportPath = result.artifactsDir !== void 0 ? import_path101.default.relative(workspace.rootDir, result.artifactsDir).split(import_path101.default.sep).join("/") : void 0; + const reportPath = result.artifactsDir !== void 0 ? import_path99.default.relative(workspace.rootDir, result.artifactsDir).split(import_path99.default.sep).join("/") : void 0; const commandLines = commands.map( (command) => `- ${command.name}: ${command.disposition}${command.disposition === "executed" ? command.passed ? " (passed)" : ` (FAILED, exit ${command.exitCode ?? "none"})` : ""}` ); @@ -135383,18 +135474,18 @@ var conformanceSummaryShape = external_exports.object({ note: external_exports.string() }); async function invocationFreeConformanceSummary(profile) { - const scratch = (0, import_fs93.mkdtempSync)(import_path102.default.join(import_os3.default.tmpdir(), "specbridge-mcp-conformance-")); + const scratch = (0, import_fs92.mkdtempSync)(import_path100.default.join(import_os3.default.tmpdir(), "specbridge-mcp-conformance-")); let result; try { result = await runRunnerConformance({ profile, workspaceRoot: scratch, - runDir: import_path102.default.join(scratch, ".specbridge-conformance-runs"), + runDir: import_path100.default.join(scratch, ".specbridge-conformance-runs"), invocationsAllowed: false, timeoutMs: RUNNER_PROBE_TIMEOUT_MS }); } finally { - (0, import_fs93.rmSync)(scratch, { recursive: true, force: true }); + (0, import_fs92.rmSync)(scratch, { recursive: true, force: true }); } return { passed: result.passed, @@ -139281,8 +139372,8 @@ async function runMcpServe(argv2, io = { } // ../../packages/mcp-server/dist/index.js -var import_fs94 = require("fs"); -var import_path103 = __toESM(require("path"), 1); +var import_fs93 = require("fs"); +var import_path101 = __toESM(require("path"), 1); async function runMcpDoctor(options = {}) { const checks = []; const env = options.env ?? process.env; @@ -139375,7 +139466,7 @@ async function runMcpDoctor(options = {}) { const pluginRoot = env["CLAUDE_PLUGIN_ROOT"]; if (pluginRoot !== void 0 && pluginRoot.length > 0) { const missing = ["dist/mcp-server.cjs", "dist/cli.cjs"].filter( - (relative) => !(0, import_fs94.existsSync)(import_path103.default.join(pluginRoot, relative)) + (relative) => !(0, import_fs93.existsSync)(import_path101.default.join(pluginRoot, relative)) ); checks.push( missing.length === 0 ? { name: "plugin-bundle", status: "ok", detail: `Bundled executables present under ${pluginRoot}` } : { diff --git a/integrations/claude-code-plugin/specbridge/dist/mcp-server.cjs b/integrations/claude-code-plugin/specbridge/dist/mcp-server.cjs index c00c572..2f96c74 100644 --- a/integrations/claude-code-plugin/specbridge/dist/mcp-server.cjs +++ b/integrations/claude-code-plugin/specbridge/dist/mcp-server.cjs @@ -42984,6 +42984,29 @@ function parseClaudeEnvelope(stdout) { } return { problem: "no JSON result envelope found in the runner output" }; } +var MAX_STDERR_DIAGNOSTIC_CHARS = 500; +var CREDENTIAL_PATTERNS = [ + /\bsk-[A-Za-z0-9_-]{8,}/gi, + /\bbearer\s+[A-Za-z0-9._-]{8,}/gi, + /\boauth-[A-Za-z0-9-]{6,}/gi, + /\b(?:api[-_]?keys?|access[-_]?tokens?|secrets?|passwords?)\b(?:\s*[:=]\s*\S+)?/gi +]; +function redactCredentials(text15) { + let redacted = text15; + for (const pattern of CREDENTIAL_PATTERNS) redacted = redacted.replace(pattern, "[redacted]"); + return redacted; +} +function stderrDiagnostic(stderr) { + const collapsed = redactCredentials(stderr).replace(/\s+/g, " ").trim(); + if (collapsed.length === 0) return void 0; + if (collapsed.length <= MAX_STDERR_DIAGNOSTIC_CHARS) return collapsed; + return `${collapsed.slice(0, MAX_STDERR_DIAGNOSTIC_CHARS)}\u2026 [truncated]`; +} +function claudeFailureProblem(problem, processResult) { + if (processResult.status !== "nonzero-exit") return problem; + const diagnostic = stderrDiagnostic(processResult.stderr); + return diagnostic === void 0 ? problem : `${problem} (claude stderr: ${diagnostic})`; +} var ClaudeCodeRunner = class { name = "claude-code"; kind = "claude-code"; @@ -43125,6 +43148,85 @@ var ClaudeCodeRunner = class { const stageReport = report; return { ...rest, ...stageReport !== void 0 ? { report: stageReport } : {} }; } + async invokeStructured(input, execution) { + const started = Date.now(); + const probe = await this.probe(); + const unavailable = this.unavailableResult(probe, started); + if (unavailable !== void 0) { + const { report: _report, ...rest } = unavailable; + return rest; + } + const plan = buildClaudeInvocation({ + config: this.config, + probe, + prompt: input.prompt, + toolPolicy: input.toolPolicy, + outputJsonSchema: input.outputJsonSchema, + execution + }); + try { + const processResult = await runClaudeInvocation(plan, this.config, execution); + const parsed = parseClaudeEnvelope(processResult.stdout); + const usage = usageFromEnvelope(parsed.envelope, processResult.observation.durationMs); + const cost = costFromEnvelope(parsed.envelope); + const base = { + runner: this.name, + rawStdout: processResult.stdout, + rawStderr: processResult.stderr, + process: processResult.observation, + durationMs: Math.max(0, Date.now() - started), + warnings: plan.skippedFlags.map( + (flag) => `flag ${flag} is unsupported by this Claude Code version and was skipped` + ), + ...parsed.envelope?.session_id !== void 0 ? { sessionId: parsed.envelope.session_id } : {}, + ...usage !== void 0 ? { usage } : {}, + ...cost !== void 0 ? { cost } : {} + }; + switch (processResult.status) { + case "timeout": + return { ...base, outcome: "timed-out", failureReason: processResult.failureReason ?? "timeout" }; + case "cancelled": + return { ...base, outcome: "cancelled", failureReason: processResult.failureReason ?? "cancelled" }; + case "output-limit": + case "spawn-failed": + return { + ...base, + outcome: "failed", + failureReason: processResult.failureReason ?? processResult.status + }; + case "ok": + case "nonzero-exit": + break; + } + if (this.looksPermissionDenied(processResult, parsed.envelope?.subtype, parsed.envelope)) { + return { + ...base, + outcome: "permission-denied", + failureReason: "Claude Code reported a permission denial." + }; + } + if (processResult.status === "nonzero-exit" || parsed.envelope?.is_error === true) { + return { + ...base, + outcome: "malformed-output", + failureReason: processResult.status === "nonzero-exit" ? claudeFailureProblem(parsed.problem ?? "the runner produced no output", processResult) : `Claude Code reported an error result${parsed.envelope?.subtype !== void 0 ? ` (${parsed.envelope.subtype})` : ""}`, + ...parsed.reportText !== void 0 ? { invalidStructuredOutput: parsed.reportText } : {} + }; + } + const text15 = parsed.structuredResult !== void 0 ? JSON.stringify(parsed.structuredResult) : parsed.reportText; + if (text15 === void 0 || safeJsonParse(text15) === void 0) { + return { + ...base, + outcome: "malformed-output", + failureReason: parsed.problem ?? "the runner returned no valid JSON document", + ...text15 !== void 0 ? { invalidStructuredOutput: text15 } : {} + }; + } + return { ...base, outcome: "completed", text: text15.trim() }; + } finally { + cleanupTempFiles(plan); + } + } async executeTask(input, execution) { return this.runTask(input.prompt, execution, { ...input.sessionId !== void 0 ? { sessionId: input.sessionId } : {} @@ -44161,6 +44263,115 @@ var CodexCliRunner = class { const stageReport = report; return { ...rest, ...stageReport !== void 0 ? { report: stageReport } : {} }; } + async invokeStructured(input, execution) { + const started = Date.now(); + const probe = await this.probe(); + const unavailable = this.unavailableResult(probe, started); + if (unavailable !== void 0) { + const { report: _report, ...rest } = unavailable; + return rest; + } + const plan = buildCodexInvocation({ + config: this.config, + probe, + prompt: input.prompt, + toolPolicy: input.toolPolicy, + outputJsonSchema: input.outputJsonSchema, + execution + }); + try { + const processResult = await runCodexInvocation(plan, this.config, execution); + const stream = parseCodexEventStream(processResult.stdout); + const warnings = plan.skippedFlags.map( + (flag) => `flag ${flag} is unsupported by this Codex CLI version and was skipped` + ); + const normalizedEvents = normalizeCodexEvents( + stream, + { + runner: this.name, + profile: this.name, + runId: "pending", + attemptId: "pending" + }, + () => (/* @__PURE__ */ new Date()).toISOString() + ); + const usage = usageFromStream(stream, processResult.observation.durationMs, this.config.model); + const base = { + runner: this.name, + rawStdout: redactCodexStdoutForRetention(processResult.stdout), + rawStderr: processResult.stderr, + process: processResult.observation, + durationMs: Math.max(0, Date.now() - started), + warnings, + normalizedEvents, + ...usage !== void 0 ? { usage } : {}, + ...stream.threadId !== void 0 ? { sessionId: stream.threadId } : {} + }; + switch (processResult.status) { + case "timeout": + return { + ...base, + outcome: "timed-out", + failureReason: processResult.failureReason ?? "timeout", + error: runnerError({ code: "timed_out", message: "The Codex process timed out." }) + }; + case "cancelled": + return { + ...base, + outcome: "cancelled", + failureReason: processResult.failureReason ?? "cancelled", + error: runnerError({ code: "cancelled", message: "The Codex process was cancelled." }) + }; + case "output-limit": + return { + ...base, + outcome: "failed", + failureReason: processResult.failureReason ?? "output limit exceeded", + error: runnerError({ + code: "output_limit_exceeded", + message: "The Codex process exceeded its output limit." + }) + }; + case "spawn-failed": + return { + ...base, + outcome: "failed", + failureReason: processResult.failureReason ?? "spawn failed", + error: runnerError({ + code: "executable_not_found", + message: "The Codex CLI could not be started." + }) + }; + case "ok": + break; + case "nonzero-exit": { + const error2 = classifyCodexFailure(processResult.stderr, stream.errors); + return { + ...base, + outcome: error2.code === "permission_denied" ? "permission-denied" : "failed", + failureReason: error2.message, + error: error2 + }; + } + } + const finalText = readLastMessage(plan) ?? stream.lastAgentMessage; + if (finalText === void 0 || strictJsonParse(finalText) === void 0) { + return { + ...base, + outcome: "malformed-output", + failureReason: finalText === void 0 ? "the runner returned no final structured result" : "the final Codex message is not a bare JSON document", + error: runnerError({ + code: "structured_output_invalid", + message: "The Codex orchestration response was not a valid JSON document." + }), + ...finalText !== void 0 ? { invalidStructuredOutput: finalText } : {} + }; + } + return { ...base, outcome: "completed", text: finalText.trim() }; + } finally { + cleanupCodexTempFiles(plan); + } + } async executeTask(input, execution) { return this.runTask(input.prompt, execution, {}); } diff --git a/integrations/codex-plugin/specbridge/dist/checksums.json b/integrations/codex-plugin/specbridge/dist/checksums.json index 5821889..76cd23f 100644 --- a/integrations/codex-plugin/specbridge/dist/checksums.json +++ b/integrations/codex-plugin/specbridge/dist/checksums.json @@ -7,16 +7,16 @@ "bytes": 155992 }, "cli.cjs": { - "sha256": "db1ed5fb7eacf11662e1206e27b74004cb31f1df88c4ee30b65ce092eb78e377", - "bytes": 5858878 + "sha256": "7d741451a5008788fe716220e57fd96c38f7d15ae27db32ba863e02ad56748db", + "bytes": 5863147 }, "mcp-launcher.cjs": { "sha256": "5ad0dca58e1f0195ba8819144d44c5285bf43a360090e681594df050899c7770", "bytes": 4178 }, "mcp-server.cjs": { - "sha256": "090a7686c19a94495baa75b230899f15a8e0b6036cfb09f73d4482686b621a3e", - "bytes": 3777757 + "sha256": "6568a89c0c530f599038bc4aa7eb63de37b05c3c7e464c8771fa6e620d9ff61f", + "bytes": 3786242 } } } diff --git a/integrations/codex-plugin/specbridge/dist/cli.cjs b/integrations/codex-plugin/specbridge/dist/cli.cjs index c194ec4..75dd9b5 100644 --- a/integrations/codex-plugin/specbridge/dist/cli.cjs +++ b/integrations/codex-plugin/specbridge/dist/cli.cjs @@ -39182,6 +39182,85 @@ var ClaudeCodeRunner = class { const stageReport = report; return { ...rest, ...stageReport !== void 0 ? { report: stageReport } : {} }; } + async invokeStructured(input, execution) { + const started = Date.now(); + const probe = await this.probe(); + const unavailable = this.unavailableResult(probe, started); + if (unavailable !== void 0) { + const { report: _report, ...rest } = unavailable; + return rest; + } + const plan = buildClaudeInvocation({ + config: this.config, + probe, + prompt: input.prompt, + toolPolicy: input.toolPolicy, + outputJsonSchema: input.outputJsonSchema, + execution + }); + try { + const processResult = await runClaudeInvocation(plan, this.config, execution); + const parsed = parseClaudeEnvelope(processResult.stdout); + const usage = usageFromEnvelope(parsed.envelope, processResult.observation.durationMs); + const cost = costFromEnvelope(parsed.envelope); + const base = { + runner: this.name, + rawStdout: processResult.stdout, + rawStderr: processResult.stderr, + process: processResult.observation, + durationMs: Math.max(0, Date.now() - started), + warnings: plan.skippedFlags.map( + (flag) => `flag ${flag} is unsupported by this Claude Code version and was skipped` + ), + ...parsed.envelope?.session_id !== void 0 ? { sessionId: parsed.envelope.session_id } : {}, + ...usage !== void 0 ? { usage } : {}, + ...cost !== void 0 ? { cost } : {} + }; + switch (processResult.status) { + case "timeout": + return { ...base, outcome: "timed-out", failureReason: processResult.failureReason ?? "timeout" }; + case "cancelled": + return { ...base, outcome: "cancelled", failureReason: processResult.failureReason ?? "cancelled" }; + case "output-limit": + case "spawn-failed": + return { + ...base, + outcome: "failed", + failureReason: processResult.failureReason ?? processResult.status + }; + case "ok": + case "nonzero-exit": + break; + } + if (this.looksPermissionDenied(processResult, parsed.envelope?.subtype, parsed.envelope)) { + return { + ...base, + outcome: "permission-denied", + failureReason: "Claude Code reported a permission denial." + }; + } + if (processResult.status === "nonzero-exit" || parsed.envelope?.is_error === true) { + return { + ...base, + outcome: "malformed-output", + failureReason: processResult.status === "nonzero-exit" ? claudeFailureProblem(parsed.problem ?? "the runner produced no output", processResult) : `Claude Code reported an error result${parsed.envelope?.subtype !== void 0 ? ` (${parsed.envelope.subtype})` : ""}`, + ...parsed.reportText !== void 0 ? { invalidStructuredOutput: parsed.reportText } : {} + }; + } + const text15 = parsed.structuredResult !== void 0 ? JSON.stringify(parsed.structuredResult) : parsed.reportText; + if (text15 === void 0 || safeJsonParse(text15) === void 0) { + return { + ...base, + outcome: "malformed-output", + failureReason: parsed.problem ?? "the runner returned no valid JSON document", + ...text15 !== void 0 ? { invalidStructuredOutput: text15 } : {} + }; + } + return { ...base, outcome: "completed", text: text15.trim() }; + } finally { + cleanupTempFiles(plan); + } + } async executeTask(input, execution) { return this.runTask(input.prompt, execution, { ...input.sessionId !== void 0 ? { sessionId: input.sessionId } : {} @@ -40218,6 +40297,115 @@ var CodexCliRunner = class { const stageReport = report; return { ...rest, ...stageReport !== void 0 ? { report: stageReport } : {} }; } + async invokeStructured(input, execution) { + const started = Date.now(); + const probe = await this.probe(); + const unavailable = this.unavailableResult(probe, started); + if (unavailable !== void 0) { + const { report: _report, ...rest } = unavailable; + return rest; + } + const plan = buildCodexInvocation({ + config: this.config, + probe, + prompt: input.prompt, + toolPolicy: input.toolPolicy, + outputJsonSchema: input.outputJsonSchema, + execution + }); + try { + const processResult = await runCodexInvocation(plan, this.config, execution); + const stream = parseCodexEventStream(processResult.stdout); + const warnings = plan.skippedFlags.map( + (flag) => `flag ${flag} is unsupported by this Codex CLI version and was skipped` + ); + const normalizedEvents = normalizeCodexEvents( + stream, + { + runner: this.name, + profile: this.name, + runId: "pending", + attemptId: "pending" + }, + () => (/* @__PURE__ */ new Date()).toISOString() + ); + const usage = usageFromStream(stream, processResult.observation.durationMs, this.config.model); + const base = { + runner: this.name, + rawStdout: redactCodexStdoutForRetention(processResult.stdout), + rawStderr: processResult.stderr, + process: processResult.observation, + durationMs: Math.max(0, Date.now() - started), + warnings, + normalizedEvents, + ...usage !== void 0 ? { usage } : {}, + ...stream.threadId !== void 0 ? { sessionId: stream.threadId } : {} + }; + switch (processResult.status) { + case "timeout": + return { + ...base, + outcome: "timed-out", + failureReason: processResult.failureReason ?? "timeout", + error: runnerError({ code: "timed_out", message: "The Codex process timed out." }) + }; + case "cancelled": + return { + ...base, + outcome: "cancelled", + failureReason: processResult.failureReason ?? "cancelled", + error: runnerError({ code: "cancelled", message: "The Codex process was cancelled." }) + }; + case "output-limit": + return { + ...base, + outcome: "failed", + failureReason: processResult.failureReason ?? "output limit exceeded", + error: runnerError({ + code: "output_limit_exceeded", + message: "The Codex process exceeded its output limit." + }) + }; + case "spawn-failed": + return { + ...base, + outcome: "failed", + failureReason: processResult.failureReason ?? "spawn failed", + error: runnerError({ + code: "executable_not_found", + message: "The Codex CLI could not be started." + }) + }; + case "ok": + break; + case "nonzero-exit": { + const error2 = classifyCodexFailure(processResult.stderr, stream.errors); + return { + ...base, + outcome: error2.code === "permission_denied" ? "permission-denied" : "failed", + failureReason: error2.message, + error: error2 + }; + } + } + const finalText = readLastMessage(plan) ?? stream.lastAgentMessage; + if (finalText === void 0 || strictJsonParse(finalText) === void 0) { + return { + ...base, + outcome: "malformed-output", + failureReason: finalText === void 0 ? "the runner returned no final structured result" : "the final Codex message is not a bare JSON document", + error: runnerError({ + code: "structured_output_invalid", + message: "The Codex orchestration response was not a valid JSON document." + }), + ...finalText !== void 0 ? { invalidStructuredOutput: finalText } : {} + }; + } + return { ...base, outcome: "completed", text: finalText.trim() }; + } finally { + cleanupCodexTempFiles(plan); + } + } async executeTask(input, execution) { return this.runTask(input.prompt, execution, {}); } @@ -54035,8 +54223,6 @@ var import_fs42 = require("fs"); var import_path44 = __toESM(require("path"), 1); var import_fs43 = require("fs"); var import_path45 = __toESM(require("path"), 1); -var import_fs44 = require("fs"); -var import_path46 = __toESM(require("path"), 1); // ../../packages/mission/dist/index.js var import_fs30 = require("fs"); @@ -56386,28 +56572,27 @@ function observeSpecApproval(deps4, missionId) { } // ../../packages/orchestration/dist/index.js +var import_path46 = __toESM(require("path"), 1); +var import_fs44 = require("fs"); var import_path47 = __toESM(require("path"), 1); +var import_crypto23 = require("crypto"); var import_fs45 = require("fs"); var import_path48 = __toESM(require("path"), 1); -var import_crypto23 = require("crypto"); -var import_fs46 = require("fs"); var import_path49 = __toESM(require("path"), 1); +var import_fs46 = require("fs"); var import_path50 = __toESM(require("path"), 1); -var import_path51 = __toESM(require("path"), 1); var import_fs47 = require("fs"); -var import_path52 = __toESM(require("path"), 1); +var import_path51 = __toESM(require("path"), 1); var import_fs48 = require("fs"); -var import_path53 = __toESM(require("path"), 1); +var import_path52 = __toESM(require("path"), 1); var import_fs49 = require("fs"); -var import_path54 = __toESM(require("path"), 1); +var import_path53 = __toESM(require("path"), 1); var import_fs50 = require("fs"); -var import_path55 = __toESM(require("path"), 1); +var import_path54 = __toESM(require("path"), 1); var import_fs51 = require("fs"); -var import_path56 = __toESM(require("path"), 1); +var import_path55 = __toESM(require("path"), 1); var import_fs52 = require("fs"); -var import_path57 = __toESM(require("path"), 1); -var import_fs53 = require("fs"); -var import_path58 = __toESM(require("path"), 1); +var import_path56 = __toESM(require("path"), 1); var import_crypto24 = require("crypto"); var ORCHESTRATION_PHASES = [ /** The run exists; no intent has been assessed yet. */ @@ -60550,7 +60735,6 @@ function assessCompletion(gate, jobId) { } } var LOCAL_WORKER_ID = "local-llamacpp"; -var CLAUDE_WORKER_ID = "claude-code"; function resolveWorkers(config2) { const workers = []; const local = config2.localInference; @@ -60575,7 +60759,7 @@ function resolveWorkers(config2) { }); } workers.push({ - workerId: CLAUDE_WORKER_ID, + workerId: config2.defaultRunner, runnerProfile: config2.defaultRunner, roles: [ "CLASSIFIER", @@ -60649,7 +60833,7 @@ function selectWorker(input) { ); if (writer === void 0) { throw new OrchestrationError("SBO034", `No repository-writing worker is available for ${role}.`, { - remediation: ["Check the Claude Code runner with `specbridge runner doctor claude-code`."], + remediation: ["Check the configured default runner with `specbridge runner doctor`."], failureCategory: "CAPABILITY_UNAVAILABLE" }); } @@ -68913,10 +69097,10 @@ function assessContextMiss(input) { for (const symbol of extractSymbolReferences(input.workerReportedText ?? "")) { const declaring = input.index?.declaring(symbol) ?? []; if (declaring.length === 0) continue; - if (declaring.some((path272) => provided.has(path272))) continue; + if (declaring.some((path252) => provided.has(path252))) continue; signals2.add("UNKNOWN_SYMBOL_REFERENCE"); if (!missingSymbols.includes(symbol)) missingSymbols.push(symbol); - for (const path272 of declaring) if (!missingPaths.includes(path272)) missingPaths.push(path272); + for (const path252 of declaring) if (!missingPaths.includes(path252)) missingPaths.push(path252); } for (const candidate of extractPathReferences2(input.failureText ?? "")) { if (provided.has(candidate)) continue; @@ -68924,7 +69108,7 @@ function assessContextMiss(input) { signals2.add("FAILURE_IN_UNSELECTED_FILE"); if (!missingPaths.includes(candidate)) missingPaths.push(candidate); } - const staleSelected = (input.refreshedPaths ?? []).filter((path272) => provided.has(path272)); + const staleSelected = (input.refreshedPaths ?? []).filter((path252) => provided.has(path252)); if (staleSelected.length > 0) signals2.add("SELECTED_ARTIFACT_STALE"); const droppedMandatory = (input.plan?.excludedCandidates ?? []).filter( (entry2) => entry2.reason === "BUDGET_EXHAUSTED" || entry2.reason === "TOO_LARGE" @@ -69028,7 +69212,7 @@ function runCriterionCheck(check22, evidence) { case "changed-within": { const prefix = normalizePath2(check22.value); const outside = evidence.changedPaths.filter( - (path272) => !normalizePath2(path272).startsWith(prefix) + (path252) => !normalizePath2(path252).startsWith(prefix) ); return outside.length === 0 ? { outcome: "PASSED", detail: `every change is inside ${check22.value}` } : { outcome: "FAILED", @@ -69044,8 +69228,8 @@ function runCriterionCheck(check22, evidence) { } } } -function normalizePath2(path272) { - return path272.replace(/\\/g, "/").replace(/^\.\//, ""); +function normalizePath2(path252) { + return path252.replace(/\\/g, "/").replace(/^\.\//, ""); } function inferLevel(name) { return /test|spec|e2e|integration|regression|contract/i.test(name) ? "TESTS" : "BUILD_STATIC"; @@ -73062,17 +73246,6 @@ var AUTH_FAILURE_PATTERN = new RegExp( String.raw`\b(401|403|unauthorized|unauthenticated|failed to authenticate` + String.raw`|re-?authenticate|oauth[^.]{0,40}\bexpired\b|token has expired` + String.raw`|expired token|invalid api key|api key not found|please log ?in` + String.raw`|credentials? (are )?(invalid|missing|expired))\b`, "i" ); -var AUTH_FAILURE_MAX_CHARS = 2e3; -function looksLikeAuthenticationFailure(text93) { - const collapsed = text93.trim(); - if (collapsed.length === 0 || collapsed.length > AUTH_FAILURE_MAX_CHARS) return false; - try { - JSON.parse(collapsed); - return false; - } catch { - } - return AUTH_FAILURE_PATTERN.test(collapsed); -} function observedExcerpt(text93) { return text93.replace(/\s+/g, " ").trim().slice(0, OBSERVED_OUTPUT_EXCERPT_CHARS); } @@ -73179,23 +73352,22 @@ ${correctionMessage(invocation.role, validated.problem)}`; }; } async function runLargeRole(invocation) { - const profile = invocation.config.runnerProfiles[invocation.runnerProfile]; - if (profile === void 0 || profile.runner !== "claude-code") { + const registry2 = invocation.registry ?? createDefaultRunnerRegistry(invocation.config); + let profile; + try { + profile = registry2.getProfile(invocation.runnerProfile); + } catch (cause) { return { ok: false, kind: "worker-unavailable", - problem: `Runner profile "${invocation.runnerProfile}" is not a Claude Code profile.` + problem: cause instanceof Error ? cause.message : `Runner profile "${invocation.runnerProfile}" is unavailable.` }; } - const claudeProfile = profile; - const probe = invocation.cachedProbe ?? await probeClaude(claudeProfile, { - ...invocation.signal !== void 0 ? { signal: invocation.signal } : {} - }); - if (!probe.found || probe.status === "unavailable" || probe.status === "error") { + if (profile.config.enabled !== true || profile.runner.invokeStructured === void 0) { return { ok: false, kind: "worker-unavailable", - problem: `The Claude Code CLI is not available (status ${probe.status}).` + problem: profile.config.enabled !== true ? `Runner profile "${invocation.runnerProfile}" is disabled.` : `Runner profile "${invocation.runnerProfile}" does not support structured orchestration roles.` }; } const prompt = [ @@ -73205,89 +73377,49 @@ async function runLargeRole(invocation) { "", invocation.packet ].join("\n"); - const plan = buildClaudeInvocation({ - config: claudeProfile, - probe, + const result = await profile.runner.invokeStructured({ prompt, toolPolicy: "inspect-only", - outputJsonSchema: AGENT_OUTPUT_JSON_SCHEMAS[invocation.role], - execution: { - workspaceRoot: invocation.workspace.rootDir, - runDir: invocation.scratchDir, - timeoutMs: invocation.timeoutMs - } + schemaName: invocation.role, + outputJsonSchema: AGENT_OUTPUT_JSON_SCHEMAS[invocation.role] + }, { + workspaceRoot: invocation.workspace.rootDir, + runDir: invocation.scratchDir, + timeoutMs: invocation.timeoutMs, + ...invocation.signal !== void 0 ? { signal: invocation.signal } : {} }); - try { - const processResult = await runSafeProcess({ - executable: plan.executable, - argv: plan.argv, - cwd: invocation.workspace.rootDir, - timeoutMs: invocation.timeoutMs, - stdin: plan.stdin, - ...invocation.signal !== void 0 ? { signal: invocation.signal } : {} - }); - if (processResult.status === "cancelled") { - return { ok: false, kind: "cancelled", problem: "The role invocation was cancelled." }; - } - if (processResult.status !== "ok" && processResult.status !== "nonzero-exit") { - return { - ok: false, - kind: "worker-unavailable", - problem: processResult.failureReason ?? `the runner process ended with status ${processResult.status}` - }; - } - const parsed = parseClaudeEnvelope(processResult.stdout); - if (parsed.problem !== void 0) { - return { - ok: false, - kind: "invalid-output", - problem: claudeFailureProblem(parsed.problem, processResult), - probe - }; - } - const text93 = parsed.structuredResult !== void 0 ? JSON.stringify(parsed.structuredResult) : parsed.reportText ?? ""; - const validated = validateAgentOutput(invocation.role, text93); - if (!validated.ok) { - if (looksLikeAuthenticationFailure(text93)) { - return { - ok: false, - // NOT invalid-output: the worker is unusable, not incoherent, and - // the two need different answers from a person. - kind: "worker-unavailable", - problem: `The ${invocation.role} worker is not authenticated: ${observedExcerpt(text93)}`, - observed: observedExcerpt(text93), - probe - }; - } - return { - ok: false, - kind: "invalid-output", - problem: validated.problem, - observed: observedExcerpt(text93), - probe - }; - } - const usage = usageFromEnvelope(parsed.envelope, 0); - const cost = costFromEnvelope(parsed.envelope); + if (result.outcome === "cancelled") { + return { ok: false, kind: "cancelled", problem: result.failureReason ?? "The role invocation was cancelled." }; + } + if (result.outcome !== "completed" || result.text === void 0) { + const observed = result.invalidStructuredOutput; return { - ok: true, - output: validated.output, - raw: text93, - usage: { - inputTokens: usage?.inputTokens ?? null, - outputTokens: usage?.outputTokens ?? null, - // Only provider-reported USD amounts count; nothing is fabricated. - costUsd: cost !== null && cost !== void 0 && cost.currency === "USD" ? cost.amount : null - }, - corrected: false, - probe + ok: false, + kind: result.outcome === "malformed-output" ? "invalid-output" : "worker-unavailable", + problem: result.failureReason ?? result.error?.message ?? `Runner profile "${invocation.runnerProfile}" ended with ${result.outcome}.`, + ...observed !== void 0 ? { observed: observedExcerpt(observed) } : {} }; - } finally { - try { - (0, import_fs43.rmSync)(import_path45.default.join(invocation.scratchDir, "tmp"), { recursive: true, force: true }); - } catch { - } } + const validated = validateAgentOutput(invocation.role, result.text); + if (!validated.ok) { + return { + ok: false, + kind: "invalid-output", + problem: validated.problem, + observed: observedExcerpt(result.text) + }; + } + return { + ok: true, + output: validated.output, + raw: result.text, + usage: { + inputTokens: result.usage?.inputTokens ?? null, + outputTokens: result.usage?.outputTokens ?? null, + costUsd: result.cost?.currency === "USD" ? result.cost.amount : null + }, + corrected: false + }; } function createLocalManager(config2, onEvent) { if (!config2.localInference.enabled) return void 0; @@ -73703,13 +73835,13 @@ function findResearchReuse(records, request) { var RESEARCH_DIR_NAME = "research"; var ID_PATTERN7 = /^[A-Za-z0-9][A-Za-z0-9._-]{0,127}$/; function researchRootDir(workspace) { - return assertInsideWorkspace(workspace.rootDir, import_path48.default.join(workspace.sidecarDir, RESEARCH_DIR_NAME)); + return assertInsideWorkspace(workspace.rootDir, import_path47.default.join(workspace.sidecarDir, RESEARCH_DIR_NAME)); } function researchRecordsDir(workspace) { - return assertInsideWorkspace(workspace.rootDir, import_path48.default.join(researchRootDir(workspace), "records")); + return assertInsideWorkspace(workspace.rootDir, import_path47.default.join(researchRootDir(workspace), "records")); } function researchUsesDir(workspace) { - return assertInsideWorkspace(workspace.rootDir, import_path48.default.join(researchRootDir(workspace), "uses")); + return assertInsideWorkspace(workspace.rootDir, import_path47.default.join(researchRootDir(workspace), "uses")); } function assertResearchId(researchId) { if (!ID_PATTERN7.test(researchId)) throw new Error(`Invalid research id "${researchId}".`); @@ -73719,7 +73851,7 @@ function researchRecordFile(workspace, researchId) { assertResearchId(researchId); return assertInsideWorkspace( workspace.rootDir, - import_path48.default.join(researchRecordsDir(workspace), `${researchId}.json`) + import_path47.default.join(researchRecordsDir(workspace), `${researchId}.json`) ); } function majorOf3(value) { @@ -73727,10 +73859,10 @@ function majorOf3(value) { } function readResearchRecord(workspace, researchId) { const file = researchRecordFile(workspace, researchId); - if (!(0, import_fs45.existsSync)(file)) return { kind: "missing" }; + if (!(0, import_fs44.existsSync)(file)) return { kind: "missing" }; let value; try { - value = JSON.parse((0, import_fs45.readFileSync)(file, "utf8")); + value = JSON.parse((0, import_fs44.readFileSync)(file, "utf8")); } catch (cause) { return { kind: "corrupt", problem: cause instanceof Error ? cause.message : String(cause), file }; } @@ -73752,31 +73884,31 @@ function readResearchRecord(workspace, researchId) { function writeResearchRecord(workspace, value) { const record32 = researchRecordSchema.parse(value); const file = researchRecordFile(workspace, record32.researchId); - (0, import_fs45.mkdirSync)(import_path48.default.dirname(file), { recursive: true }); + (0, import_fs44.mkdirSync)(import_path47.default.dirname(file), { recursive: true }); writeFileAtomic(file, `${JSON.stringify(record32, null, 2)} `); return record32; } function researchUseFile(workspace, useId) { - return assertInsideWorkspace(workspace.rootDir, import_path48.default.join(researchUsesDir(workspace), `${useId}.json`)); + return assertInsideWorkspace(workspace.rootDir, import_path47.default.join(researchUsesDir(workspace), `${useId}.json`)); } function writeResearchUseRecord(workspace, value) { const record32 = researchUseRecordSchema.parse(value); const file = researchUseFile(workspace, record32.useId); - if ((0, import_fs45.existsSync)(file)) throw new Error(`research use id ${record32.useId} already exists`); - (0, import_fs45.mkdirSync)(import_path48.default.dirname(file), { recursive: true }); + if ((0, import_fs44.existsSync)(file)) throw new Error(`research use id ${record32.useId} already exists`); + (0, import_fs44.mkdirSync)(import_path47.default.dirname(file), { recursive: true }); writeFileAtomic(file, `${JSON.stringify(record32, null, 2)} `); return record32; } function listResearchUseRecords(workspace) { const dir = researchUsesDir(workspace); - if (!(0, import_fs45.existsSync)(dir)) return []; + if (!(0, import_fs44.existsSync)(dir)) return []; const records = []; - for (const entry2 of (0, import_fs45.readdirSync)(dir, { withFileTypes: true })) { + for (const entry2 of (0, import_fs44.readdirSync)(dir, { withFileTypes: true })) { if (!entry2.isFile() || !entry2.name.endsWith(".json")) continue; try { - const value = JSON.parse((0, import_fs45.readFileSync)(import_path48.default.join(dir, entry2.name), "utf8")); + const value = JSON.parse((0, import_fs44.readFileSync)(import_path47.default.join(dir, entry2.name), "utf8")); const version2 = value !== null && typeof value === "object" && typeof value.schemaVersion === "string" ? value.schemaVersion : ""; if (majorOf3(version2) !== majorOf3(RESEARCH_USE_SCHEMA_VERSION)) continue; const parsed = researchUseRecordSchema.safeParse(value); @@ -73788,10 +73920,10 @@ function listResearchUseRecords(workspace) { } function listResearchRecords(workspace) { const dir = researchRecordsDir(workspace); - if (!(0, import_fs45.existsSync)(dir)) return { records: [], diagnostics: [] }; + if (!(0, import_fs44.existsSync)(dir)) return { records: [], diagnostics: [] }; const records = []; const diagnostics = []; - for (const entry2 of (0, import_fs45.readdirSync)(dir, { withFileTypes: true })) { + for (const entry2 of (0, import_fs44.readdirSync)(dir, { withFileTypes: true })) { if (!entry2.isFile() || !entry2.name.endsWith(".json")) continue; const researchId = entry2.name.slice(0, -5); if (!ID_PATTERN7.test(researchId)) continue; @@ -74390,13 +74522,13 @@ function emptyResearchTelemetry(now52) { }; } function researchTelemetryFile(workspace) { - return assertInsideWorkspace(workspace.rootDir, import_path49.default.join(researchRootDir(workspace), "telemetry.json")); + return assertInsideWorkspace(workspace.rootDir, import_path48.default.join(researchRootDir(workspace), "telemetry.json")); } function readResearchTelemetry(workspace, now52 = /* @__PURE__ */ new Date()) { const file = researchTelemetryFile(workspace); - if (!(0, import_fs46.existsSync)(file)) return { telemetry: emptyResearchTelemetry(now52) }; + if (!(0, import_fs45.existsSync)(file)) return { telemetry: emptyResearchTelemetry(now52) }; try { - const parsed = researchTelemetrySchema.safeParse(JSON.parse((0, import_fs46.readFileSync)(file, "utf8"))); + const parsed = researchTelemetrySchema.safeParse(JSON.parse((0, import_fs45.readFileSync)(file, "utf8"))); return parsed.success ? { telemetry: parsed.data } : { telemetry: emptyResearchTelemetry(now52), diagnostic: "research telemetry is schema-invalid" }; } catch { return { telemetry: emptyResearchTelemetry(now52), diagnostic: "research telemetry is unreadable" }; @@ -74405,7 +74537,7 @@ function readResearchTelemetry(workspace, now52 = /* @__PURE__ */ new Date()) { function writeTelemetry(workspace, value) { const telemetry = researchTelemetrySchema.parse(value); const file = researchTelemetryFile(workspace); - (0, import_fs46.mkdirSync)(import_path49.default.dirname(file), { recursive: true }); + (0, import_fs45.mkdirSync)(import_path48.default.dirname(file), { recursive: true }); writeFileAtomic(file, `${JSON.stringify(telemetry, null, 2)} `); return telemetry; @@ -75692,23 +75824,22 @@ ${correctionMessage(invocation.role, validated.problem)}`; }; } async function runLargeObjectiveRole(invocation) { - const profile = invocation.config.runnerProfiles[invocation.runnerProfile]; - if (profile === void 0 || profile.runner !== "claude-code") { + const registry2 = invocation.registry ?? createDefaultRunnerRegistry(invocation.config); + let profile; + try { + profile = registry2.getProfile(invocation.runnerProfile); + } catch (cause) { return { ok: false, kind: "worker-unavailable", - problem: `Runner profile "${invocation.runnerProfile}" is not a Claude Code profile.` + problem: cause instanceof Error ? cause.message : `Runner profile "${invocation.runnerProfile}" is unavailable.` }; } - const claudeProfile = profile; - const probe = invocation.cachedProbe ?? await probeClaude(claudeProfile, { - ...invocation.signal !== void 0 ? { signal: invocation.signal } : {} - }); - if (!probe.found || probe.status === "unavailable" || probe.status === "error") { + if (profile.config.enabled !== true || profile.runner.invokeStructured === void 0) { return { ok: false, kind: "worker-unavailable", - problem: `The Claude Code CLI is not available (status ${probe.status}).` + problem: profile.config.enabled !== true ? `Runner profile "${invocation.runnerProfile}" is disabled.` : `Runner profile "${invocation.runnerProfile}" does not support structured orchestration roles.` }; } const prompt = [ @@ -75718,73 +75849,41 @@ async function runLargeObjectiveRole(invocation) { "", invocation.packet ].join("\n"); - const plan = buildClaudeInvocation({ - config: claudeProfile, - probe, + const result = await profile.runner.invokeStructured({ prompt, toolPolicy: invocation.role === "BUILDER" ? "implementation" : "inspect-only", - outputJsonSchema: OBJECTIVE_OUTPUT_JSON_SCHEMAS[invocation.role], - execution: { - workspaceRoot: invocation.cwd, - runDir: invocation.scratchDir, - timeoutMs: invocation.timeoutMs - } + schemaName: invocation.role, + outputJsonSchema: OBJECTIVE_OUTPUT_JSON_SCHEMAS[invocation.role] + }, { + workspaceRoot: invocation.cwd, + runDir: invocation.scratchDir, + timeoutMs: invocation.timeoutMs, + ...invocation.signal !== void 0 ? { signal: invocation.signal } : {} }); - try { - const processResult = await runSafeProcess({ - executable: plan.executable, - argv: plan.argv, - cwd: invocation.cwd, - timeoutMs: invocation.timeoutMs, - stdin: plan.stdin, - ...invocation.signal !== void 0 ? { signal: invocation.signal } : {} - }); - if (processResult.status === "cancelled") { - return { ok: false, kind: "cancelled", problem: "The worker invocation was cancelled.", probe }; - } - if (processResult.status !== "ok" && processResult.status !== "nonzero-exit") { - return { - ok: false, - kind: "worker-unavailable", - problem: processResult.failureReason ?? `the worker process ended with status ${processResult.status}`, - probe - }; - } - const parsed = parseClaudeEnvelope(processResult.stdout); - if (parsed.problem !== void 0) { - return { - ok: false, - kind: "invalid-output", - problem: claudeFailureProblem(parsed.problem, processResult), - probe - }; - } - const text93 = parsed.structuredResult !== void 0 ? JSON.stringify(parsed.structuredResult) : parsed.reportText ?? ""; - const validated = validateObjectiveOutput(invocation.role, text93); - if (!validated.ok) { - return { ok: false, kind: "invalid-output", problem: validated.problem, probe }; - } - const usage = usageFromEnvelope(parsed.envelope, 0); - const cost = costFromEnvelope(parsed.envelope); + if (result.outcome === "cancelled") { + return { ok: false, kind: "cancelled", problem: result.failureReason ?? "The worker invocation was cancelled." }; + } + if (result.outcome !== "completed" || result.text === void 0) { return { - ok: true, - output: validated.output, - raw: text93, - usage: { - inputTokens: usage?.inputTokens ?? null, - outputTokens: usage?.outputTokens ?? null, - costUsd: cost !== null && cost !== void 0 && cost.currency === "USD" ? cost.amount : null - }, - probe + ok: false, + kind: result.outcome === "malformed-output" ? "invalid-output" : "worker-unavailable", + problem: result.failureReason ?? result.error?.message ?? `Runner profile "${invocation.runnerProfile}" ended with ${result.outcome}.` }; - } finally { - cleanupTempFiles(plan); - try { - const { rmSync: rmSync82 } = await import("fs"); - rmSync82(import_path51.default.join(invocation.scratchDir, "tmp"), { recursive: true, force: true }); - } catch { - } } + const validated = validateObjectiveOutput(invocation.role, result.text); + if (!validated.ok) { + return { ok: false, kind: "invalid-output", problem: validated.problem }; + } + return { + ok: true, + output: validated.output, + raw: result.text, + usage: { + inputTokens: result.usage?.inputTokens ?? null, + outputTokens: result.usage?.outputTokens ?? null, + costUsd: result.cost?.currency === "USD" ? result.cost.amount : null + } + }; } async function applyPatch(workspaceRoot, patch) { const result = await runSafeProcess({ @@ -75890,14 +75989,14 @@ async function integrateObjective(input) { const reconcile = await runLargeObjectiveRole({ workspace: input.workspace, config: input.config, + registry: input.registry, runnerProfile: input.runnerProfile, role: "BUILDER", packet, cwd: input.workspace.rootDir, - scratchDir: import_path50.default.join(jobDir(input.workspace, input.jobId), "scratch"), + scratchDir: import_path49.default.join(jobDir(input.workspace, input.jobId), "scratch"), timeoutMs: input.reconcileTimeoutMs ?? 6e5, - ...input.signal !== void 0 ? { signal: input.signal } : {}, - ...input.cachedProbe !== void 0 ? { cachedProbe: input.cachedProbe } : {} + ...input.signal !== void 0 ? { signal: input.signal } : {} }); if (!reconcile.ok || reconcile.output.outcome !== "CANDIDATE_COMPLETE") { await abort(`reconciliation of ${entry2.unit.workUnitId} failed`); @@ -76347,7 +76446,7 @@ async function git3(cwd, argv2, timeoutMs = GIT_TIMEOUT_MS3) { return { ok: result.status === "ok", stdout: result.stdout, stderr: result.stderr }; } function worktreesRootDir(workspace, jobId) { - return import_path52.default.join(jobDir(workspace, jobId), "worktrees"); + return import_path50.default.join(jobDir(workspace, jobId), "worktrees"); } async function readCanonicalHead(workspace) { const head = await git3(workspace.rootDir, ["rev-parse", "HEAD"]); @@ -76366,13 +76465,13 @@ async function createWorkerWorktree(input) { } const dir = assertInsideWorkspace( input.workspace.rootDir, - import_path52.default.join(worktreesRootDir(input.workspace, input.jobId), name) + import_path50.default.join(worktreesRootDir(input.workspace, input.jobId), name) ); const baselineCommit = await readCanonicalHead(input.workspace); - if ((0, import_fs47.existsSync)(dir)) { + if ((0, import_fs46.existsSync)(dir)) { await removeWorkerWorktree(input.workspace, input.jobId, { dir }); } - (0, import_fs47.mkdirSync)(import_path52.default.dirname(dir), { recursive: true }); + (0, import_fs46.mkdirSync)(import_path50.default.dirname(dir), { recursive: true }); const added = await git3(input.workspace.rootDir, ["worktree", "add", "--detach", dir, baselineCommit], 18e4); if (!added.ok) { throw new OrchestrationError("SBO048", `git worktree add failed: ${added.stderr.slice(0, 500)}`, { @@ -76447,7 +76546,7 @@ async function runWorktreeVerification(handle, commands, signal) { async function removeWorkerWorktree(workspace, jobId, handle) { await git3(workspace.rootDir, ["worktree", "remove", "--force", handle.dir], 12e4); try { - (0, import_fs47.rmSync)(handle.dir, { recursive: true, force: true }); + (0, import_fs46.rmSync)(handle.dir, { recursive: true, force: true }); } catch { } await git3(workspace.rootDir, ["worktree", "prune"]); @@ -76456,14 +76555,14 @@ async function removeWorkerWorktree(workspace, jobId, handle) { async function pruneWorktrees(workspace, jobId) { const removed = []; const root = worktreesRootDir(workspace, jobId); - if ((0, import_fs47.existsSync)(root)) { + if ((0, import_fs46.existsSync)(root)) { const { readdirSync: readdirSync112 } = await import("fs"); for (const entry2 of readdirSync112(root, { withFileTypes: true })) { if (!entry2.isDirectory()) continue; - const dir = import_path52.default.join(root, entry2.name); + const dir = import_path50.default.join(root, entry2.name); await git3(workspace.rootDir, ["worktree", "remove", "--force", dir], 12e4); try { - (0, import_fs47.rmSync)(dir, { recursive: true, force: true }); + (0, import_fs46.rmSync)(dir, { recursive: true, force: true }); } catch { } removed.push(entry2.name); @@ -77248,16 +77347,15 @@ async function decomposeObjective(input, truth, relevantContractIds, acceptance) const large = await runLargeObjectiveRole({ workspace: input.workspace, config: input.config, + registry: input.registry, runnerProfile: selection.worker.runnerProfile ?? input.config.defaultRunner, role: "DECOMPOSER", packet, cwd: input.workspace.rootDir, - scratchDir: import_path47.default.join(jobDir(input.workspace, input.jobId), "scratch"), + scratchDir: import_path46.default.join(jobDir(input.workspace, input.jobId), "scratch"), timeoutMs: 6e5, - signal: input.signal, - cachedProbe: input.probeCache.probe + signal: input.signal }); - if (large.probe !== void 0) input.probeCache.probe = large.probe; return large; })(); input.countWorkerRun({ @@ -78269,18 +78367,18 @@ async function executeBuilder(context, prepared) { const reconcile = await runLargeObjectiveRole({ workspace: input.workspace, config: input.config, + registry: input.registry, runnerProfile: input.runnerProfile ?? input.config.defaultRunner, role: "BUILDER", packet: packet2, cwd: worktree.dir, - scratchDir: import_path47.default.join( + scratchDir: import_path46.default.join( jobDir(input.workspace, input.jobId), "scratch", `${prepared.unitId}-a${prepared.attempt}-depfix` ), timeoutMs: input.policy.objectives.builderTimeoutMs, - signal: input.signal, - cachedProbe: input.probeCache.probe + signal: input.signal }); if (!reconcile.ok || reconcile.output.outcome !== "CANDIDATE_COMPLETE") { const why = !reconcile.ok ? `${reconcile.kind}: ${reconcile.problem.slice(0, 400)}` : `worker outcome ${reconcile.output.outcome}: ${(reconcile.output.summary ?? "").slice(0, 300)}`; @@ -78293,7 +78391,6 @@ async function executeBuilder(context, prepared) { } }; } - if (reconcile.probe !== void 0) input.probeCache.probe = reconcile.probe; } if (prepared.priorCandidatePatch !== void 0 && prepared.priorCandidatePatch.trim().length > 0) { try { @@ -78384,20 +78481,19 @@ async function executeBuilder(context, prepared) { const result = await runLargeObjectiveRole({ workspace: input.workspace, config: input.config, + registry: input.registry, runnerProfile: input.runnerProfile ?? input.config.defaultRunner, role: "BUILDER", packet, cwd: worktree.dir, - scratchDir: import_path47.default.join( + scratchDir: import_path46.default.join( jobDir(input.workspace, input.jobId), "scratch", `${prepared.unitId}-a${prepared.attempt}` ), timeoutMs: input.policy.objectives.builderTimeoutMs, - signal: input.signal, - cachedProbe: input.probeCache.probe + signal: input.signal }); - if (result.probe !== void 0) input.probeCache.probe = result.probe; if (!result.ok && isStrongQuotaFailure(result.problem)) { const resource = quotaFailureResource({ observedAt: nowIso3(input), @@ -79080,16 +79176,15 @@ async function runSemanticEvaluation(context, graph, unitId) { const large = await runLargeObjectiveRole({ workspace: input.workspace, config: input.config, + registry: input.registry, runnerProfile: selection.worker.runnerProfile ?? input.config.defaultRunner, role: "EVALUATOR", packet: packetOverride ?? packet, cwd: input.workspace.rootDir, - scratchDir: import_path47.default.join(jobDir(input.workspace, input.jobId), "scratch"), + scratchDir: import_path46.default.join(jobDir(input.workspace, input.jobId), "scratch"), timeoutMs: 6e5, - signal: input.signal, - cachedProbe: input.probeCache.probe + signal: input.signal }); - if (large.probe !== void 0) input.probeCache.probe = large.probe; return large; }; const ranLocally = selection.worker.reasoningTier === "LOCAL_SMALL" && input.localManager !== void 0; @@ -79721,16 +79816,15 @@ async function maybeAggregateSemantically(context, graph) { const large = await runLargeObjectiveRole({ workspace: input.workspace, config: input.config, + registry: input.registry, runnerProfile: selection.worker.runnerProfile ?? input.config.defaultRunner, role: "AGGREGATOR", packet, cwd: input.workspace.rootDir, - scratchDir: import_path47.default.join(jobDir(input.workspace, input.jobId), "scratch"), + scratchDir: import_path46.default.join(jobDir(input.workspace, input.jobId), "scratch"), timeoutMs: 6e5, - signal: input.signal, - cachedProbe: input.probeCache.probe + signal: input.signal }); - if (large.probe !== void 0) input.probeCache.probe = large.probe; return large; })(); input.countWorkerRun({ @@ -79851,6 +79945,7 @@ async function integrateVerifiedCandidates(input, graph) { const result = await integrateObjective({ workspace: input.workspace, config: input.config, + registry: input.registry, jobId: input.jobId, // Reconciling a conflicting candidate is a BUILD-sized job, not a // question-sized one: the worker reads the conflict, understands two @@ -79869,7 +79964,6 @@ async function integrateVerifiedCandidates(input, graph) { clock: input.clock, idFactory: input.idFactory, signal: input.signal, - cachedProbe: input.probeCache.probe, onProgress: input.onProgress }); if (!result.ok) { @@ -80072,37 +80166,37 @@ var schedulingDecisionSchema = external_exports.object({ createdAt: shortText15 }).passthrough(); function schedulingDir(workspace, jobId) { - return assertInsideWorkspace(workspace.rootDir, import_path53.default.join(jobDir(workspace, jobId), "scheduling")); + return assertInsideWorkspace(workspace.rootDir, import_path51.default.join(jobDir(workspace, jobId), "scheduling")); } function decisionsFile2(workspace, jobId) { return assertInsideWorkspace( workspace.rootDir, - import_path53.default.join(schedulingDir(workspace, jobId), "decisions.jsonl") + import_path51.default.join(schedulingDir(workspace, jobId), "decisions.jsonl") ); } function appendSchedulingDecision(workspace, record32, options) { const validated = schedulingDecisionSchema.parse(record32); const dir = schedulingDir(workspace, record32.jobId); - (0, import_fs48.mkdirSync)(dir, { recursive: true }); + (0, import_fs47.mkdirSync)(dir, { recursive: true }); const file = decisionsFile2(workspace, record32.jobId); const line = `${JSON.stringify(validated)} `; - const existing = (0, import_fs48.existsSync)(file) ? (0, import_fs48.readFileSync)(file, "utf8") : ""; + const existing = (0, import_fs47.existsSync)(file) ? (0, import_fs47.readFileSync)(file, "utf8") : ""; const lines = existing.split("\n").filter((entry2) => entry2.length > 0); if (lines.length + 1 > options.maxRecords) { const retained = [...lines, line.trimEnd()].slice(-options.maxRecords); writeFileAtomic(file, `${retained.join("\n")} `); } else { - (0, import_fs48.appendFileSync)(file, line, "utf8"); + (0, import_fs47.appendFileSync)(file, line, "utf8"); } return validated; } function readSchedulingDecisions(workspace, jobId, options = {}) { const file = decisionsFile2(workspace, jobId); - if (!(0, import_fs48.existsSync)(file)) return []; + if (!(0, import_fs47.existsSync)(file)) return []; const records = []; - for (const line of (0, import_fs48.readFileSync)(file, "utf8").split("\n")) { + for (const line of (0, import_fs47.readFileSync)(file, "utf8").split("\n")) { if (line.length === 0) continue; try { const parsed = schedulingDecisionSchema.safeParse(JSON.parse(line)); @@ -80665,7 +80759,7 @@ var ID_PATTERN8 = /^[A-Za-z0-9][A-Za-z0-9._-]{0,63}$/; function approvalsDir(workspace, jobId) { return assertInsideWorkspace( workspace.rootDir, - import_path54.default.join(jobDir(workspace, jobId), "api-approvals") + import_path52.default.join(jobDir(workspace, jobId), "api-approvals") ); } function approvalFile(workspace, jobId, approvalId) { @@ -80674,26 +80768,26 @@ function approvalFile(workspace, jobId, approvalId) { } return assertInsideWorkspace( workspace.rootDir, - import_path54.default.join(approvalsDir(workspace, jobId), `${approvalId}.json`) + import_path52.default.join(approvalsDir(workspace, jobId), `${approvalId}.json`) ); } function writeApiSpendApproval(workspace, approval) { const validated = apiSpendApprovalSchema.parse(approval); const file = approvalFile(workspace, validated.jobId, validated.approvalId); - (0, import_fs49.mkdirSync)(import_path54.default.dirname(file), { recursive: true }); + (0, import_fs48.mkdirSync)(import_path52.default.dirname(file), { recursive: true }); writeFileAtomic(file, `${JSON.stringify(validated, null, 2)} `); return validated; } function listApiSpendApprovals(workspace, jobId, options = {}) { const dir = approvalsDir(workspace, jobId); - if (!(0, import_fs49.existsSync)(dir)) return []; + if (!(0, import_fs48.existsSync)(dir)) return []; const approvals = []; - for (const name of (0, import_fs49.readdirSync)(dir).sort()) { + for (const name of (0, import_fs48.readdirSync)(dir).sort()) { if (!name.endsWith(".json")) continue; try { const parsed = apiSpendApprovalSchema.safeParse( - JSON.parse((0, import_fs49.readFileSync)(import_path54.default.join(dir, name), "utf8")) + JSON.parse((0, import_fs48.readFileSync)(import_path52.default.join(dir, name), "utf8")) ); if (parsed.success) approvals.push(parsed.data); } catch { @@ -80704,8 +80798,8 @@ function listApiSpendApprovals(workspace, jobId, options = {}) { } function readApiSpendApproval(workspace, jobId, approvalId) { const file = approvalFile(workspace, jobId, approvalId); - if (!(0, import_fs49.existsSync)(file)) return void 0; - const parsed = apiSpendApprovalSchema.safeParse(JSON.parse((0, import_fs49.readFileSync)(file, "utf8"))); + if (!(0, import_fs48.existsSync)(file)) return void 0; + const parsed = apiSpendApprovalSchema.safeParse(JSON.parse((0, import_fs48.readFileSync)(file, "utf8"))); return parsed.success ? parsed.data : void 0; } function requestApiSpendApproval(input) { @@ -80853,14 +80947,14 @@ var MANUAL_TELEMETRY_SOURCE = "manual-file"; function quotaTelemetryFilePath(workspace) { return assertInsideWorkspace( workspace.rootDir, - import_path55.default.join(workspace.sidecarDir, QUOTA_TELEMETRY_FILE_NAME) + import_path53.default.join(workspace.sidecarDir, QUOTA_TELEMETRY_FILE_NAME) ); } function readQuotaTelemetryFile(workspace) { const file = quotaTelemetryFilePath(workspace); - if (!(0, import_fs50.existsSync)(file)) return quotaTelemetryFileSchema.parse({}); + if (!(0, import_fs49.existsSync)(file)) return quotaTelemetryFileSchema.parse({}); try { - const parsed = quotaTelemetryFileSchema.safeParse(JSON.parse((0, import_fs50.readFileSync)(file, "utf8"))); + const parsed = quotaTelemetryFileSchema.safeParse(JSON.parse((0, import_fs49.readFileSync)(file, "utf8"))); return parsed.success ? parsed.data : quotaTelemetryFileSchema.parse({}); } catch { return quotaTelemetryFileSchema.parse({}); @@ -82895,7 +82989,7 @@ function specExcerptFor(workspace, specName, maxChars) { if (file === void 0) continue; try { parts.push(`--- ${kind} --- -${(0, import_fs44.readFileSync)(file.path, "utf8")}`); +${(0, import_fs43.readFileSync)(file.path, "utf8")}`); } catch { } } @@ -82936,7 +83030,6 @@ async function driveJob(deps4, jobId, options = {}) { return { stop: { kind: "final", status: job.status }, job }; } } - const probeCache = { probe: void 0 }; const localManager = createLocalManager(deps4.config, (event) => { emit22("local-model", `${event.type}: ${event.detail}`); if (event.type === "ready") { @@ -83023,7 +83116,6 @@ async function driveJob(deps4, jobId, options = {}) { case "RUN_ROLE": { const outcome = await handleRoleDecision(deps4, jobId, decision, { localManager, - probeCache, signal, emit: emit22 }); @@ -83422,6 +83514,7 @@ async function driveJob(deps4, jobId, options = {}) { }) : mission !== void 0 ? await driveObjective({ workspace: deps4.workspace, config: deps4.config, + registry: deps4.registry, jobId, specName: job.specName, node, @@ -83431,7 +83524,6 @@ async function driveJob(deps4, jobId, options = {}) { allowDirty, runnerProfile: decision.worker.runnerProfile, localManager, - probeCache, ...deps4.clock !== void 0 ? { clock: deps4.clock } : {}, ...deps4.idFactory !== void 0 ? { idFactory: deps4.idFactory } : {}, ...signal !== void 0 ? { signal } : {}, @@ -83863,7 +83955,7 @@ function buildCriteriaEvidence(input) { const normalized = input.changedPaths.map((entry2) => entry2.replaceAll("\\", "/")); const existing = /* @__PURE__ */ new Set(); for (const changed of normalized) { - if ((0, import_fs44.existsSync)(import_path46.default.join(input.workspaceRoot, changed))) existing.add(changed); + if ((0, import_fs43.existsSync)(import_path45.default.join(input.workspaceRoot, changed))) existing.add(changed); } return { existingPaths: existing, @@ -84473,7 +84565,7 @@ async function handleRoleDecision(deps4, jobId, decision, runtime) { code: "LARGE_WORKER_FAILED", message: `The large-agent ${role} failed twice: ${result.problem.slice(0, 500)}`, remediation: [ - "Check the Claude Code installation with `specbridge runner doctor claude-code`.", + `Check runner profile "${decision.worker.runnerProfile ?? deps4.config.defaultRunner}" with \`specbridge runner doctor ${decision.worker.runnerProfile ?? deps4.config.defaultRunner}\`.`, // The excerpt is the whole point of the remediation. A job blocked // on "the response is not a single valid JSON document" with // nothing retained leaves an operator a message and no evidence, @@ -84608,15 +84700,14 @@ async function runRole(deps4, jobId, role, decision, packet, runtime) { const result = await runLargeRole({ workspace: deps4.workspace, config: deps4.config, + registry: deps4.registry, runnerProfile: decision.worker.runnerProfile ?? deps4.config.defaultRunner, role, packet, - scratchDir: import_path46.default.join(jobDir(deps4.workspace, jobId), "scratch"), + scratchDir: import_path45.default.join(jobDir(deps4.workspace, jobId), "scratch"), timeoutMs: 6e5, - signal: runtime.signal, - cachedProbe: runtime.probeCache.probe + signal: runtime.signal }); - if (result.probe !== void 0) runtime.probeCache.probe = result.probe; return result; } async function applyRoleOutput(deps4, jobId, role, result, context, node, activePlan) { @@ -84954,17 +85045,17 @@ async function git22(cwd, argv2, timeoutMs = GIT_TIMEOUT_MS22) { return { ok: result.status === "ok", stdout: result.stdout, stderr: result.stderr }; } function seedSidecar(source, targetRoot, specNames) { - const sidecar = import_path56.default.join(targetRoot, ".specbridge"); - (0, import_fs51.mkdirSync)(sidecar, { recursive: true }); - const config2 = import_path56.default.join(source.sidecarDir, "config.json"); - if ((0, import_fs51.existsSync)(config2)) (0, import_fs51.copyFileSync)(config2, import_path56.default.join(sidecar, "config.json")); - const stateDir = import_path56.default.join(source.sidecarDir, "state", "specs"); - if (!(0, import_fs51.existsSync)(stateDir)) return; - const targetState = import_path56.default.join(sidecar, "state", "specs"); - (0, import_fs51.mkdirSync)(targetState, { recursive: true }); + const sidecar = import_path54.default.join(targetRoot, ".specbridge"); + (0, import_fs50.mkdirSync)(sidecar, { recursive: true }); + const config2 = import_path54.default.join(source.sidecarDir, "config.json"); + if ((0, import_fs50.existsSync)(config2)) (0, import_fs50.copyFileSync)(config2, import_path54.default.join(sidecar, "config.json")); + const stateDir = import_path54.default.join(source.sidecarDir, "state", "specs"); + if (!(0, import_fs50.existsSync)(stateDir)) return; + const targetState = import_path54.default.join(sidecar, "state", "specs"); + (0, import_fs50.mkdirSync)(targetState, { recursive: true }); for (const name of new Set(specNames)) { - const file = import_path56.default.join(stateDir, `${name}.json`); - if ((0, import_fs51.existsSync)(file)) (0, import_fs51.copyFileSync)(file, import_path56.default.join(targetState, `${name}.json`)); + const file = import_path54.default.join(stateDir, `${name}.json`); + if ((0, import_fs50.existsSync)(file)) (0, import_fs50.copyFileSync)(file, import_path54.default.join(targetState, `${name}.json`)); } } function syntheticNode(evaluationCase) { @@ -84992,8 +85083,8 @@ async function evaluateLocalRuntime(input) { const modes = input.modes ?? ["DIRECT_MODEL", "HARNESS"]; const binding = resolveLocalHarnessBinding(input.config); const harnessProfile = input.harnessProfile ?? binding.profileName ?? void 0; - const workRoot = input.workRoot ?? import_path56.default.join(input.workspace.sidecarDir, "local-runtime-eval"); - (0, import_fs51.mkdirSync)(workRoot, { recursive: true }); + const workRoot = input.workRoot ?? import_path54.default.join(input.workspace.sidecarDir, "local-runtime-eval"); + (0, import_fs50.mkdirSync)(workRoot, { recursive: true }); const head = await git22(input.workspace.rootDir, ["rev-parse", "HEAD"]); if (!head.ok) { throw new OrchestrationError( @@ -85052,7 +85143,7 @@ async function evaluateLocalRuntime(input) { } async function runArm(options) { const { input, evaluationCase, mode, workRoot } = options; - const armDir = import_path56.default.join( + const armDir = import_path54.default.join( workRoot, `${evaluationCase.caseId}-${mode === "HARNESS" ? "harness" : "direct"}`.replace( /[^A-Za-z0-9._-]/g, @@ -85083,9 +85174,9 @@ async function runArm(options) { if (mode === "HARNESS" && options.harnessProfile === void 0) { return unavailable("no harness profile is bound or configured for the harness arm"); } - if ((0, import_fs51.existsSync)(armDir)) { + if ((0, import_fs50.existsSync)(armDir)) { await git22(input.workspace.rootDir, ["worktree", "remove", "--force", armDir]); - (0, import_fs51.rmSync)(armDir, { recursive: true, force: true }); + (0, import_fs50.rmSync)(armDir, { recursive: true, force: true }); } const added = await git22( input.workspace.rootDir, @@ -85161,7 +85252,7 @@ async function runArm(options) { if (input.keepWorktrees !== true) { await git22(input.workspace.rootDir, ["worktree", "remove", "--force", armDir]); try { - (0, import_fs51.rmSync)(armDir, { recursive: true, force: true }); + (0, import_fs50.rmSync)(armDir, { recursive: true, force: true }); } catch { } await git22(input.workspace.rootDir, ["worktree", "prune"]); @@ -85879,46 +85970,46 @@ function assertRecordId4(kind, id) { function qualificationDir(workspace) { return assertInsideWorkspace( workspace.rootDir, - import_path57.default.join(workspace.rootDir, ".specbridge", "qualification") + import_path55.default.join(workspace.rootDir, ".specbridge", "qualification") ); } function dogfoodRunDir(workspace, runId) { assertRecordId4("dogfood run", runId); - return assertInsideWorkspace(workspace.rootDir, import_path57.default.join(qualificationDir(workspace), runId)); + return assertInsideWorkspace(workspace.rootDir, import_path55.default.join(qualificationDir(workspace), runId)); } function runFile(workspace, runId) { - return assertInsideWorkspace(workspace.rootDir, import_path57.default.join(dogfoodRunDir(workspace, runId), "run.json")); + return assertInsideWorkspace(workspace.rootDir, import_path55.default.join(dogfoodRunDir(workspace, runId), "run.json")); } function recordDir2(workspace, runId, kind) { - return assertInsideWorkspace(workspace.rootDir, import_path57.default.join(dogfoodRunDir(workspace, runId), kind)); + return assertInsideWorkspace(workspace.rootDir, import_path55.default.join(dogfoodRunDir(workspace, runId), kind)); } function recordFile2(workspace, runId, kind, id) { assertRecordId4(kind, id); return assertInsideWorkspace( workspace.rootDir, - import_path57.default.join(recordDir2(workspace, runId, kind), `${id}.json`) + import_path55.default.join(recordDir2(workspace, runId, kind), `${id}.json`) ); } function writeRecord2(file, value) { - (0, import_fs52.mkdirSync)(import_path57.default.dirname(file), { recursive: true }); + (0, import_fs51.mkdirSync)(import_path55.default.dirname(file), { recursive: true }); writeFileAtomic(file, `${JSON.stringify(value, null, 2)} `); } function readRecord2(file, parse3) { - if (!(0, import_fs52.existsSync)(file)) return void 0; + if (!(0, import_fs51.existsSync)(file)) return void 0; try { - return parse3(JSON.parse((0, import_fs52.readFileSync)(file, "utf8"))); + return parse3(JSON.parse((0, import_fs51.readFileSync)(file, "utf8"))); } catch { return void 0; } } function listRecords2(workspace, runId, kind, parse3) { const dir = recordDir2(workspace, runId, kind); - if (!(0, import_fs52.existsSync)(dir)) return []; + if (!(0, import_fs51.existsSync)(dir)) return []; const records = []; - for (const entry2 of (0, import_fs52.readdirSync)(dir).sort()) { + for (const entry2 of (0, import_fs51.readdirSync)(dir).sort()) { if (!entry2.endsWith(".json")) continue; - const record32 = readRecord2(import_path57.default.join(dir, entry2), parse3); + const record32 = readRecord2(import_path55.default.join(dir, entry2), parse3); if (record32 !== void 0) records.push(record32); } return records; @@ -85945,9 +86036,9 @@ function requireDogfoodRun(workspace, runId) { } function listDogfoodRuns(workspace) { const dir = qualificationDir(workspace); - if (!(0, import_fs52.existsSync)(dir)) return []; + if (!(0, import_fs51.existsSync)(dir)) return []; const runs = []; - for (const entry2 of (0, import_fs52.readdirSync)(dir, { withFileTypes: true })) { + for (const entry2 of (0, import_fs51.readdirSync)(dir, { withFileTypes: true })) { if (!entry2.isDirectory()) continue; if (!ID_PATTERN9.test(entry2.name)) continue; const run = readDogfoodRun(workspace, entry2.name); @@ -85984,9 +86075,9 @@ function writeQualificationArtifact(workspace, runId, name, contents) { } const file = assertInsideWorkspace( workspace.rootDir, - import_path57.default.join(recordDir2(workspace, runId, "reports"), name) + import_path55.default.join(recordDir2(workspace, runId, "reports"), name) ); - (0, import_fs52.mkdirSync)(import_path57.default.dirname(file), { recursive: true }); + (0, import_fs51.mkdirSync)(import_path55.default.dirname(file), { recursive: true }); writeFileAtomic(file, contents); return file; } @@ -85996,7 +86087,7 @@ function qualificationArtifactPath(workspace, runId, name) { } return assertInsideWorkspace( workspace.rootDir, - import_path57.default.join(recordDir2(workspace, runId, "reports"), name) + import_path55.default.join(recordDir2(workspace, runId, "reports"), name) ); } var PROFILE_ORDER = ["offline", "local", "subscription", "full"]; @@ -86682,7 +86773,7 @@ function runPreflight(input) { "Offline qualification does not need a target: run it with --profile offline." ]) ); - } else if (!(0, import_fs53.existsSync)(target.repositoryPath) || !(0, import_fs53.statSync)(target.repositoryPath).isDirectory()) { + } else if (!(0, import_fs52.existsSync)(target.repositoryPath) || !(0, import_fs52.statSync)(target.repositoryPath).isDirectory()) { findings2.push( refuse2( "target.repository", @@ -86878,7 +86969,7 @@ function normalizeTargetPath(value) { if (value === null || value === void 0) return null; const trimmed = value.trim(); if (trimmed.length === 0) return null; - return import_path58.default.resolve(trimmed); + return import_path56.default.resolve(trimmed); } function add(current, reported) { if (reported === null || reported === void 0) return current; @@ -91000,20 +91091,20 @@ var import_node_fs6 = require("fs"); var import_node_path8 = __toESM(require("path"), 1); // ../../packages/drift/dist/index.js -var import_fs54 = require("fs"); -var import_path59 = __toESM(require("path"), 1); +var import_fs53 = require("fs"); +var import_path57 = __toESM(require("path"), 1); var import_picomatch = __toESM(require_picomatch2(), 1); +var import_fs54 = require("fs"); +var import_path58 = __toESM(require("path"), 1); var import_fs55 = require("fs"); -var import_path60 = __toESM(require("path"), 1); +var import_path59 = __toESM(require("path"), 1); var import_fs56 = require("fs"); -var import_path61 = __toESM(require("path"), 1); +var import_path60 = __toESM(require("path"), 1); var import_fs57 = require("fs"); -var import_path62 = __toESM(require("path"), 1); +var import_path61 = __toESM(require("path"), 1); var import_fs58 = require("fs"); -var import_path63 = __toESM(require("path"), 1); -var import_fs59 = require("fs"); var import_crypto25 = require("crypto"); -var import_path64 = __toESM(require("path"), 1); +var import_path62 = __toESM(require("path"), 1); var taskEvidenceSchema = external_exports.object({ taskId: external_exports.string().min(1), status: external_exports.enum(["recorded", "verified", "rejected"]), @@ -91114,24 +91205,24 @@ var verificationPolicySchema = external_exports.object({ } }); function policyDir(workspace) { - return import_path59.default.join(workspace.sidecarDir, "policies"); + return import_path57.default.join(workspace.sidecarDir, "policies"); } function policyPath(workspace, specName) { - const resolved2 = import_path59.default.resolve(policyDir(workspace), `${specName}.json`); - const relative = import_path59.default.relative(workspace.rootDir, resolved2); - if (relative.startsWith("..") || import_path59.default.isAbsolute(relative)) { - return import_path59.default.join(policyDir(workspace), "invalid-spec-name.json"); + const resolved2 = import_path57.default.resolve(policyDir(workspace), `${specName}.json`); + const relative = import_path57.default.relative(workspace.rootDir, resolved2); + if (relative.startsWith("..") || import_path57.default.isAbsolute(relative)) { + return import_path57.default.join(policyDir(workspace), "invalid-spec-name.json"); } return resolved2; } function readVerificationPolicy(workspace, specName, explicitPath) { - const filePath = explicitPath !== void 0 ? import_path59.default.resolve(workspace.rootDir, explicitPath) : policyPath(workspace, specName); - if (!(0, import_fs54.existsSync)(filePath)) { + const filePath = explicitPath !== void 0 ? import_path57.default.resolve(workspace.rootDir, explicitPath) : policyPath(workspace, specName); + if (!(0, import_fs53.existsSync)(filePath)) { return { path: filePath, exists: false, diagnostics: [] }; } let parsed; try { - parsed = JSON.parse((0, import_fs54.readFileSync)(filePath, "utf8")); + parsed = JSON.parse((0, import_fs53.readFileSync)(filePath, "utf8")); } catch (cause) { return { path: filePath, @@ -91194,7 +91285,7 @@ function resolveEffectivePolicy(workspace, specName, options = {}) { const storedMode = policy?.mode ?? "advisory"; const strictFromCli = options.strict === true && storedMode !== "strict"; const mode = options.strict === true ? "strict" : storedMode; - const workspaceRelativePolicyPath = import_path59.default.relative(workspace.rootDir, read.path).split(import_path59.default.sep).join("/"); + const workspaceRelativePolicyPath = import_path57.default.relative(workspace.rootDir, read.path).split(import_path57.default.sep).join("/"); return { specName, mode, @@ -91355,33 +91446,33 @@ function mergeNumstat(files, stats) { function sniffBinary(absolutePath) { let fd; try { - fd = (0, import_fs55.openSync)(absolutePath, "r"); + fd = (0, import_fs54.openSync)(absolutePath, "r"); const buffer = Buffer.alloc(8e3); - const bytesRead = (0, import_fs55.readSync)(fd, buffer, 0, buffer.length, 0); + const bytesRead = (0, import_fs54.readSync)(fd, buffer, 0, buffer.length, 0); return buffer.subarray(0, bytesRead).includes(0); } catch { return false; } finally { - if (fd !== void 0) (0, import_fs55.closeSync)(fd); + if (fd !== void 0) (0, import_fs54.closeSync)(fd); } } function flagSymlinkEscapes(repoRoot, files) { const resolvedRoot = (() => { try { - return (0, import_fs55.realpathSync)(repoRoot); + return (0, import_fs54.realpathSync)(repoRoot); } catch { - return import_path60.default.resolve(repoRoot); + return import_path58.default.resolve(repoRoot); } })(); for (const file of files) { if (file.changeType === "deleted") continue; - const absolute = import_path60.default.join(repoRoot, file.path.split("/").join(import_path60.default.sep)); + const absolute = import_path58.default.join(repoRoot, file.path.split("/").join(import_path58.default.sep)); try { - const stats = (0, import_fs55.lstatSync)(absolute); + const stats = (0, import_fs54.lstatSync)(absolute); if (!stats.isSymbolicLink()) continue; - const target = (0, import_fs55.realpathSync)(absolute); - const relative = import_path60.default.relative(resolvedRoot, target); - if (relative.startsWith("..") || import_path60.default.isAbsolute(relative)) { + const target = (0, import_fs54.realpathSync)(absolute); + const relative = import_path58.default.relative(resolvedRoot, target); + if (relative.startsWith("..") || import_path58.default.isAbsolute(relative)) { file.symlinkOutsideRepository = true; } } catch { @@ -91509,7 +91600,7 @@ async function resolveComparison(repoRoot, request, options = {}) { const known = new Set(files.map((file) => file.path)); for (const token of untracked.stdout.split("\0")) { if (token.length === 0 || known.has(token)) continue; - const absolute = import_path60.default.join(repoRoot, token.split("/").join(import_path60.default.sep)); + const absolute = import_path58.default.join(repoRoot, token.split("/").join(import_path58.default.sep)); files.push({ path: token, changeType: "untracked", @@ -91589,9 +91680,9 @@ function specMatchReasons(specName, policy, validEvidencePaths, designPathRefere function readSpecEvidenceRecords(workspace, specName) { const byTask = /* @__PURE__ */ new Map(); let invalidRecordCount = 0; - const specDir = import_path61.default.join(workspace.sidecarDir, "evidence", specName); - if ((0, import_fs56.existsSync)(specDir)) { - const taskDirs = (0, import_fs56.readdirSync)(specDir, { withFileTypes: true }).filter((entry2) => entry2.isDirectory()).map((entry2) => entry2.name).sort((a2, b) => a2.localeCompare(b, "en")); + const specDir = import_path59.default.join(workspace.sidecarDir, "evidence", specName); + if ((0, import_fs55.existsSync)(specDir)) { + const taskDirs = (0, import_fs55.readdirSync)(specDir, { withFileTypes: true }).filter((entry2) => entry2.isDirectory()).map((entry2) => entry2.name).sort((a2, b) => a2.localeCompare(b, "en")); for (const taskDir of taskDirs) { const { records, diagnostics } = listTaskEvidence(workspace, specName, taskDir); invalidRecordCount += diagnostics.length; @@ -91638,7 +91729,7 @@ async function buildSpecVerificationContext(options) { } if (effective("tasks") && tasksStage !== void 0) { const planHash2 = typeof tasksStage.approvedPlanHash === "string" ? tasksStage.approvedPlanHash : tryTaskPlanHashOfFile( - import_path61.default.join(workspace.rootDir, tasksStage.file.split("/").join(import_path61.default.sep)) + import_path59.default.join(workspace.rootDir, tasksStage.file.split("/").join(import_path59.default.sep)) ); if (planHash2 !== void 0) approved.tasksPlanHash = planHash2; } @@ -91866,7 +91957,7 @@ async function evaluateGlobalRules(rules, context) { return { diagnostics, disabledRules }; } function repoRelative(workspace, absolutePath) { - return import_path62.default.relative(workspace.rootDir, absolutePath).split(import_path62.default.sep).join("/"); + return import_path60.default.relative(workspace.rootDir, absolutePath).split(import_path60.default.sep).join("/"); } function isSpecInfraPath(candidate) { return candidate === ".git" || candidate.startsWith(".git/") || candidate.startsWith(".kiro/") || candidate.startsWith(".specbridge/"); @@ -92547,14 +92638,14 @@ var sbv018 = { if (designDocument === void 0) return []; const designFile = designDocument.filePath; const designRepoPath = designFile !== void 0 ? repoRelative(context.workspace, designFile) : void 0; - const specDir = import_path62.default.join(context.workspace.rootDir, ".kiro", "specs", context.specName); + const specDir = import_path60.default.join(context.workspace.rootDir, ".kiro", "specs", context.specName); return context.traceability.designPathReferences.filter((reference) => !reference.isGlob).filter((reference) => { - const fromRoot = import_path62.default.join( + const fromRoot = import_path60.default.join( context.workspace.rootDir, - reference.path.split("/").join(import_path62.default.sep) + reference.path.split("/").join(import_path60.default.sep) ); - const fromSpecDir = import_path62.default.join(specDir, reference.path.split("/").join(import_path62.default.sep)); - return !(0, import_fs57.existsSync)(fromRoot) && !(0, import_fs57.existsSync)(fromSpecDir); + const fromSpecDir = import_path60.default.join(specDir, reference.path.split("/").join(import_path60.default.sep)); + return !(0, import_fs56.existsSync)(fromRoot) && !(0, import_fs56.existsSync)(fromSpecDir); }).map( (reference) => makeDiagnostic({ rule: this, @@ -92801,9 +92892,9 @@ function loadSpecMatchingInfo(workspace, folder, options) { } } const evidencePaths = /* @__PURE__ */ new Set(); - const evidenceDir2 = import_path63.default.join(workspace.sidecarDir, "evidence", folder.name); - if ((0, import_fs58.existsSync)(evidenceDir2)) { - for (const entry2 of (0, import_fs59.readdirSync)(evidenceDir2, { withFileTypes: true })) { + const evidenceDir2 = import_path61.default.join(workspace.sidecarDir, "evidence", folder.name); + if ((0, import_fs57.existsSync)(evidenceDir2)) { + for (const entry2 of (0, import_fs58.readdirSync)(evidenceDir2, { withFileTypes: true })) { if (!entry2.isDirectory()) continue; const { records } = listTaskEvidence(workspace, folder.name, entry2.name); for (const record5 of records) { @@ -92912,8 +93003,8 @@ async function verifySpecs(request) { let artifactsDir; const ensureArtifactsDir = () => { if (artifactsDir === void 0) { - const base = request.reportsDir ?? import_path64.default.join(workspace.sidecarDir, "reports"); - artifactsDir = import_path64.default.join(base, verificationId); + const base = request.reportsDir ?? import_path62.default.join(workspace.sidecarDir, "reports"); + artifactsDir = import_path62.default.join(base, verificationId); } return artifactsDir; }; @@ -92936,8 +93027,8 @@ async function verifySpecs(request) { onCommandFinished: (result, stdout, stderr) => { const dir = ensureArtifactsDir(); const safeName = result.name.replace(/[^A-Za-z0-9._-]+/g, "-"); - writeFileAtomic(import_path64.default.join(dir, "commands", `${safeName}.stdout.log`), stdout); - writeFileAtomic(import_path64.default.join(dir, "commands", `${safeName}.stderr.log`), stderr); + writeFileAtomic(import_path62.default.join(dir, "commands", `${safeName}.stdout.log`), stdout); + writeFileAtomic(import_path62.default.join(dir, "commands", `${safeName}.stderr.log`), stderr); } } : {} }) : { mode: "none", commands: [], missingRequired: [] }; @@ -93088,7 +93179,7 @@ async function verifySpecs(request) { verificationReportSchema.parse(report); if (persistArtifacts && artifactsDir !== void 0) { writeFileAtomic( - import_path64.default.join(artifactsDir, "report.json"), + import_path62.default.join(artifactsDir, "report.json"), `${JSON.stringify(report, null, 2)} ` ); @@ -93197,18 +93288,18 @@ function resolveExitCode(report, comparison, commands, failOn) { } // ../../packages/templates/dist/index.js +var import_fs59 = require("fs"); +var import_path63 = __toESM(require("path"), 1); var import_fs60 = require("fs"); -var import_path65 = __toESM(require("path"), 1); +var import_path64 = __toESM(require("path"), 1); var import_fs61 = require("fs"); +var import_path65 = __toESM(require("path"), 1); var import_path66 = __toESM(require("path"), 1); var import_fs62 = require("fs"); var import_path67 = __toESM(require("path"), 1); -var import_path68 = __toESM(require("path"), 1); var import_fs63 = require("fs"); -var import_path69 = __toESM(require("path"), 1); -var import_fs64 = require("fs"); var import_os = require("os"); -var import_path70 = __toESM(require("path"), 1); +var import_path68 = __toESM(require("path"), 1); var SPECBRIDGE_VERSION = "1.0.0"; var TEMPLATE_ERROR_CODES = { SBT001: "template not found", @@ -94037,11 +94128,11 @@ function readTemplatePackDirectory(dir) { { path: currentDir } ); } - const entries = (0, import_fs60.readdirSync)(currentDir, { withFileTypes: true }).sort( + const entries = (0, import_fs59.readdirSync)(currentDir, { withFileTypes: true }).sort( (a2, b) => a2.name.localeCompare(b.name, "en") ); for (const entry2 of entries) { - const entryPath = import_path65.default.join(currentDir, entry2.name); + const entryPath = import_path63.default.join(currentDir, entry2.name); const entryRelative = relative === "" ? entry2.name : `${relative}/${entry2.name}`; const stat = statNoFollow(entryPath); if (stat.isSymbolicLink()) { @@ -94093,7 +94184,7 @@ function readTemplatePackDirectory(dir) { { path: dir } ); } - const buffer = (0, import_fs60.readFileSync)(entryPath); + const buffer = (0, import_fs59.readFileSync)(entryPath); const text15 = buffer.toString("utf8"); if (!Buffer.from(text15, "utf8").equals(buffer)) { throw new TemplateError( @@ -94119,7 +94210,7 @@ function readTemplatePackDirectory(dir) { } function statNoFollow(target) { try { - return (0, import_fs60.lstatSync)(target); + return (0, import_fs59.lstatSync)(target); } catch (cause) { throw new TemplateError( "SBT007", @@ -94483,7 +94574,7 @@ var BUILTIN_TEMPLATE_PACKS = [ } ]; function projectTemplatesDir(workspace) { - return import_path66.default.join(workspace.sidecarDir, "templates"); + return import_path64.default.join(workspace.sidecarDir, "templates"); } function builtinEntries(options) { const entries = []; @@ -94508,11 +94599,11 @@ function builtinEntries(options) { function projectEntries(workspace, options, diagnostics) { if (workspace === void 0) return []; const dir = projectTemplatesDir(workspace); - if (!(0, import_fs61.existsSync)(dir)) return []; + if (!(0, import_fs60.existsSync)(dir)) return []; const entries = []; let names; try { - names = (0, import_fs61.readdirSync)(dir, { withFileTypes: true }).filter((entry2) => entry2.isDirectory() && !entry2.isSymbolicLink()).map((entry2) => entry2.name).sort((a2, b) => a2.localeCompare(b, "en")); + names = (0, import_fs60.readdirSync)(dir, { withFileTypes: true }).filter((entry2) => entry2.isDirectory() && !entry2.isSymbolicLink()).map((entry2) => entry2.name).sort((a2, b) => a2.localeCompare(b, "en")); } catch (cause) { diagnostics.push({ severity: "warning", @@ -94522,7 +94613,7 @@ function projectEntries(workspace, options, diagnostics) { return []; } for (const name of names) { - const packDir = import_path66.default.join(dir, name); + const packDir = import_path64.default.join(dir, name); let pack; try { const data = readTemplatePackDirectory(packDir); @@ -94766,7 +94857,7 @@ var templateRecordSchema = external_exports.discriminatedUnion("type", [ templateScaffoldRecordSchema ]); function templateRecordsPath(workspace) { - return import_path67.default.join(workspace.sidecarDir, TEMPLATE_RECORDS_FILE_NAME); + return import_path65.default.join(workspace.sidecarDir, TEMPLATE_RECORDS_FILE_NAME); } var recordCounter = 0; function newTemplateRecordId(clock = systemClock) { @@ -94777,8 +94868,8 @@ function appendTemplateRecord(workspace, record5) { const validated = templateRecordSchema.parse(record5); const filePath = templateRecordsPath(workspace); try { - (0, import_fs62.mkdirSync)(workspace.sidecarDir, { recursive: true }); - (0, import_fs62.appendFileSync)(filePath, `${JSON.stringify(validated)} + (0, import_fs61.mkdirSync)(workspace.sidecarDir, { recursive: true }); + (0, import_fs61.appendFileSync)(filePath, `${JSON.stringify(validated)} `, "utf8"); } catch (cause) { throw ioError("append template record to", filePath, cause); @@ -94787,10 +94878,10 @@ function appendTemplateRecord(workspace, record5) { function readTemplateRecords(workspace) { const filePath = templateRecordsPath(workspace); const diagnostics = []; - if (!(0, import_fs62.existsSync)(filePath)) return { records: [], diagnostics }; + if (!(0, import_fs61.existsSync)(filePath)) return { records: [], diagnostics }; let text15; try { - text15 = (0, import_fs62.readFileSync)(filePath, "utf8"); + text15 = (0, import_fs61.readFileSync)(filePath, "utf8"); } catch (cause) { diagnostics.push({ severity: "warning", @@ -94989,7 +95080,7 @@ function planTemplateApplication(workspace, catalog, request, clock = systemCloc }; } function toPosix2(relative) { - return relative.split(import_path68.default.sep).join("/"); + return relative.split(import_path66.default.sep).join("/"); } function executeTemplateApplication(workspace, plan, clock = systemClock, recordId) { let creation; @@ -95019,15 +95110,15 @@ function executeTemplateApplication(workspace, plan, clock = systemClock, record })), variableNames: plan.variableNames, createdPaths: [ - ...creation.writtenFiles.map((file) => toPosix2(import_path68.default.relative(workspace.rootDir, file))), - toPosix2(import_path68.default.relative(workspace.rootDir, creation.statePath)) + ...creation.writtenFiles.map((file) => toPosix2(import_path66.default.relative(workspace.rootDir, file))), + toPosix2(import_path66.default.relative(workspace.rootDir, creation.statePath)) ] }; appendTemplateRecord(workspace, record5); return { plan, creation, recordId: id }; } function planTemplateInstall(workspace, catalog, request) { - const sourceDir = import_path69.default.resolve(request.cwd ?? workspace.rootDir, request.sourcePath); + const sourceDir = import_path67.default.resolve(request.cwd ?? workspace.rootDir, request.sourcePath); try { assertInsideWorkspace(workspace.rootDir, sourceDir); } catch (cause) { @@ -95053,8 +95144,8 @@ function planTemplateInstall(workspace, catalog, request) { ); } const templateId = pack.manifest.id; - const targetDir = import_path69.default.join(projectTemplatesDir(workspace), templateId); - if ((0, import_fs63.existsSync)(targetDir)) { + const targetDir = import_path67.default.join(projectTemplatesDir(workspace), templateId); + if ((0, import_fs62.existsSync)(targetDir)) { throw new TemplateError( "SBT021", `Template "project:${templateId}" is already installed at ${targetDir}.`, @@ -95080,16 +95171,16 @@ function planTemplateInstall(workspace, catalog, request) { }; } function executeTemplateInstall(workspace, plan, clock = systemClock, recordId) { - const tmpParent = import_path69.default.join(workspace.sidecarDir, "tmp"); - const tempDir = import_path69.default.join( + const tmpParent = import_path67.default.join(workspace.sidecarDir, "tmp"); + const tempDir = import_path67.default.join( tmpParent, `template-install-${plan.templateId}-${process.pid}-${Math.random().toString(36).slice(2, 8)}` ); try { - (0, import_fs63.mkdirSync)(tempDir, { recursive: true }); + (0, import_fs62.mkdirSync)(tempDir, { recursive: true }); for (const [relative, content] of plan.pack.files) { - const target = import_path69.default.join(tempDir, relative); - (0, import_fs63.mkdirSync)(import_path69.default.dirname(target), { recursive: true }); + const target = import_path67.default.join(tempDir, relative); + (0, import_fs62.mkdirSync)(import_path67.default.dirname(target), { recursive: true }); writeFileAtomic(target, content); } const copied = loadTemplatePack(readTemplatePackDirectory(tempDir)); @@ -95101,8 +95192,8 @@ function executeTemplateInstall(workspace, plan, clock = systemClock, recordId) { path: plan.sourceDir } ); } - (0, import_fs63.mkdirSync)(import_path69.default.dirname(plan.targetDir), { recursive: true }); - if ((0, import_fs63.existsSync)(plan.targetDir)) { + (0, import_fs62.mkdirSync)(import_path67.default.dirname(plan.targetDir), { recursive: true }); + if ((0, import_fs62.existsSync)(plan.targetDir)) { throw new TemplateError( "SBT021", `Template "project:${plan.templateId}" was installed by another process.`, @@ -95110,11 +95201,11 @@ function executeTemplateInstall(workspace, plan, clock = systemClock, recordId) { path: plan.targetDir } ); } - (0, import_fs63.renameSync)(tempDir, plan.targetDir); + (0, import_fs62.renameSync)(tempDir, plan.targetDir); } finally { - (0, import_fs63.rmSync)(tempDir, { recursive: true, force: true }); + (0, import_fs62.rmSync)(tempDir, { recursive: true, force: true }); try { - (0, import_fs63.rmdirSync)(tmpParent); + (0, import_fs62.rmdirSync)(tmpParent); } catch { } } @@ -95129,8 +95220,8 @@ function executeTemplateInstall(workspace, plan, clock = systemClock, recordId) templateId: plan.templateId, templateVersion: plan.templateVersion, manifestHash: plan.manifestHash, - sourcePath: import_path69.default.relative(workspace.rootDir, plan.sourceDir).split(import_path69.default.sep).join("/"), - installedPath: import_path69.default.relative(workspace.rootDir, plan.targetDir).split(import_path69.default.sep).join("/") + sourcePath: import_path67.default.relative(workspace.rootDir, plan.sourceDir).split(import_path67.default.sep).join("/"), + installedPath: import_path67.default.relative(workspace.rootDir, plan.targetDir).split(import_path67.default.sep).join("/") }); return { plan, installedPath: plan.targetDir, recordId: id }; } @@ -95160,10 +95251,10 @@ function planTemplateUninstall(workspace, rawReference) { { reference: rawReference } ); } - const dir = import_path69.default.join(projectTemplatesDir(workspace), reference.id); + const dir = import_path67.default.join(projectTemplatesDir(workspace), reference.id); let stat; try { - stat = (0, import_fs63.lstatSync)(dir); + stat = (0, import_fs62.lstatSync)(dir); } catch { throw new TemplateError( "SBT001", @@ -95183,18 +95274,18 @@ function planTemplateUninstall(workspace, rawReference) { return { templateId: reference.id, ref: `project:${reference.id}`, dir }; } function executeTemplateUninstall(workspace, plan, clock = systemClock, recordId) { - const tmpParent = import_path69.default.join(workspace.sidecarDir, "tmp"); - const tempDir = import_path69.default.join( + const tmpParent = import_path67.default.join(workspace.sidecarDir, "tmp"); + const tempDir = import_path67.default.join( tmpParent, `template-uninstall-${plan.templateId}-${process.pid}-${Math.random().toString(36).slice(2, 8)}` ); - (0, import_fs63.mkdirSync)(tmpParent, { recursive: true }); - (0, import_fs63.renameSync)(plan.dir, tempDir); + (0, import_fs62.mkdirSync)(tmpParent, { recursive: true }); + (0, import_fs62.renameSync)(plan.dir, tempDir); try { - (0, import_fs63.rmSync)(tempDir, { recursive: true, force: true }); + (0, import_fs62.rmSync)(tempDir, { recursive: true, force: true }); } finally { try { - (0, import_fs63.rmdirSync)(tmpParent); + (0, import_fs62.rmdirSync)(tmpParent); } catch { } } @@ -95207,7 +95298,7 @@ function executeTemplateUninstall(workspace, plan, clock = systemClock, recordId result: "ok", templateRef: plan.ref, templateId: plan.templateId, - uninstalledPath: import_path69.default.relative(workspace.rootDir, plan.dir).split(import_path69.default.sep).join("/") + uninstalledPath: import_path67.default.relative(workspace.rootDir, plan.dir).split(import_path67.default.sep).join("/") }); return { plan, recordId: id }; } @@ -95293,10 +95384,10 @@ The built-in variables \`specName\`, \`title\`, \`description\`, \`kind\`, and \`\`\`bash # From the directory containing this template pack: -specbridge template validate ./${import_path70.default.basename(request.outputPath)} +specbridge template validate ./${import_path68.default.basename(request.outputPath)} # Then install it into a project for a real preview: -specbridge template install ./${import_path70.default.basename(request.outputPath)} +specbridge template install ./${import_path68.default.basename(request.outputPath)} specbridge template preview project:${request.templateId} --name example-spec \`\`\` @@ -95516,9 +95607,9 @@ ${idCheck.problems.map((p) => ` - ${p}`).join("\n")}`, if (new Set(modes).size !== modes.length) { throw new TemplateError("SBT015", "--modes contains duplicates.", "List each mode once.", {}); } - const outputDir = import_path70.default.resolve(request.cwd, request.outputPath); - const relative = import_path70.default.relative(import_path70.default.resolve(request.cwd), outputDir); - if (relative.startsWith("..") || import_path70.default.isAbsolute(relative)) { + const outputDir = import_path68.default.resolve(request.cwd, request.outputPath); + const relative = import_path68.default.relative(import_path68.default.resolve(request.cwd), outputDir); + if (relative.startsWith("..") || import_path68.default.isAbsolute(relative)) { throw new TemplateError( "SBT007", `Scaffold output ${outputDir} is outside the current directory.`, @@ -95526,7 +95617,7 @@ ${idCheck.problems.map((p) => ` - ${p}`).join("\n")}`, { path: outputDir } ); } - if ((0, import_fs64.existsSync)(outputDir)) { + if ((0, import_fs63.existsSync)(outputDir)) { throw new TemplateError( "SBT025", `Scaffold output directory already exists: ${outputDir}.`, @@ -95558,21 +95649,21 @@ ${idCheck.problems.map((p) => ` - ${p}`).join("\n")}`, return { templateId: request.templateId, kind: request.kind, outputDir, files }; } function executeTemplateScaffold(plan, workspace, clock = systemClock, recordId) { - const tmpParent = workspace !== void 0 ? import_path70.default.join(workspace.sidecarDir, "tmp") : import_path70.default.join((0, import_os.tmpdir)(), "specbridge-scaffold"); - const tempDir = import_path70.default.join( + const tmpParent = workspace !== void 0 ? import_path68.default.join(workspace.sidecarDir, "tmp") : import_path68.default.join((0, import_os.tmpdir)(), "specbridge-scaffold"); + const tempDir = import_path68.default.join( tmpParent, `template-scaffold-${plan.templateId}-${process.pid}-${Math.random().toString(36).slice(2, 8)}` ); const writtenFiles = []; try { - (0, import_fs64.mkdirSync)(tempDir, { recursive: true }); + (0, import_fs63.mkdirSync)(tempDir, { recursive: true }); for (const [relative, content] of plan.files) { - const target = import_path70.default.join(tempDir, relative); - (0, import_fs64.mkdirSync)(import_path70.default.dirname(target), { recursive: true }); + const target = import_path68.default.join(tempDir, relative); + (0, import_fs63.mkdirSync)(import_path68.default.dirname(target), { recursive: true }); writeFileAtomic(target, content); } - (0, import_fs64.mkdirSync)(import_path70.default.dirname(plan.outputDir), { recursive: true }); - if ((0, import_fs64.existsSync)(plan.outputDir)) { + (0, import_fs63.mkdirSync)(import_path68.default.dirname(plan.outputDir), { recursive: true }); + if ((0, import_fs63.existsSync)(plan.outputDir)) { throw new TemplateError( "SBT025", `Scaffold output directory was created by another process: ${plan.outputDir}.`, @@ -95580,14 +95671,14 @@ function executeTemplateScaffold(plan, workspace, clock = systemClock, recordId) { path: plan.outputDir } ); } - (0, import_fs64.renameSync)(tempDir, plan.outputDir); + (0, import_fs63.renameSync)(tempDir, plan.outputDir); for (const relative of plan.files.keys()) { - writtenFiles.push(import_path70.default.join(plan.outputDir, relative)); + writtenFiles.push(import_path68.default.join(plan.outputDir, relative)); } } finally { - (0, import_fs64.rmSync)(tempDir, { recursive: true, force: true }); + (0, import_fs63.rmSync)(tempDir, { recursive: true, force: true }); try { - (0, import_fs64.rmdirSync)(tmpParent); + (0, import_fs63.rmdirSync)(tmpParent); } catch { } } @@ -95602,7 +95693,7 @@ function executeTemplateScaffold(plan, workspace, clock = systemClock, recordId) result: "ok", templateId: plan.templateId, kind: plan.kind, - outputPath: import_path70.default.relative(workspace.rootDir, plan.outputDir).split(import_path70.default.sep).join("/") + outputPath: import_path68.default.relative(workspace.rootDir, plan.outputDir).split(import_path68.default.sep).join("/") }); } return { plan, writtenFiles, recordId: id }; @@ -96519,26 +96610,26 @@ var TEMPLATE_PROVIDER_TEMPLATES_DIR = "templates"; var MAX_TEMPLATE_PROVIDER_PACKS = 20; // ../../packages/extensions/dist/index.js -var import_fs65 = require("fs"); -var import_path71 = __toESM(require("path"), 1); +var import_fs64 = require("fs"); +var import_path69 = __toESM(require("path"), 1); var import_crypto27 = require("crypto"); -var import_fs66 = require("fs"); -var import_path72 = __toESM(require("path"), 1); +var import_fs65 = require("fs"); +var import_path70 = __toESM(require("path"), 1); var import_child_process2 = require("child_process"); +var import_fs66 = require("fs"); +var import_path71 = __toESM(require("path"), 1); var import_fs67 = require("fs"); -var import_path73 = __toESM(require("path"), 1); +var import_path72 = __toESM(require("path"), 1); var import_fs68 = require("fs"); -var import_path74 = __toESM(require("path"), 1); +var import_path73 = __toESM(require("path"), 1); var import_fs69 = require("fs"); -var import_path75 = __toESM(require("path"), 1); +var import_path74 = __toESM(require("path"), 1); var import_fs70 = require("fs"); -var import_path76 = __toESM(require("path"), 1); +var import_path75 = __toESM(require("path"), 1); var import_fs71 = require("fs"); -var import_path77 = __toESM(require("path"), 1); +var import_path76 = __toESM(require("path"), 1); var import_fs72 = require("fs"); -var import_path78 = __toESM(require("path"), 1); -var import_fs73 = require("fs"); -var import_path79 = __toESM(require("path"), 1); +var import_path77 = __toESM(require("path"), 1); var ExtensionError = class extends SpecBridgeError { extensionCode; /** Actionable next step, always present. */ @@ -97040,7 +97131,7 @@ var FORBIDDEN_LIFECYCLE_SCRIPTS = [ "postuninstall" ]; function readExtensionPackageDirectory(dir) { - const rootStat = (0, import_fs65.lstatSync)(dir, { throwIfNoEntry: false }); + const rootStat = (0, import_fs64.lstatSync)(dir, { throwIfNoEntry: false }); if (rootStat === void 0 || !rootStat.isDirectory()) { throw new ExtensionError( "SBE008", @@ -97065,7 +97156,7 @@ function readExtensionPackageDirectory(dir) { "Flatten the package layout." ); } - for (const entry2 of (0, import_fs65.readdirSync)(currentDir, { withFileTypes: true })) { + for (const entry2 of (0, import_fs64.readdirSync)(currentDir, { withFileTypes: true })) { const relativePath = relativePrefix === "" ? entry2.name : `${relativePrefix}/${entry2.name}`; if (entry2.isSymbolicLink()) { throw new ExtensionError( @@ -97091,7 +97182,7 @@ function readExtensionPackageDirectory(dir) { "Remove the directory before validating or packaging." ); } - walk(import_path71.default.join(currentDir, entry2.name), relativePath, depth + 1); + walk(import_path69.default.join(currentDir, entry2.name), relativePath, depth + 1); continue; } if (!entry2.isFile()) { @@ -97108,7 +97199,7 @@ function readExtensionPackageDirectory(dir) { "Reduce the package contents." ); } - const content = (0, import_fs65.readFileSync)(import_path71.default.join(currentDir, entry2.name)); + const content = (0, import_fs64.readFileSync)(import_path69.default.join(currentDir, entry2.name)); totalBytes += content.length; if (totalBytes > EXTENSION_LIMITS.maxExtractedTotalBytes) { throw new ExtensionError( @@ -97383,10 +97474,10 @@ var EXTENSION_RECORDS_FILE_NAME = "records.jsonl"; var EXTENSION_STATE_SCHEMA_VERSION = "1.0.0"; var systemClock2 = () => /* @__PURE__ */ new Date(); function extensionsDir(workspace) { - return import_path72.default.join(workspace.sidecarDir, EXTENSIONS_DIR_NAME); + return import_path70.default.join(workspace.sidecarDir, EXTENSIONS_DIR_NAME); } function installedRootDir(workspace) { - return import_path72.default.join(extensionsDir(workspace), "installed"); + return import_path70.default.join(extensionsDir(workspace), "installed"); } function installedVersionDir(workspace, id, version2) { if (!validateExtensionId(id).valid || parseSemver2(version2) === void 0) { @@ -97396,7 +97487,7 @@ function installedVersionDir(workspace, id, version2) { "Use a valid extension ID and X.Y.Z version." ); } - const dir = import_path72.default.join(installedRootDir(workspace), id, version2); + const dir = import_path70.default.join(installedRootDir(workspace), id, version2); assertInsideWorkspace(workspace.rootDir, dir); return dir; } @@ -97440,12 +97531,12 @@ function emptyPermissionGrants() { return { schemaVersion: EXTENSION_STATE_SCHEMA_VERSION, grants: {} }; } function readValidatedJson(filePath, schema, empty, label) { - if (!(0, import_fs66.existsSync)(filePath)) { + if (!(0, import_fs65.existsSync)(filePath)) { return { value: empty, diagnostics: [], exists: false }; } let text15; try { - text15 = (0, import_fs66.readFileSync)(filePath, "utf8"); + text15 = (0, import_fs65.readFileSync)(filePath, "utf8"); } catch (cause) { return { value: empty, @@ -97495,13 +97586,13 @@ function readValidatedJson(filePath, schema, empty, label) { return { value: result.data, diagnostics: [], exists: true }; } function extensionStatePath(workspace) { - return import_path72.default.join(extensionsDir(workspace), EXTENSION_STATE_FILE_NAME); + return import_path70.default.join(extensionsDir(workspace), EXTENSION_STATE_FILE_NAME); } function permissionGrantsPath(workspace) { - return import_path72.default.join(extensionsDir(workspace), EXTENSION_GRANTS_FILE_NAME); + return import_path70.default.join(extensionsDir(workspace), EXTENSION_GRANTS_FILE_NAME); } function extensionRecordsPath(workspace) { - return import_path72.default.join(extensionsDir(workspace), EXTENSION_RECORDS_FILE_NAME); + return import_path70.default.join(extensionsDir(workspace), EXTENSION_RECORDS_FILE_NAME); } function readExtensionState(workspace) { const { value, diagnostics, exists } = readValidatedJson( @@ -97552,8 +97643,8 @@ function appendExtensionRecord(workspace, record5) { const filePath = extensionRecordsPath(workspace); assertInsideWorkspace(workspace.rootDir, filePath); try { - (0, import_fs66.mkdirSync)(extensionsDir(workspace), { recursive: true }); - (0, import_fs66.appendFileSync)(filePath, `${JSON.stringify(validated)} + (0, import_fs65.mkdirSync)(extensionsDir(workspace), { recursive: true }); + (0, import_fs65.appendFileSync)(filePath, `${JSON.stringify(validated)} `, "utf8"); } catch (cause) { throw ioError("append extension record to", filePath, cause); @@ -97786,9 +97877,9 @@ function resolveEntrypoint(installedDir, entrypoint) { if (problem !== void 0) { throw new ExtensionError("SBE012", `entrypoint "${entrypoint}": ${problem}.`, "Fix the extension manifest."); } - const resolved2 = import_path73.default.join(installedDir, ...entrypoint.split("/")); - const relative = import_path73.default.relative(installedDir, resolved2); - if (relative.startsWith("..") || import_path73.default.isAbsolute(relative)) { + const resolved2 = import_path71.default.join(installedDir, ...entrypoint.split("/")); + const relative = import_path71.default.relative(installedDir, resolved2); + if (relative.startsWith("..") || import_path71.default.isAbsolute(relative)) { throw new ExtensionError( "SBE012", `entrypoint "${entrypoint}" escapes the installed extension directory.`, @@ -97796,9 +97887,9 @@ function resolveEntrypoint(installedDir, entrypoint) { ); } let current = installedDir; - for (const segment of relative.split(import_path73.default.sep)) { - current = import_path73.default.join(current, segment); - const stat = (0, import_fs67.lstatSync)(current, { throwIfNoEntry: false }); + for (const segment of relative.split(import_path71.default.sep)) { + current = import_path71.default.join(current, segment); + const stat = (0, import_fs66.lstatSync)(current, { throwIfNoEntry: false }); if (stat === void 0) { throw new ExtensionError( "SBE012", @@ -97814,7 +97905,7 @@ function resolveEntrypoint(installedDir, entrypoint) { ); } } - const finalStat = (0, import_fs67.lstatSync)(resolved2, { throwIfNoEntry: false }); + const finalStat = (0, import_fs66.lstatSync)(resolved2, { throwIfNoEntry: false }); if (finalStat === void 0 || !finalStat.isFile()) { throw new ExtensionError( "SBE012", @@ -98395,14 +98486,14 @@ async function runAnalyzerExtension(workspace, extensionId, input, options = {}) } function compatibilityOf(workspace, record5, specbridgeVersion) { try { - const manifestPath = import_path74.default.join( + const manifestPath = import_path72.default.join( installedVersionDir(workspace, record5.id, record5.version), EXTENSION_MANIFEST_FILE_NAME ); - if (!(0, import_fs68.existsSync)(manifestPath)) { + if (!(0, import_fs67.existsSync)(manifestPath)) { return { compatibility: "unknown", deprecated: false }; } - const parsed = parseExtensionManifest((0, import_fs68.readFileSync)(manifestPath, "utf8")); + const parsed = parseExtensionManifest((0, import_fs67.readFileSync)(manifestPath, "utf8")); if (parsed.manifest === void 0) { return { compatibility: "unknown", deprecated: false }; } @@ -98681,8 +98772,8 @@ async function runExporterExtension(workspace, extensionId, input, options = {}) }; } function validateExportTargets(outputDir, files) { - const resolvedRoot = import_path75.default.resolve(outputDir); - const rootStat = (0, import_fs69.lstatSync)(resolvedRoot, { throwIfNoEntry: false }); + const resolvedRoot = import_path73.default.resolve(outputDir); + const rootStat = (0, import_fs68.lstatSync)(resolvedRoot, { throwIfNoEntry: false }); if (rootStat !== void 0 && rootStat.isSymbolicLink()) { throw new ExtensionError( "SBE011", @@ -98701,9 +98792,9 @@ function validateExportTargets(outputDir, files) { "Report this to the extension author; nothing was written." ); } - const target = import_path75.default.resolve(resolvedRoot, ...file.path.split("/")); - const relative = import_path75.default.relative(resolvedRoot, target); - if (relative.startsWith("..") || import_path75.default.isAbsolute(relative)) { + const target = import_path73.default.resolve(resolvedRoot, ...file.path.split("/")); + const relative = import_path73.default.relative(resolvedRoot, target); + if (relative.startsWith("..") || import_path73.default.isAbsolute(relative)) { throw new ExtensionError( "SBE030", `exporter output path "${file.path}" escapes the output directory.`, @@ -98719,9 +98810,9 @@ function validateExportTargets(outputDir, files) { } seen.add(target.toLowerCase()); let current = resolvedRoot; - for (const segment of relative.split(import_path75.default.sep)) { - current = import_path75.default.join(current, segment); - const stat = (0, import_fs69.lstatSync)(current, { throwIfNoEntry: false }); + for (const segment of relative.split(import_path73.default.sep)) { + current = import_path73.default.join(current, segment); + const stat = (0, import_fs68.lstatSync)(current, { throwIfNoEntry: false }); if (stat?.isSymbolicLink() === true) { throw new ExtensionError( "SBE011", @@ -98730,7 +98821,7 @@ function validateExportTargets(outputDir, files) { ); } } - if ((0, import_fs69.existsSync)(target)) { + if ((0, import_fs68.existsSync)(target)) { throw new ExtensionError( "SBE030", `export target "${file.path}" already exists in the output directory.`, @@ -98750,7 +98841,7 @@ function writeExportFiles(workspace, extensionId, extensionVersion, specName, ou if (target === void 0 || file === void 0) { continue; } - (0, import_fs69.mkdirSync)(import_path75.default.dirname(target.target), { recursive: true }); + (0, import_fs68.mkdirSync)(import_path73.default.dirname(target.target), { recursive: true }); writeFileAtomic(target.target, file.content); written.push(target.relative); } @@ -98837,19 +98928,19 @@ function installExtensionPackage(files, options, archiveSha256) { return { ...base, dryRun: true }; } const recordId = newExtensionRecordId(clock); - const stagingDir = import_path76.default.join(extensionsDir(workspace), `tmp-install-${recordId}`); + const stagingDir = import_path74.default.join(extensionsDir(workspace), `tmp-install-${recordId}`); assertInsideWorkspace(workspace.rootDir, stagingDir); try { for (const [name, content] of files) { - const target = import_path76.default.join(stagingDir, ...name.split("/")); + const target = import_path74.default.join(stagingDir, ...name.split("/")); assertInsideWorkspace(workspace.rootDir, target); - (0, import_fs70.mkdirSync)(import_path76.default.dirname(target), { recursive: true }); + (0, import_fs69.mkdirSync)(import_path74.default.dirname(target), { recursive: true }); writeFileAtomic(target, content); } - (0, import_fs70.mkdirSync)(import_path76.default.dirname(targetDir), { recursive: true }); - (0, import_fs70.renameSync)(stagingDir, targetDir); + (0, import_fs69.mkdirSync)(import_path74.default.dirname(targetDir), { recursive: true }); + (0, import_fs69.renameSync)(stagingDir, targetDir); } catch (cause) { - (0, import_fs70.rmSync)(stagingDir, { recursive: true, force: true }); + (0, import_fs69.rmSync)(stagingDir, { recursive: true, force: true }); if (cause instanceof ExtensionError) { throw cause; } @@ -98902,7 +98993,7 @@ function installExtensionPackage(files, options, archiveSha256) { } }); } catch (cause) { - (0, import_fs70.rmSync)(targetDir, { recursive: true, force: true }); + (0, import_fs69.rmSync)(targetDir, { recursive: true, force: true }); if (cause instanceof ExtensionError) { throw cause; } @@ -98957,8 +99048,8 @@ function buildExtensionArchive(sourceDir, options = {}) { const manifest = validation.manifest; const archive = createDeterministicZip(runtimeFiles); const archiveSha256 = sha256HexOf(archive); - const outputDir = options.outputDir ?? import_path77.default.join(sourceDir, "dist"); - const archivePath = import_path77.default.join( + const outputDir = options.outputDir ?? import_path75.default.join(sourceDir, "dist"); + const archivePath = import_path75.default.join( outputDir, `${manifest.id}-${manifest.version}${EXTENSION_ARCHIVE_SUFFIX}` ); @@ -98972,7 +99063,7 @@ function buildExtensionArchive(sourceDir, options = {}) { ); } if (options.dryRun !== true) { - (0, import_fs71.mkdirSync)(outputDir, { recursive: true }); + (0, import_fs70.mkdirSync)(outputDir, { recursive: true }); writeFileAtomic(archivePath, archive); } return { @@ -99770,7 +99861,7 @@ function scaffoldExtension(options) { ); } const outputDir = options.outputDir; - if ((0, import_fs72.existsSync)(outputDir) && (0, import_fs72.readdirSync)(outputDir).length > 0) { + if ((0, import_fs71.existsSync)(outputDir) && (0, import_fs71.readdirSync)(outputDir).length > 0) { throw new ExtensionError( "SBE030", `output directory "${outputDir}" already exists and is not empty.`, @@ -99830,8 +99921,8 @@ function scaffoldExtension(options) { }; } for (const [name, content] of files) { - const target = import_path78.default.join(outputDir, ...name.split("/")); - (0, import_fs72.mkdirSync)(import_path78.default.dirname(target), { recursive: true }); + const target = import_path76.default.join(outputDir, ...name.split("/")); + (0, import_fs71.mkdirSync)(import_path76.default.dirname(target), { recursive: true }); writeFileAtomic(target, content); } return { @@ -99944,7 +100035,7 @@ function uninstallExtension(options) { ); } const installedDir = installedVersionDir(workspace, options.id, version2); - const stat = (0, import_fs73.lstatSync)(installedDir, { throwIfNoEntry: false }); + const stat = (0, import_fs72.lstatSync)(installedDir, { throwIfNoEntry: false }); if (stat !== void 0 && stat.isSymbolicLink()) { throw new ExtensionError( "SBE011", @@ -99958,11 +100049,11 @@ function uninstallExtension(options) { const recordId = newExtensionRecordId(clock); let trashPath; if (stat !== void 0) { - const trashDir = import_path79.default.join(extensionsDir(workspace), "trash"); - trashPath = import_path79.default.join(trashDir, `${options.id}-${version2}-${recordId}`); + const trashDir = import_path77.default.join(extensionsDir(workspace), "trash"); + trashPath = import_path77.default.join(trashDir, `${options.id}-${version2}-${recordId}`); assertInsideWorkspace(workspace.rootDir, trashPath); - (0, import_fs73.mkdirSync)(trashDir, { recursive: true }); - (0, import_fs73.renameSync)(installedDir, trashPath); + (0, import_fs72.mkdirSync)(trashDir, { recursive: true }); + (0, import_fs72.renameSync)(installedDir, trashPath); } writeExtensionState(workspace, { ...state, @@ -100076,11 +100167,11 @@ function createExtensionVerifierHook(workspace, options = {}) { } // ../../packages/registry/dist/index.js -var import_fs74 = require("fs"); -var import_path80 = __toESM(require("path"), 1); +var import_fs73 = require("fs"); +var import_path78 = __toESM(require("path"), 1); var import_crypto28 = require("crypto"); -var import_fs75 = require("fs"); -var import_path81 = __toESM(require("path"), 1); +var import_fs74 = require("fs"); +var import_path79 = __toESM(require("path"), 1); var BUILTIN_REGISTRY_INDEX_JSON = '{\n "schemaVersion": "1.0.0",\n "name": "specbridge-examples",\n "updatedAt": "2026-01-01T00:00:00.000Z",\n "extensions": [\n {\n "id": "example-analyzer",\n "displayName": "example-analyzer",\n "description": "Deterministic spec diagnostics contributed by the example-analyzer analyzer extension.",\n "kind": "analyzer",\n "latestVersion": "1.0.0",\n "versions": [\n {\n "version": "1.0.0",\n "archiveUrl": "https://example.invalid/specbridge-extensions/example-analyzer-1.0.0.specbridge-extension.zip",\n "sha256": "e6e0948a315b09e53bd18997dce21888af9adbb3997fbf82955399dcf3252a19",\n "manifest": {\n "protocolVersion": "1.0.0",\n "compatibility": {\n "specbridge": ">=0.7.1 <2.0.0"\n },\n "permissions": {\n "specRead": true,\n "repositoryRead": false,\n "repositoryWrite": false,\n "network": false,\n "childProcess": false,\n "environmentVariables": []\n }\n }\n }\n ],\n "repository": "https://github.com/HelloThisWorld/specbridge",\n "license": "MIT",\n "keywords": [\n "analyzer",\n "specbridge-extension"\n ]\n },\n {\n "id": "example-exporter",\n "displayName": "example-exporter",\n "description": "Candidate export files produced by the example-exporter exporter extension.",\n "kind": "exporter",\n "latestVersion": "1.0.0",\n "versions": [\n {\n "version": "1.0.0",\n "archiveUrl": "https://example.invalid/specbridge-extensions/example-exporter-1.0.0.specbridge-extension.zip",\n "sha256": "68f42755a4e56d0e318012ec8c0e3b093e44429182ca93b02d9fb4ce2ec308a3",\n "manifest": {\n "protocolVersion": "1.0.0",\n "compatibility": {\n "specbridge": ">=0.7.1 <2.0.0"\n },\n "permissions": {\n "specRead": true,\n "repositoryRead": false,\n "repositoryWrite": false,\n "network": false,\n "childProcess": false,\n "environmentVariables": []\n }\n }\n }\n ],\n "repository": "https://github.com/HelloThisWorld/specbridge",\n "license": "MIT",\n "keywords": [\n "exporter",\n "specbridge-extension"\n ]\n },\n {\n "id": "example-runner",\n "displayName": "example-runner",\n "description": "An out-of-process runner adapter provided by the example-runner extension.",\n "kind": "runner",\n "latestVersion": "1.0.0",\n "versions": [\n {\n "version": "1.0.0",\n "archiveUrl": "https://example.invalid/specbridge-extensions/example-runner-1.0.0.specbridge-extension.zip",\n "sha256": "5ef3db937d872bfe09495695e9ecb0a3cf3beaf9e006fabdc2972ef55ace80ef",\n "manifest": {\n "protocolVersion": "1.0.0",\n "compatibility": {\n "specbridge": ">=0.7.1 <2.0.0"\n },\n "permissions": {\n "specRead": true,\n "repositoryRead": true,\n "repositoryWrite": true,\n "network": false,\n "childProcess": false,\n "environmentVariables": []\n }\n }\n }\n ],\n "repository": "https://github.com/HelloThisWorld/specbridge",\n "license": "MIT",\n "keywords": [\n "runner",\n "specbridge-extension"\n ]\n },\n {\n "id": "example-template-provider",\n "displayName": "example-template-provider",\n "description": "Spec template packs contributed by the example-template-provider template-provider extension.",\n "kind": "template-provider",\n "latestVersion": "1.0.0",\n "versions": [\n {\n "version": "1.0.0",\n "archiveUrl": "https://example.invalid/specbridge-extensions/example-template-provider-1.0.0.specbridge-extension.zip",\n "sha256": "f7caa11a13473f0891cc8d237ec4f9f2962a2dd1bd2baba4e9d01570de29044b",\n "manifest": {\n "protocolVersion": "1.0.0",\n "compatibility": {\n "specbridge": ">=0.7.1 <2.0.0"\n },\n "permissions": {\n "specRead": false,\n "repositoryRead": false,\n "repositoryWrite": false,\n "network": false,\n "childProcess": false,\n "environmentVariables": []\n }\n }\n }\n ],\n "repository": "https://github.com/HelloThisWorld/specbridge",\n "license": "MIT",\n "keywords": [\n "template-provider",\n "specbridge-extension"\n ]\n },\n {\n "id": "example-verifier",\n "displayName": "example-verifier",\n "description": "Verification diagnostics contributed by the example-verifier verifier extension.",\n "kind": "verifier",\n "latestVersion": "1.0.0",\n "versions": [\n {\n "version": "1.0.0",\n "archiveUrl": "https://example.invalid/specbridge-extensions/example-verifier-1.0.0.specbridge-extension.zip",\n "sha256": "d531c9078fcbeef6573a95773eefafd409d798bac1223c83748e0229ae0225bf",\n "manifest": {\n "protocolVersion": "1.0.0",\n "compatibility": {\n "specbridge": ">=0.7.1 <2.0.0"\n },\n "permissions": {\n "specRead": true,\n "repositoryRead": false,\n "repositoryWrite": false,\n "network": false,\n "childProcess": false,\n "environmentVariables": []\n }\n }\n }\n ],\n "repository": "https://github.com/HelloThisWorld/specbridge",\n "license": "MIT",\n "keywords": [\n "verifier",\n "specbridge-extension"\n ]\n }\n ]\n}\n'; var REGISTRY_ERROR_CODES = { SBR001: "registry not found", @@ -100237,20 +100328,20 @@ var cachedRegistrySchema = external_exports.object({ index: registryIndexSchema }).passthrough(); function registryCacheDir(workspace) { - return import_path80.default.join(workspace.sidecarDir, REGISTRY_CACHE_DIR_NAME); + return import_path78.default.join(workspace.sidecarDir, REGISTRY_CACHE_DIR_NAME); } function registryCachePath(workspace, name) { - const target = import_path80.default.join(registryCacheDir(workspace), `${name}.json`); + const target = import_path78.default.join(registryCacheDir(workspace), `${name}.json`); assertInsideWorkspace(workspace.rootDir, target); return target; } function readRegistryCache(workspace, name) { const filePath = registryCachePath(workspace, name); - if (!(0, import_fs74.existsSync)(filePath)) { + if (!(0, import_fs73.existsSync)(filePath)) { return { diagnostics: [] }; } try { - const parsed = cachedRegistrySchema.safeParse(JSON.parse((0, import_fs74.readFileSync)(filePath, "utf8"))); + const parsed = cachedRegistrySchema.safeParse(JSON.parse((0, import_fs73.readFileSync)(filePath, "utf8"))); if (!parsed.success) { return { diagnostics: [ @@ -100303,9 +100394,9 @@ function resolveRegistryIndex(workspace, source) { return { sourceName: source.name, index: parsed.index, origin: "builtin", diagnostics: [] }; } if (source.type === "local-file") { - const filePath = import_path80.default.resolve(workspace.rootDir, source.file); + const filePath = import_path78.default.resolve(workspace.rootDir, source.file); assertInsideWorkspace(workspace.rootDir, filePath); - if (!(0, import_fs74.existsSync)(filePath)) { + if (!(0, import_fs73.existsSync)(filePath)) { return { sourceName: source.name, index: { schemaVersion: "1.0.0", name: source.name, updatedAt: "unknown", extensions: [] }, @@ -100320,7 +100411,7 @@ function resolveRegistryIndex(workspace, source) { ] }; } - const text15 = (0, import_fs74.readFileSync)(filePath, "utf8"); + const text15 = (0, import_fs73.readFileSync)(filePath, "utf8"); const parsed = parseRegistryIndex(text15); if (parsed.index === void 0) { throw new RegistryError( @@ -100563,7 +100654,7 @@ var registriesConfigSchema = external_exports.object({ registries: external_exports.array(registrySourceSchema).max(20) }).passthrough(); function registriesConfigPath(workspace) { - return import_path81.default.join(workspace.sidecarDir, REGISTRIES_FILE_NAME); + return import_path79.default.join(workspace.sidecarDir, REGISTRIES_FILE_NAME); } function defaultRegistriesConfig() { return { @@ -100573,12 +100664,12 @@ function defaultRegistriesConfig() { } function readRegistriesConfig(workspace) { const filePath = registriesConfigPath(workspace); - if (!(0, import_fs75.existsSync)(filePath)) { + if (!(0, import_fs74.existsSync)(filePath)) { return { config: defaultRegistriesConfig(), diagnostics: [], exists: false }; } let parsed; try { - parsed = JSON.parse((0, import_fs75.readFileSync)(filePath, "utf8")); + parsed = JSON.parse((0, import_fs74.readFileSync)(filePath, "utf8")); } catch (cause) { return { config: defaultRegistriesConfig(), @@ -103195,30 +103286,30 @@ var import_node_fs9 = require("fs"); // ../../packages/intake/dist/index.js var import_crypto30 = require("crypto"); +var import_fs81 = require("fs"); +var import_path89 = __toESM(require("path"), 1); var import_fs82 = require("fs"); -var import_path91 = __toESM(require("path"), 1); -var import_fs83 = require("fs"); -var import_path92 = __toESM(require("path"), 1); +var import_path90 = __toESM(require("path"), 1); // ../../packages/autonomy/dist/index.js var import_crypto29 = require("crypto"); +var import_fs75 = require("fs"); +var import_path80 = __toESM(require("path"), 1); +var import_os2 = __toESM(require("os"), 1); var import_fs76 = require("fs"); +var import_path81 = __toESM(require("path"), 1); var import_path82 = __toESM(require("path"), 1); -var import_os2 = __toESM(require("os"), 1); -var import_fs77 = require("fs"); var import_path83 = __toESM(require("path"), 1); +var import_net2 = require("net"); +var import_fs77 = require("fs"); var import_path84 = __toESM(require("path"), 1); var import_path85 = __toESM(require("path"), 1); -var import_net2 = require("net"); var import_fs78 = require("fs"); var import_path86 = __toESM(require("path"), 1); -var import_path87 = __toESM(require("path"), 1); var import_fs79 = require("fs"); -var import_path88 = __toESM(require("path"), 1); +var import_path87 = __toESM(require("path"), 1); var import_fs80 = require("fs"); -var import_path89 = __toESM(require("path"), 1); -var import_fs81 = require("fs"); -var import_path90 = __toESM(require("path"), 1); +var import_path88 = __toESM(require("path"), 1); var SEAL_STATUSES = [ /** Drafted from mission state; not yet authorized by a human. */ "DRAFT", @@ -103740,43 +103831,43 @@ function assertAutonomyId(kind, id) { function autonomyDir(workspace) { return assertInsideWorkspace( workspace.rootDir, - import_path82.default.join(workspace.rootDir, ".specbridge", "autonomy") + import_path80.default.join(workspace.rootDir, ".specbridge", "autonomy") ); } function autonomyPath(workspace, ...segments) { - return assertInsideWorkspace(workspace.rootDir, import_path82.default.join(autonomyDir(workspace), ...segments)); + return assertInsideWorkspace(workspace.rootDir, import_path80.default.join(autonomyDir(workspace), ...segments)); } function writeJsonRecord(file, value) { - (0, import_fs76.mkdirSync)(import_path82.default.dirname(file), { recursive: true }); + (0, import_fs75.mkdirSync)(import_path80.default.dirname(file), { recursive: true }); writeFileAtomic(file, `${JSON.stringify(value, null, 2)} `); } function readJsonRecord(file, parse3) { - if (!(0, import_fs76.existsSync)(file)) return void 0; + if (!(0, import_fs75.existsSync)(file)) return void 0; try { - return parse3(JSON.parse((0, import_fs76.readFileSync)(file, "utf8"))); + return parse3(JSON.parse((0, import_fs75.readFileSync)(file, "utf8"))); } catch { return void 0; } } function listJsonRecords(dir, parse3) { - if (!(0, import_fs76.existsSync)(dir)) return []; + if (!(0, import_fs75.existsSync)(dir)) return []; const out = []; - for (const entry2 of (0, import_fs76.readdirSync)(dir).sort()) { + for (const entry2 of (0, import_fs75.readdirSync)(dir).sort()) { if (!entry2.endsWith(".json")) continue; - const value = readJsonRecord(import_path82.default.join(dir, entry2), parse3); + const value = readJsonRecord(import_path80.default.join(dir, entry2), parse3); if (value !== void 0) out.push(value); } return out; } function appendJsonl2(file, value) { - (0, import_fs76.mkdirSync)(import_path82.default.dirname(file), { recursive: true }); - (0, import_fs76.appendFileSync)(file, `${JSON.stringify(value)} + (0, import_fs75.mkdirSync)(import_path80.default.dirname(file), { recursive: true }); + (0, import_fs75.appendFileSync)(file, `${JSON.stringify(value)} `, "utf8"); } function readJsonl2(file, parse3, limit = 5e3) { - if (!(0, import_fs76.existsSync)(file)) return { entries: [], skipped: 0 }; - const lines = (0, import_fs76.readFileSync)(file, "utf8").split("\n").filter((line) => line.trim().length > 0); + if (!(0, import_fs75.existsSync)(file)) return { entries: [], skipped: 0 }; + const lines = (0, import_fs75.readFileSync)(file, "utf8").split("\n").filter((line) => line.trim().length > 0); const slice = lines.slice(-limit); const entries = []; let skipped = 0; @@ -103790,12 +103881,12 @@ function readJsonl2(file, parse3, limit = 5e3) { return { entries, skipped }; } function writeImmutableRecord(file, value, kind) { - if ((0, import_fs76.existsSync)(file)) { + if ((0, import_fs75.existsSync)(file)) { throw new AutonomyError("SBA024", `A ${kind} already exists at this identity and is immutable.`, { remediation: [ `Create a new ${kind} that supersedes the existing one instead of rewriting history.` ], - details: { file: import_path82.default.basename(file), kind } + details: { file: import_path80.default.basename(file), kind } }); } writeJsonRecord(file, value); @@ -105364,7 +105455,7 @@ function createProcessProbeRunner(cwd) { } function isWritableDirectory(dir) { try { - (0, import_fs77.accessSync)(dir, import_fs77.constants.W_OK); + (0, import_fs76.accessSync)(dir, import_fs76.constants.W_OK); return true; } catch { return false; @@ -105372,7 +105463,7 @@ function isWritableDirectory(dir) { } function freeDiskBytes(target) { try { - const stats = (0, import_fs77.statfsSync)(target); + const stats = (0, import_fs76.statfsSync)(target); return Number(stats.bavail) * Number(stats.bsize); } catch { return null; @@ -105380,7 +105471,7 @@ function freeDiskBytes(target) { } function pathExists(target) { try { - return (0, import_fs77.existsSync)(target); + return (0, import_fs76.existsSync)(target); } catch { return false; } @@ -105418,10 +105509,10 @@ async function probeCompose(run) { }; } function detectPackageManager(projectDir) { - const manifest = import_path83.default.join(projectDir, "package.json"); + const manifest = import_path81.default.join(projectDir, "package.json"); if (pathExists(manifest)) { try { - const raw = JSON.parse((0, import_fs77.readFileSync)(manifest, "utf8")); + const raw = JSON.parse((0, import_fs76.readFileSync)(manifest, "utf8")); if (typeof raw.packageManager === "string" && raw.packageManager.length > 0) { return raw.packageManager.split("@")[0] ?? null; } @@ -105434,7 +105525,7 @@ function detectPackageManager(projectDir) { ["package-lock.json", "npm"], ["bun.lockb", "bun"] ]) { - if (pathExists(import_path83.default.join(projectDir, lockfile))) return manager; + if (pathExists(import_path81.default.join(projectDir, lockfile))) return manager; } return null; } @@ -105448,7 +105539,7 @@ function detectBuildTool(projectDir) { ["Cargo.toml", "cargo"], ["go.mod", "go"] ]) { - if (pathExists(import_path83.default.join(projectDir, marker))) return tool; + if (pathExists(import_path81.default.join(projectDir, marker))) return tool; } return null; } @@ -105772,7 +105863,7 @@ function assertOvernightReady(report) { { remediation: [ ...report.checks.filter((check6) => check6.outcome === "HUMAN_REQUIRED" || check6.outcome === "UNKNOWN").flatMap((check6) => check6.remediation).slice(0, 10), - `Full report: ${import_path84.default.posix.join(".specbridge", "autonomy", "preflight", `${report.reportId}.json`)}` + `Full report: ${import_path82.default.posix.join(".specbridge", "autonomy", "preflight", `${report.reportId}.json`)}` ], details: { verdict: report.verdict, reportId: report.reportId } } @@ -105933,19 +106024,19 @@ function decideToolsmithRequest(request, context) { }; } function assertInsideWorkspaceBoundary(target, context) { - if (import_path85.default.isAbsolute(target)) { - const resolved2 = import_path85.default.resolve(target); - const root = import_path85.default.resolve(context.workspaceRoot); - if (resolved2 !== root && !resolved2.startsWith(root + import_path85.default.sep)) { + if (import_path83.default.isAbsolute(target)) { + const resolved2 = import_path83.default.resolve(target); + const root = import_path83.default.resolve(context.workspaceRoot); + if (resolved2 !== root && !resolved2.startsWith(root + import_path83.default.sep)) { return { granted: false, reason: "TARGET_OUTSIDE_WORKSPACE", detail: `"${target}" is outside the workspace. Project tooling lives in the project.` }; } - return matchesProtected(import_path85.default.relative(root, resolved2), context); + return matchesProtected(import_path83.default.relative(root, resolved2), context); } - const normalized = import_path85.default.normalize(target).replace(/\\/g, "/"); + const normalized = import_path83.default.normalize(target).replace(/\\/g, "/"); if (normalized.startsWith("../") || normalized === "..") { return { granted: false, @@ -106625,7 +106716,7 @@ async function finishFailed(deps4, options, plan, instance, failure3) { return failed; } function retainLog(deps4, instanceId, serviceId, text142) { - const relative = import_path86.default.posix.join( + const relative = import_path84.default.posix.join( ".specbridge", "autonomy", "environments", @@ -106634,8 +106725,8 @@ function retainLog(deps4, instanceId, serviceId, text142) { `${serviceId}.log` ); const absolute = autonomyPath(deps4.workspace, "environments", "logs", instanceId, `${serviceId}.log`); - (0, import_fs78.mkdirSync)(import_path86.default.dirname(absolute), { recursive: true }); - (0, import_fs78.writeFileSync)(absolute, text142, "utf8"); + (0, import_fs77.mkdirSync)(import_path84.default.dirname(absolute), { recursive: true }); + (0, import_fs77.writeFileSync)(absolute, text142, "utf8"); return relative; } async function teardownEnvironment(deps4, input) { @@ -106714,7 +106805,7 @@ function createComposeRuntime(options) { const composeArgs = (plan, rest) => { const args = ["compose"]; if (plan.composeFile !== void 0) { - args.push("-f", import_path87.default.resolve(options.cwd, plan.composeFile)); + args.push("-f", import_path85.default.resolve(options.cwd, plan.composeFile)); } args.push("--project-name", plan.projectName ?? plan.planId); args.push(...rest); @@ -107120,9 +107211,9 @@ function writeEvidenceFile(deps4, resultId, name, extension, data) { resultId, `${safe}.${extension}` ); - (0, import_fs79.mkdirSync)(import_path88.default.dirname(absolute), { recursive: true }); - (0, import_fs79.writeFileSync)(absolute, data); - return import_path88.default.posix.join( + (0, import_fs78.mkdirSync)(import_path86.default.dirname(absolute), { recursive: true }); + (0, import_fs78.writeFileSync)(absolute, data); + return import_path86.default.posix.join( ".specbridge", "autonomy", "browser", @@ -108644,7 +108735,7 @@ async function runReproducibilityPhase(deps4, options) { } const runId = newRecordId(deps4, "rp"); const checkoutPath = autonomyPath(deps4.workspace, "reproducibility", "checkouts", runId); - (0, import_fs80.mkdirSync)(import_path89.default.dirname(checkoutPath), { recursive: true }); + (0, import_fs79.mkdirSync)(import_path87.default.dirname(checkoutPath), { recursive: true }); const head = await runSafeProcess({ executable: "git", argv: ["rev-parse", "HEAD"], @@ -108712,9 +108803,9 @@ async function runReproducibilityPhase(deps4, options) { } function detectNodeInstaller(workspace) { const root = workspace.rootDir; - if ((0, import_fs80.existsSync)(import_path89.default.join(root, "pnpm-lock.yaml"))) return ["pnpm", "install", "--frozen-lockfile"]; - if ((0, import_fs80.existsSync)(import_path89.default.join(root, "package-lock.json"))) return ["npm", "ci"]; - if ((0, import_fs80.existsSync)(import_path89.default.join(root, "yarn.lock"))) return ["yarn", "install", "--frozen-lockfile"]; + if ((0, import_fs79.existsSync)(import_path87.default.join(root, "pnpm-lock.yaml"))) return ["pnpm", "install", "--frozen-lockfile"]; + if ((0, import_fs79.existsSync)(import_path87.default.join(root, "package-lock.json"))) return ["npm", "ci"]; + if ((0, import_fs79.existsSync)(import_path87.default.join(root, "yarn.lock"))) return ["yarn", "install", "--frozen-lockfile"]; return void 0; } async function removeCheckout(workspace, checkoutPath) { @@ -108796,12 +108887,12 @@ async function runGapRepairs(deps4, options) { fail(`the trusted suite failed in the repair worktree: ${verification.requiredFailed.join(", ").slice(0, 200)}`); continue; } - const patchFile = import_path89.default.join( + const patchFile = import_path87.default.join( autonomyPath(deps4.workspace, "closure", options.jobId, "scratch", item.gapId), "repair.patch" ); - (0, import_fs80.mkdirSync)(import_path89.default.dirname(patchFile), { recursive: true }); - (0, import_fs80.writeFileSync)(patchFile, collected.patch, "utf8"); + (0, import_fs79.mkdirSync)(import_path87.default.dirname(patchFile), { recursive: true }); + (0, import_fs79.writeFileSync)(patchFile, collected.patch, "utf8"); const applied = await runSafeProcess({ executable: "git", argv: ["apply", "--3way", patchFile], @@ -110439,13 +110530,13 @@ function executionTelemetryReportFile(workspace, jobId) { } return assertInsideWorkspace( workspace.rootDir, - import_path90.default.join(workspace.sidecarDir, "reports", `job-${jobId}-telemetry.json`) + import_path88.default.join(workspace.sidecarDir, "reports", `job-${jobId}-telemetry.json`) ); } function persistExecutionTelemetryReport(workspace, report) { const validated = executionTelemetryReportSchema.parse(report); const file = executionTelemetryReportFile(workspace, validated.jobId); - (0, import_fs81.mkdirSync)(import_path90.default.dirname(file), { recursive: true }); + (0, import_fs80.mkdirSync)(import_path88.default.dirname(file), { recursive: true }); writeFileAtomic(file, `${JSON.stringify(validated, null, 2)} `); return file; @@ -111109,18 +111200,18 @@ function listCertificationRuns(workspace) { } // ../../packages/intake/dist/index.js +var import_fs83 = require("fs"); +var import_path91 = __toESM(require("path"), 1); var import_fs84 = require("fs"); -var import_path93 = __toESM(require("path"), 1); +var import_path92 = __toESM(require("path"), 1); var import_fs85 = require("fs"); -var import_path94 = __toESM(require("path"), 1); +var import_path93 = __toESM(require("path"), 1); var import_fs86 = require("fs"); -var import_path95 = __toESM(require("path"), 1); +var import_path94 = __toESM(require("path"), 1); var import_fs87 = require("fs"); -var import_path96 = __toESM(require("path"), 1); +var import_path95 = __toESM(require("path"), 1); var import_fs88 = require("fs"); -var import_path97 = __toESM(require("path"), 1); -var import_fs89 = require("fs"); -var import_path98 = __toESM(require("path"), 1); +var import_path96 = __toESM(require("path"), 1); var INTAKE_STATUSES = [ /** The source specification is ingested; discovery has not run. */ "INGESTED", @@ -111845,41 +111936,41 @@ function assertIntakeId(id) { function intakeRootDir(workspace) { return assertInsideWorkspace( workspace.rootDir, - import_path91.default.join(workspace.rootDir, ".specbridge", INTAKE_DIR_NAME) + import_path89.default.join(workspace.rootDir, ".specbridge", INTAKE_DIR_NAME) ); } function intakeDir(workspace, intakeId) { assertIntakeId(intakeId); - return assertInsideWorkspace(workspace.rootDir, import_path91.default.join(intakeRootDir(workspace), intakeId)); + return assertInsideWorkspace(workspace.rootDir, import_path89.default.join(intakeRootDir(workspace), intakeId)); } function intakePath(workspace, intakeId, ...segments) { return assertInsideWorkspace( workspace.rootDir, - import_path91.default.join(intakeDir(workspace, intakeId), ...segments) + import_path89.default.join(intakeDir(workspace, intakeId), ...segments) ); } function writeJson(file, value) { - (0, import_fs82.mkdirSync)(import_path91.default.dirname(file), { recursive: true }); + (0, import_fs81.mkdirSync)(import_path89.default.dirname(file), { recursive: true }); writeFileAtomic(file, `${JSON.stringify(value, null, 2)} `); } function readJson2(file, parse3) { - if (!(0, import_fs82.existsSync)(file)) return void 0; + if (!(0, import_fs81.existsSync)(file)) return void 0; try { - return parse3(JSON.parse((0, import_fs82.readFileSync)(file, "utf8"))); + return parse3(JSON.parse((0, import_fs81.readFileSync)(file, "utf8"))); } catch { return void 0; } } function appendJsonl3(file, value) { - (0, import_fs82.mkdirSync)(import_path91.default.dirname(file), { recursive: true }); - (0, import_fs82.appendFileSync)(file, `${JSON.stringify(value)} + (0, import_fs81.mkdirSync)(import_path89.default.dirname(file), { recursive: true }); + (0, import_fs81.appendFileSync)(file, `${JSON.stringify(value)} `, "utf8"); } function readFolded(file, key, parse3) { - if (!(0, import_fs82.existsSync)(file)) return []; + if (!(0, import_fs81.existsSync)(file)) return []; const folded = /* @__PURE__ */ new Map(); - for (const line of (0, import_fs82.readFileSync)(file, "utf8").split("\n")) { + for (const line of (0, import_fs81.readFileSync)(file, "utf8").split("\n")) { if (line.trim().length === 0) continue; try { const value = parse3(JSON.parse(line)); @@ -111912,16 +112003,16 @@ function writeIntakeState(workspace, state) { } function listIntakes(workspace) { const root = intakeRootDir(workspace); - if (!(0, import_fs82.existsSync)(root)) return { intakes: [], diagnostics: [] }; + if (!(0, import_fs81.existsSync)(root)) return { intakes: [], diagnostics: [] }; const intakes = []; const diagnostics = []; - for (const entry2 of (0, import_fs82.readdirSync)(root, { withFileTypes: true })) { + for (const entry2 of (0, import_fs81.readdirSync)(root, { withFileTypes: true })) { if (!entry2.isDirectory()) continue; if (!ID_PATTERN11.test(entry2.name)) continue; - const file = import_path91.default.join(root, entry2.name, "intake.json"); - if (!(0, import_fs82.existsSync)(file)) continue; + const file = import_path89.default.join(root, entry2.name, "intake.json"); + if (!(0, import_fs81.existsSync)(file)) continue; try { - intakes.push(specIntakeStateSchema.parse(JSON.parse((0, import_fs82.readFileSync)(file, "utf8")))); + intakes.push(specIntakeStateSchema.parse(JSON.parse((0, import_fs81.readFileSync)(file, "utf8")))); } catch (cause) { diagnostics.push({ intakeId: entry2.name, @@ -111951,8 +112042,8 @@ function sourceFile(workspace, intakeId, contentHash) { } function storeSourceText(workspace, intakeId, contentHash, content) { const file = sourceFile(workspace, intakeId, contentHash); - if (!(0, import_fs82.existsSync)(file)) { - (0, import_fs82.mkdirSync)(import_path91.default.dirname(file), { recursive: true }); + if (!(0, import_fs81.existsSync)(file)) { + (0, import_fs81.mkdirSync)(import_path89.default.dirname(file), { recursive: true }); writeFileAtomic(file, content); } return file; @@ -112037,7 +112128,7 @@ function approvalFile2(workspace, intakeId) { function writeApproval(workspace, approval) { const validated = intakeApprovalSchema.parse(approval); const file = approvalFile2(workspace, validated.intakeId); - if ((0, import_fs82.existsSync)(file)) { + if ((0, import_fs81.existsSync)(file)) { throw new IntakeError( "SBI017", `Spec intake "${validated.intakeId}" is already approved; an approval is immutable.`, @@ -112096,7 +112187,7 @@ function appendIntakeEvent(workspace, intakeId, event) { function baselineFile(workspace) { return assertInsideWorkspace( workspace.rootDir, - import_path91.default.join(intakeRootDir(workspace), "baseline.json") + import_path89.default.join(intakeRootDir(workspace), "baseline.json") ); } function readProductBaseline(workspace) { @@ -112711,7 +112802,7 @@ var BUILD_MARKERS = [ ]; function detectBuildSystem(rootDir) { for (const marker of BUILD_MARKERS) { - if ((0, import_fs83.existsSync)(import_path92.default.join(rootDir, marker.file))) return marker.system; + if ((0, import_fs82.existsSync)(import_path90.default.join(rootDir, marker.file))) return marker.system; } return null; } @@ -112748,33 +112839,33 @@ var PUBLIC_INTERFACE_PATTERNS = [ var TEST_DIR_PATTERN = /^(tests?|spec|specs|__tests__|it|integration-tests?|e2e)$/i; function readGitHead(rootDir) { try { - const dotGit = import_path92.default.join(rootDir, ".git"); - if (!(0, import_fs83.existsSync)(dotGit)) return null; + const dotGit = import_path90.default.join(rootDir, ".git"); + if (!(0, import_fs82.existsSync)(dotGit)) return null; let gitDir = dotGit; - if ((0, import_fs83.statSync)(dotGit).isFile()) { - const pointer = (0, import_fs83.readFileSync)(dotGit, "utf8").trim(); + if ((0, import_fs82.statSync)(dotGit).isFile()) { + const pointer = (0, import_fs82.readFileSync)(dotGit, "utf8").trim(); const match = /^gitdir:\s*(.+)$/.exec(pointer); if (match === null) return null; const target = match[1] ?? ""; - gitDir = import_path92.default.isAbsolute(target) ? target : import_path92.default.resolve(rootDir, target); + gitDir = import_path90.default.isAbsolute(target) ? target : import_path90.default.resolve(rootDir, target); } - const headFile = import_path92.default.join(gitDir, "HEAD"); - if (!(0, import_fs83.existsSync)(headFile)) return null; - const head = (0, import_fs83.readFileSync)(headFile, "utf8").trim(); + const headFile = import_path90.default.join(gitDir, "HEAD"); + if (!(0, import_fs82.existsSync)(headFile)) return null; + const head = (0, import_fs82.readFileSync)(headFile, "utf8").trim(); if (/^[0-9a-f]{40}$/i.test(head)) return head.toLowerCase(); const refMatch = /^ref:\s*(.+)$/.exec(head); if (refMatch === null) return null; const ref = (refMatch[1] ?? "").trim(); for (const dir of refDirsFor(gitDir)) { - const refFile = import_path92.default.join(dir, ...ref.split("/")); - if (!(0, import_fs83.existsSync)(refFile)) continue; - const sha = (0, import_fs83.readFileSync)(refFile, "utf8").trim(); + const refFile = import_path90.default.join(dir, ...ref.split("/")); + if (!(0, import_fs82.existsSync)(refFile)) continue; + const sha = (0, import_fs82.readFileSync)(refFile, "utf8").trim(); if (/^[0-9a-f]{40}$/i.test(sha)) return sha.toLowerCase(); } for (const dir of refDirsFor(gitDir)) { - const packed = import_path92.default.join(dir, "packed-refs"); - if (!(0, import_fs83.existsSync)(packed)) continue; - for (const line of (0, import_fs83.readFileSync)(packed, "utf8").split("\n")) { + const packed = import_path90.default.join(dir, "packed-refs"); + if (!(0, import_fs82.existsSync)(packed)) continue; + for (const line of (0, import_fs82.readFileSync)(packed, "utf8").split("\n")) { const entry2 = /^([0-9a-f]{40})\s+(.+)$/.exec(line.trim()); if (entry2 !== null && entry2[2] === ref) return (entry2[1] ?? "").toLowerCase(); } @@ -112786,12 +112877,12 @@ function readGitHead(rootDir) { } function refDirsFor(gitDir) { const dirs = [gitDir]; - const commonFile = import_path92.default.join(gitDir, "commondir"); - if ((0, import_fs83.existsSync)(commonFile)) { + const commonFile = import_path90.default.join(gitDir, "commondir"); + if ((0, import_fs82.existsSync)(commonFile)) { try { - const target = (0, import_fs83.readFileSync)(commonFile, "utf8").trim(); + const target = (0, import_fs82.readFileSync)(commonFile, "utf8").trim(); if (target.length > 0) { - dirs.push(import_path92.default.isAbsolute(target) ? target : import_path92.default.resolve(gitDir, target)); + dirs.push(import_path90.default.isAbsolute(target) ? target : import_path90.default.resolve(gitDir, target)); } } catch { } @@ -112843,7 +112934,7 @@ function groundInRepository(deps4, request) { summary: `existing Kiro spec with ${folder.files.length} document(s)`, authoritative: false, topics: [], - path: import_path92.default.posix.join(".kiro", "specs", folder.name) + path: import_path90.default.posix.join(".kiro", "specs", folder.name) }); } for (const steering of safeSteering(workspace, notes)) { @@ -112854,7 +112945,7 @@ function groundInRepository(deps4, request) { summary: `steering document (${steering.inclusion})`, authoritative: false, topics: [], - path: import_path92.default.posix.join(".kiro", "steering", steering.fileName) + path: import_path90.default.posix.join(".kiro", "steering", steering.fileName) }); } const buildSystem = detectBuildSystem(workspace.rootDir); @@ -112898,7 +112989,7 @@ function groundInRepository(deps4, request) { }); } for (const container of modules.slice(0, 40)) { - const dir = import_path92.default.join(workspace.rootDir, container); + const dir = import_path90.default.join(workspace.rootDir, container); for (const entry2 of safeReaddir(dir, notes)) { if (!entry2.isDirectory()) continue; if (MODULE_DENYLIST.has(entry2.name) || entry2.name.startsWith(".")) continue; @@ -113050,7 +113141,7 @@ function safeSteering(workspace, notes) { } function safeReaddir(dir, notes) { try { - return (0, import_fs83.readdirSync)(dir, { withFileTypes: true }); + return (0, import_fs82.readdirSync)(dir, { withFileTypes: true }); } catch (cause) { notes.push(`Directory ${dir} could not be listed: ${message(cause)}.`); return []; @@ -113863,14 +113954,14 @@ function emptyProjectionMap() { function mapFile(workspace, intakeId) { return assertInsideWorkspace( workspace.rootDir, - import_path93.default.join(workspace.rootDir, ".specbridge", "intake", intakeId, "mission-map.json") + import_path91.default.join(workspace.rootDir, ".specbridge", "intake", intakeId, "mission-map.json") ); } function readProjectionMap(workspace, intakeId) { const file = mapFile(workspace, intakeId); - if (!(0, import_fs84.existsSync)(file)) return emptyProjectionMap(); + if (!(0, import_fs83.existsSync)(file)) return emptyProjectionMap(); try { - const raw = JSON.parse((0, import_fs84.readFileSync)(file, "utf8")); + const raw = JSON.parse((0, import_fs83.readFileSync)(file, "utf8")); return { itemContracts: raw.itemContracts ?? {}, itemDecisions: raw.itemDecisions ?? {}, @@ -113886,7 +113977,7 @@ function readProjectionMap(workspace, intakeId) { } function writeProjectionMap(workspace, intakeId, map) { const file = mapFile(workspace, intakeId); - (0, import_fs84.mkdirSync)(import_path93.default.dirname(file), { recursive: true }); + (0, import_fs83.mkdirSync)(import_path91.default.dirname(file), { recursive: true }); writeFileAtomic(file, `${JSON.stringify(map, null, 2)} `); } @@ -114546,8 +114637,8 @@ function checkProjectionEquivalence(request) { let checked = 0; let traced = 0; for (const stage of stages) { - const file = import_path94.default.join(folder.dir, `${stage}.md`); - if (!(0, import_fs85.existsSync)(file)) { + const file = import_path92.default.join(folder.dir, `${stage}.md`); + if (!(0, import_fs84.existsSync)(file)) { divergences.push({ kind: "UNRELATED_ARTIFACT", stage, @@ -114555,7 +114646,7 @@ function checkProjectionEquivalence(request) { }); continue; } - const content = (0, import_fs85.readFileSync)(file, "utf8"); + const content = (0, import_fs84.readFileSync)(file, "utf8"); artifactHashes[stage] = sha256Hex(content); for (const statement of extractNormativeStatements(stage, content)) { checked += 1; @@ -115298,7 +115389,7 @@ function startSpecIntake(deps4, request) { receivedVia: hostOf2(deps4), byteLength, contentHash, - storedAt: import_path95.default.posix.join( + storedAt: import_path93.default.posix.join( ".specbridge", "intake", intakeId, @@ -115354,20 +115445,20 @@ function startSpecIntake(deps4, request) { return { intake, source, mission }; } function startSpecIntakeFromFile(deps4, request) { - const resolved2 = import_path95.default.resolve(request.file); - if (!(0, import_fs86.existsSync)(resolved2)) { + const resolved2 = import_path93.default.resolve(request.file); + if (!(0, import_fs85.existsSync)(resolved2)) { throw new IntakeError("SBI007", `No specification file at ${request.file}.`, { remediation: ["Check the path, or pass the specification text with --text."] }); } - const size = (0, import_fs86.statSync)(resolved2).size; + const size = (0, import_fs85.statSync)(resolved2).size; if (size > INTAKE_LIMITS.maxSourceBytes) { throw new IntakeError( "SBI006", `${request.file} is ${size} bytes, over the ${INTAKE_LIMITS.maxSourceBytes}-byte bound.` ); } - const content = (0, import_fs86.readFileSync)(resolved2, "utf8"); + const content = (0, import_fs85.readFileSync)(resolved2, "utf8"); return startSpecIntake(deps4, { ...request, kind: "file", @@ -116092,7 +116183,7 @@ var repositoryManifestSchema = external_exports.object({ function repositoryManifestFile(workspace) { return assertInsideWorkspace( workspace.rootDir, - import_path96.default.join(workspace.sidecarDir, "repositories.json") + import_path94.default.join(workspace.sidecarDir, "repositories.json") ); } var DETECTION_DENYLIST = /* @__PURE__ */ new Set([ @@ -116109,10 +116200,10 @@ var DETECTION_DENYLIST = /* @__PURE__ */ new Set([ ]); function readRepositoryManifest(workspace) { const file = repositoryManifestFile(workspace); - if (!(0, import_fs87.existsSync)(file)) return void 0; + if (!(0, import_fs86.existsSync)(file)) return void 0; let raw; try { - raw = JSON.parse((0, import_fs87.readFileSync)(file, "utf8")); + raw = JSON.parse((0, import_fs86.readFileSync)(file, "utf8")); } catch (cause) { throw new IntakeError("SBI018", `The repository manifest at ${file} is not valid JSON.`, { remediation: ["Fix or delete .specbridge/repositories.json; without it the workspace root is the repository."], @@ -116132,7 +116223,7 @@ function resolveRepositories(workspace) { } seen.add(entry2.id); const absDir = assertInsideWorkspace(workspace.rootDir, entry2.path); - if (!(0, import_fs87.existsSync)(absDir) || !(0, import_fs87.statSync)(absDir).isDirectory()) { + if (!(0, import_fs86.existsSync)(absDir) || !(0, import_fs86.statSync)(absDir).isDirectory()) { throw new IntakeError( "SBI018", `The repository manifest names "${entry2.id}" at ${entry2.path}, which is not a directory.`, @@ -116149,11 +116240,11 @@ function resolveRepositories(workspace) { } const children = []; try { - for (const entry2 of (0, import_fs87.readdirSync)(workspace.rootDir, { withFileTypes: true })) { + for (const entry2 of (0, import_fs86.readdirSync)(workspace.rootDir, { withFileTypes: true })) { if (!entry2.isDirectory()) continue; if (DETECTION_DENYLIST.has(entry2.name) || entry2.name.startsWith(".")) continue; - const absDir = import_path96.default.join(workspace.rootDir, entry2.name); - if (!(0, import_fs87.existsSync)(import_path96.default.join(absDir, ".git"))) continue; + const absDir = import_path94.default.join(workspace.rootDir, entry2.name); + if (!(0, import_fs86.existsSync)(import_path94.default.join(absDir, ".git"))) continue; if (children.length >= BOOTSTRAP_LIMITS.maxRepositories) { notes.push("More child repositories exist than the bootstrap bound; declare a manifest to choose."); break; @@ -116164,7 +116255,7 @@ function resolveRepositories(workspace) { notes.push(`The workspace root could not be listed: ${cause instanceof Error ? cause.message : String(cause)}.`); } if (children.length > 0) { - const rootIsRepo = (0, import_fs87.existsSync)(import_path96.default.join(workspace.rootDir, ".git")); + const rootIsRepo = (0, import_fs86.existsSync)(import_path94.default.join(workspace.rootDir, ".git")); const repositories = rootIsRepo ? [resolved(workspace, rootRepositoryId(workspace), workspace.rootDir, void 0), ...children] : children; return { repositories: repositories.slice(0, BOOTSTRAP_LIMITS.maxRepositories), @@ -116179,18 +116270,18 @@ function resolveRepositories(workspace) { }; } function rootRepositoryId(workspace) { - const base = import_path96.default.basename(workspace.rootDir).replace(/[^A-Za-z0-9._-]/g, "-").replace(/^[^A-Za-z0-9]+/, ""); + const base = import_path94.default.basename(workspace.rootDir).replace(/[^A-Za-z0-9._-]/g, "-").replace(/^[^A-Za-z0-9]+/, ""); return base.length > 0 ? base.slice(0, 64) : "workspace"; } function resolved(workspace, repositoryId, absDir, role) { - const relPath2 = import_path96.default.relative(workspace.rootDir, absDir).replace(/\\/g, "/"); + const relPath2 = import_path94.default.relative(workspace.rootDir, absDir).replace(/\\/g, "/"); return { repositoryId, relPath: relPath2, ...role !== void 0 ? { role } : {}, absDir, gitHead: readGitHead(absDir), - isGitRepository: (0, import_fs87.existsSync)(import_path96.default.join(absDir, ".git")) + isGitRepository: (0, import_fs86.existsSync)(import_path94.default.join(absDir, ".git")) }; } function repositoryOfPath(repositories, workspaceRelativePath) { @@ -116383,7 +116474,7 @@ function synthesizeSystemFindings(input) { }); } const manifestEntries = entries.filter( - (entry2) => MANIFEST_BASENAMES.has(import_path97.default.posix.basename(entry2.path).toLowerCase()) + (entry2) => MANIFEST_BASENAMES.has(import_path95.default.posix.basename(entry2.path).toLowerCase()) ); const architectureLabels = /* @__PURE__ */ new Map(); for (const entry2 of manifestEntries.slice(0, 40)) { @@ -116428,7 +116519,7 @@ function synthesizeSystemFindings(input) { architecture.push({ findingId: ids("arc"), class: "OBSERVED_IMPLEMENTATION", - statement: clip3(`${label} (declared by ${import_path97.default.posix.basename(entry2.path)}).`), + statement: clip3(`${label} (declared by ${import_path95.default.posix.basename(entry2.path)}).`), evidence: [fileRef(entry2)] }); } @@ -116573,7 +116664,7 @@ function synthesizeSystemFindings(input) { findingId: ids("con"), class: "OBSERVED_IMPLEMENTATION", statement: clip3( - `Repository "${repo.repositoryId}" builds with ${import_path97.default.posix.basename(marker.path)}.` + `Repository "${repo.repositoryId}" builds with ${import_path95.default.posix.basename(marker.path)}.` ), evidence: [fileRef(marker)] }); @@ -116645,9 +116736,9 @@ function clip3(value) { } function boundedRead(workspace, relPath2) { try { - const abs = import_path97.default.join(workspace.rootDir, relPath2); - if (!(0, import_fs88.existsSync)(abs)) return void 0; - const body = (0, import_fs88.readFileSync)(abs, "utf8"); + const abs = import_path95.default.join(workspace.rootDir, relPath2); + if (!(0, import_fs87.existsSync)(abs)) return void 0; + const body = (0, import_fs87.readFileSync)(abs, "utf8"); return body.length > MAX_MANIFEST_READ_BYTES ? body.slice(0, MAX_MANIFEST_READ_BYTES) : body; } catch { return void 0; @@ -116688,25 +116779,25 @@ function safeSeals(workspace) { } } function bootstrapDir(workspace) { - return assertInsideWorkspace(workspace.rootDir, import_path98.default.join(workspace.sidecarDir, "bootstrap")); + return assertInsideWorkspace(workspace.rootDir, import_path96.default.join(workspace.sidecarDir, "bootstrap")); } function snapshotFile(workspace) { return assertInsideWorkspace( workspace.rootDir, - import_path98.default.join(bootstrapDir(workspace), "current-system-snapshot.json") + import_path96.default.join(bootstrapDir(workspace), "current-system-snapshot.json") ); } function readCurrentSystemSnapshot(workspace) { const file = snapshotFile(workspace); - if (!(0, import_fs89.existsSync)(file)) return void 0; + if (!(0, import_fs88.existsSync)(file)) return void 0; try { - return currentSystemSnapshotSchema.parse(JSON.parse((0, import_fs89.readFileSync)(file, "utf8"))); + return currentSystemSnapshotSchema.parse(JSON.parse((0, import_fs88.readFileSync)(file, "utf8"))); } catch { return void 0; } } function persistSnapshot(workspace, snapshot2) { - (0, import_fs89.mkdirSync)(bootstrapDir(workspace), { recursive: true }); + (0, import_fs88.mkdirSync)(bootstrapDir(workspace), { recursive: true }); writeFileAtomic(snapshotFile(workspace), `${JSON.stringify(snapshot2, null, 2)} `); } @@ -116879,7 +116970,7 @@ function inspectWorkspace(deps4, options) { } let body; try { - body = (0, import_fs89.readFileSync)( + body = (0, import_fs88.readFileSync)( assertInsideWorkspace(workspace.rootDir, entry2.path), "utf8" ); @@ -121519,10 +121610,10 @@ Examples: // ../../packages/mcp-server/dist/chunk-XJ3HVTHJ.js var import_buffer7 = require("buffer"); -var import_fs90 = require("fs"); -var import_path99 = __toESM(require("path"), 1); +var import_fs89 = require("fs"); +var import_path97 = __toESM(require("path"), 1); var import_crypto31 = require("crypto"); -var import_path100 = __toESM(require("path"), 1); +var import_path98 = __toESM(require("path"), 1); // ../../node_modules/.pnpm/zod@3.25.76/node_modules/zod/v4/core/core.js var NEVER2 = Object.freeze({ @@ -131850,12 +131941,12 @@ var EMPTY_COMPLETION_RESULT = { }; // ../../packages/mcp-server/dist/chunk-XJ3HVTHJ.js +var import_fs90 = require("fs"); var import_fs91 = require("fs"); +var import_path99 = __toESM(require("path"), 1); var import_fs92 = require("fs"); -var import_path101 = __toESM(require("path"), 1); -var import_fs93 = require("fs"); var import_os3 = __toESM(require("os"), 1); -var import_path102 = __toESM(require("path"), 1); +var import_path100 = __toESM(require("path"), 1); // ../../node_modules/.pnpm/@modelcontextprotocol+sdk@1.29.0_zod@3.25.76/node_modules/@modelcontextprotocol/sdk/dist/esm/server/stdio.js var import_node_process11 = __toESM(require("process"), 1); @@ -132314,10 +132405,10 @@ function validateProjectRoot(value, source, cwd) { remediation: ["Pass a plain filesystem path as --project-root."] }; } - const resolved2 = import_path99.default.resolve(cwd, value); + const resolved2 = import_path97.default.resolve(cwd, value); let canonical; try { - canonical = (0, import_fs90.realpathSync)(resolved2); + canonical = (0, import_fs89.realpathSync)(resolved2); } catch { return { ok: false, @@ -132330,7 +132421,7 @@ function validateProjectRoot(value, source, cwd) { } let stats; try { - stats = (0, import_fs90.statSync)(canonical); + stats = (0, import_fs89.statSync)(canonical); } catch { return { ok: false, @@ -132587,8 +132678,8 @@ var paginationShape = external_exports.object({ nextCursor: external_exports.string().optional() }); function repoRelative2(workspace, target) { - const relative = import_path100.default.isAbsolute(target) ? import_path100.default.relative(workspace.rootDir, target) : target; - const posix = relative.split(import_path100.default.sep).join("/"); + const relative = import_path98.default.isAbsolute(target) ? import_path98.default.relative(workspace.rootDir, target) : target; + const posix = relative.split(import_path98.default.sep).join("/"); return posix === "" ? "." : posix; } function toDiagnosticView(workspace, diagnostic) { @@ -133088,7 +133179,7 @@ function registerRunResources(server, context) { throw resourceNotFound(`Run "${runId}"`, "List runs with the run_list tool."); } const directory = runDir(workspace, record5.runId); - const artifactNames = (0, import_fs91.existsSync)(directory) ? (0, import_fs91.readdirSync)(directory).filter((name) => !REDACTED_ARTIFACTS.has(name)).sort((a2, b) => a2.localeCompare(b, "en")) : []; + const artifactNames = (0, import_fs90.existsSync)(directory) ? (0, import_fs90.readdirSync)(directory).filter((name) => !REDACTED_ARTIFACTS.has(name)).sort((a2, b) => a2.localeCompare(b, "en")) : []; return jsonContents(context, uri.href, buildRunDetail(workspace, record5, artifactNames)); } ); @@ -134904,7 +134995,7 @@ function registerRunReadTool(server, context) { }); } const directory = runDir(workspace, record5.runId); - const artifactNames = (0, import_fs92.existsSync)(directory) ? (0, import_fs92.readdirSync)(directory).filter((name) => !REDACTED_ARTIFACTS2.has(name)).sort((a2, b) => a2.localeCompare(b, "en")) : []; + const artifactNames = (0, import_fs91.existsSync)(directory) ? (0, import_fs91.readdirSync)(directory).filter((name) => !REDACTED_ARTIFACTS2.has(name)).sort((a2, b) => a2.localeCompare(b, "en")) : []; const detail = buildRunDetail(workspace, record5, artifactNames); const lines = [ `Run ${detail.summary.runId} \u2014 ${detail.summary.runType} for spec "${detail.summary.specName}"${detail.summary.taskId !== void 0 ? `, task ${detail.summary.taskId}` : ""}.`, @@ -135264,7 +135355,7 @@ function registerSpecRunVerificationTool(server, context) { durationMs: command.durationMs, timedOut: command.timedOut })); - const reportPath = result.artifactsDir !== void 0 ? import_path101.default.relative(workspace.rootDir, result.artifactsDir).split(import_path101.default.sep).join("/") : void 0; + const reportPath = result.artifactsDir !== void 0 ? import_path99.default.relative(workspace.rootDir, result.artifactsDir).split(import_path99.default.sep).join("/") : void 0; const commandLines = commands.map( (command) => `- ${command.name}: ${command.disposition}${command.disposition === "executed" ? command.passed ? " (passed)" : ` (FAILED, exit ${command.exitCode ?? "none"})` : ""}` ); @@ -135383,18 +135474,18 @@ var conformanceSummaryShape = external_exports.object({ note: external_exports.string() }); async function invocationFreeConformanceSummary(profile) { - const scratch = (0, import_fs93.mkdtempSync)(import_path102.default.join(import_os3.default.tmpdir(), "specbridge-mcp-conformance-")); + const scratch = (0, import_fs92.mkdtempSync)(import_path100.default.join(import_os3.default.tmpdir(), "specbridge-mcp-conformance-")); let result; try { result = await runRunnerConformance({ profile, workspaceRoot: scratch, - runDir: import_path102.default.join(scratch, ".specbridge-conformance-runs"), + runDir: import_path100.default.join(scratch, ".specbridge-conformance-runs"), invocationsAllowed: false, timeoutMs: RUNNER_PROBE_TIMEOUT_MS }); } finally { - (0, import_fs93.rmSync)(scratch, { recursive: true, force: true }); + (0, import_fs92.rmSync)(scratch, { recursive: true, force: true }); } return { passed: result.passed, @@ -139281,8 +139372,8 @@ async function runMcpServe(argv2, io = { } // ../../packages/mcp-server/dist/index.js -var import_fs94 = require("fs"); -var import_path103 = __toESM(require("path"), 1); +var import_fs93 = require("fs"); +var import_path101 = __toESM(require("path"), 1); async function runMcpDoctor(options = {}) { const checks = []; const env = options.env ?? process.env; @@ -139375,7 +139466,7 @@ async function runMcpDoctor(options = {}) { const pluginRoot = env["CLAUDE_PLUGIN_ROOT"]; if (pluginRoot !== void 0 && pluginRoot.length > 0) { const missing = ["dist/mcp-server.cjs", "dist/cli.cjs"].filter( - (relative) => !(0, import_fs94.existsSync)(import_path103.default.join(pluginRoot, relative)) + (relative) => !(0, import_fs93.existsSync)(import_path101.default.join(pluginRoot, relative)) ); checks.push( missing.length === 0 ? { name: "plugin-bundle", status: "ok", detail: `Bundled executables present under ${pluginRoot}` } : { diff --git a/integrations/codex-plugin/specbridge/dist/mcp-server.cjs b/integrations/codex-plugin/specbridge/dist/mcp-server.cjs index c00c572..2f96c74 100644 --- a/integrations/codex-plugin/specbridge/dist/mcp-server.cjs +++ b/integrations/codex-plugin/specbridge/dist/mcp-server.cjs @@ -42984,6 +42984,29 @@ function parseClaudeEnvelope(stdout) { } return { problem: "no JSON result envelope found in the runner output" }; } +var MAX_STDERR_DIAGNOSTIC_CHARS = 500; +var CREDENTIAL_PATTERNS = [ + /\bsk-[A-Za-z0-9_-]{8,}/gi, + /\bbearer\s+[A-Za-z0-9._-]{8,}/gi, + /\boauth-[A-Za-z0-9-]{6,}/gi, + /\b(?:api[-_]?keys?|access[-_]?tokens?|secrets?|passwords?)\b(?:\s*[:=]\s*\S+)?/gi +]; +function redactCredentials(text15) { + let redacted = text15; + for (const pattern of CREDENTIAL_PATTERNS) redacted = redacted.replace(pattern, "[redacted]"); + return redacted; +} +function stderrDiagnostic(stderr) { + const collapsed = redactCredentials(stderr).replace(/\s+/g, " ").trim(); + if (collapsed.length === 0) return void 0; + if (collapsed.length <= MAX_STDERR_DIAGNOSTIC_CHARS) return collapsed; + return `${collapsed.slice(0, MAX_STDERR_DIAGNOSTIC_CHARS)}\u2026 [truncated]`; +} +function claudeFailureProblem(problem, processResult) { + if (processResult.status !== "nonzero-exit") return problem; + const diagnostic = stderrDiagnostic(processResult.stderr); + return diagnostic === void 0 ? problem : `${problem} (claude stderr: ${diagnostic})`; +} var ClaudeCodeRunner = class { name = "claude-code"; kind = "claude-code"; @@ -43125,6 +43148,85 @@ var ClaudeCodeRunner = class { const stageReport = report; return { ...rest, ...stageReport !== void 0 ? { report: stageReport } : {} }; } + async invokeStructured(input, execution) { + const started = Date.now(); + const probe = await this.probe(); + const unavailable = this.unavailableResult(probe, started); + if (unavailable !== void 0) { + const { report: _report, ...rest } = unavailable; + return rest; + } + const plan = buildClaudeInvocation({ + config: this.config, + probe, + prompt: input.prompt, + toolPolicy: input.toolPolicy, + outputJsonSchema: input.outputJsonSchema, + execution + }); + try { + const processResult = await runClaudeInvocation(plan, this.config, execution); + const parsed = parseClaudeEnvelope(processResult.stdout); + const usage = usageFromEnvelope(parsed.envelope, processResult.observation.durationMs); + const cost = costFromEnvelope(parsed.envelope); + const base = { + runner: this.name, + rawStdout: processResult.stdout, + rawStderr: processResult.stderr, + process: processResult.observation, + durationMs: Math.max(0, Date.now() - started), + warnings: plan.skippedFlags.map( + (flag) => `flag ${flag} is unsupported by this Claude Code version and was skipped` + ), + ...parsed.envelope?.session_id !== void 0 ? { sessionId: parsed.envelope.session_id } : {}, + ...usage !== void 0 ? { usage } : {}, + ...cost !== void 0 ? { cost } : {} + }; + switch (processResult.status) { + case "timeout": + return { ...base, outcome: "timed-out", failureReason: processResult.failureReason ?? "timeout" }; + case "cancelled": + return { ...base, outcome: "cancelled", failureReason: processResult.failureReason ?? "cancelled" }; + case "output-limit": + case "spawn-failed": + return { + ...base, + outcome: "failed", + failureReason: processResult.failureReason ?? processResult.status + }; + case "ok": + case "nonzero-exit": + break; + } + if (this.looksPermissionDenied(processResult, parsed.envelope?.subtype, parsed.envelope)) { + return { + ...base, + outcome: "permission-denied", + failureReason: "Claude Code reported a permission denial." + }; + } + if (processResult.status === "nonzero-exit" || parsed.envelope?.is_error === true) { + return { + ...base, + outcome: "malformed-output", + failureReason: processResult.status === "nonzero-exit" ? claudeFailureProblem(parsed.problem ?? "the runner produced no output", processResult) : `Claude Code reported an error result${parsed.envelope?.subtype !== void 0 ? ` (${parsed.envelope.subtype})` : ""}`, + ...parsed.reportText !== void 0 ? { invalidStructuredOutput: parsed.reportText } : {} + }; + } + const text15 = parsed.structuredResult !== void 0 ? JSON.stringify(parsed.structuredResult) : parsed.reportText; + if (text15 === void 0 || safeJsonParse(text15) === void 0) { + return { + ...base, + outcome: "malformed-output", + failureReason: parsed.problem ?? "the runner returned no valid JSON document", + ...text15 !== void 0 ? { invalidStructuredOutput: text15 } : {} + }; + } + return { ...base, outcome: "completed", text: text15.trim() }; + } finally { + cleanupTempFiles(plan); + } + } async executeTask(input, execution) { return this.runTask(input.prompt, execution, { ...input.sessionId !== void 0 ? { sessionId: input.sessionId } : {} @@ -44161,6 +44263,115 @@ var CodexCliRunner = class { const stageReport = report; return { ...rest, ...stageReport !== void 0 ? { report: stageReport } : {} }; } + async invokeStructured(input, execution) { + const started = Date.now(); + const probe = await this.probe(); + const unavailable = this.unavailableResult(probe, started); + if (unavailable !== void 0) { + const { report: _report, ...rest } = unavailable; + return rest; + } + const plan = buildCodexInvocation({ + config: this.config, + probe, + prompt: input.prompt, + toolPolicy: input.toolPolicy, + outputJsonSchema: input.outputJsonSchema, + execution + }); + try { + const processResult = await runCodexInvocation(plan, this.config, execution); + const stream = parseCodexEventStream(processResult.stdout); + const warnings = plan.skippedFlags.map( + (flag) => `flag ${flag} is unsupported by this Codex CLI version and was skipped` + ); + const normalizedEvents = normalizeCodexEvents( + stream, + { + runner: this.name, + profile: this.name, + runId: "pending", + attemptId: "pending" + }, + () => (/* @__PURE__ */ new Date()).toISOString() + ); + const usage = usageFromStream(stream, processResult.observation.durationMs, this.config.model); + const base = { + runner: this.name, + rawStdout: redactCodexStdoutForRetention(processResult.stdout), + rawStderr: processResult.stderr, + process: processResult.observation, + durationMs: Math.max(0, Date.now() - started), + warnings, + normalizedEvents, + ...usage !== void 0 ? { usage } : {}, + ...stream.threadId !== void 0 ? { sessionId: stream.threadId } : {} + }; + switch (processResult.status) { + case "timeout": + return { + ...base, + outcome: "timed-out", + failureReason: processResult.failureReason ?? "timeout", + error: runnerError({ code: "timed_out", message: "The Codex process timed out." }) + }; + case "cancelled": + return { + ...base, + outcome: "cancelled", + failureReason: processResult.failureReason ?? "cancelled", + error: runnerError({ code: "cancelled", message: "The Codex process was cancelled." }) + }; + case "output-limit": + return { + ...base, + outcome: "failed", + failureReason: processResult.failureReason ?? "output limit exceeded", + error: runnerError({ + code: "output_limit_exceeded", + message: "The Codex process exceeded its output limit." + }) + }; + case "spawn-failed": + return { + ...base, + outcome: "failed", + failureReason: processResult.failureReason ?? "spawn failed", + error: runnerError({ + code: "executable_not_found", + message: "The Codex CLI could not be started." + }) + }; + case "ok": + break; + case "nonzero-exit": { + const error2 = classifyCodexFailure(processResult.stderr, stream.errors); + return { + ...base, + outcome: error2.code === "permission_denied" ? "permission-denied" : "failed", + failureReason: error2.message, + error: error2 + }; + } + } + const finalText = readLastMessage(plan) ?? stream.lastAgentMessage; + if (finalText === void 0 || strictJsonParse(finalText) === void 0) { + return { + ...base, + outcome: "malformed-output", + failureReason: finalText === void 0 ? "the runner returned no final structured result" : "the final Codex message is not a bare JSON document", + error: runnerError({ + code: "structured_output_invalid", + message: "The Codex orchestration response was not a valid JSON document." + }), + ...finalText !== void 0 ? { invalidStructuredOutput: finalText } : {} + }; + } + return { ...base, outcome: "completed", text: finalText.trim() }; + } finally { + cleanupCodexTempFiles(plan); + } + } async executeTask(input, execution) { return this.runTask(input.prompt, execution, {}); } diff --git a/packages/orchestration/src/driver/driver.ts b/packages/orchestration/src/driver/driver.ts index 16ca7cc..d8b5321 100644 --- a/packages/orchestration/src/driver/driver.ts +++ b/packages/orchestration/src/driver/driver.ts @@ -2,7 +2,7 @@ import { existsSync, readFileSync } from 'node:fs'; import path from 'node:path'; import { analyzeSpec, requireSpec } from '@specbridge/compat-kiro'; import type { LocalExecutionMode, WorkspaceInfo } from '@specbridge/core'; -import type { ClaudeProbe, RunnerRegistry } from '@specbridge/runners'; +import type { RunnerRegistry } from '@specbridge/runners'; import type { LocalModelManager } from '@specbridge/runners'; import type { AgentContractRole, @@ -338,10 +338,6 @@ export async function driveJob( } } - // One Claude probe per driver run: the CLI's flag surface cannot change - // mid-run, and re-probing spawns three processes per reasoning role. - const probeCache: { probe: ClaudeProbe | undefined } = { probe: undefined }; - const localManager: LocalModelManager | undefined = createLocalManager(deps.config, (event) => { emit('local-model', `${event.type}: ${event.detail}`); if (event.type === 'ready') { @@ -463,7 +459,6 @@ export async function driveJob( case 'RUN_ROLE': { const outcome = await handleRoleDecision(deps, jobId, decision, { localManager, - probeCache, signal, emit, }); @@ -1013,6 +1008,7 @@ export async function driveJob( ? await driveObjective({ workspace: deps.workspace, config: deps.config, + registry: deps.registry, jobId, specName: job.specName, node, @@ -1022,7 +1018,6 @@ export async function driveJob( allowDirty, runnerProfile: decision.worker.runnerProfile, localManager, - probeCache, ...(deps.clock !== undefined ? { clock: deps.clock } : {}), ...(deps.idFactory !== undefined ? { idFactory: deps.idFactory } : {}), ...(signal !== undefined ? { signal } : {}), @@ -2372,7 +2367,6 @@ async function handleRoleDecision( decision: Extract, runtime: { localManager: LocalModelManager | undefined; - probeCache: { probe: ClaudeProbe | undefined }; signal: AbortSignal | undefined; emit: (kind: DriverEvent['kind'], message: string) => void; }, @@ -2477,7 +2471,8 @@ async function handleRoleDecision( code: 'LARGE_WORKER_FAILED', message: `The large-agent ${role} failed twice: ${result.problem.slice(0, 500)}`, remediation: [ - 'Check the Claude Code installation with `specbridge runner doctor claude-code`.', + `Check runner profile "${decision.worker.runnerProfile ?? deps.config.defaultRunner}" with ` + + `\`specbridge runner doctor ${decision.worker.runnerProfile ?? deps.config.defaultRunner}\`.`, // The excerpt is the whole point of the remediation. A job blocked // on "the response is not a single valid JSON document" with // nothing retained leaves an operator a message and no evidence, @@ -2629,7 +2624,6 @@ async function runRole( packet: string, runtime: { localManager: LocalModelManager | undefined; - probeCache: { probe: ClaudeProbe | undefined }; signal: AbortSignal | undefined; }, ): Promise> { @@ -2650,15 +2644,14 @@ async function runRole( const result = await runLargeRole({ workspace: deps.workspace, config: deps.config, + registry: deps.registry, runnerProfile: decision.worker.runnerProfile ?? deps.config.defaultRunner, role, packet, scratchDir: path.join(jobDir(deps.workspace, jobId), 'scratch'), timeoutMs: 600_000, signal: runtime.signal, - cachedProbe: runtime.probeCache.probe, }); - if (result.probe !== undefined) runtime.probeCache.probe = result.probe; return result; } diff --git a/packages/orchestration/src/driver/workers.ts b/packages/orchestration/src/driver/workers.ts index f4fd42f..9054a9c 100644 --- a/packages/orchestration/src/driver/workers.ts +++ b/packages/orchestration/src/driver/workers.ts @@ -1,19 +1,11 @@ -import { rmSync } from 'node:fs'; -import path from 'node:path'; -import type { AgentConfig, ClaudeProfileConfig, WorkspaceInfo } from '@specbridge/core'; +import type { AgentConfig, WorkspaceInfo } from '@specbridge/core'; import { effectiveLocalInputCharacters } from '@specbridge/core'; import { LocalModelManager, - buildClaudeInvocation, + createDefaultRunnerRegistry, localStructuredInference, - claudeFailureProblem, - parseClaudeEnvelope, - probeClaude, - runSafeProcess, - usageFromEnvelope, - costFromEnvelope, } from '@specbridge/runners'; -import type { ClaudeProbe, LocalModelEvent } from '@specbridge/runners'; +import type { LocalModelEvent, RunnerRegistry } from '@specbridge/runners'; import { AGENT_OUTPUT_JSON_SCHEMAS, correctionMessage, @@ -260,6 +252,8 @@ export async function runLocalRole( export interface LargeRoleInvocation { workspace: WorkspaceInfo; config: AgentConfig; + /** Optional for backwards compatibility; production injects the shared registry. */ + registry?: RunnerRegistry | undefined; /** Runner profile name of the large-agent worker. */ runnerProfile: string; role: AgentContractRole; @@ -268,17 +262,10 @@ export interface LargeRoleInvocation { scratchDir: string; timeoutMs: number; signal?: AbortSignal | undefined; - /** - * Reuse a probe from earlier in the SAME driver run. Probing spawns three - * short-lived processes; a long-running job invoking many reasoning roles - * would otherwise re-detect an executable that cannot change flag surface - * mid-run. A vanished CLI still fails safely at the real invocation. - */ - cachedProbe?: ClaudeProbe | undefined; } /** - * Run one reasoning role on Claude Code. + * Run one reasoning role on the explicitly selected runner profile. * * Inspect-only: the invocation gets Read/Glob/Grep, never Edit/Write/Bash, * so a reasoning dispatch cannot mutate the repository whatever the model @@ -288,27 +275,26 @@ export interface LargeRoleInvocation { */ export async function runLargeRole( invocation: LargeRoleInvocation & { role: Role }, -): Promise & { probe?: ClaudeProbe }> { - const profile = invocation.config.runnerProfiles[invocation.runnerProfile]; - if (profile === undefined || profile.runner !== 'claude-code') { +): Promise> { + const registry = invocation.registry ?? createDefaultRunnerRegistry(invocation.config); + let profile; + try { + profile = registry.getProfile(invocation.runnerProfile); + } catch (cause) { return { ok: false, kind: 'worker-unavailable', - problem: `Runner profile "${invocation.runnerProfile}" is not a Claude Code profile.`, + problem: cause instanceof Error ? cause.message : `Runner profile "${invocation.runnerProfile}" is unavailable.`, }; } - const claudeProfile = profile as ClaudeProfileConfig; - - const probe = - invocation.cachedProbe ?? - (await probeClaude(claudeProfile, { - ...(invocation.signal !== undefined ? { signal: invocation.signal } : {}), - })); - if (!probe.found || probe.status === 'unavailable' || probe.status === 'error') { + if (profile.config.enabled !== true || profile.runner.invokeStructured === undefined) { return { ok: false, kind: 'worker-unavailable', - problem: `The Claude Code CLI is not available (status ${probe.status}).`, + problem: + profile.config.enabled !== true + ? `Runner profile "${invocation.runnerProfile}" is disabled.` + : `Runner profile "${invocation.runnerProfile}" does not support structured orchestration roles.`, }; } @@ -320,99 +306,52 @@ export async function runLargeRole( invocation.packet, ].join('\n'); - const plan = buildClaudeInvocation({ - config: claudeProfile, - probe, + const result = await profile.runner.invokeStructured({ prompt, toolPolicy: 'inspect-only', + schemaName: invocation.role, outputJsonSchema: AGENT_OUTPUT_JSON_SCHEMAS[invocation.role], - execution: { - workspaceRoot: invocation.workspace.rootDir, - runDir: invocation.scratchDir, - timeoutMs: invocation.timeoutMs, - }, + }, { + workspaceRoot: invocation.workspace.rootDir, + runDir: invocation.scratchDir, + timeoutMs: invocation.timeoutMs, + ...(invocation.signal !== undefined ? { signal: invocation.signal } : {}), }); - - try { - const processResult = await runSafeProcess({ - executable: plan.executable, - argv: plan.argv, - cwd: invocation.workspace.rootDir, - timeoutMs: invocation.timeoutMs, - stdin: plan.stdin, - ...(invocation.signal !== undefined ? { signal: invocation.signal } : {}), - }); - if (processResult.status === 'cancelled') { - return { ok: false, kind: 'cancelled', problem: 'The role invocation was cancelled.' }; - } - if (processResult.status !== 'ok' && processResult.status !== 'nonzero-exit') { - return { - ok: false, - kind: 'worker-unavailable', - problem: processResult.failureReason ?? `the runner process ended with status ${processResult.status}`, - }; - } - const parsed = parseClaudeEnvelope(processResult.stdout); - if (parsed.problem !== undefined) { - // A non-zero exit with no usable envelope means the CLI's own stderr - // message is the only explanation. Keep a bounded, scrubbed excerpt: - // a provider failure stays a WORKER failure, never a task failure. - return { - ok: false, - kind: 'invalid-output', - problem: claudeFailureProblem(parsed.problem, processResult), - probe, - }; - } - const text = - parsed.structuredResult !== undefined - ? JSON.stringify(parsed.structuredResult) - : (parsed.reportText ?? ''); - const validated = validateAgentOutput(invocation.role, text); - if (!validated.ok) { - if (looksLikeAuthenticationFailure(text)) { - return { - ok: false, - // NOT invalid-output: the worker is unusable, not incoherent, and - // the two need different answers from a person. - kind: 'worker-unavailable', - problem: - `The ${invocation.role} worker is not authenticated: ${observedExcerpt(text)}`, - observed: observedExcerpt(text), - probe, - }; - } - return { - ok: false, - kind: 'invalid-output', - problem: validated.problem, - observed: observedExcerpt(text), - probe, - }; - } - const usage = usageFromEnvelope(parsed.envelope, 0); - const cost = costFromEnvelope(parsed.envelope); + if (result.outcome === 'cancelled') { + return { ok: false, kind: 'cancelled', problem: result.failureReason ?? 'The role invocation was cancelled.' }; + } + if (result.outcome !== 'completed' || result.text === undefined) { + const observed = result.invalidStructuredOutput; + return { + ok: false, + kind: result.outcome === 'malformed-output' ? 'invalid-output' : 'worker-unavailable', + problem: + result.failureReason ?? + result.error?.message ?? + `Runner profile "${invocation.runnerProfile}" ended with ${result.outcome}.`, + ...(observed !== undefined ? { observed: observedExcerpt(observed) } : {}), + }; + } + const validated = validateAgentOutput(invocation.role, result.text); + if (!validated.ok) { return { - ok: true, - output: validated.output, - raw: text, - usage: { - inputTokens: usage?.inputTokens ?? null, - outputTokens: usage?.outputTokens ?? null, - // Only provider-reported USD amounts count; nothing is fabricated. - costUsd: cost !== null && cost !== undefined && cost.currency === 'USD' ? cost.amount : null, - }, - corrected: false, - probe, + ok: false, + kind: 'invalid-output', + problem: validated.problem, + observed: observedExcerpt(result.text), }; - } finally { - // The scratch directory holds only this invocation's temp schema file. - try { - rmSync(path.join(invocation.scratchDir, 'tmp'), { recursive: true, force: true }); - } catch { - // Temp cleanup is best-effort; nothing durable lives there. - } } + return { + ok: true, + output: validated.output, + raw: result.text, + usage: { + inputTokens: result.usage?.inputTokens ?? null, + outputTokens: result.usage?.outputTokens ?? null, + costUsd: result.cost?.currency === 'USD' ? result.cost.amount : null, + }, + corrected: false, + }; } /** Build (or reuse) the shared local model manager for a driver run. */ diff --git a/packages/orchestration/src/jobs/routing.ts b/packages/orchestration/src/jobs/routing.ts index d29f8af..93bc846 100644 --- a/packages/orchestration/src/jobs/routing.ts +++ b/packages/orchestration/src/jobs/routing.ts @@ -25,13 +25,14 @@ import type { AgentRole, ComplexityClass, EscalationReason } from './vocabulary. */ export const LOCAL_WORKER_ID = 'local-llamacpp'; +/** @deprecated Persisted legacy identity; new strong workers use their runner profile name. */ export const CLAUDE_WORKER_ID = 'claude-code'; /** * Derive the worker roster from configuration. The local worker exists only - * when local inference is enabled and coherently configured; the Claude Code - * worker always exists (its availability is probed at dispatch time by the - * existing runner platform, which owns detection). + * when local inference is enabled and coherently configured; the strong + * worker is the explicitly selected default runner profile. Availability is + * probed by the runner adapter at dispatch time. */ export function resolveWorkers(config: AgentConfig): JobWorkerProfile[] { const workers: JobWorkerProfile[] = []; @@ -59,7 +60,7 @@ export function resolveWorkers(config: AgentConfig): JobWorkerProfile[] { } workers.push({ - workerId: CLAUDE_WORKER_ID, + workerId: config.defaultRunner, runnerProfile: config.defaultRunner, roles: [ 'CLASSIFIER', @@ -170,7 +171,7 @@ export function selectWorker(input: SelectWorkerInput): WorkerSelection { ); if (writer === undefined) { throw new OrchestrationError('SBO034', `No repository-writing worker is available for ${role}.`, { - remediation: ['Check the Claude Code runner with `specbridge runner doctor claude-code`.'], + remediation: ['Check the configured default runner with `specbridge runner doctor`.'], failureCategory: 'CAPABILITY_UNAVAILABLE', }); } diff --git a/packages/orchestration/src/objectives/integrator.ts b/packages/orchestration/src/objectives/integrator.ts index a7bf92d..d639bc4 100644 --- a/packages/orchestration/src/objectives/integrator.ts +++ b/packages/orchestration/src/objectives/integrator.ts @@ -6,7 +6,7 @@ import { completeInteractiveTask, } from '@specbridge/execution'; import { runSafeProcess } from '@specbridge/runners'; -import type { ClaudeProbe } from '@specbridge/runners'; +import type { RunnerRegistry } from '@specbridge/runners'; import type { Clock } from '@specbridge/workflow'; import type { FailureCategory } from '../vocabulary.js'; import { jobDir } from '../jobs/store.js'; @@ -46,6 +46,8 @@ export interface IntegrationCandidate { export interface IntegrateObjectiveInput { workspace: WorkspaceInfo; config: AgentConfig; + /** Optional for source compatibility; the worker can reconstruct it from config. */ + registry?: RunnerRegistry | undefined; jobId: string; specName: string; /** The approved objective's task id (the checkbox the pipeline may flip). */ @@ -58,7 +60,6 @@ export interface IntegrateObjectiveInput { clock?: Clock | undefined; idFactory?: (() => string) | undefined; signal?: AbortSignal | undefined; - cachedProbe?: ClaudeProbe | undefined; onProgress?: ((message: string) => void) | undefined; /** Bounded reconciliation dispatch timeout. */ reconcileTimeoutMs?: number | undefined; @@ -210,6 +211,7 @@ export async function integrateObjective(input: IntegrateObjectiveInput): Promis const reconcile = await runLargeObjectiveRole({ workspace: input.workspace, config: input.config, + registry: input.registry, runnerProfile: input.runnerProfile, role: 'BUILDER', packet, @@ -217,7 +219,6 @@ export async function integrateObjective(input: IntegrateObjectiveInput): Promis scratchDir: path.join(jobDir(input.workspace, input.jobId), 'scratch'), timeoutMs: input.reconcileTimeoutMs ?? 600_000, ...(input.signal !== undefined ? { signal: input.signal } : {}), - ...(input.cachedProbe !== undefined ? { cachedProbe: input.cachedProbe } : {}), }); if (!reconcile.ok || reconcile.output.outcome !== 'CANDIDATE_COMPLETE') { await abort(`reconciliation of ${entry.unit.workUnitId} failed`); diff --git a/packages/orchestration/src/objectives/objective-driver.ts b/packages/orchestration/src/objectives/objective-driver.ts index ce321dd..c27f3a2 100644 --- a/packages/orchestration/src/objectives/objective-driver.ts +++ b/packages/orchestration/src/objectives/objective-driver.ts @@ -15,7 +15,7 @@ import { readDecisions, readSpecCandidate, } from '@specbridge/mission'; -import type { ClaudeProbe, LocalModelManager } from '@specbridge/runners'; +import type { LocalModelManager, RunnerRegistry } from '@specbridge/runners'; import type { Clock } from '@specbridge/workflow'; import type { ResearchBridge } from '../research/index.js'; import { @@ -188,6 +188,10 @@ import { export interface ObjectiveDriveInput { workspace: WorkspaceInfo; config: AgentConfig; + /** Optional for source compatibility; production injects the shared registry. */ + registry?: RunnerRegistry | undefined; + /** @deprecated Retained as an ignored source-compatibility field. */ + probeCache?: unknown; jobId: string; specName: string; node: JobNode; @@ -197,7 +201,6 @@ export interface ObjectiveDriveInput { allowDirty: boolean; runnerProfile: string | undefined; localManager?: LocalModelManager | undefined; - probeCache: { probe: ClaudeProbe | undefined }; clock?: Clock | undefined; idFactory?: (() => string) | undefined; signal?: AbortSignal | undefined; @@ -437,6 +440,7 @@ async function decomposeObjective( const large = await runLargeObjectiveRole({ workspace: input.workspace, config: input.config, + registry: input.registry, runnerProfile: selection.worker.runnerProfile ?? input.config.defaultRunner, role: 'DECOMPOSER', packet, @@ -444,9 +448,7 @@ async function decomposeObjective( scratchDir: path.join(jobDir(input.workspace, input.jobId), 'scratch'), timeoutMs: 600_000, signal: input.signal, - cachedProbe: input.probeCache.probe, }); - if (large.probe !== undefined) input.probeCache.probe = large.probe; return large; })(); input.countWorkerRun({ @@ -1754,6 +1756,7 @@ async function executeBuilder( const reconcile = await runLargeObjectiveRole({ workspace: input.workspace, config: input.config, + registry: input.registry, runnerProfile: input.runnerProfile ?? input.config.defaultRunner, role: 'BUILDER', packet, @@ -1765,7 +1768,6 @@ async function executeBuilder( ), timeoutMs: input.policy.objectives.builderTimeoutMs, signal: input.signal, - cachedProbe: input.probeCache.probe, }); if (!reconcile.ok || reconcile.output.outcome !== 'CANDIDATE_COMPLETE') { // Say why the RECONCILIATION failed, not just why the apply did — the @@ -1783,7 +1785,6 @@ async function executeBuilder( }, }; } - if (reconcile.probe !== undefined) input.probeCache.probe = reconcile.probe; } if (prepared.priorCandidatePatch !== undefined && prepared.priorCandidatePatch.trim().length > 0) { @@ -1897,6 +1898,7 @@ async function executeBuilder( const result = await runLargeObjectiveRole({ workspace: input.workspace, config: input.config, + registry: input.registry, runnerProfile: input.runnerProfile ?? input.config.defaultRunner, role: 'BUILDER', packet, @@ -1908,9 +1910,7 @@ async function executeBuilder( ), timeoutMs: input.policy.objectives.builderTimeoutMs, signal: input.signal, - cachedProbe: input.probeCache.probe, }); - if (result.probe !== undefined) input.probeCache.probe = result.probe; if (!result.ok && isStrongQuotaFailure(result.problem)) { const resource = quotaFailureResource({ observedAt: nowIso(input), @@ -2818,10 +2818,11 @@ async function runSemanticEvaluation( input.onProgress?.(`EVALUATOR on ${selection.worker.workerId} for ${unitId}`); const runLarge = async ( packetOverride?: string, - ): Promise>> => { + ): Promise>>> => { const large = await runLargeObjectiveRole({ workspace: input.workspace, config: input.config, + registry: input.registry, runnerProfile: selection.worker.runnerProfile ?? input.config.defaultRunner, role: 'EVALUATOR', packet: packetOverride ?? packet, @@ -2829,9 +2830,7 @@ async function runSemanticEvaluation( scratchDir: path.join(jobDir(input.workspace, input.jobId), 'scratch'), timeoutMs: 600_000, signal: input.signal, - cachedProbe: input.probeCache.probe, }); - if (large.probe !== undefined) input.probeCache.probe = large.probe; return large; }; const ranLocally = @@ -3681,6 +3680,7 @@ async function maybeAggregateSemantically( const large = await runLargeObjectiveRole({ workspace: input.workspace, config: input.config, + registry: input.registry, runnerProfile: selection.worker.runnerProfile ?? input.config.defaultRunner, role: 'AGGREGATOR', packet, @@ -3688,9 +3688,7 @@ async function maybeAggregateSemantically( scratchDir: path.join(jobDir(input.workspace, input.jobId), 'scratch'), timeoutMs: 600_000, signal: input.signal, - cachedProbe: input.probeCache.probe, }); - if (large.probe !== undefined) input.probeCache.probe = large.probe; return large; })(); input.countWorkerRun({ @@ -3830,6 +3828,7 @@ async function integrateVerifiedCandidates( const result = await integrateObjective({ workspace: input.workspace, config: input.config, + registry: input.registry, jobId: input.jobId, // Reconciling a conflicting candidate is a BUILD-sized job, not a // question-sized one: the worker reads the conflict, understands two @@ -3848,7 +3847,6 @@ async function integrateVerifiedCandidates( clock: input.clock, idFactory: input.idFactory, signal: input.signal, - cachedProbe: input.probeCache.probe, onProgress: input.onProgress, }); if (!result.ok) { diff --git a/packages/orchestration/src/objectives/workers.ts b/packages/orchestration/src/objectives/workers.ts index fcfa45a..5a03492 100644 --- a/packages/orchestration/src/objectives/workers.ts +++ b/packages/orchestration/src/objectives/workers.ts @@ -1,18 +1,7 @@ -import path from 'node:path'; -import type { AgentConfig, ClaudeProfileConfig, WorkspaceInfo } from '@specbridge/core'; +import type { AgentConfig, WorkspaceInfo } from '@specbridge/core'; import { effectiveLocalInputCharacters } from '@specbridge/core'; -import { - buildClaudeInvocation, - cleanupTempFiles, - costFromEnvelope, - localStructuredInference, - claudeFailureProblem, - parseClaudeEnvelope, - probeClaude, - runSafeProcess, - usageFromEnvelope, -} from '@specbridge/runners'; -import type { ClaudeProbe, LocalModelManager } from '@specbridge/runners'; +import { createDefaultRunnerRegistry, localStructuredInference } from '@specbridge/runners'; +import type { LocalModelManager, RunnerRegistry } from '@specbridge/runners'; import { correctionMessage } from '../agents/contracts.js'; import type { ObjectiveContractRole, ObjectiveOutputFor } from './contracts.js'; import { OBJECTIVE_OUTPUT_JSON_SCHEMAS, validateObjectiveOutput } from './contracts.js'; @@ -154,6 +143,8 @@ export async function runLocalObjectiveRole( export interface LargeObjectiveInvocation { workspace: WorkspaceInfo; config: AgentConfig; + /** Optional for backwards compatibility; production injects the shared registry. */ + registry?: RunnerRegistry | undefined; runnerProfile: string; role: Role; packet: string; @@ -167,36 +158,35 @@ export interface LargeObjectiveInvocation { scratchDir: string; timeoutMs: number; signal?: AbortSignal | undefined; - cachedProbe?: ClaudeProbe | undefined; } /** - * Run one objective role on Claude Code. Reasoning roles get read-only + * Run one objective role on the explicitly selected runner. Reasoning roles get read-only * tools; the BUILDER gets the configured implementation tool policy — the * same policy task execution already uses, no wider. */ export async function runLargeObjectiveRole( invocation: LargeObjectiveInvocation, -): Promise & { probe?: ClaudeProbe }> { - const profile = invocation.config.runnerProfiles[invocation.runnerProfile]; - if (profile === undefined || profile.runner !== 'claude-code') { +): Promise> { + const registry = invocation.registry ?? createDefaultRunnerRegistry(invocation.config); + let profile; + try { + profile = registry.getProfile(invocation.runnerProfile); + } catch (cause) { return { ok: false, kind: 'worker-unavailable', - problem: `Runner profile "${invocation.runnerProfile}" is not a Claude Code profile.`, + problem: cause instanceof Error ? cause.message : `Runner profile "${invocation.runnerProfile}" is unavailable.`, }; } - const claudeProfile = profile as ClaudeProfileConfig; - const probe = - invocation.cachedProbe ?? - (await probeClaude(claudeProfile, { - ...(invocation.signal !== undefined ? { signal: invocation.signal } : {}), - })); - if (!probe.found || probe.status === 'unavailable' || probe.status === 'error') { + if (profile.config.enabled !== true || profile.runner.invokeStructured === undefined) { return { ok: false, kind: 'worker-unavailable', - problem: `The Claude Code CLI is not available (status ${probe.status}).`, + problem: + profile.config.enabled !== true + ? `Runner profile "${invocation.runnerProfile}" is disabled.` + : `Runner profile "${invocation.runnerProfile}" does not support structured orchestration roles.`, }; } @@ -208,78 +198,42 @@ export async function runLargeObjectiveRole( invocation.packet, ].join('\n'); - const plan = buildClaudeInvocation({ - config: claudeProfile, - probe, + const result = await profile.runner.invokeStructured({ prompt, toolPolicy: invocation.role === 'BUILDER' ? 'implementation' : 'inspect-only', + schemaName: invocation.role, outputJsonSchema: OBJECTIVE_OUTPUT_JSON_SCHEMAS[invocation.role], - execution: { - workspaceRoot: invocation.cwd, - runDir: invocation.scratchDir, - timeoutMs: invocation.timeoutMs, - }, + }, { + workspaceRoot: invocation.cwd, + runDir: invocation.scratchDir, + timeoutMs: invocation.timeoutMs, + ...(invocation.signal !== undefined ? { signal: invocation.signal } : {}), }); - - try { - const processResult = await runSafeProcess({ - executable: plan.executable, - argv: plan.argv, - cwd: invocation.cwd, - timeoutMs: invocation.timeoutMs, - stdin: plan.stdin, - ...(invocation.signal !== undefined ? { signal: invocation.signal } : {}), - }); - if (processResult.status === 'cancelled') { - return { ok: false, kind: 'cancelled', problem: 'The worker invocation was cancelled.', probe }; - } - if (processResult.status !== 'ok' && processResult.status !== 'nonzero-exit') { - return { - ok: false, - kind: 'worker-unavailable', - problem: processResult.failureReason ?? `the worker process ended with status ${processResult.status}`, - probe, - }; - } - const parsed = parseClaudeEnvelope(processResult.stdout); - if (parsed.problem !== undefined) { - // See runLargeRole: a non-zero exit without an envelope must keep the - // CLI's bounded stderr diagnostic, and remains a worker failure. - return { - ok: false, - kind: 'invalid-output', - problem: claudeFailureProblem(parsed.problem, processResult), - probe, - }; - } - const text = - parsed.structuredResult !== undefined - ? JSON.stringify(parsed.structuredResult) - : (parsed.reportText ?? ''); - const validated = validateObjectiveOutput(invocation.role, text); - if (!validated.ok) { - return { ok: false, kind: 'invalid-output', problem: validated.problem, probe }; - } - const usage = usageFromEnvelope(parsed.envelope, 0); - const cost = costFromEnvelope(parsed.envelope); + if (result.outcome === 'cancelled') { + return { ok: false, kind: 'cancelled', problem: result.failureReason ?? 'The worker invocation was cancelled.' }; + } + if (result.outcome !== 'completed' || result.text === undefined) { return { - ok: true, - output: validated.output, - raw: text, - usage: { - inputTokens: usage?.inputTokens ?? null, - outputTokens: usage?.outputTokens ?? null, - costUsd: cost !== null && cost !== undefined && cost.currency === 'USD' ? cost.amount : null, - }, - probe, + ok: false, + kind: result.outcome === 'malformed-output' ? 'invalid-output' : 'worker-unavailable', + problem: + result.failureReason ?? + result.error?.message ?? + `Runner profile "${invocation.runnerProfile}" ended with ${result.outcome}.`, }; - } finally { - cleanupTempFiles(plan); - try { - const { rmSync } = await import('node:fs'); - rmSync(path.join(invocation.scratchDir, 'tmp'), { recursive: true, force: true }); - } catch { - // Temp cleanup is best-effort. - } } + const validated = validateObjectiveOutput(invocation.role, result.text); + if (!validated.ok) { + return { ok: false, kind: 'invalid-output', problem: validated.problem }; + } + return { + ok: true, + output: validated.output, + raw: result.text, + usage: { + inputTokens: result.usage?.inputTokens ?? null, + outputTokens: result.usage?.outputTokens ?? null, + costUsd: result.cost?.currency === 'USD' ? result.cost.amount : null, + }, + }; } diff --git a/packages/runners/src/claude-code/runner.ts b/packages/runners/src/claude-code/runner.ts index ddcfebc..3ec330a 100644 --- a/packages/runners/src/claude-code/runner.ts +++ b/packages/runners/src/claude-code/runner.ts @@ -20,6 +20,8 @@ import type { RunnerDetectionResult, RunnerExecutionOptions, RunnerSelfTestResult, + StructuredInvocationInput, + StructuredInvocationResult, RunnerToolPolicy, StageGenerationInput, StageGenerationResult, @@ -38,6 +40,7 @@ import { CLAUDE_DECLARED_CAPABILITIES, claudeCapabilitySet, probeClaude } from ' import type { ClaudeEnvelope, ClaudeInvocationPlan } from './invocation.js'; import { buildClaudeInvocation, + claudeFailureProblem, cleanupTempFiles, parseClaudeEnvelope, runClaudeInvocation, @@ -232,6 +235,99 @@ export class ClaudeCodeRunner implements AgentRunner { return { ...rest, ...(stageReport !== undefined ? { report: stageReport } : {}) }; } + async invokeStructured( + input: StructuredInvocationInput, + execution: RunnerExecutionOptions, + ): Promise { + const started = Date.now(); + const probe = await this.probe(); + const unavailable = this.unavailableResult(probe, started); + if (unavailable !== undefined) { + const { report: _report, ...rest } = unavailable; + return rest; + } + const plan = buildClaudeInvocation({ + config: this.config, + probe, + prompt: input.prompt, + toolPolicy: input.toolPolicy, + outputJsonSchema: input.outputJsonSchema, + execution, + }); + try { + const processResult = await runClaudeInvocation(plan, this.config, execution); + const parsed = parseClaudeEnvelope(processResult.stdout); + const usage = usageFromEnvelope(parsed.envelope, processResult.observation.durationMs); + const cost = costFromEnvelope(parsed.envelope); + const base = { + runner: this.name, + rawStdout: processResult.stdout, + rawStderr: processResult.stderr, + process: processResult.observation, + durationMs: Math.max(0, Date.now() - started), + warnings: plan.skippedFlags.map( + (flag) => `flag ${flag} is unsupported by this Claude Code version and was skipped`, + ), + ...(parsed.envelope?.session_id !== undefined + ? { sessionId: parsed.envelope.session_id } + : {}), + ...(usage !== undefined ? { usage } : {}), + ...(cost !== undefined ? { cost } : {}), + }; + switch (processResult.status) { + case 'timeout': + return { ...base, outcome: 'timed-out', failureReason: processResult.failureReason ?? 'timeout' }; + case 'cancelled': + return { ...base, outcome: 'cancelled', failureReason: processResult.failureReason ?? 'cancelled' }; + case 'output-limit': + case 'spawn-failed': + return { + ...base, + outcome: 'failed', + failureReason: processResult.failureReason ?? processResult.status, + }; + case 'ok': + case 'nonzero-exit': + break; + } + if (this.looksPermissionDenied(processResult, parsed.envelope?.subtype, parsed.envelope)) { + return { + ...base, + outcome: 'permission-denied', + failureReason: 'Claude Code reported a permission denial.', + }; + } + if (processResult.status === 'nonzero-exit' || parsed.envelope?.is_error === true) { + return { + ...base, + outcome: 'malformed-output', + failureReason: + processResult.status === 'nonzero-exit' + ? claudeFailureProblem(parsed.problem ?? 'the runner produced no output', processResult) + : `Claude Code reported an error result${parsed.envelope?.subtype !== undefined ? ` (${parsed.envelope.subtype})` : ''}`, + ...(parsed.reportText !== undefined + ? { invalidStructuredOutput: parsed.reportText } + : {}), + }; + } + const text = + parsed.structuredResult !== undefined + ? JSON.stringify(parsed.structuredResult) + : parsed.reportText; + if (text === undefined || safeJsonParse(text) === undefined) { + return { + ...base, + outcome: 'malformed-output', + failureReason: parsed.problem ?? 'the runner returned no valid JSON document', + ...(text !== undefined ? { invalidStructuredOutput: text } : {}), + }; + } + return { ...base, outcome: 'completed', text: text.trim() }; + } finally { + cleanupTempFiles(plan); + } + } + async executeTask( input: TaskExecutionInput, execution: RunnerExecutionOptions, diff --git a/packages/runners/src/codex-cli/runner.ts b/packages/runners/src/codex-cli/runner.ts index 299bcdb..b709fd6 100644 --- a/packages/runners/src/codex-cli/runner.ts +++ b/packages/runners/src/codex-cli/runner.ts @@ -18,6 +18,8 @@ import type { RunnerExecutionOptions, RunnerModelListResult, RunnerSelfTestResult, + StructuredInvocationInput, + StructuredInvocationResult, RunnerToolPolicy, StageGenerationInput, StageGenerationResult, @@ -250,6 +252,122 @@ export class CodexCliRunner implements AgentRunner { return { ...rest, ...(stageReport !== undefined ? { report: stageReport } : {}) }; } + async invokeStructured( + input: StructuredInvocationInput, + execution: RunnerExecutionOptions, + ): Promise { + const started = Date.now(); + const probe = await this.probe(); + const unavailable = this.unavailableResult(probe, started); + if (unavailable !== undefined) { + const { report: _report, ...rest } = unavailable; + return rest; + } + const plan = buildCodexInvocation({ + config: this.config, + probe, + prompt: input.prompt, + toolPolicy: input.toolPolicy, + outputJsonSchema: input.outputJsonSchema, + execution, + }); + try { + const processResult = await runCodexInvocation(plan, this.config, execution); + const stream = parseCodexEventStream(processResult.stdout); + const warnings = plan.skippedFlags.map( + (flag) => `flag ${flag} is unsupported by this Codex CLI version and was skipped`, + ); + const normalizedEvents = normalizeCodexEvents( + stream, + { + runner: this.name, + profile: this.name, + runId: 'pending', + attemptId: 'pending', + }, + () => new Date().toISOString(), + ); + const usage = usageFromStream(stream, processResult.observation.durationMs, this.config.model); + const base = { + runner: this.name, + rawStdout: redactCodexStdoutForRetention(processResult.stdout), + rawStderr: processResult.stderr, + process: processResult.observation, + durationMs: Math.max(0, Date.now() - started), + warnings, + normalizedEvents, + ...(usage !== undefined ? { usage } : {}), + ...(stream.threadId !== undefined ? { sessionId: stream.threadId } : {}), + }; + switch (processResult.status) { + case 'timeout': + return { + ...base, + outcome: 'timed-out', + failureReason: processResult.failureReason ?? 'timeout', + error: runnerError({ code: 'timed_out', message: 'The Codex process timed out.' }), + }; + case 'cancelled': + return { + ...base, + outcome: 'cancelled', + failureReason: processResult.failureReason ?? 'cancelled', + error: runnerError({ code: 'cancelled', message: 'The Codex process was cancelled.' }), + }; + case 'output-limit': + return { + ...base, + outcome: 'failed', + failureReason: processResult.failureReason ?? 'output limit exceeded', + error: runnerError({ + code: 'output_limit_exceeded', + message: 'The Codex process exceeded its output limit.', + }), + }; + case 'spawn-failed': + return { + ...base, + outcome: 'failed', + failureReason: processResult.failureReason ?? 'spawn failed', + error: runnerError({ + code: 'executable_not_found', + message: 'The Codex CLI could not be started.', + }), + }; + case 'ok': + break; + case 'nonzero-exit': { + const error = classifyCodexFailure(processResult.stderr, stream.errors); + return { + ...base, + outcome: error.code === 'permission_denied' ? 'permission-denied' : 'failed', + failureReason: error.message, + error, + }; + } + } + const finalText = readLastMessage(plan) ?? stream.lastAgentMessage; + if (finalText === undefined || strictJsonParse(finalText) === undefined) { + return { + ...base, + outcome: 'malformed-output', + failureReason: + finalText === undefined + ? 'the runner returned no final structured result' + : 'the final Codex message is not a bare JSON document', + error: runnerError({ + code: 'structured_output_invalid', + message: 'The Codex orchestration response was not a valid JSON document.', + }), + ...(finalText !== undefined ? { invalidStructuredOutput: finalText } : {}), + }; + } + return { ...base, outcome: 'completed', text: finalText.trim() }; + } finally { + cleanupCodexTempFiles(plan); + } + } + async executeTask( input: TaskExecutionInput, execution: RunnerExecutionOptions, diff --git a/packages/runners/src/contract.ts b/packages/runners/src/contract.ts index ab34c6d..855dd29 100644 --- a/packages/runners/src/contract.ts +++ b/packages/runners/src/contract.ts @@ -179,7 +179,7 @@ export interface ProcessObservation { stderrTruncated: boolean; } -interface RunnerResultBase { +export interface RunnerResultBase { runner: string; outcome: ExecutionOutcome; /** Present when the outcome is not `completed`/`no-change`. */ @@ -207,6 +207,26 @@ interface RunnerResultBase { invalidStructuredOutput?: string; } +/** + * One provider-neutral, schema-constrained orchestration invocation. + * + * Unlike stage generation and task execution this operation does not impose + * a SpecBridge report schema of its own. The caller supplies the exact JSON + * Schema for a bounded orchestration role and remains responsible for + * validating the returned document against its domain contract. + */ +export interface StructuredInvocationInput { + prompt: string; + schemaName: string; + outputJsonSchema: Record; + toolPolicy: RunnerToolPolicy; +} + +export interface StructuredInvocationResult extends RunnerResultBase { + /** Complete final response. Present only for a strict JSON document. */ + text?: string; +} + export interface StageGenerationResult extends RunnerResultBase { /** Validated structured output. Present only when parsing succeeded. */ report?: StageRunnerReport; @@ -282,6 +302,15 @@ export interface AgentRunner { detect(context: RunnerDetectionContext): Promise; + /** + * Additive orchestration capability. Provider-specific CLI details stay + * inside the adapter; orchestration never branches on runner names. + */ + invokeStructured?( + input: StructuredInvocationInput, + execution: RunnerExecutionOptions, + ): Promise; + generateStage( input: StageGenerationInput, execution: RunnerExecutionOptions, diff --git a/tests/fixtures/fake-codex/fake-codex.mjs b/tests/fixtures/fake-codex/fake-codex.mjs index f298422..090b3fb 100644 --- a/tests/fixtures/fake-codex/fake-codex.mjs +++ b/tests/fixtures/fake-codex/fake-codex.mjs @@ -198,6 +198,71 @@ emit({ }); const stageMatch = /Stage to produce: (\w+)/.exec(stdin); +const orchestrationRoleMatch = /SpecBridge orchestration role: (\w+)/.exec(stdin); + +if (orchestrationRoleMatch !== null) { + const role = orchestrationRoleMatch[1]; + const responses = { + CLASSIFIER: { complexity: 'HIGH', reasons: ['architecture-sensitive work'] }, + PLANNER: { + decision: 'PLAN', + goal: 'Implement the approved task with architectural care.', + steps: [ + { id: '1', action: 'Study the existing architecture and constraints.' }, + { id: '2', action: 'Implement the change behind the existing interfaces.' }, + { id: '3', action: 'Add tests covering the acceptance criteria.' }, + ], + testStrategy: 'Unit plus integration tests.', + verificationStrategy: 'Run the configured trusted verification commands.', + assumptions: [], + risks: [], + requiresEscalation: false, + }, + CRITIC: { verdict: 'ACCEPT', reasons: ['plan is sound'] }, + DIAGNOSER: { + category: 'IMPLEMENTATION_DEFECT', + rootCause: 'The failure originates in the save path.', + planValidity: 'VALID', + recommendedAction: 'REPAIR', + evidence: ['failing verifier output'], + }, + REPLANNER: { + decision: 'REVISED_PLAN', + reason: 'The prior strategy conflicted with the observed architecture.', + goal: 'Implement via the existing extension point instead.', + steps: [{ id: '1', action: 'Use the existing extension point.' }], + assumptions: [], + impactsApprovedIntent: false, + }, + DECOMPOSER: { + decision: 'SINGLE_UNIT', + reason: 'The objective is cohesive enough to implement as one unit.', + units: [], + }, + EVALUATOR: { + verdict: 'PASS', + reasons: ['the candidate satisfies the projected contracts'], + evidenceRefs: [], + affectedContractIds: [], + }, + AGGREGATOR: { + synthesis: 'The reports agree; no conflicts were detected.', + findings: [], + contractChangeSuggestions: [], + conflictsDetected: [], + }, + BUILDER: { + outcome: 'CANDIDATE_COMPLETE', + summary: 'Implemented the bounded work unit.', + changedFiles: [], + assumptionsDiscovered: [], + contractChangeRequests: [], + knownLimitations: [], + blockingQuestions: [], + }, + }; + finish(JSON.stringify(responses[role] ?? responses.PLANNER)); +} function stageMarkdownFor(stage) { if (scenario === 'stage-invalid') { diff --git a/tests/orchestration/codex-large-role.test.ts b/tests/orchestration/codex-large-role.test.ts new file mode 100644 index 0000000..8ac065f --- /dev/null +++ b/tests/orchestration/codex-large-role.test.ts @@ -0,0 +1,32 @@ +import { mkdtempSync } from 'node:fs'; +import os from 'node:os'; +import path from 'node:path'; +import { afterEach, describe, expect, it } from 'vitest'; +import { runLargeRole } from '@specbridge/orchestration'; +import { setupExecutionFixtureV2 } from '../helpers-execution.js'; + +afterEach(() => { + delete process.env['FAKE_CODEX_SCENARIO']; +}); + +describe('runLargeRole against the fake Codex CLI', () => { + it('routes a PLANNER through the selected Codex profile and validates its schema', async () => { + process.env['FAKE_CODEX_SCENARIO'] = 'success'; + const fixture = setupExecutionFixtureV2({ useFakeCodex: true }); + const result = await runLargeRole({ + workspace: fixture.workspace, + config: fixture.config, + registry: fixture.registry, + runnerProfile: 'codex-default', + role: 'PLANNER', + packet: 'Task 1: implement the workflow definition schema.', + scratchDir: mkdtempSync(path.join(os.tmpdir(), 'specbridge-codex-large-role-')), + timeoutMs: 60_000, + }); + + expect(result.ok).toBe(true); + if (!result.ok) return; + expect(result.output.decision).toBe('PLAN'); + expect(result.output.steps).toHaveLength(3); + }); +}); diff --git a/tests/orchestration/driver.test.ts b/tests/orchestration/driver.test.ts index 6af71e6..7c47492 100644 --- a/tests/orchestration/driver.test.ts +++ b/tests/orchestration/driver.test.ts @@ -1,6 +1,5 @@ import { describe, expect, it } from 'vitest'; import { - CLAUDE_WORKER_ID, LOCAL_WORKER_ID, driveJob, createJob, @@ -115,7 +114,7 @@ describe('driveJob — StepRelay readiness scenarios', () => { expect( attempts .filter((attempt) => attempt.role === 'EXECUTOR') - .every((attempt) => attempt.workerId === CLAUDE_WORKER_ID), + .every((attempt) => attempt.workerId === fixture.config.defaultRunner), ).toBe(true); // The audit trail answers "why" questions from persisted state. const trail = readJobEvents(fixture.workspace, jobId, { limit: 500 }); diff --git a/tests/orchestration/jobs-routing.test.ts b/tests/orchestration/jobs-routing.test.ts index 16b162c..365c722 100644 --- a/tests/orchestration/jobs-routing.test.ts +++ b/tests/orchestration/jobs-routing.test.ts @@ -9,6 +9,7 @@ import { import type { JobWorkerProfile , OrchestrationError} from '@specbridge/orchestration'; import { setupOrchestrationFixture } from '../helpers-orchestration.js'; +import { setupExecutionFixtureV2 } from '../helpers-execution.js'; /** * Role routing: local-first, escalate-on-evidence, and the executor is @@ -59,6 +60,17 @@ describe('resolveWorkers', () => { expect(workers[0]?.repositoryWrite).toBe(true); }); + it('uses the configured Codex default profile as the large worker identity', () => { + const fixture = setupExecutionFixtureV2({ + useFakeCodex: true, + defaultRunner: 'codex-default', + }); + const workers = resolveWorkers(fixture.config); + expect(workers).toHaveLength(1); + expect(workers[0]?.workerId).toBe('codex-default'); + expect(workers[0]?.runnerProfile).toBe('codex-default'); + }); + it('with local inference enabled and coherent, the local worker joins the roster', () => { const fixture = setupOrchestrationFixture({ extraConfig: { diff --git a/tests/orchestration/secondary-objective-builder.test.ts b/tests/orchestration/secondary-objective-builder.test.ts index 5bfe9a8..316b7dd 100644 --- a/tests/orchestration/secondary-objective-builder.test.ts +++ b/tests/orchestration/secondary-objective-builder.test.ts @@ -1394,6 +1394,7 @@ describe('Secondary Objective Builder governed lifecycle', () => { const resumed = await driveObjective({ workspace: fixture.workspace, config: fixture.driverDeps.config, + registry: fixture.driverDeps.registry, jobId: job.jobId, specName: 'steprelay-secondary', node: jobGraph.nodes[0]!, @@ -1532,6 +1533,7 @@ describe('Secondary Objective Builder governed lifecycle', () => { const resumed = await driveObjective({ workspace: fixture.workspace, config: fixture.driverDeps.config, + registry: fixture.driverDeps.registry, jobId: job.jobId, specName: 'steprelay-secondary', node: jobGraph.nodes[0]!,