zidane 5.4.0 → 5.4.2

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 (47) hide show
  1. package/dist/{agent-DHQAsdj6.d.ts → agent-DxBoKDba.d.ts} +140 -1
  2. package/dist/agent-DxBoKDba.d.ts.map +1 -0
  3. package/dist/chat.d.ts +62 -5
  4. package/dist/chat.d.ts.map +1 -1
  5. package/dist/chat.js +2 -2
  6. package/dist/{index-CrqFoaQA.d.ts → index-B2VOOijU.d.ts} +461 -8
  7. package/dist/index-B2VOOijU.d.ts.map +1 -0
  8. package/dist/{index-CHSaLab5.d.ts → index-BOtXdQkW.d.ts} +2 -2
  9. package/dist/{index-CHSaLab5.d.ts.map → index-BOtXdQkW.d.ts.map} +1 -1
  10. package/dist/index.d.ts +4 -4
  11. package/dist/index.js +1522 -47
  12. package/dist/index.js.map +1 -1
  13. package/dist/{login-bK0EP8La.js → login-CJbeAadS.js} +2 -2
  14. package/dist/{login-bK0EP8La.js.map → login-CJbeAadS.js.map} +1 -1
  15. package/dist/mcp.d.ts +1 -1
  16. package/dist/{presets-M8f6lDnW.js → presets-MCcvxiNT.js} +2 -2
  17. package/dist/{presets-M8f6lDnW.js.map → presets-MCcvxiNT.js.map} +1 -1
  18. package/dist/presets.d.ts +2 -2
  19. package/dist/presets.js +1 -1
  20. package/dist/providers.d.ts +1 -1
  21. package/dist/session/sqlite.d.ts +1 -1
  22. package/dist/session.d.ts +1 -1
  23. package/dist/skills.d.ts +2 -2
  24. package/dist/{tools-DKdyPoUf.js → tools-BNfyY14s.js} +192 -34
  25. package/dist/tools-BNfyY14s.js.map +1 -0
  26. package/dist/tools.d.ts +2 -2
  27. package/dist/tools.js +1 -1
  28. package/dist/{transcript-anchors-Fgh_rZ04.d.ts → transcript-anchors-DonKvoh4.d.ts} +19 -4
  29. package/dist/transcript-anchors-DonKvoh4.d.ts.map +1 -0
  30. package/dist/tui.d.ts +3 -3
  31. package/dist/tui.d.ts.map +1 -1
  32. package/dist/tui.js +52 -23
  33. package/dist/tui.js.map +1 -1
  34. package/dist/{turn-operations-DDokWR8p.js → turn-operations-TKvy0q29.js} +128 -4
  35. package/dist/turn-operations-TKvy0q29.js.map +1 -0
  36. package/dist/types-IcokUOyC.js.map +1 -1
  37. package/dist/types.d.ts +2 -2
  38. package/docs/ARCHITECTURE.md +6 -5
  39. package/docs/CHAT.md +21 -3
  40. package/docs/SKILL.md +1 -1
  41. package/docs/TUI.md +2 -2
  42. package/package.json +1 -1
  43. package/dist/agent-DHQAsdj6.d.ts.map +0 -1
  44. package/dist/index-CrqFoaQA.d.ts.map +0 -1
  45. package/dist/tools-DKdyPoUf.js.map +0 -1
  46. package/dist/transcript-anchors-Fgh_rZ04.d.ts.map +0 -1
  47. package/dist/turn-operations-DDokWR8p.js.map +0 -1
@@ -1 +1 @@
1
- {"version":3,"file":"presets-M8f6lDnW.js","names":[],"sources":["../src/presets/basic.ts","../src/presets/index.ts"],"sourcesContent":["import { definePreset } from '.'\nimport { edit, listFiles, multiEdit, readFile, shell, writeFile } from '../tools'\nimport { createSpawnTool } from '../tools/spawn'\n\n/**\n * Core tools available in every basic preset (without spawn).\n *\n * `edit` and `multi_edit` ship in the basic set because surgical edits are the\n * default modality for production agents — `write_file` is for full overwrites.\n * `glob` and `grep` are exported but opt-in: not every agent needs codebase\n * search, and shipping them by default would force `tool:gate` work onto\n * consumers that prefer the model to use `shell` + classic Unix tools.\n */\nexport const basicTools = { shell, readFile, writeFile, listFiles, edit, multiEdit }\n\nexport default definePreset({\n name: 'basic',\n system: 'You are a helpful assistant with access to shell, file reading, file writing, surgical and multi-edit tools, directory listing, and sub-agent spawning. Prefer `edit` / `multi_edit` for in-place changes and `write_file` for full file overwrites. Use them to accomplish tasks in the project directory.',\n // `persist: true` shares the parent's session with every child agent — child\n // turns land in `session.turns` tagged with their own `runId`, and the run\n // itself is recorded in `session.runs` with `parentRunId` + `depth`. That's\n // what lets a reloaded TUI session reconstruct the full subagent tree (see\n // `eventsFromTurns` in `tui/store.ts`). Hosts that want children in-memory\n // only can construct their own preset with `createSpawnTool()`.\n tools: { ...basicTools, spawn: createSpawnTool({ persist: true }) },\n})\n","import type { AgentHooks, AgentOptions } from '../agent'\n\nexport type { AgentHookMap } from '../agent'\n\n/**\n * A preset is a reusable slice of `AgentOptions` — spread it into `createAgent()`\n * to configure tools, a default system prompt, aliases, behavior defaults, and\n * agent-lifetime hooks.\n *\n * `provider`, `execution`, `session`, and internal fields are excluded so presets\n * remain shareable and composable.\n *\n * ```ts\n * import { basic } from 'zidane/presets'\n * createAgent({ ...basic, provider })\n * ```\n *\n * ### Composing multiple presets\n *\n * Bare `...spread` is shallow — `{ ...a, ...b }` overwrites every key `b`\n * defines, including `hooks`. Use {@link composePresets} when you want\n * field-aware merging (per-event hook concat, tools shallow-merge, etc.):\n *\n * ```ts\n * createAgent({ ...composePresets(basic, telemetry, mine), provider })\n * ```\n */\nexport type Preset = Omit<Partial<AgentOptions>, 'provider' | 'execution' | 'session' | 'mcpConnector'>\n\n/**\n * Identity helper for type inference when defining a preset.\n */\nexport function definePreset(config: Preset): Preset {\n return config\n}\n\n/**\n * Field-aware composition of presets. Right-most preset wins for scalar fields;\n * objects shallow-merge; arrays and hook handler lists concatenate. Designed so\n * stacking presets does the obvious thing without the spread-collision footgun:\n *\n * - `name`, `system`, `eager`, `skills` → last-defined wins\n * - `tools`, `toolAliases`, `behavior` → shallow-merge (later keys override)\n * - `behavior.dedupTools`, `behavior.toolBudgets` → **deep-merge** (per-tool-name; later wins on collision)\n * - `mcpServers` → concat with last-wins on `name` collision\n * - `hooks` → per-event concat; every handler fires\n *\n * `hooks` always emerges as `event → handler[]` so downstream registration\n * (in `createAgent`) sees a uniform shape. Order of handlers within an event\n * follows preset order: earlier presets register first.\n *\n * `mcpServers` is deduped by `name` because shipping two servers with the same\n * name would trip the connector at runtime — a later preset overriding an\n * earlier preset's `github` server is the practical intent.\n *\n * `behavior.dedupTools` and `behavior.toolBudgets` get the same per-key deep-merge\n * because they are tool-name-keyed records — a preset that ships a dedup hasher\n * for one tool should not erase a hasher another preset ships for a different\n * tool. Last-wins still applies on a per-tool collision so a downstream preset\n * can override an upstream preset's policy for one specific tool. Other\n * `behavior` fields keep last-wins semantics.\n */\nexport function composePresets(...presets: Preset[]): Preset {\n const out: Preset = {}\n const hooksByEvent: { [K in keyof AgentHooks]?: AgentHooks[K][] } = {}\n // Keep mcpServers in source-order on first sight, but allow later\n // declarations to override earlier ones with the same `name`. A `Map`\n // keyed by name gives O(1) override + stable iteration.\n const mcpByName = new Map<string, NonNullable<Preset['mcpServers']>[number]>()\n\n for (const p of presets) {\n if (p.name !== undefined)\n out.name = p.name\n if (p.system !== undefined)\n out.system = p.system\n if (p.eager !== undefined)\n out.eager = p.eager\n if (p.skills !== undefined)\n out.skills = p.skills\n if (p.tools)\n out.tools = { ...out.tools, ...p.tools }\n if (p.toolAliases)\n out.toolAliases = { ...out.toolAliases, ...p.toolAliases }\n if (p.behavior) {\n // Top-level shallow-merge first; then deep-merge the two tool-name-keyed\n // sub-records so per-tool entries from earlier presets aren't clobbered.\n const merged: NonNullable<Preset['behavior']> = { ...out.behavior, ...p.behavior }\n if (out.behavior?.dedupTools || p.behavior.dedupTools) {\n merged.dedupTools = { ...out.behavior?.dedupTools, ...p.behavior.dedupTools }\n }\n if (out.behavior?.toolBudgets || p.behavior.toolBudgets) {\n merged.toolBudgets = { ...out.behavior?.toolBudgets, ...p.behavior.toolBudgets }\n }\n out.behavior = merged\n }\n if (p.mcpServers) {\n for (const server of p.mcpServers)\n mcpByName.set(server.name, server)\n }\n if (p.hooks) {\n for (const [event, handler] of Object.entries(p.hooks)) {\n if (handler === undefined)\n continue\n const list = Array.isArray(handler) ? handler : [handler]\n const key = event as keyof AgentHooks\n // Safe cast: we read the loose `AgentHookMap` shape (handler-or-array)\n // and re-emit only as arrays. Each `list` element matches the event's\n // handler signature by construction (the input was typed `AgentHookMap`).\n const bucket = (hooksByEvent[key] ??= []) as unknown[]\n bucket.push(...(list as unknown[]))\n }\n }\n }\n\n if (mcpByName.size > 0)\n out.mcpServers = [...mcpByName.values()]\n\n if (Object.keys(hooksByEvent).length > 0)\n out.hooks = hooksByEvent\n\n return out\n}\n\nexport { default as basic, basicTools } from './basic'\n"],"mappings":";;;;;;;;;;;AAaA,MAAa,aAAa;CAAE;CAAO;CAAU;CAAW;CAAW;CAAM;CAAW;AAEpF,IAAA,gBAAe,aAAa;CAC1B,MAAM;CACN,QAAQ;CAOR,OAAO;EAAE,GAAG;EAAY,OAAO,gBAAgB,EAAE,SAAS,MAAM,CAAC;EAAE;CACpE,CAAC;;;;;;ACOF,SAAgB,aAAa,QAAwB;CACnD,OAAO;;;;;;;;;;;;;;;;;;;;;;;;;;;;AA6BT,SAAgB,eAAe,GAAG,SAA2B;CAC3D,MAAM,MAAc,EAAE;CACtB,MAAM,eAA8D,EAAE;CAItE,MAAM,4BAAY,IAAI,KAAwD;CAE9E,KAAK,MAAM,KAAK,SAAS;EACvB,IAAI,EAAE,SAAS,KAAA,GACb,IAAI,OAAO,EAAE;EACf,IAAI,EAAE,WAAW,KAAA,GACf,IAAI,SAAS,EAAE;EACjB,IAAI,EAAE,UAAU,KAAA,GACd,IAAI,QAAQ,EAAE;EAChB,IAAI,EAAE,WAAW,KAAA,GACf,IAAI,SAAS,EAAE;EACjB,IAAI,EAAE,OACJ,IAAI,QAAQ;GAAE,GAAG,IAAI;GAAO,GAAG,EAAE;GAAO;EAC1C,IAAI,EAAE,aACJ,IAAI,cAAc;GAAE,GAAG,IAAI;GAAa,GAAG,EAAE;GAAa;EAC5D,IAAI,EAAE,UAAU;GAGd,MAAM,SAA0C;IAAE,GAAG,IAAI;IAAU,GAAG,EAAE;IAAU;GAClF,IAAI,IAAI,UAAU,cAAc,EAAE,SAAS,YACzC,OAAO,aAAa;IAAE,GAAG,IAAI,UAAU;IAAY,GAAG,EAAE,SAAS;IAAY;GAE/E,IAAI,IAAI,UAAU,eAAe,EAAE,SAAS,aAC1C,OAAO,cAAc;IAAE,GAAG,IAAI,UAAU;IAAa,GAAG,EAAE,SAAS;IAAa;GAElF,IAAI,WAAW;;EAEjB,IAAI,EAAE,YACJ,KAAK,MAAM,UAAU,EAAE,YACrB,UAAU,IAAI,OAAO,MAAM,OAAO;EAEtC,IAAI,EAAE,OACJ,KAAK,MAAM,CAAC,OAAO,YAAY,OAAO,QAAQ,EAAE,MAAM,EAAE;GACtD,IAAI,YAAY,KAAA,GACd;GACF,MAAM,OAAO,MAAM,QAAQ,QAAQ,GAAG,UAAU,CAAC,QAAQ;GACzD,MAAM,MAAM;GAKZ,CADgB,aAAa,SAAS,EAAE,EACjC,KAAK,GAAI,KAAmB;;;CAKzC,IAAI,UAAU,OAAO,GACnB,IAAI,aAAa,CAAC,GAAG,UAAU,QAAQ,CAAC;CAE1C,IAAI,OAAO,KAAK,aAAa,CAAC,SAAS,GACrC,IAAI,QAAQ;CAEd,OAAO"}
1
+ {"version":3,"file":"presets-MCcvxiNT.js","names":[],"sources":["../src/presets/basic.ts","../src/presets/index.ts"],"sourcesContent":["import { definePreset } from '.'\nimport { edit, listFiles, multiEdit, readFile, shell, writeFile } from '../tools'\nimport { createSpawnTool } from '../tools/spawn'\n\n/**\n * Core tools available in every basic preset (without spawn).\n *\n * `edit` and `multi_edit` ship in the basic set because surgical edits are the\n * default modality for production agents — `write_file` is for full overwrites.\n * `glob` and `grep` are exported but opt-in: not every agent needs codebase\n * search, and shipping them by default would force `tool:gate` work onto\n * consumers that prefer the model to use `shell` + classic Unix tools.\n */\nexport const basicTools = { shell, readFile, writeFile, listFiles, edit, multiEdit }\n\nexport default definePreset({\n name: 'basic',\n system: 'You are a helpful assistant with access to shell, file reading, file writing, surgical and multi-edit tools, directory listing, and sub-agent spawning. Prefer `edit` / `multi_edit` for in-place changes and `write_file` for full file overwrites. Use them to accomplish tasks in the project directory.',\n // `persist: true` shares the parent's session with every child agent — child\n // turns land in `session.turns` tagged with their own `runId`, and the run\n // itself is recorded in `session.runs` with `parentRunId` + `depth`. That's\n // what lets a reloaded TUI session reconstruct the full subagent tree (see\n // `eventsFromTurns` in `tui/store.ts`). Hosts that want children in-memory\n // only can construct their own preset with `createSpawnTool()`.\n tools: { ...basicTools, spawn: createSpawnTool({ persist: true }) },\n})\n","import type { AgentHooks, AgentOptions } from '../agent'\n\nexport type { AgentHookMap } from '../agent'\n\n/**\n * A preset is a reusable slice of `AgentOptions` — spread it into `createAgent()`\n * to configure tools, a default system prompt, aliases, behavior defaults, and\n * agent-lifetime hooks.\n *\n * `provider`, `execution`, `session`, and internal fields are excluded so presets\n * remain shareable and composable.\n *\n * ```ts\n * import { basic } from 'zidane/presets'\n * createAgent({ ...basic, provider })\n * ```\n *\n * ### Composing multiple presets\n *\n * Bare `...spread` is shallow — `{ ...a, ...b }` overwrites every key `b`\n * defines, including `hooks`. Use {@link composePresets} when you want\n * field-aware merging (per-event hook concat, tools shallow-merge, etc.):\n *\n * ```ts\n * createAgent({ ...composePresets(basic, telemetry, mine), provider })\n * ```\n */\nexport type Preset = Omit<Partial<AgentOptions>, 'provider' | 'execution' | 'session' | 'mcpConnector'>\n\n/**\n * Identity helper for type inference when defining a preset.\n */\nexport function definePreset(config: Preset): Preset {\n return config\n}\n\n/**\n * Field-aware composition of presets. Right-most preset wins for scalar fields;\n * objects shallow-merge; arrays and hook handler lists concatenate. Designed so\n * stacking presets does the obvious thing without the spread-collision footgun:\n *\n * - `name`, `system`, `eager`, `skills` → last-defined wins\n * - `tools`, `toolAliases`, `behavior` → shallow-merge (later keys override)\n * - `behavior.dedupTools`, `behavior.toolBudgets` → **deep-merge** (per-tool-name; later wins on collision)\n * - `mcpServers` → concat with last-wins on `name` collision\n * - `hooks` → per-event concat; every handler fires\n *\n * `hooks` always emerges as `event → handler[]` so downstream registration\n * (in `createAgent`) sees a uniform shape. Order of handlers within an event\n * follows preset order: earlier presets register first.\n *\n * `mcpServers` is deduped by `name` because shipping two servers with the same\n * name would trip the connector at runtime — a later preset overriding an\n * earlier preset's `github` server is the practical intent.\n *\n * `behavior.dedupTools` and `behavior.toolBudgets` get the same per-key deep-merge\n * because they are tool-name-keyed records — a preset that ships a dedup hasher\n * for one tool should not erase a hasher another preset ships for a different\n * tool. Last-wins still applies on a per-tool collision so a downstream preset\n * can override an upstream preset's policy for one specific tool. Other\n * `behavior` fields keep last-wins semantics.\n */\nexport function composePresets(...presets: Preset[]): Preset {\n const out: Preset = {}\n const hooksByEvent: { [K in keyof AgentHooks]?: AgentHooks[K][] } = {}\n // Keep mcpServers in source-order on first sight, but allow later\n // declarations to override earlier ones with the same `name`. A `Map`\n // keyed by name gives O(1) override + stable iteration.\n const mcpByName = new Map<string, NonNullable<Preset['mcpServers']>[number]>()\n\n for (const p of presets) {\n if (p.name !== undefined)\n out.name = p.name\n if (p.system !== undefined)\n out.system = p.system\n if (p.eager !== undefined)\n out.eager = p.eager\n if (p.skills !== undefined)\n out.skills = p.skills\n if (p.tools)\n out.tools = { ...out.tools, ...p.tools }\n if (p.toolAliases)\n out.toolAliases = { ...out.toolAliases, ...p.toolAliases }\n if (p.behavior) {\n // Top-level shallow-merge first; then deep-merge the two tool-name-keyed\n // sub-records so per-tool entries from earlier presets aren't clobbered.\n const merged: NonNullable<Preset['behavior']> = { ...out.behavior, ...p.behavior }\n if (out.behavior?.dedupTools || p.behavior.dedupTools) {\n merged.dedupTools = { ...out.behavior?.dedupTools, ...p.behavior.dedupTools }\n }\n if (out.behavior?.toolBudgets || p.behavior.toolBudgets) {\n merged.toolBudgets = { ...out.behavior?.toolBudgets, ...p.behavior.toolBudgets }\n }\n out.behavior = merged\n }\n if (p.mcpServers) {\n for (const server of p.mcpServers)\n mcpByName.set(server.name, server)\n }\n if (p.hooks) {\n for (const [event, handler] of Object.entries(p.hooks)) {\n if (handler === undefined)\n continue\n const list = Array.isArray(handler) ? handler : [handler]\n const key = event as keyof AgentHooks\n // Safe cast: we read the loose `AgentHookMap` shape (handler-or-array)\n // and re-emit only as arrays. Each `list` element matches the event's\n // handler signature by construction (the input was typed `AgentHookMap`).\n const bucket = (hooksByEvent[key] ??= []) as unknown[]\n bucket.push(...(list as unknown[]))\n }\n }\n }\n\n if (mcpByName.size > 0)\n out.mcpServers = [...mcpByName.values()]\n\n if (Object.keys(hooksByEvent).length > 0)\n out.hooks = hooksByEvent\n\n return out\n}\n\nexport { default as basic, basicTools } from './basic'\n"],"mappings":";;;;;;;;;;;AAaA,MAAa,aAAa;CAAE;CAAO;CAAU;CAAW;CAAW;CAAM;CAAW;AAEpF,IAAA,gBAAe,aAAa;CAC1B,MAAM;CACN,QAAQ;CAOR,OAAO;EAAE,GAAG;EAAY,OAAO,gBAAgB,EAAE,SAAS,MAAM,CAAC;EAAE;CACpE,CAAC;;;;;;ACOF,SAAgB,aAAa,QAAwB;CACnD,OAAO;;;;;;;;;;;;;;;;;;;;;;;;;;;;AA6BT,SAAgB,eAAe,GAAG,SAA2B;CAC3D,MAAM,MAAc,EAAE;CACtB,MAAM,eAA8D,EAAE;CAItE,MAAM,4BAAY,IAAI,KAAwD;CAE9E,KAAK,MAAM,KAAK,SAAS;EACvB,IAAI,EAAE,SAAS,KAAA,GACb,IAAI,OAAO,EAAE;EACf,IAAI,EAAE,WAAW,KAAA,GACf,IAAI,SAAS,EAAE;EACjB,IAAI,EAAE,UAAU,KAAA,GACd,IAAI,QAAQ,EAAE;EAChB,IAAI,EAAE,WAAW,KAAA,GACf,IAAI,SAAS,EAAE;EACjB,IAAI,EAAE,OACJ,IAAI,QAAQ;GAAE,GAAG,IAAI;GAAO,GAAG,EAAE;GAAO;EAC1C,IAAI,EAAE,aACJ,IAAI,cAAc;GAAE,GAAG,IAAI;GAAa,GAAG,EAAE;GAAa;EAC5D,IAAI,EAAE,UAAU;GAGd,MAAM,SAA0C;IAAE,GAAG,IAAI;IAAU,GAAG,EAAE;IAAU;GAClF,IAAI,IAAI,UAAU,cAAc,EAAE,SAAS,YACzC,OAAO,aAAa;IAAE,GAAG,IAAI,UAAU;IAAY,GAAG,EAAE,SAAS;IAAY;GAE/E,IAAI,IAAI,UAAU,eAAe,EAAE,SAAS,aAC1C,OAAO,cAAc;IAAE,GAAG,IAAI,UAAU;IAAa,GAAG,EAAE,SAAS;IAAa;GAElF,IAAI,WAAW;;EAEjB,IAAI,EAAE,YACJ,KAAK,MAAM,UAAU,EAAE,YACrB,UAAU,IAAI,OAAO,MAAM,OAAO;EAEtC,IAAI,EAAE,OACJ,KAAK,MAAM,CAAC,OAAO,YAAY,OAAO,QAAQ,EAAE,MAAM,EAAE;GACtD,IAAI,YAAY,KAAA,GACd;GACF,MAAM,OAAO,MAAM,QAAQ,QAAQ,GAAG,UAAU,CAAC,QAAQ;GACzD,MAAM,MAAM;GAKZ,CADgB,aAAa,SAAS,EAAE,EACjC,KAAK,GAAI,KAAmB;;;CAKzC,IAAI,UAAU,OAAO,GACnB,IAAI,aAAa,CAAC,GAAG,UAAU,QAAQ,CAAC;CAE1C,IAAI,OAAO,KAAK,aAAa,CAAC,SAAS,GACrC,IAAI,QAAQ;CAEd,OAAO"}
package/dist/presets.d.ts CHANGED
@@ -1,3 +1,3 @@
1
- import { n as AgentHookMap } from "./agent-DHQAsdj6.js";
2
- import { a as basicTools, i as _default, n as composePresets, r as definePreset, t as Preset } from "./index-CrqFoaQA.js";
1
+ import { n as AgentHookMap } from "./agent-DxBoKDba.js";
2
+ import { a as basicTools, i as _default, n as composePresets, r as definePreset, t as Preset } from "./index-B2VOOijU.js";
3
3
  export { AgentHookMap, Preset, _default as basic, basicTools, composePresets, definePreset };
package/dist/presets.js CHANGED
@@ -1,2 +1,2 @@
1
- import { i as basic_default, n as definePreset, r as basicTools, t as composePresets } from "./presets-M8f6lDnW.js";
1
+ import { i as basic_default, n as definePreset, r as basicTools, t as composePresets } from "./presets-MCcvxiNT.js";
2
2
  export { basic_default as basic, basicTools, composePresets, definePreset };
@@ -1,2 +1,2 @@
1
- import { At as cerebras, Ct as OpenAICompatParams, Dt as OpenAIParams, Et as openaiCompat, Mt as anthropic, Ot as openai, St as OpenAICompatHttpError, Tt as mapOAIFinishReason, _t as sanitizeToolSchema, bt as openrouter, ct as StreamCallbacks, dt as ToolResult, ft as ToolSpec, gt as SchemaSanitizeResult, ht as SchemaSanitizeProfile, jt as AnthropicParams, kt as CerebrasParams, lt as StreamOptions, mt as SchemaSanitizeOptions, ot as Provider, pt as TurnResult, st as ProviderCapabilities, ut as ToolCall, vt as sanitizeToolSpecs, wt as classifyOpenAICompatError, xt as OpenAICompatAuthHeader, yt as OpenRouterParams } from "./agent-DHQAsdj6.js";
1
+ import { At as cerebras, Ct as OpenAICompatParams, Dt as OpenAIParams, Et as openaiCompat, Mt as anthropic, Ot as openai, St as OpenAICompatHttpError, Tt as mapOAIFinishReason, _t as sanitizeToolSchema, bt as openrouter, ct as StreamCallbacks, dt as ToolResult, ft as ToolSpec, gt as SchemaSanitizeResult, ht as SchemaSanitizeProfile, jt as AnthropicParams, kt as CerebrasParams, lt as StreamOptions, mt as SchemaSanitizeOptions, ot as Provider, pt as TurnResult, st as ProviderCapabilities, ut as ToolCall, vt as sanitizeToolSpecs, wt as classifyOpenAICompatError, xt as OpenAICompatAuthHeader, yt as OpenRouterParams } from "./agent-DxBoKDba.js";
2
2
  export { AnthropicParams, CerebrasParams, OpenAICompatAuthHeader, OpenAICompatHttpError, OpenAICompatParams, OpenAIParams, OpenRouterParams, Provider, ProviderCapabilities, SchemaSanitizeOptions, SchemaSanitizeProfile, SchemaSanitizeResult, StreamCallbacks, StreamOptions, ToolCall, ToolResult, ToolSpec, TurnResult, anthropic, cerebras, classifyOpenAICompatError, mapOAIFinishReason, openai, openaiCompat, openrouter, sanitizeToolSchema, sanitizeToolSpecs };
@@ -1,4 +1,4 @@
1
- import { I as SessionStore } from "../agent-DHQAsdj6.js";
1
+ import { I as SessionStore } from "../agent-DxBoKDba.js";
2
2
 
3
3
  //#region src/session/sqlite.d.ts
4
4
  interface SqliteStoreOptions {
package/dist/session.d.ts CHANGED
@@ -1,2 +1,2 @@
1
- import { $ as fromOpenAI, B as createRemoteStore, F as SessionRun, I as SessionStore, J as autoDetectAndConvert, Kt as SessionContentBlock, L as createSession, M as CreateSessionOptions, N as Session, P as SessionData, Q as fromAnthropic, R as loadSession, Xt as SessionTurn, Yt as SessionMessage, at as createFileMapStore, et as toAnthropic, it as FileMapStoreOptions, nt as createMemoryStore, rt as FileMapAdapter, tt as toOpenAI, z as RemoteStoreOptions } from "./agent-DHQAsdj6.js";
1
+ import { $ as fromOpenAI, B as createRemoteStore, F as SessionRun, I as SessionStore, J as autoDetectAndConvert, Kt as SessionContentBlock, L as createSession, M as CreateSessionOptions, N as Session, P as SessionData, Q as fromAnthropic, R as loadSession, Xt as SessionTurn, Yt as SessionMessage, at as createFileMapStore, et as toAnthropic, it as FileMapStoreOptions, nt as createMemoryStore, rt as FileMapAdapter, tt as toOpenAI, z as RemoteStoreOptions } from "./agent-DxBoKDba.js";
2
2
  export { CreateSessionOptions, FileMapAdapter, FileMapStoreOptions, RemoteStoreOptions, Session, SessionContentBlock, SessionData, SessionMessage, SessionRun, SessionStore, SessionTurn, autoDetectAndConvert, createFileMapStore, createMemoryStore, createRemoteStore, createSession, fromAnthropic, fromOpenAI, loadSession, toAnthropic, toOpenAI };
package/dist/skills.d.ts CHANGED
@@ -1,3 +1,3 @@
1
- import { A as SkillSource, D as SkillConfig, O as SkillDiagnostic, c as DeactivationReason, d as createSkillActivationState, j as SkillsConfig, k as SkillResource, l as SkillActivationState, o as ActivationVia, s as ActiveSkill, u as SkillActivationStateOptions } from "./agent-DHQAsdj6.js";
2
- import { S as installAllowedToolsGate, _ as inferSource, a as SkillValidationResult, b as buildCatalog, c as parseAllowedToolPattern, d as validateSkillName, f as resolveSkills, g as getDefaultScanPaths, h as discoverSkills, i as SkillValidationIssue, l as validateResourcePath, m as SourcedScanPath, n as writeSkillToDisk, o as isToolAllowedByUnion, p as interpolateShellCommands, r as writeSkillsToDisk, s as matchesAllowedTool, t as defineSkill, u as validateSkillForWrite, v as parseFrontmatter, x as IMPLICITLY_ALLOWED_SKILL_TOOLS, y as parseSkillFile } from "./index-CHSaLab5.js";
1
+ import { A as SkillSource, D as SkillConfig, O as SkillDiagnostic, c as DeactivationReason, d as createSkillActivationState, j as SkillsConfig, k as SkillResource, l as SkillActivationState, o as ActivationVia, s as ActiveSkill, u as SkillActivationStateOptions } from "./agent-DxBoKDba.js";
2
+ import { S as installAllowedToolsGate, _ as inferSource, a as SkillValidationResult, b as buildCatalog, c as parseAllowedToolPattern, d as validateSkillName, f as resolveSkills, g as getDefaultScanPaths, h as discoverSkills, i as SkillValidationIssue, l as validateResourcePath, m as SourcedScanPath, n as writeSkillToDisk, o as isToolAllowedByUnion, p as interpolateShellCommands, r as writeSkillsToDisk, s as matchesAllowedTool, t as defineSkill, u as validateSkillForWrite, v as parseFrontmatter, x as IMPLICITLY_ALLOWED_SKILL_TOOLS, y as parseSkillFile } from "./index-BOtXdQkW.js";
3
3
  export { ActivationVia, ActiveSkill, DeactivationReason, IMPLICITLY_ALLOWED_SKILL_TOOLS, SkillActivationState, SkillActivationStateOptions, SkillConfig, SkillDiagnostic, SkillResource, SkillSource, SkillValidationIssue, SkillValidationResult, SkillsConfig, SourcedScanPath, buildCatalog, createSkillActivationState, defineSkill, discoverSkills, getDefaultScanPaths, inferSource, installAllowedToolsGate, interpolateShellCommands, isToolAllowedByUnion, matchesAllowedTool, parseAllowedToolPattern, parseFrontmatter, parseSkillFile, resolveSkills, validateResourcePath, validateSkillForWrite, validateSkillName, writeSkillToDisk, writeSkillsToDisk };
@@ -1071,6 +1071,8 @@ function applyStaleReadElision(messages) {
1071
1071
  messages,
1072
1072
  elidedPaths: []
1073
1073
  };
1074
+ const pathsWithFreshRead = /* @__PURE__ */ new Set();
1075
+ for (const [callId, info] of readCallInfo) if (!staleCallIds.has(callId)) pathsWithFreshRead.add(info.path);
1074
1076
  let changed = false;
1075
1077
  const out = messages.slice();
1076
1078
  for (let i = 0; i < out.length; i++) {
@@ -1094,7 +1096,7 @@ function applyStaleReadElision(messages) {
1094
1096
  }
1095
1097
  return {
1096
1098
  messages: changed ? out : messages,
1097
- elidedPaths: [...elidedPathSet]
1099
+ elidedPaths: [...elidedPathSet].filter((p) => !pathsWithFreshRead.has(p))
1098
1100
  };
1099
1101
  }
1100
1102
  /**
@@ -1182,7 +1184,14 @@ async function runLoop(ctx) {
1182
1184
  await ctx.hooks.callHook("agent:abort", {});
1183
1185
  break;
1184
1186
  }
1185
- const result = await executeTurn(ctx, turn);
1187
+ const result = await executeTurn(ctx, turn, {
1188
+ input: totalIn,
1189
+ output: totalOut,
1190
+ cacheRead: totalCacheRead,
1191
+ cacheCreation: totalCacheCreation,
1192
+ priorCost: turnUsages.reduce((s, t) => s + (t.cost ?? 0), 0),
1193
+ priorTurns: turnsCompleted
1194
+ });
1186
1195
  turnsCompleted = turn + 1;
1187
1196
  totalIn += result.usage.input;
1188
1197
  totalOut += result.usage.output;
@@ -1293,7 +1302,49 @@ function buildStreamErrorPlaceholder(err) {
1293
1302
  const oneLine = raw.replace(/\s+/g, " ");
1294
1303
  return `[✗ Streaming failed before any output: ${oneLine.length > ERROR_PLACEHOLDER_MAX ? `${oneLine.slice(0, ERROR_PLACEHOLDER_MAX - 1).trimEnd()}…` : oneLine}]`;
1295
1304
  }
1296
- async function executeTurn(ctx, turn) {
1305
+ function buildCumulativeUsage(prior, turnUsage) {
1306
+ const cost = prior.priorCost + (turnUsage.cost ?? 0);
1307
+ return Object.freeze({
1308
+ input: prior.input + turnUsage.input,
1309
+ output: prior.output + turnUsage.output,
1310
+ cacheRead: prior.cacheRead + (turnUsage.cacheRead ?? 0),
1311
+ cacheCreation: prior.cacheCreation + (turnUsage.cacheCreation ?? 0),
1312
+ ...cost > 0 ? { cost } : {},
1313
+ turns: prior.priorTurns + 1
1314
+ });
1315
+ }
1316
+ /**
1317
+ * Best-effort extraction of `statusCode` / `requestId` from native provider
1318
+ * SDK exceptions, attached to `stream:error` ctx so observability handlers
1319
+ * don't have to walk `cause` chains. Recognized shapes:
1320
+ *
1321
+ * - Anthropic SDK `APIError` (`status`, `headers['request-id']`)
1322
+ * - OpenAI SDK error (`status`, `headers['x-request-id']`)
1323
+ * - OpenRouter / OpenAI-compat HTTP errors (`status`)
1324
+ * - Cerebras (`status`)
1325
+ *
1326
+ * Silent no-op for arbitrary `Error`s and primitives — the fields stay
1327
+ * undefined and the raw `err` is still forwarded for handlers that need
1328
+ * full context.
1329
+ */
1330
+ function extractStreamErrorMeta(err) {
1331
+ if (!err || typeof err !== "object") return {};
1332
+ const e = err;
1333
+ const out = {};
1334
+ const statusCandidate = typeof e.status === "number" ? e.status : typeof e.statusCode === "number" ? e.statusCode : void 0;
1335
+ if (typeof statusCandidate === "number" && Number.isFinite(statusCandidate)) out.statusCode = statusCandidate;
1336
+ if (typeof e.requestId === "string") out.requestId = e.requestId;
1337
+ else {
1338
+ const headers = e.headers;
1339
+ if (headers && typeof headers === "object") {
1340
+ const h = headers;
1341
+ const candidate = h["request-id"] ?? h["x-request-id"] ?? h.requestId;
1342
+ if (typeof candidate === "string") out.requestId = candidate;
1343
+ }
1344
+ }
1345
+ return out;
1346
+ }
1347
+ async function executeTurn(ctx, turn, priorUsage) {
1297
1348
  const turnId = await ctx.generateTurnId();
1298
1349
  let canonicalMessages = turnsToMessages(applyCompactSummaryCutoff(ctx.turns));
1299
1350
  if (ctx.elideStaleReads === true) {
@@ -1341,10 +1392,20 @@ async function executeTurn(ctx, turn) {
1341
1392
  });
1342
1393
  let currentText = "";
1343
1394
  let currentThinking = "";
1395
+ const streamStartedAt = Date.now();
1396
+ let turnTtftMs;
1397
+ const markTurnTtft = () => {
1398
+ if (turnTtftMs === void 0) turnTtftMs = Date.now() - streamStartedAt;
1399
+ };
1400
+ await ctx.hooks.callHook("stream:start", {
1401
+ turnId,
1402
+ startedAt: streamStartedAt
1403
+ });
1344
1404
  let result;
1345
1405
  try {
1346
1406
  result = await ctx.provider.stream(streamOptions, {
1347
1407
  onText(delta) {
1408
+ markTurnTtft();
1348
1409
  currentText += delta;
1349
1410
  ctx.hooks.callHook("stream:text", {
1350
1411
  delta,
@@ -1353,6 +1414,7 @@ async function executeTurn(ctx, turn) {
1353
1414
  });
1354
1415
  },
1355
1416
  onThinking(delta) {
1417
+ markTurnTtft();
1356
1418
  currentThinking += delta;
1357
1419
  ctx.hooks.callHook("stream:thinking", {
1358
1420
  delta,
@@ -1387,10 +1449,14 @@ async function executeTurn(ctx, turn) {
1387
1449
  createdAt: Date.now()
1388
1450
  };
1389
1451
  ctx.turns.push(errorTurn);
1390
- if (!wasAborted) await ctx.hooks.callHook("stream:error", {
1391
- err,
1392
- turnId
1393
- });
1452
+ if (!wasAborted) {
1453
+ const meta = extractStreamErrorMeta(err);
1454
+ await ctx.hooks.callHook("stream:error", {
1455
+ err,
1456
+ turnId,
1457
+ ...meta
1458
+ });
1459
+ }
1394
1460
  await ctx.hooks.callHook("turn:after", {
1395
1461
  turn,
1396
1462
  turnId,
@@ -1399,7 +1465,8 @@ async function executeTurn(ctx, turn) {
1399
1465
  toolCounts: {
1400
1466
  turn: Object.freeze({}),
1401
1467
  run: Object.freeze({ ...ctx.runToolCounts })
1402
- }
1468
+ },
1469
+ cumulativeUsage: buildCumulativeUsage(priorUsage, errorUsage)
1403
1470
  });
1404
1471
  throw wrapProviderError(err, ctx);
1405
1472
  }
@@ -1407,11 +1474,13 @@ async function executeTurn(ctx, turn) {
1407
1474
  text: currentText,
1408
1475
  turnId
1409
1476
  });
1477
+ if (turnTtftMs === void 0 && result.toolCalls.length > 0) turnTtftMs = Date.now() - streamStartedAt;
1410
1478
  const canonicalToolCalls = result.toolCalls.map((tc) => ({
1411
1479
  ...tc,
1412
1480
  name: toCanonicalName(tc.name, ctx.aliasMaps)
1413
1481
  }));
1414
1482
  const canonicalContent = rewriteContentToCanonical(result.assistantMessage?.content ?? [], ctx.aliasMaps);
1483
+ if (turnTtftMs !== void 0 && result.usage.timeToFirstTokenMs === void 0) result.usage.timeToFirstTokenMs = turnTtftMs;
1415
1484
  const assistantTurn = {
1416
1485
  id: turnId,
1417
1486
  runId: ctx.runId,
@@ -1434,7 +1503,8 @@ async function executeTurn(ctx, turn) {
1434
1503
  toolCounts: {
1435
1504
  turn: Object.freeze(turnCounts),
1436
1505
  run: Object.freeze({ ...ctx.runToolCounts })
1437
- }
1506
+ },
1507
+ cumulativeUsage: buildCumulativeUsage(priorUsage, result.usage)
1438
1508
  });
1439
1509
  if (result.done) {
1440
1510
  if (ctx.schema && !ctx.signal.aborted) {
@@ -2588,9 +2658,11 @@ function createToolSearchTool(options) {
2588
2658
  //#region src/agent.ts
2589
2659
  const HOOK_EVENT_SET = new Set([
2590
2660
  "system:before",
2661
+ "agent:start",
2591
2662
  "turn:before",
2592
2663
  "turn:after",
2593
2664
  "tool-results:after",
2665
+ "stream:start",
2594
2666
  "stream:text",
2595
2667
  "stream:end",
2596
2668
  "stream:thinking",
@@ -2647,6 +2719,7 @@ const HOOK_EVENT_SET = new Set([
2647
2719
  "budget:exceeded",
2648
2720
  "tool-budget:exceeded",
2649
2721
  "pairing:repair",
2722
+ "tracing:redact",
2650
2723
  "agent:abort",
2651
2724
  "agent:done",
2652
2725
  "session:start",
@@ -2981,6 +3054,16 @@ function createAgent({ provider, name: agentName, system: agentSystem, tools: ag
2981
3054
  prompt: promptLabel
2982
3055
  });
2983
3056
  }
3057
+ const runStartedAt = Date.now();
3058
+ await hooks.callHook("agent:start", {
3059
+ runId,
3060
+ ...options.parentRunId ? { parentRunId: options.parentRunId } : {},
3061
+ depth: typeof options.depth === "number" ? options.depth : 0,
3062
+ ...agentName ? { agentName } : {},
3063
+ ...provider.name ? { providerName: provider.name } : {},
3064
+ startedAt: runStartedAt,
3065
+ ...options.tracingContext ? { tracingContext: Object.freeze({ ...options.tracingContext }) } : {}
3066
+ });
2984
3067
  if (externalSignal) if (externalSignal.aborted) abortController.abort();
2985
3068
  else {
2986
3069
  externalAbortListener = () => abortController?.abort();
@@ -3770,7 +3853,7 @@ const edit = {
3770
3853
  if (readState) {
3771
3854
  const absKey = readStateKey(ctx.handle.cwd, target);
3772
3855
  const prior = readState.get(absKey);
3773
- if (!prior) return `Edit error: ${target} has not been read in this session. Call read_file first so the edit applies against the current contents. (Reads inside a \`spawn\` subagent with \`persist: false\` and without \`shareReadState: true\` do NOT propagate to the parent — re-read in the calling context.)`;
3856
+ if (!prior) return `Edit error: ${target} has not been read in this session. Call read_file first so the edit applies against the current contents.`;
3774
3857
  if (prior.contentHash !== hashContent(original)) return `Edit error: ${target} has changed on disk since the last read. Re-read the file before editing.`;
3775
3858
  }
3776
3859
  }
@@ -4193,10 +4276,33 @@ const listFiles = {
4193
4276
  };
4194
4277
  //#endregion
4195
4278
  //#region src/tools/multi-edit.ts
4279
+ /**
4280
+ * Inline annotation builder — kept local to avoid importing from
4281
+ * `chat/edit-approval.ts` (that's a renderer-side module; tools live
4282
+ * one layer below). Line shape matches `parseEditOutcomesFromResult`'s
4283
+ * regex so the round-trip is lossless.
4284
+ *
4285
+ * Newlines in `reason` are folded to spaces because the parser is line-
4286
+ * scoped (`body.split('\n')`); a multi-line reason would split into a
4287
+ * "trailing prose" line and trip the malformed-block guard, losing every
4288
+ * outcome below it. Static reasons in this file are single-line; the
4289
+ * sanitize is a guard against a pathological `target` (file path
4290
+ * containing a newline) leaking into `old_string not found in <target>`.
4291
+ */
4292
+ function annotationFor(outcomes) {
4293
+ const lines = ["<edit-outcomes>"];
4294
+ for (let i = 0; i < outcomes.length; i++) {
4295
+ const o = outcomes[i];
4296
+ const reason = o.reason ? `: ${o.reason.replace(/\r?\n/g, " ")}` : "";
4297
+ lines.push(`#${i + 1} ${o.kind}${reason}`);
4298
+ }
4299
+ lines.push("</edit-outcomes>");
4300
+ return lines.join("\n");
4301
+ }
4196
4302
  const multiEdit = {
4197
4303
  spec: {
4198
4304
  name: "multi_edit",
4199
- description: "Apply a sequential list of edits to a file. Each edit operates on the result of the previous edit. Prefer this over multiple `edit` calls when several non-overlapping changes are needed in the same file. The batch is atomic if any edit fails to apply, none are written. Each step tolerates `read_file` line-number prefixes (`<N>\\t…`, `<N>|…`, or `<N>→…`) in `old_string` / `new_string`.",
4305
+ description: "Apply a sequential list of edits to a file. Each edit operates on the result of the previous APPLIED edit. Prefer this over multiple `edit` calls when several non-overlapping changes are needed in the same file. Edits run **best-effort**: a per-step failure (`old_string` not found, ambiguous match without `replace_all`, identical strings) is reported in the result but does NOT block the remaining steps. The file is written iff at least one step applied. The result lists per-hunk outcomes (`applied` / `failed`) so the model can re-issue just the failures without resending the whole batch. Each step tolerates `read_file` line-number prefixes (`<N>\\t…`, `<N>|…`, or `<N>→…`) in `old_string` / `new_string`.",
4200
4306
  inputSchema: {
4201
4307
  type: "object",
4202
4308
  properties: {
@@ -4206,7 +4312,7 @@ const multiEdit = {
4206
4312
  },
4207
4313
  edits: {
4208
4314
  type: "array",
4209
- description: "List of edits applied in order; each operates on the previous edit's output.",
4315
+ description: "List of edits applied in order; each operates on the previous applied edit's output.",
4210
4316
  items: {
4211
4317
  type: "object",
4212
4318
  properties: {
@@ -4236,40 +4342,88 @@ const multiEdit = {
4236
4342
  if (readState) {
4237
4343
  const absKey = readStateKey(ctx.handle.cwd, target);
4238
4344
  const prior = readState.get(absKey);
4239
- if (!prior) return `multi_edit error: ${target} has not been read in this session. Call read_file first so the edits apply against the current contents. (Reads inside a \`spawn\` subagent with \`persist: false\` and without \`shareReadState: true\` do NOT propagate to the parent — re-read in the calling context.)`;
4345
+ if (!prior) return `multi_edit error: ${target} has not been read in this session. Call read_file first so the edits apply against the current contents.`;
4240
4346
  if (prior.contentHash !== hashContent(current)) return `multi_edit error: ${target} has changed on disk since the last read. Re-read the file before editing.`;
4241
4347
  }
4242
4348
  }
4349
+ const outcomes = [];
4243
4350
  let totalReplacements = 0;
4244
4351
  for (let i = 0; i < steps.length; i++) {
4245
4352
  const step = steps[i];
4246
4353
  const find = step.old_string;
4247
4354
  const replacement = step.new_string;
4248
4355
  const replaceAll = step.replace_all === true;
4249
- if (typeof find !== "string" || typeof replacement !== "string") return `multi_edit error: edit #${i + 1} is missing old_string or new_string.`;
4250
- if (find.length === 0) return `multi_edit error: edit #${i + 1} has empty old_string. Use write_file to fully replace a file.`;
4251
- if (find === replacement) return `multi_edit error: edit #${i + 1} old_string and new_string are identical.`;
4356
+ if (typeof find !== "string" || typeof replacement !== "string") {
4357
+ outcomes.push({
4358
+ kind: "failed",
4359
+ reason: "missing old_string or new_string"
4360
+ });
4361
+ continue;
4362
+ }
4363
+ if (find.length === 0) {
4364
+ outcomes.push({
4365
+ kind: "failed",
4366
+ reason: "empty old_string (use write_file to fully replace a file)"
4367
+ });
4368
+ continue;
4369
+ }
4370
+ if (find === replacement) {
4371
+ outcomes.push({
4372
+ kind: "failed",
4373
+ reason: "old_string and new_string are identical"
4374
+ });
4375
+ continue;
4376
+ }
4252
4377
  const match = resolveOldString(current, find);
4253
- if (!match) return `multi_edit error: edit #${i + 1} old_string not found in ${target}.`;
4378
+ if (!match) {
4379
+ outcomes.push({
4380
+ kind: "failed",
4381
+ reason: `old_string not found in ${target}`
4382
+ });
4383
+ continue;
4384
+ }
4254
4385
  const { actual, occurrences, via } = match;
4255
- if (occurrences > 1 && !replaceAll) return `multi_edit error: edit #${i + 1} old_string appears ${occurrences} times. Pass replace_all=true on this edit or expand old_string for uniqueness.`;
4386
+ if (occurrences > 1 && !replaceAll) {
4387
+ outcomes.push({
4388
+ kind: "failed",
4389
+ reason: `old_string appears ${occurrences} times — pass replace_all=true on this edit or expand old_string for uniqueness`
4390
+ });
4391
+ continue;
4392
+ }
4256
4393
  const styledReplacement = styleReplacementForVia(replacement, via, actual);
4257
4394
  current = replaceAll ? current.split(actual).join(styledReplacement) : current.replace(actual, styledReplacement);
4258
4395
  totalReplacements += occurrences;
4396
+ outcomes.push({ kind: "applied" });
4259
4397
  }
4260
- await ctx.execution.writeFile(ctx.handle, target, current);
4261
- const readState = resolveReadStateMap(ctx);
4262
- if (readState) {
4263
- const absKey = readStateKey(ctx.handle.cwd, target);
4264
- const prior = readState.get(absKey);
4265
- if (prior) readState.set(absKey, {
4266
- ...prior,
4267
- contentHash: hashContent(current),
4268
- mtimeMs: Date.now()
4269
- });
4398
+ const appliedCount = outcomes.reduce((n, o) => o.kind === "applied" ? n + 1 : n, 0);
4399
+ const failedCount = outcomes.length - appliedCount;
4400
+ if (appliedCount > 0) {
4401
+ await ctx.execution.writeFile(ctx.handle, target, current);
4402
+ const readState = resolveReadStateMap(ctx);
4403
+ if (readState) {
4404
+ const absKey = readStateKey(ctx.handle.cwd, target);
4405
+ const prior = readState.get(absKey);
4406
+ if (prior) readState.set(absKey, {
4407
+ ...prior,
4408
+ contentHash: hashContent(current),
4409
+ mtimeMs: Date.now()
4410
+ });
4411
+ }
4270
4412
  }
4271
4413
  const n = steps.length;
4272
- return `Edited ${target}: applied ${n} edit${n === 1 ? "" : "s"} (${totalReplacements} replacement${totalReplacements === 1 ? "" : "s"}).`;
4414
+ let header;
4415
+ if (appliedCount === n) header = `Edited ${target}: applied ${n} edit${n === 1 ? "" : "s"} (${totalReplacements} replacement${totalReplacements === 1 ? "" : "s"}).`;
4416
+ else if (appliedCount > 0) header = `Edited ${target}: applied ${appliedCount} of ${n} edits (${totalReplacements} replacement${totalReplacements === 1 ? "" : "s"}).`;
4417
+ else header = `multi_edit error: no edits applied to ${target} (${n} attempted).`;
4418
+ const failureLines = [];
4419
+ for (let i = 0; i < outcomes.length; i++) {
4420
+ const o = outcomes[i];
4421
+ if (o.kind === "failed") failureLines.push(`edit #${i + 1} failed: ${o.reason}`);
4422
+ }
4423
+ const parts = [header];
4424
+ if (failureLines.length > 0) parts.push(failureLines.join("\n"));
4425
+ if (failedCount > 0) parts.push(annotationFor(outcomes));
4426
+ return parts.join("\n\n");
4273
4427
  }
4274
4428
  };
4275
4429
  //#endregion
@@ -5007,11 +5161,14 @@ function createSpawnTool(options = {}) {
5007
5161
  };
5008
5162
  }
5009
5163
  options.onSpawn?.(child);
5010
- await ctx.hooks.callHook("spawn:before", {
5164
+ const spawnHookCtx = {
5011
5165
  id,
5012
5166
  task,
5013
- depth: childDepth
5014
- });
5167
+ depth: childDepth,
5168
+ tracingContext: {}
5169
+ };
5170
+ await ctx.hooks.callHook("spawn:before", spawnHookCtx);
5171
+ const propagatedTracing = Object.keys(spawnHookCtx.tracingContext).length > 0 ? Object.freeze({ ...spawnHookCtx.tracingContext }) : void 0;
5015
5172
  const runPromise = agent.run({
5016
5173
  prompt: task,
5017
5174
  model: options.model,
@@ -5019,7 +5176,8 @@ function createSpawnTool(options = {}) {
5019
5176
  thinking: options.thinking,
5020
5177
  signal: ctx.signal,
5021
5178
  depth: childDepth,
5022
- ...options.persist && ctx.runId ? { parentRunId: ctx.runId } : {}
5179
+ ...options.persist && ctx.runId ? { parentRunId: ctx.runId } : {},
5180
+ ...propagatedTracing ? { tracingContext: propagatedTracing } : {}
5023
5181
  });
5024
5182
  try {
5025
5183
  finalStats = await raceWithTimeout(runPromise, options.timeoutMs);
@@ -5165,4 +5323,4 @@ const writeFile$1 = {
5165
5323
  //#endregion
5166
5324
  export { readStateKey as A, PERSISTENCE_PREVIEW_BYTES as C, resolvePersistDir as D, maybePersistToolResult as E, getReadState as O, PERSISTED_STUB_PREFIX as S, cleanupPersistedSession as T, createSkillsReadTool as _, multiEdit as a, TOOL_USE_SKIPPED_MESSAGE as b, grep as c, resolveOldString as d, styleReplacementForVia as f, createSkillsRunScriptTool as g, createSkillsUseTool as h, readFile$1 as i, resolveReadStateMap as j, hashContent as k, glob as l, createToolSearchTool as m, createSpawnTool as n, listFiles as o, createAgent as p, shell as r, createInteractionTool as s, writeFile$1 as t, edit as u, INTERRUPT_MESSAGE_FOR_TOOL_USE as v, buildPersistedStub as w, validateToolArgs as x, SHELL_CASCADE_CANCEL_MESSAGE as y };
5167
5325
 
5168
- //# sourceMappingURL=tools-DKdyPoUf.js.map
5326
+ //# sourceMappingURL=tools-BNfyY14s.js.map