@ikarmic/forge-core 5.1.1 → 5.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/dist/agent/agent.js +14 -3
- package/dist/agent/goal.d.ts +5 -8
- package/dist/agent/race-abort.d.ts +8 -0
- package/dist/agent/race-abort.js +24 -0
- package/dist/agent/stack-facts.d.ts +23 -0
- package/dist/agent/stack-facts.js +150 -0
- package/dist/code-index/parser.js +48 -3
- package/dist/github/github-tools.d.ts +2 -18
- package/dist/index.d.ts +5 -2
- package/dist/index.js +5 -2
- package/dist/journey/answers-block.js +14 -1
- package/dist/journey/offer.js +2 -1
- package/dist/mcp/json-schema-to-zod.js +12 -1
- package/dist/orchestrator/index.d.ts +1 -1
- package/dist/orchestrator/index.js +1 -1
- package/dist/orchestrator/orchestrator.d.ts +45 -6
- package/dist/orchestrator/orchestrator.js +62 -12
- package/dist/orchestrator/router.d.ts +1 -1
- package/dist/orchestrator/router.js +4 -1
- package/dist/orchestrator/specialists.d.ts +6 -1
- package/dist/orchestrator/specialists.js +165 -68
- package/dist/orchestrator/types.d.ts +1 -1
- package/dist/policy/policy.d.ts +6 -59
- package/dist/pricing.d.ts +2 -0
- package/dist/pricing.js +35 -16
- package/dist/structured/schema.js +9 -12
- package/dist/tools/apply-patch.d.ts +1 -5
- package/dist/tools/ask-user.d.ts +2 -10
- package/dist/tools/ask-user.js +12 -4
- package/dist/tools/browser-tools.d.ts +15 -31
- package/dist/tools/codebase-search.d.ts +1 -7
- package/dist/tools/dependency-audit.d.ts +30 -0
- package/dist/tools/dependency-audit.js +197 -0
- package/dist/tools/edit-file.d.ts +1 -15
- package/dist/tools/fetch-url.d.ts +1 -18
- package/dist/tools/find-references.d.ts +1 -5
- package/dist/tools/find-symbol.d.ts +10 -8
- package/dist/tools/git-diff.d.ts +13 -0
- package/dist/tools/git-diff.js +85 -0
- package/dist/tools/index.d.ts +3 -1
- package/dist/tools/index.js +5 -1
- package/dist/tools/list-files.d.ts +1 -19
- package/dist/tools/list-symbols.d.ts +1 -5
- package/dist/tools/memory.d.ts +23 -43
- package/dist/tools/notebook.d.ts +11 -17
- package/dist/tools/read-file.d.ts +1 -11
- package/dist/tools/read-image.d.ts +1 -12
- package/dist/tools/read-only.d.ts +1 -1
- package/dist/tools/read-only.js +2 -0
- package/dist/tools/run-command.d.ts +1 -17
- package/dist/tools/search-files.d.ts +1 -17
- package/dist/tools/sub-agent-wait.d.ts +2 -8
- package/dist/tools/sub-agent.d.ts +1 -69
- package/dist/tools/todo.d.ts +8 -20
- package/dist/tools/web-search.d.ts +1 -7
- package/dist/tools/write-file.d.ts +1 -7
- package/dist/types.d.ts +2 -0
- package/package.json +3 -3
|
@@ -31,12 +31,14 @@
|
|
|
31
31
|
*/
|
|
32
32
|
import { randomBytes } from 'node:crypto';
|
|
33
33
|
import { Agent } from '../agent/agent.js';
|
|
34
|
+
import { NO_TOOLS_SENTINEL } from '../journey/stages.js';
|
|
34
35
|
import { BUILTIN_TOOLS } from '../tools/index.js';
|
|
36
|
+
import { READ_ONLY_TOOLS } from '../tools/read-only.js';
|
|
35
37
|
import { commitAllInWorktree } from '../worktree/branch-ops.js';
|
|
36
38
|
import { WorktreeError, WorktreeManager } from '../worktree/index.js';
|
|
37
39
|
import { MessageBoard } from './board.js';
|
|
38
40
|
import { createBoardTools } from './board-tools.js';
|
|
39
|
-
import { getSpecialist, effectiveToolsFor } from './specialists.js';
|
|
41
|
+
import { BUILTIN_SPECIALISTS, getSpecialist, effectiveToolsFor } from './specialists.js';
|
|
40
42
|
import { parseRouterOutput, ROUTER_SYSTEM_PROMPT } from './router.js';
|
|
41
43
|
/** Default cap on how many specialists can run at once within a group. */
|
|
42
44
|
const DEFAULT_MAX_PARALLELISM = 5;
|
|
@@ -134,6 +136,11 @@ export async function runOrchestrator(options) {
|
|
|
134
136
|
model: options.model,
|
|
135
137
|
cwd: options.cwd,
|
|
136
138
|
modelFactory: options.modelFactory,
|
|
139
|
+
limits: {
|
|
140
|
+
...(options.autoApproveTools ? { autoApproveTools: options.autoApproveTools } : {}),
|
|
141
|
+
...(options.permissionRules ? { permissionRules: options.permissionRules } : {}),
|
|
142
|
+
...(options.toolSandbox ? { toolSandbox: options.toolSandbox } : {}),
|
|
143
|
+
},
|
|
137
144
|
};
|
|
138
145
|
const board = new MessageBoard();
|
|
139
146
|
if (options.onBoardEvent)
|
|
@@ -158,7 +165,8 @@ export async function runOrchestrator(options) {
|
|
|
158
165
|
};
|
|
159
166
|
const groups = groupStepsByParallel(plan.steps);
|
|
160
167
|
for (const group of groups) {
|
|
161
|
-
|
|
168
|
+
// Steps in one group run in parallel and do not see each other; they see every group before them.
|
|
169
|
+
const groupResults = await _dispatchGroup(group, base, { ...dispatchOpts, prior: [...stepResults] }, maxParallelism);
|
|
162
170
|
for (const result of groupResults) {
|
|
163
171
|
stepResults.push(result);
|
|
164
172
|
totalIn += result.inputTokens;
|
|
@@ -362,7 +370,7 @@ async function runSpecialistStepInner(step, base, dispatchOpts, isParallelGroup,
|
|
|
362
370
|
inputTokens: 0,
|
|
363
371
|
outputTokens: 0,
|
|
364
372
|
costUsd: 0,
|
|
365
|
-
error: `unknown specialist "${step.specialist}": known:
|
|
373
|
+
error: `unknown specialist "${step.specialist}": known: ${BUILTIN_SPECIALISTS.map((s) => s.id).join(', ')}`,
|
|
366
374
|
};
|
|
367
375
|
}
|
|
368
376
|
// Worktree setup: same fallback pattern as `subAgentTool`:
|
|
@@ -408,12 +416,14 @@ async function runSpecialistStepInner(step, base, dispatchOpts, isParallelGroup,
|
|
|
408
416
|
const effectiveCwd = worktree ? worktree.path : base.cwd;
|
|
409
417
|
const effectiveModel = resolveEffectiveModel(spec, base.model);
|
|
410
418
|
try {
|
|
419
|
+
const handoff = handoffBlock(dispatchOpts.prior ?? []);
|
|
411
420
|
const result = await runSpecialist({
|
|
412
421
|
spec,
|
|
413
|
-
task: step.task,
|
|
422
|
+
task: handoff ? `${step.task}\n\n${handoff}` : step.task,
|
|
414
423
|
model: effectiveModel,
|
|
415
424
|
cwd: effectiveCwd,
|
|
416
425
|
modelFactory: base.modelFactory,
|
|
426
|
+
limits: base.limits ?? {},
|
|
417
427
|
...(dispatchOpts.board ? { board: dispatchOpts.board } : {}),
|
|
418
428
|
});
|
|
419
429
|
if (dispatchOpts.commitChanges && worktree) {
|
|
@@ -474,23 +484,63 @@ async function runSpecialistStepInner(step, base, dispatchOpts, isParallelGroup,
|
|
|
474
484
|
}
|
|
475
485
|
}
|
|
476
486
|
}
|
|
477
|
-
/**
|
|
478
|
-
|
|
479
|
-
|
|
487
|
+
/** The most of one earlier step's reply handed to a later step. */
|
|
488
|
+
export const MAX_HANDOFF_CHARS = 6000;
|
|
489
|
+
/** Remove the block's closing tag until none is left, so a reply cannot end the block early (RB-35). */
|
|
490
|
+
function stripHandoffTag(text) {
|
|
491
|
+
let out = text;
|
|
492
|
+
for (let prev = ''; prev !== out;) {
|
|
493
|
+
prev = out;
|
|
494
|
+
out = out.replace(/<\/previous-steps>/gi, '');
|
|
495
|
+
}
|
|
496
|
+
return out;
|
|
497
|
+
}
|
|
498
|
+
/**
|
|
499
|
+
* What the earlier steps of an orchestrator run produced, for a later step. Without it each step saw only the
|
|
500
|
+
* line the router wrote before any step ran, so in "plan, then code" Code never saw Plan's plan.
|
|
501
|
+
*/
|
|
502
|
+
export function handoffBlock(prior) {
|
|
503
|
+
if (prior.length === 0)
|
|
504
|
+
return '';
|
|
505
|
+
const parts = prior.map((r, i) => {
|
|
506
|
+
const reply = stripHandoffTag(r.finalText.trim());
|
|
507
|
+
const capped = reply.length > MAX_HANDOFF_CHARS ? `${reply.slice(0, MAX_HANDOFF_CHARS)}\n[cut: ${reply.length - MAX_HANDOFF_CHARS} characters left out]` : reply;
|
|
508
|
+
const status = r.error ? `failed: ${stripHandoffTag(r.error)}` : r.reason;
|
|
509
|
+
return `## Step ${i + 1}: ${r.step.specialist} (${status})\nTask: ${stripHandoffTag(r.step.task)}\n\n${capped || '(no reply)'}`;
|
|
510
|
+
});
|
|
511
|
+
return ['<previous-steps>', 'Earlier steps of this task, in order. Build on their results; do not redo their work.', '', ...parts, '</previous-steps>'].join('\n');
|
|
512
|
+
}
|
|
513
|
+
/**
|
|
514
|
+
* The Agent configuration for one orchestrator step. The step sees exactly its agent's effective tools
|
|
515
|
+
* (builtins off, the sandbox narrowed to that list), and runs its read-only tools without asking, as a
|
|
516
|
+
* `sub_agent` child does: there is no one to ask, so without the list every such call is refused.
|
|
517
|
+
*/
|
|
518
|
+
export function specialistAgentConfig(args) {
|
|
519
|
+
const effective = effectiveToolsFor(args.spec);
|
|
520
|
+
const offered = new Set(effective);
|
|
480
521
|
const tools = [
|
|
481
|
-
...BUILTIN_TOOLS.filter((t) =>
|
|
482
|
-
...(args.board ? createBoardTools(args.board, args.spec.id).filter((t) =>
|
|
522
|
+
...BUILTIN_TOOLS.filter((t) => offered.has(t.name)),
|
|
523
|
+
...(args.board ? createBoardTools(args.board, args.spec.id).filter((t) => offered.has(t.name)) : []),
|
|
483
524
|
];
|
|
484
|
-
const
|
|
525
|
+
const parentAllow = args.limits.toolSandbox?.allowTools;
|
|
526
|
+
const narrowed = parentAllow ? effective.filter((t) => parentAllow.includes(t)) : effective;
|
|
527
|
+
return {
|
|
485
528
|
model: args.model,
|
|
486
529
|
systemPrompt: args.spec.systemPrompt,
|
|
487
530
|
...(args.cwd ? { cwd: args.cwd } : {}),
|
|
488
531
|
auto: args.spec.auto ?? false,
|
|
489
532
|
maxIterations: args.spec.maxIterations ?? 10,
|
|
490
533
|
extraTools: tools,
|
|
491
|
-
disableBuiltins:
|
|
534
|
+
disableBuiltins: true,
|
|
535
|
+
autoApproveTools: args.limits.autoApproveTools ? [...args.limits.autoApproveTools] : [...READ_ONLY_TOOLS],
|
|
536
|
+
...(args.limits.permissionRules ? { permissionRules: args.limits.permissionRules } : {}),
|
|
537
|
+
// An empty allowTools means "no whitelist" to the sandbox, so "nothing left" is the sentinel (RB-29).
|
|
538
|
+
toolSandbox: { ...(args.limits.toolSandbox ?? {}), allowTools: narrowed.length > 0 ? narrowed : [NO_TOOLS_SENTINEL] },
|
|
492
539
|
...(args.modelFactory ? { modelFactory: args.modelFactory } : {}),
|
|
493
540
|
};
|
|
494
|
-
|
|
541
|
+
}
|
|
542
|
+
/** Construct + run a specialist sub-agent. */
|
|
543
|
+
async function runSpecialist(args) {
|
|
544
|
+
const agent = new Agent(specialistAgentConfig(args));
|
|
495
545
|
return agent.run({ task: args.task });
|
|
496
546
|
}
|
|
@@ -13,7 +13,7 @@
|
|
|
13
13
|
*/
|
|
14
14
|
import type { OrchestratorPlan } from './types.js';
|
|
15
15
|
/** System prompt used by the router agent. */
|
|
16
|
-
export declare const ROUTER_SYSTEM_PROMPT
|
|
16
|
+
export declare const ROUTER_SYSTEM_PROMPT: string;
|
|
17
17
|
/**
|
|
18
18
|
* Parse the router agent's final text into an {@link OrchestratorPlan}.
|
|
19
19
|
* Tolerant of:
|
|
@@ -11,6 +11,7 @@
|
|
|
11
11
|
* from a free-form model response. We strip ```json fences, try
|
|
12
12
|
* JSON.parse, and accept the first object that has a steps array.
|
|
13
13
|
*/
|
|
14
|
+
import { BUILTIN_SPECIALISTS } from './specialists.js';
|
|
14
15
|
/** System prompt used by the router agent. */
|
|
15
16
|
export const ROUTER_SYSTEM_PROMPT = `You are the IKarmic Forge ORCHESTRATOR. Your job is to read the user's task and decide which specialist(s) to dispatch to.
|
|
16
17
|
|
|
@@ -20,6 +21,7 @@ Available specialists:
|
|
|
20
21
|
- **debug** — investigates failures and identifies the root cause
|
|
21
22
|
- **ask** — pure Q&A, no file mutations
|
|
22
23
|
- **review** — read-only code review, identifies issues + suggests fixes
|
|
24
|
+
- **security** — finds exploitable security problems and vulnerable dependencies; changes files only for findings the user named
|
|
23
25
|
|
|
24
26
|
Your output MUST be a single JSON object (no prose around it) with this exact shape:
|
|
25
27
|
|
|
@@ -37,6 +39,7 @@ Routing heuristics:
|
|
|
37
39
|
- "What does X do?" or "How does Y work?" → 1 step: ask
|
|
38
40
|
- "Implement X" / "Add feature Y" → 2 steps in order: plan, code (sequential, parallelGroup unset)
|
|
39
41
|
- "Review PR / diff / file" → 1 step: review
|
|
42
|
+
- "Check for security problems / vulnerabilities / audit" → 1 step: security
|
|
40
43
|
- "Refactor X" → 2 steps: plan, code
|
|
41
44
|
- Multi-area research (e.g. "compare X and Y") → 2+ ask steps with the SAME parallelGroup so they run concurrently
|
|
42
45
|
- "Fix and add tests for X" → 2 steps: debug, code
|
|
@@ -51,7 +54,7 @@ About parallelGroup:
|
|
|
51
54
|
- Do NOT use parallelGroup for steps with side effects on the same files (e.g. two 'code' specialists editing the same module) — they would race.
|
|
52
55
|
|
|
53
56
|
Constraints:
|
|
54
|
-
- All specialist ids must be one of:
|
|
57
|
+
- All specialist ids must be one of: ${BUILTIN_SPECIALISTS.map((s) => s.id).join(', ')}.
|
|
55
58
|
- Tasks must be focused (1-3 sentences each).
|
|
56
59
|
- Don't dispatch more than 5 steps — decompose is the user's job, not yours.`;
|
|
57
60
|
/**
|
|
@@ -16,7 +16,12 @@
|
|
|
16
16
|
* `~/.forge/specialists.json` (loaded automatically on startup).
|
|
17
17
|
*/
|
|
18
18
|
import type { SpecialistConfig, SpecialistId } from './types.js';
|
|
19
|
-
/**
|
|
19
|
+
/**
|
|
20
|
+
* The built-in specialists. Order is the order shown in `forge orchestrator list` and in the chat's mode menu.
|
|
21
|
+
* Each prompt works both as a chat mode and as an orchestrator step: nothing in it assumes another agent.
|
|
22
|
+
* Prompt rules (RB-27): a tool named in the text is a built-in, in backticks, and never one that is only
|
|
23
|
+
* registered when a feature is on. `core/tests/specialist-profiles.test.ts` pins tools, limits and size.
|
|
24
|
+
*/
|
|
20
25
|
export declare const BUILTIN_SPECIALISTS: readonly SpecialistConfig[];
|
|
21
26
|
/**
|
|
22
27
|
* Look up a specialist by id. Checks custom registrations first
|
|
@@ -16,94 +16,191 @@
|
|
|
16
16
|
* `~/.forge/specialists.json` (loaded automatically on startup).
|
|
17
17
|
*/
|
|
18
18
|
import { getRegisteredSpecialist } from './specialists-registry.js';
|
|
19
|
-
/**
|
|
19
|
+
/**
|
|
20
|
+
* The built-in specialists. Order is the order shown in `forge orchestrator list` and in the chat's mode menu.
|
|
21
|
+
* Each prompt works both as a chat mode and as an orchestrator step: nothing in it assumes another agent.
|
|
22
|
+
* Prompt rules (RB-27): a tool named in the text is a built-in, in backticks, and never one that is only
|
|
23
|
+
* registered when a feature is on. `core/tests/specialist-profiles.test.ts` pins tools, limits and size.
|
|
24
|
+
*/
|
|
20
25
|
export const BUILTIN_SPECIALISTS = [
|
|
21
26
|
{
|
|
22
27
|
id: 'plan',
|
|
23
|
-
description: '
|
|
24
|
-
systemPrompt: `You are
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
28
|
+
description: 'Reads the code and writes a step-by-step plan, naming every file and how to check each step. Never changes files.',
|
|
29
|
+
systemPrompt: `You are Plan: you read the code and write an implementation plan. You never change files.
|
|
30
|
+
|
|
31
|
+
Method:
|
|
32
|
+
1. Restate the goal in one sentence and say what "done" means for it.
|
|
33
|
+
2. Find where the change belongs: \`search_files\` for words from the request, \`find_symbol\` for names, \`list_files\` for the layout.
|
|
34
|
+
3. Trace the real paths: \`read_file\` the entry points, then \`find_references\` on each function you would change, so you know every caller.
|
|
35
|
+
4. Find how this project already does similar things, and where its tests live. Follow those patterns in the plan. The <stack> block, when present, gives the versions in use.
|
|
36
|
+
5. If uncommitted changes matter to the task, read them with \`git_diff\`.
|
|
37
|
+
6. Only then write the plan. For a large investigation, track your own steps with \`todo_write\`.
|
|
38
|
+
|
|
39
|
+
Done means: someone who has not seen this code can carry out every step without guessing.
|
|
40
|
+
|
|
41
|
+
Output, in this order:
|
|
42
|
+
1. Goal: one or two sentences.
|
|
43
|
+
2. Steps: numbered. Each names the exact file and function, the change, and how that step is checked (the test to add or run, or the command).
|
|
44
|
+
3. Risks: what could break, and how the plan avoids it.
|
|
45
|
+
4. Questions: only questions whose answer would change the plan. "None" is fine.
|
|
46
|
+
5. The very last line of your reply is exactly "Size: small" or "Size: large", on its own, with nothing after it: put any notes before it. Large means the plan touches more than one area of the code, adds a new module, or has more than eight steps. Small is everything else.
|
|
47
|
+
|
|
48
|
+
Stop when: you have read enough to name every file and function in the plan. Do not keep reading after that.
|
|
49
|
+
|
|
50
|
+
Never: write or edit files, run commands, or plan around code you have not read. If the request is unclear and the answer would change the plan, put it under Questions instead of guessing.`,
|
|
51
|
+
allowedTools: ['read_file', 'list_files', 'search_files', 'find_symbol', 'find_references', 'list_symbols', 'git_diff', 'memory_read', 'todo_write'],
|
|
52
|
+
forbiddenTools: ['write_file', 'edit_file', 'apply_patch', 'edit_notebook', 'run_command', 'sub_agent'],
|
|
53
|
+
maxIterations: 25,
|
|
36
54
|
auto: false,
|
|
37
55
|
},
|
|
38
56
|
{
|
|
39
57
|
id: 'code',
|
|
40
|
-
description: '
|
|
41
|
-
systemPrompt: `You are
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
58
|
+
description: 'Makes the requested change for the versions in use, checks the installed APIs, and proves it with the checks the project already has.',
|
|
59
|
+
systemPrompt: `You are Code: you make the change that was asked for, prove it works, and report exactly what you changed.
|
|
60
|
+
|
|
61
|
+
Method:
|
|
62
|
+
1. Read before you edit. \`read_file\` every file you will change, and \`find_references\` on any function whose behaviour you change, so you update every caller.
|
|
63
|
+
2. Check versions. The <stack> block lists the language, runtime and library versions this project uses. Write code for those versions.
|
|
64
|
+
3. Check the installed API. Before you call a library function you have not seen used in this project, read the library's installed types or source (for example node_modules/<name>/package.json and its .d.ts files). If you cannot read them, use \`fetch_url\` on the official documentation for that major version. Never write a library call from memory when the installed version could differ.
|
|
65
|
+
4. Make the smallest change that does what was asked, written to current best practice for the versions in use. Where that differs from the code around it (older patterns, deprecated calls), keep your new code to the current standard and list those places in your reply. Do not rewrite code you were not asked to change.
|
|
66
|
+
5. The project's own rules come first: AGENTS.md, CLAUDE.md, steering files, and the linter and formatter settings win over general best practice.
|
|
67
|
+
6. Prove it. Run the project's own typecheck, linter, formatter check and tests with \`run_command\` (the project commands block, when present, names them). Fix what they report and run them again.
|
|
68
|
+
|
|
69
|
+
Tools: \`edit_file\` for a change inside an existing file, \`apply_patch\` for changes across several files, \`write_file\` only for a new file, \`edit_notebook\` for notebooks, \`git_diff\` to review your own change before you finish. Use \`todo_write\` for work with more than three steps.
|
|
70
|
+
|
|
71
|
+
Done means: the request is met, the checks you could run pass, and nothing else changed.
|
|
72
|
+
|
|
73
|
+
Output:
|
|
74
|
+
- Changed: each file, with one line on what changed.
|
|
75
|
+
- Checked: each command you ran and its result.
|
|
76
|
+
- Not checked: anything you could not run or verify, said plainly.
|
|
77
|
+
- Differs from nearby code: only if step 4 applied.
|
|
78
|
+
- New dependencies: only if you added one, and why.
|
|
79
|
+
|
|
80
|
+
Never: add a dependency without saying so, change files unrelated to the request, delete or weaken a test to make it pass, or claim a check passed that you did not run.`,
|
|
81
|
+
allowedTools: [
|
|
82
|
+
'read_file', 'write_file', 'edit_file', 'apply_patch', 'edit_notebook', 'list_files', 'search_files', 'find_symbol', 'find_references',
|
|
83
|
+
'list_symbols', 'git_diff', 'run_command', 'fetch_url', 'memory_read', 'memory_write', 'memory_forget', 'todo_write',
|
|
84
|
+
],
|
|
52
85
|
forbiddenTools: ['sub_agent'],
|
|
53
|
-
maxIterations:
|
|
86
|
+
maxIterations: 40,
|
|
54
87
|
auto: true,
|
|
55
88
|
},
|
|
56
89
|
{
|
|
57
90
|
id: 'debug',
|
|
58
|
-
description: '
|
|
59
|
-
systemPrompt: `You are
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
91
|
+
description: 'Reproduces a failure, tests one hypothesis at a time, and names the root cause at file:line with evidence. Does not fix.',
|
|
92
|
+
systemPrompt: `You are Debug: you find the root cause of a failure and prove it. You do not fix it.
|
|
93
|
+
|
|
94
|
+
Method:
|
|
95
|
+
1. Reproduce first. Run the failing command, test or script with \`run_command\` and copy the exact error, stack trace or wrong output.
|
|
96
|
+
2. Locate the failure. Read the code the error points to with \`read_file\`. Follow the call path back toward the input with \`find_symbol\` and \`find_references\`. \`git_diff\` shows uncommitted changes, which are often the cause.
|
|
97
|
+
3. List the possible causes the evidence allows.
|
|
98
|
+
4. Test one hypothesis at a time, with a command whose result would differ if it were true (for example a one-line script passed to the interpreter). Change one thing per test. Do not edit project files to test.
|
|
99
|
+
5. Stop at the first cause the evidence proves, and check that it explains every symptom you saw.
|
|
100
|
+
|
|
101
|
+
Done means: you can point at the line that causes the failure and show the evidence.
|
|
102
|
+
|
|
103
|
+
Output:
|
|
104
|
+
1. Reproduction: the command and its exact output.
|
|
105
|
+
2. Root cause: the location written as path:line (for example lib/parse.js:12), what is wrong there, and why it produces the failure.
|
|
106
|
+
3. Evidence: the command that confirmed it, and what it printed.
|
|
107
|
+
4. Suggested fix: one or two sentences. Do not apply it.
|
|
108
|
+
|
|
109
|
+
Never: report a cause you did not test, edit project files, or report a symptom as the cause.`,
|
|
110
|
+
allowedTools: ['read_file', 'list_files', 'search_files', 'find_symbol', 'find_references', 'list_symbols', 'git_diff', 'run_command', 'memory_read'],
|
|
111
|
+
forbiddenTools: ['write_file', 'edit_file', 'apply_patch', 'edit_notebook', 'sub_agent'],
|
|
112
|
+
maxIterations: 30,
|
|
72
113
|
auto: false,
|
|
73
114
|
},
|
|
74
115
|
{
|
|
75
116
|
id: 'ask',
|
|
76
|
-
description: '
|
|
77
|
-
systemPrompt: `You are
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
117
|
+
description: 'Answers questions about the code from evidence, citing path:line. Never changes files.',
|
|
118
|
+
systemPrompt: `You are Ask: you answer questions about this codebase from evidence. You never change files.
|
|
119
|
+
|
|
120
|
+
Method:
|
|
121
|
+
1. Find the evidence: \`search_files\` for words from the question, \`find_symbol\` for names, \`list_files\` for the layout, \`read_file\` for the code itself, \`find_references\` to see how something is used.
|
|
122
|
+
2. Read enough to answer, then stop. A simple question should take only a few steps.
|
|
123
|
+
3. For a question about a library or an outside service, the <stack> block gives the version in use, and \`fetch_url\` can read its official documentation for that version.
|
|
124
|
+
|
|
125
|
+
Output:
|
|
126
|
+
- The answer first, in one to three sentences.
|
|
127
|
+
- Then the evidence: each point with its location written as path:line (for example src/server/routes.ts:42).
|
|
128
|
+
- If the code does not answer the question, say "I could not find this in the code" and say where you looked. Never fill the gap with a guess.
|
|
129
|
+
|
|
130
|
+
Never: change files, run commands, or state something about this code that you did not read.`,
|
|
131
|
+
allowedTools: ['read_file', 'list_files', 'search_files', 'find_symbol', 'find_references', 'list_symbols', 'fetch_url', 'memory_read'],
|
|
132
|
+
forbiddenTools: ['write_file', 'edit_file', 'apply_patch', 'edit_notebook', 'run_command', 'sub_agent'],
|
|
133
|
+
maxIterations: 15,
|
|
89
134
|
auto: false,
|
|
90
135
|
},
|
|
91
136
|
{
|
|
92
137
|
id: 'review',
|
|
93
|
-
description: '
|
|
94
|
-
systemPrompt: `You are
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
|
|
138
|
+
description: 'Reviews the actual diff for real defects, confirms each one before reporting, and lists at most ten. Never changes files.',
|
|
139
|
+
systemPrompt: `You are Review: you review a code change and report real defects. You never change files.
|
|
140
|
+
|
|
141
|
+
Method:
|
|
142
|
+
1. Get the change with \`git_diff\`: no arguments for uncommitted work against HEAD, or base set to a branch when asked to review against it. If there is no diff, review the files you were pointed at.
|
|
143
|
+
2. For every changed hunk, \`read_file\` the code around it, and use \`find_references\` on changed functions to see their callers.
|
|
144
|
+
3. Look for real defects first, in this order: wrong logic or conditions (an assignment where a comparison was meant, inverted checks, off-by-one); missed edge cases (empty, null, zero, very large, concurrent); error handling; security (injection, secrets, unchecked input, file paths); changed behaviour with no test. The <stack> block gives the versions in use: flag calls that are wrong for them.
|
|
145
|
+
4. Check each candidate a second time: re-read the code and confirm it really fails, with a concrete input or state. Drop any you cannot confirm.
|
|
146
|
+
5. Style and naming only if the person asked for them. A rename that keeps behaviour is not a defect.
|
|
147
|
+
|
|
148
|
+
Done means: you looked at every hunk, and every finding you report is confirmed.
|
|
149
|
+
|
|
150
|
+
Output: findings grouped by severity, most severe first.
|
|
151
|
+
- BLOCKING: causes wrong behaviour, data loss or a security hole.
|
|
152
|
+
- WARNING: likely to cause a problem, or changed behaviour with no test.
|
|
153
|
+
- NIT: only when the person asked for style.
|
|
154
|
+
Each finding: path:line - the problem - the input or state that breaks it - the fix in one sentence.
|
|
155
|
+
Report at most ten findings. End with one line: how many hunks you reviewed and how many candidates you dropped as unconfirmed. If you found nothing, say so plainly.
|
|
156
|
+
|
|
157
|
+
Never: change files, run commands, or report something you did not confirm in the code.`,
|
|
158
|
+
allowedTools: ['read_file', 'list_files', 'search_files', 'find_symbol', 'find_references', 'list_symbols', 'git_diff', 'memory_read'],
|
|
159
|
+
forbiddenTools: ['write_file', 'edit_file', 'apply_patch', 'edit_notebook', 'run_command', 'sub_agent'],
|
|
160
|
+
maxIterations: 20,
|
|
161
|
+
auto: false,
|
|
162
|
+
},
|
|
163
|
+
{
|
|
164
|
+
id: 'security',
|
|
165
|
+
description: 'Audits changes or the repository for exploitable problems and vulnerable dependencies, then fixes only the findings you pick.',
|
|
166
|
+
systemPrompt: `You are Security: you find exploitable security problems, report them, and fix only the ones the person picks.
|
|
167
|
+
|
|
168
|
+
Scope: by default, the current changes (\`git_diff\`). The whole repository only when the person asks for it.
|
|
169
|
+
|
|
170
|
+
Phase 1, audit:
|
|
171
|
+
1. Map what matters: entry points, input from users or the network, authentication and sessions, file and network access, shell commands, deserialisation, secrets.
|
|
172
|
+
2. Check each against this list:
|
|
173
|
+
- injection: SQL, shell command, template, LDAP or XPath built from input;
|
|
174
|
+
- path traversal: file paths built from input without a containment check;
|
|
175
|
+
- SSRF: server-side requests to URLs taken from input;
|
|
176
|
+
- authentication and authorisation: missing checks, checks on the wrong value, comparisons that always pass;
|
|
177
|
+
- unsafe deserialisation, or evaluating input as code;
|
|
178
|
+
- secrets: keys, tokens or passwords written in code or config;
|
|
179
|
+
- weak cryptography: MD5 or SHA-1 for passwords, fixed IVs, home-made schemes;
|
|
180
|
+
- missing limits on input size or rate where the input reaches something expensive.
|
|
181
|
+
3. Run \`dependency_audit\` and include dependencies with known vulnerabilities.
|
|
182
|
+
4. Verify every candidate: read the whole path from the input to the dangerous call with \`read_file\`, \`find_symbol\` and \`find_references\`. Keep only findings with a concrete way to exploit them. Drop the rest.
|
|
183
|
+
|
|
184
|
+
Phase 1 output: numbered findings, most severe first. Each one has:
|
|
185
|
+
- severity: critical, high, medium or low;
|
|
186
|
+
- the location, written as path:line;
|
|
187
|
+
- how it can be exploited, in one or two sentences, with an example input;
|
|
188
|
+
- the fix, in one or two sentences.
|
|
189
|
+
If you found nothing, say so and list what you checked. End Phase 1 with this line exactly:
|
|
190
|
+
Reply with the numbers to fix.
|
|
191
|
+
|
|
192
|
+
Phase 2, fix: only when the message you were given names findings to fix (for example "fix 1 and 3"). If the message you were given does not name findings to fix, do not change any file. When it does:
|
|
193
|
+
1. Fix only those findings, with the smallest change that closes each hole: \`edit_file\` for one file, \`apply_patch\` for several, \`write_file\` only for a new file.
|
|
194
|
+
2. Run the project's tests with \`run_command\` and report the result.
|
|
195
|
+
3. Report each fix: path:line, what changed, and how it closes the hole.
|
|
196
|
+
|
|
197
|
+
Never: change a file before the person has named what to fix, report a finding you did not trace from input to the dangerous call, or print a secret you found in full (show only its first four characters).`,
|
|
198
|
+
allowedTools: [
|
|
199
|
+
'read_file', 'list_files', 'search_files', 'find_symbol', 'find_references', 'list_symbols', 'git_diff', 'dependency_audit', 'memory_read',
|
|
200
|
+
'edit_file', 'apply_patch', 'write_file', 'edit_notebook', 'run_command',
|
|
201
|
+
],
|
|
202
|
+
forbiddenTools: ['sub_agent'],
|
|
203
|
+
maxIterations: 30,
|
|
107
204
|
auto: false,
|
|
108
205
|
},
|
|
109
206
|
];
|
|
@@ -12,7 +12,7 @@
|
|
|
12
12
|
*/
|
|
13
13
|
import type { ProviderName } from '../types.js';
|
|
14
14
|
/** Built-in specialist ids. Free-form ids are also allowed (see {@link SpecialistConfig.id}). */
|
|
15
|
-
export type BuiltInSpecialistId = 'plan' | 'code' | 'debug' | 'ask' | 'review';
|
|
15
|
+
export type BuiltInSpecialistId = 'plan' | 'code' | 'debug' | 'ask' | 'review' | 'security';
|
|
16
16
|
export type SpecialistId = BuiltInSpecialistId | (string & {});
|
|
17
17
|
/**
|
|
18
18
|
* A specialist profile: the "personality" + tool allowlist for one
|
package/dist/policy/policy.d.ts
CHANGED
|
@@ -2,75 +2,22 @@ import { z } from 'zod';
|
|
|
2
2
|
import type { AgentConfig, ProviderName } from '../types.js';
|
|
3
3
|
declare const policySchema: z.ZodObject<{
|
|
4
4
|
version: z.ZodOptional<z.ZodLiteral<1>>;
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
deniedTools: z.ZodOptional<z.ZodArray<z.ZodString, "many">>;
|
|
9
|
-
/** Only these MCP server names may connect. Omit for no restriction; [] blocks all. */
|
|
10
|
-
allowedMcpServers: z.ZodOptional<z.ZodArray<z.ZodString, "many">>;
|
|
5
|
+
allowedProviders: z.ZodOptional<z.ZodArray<z.ZodString>>;
|
|
6
|
+
deniedTools: z.ZodOptional<z.ZodArray<z.ZodString>>;
|
|
7
|
+
allowedMcpServers: z.ZodOptional<z.ZodArray<z.ZodString>>;
|
|
11
8
|
disableBrowser: z.ZodOptional<z.ZodBoolean>;
|
|
12
|
-
/** Forces every tool call through approval, even if the user enables autonomous mode. */
|
|
13
9
|
disableAutonomous: z.ZodOptional<z.ZodBoolean>;
|
|
14
|
-
/** Hard per-run spend ceiling in USD; a user's own cap can only be lower. */
|
|
15
10
|
maxCostUsdPerRun: z.ZodOptional<z.ZodNumber>;
|
|
16
11
|
audit: z.ZodOptional<z.ZodObject<{
|
|
17
12
|
enabled: z.ZodBoolean;
|
|
18
13
|
file: z.ZodOptional<z.ZodString>;
|
|
19
|
-
},
|
|
20
|
-
enabled: boolean;
|
|
21
|
-
file?: string | undefined;
|
|
22
|
-
}, {
|
|
23
|
-
enabled: boolean;
|
|
24
|
-
file?: string | undefined;
|
|
25
|
-
}>>;
|
|
14
|
+
}, z.core.$strip>>;
|
|
26
15
|
proxy: z.ZodOptional<z.ZodObject<{
|
|
27
16
|
https: z.ZodOptional<z.ZodString>;
|
|
28
17
|
http: z.ZodOptional<z.ZodString>;
|
|
29
18
|
noProxy: z.ZodOptional<z.ZodString>;
|
|
30
|
-
},
|
|
31
|
-
|
|
32
|
-
http?: string | undefined;
|
|
33
|
-
noProxy?: string | undefined;
|
|
34
|
-
}, {
|
|
35
|
-
https?: string | undefined;
|
|
36
|
-
http?: string | undefined;
|
|
37
|
-
noProxy?: string | undefined;
|
|
38
|
-
}>>;
|
|
39
|
-
}, "strict", z.ZodTypeAny, {
|
|
40
|
-
version?: 1 | undefined;
|
|
41
|
-
allowedProviders?: string[] | undefined;
|
|
42
|
-
deniedTools?: string[] | undefined;
|
|
43
|
-
allowedMcpServers?: string[] | undefined;
|
|
44
|
-
disableBrowser?: boolean | undefined;
|
|
45
|
-
disableAutonomous?: boolean | undefined;
|
|
46
|
-
maxCostUsdPerRun?: number | undefined;
|
|
47
|
-
audit?: {
|
|
48
|
-
enabled: boolean;
|
|
49
|
-
file?: string | undefined;
|
|
50
|
-
} | undefined;
|
|
51
|
-
proxy?: {
|
|
52
|
-
https?: string | undefined;
|
|
53
|
-
http?: string | undefined;
|
|
54
|
-
noProxy?: string | undefined;
|
|
55
|
-
} | undefined;
|
|
56
|
-
}, {
|
|
57
|
-
version?: 1 | undefined;
|
|
58
|
-
allowedProviders?: string[] | undefined;
|
|
59
|
-
deniedTools?: string[] | undefined;
|
|
60
|
-
allowedMcpServers?: string[] | undefined;
|
|
61
|
-
disableBrowser?: boolean | undefined;
|
|
62
|
-
disableAutonomous?: boolean | undefined;
|
|
63
|
-
maxCostUsdPerRun?: number | undefined;
|
|
64
|
-
audit?: {
|
|
65
|
-
enabled: boolean;
|
|
66
|
-
file?: string | undefined;
|
|
67
|
-
} | undefined;
|
|
68
|
-
proxy?: {
|
|
69
|
-
https?: string | undefined;
|
|
70
|
-
http?: string | undefined;
|
|
71
|
-
noProxy?: string | undefined;
|
|
72
|
-
} | undefined;
|
|
73
|
-
}>;
|
|
19
|
+
}, z.core.$strip>>;
|
|
20
|
+
}, z.core.$strict>;
|
|
74
21
|
export type OrgPolicy = z.infer<typeof policySchema>;
|
|
75
22
|
export declare class PolicyError extends Error {
|
|
76
23
|
constructor(message: string);
|
package/dist/pricing.d.ts
CHANGED
|
@@ -24,6 +24,8 @@ export interface ResolvedPricing extends ModelPricing {
|
|
|
24
24
|
/** True when the lookup hit a known table entry. False when the caller
|
|
25
25
|
* supplied a manual override or when the model id is unknown. */
|
|
26
26
|
known: boolean;
|
|
27
|
+
/** The table key a substring lookup matched, when it was not an exact hit. */
|
|
28
|
+
matchedKey?: string;
|
|
27
29
|
}
|
|
28
30
|
/**
|
|
29
31
|
* Look up the pricing for a given model id. Falls back to a substring match
|