zidane 6.3.7 → 6.3.9

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 (80) hide show
  1. package/dist/acp-cli.js +2 -2
  2. package/dist/{acp-CAX3tOEY.js → acp-dthcOo20.js} +3 -3
  3. package/dist/{acp-CAX3tOEY.js.map → acp-dthcOo20.js.map} +1 -1
  4. package/dist/acp.d.ts +1 -1
  5. package/dist/acp.js +1 -1
  6. package/dist/{agent-Emjeo5Ad.js → agent-XcXGROHb.js} +38 -18
  7. package/dist/agent-XcXGROHb.js.map +1 -0
  8. package/dist/agent.d.ts +1 -1
  9. package/dist/agent.js +2 -2
  10. package/dist/chat/pure.d.ts +3 -3
  11. package/dist/chat.d.ts +4 -4
  12. package/dist/chat.js +2 -2
  13. package/dist/{context-transport-3b0qpM2m.js → context-transport-DIVijS-Z.js} +3 -3
  14. package/dist/{context-transport-3b0qpM2m.js.map → context-transport-DIVijS-Z.js.map} +1 -1
  15. package/dist/contexts/daytona.d.ts +1 -1
  16. package/dist/contexts/e2b.d.ts +1 -1
  17. package/dist/eval.d.ts +1 -1
  18. package/dist/extensions.d.ts +1 -1
  19. package/dist/extensions.js +1 -1
  20. package/dist/headless.d.ts +1 -1
  21. package/dist/headless.js +1 -1
  22. package/dist/{index-DTIe4e1R.d.ts → index-DEqQQ8tN.d.ts} +42 -6
  23. package/dist/index-DEqQQ8tN.d.ts.map +1 -0
  24. package/dist/index.d.ts +1 -1
  25. package/dist/index.js +4 -4
  26. package/dist/{login-C41DcRK-.js → login-COaVj9kG.js} +35 -5
  27. package/dist/{login-C41DcRK-.js.map → login-COaVj9kG.js.map} +1 -1
  28. package/dist/{mcp-8bEHU4WB.js → mcp-CzUy5luo.js} +3 -3
  29. package/dist/{mcp-8bEHU4WB.js.map → mcp-CzUy5luo.js.map} +1 -1
  30. package/dist/mcp.d.ts +1 -1
  31. package/dist/mcp.js +1 -1
  32. package/dist/output/stream-json.d.ts +1 -1
  33. package/dist/output/terminal.d.ts +1 -1
  34. package/dist/presets.d.ts +1 -1
  35. package/dist/presets.js +1 -1
  36. package/dist/providers/anthropic.d.ts +1 -1
  37. package/dist/providers/arcee.d.ts +1 -1
  38. package/dist/providers/baseten.d.ts +1 -1
  39. package/dist/providers/cerebras.d.ts +1 -1
  40. package/dist/providers/local.d.ts +1 -1
  41. package/dist/providers/openai-compat.d.ts +1 -1
  42. package/dist/providers/openai.d.ts +1 -1
  43. package/dist/providers/openrouter.d.ts +1 -1
  44. package/dist/providers/xai.d.ts +1 -1
  45. package/dist/providers.d.ts +1 -1
  46. package/dist/{read-state-CNyeMG7W.js → read-state-Dt7UbVsd.js} +26 -2
  47. package/dist/read-state-Dt7UbVsd.js.map +1 -0
  48. package/dist/restate.d.ts +1 -1
  49. package/dist/restate.js +1 -1
  50. package/dist/session/sqlite.d.ts +1 -1
  51. package/dist/session.d.ts +1 -1
  52. package/dist/skills.d.ts +1 -1
  53. package/dist/{stdio-BKFpPNvX.js → stdio-D7Nf908h.js} +2 -2
  54. package/dist/{stdio-BKFpPNvX.js.map → stdio-D7Nf908h.js.map} +1 -1
  55. package/dist/{stdio-loader-DFDpBwwI.js → stdio-loader-C3ydQZyZ.js} +2 -2
  56. package/dist/{stdio-loader-DFDpBwwI.js.map → stdio-loader-C3ydQZyZ.js.map} +1 -1
  57. package/dist/{tool-formatters-B_dGfYqT.d.ts → tool-formatters-B0y_dlZ-.d.ts} +2 -2
  58. package/dist/{tool-formatters-B_dGfYqT.d.ts.map → tool-formatters-B0y_dlZ-.d.ts.map} +1 -1
  59. package/dist/tools/fetch-url.d.ts +1 -1
  60. package/dist/tools/web-search.d.ts +1 -1
  61. package/dist/tools.d.ts +1 -1
  62. package/dist/tools.js +2 -2
  63. package/dist/{transcript-anchors-CqxIlrhv.d.ts → transcript-anchors-B93mTVIi.d.ts} +2 -2
  64. package/dist/{transcript-anchors-CqxIlrhv.d.ts.map → transcript-anchors-B93mTVIi.d.ts.map} +1 -1
  65. package/dist/{transcript-anchors-DhBTqTyc.js → transcript-anchors-BeCn35OE.js} +3 -3
  66. package/dist/{transcript-anchors-DhBTqTyc.js.map → transcript-anchors-BeCn35OE.js.map} +1 -1
  67. package/dist/tui.d.ts +3 -3
  68. package/dist/tui.js +52 -8
  69. package/dist/tui.js.map +1 -1
  70. package/dist/{turn-operations-D7JhuW_X.d.ts → turn-operations-B-t6VR01.d.ts} +3 -3
  71. package/dist/{turn-operations-D7JhuW_X.d.ts.map → turn-operations-B-t6VR01.d.ts.map} +1 -1
  72. package/dist/types-BIarq1qE.js.map +1 -1
  73. package/dist/types.d.ts +1 -1
  74. package/docs/ARCHITECTURE.md +1 -1
  75. package/docs/CHAT.md +1 -1
  76. package/docs/TUI.md +4 -2
  77. package/package.json +1 -1
  78. package/dist/agent-Emjeo5Ad.js.map +0 -1
  79. package/dist/index-DTIe4e1R.d.ts.map +0 -1
  80. package/dist/read-state-CNyeMG7W.js.map +0 -1
package/dist/types.d.ts CHANGED
@@ -1,5 +1,5 @@
1
1
  import { a as AgentProviderError, c as CONTEXT_EXCEEDED_MESSAGE_PATTERNS, f as matchesContextExceeded, i as AgentContextExceededError, l as ClassifiedError, n as AgentAbortedError, o as AgentToolNotAllowedError, r as AgentBudgetExceededError, u as ClassifiedErrorKind } from "./timeout-8vSIRjzl.js";
2
2
  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-M2DauK2D.js";
3
3
  import { t as SandboxProvider } from "./sandbox-DLY6uah7.js";
4
- import { Am as ToolOutcome, As as SpawnToolOptions, Bm as TurnFinishReason, C as SuccessfulMutationOutcome, Dm as ThinkingLevel, Fm as ToolResultImageContent, Fp as AgentClock, Gi as AgentHookMap, Gm as ToolContext, Hm as toolOutputBudgetByteLength, Im as ToolResultImageRefContent, Ip as AgentRunOptions, Ki as AgentHooks, Km as ToolDef, Kp as DEFAULT_AGENT_CLOCK, Lm as ToolResultMetadata, Lp as AgentStats, Mm as ToolResultContent, Nm as ToolResultDocumentContent, Pm as ToolResultDocumentRefContent, Pp as AgentBehavior, Qf as CerebrasParams, Qp as McpToolHookContext, Rm as ToolResultTextContent, Rr as InteractionToolOptions, S as MutationValidationResult, Sm as SpawnHookContext, Th as RemoteStoreOptions, Tm as StreamHookContext, Ts as ChildAgent, Uf as OpenAIParams, Um as toolOutputByteLength, Vm as TurnUsage, Vp as ChildRunStats, Wi as Agent, Wm as toolResultToText, Xm as ReadStateMap, Ym as ReadStateEntry, Zp as McpServerConfig, _ as statsByModel, _m as SessionContentBlock, af as StreamCallbacks, am as PromptAudioPart, b as MutationValidationHooksOptions, bm as SessionMessage, cf as ToolCall, ch as SkillsConfig, cm as PromptPart, df as TurnResult, dh as Session, dm as RepeatGuardConfig, fh as SessionData, fm as RepeatGuardToolMatcher, gm as RunHookMap, h as flattenTurns, hh as SessionStore, hm as RetryConfig, if as ProviderCapabilities, ig as McpConnection, ih as SkillConfig, im as ProgressNudgeMessage, jm as ToolResultAudioContent, js as SpawnToolState, km as ToolHookContext, lf as ToolResult, lh as CreateSessionOptions, lm as PromptTextPart, mh as SessionRun, mm as ResolveContentRefBlock, nm as OAuthRefreshHookContext, of as StreamOptions, oh as SkillResource, om as PromptDocumentPart, op as AnthropicParams, p as ModelUsage, pm as ResolveContentRef, qi as AgentOptions, qm as ToolMap, rf as Provider, rm as ProgressNudgeKind, sm as PromptImagePart, t as Preset, uf as ToolSpec, um as PromptVideoPart, v as MutationValidationBatch, vm as SessionEndStatus, vr as ValidationResult, x as MutationValidationMutation, xm as SessionTurn, y as MutationValidationFailure, yf as OpenRouterParams, ym as SessionHookContext, zm as ToolResultVideoContent } from "./index-DTIe4e1R.js";
4
+ import { Am as ToolOutcome, As as SpawnToolOptions, Bm as TurnFinishReason, C as SuccessfulMutationOutcome, Dm as ThinkingLevel, Fm as ToolResultImageContent, Fp as AgentClock, Gi as AgentHookMap, Gm as ToolContext, Hm as toolOutputBudgetByteLength, Im as ToolResultImageRefContent, Ip as AgentRunOptions, Ki as AgentHooks, Km as ToolDef, Kp as DEFAULT_AGENT_CLOCK, Lm as ToolResultMetadata, Lp as AgentStats, Mm as ToolResultContent, Nm as ToolResultDocumentContent, Pm as ToolResultDocumentRefContent, Pp as AgentBehavior, Qf as CerebrasParams, Qp as McpToolHookContext, Rm as ToolResultTextContent, Rr as InteractionToolOptions, S as MutationValidationResult, Sm as SpawnHookContext, Th as RemoteStoreOptions, Tm as StreamHookContext, Ts as ChildAgent, Uf as OpenAIParams, Um as toolOutputByteLength, Vm as TurnUsage, Vp as ChildRunStats, Wi as Agent, Wm as toolResultToText, Xm as ReadStateMap, Ym as ReadStateEntry, Zp as McpServerConfig, _ as statsByModel, _m as SessionContentBlock, af as StreamCallbacks, am as PromptAudioPart, b as MutationValidationHooksOptions, bm as SessionMessage, cf as ToolCall, ch as SkillsConfig, cm as PromptPart, df as TurnResult, dh as Session, dm as RepeatGuardConfig, fh as SessionData, fm as RepeatGuardToolMatcher, gm as RunHookMap, h as flattenTurns, hh as SessionStore, hm as RetryConfig, if as ProviderCapabilities, ig as McpConnection, ih as SkillConfig, im as ProgressNudgeMessage, jm as ToolResultAudioContent, js as SpawnToolState, km as ToolHookContext, lf as ToolResult, lh as CreateSessionOptions, lm as PromptTextPart, mh as SessionRun, mm as ResolveContentRefBlock, nm as OAuthRefreshHookContext, of as StreamOptions, oh as SkillResource, om as PromptDocumentPart, op as AnthropicParams, p as ModelUsage, pm as ResolveContentRef, qi as AgentOptions, qm as ToolMap, rf as Provider, rm as ProgressNudgeKind, sm as PromptImagePart, t as Preset, uf as ToolSpec, um as PromptVideoPart, v as MutationValidationBatch, vm as SessionEndStatus, vr as ValidationResult, x as MutationValidationMutation, xm as SessionTurn, y as MutationValidationFailure, yf as OpenRouterParams, ym as SessionHookContext, zm as ToolResultVideoContent } from "./index-DEqQQ8tN.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 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 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 TurnFinishReason, type TurnResult, type TurnUsage, type ValidationResult, flattenTurns, matchesContextExceeded, statsByModel, toolOutputBudgetByteLength, toolOutputByteLength, toolResultToText };
@@ -826,7 +826,7 @@ Defaults: unlimited turns (set `maxTurns` as a safety net), model-driven output
826
826
 
827
827
  **`autoCompact`** — loop-native auto-compaction (`boolean | AutoCompactConfig`). At each turn boundary the loop computes the last turn's effective input (`input + cacheRead + cacheCreation`, so prompt caching can't hide a full window) and, once it crosses `threshold` (default `0.85`) of the effective context window, runs a real summary over the older turns (`compactConversation`, `scope: 'tail'`), appends a `compact-summary` marker plus optional file/skill restoration (`buildPostCompactAttachments`) to the session, and continues — the next turn's `applyCompactSummaryCutoff` elides the summarized turns. Fires mid tool-loop AND at end-of-run (so a resumed session starts compacted). Window source is `autoCompact.contextWindow` → `provider.meta.contextWindow`; with neither it's inert (the SDK core has no model catalog, so chat hosts inject the picked model's window). A hysteresis floor (`minGrowthFraction`, default `0.1`) stops post-compact thrashing and a consecutive-failure circuit breaker (`maxFailures`, default `2`) disables it for the run if the summary keeps failing. Distinct from `compactStrategy` (per-turn wire-only tool-output trim) and the always-on reactive `context_exceeded` recovery; observe via the `compact:start` / `compact:end` hooks. SDK default off; chat profiles enable it (`{ threshold: 0.85 }`) and the TUI/GUI inject the window + honor the user's enable/threshold settings per run.
828
828
 
829
- **`dedupReads` + `requireReadBeforeEdit`** share the same per-session `WeakMap<Session, Map<canonicalAbsPath, { contentHash, offset, limit, maxBytes, mtimeMs }>>`. The key is the canonical absolute path (via `node:path.resolve(handle.cwd, modelProvidedPath)`) so all path shapes the model can write — `src/x`, `./src/x`, `/abs/cwd/src/x` — converge on one entry. Map population happens whenever **either** feature is enabled; the dedup *short-circuit* (returning the "unchanged since the previous read" stub) is what `dedupReads` actually gates. This decoupling means `dedupReads: false` no longer breaks the `requireReadBeforeEdit` guard. `edit` / `multi_edit` reject when no entry exists or the on-disk hash drifted; both update the hash on success so chained edits don't need re-reads — and set the entry's `elided` flag in the same update, so the next re-read misses dedup and serves the fresh body instead of a "still current" stub pointing at the pre-mutation output (`write_file` seeds with the flag for the same reason). Dedup resumes after that one honest read; the flag never affects the edit gate. Optional `AgentOptions.readState` (also exposed on `ToolContext.readState`) forwards an explicit map — used by `createSpawnTool({ shareReadState: true })` so a sessionless child still feeds the parent's tracking without sharing turn persistence. GC'd with the session when no explicit map is in play. The map is **never serialized**, so a host that reloads a fresh `Session` from a store on resume — or a durable/replay runtime where a journaled `read_file` never re-runs its live `rememberRead` — would start with an empty map and the gate would reject every edit whose read happened in an earlier turn. To prevent that, a `tool:gate` hook (installed when `requireReadBeforeEdit` is on) pre-hydrates the map from `session.turns` right before an `edit`/`multi_edit` runs, when its target path is missing from the map: transcript reconstruction for full reads, an `execution.readFile` re-hash fallback for partial ones, alias-aware for wire-named transcripts. It fires **inside the loop, outside any journaled tool body, on every replay**, so the verdict can never be frozen by a host that journals tool execution; it has an O(1) `Map.has` fast path so it only scans when a path is genuinely absent, and never blocks (the authoritative read/drift verdict stays in the tool body). The `restateTool` wrapper additionally hydrates once per process for `read_file` dedup; it is a complement, not what the gate depends on.
829
+ **`dedupReads` + `requireReadBeforeEdit`** share the same per-session `WeakMap<Session, Map<canonicalAbsPath, { contentHash, offset, limit, maxBytes, mtimeMs }>>`. The key is the canonical absolute path (via `node:path.resolve(handle.cwd, modelProvidedPath)`) so all path shapes the model can write — `src/x`, `./src/x`, `/abs/cwd/src/x` — converge on one entry. Map population happens whenever **either** feature is enabled; the dedup *short-circuit* (returning the "unchanged since the previous read" stub) is what `dedupReads` actually gates. This decoupling means `dedupReads: false` no longer breaks the `requireReadBeforeEdit` guard. `edit` / `multi_edit` reject when no entry exists (the message notes that shell reads — cat/grep/sed — don't register). On-disk hash drift is **anchor-tolerant**: the hazard drift creates is a mis-anchored `old_string`, so if the edit still resolves cleanly against the current bytes it proceeds with a disclosure note; only when resolution fails (not found / newly ambiguous, or a `multi_edit` batch where nothing anchors) is the drift refused with "re-read the file" — and consecutive refusals without an intervening read escalate (`staleRejections` on the entry, reset by any fresh read or successful edit) to name the likely external writer (editor auto-save, formatter, codegen watcher). Flat drift refusals are what taught models to abandon `edit` for shell one-liners that bypass every guard. Both tools update the hash on success so chained edits don't need re-reads — and set the entry's `elided` flag in the same update, so the next re-read misses dedup and serves the fresh body instead of a "still current" stub pointing at the pre-mutation output (`write_file` seeds with the flag for the same reason). Dedup resumes after that one honest read; the flag never affects the edit gate. Optional `AgentOptions.readState` (also exposed on `ToolContext.readState`) forwards an explicit map — used by `createSpawnTool({ shareReadState: true })` so a sessionless child still feeds the parent's tracking without sharing turn persistence. GC'd with the session when no explicit map is in play. The map is **never serialized**, so a host that reloads a fresh `Session` from a store on resume — or a durable/replay runtime where a journaled `read_file` never re-runs its live `rememberRead` — would start with an empty map and the gate would reject every edit whose read happened in an earlier turn. To prevent that, a `tool:gate` hook (installed when `requireReadBeforeEdit` is on) pre-hydrates the map from `session.turns` right before an `edit`/`multi_edit` runs, when its target path is missing from the map: transcript reconstruction for full reads, an `execution.readFile` re-hash fallback for partial ones, alias-aware for wire-named transcripts. It fires **inside the loop, outside any journaled tool body, on every replay**, so the verdict can never be frozen by a host that journals tool execution; it has an O(1) `Map.has` fast path so it only scans when a path is genuinely absent, and never blocks (the authoritative read/drift verdict stays in the tool body). The `restateTool` wrapper additionally hydrates once per process for `read_file` dedup; it is a complement, not what the gate depends on.
830
830
 
831
831
  **`dedupTools`** generalizes the same pattern via a consumer hasher per tool. Parallel `WeakMap<Session, Map<toolName, { hash, result, repeats }>>` keeps only the most recent hash. Two forms:
832
832
 
package/docs/CHAT.md CHANGED
@@ -388,7 +388,7 @@ Both share `SHARED_BEHAVIOR`:
388
388
 
389
389
  `DEFAULT_PERSIST_EXCLUDE_TOOLS` = `['read_file', 'tool_search', 'skills_use', 'skills_read', 'present_plan', 'ask_user', 'spawn', 'todowrite', 'todoread']`. SDK consumers building custom profiles can extend this for any host-defined tool whose output should stay inline regardless of size. `DEFAULT_BUDGET_EXCLUDE_TOOLS` = `['tool_search', 'skills_use', 'skills_read']` (context-loading tools exempt from the per-turn output budget).
390
390
 
391
- The loop-prevention knobs are the one place the chat profiles diverge from the SDK core's conservative defaults. `requireReadBeforeEdit` is the only one that can *reject* a previously-succeeding call — but the refusal is a recoverable `tool_result` (`Edit error: <path> has not been read in this session. Call read_file first …`), not a hard error, so a model self-corrects in one turn. `repeatGuard` defaults to strict chain mode: it blocks the 4th back-to-back identical `shell` call (6th for `read_file`) and aborts at the 8th (12th for `read_file`) via the agent's `AbortController`, preceded by a `repeat-guard:exceeded` event. Consumers can opt into `mode: 'window' | 'both'`, custom `steeringMessage`, or `thresholdAction: 'steer'` to append guidance to the tool result instead of blocking at the soft threshold. `dedupTools` replay and `thinkingDecay` are free / inert until triggered.
391
+ The loop-prevention knobs are the one place the chat profiles diverge from the SDK core's conservative defaults. `requireReadBeforeEdit` is the only one that can *reject* a previously-succeeding call — but the refusal is a recoverable `tool_result` (`Edit error: <path> has not been read in this session. Call read_file first …`), not a hard error, so a model self-corrects in one turn. Content drift after a read is anchor-tolerant (the edit proceeds with a note when `old_string` still resolves cleanly against the current bytes; refusal is reserved for anchors the drift broke, escalating on repeat to name the likely external writer) so churny workflows — a formatter or codegen touching files between edits — don't turn the guard into a standing "re-read everything" tax that pushes models toward shell-based edits. `repeatGuard` defaults to strict chain mode: it blocks the 4th back-to-back identical `shell` call (6th for `read_file`) and aborts at the 8th (12th for `read_file`) via the agent's `AbortController`, preceded by a `repeat-guard:exceeded` event. Consumers can opt into `mode: 'window' | 'both'`, custom `steeringMessage`, or `thresholdAction: 'steer'` to append guidance to the tool result instead of blocking at the soft threshold. `dedupTools` replay and `thinkingDecay` are free / inert until triggered.
392
392
 
393
393
  Other reliability levers stay off unless a profile opts in: `writeFileExistingPolicy: 'block-with-edit-recipe'` converts existing-file `write_file` rewrites into a recoverable `tool:gate` refusal; `readFileDefaults` sets omitted `read_file` slice defaults without changing explicit calls; `thinkingOverrunPolicy` queues a non-interrupting next-turn nudge when streamed thinking crosses a threshold; `textToolCallRescue` steers after a model writes JSON/XML/Liquid-style tool-call markup as plain text; `unknownToolRecovery` queues closest currently-advertised tool-name suggestions after an unknown native tool call; `progressNudges` emits bounded pivot nudges for repeated read-only turns, failed/no-op mutations, validation rejects, or tool errors; `maxTurnsWarningMessage` customizes the built-in turn-cap warning.
394
394
 
package/docs/TUI.md CHANGED
@@ -502,13 +502,15 @@ Interactive login flow (triggered by `ctrl+L` on a needs-auth row in `SettingsMo
502
502
 
503
503
  1. App calls `loginMcpServer(config, { store, hooks, signal })` from `zidane/mcp`.
504
504
  2. `startOAuthCallback` spawns a loopback `127.0.0.1` HTTP listener on a random port.
505
- 3. `McpOAuthProvider` is built with `redirectUri` pointing at the loopback; `client.connect()` triggers `redirectToAuthorization` which fires `mcp:auth:url` (the modal renders the URL via `<McpAuthorizingPanel>` — `tryOpenBrowser` opens it automatically).
505
+ 3. `McpOAuthProvider` is built with `redirectUri` pointing at the loopback; `client.connect()` triggers `redirectToAuthorization`, which reaches the host through `onAuthorizationUrl` (and fires `mcp:auth:url` for other listeners). `onLoginMcp` dispatches `auth-url` / `auth-success` / `auth-error` **directly** rather than reading them back off the agent hooks: `agentRef.current` is null before the first session activation and while `rebuildActiveAgent` swaps agents (the window the post-add auto-login runs in), and a hook-only path left the panel on a spinner with no URL, no copy button and no paste input — for a login that had already succeeded in the browser. The modal renders the URL via `<McpAuthorizingPanel>`; `tryOpenBrowser` opens it automatically.
506
+
507
+ This phase is bounded by `connectTimeoutMs` (default 30s) and races the abort signal, because the SDK's `connect()` accepts no `AbortSignal` — without that, an unreachable server hangs pre-URL forever and `esc` can't reach it.
506
508
  4. SDK throws `UnauthorizedError`; we wait on the callback for the `?code=...` redirect. The same `<McpAuthorizingPanel>` includes a paste-back input (step 2 of the panel) — submitting pipes the value through `fetchOAuthRedirect`, which `fetch()`s the loopback callback URL from inside the agent process and lands a `?code=...` on the listener exactly like a real browser would. This unblocks SSH sessions where the loopback redirect can't reach the remote server directly. The panel prints the exact `redirect_uri` the request was minted with (`redirectUriFromAuthUrl`) so there's no guessing about which address to copy out of the browser's URL bar, and it force-focuses the input on mount — `focused` as a prop alone leaves keys with whichever field owned focus before the takeover mounted.
507
509
  5. `transport.finishAuth(code)` exchanges the code for tokens; `provider.saveTokens` persists via `createFileMcpCredentialStore(userDir)`.
508
510
  6. Reconnect with the new tokens; `client.listTools()` returns the catalog.
509
511
  7. App rebuilds the active agent so the now-authenticated server connects on the next run.
510
512
 
511
- `ctrl+O` (logout) wipes credentials via the store and dispatches `{ type: 'logout', name }`. `esc` on an `authorizing` row dispatches `{ type: 'login:cancel', name }` before the Modal's generic esc-close fires (single keystroke cancels + dismisses).
513
+ `ctrl+O` (logout) wipes credentials via the store and dispatches `{ type: 'logout', name }`. `esc` on an `authorizing` row dispatches `{ type: 'login:cancel', name }` before the Modal's generic esc-close fires (single keystroke cancels + dismisses). `onCancelLoginMcp` dispatches that event *before* aborting the controller so a wedged flow still tears the panel down immediately; the login's own catch re-dispatches it when the abort finally lands.
512
514
 
513
515
  ## Auth wizard (AI providers)
514
516
 
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "zidane",
3
- "version": "6.3.7",
3
+ "version": "6.3.9",
4
4
  "description": "an agent that goes straight to the goal",
5
5
  "type": "module",
6
6
  "private": false,