@zhixuan92/multi-model-agent-core 0.1.0 → 0.2.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/README.md +0 -6
- package/dist/config/schema.d.ts +27 -0
- package/dist/config/schema.d.ts.map +1 -1
- package/dist/config/schema.js +13 -0
- package/dist/config/schema.js.map +1 -1
- package/dist/context/context-block-store.d.ts +75 -0
- package/dist/context/context-block-store.d.ts.map +1 -0
- package/dist/context/context-block-store.js +82 -0
- package/dist/context/context-block-store.js.map +1 -0
- package/dist/context/expand-context-blocks.d.ts +20 -0
- package/dist/context/expand-context-blocks.d.ts.map +1 -0
- package/dist/context/expand-context-blocks.js +46 -0
- package/dist/context/expand-context-blocks.js.map +1 -0
- package/dist/delegate-with-escalation.d.ts +34 -0
- package/dist/delegate-with-escalation.d.ts.map +1 -0
- package/dist/delegate-with-escalation.js +168 -0
- package/dist/delegate-with-escalation.js.map +1 -0
- package/dist/index.d.ts +4 -1
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +3 -0
- package/dist/index.js.map +1 -1
- package/dist/model-profiles.json +8 -4
- package/dist/provider.d.ts.map +1 -1
- package/dist/provider.js +7 -1
- package/dist/provider.js.map +1 -1
- package/dist/routing/model-profiles.d.ts +1 -0
- package/dist/routing/model-profiles.d.ts.map +1 -1
- package/dist/routing/model-profiles.js +4 -0
- package/dist/routing/model-profiles.js.map +1 -1
- package/dist/run-tasks.d.ts +26 -2
- package/dist/run-tasks.d.ts.map +1 -1
- package/dist/run-tasks.js +61 -19
- package/dist/run-tasks.js.map +1 -1
- package/dist/runners/claude-runner.d.ts.map +1 -1
- package/dist/runners/claude-runner.js +643 -32
- package/dist/runners/claude-runner.js.map +1 -1
- package/dist/runners/codex-runner.d.ts.map +1 -1
- package/dist/runners/codex-runner.js +473 -48
- package/dist/runners/codex-runner.js.map +1 -1
- package/dist/runners/error-classification.d.ts +30 -0
- package/dist/runners/error-classification.d.ts.map +1 -0
- package/dist/runners/error-classification.js +72 -0
- package/dist/runners/error-classification.js.map +1 -0
- package/dist/runners/injection-type.d.ts +17 -0
- package/dist/runners/injection-type.d.ts.map +1 -0
- package/dist/runners/injection-type.js +27 -0
- package/dist/runners/injection-type.js.map +1 -0
- package/dist/runners/openai-runner.d.ts +5 -0
- package/dist/runners/openai-runner.d.ts.map +1 -1
- package/dist/runners/openai-runner.js +508 -36
- package/dist/runners/openai-runner.js.map +1 -1
- package/dist/runners/prevention.d.ts +41 -0
- package/dist/runners/prevention.d.ts.map +1 -0
- package/dist/runners/prevention.js +68 -0
- package/dist/runners/prevention.js.map +1 -0
- package/dist/runners/supervision.d.ts +130 -0
- package/dist/runners/supervision.d.ts.map +1 -0
- package/dist/runners/supervision.js +238 -0
- package/dist/runners/supervision.js.map +1 -0
- package/dist/tools/claude-adapter.d.ts.map +1 -1
- package/dist/tools/claude-adapter.js +6 -3
- package/dist/tools/claude-adapter.js.map +1 -1
- package/dist/tools/definitions.d.ts +3 -1
- package/dist/tools/definitions.d.ts.map +1 -1
- package/dist/tools/definitions.js +56 -5
- package/dist/tools/definitions.js.map +1 -1
- package/dist/tools/openai-adapter.d.ts.map +1 -1
- package/dist/tools/openai-adapter.js +6 -3
- package/dist/tools/openai-adapter.js.map +1 -1
- package/dist/tools/scratchpad.d.ts +28 -0
- package/dist/tools/scratchpad.d.ts.map +1 -0
- package/dist/tools/scratchpad.js +49 -0
- package/dist/tools/scratchpad.js.map +1 -0
- package/dist/tools/tracker.d.ts +38 -2
- package/dist/tools/tracker.d.ts.map +1 -1
- package/dist/tools/tracker.js +54 -5
- package/dist/tools/tracker.js.map +1 -1
- package/dist/types.d.ts +184 -2
- package/dist/types.d.ts.map +1 -1
- package/dist/types.js +17 -1
- package/dist/types.js.map +1 -1
- package/package.json +9 -15
|
@@ -0,0 +1,41 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Sub-agent prevention layer.
|
|
3
|
+
*
|
|
4
|
+
* Provides the strong default system prompt, the budget hint preamble,
|
|
5
|
+
* and the periodic re-grounding message that the runners inject to keep
|
|
6
|
+
* the model focused. The goal is to make the first attempt succeed.
|
|
7
|
+
*
|
|
8
|
+
* All builders here MUST be deterministic: same input → byte-identical
|
|
9
|
+
* output. No Date.now(), no Math.random(), no environment variable
|
|
10
|
+
* leakage. Tests in tests/runners/prevention.test.ts assert this.
|
|
11
|
+
*
|
|
12
|
+
* See spec Part A.1 for the design rationale.
|
|
13
|
+
*/
|
|
14
|
+
export declare function buildSystemPrompt(): string;
|
|
15
|
+
export interface BuildBudgetHintOptions {
|
|
16
|
+
maxTurns: number;
|
|
17
|
+
}
|
|
18
|
+
export declare function buildBudgetHint(opts: BuildBudgetHintOptions): string;
|
|
19
|
+
export declare const RE_GROUNDING_INTERVAL_TURNS = 10;
|
|
20
|
+
export interface BuildReGroundingMessageOptions {
|
|
21
|
+
originalPromptExcerpt: string;
|
|
22
|
+
currentTurn: number;
|
|
23
|
+
maxTurns: number;
|
|
24
|
+
toolCallsSoFar: number;
|
|
25
|
+
filesReadSoFar: number;
|
|
26
|
+
}
|
|
27
|
+
export declare function buildReGroundingMessage(opts: BuildReGroundingMessageOptions): string;
|
|
28
|
+
export interface BuildBudgetPressureNudgeOptions {
|
|
29
|
+
inputTokens: number;
|
|
30
|
+
softLimit: number;
|
|
31
|
+
}
|
|
32
|
+
/**
|
|
33
|
+
* Nudge message the runner injects when the watchdog crosses its
|
|
34
|
+
* `warning` threshold (see supervision.checkWatchdogThreshold). The text
|
|
35
|
+
* is deliberately terse: the model is already close to the soft limit, so
|
|
36
|
+
* we tell it to stop exploring and produce a final answer from whatever
|
|
37
|
+
* it has gathered. Kept here so every runner (openai, claude, codex)
|
|
38
|
+
* uses byte-identical wording.
|
|
39
|
+
*/
|
|
40
|
+
export declare function buildBudgetPressureNudge(opts: BuildBudgetPressureNudgeOptions): string;
|
|
41
|
+
//# sourceMappingURL=prevention.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"prevention.d.ts","sourceRoot":"","sources":["../../src/runners/prevention.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;GAYG;AAEH,wBAAgB,iBAAiB,IAAI,MAAM,CAoB1C;AAED,MAAM,WAAW,sBAAsB;IACrC,QAAQ,EAAE,MAAM,CAAC;CAClB;AAED,wBAAgB,eAAe,CAAC,IAAI,EAAE,sBAAsB,GAAG,MAAM,CAOpE;AAED,eAAO,MAAM,2BAA2B,KAAK,CAAC;AAE9C,MAAM,WAAW,8BAA8B;IAC7C,qBAAqB,EAAE,MAAM,CAAC;IAC9B,WAAW,EAAE,MAAM,CAAC;IACpB,QAAQ,EAAE,MAAM,CAAC;IACjB,cAAc,EAAE,MAAM,CAAC;IACvB,cAAc,EAAE,MAAM,CAAC;CACxB;AAED,wBAAgB,uBAAuB,CAAC,IAAI,EAAE,8BAA8B,GAAG,MAAM,CAUpF;AAED,MAAM,WAAW,+BAA+B;IAC9C,WAAW,EAAE,MAAM,CAAC;IACpB,SAAS,EAAE,MAAM,CAAC;CACnB;AAED;;;;;;;GAOG;AACH,wBAAgB,wBAAwB,CAAC,IAAI,EAAE,+BAA+B,GAAG,MAAM,CAMtF"}
|
|
@@ -0,0 +1,68 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Sub-agent prevention layer.
|
|
3
|
+
*
|
|
4
|
+
* Provides the strong default system prompt, the budget hint preamble,
|
|
5
|
+
* and the periodic re-grounding message that the runners inject to keep
|
|
6
|
+
* the model focused. The goal is to make the first attempt succeed.
|
|
7
|
+
*
|
|
8
|
+
* All builders here MUST be deterministic: same input → byte-identical
|
|
9
|
+
* output. No Date.now(), no Math.random(), no environment variable
|
|
10
|
+
* leakage. Tests in tests/runners/prevention.test.ts assert this.
|
|
11
|
+
*
|
|
12
|
+
* See spec Part A.1 for the design rationale.
|
|
13
|
+
*/
|
|
14
|
+
export function buildSystemPrompt() {
|
|
15
|
+
return [
|
|
16
|
+
'You are a sub-agent completing a single task end-to-end. Read these rules before you begin any tool calls.',
|
|
17
|
+
'',
|
|
18
|
+
'The deliverable. Your final assistant message — and only your final assistant message — is what gets returned to the caller. Intermediate tool outputs and earlier turns are discarded. If your final message is empty, a fragment, or contained only <think> reasoning, the caller receives nothing useful and the dispatch is considered failed.',
|
|
19
|
+
'',
|
|
20
|
+
'Plan before you act. Before any tool call, identify (1) what the task is asking for, (2) what success criteria the task specifies (output format, required sections, acceptance tests), (3) what files or information you need to gather, and (4) approximately how many tool calls you will need. State your plan at the top of your investigation if you find that helpful — it does not waste budget, it focuses you.',
|
|
21
|
+
'',
|
|
22
|
+
'Investigate efficiently. Prefer one recursive grep over many readFile calls. Use glob to find files before you read them. Batch related questions into a single tool call when possible. The goal is to gather enough evidence to produce the deliverable, not to read every file.',
|
|
23
|
+
'',
|
|
24
|
+
'Write findings as you go. Do not save all your findings for the final message. As you discover things, mention them in your assistant turns. This is preserved in the runner scratchpad and salvageable if the run is interrupted. The final message should be the synthesis, not the only place findings appear.',
|
|
25
|
+
'',
|
|
26
|
+
'Anti-pattern: do not end with "let me check X next." That is a tool call you are describing instead of executing. Either call the tool you described, or produce your final answer now.',
|
|
27
|
+
'',
|
|
28
|
+
'Anti-pattern: do not produce only <think> content as your final message. Reasoning tags are stripped before the response is returned, so a thinking-only message is equivalent to no message. Your final answer must be plain text outside any reasoning tags.',
|
|
29
|
+
'',
|
|
30
|
+
'Anti-pattern: do not bail mid-task. If you encounter a problem (a tool did not work, you cannot find a file, the task is unclear), produce a partial answer that explains what you found and what blocked you. A partial answer is useful; an empty message is not.',
|
|
31
|
+
'',
|
|
32
|
+
'If the task specifies an output format, follow it exactly. Match required headers, table formats, prefixes (e.g. "start with # Gap Report:"), section structures. The caller is checking for the format; getting close is not the same as getting it right.',
|
|
33
|
+
].join('\n');
|
|
34
|
+
}
|
|
35
|
+
export function buildBudgetHint(opts) {
|
|
36
|
+
const half = Math.floor(opts.maxTurns / 2);
|
|
37
|
+
return [
|
|
38
|
+
`Budget reminder: this task should complete in approximately ${opts.maxTurns} tool calls or fewer.`,
|
|
39
|
+
'Batch your investigation, prefer recursive grep over file-by-file reads, and produce findings progressively as you gather them.',
|
|
40
|
+
`Hit ${half} calls? You should already be drafting your final answer.`,
|
|
41
|
+
].join(' ');
|
|
42
|
+
}
|
|
43
|
+
export const RE_GROUNDING_INTERVAL_TURNS = 10;
|
|
44
|
+
export function buildReGroundingMessage(opts) {
|
|
45
|
+
const percent = Math.round((opts.currentTurn / opts.maxTurns) * 100);
|
|
46
|
+
const excerpt = opts.originalPromptExcerpt.slice(0, 200);
|
|
47
|
+
return [
|
|
48
|
+
`Reminder: your task is "${excerpt}${opts.originalPromptExcerpt.length > 200 ? '...' : ''}".`,
|
|
49
|
+
`You are at turn ${opts.currentTurn} of ${opts.maxTurns} (≈ ${percent}% of budget used).`,
|
|
50
|
+
`Tool calls so far: ${opts.toolCallsSoFar}. Files read: ${opts.filesReadSoFar}.`,
|
|
51
|
+
'If you have not yet started drafting the final answer, do so now.',
|
|
52
|
+
'Make sure you have a plan to produce the final answer with your remaining budget.',
|
|
53
|
+
].join(' ');
|
|
54
|
+
}
|
|
55
|
+
/**
|
|
56
|
+
* Nudge message the runner injects when the watchdog crosses its
|
|
57
|
+
* `warning` threshold (see supervision.checkWatchdogThreshold). The text
|
|
58
|
+
* is deliberately terse: the model is already close to the soft limit, so
|
|
59
|
+
* we tell it to stop exploring and produce a final answer from whatever
|
|
60
|
+
* it has gathered. Kept here so every runner (openai, claude, codex)
|
|
61
|
+
* uses byte-identical wording.
|
|
62
|
+
*/
|
|
63
|
+
export function buildBudgetPressureNudge(opts) {
|
|
64
|
+
return (`Budget pressure: you have used approximately ${opts.inputTokens} ` +
|
|
65
|
+
`input tokens out of a soft limit of ${opts.softLimit}. Stop exploring and ` +
|
|
66
|
+
`produce your complete final answer now with whatever you have gathered.`);
|
|
67
|
+
}
|
|
68
|
+
//# sourceMappingURL=prevention.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"prevention.js","sourceRoot":"","sources":["../../src/runners/prevention.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;GAYG;AAEH,MAAM,UAAU,iBAAiB;IAC/B,OAAO;QACL,4GAA4G;QAC5G,EAAE;QACF,oVAAoV;QACpV,EAAE;QACF,0ZAA0Z;QAC1Z,EAAE;QACF,oRAAoR;QACpR,EAAE;QACF,mTAAmT;QACnT,EAAE;QACF,yLAAyL;QACzL,EAAE;QACF,gQAAgQ;QAChQ,EAAE;QACF,qQAAqQ;QACrQ,EAAE;QACF,6PAA6P;KAC9P,CAAC,IAAI,CAAC,IAAI,CAAC,CAAC;AACf,CAAC;AAMD,MAAM,UAAU,eAAe,CAAC,IAA4B;IAC1D,MAAM,IAAI,GAAG,IAAI,CAAC,KAAK,CAAC,IAAI,CAAC,QAAQ,GAAG,CAAC,CAAC,CAAC;IAC3C,OAAO;QACL,+DAA+D,IAAI,CAAC,QAAQ,uBAAuB;QACnG,iIAAiI;QACjI,OAAO,IAAI,2DAA2D;KACvE,CAAC,IAAI,CAAC,GAAG,CAAC,CAAC;AACd,CAAC;AAED,MAAM,CAAC,MAAM,2BAA2B,GAAG,EAAE,CAAC;AAU9C,MAAM,UAAU,uBAAuB,CAAC,IAAoC;IAC1E,MAAM,OAAO,GAAG,IAAI,CAAC,KAAK,CAAC,CAAC,IAAI,CAAC,WAAW,GAAG,IAAI,CAAC,QAAQ,CAAC,GAAG,GAAG,CAAC,CAAC;IACrE,MAAM,OAAO,GAAG,IAAI,CAAC,qBAAqB,CAAC,KAAK,CAAC,CAAC,EAAE,GAAG,CAAC,CAAC;IACzD,OAAO;QACL,2BAA2B,OAAO,GAAG,IAAI,CAAC,qBAAqB,CAAC,MAAM,GAAG,GAAG,CAAC,CAAC,CAAC,KAAK,CAAC,CAAC,CAAC,EAAE,IAAI;QAC7F,mBAAmB,IAAI,CAAC,WAAW,OAAO,IAAI,CAAC,QAAQ,OAAO,OAAO,oBAAoB;QACzF,sBAAsB,IAAI,CAAC,cAAc,iBAAiB,IAAI,CAAC,cAAc,GAAG;QAChF,mEAAmE;QACnE,mFAAmF;KACpF,CAAC,IAAI,CAAC,GAAG,CAAC,CAAC;AACd,CAAC;AAOD;;;;;;;GAOG;AACH,MAAM,UAAU,wBAAwB,CAAC,IAAqC;IAC5E,OAAO,CACL,gDAAgD,IAAI,CAAC,WAAW,GAAG;QACnE,uCAAuC,IAAI,CAAC,SAAS,uBAAuB;QAC5E,yEAAyE,CAC1E,CAAC;AACJ,CAAC"}
|
|
@@ -0,0 +1,130 @@
|
|
|
1
|
+
import type { ProviderConfig } from '../types.js';
|
|
2
|
+
import type { ModelProfile } from '../routing/model-profiles.js';
|
|
3
|
+
/**
|
|
4
|
+
* Sub-agent completion supervision.
|
|
5
|
+
*
|
|
6
|
+
* The runner calls validateCompletion() after every turn that ends without
|
|
7
|
+
* a tool call (the SDK signal "agent done"). If the result is degenerate,
|
|
8
|
+
* the runner injects a re-prompt and continues the loop instead of returning.
|
|
9
|
+
*
|
|
10
|
+
* See docs/superpowers/specs/2026-04-10-subagent-completion-supervision-design.md
|
|
11
|
+
* Parts A.2.2 and A.4 for the full contract.
|
|
12
|
+
*
|
|
13
|
+
* --- @openai/agents SDK introspection finding (from Task 1, Step 1) ---
|
|
14
|
+
* Happy path confirmed. The `@openai/agents` SDK (via @openai/agents-core)
|
|
15
|
+
* exposes intermediate assistant text on the returned `RunResult`:
|
|
16
|
+
* - `result.newItems: RunItem[]` is a discriminated union where entries of
|
|
17
|
+
* type `"message_output_item"` are `RunMessageOutputItem` instances with
|
|
18
|
+
* `rawItem.role === "assistant"` and `rawItem.content` carrying
|
|
19
|
+
* `{ type: "output_text", text: string }` parts (plus `refusal`, `audio`,
|
|
20
|
+
* `image`). See node_modules/@openai/agents-core/dist/items.d.ts around
|
|
21
|
+
* line 337 (class RunMessageOutputItem) and dist/result.d.ts lines 17-76
|
|
22
|
+
* (RunResultData interface — `newItems`, `output`, `history`, `state`).
|
|
23
|
+
* - `result.state` additionally holds the full RunState, and
|
|
24
|
+
* StreamedRunResult exposes a `RunStreamEvent` async iterator for
|
|
25
|
+
* live mid-run observation if we ever need true streaming.
|
|
26
|
+
* For our scratchpad needs the non-streaming path is sufficient: after any
|
|
27
|
+
* `agentRun(...)` call (including iterative re-prompt turns), we can iterate
|
|
28
|
+
* `result.newItems`, pick every `message_output_item`, concatenate its
|
|
29
|
+
* `output_text` parts, and append the result to the TextScratchpad. This
|
|
30
|
+
* gives us full intermediate salvage for openai-runner without dropping to
|
|
31
|
+
* the lower-level OpenAI client or patching hooks. Task 3 should implement
|
|
32
|
+
* this salvage extraction.
|
|
33
|
+
* ----------------------------------------------------------------------
|
|
34
|
+
*/
|
|
35
|
+
export type DegenerateKind = 'empty' | 'thinking_only' | 'fragment' | 'no_terminator';
|
|
36
|
+
export interface ValidationResult {
|
|
37
|
+
valid: boolean;
|
|
38
|
+
kind?: DegenerateKind;
|
|
39
|
+
reason?: string;
|
|
40
|
+
/** Last 60 characters of the trimmed text, used by buildRePrompt. */
|
|
41
|
+
tail?: string;
|
|
42
|
+
}
|
|
43
|
+
export interface ValidateCompletionOptions {
|
|
44
|
+
minLength?: number;
|
|
45
|
+
}
|
|
46
|
+
/**
|
|
47
|
+
* Marker returned by `stripThinkingTags` when the entire final message was
|
|
48
|
+
* `<think>...</think>` reasoning and stripping left nothing. Exported here
|
|
49
|
+
* (rather than from openai-runner.ts) so that `validateCompletion` can detect
|
|
50
|
+
* the marker without importing the runner, and so other runners can reuse it
|
|
51
|
+
* when they implement their own thinking-only salvage. There is exactly one
|
|
52
|
+
* canonical constant.
|
|
53
|
+
*/
|
|
54
|
+
export declare const THINKING_DIAGNOSTIC_MARKER = "[model final message contained only <think>...</think> reasoning, no plain-text answer]";
|
|
55
|
+
export declare function validateCompletion(text: string, opts?: ValidateCompletionOptions): ValidationResult;
|
|
56
|
+
export declare function buildRePrompt(result: ValidationResult): string;
|
|
57
|
+
/**
|
|
58
|
+
* Compares two consecutive degenerate outputs for byte equality. Used by
|
|
59
|
+
* the supervision loop's same-output early-out: if the model produces
|
|
60
|
+
* identical garbage twice in a row, give up immediately instead of
|
|
61
|
+
* burning the third retry.
|
|
62
|
+
*/
|
|
63
|
+
export declare function sameDegenerateOutput(a: string, b: string): boolean;
|
|
64
|
+
/**
|
|
65
|
+
* Resolves the effective inputTokenSoftLimit for a (provider, profile) pair.
|
|
66
|
+
*
|
|
67
|
+
* Precedence: `config.inputTokenSoftLimit` (user override) wins over
|
|
68
|
+
* `profile.inputTokenSoftLimit` (family default).
|
|
69
|
+
*
|
|
70
|
+
* Both fields are Zod-validated upstream:
|
|
71
|
+
* - `ProviderConfig.inputTokenSoftLimit` — `z.number().int().positive().optional()`
|
|
72
|
+
* (see packages/core/src/config/schema.ts)
|
|
73
|
+
* - `ModelProfile.inputTokenSoftLimit` — `z.number().int().positive()` (required)
|
|
74
|
+
*
|
|
75
|
+
* Because profile is guaranteed to carry a positive integer (DEFAULT_PROFILE
|
|
76
|
+
* supplies `100_000` when no prefix matches), there is no hardcoded
|
|
77
|
+
* constant fallback — the DEFAULT_PROFILE value is the de-facto fallback
|
|
78
|
+
* for unprofiled model IDs.
|
|
79
|
+
*/
|
|
80
|
+
export declare function resolveInputTokenSoftLimit(config: ProviderConfig, profile: ModelProfile): number;
|
|
81
|
+
export type WatchdogStatus = 'ok' | 'warning' | 'force_salvage';
|
|
82
|
+
/**
|
|
83
|
+
* Watchdog threshold ratios (fraction of the resolved `softLimit`).
|
|
84
|
+
* Exported so tests can reference the exact boundary values.
|
|
85
|
+
*
|
|
86
|
+
* - At/above `WATCHDOG_WARNING_RATIO` (80%) the supervision loop nudges
|
|
87
|
+
* the model toward salvage.
|
|
88
|
+
* - At/above `WATCHDOG_FORCE_SALVAGE_RATIO` (95%) the loop is forcibly
|
|
89
|
+
* terminated and the scratchpad is salvaged.
|
|
90
|
+
*/
|
|
91
|
+
export declare const WATCHDOG_WARNING_RATIO = 0.8;
|
|
92
|
+
export declare const WATCHDOG_FORCE_SALVAGE_RATIO = 0.95;
|
|
93
|
+
/**
|
|
94
|
+
* Given the cumulative input token usage and the resolved soft limit,
|
|
95
|
+
* returns the watchdog status:
|
|
96
|
+
* - 'ok' below 80%
|
|
97
|
+
* - 'warning' at or above 80%, below 95% (model is nudged)
|
|
98
|
+
* - 'force_salvage' at or above 95% (loop is forcibly terminated)
|
|
99
|
+
*
|
|
100
|
+
* Throws if `softLimit` is not a positive finite number. Runners call
|
|
101
|
+
* this independently, so a silent `'ok'` on a bad limit would mask
|
|
102
|
+
* upstream config bugs.
|
|
103
|
+
*/
|
|
104
|
+
export declare function checkWatchdogThreshold(cumulativeInputTokens: number, softLimit: number): WatchdogStatus;
|
|
105
|
+
export interface WatchdogEventDetails {
|
|
106
|
+
provider: string;
|
|
107
|
+
model: string;
|
|
108
|
+
turn: number;
|
|
109
|
+
inputTokens: number;
|
|
110
|
+
softLimit: number;
|
|
111
|
+
scratchpadChars: number;
|
|
112
|
+
}
|
|
113
|
+
/**
|
|
114
|
+
* Emits a structured log line at watchdog threshold crossings.
|
|
115
|
+
*
|
|
116
|
+
* Gated on the `MULTI_MODEL_DEBUG` environment variable: the function is
|
|
117
|
+
* a no-op unless `process.env.MULTI_MODEL_DEBUG === '1'`. Any other value
|
|
118
|
+
* (including `'true'`, `'yes'`, or unset) suppresses the log.
|
|
119
|
+
*
|
|
120
|
+
* When enabled, a single line is written to `stderr` via `console.error`
|
|
121
|
+
* of the form:
|
|
122
|
+
* `[multi-model-agent] WATCHDOG <status>: provider=… model=… turn=… inputTokens=… softLimit=… percentOfLimit=… [scratchpadChars=…]`
|
|
123
|
+
*
|
|
124
|
+
* `scratchpadChars` is only appended when `status === 'force_salvage'`,
|
|
125
|
+
* since that is the transition where salvage content size matters for
|
|
126
|
+
* calibration. Used for empirical calibration of the 80% / 95%
|
|
127
|
+
* thresholds. See spec Part A.1.4 calibration logging.
|
|
128
|
+
*/
|
|
129
|
+
export declare function logWatchdogEvent(status: 'warning' | 'force_salvage', details: WatchdogEventDetails): void;
|
|
130
|
+
//# sourceMappingURL=supervision.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"supervision.d.ts","sourceRoot":"","sources":["../../src/runners/supervision.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,cAAc,EAAE,MAAM,aAAa,CAAC;AAClD,OAAO,KAAK,EAAE,YAAY,EAAE,MAAM,8BAA8B,CAAC;AAEjE;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GA+BG;AAEH,MAAM,MAAM,cAAc,GAAG,OAAO,GAAG,eAAe,GAAG,UAAU,GAAG,eAAe,CAAC;AAEtF,MAAM,WAAW,gBAAgB;IAC/B,KAAK,EAAE,OAAO,CAAC;IACf,IAAI,CAAC,EAAE,cAAc,CAAC;IACtB,MAAM,CAAC,EAAE,MAAM,CAAC;IAChB,qEAAqE;IACrE,IAAI,CAAC,EAAE,MAAM,CAAC;CACf;AAED,MAAM,WAAW,yBAAyB;IACxC,SAAS,CAAC,EAAE,MAAM,CAAC;CACpB;AAUD;;;;;;;GAOG;AACH,eAAO,MAAM,0BAA0B,4FACoD,CAAC;AA+C5F,wBAAgB,kBAAkB,CAChC,IAAI,EAAE,MAAM,EACZ,IAAI,GAAE,yBAA8B,GACnC,gBAAgB,CAiDlB;AAED,wBAAgB,aAAa,CAAC,MAAM,EAAE,gBAAgB,GAAG,MAAM,CAkD9D;AAED;;;;;GAKG;AACH,wBAAgB,oBAAoB,CAAC,CAAC,EAAE,MAAM,EAAE,CAAC,EAAE,MAAM,GAAG,OAAO,CAElE;AAED;;;;;;;;;;;;;;;GAeG;AACH,wBAAgB,0BAA0B,CACxC,MAAM,EAAE,cAAc,EACtB,OAAO,EAAE,YAAY,GACpB,MAAM,CAER;AAED,MAAM,MAAM,cAAc,GAAG,IAAI,GAAG,SAAS,GAAG,eAAe,CAAC;AAEhE;;;;;;;;GAQG;AACH,eAAO,MAAM,sBAAsB,MAAO,CAAC;AAC3C,eAAO,MAAM,4BAA4B,OAAO,CAAC;AAEjD;;;;;;;;;;GAUG;AACH,wBAAgB,sBAAsB,CACpC,qBAAqB,EAAE,MAAM,EAC7B,SAAS,EAAE,MAAM,GAChB,cAAc,CAUhB;AAED,MAAM,WAAW,oBAAoB;IACnC,QAAQ,EAAE,MAAM,CAAC;IACjB,KAAK,EAAE,MAAM,CAAC;IACd,IAAI,EAAE,MAAM,CAAC;IACb,WAAW,EAAE,MAAM,CAAC;IACpB,SAAS,EAAE,MAAM,CAAC;IAClB,eAAe,EAAE,MAAM,CAAC;CACzB;AAED;;;;;;;;;;;;;;;GAeG;AACH,wBAAgB,gBAAgB,CAC9B,MAAM,EAAE,SAAS,GAAG,eAAe,EACnC,OAAO,EAAE,oBAAoB,GAC5B,IAAI,CAgBN"}
|
|
@@ -0,0 +1,238 @@
|
|
|
1
|
+
const DEFAULT_MIN_LENGTH = 200;
|
|
2
|
+
/** Tail window (chars) inspected by `endsWithContinuation` for continuation phrases. */
|
|
3
|
+
const CONTINUATION_TAIL_WINDOW = 80;
|
|
4
|
+
/** Tail length (chars) quoted back to the model in the re-prompt via `result.tail`. */
|
|
5
|
+
const REPROMPT_TAIL_QUOTE = 60;
|
|
6
|
+
/**
|
|
7
|
+
* Marker returned by `stripThinkingTags` when the entire final message was
|
|
8
|
+
* `<think>...</think>` reasoning and stripping left nothing. Exported here
|
|
9
|
+
* (rather than from openai-runner.ts) so that `validateCompletion` can detect
|
|
10
|
+
* the marker without importing the runner, and so other runners can reuse it
|
|
11
|
+
* when they implement their own thinking-only salvage. There is exactly one
|
|
12
|
+
* canonical constant.
|
|
13
|
+
*/
|
|
14
|
+
export const THINKING_DIAGNOSTIC_MARKER = '[model final message contained only <think>...</think> reasoning, no plain-text answer]';
|
|
15
|
+
const CONTINUATION_PHRASES = [
|
|
16
|
+
'let me',
|
|
17
|
+
'let me check',
|
|
18
|
+
'let me read',
|
|
19
|
+
'let me look',
|
|
20
|
+
'next i',
|
|
21
|
+
"i'll continue",
|
|
22
|
+
'i need to',
|
|
23
|
+
"now i'll",
|
|
24
|
+
'i should also',
|
|
25
|
+
'checking',
|
|
26
|
+
"i'll now",
|
|
27
|
+
];
|
|
28
|
+
const FRAGMENT_PUNCTUATION = [':', ',', '…'];
|
|
29
|
+
const TERMINAL_PUNCTUATION = ['.', '!', '?', '`', ')', ']', '}'];
|
|
30
|
+
const MARKDOWN_HINTS = [/^#{1,6} /m, /^- /m, /^\d+\. /m, /```/];
|
|
31
|
+
function endsWithContinuation(tail) {
|
|
32
|
+
const lower = tail.toLowerCase();
|
|
33
|
+
return CONTINUATION_PHRASES.some((p) => lower.endsWith(p) || lower.includes(p));
|
|
34
|
+
}
|
|
35
|
+
function endsWithFragmentPunctuation(text) {
|
|
36
|
+
const trimmed = text.trimEnd();
|
|
37
|
+
return FRAGMENT_PUNCTUATION.some((p) => trimmed.endsWith(p));
|
|
38
|
+
}
|
|
39
|
+
function hasMarkdownStructure(text) {
|
|
40
|
+
return MARKDOWN_HINTS.some((re) => re.test(text));
|
|
41
|
+
}
|
|
42
|
+
function endsWithTerminalPunctuation(text) {
|
|
43
|
+
const trimmed = text.trimEnd();
|
|
44
|
+
if (trimmed.length === 0)
|
|
45
|
+
return false;
|
|
46
|
+
const last = trimmed[trimmed.length - 1];
|
|
47
|
+
return TERMINAL_PUNCTUATION.includes(last);
|
|
48
|
+
}
|
|
49
|
+
// Detector order is most-specific-first: empty → thinking_only → long-enough →
|
|
50
|
+
// markdown → fragment → no_terminator. Markdown precedes fragment so that
|
|
51
|
+
// `Here:\n\`\`\`...\`\`\`` passes as a valid short response; fragment precedes
|
|
52
|
+
// no_terminator because the fragment re-prompt (which quotes the continuation
|
|
53
|
+
// phrase back at the model) is more actionable than the generic no-terminator one.
|
|
54
|
+
export function validateCompletion(text, opts = {}) {
|
|
55
|
+
const minLength = opts.minLength ?? DEFAULT_MIN_LENGTH;
|
|
56
|
+
if (!text || text.trim().length === 0) {
|
|
57
|
+
return { valid: false, kind: 'empty', reason: 'response was empty' };
|
|
58
|
+
}
|
|
59
|
+
if (text.trim() === THINKING_DIAGNOSTIC_MARKER) {
|
|
60
|
+
return {
|
|
61
|
+
valid: false,
|
|
62
|
+
kind: 'thinking_only',
|
|
63
|
+
reason: 'response contained only <think> reasoning content',
|
|
64
|
+
};
|
|
65
|
+
}
|
|
66
|
+
const trimmed = text.trim();
|
|
67
|
+
const tail = trimmed.slice(-CONTINUATION_TAIL_WINDOW);
|
|
68
|
+
// Long enough → trust the response.
|
|
69
|
+
if (trimmed.length >= minLength) {
|
|
70
|
+
return { valid: true };
|
|
71
|
+
}
|
|
72
|
+
// Short responses are valid only if they look complete (terminal punctuation
|
|
73
|
+
// or markdown structure). Without that, they're either fragments or
|
|
74
|
+
// unterminated.
|
|
75
|
+
const hasMarkdown = hasMarkdownStructure(trimmed);
|
|
76
|
+
if (hasMarkdown) {
|
|
77
|
+
return { valid: true };
|
|
78
|
+
}
|
|
79
|
+
if (endsWithFragmentPunctuation(trimmed) || endsWithContinuation(tail)) {
|
|
80
|
+
return {
|
|
81
|
+
valid: false,
|
|
82
|
+
kind: 'fragment',
|
|
83
|
+
reason: 'response is short and ends like an exploration fragment',
|
|
84
|
+
tail: trimmed.slice(-REPROMPT_TAIL_QUOTE),
|
|
85
|
+
};
|
|
86
|
+
}
|
|
87
|
+
if (!endsWithTerminalPunctuation(trimmed)) {
|
|
88
|
+
return {
|
|
89
|
+
valid: false,
|
|
90
|
+
kind: 'no_terminator',
|
|
91
|
+
reason: 'response is short and has no terminal punctuation or markdown structure',
|
|
92
|
+
tail: trimmed.slice(-REPROMPT_TAIL_QUOTE),
|
|
93
|
+
};
|
|
94
|
+
}
|
|
95
|
+
return { valid: true };
|
|
96
|
+
}
|
|
97
|
+
export function buildRePrompt(result) {
|
|
98
|
+
if (result.valid) {
|
|
99
|
+
throw new Error('buildRePrompt called on a valid response — this is a bug');
|
|
100
|
+
}
|
|
101
|
+
switch (result.kind) {
|
|
102
|
+
case 'empty':
|
|
103
|
+
return [
|
|
104
|
+
'Your previous response was empty. You did not produce a final answer to the task',
|
|
105
|
+
'and did not call any tools. Please respond again with your complete final answer',
|
|
106
|
+
'as plain text in this assistant message. The final answer is what gets returned',
|
|
107
|
+
'to the caller — there are no follow-up turns after you produce it. If you are not',
|
|
108
|
+
'yet done, call the tools you need first; otherwise produce the final answer now.',
|
|
109
|
+
].join(' ');
|
|
110
|
+
case 'thinking_only':
|
|
111
|
+
return [
|
|
112
|
+
'Your previous response contained only <think>...</think> reasoning, with no',
|
|
113
|
+
'plain-text answer outside the tags. The reasoning tags are stripped before the',
|
|
114
|
+
'response is returned to the caller, so a thinking-only response is equivalent',
|
|
115
|
+
'to no response at all. Please respond again with your complete final answer as',
|
|
116
|
+
'plain text outside any reasoning tags.',
|
|
117
|
+
].join(' ');
|
|
118
|
+
case 'fragment': {
|
|
119
|
+
const tail = result.tail ?? '';
|
|
120
|
+
return [
|
|
121
|
+
`Your previous response was an exploration fragment (it ended with "${tail}")`,
|
|
122
|
+
'rather than a final answer. You appear to have stopped mid-thought instead of',
|
|
123
|
+
'completing the task. Either: (a) continue exploring by calling the tools you',
|
|
124
|
+
'need, then produce your final answer, or (b) produce your complete final answer',
|
|
125
|
+
'now with whatever you have gathered so far — partial answers are acceptable and',
|
|
126
|
+
'useful, empty responses are not. Your final answer must be a plain-text',
|
|
127
|
+
'assistant message, not a tool call and not a thinking block.',
|
|
128
|
+
].join(' ');
|
|
129
|
+
}
|
|
130
|
+
case 'no_terminator': {
|
|
131
|
+
const tail = result.tail ?? '';
|
|
132
|
+
return [
|
|
133
|
+
`Your previous response appears to have stopped mid-thought (it ended with`,
|
|
134
|
+
`"${tail}"). Please produce your complete final answer with terminal punctuation`,
|
|
135
|
+
'or proper markdown structure. If you are still working, call the tools you need',
|
|
136
|
+
'first; otherwise produce the final answer now.',
|
|
137
|
+
].join(' ');
|
|
138
|
+
}
|
|
139
|
+
default:
|
|
140
|
+
return 'Your previous response was incomplete. Please produce your complete final answer as plain text.';
|
|
141
|
+
}
|
|
142
|
+
}
|
|
143
|
+
/**
|
|
144
|
+
* Compares two consecutive degenerate outputs for byte equality. Used by
|
|
145
|
+
* the supervision loop's same-output early-out: if the model produces
|
|
146
|
+
* identical garbage twice in a row, give up immediately instead of
|
|
147
|
+
* burning the third retry.
|
|
148
|
+
*/
|
|
149
|
+
export function sameDegenerateOutput(a, b) {
|
|
150
|
+
return a.trim() === b.trim();
|
|
151
|
+
}
|
|
152
|
+
/**
|
|
153
|
+
* Resolves the effective inputTokenSoftLimit for a (provider, profile) pair.
|
|
154
|
+
*
|
|
155
|
+
* Precedence: `config.inputTokenSoftLimit` (user override) wins over
|
|
156
|
+
* `profile.inputTokenSoftLimit` (family default).
|
|
157
|
+
*
|
|
158
|
+
* Both fields are Zod-validated upstream:
|
|
159
|
+
* - `ProviderConfig.inputTokenSoftLimit` — `z.number().int().positive().optional()`
|
|
160
|
+
* (see packages/core/src/config/schema.ts)
|
|
161
|
+
* - `ModelProfile.inputTokenSoftLimit` — `z.number().int().positive()` (required)
|
|
162
|
+
*
|
|
163
|
+
* Because profile is guaranteed to carry a positive integer (DEFAULT_PROFILE
|
|
164
|
+
* supplies `100_000` when no prefix matches), there is no hardcoded
|
|
165
|
+
* constant fallback — the DEFAULT_PROFILE value is the de-facto fallback
|
|
166
|
+
* for unprofiled model IDs.
|
|
167
|
+
*/
|
|
168
|
+
export function resolveInputTokenSoftLimit(config, profile) {
|
|
169
|
+
return config.inputTokenSoftLimit ?? profile.inputTokenSoftLimit;
|
|
170
|
+
}
|
|
171
|
+
/**
|
|
172
|
+
* Watchdog threshold ratios (fraction of the resolved `softLimit`).
|
|
173
|
+
* Exported so tests can reference the exact boundary values.
|
|
174
|
+
*
|
|
175
|
+
* - At/above `WATCHDOG_WARNING_RATIO` (80%) the supervision loop nudges
|
|
176
|
+
* the model toward salvage.
|
|
177
|
+
* - At/above `WATCHDOG_FORCE_SALVAGE_RATIO` (95%) the loop is forcibly
|
|
178
|
+
* terminated and the scratchpad is salvaged.
|
|
179
|
+
*/
|
|
180
|
+
export const WATCHDOG_WARNING_RATIO = 0.80;
|
|
181
|
+
export const WATCHDOG_FORCE_SALVAGE_RATIO = 0.95;
|
|
182
|
+
/**
|
|
183
|
+
* Given the cumulative input token usage and the resolved soft limit,
|
|
184
|
+
* returns the watchdog status:
|
|
185
|
+
* - 'ok' below 80%
|
|
186
|
+
* - 'warning' at or above 80%, below 95% (model is nudged)
|
|
187
|
+
* - 'force_salvage' at or above 95% (loop is forcibly terminated)
|
|
188
|
+
*
|
|
189
|
+
* Throws if `softLimit` is not a positive finite number. Runners call
|
|
190
|
+
* this independently, so a silent `'ok'` on a bad limit would mask
|
|
191
|
+
* upstream config bugs.
|
|
192
|
+
*/
|
|
193
|
+
export function checkWatchdogThreshold(cumulativeInputTokens, softLimit) {
|
|
194
|
+
if (!Number.isFinite(softLimit) || !(softLimit > 0)) {
|
|
195
|
+
throw new Error(`checkWatchdogThreshold: softLimit must be a positive finite number, got ${softLimit}`);
|
|
196
|
+
}
|
|
197
|
+
const ratio = cumulativeInputTokens / softLimit;
|
|
198
|
+
if (ratio >= WATCHDOG_FORCE_SALVAGE_RATIO)
|
|
199
|
+
return 'force_salvage';
|
|
200
|
+
if (ratio >= WATCHDOG_WARNING_RATIO)
|
|
201
|
+
return 'warning';
|
|
202
|
+
return 'ok';
|
|
203
|
+
}
|
|
204
|
+
/**
|
|
205
|
+
* Emits a structured log line at watchdog threshold crossings.
|
|
206
|
+
*
|
|
207
|
+
* Gated on the `MULTI_MODEL_DEBUG` environment variable: the function is
|
|
208
|
+
* a no-op unless `process.env.MULTI_MODEL_DEBUG === '1'`. Any other value
|
|
209
|
+
* (including `'true'`, `'yes'`, or unset) suppresses the log.
|
|
210
|
+
*
|
|
211
|
+
* When enabled, a single line is written to `stderr` via `console.error`
|
|
212
|
+
* of the form:
|
|
213
|
+
* `[multi-model-agent] WATCHDOG <status>: provider=… model=… turn=… inputTokens=… softLimit=… percentOfLimit=… [scratchpadChars=…]`
|
|
214
|
+
*
|
|
215
|
+
* `scratchpadChars` is only appended when `status === 'force_salvage'`,
|
|
216
|
+
* since that is the transition where salvage content size matters for
|
|
217
|
+
* calibration. Used for empirical calibration of the 80% / 95%
|
|
218
|
+
* thresholds. See spec Part A.1.4 calibration logging.
|
|
219
|
+
*/
|
|
220
|
+
export function logWatchdogEvent(status, details) {
|
|
221
|
+
if (process.env.MULTI_MODEL_DEBUG !== '1')
|
|
222
|
+
return;
|
|
223
|
+
const percent = Math.round((details.inputTokens / details.softLimit) * 100);
|
|
224
|
+
const parts = [
|
|
225
|
+
`[multi-model-agent] WATCHDOG ${status}:`,
|
|
226
|
+
`provider=${details.provider}`,
|
|
227
|
+
`model=${details.model}`,
|
|
228
|
+
`turn=${details.turn}`,
|
|
229
|
+
`inputTokens=${details.inputTokens}`,
|
|
230
|
+
`softLimit=${details.softLimit}`,
|
|
231
|
+
`percentOfLimit=${percent}`,
|
|
232
|
+
];
|
|
233
|
+
if (status === 'force_salvage') {
|
|
234
|
+
parts.push(`scratchpadChars=${details.scratchpadChars}`);
|
|
235
|
+
}
|
|
236
|
+
console.error(parts.join(' '));
|
|
237
|
+
}
|
|
238
|
+
//# sourceMappingURL=supervision.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"supervision.js","sourceRoot":"","sources":["../../src/runners/supervision.ts"],"names":[],"mappings":"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"}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"claude-adapter.d.ts","sourceRoot":"","sources":["../../src/tools/claude-adapter.ts"],"names":[],"mappings":"AAEA,OAAO,KAAK,EAAE,mBAAmB,EAAE,MAAM,kBAAkB,CAAC;AAC5D,OAAO,KAAK,EAAE,aAAa,EAAE,MAAM,aAAa,CAAC;AAEjD,wBAAgB,sBAAsB,CAAC,IAAI,EAAE,mBAAmB,EAAE,aAAa,GAAE,aAA0B,
|
|
1
|
+
{"version":3,"file":"claude-adapter.d.ts","sourceRoot":"","sources":["../../src/tools/claude-adapter.ts"],"names":[],"mappings":"AAEA,OAAO,KAAK,EAAE,mBAAmB,EAAE,MAAM,kBAAkB,CAAC;AAC5D,OAAO,KAAK,EAAE,aAAa,EAAE,MAAM,aAAa,CAAC;AAEjD,wBAAgB,sBAAsB,CAAC,IAAI,EAAE,mBAAmB,EAAE,aAAa,GAAE,aAA0B,2EA6E1G"}
|
|
@@ -19,9 +19,12 @@ export function createClaudeToolServer(impl, sandboxPolicy = 'cwd-only') {
|
|
|
19
19
|
const files = await impl.glob(pattern);
|
|
20
20
|
return { content: [{ type: 'text', text: files.join('\n') || 'No files found.' }] };
|
|
21
21
|
});
|
|
22
|
-
const grepTool = tool('grep', 'Search for a pattern in a file
|
|
23
|
-
|
|
24
|
-
|
|
22
|
+
const grepTool = tool('grep', 'Search for a regex pattern in a file or directory. When given a directory, ' +
|
|
23
|
+
'recursively searches all files (output is prefixed with file:line). When given ' +
|
|
24
|
+
'a single file, returns matching lines with line numbers. Use this — not multiple ' +
|
|
25
|
+
'readFile calls — to find usages, imports, or patterns across a codebase.', {
|
|
26
|
+
pattern: z.string().describe('Regex pattern to search for'),
|
|
27
|
+
path: z.string().describe('File OR directory path. Directories are searched recursively.'),
|
|
25
28
|
}, async ({ pattern, path }) => {
|
|
26
29
|
const result = await impl.grep(pattern, path);
|
|
27
30
|
return { content: [{ type: 'text', text: result || 'No matches found.' }] };
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"claude-adapter.js","sourceRoot":"","sources":["../../src/tools/claude-adapter.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,IAAI,EAAE,kBAAkB,EAAE,MAAM,gCAAgC,CAAC;AAC1E,OAAO,EAAE,CAAC,EAAE,MAAM,KAAK,CAAC;AAIxB,MAAM,UAAU,sBAAsB,CAAC,IAAyB,EAAE,gBAA+B,UAAU;IACzG,MAAM,QAAQ,GAAG,IAAI,CACnB,WAAW,EACX,2FAA2F,EAC3F,EAAE,IAAI,EAAE,CAAC,CAAC,MAAM,EAAE,CAAC,QAAQ,CAAC,gCAAgC,CAAC,EAAE,EAC/D,KAAK,EAAE,EAAE,IAAI,EAAE,EAAE,EAAE,CAAC,CAAC;QACnB,OAAO,EAAE,CAAC,EAAE,IAAI,EAAE,MAAe,EAAE,IAAI,EAAE,MAAM,IAAI,CAAC,QAAQ,CAAC,IAAI,CAAC,EAAE,CAAC;KACtE,CAAC,CACH,CAAC;IAEF,MAAM,SAAS,GAAG,IAAI,CACpB,YAAY,EACZ,4FAA4F,EAC5F;QACE,IAAI,EAAE,CAAC,CAAC,MAAM,EAAE,CAAC,QAAQ,CAAC,uBAAuB,CAAC;QAClD,OAAO,EAAE,CAAC,CAAC,MAAM,EAAE,CAAC,QAAQ,CAAC,kBAAkB,CAAC;KACjD,EACD,KAAK,EAAE,EAAE,IAAI,EAAE,OAAO,EAAE,EAAE,EAAE;QAC1B,MAAM,IAAI,CAAC,SAAS,CAAC,IAAI,EAAE,OAAO,CAAC,CAAC;QACpC,OAAO,EAAE,OAAO,EAAE,CAAC,EAAE,IAAI,EAAE,MAAe,EAAE,IAAI,EAAE,iBAAiB,IAAI,EAAE,EAAE,CAAC,EAAE,CAAC;IACjF,CAAC,CACF,CAAC;IAEF,MAAM,QAAQ,GAAG,IAAI,CACnB,WAAW,EACX,oHAAoH,EACpH,EAAE,OAAO,EAAE,CAAC,CAAC,MAAM,EAAE,CAAC,QAAQ,CAAC,0BAA0B,CAAC,EAAE,EAC5D,KAAK,EAAE,EAAE,OAAO,EAAE,EAAE,EAAE;QACpB,MAAM,MAAM,GAAG,MAAM,IAAI,CAAC,QAAQ,CAAC,OAAO,CAAC,CAAC;QAC5C,OAAO,EAAE,OAAO,EAAE,CAAC,EAAE,IAAI,EAAE,MAAe,EAAE,IAAI,EAAE,IAAI,CAAC,SAAS,CAAC,MAAM,CAAC,EAAE,CAAC,EAAE,CAAC;IAChF,CAAC,CACF,CAAC;IAEF,MAAM,QAAQ,GAAG,IAAI,CACnB,MAAM,EACN,8DAA8D,EAC9D,EAAE,OAAO,EAAE,CAAC,CAAC,MAAM,EAAE,CAAC,QAAQ,CAAC,4CAA4C,CAAC,EAAE,EAC9E,KAAK,EAAE,EAAE,OAAO,EAAE,EAAE,EAAE;QACpB,MAAM,KAAK,GAAG,MAAM,IAAI,CAAC,IAAI,CAAC,OAAO,CAAC,CAAC;QACvC,OAAO,EAAE,OAAO,EAAE,CAAC,EAAE,IAAI,EAAE,MAAe,EAAE,IAAI,EAAE,KAAK,CAAC,IAAI,CAAC,IAAI,CAAC,IAAI,iBAAiB,EAAE,CAAC,EAAE,CAAC;IAC/F,CAAC,CACF,CAAC;IAEF,MAAM,QAAQ,GAAG,IAAI,CACnB,MAAM,EACN,
|
|
1
|
+
{"version":3,"file":"claude-adapter.js","sourceRoot":"","sources":["../../src/tools/claude-adapter.ts"],"names":[],"mappings":"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"}
|
|
@@ -2,6 +2,8 @@ import type { FileTracker } from './tracker.js';
|
|
|
2
2
|
import type { SandboxPolicy } from '../types.js';
|
|
3
3
|
export declare const MAX_READ_FILE_BYTES: number;
|
|
4
4
|
export declare const MAX_WRITE_FILE_BYTES: number;
|
|
5
|
+
export declare const MAX_GREP_OUTPUT_BYTES: number;
|
|
6
|
+
export declare const GREP_CHILD_BUFFER_BYTES: number;
|
|
5
7
|
export interface ShellResult {
|
|
6
8
|
stdout: string;
|
|
7
9
|
stderr: string;
|
|
@@ -12,7 +14,7 @@ export interface ToolImplementations {
|
|
|
12
14
|
writeFile(filePath: string, content: string): Promise<void>;
|
|
13
15
|
runShell(command: string): Promise<ShellResult>;
|
|
14
16
|
glob(pattern: string): Promise<string[]>;
|
|
15
|
-
grep(pattern: string,
|
|
17
|
+
grep(pattern: string, target: string): Promise<string>;
|
|
16
18
|
listFiles(dirPath: string): Promise<string[]>;
|
|
17
19
|
}
|
|
18
20
|
export declare function createToolImplementations(tracker: FileTracker, cwd: string, sandboxPolicy?: SandboxPolicy, signal?: AbortSignal): ToolImplementations;
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"definitions.d.ts","sourceRoot":"","sources":["../../src/tools/definitions.ts"],"names":[],"mappings":"AAKA,OAAO,KAAK,EAAE,WAAW,EAAE,MAAM,cAAc,CAAC;AAChD,OAAO,KAAK,EAAE,aAAa,EAAE,MAAM,aAAa,CAAC;
|
|
1
|
+
{"version":3,"file":"definitions.d.ts","sourceRoot":"","sources":["../../src/tools/definitions.ts"],"names":[],"mappings":"AAKA,OAAO,KAAK,EAAE,WAAW,EAAE,MAAM,cAAc,CAAC;AAChD,OAAO,KAAK,EAAE,aAAa,EAAE,MAAM,aAAa,CAAC;AAoBjD,eAAO,MAAM,mBAAmB,QAAmB,CAAC;AACpD,eAAO,MAAM,oBAAoB,QAAoB,CAAC;AACtD,eAAO,MAAM,qBAAqB,QAAa,CAAC;AAChD,eAAO,MAAM,uBAAuB,QAAkB,CAAC;AA6BvD,MAAM,WAAW,WAAW;IAC1B,MAAM,EAAE,MAAM,CAAC;IACf,MAAM,EAAE,MAAM,CAAC;IACf,QAAQ,EAAE,MAAM,CAAC;CAClB;AAED,MAAM,WAAW,mBAAmB;IAClC,QAAQ,CAAC,QAAQ,EAAE,MAAM,GAAG,OAAO,CAAC,MAAM,CAAC,CAAC;IAC5C,SAAS,CAAC,QAAQ,EAAE,MAAM,EAAE,OAAO,EAAE,MAAM,GAAG,OAAO,CAAC,IAAI,CAAC,CAAC;IAC5D,QAAQ,CAAC,OAAO,EAAE,MAAM,GAAG,OAAO,CAAC,WAAW,CAAC,CAAC;IAChD,IAAI,CAAC,OAAO,EAAE,MAAM,GAAG,OAAO,CAAC,MAAM,EAAE,CAAC,CAAC;IACzC,IAAI,CAAC,OAAO,EAAE,MAAM,EAAE,MAAM,EAAE,MAAM,GAAG,OAAO,CAAC,MAAM,CAAC,CAAC;IACvD,SAAS,CAAC,OAAO,EAAE,MAAM,GAAG,OAAO,CAAC,MAAM,EAAE,CAAC,CAAC;CAC/C;AAED,wBAAgB,yBAAyB,CACvC,OAAO,EAAE,WAAW,EACpB,GAAG,EAAE,MAAM,EACX,aAAa,GAAE,aAA0B,EACzC,MAAM,CAAC,EAAE,WAAW,GACnB,mBAAmB,CAwJrB"}
|