zidane 7.0.32 → 7.1.1

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 (124) hide show
  1. package/README.md +19 -0
  2. package/dist/{acp-bBDF-dhy.js → acp-B5UfEhMd.js} +4 -4
  3. package/dist/{acp-bBDF-dhy.js.map → acp-B5UfEhMd.js.map} +1 -1
  4. package/dist/acp-cli.js +6 -6
  5. package/dist/acp.d.ts +2 -2
  6. package/dist/acp.js +1 -1
  7. package/dist/{agent-BCG9JcZl.js → agent-_xxElOgH.js} +162 -18
  8. package/dist/agent-_xxElOgH.js.map +1 -0
  9. package/dist/agent.d.ts +1 -1
  10. package/dist/agent.js +1 -1
  11. package/dist/{anthropic-B4UmPm4H.js → anthropic-DPPs3wjJ.js} +111 -7
  12. package/dist/anthropic-DPPs3wjJ.js.map +1 -0
  13. package/dist/{approvals-DkwGBMZ9.js → approvals-u2T19Dc-.js} +275 -88
  14. package/dist/approvals-u2T19Dc-.js.map +1 -0
  15. package/dist/{auth-D0BHBb02.js → auth-HRKkOErn.js} +3 -3
  16. package/dist/{auth-D0BHBb02.js.map → auth-HRKkOErn.js.map} +1 -1
  17. package/dist/chat/pure.d.ts +3 -3
  18. package/dist/chat/pure.js +1 -1
  19. package/dist/chat.d.ts +6 -6
  20. package/dist/chat.js +8 -8
  21. package/dist/contexts/daytona.d.ts +3 -3
  22. package/dist/contexts/docker.d.ts +1 -1
  23. package/dist/contexts/e2b.d.ts +2 -2
  24. package/dist/contexts/sandbox.d.ts +1 -1
  25. package/dist/{contexts-BSFQVclW.js → contexts-CRE9pw07.js} +544 -17
  26. package/dist/contexts-CRE9pw07.js.map +1 -0
  27. package/dist/contexts.d.ts +4 -4
  28. package/dist/contexts.js +2 -2
  29. package/dist/daemon-cli.js +1 -1
  30. package/dist/eval.d.ts +1 -1
  31. package/dist/eval.js +3 -3
  32. package/dist/extensions.d.ts +1 -1
  33. package/dist/extensions.js +1 -1
  34. package/dist/headless.d.ts +1 -1
  35. package/dist/headless.js +3 -3
  36. package/dist/{index-B6FkK6ZN.d.ts → index-C37_ppWv.d.ts} +2 -2
  37. package/dist/index-C37_ppWv.d.ts.map +1 -0
  38. package/dist/{index-Ogi2CeHf.d.ts → index-D3TTaaQ5.d.ts} +20 -7
  39. package/dist/index-D3TTaaQ5.d.ts.map +1 -0
  40. package/dist/{index-CCB05-JZ.d.ts → index-DZhrTn-v.d.ts} +259 -12
  41. package/dist/index-DZhrTn-v.d.ts.map +1 -0
  42. package/dist/index.d.ts +5 -5
  43. package/dist/index.js +8 -8
  44. package/dist/{ipc-C10lnS-a.js → ipc-DwauKEE-.js} +3 -3
  45. package/dist/{ipc-C10lnS-a.js.map → ipc-DwauKEE-.js.map} +1 -1
  46. package/dist/ipc.d.ts +5 -5
  47. package/dist/ipc.js +3 -3
  48. package/dist/{types-U-rIVUDw.d.ts → isolation-Kfoof0MM.d.ts} +93 -2
  49. package/dist/isolation-Kfoof0MM.d.ts.map +1 -0
  50. package/dist/mcp.d.ts +1 -1
  51. package/dist/output/stream-json.d.ts +1 -1
  52. package/dist/output/terminal.d.ts +1 -1
  53. package/dist/{plugins-B531Tz5b.js → plugins-gbI7SBA6.js} +3 -3
  54. package/dist/{plugins-B531Tz5b.js.map → plugins-gbI7SBA6.js.map} +1 -1
  55. package/dist/plugins.d.ts +1 -1
  56. package/dist/plugins.js +1 -1
  57. package/dist/presets.d.ts +1 -1
  58. package/dist/presets.js +1 -1
  59. package/dist/providers/anthropic.d.ts +2 -2
  60. package/dist/providers/anthropic.js +2 -2
  61. package/dist/providers/arcee.d.ts +1 -1
  62. package/dist/providers/baseten.d.ts +1 -1
  63. package/dist/providers/cerebras.d.ts +1 -1
  64. package/dist/providers/local.d.ts +1 -1
  65. package/dist/providers/openai-compat.d.ts +1 -1
  66. package/dist/providers/openai.d.ts +1 -1
  67. package/dist/providers/openrouter.d.ts +1 -1
  68. package/dist/providers/xai.d.ts +1 -1
  69. package/dist/providers.d.ts +2 -2
  70. package/dist/providers.js +1 -1
  71. package/dist/restate.d.ts +2 -2
  72. package/dist/restate.js +1 -1
  73. package/dist/{sandbox-CDl9tfTo.d.ts → sandbox-BH0WQkXn.d.ts} +2 -2
  74. package/dist/{sandbox-CDl9tfTo.d.ts.map → sandbox-BH0WQkXn.d.ts.map} +1 -1
  75. package/dist/{server--mwNA7oC.js → server-DvPnPSZb.js} +8 -8
  76. package/dist/{server--mwNA7oC.js.map → server-DvPnPSZb.js.map} +1 -1
  77. package/dist/session/sqlite.d.ts +1 -1
  78. package/dist/session/sqlite.d.ts.map +1 -1
  79. package/dist/session/sqlite.js +7 -0
  80. package/dist/session/sqlite.js.map +1 -1
  81. package/dist/{session-CVy-2mAy.js → session-BqRP1hS7.js} +33 -3
  82. package/dist/session-BqRP1hS7.js.map +1 -0
  83. package/dist/session.d.ts +1 -1
  84. package/dist/session.js +1 -1
  85. package/dist/{skills-PJyiSRCp.js → skills-BC3dodWp.js} +2 -2
  86. package/dist/{skills-PJyiSRCp.js.map → skills-BC3dodWp.js.map} +1 -1
  87. package/dist/skills.d.ts +1 -1
  88. package/dist/skills.js +1 -1
  89. package/dist/{src-CjkY6fBj.js → src-CkNQKFKr.js} +3 -3
  90. package/dist/{src-CjkY6fBj.js.map → src-CkNQKFKr.js.map} +1 -1
  91. package/dist/{tool-formatters-Bu4repDJ.d.ts → tool-formatters-C5s88MUd.d.ts} +2 -2
  92. package/dist/{tool-formatters-Bu4repDJ.d.ts.map → tool-formatters-C5s88MUd.d.ts.map} +1 -1
  93. package/dist/tools/fetch-url.d.ts +1 -1
  94. package/dist/tools/web-search.d.ts +1 -1
  95. package/dist/tools.d.ts +1 -1
  96. package/dist/tools.js +1 -1
  97. package/dist/{transcript-anchors-CJCxyVE-.d.ts → transcript-anchors-DxofXw_y.d.ts} +3 -3
  98. package/dist/transcript-anchors-DxofXw_y.d.ts.map +1 -0
  99. package/dist/tui.d.ts +21 -28
  100. package/dist/tui.d.ts.map +1 -1
  101. package/dist/tui.js +163 -69
  102. package/dist/tui.js.map +1 -1
  103. package/dist/{turn-operations-HX3g3XIc.js → turn-operations-6JDu2ewy.js} +14 -2
  104. package/dist/{turn-operations-HX3g3XIc.js.map → turn-operations-6JDu2ewy.js.map} +1 -1
  105. package/dist/{turn-operations-qcv9UFcL.d.ts → turn-operations-BaQnxzhr.d.ts} +14 -4
  106. package/dist/{turn-operations-qcv9UFcL.d.ts.map → turn-operations-BaQnxzhr.d.ts.map} +1 -1
  107. package/dist/types-CWgENVLY.js.map +1 -1
  108. package/dist/types.d.ts +3 -3
  109. package/docs/ARCHITECTURE.md +2 -1
  110. package/docs/CHAT.md +9 -1
  111. package/docs/EXECUTION_CONTEXT.md +64 -3
  112. package/docs/SKILL.md +28 -0
  113. package/docs/TUI.md +6 -5
  114. package/package.json +1 -1
  115. package/dist/agent-BCG9JcZl.js.map +0 -1
  116. package/dist/anthropic-B4UmPm4H.js.map +0 -1
  117. package/dist/approvals-DkwGBMZ9.js.map +0 -1
  118. package/dist/contexts-BSFQVclW.js.map +0 -1
  119. package/dist/index-B6FkK6ZN.d.ts.map +0 -1
  120. package/dist/index-CCB05-JZ.d.ts.map +0 -1
  121. package/dist/index-Ogi2CeHf.d.ts.map +0 -1
  122. package/dist/session-CVy-2mAy.js.map +0 -1
  123. package/dist/transcript-anchors-CJCxyVE-.d.ts.map +0 -1
  124. package/dist/types-U-rIVUDw.d.ts.map +0 -1
package/dist/types.d.ts CHANGED
@@ -1,5 +1,5 @@
1
- import { $_ as SessionData, B_ as ToolStateView, Bg as PromptVideoPart, Br as InteractionToolOptions, C as SuccessfulMutationOutcome, C_ as ToolContext, D_ as ReadStateEntry, Dg as McpServerConfig, Dm as TurnResult, Em as ToolSpec, Fg as PromptAudioPart, G_ as SkillConfig, Gg as RetryConfig, Hg as RepeatGuardToolMatcher, I_ as StagedToolState, Ig as PromptDocumentPart, Jg as SessionEndStatus, Ji as AgentHookMap, Kg as RunHookMap, L_ as ToolStateController, Lg as PromptImagePart, Mg as OAuthRefreshHookContext, Ng as ProgressNudgeKind, O_ as ReadStateMap, Og as McpToolHookContext, Pg as ProgressNudgeMessage, Q_ as Session, Qg as SpawnHookContext, R_ as ToolStateOp, Rg as PromptPart, Rm as OpenRouterParams, S as MutationValidationResult, S_ as toolResultToText, Sg as DEFAULT_AGENT_CLOCK, Sm as StreamOptions, T_ as ToolMap, Tm as ToolResult, Ug as ResolveContentRef, Vg as RepeatGuardConfig, Wg as ResolveContentRefBlock, X_ as CreateSessionOptions, Xg as SessionMessage, Xi as AgentOptions, Y_ as SkillsConfig, Yg as SessionHookContext, Yi as AgentHooks, Zg as SessionTurn, Zi as AgentToolState, _ as statsByModel, __ as ToolResultVideoContent, _g as ChildRunStats, a_ as ThinkingLevel, b as MutationValidationHooksOptions, b_ as toolOutputBudgetByteLength, bh as CerebrasParams, c_ as ToolOutcome, ch as OpenAIParams, d_ as ToolResultDocumentContent, dg as AgentClock, f_ as ToolResultDocumentRefContent, fg as AgentRunOptions, fv as RemoteStoreOptions, g_ as ToolResultTextContent, gc as SpawnToolState, h as flattenTurns, h_ as ToolResultMetadata, hc as SpawnToolOptions, kh as AnthropicParams, l_ as ToolResultAudioContent, m_ as ToolResultImageRefContent, nv as SessionStore, p as ModelUsage, p_ as ToolResultImageContent, pg as AgentStats, q_ as SkillResource, qg as SessionContentBlock, qi as Agent, ry as McpConnection, s_ as ToolHookContext, t as Preset, t_ as StreamHookContext, tv as SessionRun, u_ as ToolResultContent, uc as ChildAgent, ug as AgentBehavior, v as MutationValidationBatch, v_ as TurnFinishReason, vm as Provider, vr as ValidationResult, w_ as ToolDef, wm as ToolCall, x as MutationValidationMutation, x_ as toolOutputByteLength, xm as StreamCallbacks, y as MutationValidationFailure, y_ as TurnUsage, ym as ProviderCapabilities, z_ as ToolStateStore, zg as PromptTextPart } from "./index-CCB05-JZ.js";
1
+ import { $_ as SkillsConfig, $g as SessionHookContext, Ah as AnthropicParams, B_ as StagedToolState, Bg as PromptDocumentPart, Br as InteractionToolOptions, C as SuccessfulMutationOutcome, C_ as TurnUsage, Cm as StreamOptions, D_ as ToolContext, Dm as ToolSpec, E_ as toolResultToText, Eg as DEFAULT_AGENT_CLOCK, Em as ToolResult, Gg as RepeatGuardConfig, H_ as ToolStateOp, Hg as PromptPart, Ig as OAuthRefreshHookContext, Jg as ResolveContentRefBlock, Ji as AgentHookMap, Kg as RepeatGuardToolMatcher, Lg as ProgressNudgeKind, M_ as ReadStateMap, Mg as McpToolHookContext, O_ as ToolDef, Om as TurnResult, Qg as SessionEndStatus, Rg as ProgressNudgeMessage, S as MutationValidationResult, S_ as TurnFinishReason, Sm as StreamCallbacks, T_ as toolOutputByteLength, Tm as ToolCall, U_ as ToolStateStore, Ug as PromptTextPart, V_ as ToolStateController, Vg as PromptImagePart, W_ as ToolStateView, Wg as PromptVideoPart, Xg as RunHookMap, Xi as AgentOptions, Y_ as SkillConfig, Yg as RetryConfig, Yi as AgentHooks, Z_ as SkillResource, Zg as SessionContentBlock, Zi as AgentToolState, _ as statsByModel, __ as ToolResultImageContent, _g as AgentStats, a_ as StreamHookContext, av as SessionRun, b as MutationValidationHooksOptions, b_ as ToolResultTextContent, bm as ProviderCapabilities, d_ as ToolHookContext, e_ as SessionMessage, ev as CreateSessionOptions, f_ as ToolOutcome, g_ as ToolResultDocumentRefContent, gc as SpawnToolState, gg as AgentRunOptions, gv as RemoteStoreOptions, h as flattenTurns, h_ as ToolResultDocumentContent, hc as SpawnToolOptions, hg as AgentClock, j_ as ReadStateEntry, jg as McpServerConfig, k_ as ToolMap, l_ as ThinkingLevel, lh as OpenAIParams, m_ as ToolResultContent, mg as AgentBehavior, n_ as SpawnHookContext, nv as Session, ov as SessionStore, p as ModelUsage, p_ as ToolResultAudioContent, qg as ResolveContentRef, qi as Agent, rv as SessionData, sy as McpConnection, t as Preset, t_ as SessionTurn, uc as ChildAgent, v as MutationValidationBatch, v_ as ToolResultImageRefContent, vr as ValidationResult, w_ as toolOutputBudgetByteLength, x as MutationValidationMutation, x_ as ToolResultVideoContent, xg as ChildRunStats, xh as CerebrasParams, y as MutationValidationFailure, y_ as ToolResultMetadata, ym as Provider, zg as PromptAudioPart, zm as OpenRouterParams } from "./index-DZhrTn-v.js";
2
2
  import { d as ClassifiedErrorKind, i as AgentContextExceededError, l as CONTEXT_EXCEEDED_MESSAGE_PATTERNS, n as AgentAbortedError, o as AgentProviderError, p as matchesContextExceeded, r as AgentBudgetExceededError, s as AgentToolNotAllowedError, u as ClassifiedError } from "./timeout-DEaqslLO.js";
3
- import { _ as TaskHandle, a as ContextType, c as ExecutionContext, g as TaskExitInfo, h as TaskEntry, l as ExecutionHandle, n as ContextCapabilities, o as DetachedTasksCapability, p as SpawnConfig, r as ContextHardening, s as ExecResult, t as BackgroundTaskStatus, v as TaskStallInfo } from "./types-U-rIVUDw.js";
4
- import { t as SandboxProvider } from "./sandbox-CDl9tfTo.js";
3
+ import { C as TaskHandle, S as TaskExitInfo, c as ContextCapabilities, d as ContextType, f as DetachedTasksCapability, h as ExecutionHandle, l as ContextHardening, m as ExecutionContext, p as ExecResult, s as BackgroundTaskStatus, w as TaskStallInfo, x as TaskEntry, y as SpawnConfig } from "./isolation-Kfoof0MM.js";
4
+ import { t as SandboxProvider } from "./sandbox-BH0WQkXn.js";
5
5
  export { type Agent, AgentAbortedError, type AgentBehavior, AgentBudgetExceededError, type AgentClock, AgentContextExceededError, type AgentHookMap, type AgentHooks, type AgentOptions, AgentProviderError, type AgentRunOptions, type AgentStats, AgentToolNotAllowedError, type AgentToolState, type AnthropicParams, type BackgroundTaskStatus, CONTEXT_EXCEEDED_MESSAGE_PATTERNS, type CerebrasParams, type ChildAgent, type ChildRunStats, type ClassifiedError, type ClassifiedErrorKind, type ContextCapabilities, type ContextHardening, type ContextType, type CreateSessionOptions, DEFAULT_AGENT_CLOCK, type DetachedTasksCapability, type ExecResult, type ExecutionContext, type ExecutionHandle, type InteractionToolOptions, type McpConnection, type McpServerConfig, type McpToolHookContext, type ModelUsage, type MutationValidationBatch, type MutationValidationFailure, type MutationValidationHooksOptions, type MutationValidationMutation, type MutationValidationResult, type OAuthRefreshHookContext, type OpenAIParams, type OpenRouterParams, type Preset, type ProgressNudgeKind, type ProgressNudgeMessage, type PromptAudioPart, type PromptDocumentPart, type PromptImagePart, type PromptPart, type PromptTextPart, type PromptVideoPart, type Provider, type ProviderCapabilities, type ReadStateEntry, type ReadStateMap, type RemoteStoreOptions, type RepeatGuardConfig, type RepeatGuardToolMatcher, type ResolveContentRef, type ResolveContentRefBlock, type RetryConfig, type RunHookMap, type SandboxProvider, type Session, type SessionContentBlock, type SessionData, type SessionEndStatus, type SessionHookContext, type SessionMessage, type SessionRun, type SessionStore, type SessionTurn, type SkillConfig, type SkillResource, type SkillsConfig, type SpawnConfig, type SpawnHookContext, type SpawnToolOptions, type SpawnToolState, type StagedToolState, type StreamCallbacks, type StreamHookContext, type StreamOptions, type SuccessfulMutationOutcome, type TaskEntry, type TaskExitInfo, type TaskHandle, type TaskStallInfo, type ThinkingLevel, type ToolCall, type ToolContext, type ToolDef, type ToolHookContext, type ToolMap, type ToolOutcome, type ToolResult, type ToolResultAudioContent, type ToolResultContent, type ToolResultDocumentContent, type ToolResultDocumentRefContent, type ToolResultImageContent, type ToolResultImageRefContent, type ToolResultMetadata, type ToolResultTextContent, type ToolResultVideoContent, type ToolSpec, type ToolStateController, type ToolStateOp, type ToolStateStore, type ToolStateView, type TurnFinishReason, type TurnResult, type TurnUsage, type ValidationResult, flattenTurns, matchesContextExceeded, statsByModel, toolOutputBudgetByteLength, toolOutputByteLength, toolResultToText };
@@ -312,6 +312,7 @@ A prefix cache breaks at the first differing byte — the wire must be append-on
312
312
  | `thinkingDecay` | Step form `{ afterTurn, floor }` | Every distinct budget is one cache loss; step emits one change per run, geometric emits one per halving, a per-turn function disables caching for the rest of the run. |
313
313
  | `cacheTtl` | `'1h'` for interactive hosts, `'5m'` for headless back-to-back runs | The 5-minute default TTL is the dominant cache cost for a human-paced session. |
314
314
  | `compactStrategy` / `mediaKeepTurns` | Keep windows equal, chunk-aligned (`COMPACT_CHUNK_TURNS`) | A window then costs one invalidation, not two. |
315
+ | `compactThresholdWindowFraction` | `0.25` for interactive hosts | Each tail-compaction frontier step rewrites an early message and re-bills the whole suffix (~50x the cost of keeping the bytes cached). Scaling the byte threshold with the model's window removes the steps on large-context models; ≤128k models keep the byte rule. Live A/B in `test-inference/cache-stability.inference.test.ts` § tail compaction vs context window. |
315
316
  | `thinking` level | Constant for the whole run | The thinking config is part of the cache key like the budget; toggling it mid-run forfeits the prefix. |
316
317
  | `system:transform` handlers | Byte-stable output only | Per-turn counters/timestamps belong in the transcript, not the system prompt. |
317
318
 
@@ -670,7 +671,7 @@ Use `agent.hooks.hook(event, fn)` for dynamic post-creation registration (return
670
671
  **Defaults:** unlimited turns, model-driven output tokens, level-based thinking, `cache: true`, `dedupReads: true`, `requireReadBeforeEdit: false`, `maxConcurrentTools: 10`, `compactStrategy: 'off'`, `compactThreshold: 128 KiB`, `compactKeepTurns: 4`, `toolDisclosure: 'eager'`, `readLineNumbers: true`, `elideStaleReads: false`, `surfaceMcpInstructions: true`, `maxConsecutivePauseTurns: 0` (unlimited), `persistTurns: true`, `providerStreamStartTimeoutMs: 300000`, `providerStreamTimeoutMs: 1800000`, `mcpConnectTimeoutMs: 120000`. All other knobs below default to `undefined`/off unless noted. The chat-layer profiles (`BUILD_AGENT`/`PLAN_AGENT`) override several — see `docs/CHAT.md` § Agents.
671
672
 
672
673
  - **`toolOutputBudget`** (off). Sums every result's `outputBytes` after `tool:transform`. On overshoot, appends a synthetic user message (`[Tool output budget exceeded: N bytes … cap: M]`) and fires `budget:exceeded` — the model self-compacts next turn instead of being cut.
673
- - **`compactStrategy: 'tail' | CompactFunction'`** — client-side, wire-only trim for non-Anthropic providers. `'tail'` stubs `tool_result` blocks older than `compactKeepTurns` past a byte threshold; `session.turns` is untouched. Anthropic should prefer server-side `context-management-2025-06-27` (token-accurate). A custom function must preserve `tool_use ↔ tool_result` adjacency and the trailing user message.
674
+ - **`compactStrategy: 'tail' | CompactFunction'`** — client-side, wire-only trim for non-Anthropic providers. `'tail'` stubs `tool_result` blocks older than `compactKeepTurns` past a byte threshold (`compactThreshold`, raised to `compactThresholdWindowFraction × contextWindow × 4 B` when the run knows its window — `autoCompact.contextWindow`, the active `ModelTarget`, or `provider.meta.contextWindow`); `session.turns` is untouched. Anthropic should prefer server-side `context-management-2025-06-27` (token-accurate). A custom function must preserve `tool_use ↔ tool_result` adjacency and the trailing user message.
674
675
  - **`mediaKeepTurns`** — wire-only media age-out. Inline `image`/`document` blocks older than N messages become an `attachment://<hash>` marker on the wire; `session.turns` keeps the bytes, `read_file path="attachment://<hash>"` re-inlines. Chunk-snapped for cache stability; requires `read_file` registered to be actionable.
675
676
  - **`autoCompact`** (`boolean | AutoCompactConfig`, off). At each turn boundary, once effective input (`input + cacheRead + cacheCreation`) crosses `threshold` (default `0.85`) of the context window, summarizes older turns and continues. Window source: `autoCompact.contextWindow` → `provider.meta.contextWindow`, else inert. `minGrowthFraction` (default `0.1`) stops post-compact thrashing; `maxFailures` (default `2`) disables it after consecutive summary failures. Chat profiles enable at `{ threshold: 0.85 }`.
676
677
  - **`modelCatalog`** — override model metadata keyed `"<provider>:<modelId>"` or via `{ get, list }`. Resolution: run → agent `behavior.modelCatalog` → process-wide `setModelCatalog()` → bundled registry → local fallback tables, merged field-level. `list` returning `[]` empties a provider's picker; `get` returning `undefined` falls through. Unset by default.
package/docs/CHAT.md CHANGED
@@ -166,6 +166,10 @@ const SHARED_BEHAVIOR: AgentBehavior = {
166
166
  // context / token discipline
167
167
  compactStrategy: 'tail',
168
168
  compactThreshold: 128 * 1024,
169
+ compactThresholdWindowFraction: 0.25, // effective threshold = max(128 KiB,
170
+ // 0.25 × window × 4 B/tok): 1M-context
171
+ // runs never trip tail compaction on
172
+ // ordinary sessions; ≤128k stays on bytes
169
173
  compactKeepTurns: 8, // floor, not a cap — see below
170
174
  elideStaleReads: 'notice', // append-only staleness note; never rewrites
171
175
  // cached prefix bytes ('stub' re-bills the
@@ -247,10 +251,14 @@ Both join `[...doctrine, userInstructions]` as the **static** half and `[envSect
247
251
  | `showAllProjects` | `boolean` | `false` | Sessions list scope. |
248
252
  | `resumeLastSession` | `boolean` | `true` | |
249
253
  | `persistToolResults` | `boolean` | `true` | See **Tool-result persistence**. |
250
- | `stableToolList` | `boolean` | `false` | Lazy MCP tools surfaced by `tool_search` are invoked through `call_tool` instead of growing the model's tool list (`behavior.toolSearch.mode: 'dispatch'`), so discovery never rewrites the prompt cache. Transcript shows the inner tool as with a direct call. |
254
+ | `stableToolList` | `boolean` | `false` | Lazy MCP tools surfaced by `tool_search` are invoked through `call_tool` instead of growing the model's tool list (`behavior.toolSearch.mode: 'dispatch'`), so discovery never rewrites the prompt cache. Transcript shows the inner tool as with a direct call. Anthropic always uses dispatch (replayed thinking is bound to the tool list — see Preserved thinking in `SKILL.md`); the toggle only affects other providers. |
255
+ | `sandbox` | `boolean` | `false` | Run the model's `shell` commands under the OS sandbox (macOS Seatbelt / Linux bwrap). Writes limited to cwd, temp dirs, a worktree's git dir and `sandboxAllowWrite`; credential dirs unreadable; `write_file`/`edit` follow the same roots. Not covered: `!` commands, MCP servers, `fetch_url`, reads. Fails closed when the backend is missing (a `sandbox` notice explains on activation). Resolved by `chat/sandbox-settings.ts` → `SpawnConfig.isolation`; see `docs/EXECUTION_CONTEXT.md`. Next session activation. |
256
+ | `sandboxNetwork` | `'allow'\|'deny'` | `'allow'` | Network for sandboxed commands. `deny` removes all sockets — the only way to stop exfiltration of readable files, at the cost of `bun install` / `git fetch`. |
257
+ | `sandboxAllowWrite` | `string` | `''` | Extra writable paths, comma/newline separated, `~` expanded, relative to cwd. |
251
258
  | `autoCompact` | `boolean` | `true` | See **Auto-compaction**. |
252
259
  | `autoCompactThreshold` | `number` | `0.8` | Cycles 0.6/0.7/0.8/0.9 in the settings modal. |
253
260
  | `resumeOnMaxTokens`, `maxOutputTokens`, `maxTurns`, `retryMaxAttempts`, `maxCostUsd`, `maxTotalTokens`, `maxWallMinutes` | mixed | off/`0` | Runaway ceilings; all off by default (model-driven, uncapped). |
261
+ | `thinkingBinding` | `'drop_block' \| 'error' \| 'off'` | `'drop_block'` | Anthropic only. What the API does with replayed thinking whose prefix changed: drop server-side and continue, fail the run with the API's 400 (no retry, history untouched — debugging), or account default. Published to `ZIDANE_THINKING_BINDING_SETTING` by the TUI on change and by the chat host per send; the provider reads it per request. Precedence: `anthropic({ thinkingBinding })` > `ZIDANE_THINKING_BINDING` set in the launching shell > this setting > `'drop_block'`. |
254
262
  | `showEditDiffs` | `boolean` | `true` | See **Edit-diff rendering**. |
255
263
  | `editDiffDisplay` | `'full'\|'compact'` | `'full'` | Approval modal always shows the full diff regardless. |
256
264
  | `showCompactionSummary` | `boolean` | `false` | Render the LLM summary text under the compaction card, not just metadata. |
@@ -275,9 +275,70 @@ bun run headless "fix the lint errors" --cwd /tmp/project
275
275
  Applicable flags: `--cwd` (defaults to the current directory), `--env`, and
276
276
  `--pass-env`. The `--image`, `--pregame`, and `--sandbox` flags don't apply.
277
277
 
278
- Because there's no isolation, the agent's commands can read and modify anything
279
- your own shell can. Prefer `docker` or `e2b` when you don't fully trust what the
280
- agent might run.
278
+ Because there's no isolation by default, the agent's commands can read and
279
+ modify anything your own shell can. Prefer `docker` or `e2b` when you don't
280
+ fully trust what the agent might run, or turn on the OS sandbox below for a
281
+ lightweight middle ground.
282
+
283
+ ### OS sandbox (`isolation`)
284
+
285
+ `createProcessContext({ cwd, isolation })` wraps every shell command in a
286
+ kernel-enforced sandbox — the same primitive Claude Code and Codex use:
287
+
288
+ | Platform | Backend | Mechanism |
289
+ |---|---|---|
290
+ | macOS | `/usr/bin/sandbox-exec` (Seatbelt) | deny-default SBPL profile derived from Codex's base policy; paths are passed as `-D` params, never inlined |
291
+ | Linux | `bwrap` (bubblewrap) | `--ro-bind / /`, per-root `--bind`, `--tmpfs` over credential dirs, `--unshare-net` when denied |
292
+ | Windows | — | unsupported; requesting isolation fails closed |
293
+
294
+ ```ts
295
+ import { createProcessContext, resolveWritableRoots } from 'zidane'
296
+
297
+ const execution = createProcessContext({
298
+ cwd,
299
+ isolation: {
300
+ writableRoots: resolveWritableRoots(cwd, { extra: ['~/.cache'] }),
301
+ network: 'allow', // or 'deny'
302
+ },
303
+ })
304
+ ```
305
+
306
+ **Policy.** `writableRoots` is where commands may create / modify / delete;
307
+ `resolveWritableRoots` (sync) / `resolveWritableRootsAsync` build the default set: `cwd`, `$TMPDIR`, `/tmp`,
308
+ `/var/tmp` (+ `/private` aliases on macOS), git's common dir when `cwd` is a
309
+ linked worktree (otherwise `git commit` fails there), plus extras — all
310
+ canonicalized and deduped. Reads are allowed everywhere except
311
+ `unreadableDirs` (default: `~/.ssh`, `~/.aws`, `~/.gnupg`, `~/.kube`,
312
+ `~/.azure`, `~/.config/gcloud`, `~/.docker/config.json`, `~/.netrc`,
313
+ zidane's `credentials.json` and `mcp-credentials.json`; the chat hosts
314
+ resolve the latter two from the actual data dir). `network` is
315
+ all-or-nothing; per-domain allowlists need a proxy and are not implemented.
316
+
317
+ **Scope.** Kernel enforcement covers `exec`, `execStream` and
318
+ `execBackground` — i.e. the model's `shell` tool and anything else routed
319
+ through the context (stdio MCP servers bound to it, subagents). `writeFile`
320
+ applies the same `writableRoots` in-process, so `write_file` / `edit` /
321
+ `multi_edit` cannot write where a shell command couldn't. `readFile`, `glob`
322
+ and `listFiles` are unrestricted (persisted tool outputs and skills under
323
+ `~/.zidane` / `~/.agents` must stay reachable). Not covered: MCP servers
324
+ spawned by the host, `fetch_url`, the `!` shell in the TUI, `$EDITOR`.
325
+
326
+ **Fail closed.** If the backend is missing (`bwrap` not installed,
327
+ non-darwin/linux), every shell call returns exit `127` with the reason and
328
+ `execStream` / `execBackground` throw; nothing runs unsandboxed. Probed once
329
+ at context creation; `capabilities.isolated` is `true` only when isolation is
330
+ actually enforced (requested AND backend available), `false` when it was
331
+ requested but the context is in this fail-closed state.
332
+
333
+ **Denials.** A failed command whose stderr matches `Operation not permitted`
334
+ / `Read-only file system` gets one appended `[sandbox] …` line naming the
335
+ writable roots and the settings that widen them, so the model asks instead
336
+ of guessing. Process-group kill semantics are unchanged: `sandbox-exec`
337
+ execs the shell in place and `bwrap` runs without `--new-session`.
338
+
339
+ In the chat hosts this is `Settings.sandbox` / `sandboxNetwork` /
340
+ `sandboxAllowWrite` (see `docs/CHAT.md`). Docker / e2b / daytona contexts
341
+ ignore it — they are already isolated.
281
342
 
282
343
  The process context also implements the optional `fileSize` capability — a
283
344
  containment-aware size probe that lets whole-file tools (`read_file`, `edit`,
package/docs/SKILL.md CHANGED
@@ -49,6 +49,7 @@ const agent = createAgent({
49
49
  toolBudgets: { run_query: { max: 20, onExceed: 'steer' } },
50
50
  compactStrategy: 'off', // 'off' | 'tail' (non-Anthropic compaction)
51
51
  compactThreshold: 131_072, // 128 KiB
52
+ compactThresholdWindowFraction: 0.25, // raise it to 25% of the model's window (large-context runs stop re-billing the prefix)
52
53
  compactKeepTurns: 4,
53
54
  toolDisclosure: 'eager', // 'eager' | 'lazy'
54
55
  toolSearch: { limit: 20 },
@@ -182,6 +183,32 @@ Flatten via `toolResultToText(result)`. MCP image blocks are auto-normalized via
182
183
 
183
184
  Cache hits surface on `TurnUsage.cacheRead` / `cacheCreation`. Anthropic + OpenRouter populate them per turn. Live break/drift telemetry is a hook concern — see `cache:break` / `cache:drift` in the [Hook reference](#hook-reference).
184
185
 
186
+ ### Preserved thinking (Anthropic)
187
+
188
+ Claude Fable 5.1 and later bind every replayed `thinking` block to the exact prefix it was produced under — `system`, `tools`, and every earlier message. If any of those bytes changed since, that block and every later one are invalid. By default the API rejects the whole request with a 400; on accounts created after 2026-08-31 this is enforced out of the box, on older accounts only when the field below is set. (`platform.claude.com/docs/en/build-with-claude/preserved-thinking`)
189
+
190
+ Zidane opts into the safe behavior on every thinking-enabled Anthropic request: `thinking.block_binding.prefix_mismatch_behavior: "drop_block"` plus the `thinking-binding-controls-2026-08-01` beta. Invalid blocks are dropped server-side (unbilled), the request succeeds, and history is never mutated. Thinking-off requests and Bedrock are untouched.
191
+
192
+ ```ts
193
+ anthropic({ apiKey: 'sk-ant-...', thinkingBinding: 'drop_block' }) // default
194
+ anthropic({ apiKey: 'sk-ant-...', thinkingBinding: 'error' }) // fail the run on a prefix edit — debugging / CI
195
+ anthropic({ apiKey: 'sk-ant-...', thinkingBinding: 'off' }) // send nothing; pre-feature wire bytes, account default applies
196
+ ```
197
+
198
+ Precedence: `thinkingBinding` param > shell `ZIDANE_THINKING_BINDING` > chat setting `thinkingBinding` (published on `ZIDANE_THINKING_BINDING_SETTING` before each run by the TUI / daemon) > `'drop_block'`. Read per request, so a flip lands on the next call without rebuilding the provider. The setting is labelled "Preserved thinking (Anthropic only)" and is inert on every other provider.
199
+
200
+ Effect per mode:
201
+
202
+ | Mode | On a prefix edit | History | Visible as |
203
+ |---|---|---|---|
204
+ | `drop_block` | Stale blocks dropped server-side; model answers without that reasoning; cache restarts at the edit | untouched | `provider:input-transformations` (`prefixMismatches` / `modelMismatches`) |
205
+ | `error` | 400; the run rejects, no retry | untouched | run error `Invalid \`signature\` in \`thinking\` block … bound to a different conversation` |
206
+ | `off` | Account default: old accounts tolerate, new accounts 400 → the signature-scrub ladder strips **all** history thinking and retries | scrubbed on 400 | `history:repair` |
207
+
208
+ What zidane already keeps prefix-safe: append-only turns, nudges and stale-read notices, tail truncation, compaction (`applyCompactSummaryCutoff` strips thinking from the kept tail), the `__output__` structured-output call, lazy MCP disclosure (defaults to `toolSearch.mode: 'dispatch'` on Anthropic so `tools` stays byte-stable), the TUI `<env> Date:` (pinned per session). What still costs reasoning once per edit: rewriting `system` between turns (cwd change, interaction-mode flip, rules-file edit, skill activation), a run-level `tools` change, `elideStaleReads: 'stub'`, `compactStrategy: 'tail'`, `mediaKeepTurns`. A model downgrade mid-conversation drops the newer model's blocks with `model_binding_mismatch`; that is not an edit and needs no action. Every `cache:break` attributed to `system`, `tools` or `messagePrefix` is also a preserved-thinking drop, so wire the two hooks to the same telemetry.
209
+
210
+ Live coverage: `test-inference/preserved-thinking.inference.test.ts`.
211
+
185
212
  ### Provider error classification
186
213
 
187
214
  Provider failures are wrapped via `toTypedError(err)` before leaving `agent.run()` — each provider implements `Provider.classifyError(err): ClassifiedError | null`; unrecognized errors fall back to `AgentProviderError`. Aborts always produce `AgentAbortedError`. Match on `instanceof`, not on `err.message`.
@@ -339,6 +366,7 @@ Every event in `AgentHooks` (`src/agent.ts`), one row each. `child:*` bubble eve
339
366
  | `compact:progress` | Throttled, while the compaction summary streams | — | `+ estOutputTokens` |
340
367
  | `compact:end` | Exactly once per `compact:start`, on success or failure | — | `+ status: 'success' \| 'error'` (success: replaced/dropped/restored counts + `effectiveTokens`; error: `error`) |
341
368
  | `history:repair` | After a provider stream settles when the loop scrubbed provider-rejected reasoning state (e.g. Anthropic signature-400 poisoning) | — | `reason: 'invalid-anthropic-thinking-signature', runId, sessionId?, turnIds, removedBlocks` |
369
+ | `provider:input-transformations` | Provider dropped replayed thinking blocks server-side before sampling (Anthropic preserved-thinking binding, `input_transformations`). History untouched; `prefix_binding_mismatch` entries mean this host edited system/tools/an earlier message | — | `runId, turnId, sessionId?, transformations[{type,path,reason}], prefixMismatches, modelMismatches` |
342
370
  | `oauth:refresh` | OAuth token refreshed | — | `provider, providerId, source: 'params'\|'file', previousCredentials, credentials` |
343
371
  | `content-ref:resolve` | Wire assembly sees a block carrying `ref`; resolves persisted content refs into provider-bound bytes | **yes** (`data`) | `block, data?, childId?, depth?` |
344
372
  | `tool:gate` | Before each tool call | **yes** (`block, reason, result?`) | `turnId, callId, name, displayName, input, runToolCounts` — `block` refuses with `tool_result.content = "Blocked: <reason>"` + `is_error: true`; `result` substitutes and skips execute; `block` wins over `result` |
package/docs/TUI.md CHANGED
@@ -108,7 +108,7 @@ Not re-exported: the renderer-agnostic engine (settings, theme, safe-mode, store
108
108
  Layered slots, in priority order:
109
109
 
110
110
  1. **Transcript-slot takeover** — a file-edit approval (`edit`/`multi_edit`/`write_file`) renders `FileEditApprovalModal` inline in the transcript slot (`<Transcript visible={!fileEditPending}>` so scroll position and memoized turn anchors survive open→close). See [File-edit approval modal](#file-edit-approval-modal).
111
- 2. **Bottom slot**, one of: non-file-edit `pendingApproval` → `ApprovalBlock` (accept once / for session / + remember / deny, esc aborts the run) · `pendingInteraction` → `InteractionBlock` (plan picker or Q&A wizard; esc aborts if live, exits the form if resumed) · else `PromptBlock` (textarea + history), kept mounted (`Display.None` when hidden) so draft/cursor/attachments survive an approval round-trip.
111
+ 2. **Bottom slot**, one of: non-file-edit `pendingApproval` → `ApprovalBlock` (accept once / for session / + remember / deny, `ctrl+esc` aborts the run) · `pendingInteraction` → `InteractionBlock` (plan picker or Q&A wizard; `ctrl+esc` aborts if live, esc leaves for the sessions list) · else `PromptBlock` (textarea + history), kept mounted (`Display.None` when hidden) so draft/cursor/attachments survive an approval round-trip.
112
112
 
113
113
  When the queue has entries, a `QueuedMessagesBlock` renders between transcript and prompt:
114
114
 
@@ -140,7 +140,7 @@ Every action routes through `<userDir>/keybindings.json`, matched via `matchesBi
140
140
  | `ctrl+shift+m` | `openMainSession` | any, when `Settings.orchestration` is on | jump to (or lazily create) the pinned Main orchestration session for the cwd; requires the daemon runtime. Silent no-op when the setting/runtime is off |
141
141
  | `ctrl+p` | `openUsagePanel` | chat | subscription usage panel; also opens by clicking the footer's `▰ 5h 43%` segment |
142
142
  | `ctrl+y` | `openKeybindings` | non-auth | keybindings panel, `↵` opens `keybindings.json` |
143
- | `ctrl+k` | `cancelToolCall` | chat (busy, ≥1 in-flight call) | cancel-tool picker (`agent.cancelTool(callId)`); distinct from `esc` (whole-run abort) |
143
+ | `ctrl+k` | `cancelToolCall` | chat (busy, ≥1 in-flight call) | cancel-tool picker (`agent.cancelTool(callId)`); distinct from `ctrl+esc` (whole-run abort) |
144
144
  | `ctrl+h` | `changeCwd` | non-auth | open the directory picker to change cwd |
145
145
  | `ctrl+v` | `attachClipboardImage` | textarea | paste text or an image from the OS clipboard, read by the process itself (bypasses a frozen terminal paste path) |
146
146
  | `ctrl+g` | `openExternalEditor` | textarea | edit the prompt draft in `$EDITOR`/`$VISUAL`; gated on `externalEditor` setting |
@@ -151,7 +151,8 @@ Every action routes through `<userDir>/keybindings.json`, matched via `matchesBi
151
151
  | — (unbound) | `enterQueueSelection` | textarea | move focus into the queue box |
152
152
  | `enter` / `shift+enter` | — | textarea | submit / newline |
153
153
  | `↑`/`↓` at buffer edges | — | textarea (popup closed) | cycle prompt history |
154
- | `esc` | — | popup / run in flight / modal open | dismiss popup, or abort run + drain approval+interaction queues, or close modal |
154
+ | `ctrl+esc` | `abortRun` | chat (busy or pending approval) | abort run + drain approval+interaction queues. Rebind (e.g. `ctrl+x`) if the terminal cannot report ctrl on esc |
155
+ | `esc` | — | popup / chat / modal open | dismiss popup, or leave the chat for the sessions list (busy or not — the run continues), or close modal |
155
156
  | `pageup`/`pagedown`, `shift+pageup`/`shift+pagedown` | — | chat, no modal | scroll transcript one page / jump to top-bottom |
156
157
  | `ctrl+a` | (textarea built-in) | textarea | select-all |
157
158
 
@@ -176,7 +177,7 @@ Every action routes through `<userDir>/keybindings.json`, matched via `matchesBi
176
177
 
177
178
  `<userDir>/keybindings.json` is JSONC. `ensureKeybindingsFile` never overwrites an existing file. Invalid/unknown entries silently fall back to defaults (`ZIDANE_DEBUG` logs why). Settings → General → "Keybindings" opens the file in `$EDITOR`/`$VISUAL`; changes apply on restart. Full alias/parsing rules: [CHAT.md § Keybindings](./CHAT.md#keybindings).
178
179
 
179
- **Esc semantics.** While busy, `esc` runs `denyAll → interactions.cancelAll → abort → stream.reset → agent.destroy` (shared with session-switching/re-auth teardown). `applyGate` does **not** cascade a single deny through the rest of the queue: denying one approval blocks only that call (`Blocked: …`), other pending approvals stay queued. The global `esc abort` is the only path that denies everything + aborts the run. Esc is consumed by the completion popup first when one is open.
180
+ **Abort semantics.** `abortRun` (`ctrl+esc`) runs `denyAll → interactions.cancelAll → abort → stream.reset → agent.destroy` (shared with session-switching/re-auth teardown). `applyGate` does **not** cascade a single deny through the rest of the queue: denying one approval blocks only that call (`Blocked: …`), other pending approvals stay queued. The global `abortRun` is the only path that denies everything + aborts the run. Plain `esc` never aborts: under the daemon a window can reattach to a session mid-run, and esc must always be able to return to the sessions list. Local runtime only: picking another session (or new) while a run is in flight is refused with a notice under the list — the window's single agent would otherwise be torn down by `activateSession`; abort first, or run under the daemon where runs outlive the window's session choice. Esc is consumed by the completion popup first when one is open.
180
181
 
181
182
  ## Modals
182
183
 
@@ -199,7 +200,7 @@ Inline-mounted in `ChatScreen`'s transcript slot (not stacked); keyed on `reques
199
200
 
200
201
  ### Tool-call cancellation
201
202
 
202
- `CancelToolModal` lists the in-flight tool registry (`App.tsx` tracks it via `tool:before`/`tool:after`/`tool:error`/`tool:cancelled`, mirrored for `child:*`), opened by `ctrl+k` when `busy && !pendingApproval && inFlightTools.length > 0`. Row: `›` cursor, tool name, truncated `callId`, optional `· child-N`, elapsed time. `↑`/`↓` navigate, `↵` cancels the focused row, `a` cancels every call, `esc` dismisses. Auto-dismisses 500ms after the in-flight list empties. Cancellation resolves that call with `TOOL_USE_CANCELLED_MESSAGE` (`isError: true`); other in-flight tools keep running — distinct from the run-wide `esc abort`.
203
+ `CancelToolModal` lists the in-flight tool registry (`App.tsx` tracks it via `tool:before`/`tool:after`/`tool:error`/`tool:cancelled`, mirrored for `child:*`), opened by `ctrl+k` when `busy && !pendingApproval && inFlightTools.length > 0`. Row: `›` cursor, tool name, truncated `callId`, optional `· child-N`, elapsed time. `↑`/`↓` navigate, `↵` cancels the focused row, `a` cancels every call, `esc` dismisses. Auto-dismisses 500ms after the in-flight list empties. Cancellation resolves that call with `TOOL_USE_CANCELLED_MESSAGE` (`isError: true`); other in-flight tools keep running — distinct from the run-wide `ctrl+esc` abort.
203
204
 
204
205
  ### TodosModal
205
206
 
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "zidane",
3
- "version": "7.0.32",
3
+ "version": "7.1.1",
4
4
  "description": "an agent that goes straight to the goal",
5
5
  "type": "module",
6
6
  "private": false,