@ikarmic/forge-core 5.1.0 → 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/README.md +1 -1
- package/dist/agent/agent.js +39 -5
- package/dist/agent/goal.d.ts +5 -8
- package/dist/agent/prompts.d.ts +25 -1
- package/dist/agent/prompts.js +61 -17
- 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 +7 -3
- package/dist/index.js +8 -3
- package/dist/journey/answers-block.d.ts +17 -0
- package/dist/journey/answers-block.js +28 -0
- package/dist/journey/index.d.ts +9 -0
- package/dist/journey/index.js +8 -0
- package/dist/journey/journey.d.ts +13 -0
- package/dist/journey/journey.js +40 -0
- package/dist/journey/offer.d.ts +25 -0
- package/dist/journey/offer.js +49 -0
- package/dist/journey/questions.d.ts +23 -0
- package/dist/journey/questions.js +49 -0
- package/dist/journey/stages.d.ts +13 -0
- package/dist/journey/stages.js +107 -0
- package/dist/journey/survey.d.ts +10 -0
- package/dist/journey/survey.js +203 -0
- package/dist/journey/trigger.d.ts +19 -0
- package/dist/journey/trigger.js +79 -0
- package/dist/journey/types.d.ts +72 -0
- package/dist/journey/types.js +1 -0
- 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/skills/builtin.js +1 -1
- package/dist/specs/generate.d.ts +18 -9
- package/dist/specs/generate.js +174 -14
- package/dist/specs/index.d.ts +2 -2
- package/dist/specs/index.js +1 -1
- package/dist/specs/store.d.ts +14 -2
- package/dist/specs/store.js +26 -1
- package/dist/specs/types.d.ts +11 -0
- 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 +39 -3
- 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-file.js +5 -0
- 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 +2 -70
- package/dist/tools/sub-agent.js +3 -0
- 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 +27 -0
- package/package.json +3 -3
|
@@ -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
|
package/dist/pricing.js
CHANGED
|
@@ -9,6 +9,9 @@ const PRICING = {
|
|
|
9
9
|
// OpenAI ----------------------------------------------------
|
|
10
10
|
'gpt-4o': { input: 2.5, output: 10 },
|
|
11
11
|
'gpt-4o-mini': { input: 0.15, output: 0.6 },
|
|
12
|
+
'gpt-4.1': { input: 2, output: 8 },
|
|
13
|
+
'gpt-4.1-mini': { input: 0.4, output: 1.6 },
|
|
14
|
+
'gpt-4.1-nano': { input: 0.1, output: 0.4 },
|
|
12
15
|
'gpt-4-turbo': { input: 10, output: 30 },
|
|
13
16
|
'gpt-4': { input: 30, output: 60 },
|
|
14
17
|
'gpt-3.5-turbo': { input: 0.5, output: 1.5 },
|
|
@@ -26,7 +29,9 @@ const PRICING = {
|
|
|
26
29
|
'claude-sonnet-4-5': { input: 3, output: 15 },
|
|
27
30
|
'claude-opus-4': { input: 15, output: 75 },
|
|
28
31
|
'claude-opus-4-1': { input: 15, output: 75 },
|
|
32
|
+
'claude-opus-4-5': { input: 5, output: 25 },
|
|
29
33
|
'claude-haiku-4': { input: 0.8, output: 4 },
|
|
34
|
+
'claude-haiku-4-5': { input: 1, output: 5 },
|
|
30
35
|
// Google ----------------------------------------------------
|
|
31
36
|
'gemini-1.5-pro': { input: 1.25, output: 5 },
|
|
32
37
|
'gemini-1.5-flash': { input: 0.075, output: 0.3 },
|
|
@@ -35,25 +40,11 @@ const PRICING = {
|
|
|
35
40
|
'gemini-2.0-flash-lite': { input: 0.025, output: 0.1 },
|
|
36
41
|
'gemini-2.5-pro': { input: 1.25, output: 10 },
|
|
37
42
|
'gemini-2.5-flash': { input: 0.3, output: 2.5 },
|
|
38
|
-
// Ollama is local: no API cost. We surface this as 0/$0.00 in the UI so
|
|
39
|
-
// users see the meter running with $0.00 instead of "unpriced" churn.
|
|
40
|
-
// List a few popular model id prefixes so substring matching resolves them.
|
|
41
|
-
'llama3.1': { input: 0, output: 0 },
|
|
42
|
-
'llama3.2': { input: 0, output: 0 },
|
|
43
|
-
'llama3.3': { input: 0, output: 0 },
|
|
44
|
-
'qwen2.5': { input: 0, output: 0 },
|
|
45
|
-
'mistral': { input: 0, output: 0 },
|
|
46
|
-
'mixtral': { input: 0, output: 0 },
|
|
47
|
-
'gemma2': { input: 0, output: 0 },
|
|
48
|
-
'codellama': { input: 0, output: 0 },
|
|
49
|
-
'deepseek-coder': { input: 0, output: 0 },
|
|
50
|
-
'phi3': { input: 0, output: 0 },
|
|
51
43
|
// OpenRouter ----------------------------------------------------
|
|
52
44
|
// OpenRouter passes through third-party models. We list the popular ones
|
|
53
45
|
// explicitly; everything else falls back to "unpriced" but still works
|
|
54
46
|
// (cost events just get suppressed). Prices track OpenRouter's published
|
|
55
47
|
// list at the time of writing.
|
|
56
|
-
'openrouter/auto': { input: 0, output: 0 }, // dynamic routing, unknown
|
|
57
48
|
'anthropic/claude-3.5-sonnet': { input: 3, output: 15 },
|
|
58
49
|
'anthropic/claude-3.5-haiku': { input: 0.8, output: 4 },
|
|
59
50
|
'anthropic/claude-3-opus': { input: 15, output: 75 },
|
|
@@ -70,6 +61,30 @@ const PRICING = {
|
|
|
70
61
|
// MiniMax --------------------------------------------------
|
|
71
62
|
'minimax-m3': { input: 0.3, output: 1.2 },
|
|
72
63
|
};
|
|
64
|
+
/**
|
|
65
|
+
* Providers that run on the person's own machine. Their models cost nothing
|
|
66
|
+
* whatever they are called, and only they may be priced at $0: a free entry
|
|
67
|
+
* keyed by a model name ("mistral") once priced the paid hosted model of the
|
|
68
|
+
* same name at $0 too, so a cost cap never stopped it.
|
|
69
|
+
*/
|
|
70
|
+
const LOCAL_PROVIDERS = new Set(['ollama', 'lmstudio']);
|
|
71
|
+
/**
|
|
72
|
+
* Whether `key` names the model `id` at `at`, rather than an older version of
|
|
73
|
+
* it or part of another word. `claude-opus-4` is inside `claude-opus-4-5` and
|
|
74
|
+
* `gpt-4` inside `gpt-4.1`, and each once priced the newer model at the older
|
|
75
|
+
* one's rate. A date (`-20241022`), revision (`-002`), size (`:7b`) or variant
|
|
76
|
+
* (`-latest`) after the key is still the same model.
|
|
77
|
+
*/
|
|
78
|
+
function namesModelAt(id, key, at) {
|
|
79
|
+
if (at > 0 && /[a-z0-9]/.test(id[at - 1]))
|
|
80
|
+
return false;
|
|
81
|
+
const rest = id.slice(at + key.length);
|
|
82
|
+
if (rest === '')
|
|
83
|
+
return true;
|
|
84
|
+
if (/^[a-z0-9]/.test(rest))
|
|
85
|
+
return false;
|
|
86
|
+
return !/^(?:\.\d|-\d{1,2}(?=$|[-.:@_]))/.test(rest);
|
|
87
|
+
}
|
|
73
88
|
/** Provider prompt-cache billing relative to the normal input rate. */
|
|
74
89
|
function cacheMultipliersFor(model) {
|
|
75
90
|
switch (model.provider) {
|
|
@@ -103,6 +118,8 @@ export function lookupPricing(model, override) {
|
|
|
103
118
|
const configured = overrideCost(model);
|
|
104
119
|
if (configured)
|
|
105
120
|
return { ...cache, ...configured, known: true };
|
|
121
|
+
if (typeof model !== 'string' && LOCAL_PROVIDERS.has(model.provider))
|
|
122
|
+
return { input: 0, output: 0, known: true };
|
|
106
123
|
const id = (typeof model === 'string' ? model : model.model).toLowerCase();
|
|
107
124
|
// Strip only the OpenRouter-style 'models/' prefix so 'models/gemini-1.5-pro'
|
|
108
125
|
// resolves the same way as 'gemini-1.5-pro'. We do NOT strip provider prefixes
|
|
@@ -115,8 +132,10 @@ export function lookupPricing(model, override) {
|
|
|
115
132
|
// Substring match: longest key wins, so 'gpt-4o-mini' beats 'gpt-4o'.
|
|
116
133
|
const keys = Object.keys(PRICING).sort((a, b) => b.length - a.length);
|
|
117
134
|
for (const key of keys) {
|
|
118
|
-
|
|
119
|
-
|
|
135
|
+
for (let at = stripped.indexOf(key); at !== -1; at = stripped.indexOf(key, at + 1)) {
|
|
136
|
+
if (namesModelAt(stripped, key, at))
|
|
137
|
+
return { ...PRICING[key], ...cache, known: true, matchedKey: key };
|
|
138
|
+
}
|
|
120
139
|
}
|
|
121
140
|
return { input: 0, output: 0, known: false };
|
|
122
141
|
}
|
package/dist/skills/builtin.js
CHANGED
|
@@ -51,7 +51,7 @@ ${keys}
|
|
|
51
51
|
1. Read the file first. If it does not exist, create it with only the keys you are setting.
|
|
52
52
|
2. Change only what was asked. Keep every other key and keep the file valid JSON (double quotes, no trailing commas, no comments).
|
|
53
53
|
3. A secret never goes in a project file. Name an environment variable (\`apiKeyEnv\`) instead of pasting a key.
|
|
54
|
-
4. Do not widen permissions (\`autoApproveTools\`, \`permission\` allow rules, \`autoApproveCommands\`) unless the user
|
|
54
|
+
4. Do not widen permissions (\`autoApproveTools\`, \`permission\` allow rules, \`autoApproveCommands\`) unless the user named what to allow. A general request such as "make the prompts go away" or "allow everything" names nothing: do not write it, and never grant \`run_command\`, \`write_file\`, \`edit_file\`, \`apply_patch\` or \`sub_agent\` because of it. Say in one sentence why, then offer the narrow forms: allow one named tool, or choose "Always allow" on a specific command when its card appears. When you do widen, say what the change allows.
|
|
55
55
|
5. After saving, say what changed and where. Most changes apply on the next message; \`caCertificates\` and the language need a window reload, MCP servers and skills need \`/reload\`.
|
|
56
56
|
6. Do not run Forge commands that write to the real \`~/.forge\` to test a change. Edit the file and let the user check it.
|
|
57
57
|
`;
|
package/dist/specs/generate.d.ts
CHANGED
|
@@ -12,15 +12,24 @@
|
|
|
12
12
|
import type { SpecSections } from './types.js';
|
|
13
13
|
/** Read-only tools exposed to the model during spec-mode generation. */
|
|
14
14
|
export declare const SPEC_READONLY_TOOLS: readonly string[];
|
|
15
|
+
export declare function splitSpecSections(raw: string): SpecSections;
|
|
16
|
+
/**
|
|
17
|
+
* The Spec stage's two documents. Returns **only** those two keys: a model that
|
|
18
|
+
* volunteers a `## tasks.md` section despite being told not to must not be able
|
|
19
|
+
* to smuggle it into a draft, because the person has not reached the
|
|
20
|
+
* implementation-plan gate yet.
|
|
21
|
+
*/
|
|
22
|
+
export declare function splitRequirementsDesign(raw: string): {
|
|
23
|
+
requirements: string;
|
|
24
|
+
design: string;
|
|
25
|
+
};
|
|
15
26
|
/**
|
|
16
|
-
*
|
|
17
|
-
* section is introduced by a `## <file>.md` markdown heading (case-
|
|
18
|
-
* insensitive, arbitrary surrounding whitespace); the body runs until
|
|
19
|
-
* the next matching heading or end of text. Section order in `raw`
|
|
20
|
-
* does not matter.
|
|
27
|
+
* The ImplPlan stage's single document.
|
|
21
28
|
*
|
|
22
|
-
* Throws when
|
|
23
|
-
*
|
|
24
|
-
*
|
|
29
|
+
* Throws when the reply holds no task at all: an empty body or a prose-only
|
|
30
|
+
* "Plan is complete." would otherwise be written by `writeDraftTasks` and then
|
|
31
|
+
* approved, because `approveDraft` only checks that the file exists — leaving a
|
|
32
|
+
* spec with nothing to run, which is the very outcome that guard exists to
|
|
33
|
+
* prevent. Failing generation here sends the journey back to "Revise".
|
|
25
34
|
*/
|
|
26
|
-
export declare function
|
|
35
|
+
export declare function splitTasksSection(raw: string): string;
|