@cerebriline/core 4.100.274 → 4.100.277

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.
Files changed (76) hide show
  1. package/assets/prompt-templates/claude.md +7 -7
  2. package/assets/prompt-templates/deepseek.md +8 -8
  3. package/assets/prompt-templates/default.md +14 -14
  4. package/assets/prompt-templates/gemma.md +5 -5
  5. package/assets/prompt-templates/glm.md +9 -9
  6. package/assets/prompt-templates/kimi-k3.md +8 -8
  7. package/assets/prompt-templates/kimi.md +10 -10
  8. package/assets/prompt-templates/minimax-m3.1-flash.md +9 -8
  9. package/assets/prompt-templates/minimax.md +12 -10
  10. package/assets/prompt-templates/nemotron.md +9 -9
  11. package/assets/prompt-templates/qwen.md +9 -9
  12. package/dist/cloud/index.js +2 -2
  13. package/dist/document-reader-child.js +20 -20
  14. package/dist/extensions/browser/chrome-browser-driver.d.ts +50 -0
  15. package/dist/extensions/browser/chrome-render-driver.d.ts +36 -0
  16. package/dist/extensions/config/prompt-template-review.d.ts +1 -1
  17. package/dist/extensions/diagnostics/check-file-diagnostics.d.ts +99 -0
  18. package/dist/extensions/diagnostics/editor-diagnostics.d.ts +125 -0
  19. package/dist/extensions/diagnostics/render.d.ts +25 -0
  20. package/dist/extensions/diagnostics/types.d.ts +52 -0
  21. package/dist/extensions/lsp/lsp-connection.d.ts +138 -0
  22. package/dist/extensions/lsp/lsp-host.d.ts +134 -0
  23. package/dist/extensions/tools/ask-lsp.d.ts +1 -1
  24. package/dist/extensions/tools/browser.d.ts +1 -1
  25. package/dist/extensions/tools/check-file.d.ts +18 -3
  26. package/dist/extensions/tools/command-guard.d.ts +4 -3
  27. package/dist/extensions/tools/delimiter-balance.d.ts +50 -0
  28. package/dist/extensions/tools/edit-verification.d.ts +13 -4
  29. package/dist/extensions/tools/error-locations.d.ts +1 -1
  30. package/dist/extensions/tools/executors/awk-parser.d.ts +5 -0
  31. package/dist/extensions/tools/executors/awk.d.ts +14 -2
  32. package/dist/extensions/tools/executors/bash.d.ts +2 -0
  33. package/dist/extensions/tools/executors/document/formats.d.ts +4 -0
  34. package/dist/extensions/tools/executors/document/images.d.ts +6 -1
  35. package/dist/extensions/tools/executors/document/legacy.d.ts +9 -0
  36. package/dist/extensions/tools/executors/file-read.d.ts +15 -0
  37. package/dist/extensions/tools/executors/read-receipts.d.ts +10 -0
  38. package/dist/extensions/tools/executors/search.d.ts +2 -2
  39. package/dist/extensions/tools/executors/sed.d.ts +40 -0
  40. package/dist/extensions/tools/executors/unchanged-reads.d.ts +6 -0
  41. package/dist/extensions/tools/executors/web-fetch.d.ts +12 -0
  42. package/dist/extensions/tools/executors/whole-file.d.ts +7 -2
  43. package/dist/extensions/tools/helpers.d.ts +15 -1
  44. package/dist/extensions/tools/jev.d.ts +1 -1
  45. package/dist/extensions/tools/list-files.d.ts +17 -1
  46. package/dist/extensions/tools/media-endpoint.d.ts +16 -0
  47. package/dist/extensions/tools/model-tool-routing.d.ts +6 -0
  48. package/dist/extensions/tools/schemas.d.ts +12 -12
  49. package/dist/extensions/tools/task-progress.d.ts +2 -0
  50. package/dist/extensions/tools/team/agent-check.d.ts +15 -2
  51. package/dist/extensions/tools/team/agent-controls.d.ts +1 -1
  52. package/dist/extensions/tools/team/batch-report.d.ts +8 -0
  53. package/dist/extensions/tools/team/lead-agent-tools.d.ts +2 -2
  54. package/dist/extensions/tools/transform-image.d.ts +11 -0
  55. package/dist/hub/daemon/entry.js +718 -635
  56. package/dist/hub/index.js +703 -620
  57. package/dist/index.d.ts +9 -1
  58. package/dist/index.js +987 -874
  59. package/dist/remote/remote-helper-entry.js +2 -2
  60. package/dist/remote/remote-helper.js +2 -2
  61. package/dist/runtime/atomic/check-progress.d.ts +13 -0
  62. package/dist/runtime/atomic/file-revisions.d.ts +15 -1
  63. package/dist/runtime/atomic/oracle.d.ts +14 -0
  64. package/dist/runtime/atomic/page-check.d.ts +12 -2
  65. package/dist/runtime/atomic/plan-tool.d.ts +14 -2
  66. package/dist/runtime/atomic/protocol.d.ts +2 -0
  67. package/dist/runtime/atomic/restore-file-tool.d.ts +3 -3
  68. package/dist/runtime/atomic/snapshot.d.ts +15 -0
  69. package/dist/runtime/atomic/stalled-checks.d.ts +12 -4
  70. package/dist/runtime/atomic/transaction-controller.d.ts +10 -0
  71. package/dist/runtime/atomic/unparseable-change.d.ts +3 -1
  72. package/dist/runtime/safety/loop-detection.d.ts +15 -1
  73. package/dist/services/global-settings.d.ts +3 -3
  74. package/dist/services/retrieval/memory.d.ts +9 -0
  75. package/dist/services/sdd/sdd-engine.d.ts +14 -1
  76. package/package.json +11 -7
@@ -0,0 +1,134 @@
1
+ import type { HostFileDiagnostics } from "../diagnostics/types";
2
+ import type { CodeIntelProvider } from "../tools/ask-lsp";
3
+ import { type LspServerSpec } from "./lsp-connection";
4
+ /**
5
+ * The language servers of a host that has no editor to ask.
6
+ *
7
+ * In the extension, `ask_lsp`, `check_file` and the post-edit report read VS
8
+ * Code, which already runs a server for every language in the workspace. A
9
+ * terminal runs none, so this resolves a server per language, starts it on
10
+ * the first question about a file of it, keeps it for the session, and keeps
11
+ * what it publishes as the editor keeps it.
12
+ *
13
+ * The tools' contract is what makes this tractable: an operation that cannot
14
+ * be answered returns nothing, and the tool says so plainly. So a language
15
+ * with no server installed is a quiet "no answer" rather than an error -- the
16
+ * same outcome the extension produces for a language whose extension the user
17
+ * never installed.
18
+ */
19
+ /** LSP language ids, by the extension that implies them. */
20
+ export declare const LANGUAGE_BY_EXTENSION: Readonly<Record<string, string>>;
21
+ export declare function languageIdFor(filePath: string): string | undefined;
22
+ /**
23
+ * A TypeScript installation this machine already has, for servers that need one.
24
+ *
25
+ * Only the globally installed one is considered: a workspace that has its own
26
+ * is found by the server without help, and this exists for the case where there
27
+ * is nothing to find. `tsserver.js` rather than the package root, because the
28
+ * 7.x `typescript` package is a native wrapper that ships no `tsserver` at all
29
+ * and would otherwise be handed over as if it were usable.
30
+ */
31
+ export declare function findGlobalTypescriptLib(): string | undefined;
32
+ /**
33
+ * What VS Code's HTML extension hands its server, and what it answers when
34
+ * the server asks: the script and the style of a page are checked as
35
+ * JavaScript and CSS. The server's script mode reads
36
+ * `js/ts.implicitProjectConfig` with no guard, so it is given VS Code's
37
+ * defaults rather than left to fail.
38
+ */
39
+ export declare const HTML_SERVER_SETTINGS: Record<string, unknown>;
40
+ /**
41
+ * The servers this host will try, and what each one answers for.
42
+ *
43
+ * Resolution is by what is actually on the machine: the first spec whose
44
+ * command exists and whose language list covers the file wins. Nothing is
45
+ * installed on demand -- a host that pulled down a language server mid-turn
46
+ * would be a surprise, and the tools degrade honestly without one. A host
47
+ * with its own install prefix (automaticum's runner) passes its own specs.
48
+ */
49
+ export declare const DEFAULT_LSP_SERVERS: LspServerSpec[];
50
+ /**
51
+ * Whether a command can be run, without running it.
52
+ *
53
+ * A PATH walk rather than `which`: spawning a shell to ask about ten servers
54
+ * costs more than reading ten directories, and this runs before the first
55
+ * question is answered.
56
+ */
57
+ export declare function commandExists(command: string): boolean;
58
+ /** Whether a spec's server is on this machine. */
59
+ export declare function isServerInstalled(spec: LspServerSpec): boolean;
60
+ export interface HtmlServerTypescriptGap {
61
+ /** Where the server reads TypeScript's `lib.*.d.ts` from. */
62
+ expected: string;
63
+ /** A TypeScript beside the server that would fill it, if there is one. */
64
+ typescript?: string;
65
+ }
66
+ /**
67
+ * Whether the HTML server of `vscode-langservers-extracted` will find
68
+ * TypeScript's library files.
69
+ *
70
+ * Its bundle (4.10.0) computes them as `<prefix>/node_modules/node_modules/
71
+ * typescript/lib` -- one `node_modules` too many -- so in a normal install
72
+ * they are never there, and a page's script is checked with no types: hover
73
+ * says `any`. Nothing the client sends changes the path. Returns the gap, or
74
+ * nothing when the server is not that one or the files are there.
75
+ */
76
+ export declare function findHtmlServerTypescriptGap(spec: LspServerSpec): HtmlServerTypescriptGap | undefined;
77
+ /**
78
+ * Fill the gap with a link to the TypeScript beside the server.
79
+ *
80
+ * Writes into the server's install prefix, so it is never done implicitly: a
81
+ * host calls this when its owner has said so (automaticum's image does the
82
+ * same at build time). Returns whether the link now exists.
83
+ */
84
+ export declare function repairHtmlServerTypescript(gap: HtmlServerTypescriptGap): boolean;
85
+ /** The languages of a workspace's files, looked for at most `limit` entries deep. */
86
+ export declare function workspaceLanguages(root: string, limit?: number): string[];
87
+ export interface LspServerStatus {
88
+ server: string;
89
+ languages: string[];
90
+ installed: boolean;
91
+ started?: boolean;
92
+ failed?: string;
93
+ /** Something the server needs that this machine lacks. */
94
+ warning?: string;
95
+ }
96
+ export interface LspHostOptions {
97
+ /** The workspace root the servers start in. */
98
+ cwd: string;
99
+ /** Tried in order; the first installed one for a language wins. */
100
+ servers?: LspServerSpec[];
101
+ onError?: (message: string, error: unknown) => void;
102
+ /** Whether a spec's server is installed. Defaults to a PATH/probe check. */
103
+ isInstalled?: (spec: LspServerSpec) => boolean;
104
+ }
105
+ export interface LspHost {
106
+ /** `ask_lsp`'s host half. */
107
+ provider: CodeIntelProvider;
108
+ /**
109
+ * Hand the file's server the file as it is on disk (`save`: as just
110
+ * written). Undefined when no server answers for it or it cannot be read.
111
+ */
112
+ touch(filePath: string, options?: {
113
+ save?: boolean;
114
+ }): Promise<{
115
+ languageId: string;
116
+ } | undefined>;
117
+ /** Whether an installed server answers for this file's language. */
118
+ serves(filePath: string): boolean;
119
+ /**
120
+ * Every file a server has reported on, with `pending` set when the file
121
+ * changed after the server last reported on it.
122
+ */
123
+ readDiagnostics(): Promise<HostFileDiagnostics[]>;
124
+ /** Start the servers for these languages (default: the workspace's) now. */
125
+ prewarm(languages?: string[]): string[];
126
+ /** Which server answers for which language, and in what state. */
127
+ status(): LspServerStatus[];
128
+ dispose(): Promise<void>;
129
+ }
130
+ /**
131
+ * The session's language servers, behind `ask_lsp`, `check_file` and the
132
+ * post-edit report.
133
+ */
134
+ export declare function createLspHost(options: LspHostOptions): LspHost;
@@ -35,7 +35,7 @@ import { type AgentTool } from "@cerebriline/shared";
35
35
  export declare const ASK_LSP_TOOL_NAME = "ask_lsp";
36
36
  export declare const ASK_LSP_OPERATIONS: readonly ["definition", "references", "implementations", "type_definition", "hover", "document_symbols", "workspace_symbols", "callers"];
37
37
  export type AskLspOperation = (typeof ASK_LSP_OPERATIONS)[number];
38
- export declare const ASK_LSP_TOOL_DESCRIPTION = "Ask the language servers \u2014 the LSP \u2014 about a symbol. If you are reaching for an LSP tool or an MCP server that wraps one, this is it: the same protocol, already running against this workspace and its open files, with no server to start. This answers questions a text search cannot, because it understands the code: it distinguishes a definition from a mention, and this class's method from another class's method of the same name.\n\nUse this before falling back to `search_codebase` for anything about a symbol. It is faster, exact, and does not need you to read files to interpret the result.\n\nReach for it the moment you are about to do one of these by hand:\n- search for a name to find where it is defined -> `definition`\n- search for a name to find what uses it, or what would break -> `references` or `callers`\n- open a file just to read a signature, type or doc comment -> `hover`\n- scroll a file, or count brackets, to work out its structure -> `document_symbols`\n- grep the repo to find which file something lives in -> `workspace_symbols`\n\nOperations:\n- `definition` \u2014 where a symbol is defined.\n- `references` \u2014 every place it is actually used.\n- `implementations` \u2014 the classes or functions implementing an interface or abstract method.\n- `type_definition` \u2014 where the type of an expression is defined.\n- `hover` \u2014 the signature, type and documentation, as an editor shows on hover.\n- `document_symbols` \u2014 an outline of one file: its classes, functions and methods.\n- `workspace_symbols` \u2014 find a symbol by name across the whole project when you do not know which file it is in.\n- `callers` \u2014 what calls this function.\n\nHow to address a symbol:\n- Usually: `path` plus `symbol` \u2014 the name as it appears in that file.\n- If you know the exact position: `path`, `line` and `character` (both 1-based).\n- If you do not know the file: `symbol` alone, with `operation: \"workspace_symbols\"`.\n\nOutput: plain text, one result per line as `file:line:column` followed by that source line, so you can go straight to the one you want rather than reading each candidate. `hover` returns the signature and documentation as text instead, and `document_symbols` and `workspace_symbols` name each symbol's kind.\n\nAn empty answer is a real answer once the symbol resolved: for `definition`, `references`, `implementations`, `type_definition`, `callers` and `hover` the server understood the symbol and nothing matched, so a text search for the same question will not find more. `workspace_symbols` is the exception, and says so when it comes back empty: it reads a project-wide index that covers the languages a server is installed for and does not index script embedded in `.html` or other template files, so nothing there is not proof of nothing anywhere. And when the file does not parse, every answer about it opens with that line \u2014 while it is there, the server is answering from a partial parse and you are reading guesses.";
38
+ export declare const ASK_LSP_TOOL_DESCRIPTION = "Ask the language servers \u2014 the LSP \u2014 about a symbol. If you are reaching for an LSP tool or an MCP server that wraps one, this is it: the same protocol, already running against this workspace and its open files, with no server to start. This answers questions a text search cannot, because it understands the code: it distinguishes a definition from a mention, and this class's method from another class's method of the same name.\n\nUse this before falling back to `search_codebase` for anything about a symbol. It is faster, exact, and does not need you to read files to interpret the result.\n\nReach for it the moment you are about to do one of these by hand:\n- search for a name to find where it is defined -> `definition`\n- search for a name to find what uses it, or what would break -> `references` or `callers`\n- open a file just to read a signature, type or doc comment -> `hover`\n- scroll a file, or count brackets, to work out its structure -> `document_symbols`\n- grep the repo to find which file something lives in -> `workspace_symbols`\n\nOperations:\n- `definition` \u2014 where a symbol is defined.\n- `references` \u2014 every place it is actually used.\n- `implementations` \u2014 the classes or functions implementing an interface or abstract method.\n- `type_definition` \u2014 where the type of an expression is defined.\n- `hover` \u2014 the signature, type and documentation, as an editor shows on hover.\n- `document_symbols` \u2014 an outline of one file: its classes, functions and methods.\n- `workspace_symbols` \u2014 find a symbol by name across the whole project when you do not know which file it is in.\n- `callers` \u2014 what calls this function.\n\nHow to address a symbol:\n- Usually: `path` plus `symbol` \u2014 the name as it appears in that file.\n- If you know the exact position: `path`, `line` and `character` (both 1-based).\n- If you do not know the file: `symbol` alone, with `operation: \"workspace_symbols\"`.\n\nOutput: plain text, one result per line as `file:line:column` followed by that source line, so you can go straight to the one you want rather than reading each candidate. `hover` returns the signature and documentation as text instead, and `document_symbols` and `workspace_symbols` name each symbol's kind.\n\nAn empty answer once the symbol resolved means the server understood the symbol and found nothing for it \u2014 within what it can see. It does not see uses it cannot resolve statically (`obj[name]`, a name built in a string, reflection), files it does not load, or other languages, so for `references` and `callers` above all, a text search can still find uses the server missed; run one when the answer decides an edit. `workspace_symbols` says so when it comes back empty too: it reads a project-wide index that covers the languages a server is installed for and does not index script embedded in `.html` or other template files, so nothing there is not proof of nothing anywhere. And when a JavaScript or HTML file does not parse, every answer about it opens with that line \u2014 while it is there, the server is answering from a partial parse and you are reading guesses. Other languages are not parsed here, so no such line is no promise for them.";
39
39
  /** Exported for the same reason as `CHECK_FILE_TOOL_INPUT_SCHEMA`. */
40
40
  export declare const ASK_LSP_TOOL_INPUT_SCHEMA: {
41
41
  readonly type: "object";
@@ -41,7 +41,7 @@ export declare const BROWSER_TOOL_NAME = "browser";
41
41
  * user. So the description says, in the place the model is standing when it is
42
42
  * about to ask, that the answer is available without them.
43
43
  */
44
- export declare const BROWSER_TOOL_DESCRIPTION = "Open a page in a real browser and report what it printed to the console and what it threw. This is how you check that a page works. Do not ask the user whether it works \u2014 open it and read the errors yourself.\n\nUse it after editing any HTML, CSS or JavaScript the page loads, and before reporting a task finished. `check_file` cannot answer this: no language server checks the script inside an `.html` file, and a file that parses can still throw the moment it runs.\n\nActions:\n- `open` \u2014 go to `url` and report the console output. Launches the browser on first use. A local file is a URL: pass the absolute path and it is converted for you.\n- `click` \u2014 click at `coordinate` (\"x,y\" in page pixels, from the screenshot).\n- `type` \u2014 type `text` at the current focus.\n- `scroll_down`, `scroll_up` \u2014 one viewport.\n- `close` \u2014 shut the browser down. Do this when finished with it.\n\nEvery action reports the console messages and uncaught errors produced while it ran, so a syntax error, a failed fetch or a null dereference comes back as text you can act on. `[error]` and `[Page Error]` lines are real failures. A page that says nothing printed nothing \u2014 that is a pass, not a failed call, and for a local file it is checked: a silent console over a file that does not parse is reported as the failure it is, not as a pass.\n\nA parse error from the browser names no line, because the script never ran. For a local file a `Delimiter scan` section follows it and names the line to edit and how many brackets that line is out by \u2014 one line per place the trouble starts, since a file can be broken in several spots at once. Fix every line it lists in one edit and reload once, rather than one edit and one reload per line. Edit those lines instead of counting brackets yourself: counting a whole file by hand costs more thinking than you have, and the scan skips strings, comments and regex literals, which counting does not.\n\nThe browser stays open between calls, so `open` once and then interact. Only one page is open at a time; `open` again to go elsewhere.";
44
+ export declare const BROWSER_TOOL_DESCRIPTION = "Open a page in a real browser and report what it printed to the console and what it threw. This is how you check that a page works. Do not ask the user whether it works \u2014 open it and read the errors yourself.\n\nUse it after editing any HTML, CSS or JavaScript the page loads, and before reporting a task finished. `check_file` cannot answer this: no language server checks the script inside an `.html` file, and a file that parses can still throw the moment it runs.\n\nActions:\n- `open` \u2014 go to `url` and report the console output. Launches the browser on first use. A local file is a URL: pass the absolute path and it is converted for you.\n- `click` \u2014 click at `coordinate` (\"x,y\" in page pixels, from the screenshot).\n- `type` \u2014 type `text` at the current focus.\n- `scroll_down`, `scroll_up` \u2014 one viewport.\n- `close` \u2014 shut the browser down. Do this when finished with it.\n\nEvery action reports the console messages and uncaught errors produced while it ran, so a syntax error, a failed fetch or a null dereference comes back as text you can act on. `[error]` and `[Page Error]` lines are real failures. A page that says nothing printed nothing \u2014 that is a pass, not a failed call, and for a local file it is checked: the page's own scripts and the local scripts it loads by `src` are parsed, and a silent console over one that does not parse is reported as the failure it is, not as a pass. Silence says nothing threw; it does not say the page drew what it should.\n\nA parse error from the browser often names no line. For a local file the parser's own answer follows, with where it stopped, and a `Delimiter scan` section names the lines where brackets stop pairing up \u2014 one per place the trouble starts, since a file can be broken in several spots at once. Where the parser stops is where it gave up, which is not always where the mistake is, and a count says where brackets stop pairing, not which one is wrong: read the code at those lines, fix every place listed, and reload once. Do not count brackets yourself \u2014 the scan skips strings, comments and regex literals, which counting by hand does not.\n\nThe browser stays open between calls, so `open` once and then interact. Only one page is open at a time; `open` again to go elsewhere.";
45
45
  /**
46
46
  * Exported so the template generator can state this tool's real call shape.
47
47
  *
@@ -27,6 +27,7 @@
27
27
  * therefore forces a much weaker check on the CLI than the words suggest.
28
28
  */
29
29
  import type { AgentTool } from "@cerebriline/shared";
30
+ import { type ScriptGoal } from "./delimiter-balance";
30
31
  export declare const CHECK_FILE_TOOL_NAME = "check_file";
31
32
  /**
32
33
  * Written at the model that gets this wrong.
@@ -36,7 +37,7 @@ export declare const CHECK_FILE_TOOL_NAME = "check_file";
36
37
  * otherwise type are named here, in the description of the tool that replaces
37
38
  * them, which is where the model is standing when it is about to type one.
38
39
  */
39
- export declare const CHECK_FILE_TOOL_DESCRIPTION = "Check files for syntax errors and unbalanced brackets. **This is the syntax check.** Whichever word the question uses \u2014 parse error, syntax error, \"is it valid\", \"is it clean now\", \"what is still broken\" \u2014 this is the tool that answers it, for the files you name, in milliseconds, without running anything.\n\nAsk this instead of running `node --check`, `python -m py_compile`, `tsc --noEmit` or a bracket-counting script of your own. It parses the file without executing it, so it is safe on a page that would otherwise start a game or a server.\n\nWhen to call it:\n- After editing a file, to confirm the edit is valid before moving on.\n- Before reporting a task finished, on every file you changed. An edit you have not looked at is not a change you can report as done.\n- On a file you are about to change, to see what was already wrong with it.\n\nPass every file you want checked in one call.\n\nFor `.html` files each `<script>` block is parsed as JavaScript and the line numbers reported are the line numbers in the HTML file, not in the block.\n\nRead the bound carefully. This reports **syntax**, not meaning: a file that parses may still call a function that does not exist, and this will not say so. It is not a type checker, it does not run tests, and it does not know your project's lint rules \u2014 for those, use `run_commands`.\n\nOutput: JSON, in the shape a language server answers in. `files` holds one entry per path you named, each with `uri`, `checked` and `diagnostics`. There is no `success` field \u2014 diagnostics being listed is this tool working, not failing.\n\n`checked` says which check actually ran, and you have to read it before calling a file clean: `parsed` a real parser was satisfied, `delimiters` only the bracket scan reads this language so anything past brackets is unchecked, `none` nothing here reads this file type at all \u2014 empty `diagnostics` there means nothing was checked, not that the file is sound.\n\nEach diagnostic carries `range` (zero-based line and character), `severity` (1 = error) and `message`. `located: false` means the parser could not name a position, so the range is a placeholder rather than a claim about the top of the file. `complexity` appears on a file big enough to be worth a word about \u2014 a note to you, not a diagnostic.\n\nWhen a file's brackets do not match, a diagnostic names the line to edit and how many brackets that line is out by, one line per place the trouble starts \u2014 a file can be broken in several spots at once, so work through every line it lists before re-checking rather than re-checking after each one. Edit the lines it names; do not re-derive the balance by hand or with a script you write, which costs more thinking than you have and counts brackets inside strings, comments and regex literals that this scan skips. A parse error is always reported where the parser gave up, which is the closing bracket; the line named here is the one the error cannot name. The scan reads the whole file from line 1, and names each bracket by its line, its column and the text just before it: find it by that text rather than by counting columns.";
40
+ export declare const CHECK_FILE_TOOL_DESCRIPTION = "Check files for syntax errors and unbalanced brackets. **This is the syntax check.** Whichever word the question uses \u2014 parse error, syntax error, \"is it valid\", \"is it clean now\", \"what is still broken\" \u2014 this is the tool that answers it, for the files you name, in milliseconds, without running anything.\n\nAsk this instead of running `node --check`, `python -m py_compile`, `tsc --noEmit` or a bracket-counting script of your own. It parses the file without executing it, so it is safe on a page that would otherwise start a game or a server.\n\nWhen to call it:\n- After editing a file, to confirm the edit is valid before moving on.\n- Before reporting a task finished, on every file you changed. An edit you have not looked at is not a change you can report as done.\n- On a file you are about to change, to see what was already wrong with it.\n\nPass every file you want checked in one call.\n\nFor `.html` files each `<script>` block is parsed as JavaScript and the line numbers reported are the line numbers in the HTML file, not in the block.\n\nRead the bound carefully. This reports **syntax**, not meaning: a file that parses may still call a function that does not exist, and this will not say so. It is not a type checker, it does not run tests, and it does not know your project's lint rules \u2014 for those, use `run_commands`.\n\nOutput: JSON, in the shape a language server answers in. `files` holds one entry per path you named, each with `uri`, `checked` and `diagnostics`. There is no `success` field \u2014 diagnostics being listed is this tool working, not failing.\n\n`checked` says which check actually ran, and you have to read it before calling a file clean: `parsed` a real parser was satisfied, `delimiters` only the bracket scan reads this language so anything past brackets is unchecked, `none` nothing here reads this file type at all \u2014 empty `diagnostics` there means nothing was checked, not that the file is sound.\n\nEach diagnostic carries `range` (zero-based line and character), `severity` (1 = error) and `message`. `located: false` means the parser could not name a position, so the range is a placeholder rather than a claim about the top of the file. `complexity` appears on a file big enough to be worth a word about \u2014 a note to you, not a diagnostic.\n\nA parse error is reported where the parser gave up. That is where the code stopped making sense to it, which is often but not always where the mistake is: a bracket left open earlier makes it give up later. When a file's brackets do not match, the bracket scan adds the lines where they stop pairing up and how many each line is out by, one line per place the trouble starts \u2014 read both before you edit, and work through every line listed before re-checking. A count says where brackets stop pairing, not which bracket is wrong, so read the code at those lines rather than editing them blind; do not re-derive the balance by hand or with a script of your own, which counts brackets inside strings, comments and regex literals that this scan skips. The scan names each bracket by its line, its column and the text just before it: find it by that text rather than by counting columns.";
40
41
  /**
41
42
  * The description when the host has a real checker to point at.
42
43
  *
@@ -96,12 +97,26 @@ export interface CheckFinding {
96
97
  */
97
98
  sourceText?: string;
98
99
  }
99
- /** Compile without running, and say what and where. */
100
- export declare function compileCheck(code: string, label: string, lineOffset?: number): CheckFinding | undefined;
100
+ /**
101
+ * Compile without running, and say what and where.
102
+ *
103
+ * The parse the delimiter scan gates its edits on, so the two never disagree.
104
+ * The parse this had of its own threw nothing under Bun, which parses
105
+ * `vm.Script` lazily, so it never named a line there (automaticum audit
106
+ * 2026-11-08, B1); turned off on any line starting `import`, so a broken ES
107
+ * module was "No syntax errors" (B5); and refused a `#!` line and top-level
108
+ * `await`, which are correct (B6).
109
+ *
110
+ * `undefined` when the code parses, and equally when nothing here can tell --
111
+ * {@link parseVerdict} is the one that tells those apart.
112
+ */
113
+ export declare function compileCheck(code: string, _label: string, lineOffset?: number, goal?: ScriptGoal): CheckFinding | undefined;
101
114
  /** A `<script>` body, and the line the body starts on. */
102
115
  export interface ScriptBlock {
103
116
  code: string;
104
117
  startLine: number;
118
+ /** `type="module"`: parsed by a module's rules. */
119
+ module?: boolean;
105
120
  }
106
121
  /**
107
122
  * The inline scripts of an HTML page.
@@ -12,9 +12,10 @@
12
12
  * so they cannot false-positive), split on shell separators, and the leading
13
13
  * command word of each part is compared against the lists below; output
14
14
  * redirection to files is also rejected. It will not catch every possible
15
- * mutation (e.g. `python -c "open(..., 'w')"` or commands hidden inside a
16
- * quoted `bash -c` string) — it exists to stop the common ways models edit
17
- * files from the shell, and entries are easy to add.
15
+ * mutation (e.g. `python -c "open(..., 'w')"`, or a command assembled at run
16
+ * time from variables) — it exists to stop the common ways models edit files
17
+ * from the shell, and entries are easy to add. A quoted `bash -c` / `eval`
18
+ * string is read as the command line it is.
18
19
  */
19
20
  import type { StructuredCommandInput } from "./schemas";
20
21
  /**
@@ -72,6 +72,56 @@ export declare function scanDelimiters(text: string, origin?: Cursor): Delimiter
72
72
  * in code and skips every one in a string.
73
73
  */
74
74
  export declare function scanWithBalance(text: string, origin?: Cursor): DelimiterScan;
75
+ /**
76
+ * Where a real parser stops on one script, in the file's own coordinates.
77
+ *
78
+ * The scan counts brackets. It cannot say which one is wrong, and an edit it
79
+ * worked out from the count alone was offered as checked when it fixed
80
+ * nothing. Measured on the manic_miner seed: line 95 holds a spare `}` that
81
+ * closes `class Level` early. Once line 90 was fixed the count no longer saw
82
+ * line 95 at all -- the class's own closing brace on line 97 was the one left
83
+ * over -- and the scan offered to delete that brace, with the exact editor
84
+ * call. The parser had stopped at line 96 the whole time, and the edit left it
85
+ * there. Every manic_miner gate from 2026-10-25 on was handed that edit.
86
+ *
87
+ * So no edit is offered unless the parser agrees it helped, and the line the
88
+ * parser stops on is named whatever the count says.
89
+ *
90
+ * `line` and `column` are absent where the runtime does not say: Bun 1.3
91
+ * names the line and no column, and a parse that only succeeds or fails, such
92
+ * as top-level `await` or JSON, names neither. On the same line without a
93
+ * column, an edit is offered only when the whole script parses after it.
94
+ */
95
+ export interface ParseStop {
96
+ message: string;
97
+ line?: number;
98
+ column?: number;
99
+ }
100
+ /** Which grammar a span is read under. `either` is a `.js` file: a script, unless its own syntax says module. */
101
+ export type ScriptGoal = "script" | "module" | "either";
102
+ /**
103
+ * Parse one script and say where the parser stops: nothing when it parses,
104
+ * and null when this cannot tell.
105
+ *
106
+ * `goal` is what the span is: `script` a classic `<script>`, `module` a module
107
+ * script or `.mjs`, `either` a `.js` file, which is a script unless its own
108
+ * syntax says module. A script is parsed as a function body, so for a `.js`
109
+ * file a top-level `return` passes, as it does in CommonJS; a classic
110
+ * `<script>` is held to the stricter rule. What a real file holds and a
111
+ * parser here does not accept as written:
112
+ *
113
+ * - a `#!` line, which Node runs, is blanked with the line count kept;
114
+ * - top-level `await` in a `.js` file is parsed as an async body;
115
+ * - `import` or `export` in a `.js` file makes it a module, read by
116
+ * {@link moduleAsScript}'s rules. In a classic `<script>` it is the error a
117
+ * browser reports.
118
+ *
119
+ * `vm.compileFunction` throws on a broken body under Node and Bun alike (Bun's
120
+ * `vm.Script` parses lazily and throws nothing), and names the line in its
121
+ * stack: V8 and Bun 1.4 as `<filename>:<line>` over the source line and a
122
+ * caret, Bun 1.3 as `at <parse> (<filename>:<line>)`.
123
+ */
124
+ export declare function findParseStop(body: string, origin?: Cursor, goal?: ScriptGoal): ParseStop | null | undefined;
75
125
  /**
76
126
  * How a prescribed repair opens, so a caller can tell a scan that names the
77
127
  * exact call from one that only names the line.
@@ -73,6 +73,14 @@ export declare class EditVerificationTracker {
73
73
  private key;
74
74
  /** Whether this tracker can ever have anything to say. */
75
75
  get armed(): boolean;
76
+ /** Whether calls to this tool verify files. */
77
+ isCheckTool(toolName: string): boolean;
78
+ /**
79
+ * A checker call that returned: the files its answer reported on are
80
+ * checked, or every file the call named when the answer says nothing a
81
+ * file-by-file reading can use.
82
+ */
83
+ recordCheckResult(input: unknown, output: unknown): void;
76
84
  recordToolCall(toolName: string, input: unknown): void;
77
85
  /**
78
86
  * Files a checker reported on outside a checker call: the one a failing
@@ -99,9 +107,10 @@ export declare function isEditVerificationWrapped(tool: object): boolean;
99
107
  /**
100
108
  * Watch a tool's calls without changing what it does.
101
109
  *
102
- * Only the call is observed — not the result. A failed edit still counts as
103
- * having touched the file: the tool may have written part of it, and a guard
104
- * that trusted `success` would be answering a different question than "is this
105
- * file in a state anyone has looked at".
110
+ * An edit is observed at the call, not the result. A failed edit still counts
111
+ * as having touched the file: the tool may have written part of it, and a
112
+ * guard that trusted `success` would be answering a different question than
113
+ * "is this file in a state anyone has looked at". A check is the opposite: it
114
+ * counts only once it has answered, for the files the answer covers.
106
115
  */
107
116
  export declare function withEditVerificationCapture<TInput, TOutput>(tool: AgentTool<TInput, TOutput>, tracker: EditVerificationTracker): AgentTool<TInput, TOutput>;
@@ -2,7 +2,7 @@
2
2
  * The files a command's output blames, whatever produced the output.
3
3
  *
4
4
  * A model that runs the build gets told *that* something is broken and where
5
- * the tool gave up. A checker tells it *which line to edit*. Measured across
5
+ * the tool gave up. A checker reads the whole file and tells it where. Measured across
6
6
  * four transactions on three builds of the harness arm: the model that had the
7
7
  * first and not the second wrote its own analyser rather than asking for the
8
8
  * second — `find_error.js`, then five checkers in one transaction, then
@@ -104,6 +104,11 @@ export type Expr = {
104
104
  } | {
105
105
  type: "grouping";
106
106
  inner: Expr;
107
+ }
108
+ /** `(a, b)`: only after `print` or before `in`. */
109
+ | {
110
+ type: "exprList";
111
+ items: Expr[];
107
112
  };
108
113
  export type Stmt = {
109
114
  type: "print";
@@ -1,8 +1,20 @@
1
1
  import type { AgentOverlay } from "../../../runtime/sandbox/overlay-fs";
2
2
  import { type Rule } from "./awk-parser";
3
3
  import { type ReadReceipts } from "./read-receipts";
4
- /** An awk value: a number, a string, or a string that looks like a number. */
5
- type Value = string | number;
4
+ /**
5
+ * Input that looks like a number: a field, a `split` part, a `-v` value.
6
+ *
7
+ * awk's strnum. It tests and compares as a number and prints as it was
8
+ * written. A string *constant* is never one: `x = "0"` is true and
9
+ * `$1 == "1.0"` compares text. With the two the same, both were judged by
10
+ * whether the text looked numeric (automaticum audit 2026-11-08, readers 21).
11
+ */
12
+ declare class StrNum {
13
+ readonly text: string;
14
+ constructor(text: string);
15
+ }
16
+ /** An awk value: a number, a string, or input that looks like a number. */
17
+ type Value = string | number | StrNum;
6
18
  export declare function awkSprintf(format: string, args: Value[]): string;
7
19
  export interface RunAwkOptions {
8
20
  /** `-F`: the field separator. */
@@ -148,6 +148,8 @@ export interface ShellExecutorOptions {
148
148
  export declare function createDetachedLog(retentionMs: number, marker: DetachedCommandMarker): {
149
149
  path: string;
150
150
  write(text: string): void;
151
+ /** Lifecycle lines -- the exit, a failure -- which the cap never drops. */
152
+ note(text: string): void;
151
153
  complete: () => void;
152
154
  };
153
155
  /**
@@ -94,6 +94,8 @@ export interface DocumentReadResult {
94
94
  };
95
95
  /** Chapter titles for an ebook, in reading order. */
96
96
  contents?: string[];
97
+ /** Every contents entry, of which `contents` holds the first 200. */
98
+ contentsTotal?: number;
97
99
  /** Pages whose text is an image of text, for OCR. */
98
100
  scannedPages?: number[];
99
101
  /** Scanned pages whose text was recognized here, by `ReadOptions.recognize`. */
@@ -102,6 +104,8 @@ export interface DocumentReadResult {
102
104
  ocrLayerPages?: number[];
103
105
  /** Pages drawn as shapes: no text and no pictures to read. */
104
106
  vectorPages?: number[];
107
+ /** PDF pages with no text and no picture found on them. */
108
+ emptyPages?: number[];
105
109
  notes?: string[];
106
110
  }
107
111
  /**
@@ -22,7 +22,7 @@ export interface ExtractedImage {
22
22
  /** What the vision model saw in it, when descriptions were asked for. */
23
23
  description?: string;
24
24
  /**
25
- * Set for a format no model can view (EMF, WMF, PICT, TIFF, BMP): the file is
25
+ * Set for a format no model can view (EMF, WMF, PICT, TIFF, BMP, JPEG 2000, …): the file is
26
26
  * kept, but it is not a picture anyone downstream can look at as it is.
27
27
  */
28
28
  notViewable?: boolean;
@@ -30,6 +30,11 @@ export interface ExtractedImage {
30
30
  }
31
31
  /** The formats a vision model is sent, which is what `inline` can attach. */
32
32
  export declare const VIEWABLE_MEDIA_TYPES: Set<string>;
33
+ /**
34
+ * What to call an image's format in a sentence: the extension it was saved
35
+ * with, or its media type when it has none of its own.
36
+ */
37
+ export declare function formatName(mediaType: string): string;
33
38
  /**
34
39
  * The media type of an image, from its first bytes.
35
40
  *
@@ -15,6 +15,15 @@
15
15
  * office_oxide reads whole.
16
16
  */
17
17
  import type { DocumentReadResult, ReadOptions } from "./formats";
18
+ /**
19
+ * Whether a compound file is a password-protected .docx, .xlsx or .pptx.
20
+ *
21
+ * Encrypting an OOXML file wraps it in the same container as a 97-2003 one,
22
+ * so the bytes and often the name both say "legacy"; the old reader then
23
+ * reported "WordDocument stream not found" about a file that only needed its
24
+ * password (automaticum audit 2026-11-08, readers 28).
25
+ */
26
+ export declare function isEncryptedOoxml(data: Uint8Array): Promise<boolean>;
18
27
  /** Complete PNG and JPEG images inside a compound file's streams. */
19
28
  export declare function scanCompoundImages(data: Uint8Array): Promise<Uint8Array[]>;
20
29
  export declare function readLegacyOffice(data: Uint8Array, format: "doc" | "xls" | "ppt", options: ReadOptions): Promise<DocumentReadResult>;
@@ -79,6 +79,19 @@ export interface FileReadExecutorOptions {
79
79
  */
80
80
  documentReader?: boolean;
81
81
  }
82
+ /**
83
+ * How far the reader keeps counting lines after it has stopped capturing them,
84
+ * so that every read can report how long the file is.
85
+ *
86
+ * A ranged read used to stop at `end_line` and never learn the file's length,
87
+ * which left no way to ask the question at all. Measured on a 265-message
88
+ * session: five consecutive shell commands trying to find a line count —
89
+ * `wc -l` (not a Windows command), `type | find`, and three spellings of
90
+ * `(Get-Content).Count` — followed by two `editor` calls sending
91
+ * `end_line: 9999` as a guess at EOF. Counting the remaining lines costs one
92
+ * stream pass with no capture and no allocation.
93
+ */
94
+ export declare const MAX_LINE_COUNT_SCAN = 500000;
82
95
  /** A read's text together with the line span it actually returned. */
83
96
  /**
84
97
  * Where a read's lines come from.
@@ -112,6 +125,8 @@ export interface ReadWindow {
112
125
  }[];
113
126
  /** The file holds no lines at all: seeing that is seeing all of it. */
114
127
  empty?: true;
128
+ /** Every line of the file is in this window. */
129
+ whole?: true;
115
130
  }
116
131
  /**
117
132
  * Create a file read executor using Node.js fs module
@@ -17,6 +17,7 @@
17
17
  * be taken must not be allowed to invent a change.
18
18
  */
19
19
  export type FileStamp = string;
20
+ export declare const ABSENT_STAMP: FileStamp;
20
21
  export declare function readFileStamp(filePath: string): Promise<FileStamp | undefined>;
21
22
  /**
22
23
  * The stamp of a file as the caller sees it: through an agent's overlay, the
@@ -180,3 +181,12 @@ export interface OwnedReadReceipts extends ReadReceipts {
180
181
  clear(): void;
181
182
  }
182
183
  export declare function createReadReceipts(): OwnedReadReceipts;
184
+ /**
185
+ * Record the lines a tool printed as read, one receipt per unbroken run.
186
+ *
187
+ * A receipt must cover an edit's whole range in one read, so runs are kept
188
+ * apart: lines 3 and 9 shown do not make 3-9 seen.
189
+ */
190
+ export declare function creditShown(receipts: {
191
+ noteRead(filePath: string, first: number, last: number): void;
192
+ } | undefined, filePath: string, lineNumbers: readonly number[]): void;
@@ -9,8 +9,8 @@ import type { SearchExecutor } from "../types";
9
9
  */
10
10
  export interface SearchExecutorOptions {
11
11
  /**
12
- * File extensions to include in search (without dot)
13
- * @default common code extensions
12
+ * File extensions the built-in search is limited to (without dot).
13
+ * @default every text file; ripgrep is never limited by this
14
14
  */
15
15
  includeExtensions?: string[];
16
16
  /**
@@ -10,6 +10,26 @@ type Address = {
10
10
  regex: RegExp;
11
11
  } | {
12
12
  kind: "every";
13
+ }
14
+ /** `first~step`: every step-th line from first (GNU). */
15
+ | {
16
+ kind: "step";
17
+ first: number;
18
+ step: number;
19
+ }
20
+ /** `0` as a range start: the range is open before line 1 (GNU `0,/re/`). */
21
+ | {
22
+ kind: "zero";
23
+ }
24
+ /** `+N` as a range end: N lines after the line that opened it (GNU). */
25
+ | {
26
+ kind: "relative";
27
+ count: number;
28
+ }
29
+ /** `~N` as a range end: up to the next line that is a multiple of N (GNU). */
30
+ | {
31
+ kind: "multiple";
32
+ of: number;
13
33
  };
14
34
  interface Command {
15
35
  start?: Address;
@@ -32,6 +52,10 @@ interface Command {
32
52
  text?: string;
33
53
  /** Range state while executing. */
34
54
  active?: boolean;
55
+ /** For a range whose end is a line count: the line it closes on. */
56
+ closesAt?: number;
57
+ /** Set by `selects`: this line is the last one of the command's range. */
58
+ closing?: boolean;
35
59
  }
36
60
  /**
37
61
  * Parse a sed script into commands.
@@ -45,6 +69,22 @@ export interface RunSedResult {
45
69
  output: string;
46
70
  /** Whether any command changed the text. */
47
71
  changed: boolean;
72
+ /**
73
+ * Whether any command selected a line and acted on it: an `s` that matched
74
+ * but wrote the same text back matched something, and "matched nothing"
75
+ * was a false account of it.
76
+ */
77
+ matched: boolean;
78
+ /**
79
+ * Input lines whose text reached the output, in order and once each: what
80
+ * a read-only run showed, and so what it may credit as read.
81
+ */
82
+ shown: number[];
83
+ /**
84
+ * Input lines the script changed: rewritten, deleted, replaced, or with
85
+ * text put before or after them. What an in-place run must have seen.
86
+ */
87
+ touched: number[];
48
88
  }
49
89
  /** Run a parsed script over text. Pure: no filesystem, so it is easy to test. */
50
90
  export declare function runSedScript(input: string, commands: Command[], quiet?: boolean): RunSedResult;
@@ -36,6 +36,12 @@ export interface ReadKeyParts {
36
36
  firstLine: number;
37
37
  lastLine: number;
38
38
  withLineNumbers: boolean;
39
+ /**
40
+ * The window was the whole file. Otherwise the comparison says nothing
41
+ * about the lines outside it, and "has not changed" claimed it did
42
+ * (automaticum audit 2026-11-08, readers 15).
43
+ */
44
+ wholeFile?: boolean;
39
45
  }
40
46
  export interface ReadLedger {
41
47
  /**
@@ -38,6 +38,18 @@ export interface WebFetchExecutorOptions {
38
38
  */
39
39
  maxRedirects?: number;
40
40
  }
41
+ /**
42
+ * Decode character references in one pass, so `&amp;lt;` is the text
43
+ * `&lt;` -- decoded entity by entity it became `<`, and `&#x27;` or an emoji
44
+ * above U+FFFF was not decoded at all (automaticum audit 2026-11-08,
45
+ * readers 28).
46
+ */
47
+ export declare function decodeEntities(text: string): string;
48
+ /**
49
+ * Extract text content from HTML: tags stripped, block elements as line
50
+ * breaks, `<pre>` kept as written, entities decoded once.
51
+ */
52
+ export declare function htmlToText(html: string): string;
41
53
  /**
42
54
  * Create a web fetch executor using native fetch
43
55
  *
@@ -15,11 +15,16 @@ export declare const MAX_TEXT_STREAM_BYTES = 100000000;
15
15
  export declare const BINARY_SNIFF_BYTES = 8000;
16
16
  /** Why a file is not read whole: too large, or not text. */
17
17
  export interface WholeFileRefusal {
18
- kind: "size" | "binary";
18
+ kind: "size" | "binary" | "utf16";
19
19
  message: string;
20
20
  }
21
21
  /**
22
22
  * Why `filePath` must not be read whole, or nothing when it may. A path that
23
23
  * cannot be opened is left to the caller's own read, which says why.
24
24
  */
25
- export declare function wholeFileRefusal(filePath: string, tool: string): Promise<WholeFileRefusal | undefined>;
25
+ export declare function wholeFileRefusal(filePath: string, tool: string,
26
+ /** How the refusal opens: what did not happen to the file. */
27
+ outcome?: string): Promise<WholeFileRefusal | undefined>;
28
+ /** The UTF-16 byte order a file's byte-order mark names, if it has one. */
29
+ export declare function utf16ByteOrder(head: Uint8Array): "utf-16le" | "utf-16be" | undefined;
30
+ export declare function readHead(filePath: string, bytes: number): Promise<Uint8Array>;