zidane 6.3.5 → 6.3.7

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 (123) hide show
  1. package/dist/{acp-IgXPptYr.js → acp-CAX3tOEY.js} +4 -4
  2. package/dist/{acp-IgXPptYr.js.map → acp-CAX3tOEY.js.map} +1 -1
  3. package/dist/acp-cli.js +6 -6
  4. package/dist/acp.d.ts +2 -2
  5. package/dist/acp.js +1 -1
  6. package/dist/{agent-BJuiohV_.js → agent-Emjeo5Ad.js} +236 -23
  7. package/dist/{agent-BJuiohV_.js.map → agent-Emjeo5Ad.js.map} +1 -1
  8. package/dist/agent.d.ts +1 -1
  9. package/dist/agent.js +1 -1
  10. package/dist/{anthropic-Drd3842n.js → anthropic-BN0iPMox.js} +20 -5
  11. package/dist/anthropic-BN0iPMox.js.map +1 -0
  12. package/dist/{auth-WkdClN2Y.js → auth-Dptb8U5J.js} +10 -5
  13. package/dist/auth-Dptb8U5J.js.map +1 -0
  14. package/dist/chat/pure.d.ts +3 -3
  15. package/dist/chat.d.ts +50 -6
  16. package/dist/chat.d.ts.map +1 -1
  17. package/dist/chat.js +4 -4
  18. package/dist/{context-transport-DAjjzK_x.js → context-transport-3b0qpM2m.js} +3 -3
  19. package/dist/{context-transport-DAjjzK_x.js.map → context-transport-3b0qpM2m.js.map} +1 -1
  20. package/dist/contexts/daytona.d.ts +3 -3
  21. package/dist/contexts/docker.d.ts +1 -1
  22. package/dist/contexts/e2b.d.ts +2 -2
  23. package/dist/contexts/sandbox.d.ts +1 -1
  24. package/dist/contexts-DWVt7MvF.js.map +1 -1
  25. package/dist/contexts.d.ts +3 -3
  26. package/dist/eval.d.ts +1 -1
  27. package/dist/eval.js +1 -1
  28. package/dist/extensions.d.ts +1 -1
  29. package/dist/extensions.js +1 -1
  30. package/dist/headless.d.ts +1 -1
  31. package/dist/headless.js +3 -3
  32. package/dist/{index-B9jSo84a.d.ts → index-C8TwH34B.d.ts} +2 -2
  33. package/dist/index-C8TwH34B.d.ts.map +1 -0
  34. package/dist/{index-D-DPqXxU.d.ts → index-DTIe4e1R.d.ts} +268 -10
  35. package/dist/index-DTIe4e1R.d.ts.map +1 -0
  36. package/dist/index.d.ts +5 -5
  37. package/dist/index.js +9 -9
  38. package/dist/{login-ClSY8pN6.js → login-C41DcRK-.js} +2 -2
  39. package/dist/{login-ClSY8pN6.js.map → login-C41DcRK-.js.map} +1 -1
  40. package/dist/{mcp-UIBc81Zm.js → mcp-8bEHU4WB.js} +3 -3
  41. package/dist/{mcp-UIBc81Zm.js.map → mcp-8bEHU4WB.js.map} +1 -1
  42. package/dist/mcp.d.ts +1 -1
  43. package/dist/mcp.js +1 -1
  44. package/dist/{messages-B-Zt-_Oz.js → messages-DoHRwMw-.js} +2 -2
  45. package/dist/{messages-B-Zt-_Oz.js.map → messages-DoHRwMw-.js.map} +1 -1
  46. package/dist/{openai-compat-BF1pSB_F.js → openai-compat-Bf9uMTMa.js} +60 -3
  47. package/dist/openai-compat-Bf9uMTMa.js.map +1 -0
  48. package/dist/output/stream-json.d.ts +1 -1
  49. package/dist/output/terminal.d.ts +1 -1
  50. package/dist/presets.d.ts +1 -1
  51. package/dist/presets.js +1 -1
  52. package/dist/providers/anthropic.d.ts +1 -1
  53. package/dist/providers/anthropic.js +1 -1
  54. package/dist/providers/arcee.d.ts +1 -1
  55. package/dist/providers/arcee.js +1 -1
  56. package/dist/providers/baseten.d.ts +1 -1
  57. package/dist/providers/baseten.js +4 -2
  58. package/dist/providers/baseten.js.map +1 -1
  59. package/dist/providers/cerebras.d.ts +1 -1
  60. package/dist/providers/cerebras.js +1 -1
  61. package/dist/providers/local.d.ts +1 -1
  62. package/dist/providers/local.js +1 -1
  63. package/dist/providers/openai-compat.d.ts +1 -1
  64. package/dist/providers/openai-compat.js +1 -1
  65. package/dist/providers/openai.d.ts +1 -1
  66. package/dist/providers/openai.js +2 -2
  67. package/dist/providers/openrouter.d.ts +1 -1
  68. package/dist/providers/openrouter.js +3 -1
  69. package/dist/providers/openrouter.js.map +1 -1
  70. package/dist/providers/xai.d.ts +1 -1
  71. package/dist/providers/xai.js +1 -1
  72. package/dist/{providers-C-lh3sVD.js → providers-BlaXBUwd.js} +3 -3
  73. package/dist/{providers-C-lh3sVD.js.map → providers-BlaXBUwd.js.map} +1 -1
  74. package/dist/providers.d.ts +2 -2
  75. package/dist/providers.js +5 -5
  76. package/dist/restate.d.ts +2 -2
  77. package/dist/{sandbox-DqwpKX18.d.ts → sandbox-DLY6uah7.d.ts} +2 -2
  78. package/dist/{sandbox-DqwpKX18.d.ts.map → sandbox-DLY6uah7.d.ts.map} +1 -1
  79. package/dist/session/sqlite.d.ts +1 -1
  80. package/dist/{session-BE9_p0sN.js → session-Bdk31kA5.js} +2 -2
  81. package/dist/{session-BE9_p0sN.js.map → session-Bdk31kA5.js.map} +1 -1
  82. package/dist/session.d.ts +1 -1
  83. package/dist/session.js +2 -2
  84. package/dist/skills.d.ts +1 -1
  85. package/dist/{stdio-e45MhlYy.js → stdio-BKFpPNvX.js} +2 -2
  86. package/dist/{stdio-e45MhlYy.js.map → stdio-BKFpPNvX.js.map} +1 -1
  87. package/dist/{stdio-loader-Bkrs9e8t.js → stdio-loader-DFDpBwwI.js} +2 -2
  88. package/dist/{stdio-loader-Bkrs9e8t.js.map → stdio-loader-DFDpBwwI.js.map} +1 -1
  89. package/dist/{tool-formatters-BX9TaWAr.d.ts → tool-formatters-B_dGfYqT.d.ts} +2 -2
  90. package/dist/{tool-formatters-BX9TaWAr.d.ts.map → tool-formatters-B_dGfYqT.d.ts.map} +1 -1
  91. package/dist/tools/fetch-url.d.ts +1 -1
  92. package/dist/tools/web-search.d.ts +1 -1
  93. package/dist/tools.d.ts +1 -1
  94. package/dist/tools.js +1 -1
  95. package/dist/{transcript-anchors-BpCQElT6.d.ts → transcript-anchors-CqxIlrhv.d.ts} +15 -6
  96. package/dist/transcript-anchors-CqxIlrhv.d.ts.map +1 -0
  97. package/dist/{transcript-anchors-9LKsM0OZ.js → transcript-anchors-DhBTqTyc.js} +146 -32
  98. package/dist/transcript-anchors-DhBTqTyc.js.map +1 -0
  99. package/dist/tui.d.ts +3 -3
  100. package/dist/tui.d.ts.map +1 -1
  101. package/dist/tui.js +350 -132
  102. package/dist/tui.js.map +1 -1
  103. package/dist/{turn-operations-C_bn9UmE.d.ts → turn-operations-D7JhuW_X.d.ts} +3 -3
  104. package/dist/{turn-operations-C_bn9UmE.d.ts.map → turn-operations-D7JhuW_X.d.ts.map} +1 -1
  105. package/dist/types-BIarq1qE.js.map +1 -1
  106. package/dist/{types-DdZMQxfT.d.ts → types-M2DauK2D.d.ts} +2 -2
  107. package/dist/{types-DdZMQxfT.d.ts.map → types-M2DauK2D.d.ts.map} +1 -1
  108. package/dist/types.d.ts +3 -3
  109. package/dist/{xai-CLqZQdcj.js → xai-BI4KgCGS.js} +2 -2
  110. package/dist/{xai-CLqZQdcj.js.map → xai-BI4KgCGS.js.map} +1 -1
  111. package/docs/ARCHITECTURE.md +29 -1
  112. package/docs/CHAT.md +4 -2
  113. package/docs/RUN_IN_BACKGROUND.md +2 -2
  114. package/docs/SKILL.md +1 -1
  115. package/docs/TUI.md +3 -3
  116. package/package.json +1 -1
  117. package/dist/anthropic-Drd3842n.js.map +0 -1
  118. package/dist/auth-WkdClN2Y.js.map +0 -1
  119. package/dist/index-B9jSo84a.d.ts.map +0 -1
  120. package/dist/index-D-DPqXxU.d.ts.map +0 -1
  121. package/dist/openai-compat-BF1pSB_F.js.map +0 -1
  122. package/dist/transcript-anchors-9LKsM0OZ.js.map +0 -1
  123. package/dist/transcript-anchors-BpCQElT6.d.ts.map +0 -1
package/dist/tui.js CHANGED
@@ -1,5 +1,5 @@
1
- import { $ as InteractionsProvider, $t as countClaudeSessionFiles, A as runOAuthLogin, Bt as ConfigProvider, Cn as discoverAgentsMd, Cr as toolCallPreview, Ct as clampFps, D as formatPathForCwd, Dt as resolveChipColor, Er as usageByModel, F as refreshMcpToolsCatalog, Gt as chainPositionOf, H as indexOfServerRow, Ht as resolveConfig, It as createDiscoverySlot, J as useMcpAuthDispatch, Jt as providerKeySetChanged, Kt as chainsEqual, L as subscribeMcpToolsCache, Ln as TODO_STATUS_GLYPHS, Lt as DiscoveryProvider, N as loadMcpToolsCache, O as fetchOAuthRedirect, Ot as resolveTheme, P as mcpToolsCachePath, Q as ASK_USER_TOOL, Qt as useCompletion, Rt as useDiscovery, Sn as chatAutoCompactBehavior, St as SettingsProvider, T as suggestSafelistEntries, Tr as updateToolEventOutcomes, U as parentServerName, Ut as resolveStoragePaths, V as buildVisibleMcpRows, Vt as useConfig, W as createFileMcpCredentialStore, Wn as pruneTodosByRun, Wt as buildRunModel, X as getMcpAuthStatus, Y as useMcpAuthState, Yt as restoreChainFallbacks, Zt as toggleChainEntry, _ as useSafeModeQueue, _r as marginTopFor, _t as DEFAULT_SETTINGS, a as useSurfaces, an as buildMcpServers, ar as buildPlanSystem, at as pendingInteractionsFromTurns, b as getSafelist, br as stripSpawnTokensLine, bt as SETTINGS_INPUTS, c as useStreamBuffer, cr as createChatTurnsReadTool, ct as useInteractionsQueue, d as discoverProjectSkills, dn as bootTick, dt as hintsLength, en as hasClaudeCodeSetup, et as PRESENT_PLAN_TOOL, fn as buildUpdateHint, fr as deriveSessionTitle, ft as truncateTrailing, g as useSafeModeActions, gr as loadState, gt as useEnabledToggleSet, h as SafeModeProvider, hr as listSessionMeta, ht as listProjectFiles, i as useSelectStyle, in as upsertMcpServer, ir as buildBuildSystem, it as makeRequestInteraction, j as supportsOAuth, jn as accentColor, k as oauthUsesManualCodePaste, l as buildSkillsConfig, ln as tryOpenBrowser, lt as EMPTY_HINTS, m as writeSessionExport, mr as lastContextSizeFromTurns, n as ThemeProvider, nn as mcpsWritePath, nt as createInteractionTools, or as envSection, pn as useUpdateCheck, pr as eventsFromTurns, pt as buildHints, q as McpAuthProvider, qn as useActiveTodos, qt as filterChainFallbacks, r as useColors, rn as removeMcpServer, s as useTheme, sn as discoverProjectMcps, sr as chatReferenceTokensForPrompt, st as useInteractionsActions, t as computeTurnAnchors, tn as importFromClaudeCode, tt as buildResumedToolResultsTurn, u as defaultSkillScanPaths, ur as compactSummaryEvent, ut as clipHintsToWidth, vt as SETTINGS_CATEGORIES, wr as toolResultText, wt as useSettings, x as isOnSafelist, xr as sumRunCosts, xt as SETTINGS_TOGGLES, y as addToSafelist, yr as serverToolResultSummary, yt as SETTINGS_CHOICES, z as useMcpToolToggleMap, zt as useDiscoveryOptional } from "./transcript-anchors-9LKsM0OZ.js";
2
- import { $t as registerExtensionHostModules, Et as looksBinary, G as installExtensionFromSpec, Gt as keybindingsPath, H as createExtensionPromptBridge, Ht as ensureKeybindingsFile, Kt as matchesBinding, L as summaryToTurn, Ot as createModelSearchTool, Q as discoverAndBuildExtensionRegistry, S as selectFilesFromSession, U as ExtensionInstallError, Ut as formatBindingForDisplay, Vt as KEYBINDING_KEY_COL_WIDTH, Wt as groupBindings, X as updateInstalledExtension, Y as removeInstalledExtension, Z as createExtensionHud, Zt as resolveExtensionKeybindings, _ as resolveMcpWarningsDir, _n as OUTPUT_RESERVE_TOKENS, _t as isSpawnTool, an as parseExtensionScanPathEnv, at as partitionExtensionCatalog, b as buildPostCompactAttachments, c as applyCompactSummaryCutoff, dt as basic_default, g as rehomePersistedStubs, ln as coerceExtensionConfigInput, m as cleanupPersistedSession, mn as sanitizeExtensionConfigActionPatch, nt as disposeExtensionRegistry, on as scanExtensionDirectories, ot as createExtensionHostRuntimeBridge, pn as resolveExtensionConfigValue, rn as inlineLoadedExtension, rt as filterExtensionRegistry, t as createAgent, tt as composeExtensionPreset, v as resolvePersistDir, vn as effectiveContextWindow, w as compactConversation, wt as tailTruncate, y as resolveTasksDir } from "./agent-BJuiohV_.js";
1
+ import { $t as toggleChainEntry, A as redirectUriFromAuthUrl, At as resolveTheme, Bt as useDiscovery, Cr as sumRunCosts, Ct as SETTINGS_TOGGLES, D as formatPathForCwd, Dr as updateToolEventOutcomes, Er as toolResultText, Et as useSettings, F as loadMcpToolsCache, G as parentServerName, Gt as resolveStoragePaths, Ht as ConfigProvider, I as mcpToolsCachePath, Jt as chainsEqual, K as createFileMcpCredentialStore, Kn as pruneTodosByRun, Kt as buildRunModel, L as refreshMcpToolsCatalog, M as runOAuthLogin, N as supportsOAuth, Nn as accentColor, O as fetchOAuthRedirect, Or as usageByModel, Q as getMcpAuthStatus, Rt as createDiscoverySlot, Sr as stripSpawnTokensLine, St as SETTINGS_INPUTS, T as suggestSafelistEntries, Tn as discoverAgentsMd, Tr as toolCallPreview, Tt as clampFps, U as buildVisibleMcpRows, Ut as useConfig, V as useMcpToolToggleMap, Vt as useDiscoveryOptional, W as indexOfServerRow, Wt as resolveConfig, X as useMcpAuthDispatch, Xt as providerKeySetChanged, Y as McpAuthProvider, Yn as useActiveTodos, Yt as filterChainFallbacks, Z as useMcpAuthState, Zt as restoreChainFallbacks, _ as useSafeModeQueue, _r as listSessionMeta, _t as listProjectFiles, a as useSurfaces, an as removeMcpServer, b as getSafelist, bt as SETTINGS_CATEGORIES, c as useStreamBuffer, cr as envSection, d as discoverProjectSkills, dn as tryOpenBrowser, dt as EMPTY_HINTS, en as useCompletion, et as ASK_USER_TOOL, fr as compactSummaryEvent, ft as clipHintsToWidth, g as useSafeModeActions, gr as lastContextSizeFromTurns, h as SafeModeProvider, hn as useUpdateCheck, hr as eventsFromTurns, ht as buildHints, i as useSelectStyle, in as mcpsWritePath, it as createInteractionTools, j as oauthUsesManualCodePaste, kt as resolveChipColor, l as buildSkillsConfig, ln as discoverProjectMcps, lr as chatReferenceTokensForPrompt, lt as useInteractionsActions, m as writeSessionExport, mn as buildUpdateHint, mr as deriveSessionTitle, mt as truncateTrailing, n as ThemeProvider, nn as hasClaudeCodeSetup, nt as PRESENT_PLAN_TOOL, on as upsertMcpServer, or as buildBuildSystem, ot as makeRequestInteraction, pn as bootTick, pt as hintsLength, qt as chainPositionOf, r as useColors, rn as importFromClaudeCode, rt as buildResumedToolResultsTurn, s as useTheme, sn as buildMcpServers, sr as buildPlanSystem, st as pendingInteractionsFromTurns, t as computeTurnAnchors, tn as countClaudeSessionFiles, tt as InteractionsProvider, u as defaultSkillScanPaths, ur as createChatTurnsReadTool, ut as useInteractionsQueue, vr as loadState, vt as useEnabledToggleSet, wn as chatAutoCompactBehavior, wt as SettingsProvider, x as isOnSafelist, xr as serverToolResultSummary, xt as SETTINGS_CHOICES, y as addToSafelist, yr as marginTopFor, yt as DEFAULT_SETTINGS, z as subscribeMcpToolsCache, zn as TODO_STATUS_GLYPHS, zt as DiscoveryProvider } from "./transcript-anchors-DhBTqTyc.js";
2
+ import { $ as removeInstalledExtension, At as looksBinary, C as resolveTasksDir, E as selectFilesFromSession, Gt as KEYBINDING_KEY_COL_WIDTH, Jt as groupBindings, K as createExtensionPromptBridge, Kt as ensureKeybindingsFile, Mt as createModelSearchTool, O as compactConversation, Ot as tailTruncate, S as resolveSessionDir, Sn as effectiveContextWindow, V as summaryToTurn, Xt as matchesBinding, Y as installExtensionFromSpec, Yt as keybindingsPath, _ as rehomePersistedStubs, _n as resolveExtensionConfigValue, at as composeExtensionPreset, b as resolveMcpWarningsDir, c as applyCompactSummaryCutoff, et as updateInstalledExtension, ht as basic_default, ln as parseExtensionScanPathEnv, lt as partitionExtensionCatalog, m as cleanupPersistedSession, nt as discoverAndBuildExtensionRegistry, ot as disposeExtensionRegistry, pn as coerceExtensionConfigInput, q as ExtensionInstallError, qt as formatBindingForDisplay, rn as registerExtensionHostModules, sn as inlineLoadedExtension, st as filterExtensionRegistry, t as createAgent, tn as resolveExtensionKeybindings, tt as createExtensionHud, un as scanExtensionDirectories, ut as createExtensionHostRuntimeBridge, v as resolveLegacyPersistDir, vn as sanitizeExtensionConfigActionPatch, w as buildPostCompactAttachments, x as resolvePersistDir, xn as OUTPUT_RESERVE_TOKENS, xt as isSpawnTool, y as resolveLegacySessionDir } from "./agent-Emjeo5Ad.js";
3
3
  import { a as formatDuration, c as previewLine, i as fmtTokens, l as shortId, n as compactPath, o as formatTaskStatus, r as fmtCost, s as formatTaskSummary, t as ageString } from "./format-BF745pLL.js";
4
4
  import { n as appendCapped } from "./output-cap-C9zukcCj.js";
5
5
  import { i as killProcessGroup, n as SUPPORTS_PROCESS_GROUPS, r as createProcessContext } from "./contexts-DWVt7MvF.js";
@@ -7,18 +7,18 @@ import { l as errorMessage } from "./errors-DJUxZg9b.js";
7
7
  import { M as findGitRoot } from "./resolve-BPTA1RpX.js";
8
8
  import { a as createRingBufferSink, i as createLoggingHooks, l as logFileTimestamp, n as createFileSink, o as fanoutSink, r as createLogger, s as formatLogLine, t as consoleSink } from "./logger-DBX9uYOw.js";
9
9
  import { i as sniffImageMediaType } from "./media-sniff-UKijEaiQ.js";
10
- import { g as hasAuthorizationHeader, m as McpOAuthProvider, n as connectMcpServers, s as nonOwningMcpView } from "./mcp-UIBc81Zm.js";
11
- import { x as estimateRebilledCostUsd } from "./openai-compat-BF1pSB_F.js";
10
+ import { g as hasAuthorizationHeader, m as McpOAuthProvider, n as connectMcpServers, s as nonOwningMcpView } from "./mcp-8bEHU4WB.js";
11
+ import { C as estimateRebilledCostUsd } from "./openai-compat-Bf9uMTMa.js";
12
12
  import { replaceDynamicSection } from "./prompt.js";
13
- import { a as getExtensionSessionState, c as setExtensionSessionState, i as demoteStaleRunningStatus, o as loadSession, t as createSession } from "./session-BE9_p0sN.js";
13
+ import { a as getExtensionSessionState, c as setExtensionSessionState, i as demoteStaleRunningStatus, o as loadSession, t as createSession } from "./session-Bdk31kA5.js";
14
14
  import { n as flattenTurns, r as formatTokenUsage, t as effectiveInputFromTurn } from "./stats-DAKBEKjc.js";
15
15
  import { o as withTimeout } from "./timeout-BDetbuN6.js";
16
16
  import { i as installCacheBreakLogger } from "./cache-telemetry-ehVdWsg2.js";
17
17
  import { a as generateStructuredData, c as shouldAutoGenerateTitle, f as commandBlockToEvent, g as parseCommandInvocation, h as executeCommand, n as patchInlineGhost, p as commandBlocksToContent, r as resolveInlineSuggestion, s as generateSessionTitle, t as createPathAutocompleteProvider } from "./inline-autocomplete-DC2cxprN.js";
18
18
  import { s as resolveCompletionSubmit } from "./completion-core-BRo86R_L.js";
19
19
  import { i as installSkillFromSpec, r as SkillInstallError } from "./skills-C7eIjWYm.js";
20
- import { a as ZIDANE_EXTENSION_SKILL_NAME, d as extensionScaffold, f as readSkillEditMarker, h as withZidaneSkillEditSkill, i as SKILL_EDIT_METADATA_KEY, l as editSessionMetadata, m as withZidaneExtensionSkill, o as ZIDANE_SKILL_EDIT_SKILL_NAME, t as loginMcpServer, u as extensionDirForCwd } from "./login-ClSY8pN6.js";
21
- import { C as restoreModelOptions, S as piIdOf, _ as modelOptionsFor, m as getContextWindow, p as enabledModelOptions, s as setProviderCredential, t as detectAuth, v as modelSupportsReasoning, w as renderDefaultCallbackPage } from "./auth-WkdClN2Y.js";
20
+ import { a as ZIDANE_EXTENSION_SKILL_NAME, d as extensionScaffold, f as readSkillEditMarker, h as withZidaneSkillEditSkill, i as SKILL_EDIT_METADATA_KEY, l as editSessionMetadata, m as withZidaneExtensionSkill, o as ZIDANE_SKILL_EDIT_SKILL_NAME, t as loginMcpServer, u as extensionDirForCwd } from "./login-C41DcRK-.js";
21
+ import { C as restoreModelOptions, S as piIdOf, _ as modelOptionsFor, m as getContextWindow, p as enabledModelOptions, s as setProviderCredential, t as detectAuth, v as modelSupportsReasoning, w as renderDefaultCallbackPage } from "./auth-Dptb8U5J.js";
22
22
  import { createDockerContext } from "./contexts/docker.js";
23
23
  import { createTuiStore } from "./session/sqlite.js";
24
24
  import { $ as clipDiffForRender, B as isTurnHighlighted, C as chunkMarkdownForRender, E as stripHtmlCommentsOutsideCode, F as blendHsl, G as retagUserPromptEvents, H as isVisible, I as buildLinearRamp, J as streamEventSearchText, K as selectableTurnIds, L as BASH_CALL_ID_PREFIX, O as createSkillsCompletionProvider, P as createCommandsCompletionProvider, Q as buildUnifiedDiff, R as EDIT_TOOL_NAMES, T as splitMarkdownCodeBlocks, U as isVisibleDuringSearch, V as isUserBashEvent, W as promptHistoryFromEvents, Y as turnSelectionOwnership, Z as buildContextualDiff, _ as turnContextSize, a as TOOL_DISPLAY, b as filterModelCatalog, ct as buildEditOutcomesAnnotation, d as finalizeStreamingMarkdown, dt as parseEditOutcomesFromResult, f as finalizeStreamingMarkdownForOwner, ft as resolveApprovalForPayload, g as revealEarlierTranscriptEvents, h as replaceTranscriptTurnEvents, ht as summarizeOutcomes, i as turnAsText, it as previewEditPayload, j as createFilesCompletionProvider, k as uniqueSkillNamesFromReferences, mt as stripEditOutcomesAnnotation, n as deleteTurnSafely, nt as extractEditPayload, o as displayNameFor, ot as summarizeEditPayload, p as hiddenEarlierTranscriptCount, pt as rewriteMultiEditHeader, q as selectionAfterTurnRemoval, r as truncateTurnsAt, rt as filetypeFromPath, s as formatToolCall, u as capTranscriptEvents, ut as mergeApprovalAndBodyOutcomes, v as splitPromptSegments, vt as createChatsCompletionProvider, w as normalizeMarkdownCodeFences, y as buildModelCatalog, z as isEditErrorResult } from "./turn-operations-CBdrdYiF.js";
@@ -233,17 +233,43 @@ function inCheckboxZone(event, startCol, width = 4) {
233
233
  const col = event.x - target.x;
234
234
  return col >= startCol && col < startCol + width;
235
235
  }
236
+ /**
237
+ * True when a click on a compound hint landed on its SECONDARY chord —
238
+ * `extra.key` or `extra.label` (e.g. the `/l medium fast` run inside
239
+ * `ctrl+m/l claude-opus-4-8 medium fast`). Same coordinate technique as
240
+ * {@link inCheckboxZone}: the hint must stay ONE `<text>` renderable
241
+ * because {@link hintSpans} interleaves primary and secondary spans
242
+ * (`key`, `extra.key`, ` label`, ` extra.label`), so the zones can only
243
+ * be told apart by glyph column.
244
+ *
245
+ * The leading space before `label` belongs to the primary zone; line 0 is
246
+ * pinned so a wrapped continuation line reads as primary. Degrades to
247
+ * `false` (primary semantics) when the target's geometry is unavailable.
248
+ */
249
+ function inHintExtraZone(event, hint) {
250
+ if (!hint.extra) return false;
251
+ const target = event.target;
252
+ if (!target || typeof target.x !== "number" || typeof target.y !== "number") return false;
253
+ if (event.y - target.y !== 0) return false;
254
+ const col = event.x - target.x;
255
+ const keyLen = hint.key === "" ? 0 : hint.key.length;
256
+ const extraKeyEnd = keyLen + hint.extra.key.length;
257
+ if (col >= keyLen && col < extraKeyEnd) return true;
258
+ return col >= extraKeyEnd + (hint.key === "" ? 0 : 1) + hint.label.length;
259
+ }
236
260
  /** Spans for one hint's text content — shared by hover and rest states. */
237
261
  function hintSpans(hint, COLOR, hovered) {
238
- const keyColor = hovered ? COLOR.accent : hint.keyColor ?? COLOR.warn;
239
- const labelColor = hovered ? COLOR.accent : hint.labelColor ?? COLOR.dim;
262
+ const primaryHover = hovered === "primary";
263
+ const extraHover = hovered === "extra";
264
+ const keyColor = primaryHover ? COLOR.accent : hint.keyColor ?? COLOR.warn;
265
+ const labelColor = primaryHover ? COLOR.accent : hint.labelColor ?? COLOR.dim;
240
266
  return /* @__PURE__ */ jsxs(Fragment, { children: [
241
267
  hint.key !== "" && /* @__PURE__ */ jsx$1("span", {
242
268
  fg: keyColor,
243
269
  children: hint.key
244
270
  }),
245
271
  hint.extra && /* @__PURE__ */ jsx$1("span", {
246
- fg: hovered ? COLOR.accent : hint.extra.keyColor ?? COLOR.mute,
272
+ fg: extraHover ? COLOR.accent : hint.extra.keyColor ?? COLOR.mute,
247
273
  children: hint.extra.key
248
274
  }),
249
275
  /* @__PURE__ */ jsx$1("span", {
@@ -251,33 +277,58 @@ function hintSpans(hint, COLOR, hovered) {
251
277
  children: hint.key === "" ? hint.label : ` ${hint.label}`
252
278
  }),
253
279
  hint.extra && /* @__PURE__ */ jsx$1("span", {
254
- fg: hovered ? COLOR.accent : hint.extra.labelColor ?? COLOR.dim,
280
+ fg: extraHover ? COLOR.accent : hint.extra.labelColor ?? COLOR.dim,
255
281
  children: ` ${hint.extra.label}`
256
282
  })
257
283
  ] });
258
284
  }
259
285
  /**
286
+ * Hover zone for a pointer event, or `null` when the zone under the
287
+ * pointer has no key to dispatch (an inert half must not light up).
288
+ * Split out so the render surface stays declarative and the mapping is
289
+ * testable without a renderer.
290
+ */
291
+ function hoverZoneFor(event, hint, clickable) {
292
+ if ((inHintExtraZone(event, hint) ? "extra" : "primary") === "extra") return clickable.extra ? "extra" : null;
293
+ return clickable.primary ? "primary" : null;
294
+ }
295
+ /**
260
296
  * One hint as its own `<text>` renderable — clickable when its key spec
261
297
  * parses, hover-highlighted in the theme accent. Non-parseable hints
262
298
  * render identically but inert, so decorative chips cost nothing.
299
+ *
300
+ * A compound hint (`ctrl+m/l gpt-5 high fast`) carries TWO targets in the
301
+ * one renderable: the primary pair dispatches `hint.key`, the secondary
302
+ * chord + label dispatch `extra.keySpec`. Both click and hover resolve the
303
+ * zone by glyph column ({@link inHintExtraZone}), so hovering the model id
304
+ * highlights only the model id.
263
305
  */
264
306
  function ClickableHint({ hint }) {
265
307
  const COLOR = useColors();
266
308
  const renderer = useRenderer();
267
- const [hovered, setHovered] = useState(false);
268
- if (!(parseHintKeySpec(hint.key) !== null)) return /* @__PURE__ */ jsx$1("text", {
309
+ const [hovered, setHovered] = useState(null);
310
+ const primaryClickable = parseHintKeySpec(hint.key) !== null;
311
+ const extraSpec = hint.extra ? hint.extra.keySpec ?? hint.extra.key : null;
312
+ const extraClickable = extraSpec !== null && parseHintKeySpec(extraSpec) !== null;
313
+ if (!primaryClickable && !extraClickable) return /* @__PURE__ */ jsx$1("text", {
269
314
  selectable: false,
270
- children: hintSpans(hint, COLOR, false)
315
+ children: hintSpans(hint, COLOR, null)
271
316
  });
317
+ const clickable = {
318
+ primary: primaryClickable,
319
+ extra: extraClickable
320
+ };
272
321
  return /* @__PURE__ */ jsx$1("text", {
273
322
  selectable: false,
274
323
  onMouseDown: (event) => {
275
324
  event.stopPropagation();
276
325
  event.preventDefault();
277
- dispatchHintKey(renderer, hint.key);
326
+ if (extraClickable && inHintExtraZone(event, hint)) dispatchHintKey(renderer, extraSpec);
327
+ else if (primaryClickable) dispatchHintKey(renderer, hint.key);
278
328
  },
279
- onMouseOver: () => setHovered(true),
280
- onMouseOut: () => setHovered(false),
329
+ onMouseOver: (event) => setHovered(hoverZoneFor(event, hint, clickable)),
330
+ onMouseMove: (event) => setHovered(hoverZoneFor(event, hint, clickable)),
331
+ onMouseOut: () => setHovered(null),
281
332
  children: hintSpans(hint, COLOR, hovered)
282
333
  });
283
334
  }
@@ -2280,7 +2331,8 @@ function StatusSpinner({ color }) {
2280
2331
  });
2281
2332
  }
2282
2333
  /**
2283
- * Bordered container for one run of subagent events. The box's border +
2334
+ * Bordered container for one subagent's events inside a run — concurrent
2335
+ * siblings each get their own box (see {@link partitionTranscript}). The box's border +
2284
2336
  * left padding give the visual "this is a subagent" affordance, so events
2285
2337
  * inside render with `depthOffset: 1` — a direct child of the parent
2286
2338
  * (depth 1) sits flush against the box's inner padding rather than being
@@ -2857,18 +2909,21 @@ function partitionTranscript(events, settings, searchReveal) {
2857
2909
  const groupKeys = settings.groupToolCalls && searchReveal === void 0 ? computeToolGroupKeys(visible) : null;
2858
2910
  const groups = /* @__PURE__ */ new Map();
2859
2911
  const items = [];
2860
- let run = [];
2912
+ let lanes = /* @__PURE__ */ new Map();
2861
2913
  let runPrevious;
2862
2914
  const flush = () => {
2863
- if (run.length > 0) {
2915
+ if (lanes.size === 0) return;
2916
+ let previous = runPrevious;
2917
+ for (const lane of lanes.values()) {
2864
2918
  items.push({
2865
2919
  kind: "child-run",
2866
- events: run,
2867
- previous: runPrevious
2920
+ events: lane,
2921
+ previous
2868
2922
  });
2869
- run = [];
2870
- runPrevious = void 0;
2923
+ previous = lane[lane.length - 1];
2871
2924
  }
2925
+ lanes = /* @__PURE__ */ new Map();
2926
+ runPrevious = void 0;
2872
2927
  };
2873
2928
  for (let i = 0; i < visible.length; i++) {
2874
2929
  const event = visible[i];
@@ -2890,8 +2945,10 @@ function partitionTranscript(events, settings, searchReveal) {
2890
2945
  continue;
2891
2946
  }
2892
2947
  if (!settings.hideSubagentOutput && isChild(event)) {
2893
- if (run.length === 0) runPrevious = visible[i - 1];
2894
- run.push(event);
2948
+ if (lanes.size === 0) runPrevious = visible[i - 1];
2949
+ const lane = lanes.get(event.childId ?? "");
2950
+ if (lane) lane.push(event);
2951
+ else lanes.set(event.childId ?? "", [event]);
2895
2952
  } else {
2896
2953
  flush();
2897
2954
  items.push({
@@ -3418,7 +3475,7 @@ function UserPromptBlock({ text, refs, attachments, searchQuery }) {
3418
3475
  });
3419
3476
  }
3420
3477
  /** Click-feedback delay before `[copied]` reverts to `[copy]`, in ms. */
3421
- const COPY_FEEDBACK_MS = 1200;
3478
+ const COPY_FEEDBACK_MS$1 = 1200;
3422
3479
  /**
3423
3480
  * BoxRenderable subclass that hosts a `[lang] … [copy]` header above
3424
3481
  * an inner {@link CodeRenderable} body. Returned from
@@ -3526,7 +3583,7 @@ var CodeBlockWrapper = class extends BoxRenderable {
3526
3583
  if (this.button.isDestroyed) return;
3527
3584
  this.button.content = "[copy]";
3528
3585
  this.button.fg = this.bag.colors.warn;
3529
- }, COPY_FEEDBACK_MS);
3586
+ }, COPY_FEEDBACK_MS$1);
3530
3587
  };
3531
3588
  this.header.add(this.langLabel);
3532
3589
  this.header.add(this.spacer);
@@ -9362,11 +9419,12 @@ function OptionList({ items, initialCursor, onPick }) {
9362
9419
  * budget — default 14 fits a typical modal; pass a smaller value for
9363
9420
  * less-padded containers (e.g. 6 for the auth wizard panel).
9364
9421
  */
9365
- function OAuthUrlBlock({ url, fg, maxLineWidth, chromeWidth = 14 }) {
9422
+ function OAuthUrlBlock({ url, fg, bg, maxLineWidth, chromeWidth = 14 }) {
9366
9423
  const { width: termWidth } = useTerminalDimensions();
9367
9424
  return /* @__PURE__ */ jsx$1(Fragment, { children: chunkString(url, Math.max(20, Math.min(maxLineWidth, termWidth - chromeWidth))).map((line, i) => /* @__PURE__ */ jsx$1("text", {
9368
9425
  wrapMode: "none",
9369
9426
  fg,
9427
+ ...bg ? { bg } : {},
9370
9428
  children: /* @__PURE__ */ jsx$1("a", {
9371
9429
  href: url,
9372
9430
  children: line
@@ -9384,6 +9442,45 @@ function chunkString(s, n) {
9384
9442
  /** Keystroke shown next to the open-browser button. */
9385
9443
  const OPEN_BROWSER_KEY = "ctrl+b";
9386
9444
  /**
9445
+ * Keystroke shown next to the copy-URL button. `ctrl+y` is the global
9446
+ * `openKeybindings` chord, but it can't collide here: every surface that
9447
+ * mounts this block is either a modal (`app.tsx` bails on `modal.isOpen`
9448
+ * before dispatching global bindings) or the auth screen (excluded by
9449
+ * `screen !== 'auth'`). It's also absent from OpenTUI's input key table,
9450
+ * so the focused paste field ignores it.
9451
+ */
9452
+ const COPY_URL_KEY = "ctrl+y";
9453
+ /** How long a `copied` confirmation stays on screen. */
9454
+ const COPY_FEEDBACK_MS = 4e3;
9455
+ /**
9456
+ * Paste-input capacity. OpenTUI's `InputRenderable` defaults to 1000 chars
9457
+ * and silently drops the overflow — long enough to truncate a callback URL
9458
+ * carrying a fat `code` + `state` and make a correct paste look corrupt.
9459
+ */
9460
+ const PASTE_MAX_LENGTH = 4096;
9461
+ /**
9462
+ * Copy-to-clipboard with a self-clearing "copied" flag. Every copy
9463
+ * affordance in this module shares it so the timers are cleaned up on
9464
+ * unmount in exactly one place.
9465
+ */
9466
+ function useCopyFlash() {
9467
+ const [copied, setCopied] = useState(false);
9468
+ const timer = useRef(null);
9469
+ const copy = useCallback((text) => {
9470
+ if (!writeToClipboard(text)) return;
9471
+ setCopied(true);
9472
+ if (timer.current) clearTimeout(timer.current);
9473
+ timer.current = setTimeout(setCopied, COPY_FEEDBACK_MS, false);
9474
+ }, []);
9475
+ useEffect(() => () => {
9476
+ if (timer.current) clearTimeout(timer.current);
9477
+ }, []);
9478
+ return {
9479
+ copied,
9480
+ copy
9481
+ };
9482
+ }
9483
+ /**
9387
9484
  * Device-authorization affordance (RFC 8628): the user code the user must see
9388
9485
  * on the consent screen, plus the verification URI they type it into.
9389
9486
  *
@@ -9395,19 +9492,13 @@ const OPEN_BROWSER_KEY = "ctrl+b";
9395
9492
  */
9396
9493
  function OAuthDeviceCodeBlock({ userCode, verificationUri, expiresInSeconds }) {
9397
9494
  const COLOR = useColors();
9398
- const [copied, setCopied] = useState(false);
9399
- const copyTimer = useRef(null);
9495
+ const { copied, copy } = useCopyFlash();
9400
9496
  useKeyboard((key) => {
9401
9497
  if (key.name === "u" && key.ctrl) {
9402
9498
  key.preventDefault();
9403
- setCopied(writeToClipboard(userCode));
9404
- if (copyTimer.current) clearTimeout(copyTimer.current);
9405
- copyTimer.current = setTimeout(setCopied, 4e3, false);
9499
+ copy(userCode);
9406
9500
  }
9407
9501
  });
9408
- useEffect(() => () => {
9409
- if (copyTimer.current) clearTimeout(copyTimer.current);
9410
- }, []);
9411
9502
  return /* @__PURE__ */ jsxs("box", {
9412
9503
  style: { flexDirection: "column" },
9413
9504
  children: [
@@ -9432,23 +9523,29 @@ function OAuthDeviceCodeBlock({ userCode, verificationUri, expiresInSeconds }) {
9432
9523
  });
9433
9524
  }
9434
9525
  /**
9435
- * Unified affordance for surfacing an OAuth authorization URL in the TUI:
9526
+ * Unified affordance for surfacing an OAuth authorization URL in the TUI,
9527
+ * laid out as two numbered steps so the remote-session path reads as a
9528
+ * first-class option instead of a footnote:
9436
9529
  *
9437
- * 1. A prominent OSC 8 hyperlink button — "Click here to open auth URL in
9438
- * browser". Honored by every terminal that supports clickable links
9439
- * (iTerm2, Kitty, WezTerm, Ghostty, Alacritty, …). Lands on the user's
9440
- * LOCAL terminal even over SSH because OSC 8 is interpreted client-side.
9441
- * 2. The full URL rendered greyed below, chunked into per-row hyperlinks
9442
- * via {@link OAuthUrlBlock}. Backup channel for terminals without OSC 8,
9443
- * and for users who'd rather copy via drag-select than click.
9444
- * 3. Optional paste-back input. When `paste` is provided, an `<input>`
9445
- * renders below the URL so the user can paste the FULL redirect URL
9446
- * their browser ended up at after authorizing — useful when zidane runs
9447
- * over SSH and the browser-side redirect to loopback can't reach the
9448
- * remote callback server. The caller's `onSubmit` typically pipes the
9449
- * pasted value through {@link fetchOAuthRedirect} so the in-process
9450
- * server receives the request and the OAuth promise resolves through
9451
- * the same happy path a real browser would have taken.
9530
+ * **1 — get to the consent screen.** Two real buttons (mouse-clickable
9531
+ * AND keyed): {@link OPEN_BROWSER_KEY} opens the URL via the OS handler,
9532
+ * {@link COPY_URL_KEY} copies it to the clipboard (OSC 52 → lands on the
9533
+ * user's LOCAL clipboard over SSH). Below them the URL itself sits in a
9534
+ * framed panel with its own `[copy]` button, chunked into per-row OSC 8
9535
+ * hyperlinks by {@link OAuthUrlBlock} — the backup channel for terminals
9536
+ * without clickable links and for drag-select copying.
9537
+ *
9538
+ * **2 — hand the redirect back.** When `paste` is provided, an `<input>`
9539
+ * accepts whatever the browser (or consent page) produced. This is THE
9540
+ * path for `zidane` running over SSH: the browser sits on the user's
9541
+ * laptop and cannot reach the remote box's loopback callback server, so
9542
+ * they copy the failed `http://127.0.0.1:<port>/callback?code=…` out of
9543
+ * the URL bar and paste it here. Servers that render the code on the
9544
+ * page instead of redirecting have no failed URL to copy — paste the
9545
+ * code itself; `normalizeRedirectPaste` completes it against the
9546
+ * authorization URL's `redirect_uri` + `state`. We name that exact
9547
+ * address (parsed out of the authorization URL) so there's no guessing
9548
+ * about which string to copy.
9452
9549
  *
9453
9550
  * The component owns presentation only — keyboard focus, input ref, and
9454
9551
  * the submit handler stay with the caller so the affordance composes
@@ -9456,31 +9553,40 @@ function OAuthDeviceCodeBlock({ userCode, verificationUri, expiresInSeconds }) {
9456
9553
  */
9457
9554
  function OAuthAuthBlock({ authUrl, maxLineWidth, chromeWidth = 14, paste }) {
9458
9555
  const COLOR = useColors();
9556
+ const callbackUri = redirectUriFromAuthUrl(authUrl);
9557
+ const pasteRef = paste?.inputRef;
9558
+ const pasteFocused = paste?.focused ?? false;
9559
+ useEffect(() => {
9560
+ if (pasteFocused) pasteRef?.current?.focus();
9561
+ }, [pasteFocused, pasteRef]);
9459
9562
  return /* @__PURE__ */ jsxs("box", {
9460
9563
  style: {
9461
9564
  flexDirection: "column",
9462
9565
  gap: 1
9463
9566
  },
9464
9567
  children: [
9465
- /* @__PURE__ */ jsx$1(OpenBrowserButton, { authUrl }),
9466
9568
  /* @__PURE__ */ jsxs("box", {
9467
9569
  style: { flexDirection: "column" },
9468
- children: [/* @__PURE__ */ jsx$1("text", {
9469
- fg: COLOR.mute,
9470
- children: "or copy the URL manually:"
9471
- }), /* @__PURE__ */ jsx$1(OAuthUrlBlock, {
9472
- url: authUrl,
9473
- fg: COLOR.dim,
9474
- maxLineWidth,
9475
- chromeWidth
9476
- })]
9570
+ children: [/* @__PURE__ */ jsx$1(StepHeading, {
9571
+ step: paste ? "1" : void 0,
9572
+ text: "open this URL in your browser to authorize"
9573
+ }), /* @__PURE__ */ jsx$1(ActionButtons, { authUrl })]
9574
+ }),
9575
+ /* @__PURE__ */ jsx$1(AuthUrlPanel, {
9576
+ url: authUrl,
9577
+ maxLineWidth,
9578
+ chromeWidth
9477
9579
  }),
9478
9580
  paste && /* @__PURE__ */ jsxs("box", {
9479
9581
  style: { flexDirection: "column" },
9480
9582
  children: [
9583
+ /* @__PURE__ */ jsx$1(StepHeading, {
9584
+ step: "2",
9585
+ text: "no browser on this machine? (SSH, headless, firewalled)"
9586
+ }),
9481
9587
  /* @__PURE__ */ jsx$1("text", {
9482
- fg: COLOR.mute,
9483
- children: "or — if the browser couldn't reach this machine (SSH, firewall) — paste the URL it tried to redirect to:"
9588
+ fg: COLOR.dim,
9589
+ children: callbackUri ? `after you authorize, the browser lands on ${callbackUri} and fails — copy that whole address from its URL bar and paste it below.` : "after you authorize, copy the address the browser failed to reach (it starts with http://127.0.0.1) and paste it below."
9484
9590
  }),
9485
9591
  paste.hint && /* @__PURE__ */ jsx$1("text", {
9486
9592
  fg: toneColor(paste.hint.tone, COLOR),
@@ -9497,7 +9603,8 @@ function OAuthAuthBlock({ authUrl, maxLineWidth, chromeWidth = 14, paste }) {
9497
9603
  children: /* @__PURE__ */ jsx$1("input", {
9498
9604
  ref: paste.inputRef,
9499
9605
  focused: paste.focused,
9500
- placeholder: paste.placeholder ?? "paste redirect URL and press enter…",
9606
+ maxLength: PASTE_MAX_LENGTH,
9607
+ placeholder: paste.placeholder ?? "paste redirect URL (or just the code) and press enter…",
9501
9608
  onSubmit: paste.onSubmit,
9502
9609
  style: { flexGrow: 1 }
9503
9610
  })
@@ -9508,76 +9615,181 @@ function OAuthAuthBlock({ authUrl, maxLineWidth, chromeWidth = 14, paste }) {
9508
9615
  });
9509
9616
  }
9510
9617
  /**
9511
- * Real button: a bordered, brand-colored box hosting a labeled keystroke
9512
- * hint. Pressing `ctrl+b` (anywhere this block is mounted) calls
9513
- * {@link tryOpenBrowser} to launch the URL via the OS handler AND
9514
- * {@link writeToClipboard} so the URL also lands in the user's clipboard
9515
- * (OSC 52 → works over SSH, native helper → works locally). Both fire
9516
- * because either alone can fail silently — `open`/`xdg-open` are no-ops
9517
- * on a headless box, and OSC 52 is disabled on some terminals — and the
9518
- * combination covers both failure modes without prompting again.
9618
+ * Numbered step label. The numeral is dropped when the block has only one
9619
+ * step (no paste input) — "1" with no "2" reads like a truncated list.
9620
+ */
9621
+ function StepHeading({ step, text }) {
9622
+ const COLOR = useColors();
9623
+ return /* @__PURE__ */ jsxs("text", { children: [step !== void 0 && /* @__PURE__ */ jsx$1("span", {
9624
+ fg: COLOR.brand,
9625
+ attributes: 1,
9626
+ children: `${step} `
9627
+ }), /* @__PURE__ */ jsx$1("span", {
9628
+ fg: COLOR.mute,
9629
+ children: text
9630
+ })] });
9631
+ }
9632
+ /**
9633
+ * The two primary buttons, side by side: open-in-browser and copy-URL.
9634
+ *
9635
+ * Both are bordered boxes with a labeled keystroke, both are clickable
9636
+ * (`onMouseDown`), and both keys work regardless of which input owns focus
9637
+ * (`preventDefault` keeps the focused field from double-handling). Splitting
9638
+ * copy out of open matters over SSH: `open`/`xdg-open` on a headless remote
9639
+ * box does nothing, and the user's real intent is "give me the URL".
9519
9640
  *
9520
- * The visible label is also wrapped in an OSC 8 hyperlink so a mouse
9521
- * click in a supporting terminal (iTerm2, Kitty, WezTerm, …) reaches the
9522
- * same browser. The keybind is the authoritative path — it doesn't
9523
- * depend on terminal capability — and the hyperlink is the bonus.
9641
+ * The open button's label is additionally wrapped in an OSC 8 hyperlink so
9642
+ * a click in a terminal that supports links (iTerm2, Kitty, WezTerm, …)
9643
+ * reaches the browser on the user's LOCAL machine even over SSH.
9524
9644
  */
9525
- function OpenBrowserButton({ authUrl }) {
9645
+ function ActionButtons({ authUrl }) {
9526
9646
  const COLOR = useColors();
9527
- const [feedback, setFeedback] = useState(null);
9528
- const feedbackTimer = useRef(null);
9529
- const trigger = useCallback(() => {
9647
+ const [opened, setOpened] = useState(false);
9648
+ const openedTimer = useRef(null);
9649
+ const { copied, copy } = useCopyFlash();
9650
+ const open = useCallback(() => {
9530
9651
  tryOpenBrowser(authUrl);
9531
- const copied = writeToClipboard(authUrl);
9532
- setFeedback(copied ? "opened in browser · URL copied to clipboard" : "opened in browser");
9533
- if (feedbackTimer.current) clearTimeout(feedbackTimer.current);
9534
- feedbackTimer.current = setTimeout(setFeedback, 4e3, null);
9652
+ setOpened(true);
9653
+ if (openedTimer.current) clearTimeout(openedTimer.current);
9654
+ openedTimer.current = setTimeout(setOpened, COPY_FEEDBACK_MS, false);
9535
9655
  }, [authUrl]);
9656
+ useEffect(() => () => {
9657
+ if (openedTimer.current) clearTimeout(openedTimer.current);
9658
+ }, []);
9536
9659
  useKeyboard((key) => {
9537
- if (key.ctrl && key.name === "b") {
9660
+ if (!key.ctrl) return;
9661
+ if (key.name === "b") {
9538
9662
  key.preventDefault();
9539
- trigger();
9663
+ open();
9664
+ return;
9665
+ }
9666
+ if (key.name === "y") {
9667
+ key.preventDefault();
9668
+ copy(authUrl);
9540
9669
  }
9541
9670
  });
9542
- useEffect(() => () => {
9543
- if (feedbackTimer.current) clearTimeout(feedbackTimer.current);
9544
- }, []);
9545
9671
  return /* @__PURE__ */ jsxs("box", {
9546
9672
  style: { flexDirection: "column" },
9547
- children: [/* @__PURE__ */ jsx$1("box", {
9673
+ children: [/* @__PURE__ */ jsxs("box", {
9674
+ style: {
9675
+ flexDirection: "row",
9676
+ gap: 2,
9677
+ flexWrap: "wrap"
9678
+ },
9679
+ children: [/* @__PURE__ */ jsx$1(Button, {
9680
+ color: COLOR.brand,
9681
+ chord: OPEN_BROWSER_KEY,
9682
+ onPress: open,
9683
+ children: /* @__PURE__ */ jsx$1("a", {
9684
+ href: authUrl,
9685
+ fg: COLOR.brand,
9686
+ children: "↗ Open in browser"
9687
+ })
9688
+ }), /* @__PURE__ */ jsx$1(Button, {
9689
+ color: COLOR.warn,
9690
+ chord: COPY_URL_KEY,
9691
+ onPress: () => copy(authUrl),
9692
+ children: /* @__PURE__ */ jsx$1("span", {
9693
+ fg: COLOR.warn,
9694
+ children: "⧉ Copy URL"
9695
+ })
9696
+ })]
9697
+ }), (opened || copied) && /* @__PURE__ */ jsx$1("text", {
9698
+ fg: COLOR.accent,
9699
+ children: `✓ ${[opened ? "opened in browser" : null, copied ? "URL copied to clipboard" : null].filter(Boolean).join(" · ")}`
9700
+ })]
9701
+ });
9702
+ }
9703
+ /**
9704
+ * Bordered, clickable button hosting a label plus its keystroke hint.
9705
+ * The handler lives on the outer box: mouse events bubble from whatever
9706
+ * renderable was hit (label, padding, border) up through ancestors, so
9707
+ * the whole framed area is the target — not just the label text. Same
9708
+ * pattern as the settings-modal rows (`settings-modal.tsx:4006`).
9709
+ */
9710
+ function Button({ color, chord, onPress, children }) {
9711
+ const COLOR = useColors();
9712
+ return /* @__PURE__ */ jsx$1("box", {
9713
+ style: {
9714
+ border: true,
9715
+ borderColor: color,
9716
+ paddingLeft: 1,
9717
+ paddingRight: 1,
9718
+ alignSelf: "flex-start"
9719
+ },
9720
+ onMouseDown: (event) => {
9721
+ event.stopPropagation();
9722
+ event.preventDefault();
9723
+ onPress();
9724
+ },
9725
+ children: /* @__PURE__ */ jsxs("text", {
9726
+ wrapMode: "none",
9727
+ selectable: false,
9728
+ children: [
9729
+ children,
9730
+ /* @__PURE__ */ jsx$1("span", {
9731
+ fg: COLOR.mute,
9732
+ children: " "
9733
+ }),
9734
+ /* @__PURE__ */ jsx$1("span", {
9735
+ fg: COLOR.warn,
9736
+ children: chord
9737
+ })
9738
+ ]
9739
+ })
9740
+ });
9741
+ }
9742
+ /**
9743
+ * The authorization URL as a framed, copy-able panel — same idea as a
9744
+ * fenced code block in the transcript (`auth url` label left, clickable
9745
+ * `[copy]` right), so "here is a literal string you need to move
9746
+ * somewhere else" reads the same everywhere in the app.
9747
+ *
9748
+ * Framed rather than background-tinted because this block renders inside
9749
+ * containers that are already painted with the elevated modal surface; a
9750
+ * border is the only contrast that survives every host.
9751
+ */
9752
+ function AuthUrlPanel({ url, maxLineWidth, chromeWidth }) {
9753
+ const COLOR = useColors();
9754
+ const { copied, copy } = useCopyFlash();
9755
+ return /* @__PURE__ */ jsx$1("box", {
9756
+ style: { flexDirection: "column" },
9757
+ children: /* @__PURE__ */ jsxs("box", {
9548
9758
  style: {
9759
+ flexDirection: "column",
9549
9760
  border: true,
9550
- borderColor: COLOR.brand,
9761
+ borderColor: COLOR.border,
9551
9762
  paddingLeft: 1,
9552
- paddingRight: 1,
9553
- alignSelf: "flex-start"
9763
+ paddingRight: 1
9554
9764
  },
9555
- children: /* @__PURE__ */ jsxs("text", {
9556
- wrapMode: "none",
9765
+ children: [/* @__PURE__ */ jsxs("box", {
9766
+ style: { flexDirection: "row" },
9557
9767
  children: [
9558
- /* @__PURE__ */ jsx$1("a", {
9559
- href: authUrl,
9560
- fg: COLOR.brand,
9561
- children: "↗ Open auth URL in browser"
9562
- }),
9563
- /* @__PURE__ */ jsx$1("span", {
9768
+ /* @__PURE__ */ jsx$1("text", {
9564
9769
  fg: COLOR.mute,
9565
- children: " "
9770
+ children: "auth url"
9566
9771
  }),
9567
- /* @__PURE__ */ jsx$1("span", {
9568
- fg: COLOR.warn,
9569
- children: OPEN_BROWSER_KEY
9570
- }),
9571
- /* @__PURE__ */ jsx$1("span", {
9572
- fg: COLOR.mute,
9573
- children: " open · click also works"
9772
+ /* @__PURE__ */ jsx$1("box", { style: { flexGrow: 1 } }),
9773
+ /* @__PURE__ */ jsx$1("box", {
9774
+ onMouseDown: (event) => {
9775
+ event.stopPropagation();
9776
+ event.preventDefault();
9777
+ copy(url);
9778
+ },
9779
+ children: /* @__PURE__ */ jsx$1("text", {
9780
+ selectable: false,
9781
+ fg: copied ? COLOR.accent : COLOR.warn,
9782
+ children: copied ? "[copied]" : "[copy]"
9783
+ })
9574
9784
  })
9575
9785
  ]
9576
- })
9577
- }), feedback && /* @__PURE__ */ jsx$1("text", {
9578
- fg: COLOR.accent,
9579
- children: `✓ ${feedback}`
9580
- })]
9786
+ }), /* @__PURE__ */ jsx$1(OAuthUrlBlock, {
9787
+ url,
9788
+ fg: COLOR.dim,
9789
+ maxLineWidth: maxLineWidth - 4,
9790
+ chromeWidth: chromeWidth + 4
9791
+ })]
9792
+ })
9581
9793
  });
9582
9794
  }
9583
9795
  function toneColor(tone, COLOR) {
@@ -9596,7 +9808,9 @@ function toneColor(tone, COLOR) {
9596
9808
  * MCP SDK's loopback callback server resolves the OAuth promise
9597
9809
  * through the same code path a real browser-redirect would have
9598
9810
  * taken — works over SSH where the browser can't reach the remote
9599
- * callback server directly.
9811
+ * callback server directly. `authUrl` rides along so a partial paste
9812
+ * (bare code, query string) is completed against the callback URI
9813
+ * the authorization request was minted with.
9600
9814
  *
9601
9815
  * `inputFocused` is forwarded as the input's `focused` prop AND read by
9602
9816
  * the parent picker's `useKeyboard` (via a ref) to suppress single-key
@@ -9616,7 +9830,7 @@ function McpAuthorizingPanel({ serverName, authUrl, maxLineWidth, chromeWidth, i
9616
9830
  });
9617
9831
  (async () => {
9618
9832
  try {
9619
- const result = await fetchOAuthRedirect(value);
9833
+ const result = await fetchOAuthRedirect(value, { ...authUrl ? { authUrl } : {} });
9620
9834
  if (result.status >= 200 && result.status < 300) setHint({
9621
9835
  text: "redirect accepted — finalizing…",
9622
9836
  tone: "accent"
@@ -9632,7 +9846,7 @@ function McpAuthorizingPanel({ serverName, authUrl, maxLineWidth, chromeWidth, i
9632
9846
  });
9633
9847
  }
9634
9848
  })();
9635
- }, []);
9849
+ }, [authUrl]);
9636
9850
  return /* @__PURE__ */ jsxs("box", {
9637
9851
  style: {
9638
9852
  flexDirection: "column",
@@ -9651,13 +9865,12 @@ function McpAuthorizingPanel({ serverName, authUrl, maxLineWidth, chromeWidth, i
9651
9865
  /* @__PURE__ */ jsx$1(Spinner, { label: status }),
9652
9866
  authUrl !== null && /* @__PURE__ */ jsx$1(OAuthAuthBlock, {
9653
9867
  authUrl,
9654
- maxLineWidth,
9868
+ maxLineWidth: maxLineWidth - 4,
9655
9869
  chromeWidth: (chromeWidth ?? 14) + 4,
9656
9870
  paste: {
9657
9871
  inputRef,
9658
9872
  focused: inputFocused,
9659
9873
  onSubmit,
9660
- placeholder: "paste redirect URL and press enter…",
9661
9874
  hint: hint?.tone === "error" ? hint : void 0
9662
9875
  }
9663
9876
  }),
@@ -10470,7 +10683,7 @@ function OAuthRunningStep({ descriptor, dataDir, onSuccess, onError }) {
10470
10683
  });
10471
10684
  (async () => {
10472
10685
  try {
10473
- const result = await fetchOAuthRedirect(value);
10686
+ const result = await fetchOAuthRedirect(value, { ...url ? { authUrl: url } : {} });
10474
10687
  if (result.status >= 200 && result.status < 300) {
10475
10688
  setPasteHint({
10476
10689
  text: "redirect accepted — exchanging code…",
@@ -10488,7 +10701,7 @@ function OAuthRunningStep({ descriptor, dataDir, onSuccess, onError }) {
10488
10701
  });
10489
10702
  }
10490
10703
  })();
10491
- }, []);
10704
+ }, [url]);
10492
10705
  return /* @__PURE__ */ jsxs(WizardPanel, {
10493
10706
  title: `configure ${descriptor.label} — OAuth`,
10494
10707
  children: [
@@ -22111,13 +22324,19 @@ function AppShell() {
22111
22324
  store,
22112
22325
  ...source.projectRoot ? { projectRoot: source.projectRoot } : {}
22113
22326
  });
22114
- const rehomed = await rehomePersistedStubs(slice, resolvePersistDir({
22327
+ const forkPersistDir = resolvePersistDir({
22328
+ userDir: cacheDir,
22329
+ sessionId: fork.id
22330
+ });
22331
+ let rehomed = await rehomePersistedStubs(slice, resolvePersistDir({
22115
22332
  userDir: cacheDir,
22116
22333
  sessionId: source.id
22117
- }), resolvePersistDir({
22334
+ }), forkPersistDir);
22335
+ const legacyPersistDir = resolveLegacyPersistDir({
22118
22336
  userDir: cacheDir,
22119
- sessionId: fork.id
22120
- }));
22337
+ sessionId: source.id
22338
+ });
22339
+ if (legacyPersistDir !== void 0) rehomed = await rehomePersistedStubs(rehomed, legacyPersistDir, forkPersistDir);
22121
22340
  fork.setTurns(rehomed);
22122
22341
  fork.setRuns(inheritedRuns);
22123
22342
  try {
@@ -22216,18 +22435,17 @@ function AppShell() {
22216
22435
  debugLog("delete session failed", err);
22217
22436
  return;
22218
22437
  }
22219
- cleanupPersistedSession(resolvePersistDir({
22438
+ cleanupPersistedSession(resolveSessionDir({
22220
22439
  userDir: cacheDir,
22221
22440
  sessionId: id
22222
22441
  }));
22223
- cleanupPersistedSession(resolveTasksDir({
22442
+ for (const stale of [resolveLegacyPersistDir({
22224
22443
  userDir: cacheDir,
22225
22444
  sessionId: id
22226
- }));
22227
- cleanupPersistedSession(resolveMcpWarningsDir({
22445
+ }), resolveLegacySessionDir({
22228
22446
  userDir: cacheDir,
22229
22447
  sessionId: id
22230
- }));
22448
+ })]) if (stale !== void 0) cleanupPersistedSession(stale);
22231
22449
  const wasCurrent = id === currentSession?.id;
22232
22450
  if (wasCurrent) {
22233
22451
  await teardown();