@cerebriline/core 4.100.254 → 4.100.256
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/assets/prompt-templates/claude.md +123 -90
- package/assets/prompt-templates/deepseek.md +62 -20
- package/assets/prompt-templates/default.md +14 -14
- package/assets/prompt-templates/gemma.md +46 -18
- package/assets/prompt-templates/glm.md +60 -35
- package/assets/prompt-templates/kimi-k3.md +41 -32
- package/assets/prompt-templates/kimi.md +35 -31
- package/assets/prompt-templates/minimax-m3.1-flash.md +563 -0
- package/assets/prompt-templates/minimax.md +72 -92
- package/assets/prompt-templates/nemotron.md +50 -78
- package/assets/prompt-templates/qwen.md +83 -69
- package/dist/cloud/index.js +1 -1
- package/dist/document-reader-child.js +13 -13
- package/dist/extensions/config/prompt-template-review.d.ts +41 -1
- package/dist/extensions/context/agentic-compaction.d.ts +2 -1
- package/dist/extensions/context/capped-thinking.d.ts +24 -2
- package/dist/extensions/context/compaction-shared.d.ts +47 -11
- package/dist/extensions/context/compaction.d.ts +1 -1
- package/dist/extensions/context/council-compaction.d.ts +3 -1
- package/dist/extensions/context/replay-compaction.d.ts +1 -1
- package/dist/extensions/context/tool-ledger.d.ts +13 -1
- package/dist/extensions/tools/check-on-failure.d.ts +3 -0
- package/dist/extensions/tools/edit-verification.d.ts +26 -5
- package/dist/extensions/tools/executors/bash.d.ts +22 -0
- package/dist/extensions/tools/executors/document/formats.d.ts +6 -0
- package/dist/extensions/tools/executors/read-receipts.d.ts +12 -0
- package/dist/extensions/tools/helpers.d.ts +12 -0
- package/dist/extensions/tools/list-files.d.ts +1 -1
- package/dist/extensions/tools/schemas.d.ts +32 -0
- package/dist/extensions/tools/team/delegated-agent.d.ts +8 -1
- package/dist/hub/daemon/entry.js +625 -615
- package/dist/hub/index.js +637 -627
- package/dist/index.js +1421 -3762
- package/dist/remote/remote-helper-entry.js +1 -1
- package/dist/remote/remote-helper.js +1 -1
- package/dist/runtime/atomic/file-revisions.d.ts +2 -0
- package/dist/runtime/atomic/revision-capture.d.ts +1 -1
- package/dist/runtime/host/loop-cut-log.d.ts +18 -0
- package/dist/runtime/orchestration/session-runtime.d.ts +5 -0
- package/dist/runtime/safety/struggle-detector.d.ts +1 -1
- package/dist/services/llms/apihandler-agent-model-adapter.d.ts +4 -2
- package/dist/services/retrieval/tar-extract.d.ts +4 -0
- package/dist/session/persisted-tool-result-content.d.ts +10 -1
- package/dist/session/services/message-builder.d.ts +15 -1
- package/dist/session/tool-result-format-pin.d.ts +17 -0
- package/dist/types/config.d.ts +9 -2
- package/package.json +4 -4
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
import { type PromptTemplate } from "@cerebriline/shared";
|
|
2
2
|
import { type CompactionPromptSources } from "./prompt-template-compaction";
|
|
3
3
|
export declare const DEFAULT_REQUIRED_REWRITES: readonly ["read_files", "search_codebase", "editor", "apply_patch", "run_commands", "fetch_web_content", "check_file", "ask_lsp", "grep", "sed", "awk"];
|
|
4
|
-
export declare const PROMPT_TEMPLATE_REVIEW_INSTRUCTIONS = "You are being shown the prompt Cline gives you when it uses you as a coding agent: a system prompt and a set of tool descriptions. You are the intended reader of this text. Rewrite it into the form you would rather receive.\n\nThis is not a copy-editing task. The prompt below was written by someone reasoning about how you read, and they may have been wrong. Where a section is phrased in a way you would skim, reorder or restate it. Where a rule matters and is buried, put it where you would be when you are about to break it. Where something is repeated to no effect, cut it.\n\nFour failures are observed in practice with models in your family, and the rewrite is judged on whether it prevents them:\n\n1. Reaching for shell commands to do file work \u2014 'cat' to read, 'sed -i' or 'echo >' to write, 'grep' to search \u2014 when dedicated tools exist and are always available.\n2. Serialising independent work across turns: one read, wait, one read, wait, when all of them were known at the start.\n3. Announcing an intention instead of acting on it, or claiming a task is finished without reading back what was written.\n4. Answering a question about a symbol with a text search. Asking where something is defined, what uses it, what implements it, or what a name means, and then grepping for it and reading several files to work out which hit was the real one \u2014 when the IDE's language servers already know the answer exactly and will give it in one call. The same failure in its other form: running a compiler or a linter through the shell to find out whether one file is valid, and waiting for a whole-project build to answer a one-file question.\n\nOne thing you must get right, and which prompts written for this get wrong in both directions:\n\nA coding agent is multi-turn, and the end of a turn is never in itself the end of the work. What is final is the completion of the assigned work \u2014 the task, the work package, the milestone, the request. An end of turn may be a milestone reached, a point where you need direction, guidance or a clarification from the user, or simply an intermediate step between two things the user asked for. So:\n\n- Do not treat \"I have stopped emitting tool calls\" as \"the work is done\". It is not a signal about the work at all.\n- Do not write a rule that makes stopping feel like failure, such as \"a response without a tool call is considered complete\". A rule like that buys one more unnecessary tool call on every finished task, and it teaches the wrong thing: that continuing is always correct.\n- Keep your attention on the long horizon of the assigned work rather than on the current turn. Ending a turn to ask a question is correct when you need the answer. Ending it while work you were asked to do remains untouched, and saying nothing about that, is not.\n\nFour more things are settled by measurement rather than by opinion, and a rewrite is judged on whether it keeps them:\n\n- The checker and the run belong in the same turn. Call 'check_file' and the thing that executes the code - 'run_commands', or 'browser' for a page - together, not one or the other. Running it says *that* something is broken and where the parser gave up; the checker says *which line* to edit. Each is half the answer, and the half you skip is the half the turn gets spent guessing at.\n- A tool's report outranks your own reasoning about the same question. Where a tool has measured something - a delimiter scan naming the line to edit, a diagnostic naming a type - that is the measurement, and re-deriving it yourself is an estimate. Where the two disagree, it is the estimate that is wrong. Measured twice: a model called a delimiter scan a false positive, counted brackets by hand instead, and was wrong both times, at over 30,000 thinking tokens a turn. If you doubt a report, do not re-derive it - act on it and run the result. That costs milliseconds and settles it either way.\n- Make the planned changes one at a time, and confirm each one before starting the next. The cheap check that does not execute the code goes after every edit; the thing that runs the code is what settles whether the change was right. Six edits made together and checked once leave six things to undo and no way to tell which one was wrong - measured across ten runs, the template that said otherwise drew 13.6 'restore_file' calls per run against 0.23 for the family that did not.\n- Do not re-read a file to confirm your own edit. The edit call already reports whether it landed and what changed, and that is the confirmation. Measured on one session: 'read_files' was called 33 times, 31 of them byte-identical, returning 440,013 characters against a file of 14 KB - four times the tokens that session spent on all of its reasoning. Read again when the call failed, or when you need content you have not seen. A rule that says to read a file back after every edit is the one thing here you must not write.\n\nHard constraints:\n\n- Keep every placeholder exactly as written, including the braces: {{PLATFORM_NAME}}, {{CURRENT_DATE}}, {{IDE_NAME}}, {{CWD}}, {{CLINE_RULES}}, {{CLINE_METADATA}}, {{DEFAULT}}. They are substituted at runtime; a renamed or dropped placeholder breaks the prompt.\n- Keep the tool names exactly as written. They are the wire names of real tools.\n- Do not invent tools, parameters, or capabilities. Describe only what is described below.\n- Keep the file structure: YAML frontmatter between --- markers, then '# system', then one '# tool: <name>' section for EVERY tool listed below. Not only the ones you want to change \u2014 every one of them.\n- Covering every tool is a hard requirement. A tool with no section keeps its built-in description, and that fallback exists for exactly one situation: Cline gains a new tool and the templates have not been regenerated yet. It is not a way to skip tools you have nothing to say about. A template that covers six of thirty-one tools is not using the fallback, it is relying on it, and nothing in the file shows which twenty-five were left out.\n- '{{DEFAULT}}' expands to the tool's built-in description at runtime, and it is how you cover a tool cheaply. Where you would improve on the built-in wording, write your own and put '{{DEFAULT}}' on its own line where the built-in text should follow. Where you would not, the entire section body may be just '{{DEFAULT}}'. Either is a covered tool; an absent section is not.\n- These eight tools must be written in your own words, and a section containing only '{{DEFAULT}}' is not acceptable for any of them: read_files, search_codebase, editor, apply_patch, run_commands, fetch_web_content, check_file, ask_lsp. They are the tools models in your family are observed to misuse, so they are the whole reason this template exists. For these eight, your own text \u2014 not the inherited text \u2014 has to carry the three things listed below: every value of any closed set, every argument and when to use it, and what comes back. Adding '{{DEFAULT}}' after your text is allowed and does not excuse you from any of that.\n- For 'ask_lsp', say when to reach for it, not only what it does. List at least three concrete situations as the reader actually meets them \u2014 \"about to search for a name to find where it is defined\", \"about to open a file just to read a signature\", \"about to scroll or count brackets to work out a file's structure\" \u2014 each pointing at the operation that answers it, and name 'search_codebase' as the thing those situations would otherwise go to. This is not padding: measured across a whole session against a real bug, a template that described all eight operations correctly and said only \"prefer this over search_codebase for anything about a symbol\" produced zero calls to the tool, while the model hand-rolled the same answers in the shell twenty times. It could not recognise the moment.\n- Do not copy a built-in description out word for word. That is a snapshot: it says the same thing today and stops tracking the built-in text tomorrow, so the next edit to it reaches every model except you. If you would not change the wording, write '{{DEFAULT}}' \u2014 that is what it is for.\n- Where you rule a use of a tool out, name the tool that replaces it. 'run_commands' in particular must name 'read_files', 'editor' and 'search_codebase' in your own text: \"do not use this for file operations when dedicated tools exist\" is a rule the reader has to already know the answer to in order to follow, which is the same as not writing it.\n- Two of those eight \u2014 'run_commands' and 'skills' \u2014 must keep '{{DEFAULT}}' in the section as well. Their built-in descriptions are not fixed text: 'run_commands' is composed against the shell that will actually run the commands, so PowerShell, cmd and a POSIX shell get different quoting and sequencing advice, and 'skills' appends the skills installed on that machine. Replace either outright and you freeze one machine's answer into every machine's prompt, silently. Write your own text and put '{{DEFAULT}}' on its own line where the built-in text should follow.\n- For the other tools, a section whose body is exactly '{{DEFAULT}}' is a complete and correct answer. Do not pad them. Nothing about how you read changes what 'team_finalize_outcome' should say.\n- Emit every section exactly once. Do not repeat '# system', and do not write two '# tool: <name>' headings for the same tool. Only the last copy of a repeated section survives, so a duplicate silently throws away your own work.\n- Leave the 'match:' block in the frontmatter exactly as it is. It is what routes this template to you; change it and your rewrite reaches a different model, or none.\n\nReplace a description and you inherit everything it was carrying:\n\nIf a '# tool:' section contains '{{DEFAULT}}' anywhere, the built-in description is still delivered and you are only adding emphasis on top of it. Nothing further is asked of you for that tool \u2014 unless it is one of the eight above, which are judged on your words alone.\n\nIf it does not, your text is the only thing a model will ever see about that tool, so it must not lose what the built-in description said. Specifically, and only where the built-in text below says it:\n\n1. Closed sets. Where the built-in description lists the values an argument accepts \u2014 the operations of a language-server tool, the actions of a task tool, the statuses of a run \u2014 your replacement must list all of them too. Naming four of eight hides half the tool and a model cannot guess the rest.\n2. How to address the tool. Where the built-in description names the arguments, and says which one to use when, your replacement must say the same. A tool that can be addressed three ways needs all three, and what each is for.\n3. What comes back. Where the built-in description states an output, your replacement must state one: the shape, what the fields hold, and what an empty or failed result means. A model that cannot map a result back onto its request spends a turn working it out, and then stops trusting the tool.\n\nNothing is required of you that the built-in text did not already provide. Reproducing what is there is always enough; improving on it is better; dropping it is the one thing that is not allowed.\n\nDo not write 'Output:' followed by nothing, or by a single word. An empty label reads as an empty result, which is worse than saying nothing at all.\n\nFor any tool outside the eight, if restating all of that is not worth it to you, do not replace its description. Write your emphasis and leave '{{DEFAULT}}' in the section, or write nothing but the marker. That is cheaper and it is always correct.\n\nReply with the complete template file and nothing else: no preamble, no explanation, no code fence.";
|
|
4
|
+
export declare const PROMPT_TEMPLATE_REVIEW_INSTRUCTIONS = "You are being shown the prompt Cline gives you when it uses you as a coding agent: a system prompt and a set of tool descriptions. You are the intended reader of this text. Rewrite it into the form you would rather receive.\n\nThis is not a copy-editing task. The prompt below was written by someone reasoning about how you read, and they may have been wrong. Where a section is phrased in a way you would skim, reorder or restate it. Where a rule matters and is buried, put it where you would be when you are about to break it. Where something is repeated to no effect, cut it.\n\nFour failures are observed in practice with models in your family, and the rewrite is judged on whether it prevents them:\n\n1. Reaching for shell commands to do file work \u2014 'cat' to read, 'sed -i' or 'echo >' to write, 'grep' to search \u2014 when dedicated tools exist and are always available.\n2. Serialising independent work across turns: one read, wait, one read, wait, when all of them were known at the start.\n3. Announcing an intention instead of acting on it, or claiming a task is finished without reading back what was written.\n4. Answering a question about a symbol with a text search. Asking where something is defined, what uses it, what implements it, or what a name means, and then grepping for it and reading several files to work out which hit was the real one \u2014 when the IDE's language servers already know the answer exactly and will give it in one call. The same failure in its other form: running a compiler or a linter through the shell to find out whether one file is valid, and waiting for a whole-project build to answer a one-file question.\n\nOne thing you must get right, and which prompts written for this get wrong in both directions:\n\nA coding agent is multi-turn, and the end of a turn is never in itself the end of the work. What is final is the completion of the assigned work \u2014 the task, the work package, the milestone, the request. An end of turn may be a milestone reached, a point where you need direction, guidance or a clarification from the user, or simply an intermediate step between two things the user asked for. So:\n\n- Do not treat \"I have stopped emitting tool calls\" as \"the work is done\". It is not a signal about the work at all.\n- Do not write a rule that makes stopping feel like failure, such as \"a response without a tool call is considered complete\". A rule like that buys one more unnecessary tool call on every finished task, and it teaches the wrong thing: that continuing is always correct.\n- Keep your attention on the long horizon of the assigned work rather than on the current turn. Ending a turn to ask a question is correct when you need the answer. Ending it while work you were asked to do remains untouched, and saying nothing about that, is not.\n\nFour more things are settled by measurement rather than by opinion, and a rewrite is judged on whether it keeps them:\n\n- The checker and the run belong in the same turn. Call 'check_file' and the thing that executes the code - 'run_commands', or 'browser' for a page - together, not one or the other. Running it says *that* something is broken and where the parser gave up; the checker says *which line* to edit. Each is half the answer, and the half you skip is the half the turn gets spent guessing at.\n- A tool's report outranks your own reasoning about the same question. Where a tool has measured something - a delimiter scan naming the line to edit, a diagnostic naming a type - that is the measurement, and re-deriving it yourself is an estimate. Where the two disagree, it is the estimate that is wrong. Measured twice: a model called a delimiter scan a false positive, counted brackets by hand instead, and was wrong both times, at over 30,000 thinking tokens a turn. If you doubt a report, do not re-derive it - act on it and run the result. That costs milliseconds and settles it either way.\n- Make the planned changes one at a time, and confirm each one before starting the next. The cheap check that does not execute the code goes after every edit; the thing that runs the code is what settles whether the change was right. Six edits made together and checked once leave six things to undo and no way to tell which one was wrong - measured across ten runs, the template that said otherwise drew 13.6 'restore_file' calls per run against 0.23 for the family that did not.\n- Do not re-read a file to confirm your own edit. The edit call already reports whether it landed and what changed, and that is the confirmation. Measured on one session: 'read_files' was called 33 times, 31 of them byte-identical, returning 440,013 characters against a file of 14 KB - four times the tokens that session spent on all of its reasoning. Read again when the call failed, or when you need content you have not seen. A rule that says to read a file back after every edit is the one thing here you must not write.\n\nHard constraints:\n\n- Keep every placeholder exactly as written, including the braces: {{PLATFORM_NAME}}, {{CURRENT_DATE}}, {{IDE_NAME}}, {{CWD}}, {{CLINE_RULES}}, {{CLINE_METADATA}}, {{DEFAULT}}. They are substituted at runtime; a renamed or dropped placeholder breaks the prompt.\n- Keep the tool names exactly as written. They are the wire names of real tools.\n- Do not invent tools, parameters, or capabilities. Describe only what is described below.\n- Keep the file structure: YAML frontmatter between --- markers, then '# system', then one '# tool: <name>' section for EVERY tool listed below. Not only the ones you want to change \u2014 every one of them.\n- Covering every tool is a hard requirement. A tool with no section keeps its built-in description, and that fallback exists for exactly one situation: Cline gains a new tool and the templates have not been regenerated yet. It is not a way to skip tools you have nothing to say about. A template that covers six of thirty-one tools is not using the fallback, it is relying on it, and nothing in the file shows which twenty-five were left out.\n- '{{DEFAULT}}' expands to the tool's built-in description at runtime, and it is how you cover a tool cheaply. Where you would improve on the built-in wording, write your own and put '{{DEFAULT}}' on its own line where the built-in text should follow. Where you would not, the entire section body may be just '{{DEFAULT}}'. Either is a covered tool; an absent section is not.\n- Text you write beside '{{DEFAULT}}' is sent in addition to the built-in description, not instead of it, on every request. So write only what you add: the rule a model in your family breaks, the case it misreads, the one example that settles it. Do not restate or paraphrase what the built-in text already says; it follows anyway, and a restatement means the reader takes in the same thing twice. If you want to say everything in your own words, drop '{{DEFAULT}}' and write the whole description instead.\n- These eight tools must be written in your own words, and a section containing only '{{DEFAULT}}' is not acceptable for any of them: read_files, search_codebase, editor, apply_patch, run_commands, fetch_web_content, check_file, ask_lsp. They are the tools models in your family are observed to misuse, so they are the whole reason this template exists. For these eight, your own text \u2014 not the inherited text \u2014 has to carry the three things listed below: every value of any closed set, every argument and when to use it, and what comes back. Adding '{{DEFAULT}}' after your text is allowed and does not excuse you from any of that.\n- For 'ask_lsp', say when to reach for it, not only what it does. List at least three concrete situations as the reader actually meets them \u2014 \"about to search for a name to find where it is defined\", \"about to open a file just to read a signature\", \"about to scroll or count brackets to work out a file's structure\" \u2014 each pointing at the operation that answers it, and name 'search_codebase' as the thing those situations would otherwise go to. This is not padding: measured across a whole session against a real bug, a template that described all eight operations correctly and said only \"prefer this over search_codebase for anything about a symbol\" produced zero calls to the tool, while the model hand-rolled the same answers in the shell twenty times. It could not recognise the moment.\n- Do not copy a built-in description out word for word. That is a snapshot: it says the same thing today and stops tracking the built-in text tomorrow, so the next edit to it reaches every model except you. If you would not change the wording, write '{{DEFAULT}}' \u2014 that is what it is for.\n- Where you rule a use of a tool out, name the tool that replaces it. 'run_commands' in particular must name 'read_files', 'editor' and 'search_codebase' in your own text: \"do not use this for file operations when dedicated tools exist\" is a rule the reader has to already know the answer to in order to follow, which is the same as not writing it.\n- Two of those eight \u2014 'run_commands' and 'skills' \u2014 must keep '{{DEFAULT}}' in the section as well. Their built-in descriptions are not fixed text: 'run_commands' is composed against the shell that will actually run the commands, so PowerShell, cmd and a POSIX shell get different quoting and sequencing advice, and 'skills' appends the skills installed on that machine. Replace either outright and you freeze one machine's answer into every machine's prompt, silently. Write your own text and put '{{DEFAULT}}' on its own line where the built-in text should follow.\n- For the other tools, a section whose body is exactly '{{DEFAULT}}' is a complete and correct answer. Do not pad them. Nothing about how you read changes what 'team_finalize_outcome' should say.\n- Emit every section exactly once. Do not repeat '# system', and do not write two '# tool: <name>' headings for the same tool. Only the last copy of a repeated section survives, so a duplicate silently throws away your own work.\n- Leave the 'match:' block in the frontmatter exactly as it is. It is what routes this template to you; change it and your rewrite reaches a different model, or none.\n\nReplace a description and you inherit everything it was carrying:\n\nIf a '# tool:' section contains '{{DEFAULT}}' anywhere, the built-in description is still delivered and you are only adding emphasis on top of it. Nothing further is asked of you for that tool \u2014 unless it is one of the eight above, which are judged on your words alone.\n\nIf it does not, your text is the only thing a model will ever see about that tool, so it must not lose what the built-in description said. Specifically, and only where the built-in text below says it:\n\n1. Closed sets. Where the built-in description lists the values an argument accepts \u2014 the operations of a language-server tool, the actions of a task tool, the statuses of a run \u2014 your replacement must list all of them too. Naming four of eight hides half the tool and a model cannot guess the rest.\n2. How to address the tool. Where the built-in description names the arguments, and says which one to use when, your replacement must say the same. A tool that can be addressed three ways needs all three, and what each is for.\n3. What comes back. Where the built-in description states an output, your replacement must state one: what each entry is named by, what it holds, and what an empty or failed result means. A model that cannot map a result back onto its request spends a turn working it out, and then stops trusting the tool. Say it in words, as the built-in text does: do not name fields such as 'query', 'result' or 'success', and do not write the result as an object like '{query, result, success, error?}'. A session receives tool results as text or as JSON, chosen when it starts, so a description of one format is false in the other.\n\nNothing is required of you that the built-in text did not already provide. Reproducing what is there is always enough; improving on it is better; dropping it is the one thing that is not allowed.\n\nDo not write 'Output:' followed by nothing, or by a single word. An empty label reads as an empty result, which is worse than saying nothing at all.\n\nFor any tool outside the eight, if restating all of that is not worth it to you, do not replace its description. Write your emphasis and leave '{{DEFAULT}}' in the section, or write nothing but the marker. That is cheaper and it is always correct.\n\nReply with the complete template file and nothing else: no preamble, no explanation, no code fence.";
|
|
5
5
|
export declare function buildPromptTemplateReviewPrompt(defaultTemplate: string, familyTemplate: string | undefined, familyTemplateName: string | undefined,
|
|
6
6
|
/** What this model is, so a new template can be told how to claim itself. */
|
|
7
7
|
target?: {
|
|
@@ -202,6 +202,39 @@ export interface AuditPromptTemplateProposalArgs {
|
|
|
202
202
|
* audit cannot drift apart.
|
|
203
203
|
*/
|
|
204
204
|
export declare function findBatchedEditRules(system: string): string[];
|
|
205
|
+
/**
|
|
206
|
+
* Lines that describe a tool's result by its JSON fields.
|
|
207
|
+
*
|
|
208
|
+
* Results reach the model as text or as JSON, chosen per session, and the
|
|
209
|
+
* text form has no `query`, `result` or `success` in it: an entry is its query
|
|
210
|
+
* on one line, then `→ ` and what came back, or `→ failed: ` and why. A
|
|
211
|
+
* description that says "`success: false` means the file was not touched" is
|
|
212
|
+
* false for every text session. Every shipped template said it, because the
|
|
213
|
+
* built-in descriptions did and the review instructions asked for "the shape,
|
|
214
|
+
* what the fields hold". Exported for the same reason as
|
|
215
|
+
* `findBatchedEditRules`: the shipped-template test and the proposal audit
|
|
216
|
+
* must use one detector.
|
|
217
|
+
*/
|
|
218
|
+
export declare function findResultFieldClaims(text: string): string[];
|
|
219
|
+
/** Whether `own`, sent beside the marker, is longer than an addition should be. */
|
|
220
|
+
export declare function exceedsAdditionCap(own: string, builtin: string): boolean;
|
|
221
|
+
/**
|
|
222
|
+
* The built-in sentences, 40 characters or longer, that `own` repeats verbatim
|
|
223
|
+
* (ignoring whitespace and case). Next to '{{DEFAULT}}' each one is sent twice.
|
|
224
|
+
*/
|
|
225
|
+
export declare function findRestatedBuiltinSentences(own: string, builtin: string): string[];
|
|
226
|
+
/**
|
|
227
|
+
* Lines in a `run_commands` section that say a non-zero exit is not a failure.
|
|
228
|
+
*
|
|
229
|
+
* It is one: the command throws `CommandExitError`, the entry comes back
|
|
230
|
+
* failed with the exit in its reason, and a text session reads `→ failed:`
|
|
231
|
+
* above the output. deepseek-v4.1-flash wrote "has not failed as a tool call"
|
|
232
|
+
* and then "is not a failed tool call" on two separate runs on 2026-10-22,
|
|
233
|
+
* the second after being told the result format, and nothing flagged either.
|
|
234
|
+
* The point it means to make -- read the output, the error is in it -- is the
|
|
235
|
+
* right one, and saying it does not need the denial.
|
|
236
|
+
*/
|
|
237
|
+
export declare function findNonZeroExitDenials(text: string): string[];
|
|
205
238
|
export declare function auditPromptTemplateProposal(args: AuditPromptTemplateProposalArgs): PromptTemplateProposalAudit;
|
|
206
239
|
/**
|
|
207
240
|
* Hand a model its own file back with the list of what is wrong with it.
|
|
@@ -312,6 +345,13 @@ export interface GeneratePromptTemplateResult {
|
|
|
312
345
|
audit: PromptTemplateProposalAudit;
|
|
313
346
|
/** How many model calls it took. */
|
|
314
347
|
attempts: number;
|
|
348
|
+
/**
|
|
349
|
+
* Which of those calls produced `raw`. The same as `attempts` for a clean
|
|
350
|
+
* result; for one that never got clean it is the cleanest attempt, which
|
|
351
|
+
* is often an early one. The two used to share a field, and a run that
|
|
352
|
+
* spent four calls was logged as "NOT CLEAN after 1 attempt(s)".
|
|
353
|
+
*/
|
|
354
|
+
bestAttempt: number;
|
|
315
355
|
/**
|
|
316
356
|
* The compaction sections the file carries, and the ones that were removed
|
|
317
357
|
* because they still failed the check after the last attempt. Present only
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import type { BasicLogger, MessageWithMetadata } from "@cerebriline/shared";
|
|
1
|
+
import type { BasicLogger, MessageWithMetadata, ToolResultFormat } from "@cerebriline/shared";
|
|
2
2
|
import type { CoreCompactionContext, CoreCompactionResult, CoreCompactionSummarizerConfig } from "../../types/config";
|
|
3
3
|
import type { ProviderConfig } from "../../types/provider-settings";
|
|
4
4
|
import { type BudgetProjectionResult } from "./budget-projection";
|
|
@@ -126,4 +126,5 @@ export declare function describeReplaySpan(input: {
|
|
|
126
126
|
cutIndex: number;
|
|
127
127
|
keepRecentMessages: boolean;
|
|
128
128
|
pinnedMessage?: MessageWithMetadata;
|
|
129
|
+
toolResultFormat?: ToolResultFormat;
|
|
129
130
|
}): string;
|
|
@@ -17,9 +17,10 @@
|
|
|
17
17
|
* managed to make came back as. Short enough to be read, and framed as
|
|
18
18
|
* conclusions rather than as a transcript to resume.
|
|
19
19
|
*/
|
|
20
|
-
import type { BasicLogger, DiscardedTurnCondensation, DiscardedTurnInput, MessageWithMetadata } from "@cerebriline/shared";
|
|
20
|
+
import type { BasicLogger, DiscardedTurnCondensation, DiscardedTurnInput, MessageWithMetadata, ToolResultFormat } from "@cerebriline/shared";
|
|
21
21
|
import type { CoreCompactionSummarizerConfig } from "../../types/config";
|
|
22
22
|
import type { ProviderConfig } from "../../types/provider-settings";
|
|
23
|
+
import { type ToolLedgerMessage } from "./tool-ledger";
|
|
23
24
|
/**
|
|
24
25
|
* Whether this reasoning ended because the thinking budget ran out.
|
|
25
26
|
*
|
|
@@ -145,12 +146,26 @@ export declare const CAPPED_THINKING_SYSTEM_PROMPT = "You are condensing a train
|
|
|
145
146
|
* sized to the think and the room (`resolveCondensedThinkingNoteTokens`), so a
|
|
146
147
|
* replacement prompt gets it too.
|
|
147
148
|
*/
|
|
148
|
-
export declare const DEFAULT_CAPPED_THINKING_PROMPT = "Your thinking on this turn has reached its budget, and the reasoning below is about to be replaced by what you write now. Write the reasoning that takes its place.\n\nIt goes back into **your own thinking channel**, as the thinking you are in the middle of. Your next pass reads it as its own train of thought and carries on from the end of it, so write it the way you think: in the **first person, present continuous tense**, in plain prose, one thought after another, the same voice as the reasoning it replaces.\n\nWrite every step as the step itself, then what is coming back. These are the shapes \u2014 reuse
|
|
149
|
+
export declare const DEFAULT_CAPPED_THINKING_PROMPT = "Your thinking on this turn has reached its budget, and the reasoning below is about to be replaced by what you write now. Write the reasoning that takes its place.\n\nIt goes back into **your own thinking channel**, as the thinking you are in the middle of. Your next pass reads it as its own train of thought and carries on from the end of it, so write it the way you think: in the **first person, present continuous tense**, in plain prose, one thought after another, the same voice as the reasoning it replaces.\n\nWrite every step as the step itself, then what is coming back. These are the shapes, taken from a task that is not yours \u2014 reuse the shapes, and take every name, line and value from your own reasoning:\n\n- \"I'm working out why the invoice total comes out a cent short.\"\n- \"I'm reading `billing/invoice.py` around line 58, and the rounding there happens on every line item where it needs to happen once, on the sum.\"\n- \"I'm running the tests on it, and `test_invoice_total` is failing with 99.99 against 100.00.\"\n- \"I'm setting the currency conversion aside: the fault is in the rounding, and the conversion is sound.\"\n- \"The rounding is the cause, so what I'm doing next is editing line 58.\"\n\nEvery sentence holds that voice, the middle as much as the first and the last.\n\nCarry all of this:\n\n- **What you are being asked to do**, in a sentence.\n- **Every tool call in this turn**, by name, with the argument that matters and what it is returning: the file and what is at the line you are looking at, the edit and whether it is applying, the command and what it is printing. These results are not in front of your next pass any more, so this is the only place they survive.\n- **What you are treating as settled**, stated as settled.\n- **What you are setting aside, and why**, a clause each. This is what keeps the next pass off ground this one has already covered.\n- **Where you are**, ending on the one question still open and the single next action, stated concretely.\n\nName files and lines and say what you are concluding about them; the files themselves are on disk, and the next tool result is what shows their current state.\n\nBegin with the problem itself and end on the next action.";
|
|
149
150
|
interface ToolOutcome {
|
|
150
151
|
name: string;
|
|
151
152
|
input: string;
|
|
152
153
|
result: string;
|
|
153
154
|
}
|
|
155
|
+
/**
|
|
156
|
+
* What the task's tools have done so far, as the harness recorded each call:
|
|
157
|
+
* the compaction ledger, over the calls since the last compaction plus the
|
|
158
|
+
* ones that compaction carried.
|
|
159
|
+
*
|
|
160
|
+
* A note asked for in a request of its own sees the capped reasoning and the
|
|
161
|
+
* capped turn's own calls, and a capped turn usually has none that ran -- its
|
|
162
|
+
* call is the part the cap cut. Reasoning rarely names the file it is about,
|
|
163
|
+
* so the note filled the gap itself: on pandorum (lfw1t, 2026-10-20) a 34,572-
|
|
164
|
+
* character think that named no file came back as a note about `game.js` in a
|
|
165
|
+
* task on `manic_miner.html`. The record is the harness's account, not the
|
|
166
|
+
* model's, so the names and results in it are the ones that happened.
|
|
167
|
+
*/
|
|
168
|
+
export declare function recordedToolCalls(messages: readonly ToolLedgerMessage[], format?: ToolResultFormat): string;
|
|
154
169
|
/**
|
|
155
170
|
* Why a look for a capped turn came back with nothing.
|
|
156
171
|
*
|
|
@@ -193,6 +208,13 @@ export declare function buildCappedThinkingRequest(options: {
|
|
|
193
208
|
partialAnswer?: string;
|
|
194
209
|
/** The note's length target, in tokens (`resolveCondensedThinkingNoteTokens`). */
|
|
195
210
|
noteTokens?: number;
|
|
211
|
+
/**
|
|
212
|
+
* What the user typed. Only for a request of its own: the conversation's
|
|
213
|
+
* next turn already has them in front of it.
|
|
214
|
+
*/
|
|
215
|
+
userRequests?: readonly string[];
|
|
216
|
+
/** The task's tool record (`recordedToolCalls`), on the same terms. */
|
|
217
|
+
toolRecord?: string;
|
|
196
218
|
}): string;
|
|
197
219
|
export interface CappedThinkingCondenserConfig {
|
|
198
220
|
enabled?: boolean;
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import type { ModelInfo, ReasoningHistoryMode, ToolResultContent } from "@cerebriline/llms";
|
|
2
2
|
import { resolveDefaultMaxOutputTokens } from "@cerebriline/llms";
|
|
3
|
-
import { CHARS_PER_TOKEN, estimateThinkingTokens, estimateTokens, type MessageWithMetadata } from "@cerebriline/shared";
|
|
3
|
+
import { asStructuredToolResultEntry, CHARS_PER_TOKEN, estimateThinkingTokens, estimateTokens, type MessageWithMetadata, type StructuredToolResultEntry, type ToolResultFormat } from "@cerebriline/shared";
|
|
4
4
|
export { CHARS_PER_TOKEN, estimateThinkingTokens, estimateTokens };
|
|
5
5
|
import type { CoreCompactionSummarizerConfig } from "../../types/config";
|
|
6
6
|
import type { ProviderConfig } from "../../types/provider-settings";
|
|
@@ -50,6 +50,15 @@ export declare const MAX_SUMMARY_OUTPUT_TOKENS = 8192;
|
|
|
50
50
|
export declare const SUMMARY_OUTPUT_WINDOW_SHARE = 0.08;
|
|
51
51
|
export declare const TOOL_RESULT_CHAR_LIMIT = 2000;
|
|
52
52
|
export declare const FILE_CONTENT_CHAR_LIMIT = 2000;
|
|
53
|
+
/**
|
|
54
|
+
* The longest a single argument of a tool call is written into a transcript
|
|
55
|
+
* pasted into a compaction request. Reasoning and file contents already had a
|
|
56
|
+
* bound there and arguments did not: f099u (pandorum, 2026-10-22) put two
|
|
57
|
+
* refused edits of 141,009 and 272,888 characters into every summary and
|
|
58
|
+
* reviewer request, 571,425 characters each. What a call was for is in its
|
|
59
|
+
* other arguments; what it wrote is in the file, or was never applied.
|
|
60
|
+
*/
|
|
61
|
+
export declare const TOOL_INPUT_VALUE_CHAR_LIMIT = 4000;
|
|
53
62
|
export declare const MIN_TRUNCATED_MESSAGE_TOKENS = 8;
|
|
54
63
|
export declare function resolveSummaryMaxOutputTokens(maxInputTokens: number | undefined): number;
|
|
55
64
|
export interface CompactionOutputBudgets {
|
|
@@ -287,17 +296,28 @@ export declare function truncateText(text: string, limit: number): string;
|
|
|
287
296
|
* happened, so a session whose checker returned `ok:false` three times was
|
|
288
297
|
* summarised as "the game is running" and the next context believed it.
|
|
289
298
|
*/
|
|
290
|
-
export
|
|
291
|
-
|
|
292
|
-
|
|
293
|
-
|
|
294
|
-
|
|
299
|
+
export type { StructuredToolResultEntry };
|
|
300
|
+
export { asStructuredToolResultEntry };
|
|
301
|
+
/**
|
|
302
|
+
* How a session's tool results are rendered, for the parts of compaction the
|
|
303
|
+
* model reads: the summarizer's transcript, the call record, a condensed
|
|
304
|
+
* note's outcomes. Absent, or `json`, is the rendering they always had.
|
|
305
|
+
*/
|
|
306
|
+
export interface ToolResultRendering {
|
|
307
|
+
format?: ToolResultFormat;
|
|
308
|
+
/** The block's `serialized` mark, so a codec-written string is parsed back. */
|
|
309
|
+
serialized?: string;
|
|
295
310
|
}
|
|
296
|
-
export declare function
|
|
297
|
-
export declare function flattenToolResultContent(content: ToolResultContent["content"]): string;
|
|
311
|
+
export declare function flattenToolResultContent(content: ToolResultContent["content"], rendering?: ToolResultRendering): string;
|
|
298
312
|
export declare function truncateToolResultContentForCompaction(content: ToolResultContent["content"]): ToolResultContent["content"];
|
|
299
313
|
export declare function formatToolInput(input: Record<string, unknown>): string;
|
|
300
|
-
|
|
314
|
+
/**
|
|
315
|
+
* A call's arguments for a transcript: each string value past
|
|
316
|
+
* {@link TOOL_INPUT_VALUE_CHAR_LIMIT} keeps its beginning and its end, and
|
|
317
|
+
* says how much of it was left out.
|
|
318
|
+
*/
|
|
319
|
+
export declare function formatToolInputForTranscript(input: Record<string, unknown>): string;
|
|
320
|
+
export declare function serializeMessage(message: MessageWithMetadata, format?: ToolResultFormat): string;
|
|
301
321
|
/**
|
|
302
322
|
* Render the discarded reasoning against what it produced.
|
|
303
323
|
*
|
|
@@ -311,8 +331,8 @@ export declare function serializeMessage(message: MessageWithMetadata): string;
|
|
|
311
331
|
* about method has no use for the contents of a file, and on the transcripts this
|
|
312
332
|
* was built against the results are most of the bytes.
|
|
313
333
|
*/
|
|
314
|
-
export declare function serializeReasoningWithOutcomes(messages: MessageWithMetadata[]): string;
|
|
315
|
-
export declare function serializeConversation(messages: MessageWithMetadata[]): string;
|
|
334
|
+
export declare function serializeReasoningWithOutcomes(messages: MessageWithMetadata[], format?: ToolResultFormat): string;
|
|
335
|
+
export declare function serializeConversation(messages: MessageWithMetadata[], format?: ToolResultFormat): string;
|
|
316
336
|
export declare function createTokenEstimator(): EstimateMessageTokens;
|
|
317
337
|
export declare function isCompactionSummaryMessage(message: MessageWithMetadata): boolean;
|
|
318
338
|
/**
|
|
@@ -364,6 +384,20 @@ export declare function isTurnStartMessage(message: MessageWithMetadata): boolea
|
|
|
364
384
|
*/
|
|
365
385
|
/** The most any single quoted request may take before its middle is cut. */
|
|
366
386
|
export declare const MAX_USER_REQUEST_CHARS = 2000;
|
|
387
|
+
/**
|
|
388
|
+
* Every instruction, as fully as the budget allows.
|
|
389
|
+
*
|
|
390
|
+
* Nothing is dropped, because a missing instruction is the one loss this block
|
|
391
|
+
* exists to prevent and a reader cannot tell an absent one from one that was
|
|
392
|
+
* never given. What happens under pressure instead is elision: each request
|
|
393
|
+
* keeps its head and its tail and says in the middle how much was cut, which
|
|
394
|
+
* is a statement a reader can act on.
|
|
395
|
+
*
|
|
396
|
+
* The share is spread evenly rather than favouring the newest. The standing
|
|
397
|
+
* task is usually the first thing typed and the steering is usually the last,
|
|
398
|
+
* and there is no general rule about which of them the next turn needs.
|
|
399
|
+
*/
|
|
400
|
+
export declare function renderUserRequests(requests: readonly string[], budgetChars?: number): string[];
|
|
367
401
|
export declare function collectUserRequests(messages: readonly MessageWithMetadata[]): string[];
|
|
368
402
|
export declare function findFirstUserMessageIndex(messages: MessageWithMetadata[]): number;
|
|
369
403
|
export declare function findLastTurnStartIndex(messages: MessageWithMetadata[]): number;
|
|
@@ -724,6 +758,8 @@ export declare function seedCalibrationFromTranscript(request: {
|
|
|
724
758
|
messages: MessageWithMetadata[];
|
|
725
759
|
tools?: unknown[];
|
|
726
760
|
reasoningHistory?: ReasoningHistoryMode;
|
|
761
|
+
/** As the request path renders tool results; see `ToolResultFormat`. */
|
|
762
|
+
toolResultFormat?: ToolResultFormat;
|
|
727
763
|
/** Whose transcript this is, so the seeded count answers only to it. */
|
|
728
764
|
sessionId?: string;
|
|
729
765
|
}): void;
|
|
@@ -200,7 +200,7 @@ export declare function resolveMessageTargetOnTriggerScale(input: {
|
|
|
200
200
|
export declare function scaleEstimateToObserved(estimate: number, estimateOfObserved: number, observed: number | undefined): number;
|
|
201
201
|
/** Test seam: the latch is process-wide, so a suite has to be able to clear it. */
|
|
202
202
|
export declare function resetStarvedOutputCapLatch(): void;
|
|
203
|
-
export declare function createContextCompactionPrepareTurn(config: Pick<CoreSessionConfig, "providerConfig" | "providerId" | "modelId" | "apiKey" | "baseUrl" | "headers" | "compaction" | "logger" | "telemetry" | "sessionId"> & Partial<Pick<CoreSessionConfig, "thinking" | "reasoningEffort" | "thinkingBudgetTokens" | "temperature">>, options?: ContextCompactionPrepareTurnOptions): ((context: ContextPipelinePrepareTurnInput) => Promise<ContextPipelinePrepareTurnResult | undefined>) | undefined;
|
|
203
|
+
export declare function createContextCompactionPrepareTurn(config: Pick<CoreSessionConfig, "providerConfig" | "providerId" | "modelId" | "apiKey" | "baseUrl" | "headers" | "compaction" | "logger" | "telemetry" | "sessionId"> & Partial<Pick<CoreSessionConfig, "thinking" | "reasoningEffort" | "thinkingBudgetTokens" | "temperature" | "toolResultFormat">>, options?: ContextCompactionPrepareTurnOptions): ((context: ContextPipelinePrepareTurnInput) => Promise<ContextPipelinePrepareTurnResult | undefined>) | undefined;
|
|
204
204
|
/**
|
|
205
205
|
* Compaction policy for resuming a session imported from another coding agent.
|
|
206
206
|
* The imported transcript keeps that agent's tool names and input schemas
|
|
@@ -34,7 +34,7 @@
|
|
|
34
34
|
* was given. A council that cannot run leaves an unreviewed summary, which
|
|
35
35
|
* is exactly what shipped before it existed.
|
|
36
36
|
*/
|
|
37
|
-
import type { BasicLogger, MessageWithMetadata } from "@cerebriline/shared";
|
|
37
|
+
import type { BasicLogger, MessageWithMetadata, ToolResultFormat } from "@cerebriline/shared";
|
|
38
38
|
/** What a reviewer and the synthesiser are told they are. */
|
|
39
39
|
export declare const COUNCIL_SYSTEM_PROMPTS: {
|
|
40
40
|
readonly critic: "You are rewriting one half of an account of your own work, with the record of that work in front of you. Correct what the record contradicts, add what it shows missing, fix every quotation against it, and improve the prose where it has drifted out of voice. You return your half and only your half: another writer is rewriting the other one, and the two will be joined into a single continuous replay.";
|
|
@@ -200,6 +200,8 @@ export declare function runCouncilReview(input: {
|
|
|
200
200
|
summary: string;
|
|
201
201
|
thinkingSummary?: string;
|
|
202
202
|
messages: readonly MessageWithMetadata[];
|
|
203
|
+
/** How the conversation's tool results read; the critics see the same. */
|
|
204
|
+
toolResultFormat?: ToolResultFormat;
|
|
203
205
|
/**
|
|
204
206
|
* One model call. Throwing is allowed and is handled as a decline.
|
|
205
207
|
*
|
|
@@ -55,7 +55,7 @@
|
|
|
55
55
|
* and any disagreement is visible rather than silent.
|
|
56
56
|
*/
|
|
57
57
|
/** The instruction, when the recency tail is kept. */
|
|
58
|
-
export declare const DEFAULT_REPLAY_COMPACTION_PROMPT = "Your transcript has grown too long and the earlier part of it is about to be discarded. Write the replay that takes its place.\n\nYour replay will be **prepended directly to the messages that remain** \u2014 your own most recent turns, which are still there and which you will read immediately after this. Write it so that seam is invisible.\n\nWrite in the **first person, present continuous tense**, in your own voice, in the same prose as the turns it sits in front of. You are not reporting on a session to someone else and you are not recounting something finished: you are picking the work back up, and everything in the replay is the situation as it stands right now.\n\nWrite **every step** as the step itself, then what came back. The opening of a\nstep is what you are about to do; the outcome is its own short sentence after\nit. These are the shapes \u2014 reuse
|
|
58
|
+
export declare const DEFAULT_REPLAY_COMPACTION_PROMPT = "Your transcript has grown too long and the earlier part of it is about to be discarded. Write the replay that takes its place.\n\nYour replay will be **prepended directly to the messages that remain** \u2014 your own most recent turns, which are still there and which you will read immediately after this. Write it so that seam is invisible.\n\nWrite in the **first person, present continuous tense**, in your own voice, in the same prose as the turns it sits in front of. You are not reporting on a session to someone else and you are not recounting something finished: you are picking the work back up, and everything in the replay is the situation as it stands right now.\n\nWrite **every step** as the step itself, then what came back. The opening of a\nstep is what you are about to do; the outcome is its own short sentence after\nit. These are the shapes, taken from a task that is not yours \u2014 reuse the\nshapes, and take every name, line and value from your own transcript:\n\n- \"The user is asking me to fix the invoice rounding.\"\n- \"Let me start by reading the file.\"\n- \"Let me read a large chunk of the file.\" \u2014 then what it showed.\n- \"Let me try rewriting the totals function. It failed: `test_invoice_total` expects 100.00 and is getting 99.99.\"\n- \"Now the editor is warning me this change shifted the line numbers by +11.\"\n- \"The tests pass now, so what is left is the currency conversion.\"\n\nThis holds for the middle of the replay and not only its first and last\nsentences. A step that opens by reporting itself has changed voice, and the\nseam this replay exists to remove is back.\n\nWritten in the past tense the replay reads as history, and history is something you are entitled to doubt: you will re-read files you already know, re-derive what you have already settled, and treat a warning that is still in force as something that merely once happened. Written in the present it is the state of play, which is what it actually is. Every sentence, not only the first and the last.\n\nCarry all of this:\n\n- **What you are asked to do, verbatim.** Quote the instructions you have been given word for word. Everything else here can be rebuilt from the files or from the record below your replay; what was asked exists nowhere else once these messages are gone.\n- **What you have done, in order.** Narrate it, and **cite the calls instead of writing them out.** Every call you made, with its arguments and what it returned, is numbered in the record below. Where a call belongs in your account, put its number in square brackets with a `#`, and carry on:\n\n \"Let me run the tests. [#3] `test_invoice_total` fails at line 58.\"\n\n The harness puts call 3 in at that point, exactly as it happened. Writing the call out yourself instead would spend your budget copying a record that is already exact, and yours would be the less accurate of the two. Cite each call once, in the order you made them. Where a run of calls in sequence belongs at the same point \u2014 the same check repeated, a read and the edit that followed it \u2014 cite the span as `[#2-5]` rather than one at a time; the harness puts all of them in there. A number you leave out is not lost \u2014 it is added at the end \u2014 but then it sits away from the step it belongs to.\n\n **The record is complete and closed.** Every call you have made is in it, and nothing else is. The highest number in it is the last call you made, so there is no `[#N]` past the end to cite \u2014 if you find yourself reaching for one, the step you are about to describe is a step you have not taken yet, and it does not belong in an account of what you have done. Cite only a number that is actually in the record, and leave a step with no call behind it uncited rather than attaching it to a number that names someone else's work.\n- **What is coming back wrong**, and what you are doing about it. A call that was refused or returned an error is the most important kind to keep \u2014 without it you will simply make it again.\n- **What you have concluded**, including anything you have ruled out and why. An approach that failed, omitted here, is an approach you will try again.\n- **Where you are now**, and what you are about to do next.\n\nDo not invent anything you are not sure of. In particular, do not report a result you cannot see: a call whose output is not in front of you is a call whose outcome you do not know, and writing that it succeeded is the one error here that the next turn cannot recover from.\n\nDo not write instructions to yourself to rewrite or restore content that this replay does not itself contain \u2014 you would be reconstructing from memory something that is still on disk, and producing a worse version of it.\n\nWrite the replay and stop. Do not continue the transcript that follows these instructions, and do not copy any part of it back: it is what you are replacing.";
|
|
59
59
|
export interface CutTranscript {
|
|
60
60
|
text: string;
|
|
61
61
|
/** How much of the summary was the request, copied back. */
|
|
@@ -27,6 +27,7 @@
|
|
|
27
27
|
* **Legibility.** The reader is a small model that has just lost its memory.
|
|
28
28
|
* One line per call, the failures marked, no JSON it has to parse.
|
|
29
29
|
*/
|
|
30
|
+
import { type ToolResultFormat } from "@cerebriline/shared";
|
|
30
31
|
/**
|
|
31
32
|
* Any message with a content array, in either of the two block shapes this
|
|
32
33
|
* repository carries — see {@link normaliseCall}.
|
|
@@ -90,6 +91,11 @@ export interface ToolLedgerLimits {
|
|
|
90
91
|
export declare const DEFAULT_TOOL_LEDGER_LIMITS: ToolLedgerLimits;
|
|
91
92
|
export interface ToolLedgerOptions {
|
|
92
93
|
limits?: Partial<ToolLedgerLimits>;
|
|
94
|
+
/**
|
|
95
|
+
* Under `text`, a result the transcript codec stored as JSON is read back
|
|
96
|
+
* into the entry it was, so the key quotes the result and not its escapes.
|
|
97
|
+
*/
|
|
98
|
+
toolResultFormat?: ToolResultFormat;
|
|
93
99
|
}
|
|
94
100
|
/**
|
|
95
101
|
* The revisions held for one file, as a span like `#1–#7`.
|
|
@@ -133,6 +139,12 @@ export declare function mergeToolLedger(previous: readonly ToolLedgerEntry[], ne
|
|
|
133
139
|
* something that did.
|
|
134
140
|
*/
|
|
135
141
|
export declare function evictToolLedger(entries: readonly ToolLedgerEntry[], budgetChars: number): ToolLedgerEntry[];
|
|
142
|
+
/**
|
|
143
|
+
* Which entries `evictToolLedger` keeps, by position, for a caller that has
|
|
144
|
+
* to keep its own numbering: the compaction cites a fold by the numbers it
|
|
145
|
+
* showed the writer, and stores only what survives.
|
|
146
|
+
*/
|
|
147
|
+
export declare function toolLedgerSurvivors(entries: readonly ToolLedgerEntry[], budgetChars: number): boolean[];
|
|
136
148
|
/**
|
|
137
149
|
* The ledger as the model will read it.
|
|
138
150
|
*
|
|
@@ -154,7 +166,7 @@ export declare function renderToolLedgerEntry(entry: ToolLedgerEntry): string;
|
|
|
154
166
|
* the tail, so the full ledger hands back a fragment the transcript had
|
|
155
167
|
* deliberately cut.
|
|
156
168
|
*/
|
|
157
|
-
export declare function renderToolLedgerKey(entries: readonly ToolLedgerEntry[]): string;
|
|
169
|
+
export declare function renderToolLedgerKey(entries: readonly ToolLedgerEntry[], maxChars?: number): string;
|
|
158
170
|
export interface LedgerSpliceResult {
|
|
159
171
|
text: string;
|
|
160
172
|
/** Entries the replay placed itself, in the order it placed them. */
|
|
@@ -26,6 +26,8 @@ export interface CheckOnFailureOptions {
|
|
|
26
26
|
checker: Pick<AgentTool<{
|
|
27
27
|
paths: string[];
|
|
28
28
|
}, unknown>, "execute">;
|
|
29
|
+
/** Told which files the checker reported on, so they count as checked. */
|
|
30
|
+
onChecked?: (paths: readonly string[]) => void;
|
|
29
31
|
}
|
|
30
32
|
/**
|
|
31
33
|
* Wrap a shell tool so failing commands come back with the checker's verdict.
|
|
@@ -46,4 +48,5 @@ export declare function joinCheckerToShell<TTool extends AgentTool<never, unknow
|
|
|
46
48
|
cwd: string;
|
|
47
49
|
shellName: string;
|
|
48
50
|
checkerName: string;
|
|
51
|
+
onChecked?: (paths: readonly string[]) => void;
|
|
49
52
|
}): TTool[];
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import type
|
|
1
|
+
import { type AgentTool } from "@cerebriline/shared";
|
|
2
2
|
/**
|
|
3
3
|
* Files the model changed and has not checked since.
|
|
4
4
|
*
|
|
@@ -35,6 +35,14 @@ export interface EditVerificationConfig {
|
|
|
35
35
|
* aside: the model has been told twice and the work is the user's to judge.
|
|
36
36
|
*/
|
|
37
37
|
attempts?: number;
|
|
38
|
+
/** Where relative paths resolve from, so `a.ts` and `/ws/a.ts` are one file. */
|
|
39
|
+
cwd?: string;
|
|
40
|
+
/**
|
|
41
|
+
* Whether a file is still on disk. A file the model deleted has nothing
|
|
42
|
+
* left to check: session y1t7j was asked to check six scratch scripts it
|
|
43
|
+
* had removed, and `check_file` called them clean.
|
|
44
|
+
*/
|
|
45
|
+
fileExists?: (absolutePath: string) => boolean;
|
|
38
46
|
}
|
|
39
47
|
export declare const EDIT_VERIFICATION_ATTEMPTS = 2;
|
|
40
48
|
/** Files edited since they were last checked, in the order they were edited. */
|
|
@@ -44,10 +52,12 @@ export interface EditVerificationState {
|
|
|
44
52
|
/**
|
|
45
53
|
* Every file path an argument bag names, whatever shape it names them in.
|
|
46
54
|
*
|
|
47
|
-
* `editor` sends `
|
|
48
|
-
* `files: [{ path }]`. Reading one shape would
|
|
49
|
-
* is the failure mode this whole module exists
|
|
50
|
-
* read and anything unrecognised contributes
|
|
55
|
+
* `editor` sends `file_path` (`path` before 2026-10-20), `check_file` sends
|
|
56
|
+
* `paths`, and `read_files` sends `files: [{ path }]`. Reading one shape would
|
|
57
|
+
* silently cover one tool, which is the failure mode this whole module exists
|
|
58
|
+
* to answer, so all of them are read and anything unrecognised contributes
|
|
59
|
+
* nothing. Read as `path` alone, no editor edit was tracked after the rename
|
|
60
|
+
* and the unchecked-edit guard never fired.
|
|
51
61
|
*/
|
|
52
62
|
export declare function extractPaths(input: unknown): string[];
|
|
53
63
|
/** Tracks which edited files are still unverified. */
|
|
@@ -56,10 +66,21 @@ export declare class EditVerificationTracker {
|
|
|
56
66
|
private readonly checkTools;
|
|
57
67
|
/** Normalised path → the path as the model wrote it, for the message. */
|
|
58
68
|
private readonly unchecked;
|
|
69
|
+
private readonly cwd?;
|
|
70
|
+
private readonly fileExists?;
|
|
59
71
|
constructor(config: EditVerificationConfig);
|
|
72
|
+
private absolute;
|
|
73
|
+
private key;
|
|
60
74
|
/** Whether this tracker can ever have anything to say. */
|
|
61
75
|
get armed(): boolean;
|
|
62
76
|
recordToolCall(toolName: string, input: unknown): void;
|
|
77
|
+
/**
|
|
78
|
+
* Files a checker reported on outside a checker call: the one a failing
|
|
79
|
+
* command runs on the file it names. Its report is in the model's hands
|
|
80
|
+
* either way (session y1t7j ran it after its last edit, clean, and was
|
|
81
|
+
* still asked to check the file).
|
|
82
|
+
*/
|
|
83
|
+
recordChecked(paths: readonly string[]): void;
|
|
63
84
|
getState(): EditVerificationState;
|
|
64
85
|
reset(): void;
|
|
65
86
|
}
|
|
@@ -15,6 +15,13 @@ export interface DetachedCommandLogCleanupOptions {
|
|
|
15
15
|
processStartTokenProbe?: ProcessStartTokenProbe;
|
|
16
16
|
activeCommandPollIntervalMs?: number;
|
|
17
17
|
}
|
|
18
|
+
type DetachedCommandMarker = {
|
|
19
|
+
version: 1;
|
|
20
|
+
executionId: string;
|
|
21
|
+
pid: number;
|
|
22
|
+
processStartToken: string;
|
|
23
|
+
detachedAtMs: number;
|
|
24
|
+
};
|
|
18
25
|
/**
|
|
19
26
|
* Reaps completed detached command logs outside the retention window,
|
|
20
27
|
* schedules retained logs for their remaining lifetime, and follows active
|
|
@@ -97,6 +104,14 @@ export interface ShellExecutorOptions {
|
|
|
97
104
|
* say what the command did to what the model believes.
|
|
98
105
|
*/
|
|
99
106
|
receipts?: ReadReceipts;
|
|
107
|
+
/**
|
|
108
|
+
* A delegated agent's overlay, so the sweep above stamps the agent's copy
|
|
109
|
+
* of a file rather than the workspace file its commands no longer write.
|
|
110
|
+
* Only resolving is needed: the command itself runs through `wrapSpawn`.
|
|
111
|
+
*/
|
|
112
|
+
overlay?: {
|
|
113
|
+
resolveRead(filePath: string): Promise<string>;
|
|
114
|
+
};
|
|
100
115
|
/**
|
|
101
116
|
* Optional host-scoped controller that can release an in-flight command
|
|
102
117
|
* from its tool call while continuing to drain output to a bounded log.
|
|
@@ -129,6 +144,12 @@ export interface ShellExecutorOptions {
|
|
|
129
144
|
/** Process identity provider used to enable safe command detachment. */
|
|
130
145
|
processStartTokenProbe?: ProcessStartTokenProbe;
|
|
131
146
|
}
|
|
147
|
+
/** Exported for its own tests: the order of its writes is the contract. */
|
|
148
|
+
export declare function createDetachedLog(retentionMs: number, marker: DetachedCommandMarker): {
|
|
149
|
+
path: string;
|
|
150
|
+
write(text: string): void;
|
|
151
|
+
complete: () => void;
|
|
152
|
+
};
|
|
132
153
|
/**
|
|
133
154
|
* Create a shell executor using Node.js spawn
|
|
134
155
|
*
|
|
@@ -143,3 +164,4 @@ export interface ShellExecutorOptions {
|
|
|
143
164
|
* ```
|
|
144
165
|
*/
|
|
145
166
|
export declare function createShellExecutor(options?: ShellExecutorOptions): ShellExecutor;
|
|
167
|
+
export {};
|
|
@@ -5,6 +5,12 @@ import type { ImageCollector } from "./images";
|
|
|
5
5
|
export type DocumentFormat = "pdf" | "docx" | "pptx" | "xlsx" | "odt" | "odp" | "ods" | "odg" | "rtf" | "csv" | "html" | "epub" | "mobi" | "azw3" | "fb2" | "doc" | "xls" | "ppt";
|
|
6
6
|
/** The names a format is known by, and the reader it goes to. */
|
|
7
7
|
export declare const FORMAT_LABELS: Record<DocumentFormat, string>;
|
|
8
|
+
/** Whether bytes are gzip: a book is often kept as `book.fb2.gz`. */
|
|
9
|
+
export declare function isGzip(head: Uint8Array): boolean;
|
|
10
|
+
/** A name without the `.gz` a gzipped document carries. */
|
|
11
|
+
export declare function ungzippedName(filePath: string): string;
|
|
12
|
+
/** The extension that says what a file is, under any `.gz` wrapped round it. */
|
|
13
|
+
export declare function documentExtension(filePath: string): string;
|
|
8
14
|
/** Every extension this tool reads, for the tool description and read_files. */
|
|
9
15
|
export declare const DOCUMENT_EXTENSIONS: readonly string[];
|
|
10
16
|
export type FormatVerdict = {
|
|
@@ -18,6 +18,18 @@
|
|
|
18
18
|
*/
|
|
19
19
|
export type FileStamp = string;
|
|
20
20
|
export declare function readFileStamp(filePath: string): Promise<FileStamp | undefined>;
|
|
21
|
+
/**
|
|
22
|
+
* The stamp of a file as the caller sees it: through an agent's overlay, the
|
|
23
|
+
* copy the agent is working on once it has one, else the workspace file.
|
|
24
|
+
*
|
|
25
|
+
* Everything that stamps a file has to stamp the same one. The editor stamps
|
|
26
|
+
* the overlay copy it writes; a `grep` or a command that stamped the workspace
|
|
27
|
+
* file beside it left two stamps that could never agree, and the agent's next
|
|
28
|
+
* edit was refused as "something outside this session wrote to it" (ol-22).
|
|
29
|
+
*/
|
|
30
|
+
export declare function fileStampFor(filePath: string, overlay?: {
|
|
31
|
+
resolveRead(filePath: string): Promise<string>;
|
|
32
|
+
}): Promise<FileStamp | undefined>;
|
|
21
33
|
export interface ReadReceipts {
|
|
22
34
|
/** Record that `filePath` was read over the given inclusive line span. */
|
|
23
35
|
noteRead(filePath: string, first: number, last: number): void;
|
|
@@ -20,6 +20,18 @@ export declare function getReadFileRangeError(request: ReadFileRequest): string
|
|
|
20
20
|
* orphan range entries into the preceding file entry before validation.
|
|
21
21
|
*/
|
|
22
22
|
export declare function coalesceOrphanReadRanges(input: unknown): unknown;
|
|
23
|
+
/**
|
|
24
|
+
* Hand options written beside the file list to the files in it.
|
|
25
|
+
*
|
|
26
|
+
* Measured on pandorum (klbbz): `{end_line: 120, files: [{path, start_line: 80}]}`.
|
|
27
|
+
* The schema keeps only what is on each entry, so the `end_line` was dropped
|
|
28
|
+
* without a word and the read ran from line 80 to the end of the file. A
|
|
29
|
+
* `line_numbers: false` written there was dropped the same way, and a model
|
|
30
|
+
* that asked for clean text to copy into an edit got numbered lines instead.
|
|
31
|
+
* Each entry keeps any option it sets itself; only the ones it leaves out are
|
|
32
|
+
* taken from beside the list.
|
|
33
|
+
*/
|
|
34
|
+
export declare function applySharedReadOptions(input: unknown): unknown;
|
|
23
35
|
/**
|
|
24
36
|
* Read a path field whose value is a list rendered as a string.
|
|
25
37
|
*
|
|
@@ -38,7 +38,7 @@ import { type AgentTool } from "@cerebriline/shared";
|
|
|
38
38
|
* and it reaches for `ls` for exactly the same reason.
|
|
39
39
|
*/
|
|
40
40
|
export declare const LIST_FILES_TOOL_NAME = "list_files";
|
|
41
|
-
export declare const LIST_FILES_TOOL_DESCRIPTION = "List the files in the workspace. Use this to find out what exists \u2014 do not run `ls`, `dir`, `find` or `Get-ChildItem` through `run_commands` to look around, because this is scoped to the workspace and those are not.\n\nTwo ways to ask:\n- `path` \u2014 list what is directly inside one directory. Omit it to list the workspace root.\n- `pattern` \u2014 a glob searched across the whole workspace, e.g. `**/*.html`, `src/**/*.ts`, `**/
|
|
41
|
+
export declare const LIST_FILES_TOOL_DESCRIPTION = "List the files in the workspace. Use this to find out what exists \u2014 do not run `ls`, `dir`, `find` or `Get-ChildItem` through `run_commands` to look around, because this is scoped to the workspace and those are not.\n\nTwo ways to ask:\n- `path` \u2014 list what is directly inside one directory. Omit it to list the workspace root.\n- `pattern` \u2014 a glob searched across the whole workspace, e.g. `**/*.html`, `src/**/*.ts`, `**/config.*`. Use this when you know part of a name but not where it lives.\n\nResults are limited to the workspace folders the user opened, and directories the user's settings exclude from search \u2014 `node_modules`, `.git`, build output \u2014 are left out. A path outside the workspace is refused rather than listed.\n\nThis tells you which files exist, not what is in them. To find files by their contents use `search_codebase`, which reports the line each match is on and is the right way to locate the part of a file worth reading.";
|
|
42
42
|
export declare const LIST_FILES_TOOL_INPUT_SCHEMA: {
|
|
43
43
|
readonly type: "object";
|
|
44
44
|
readonly properties: {
|
|
@@ -651,6 +651,38 @@ export declare const EditFileInputSchema: z.ZodObject<{
|
|
|
651
651
|
occurrence: z.ZodOptional<z.ZodNullable<z.ZodCoercedNumber<unknown>>>;
|
|
652
652
|
replace_all: z.ZodOptional<z.ZodNullable<z.ZodPreprocess<z.ZodBoolean>>>;
|
|
653
653
|
}, z.core.$strip>;
|
|
654
|
+
/**
|
|
655
|
+
* The editor's arguments as the model is shown them and writes them.
|
|
656
|
+
*
|
|
657
|
+
* The same arguments as {@link EditFileInputSchema}, with the file named
|
|
658
|
+
* `file_path` rather than `path` and listed first. `file_path` sorts before
|
|
659
|
+
* `new_text`, which `path` did not, and a template that renders a call's
|
|
660
|
+
* arguments by name puts it there: see `editorFilePath` in `@cerebriline/shared`
|
|
661
|
+
* for the runs this comes from. The executor still takes `path`;
|
|
662
|
+
* {@link toEditFileInput} is the one place the two meet.
|
|
663
|
+
*/
|
|
664
|
+
export declare const EditorToolInputSchema: z.ZodObject<{
|
|
665
|
+
occurrence: z.ZodOptional<z.ZodNullable<z.ZodCoercedNumber<unknown>>>;
|
|
666
|
+
start_line: z.ZodOptional<z.ZodNullable<z.ZodCoercedNumber<unknown>>>;
|
|
667
|
+
end_line: z.ZodOptional<z.ZodNullable<z.ZodCoercedNumber<unknown>>>;
|
|
668
|
+
new_text: z.ZodString;
|
|
669
|
+
old_text: z.ZodOptional<z.ZodNullable<z.ZodString>>;
|
|
670
|
+
start_column: z.ZodOptional<z.ZodNullable<z.ZodCoercedNumber<unknown>>>;
|
|
671
|
+
end_column: z.ZodOptional<z.ZodNullable<z.ZodCoercedNumber<unknown>>>;
|
|
672
|
+
insert_line: z.ZodOptional<z.ZodNullable<z.ZodCoercedNumber<unknown>>>;
|
|
673
|
+
insert_column: z.ZodOptional<z.ZodNullable<z.ZodCoercedNumber<unknown>>>;
|
|
674
|
+
replace_all: z.ZodOptional<z.ZodNullable<z.ZodPreprocess<z.ZodBoolean>>>;
|
|
675
|
+
file_path: z.ZodString;
|
|
676
|
+
}, z.core.$strip>;
|
|
677
|
+
export type EditorToolInput = z.infer<typeof EditorToolInputSchema>;
|
|
678
|
+
/**
|
|
679
|
+
* A call that names its file with the old `path` is still aimed at that
|
|
680
|
+
* file. Applied before validation, like {@link withNewTextAlias}, so the
|
|
681
|
+
* schema the model is shown names one argument. `file_path` wins when both
|
|
682
|
+
* are present.
|
|
683
|
+
*/
|
|
684
|
+
export declare function withFilePathAlias(value: unknown): unknown;
|
|
685
|
+
export declare function toEditFileInput(input: EditorToolInput): EditFileInput;
|
|
654
686
|
/**
|
|
655
687
|
* Schema for apply_patch tool input
|
|
656
688
|
*/
|