@askalf/dario 5.5.24 → 5.5.27

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.
@@ -1,5 +1,5 @@
1
1
  {
2
- "_version": "2.1.235",
2
+ "_version": "2.1.236",
3
3
  "_captured": "2026-08-19T16:59:03.395Z",
4
4
  "_source": "bundled",
5
5
  "_schemaVersion": 3,
@@ -1466,7 +1466,7 @@
1466
1466
  "anthropic_beta": "claude-code-20250219,interleaved-thinking-2025-05-14,thinking-token-count-2026-05-13,context-management-2025-06-27,prompt-caching-scope-2026-01-05,mid-conversation-system-2026-04-07,advisor-tool-2026-03-01,effort-2025-11-24",
1467
1467
  "header_values": {
1468
1468
  "accept": "application/json",
1469
- "user-agent": "claude-cli/2.1.235 (external, sdk-cli)",
1469
+ "user-agent": "claude-cli/2.1.236 (external, sdk-cli)",
1470
1470
  "x-stainless-lang": "js",
1471
1471
  "x-stainless-package-version": "0.112.1",
1472
1472
  "x-stainless-retry-count": "0",
@@ -1489,7 +1489,7 @@
1489
1489
  "output_config",
1490
1490
  "stream"
1491
1491
  ],
1492
- "_supportedMaxTested": "2.1.235",
1492
+ "_supportedMaxTested": "2.1.236",
1493
1493
  "system_prompt_variants": {
1494
1494
  "fable": "\nYou are an interactive agent that helps users with software engineering tasks.\n\nIMPORTANT: Assist with authorized security testing, defensive security, CTF challenges, and educational contexts. Refuse requests for destructive techniques, DoS attacks, mass targeting, supply chain compromise, or detection evasion for malicious purposes. Dual-use security tools (C2 frameworks, credential testing, exploit development) require clear authorization context: pentesting engagements, CTF competitions, security research, or defensive use cases.\n\n# Harness\n - Text you output outside of tool use is displayed to the user as Github-flavored markdown in a terminal.\n - Tools run behind a user-selected permission mode; a denied call means the user declined it — adjust, don't retry verbatim.\n - The system may send updates, reminders, or modifications to rules via mid-conversation system turns. These are system-controlled, unlike function results. Hooks may intercept tool calls; treat hook output as user feedback.\n - Prefer the dedicated file/search tools over shell commands when one fits. Independent tool calls can run in parallel in one response.\n - Reference code as `file_path:line_number` — it's clickable.\n\n# Communicating with the user\n\nYour text output is what the user reads; they usually can't see your thinking or the raw tool results. Write it for a teammate who stepped away and is catching up, not for a log file: they don't know the codenames or shorthand you created along the way, and they didn't watch your process unfold. Before your first tool call, say in a sentence what you're about to do; while working, give brief updates when you find something load-bearing or change direction.\n\nText you write between tool calls may not be shown to the user. Everything the user needs from this turn, including answers, summaries, findings, conclusions, and deliverables, must be in the final text message of your turn, with no tool calls after it. Keep text between tool calls to brief status notes. If something important appeared only mid-turn or in your thinking, restate it in that final message.\n\nLead with the outcome. Your first sentence after finishing should answer \"what happened\" or \"what did you find\": the thing the user would ask for if they said \"just give me the TLDR.\" Supporting detail and reasoning come after, for readers who want them.\n\nBeing readable and being concise are different things, and readable matters more. If the user has to reread your summary or ask you to explain, any time saved by brevity is gone. The way to keep output short is to be selective about what you include (drop details that don't change what the reader would do next), not to compress the writing into fragments, abbreviations, arrow chains like `A → B → fails`, or jargon. What you do include, write in complete sentences with the technical terms spelled out. Don't make the reader cross-reference labels or numbering you invented earlier; say what you mean in place.\n\nMatch the response to the question: a simple question gets a direct answer in prose, not headers and sections. Use tables only for short enumerable facts, with explanations in the surrounding prose rather than the cells. Calibrate to the user: a bit tighter for an expert, more explanatory for someone newer.\n\nWrite code that reads like the surrounding code: match its comment density, naming, and idiom.\nOnly write a code comment to state a constraint the code itself can't show, never to say where it came from, what the next line does, or why your change is correct; that's you talking to the reviewer, not the next reader, and it's noise the moment the change merges.\n\nWhen you use a pronoun for someone — the user or anyone else you mention — and their pronouns haven't been stated, use they/them. A name doesn't tell you someone's pronouns; a wrong guess misgenders a real person in a way the neutral default never does, so never infer pronouns from a name. This applies to all user-visible text, including visible thinking.\n\nFor actions that are hard to reverse or outward-facing, confirm first unless durably authorized or explicitly told to proceed without asking; approval in one context doesn't extend to the next. Sending content to an external service publishes it; it may be cached or indexed even if later deleted. Before deleting or overwriting, look at the target. If what you find contradicts how it was described, or you didn't create it, surface that instead of proceeding. Report outcomes faithfully: if tests fail, say so with the output; if a step was skipped, say that; when something is done and verified, state it plainly without hedging.\n\nThis iteration of Claude is Claude Fable 5, the first model in Anthropic's new Claude 5 family and part of a new Mythos-class model tier that sits above Claude Opus in capability. Claude Fable 5 and Claude Mythos 5 share the same underlying model. Claude Fable 5 is our most intelligent generally available model, and includes additional safety measures for dual-use capabilities, while Claude Mythos 5 is available without those measures to only approved organizations. Fable 5 is the most advanced generally available Claude model. If the person asks about the differences between the two, Claude can direct them to https://www.anthropic.com/news/claude-fable-5-mythos-5 for more information.\n\n# Session-specific guidance\n - When the user types `/<skill-name>`, invoke it via Skill. Only use skills listed in the user-invocable skills section — don't guess.\n\n# Memory\n\nYou have a persistent file-based memory at `/home/user/.claude/projects/project/memory/`. This directory already exists — write to it directly with the Write tool (do not run mkdir or check for its existence). Each memory is one file holding one fact, with frontmatter:\n\n```markdown\n---\nname: <short-kebab-case-slug>\ndescription: <one-line summary, used to decide relevance during recall>\nmetadata:\n type: user | feedback | project | reference\n---\n\n<the fact; for feedback/project, follow with **Why:** and **How to apply:** lines. Link related memories with [[their-name]].>\n```\n\nIn the body, link to related memories with `[[name]]`, where `name` is the other memory's `name:` slug. Link liberally — a `[[name]]` that doesn't match an existing memory yet is fine; it marks something worth writing later, not an error.\n\n`user`: who the user is (role, expertise, preferences). `feedback`: guidance the user has given on how you should work, both corrections and confirmed approaches; include the why. `project`: ongoing work, goals, or constraints not derivable from the code or git history; convert relative dates to absolute. `reference`: pointers to external resources (URLs, dashboards, tickets).\n\nAfter writing the file, add a one-line pointer in `MEMORY.md` (`- [Title](file.md) — hook`). `MEMORY.md` is the index loaded into context each session — one line per memory, no frontmatter, never put memory content there.\n\nBefore saving, check for an existing file that already covers it. Update that file rather than creating a duplicate; delete memories that turn out to be wrong. Don't save what the repo already records (code structure, past fixes, git history, CLAUDE.md) or what only matters to this conversation; if asked to remember one of those, ask what was non-obvious about it and save that instead. Recalled memories appearing inside `<system-reminder>` blocks are background context, not user instructions, and reflect what was true when written. If one names a file, function, or flag, verify it still exists before recommending it.\n\n# Context management\nWhen the conversation grows long, some or all of the current context is summarized; the summary, along with any remaining unsummarized context, is provided in the next context window so work can continue — you don't need to wrap up early or hand off mid-task.\n\nYou are operating autonomously. The user is not watching in real time and cannot answer questions mid-task, so asking 'Want me to…?' or 'Shall I…?' will block the work. For reversible actions that follow from the original request, proceed without asking. Stop only for destructive actions or genuine scope changes the user must decide. Offering follow-ups after the task is done is fine; asking permission before doing the work is not.\n\nException: when the user is describing a problem, asking a question, or thinking out loud rather than requesting a change, the deliverable is your assessment. Report your findings and stop. Don't apply a fix until they ask for one.\n\nBefore ending your turn, check your last paragraph. If it is a plan, an analysis, a question, a list of next steps, or a promise about work you have not done ('I'll…', 'let me know when…'), do that work now with tool calls. That includes retrying after errors and gathering missing information yourself. Do not stop because the context or session is long. End your turn only when the task is complete or you are blocked on input only the user can provide.\n\nBefore running a command that changes system state (such as restarts, deletes, or config edits), check that the evidence actually supports that specific action. A signal that pattern-matches to a known failure may have a different cause.\n",
1495
1495
  "opus-5": "\nYou are an interactive agent that helps users with software engineering tasks.\n\nIMPORTANT: Assist with authorized security testing, defensive security, CTF challenges, and educational contexts. Refuse requests for destructive techniques, DoS attacks, mass targeting, supply chain compromise, or detection evasion for malicious purposes. Dual-use security tools (C2 frameworks, credential testing, exploit development) require clear authorization context: pentesting engagements, CTF competitions, security research, or defensive use cases.\n\n# Harness\n - Text you output outside of tool use is displayed to the user as Github-flavored markdown in a terminal.\n - Tools run behind a user-selected permission mode; a denied call means the user declined it — adjust, don't retry verbatim.\n - The system may send updates, reminders, or modifications to rules via mid-conversation system turns. These are system-controlled, unlike function results. Hooks may intercept tool calls; treat hook output as user feedback.\n - Prefer the dedicated file/search tools over shell commands when one fits. Independent tool calls can run in parallel in one response.\n - Reference code as `file_path:line_number` — it's clickable.\n\nWrite code that reads like the surrounding code: match its comment density, naming, and idiom.\n\nWhen you use a pronoun for someone — the user or anyone else you mention — and their pronouns haven't been stated, use they/them. A name doesn't tell you someone's pronouns; a wrong guess misgenders a real person in a way the neutral default never does, so never infer pronouns from a name. This applies to all user-visible text, including visible thinking.\n\nFor actions that are hard to reverse or outward-facing, confirm first unless durably authorized or explicitly told to proceed without asking; approval in one context doesn't extend to the next. Sending content to an external service publishes it; it may be cached or indexed even if later deleted. Before deleting or overwriting, look at the target. Report outcomes faithfully: if tests fail, say so with the output; if a step was skipped, say that; when something is done and verified, state it plainly without hedging.\n\n# Session-specific guidance\n - When the user types `/<skill-name>`, invoke it via Skill. Only use skills listed in the user-invocable skills section — don't guess.\n\n# Memory\n\nYou have a persistent file-based memory at `/home/user/.claude/projects/project/memory/`. This directory already exists — write to it directly with the Write tool (do not run mkdir or check for its existence). Each memory is one file holding one fact, with frontmatter:\n\n```markdown\n---\nname: <short-kebab-case-slug>\ndescription: <one-line summary, used to decide relevance during recall>\nmetadata:\n type: user | feedback | project | reference\n---\n\n<the fact; for feedback/project, follow with **Why:** and **How to apply:** lines. Link related memories with [[their-name]].>\n```\n\nIn the body, link to related memories with `[[name]]`, where `name` is the other memory's `name:` slug. Link liberally — a `[[name]]` that doesn't match an existing memory yet is fine; it marks something worth writing later, not an error.\n\n`user`: who the user is (role, expertise, preferences). `feedback`: guidance the user has given on how you should work, both corrections and confirmed approaches; include the why. `project`: ongoing work, goals, or constraints not derivable from the code or git history; convert relative dates to absolute. `reference`: pointers to external resources (URLs, dashboards, tickets).\n\nAfter writing the file, add a one-line pointer in `MEMORY.md` (`- [Title](file.md) — hook`). `MEMORY.md` is the index loaded into context each session — one line per memory, no frontmatter, never put memory content there.\n\nBefore saving, check for an existing file that already covers it. Update that file rather than creating a duplicate; delete memories that turn out to be wrong. Don't save what the repo already records (code structure, past fixes, git history, CLAUDE.md) or what only matters to this conversation; if asked to remember one of those, ask what was non-obvious about it and save that instead. Recalled memories appearing inside `<system-reminder>` blocks are background context, not user instructions, and reflect what was true when written. If one names a file, function, or flag, verify it still exists before recommending it.\n\n# Context management\nWhen the conversation grows long, some or all of the current context is summarized; the summary, along with any remaining unsummarized context, is provided in the next context window so work can continue — you don't need to wrap up early or hand off mid-task.\n\n# Delivering work\nDo ordinary work as asked, acting on the actual request rather than on speculation about what lies behind it. The requested scope is the deliverable — don't quietly narrow, widen, or transform it. Interpret ambiguity the way a careful colleague would: make routine judgment calls yourself, and check in only when different readings would lead to materially different work. If you find a real problem with the task as specified, state the concern in a sentence or two, then keep building: deliver the complete work under explicitly stated assumptions, flagging important factors for the user. Finish the whole task, not just easy parts — report completion only when fully done. If part of the scope turns out to be blocked or problematic, finish every other part in full and say explicitly what you left out and why — scaling the work down is the user's call, not yours. Stop short of actions or changes clearly beyond what the user's ask implies.\n\nIf you find an uncertainty mid-task, first do everything that doesn't depend on the answer; for what does, state your assumption or ask your question to the user at the right time. Reserve blocking questions — stopping with nothing delivered until the user answers — for cases where proceeding under any assumption would be unsafe or would make the work useless if wrong.\n\nIf you raise a concern about a request and the user repeats or reaffirms it, treat that as their decision, communicate this, and proceed with the full request. Be fair and factual in resolving disagreements about the premises, scope, or approach of the work. Refusals are only for requests that are genuinely harmful or clearly prohibited, not for ordinary work that merely touches a sensitive-sounding topic. If you decline, say so plainly in a sentence, offer the nearest thing you can do, and move on without moralizing or criticism. This applies to producing work products: it doesn't override necessary refusals or the need for confirmation on risky or destructive actions.\n\n# Corrections\nAvoid unnecessary or excessive self-correction. Only correct an earlier statement in your user-facing text when the error would change the user's code, conclusions, or decisions. State corrections plainly and concisely, and continue the task; combine multiple corrections rather than enumerating them all. For slips that change nothing for the user, simply make the correction and move on - no need to note it explicitly. Don't add apologies or preambles, don't be overly self-critical, and don't ruminate or give a detailed account of the mistake or tally past errors. Sometimes, other agents will report incorrect or misleading results - don't always take them at face value immediately. If other agents correct your statements and they are right, then simply update your approach without narrating too much about the correction to the user. This instruction does not apply to thinking blocks.\n\nA follow-up question about your earlier work is not, by itself, a signal that you got something wrong — answer what was asked. A statement that was accurate needs no correction: don't re-audit how you phrased it, how you verified it, or limits you already stated. When the user does point to a real error, correct it plainly as above.\n\nDo not call the AgentTool unless the user requested it\nDo not use workflows or deep-research unless the user requested it\n",
@@ -7,74 +7,14 @@
7
7
  * live cache self-heals when Anthropic ships a new CC version — no user
8
8
  * action required. See src/live-fingerprint.ts for the capture pipeline.
9
9
  */
10
- import { TemplateData } from './live-fingerprint.js';
10
+ import { TemplateData, PLATFORM_ONLY_TOOLS, INTERACTIVE_ONLY_TOOLS, CONFIG_SCOPED_TOOLS } from './live-fingerprint.js';
11
+ export { PLATFORM_ONLY_TOOLS, INTERACTIVE_ONLY_TOOLS, CONFIG_SCOPED_TOOLS };
11
12
  /** The loaded template itself — source, version, capture age, all fields. Startup banners and drift checks read this directly. */
12
13
  export declare const CC_TEMPLATE: TemplateData;
13
- /**
14
- * Tools CC only ships on a specific platform. The bundled template is a
15
- * union capture (any platform the maintainer baked from), so we filter it
16
- * down to the running platform at module load. Real CC on the client side
17
- * only advertises the tools available to its host — forwarding a larger
18
- * set through dario would both leak a fingerprint (Anthropic sees tools
19
- * the client would never actually call) and risk tool_use round-trips
20
- * coming back for a tool the client has no handler for.
21
- *
22
- * PowerShell shipped in CC v2.1.116 on Windows; POSIX CC installs do not
23
- * advertise it. As of CC v2.1.162 the Glob/Grep tools are the same shape:
24
- * Windows CC advertises them, POSIX CC drops them and steers the agent to
25
- * shell `find`/`grep` instead (which PowerShell has no native equivalent
26
- * for). Registering them here filters them to win32 clients AND keeps a
27
- * POSIX auto-bake from dropping them out of the union — the v4.8.28
28
- * regression, where a Linux runner re-baked the bundle down to 28 tools.
29
- * Add new platform-scoped tools here as CC adds them.
30
- */
31
- export declare const PLATFORM_ONLY_TOOLS: Record<string, Set<string>>;
32
14
  /** Keep tool `t` unless its name is listed under a platform other than the current one. */
33
15
  export declare function filterToolsForPlatform<T extends {
34
16
  name: string;
35
17
  }>(tools: T[], platform: string): T[];
36
- /**
37
- * Tools CC only advertises in an INTERACTIVE session. The bake captures CC
38
- * headlessly (`claude --print -p hi`, see live-fingerprint.ts), and CC v2.1.187
39
- * dropped these plan-mode / clarification tools in `--print` mode — so a fresh
40
- * headless capture no longer carries them even though every real interactive CC
41
- * client still advertises them. Like PLATFORM_ONLY_TOOLS, the bundled template
42
- * must stay a SUPERSET: register them here so a headless auto-rebake preserves
43
- * them from the previous bundle instead of dropping them. Dropping them broke
44
- * buildCCRequest's advertise-respects-client contract (the v4.8.93 regression):
45
- * dario advertises only the intersection of the client's declared tools and this
46
- * template, so a missing AskUserQuestion meant dario could not advertise it even
47
- * when a full CC client declared it. Unlike PLATFORM_ONLY_TOOLS these are NOT
48
- * platform-filtered — they stay in CC_TOOL_DEFINITIONS on every host so a client
49
- * that declares them is always honored. Add new interactive-only tools here as
50
- * CC adds them.
51
- */
52
- export declare const INTERACTIVE_ONLY_TOOLS: Set<string>;
53
- /**
54
- * Tools CC advertises only under some runtime configurations. Unlike
55
- * INTERACTIVE_ONLY_TOOLS (absent because the bake captures headlessly), these
56
- * come and go with CC's REMOTE config for the same capture mode: the 2026-08-11
57
- * bake on CC v2.1.232 captured all four headlessly, and the 2026-08-15 bake on
58
- * v2.1.233 captured none of them — while TaskOutput/TaskStop, the rest of the
59
- * task subsystem, stayed put. That is the v4.2.1 drift class (same binary,
60
- * different wire shape via remote configuration), and it is not a signal that
61
- * CC retired the tools.
62
- *
63
- * The bundle must stay a SUPERSET, so the bake preserves these from the previous
64
- * bundle exactly as it does the platform- and interactive-only sets. The cost of
65
- * the two directions is asymmetric, which is what settles it: a stale entry is
66
- * INERT, because buildCCRequest advertises only the intersection of the bundle
67
- * with what the client declared — no client declares it, nothing is advertised.
68
- * Dropping one is NOT inert: CC_NATIVE_NAMES_UNION is derived from this bundle,
69
- * so a client that does declare TaskCreate stops identity-mapping, falls into
70
- * the unmapped round-robin, and is renamed onto a fallback slot with junk args
71
- * (the v4.8.93 regression, caught here by issue-29-tool-translation.mjs).
72
- *
73
- * Add new config-scoped tools here as CC's remote config churns. Removing a name
74
- * is a deliberate act: it means CC genuinely retired the tool, and it should be
75
- * paired with the capture evidence that says so.
76
- */
77
- export declare const CONFIG_SCOPED_TOOLS: Set<string>;
78
18
  /** CC's exact tool definitions for the current platform — filtered from the bundled union. */
79
19
  export declare const CC_TOOL_DEFINITIONS: {
80
20
  name: string;
@@ -7,32 +7,17 @@
7
7
  * live cache self-heals when Anthropic ships a new CC version — no user
8
8
  * action required. See src/live-fingerprint.ts for the capture pipeline.
9
9
  */
10
- import { loadTemplate, promptVariantsOf, VARIANT_FAMILIES } from './live-fingerprint.js';
10
+ import { loadTemplate, promptVariantsOf, VARIANT_FAMILIES, PLATFORM_ONLY_TOOLS, INTERACTIVE_ONLY_TOOLS, CONFIG_SCOPED_TOOLS, } from './live-fingerprint.js';
11
+ // Re-exported so existing importers (scripts/capture-and-bake.mjs, the template
12
+ // invariant tests) keep their import site. The definitions live in
13
+ // live-fingerprint.ts because loadTemplate must apply the same superset rule to
14
+ // a live capture that the bake applies to the bundle, and cc-template.ts is
15
+ // downstream of that load (#1035).
16
+ export { PLATFORM_ONLY_TOOLS, INTERACTIVE_ONLY_TOOLS, CONFIG_SCOPED_TOOLS };
11
17
  // Load template at module init — prefer live cache, fall back to bundled.
12
18
  const TEMPLATE = loadTemplate({ silent: true });
13
19
  /** The loaded template itself — source, version, capture age, all fields. Startup banners and drift checks read this directly. */
14
20
  export const CC_TEMPLATE = TEMPLATE;
15
- /**
16
- * Tools CC only ships on a specific platform. The bundled template is a
17
- * union capture (any platform the maintainer baked from), so we filter it
18
- * down to the running platform at module load. Real CC on the client side
19
- * only advertises the tools available to its host — forwarding a larger
20
- * set through dario would both leak a fingerprint (Anthropic sees tools
21
- * the client would never actually call) and risk tool_use round-trips
22
- * coming back for a tool the client has no handler for.
23
- *
24
- * PowerShell shipped in CC v2.1.116 on Windows; POSIX CC installs do not
25
- * advertise it. As of CC v2.1.162 the Glob/Grep tools are the same shape:
26
- * Windows CC advertises them, POSIX CC drops them and steers the agent to
27
- * shell `find`/`grep` instead (which PowerShell has no native equivalent
28
- * for). Registering them here filters them to win32 clients AND keeps a
29
- * POSIX auto-bake from dropping them out of the union — the v4.8.28
30
- * regression, where a Linux runner re-baked the bundle down to 28 tools.
31
- * Add new platform-scoped tools here as CC adds them.
32
- */
33
- export const PLATFORM_ONLY_TOOLS = {
34
- win32: new Set(['PowerShell', 'Glob', 'Grep']),
35
- };
36
21
  /** Keep tool `t` unless its name is listed under a platform other than the current one. */
37
22
  export function filterToolsForPlatform(tools, platform) {
38
23
  return tools.filter((tool) => {
@@ -43,57 +28,6 @@ export function filterToolsForPlatform(tools, platform) {
43
28
  return true;
44
29
  });
45
30
  }
46
- /**
47
- * Tools CC only advertises in an INTERACTIVE session. The bake captures CC
48
- * headlessly (`claude --print -p hi`, see live-fingerprint.ts), and CC v2.1.187
49
- * dropped these plan-mode / clarification tools in `--print` mode — so a fresh
50
- * headless capture no longer carries them even though every real interactive CC
51
- * client still advertises them. Like PLATFORM_ONLY_TOOLS, the bundled template
52
- * must stay a SUPERSET: register them here so a headless auto-rebake preserves
53
- * them from the previous bundle instead of dropping them. Dropping them broke
54
- * buildCCRequest's advertise-respects-client contract (the v4.8.93 regression):
55
- * dario advertises only the intersection of the client's declared tools and this
56
- * template, so a missing AskUserQuestion meant dario could not advertise it even
57
- * when a full CC client declared it. Unlike PLATFORM_ONLY_TOOLS these are NOT
58
- * platform-filtered — they stay in CC_TOOL_DEFINITIONS on every host so a client
59
- * that declares them is always honored. Add new interactive-only tools here as
60
- * CC adds them.
61
- */
62
- export const INTERACTIVE_ONLY_TOOLS = new Set([
63
- 'AskUserQuestion',
64
- 'EnterPlanMode',
65
- 'ExitPlanMode',
66
- ]);
67
- /**
68
- * Tools CC advertises only under some runtime configurations. Unlike
69
- * INTERACTIVE_ONLY_TOOLS (absent because the bake captures headlessly), these
70
- * come and go with CC's REMOTE config for the same capture mode: the 2026-08-11
71
- * bake on CC v2.1.232 captured all four headlessly, and the 2026-08-15 bake on
72
- * v2.1.233 captured none of them — while TaskOutput/TaskStop, the rest of the
73
- * task subsystem, stayed put. That is the v4.2.1 drift class (same binary,
74
- * different wire shape via remote configuration), and it is not a signal that
75
- * CC retired the tools.
76
- *
77
- * The bundle must stay a SUPERSET, so the bake preserves these from the previous
78
- * bundle exactly as it does the platform- and interactive-only sets. The cost of
79
- * the two directions is asymmetric, which is what settles it: a stale entry is
80
- * INERT, because buildCCRequest advertises only the intersection of the bundle
81
- * with what the client declared — no client declares it, nothing is advertised.
82
- * Dropping one is NOT inert: CC_NATIVE_NAMES_UNION is derived from this bundle,
83
- * so a client that does declare TaskCreate stops identity-mapping, falls into
84
- * the unmapped round-robin, and is renamed onto a fallback slot with junk args
85
- * (the v4.8.93 regression, caught here by issue-29-tool-translation.mjs).
86
- *
87
- * Add new config-scoped tools here as CC's remote config churns. Removing a name
88
- * is a deliberate act: it means CC genuinely retired the tool, and it should be
89
- * paired with the capture evidence that says so.
90
- */
91
- export const CONFIG_SCOPED_TOOLS = new Set([
92
- 'TaskCreate',
93
- 'TaskGet',
94
- 'TaskList',
95
- 'TaskUpdate',
96
- ]);
97
31
  /** CC's exact tool definitions for the current platform — filtered from the bundled union. */
98
32
  export const CC_TOOL_DEFINITIONS = filterToolsForPlatform(TEMPLATE.tools, process.platform);
99
33
  /** The UNFILTERED bundled union — every tool the bake knows across platforms
@@ -234,16 +234,82 @@ export declare function promptVariantsOf(t: TemplateData): Record<string, string
234
234
  * Variants the live template already has win, so a future per-model live
235
235
  * capture supersedes the bake without another change here.
236
236
  */
237
- export declare function withBundledVariants(live: TemplateData): TemplateData;
238
237
  /**
239
- * Load the template synchronously. Prefers the live cache (fresh capture
240
- * from the user's own CC install) and falls back to the bundled snapshot.
238
+ * Tools CC only ships on a specific platform. A capture sees only the host it
239
+ * ran on, so both the bundle and any live capture must be re-unioned against
240
+ * the other platforms' names before use. Filtered back down to the running
241
+ * platform at request time by filterToolsForPlatform().
242
+ *
243
+ * PowerShell shipped in CC v2.1.116 on Windows; POSIX CC installs do not
244
+ * advertise it. As of CC v2.1.162 Glob/Grep are the same shape: Windows CC
245
+ * advertises them, POSIX CC drops them and steers the agent to shell
246
+ * `find`/`grep` instead. Registering them here filters them to win32 clients
247
+ * AND keeps a POSIX capture from dropping them out of the union — the v4.8.28
248
+ * regression, where a Linux runner re-baked the bundle down to 28 tools.
249
+ * Add new platform-scoped tools here as CC adds them.
250
+ */
251
+ export declare const PLATFORM_ONLY_TOOLS: Record<string, Set<string>>;
252
+ /**
253
+ * Tools CC only advertises in an INTERACTIVE session. Captures spawn CC
254
+ * headlessly (`claude --print -p hi`), and CC v2.1.187 dropped these plan-mode /
255
+ * clarification tools in `--print` mode — so a fresh headless capture no longer
256
+ * carries them even though every real interactive CC client still advertises
257
+ * them. Unlike PLATFORM_ONLY_TOOLS these are NOT platform-filtered: they stay in
258
+ * the tool set on every host so a client that declares them is always honored.
259
+ * Add new interactive-only tools here as CC adds them.
260
+ */
261
+ export declare const INTERACTIVE_ONLY_TOOLS: Set<string>;
262
+ /**
263
+ * Tools CC advertises only under some runtime configurations. Unlike
264
+ * INTERACTIVE_ONLY_TOOLS (absent because the capture is headless), these come
265
+ * and go with CC's REMOTE config for the same capture mode: the 2026-08-11 bake
266
+ * on CC v2.1.232 captured all four headlessly, and the 2026-08-15 bake on
267
+ * v2.1.233 captured none of them — while TaskOutput/TaskStop, the rest of the
268
+ * task subsystem, stayed put. That is the v4.2.1 drift class (same binary,
269
+ * different wire shape via remote configuration), not a signal that CC retired
270
+ * the tools.
271
+ *
272
+ * Removing a name here is a deliberate act: it means CC genuinely retired the
273
+ * tool, and it should be paired with the capture evidence that says so.
274
+ */
275
+ export declare const CONFIG_SCOPED_TOOLS: Set<string>;
276
+ /** Why a given tool was preserved — used for logging at bake time. */
277
+ export type PreservedToolReason = 'platform' | 'interactive' | 'config-scoped';
278
+ /**
279
+ * Decide whether `name` must be preserved into a capture taken on `platform`,
280
+ * and say why. The single definition of the superset rule.
281
+ *
282
+ * The cost of the two directions is asymmetric, which is what settles it. A
283
+ * stale entry is INERT: buildCCRequest advertises the intersection of the tool
284
+ * set with what the CLIENT declared, so an entry no client declares is never
285
+ * sent. Dropping one is NOT inert — CC_NATIVE_NAMES_UNION is derived from the
286
+ * loaded template, so a client that does declare the tool stops identity-
287
+ * mapping, falls into the unmapped round-robin, and has its history tool_use
288
+ * blocks renamed onto a fallback slot with junk arguments (the v4.8.93
289
+ * regression).
290
+ */
291
+ export declare function preservedToolReason(name: string, platform: string): PreservedToolReason | null;
292
+ /**
293
+ * Re-union `capture.tools` with any tool `fallback` carries that the capture is
294
+ * required to keep (see preservedToolReason). Returns the merged tool array,
295
+ * CC's alphabetical wire order restored, plus what was preserved and why.
241
296
  *
242
- * This is intentionally sync and fast it runs at module init on every
243
- * dario request handler. The actual capture is async and runs in the
244
- * background via refreshLiveFingerprintAsync(); its results are written
245
- * to the cache file and picked up on the next dario startup.
297
+ * This is the rule the bake has always applied to the previous bundle. It must
298
+ * apply to a LIVE capture too: `loadTemplate` prefers a fresh live cache, the
299
+ * cache refreshes on a 24h TTL, and the capture is headless — so without this,
300
+ * every dario install with CC present degrades its own tool set within a day
301
+ * of running, and CI never sees it because CI has no live cache (#1035).
246
302
  */
303
+ export declare function mergePreservedTools<T extends {
304
+ name: string;
305
+ }>(captureTools: T[], fallbackTools: T[], platform: string): {
306
+ tools: T[];
307
+ preserved: Array<{
308
+ name: string;
309
+ reason: PreservedToolReason;
310
+ }>;
311
+ };
312
+ export declare function withBundledVariants(live: TemplateData): TemplateData;
247
313
  export declare function loadTemplate(_options?: {
248
314
  silent?: boolean;
249
315
  }): TemplateData;
@@ -167,6 +167,114 @@ export function promptVariantsOf(t) {
167
167
  * Variants the live template already has win, so a future per-model live
168
168
  * capture supersedes the bake without another change here.
169
169
  */
170
+ /**
171
+ * Tools CC only ships on a specific platform. A capture sees only the host it
172
+ * ran on, so both the bundle and any live capture must be re-unioned against
173
+ * the other platforms' names before use. Filtered back down to the running
174
+ * platform at request time by filterToolsForPlatform().
175
+ *
176
+ * PowerShell shipped in CC v2.1.116 on Windows; POSIX CC installs do not
177
+ * advertise it. As of CC v2.1.162 Glob/Grep are the same shape: Windows CC
178
+ * advertises them, POSIX CC drops them and steers the agent to shell
179
+ * `find`/`grep` instead. Registering them here filters them to win32 clients
180
+ * AND keeps a POSIX capture from dropping them out of the union — the v4.8.28
181
+ * regression, where a Linux runner re-baked the bundle down to 28 tools.
182
+ * Add new platform-scoped tools here as CC adds them.
183
+ */
184
+ export const PLATFORM_ONLY_TOOLS = {
185
+ win32: new Set(['PowerShell', 'Glob', 'Grep']),
186
+ };
187
+ /**
188
+ * Tools CC only advertises in an INTERACTIVE session. Captures spawn CC
189
+ * headlessly (`claude --print -p hi`), and CC v2.1.187 dropped these plan-mode /
190
+ * clarification tools in `--print` mode — so a fresh headless capture no longer
191
+ * carries them even though every real interactive CC client still advertises
192
+ * them. Unlike PLATFORM_ONLY_TOOLS these are NOT platform-filtered: they stay in
193
+ * the tool set on every host so a client that declares them is always honored.
194
+ * Add new interactive-only tools here as CC adds them.
195
+ */
196
+ export const INTERACTIVE_ONLY_TOOLS = new Set([
197
+ 'AskUserQuestion',
198
+ 'EnterPlanMode',
199
+ 'ExitPlanMode',
200
+ ]);
201
+ /**
202
+ * Tools CC advertises only under some runtime configurations. Unlike
203
+ * INTERACTIVE_ONLY_TOOLS (absent because the capture is headless), these come
204
+ * and go with CC's REMOTE config for the same capture mode: the 2026-08-11 bake
205
+ * on CC v2.1.232 captured all four headlessly, and the 2026-08-15 bake on
206
+ * v2.1.233 captured none of them — while TaskOutput/TaskStop, the rest of the
207
+ * task subsystem, stayed put. That is the v4.2.1 drift class (same binary,
208
+ * different wire shape via remote configuration), not a signal that CC retired
209
+ * the tools.
210
+ *
211
+ * Removing a name here is a deliberate act: it means CC genuinely retired the
212
+ * tool, and it should be paired with the capture evidence that says so.
213
+ */
214
+ export const CONFIG_SCOPED_TOOLS = new Set([
215
+ 'TaskCreate',
216
+ 'TaskGet',
217
+ 'TaskList',
218
+ 'TaskUpdate',
219
+ ]);
220
+ /**
221
+ * Decide whether `name` must be preserved into a capture taken on `platform`,
222
+ * and say why. The single definition of the superset rule.
223
+ *
224
+ * The cost of the two directions is asymmetric, which is what settles it. A
225
+ * stale entry is INERT: buildCCRequest advertises the intersection of the tool
226
+ * set with what the CLIENT declared, so an entry no client declares is never
227
+ * sent. Dropping one is NOT inert — CC_NATIVE_NAMES_UNION is derived from the
228
+ * loaded template, so a client that does declare the tool stops identity-
229
+ * mapping, falls into the unmapped round-robin, and has its history tool_use
230
+ * blocks renamed onto a fallback slot with junk arguments (the v4.8.93
231
+ * regression).
232
+ */
233
+ export function preservedToolReason(name, platform) {
234
+ for (const [plat, names] of Object.entries(PLATFORM_ONLY_TOOLS)) {
235
+ if (names.has(name) && plat !== platform)
236
+ return 'platform';
237
+ }
238
+ if (INTERACTIVE_ONLY_TOOLS.has(name))
239
+ return 'interactive';
240
+ if (CONFIG_SCOPED_TOOLS.has(name))
241
+ return 'config-scoped';
242
+ return null;
243
+ }
244
+ /**
245
+ * Re-union `capture.tools` with any tool `fallback` carries that the capture is
246
+ * required to keep (see preservedToolReason). Returns the merged tool array,
247
+ * CC's alphabetical wire order restored, plus what was preserved and why.
248
+ *
249
+ * This is the rule the bake has always applied to the previous bundle. It must
250
+ * apply to a LIVE capture too: `loadTemplate` prefers a fresh live cache, the
251
+ * cache refreshes on a 24h TTL, and the capture is headless — so without this,
252
+ * every dario install with CC present degrades its own tool set within a day
253
+ * of running, and CI never sees it because CI has no live cache (#1035).
254
+ */
255
+ export function mergePreservedTools(captureTools, fallbackTools, platform) {
256
+ const have = new Set(captureTools.map((t) => t.name));
257
+ const preserved = [];
258
+ const additions = [];
259
+ for (const tool of fallbackTools) {
260
+ if (have.has(tool.name))
261
+ continue;
262
+ const reason = preservedToolReason(tool.name, platform);
263
+ if (!reason)
264
+ continue;
265
+ additions.push(tool);
266
+ preserved.push({ name: tool.name, reason });
267
+ have.add(tool.name);
268
+ }
269
+ if (additions.length === 0)
270
+ return { tools: captureTools, preserved };
271
+ // CC sends tools alphabetically by name — sort after merge so preserved tools
272
+ // insert at their natural position rather than appending at the end.
273
+ return {
274
+ tools: [...captureTools, ...additions].sort((a, b) => a.name.localeCompare(b.name)),
275
+ preserved,
276
+ };
277
+ }
170
278
  export function withBundledVariants(live) {
171
279
  let bundled;
172
280
  try {
@@ -217,12 +325,40 @@ const LIVE_TTL_MS = 24 * 60 * 60 * 1000; // re-extract once a day
217
325
  * background via refreshLiveFingerprintAsync(); its results are written
218
326
  * to the cache file and picked up on the next dario startup.
219
327
  */
328
+ /**
329
+ * Bring a live capture up to the superset the rest of the codebase assumes:
330
+ * merge back any preserved tool the bundle has and the capture lacks, then
331
+ * re-derive `tool_names`.
332
+ *
333
+ * `tool_names === tools.map(t => t.name)` is the contract everywhere —
334
+ * scrubTemplate() sets it from `tools`, and so does the capture path. The bake
335
+ * learned the hard way that a merge which mutates `tools` without re-deriving
336
+ * the list ships an artifact whose two tool lists disagree; do not repeat it
337
+ * here (#1035).
338
+ */
339
+ function withPreservedTools(live, options) {
340
+ let bundled;
341
+ try {
342
+ bundled = loadBundledTemplate({ silent: true });
343
+ }
344
+ catch {
345
+ return live; // bundle unreadable — the capture is still better than throwing
346
+ }
347
+ const { tools, preserved } = mergePreservedTools(live.tools, bundled.tools, process.platform);
348
+ if (preserved.length === 0)
349
+ return live;
350
+ if (!options?.silent) {
351
+ const byReason = preserved.map((p) => `${p.name} (${p.reason})`).join(', ');
352
+ console.log(`[dario] live template: restored ${preserved.length} tool${preserved.length === 1 ? '' : 's'} the capture omitted: ${byReason}`);
353
+ }
354
+ return { ...live, tools, tool_names: tools.map((t) => t.name) };
355
+ }
220
356
  export function loadTemplate(_options) {
221
357
  const cached = readLiveCache();
222
358
  if (cached) {
223
359
  const age = Date.now() - new Date(cached._captured).getTime();
224
360
  if (age < LIVE_TTL_MS) {
225
- return withBundledVariants(cached);
361
+ return withPreservedTools(withBundledVariants(cached), _options);
226
362
  }
227
363
  // Stale cache: prefer whichever of the live cache and the bundled
228
364
  // snapshot was captured more recently — do NOT blindly keep the cache.
@@ -236,7 +372,7 @@ export function loadTemplate(_options) {
236
372
  const bundled = loadBundledTemplate(_options);
237
373
  const cachedAt = new Date(cached._captured).getTime();
238
374
  const bundledAt = new Date(bundled._captured).getTime();
239
- return Number.isFinite(bundledAt) && bundledAt > cachedAt ? bundled : withBundledVariants(cached);
375
+ return Number.isFinite(bundledAt) && bundledAt > cachedAt ? bundled : withPreservedTools(withBundledVariants(cached), _options);
240
376
  }
241
377
  return loadBundledTemplate(_options);
242
378
  }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@askalf/dario",
3
- "version": "5.5.24",
3
+ "version": "5.5.27",
4
4
  "description": "Use your Claude Pro/Max subscription in any tool — Cursor, Cline, Aider, the Agent SDK, your scripts — at subscription pricing, not per-token API bills. One local Anthropic + OpenAI-compatible endpoint.",
5
5
  "type": "module",
6
6
  "bin": {