zidane 7.0.13 → 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.
- package/dist/{acp-bTPYx7Sd.js → acp-Bw2b_kka.js} +3 -3
- package/dist/{acp-bTPYx7Sd.js.map → acp-Bw2b_kka.js.map} +1 -1
- package/dist/acp-cli.js +4 -4
- package/dist/acp.d.ts +1 -1
- package/dist/acp.js +1 -1
- package/dist/{agent-DRNT6DbC.js → agent-B_uQo4f1.js} +650 -49
- package/dist/agent-B_uQo4f1.js.map +1 -0
- package/dist/agent.d.ts +1 -1
- package/dist/agent.js +1 -1
- package/dist/{anthropic-Da39cE-d.js → anthropic-QbHqqTkV.js} +2 -2
- package/dist/{anthropic-Da39cE-d.js.map → anthropic-QbHqqTkV.js.map} +1 -1
- package/dist/{approvals-Ch6gmO8H.js → approvals-B9aIQJRQ.js} +287 -45
- package/dist/approvals-B9aIQJRQ.js.map +1 -0
- package/dist/artifacts-CV2hUKnL.js +112 -0
- package/dist/artifacts-CV2hUKnL.js.map +1 -0
- package/dist/{auth-Ddk-rCdG.js → auth-plw6npda.js} +19 -7
- package/dist/auth-plw6npda.js.map +1 -0
- package/dist/chat/pure.d.ts +3 -3
- package/dist/chat/pure.js +1 -1
- package/dist/chat.d.ts +6 -6
- package/dist/chat.js +5 -5
- package/dist/contexts/daytona.d.ts +1 -1
- package/dist/contexts/e2b.d.ts +1 -1
- package/dist/daemon-cli.js +1 -1
- package/dist/eval.d.ts +1 -1
- package/dist/eval.js +1 -1
- package/dist/extensions.d.ts +2 -2
- package/dist/extensions.js +2 -2
- package/dist/headless.d.ts +1 -1
- package/dist/headless.js +2 -2
- package/dist/{index-DFGECPfm.d.ts → index-Dc_zYpEu.d.ts} +351 -2
- package/dist/index-Dc_zYpEu.d.ts.map +1 -0
- package/dist/{index-DP7xYXr9.d.ts → index-Df2CH6bn.d.ts} +92 -7
- package/dist/index-Df2CH6bn.d.ts.map +1 -0
- package/dist/index.d.ts +2 -2
- package/dist/index.js +7 -6
- package/dist/{ipc-C8MCEu1E.js → ipc-T_pPnHkp.js} +2 -2
- package/dist/{ipc-C8MCEu1E.js.map → ipc-T_pPnHkp.js.map} +1 -1
- package/dist/ipc.d.ts +54 -6
- package/dist/ipc.d.ts.map +1 -1
- package/dist/ipc.js +3 -3
- package/dist/mcp.d.ts +1 -1
- package/dist/{oauth-C4GvY_p1.js → oauth-EIBUuSDK.js} +2 -2
- package/dist/{oauth-C4GvY_p1.js.map → oauth-EIBUuSDK.js.map} +1 -1
- package/dist/output/stream-json.d.ts +1 -1
- package/dist/output/terminal.d.ts +1 -1
- package/dist/plugins.d.ts +1 -1
- package/dist/presets.d.ts +2 -2
- package/dist/presets.js +2 -2
- package/dist/providers/anthropic.d.ts +1 -1
- package/dist/providers/anthropic.js +1 -1
- package/dist/providers/arcee.d.ts +1 -1
- package/dist/providers/baseten.d.ts +1 -1
- package/dist/providers/cerebras.d.ts +1 -1
- package/dist/providers/local.d.ts +1 -1
- package/dist/providers/openai-compat.d.ts +1 -1
- package/dist/providers/openai.d.ts +1 -1
- package/dist/providers/openai.js +1 -1
- package/dist/providers/openrouter.d.ts +1 -1
- package/dist/providers/xai.d.ts +1 -1
- package/dist/providers/xai.js +2 -2
- package/dist/{providers-BYgyPb15.js → providers-CoK1rZ1p.js} +2 -2
- package/dist/{providers-BYgyPb15.js.map → providers-CoK1rZ1p.js.map} +1 -1
- package/dist/providers.d.ts +1 -1
- package/dist/providers.js +3 -3
- package/dist/restate.d.ts +1 -1
- package/dist/restate.js +1 -1
- package/dist/{server-CGmy4363.js → server-DXM8ZbGi.js} +64 -6
- package/dist/server-DXM8ZbGi.js.map +1 -0
- package/dist/session/sqlite.d.ts +1 -1
- package/dist/session/sqlite.d.ts.map +1 -1
- package/dist/session/sqlite.js +117 -1
- package/dist/session/sqlite.js.map +1 -1
- package/dist/{session-K3icbPH9.js → session-DPZnncaQ.js} +60 -1
- package/dist/session-DPZnncaQ.js.map +1 -0
- package/dist/session.d.ts +2 -2
- package/dist/session.js +3 -2
- package/dist/skills.d.ts +1 -1
- package/dist/{tool-formatters-C-s_-u0v.d.ts → tool-formatters-BgdOYAy9.d.ts} +2 -2
- package/dist/{tool-formatters-C-s_-u0v.d.ts.map → tool-formatters-BgdOYAy9.d.ts.map} +1 -1
- package/dist/tools/fetch-url.d.ts +1 -1
- package/dist/tools/web-search.d.ts +1 -1
- package/dist/tools.d.ts +2 -2
- package/dist/tools.js +2 -2
- package/dist/{transcript-anchors-D1ToF-EW.d.ts → transcript-anchors-CtnwuQJM.d.ts} +3 -3
- package/dist/transcript-anchors-CtnwuQJM.d.ts.map +1 -0
- package/dist/tui.d.ts +3 -3
- package/dist/tui.d.ts.map +1 -1
- package/dist/tui.js +576 -102
- package/dist/tui.js.map +1 -1
- package/dist/{turn-operations-CZLsNgw-.js → turn-operations-DkJ1R0p8.js} +4 -3
- package/dist/turn-operations-DkJ1R0p8.js.map +1 -0
- package/dist/{turn-operations-Bp5-BAki.d.ts → turn-operations-khAvr7KJ.d.ts} +5 -3
- package/dist/turn-operations-khAvr7KJ.d.ts.map +1 -0
- package/dist/types.d.ts +1 -1
- package/dist/{xai-DILdFGzN.js → xai-CazjStOK.js} +3 -3
- package/dist/{xai-DILdFGzN.js.map → xai-CazjStOK.js.map} +1 -1
- package/docs/EXTENSIONS.md +58 -0
- package/docs/IPC.md +9 -0
- package/docs/TUI.md +2 -0
- package/package.json +1 -1
- package/dist/agent-DRNT6DbC.js.map +0 -1
- package/dist/approvals-Ch6gmO8H.js.map +0 -1
- package/dist/auth-Ddk-rCdG.js.map +0 -1
- package/dist/index-DFGECPfm.d.ts.map +0 -1
- package/dist/index-DP7xYXr9.d.ts.map +0 -1
- package/dist/server-CGmy4363.js.map +0 -1
- package/dist/session-K3icbPH9.js.map +0 -1
- package/dist/transcript-anchors-D1ToF-EW.d.ts.map +0 -1
- package/dist/turn-operations-Bp5-BAki.d.ts.map +0 -1
- package/dist/turn-operations-CZLsNgw-.js.map +0 -1
|
@@ -1,5 +1,5 @@
|
|
|
1
|
-
import {
|
|
2
|
-
import { V as ApprovalDecision } from "./tool-formatters-
|
|
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-
|
|
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 { $
|
|
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
|
|
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
|
|
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-
|
|
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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"}
|
package/docs/EXTENSIONS.md
CHANGED
|
@@ -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
|