zidane 7.0.14 → 7.0.15

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 (111) hide show
  1. package/dist/{acp-CZpX91YE.js → acp-Bw2b_kka.js} +3 -3
  2. package/dist/{acp-CZpX91YE.js.map → acp-Bw2b_kka.js.map} +1 -1
  3. package/dist/acp-cli.js +4 -4
  4. package/dist/acp.d.ts +1 -1
  5. package/dist/acp.js +1 -1
  6. package/dist/{agent-B1jsFaDq.js → agent-B_uQo4f1.js} +649 -48
  7. package/dist/agent-B_uQo4f1.js.map +1 -0
  8. package/dist/agent.d.ts +1 -1
  9. package/dist/agent.js +1 -1
  10. package/dist/{anthropic-Da39cE-d.js → anthropic-QbHqqTkV.js} +2 -2
  11. package/dist/{anthropic-Da39cE-d.js.map → anthropic-QbHqqTkV.js.map} +1 -1
  12. package/dist/{approvals-S100xf1d.js → approvals-B9aIQJRQ.js} +287 -45
  13. package/dist/approvals-B9aIQJRQ.js.map +1 -0
  14. package/dist/artifacts-CV2hUKnL.js +112 -0
  15. package/dist/artifacts-CV2hUKnL.js.map +1 -0
  16. package/dist/{auth-DfEWnkC7.js → auth-plw6npda.js} +19 -7
  17. package/dist/auth-plw6npda.js.map +1 -0
  18. package/dist/chat/pure.d.ts +3 -3
  19. package/dist/chat/pure.js +1 -1
  20. package/dist/chat.d.ts +6 -6
  21. package/dist/chat.js +5 -5
  22. package/dist/contexts/daytona.d.ts +1 -1
  23. package/dist/contexts/e2b.d.ts +1 -1
  24. package/dist/daemon-cli.js +1 -1
  25. package/dist/eval.d.ts +1 -1
  26. package/dist/eval.js +1 -1
  27. package/dist/extensions.d.ts +2 -2
  28. package/dist/extensions.js +2 -2
  29. package/dist/headless.d.ts +1 -1
  30. package/dist/headless.js +2 -2
  31. package/dist/{index-DFGECPfm.d.ts → index-Dc_zYpEu.d.ts} +351 -2
  32. package/dist/index-Dc_zYpEu.d.ts.map +1 -0
  33. package/dist/{index-DP7xYXr9.d.ts → index-Df2CH6bn.d.ts} +92 -7
  34. package/dist/index-Df2CH6bn.d.ts.map +1 -0
  35. package/dist/index.d.ts +2 -2
  36. package/dist/index.js +7 -6
  37. package/dist/{ipc-BLLscoIl.js → ipc-T_pPnHkp.js} +2 -2
  38. package/dist/{ipc-BLLscoIl.js.map → ipc-T_pPnHkp.js.map} +1 -1
  39. package/dist/ipc.d.ts +54 -6
  40. package/dist/ipc.d.ts.map +1 -1
  41. package/dist/ipc.js +3 -3
  42. package/dist/mcp.d.ts +1 -1
  43. package/dist/{oauth-C4GvY_p1.js → oauth-EIBUuSDK.js} +2 -2
  44. package/dist/{oauth-C4GvY_p1.js.map → oauth-EIBUuSDK.js.map} +1 -1
  45. package/dist/output/stream-json.d.ts +1 -1
  46. package/dist/output/terminal.d.ts +1 -1
  47. package/dist/plugins.d.ts +1 -1
  48. package/dist/presets.d.ts +2 -2
  49. package/dist/presets.js +2 -2
  50. package/dist/providers/anthropic.d.ts +1 -1
  51. package/dist/providers/anthropic.js +1 -1
  52. package/dist/providers/arcee.d.ts +1 -1
  53. package/dist/providers/baseten.d.ts +1 -1
  54. package/dist/providers/cerebras.d.ts +1 -1
  55. package/dist/providers/local.d.ts +1 -1
  56. package/dist/providers/openai-compat.d.ts +1 -1
  57. package/dist/providers/openai.d.ts +1 -1
  58. package/dist/providers/openai.js +1 -1
  59. package/dist/providers/openrouter.d.ts +1 -1
  60. package/dist/providers/xai.d.ts +1 -1
  61. package/dist/providers/xai.js +2 -2
  62. package/dist/{providers-BYgyPb15.js → providers-CoK1rZ1p.js} +2 -2
  63. package/dist/{providers-BYgyPb15.js.map → providers-CoK1rZ1p.js.map} +1 -1
  64. package/dist/providers.d.ts +1 -1
  65. package/dist/providers.js +3 -3
  66. package/dist/restate.d.ts +1 -1
  67. package/dist/restate.js +1 -1
  68. package/dist/{server-C8gULJ0d.js → server-DXM8ZbGi.js} +64 -6
  69. package/dist/server-DXM8ZbGi.js.map +1 -0
  70. package/dist/session/sqlite.d.ts +1 -1
  71. package/dist/session/sqlite.d.ts.map +1 -1
  72. package/dist/session/sqlite.js +117 -1
  73. package/dist/session/sqlite.js.map +1 -1
  74. package/dist/{session-K3icbPH9.js → session-DPZnncaQ.js} +60 -1
  75. package/dist/session-DPZnncaQ.js.map +1 -0
  76. package/dist/session.d.ts +2 -2
  77. package/dist/session.js +3 -2
  78. package/dist/skills.d.ts +1 -1
  79. package/dist/{tool-formatters-C-s_-u0v.d.ts → tool-formatters-BgdOYAy9.d.ts} +2 -2
  80. package/dist/{tool-formatters-C-s_-u0v.d.ts.map → tool-formatters-BgdOYAy9.d.ts.map} +1 -1
  81. package/dist/tools/fetch-url.d.ts +1 -1
  82. package/dist/tools/web-search.d.ts +1 -1
  83. package/dist/tools.d.ts +2 -2
  84. package/dist/tools.js +2 -2
  85. package/dist/{transcript-anchors-D1ToF-EW.d.ts → transcript-anchors-CtnwuQJM.d.ts} +3 -3
  86. package/dist/transcript-anchors-CtnwuQJM.d.ts.map +1 -0
  87. package/dist/tui.d.ts +3 -3
  88. package/dist/tui.d.ts.map +1 -1
  89. package/dist/tui.js +576 -102
  90. package/dist/tui.js.map +1 -1
  91. package/dist/{turn-operations-CZLsNgw-.js → turn-operations-DkJ1R0p8.js} +4 -3
  92. package/dist/turn-operations-DkJ1R0p8.js.map +1 -0
  93. package/dist/{turn-operations-Bp5-BAki.d.ts → turn-operations-khAvr7KJ.d.ts} +5 -3
  94. package/dist/turn-operations-khAvr7KJ.d.ts.map +1 -0
  95. package/dist/types.d.ts +1 -1
  96. package/dist/{xai-DILdFGzN.js → xai-CazjStOK.js} +3 -3
  97. package/dist/{xai-DILdFGzN.js.map → xai-CazjStOK.js.map} +1 -1
  98. package/docs/EXTENSIONS.md +58 -0
  99. package/docs/IPC.md +9 -0
  100. package/docs/TUI.md +2 -0
  101. package/package.json +1 -1
  102. package/dist/agent-B1jsFaDq.js.map +0 -1
  103. package/dist/approvals-S100xf1d.js.map +0 -1
  104. package/dist/auth-DfEWnkC7.js.map +0 -1
  105. package/dist/index-DFGECPfm.d.ts.map +0 -1
  106. package/dist/index-DP7xYXr9.d.ts.map +0 -1
  107. package/dist/server-C8gULJ0d.js.map +0 -1
  108. package/dist/session-K3icbPH9.js.map +0 -1
  109. package/dist/transcript-anchors-D1ToF-EW.d.ts.map +0 -1
  110. package/dist/turn-operations-Bp5-BAki.d.ts.map +0 -1
  111. package/dist/turn-operations-CZLsNgw-.js.map +0 -1
@@ -1,5 +1,5 @@
1
- import { Cu as StreamEvent, Hg as TurnUsage, Ng as ToolResultContent, Np as CacheBreakInfo, Rl as CompletionProvider, Sg as SessionTurn, gu as EditPayload, m_ as SkillConfig, mu as EditOutcome, ql as Command, t as Preset, vu as Owner, x_ as Session, zl as CompletionReference } from "./index-DFGECPfm.js";
2
- import { V as ApprovalDecision } from "./tool-formatters-C-s_-u0v.js";
1
+ import { Gg as SessionTurn, Ku as StreamEvent, L_ as SkillConfig, Lu as EditOutcome, Vu as Owner, W_ as Session, cu as CompletionReference, d_ as TurnUsage, gu as Command, n_ as ToolResultContent, nm as CacheBreakInfo, su as CompletionProvider, t as Preset, zu as EditPayload } from "./index-Dc_zYpEu.js";
2
+ import { V as ApprovalDecision } from "./tool-formatters-BgdOYAy9.js";
3
3
 
4
4
  //#region src/chat/color-gradient.d.ts
5
5
  /**
@@ -25,6 +25,8 @@ interface ChatReferenceEntry {
25
25
  runCount: number;
26
26
  projectRoot?: string;
27
27
  updatedAt: number;
28
+ /** Optional (store-dependent); shown as a hint when > 0. */
29
+ artifactCount?: number;
28
30
  }
29
31
  interface ChatReferenceData {
30
32
  entry: ChatReferenceEntry;
@@ -1181,4 +1183,4 @@ declare function countNeighbors(turnIds: readonly string[], turnId: string): {
1181
1183
  } | null;
1182
1184
  //#endregion
1183
1185
  export { CacheChipContent as $, finalizeStreamingMarkdown as A, stripEditOutcomesAnnotation as At, formatUsagePercent as B, listProjectFiles as Bt, sanitizeTodoItems as C, ResolvedApproval as Ct, MAX_TRANSCRIPT_EVENTS as D, parseEditOutcomesFromResult as Dt, useActiveTodos as E, mergeApprovalAndBodyOutcomes as Et, ProviderUsageStatus as F, FILES_TRIGGER as Ft, compactPath as G, ChatReferenceEntry as Gt, resetLabel as H, createCommandsCompletionProvider as Ht, ProviderUsageWindow as I, createFilesCompletionProvider as It, formatDuration as J, createChatsCompletionProvider as Jt, fmtCost as K, ChatReferenceScope as Kt, UsageTone as L, uniqueFilesFromReferences as Lt, turnContextSize as M, SKILLS_TRIGGER as Mt, ProviderUsageCredits as N, createSkillsCompletionProvider as Nt, TRANSCRIPT_TRIM_TO_EVENTS as O, resolveApprovalForPayload as Ot, ProviderUsageSnapshot as P, uniqueSkillNamesFromReferences as Pt, shortId as Q, formatBalance as R, FileEntry as Rt, pruneTodosByRun as S, tokenize as St, setTodosForRun as T, maskToOutcomeKinds as Tt, usageTone as U, CHATS_TRIGGER as Ut, mostConstrainedWindow as V, COMMANDS_TRIGGER as Vt, ageString as W, ChatReferenceData as Wt, formatTaskSummary as X, blendHsl as Xt, formatTaskStatus as Y, uniqueChatIdsFromReferences as Yt, previewLine as Z, buildLinearRamp as Zt, createTodoTools as _, extractEditPayload as _t, ActiveTodosState as a, DiffOp as at, isTodoTool as b, splitLines as bt, TODOREAD_TOOL as c, HunkResolution as ct, TODO_STATUS_GLYPHS as d, PreviewResult as dt, CacheChipTone as et, TODO_WRITE_COUNTS_METADATA_KEY as f, applyEditPayload as ft, TodosBag as g, computeLineDiff as gt, TodoTally as h, computeInlineDiff as ht, turnAsText as i, DiffLine as it, finalizeStreamingMarkdownForOwner as j, summarizeOutcomes as jt, capTranscriptEvents as k, rewriteMultiEditHeader as kt, TODOS_METADATA_KEY as l, InlineDiff as lt, TodoStatus as m, buildUnifiedDiff as mt, deleteTurnSafely as n, compactingLabel as nt, CreateTodoToolsOptions as o, EditHunkSummary as ot, TodoItem as p, buildContextualDiff as pt, fmtTokens as q, chatReferenceTokensFromText as qt, truncateTurnsAt as r, retryLabel as rt, DEFAULT_MAX_TODO_ITEMS as s, EditSummary as st, countNeighbors as t, cacheBreakChip as tt, TODOWRITE_TOOL as u, InlineSegment as ut, getArchivedTodosForRun as v, filetypeFromPath as vt, selectActiveTodos as w, buildEditOutcomesAnnotation as wt, pickActiveRunId as x, summarizeEditPayload as xt, getTodosForRun as y, previewEditPayload as yt, formatCredits as z, ListProjectFilesOptions as zt };
1184
- //# sourceMappingURL=turn-operations-Bp5-BAki.d.ts.map
1186
+ //# sourceMappingURL=turn-operations-khAvr7KJ.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"turn-operations-khAvr7KJ.d.ts","names":[],"sources":["../src/chat/color-gradient.ts","../src/chat/completion-chats.ts","../src/chat/completion-commands.ts","../src/chat/files-discovery.ts","../src/chat/completion-files.ts","../src/chat/completion-skills.ts","../src/chat/edit-approval.ts","../src/tools/edit-utils.ts","../src/chat/edit-diff.ts","../src/chat/footer-chips.ts","../src/chat/format.ts","../src/chat/provider-usage-format.ts","../src/chat/streaming-pure.ts","../src/chat/todos.ts","../src/chat/turn-operations.ts"],"mappings":"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"}
package/dist/types.d.ts CHANGED
@@ -1,5 +1,5 @@
1
1
  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";
2
- import { $h as McpToolHookContext, $p as ToolSpec, Ag as ToolHookContext, Bg as ToolResultVideoContent, C as SuccessfulMutationOutcome, Cg as SpawnHookContext, Eg as StreamHookContext, F_ as RemoteStoreOptions, Fg as ToolResultDocumentRefContent, Fh as AgentBehavior, Fm as OpenAIParams, Gg as toolResultToText, Gi as AgentHookMap, Gp as Provider, Hg as TurnUsage, Hh as ChildRunStats, Hs as SpawnToolOptions, Ig as ToolResultImageContent, Ih as AgentClock, Jg as ToolMap, Jp as StreamCallbacks, Kg as ToolContext, Ki as AgentHooks, Kp as ProviderCapabilities, Lg as ToolResultImageRefContent, Lh as AgentRunOptions, Ls as ChildAgent, Mg as ToolResultAudioContent, Ng as ToolResultContent, Og as ThinkingLevel, Pg as ToolResultDocumentContent, Qh as McpServerConfig, Qp as ToolResult, Rg as ToolResultMetadata, Rh as AgentStats, Rr as InteractionToolOptions, S as MutationValidationResult, S_ as SessionData, Sg as SessionTurn, T_ as SessionStore, Ug as toolOutputBudgetByteLength, Us as SpawnToolState, Vg as TurnFinishReason, Wg as toolOutputByteLength, Wi as Agent, Xg as ReadStateEntry, Yp as StreamOptions, Zg as ReadStateMap, Zp as ToolCall, _ as statsByModel, _g as RunHookMap, a_ as StagedToolState, ag as ProgressNudgeMessage, b as MutationValidationHooksOptions, bg as SessionHookContext, c_ as ToolStateStore, cg as PromptImagePart, dg as PromptVideoPart, dm as OpenRouterParams, em as TurnResult, fg as RepeatGuardConfig, g_ as SkillResource, gg as RetryConfig, h as flattenTurns, hg as ResolveContentRefBlock, ig as ProgressNudgeKind, jg as ToolOutcome, l_ as ToolStateView, lg as PromptPart, m_ as SkillConfig, mg as ResolveContentRef, mv as McpConnection, nh as AnthropicParams, o_ as ToolStateController, og as PromptAudioPart, p as ModelUsage, pg as RepeatGuardToolMatcher, qg as ToolDef, qh as DEFAULT_AGENT_CLOCK, qi as AgentOptions, qm as CerebrasParams, rg as OAuthRefreshHookContext, s_ as ToolStateOp, sg as PromptDocumentPart, t as Preset, ug as PromptTextPart, v as MutationValidationBatch, v_ as SkillsConfig, vg as SessionContentBlock, vr as ValidationResult, w_ as SessionRun, x as MutationValidationMutation, x_ as Session, xg as SessionMessage, y as MutationValidationFailure, y_ as CreateSessionOptions, yg as SessionEndStatus, zg as ToolResultTextContent } from "./index-DFGECPfm.js";
2
+ import { $g as ToolHookContext, A_ as ToolStateOp, Ag as PromptDocumentPart, Bg as RunHookMap, Br as InteractionToolOptions, C as SuccessfulMutationOutcome, Cm as TurnResult, Dg as ProgressNudgeKind, Eg as OAuthRefreshHookContext, Fg as RepeatGuardConfig, G_ as SessionData, Gg as SessionTurn, Gv as McpConnection, H_ as CreateSessionOptions, Hg as SessionEndStatus, Ig as RepeatGuardToolMatcher, J_ as SessionStore, Ji as AgentHookMap, Kg as SpawnHookContext, L_ as SkillConfig, Lg as ResolveContentRef, M_ as ToolStateView, Mg as PromptPart, Ng as PromptTextPart, O_ as StagedToolState, Og as ProgressNudgeMessage, Pg as PromptVideoPart, Pm as OpenRouterParams, Rg as ResolveContentRefBlock, S as MutationValidationResult, Sg as McpToolHookContext, Sm as ToolSpec, Th as AnthropicParams, Ug as SessionHookContext, V_ as SkillsConfig, Vg as SessionContentBlock, W_ as Session, Wg as SessionMessage, Xi as AgentOptions, Yg as StreamHookContext, Yi as AgentHooks, Zg as ThinkingLevel, _ as statsByModel, __ as ToolMap, _m as StreamCallbacks, a_ as ToolResultImageContent, ag as AgentClock, b as MutationValidationHooksOptions, b_ as ReadStateMap, bm as ToolCall, c_ as ToolResultTextContent, d_ as TurnUsage, dc as SpawnToolOptions, dg as ChildRunStats, e_ as ToolOutcome, f_ as toolOutputBudgetByteLength, fc as SpawnToolState, g_ as ToolDef, gg as DEFAULT_AGENT_CLOCK, gh as CerebrasParams, h as flattenTurns, h_ as ToolContext, hm as ProviderCapabilities, i_ as ToolResultDocumentRefContent, ig as AgentBehavior, ih as OpenAIParams, iv as RemoteStoreOptions, j_ as ToolStateStore, jg as PromptImagePart, k_ as ToolStateController, kg as PromptAudioPart, l_ as ToolResultVideoContent, m_ as toolResultToText, mm as Provider, n_ as ToolResultContent, o_ as ToolResultImageRefContent, oc as ChildAgent, og as AgentRunOptions, p as ModelUsage, p_ as toolOutputByteLength, q_ as SessionRun, qi as Agent, r_ as ToolResultDocumentContent, s_ as ToolResultMetadata, sg as AgentStats, t as Preset, t_ as ToolResultAudioContent, u_ as TurnFinishReason, v as MutationValidationBatch, vm as StreamOptions, vr as ValidationResult, x as MutationValidationMutation, xg as McpServerConfig, xm as ToolResult, y as MutationValidationFailure, y_ as ReadStateEntry, z_ as SkillResource, zg as RetryConfig } from "./index-Dc_zYpEu.js";
3
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
4
  import { t as SandboxProvider } from "./sandbox-CDl9tfTo.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 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 };
@@ -234,7 +234,7 @@ async function loginXai(renderPage, callbacks) {
234
234
  });
235
235
  const result = await server.waitForCallback(CALLBACK_TIMEOUT_MS);
236
236
  if (result.error) throw new Error(result.errorDescription ?? result.error);
237
- if (result.state !== state) throw new Error("xAI OAuth state mismatch — possible CSRF. Please retry.");
237
+ if (result.state !== state) throw new Error("xAI OAuth state mismatch (possible CSRF). Retry the login.");
238
238
  if (!result.code) throw new Error("xAI OAuth callback did not include an authorization code.");
239
239
  callbacks.onProgress?.("Exchanging authorization code for tokens...");
240
240
  return await exchangeCode(discovery.token_endpoint, result.code, server.redirectUri, verifier, discovery);
@@ -245,7 +245,7 @@ async function loginXai(renderPage, callbacks) {
245
245
  async function refreshXaiToken(credentials) {
246
246
  const xai = credentials;
247
247
  const clientId = xaiOAuthClientId();
248
- if (!credentials.refresh) throw new Error("xAI credentials are missing a refresh token — re-login required.");
248
+ if (!credentials.refresh) throw new Error("xAI credentials are missing a refresh token. Log in again.");
249
249
  const tokenEndpoint = xai.tokenEndpoint ?? xai.discovery?.token_endpoint ?? (await discover()).token_endpoint;
250
250
  validateEndpoint(tokenEndpoint, "token_endpoint");
251
251
  const response = await fetch(tokenEndpoint, {
@@ -293,4 +293,4 @@ function createXaiOAuthProvider(renderPage) {
293
293
  //#endregion
294
294
  export { xai_exports as i, createXaiOAuthProvider as n, xaiBaseUrl as r, XAI_OAUTH_PROVIDER_ID as t };
295
295
 
296
- //# sourceMappingURL=xai-DILdFGzN.js.map
296
+ //# sourceMappingURL=xai-CazjStOK.js.map
@@ -1 +1 @@
1
- {"version":3,"file":"xai-DILdFGzN.js","names":[],"sources":["../src/chat/oauth-page/xai.ts"],"sourcesContent":["/**\n * xAI Grok OAuth flow (SuperGrok / X Premium+).\n *\n * xAI is **not** built into pi-ai, so — like Cursor — we implement the flow\n * here and register it with pi-ai's OAuth registry (`registerXaiOAuthProvider`)\n * so `getOAuthApiKey('xai-oauth', …)` resolves to it during lazy token refresh.\n *\n * Auth shape: OAuth 2.0 + PKCE with a loopback callback server, against xAI's\n * OIDC issuer (`https://auth.x.ai`). Differs from the Anthropic / Codex flows\n * (which use the shared `startCallbackServer`) on three points, so it gets its\n * own login impl rather than reusing that primitive:\n *\n * 1. The `redirect_uri` must be `http://127.0.0.1:56121/callback` verbatim\n * (xAI matches the registered loopback IP, not `localhost`).\n * 2. Endpoints come from OIDC discovery, not hard-coded constants.\n * 3. Token exchange + refresh are `application/x-www-form-urlencoded`\n * (the Anthropic flow posts JSON).\n *\n * The callback page still routes through {@link OAuthCallbackPageRenderer} so\n * the post-redirect HTML matches the rest of zidane's themed flows.\n *\n * Flow + constants adapted from the public reference implementations\n * (`stnly/pi-grok`, `BlockedPath/pi-xai-oauth`) and the Hermes Agent docs.\n * xAI's OAuth surface is unofficial/undocumented — re-verify the client id,\n * scopes, and callback port if login starts failing.\n */\n\nimport type {\n OAuthCredentials,\n OAuthLoginCallbacks,\n} from '@earendil-works/pi-ai/oauth'\nimport type { IncomingMessage, ServerResponse } from 'node:http'\nimport type { OAuthProviderInterface } from '../../providers/oauth-registry'\nimport type { OAuthCallbackPageRenderer } from './render'\nimport { createServer } from 'node:http'\nimport { generatePkce } from './pkce'\n\n/** pi-ai / zidane OAuth provider id. Also the credentials-file key. */\nexport const XAI_OAUTH_PROVIDER_ID = 'xai-oauth'\n\nconst DEFAULT_BASE_URL = 'https://api.x.ai/v1'\nconst ISSUER = 'https://auth.x.ai'\nconst DISCOVERY_URL = `${ISSUER}/.well-known/openid-configuration`\n/** Public client id used by the Grok CLI OAuth surface. */\nconst DEFAULT_CLIENT_ID = 'b1a00492-073a-47ea-816f-4c329264a828'\nconst DEFAULT_SCOPE = 'openid profile email offline_access grok-cli:access api:access'\n/** xAI matches the registered loopback IP exactly — `localhost` is rejected. */\nconst DEFAULT_CALLBACK_HOST = '127.0.0.1'\nconst DEFAULT_CALLBACK_PORT = 56121\nconst CALLBACK_PATH = '/callback'\nconst PROVIDER_NAME = 'xAI Grok'\n/** Refresh slightly early so requests don't race expiry. */\nconst REFRESH_SKEW_MS = 120_000\nconst CALLBACK_TIMEOUT_MS = 180_000\n\nfunction xaiOAuthClientId(): string {\n return process.env.PI_XAI_OAUTH_CLIENT_ID || DEFAULT_CLIENT_ID\n}\n\nfunction xaiOAuthScope(): string {\n return process.env.PI_XAI_OAUTH_SCOPE || DEFAULT_SCOPE\n}\n\nfunction xaiOAuthCallbackHost(): string {\n return process.env.PI_XAI_OAUTH_CALLBACK_HOST || DEFAULT_CALLBACK_HOST\n}\n\nfunction xaiOAuthCallbackPort(): number {\n const parsed = Number.parseInt(process.env.PI_XAI_OAUTH_CALLBACK_PORT || String(DEFAULT_CALLBACK_PORT), 10)\n return Number.isFinite(parsed) && parsed > 0 ? parsed : DEFAULT_CALLBACK_PORT\n}\n\n/** Resolve the xAI API base URL (env override → default). */\nexport function xaiBaseUrl(): string {\n return (process.env.PI_XAI_BASE_URL || process.env.XAI_BASE_URL || DEFAULT_BASE_URL)\n .replace(/\\/+$/, '')\n}\n\ninterface XaiDiscovery {\n authorization_endpoint: string\n token_endpoint: string\n}\n\n/**\n * xAI OAuth credentials. Extends the base `{ access, refresh, expires }` with\n * the resolved `token_endpoint` so refresh doesn't need a second discovery\n * round-trip, plus the cached discovery doc for validation.\n */\nexport interface XaiOAuthCredentials extends OAuthCredentials {\n tokenEndpoint?: string\n discovery?: XaiDiscovery\n}\n\nfunction base64Url(bytes: Uint8Array): string {\n let binary = ''\n for (const b of bytes)\n binary += String.fromCharCode(b)\n return btoa(binary).replace(/\\+/g, '-').replace(/\\//g, '_').replace(/=+$/, '')\n}\n\nfunction randomToken(byteLength = 16): string {\n return base64Url(crypto.getRandomValues(new Uint8Array(byteLength)))\n}\n\n/**\n * Refuse any OIDC endpoint that isn't HTTPS on an xAI origin.\n *\n * The discovery doc is cached long-term in `credentials.json`. A single MITM\n * during initial login could otherwise pin a malicious `token_endpoint` that\n * receives every subsequent refresh token. Validating scheme + host keeps the\n * endpoint on `x.ai` / `*.x.ai`.\n */\nfunction validateEndpoint(value: string, field: string): string {\n let url: URL\n try {\n url = new URL(value)\n }\n catch {\n throw new Error(`xAI OAuth discovery returned an invalid ${field}: ${value}`)\n }\n if (url.protocol !== 'https:')\n throw new Error(`xAI OAuth ${field} must use HTTPS: ${value}`)\n const host = url.hostname.toLowerCase()\n if (host !== 'x.ai' && !host.endsWith('.x.ai'))\n throw new Error(`Refusing non-xAI OAuth ${field}: ${value}`)\n return url.toString()\n}\n\nasync function discover(): Promise<XaiDiscovery> {\n let response: Response\n try {\n response = await fetch(DISCOVERY_URL, {\n headers: { Accept: 'application/json' },\n signal: AbortSignal.timeout(15_000),\n })\n }\n catch (cause) {\n throw new Error(`xAI OIDC discovery failed: ${cause instanceof Error ? cause.message : String(cause)}`)\n }\n if (!response.ok)\n throw new Error(`xAI OIDC discovery returned ${response.status}`)\n\n const payload = await response.json() as Record<string, unknown>\n return {\n authorization_endpoint: validateEndpoint(String(payload.authorization_endpoint ?? ''), 'authorization_endpoint'),\n token_endpoint: validateEndpoint(String(payload.token_endpoint ?? ''), 'token_endpoint'),\n }\n}\n\ninterface CallbackResult {\n code?: string\n state?: string\n error?: string\n errorDescription?: string\n}\n\ninterface XaiCallbackServer {\n redirectUri: string\n waitForCallback: (timeoutMs: number) => Promise<CallbackResult>\n close: () => void\n}\n\n/**\n * Start the loopback callback server on `127.0.0.1:56121`. Falls back to an\n * OS-assigned port if 56121 is taken — xAI accepts any loopback port as long\n * as the `redirect_uri` we send on the authorize URL matches the listener.\n */\nasync function startXaiCallbackServer(renderPage: OAuthCallbackPageRenderer): Promise<XaiCallbackServer> {\n const callbackHost = xaiOAuthCallbackHost()\n const callbackPort = xaiOAuthCallbackPort()\n let settle: ((value: CallbackResult) => void) | undefined\n let settled = false\n const callbackPromise = new Promise<CallbackResult>((resolve) => {\n settle = (value) => {\n if (settled)\n return\n settled = true\n resolve(value)\n }\n })\n\n const server = createServer((req: IncomingMessage, res: ServerResponse) => {\n try {\n // accounts.x.ai may issue a CORS preflight against the loopback listener.\n const origin = req.headers.origin\n if (origin === 'https://accounts.x.ai' || origin === 'https://auth.x.ai') {\n res.setHeader('Access-Control-Allow-Origin', origin)\n res.setHeader('Access-Control-Allow-Methods', 'GET, OPTIONS')\n res.setHeader('Access-Control-Allow-Headers', 'Content-Type')\n res.setHeader('Access-Control-Allow-Private-Network', 'true')\n res.setHeader('Vary', 'Origin')\n }\n if (req.method === 'OPTIONS') {\n res.statusCode = 204\n res.end()\n return\n }\n\n const url = new URL(req.url ?? '/', `http://${callbackHost}`)\n if (url.pathname !== CALLBACK_PATH) {\n res.statusCode = 404\n res.end('Not found')\n return\n }\n\n const result: CallbackResult = {\n code: url.searchParams.get('code') ?? undefined,\n state: url.searchParams.get('state') ?? undefined,\n error: url.searchParams.get('error') ?? undefined,\n errorDescription: url.searchParams.get('error_description') ?? undefined,\n }\n\n res.statusCode = result.error ? 400 : 200\n res.setHeader('Content-Type', 'text/html; charset=utf-8')\n res.end(renderPage(result.error\n ? {\n kind: 'error',\n provider: PROVIDER_NAME,\n message: `${PROVIDER_NAME} authentication did not complete.`,\n details: result.errorDescription ?? result.error,\n }\n : {\n kind: 'success',\n provider: PROVIDER_NAME,\n message: 'xAI authentication completed. You can close this window.',\n }))\n settle?.(result)\n }\n catch {\n res.statusCode = 500\n res.end('Internal error')\n }\n })\n\n const listen = (port: number) => new Promise<number>((resolve, reject) => {\n server.once('error', reject)\n server.listen(port, callbackHost, () => {\n server.removeListener('error', reject)\n const addr = server.address()\n resolve(typeof addr === 'object' && addr ? addr.port : port)\n })\n })\n\n let actualPort: number\n try {\n actualPort = await listen(callbackPort)\n }\n catch {\n actualPort = await listen(0)\n }\n\n return {\n redirectUri: `http://${callbackHost}:${actualPort}${CALLBACK_PATH}`,\n waitForCallback: timeoutMs => Promise.race([\n callbackPromise,\n new Promise<CallbackResult>(resolve =>\n setTimeout(\n resolve,\n timeoutMs,\n { error: 'timeout', errorDescription: 'Timed out waiting for the xAI OAuth callback.' },\n )),\n ]),\n close: () => {\n try { server.close() }\n catch { /* already closed */ }\n },\n }\n}\n\nfunction expiryFromPayload(payload: Record<string, unknown>): number {\n const expiresIn = typeof payload.expires_in === 'number'\n ? payload.expires_in\n : Number(payload.expires_in ?? 3600)\n return Date.now() + expiresIn * 1000 - REFRESH_SKEW_MS\n}\n\nasync function exchangeCode(\n tokenEndpoint: string,\n code: string,\n redirectUri: string,\n verifier: string,\n discovery: XaiDiscovery,\n): Promise<XaiOAuthCredentials> {\n const clientId = xaiOAuthClientId()\n const response = await fetch(tokenEndpoint, {\n method: 'POST',\n headers: { 'Content-Type': 'application/x-www-form-urlencoded', 'Accept': 'application/json' },\n body: new URLSearchParams({\n grant_type: 'authorization_code',\n client_id: clientId,\n code,\n redirect_uri: redirectUri,\n code_verifier: verifier,\n }),\n signal: AbortSignal.timeout(30_000),\n })\n if (!response.ok)\n throw new Error(`xAI token exchange failed: ${response.status} ${await response.text().catch(() => '')}`)\n\n const payload = await response.json() as Record<string, unknown>\n const access = String(payload.access_token ?? '')\n const refresh = String(payload.refresh_token ?? '')\n if (!access)\n throw new Error('xAI token exchange did not return an access_token.')\n if (!refresh)\n throw new Error('xAI token exchange did not return a refresh_token.')\n\n return { access, refresh, expires: expiryFromPayload(payload), tokenEndpoint, discovery }\n}\n\nexport async function loginXai(\n renderPage: OAuthCallbackPageRenderer,\n callbacks: OAuthLoginCallbacks,\n): Promise<XaiOAuthCredentials> {\n const discovery = await discover()\n const { verifier, challenge } = await generatePkce()\n const state = randomToken()\n const nonce = randomToken()\n const server = await startXaiCallbackServer(renderPage)\n\n try {\n const authUrl = new URL(discovery.authorization_endpoint)\n authUrl.searchParams.set('response_type', 'code')\n authUrl.searchParams.set('client_id', xaiOAuthClientId())\n authUrl.searchParams.set('redirect_uri', server.redirectUri)\n authUrl.searchParams.set('scope', xaiOAuthScope())\n authUrl.searchParams.set('code_challenge', challenge)\n authUrl.searchParams.set('code_challenge_method', 'S256')\n authUrl.searchParams.set('state', state)\n authUrl.searchParams.set('nonce', nonce)\n // Opt into xAI's generic OAuth plan tier (SuperGrok / X Premium+).\n authUrl.searchParams.set('plan', 'generic')\n authUrl.searchParams.set('referrer', 'zidane')\n\n callbacks.onAuth({\n url: authUrl.toString(),\n instructions: `Sign in to xAI and approve access. If the browser is on another machine, the callback listener is ${server.redirectUri}.`,\n })\n\n const result = await server.waitForCallback(CALLBACK_TIMEOUT_MS)\n if (result.error)\n throw new Error(result.errorDescription ?? result.error)\n if (result.state !== state)\n throw new Error('xAI OAuth state mismatch — possible CSRF. Please retry.')\n if (!result.code)\n throw new Error('xAI OAuth callback did not include an authorization code.')\n\n callbacks.onProgress?.('Exchanging authorization code for tokens...')\n return await exchangeCode(discovery.token_endpoint, result.code, server.redirectUri, verifier, discovery)\n }\n finally {\n server.close()\n }\n}\n\nexport async function refreshXaiToken(credentials: OAuthCredentials): Promise<XaiOAuthCredentials> {\n const xai = credentials as XaiOAuthCredentials\n const clientId = xaiOAuthClientId()\n if (!credentials.refresh)\n throw new Error('xAI credentials are missing a refresh token — re-login required.')\n\n const tokenEndpoint = xai.tokenEndpoint\n ?? xai.discovery?.token_endpoint\n ?? (await discover()).token_endpoint\n validateEndpoint(tokenEndpoint, 'token_endpoint')\n\n const response = await fetch(tokenEndpoint, {\n method: 'POST',\n headers: { 'Content-Type': 'application/x-www-form-urlencoded', 'Accept': 'application/json' },\n body: new URLSearchParams({\n grant_type: 'refresh_token',\n client_id: clientId,\n refresh_token: credentials.refresh,\n }),\n signal: AbortSignal.timeout(30_000),\n })\n if (!response.ok)\n throw new Error(`xAI token refresh failed: ${response.status} ${await response.text().catch(() => '')}`)\n\n const payload = await response.json() as Record<string, unknown>\n const access = String(payload.access_token ?? '')\n if (!access)\n throw new Error('xAI token refresh did not return an access_token.')\n\n return {\n ...xai,\n access,\n // xAI may omit a rotated refresh token — keep the existing one.\n refresh: String(payload.refresh_token ?? credentials.refresh),\n expires: expiryFromPayload(payload),\n tokenEndpoint,\n }\n}\n\n/**\n * Build an xAI `OAuthProviderInterface`. Shape matches Anthropic / Codex /\n * Cursor so it drops into `BUILTIN_PROVIDERS` and `src/auth.ts` unchanged.\n *\n * `usesCallbackServer: true` — the loopback flow has a real callback server,\n * so the TUI doesn't need to prompt for a manual code paste.\n */\nexport function createXaiOAuthProvider(renderPage: OAuthCallbackPageRenderer): OAuthProviderInterface {\n return {\n id: XAI_OAUTH_PROVIDER_ID,\n name: 'xAI Grok (SuperGrok / X Premium+)',\n usesCallbackServer: true,\n login: callbacks => loginXai(renderPage, callbacks),\n refreshToken: refreshXaiToken,\n getApiKey: credentials => credentials.access,\n }\n}\n"],"mappings":"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"}
1
+ {"version":3,"file":"xai-CazjStOK.js","names":[],"sources":["../src/chat/oauth-page/xai.ts"],"sourcesContent":["/**\n * xAI Grok OAuth flow (SuperGrok / X Premium+).\n *\n * xAI is **not** built into pi-ai, so — like Cursor — we implement the flow\n * here and register it with pi-ai's OAuth registry (`registerXaiOAuthProvider`)\n * so `getOAuthApiKey('xai-oauth', …)` resolves to it during lazy token refresh.\n *\n * Auth shape: OAuth 2.0 + PKCE with a loopback callback server, against xAI's\n * OIDC issuer (`https://auth.x.ai`). Differs from the Anthropic / Codex flows\n * (which use the shared `startCallbackServer`) on three points, so it gets its\n * own login impl rather than reusing that primitive:\n *\n * 1. The `redirect_uri` must be `http://127.0.0.1:56121/callback` verbatim\n * (xAI matches the registered loopback IP, not `localhost`).\n * 2. Endpoints come from OIDC discovery, not hard-coded constants.\n * 3. Token exchange + refresh are `application/x-www-form-urlencoded`\n * (the Anthropic flow posts JSON).\n *\n * The callback page still routes through {@link OAuthCallbackPageRenderer} so\n * the post-redirect HTML matches the rest of zidane's themed flows.\n *\n * Flow + constants adapted from the public reference implementations\n * (`stnly/pi-grok`, `BlockedPath/pi-xai-oauth`) and the Hermes Agent docs.\n * xAI's OAuth surface is unofficial/undocumented — re-verify the client id,\n * scopes, and callback port if login starts failing.\n */\n\nimport type {\n OAuthCredentials,\n OAuthLoginCallbacks,\n} from '@earendil-works/pi-ai/oauth'\nimport type { IncomingMessage, ServerResponse } from 'node:http'\nimport type { OAuthProviderInterface } from '../../providers/oauth-registry'\nimport type { OAuthCallbackPageRenderer } from './render'\nimport { createServer } from 'node:http'\nimport { generatePkce } from './pkce'\n\n/** pi-ai / zidane OAuth provider id. Also the credentials-file key. */\nexport const XAI_OAUTH_PROVIDER_ID = 'xai-oauth'\n\nconst DEFAULT_BASE_URL = 'https://api.x.ai/v1'\nconst ISSUER = 'https://auth.x.ai'\nconst DISCOVERY_URL = `${ISSUER}/.well-known/openid-configuration`\n/** Public client id used by the Grok CLI OAuth surface. */\nconst DEFAULT_CLIENT_ID = 'b1a00492-073a-47ea-816f-4c329264a828'\nconst DEFAULT_SCOPE = 'openid profile email offline_access grok-cli:access api:access'\n/** xAI matches the registered loopback IP exactly — `localhost` is rejected. */\nconst DEFAULT_CALLBACK_HOST = '127.0.0.1'\nconst DEFAULT_CALLBACK_PORT = 56121\nconst CALLBACK_PATH = '/callback'\nconst PROVIDER_NAME = 'xAI Grok'\n/** Refresh slightly early so requests don't race expiry. */\nconst REFRESH_SKEW_MS = 120_000\nconst CALLBACK_TIMEOUT_MS = 180_000\n\nfunction xaiOAuthClientId(): string {\n return process.env.PI_XAI_OAUTH_CLIENT_ID || DEFAULT_CLIENT_ID\n}\n\nfunction xaiOAuthScope(): string {\n return process.env.PI_XAI_OAUTH_SCOPE || DEFAULT_SCOPE\n}\n\nfunction xaiOAuthCallbackHost(): string {\n return process.env.PI_XAI_OAUTH_CALLBACK_HOST || DEFAULT_CALLBACK_HOST\n}\n\nfunction xaiOAuthCallbackPort(): number {\n const parsed = Number.parseInt(process.env.PI_XAI_OAUTH_CALLBACK_PORT || String(DEFAULT_CALLBACK_PORT), 10)\n return Number.isFinite(parsed) && parsed > 0 ? parsed : DEFAULT_CALLBACK_PORT\n}\n\n/** Resolve the xAI API base URL (env override → default). */\nexport function xaiBaseUrl(): string {\n return (process.env.PI_XAI_BASE_URL || process.env.XAI_BASE_URL || DEFAULT_BASE_URL)\n .replace(/\\/+$/, '')\n}\n\ninterface XaiDiscovery {\n authorization_endpoint: string\n token_endpoint: string\n}\n\n/**\n * xAI OAuth credentials. Extends the base `{ access, refresh, expires }` with\n * the resolved `token_endpoint` so refresh doesn't need a second discovery\n * round-trip, plus the cached discovery doc for validation.\n */\nexport interface XaiOAuthCredentials extends OAuthCredentials {\n tokenEndpoint?: string\n discovery?: XaiDiscovery\n}\n\nfunction base64Url(bytes: Uint8Array): string {\n let binary = ''\n for (const b of bytes)\n binary += String.fromCharCode(b)\n return btoa(binary).replace(/\\+/g, '-').replace(/\\//g, '_').replace(/=+$/, '')\n}\n\nfunction randomToken(byteLength = 16): string {\n return base64Url(crypto.getRandomValues(new Uint8Array(byteLength)))\n}\n\n/**\n * Refuse any OIDC endpoint that isn't HTTPS on an xAI origin.\n *\n * The discovery doc is cached long-term in `credentials.json`. A single MITM\n * during initial login could otherwise pin a malicious `token_endpoint` that\n * receives every subsequent refresh token. Validating scheme + host keeps the\n * endpoint on `x.ai` / `*.x.ai`.\n */\nfunction validateEndpoint(value: string, field: string): string {\n let url: URL\n try {\n url = new URL(value)\n }\n catch {\n throw new Error(`xAI OAuth discovery returned an invalid ${field}: ${value}`)\n }\n if (url.protocol !== 'https:')\n throw new Error(`xAI OAuth ${field} must use HTTPS: ${value}`)\n const host = url.hostname.toLowerCase()\n if (host !== 'x.ai' && !host.endsWith('.x.ai'))\n throw new Error(`Refusing non-xAI OAuth ${field}: ${value}`)\n return url.toString()\n}\n\nasync function discover(): Promise<XaiDiscovery> {\n let response: Response\n try {\n response = await fetch(DISCOVERY_URL, {\n headers: { Accept: 'application/json' },\n signal: AbortSignal.timeout(15_000),\n })\n }\n catch (cause) {\n throw new Error(`xAI OIDC discovery failed: ${cause instanceof Error ? cause.message : String(cause)}`)\n }\n if (!response.ok)\n throw new Error(`xAI OIDC discovery returned ${response.status}`)\n\n const payload = await response.json() as Record<string, unknown>\n return {\n authorization_endpoint: validateEndpoint(String(payload.authorization_endpoint ?? ''), 'authorization_endpoint'),\n token_endpoint: validateEndpoint(String(payload.token_endpoint ?? ''), 'token_endpoint'),\n }\n}\n\ninterface CallbackResult {\n code?: string\n state?: string\n error?: string\n errorDescription?: string\n}\n\ninterface XaiCallbackServer {\n redirectUri: string\n waitForCallback: (timeoutMs: number) => Promise<CallbackResult>\n close: () => void\n}\n\n/**\n * Start the loopback callback server on `127.0.0.1:56121`. Falls back to an\n * OS-assigned port if 56121 is taken — xAI accepts any loopback port as long\n * as the `redirect_uri` we send on the authorize URL matches the listener.\n */\nasync function startXaiCallbackServer(renderPage: OAuthCallbackPageRenderer): Promise<XaiCallbackServer> {\n const callbackHost = xaiOAuthCallbackHost()\n const callbackPort = xaiOAuthCallbackPort()\n let settle: ((value: CallbackResult) => void) | undefined\n let settled = false\n const callbackPromise = new Promise<CallbackResult>((resolve) => {\n settle = (value) => {\n if (settled)\n return\n settled = true\n resolve(value)\n }\n })\n\n const server = createServer((req: IncomingMessage, res: ServerResponse) => {\n try {\n // accounts.x.ai may issue a CORS preflight against the loopback listener.\n const origin = req.headers.origin\n if (origin === 'https://accounts.x.ai' || origin === 'https://auth.x.ai') {\n res.setHeader('Access-Control-Allow-Origin', origin)\n res.setHeader('Access-Control-Allow-Methods', 'GET, OPTIONS')\n res.setHeader('Access-Control-Allow-Headers', 'Content-Type')\n res.setHeader('Access-Control-Allow-Private-Network', 'true')\n res.setHeader('Vary', 'Origin')\n }\n if (req.method === 'OPTIONS') {\n res.statusCode = 204\n res.end()\n return\n }\n\n const url = new URL(req.url ?? '/', `http://${callbackHost}`)\n if (url.pathname !== CALLBACK_PATH) {\n res.statusCode = 404\n res.end('Not found')\n return\n }\n\n const result: CallbackResult = {\n code: url.searchParams.get('code') ?? undefined,\n state: url.searchParams.get('state') ?? undefined,\n error: url.searchParams.get('error') ?? undefined,\n errorDescription: url.searchParams.get('error_description') ?? undefined,\n }\n\n res.statusCode = result.error ? 400 : 200\n res.setHeader('Content-Type', 'text/html; charset=utf-8')\n res.end(renderPage(result.error\n ? {\n kind: 'error',\n provider: PROVIDER_NAME,\n message: `${PROVIDER_NAME} authentication did not complete.`,\n details: result.errorDescription ?? result.error,\n }\n : {\n kind: 'success',\n provider: PROVIDER_NAME,\n message: 'xAI authentication completed. You can close this window.',\n }))\n settle?.(result)\n }\n catch {\n res.statusCode = 500\n res.end('Internal error')\n }\n })\n\n const listen = (port: number) => new Promise<number>((resolve, reject) => {\n server.once('error', reject)\n server.listen(port, callbackHost, () => {\n server.removeListener('error', reject)\n const addr = server.address()\n resolve(typeof addr === 'object' && addr ? addr.port : port)\n })\n })\n\n let actualPort: number\n try {\n actualPort = await listen(callbackPort)\n }\n catch {\n actualPort = await listen(0)\n }\n\n return {\n redirectUri: `http://${callbackHost}:${actualPort}${CALLBACK_PATH}`,\n waitForCallback: timeoutMs => Promise.race([\n callbackPromise,\n new Promise<CallbackResult>(resolve =>\n setTimeout(\n resolve,\n timeoutMs,\n { error: 'timeout', errorDescription: 'Timed out waiting for the xAI OAuth callback.' },\n )),\n ]),\n close: () => {\n try { server.close() }\n catch { /* already closed */ }\n },\n }\n}\n\nfunction expiryFromPayload(payload: Record<string, unknown>): number {\n const expiresIn = typeof payload.expires_in === 'number'\n ? payload.expires_in\n : Number(payload.expires_in ?? 3600)\n return Date.now() + expiresIn * 1000 - REFRESH_SKEW_MS\n}\n\nasync function exchangeCode(\n tokenEndpoint: string,\n code: string,\n redirectUri: string,\n verifier: string,\n discovery: XaiDiscovery,\n): Promise<XaiOAuthCredentials> {\n const clientId = xaiOAuthClientId()\n const response = await fetch(tokenEndpoint, {\n method: 'POST',\n headers: { 'Content-Type': 'application/x-www-form-urlencoded', 'Accept': 'application/json' },\n body: new URLSearchParams({\n grant_type: 'authorization_code',\n client_id: clientId,\n code,\n redirect_uri: redirectUri,\n code_verifier: verifier,\n }),\n signal: AbortSignal.timeout(30_000),\n })\n if (!response.ok)\n throw new Error(`xAI token exchange failed: ${response.status} ${await response.text().catch(() => '')}`)\n\n const payload = await response.json() as Record<string, unknown>\n const access = String(payload.access_token ?? '')\n const refresh = String(payload.refresh_token ?? '')\n if (!access)\n throw new Error('xAI token exchange did not return an access_token.')\n if (!refresh)\n throw new Error('xAI token exchange did not return a refresh_token.')\n\n return { access, refresh, expires: expiryFromPayload(payload), tokenEndpoint, discovery }\n}\n\nexport async function loginXai(\n renderPage: OAuthCallbackPageRenderer,\n callbacks: OAuthLoginCallbacks,\n): Promise<XaiOAuthCredentials> {\n const discovery = await discover()\n const { verifier, challenge } = await generatePkce()\n const state = randomToken()\n const nonce = randomToken()\n const server = await startXaiCallbackServer(renderPage)\n\n try {\n const authUrl = new URL(discovery.authorization_endpoint)\n authUrl.searchParams.set('response_type', 'code')\n authUrl.searchParams.set('client_id', xaiOAuthClientId())\n authUrl.searchParams.set('redirect_uri', server.redirectUri)\n authUrl.searchParams.set('scope', xaiOAuthScope())\n authUrl.searchParams.set('code_challenge', challenge)\n authUrl.searchParams.set('code_challenge_method', 'S256')\n authUrl.searchParams.set('state', state)\n authUrl.searchParams.set('nonce', nonce)\n // Opt into xAI's generic OAuth plan tier (SuperGrok / X Premium+).\n authUrl.searchParams.set('plan', 'generic')\n authUrl.searchParams.set('referrer', 'zidane')\n\n callbacks.onAuth({\n url: authUrl.toString(),\n instructions: `Sign in to xAI and approve access. If the browser is on another machine, the callback listener is ${server.redirectUri}.`,\n })\n\n const result = await server.waitForCallback(CALLBACK_TIMEOUT_MS)\n if (result.error)\n throw new Error(result.errorDescription ?? result.error)\n if (result.state !== state)\n throw new Error('xAI OAuth state mismatch (possible CSRF). Retry the login.')\n if (!result.code)\n throw new Error('xAI OAuth callback did not include an authorization code.')\n\n callbacks.onProgress?.('Exchanging authorization code for tokens...')\n return await exchangeCode(discovery.token_endpoint, result.code, server.redirectUri, verifier, discovery)\n }\n finally {\n server.close()\n }\n}\n\nexport async function refreshXaiToken(credentials: OAuthCredentials): Promise<XaiOAuthCredentials> {\n const xai = credentials as XaiOAuthCredentials\n const clientId = xaiOAuthClientId()\n if (!credentials.refresh)\n throw new Error('xAI credentials are missing a refresh token. Log in again.')\n\n const tokenEndpoint = xai.tokenEndpoint\n ?? xai.discovery?.token_endpoint\n ?? (await discover()).token_endpoint\n validateEndpoint(tokenEndpoint, 'token_endpoint')\n\n const response = await fetch(tokenEndpoint, {\n method: 'POST',\n headers: { 'Content-Type': 'application/x-www-form-urlencoded', 'Accept': 'application/json' },\n body: new URLSearchParams({\n grant_type: 'refresh_token',\n client_id: clientId,\n refresh_token: credentials.refresh,\n }),\n signal: AbortSignal.timeout(30_000),\n })\n if (!response.ok)\n throw new Error(`xAI token refresh failed: ${response.status} ${await response.text().catch(() => '')}`)\n\n const payload = await response.json() as Record<string, unknown>\n const access = String(payload.access_token ?? '')\n if (!access)\n throw new Error('xAI token refresh did not return an access_token.')\n\n return {\n ...xai,\n access,\n // xAI may omit a rotated refresh token — keep the existing one.\n refresh: String(payload.refresh_token ?? credentials.refresh),\n expires: expiryFromPayload(payload),\n tokenEndpoint,\n }\n}\n\n/**\n * Build an xAI `OAuthProviderInterface`. Shape matches Anthropic / Codex /\n * Cursor so it drops into `BUILTIN_PROVIDERS` and `src/auth.ts` unchanged.\n *\n * `usesCallbackServer: true` — the loopback flow has a real callback server,\n * so the TUI doesn't need to prompt for a manual code paste.\n */\nexport function createXaiOAuthProvider(renderPage: OAuthCallbackPageRenderer): OAuthProviderInterface {\n return {\n id: XAI_OAUTH_PROVIDER_ID,\n name: 'xAI Grok (SuperGrok / X Premium+)',\n usesCallbackServer: true,\n login: callbacks => loginXai(renderPage, callbacks),\n refreshToken: refreshXaiToken,\n getApiKey: credentials => credentials.access,\n }\n}\n"],"mappings":"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"}
@@ -136,6 +136,8 @@ ctx.registerSystemPromptFragment(text)
136
136
  ctx.registerContextBreakdownSection(contributor)
137
137
  ctx.registerContextPanelSection(section)
138
138
  ctx.registerToolResultMetadata(registration)
139
+ ctx.registerArtifactKind(registration)
140
+ ctx.artifacts.list(query) / ctx.artifacts.find(kind, uri)
139
141
  ctx.registerRunTransform(transform)
140
142
  ctx.registerBehaviorTransform(transform)
141
143
  ctx.registerProviderMiddleware(middleware)
@@ -162,6 +164,8 @@ ctx.registerConfigItem(categoryId?, item)
162
164
  | `registerContextBreakdownSection(fn)` | Existing low-level context panel contributor | May return a section or array of sections; async contributors are supported. |
163
165
  | `registerContextPanelSection(section)` | Richer context panel section with `id`, `title`, static rows, and/or async `load` | Normalized into the same context panel model. `registerContextBreakdownSection` remains supported. |
164
166
  | `registerToolResultMetadata(registration)` | Interpret namespaced metadata attached to tool results | Can project metadata entries into context-panel rows. |
167
+ | `registerArtifactKind({ kind, label, detect?, validateMeta?, render?, resolve? })` | Give meaning to a **session artifact kind** — a durable, typed pointer to something a session produced (PR, branch, commit, file, deployment, issue…). See [Session artifacts](#session-artifacts). | Kinds are a global namespace, first registration wins (built-ins `pull_request` / `branch` / `commit` / `file` go through the same API). `detect` rides `tool:transform` (and `child:tool:transform`) but can only APPEND to the reserved `metadata.artifacts` namespace — it never changes what the model sees. Unknown kinds always render via `label` + `uri`. |
168
+ | `ctx.artifacts.list({ scope?, kind?, since?, limit? })` / `ctx.artifacts.find(kind, uri)` | Read the artifact store: `scope: 'session'` (default), `'project'` (every session under the active project root), `'all'` | Late-bound like `ctx.sessions`: `[]` when detached or the store lacks artifact support. Live updates: `registerHook('artifact:recorded', …)` (and `child:artifact:recorded` for subagents). |
165
169
  | `registerRunTransform(fn)` | Synchronously rewrite `agent.run()` options before precondition checks | Applied in extension order. Throws are logged under `ZIDANE_DEBUG` and ignored. |
166
170
  | `registerBehaviorTransform(fn)` | Synchronously rewrite resolved behavior (`maxTurns`, retry, budgets, etc.) | Run-level `behavior` options are re-applied after transforms so explicit caller choices win. |
167
171
  | `registerProviderMiddleware(fn)` | Wrap provider instances for tracing, fallback, metrics, or policy | Runs when a provider factory is called; returning `undefined` is treated as pass-through. |
@@ -198,6 +202,13 @@ ctx.tui.setStatus('✓ indexed', { timeout: 3000 }) // transient
198
202
  ctx.tui.setBanner(['review pending: 3 files'], { timeout: 10_000 }) // block above the prompt, next to the todo indicator
199
203
  ctx.tui.setStatus(null) // clear (also wiped automatically on disposal)
200
204
 
205
+ // Artifact detail pane in the /artifacts modal (row rendering stays on the
206
+ // renderer-agnostic `registerArtifactKind({ render })`):
207
+ ctx.tui.registerArtifactRenderer({
208
+ kind: 'my-ext.deployment',
209
+ renderDetail: artifact => <DeploymentStatus url={artifact.uri} meta={artifact.meta} />,
210
+ })
211
+
201
212
  // Custom transcript rendering — view-layer only, fallback-mandatory:
202
213
  ctx.tui.registerToolRenderer({
203
214
  tools: ['shell'], // omit = offer for every tool
@@ -776,6 +787,53 @@ ctx.registerToolResultMetadata({
776
787
 
777
788
  Tool-result metadata is namespaced. The registry does not validate `schema` yet; it records the declaration so hosts and future tooling can project or validate metadata consistently.
778
789
 
790
+ ### Session artifacts
791
+
792
+ An **artifact** is a durable, typed pointer to something a session produced *outside* its transcript — a pull request, a branch, a commit, a created file, a deployment, an issue. The session stores the locator plus small metadata (`kind`, `uri`, `label`, `meta ≤ 16 KB`, `runId`/`turnId` for the transcript anchor, `projectRoot`); the content stays where it lives. Artifacts are queryable per session and per project (`/artifacts`, `/artifacts project`, ⌃⇧A), badge the sessions list, and show up in `#` completion (`#pr` → the PR URL).
793
+
794
+ Core knows nothing about what a `pull_request` is. `kind` is an open string; an extension gives it meaning:
795
+
796
+ ```ts
797
+ export default defineExtension({
798
+ name: 'coolify',
799
+ setup(ctx) {
800
+ ctx.registerArtifactKind({
801
+ kind: 'coolify.deployment', // namespace by extension; [\w.:-]+
802
+ label: 'Deployment',
803
+ // Inspect finished tool results; append artifacts. Never mutates the result.
804
+ detect: ({ tool, result, isError }) => {
805
+ if (isError || tool !== 'mcp_Coolify_deploy') return
806
+ const m = /deployment ([\w-]+)/.exec(String(result))
807
+ return m && { uri: `https://coolify.example/deployments/${m[1]}`, label: m[1], meta: { id: m[1], state: 'queued' } }
808
+ },
809
+ validateMeta: meta => typeof meta === 'object' && meta !== null && 'id' in meta, // failing → meta dropped, artifact kept
810
+ render: a => ({ title: a.label, href: a.uri, status: String((a.meta as { state?: string })?.state ?? '') }),
811
+ resolve: async a => ({ meta: { ...(a.meta as object), state: await fetchState(a) } }), // lazy, on view; never polled
812
+ })
813
+
814
+ // Optional TUI detail pane under the selected row.
815
+ ctx.tui.registerArtifactRenderer({ kind: 'coolify.deployment', renderDetail: a => <DeployLogs id={a.uri} /> })
816
+
817
+ // Read back: every deployment this project produced, across sessions.
818
+ ctx.registerCommand({
819
+ name: 'deployments',
820
+ run: async (c) => {
821
+ const rows = await ctx.artifacts.list({ scope: 'project', kind: 'coolify.deployment' })
822
+ c.emit({ text: rows.map(r => `- [${r.label}](${r.uri})`).join('\n') || '_none_' })
823
+ },
824
+ })
825
+ },
826
+ })
827
+ ```
828
+
829
+ Producers, from most to least explicit:
830
+
831
+ 1. **Tool-result metadata.** Any `tool:transform` hook may append to the reserved `metadata.artifacts` namespace (`SessionArtifactInput[]`); the loop lifts each entry into the store after the tool settles. `detect` is sugar over exactly this.
832
+ 2. **Built-in detectors** (`artifactsPreset`, composed under every TUI / daemon profile): `gh pr create` / `glab mr create` / `*create_pull_request` → `pull_request`; `git commit` → `commit`; `git switch -c` / `checkout -b` / `push -u` → `branch`; `write_file` that *creates* → `file`. Keyed on the command or tool name, never on free text.
833
+ 3. **`record_artifact` tool** — the model records what detection cannot see (a PR opened from a background shell task, a deployment URL). Same namespace, same bounds.
834
+
835
+ Semantics worth knowing: identity is `(sessionId, kind, uri)` — re-emitting refreshes `label` / `meta` and keeps the original `id` / `runId` / `turnId`; ≤ 200 artifacts per session (files evicted first); a stored artifact whose kind has no registration (extension removed) still renders as `label` + `uri` — rows are never dropped. Subagent artifacts land on the shared parent session under the child's `runId`; observe them with `child:artifact:recorded`.
836
+
779
837
  ### A keybinding + modal
780
838
 
781
839
  ```tsx
package/docs/IPC.md CHANGED
@@ -118,6 +118,14 @@ Every state change broadcasts a `HostSignal` to every connected client:
118
118
  - `interaction:request` / `interaction:resolved` — `present_plan` /
119
119
  `ask_user`, same fan-out semantics.
120
120
  - `sessions:changed` — create/update/delete, keeps session lists in sync.
121
+ - `artifact:recorded` / `artifact:changed` — the session recorded an artifact
122
+ (PR / branch / commit / file / extension kind; see EXTENSIONS.md → Session
123
+ artifacts) or one was deleted / refreshed through the host. Both are
124
+ wake-ups: windows re-query `sessions.artifacts` (one session) or
125
+ `artifacts.list` (project-wide / reverse lookup by `kind` + `uri`); writes go
126
+ through `artifacts.delete` / `artifacts.upsert` (the lazy `resolve()`
127
+ write-back). All additive — a pre-artifact daemon rejects the methods and
128
+ clients treat that as empty.
121
129
  - `work:snapshot`, `todos:snapshot`, `compacting:start` / `compacting:progress`
122
130
  / `compacting:end`, `model:fallback`, `stream:retry` / `stream:retry:cleared`,
123
131
  `cache:break` — auxiliary indicators. `compacting:progress` carries the live
@@ -350,6 +358,7 @@ known missing cabling, listed with its path forward.
350
358
  | Todos indicator + editing modal | synced | reads: `todos:snapshot` is MIRRORED into the window's session (`applyTodosSnapshot`) after a store reload for `runs` — the bag itself is never persisted, so a reload alone was always empty; `todos.get` backfills on attach. Writes: `todos.set` lands on the live agent's `Session`, and the host fans the result out. Same `chat/todo-edits` mutators on both surfaces |
351
359
  | Running work + ctrl+k cancel picker | synced | `work:snapshot` mirror; cancels via `chat.cancelWork` |
352
360
  | Context panel (ctrl+?) | synced | `sessions.contextBreakdown` RPC |
361
+ | Artifacts (`/artifacts`, ctrl+shift+a, sessions-list badge, `#pr` completion) | synced | reads are daemon-authoritative (`sessions.artifacts` / `artifacts.list`, falling back to the window's own store handle only on a pre-artifact daemon); `artifacts.delete` / `artifacts.upsert` fan out `artifact:changed`; the loop's `artifact:recorded` hook broadcasts the same-named signal, so an open modal refreshes the moment the agent opens a PR |
353
362
  | History edits (edit / delete turns via the selection modal) | synced | mutations refresh the wrapper from the store first (refused mid-run), retire the daemon's cached agent (`chat.rebuildAgent` — the next run uses the edited history), and announce via `sessions.touch` for immediate re-convergence |
354
363
  | History search (transcript + cross-session) | synced + accelerated | `history.searchSession`/`history.searchAll` run in the daemon over authoritative turns (fresh mid-run) with one LRU index cache shared by every window; local worker path remains the fallback |
355
364
  | Provider-retry indicator ("retrying n/m", parent + subagents) | synced | `stream:retry` signal; clears on resumed streaming / run end |
package/docs/TUI.md CHANGED
@@ -196,6 +196,7 @@ Every customizable shortcut routes through `<userDir>/keybindings.json` and is p
196
196
  | `ctrl+l` | `openEffortPicker` | chat (idle, model has reasoning) | open reasoning-effort picker |
197
197
  | `ctrl+s` | `enterSelectTurnMode` | chat (idle) | enter select-turn mode (transcript navigation) |
198
198
  | `ctrl+t` | `openTodos` | chat (session attached) | open the active run's `todowrite` checkpoints (read-only) |
199
+ | `ctrl+shift+a` | `openArtifacts` | chat (session attached) | open the session's artifacts — pull requests, branches, commits, created files, extension kinds (same modal as `/artifacts`; `/artifacts project` opens project scope). Kitty-protocol chord like `ctrl+shift+g`; rebind on Terminal.app |
199
200
  | `ctrl+shift+g` | `openContextPanel` | chat | open the context-usage breakdown panel (read-only). Needs a terminal with the kitty keyboard protocol to stay distinct from `ctrl+g`; rebind on legacy terminals like Terminal.app |
200
201
  | `ctrl+p` | `openUsagePanel` | chat | open the subscription usage panel (read-only): per-provider rate-limit windows and credit balances. `ctrl+r` refetches inside it. Also reachable by clicking the footer's `▰ 5h 43%` segment. Plain `ctrl+p` rather than a `ctrl+shift+…` chord, so it behaves the same on kitty and legacy terminals, and it is free in OpenTUI's textarea keymap |
201
202
  | `ctrl+y` | `openKeybindings` | non-auth | open the keybindings panel — read-only catalog grouped by surface, with `↵` to edit `~/.zidane/keybindings.json` |
@@ -746,6 +747,7 @@ src/tui/
746
747
  file-edit-approval-modal.tsx FileEditApprovalModal + SingleEditApprovalModal + MultiEditApprovalModal + UnresolvedHunkPanel + `originatorSuffix` (` · child-N` attribution). Inline-mounted in ChatScreen's transcript slot, keyed on `request.id` for force-remount on queue advance — bridges ApprovalDecision (including { kind: 'partial', mask }) to the gate via the helpers in `zidane/chat`'s `edit-approval` module.
747
748
  todo-indicator.tsx Single-line "in progress todo" badge between the queue block and the prompt (gated on `Settings.showTodoIndicator`; hides itself when nothing's in progress)
748
749
  todos-modal.tsx Read-only viewer for the active todowrite list (ctrl+t). Resolves the session-shared slot for top-level runs and the per-run slot for subagents; falls back to the archive snapshot when the live slot is empty so a just-completed batch stays visible — see CHAT.md Todos for the keying + archive contracts.
750
+ artifacts-modal.tsx `/artifacts` + ctrl+shift+a: what the session produced, grouped by kind (built-ins first, extension kinds after, unknown kinds via the `label + uri` fallback). `↵` opens the href (browser / editor), `tab` toggles session ↔ project scope, `c` copies the locator, `d` removes. Data comes through a `load` getter + `subscribe` tick (local `artifact:recorded` hook or daemon `artifact:recorded` / `artifact:changed` signals), so PRs the agent opens while the modal is up appear live; the focused row gets a one-shot lazy `resolve()` ("merged?") whose result is persisted via the store / `artifacts.upsert`. Extension `renderDetail` panes mount under the list inside an error boundary. See EXTENSIONS.md → Session artifacts.
749
751
  interaction-block.tsx InteractionBlock + plan picker + question wizard
750
752
  toggle-list-modal.tsx Generic checkbox-list modal (ToggleListModal)
751
753
  completion-popup.tsx Provider-agnostic autocomplete popover
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "zidane",
3
- "version": "7.0.14",
3
+ "version": "7.0.15",
4
4
  "description": "an agent that goes straight to the goal",
5
5
  "type": "module",
6
6
  "private": false,