@theokit/sdk 4.26.0 → 4.27.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/{judge-call-QG4WZ7F3.cjs → chunk-4GTXATWK.cjs} +2 -3
- package/dist/chunk-4GTXATWK.cjs.map +1 -0
- package/dist/{chunk-PSJN532Q.js → chunk-6M76Z3AI.js} +5 -5
- package/dist/{chunk-PSJN532Q.js.map → chunk-6M76Z3AI.js.map} +1 -1
- package/dist/{judge-call-CEQ3FYYD.js → chunk-CR3DGWUQ.js} +2 -3
- package/dist/chunk-CR3DGWUQ.js.map +1 -0
- package/dist/{chunk-KDNBTPY2.js → chunk-OX772BEW.js} +3 -3
- package/dist/{chunk-KDNBTPY2.js.map → chunk-OX772BEW.js.map} +1 -1
- package/dist/{chunk-ZUBIEQJF.cjs → chunk-TMNS2LOS.cjs} +7 -7
- package/dist/{chunk-ZUBIEQJF.cjs.map → chunk-TMNS2LOS.cjs.map} +1 -1
- package/dist/{chunk-R43CNCES.cjs → chunk-WBBYSW3Q.cjs} +5 -5
- package/dist/{chunk-R43CNCES.cjs.map → chunk-WBBYSW3Q.cjs.map} +1 -1
- package/dist/{cron-DivRaEmN.d.ts → cron-DPUZzncH.d.ts} +33 -1
- package/dist/{cron-D0JKR-yX.d.cts → cron-DzZ9TMa2.d.cts} +33 -1
- package/dist/cron.cjs +3 -3
- package/dist/cron.d.cts +1 -1
- package/dist/cron.d.ts +1 -1
- package/dist/cron.js +2 -2
- package/dist/errors.d.ts +424 -3
- package/dist/eval.cjs +2 -2
- package/dist/eval.js +1 -1
- package/dist/index.cjs +27 -22
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +16 -17
- package/dist/index.d.ts +16 -17
- package/dist/index.js +6 -5
- package/dist/index.js.map +1 -1
- package/dist/internal/runtime/lifecycle/run-until.d.ts +1 -2
- package/dist/internal/runtime/lifecycle/wrap-completion-check-run.d.ts +1 -1
- package/dist/judge-call-2FULQ6KL.cjs +22 -0
- package/dist/judge-call-2FULQ6KL.cjs.map +1 -0
- package/dist/judge-call-D7R3ZTZS.js +5 -0
- package/dist/judge-call-D7R3ZTZS.js.map +1 -0
- package/dist/{run-until-XLHEHRQM.js → run-until-K7IO7ZPQ.js} +2 -2
- package/dist/run-until-K7IO7ZPQ.js.map +1 -0
- package/dist/{run-until-PNO3QQLI.cjs → run-until-ZD4PRLNJ.cjs} +2 -2
- package/dist/run-until-ZD4PRLNJ.cjs.map +1 -0
- package/dist/types/goal-events.d.ts +32 -0
- package/package.json +1 -1
- package/dist/internal/judge/types.d.ts +0 -17
- package/dist/judge-call-CEQ3FYYD.js.map +0 -1
- package/dist/judge-call-QG4WZ7F3.cjs.map +0 -1
- package/dist/run-until-PNO3QQLI.cjs.map +0 -1
- package/dist/run-until-XLHEHRQM.js.map +0 -1
|
@@ -12,9 +12,8 @@
|
|
|
12
12
|
*
|
|
13
13
|
* @internal
|
|
14
14
|
*/
|
|
15
|
-
import type { GoalEvent, GoalOptions, GoalResult } from "../../../types/goal-events.js";
|
|
15
|
+
import type { GoalEvent, GoalOptions, GoalResult, JudgeResult } from "../../../types/goal-events.js";
|
|
16
16
|
import type { JudgeContext, JudgeOptions } from "../../judge/judge-call.js";
|
|
17
|
-
import type { JudgeResult } from "../../judge/types.js";
|
|
18
17
|
/** DI contract: the judge is the only external touchpoint. */
|
|
19
18
|
export interface RunUntilDeps {
|
|
20
19
|
judge: (ctx: JudgeContext, opts?: JudgeOptions) => Promise<JudgeResult>;
|
|
@@ -16,9 +16,9 @@
|
|
|
16
16
|
*
|
|
17
17
|
* @internal
|
|
18
18
|
*/
|
|
19
|
+
import type { CompletionCheck, Run } from "../../../types/run.js";
|
|
19
20
|
import { type RunEventSink } from "../../../types/run-events.js";
|
|
20
21
|
import type { JudgeContext, JudgeOptions } from "../../judge/judge-call.js";
|
|
21
|
-
import type { JudgeResult } from "../../judge/types.js";
|
|
22
22
|
export interface CompletionCheckDeps {
|
|
23
23
|
judge: (ctx: JudgeContext, opts?: JudgeOptions) => Promise<JudgeResult>;
|
|
24
24
|
}
|
|
@@ -0,0 +1,22 @@
|
|
|
1
|
+
'use strict';
|
|
2
|
+
|
|
3
|
+
var chunk4GTXATWK_cjs = require('./chunk-4GTXATWK.cjs');
|
|
4
|
+
require('./chunk-A44COQJJ.cjs');
|
|
5
|
+
require('./chunk-BEURCLDV.cjs');
|
|
6
|
+
|
|
7
|
+
|
|
8
|
+
|
|
9
|
+
Object.defineProperty(exports, "JudgeCredentialError", {
|
|
10
|
+
enumerable: true,
|
|
11
|
+
get: function () { return chunk4GTXATWK_cjs.JudgeCredentialError; }
|
|
12
|
+
});
|
|
13
|
+
Object.defineProperty(exports, "composeJudgePrompt", {
|
|
14
|
+
enumerable: true,
|
|
15
|
+
get: function () { return chunk4GTXATWK_cjs.composeJudgePrompt; }
|
|
16
|
+
});
|
|
17
|
+
Object.defineProperty(exports, "judgeCallImpl", {
|
|
18
|
+
enumerable: true,
|
|
19
|
+
get: function () { return chunk4GTXATWK_cjs.judgeCallImpl; }
|
|
20
|
+
});
|
|
21
|
+
//# sourceMappingURL=judge-call-2FULQ6KL.cjs.map
|
|
22
|
+
//# sourceMappingURL=judge-call-2FULQ6KL.cjs.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":[],"names":[],"mappings":"","file":"judge-call-2FULQ6KL.cjs"}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":[],"names":[],"mappings":"","file":"judge-call-D7R3ZTZS.js"}
|
|
@@ -173,5 +173,5 @@ ${lastResponse.slice(-1e3)}`
|
|
|
173
173
|
}
|
|
174
174
|
|
|
175
175
|
export { composeContinuation, runUntilImpl };
|
|
176
|
-
//# sourceMappingURL=run-until-
|
|
177
|
-
//# sourceMappingURL=run-until-
|
|
176
|
+
//# sourceMappingURL=run-until-K7IO7ZPQ.js.map
|
|
177
|
+
//# sourceMappingURL=run-until-K7IO7ZPQ.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":["../src/internal/runtime/lifecycle/run-until.ts"],"names":[],"mappings":"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","file":"run-until-K7IO7ZPQ.js","sourcesContent":["/**\n * Goal-driven Ralph loop (T3.2, ADRs D115-D121).\n *\n * `runUntilImpl` is an `AsyncGenerator<GoalEvent, GoalResult, void>`:\n * yields events as the loop progresses, returns the final result when\n * the goal completes, fails, or is paused. The auxiliary judge model is\n * injected via `deps.judge` so the generator stays free of the `Agent`\n * façade import.\n *\n * EC-C fix: pre-aborted signals emit only `[paused]`, not `[active, paused]`.\n * EC-D: `maxTurns: 0` is supported (vacuous yield active → failed).\n *\n * @internal\n */\n\nimport type { SDKAgent } from \"../../../types/agent.js\";\nimport type {\n GoalEvent,\n GoalOptions,\n GoalResult,\n JudgeResult,\n} from \"../../../types/goal-events.js\";\nimport type { JudgeContext, JudgeOptions } from \"../../judge/judge-call.js\";\nimport { GOAL_CONTINUATION_MARKER } from \"./goal-marker.js\";\n\n/** DI contract: the judge is the only external touchpoint. */\nexport interface RunUntilDeps {\n judge: (ctx: JudgeContext, opts?: JudgeOptions) => Promise<JudgeResult>;\n}\n\n// biome-ignore lint/complexity/noExcessiveCognitiveComplexity: the goal-loop interleaves turn-start, send, judge, continuation, abort-check, and failure-bail — extracting helpers harms the linear narrative.\nexport async function* runUntilImpl(\n agent: SDKAgent,\n goal: string,\n options: GoalOptions | undefined,\n deps: RunUntilDeps,\n): AsyncGenerator<GoalEvent, GoalResult, void> {\n const maxTurns = options?.maxTurns ?? 20;\n const maxFails = options?.maxConsecutiveJudgeFailures ?? 3;\n const tokenBudget = options?.tokenBudget; // M55 — undefined ⇒ ilimitado\n const signal = options?.signal;\n // Use a function (not direct property access) so TS does not narrow\n // `signal.aborted` to `false | undefined` after the initial check.\n // AbortSignal.aborted is a getter that can flip true between calls.\n // biome-ignore lint/complexity/useOptionalChain: optional-chain would re-narrow signal?.aborted to `false | undefined` after the first check, defeating the purpose of this helper.\n const isAborted = (): boolean => signal !== undefined && signal.aborted;\n let turn = 0;\n let consecutiveFailures = 0;\n let tokensUsed = 0; // M55 — soma observada (0 se usage ausente — fail-open)\n let lastResponse = \"\";\n\n // EC-C: signal already aborted BEFORE first event → emit only [paused].\n if (isAborted()) {\n yield {\n type: \"status_change\",\n status: \"paused\",\n reason: \"aborted via AbortSignal before first turn\",\n };\n return { status: \"paused\", turnsUsed: 0, tokensUsed, finalResponse: undefined };\n }\n\n yield { type: \"status_change\", status: \"active\", reason: \"Goal started\" };\n\n while (turn < maxTurns) {\n if (isAborted()) {\n yield { type: \"status_change\", status: \"paused\", reason: \"aborted via AbortSignal\" };\n return {\n status: \"paused\",\n turnsUsed: turn,\n tokensUsed,\n finalResponse: lastResponse || undefined,\n };\n }\n\n turn += 1;\n yield { type: \"turn_start\", turn, goal };\n\n const continuationPrompt = turn === 1 ? goal : composeContinuation(goal, lastResponse);\n\n const run = await agent.send(continuationPrompt);\n // M55 review HIGH — thread the abort INTO the in-flight run: without this, Esc mid-turn is\n // cosmetic (the turn keeps mutating the workspace until it finishes on its own). `run.cancel()`\n // aborts the stream + in-flight tool calls (types/run.ts contract).\n const cancelOnAbort = (): void => {\n void (run as { cancel?: () => Promise<void> }).cancel?.();\n };\n if (signal !== undefined) {\n if (signal.aborted) cancelOnAbort();\n else signal.addEventListener(\"abort\", cancelOnAbort, { once: true });\n }\n const result = await run.wait();\n if (signal !== undefined) signal.removeEventListener(\"abort\", cancelOnAbort);\n lastResponse = result.result ?? \"\";\n // M55 — token accounting fail-open: só soma quando o run reporta usage.\n const turnTokens = (result as { usage?: { totalTokens?: number } }).usage?.totalTokens;\n if (typeof turnTokens === \"number\") tokensUsed += turnTokens;\n // Re-check aborted right after the turn lands: a cancelled turn must NOT spend a judge call.\n if (isAborted()) {\n yield { type: \"status_change\", status: \"paused\", reason: \"aborted via AbortSignal\" };\n return {\n status: \"paused\",\n turnsUsed: turn,\n tokensUsed,\n finalResponse: lastResponse || undefined,\n };\n }\n yield { type: \"agent_response\", turn, content: lastResponse };\n\n const judgeOpts: JudgeOptions = {};\n if (options?.judgeModel !== undefined) judgeOpts.judgeModel = options.judgeModel;\n if (options?.judgeApiKey !== undefined) judgeOpts.apiKey = options.judgeApiKey;\n const judgeCtx: JudgeContext = { goal, lastResponse };\n if (options?.subgoals !== undefined) judgeCtx.subgoals = options.subgoals;\n const judgment = await deps.judge(judgeCtx, judgeOpts);\n yield {\n type: \"judge_verdict\",\n turn,\n verdict: judgment.verdict,\n reason: judgment.reason,\n parseFailed: judgment.parseFailed,\n };\n\n if (judgment.parseFailed) {\n consecutiveFailures += 1;\n if (consecutiveFailures >= maxFails) {\n yield {\n type: \"status_change\",\n status: \"failed\",\n reason: `judge model too unreliable (${consecutiveFailures} parse failures in a row)`,\n };\n return {\n status: \"failed\",\n turnsUsed: turn,\n tokensUsed,\n finalResponse: lastResponse || undefined,\n };\n }\n } else {\n consecutiveFailures = 0;\n }\n\n if (judgment.verdict === \"done\") {\n yield { type: \"status_change\", status: \"completed\", reason: judgment.reason };\n return {\n status: \"completed\",\n turnsUsed: turn,\n tokensUsed,\n finalResponse: lastResponse || undefined,\n };\n }\n // M80 — o braço que faltava. Sem ele, um judge que reconhecesse impossibilidade só podia dizer\n // \"continue\", e o loop repetia o mesmo turno até estourar o orçamento — reportando `failed` por\n // limite em vez de `blocked` por impossibilidade. Duas causas distintas com o mesmo desfecho\n // visível, e o consumidor precisava reconciliar depois do loop para distingui-las.\n if (judgment.verdict === \"blocked\") {\n yield { type: \"status_change\", status: \"blocked\", reason: judgment.reason };\n return {\n status: \"blocked\",\n turnsUsed: turn,\n tokensUsed,\n finalResponse: lastResponse || undefined,\n };\n }\n if (judgment.verdict === \"skipped\") {\n yield {\n type: \"status_change\",\n status: \"completed\",\n reason: `skipped: ${judgment.reason}`,\n };\n return {\n status: \"completed\",\n turnsUsed: turn,\n tokensUsed,\n finalResponse: lastResponse || undefined,\n };\n }\n\n // M55 — checagem de token budget APÓS o turno (Codex: wind-down suave, para o loop).\n if (tokenBudget !== undefined && tokensUsed >= tokenBudget) {\n yield {\n type: \"status_change\",\n status: \"budget_limited\",\n reason: `token budget (${tokenBudget}) reached: ${tokensUsed} used`,\n };\n return {\n status: \"budget_limited\",\n turnsUsed: turn,\n tokensUsed,\n finalResponse: lastResponse || undefined,\n };\n }\n\n yield { type: \"continuation\", turn, prompt: continuationPrompt };\n }\n\n yield {\n type: \"status_change\",\n status: \"failed\",\n reason: `max turns (${maxTurns}) exhausted`,\n };\n return {\n status: \"failed\",\n turnsUsed: turn,\n tokensUsed,\n finalResponse: lastResponse || undefined,\n };\n}\n\n/**\n * Build the continuation user message for turns 2+. Keeps last response\n * preview short so we don't bloat the prompt cache.\n *\n * @internal\n */\n\nexport function composeContinuation(goal: string, lastResponse: string): string {\n // M55 — Codex-faithful continuation (ext/goal templates/goals/continuation.md): keep the FULL\n // objective intact, work from current-state evidence, and audit completion requirement-by-requirement\n // before declaring done. Improves the quality of what the judge then evaluates.\n return [\n GOAL_CONTINUATION_MARKER,\n \"Continue working toward the active goal.\",\n \"\",\n `<objective>\\n${goal}\\n</objective>`,\n \"\",\n \"Continuation behavior:\",\n \"- This goal persists across turns. Keep the full objective intact; if it cannot be finished now,\",\n \" make concrete progress toward the real requested end state and do not redefine success around a\",\n \" smaller or easier task.\",\n \"\",\n \"Work from evidence:\",\n \"- Use the current worktree and external state as authoritative. Inspect the current state before\",\n \" relying on it. Improve, replace, or remove existing work as needed to satisfy the actual objective.\",\n \"\",\n \"Completion audit:\",\n \"- Before deciding the goal is achieved, treat completion as unproven and verify it against the actual\",\n \" current state, requirement by requirement. Treat uncertain or indirect evidence as not achieved.\",\n \"\",\n `Your last response was:\\n${lastResponse.slice(-1000)}`,\n ].join(\"\\n\");\n}\n"]}
|
|
@@ -176,5 +176,5 @@ ${lastResponse.slice(-1e3)}`
|
|
|
176
176
|
|
|
177
177
|
exports.composeContinuation = composeContinuation;
|
|
178
178
|
exports.runUntilImpl = runUntilImpl;
|
|
179
|
-
//# sourceMappingURL=run-until-
|
|
180
|
-
//# sourceMappingURL=run-until-
|
|
179
|
+
//# sourceMappingURL=run-until-ZD4PRLNJ.cjs.map
|
|
180
|
+
//# sourceMappingURL=run-until-ZD4PRLNJ.cjs.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":["../src/internal/runtime/lifecycle/run-until.ts"],"names":["GOAL_CONTINUATION_MARKER"],"mappings":"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","file":"run-until-ZD4PRLNJ.cjs","sourcesContent":["/**\n * Goal-driven Ralph loop (T3.2, ADRs D115-D121).\n *\n * `runUntilImpl` is an `AsyncGenerator<GoalEvent, GoalResult, void>`:\n * yields events as the loop progresses, returns the final result when\n * the goal completes, fails, or is paused. The auxiliary judge model is\n * injected via `deps.judge` so the generator stays free of the `Agent`\n * façade import.\n *\n * EC-C fix: pre-aborted signals emit only `[paused]`, not `[active, paused]`.\n * EC-D: `maxTurns: 0` is supported (vacuous yield active → failed).\n *\n * @internal\n */\n\nimport type { SDKAgent } from \"../../../types/agent.js\";\nimport type {\n GoalEvent,\n GoalOptions,\n GoalResult,\n JudgeResult,\n} from \"../../../types/goal-events.js\";\nimport type { JudgeContext, JudgeOptions } from \"../../judge/judge-call.js\";\nimport { GOAL_CONTINUATION_MARKER } from \"./goal-marker.js\";\n\n/** DI contract: the judge is the only external touchpoint. */\nexport interface RunUntilDeps {\n judge: (ctx: JudgeContext, opts?: JudgeOptions) => Promise<JudgeResult>;\n}\n\n// biome-ignore lint/complexity/noExcessiveCognitiveComplexity: the goal-loop interleaves turn-start, send, judge, continuation, abort-check, and failure-bail — extracting helpers harms the linear narrative.\nexport async function* runUntilImpl(\n agent: SDKAgent,\n goal: string,\n options: GoalOptions | undefined,\n deps: RunUntilDeps,\n): AsyncGenerator<GoalEvent, GoalResult, void> {\n const maxTurns = options?.maxTurns ?? 20;\n const maxFails = options?.maxConsecutiveJudgeFailures ?? 3;\n const tokenBudget = options?.tokenBudget; // M55 — undefined ⇒ ilimitado\n const signal = options?.signal;\n // Use a function (not direct property access) so TS does not narrow\n // `signal.aborted` to `false | undefined` after the initial check.\n // AbortSignal.aborted is a getter that can flip true between calls.\n // biome-ignore lint/complexity/useOptionalChain: optional-chain would re-narrow signal?.aborted to `false | undefined` after the first check, defeating the purpose of this helper.\n const isAborted = (): boolean => signal !== undefined && signal.aborted;\n let turn = 0;\n let consecutiveFailures = 0;\n let tokensUsed = 0; // M55 — soma observada (0 se usage ausente — fail-open)\n let lastResponse = \"\";\n\n // EC-C: signal already aborted BEFORE first event → emit only [paused].\n if (isAborted()) {\n yield {\n type: \"status_change\",\n status: \"paused\",\n reason: \"aborted via AbortSignal before first turn\",\n };\n return { status: \"paused\", turnsUsed: 0, tokensUsed, finalResponse: undefined };\n }\n\n yield { type: \"status_change\", status: \"active\", reason: \"Goal started\" };\n\n while (turn < maxTurns) {\n if (isAborted()) {\n yield { type: \"status_change\", status: \"paused\", reason: \"aborted via AbortSignal\" };\n return {\n status: \"paused\",\n turnsUsed: turn,\n tokensUsed,\n finalResponse: lastResponse || undefined,\n };\n }\n\n turn += 1;\n yield { type: \"turn_start\", turn, goal };\n\n const continuationPrompt = turn === 1 ? goal : composeContinuation(goal, lastResponse);\n\n const run = await agent.send(continuationPrompt);\n // M55 review HIGH — thread the abort INTO the in-flight run: without this, Esc mid-turn is\n // cosmetic (the turn keeps mutating the workspace until it finishes on its own). `run.cancel()`\n // aborts the stream + in-flight tool calls (types/run.ts contract).\n const cancelOnAbort = (): void => {\n void (run as { cancel?: () => Promise<void> }).cancel?.();\n };\n if (signal !== undefined) {\n if (signal.aborted) cancelOnAbort();\n else signal.addEventListener(\"abort\", cancelOnAbort, { once: true });\n }\n const result = await run.wait();\n if (signal !== undefined) signal.removeEventListener(\"abort\", cancelOnAbort);\n lastResponse = result.result ?? \"\";\n // M55 — token accounting fail-open: só soma quando o run reporta usage.\n const turnTokens = (result as { usage?: { totalTokens?: number } }).usage?.totalTokens;\n if (typeof turnTokens === \"number\") tokensUsed += turnTokens;\n // Re-check aborted right after the turn lands: a cancelled turn must NOT spend a judge call.\n if (isAborted()) {\n yield { type: \"status_change\", status: \"paused\", reason: \"aborted via AbortSignal\" };\n return {\n status: \"paused\",\n turnsUsed: turn,\n tokensUsed,\n finalResponse: lastResponse || undefined,\n };\n }\n yield { type: \"agent_response\", turn, content: lastResponse };\n\n const judgeOpts: JudgeOptions = {};\n if (options?.judgeModel !== undefined) judgeOpts.judgeModel = options.judgeModel;\n if (options?.judgeApiKey !== undefined) judgeOpts.apiKey = options.judgeApiKey;\n const judgeCtx: JudgeContext = { goal, lastResponse };\n if (options?.subgoals !== undefined) judgeCtx.subgoals = options.subgoals;\n const judgment = await deps.judge(judgeCtx, judgeOpts);\n yield {\n type: \"judge_verdict\",\n turn,\n verdict: judgment.verdict,\n reason: judgment.reason,\n parseFailed: judgment.parseFailed,\n };\n\n if (judgment.parseFailed) {\n consecutiveFailures += 1;\n if (consecutiveFailures >= maxFails) {\n yield {\n type: \"status_change\",\n status: \"failed\",\n reason: `judge model too unreliable (${consecutiveFailures} parse failures in a row)`,\n };\n return {\n status: \"failed\",\n turnsUsed: turn,\n tokensUsed,\n finalResponse: lastResponse || undefined,\n };\n }\n } else {\n consecutiveFailures = 0;\n }\n\n if (judgment.verdict === \"done\") {\n yield { type: \"status_change\", status: \"completed\", reason: judgment.reason };\n return {\n status: \"completed\",\n turnsUsed: turn,\n tokensUsed,\n finalResponse: lastResponse || undefined,\n };\n }\n // M80 — o braço que faltava. Sem ele, um judge que reconhecesse impossibilidade só podia dizer\n // \"continue\", e o loop repetia o mesmo turno até estourar o orçamento — reportando `failed` por\n // limite em vez de `blocked` por impossibilidade. Duas causas distintas com o mesmo desfecho\n // visível, e o consumidor precisava reconciliar depois do loop para distingui-las.\n if (judgment.verdict === \"blocked\") {\n yield { type: \"status_change\", status: \"blocked\", reason: judgment.reason };\n return {\n status: \"blocked\",\n turnsUsed: turn,\n tokensUsed,\n finalResponse: lastResponse || undefined,\n };\n }\n if (judgment.verdict === \"skipped\") {\n yield {\n type: \"status_change\",\n status: \"completed\",\n reason: `skipped: ${judgment.reason}`,\n };\n return {\n status: \"completed\",\n turnsUsed: turn,\n tokensUsed,\n finalResponse: lastResponse || undefined,\n };\n }\n\n // M55 — checagem de token budget APÓS o turno (Codex: wind-down suave, para o loop).\n if (tokenBudget !== undefined && tokensUsed >= tokenBudget) {\n yield {\n type: \"status_change\",\n status: \"budget_limited\",\n reason: `token budget (${tokenBudget}) reached: ${tokensUsed} used`,\n };\n return {\n status: \"budget_limited\",\n turnsUsed: turn,\n tokensUsed,\n finalResponse: lastResponse || undefined,\n };\n }\n\n yield { type: \"continuation\", turn, prompt: continuationPrompt };\n }\n\n yield {\n type: \"status_change\",\n status: \"failed\",\n reason: `max turns (${maxTurns}) exhausted`,\n };\n return {\n status: \"failed\",\n turnsUsed: turn,\n tokensUsed,\n finalResponse: lastResponse || undefined,\n };\n}\n\n/**\n * Build the continuation user message for turns 2+. Keeps last response\n * preview short so we don't bloat the prompt cache.\n *\n * @internal\n */\n\nexport function composeContinuation(goal: string, lastResponse: string): string {\n // M55 — Codex-faithful continuation (ext/goal templates/goals/continuation.md): keep the FULL\n // objective intact, work from current-state evidence, and audit completion requirement-by-requirement\n // before declaring done. Improves the quality of what the judge then evaluates.\n return [\n GOAL_CONTINUATION_MARKER,\n \"Continue working toward the active goal.\",\n \"\",\n `<objective>\\n${goal}\\n</objective>`,\n \"\",\n \"Continuation behavior:\",\n \"- This goal persists across turns. Keep the full objective intact; if it cannot be finished now,\",\n \" make concrete progress toward the real requested end state and do not redefine success around a\",\n \" smaller or easier task.\",\n \"\",\n \"Work from evidence:\",\n \"- Use the current worktree and external state as authoritative. Inspect the current state before\",\n \" relying on it. Improve, replace, or remove existing work as needed to satisfy the actual objective.\",\n \"\",\n \"Completion audit:\",\n \"- Before deciding the goal is achieved, treat completion as unproven and verify it against the actual\",\n \" current state, requirement by requirement. Treat uncertain or indirect evidence as not achieved.\",\n \"\",\n `Your last response was:\\n${lastResponse.slice(-1000)}`,\n ].join(\"\\n\");\n}\n"]}
|
|
@@ -1,3 +1,35 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* M80 — os verdicts terminais que um judge pode devolver.
|
|
3
|
+
*
|
|
4
|
+
* `"blocked"` entrou aqui neste milestone. `GoalResult.status` já o carregava, mas o judge não tinha
|
|
5
|
+
* como EMITI-LO: seu vocabulário era `done | continue | skipped`, então diante de um bloqueio real
|
|
6
|
+
* ele só podia dizer `continue` — e o loop repetia o mesmo turno até estourar o orçamento,
|
|
7
|
+
* reportando `failed` por limite em vez de `blocked` por impossibilidade. Duas causas diferentes com
|
|
8
|
+
* o mesmo desfecho visível.
|
|
9
|
+
*
|
|
10
|
+
* @public
|
|
11
|
+
*/
|
|
12
|
+
export type Verdict = "done" | "continue" | "skipped" | "blocked";
|
|
13
|
+
/**
|
|
14
|
+
* M80 — resultado de uma chamada ao judge, agora público.
|
|
15
|
+
*
|
|
16
|
+
* Era `internal/`, então um consumidor que quisesse tipar o retorno — para reagir a `blocked` sem
|
|
17
|
+
* string mágica — precisava redeclarar a forma. É a mesma duplicação que o M78 fechou para a
|
|
18
|
+
* hierarquia de erro: sem superfície pública, reimplementar é a única saída legal para quem está
|
|
19
|
+
* atrás da fronteira de camadas.
|
|
20
|
+
*
|
|
21
|
+
* @public
|
|
22
|
+
*/
|
|
23
|
+
export interface JudgeResult {
|
|
24
|
+
verdict: Verdict;
|
|
25
|
+
reason: string;
|
|
26
|
+
/**
|
|
27
|
+
* `true` quando o texto não começou com um dos prefixos canônicos. O verdict vira `"continue"`
|
|
28
|
+
* (fail-safe) para não parar cedo demais; o chamador conta falhas consecutivas e desiste via
|
|
29
|
+
* `maxConsecutiveJudgeFailures`.
|
|
30
|
+
*/
|
|
31
|
+
parseFailed: boolean;
|
|
32
|
+
}
|
|
1
33
|
/**
|
|
2
34
|
* Public event types emitted by {@link SDKAgent.runUntil} (ADRs D115-D117).
|
|
3
35
|
*
|
package/package.json
CHANGED
|
@@ -1,17 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* Public types for the judge subsystem (T2.1, ADR D120).
|
|
3
|
-
*
|
|
4
|
-
* @internal
|
|
5
|
-
*/
|
|
6
|
-
/** Outcome of {@link parseVerdict} / {@link judgeCallImpl}. */
|
|
7
|
-
export interface JudgeResult {
|
|
8
|
-
verdict: Verdict;
|
|
9
|
-
reason: string;
|
|
10
|
-
/**
|
|
11
|
-
* `true` when the underlying text did not start with one of the three
|
|
12
|
-
* canonical prefixes. The verdict is set to `"continue"` (fail-safe,
|
|
13
|
-
* ADR D121) to avoid stopping prematurely; callers track consecutive
|
|
14
|
-
* failures and bail via `maxConsecutiveJudgeFailures`.
|
|
15
|
-
*/
|
|
16
|
-
parseFailed: boolean;
|
|
17
|
-
}
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"sources":["../src/internal/judge/parse-verdict.ts","../src/internal/judge/judge-call.ts"],"names":[],"mappings":"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","file":"judge-call-CEQ3FYYD.js","sourcesContent":["/**\n * Pure verdict parser (T2.1, ADRs D120-D121).\n *\n * Strict prefix matching against `DONE:`, `CONTINUE:`, `SKIPPED:`. Reason\n * is the suffix, trimmed. Anything else is fail-safe: verdict = `continue`,\n * `parseFailed: true` — caller's max-consecutive-failure cap stops the\n * loop after N flakes.\n *\n * @internal\n */\n\nimport type { JudgeResult } from \"./types.js\";\n\nconst DONE_PREFIX = \"DONE:\";\nconst CONTINUE_PREFIX = \"CONTINUE:\";\nconst SKIPPED_PREFIX = \"SKIPPED:\";\n/** M80 — o judge agora pode declarar impossibilidade, não só \"continue\". */\nconst BLOCKED_PREFIX = \"BLOCKED:\";\n\nexport function parseVerdict(text: string): JudgeResult {\n const trimmed = text.trim();\n\n if (trimmed.startsWith(DONE_PREFIX)) {\n return {\n verdict: \"done\",\n reason: trimmed.slice(DONE_PREFIX.length).trim(),\n parseFailed: false,\n };\n }\n if (trimmed.startsWith(CONTINUE_PREFIX)) {\n return {\n verdict: \"continue\",\n reason: trimmed.slice(CONTINUE_PREFIX.length).trim(),\n parseFailed: false,\n };\n }\n if (trimmed.startsWith(BLOCKED_PREFIX)) {\n return {\n verdict: \"blocked\",\n reason: trimmed.slice(BLOCKED_PREFIX.length).trim(),\n parseFailed: false,\n };\n }\n if (trimmed.startsWith(SKIPPED_PREFIX)) {\n return {\n verdict: \"skipped\",\n reason: trimmed.slice(SKIPPED_PREFIX.length).trim(),\n parseFailed: false,\n };\n }\n\n // Fail-safe: ADR D121 — prefer \"continue\" so we don't stop prematurely.\n // The runUntil loop counts consecutive parseFailed responses and bails\n // after `maxConsecutiveJudgeFailures` (default 3).\n return {\n verdict: \"continue\",\n reason: `judge response malformed: \"${trimmed.slice(0, 100)}\"`,\n parseFailed: true,\n };\n}\n","import { TheokitAgentError } from \"../../errors.js\";\n/**\n * Judge call primitive (T2.2, ADRs D119-D121).\n *\n * Instantiates a short-lived auxiliary agent that evaluates whether a\n * goal is satisfied. The auxiliary judge runs with `tools: []` and a\n * cheap model (default `openai/gpt-4o-mini`) — see ADR D119. The judge\n * model needs only API access; we read `OPENROUTER_API_KEY` directly\n * from the environment (EC-A — single source of truth) and let the\n * caller override via {@link JudgeOptions.apiKey} for Anthropic or\n * direct-OpenAI environments.\n *\n * NOTE: judge aux agents created from inside a `forkAgent` context will\n * inherit the parent fork's whitelist via AsyncLocalStorage (EC-J).\n * `tools: []` keeps this benign today; future callers that add tools to\n * a judge should be aware.\n *\n * @internal\n */\n\nimport type { AgentOptions, SDKAgent } from \"../../types/agent.js\";\nimport { parseVerdict } from \"./parse-verdict.js\";\nimport type { JudgeResult } from \"./types.js\";\n\n/** Inputs to the judge — pure data. */\nexport interface JudgeContext {\n goal: string;\n lastResponse: string;\n subgoals?: string[];\n}\n\n/** Caller-supplied tuning knobs for the judge call. */\nexport interface JudgeOptions {\n /**\n * Judge model identifier. Quando ausente, DERIVA de {@link agentModel} — e só cai no literal\n * `\"openai/gpt-4o-mini\"` se nem esse for informado.\n *\n * M80 — o default fixo era provider-cego: ele só resolve em OpenRouter. Com chave Anthropic dá\n * 404, com bearer OAuth dá 401, e em ambos o goal queimava 3 turnos inteiros antes de falhar com\n * razão enganosa. O agent-builder já contornava isso derivando por conta própria; o conhecimento\n * pertence aqui.\n */\n judgeModel?: string;\n /**\n * M80 — o modelo do agente CONDUZIDO. É a base da derivação: um judge que roda no mesmo modelo do\n * chat funciona onde o chat funciona.\n */\n agentModel?: string;\n /** Override env. Default `process.env.OPENROUTER_API_KEY` (EC-A). */\n apiKey?: string;\n}\n\n/**\n * M80 — a credencial ou o modelo do judge não servem: 401/404.\n *\n * Falha RÁPIDA por design. `rules/error-handling.md § 2` separa recuperável de irrecuperável, e um\n * modelo inexistente não passa a existir no retry — dobrar isso em `{parseFailed: true}` fazia o loop\n * tentar três vezes e reportar \"failed\" por limite de falhas consecutivas, escondendo que a causa era\n * credencial. Falha lenta e opaca trocada por rápida e clara.\n *\n * Falha de PARSE e erro de rede continuam dobrados: são recuperáveis, e o loop já decide por falhas\n * consecutivas.\n */\nexport class JudgeCredentialError extends TheokitAgentError {\n override readonly name = \"JudgeCredentialError\";\n\n constructor(\n readonly httpStatus: number,\n readonly judgeModel: string,\n cause: unknown,\n ) {\n super(\n `judge unavailable: model \"${judgeModel}\" returned ${String(httpStatus)}. ` +\n \"Pass `judgeModel`/`apiKey` that resolve for this provider, or omit `judgeModel` to derive \" +\n \"it from the agent being driven.\",\n { code: \"judge_credential\", cause, isRetryable: false },\n );\n }\n}\n\n/** Extrai o status HTTP de um erro de provider, quando ele o carrega. */\nfunction statusHttpDe(err: unknown): number | undefined {\n const s =\n (err as { status?: unknown; statusCode?: unknown }).status ??\n (err as { statusCode?: unknown }).statusCode;\n if (typeof s === \"number\") return s;\n const m = /\\b(401|403|404)\\b/.exec(err instanceof Error ? err.message : String(err));\n return m?.[1] !== undefined ? Number(m[1]) : undefined;\n}\n\n/** Dependencies injected so `judge-call.ts` stays free of `Agent` import. */\nexport interface JudgeDeps {\n create: (options: AgentOptions) => Promise<SDKAgent>;\n}\n\n/**\n * Run the judge auxiliary agent and parse the verdict. Always returns a\n * `JudgeResult` — failures are folded into `{ parseFailed: true,\n * verdict: \"continue\" }` so the loop can decide based on consecutive\n * failures (ADR D121).\n *\n * @internal\n */\n// biome-ignore lint/complexity/noExcessiveCognitiveComplexity: judge call must (1) check env or override, (2) catch errors as fail-safe, (3) ensure aux dispose runs once regardless of outcome — three concerns linearly arranged, harm clarity to extract.\nexport async function judgeCallImpl(\n ctx: JudgeContext,\n options: JudgeOptions | undefined,\n deps: JudgeDeps,\n): Promise<JudgeResult> {\n const prompt = composeJudgePrompt(ctx);\n // EC-A: single env source — OpenRouter only. No multi-provider fallback.\n const apiKey = options?.apiKey ?? process.env.OPENROUTER_API_KEY;\n if (apiKey === undefined) {\n return {\n verdict: \"continue\",\n reason:\n \"judge unavailable: OPENROUTER_API_KEY missing and no override passed via options.apiKey\",\n parseFailed: true,\n };\n }\n\n // M80 — precedência: explícito > modelo do agente conduzido > o literal histórico. O literal só\n // sobrevive como último recurso, para não quebrar quem nunca passou nenhum dos dois.\n const judgeModel = options?.judgeModel ?? options?.agentModel ?? \"openai/gpt-4o-mini\";\n let auxAgent: SDKAgent | undefined;\n try {\n auxAgent = await deps.create({\n apiKey,\n model: { id: judgeModel },\n tools: [],\n local: {},\n metadata: { forkOrigin: \"judge\" },\n } as AgentOptions);\n const run = await auxAgent.send(prompt);\n const result = await run.wait();\n return parseVerdict(result.result ?? \"\");\n } catch (err) {\n // M80 — 401/403/404 são de credencial/modelo: irrecuperáveis, falham rápido e tipado. Todo o\n // resto (rede, timeout, 5xx) segue dobrado, porque É recuperável e o loop já decide por falhas\n // consecutivas.\n const status = statusHttpDe(err);\n if (status === 401 || status === 403 || status === 404) {\n throw new JudgeCredentialError(status, judgeModel, err);\n }\n return {\n verdict: \"continue\",\n reason: `judge call failed: ${err instanceof Error ? err.message : String(err)}`,\n parseFailed: true,\n };\n } finally {\n if (auxAgent !== undefined) {\n try {\n await auxAgent.dispose();\n } catch {\n // dispose errors are non-fatal; judge result is already prepared\n }\n }\n }\n}\n\n/**\n * Build the strict-format prompt the judge expects.\n *\n * @internal\n */\nexport function composeJudgePrompt(ctx: JudgeContext): string {\n const subgoals =\n ctx.subgoals !== undefined && ctx.subgoals.length > 0 ? ctx.subgoals.join(\", \") : \"(none)\";\n return `You are a goal judge. Determine if this goal is satisfied.\n\nGoal: ${ctx.goal}\nSubgoals: ${subgoals}\nLast agent response: ${ctx.lastResponse}\n\nRespond with EXACTLY one of:\n- DONE: <reason>\n- CONTINUE: <what's left>\n- SKIPPED: <why not applicable>\n\nBe strict. If unclear, prefer CONTINUE.`;\n}\n"]}
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"sources":["../src/internal/judge/parse-verdict.ts","../src/internal/judge/judge-call.ts"],"names":["TheokitAgentError"],"mappings":"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","file":"judge-call-QG4WZ7F3.cjs","sourcesContent":["/**\n * Pure verdict parser (T2.1, ADRs D120-D121).\n *\n * Strict prefix matching against `DONE:`, `CONTINUE:`, `SKIPPED:`. Reason\n * is the suffix, trimmed. Anything else is fail-safe: verdict = `continue`,\n * `parseFailed: true` — caller's max-consecutive-failure cap stops the\n * loop after N flakes.\n *\n * @internal\n */\n\nimport type { JudgeResult } from \"./types.js\";\n\nconst DONE_PREFIX = \"DONE:\";\nconst CONTINUE_PREFIX = \"CONTINUE:\";\nconst SKIPPED_PREFIX = \"SKIPPED:\";\n/** M80 — o judge agora pode declarar impossibilidade, não só \"continue\". */\nconst BLOCKED_PREFIX = \"BLOCKED:\";\n\nexport function parseVerdict(text: string): JudgeResult {\n const trimmed = text.trim();\n\n if (trimmed.startsWith(DONE_PREFIX)) {\n return {\n verdict: \"done\",\n reason: trimmed.slice(DONE_PREFIX.length).trim(),\n parseFailed: false,\n };\n }\n if (trimmed.startsWith(CONTINUE_PREFIX)) {\n return {\n verdict: \"continue\",\n reason: trimmed.slice(CONTINUE_PREFIX.length).trim(),\n parseFailed: false,\n };\n }\n if (trimmed.startsWith(BLOCKED_PREFIX)) {\n return {\n verdict: \"blocked\",\n reason: trimmed.slice(BLOCKED_PREFIX.length).trim(),\n parseFailed: false,\n };\n }\n if (trimmed.startsWith(SKIPPED_PREFIX)) {\n return {\n verdict: \"skipped\",\n reason: trimmed.slice(SKIPPED_PREFIX.length).trim(),\n parseFailed: false,\n };\n }\n\n // Fail-safe: ADR D121 — prefer \"continue\" so we don't stop prematurely.\n // The runUntil loop counts consecutive parseFailed responses and bails\n // after `maxConsecutiveJudgeFailures` (default 3).\n return {\n verdict: \"continue\",\n reason: `judge response malformed: \"${trimmed.slice(0, 100)}\"`,\n parseFailed: true,\n };\n}\n","import { TheokitAgentError } from \"../../errors.js\";\n/**\n * Judge call primitive (T2.2, ADRs D119-D121).\n *\n * Instantiates a short-lived auxiliary agent that evaluates whether a\n * goal is satisfied. The auxiliary judge runs with `tools: []` and a\n * cheap model (default `openai/gpt-4o-mini`) — see ADR D119. The judge\n * model needs only API access; we read `OPENROUTER_API_KEY` directly\n * from the environment (EC-A — single source of truth) and let the\n * caller override via {@link JudgeOptions.apiKey} for Anthropic or\n * direct-OpenAI environments.\n *\n * NOTE: judge aux agents created from inside a `forkAgent` context will\n * inherit the parent fork's whitelist via AsyncLocalStorage (EC-J).\n * `tools: []` keeps this benign today; future callers that add tools to\n * a judge should be aware.\n *\n * @internal\n */\n\nimport type { AgentOptions, SDKAgent } from \"../../types/agent.js\";\nimport { parseVerdict } from \"./parse-verdict.js\";\nimport type { JudgeResult } from \"./types.js\";\n\n/** Inputs to the judge — pure data. */\nexport interface JudgeContext {\n goal: string;\n lastResponse: string;\n subgoals?: string[];\n}\n\n/** Caller-supplied tuning knobs for the judge call. */\nexport interface JudgeOptions {\n /**\n * Judge model identifier. Quando ausente, DERIVA de {@link agentModel} — e só cai no literal\n * `\"openai/gpt-4o-mini\"` se nem esse for informado.\n *\n * M80 — o default fixo era provider-cego: ele só resolve em OpenRouter. Com chave Anthropic dá\n * 404, com bearer OAuth dá 401, e em ambos o goal queimava 3 turnos inteiros antes de falhar com\n * razão enganosa. O agent-builder já contornava isso derivando por conta própria; o conhecimento\n * pertence aqui.\n */\n judgeModel?: string;\n /**\n * M80 — o modelo do agente CONDUZIDO. É a base da derivação: um judge que roda no mesmo modelo do\n * chat funciona onde o chat funciona.\n */\n agentModel?: string;\n /** Override env. Default `process.env.OPENROUTER_API_KEY` (EC-A). */\n apiKey?: string;\n}\n\n/**\n * M80 — a credencial ou o modelo do judge não servem: 401/404.\n *\n * Falha RÁPIDA por design. `rules/error-handling.md § 2` separa recuperável de irrecuperável, e um\n * modelo inexistente não passa a existir no retry — dobrar isso em `{parseFailed: true}` fazia o loop\n * tentar três vezes e reportar \"failed\" por limite de falhas consecutivas, escondendo que a causa era\n * credencial. Falha lenta e opaca trocada por rápida e clara.\n *\n * Falha de PARSE e erro de rede continuam dobrados: são recuperáveis, e o loop já decide por falhas\n * consecutivas.\n */\nexport class JudgeCredentialError extends TheokitAgentError {\n override readonly name = \"JudgeCredentialError\";\n\n constructor(\n readonly httpStatus: number,\n readonly judgeModel: string,\n cause: unknown,\n ) {\n super(\n `judge unavailable: model \"${judgeModel}\" returned ${String(httpStatus)}. ` +\n \"Pass `judgeModel`/`apiKey` that resolve for this provider, or omit `judgeModel` to derive \" +\n \"it from the agent being driven.\",\n { code: \"judge_credential\", cause, isRetryable: false },\n );\n }\n}\n\n/** Extrai o status HTTP de um erro de provider, quando ele o carrega. */\nfunction statusHttpDe(err: unknown): number | undefined {\n const s =\n (err as { status?: unknown; statusCode?: unknown }).status ??\n (err as { statusCode?: unknown }).statusCode;\n if (typeof s === \"number\") return s;\n const m = /\\b(401|403|404)\\b/.exec(err instanceof Error ? err.message : String(err));\n return m?.[1] !== undefined ? Number(m[1]) : undefined;\n}\n\n/** Dependencies injected so `judge-call.ts` stays free of `Agent` import. */\nexport interface JudgeDeps {\n create: (options: AgentOptions) => Promise<SDKAgent>;\n}\n\n/**\n * Run the judge auxiliary agent and parse the verdict. Always returns a\n * `JudgeResult` — failures are folded into `{ parseFailed: true,\n * verdict: \"continue\" }` so the loop can decide based on consecutive\n * failures (ADR D121).\n *\n * @internal\n */\n// biome-ignore lint/complexity/noExcessiveCognitiveComplexity: judge call must (1) check env or override, (2) catch errors as fail-safe, (3) ensure aux dispose runs once regardless of outcome — three concerns linearly arranged, harm clarity to extract.\nexport async function judgeCallImpl(\n ctx: JudgeContext,\n options: JudgeOptions | undefined,\n deps: JudgeDeps,\n): Promise<JudgeResult> {\n const prompt = composeJudgePrompt(ctx);\n // EC-A: single env source — OpenRouter only. No multi-provider fallback.\n const apiKey = options?.apiKey ?? process.env.OPENROUTER_API_KEY;\n if (apiKey === undefined) {\n return {\n verdict: \"continue\",\n reason:\n \"judge unavailable: OPENROUTER_API_KEY missing and no override passed via options.apiKey\",\n parseFailed: true,\n };\n }\n\n // M80 — precedência: explícito > modelo do agente conduzido > o literal histórico. O literal só\n // sobrevive como último recurso, para não quebrar quem nunca passou nenhum dos dois.\n const judgeModel = options?.judgeModel ?? options?.agentModel ?? \"openai/gpt-4o-mini\";\n let auxAgent: SDKAgent | undefined;\n try {\n auxAgent = await deps.create({\n apiKey,\n model: { id: judgeModel },\n tools: [],\n local: {},\n metadata: { forkOrigin: \"judge\" },\n } as AgentOptions);\n const run = await auxAgent.send(prompt);\n const result = await run.wait();\n return parseVerdict(result.result ?? \"\");\n } catch (err) {\n // M80 — 401/403/404 são de credencial/modelo: irrecuperáveis, falham rápido e tipado. Todo o\n // resto (rede, timeout, 5xx) segue dobrado, porque É recuperável e o loop já decide por falhas\n // consecutivas.\n const status = statusHttpDe(err);\n if (status === 401 || status === 403 || status === 404) {\n throw new JudgeCredentialError(status, judgeModel, err);\n }\n return {\n verdict: \"continue\",\n reason: `judge call failed: ${err instanceof Error ? err.message : String(err)}`,\n parseFailed: true,\n };\n } finally {\n if (auxAgent !== undefined) {\n try {\n await auxAgent.dispose();\n } catch {\n // dispose errors are non-fatal; judge result is already prepared\n }\n }\n }\n}\n\n/**\n * Build the strict-format prompt the judge expects.\n *\n * @internal\n */\nexport function composeJudgePrompt(ctx: JudgeContext): string {\n const subgoals =\n ctx.subgoals !== undefined && ctx.subgoals.length > 0 ? ctx.subgoals.join(\", \") : \"(none)\";\n return `You are a goal judge. Determine if this goal is satisfied.\n\nGoal: ${ctx.goal}\nSubgoals: ${subgoals}\nLast agent response: ${ctx.lastResponse}\n\nRespond with EXACTLY one of:\n- DONE: <reason>\n- CONTINUE: <what's left>\n- SKIPPED: <why not applicable>\n\nBe strict. If unclear, prefer CONTINUE.`;\n}\n"]}
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"sources":["../src/internal/runtime/lifecycle/run-until.ts"],"names":["GOAL_CONTINUATION_MARKER"],"mappings":"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","file":"run-until-PNO3QQLI.cjs","sourcesContent":["/**\n * Goal-driven Ralph loop (T3.2, ADRs D115-D121).\n *\n * `runUntilImpl` is an `AsyncGenerator<GoalEvent, GoalResult, void>`:\n * yields events as the loop progresses, returns the final result when\n * the goal completes, fails, or is paused. The auxiliary judge model is\n * injected via `deps.judge` so the generator stays free of the `Agent`\n * façade import.\n *\n * EC-C fix: pre-aborted signals emit only `[paused]`, not `[active, paused]`.\n * EC-D: `maxTurns: 0` is supported (vacuous yield active → failed).\n *\n * @internal\n */\n\nimport type { SDKAgent } from \"../../../types/agent.js\";\nimport type { GoalEvent, GoalOptions, GoalResult } from \"../../../types/goal-events.js\";\nimport type { JudgeContext, JudgeOptions } from \"../../judge/judge-call.js\";\nimport type { JudgeResult } from \"../../judge/types.js\";\nimport { GOAL_CONTINUATION_MARKER } from \"./goal-marker.js\";\n\n/** DI contract: the judge is the only external touchpoint. */\nexport interface RunUntilDeps {\n judge: (ctx: JudgeContext, opts?: JudgeOptions) => Promise<JudgeResult>;\n}\n\n// biome-ignore lint/complexity/noExcessiveCognitiveComplexity: the goal-loop interleaves turn-start, send, judge, continuation, abort-check, and failure-bail — extracting helpers harms the linear narrative.\nexport async function* runUntilImpl(\n agent: SDKAgent,\n goal: string,\n options: GoalOptions | undefined,\n deps: RunUntilDeps,\n): AsyncGenerator<GoalEvent, GoalResult, void> {\n const maxTurns = options?.maxTurns ?? 20;\n const maxFails = options?.maxConsecutiveJudgeFailures ?? 3;\n const tokenBudget = options?.tokenBudget; // M55 — undefined ⇒ ilimitado\n const signal = options?.signal;\n // Use a function (not direct property access) so TS does not narrow\n // `signal.aborted` to `false | undefined` after the initial check.\n // AbortSignal.aborted is a getter that can flip true between calls.\n // biome-ignore lint/complexity/useOptionalChain: optional-chain would re-narrow signal?.aborted to `false | undefined` after the first check, defeating the purpose of this helper.\n const isAborted = (): boolean => signal !== undefined && signal.aborted;\n let turn = 0;\n let consecutiveFailures = 0;\n let tokensUsed = 0; // M55 — soma observada (0 se usage ausente — fail-open)\n let lastResponse = \"\";\n\n // EC-C: signal already aborted BEFORE first event → emit only [paused].\n if (isAborted()) {\n yield {\n type: \"status_change\",\n status: \"paused\",\n reason: \"aborted via AbortSignal before first turn\",\n };\n return { status: \"paused\", turnsUsed: 0, tokensUsed, finalResponse: undefined };\n }\n\n yield { type: \"status_change\", status: \"active\", reason: \"Goal started\" };\n\n while (turn < maxTurns) {\n if (isAborted()) {\n yield { type: \"status_change\", status: \"paused\", reason: \"aborted via AbortSignal\" };\n return {\n status: \"paused\",\n turnsUsed: turn,\n tokensUsed,\n finalResponse: lastResponse || undefined,\n };\n }\n\n turn += 1;\n yield { type: \"turn_start\", turn, goal };\n\n const continuationPrompt = turn === 1 ? goal : composeContinuation(goal, lastResponse);\n\n const run = await agent.send(continuationPrompt);\n // M55 review HIGH — thread the abort INTO the in-flight run: without this, Esc mid-turn is\n // cosmetic (the turn keeps mutating the workspace until it finishes on its own). `run.cancel()`\n // aborts the stream + in-flight tool calls (types/run.ts contract).\n const cancelOnAbort = (): void => {\n void (run as { cancel?: () => Promise<void> }).cancel?.();\n };\n if (signal !== undefined) {\n if (signal.aborted) cancelOnAbort();\n else signal.addEventListener(\"abort\", cancelOnAbort, { once: true });\n }\n const result = await run.wait();\n if (signal !== undefined) signal.removeEventListener(\"abort\", cancelOnAbort);\n lastResponse = result.result ?? \"\";\n // M55 — token accounting fail-open: só soma quando o run reporta usage.\n const turnTokens = (result as { usage?: { totalTokens?: number } }).usage?.totalTokens;\n if (typeof turnTokens === \"number\") tokensUsed += turnTokens;\n // Re-check aborted right after the turn lands: a cancelled turn must NOT spend a judge call.\n if (isAborted()) {\n yield { type: \"status_change\", status: \"paused\", reason: \"aborted via AbortSignal\" };\n return {\n status: \"paused\",\n turnsUsed: turn,\n tokensUsed,\n finalResponse: lastResponse || undefined,\n };\n }\n yield { type: \"agent_response\", turn, content: lastResponse };\n\n const judgeOpts: JudgeOptions = {};\n if (options?.judgeModel !== undefined) judgeOpts.judgeModel = options.judgeModel;\n if (options?.judgeApiKey !== undefined) judgeOpts.apiKey = options.judgeApiKey;\n const judgeCtx: JudgeContext = { goal, lastResponse };\n if (options?.subgoals !== undefined) judgeCtx.subgoals = options.subgoals;\n const judgment = await deps.judge(judgeCtx, judgeOpts);\n yield {\n type: \"judge_verdict\",\n turn,\n verdict: judgment.verdict,\n reason: judgment.reason,\n parseFailed: judgment.parseFailed,\n };\n\n if (judgment.parseFailed) {\n consecutiveFailures += 1;\n if (consecutiveFailures >= maxFails) {\n yield {\n type: \"status_change\",\n status: \"failed\",\n reason: `judge model too unreliable (${consecutiveFailures} parse failures in a row)`,\n };\n return {\n status: \"failed\",\n turnsUsed: turn,\n tokensUsed,\n finalResponse: lastResponse || undefined,\n };\n }\n } else {\n consecutiveFailures = 0;\n }\n\n if (judgment.verdict === \"done\") {\n yield { type: \"status_change\", status: \"completed\", reason: judgment.reason };\n return {\n status: \"completed\",\n turnsUsed: turn,\n tokensUsed,\n finalResponse: lastResponse || undefined,\n };\n }\n // M80 — o braço que faltava. Sem ele, um judge que reconhecesse impossibilidade só podia dizer\n // \"continue\", e o loop repetia o mesmo turno até estourar o orçamento — reportando `failed` por\n // limite em vez de `blocked` por impossibilidade. Duas causas distintas com o mesmo desfecho\n // visível, e o consumidor precisava reconciliar depois do loop para distingui-las.\n if (judgment.verdict === \"blocked\") {\n yield { type: \"status_change\", status: \"blocked\", reason: judgment.reason };\n return {\n status: \"blocked\",\n turnsUsed: turn,\n tokensUsed,\n finalResponse: lastResponse || undefined,\n };\n }\n if (judgment.verdict === \"skipped\") {\n yield {\n type: \"status_change\",\n status: \"completed\",\n reason: `skipped: ${judgment.reason}`,\n };\n return {\n status: \"completed\",\n turnsUsed: turn,\n tokensUsed,\n finalResponse: lastResponse || undefined,\n };\n }\n\n // M55 — checagem de token budget APÓS o turno (Codex: wind-down suave, para o loop).\n if (tokenBudget !== undefined && tokensUsed >= tokenBudget) {\n yield {\n type: \"status_change\",\n status: \"budget_limited\",\n reason: `token budget (${tokenBudget}) reached: ${tokensUsed} used`,\n };\n return {\n status: \"budget_limited\",\n turnsUsed: turn,\n tokensUsed,\n finalResponse: lastResponse || undefined,\n };\n }\n\n yield { type: \"continuation\", turn, prompt: continuationPrompt };\n }\n\n yield {\n type: \"status_change\",\n status: \"failed\",\n reason: `max turns (${maxTurns}) exhausted`,\n };\n return {\n status: \"failed\",\n turnsUsed: turn,\n tokensUsed,\n finalResponse: lastResponse || undefined,\n };\n}\n\n/**\n * Build the continuation user message for turns 2+. Keeps last response\n * preview short so we don't bloat the prompt cache.\n *\n * @internal\n */\n\nexport function composeContinuation(goal: string, lastResponse: string): string {\n // M55 — Codex-faithful continuation (ext/goal templates/goals/continuation.md): keep the FULL\n // objective intact, work from current-state evidence, and audit completion requirement-by-requirement\n // before declaring done. Improves the quality of what the judge then evaluates.\n return [\n GOAL_CONTINUATION_MARKER,\n \"Continue working toward the active goal.\",\n \"\",\n `<objective>\\n${goal}\\n</objective>`,\n \"\",\n \"Continuation behavior:\",\n \"- This goal persists across turns. Keep the full objective intact; if it cannot be finished now,\",\n \" make concrete progress toward the real requested end state and do not redefine success around a\",\n \" smaller or easier task.\",\n \"\",\n \"Work from evidence:\",\n \"- Use the current worktree and external state as authoritative. Inspect the current state before\",\n \" relying on it. Improve, replace, or remove existing work as needed to satisfy the actual objective.\",\n \"\",\n \"Completion audit:\",\n \"- Before deciding the goal is achieved, treat completion as unproven and verify it against the actual\",\n \" current state, requirement by requirement. Treat uncertain or indirect evidence as not achieved.\",\n \"\",\n `Your last response was:\\n${lastResponse.slice(-1000)}`,\n ].join(\"\\n\");\n}\n"]}
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"sources":["../src/internal/runtime/lifecycle/run-until.ts"],"names":[],"mappings":"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","file":"run-until-XLHEHRQM.js","sourcesContent":["/**\n * Goal-driven Ralph loop (T3.2, ADRs D115-D121).\n *\n * `runUntilImpl` is an `AsyncGenerator<GoalEvent, GoalResult, void>`:\n * yields events as the loop progresses, returns the final result when\n * the goal completes, fails, or is paused. The auxiliary judge model is\n * injected via `deps.judge` so the generator stays free of the `Agent`\n * façade import.\n *\n * EC-C fix: pre-aborted signals emit only `[paused]`, not `[active, paused]`.\n * EC-D: `maxTurns: 0` is supported (vacuous yield active → failed).\n *\n * @internal\n */\n\nimport type { SDKAgent } from \"../../../types/agent.js\";\nimport type { GoalEvent, GoalOptions, GoalResult } from \"../../../types/goal-events.js\";\nimport type { JudgeContext, JudgeOptions } from \"../../judge/judge-call.js\";\nimport type { JudgeResult } from \"../../judge/types.js\";\nimport { GOAL_CONTINUATION_MARKER } from \"./goal-marker.js\";\n\n/** DI contract: the judge is the only external touchpoint. */\nexport interface RunUntilDeps {\n judge: (ctx: JudgeContext, opts?: JudgeOptions) => Promise<JudgeResult>;\n}\n\n// biome-ignore lint/complexity/noExcessiveCognitiveComplexity: the goal-loop interleaves turn-start, send, judge, continuation, abort-check, and failure-bail — extracting helpers harms the linear narrative.\nexport async function* runUntilImpl(\n agent: SDKAgent,\n goal: string,\n options: GoalOptions | undefined,\n deps: RunUntilDeps,\n): AsyncGenerator<GoalEvent, GoalResult, void> {\n const maxTurns = options?.maxTurns ?? 20;\n const maxFails = options?.maxConsecutiveJudgeFailures ?? 3;\n const tokenBudget = options?.tokenBudget; // M55 — undefined ⇒ ilimitado\n const signal = options?.signal;\n // Use a function (not direct property access) so TS does not narrow\n // `signal.aborted` to `false | undefined` after the initial check.\n // AbortSignal.aborted is a getter that can flip true between calls.\n // biome-ignore lint/complexity/useOptionalChain: optional-chain would re-narrow signal?.aborted to `false | undefined` after the first check, defeating the purpose of this helper.\n const isAborted = (): boolean => signal !== undefined && signal.aborted;\n let turn = 0;\n let consecutiveFailures = 0;\n let tokensUsed = 0; // M55 — soma observada (0 se usage ausente — fail-open)\n let lastResponse = \"\";\n\n // EC-C: signal already aborted BEFORE first event → emit only [paused].\n if (isAborted()) {\n yield {\n type: \"status_change\",\n status: \"paused\",\n reason: \"aborted via AbortSignal before first turn\",\n };\n return { status: \"paused\", turnsUsed: 0, tokensUsed, finalResponse: undefined };\n }\n\n yield { type: \"status_change\", status: \"active\", reason: \"Goal started\" };\n\n while (turn < maxTurns) {\n if (isAborted()) {\n yield { type: \"status_change\", status: \"paused\", reason: \"aborted via AbortSignal\" };\n return {\n status: \"paused\",\n turnsUsed: turn,\n tokensUsed,\n finalResponse: lastResponse || undefined,\n };\n }\n\n turn += 1;\n yield { type: \"turn_start\", turn, goal };\n\n const continuationPrompt = turn === 1 ? goal : composeContinuation(goal, lastResponse);\n\n const run = await agent.send(continuationPrompt);\n // M55 review HIGH — thread the abort INTO the in-flight run: without this, Esc mid-turn is\n // cosmetic (the turn keeps mutating the workspace until it finishes on its own). `run.cancel()`\n // aborts the stream + in-flight tool calls (types/run.ts contract).\n const cancelOnAbort = (): void => {\n void (run as { cancel?: () => Promise<void> }).cancel?.();\n };\n if (signal !== undefined) {\n if (signal.aborted) cancelOnAbort();\n else signal.addEventListener(\"abort\", cancelOnAbort, { once: true });\n }\n const result = await run.wait();\n if (signal !== undefined) signal.removeEventListener(\"abort\", cancelOnAbort);\n lastResponse = result.result ?? \"\";\n // M55 — token accounting fail-open: só soma quando o run reporta usage.\n const turnTokens = (result as { usage?: { totalTokens?: number } }).usage?.totalTokens;\n if (typeof turnTokens === \"number\") tokensUsed += turnTokens;\n // Re-check aborted right after the turn lands: a cancelled turn must NOT spend a judge call.\n if (isAborted()) {\n yield { type: \"status_change\", status: \"paused\", reason: \"aborted via AbortSignal\" };\n return {\n status: \"paused\",\n turnsUsed: turn,\n tokensUsed,\n finalResponse: lastResponse || undefined,\n };\n }\n yield { type: \"agent_response\", turn, content: lastResponse };\n\n const judgeOpts: JudgeOptions = {};\n if (options?.judgeModel !== undefined) judgeOpts.judgeModel = options.judgeModel;\n if (options?.judgeApiKey !== undefined) judgeOpts.apiKey = options.judgeApiKey;\n const judgeCtx: JudgeContext = { goal, lastResponse };\n if (options?.subgoals !== undefined) judgeCtx.subgoals = options.subgoals;\n const judgment = await deps.judge(judgeCtx, judgeOpts);\n yield {\n type: \"judge_verdict\",\n turn,\n verdict: judgment.verdict,\n reason: judgment.reason,\n parseFailed: judgment.parseFailed,\n };\n\n if (judgment.parseFailed) {\n consecutiveFailures += 1;\n if (consecutiveFailures >= maxFails) {\n yield {\n type: \"status_change\",\n status: \"failed\",\n reason: `judge model too unreliable (${consecutiveFailures} parse failures in a row)`,\n };\n return {\n status: \"failed\",\n turnsUsed: turn,\n tokensUsed,\n finalResponse: lastResponse || undefined,\n };\n }\n } else {\n consecutiveFailures = 0;\n }\n\n if (judgment.verdict === \"done\") {\n yield { type: \"status_change\", status: \"completed\", reason: judgment.reason };\n return {\n status: \"completed\",\n turnsUsed: turn,\n tokensUsed,\n finalResponse: lastResponse || undefined,\n };\n }\n // M80 — o braço que faltava. Sem ele, um judge que reconhecesse impossibilidade só podia dizer\n // \"continue\", e o loop repetia o mesmo turno até estourar o orçamento — reportando `failed` por\n // limite em vez de `blocked` por impossibilidade. Duas causas distintas com o mesmo desfecho\n // visível, e o consumidor precisava reconciliar depois do loop para distingui-las.\n if (judgment.verdict === \"blocked\") {\n yield { type: \"status_change\", status: \"blocked\", reason: judgment.reason };\n return {\n status: \"blocked\",\n turnsUsed: turn,\n tokensUsed,\n finalResponse: lastResponse || undefined,\n };\n }\n if (judgment.verdict === \"skipped\") {\n yield {\n type: \"status_change\",\n status: \"completed\",\n reason: `skipped: ${judgment.reason}`,\n };\n return {\n status: \"completed\",\n turnsUsed: turn,\n tokensUsed,\n finalResponse: lastResponse || undefined,\n };\n }\n\n // M55 — checagem de token budget APÓS o turno (Codex: wind-down suave, para o loop).\n if (tokenBudget !== undefined && tokensUsed >= tokenBudget) {\n yield {\n type: \"status_change\",\n status: \"budget_limited\",\n reason: `token budget (${tokenBudget}) reached: ${tokensUsed} used`,\n };\n return {\n status: \"budget_limited\",\n turnsUsed: turn,\n tokensUsed,\n finalResponse: lastResponse || undefined,\n };\n }\n\n yield { type: \"continuation\", turn, prompt: continuationPrompt };\n }\n\n yield {\n type: \"status_change\",\n status: \"failed\",\n reason: `max turns (${maxTurns}) exhausted`,\n };\n return {\n status: \"failed\",\n turnsUsed: turn,\n tokensUsed,\n finalResponse: lastResponse || undefined,\n };\n}\n\n/**\n * Build the continuation user message for turns 2+. Keeps last response\n * preview short so we don't bloat the prompt cache.\n *\n * @internal\n */\n\nexport function composeContinuation(goal: string, lastResponse: string): string {\n // M55 — Codex-faithful continuation (ext/goal templates/goals/continuation.md): keep the FULL\n // objective intact, work from current-state evidence, and audit completion requirement-by-requirement\n // before declaring done. Improves the quality of what the judge then evaluates.\n return [\n GOAL_CONTINUATION_MARKER,\n \"Continue working toward the active goal.\",\n \"\",\n `<objective>\\n${goal}\\n</objective>`,\n \"\",\n \"Continuation behavior:\",\n \"- This goal persists across turns. Keep the full objective intact; if it cannot be finished now,\",\n \" make concrete progress toward the real requested end state and do not redefine success around a\",\n \" smaller or easier task.\",\n \"\",\n \"Work from evidence:\",\n \"- Use the current worktree and external state as authoritative. Inspect the current state before\",\n \" relying on it. Improve, replace, or remove existing work as needed to satisfy the actual objective.\",\n \"\",\n \"Completion audit:\",\n \"- Before deciding the goal is achieved, treat completion as unproven and verify it against the actual\",\n \" current state, requirement by requirement. Treat uncertain or indirect evidence as not achieved.\",\n \"\",\n `Your last response was:\\n${lastResponse.slice(-1000)}`,\n ].join(\"\\n\");\n}\n"]}
|