zidane 5.6.11 → 5.6.13

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 (64) hide show
  1. package/README.md +19 -2
  2. package/dist/{agent-C9AKTU_V.d.ts → agent-ClkpElCZ.d.ts} +540 -55
  3. package/dist/agent-ClkpElCZ.d.ts.map +1 -0
  4. package/dist/chat.d.ts +47 -17
  5. package/dist/chat.d.ts.map +1 -1
  6. package/dist/chat.js +3 -3
  7. package/dist/{index-6f4T7Gc0.d.ts → index-CTDMMdIy.d.ts} +348 -3
  8. package/dist/index-CTDMMdIy.d.ts.map +1 -0
  9. package/dist/{index-DPN7TcXK.d.ts → index-v3Tzobqr.d.ts} +2 -2
  10. package/dist/{index-DPN7TcXK.d.ts.map → index-v3Tzobqr.d.ts.map} +1 -1
  11. package/dist/index.d.ts +4 -4
  12. package/dist/index.js +169 -8
  13. package/dist/index.js.map +1 -1
  14. package/dist/{login-BindcfKi.js → login-DS3sf6b5.js} +4 -4
  15. package/dist/{login-BindcfKi.js.map → login-DS3sf6b5.js.map} +1 -1
  16. package/dist/{mcp-0jRkIV0g.js → mcp-DGeB7-3D.js} +13 -2
  17. package/dist/mcp-DGeB7-3D.js.map +1 -0
  18. package/dist/mcp.d.ts +1 -1
  19. package/dist/mcp.js +1 -1
  20. package/dist/{messages-BfmXLDT4.js → messages-Dym8S_YH.js} +303 -8
  21. package/dist/messages-Dym8S_YH.js.map +1 -0
  22. package/dist/{presets-CmzMeWg2.js → presets-CZXS_87d.js} +2 -2
  23. package/dist/{presets-CmzMeWg2.js.map → presets-CZXS_87d.js.map} +1 -1
  24. package/dist/presets.d.ts +2 -2
  25. package/dist/presets.js +1 -1
  26. package/dist/{providers-C_ahnRBS.js → providers-beXyD9W9.js} +137 -21
  27. package/dist/providers-beXyD9W9.js.map +1 -0
  28. package/dist/providers.d.ts +2 -2
  29. package/dist/providers.js +3 -3
  30. package/dist/restate.d.ts +1 -1
  31. package/dist/session/sqlite.d.ts +1 -1
  32. package/dist/{session-PUzXZlG6.js → session-BRIsmBSY.js} +5 -2
  33. package/dist/session-BRIsmBSY.js.map +1 -0
  34. package/dist/session.d.ts +2 -2
  35. package/dist/session.js +3 -3
  36. package/dist/skills.d.ts +2 -2
  37. package/dist/{tools-CxOfTt3R.js → tools-DE9pR_NG.js} +515 -116
  38. package/dist/tools-DE9pR_NG.js.map +1 -0
  39. package/dist/tools.d.ts +3 -3
  40. package/dist/tools.js +1 -1
  41. package/dist/{transcript-anchors-DDCHSDdX.d.ts → transcript-anchors-D0TR6djV.d.ts} +4 -4
  42. package/dist/transcript-anchors-D0TR6djV.d.ts.map +1 -0
  43. package/dist/tui.d.ts +2 -2
  44. package/dist/tui.d.ts.map +1 -1
  45. package/dist/tui.js +12 -8
  46. package/dist/tui.js.map +1 -1
  47. package/dist/{turn-operations-CxE8BBau.js → turn-operations-6Yls2HuG.js} +907 -42
  48. package/dist/turn-operations-6Yls2HuG.js.map +1 -0
  49. package/dist/types-oKPBdCmL.js.map +1 -1
  50. package/dist/types.d.ts +3 -3
  51. package/docs/ARCHITECTURE.md +101 -20
  52. package/docs/CHAT.md +27 -5
  53. package/docs/RESTATE.md +1 -1
  54. package/docs/SKILL.md +39 -3
  55. package/package.json +5 -2
  56. package/dist/agent-C9AKTU_V.d.ts.map +0 -1
  57. package/dist/index-6f4T7Gc0.d.ts.map +0 -1
  58. package/dist/mcp-0jRkIV0g.js.map +0 -1
  59. package/dist/messages-BfmXLDT4.js.map +0 -1
  60. package/dist/providers-C_ahnRBS.js.map +0 -1
  61. package/dist/session-PUzXZlG6.js.map +0 -1
  62. package/dist/tools-CxOfTt3R.js.map +0 -1
  63. package/dist/transcript-anchors-DDCHSDdX.d.ts.map +0 -1
  64. package/dist/turn-operations-CxE8BBau.js.map +0 -1
@@ -1,11 +1,12 @@
1
- import { H as previewLine, R as fmtTokens, U as shortId, a as multiEdit, c as grep, d as resolveOldString, f as styleReplacementForVia, i as readFile$1, l as glob, n as createSpawnTool, o as listFiles, t as writeFile$1, u as edit, y as shell } from "./tools-CxOfTt3R.js";
1
+ import { H as previewLine, R as fmtTokens, U as shortId, a as multiEdit, c as grep, d as resolveOldString, f as styleReplacementForVia, i as readFile$1, l as glob$1, n as createSpawnTool, o as listFiles, t as writeFile$1, u as edit, y as shell } from "./tools-DE9pR_NG.js";
2
2
  import { c as errorMessage } from "./errors-DdZXnyXE.js";
3
3
  import { r as toolResultToText } from "./types-oKPBdCmL.js";
4
- import { r as normalizeMcpServers, t as connectMcpServers } from "./mcp-0jRkIV0g.js";
4
+ import { O as joinSystemPrompt } from "./messages-Dym8S_YH.js";
5
+ import { r as normalizeMcpServers, t as connectMcpServers } from "./mcp-DGeB7-3D.js";
5
6
  import { a as discoverSkills } from "./interpolate-DM1UcKeQ.js";
6
7
  import { n as formatTokenUsage } from "./stats-Lc3zL3RM.js";
7
- import { n as definePreset, t as composePresets } from "./presets-CmzMeWg2.js";
8
- import { a as writeFileAtomic, i as anthropic, n as openai, r as cerebras, t as openrouter } from "./providers-C_ahnRBS.js";
8
+ import { n as definePreset, t as composePresets } from "./presets-CZXS_87d.js";
9
+ import { i as anthropic, n as openai, o as writeFileAtomic, r as cerebras, t as openrouter } from "./providers-beXyD9W9.js";
9
10
  import { createRequire } from "node:module";
10
11
  import { dirname, isAbsolute, join, posix, relative, resolve, sep } from "node:path";
11
12
  import { homedir, tmpdir } from "node:os";
@@ -13,9 +14,10 @@ import { spawn } from "node:child_process";
13
14
  import { chmodSync, createReadStream, createWriteStream, existsSync, mkdirSync, mkdtempSync, readFileSync, realpathSync, renameSync, rmSync, statSync, writeFileSync } from "node:fs";
14
15
  import { readdir, stat, writeFile } from "node:fs/promises";
15
16
  import { Buffer as Buffer$1 } from "node:buffer";
16
- import { getModel, getModels } from "@mariozechner/pi-ai";
17
- import { anthropicOAuthProvider, openaiCodexOAuthProvider } from "@mariozechner/pi-ai/oauth";
18
- import { createHash } from "node:crypto";
17
+ import { getModel, getModels } from "@earendil-works/pi-ai";
18
+ import { createServer } from "node:http";
19
+ import { refreshAnthropicToken, refreshOpenAICodexToken } from "@earendil-works/pi-ai/oauth";
20
+ import { createHash, randomBytes } from "node:crypto";
19
21
  import { createGunzip } from "node:zlib";
20
22
  import { createContext, useCallback, useContext, useEffect, useMemo, useRef, useState } from "react";
21
23
  import { jsx } from "react/jsx-runtime";
@@ -33,14 +35,21 @@ var __require = /* @__PURE__ */ createRequire(import.meta.url);
33
35
  *
34
36
  * The fragments are deliberately byte-stable across sessions — every doctrine
35
37
  * piece reads the same on every turn so the system-prompt cache breakpoint
36
- * (anthropic ephemeral, `src/providers/anthropic.ts:327`) keeps hitting cache.
37
- * Runtime-derived content (cwd, date) lives in {@link envSection} and is
38
- * computed at agent-build time, not per-turn — same anchor, same cache.
38
+ * (anthropic ephemeral, `src/providers/anthropic.ts`) keeps hitting cache.
39
39
  *
40
- * The TUI overrides `agent.run({ system })` with the composed string at
41
- * session activation so a moved cwd or rolled-over date refreshes the
42
- * prefix; SDK consumers calling `createAgent({ system })` directly get the
43
- * cwd-less variant from the preset and can wrap their own env section.
40
+ * Layout uses {@link SYSTEM_PROMPT_BOUNDARY} from `../system-prompt` to split
41
+ * the rendered prompt into:
42
+ *
43
+ * ┌──────────────┐ cache_control: ephemeral
44
+ * │ STATIC half │ identity + doctrine + guidance + user instructions
45
+ * ├──────────────┤ ← __ZIDANE_SYSTEM_PROMPT_BOUNDARY__
46
+ * │ DYNAMIC half │ envSection(cwd / date / platform)
47
+ * └──────────────┘ (no cache_control)
48
+ *
49
+ * Per-turn cwd / date / platform churn no longer invalidates the doctrine
50
+ * cache above. The TUI's `system:transform` hook rewrites only the dynamic
51
+ * half via `replaceDynamicSection`; the static prefix stays byte-identical
52
+ * for the lifetime of the run.
44
53
  */
45
54
  /**
46
55
  * Lead line — short, version-stable, written in the same key as Claude Code's
@@ -218,10 +227,15 @@ function buildEnvSectionFromOpts(opts) {
218
227
  /**
219
228
  * Compose the Build-mode system prompt.
220
229
  *
221
- * Order matters for cache stability: identity → env → doctrine → guidance.
222
- * Env comes before doctrine so a cwd change rebuilds only the head of the
223
- * prompt; the doctrine tail stays byte-identical across sessions and rides
224
- * the cache.
230
+ * Layout (top to bottom):
231
+ *
232
+ * identity doctrine guidance user instructions
233
+ * ─── SYSTEM_PROMPT_BOUNDARY ───
234
+ * env (cwd / project root / date / platform)
235
+ *
236
+ * Everything ABOVE the boundary is byte-stable for the lifetime of the run
237
+ * and rides the prompt cache across turns + sessions. The env block sits
238
+ * BELOW so a moved cwd or rolled-over date doesn't invalidate the doctrine.
225
239
  *
226
240
  * The fragments are joined with blank lines so the model sees clean section
227
241
  * breaks. Don't reorder lightly — every reorder costs one cache miss across
@@ -229,17 +243,20 @@ function buildEnvSectionFromOpts(opts) {
229
243
  */
230
244
  function buildBuildSystem(opts = {}) {
231
245
  const interactionGuidance = opts.allowInteraction === false ? INTERACTION_GUIDANCE_NO_PROMPTS : INTERACTION_GUIDANCE;
232
- return joinPrompt([
233
- IDENTITY_PREFIX,
234
- buildEnvSectionFromOpts(opts),
235
- DOING_TASKS_DOCTRINE,
236
- ACTIONS_WITH_CARE_DOCTRINE,
237
- TOKEN_DISCIPLINE_DOCTRINE,
238
- SUBAGENT_GUIDANCE,
239
- COMMUNICATION_DOCTRINE,
240
- interactionGuidance,
241
- opts.userInstructions || null
242
- ]);
246
+ const env = buildEnvSectionFromOpts(opts);
247
+ return composeWithBoundary({
248
+ static: [
249
+ IDENTITY_PREFIX,
250
+ DOING_TASKS_DOCTRINE,
251
+ ACTIONS_WITH_CARE_DOCTRINE,
252
+ TOKEN_DISCIPLINE_DOCTRINE,
253
+ SUBAGENT_GUIDANCE,
254
+ COMMUNICATION_DOCTRINE,
255
+ interactionGuidance,
256
+ opts.userInstructions || null
257
+ ],
258
+ dynamic: [env]
259
+ });
243
260
  }
244
261
  /**
245
262
  * Compose the Plan-mode system prompt. Subset of Build: drops the
@@ -248,15 +265,28 @@ function buildBuildSystem(opts = {}) {
248
265
  */
249
266
  function buildPlanSystem(opts = {}) {
250
267
  const noPrompts = opts.allowInteraction === false;
251
- return joinPrompt([
252
- IDENTITY_PREFIX,
253
- buildEnvSectionFromOpts(opts),
254
- noPrompts ? PLAN_MODE_DOCTRINE_NO_PROMPTS : PLAN_MODE_DOCTRINE,
255
- TOKEN_DISCIPLINE_DOCTRINE,
256
- COMMUNICATION_DOCTRINE,
257
- noPrompts ? INTERACTION_GUIDANCE_NO_PROMPTS : INTERACTION_GUIDANCE,
258
- opts.userInstructions || null
259
- ]);
268
+ const env = buildEnvSectionFromOpts(opts);
269
+ return composeWithBoundary({
270
+ static: [
271
+ IDENTITY_PREFIX,
272
+ noPrompts ? PLAN_MODE_DOCTRINE_NO_PROMPTS : PLAN_MODE_DOCTRINE,
273
+ TOKEN_DISCIPLINE_DOCTRINE,
274
+ COMMUNICATION_DOCTRINE,
275
+ noPrompts ? INTERACTION_GUIDANCE_NO_PROMPTS : INTERACTION_GUIDANCE,
276
+ opts.userInstructions || null
277
+ ],
278
+ dynamic: [env]
279
+ });
280
+ }
281
+ /**
282
+ * Glue helper — compose a system prompt with the boundary marker between the
283
+ * static and dynamic halves. Returns a marker-free string when the dynamic
284
+ * half is empty (e.g. SDK consumers building the prompt without an
285
+ * `envSection`), so the historical single-block cache path is preserved
286
+ * end-to-end.
287
+ */
288
+ function composeWithBoundary(parts) {
289
+ return joinSystemPrompt(joinPrompt(parts.static), joinPrompt(parts.dynamic));
260
290
  }
261
291
  function joinPrompt(parts) {
262
292
  return parts.filter((p) => typeof p === "string" && p.length > 0).join("\n\n");
@@ -858,7 +888,7 @@ function accentColor(accent, COLOR) {
858
888
  const READ_ONLY_TOOLS = {
859
889
  readFile: readFile$1,
860
890
  listFiles,
861
- glob,
891
+ glob: glob$1,
862
892
  grep
863
893
  };
864
894
  /** Full build-mode tool slice — read + write + shell + spawn. */
@@ -869,7 +899,7 @@ const BUILD_TOOLS = {
869
899
  listFiles,
870
900
  edit,
871
901
  multiEdit,
872
- glob,
902
+ glob: glob$1,
873
903
  grep
874
904
  };
875
905
  /**
@@ -1271,7 +1301,631 @@ function readIfPresent(path, source) {
1271
1301
  };
1272
1302
  }
1273
1303
  //#endregion
1304
+ //#region src/chat/oauth-page/pkce.ts
1305
+ /**
1306
+ * PKCE helper. Inlined here (rather than imported from pi-ai) because
1307
+ * `@earendil-works/pi-ai/oauth` does not re-export it, and we don't want
1308
+ * to reach into `node_modules/.../utils/oauth/pkce.js` — that's an
1309
+ * implementation-detail path that breaks on minor upstream rearrangements.
1310
+ *
1311
+ * Web Crypto API only. Works under Bun + Node 22+ without any node:crypto
1312
+ * import (matches pi-ai's own behavior). Output is base64url per RFC 7636.
1313
+ */
1314
+ function base64UrlEncode(bytes) {
1315
+ let binary = "";
1316
+ for (const byte of bytes) binary += String.fromCharCode(byte);
1317
+ return btoa(binary).replace(/\+/g, "-").replace(/\//g, "_").replace(/=/g, "");
1318
+ }
1319
+ async function generatePkce() {
1320
+ const verifierBytes = new Uint8Array(32);
1321
+ crypto.getRandomValues(verifierBytes);
1322
+ const verifier = base64UrlEncode(verifierBytes);
1323
+ const data = new TextEncoder().encode(verifier);
1324
+ const hashBuffer = await crypto.subtle.digest("SHA-256", data);
1325
+ return {
1326
+ verifier,
1327
+ challenge: base64UrlEncode(new Uint8Array(hashBuffer))
1328
+ };
1329
+ }
1330
+ //#endregion
1331
+ //#region src/chat/oauth-page/server.ts
1332
+ const CALLBACK_HOST = process.env.PI_OAUTH_CALLBACK_HOST || "127.0.0.1";
1333
+ function buildRequestHandler(opts, pending) {
1334
+ return (req, res) => {
1335
+ try {
1336
+ const url = new URL(req.url || "", "http://localhost");
1337
+ if (url.pathname !== opts.path) {
1338
+ respondError(res, 404, opts, "Callback route not found.");
1339
+ return;
1340
+ }
1341
+ const error = url.searchParams.get("error");
1342
+ if (error) {
1343
+ respondError(res, 400, opts, `${opts.providerName} authentication did not complete.`, `Error: ${error}`);
1344
+ return;
1345
+ }
1346
+ const code = url.searchParams.get("code");
1347
+ const state = url.searchParams.get("state");
1348
+ if (!code || !state) {
1349
+ respondError(res, 400, opts, "Missing code or state parameter.");
1350
+ return;
1351
+ }
1352
+ if (state !== opts.expectedState) {
1353
+ respondError(res, 400, opts, "State mismatch.");
1354
+ return;
1355
+ }
1356
+ res.writeHead(200, { "Content-Type": "text/html; charset=utf-8" });
1357
+ res.end(opts.renderPage({
1358
+ kind: "success",
1359
+ provider: opts.providerName,
1360
+ message: opts.successMessage
1361
+ }));
1362
+ pending.settle({
1363
+ code,
1364
+ state
1365
+ });
1366
+ } catch {
1367
+ respondError(res, 500, opts, "Internal error while processing the callback.");
1368
+ }
1369
+ };
1370
+ }
1371
+ function respondError(res, status, opts, message, details) {
1372
+ res.writeHead(status, { "Content-Type": "text/html; charset=utf-8" });
1373
+ res.end(opts.renderPage({
1374
+ kind: "error",
1375
+ provider: opts.providerName,
1376
+ message,
1377
+ details
1378
+ }));
1379
+ }
1380
+ function buildStubHandle(redirectUri, server) {
1381
+ return {
1382
+ redirectUri,
1383
+ waitForCode: async () => null,
1384
+ cancelWait: () => {},
1385
+ close: () => {
1386
+ try {
1387
+ server?.close();
1388
+ } catch {}
1389
+ }
1390
+ };
1391
+ }
1392
+ /**
1393
+ * Start the loopback HTTP callback server. The returned handle is the
1394
+ * caller's contract — they `await waitForCode()`, race it against manual
1395
+ * paste, then `close()` in a `finally`.
1396
+ */
1397
+ async function startCallbackServer(opts) {
1398
+ const onListenError = opts.onListenError ?? "reject";
1399
+ const redirectUri = `http://localhost:${opts.port}${opts.path}`;
1400
+ return new Promise((resolve, reject) => {
1401
+ let settled = false;
1402
+ const pending = { settle: () => {} };
1403
+ const waitPromise = new Promise((resolveWait) => {
1404
+ pending.settle = (value) => {
1405
+ if (settled) return;
1406
+ settled = true;
1407
+ resolveWait(value);
1408
+ };
1409
+ });
1410
+ const server = createServer(buildRequestHandler(opts, pending));
1411
+ server.on("error", (err) => {
1412
+ if (onListenError === "resolveWithStub") {
1413
+ pending.settle(null);
1414
+ resolve(buildStubHandle(redirectUri, server));
1415
+ return;
1416
+ }
1417
+ reject(err);
1418
+ });
1419
+ server.listen(opts.port, CALLBACK_HOST, () => {
1420
+ resolve({
1421
+ redirectUri,
1422
+ waitForCode: () => waitPromise,
1423
+ cancelWait: () => pending.settle(null),
1424
+ close: () => {
1425
+ try {
1426
+ server.close();
1427
+ } catch {}
1428
+ }
1429
+ });
1430
+ });
1431
+ });
1432
+ }
1433
+ //#endregion
1434
+ //#region src/chat/oauth-page/anthropic.ts
1435
+ const CLIENT_ID$1 = atob("OWQxYzI1MGEtZTYxYi00NGQ5LTg4ZWQtNTk0NGQxOTYyZjVl");
1436
+ const AUTHORIZE_URL$1 = "https://claude.ai/oauth/authorize";
1437
+ const TOKEN_URL$1 = "https://platform.claude.com/v1/oauth/token";
1438
+ const CALLBACK_PORT$1 = 53692;
1439
+ const CALLBACK_PATH$1 = "/callback";
1440
+ const SCOPES = "org:create_api_key user:profile user:inference user:sessions:claude_code user:mcp_servers user:file_upload";
1441
+ const PROVIDER_NAME$1 = "Anthropic";
1442
+ /**
1443
+ * Parse what the user pasted into the manual-code prompt. Accepts:
1444
+ * - the bare authorization code
1445
+ * - the full `redirect_uri?code=...&state=...` URL
1446
+ * - the `code#state` shorthand Anthropic surfaces on the page
1447
+ * - a raw query string with `code=...&state=...`
1448
+ *
1449
+ * Matches pi-ai's parse table so an existing user's muscle memory still works.
1450
+ */
1451
+ function parseAuthorizationInput$1(input) {
1452
+ const value = input.trim();
1453
+ if (!value) return {};
1454
+ try {
1455
+ const url = new URL(value);
1456
+ return {
1457
+ code: url.searchParams.get("code") ?? void 0,
1458
+ state: url.searchParams.get("state") ?? void 0
1459
+ };
1460
+ } catch {}
1461
+ if (value.includes("#")) {
1462
+ const [code, state] = value.split("#", 2);
1463
+ return {
1464
+ code,
1465
+ state
1466
+ };
1467
+ }
1468
+ if (value.includes("code=")) {
1469
+ const params = new URLSearchParams(value);
1470
+ return {
1471
+ code: params.get("code") ?? void 0,
1472
+ state: params.get("state") ?? void 0
1473
+ };
1474
+ }
1475
+ return { code: value };
1476
+ }
1477
+ async function postJson(url, body) {
1478
+ const response = await fetch(url, {
1479
+ method: "POST",
1480
+ headers: {
1481
+ "Content-Type": "application/json",
1482
+ "Accept": "application/json"
1483
+ },
1484
+ body: JSON.stringify(body),
1485
+ signal: AbortSignal.timeout(3e4)
1486
+ });
1487
+ const responseBody = await response.text();
1488
+ if (!response.ok) throw new Error(`HTTP request failed. status=${response.status}; url=${url}; body=${responseBody}`);
1489
+ return responseBody;
1490
+ }
1491
+ async function exchangeAuthorizationCode$1(code, state, verifier, redirectUri) {
1492
+ const responseBody = await postJson(TOKEN_URL$1, {
1493
+ grant_type: "authorization_code",
1494
+ client_id: CLIENT_ID$1,
1495
+ code,
1496
+ state,
1497
+ redirect_uri: redirectUri,
1498
+ code_verifier: verifier
1499
+ });
1500
+ const tokenData = JSON.parse(responseBody);
1501
+ return {
1502
+ refresh: tokenData.refresh_token,
1503
+ access: tokenData.access_token,
1504
+ expires: Date.now() + tokenData.expires_in * 1e3 - 300 * 1e3
1505
+ };
1506
+ }
1507
+ async function loginAnthropicWithCustomPage(options) {
1508
+ const { verifier, challenge } = await generatePkce();
1509
+ const server = await startCallbackServer({
1510
+ port: CALLBACK_PORT$1,
1511
+ path: CALLBACK_PATH$1,
1512
+ expectedState: verifier,
1513
+ providerName: PROVIDER_NAME$1,
1514
+ renderPage: options.renderPage,
1515
+ successMessage: "Anthropic authentication completed. You can close this window.",
1516
+ onListenError: "reject"
1517
+ });
1518
+ let code;
1519
+ let state;
1520
+ try {
1521
+ const authParams = new URLSearchParams({
1522
+ code: "true",
1523
+ client_id: CLIENT_ID$1,
1524
+ response_type: "code",
1525
+ redirect_uri: server.redirectUri,
1526
+ scope: SCOPES,
1527
+ code_challenge: challenge,
1528
+ code_challenge_method: "S256",
1529
+ state: verifier
1530
+ });
1531
+ options.onAuth({
1532
+ url: `${AUTHORIZE_URL$1}?${authParams.toString()}`,
1533
+ instructions: "Complete login in your browser. If the browser is on another machine, paste the final redirect URL here."
1534
+ });
1535
+ if (options.onManualCodeInput) {
1536
+ let manualInput;
1537
+ let manualError;
1538
+ const manualPromise = options.onManualCodeInput().then((input) => {
1539
+ manualInput = input;
1540
+ server.cancelWait();
1541
+ }).catch((err) => {
1542
+ manualError = err instanceof Error ? err : new Error(String(err));
1543
+ server.cancelWait();
1544
+ });
1545
+ const result = await server.waitForCode();
1546
+ if (manualError) throw manualError;
1547
+ if (result?.code) {
1548
+ code = result.code;
1549
+ state = result.state;
1550
+ } else if (manualInput) {
1551
+ const parsed = parseAuthorizationInput$1(manualInput);
1552
+ if (parsed.state && parsed.state !== verifier) throw new Error("OAuth state mismatch");
1553
+ code = parsed.code;
1554
+ state = parsed.state ?? verifier;
1555
+ }
1556
+ if (!code) {
1557
+ await manualPromise;
1558
+ if (manualError) throw manualError;
1559
+ if (manualInput) {
1560
+ const parsed = parseAuthorizationInput$1(manualInput);
1561
+ if (parsed.state && parsed.state !== verifier) throw new Error("OAuth state mismatch");
1562
+ code = parsed.code;
1563
+ state = parsed.state ?? verifier;
1564
+ }
1565
+ }
1566
+ } else {
1567
+ const result = await server.waitForCode();
1568
+ if (result?.code) {
1569
+ code = result.code;
1570
+ state = result.state;
1571
+ }
1572
+ }
1573
+ if (!code) {
1574
+ const parsed = parseAuthorizationInput$1(await options.onPrompt({
1575
+ message: "Paste the authorization code or full redirect URL:",
1576
+ placeholder: server.redirectUri
1577
+ }));
1578
+ if (parsed.state && parsed.state !== verifier) throw new Error("OAuth state mismatch");
1579
+ code = parsed.code;
1580
+ state = parsed.state ?? verifier;
1581
+ }
1582
+ if (!code) throw new Error("Missing authorization code");
1583
+ if (!state) throw new Error("Missing OAuth state");
1584
+ options.onProgress?.("Exchanging authorization code for tokens...");
1585
+ return await exchangeAuthorizationCode$1(code, state, verifier, server.redirectUri);
1586
+ } finally {
1587
+ server.close();
1588
+ }
1589
+ }
1590
+ /**
1591
+ * Build an `OAuthProviderInterface` that behaves identically to pi-ai's
1592
+ * `anthropicOAuthProvider` except for the callback page HTML. Drop this
1593
+ * onto `ProviderDescriptor.oauthProvider` to override.
1594
+ */
1595
+ function createAnthropicOAuthProviderWithCustomPage(renderPage) {
1596
+ return {
1597
+ id: "anthropic",
1598
+ name: "Anthropic (Claude Pro/Max)",
1599
+ usesCallbackServer: true,
1600
+ async login(callbacks) {
1601
+ return loginAnthropicWithCustomPage({
1602
+ renderPage,
1603
+ onAuth: callbacks.onAuth,
1604
+ onPrompt: callbacks.onPrompt,
1605
+ onProgress: callbacks.onProgress,
1606
+ onManualCodeInput: callbacks.onManualCodeInput
1607
+ });
1608
+ },
1609
+ async refreshToken(credentials) {
1610
+ return refreshAnthropicToken(credentials.refresh);
1611
+ },
1612
+ getApiKey(credentials) {
1613
+ return credentials.access;
1614
+ }
1615
+ };
1616
+ }
1617
+ //#endregion
1618
+ //#region src/chat/oauth-page/openai-codex.ts
1619
+ const CLIENT_ID = "app_EMoamEEZ73f0CkXaXp7hrann";
1620
+ const AUTHORIZE_URL = "https://auth.openai.com/oauth/authorize";
1621
+ const TOKEN_URL = "https://auth.openai.com/oauth/token";
1622
+ const CALLBACK_PORT = 1455;
1623
+ const CALLBACK_PATH = "/auth/callback";
1624
+ const SCOPE = "openid profile email offline_access";
1625
+ const JWT_CLAIM_PATH = "https://api.openai.com/auth";
1626
+ const PROVIDER_NAME = "OpenAI Codex";
1627
+ function createState() {
1628
+ return randomBytes(16).toString("hex");
1629
+ }
1630
+ function parseAuthorizationInput(input) {
1631
+ const value = input.trim();
1632
+ if (!value) return {};
1633
+ try {
1634
+ const url = new URL(value);
1635
+ return {
1636
+ code: url.searchParams.get("code") ?? void 0,
1637
+ state: url.searchParams.get("state") ?? void 0
1638
+ };
1639
+ } catch {}
1640
+ if (value.includes("#")) {
1641
+ const [code, state] = value.split("#", 2);
1642
+ return {
1643
+ code,
1644
+ state
1645
+ };
1646
+ }
1647
+ if (value.includes("code=")) {
1648
+ const params = new URLSearchParams(value);
1649
+ return {
1650
+ code: params.get("code") ?? void 0,
1651
+ state: params.get("state") ?? void 0
1652
+ };
1653
+ }
1654
+ return { code: value };
1655
+ }
1656
+ function decodeJwt(token) {
1657
+ try {
1658
+ const parts = token.split(".");
1659
+ if (parts.length !== 3) return null;
1660
+ const payload = parts[1] ?? "";
1661
+ const decoded = atob(payload);
1662
+ return JSON.parse(decoded);
1663
+ } catch {
1664
+ return null;
1665
+ }
1666
+ }
1667
+ function getAccountId(accessToken) {
1668
+ const accountId = (decodeJwt(accessToken)?.[JWT_CLAIM_PATH])?.chatgpt_account_id;
1669
+ return typeof accountId === "string" && accountId.length > 0 ? accountId : null;
1670
+ }
1671
+ async function exchangeAuthorizationCode(code, verifier, redirectUri) {
1672
+ const response = await fetch(TOKEN_URL, {
1673
+ method: "POST",
1674
+ headers: { "Content-Type": "application/x-www-form-urlencoded" },
1675
+ body: new URLSearchParams({
1676
+ grant_type: "authorization_code",
1677
+ client_id: CLIENT_ID,
1678
+ code,
1679
+ code_verifier: verifier,
1680
+ redirect_uri: redirectUri
1681
+ })
1682
+ });
1683
+ if (!response.ok) {
1684
+ const text = await response.text().catch(() => "");
1685
+ return {
1686
+ type: "failed",
1687
+ message: `OpenAI Codex token exchange failed (${response.status}): ${text || response.statusText}`
1688
+ };
1689
+ }
1690
+ const json = await response.json();
1691
+ if (!json.access_token || !json.refresh_token || typeof json.expires_in !== "number") return {
1692
+ type: "failed",
1693
+ message: `OpenAI Codex token exchange response missing fields: ${JSON.stringify(json)}`
1694
+ };
1695
+ return {
1696
+ type: "success",
1697
+ access: json.access_token,
1698
+ refresh: json.refresh_token,
1699
+ expires: Date.now() + json.expires_in * 1e3
1700
+ };
1701
+ }
1702
+ async function loginOpenAICodexWithCustomPage(options) {
1703
+ const { verifier, challenge } = await generatePkce();
1704
+ const state = createState();
1705
+ const originator = options.originator ?? "pi";
1706
+ const server = await startCallbackServer({
1707
+ port: CALLBACK_PORT,
1708
+ path: CALLBACK_PATH,
1709
+ expectedState: state,
1710
+ providerName: PROVIDER_NAME,
1711
+ renderPage: options.renderPage,
1712
+ successMessage: "OpenAI authentication completed. You can close this window.",
1713
+ onListenError: "resolveWithStub"
1714
+ });
1715
+ const authUrl = new URL(AUTHORIZE_URL);
1716
+ authUrl.searchParams.set("response_type", "code");
1717
+ authUrl.searchParams.set("client_id", CLIENT_ID);
1718
+ authUrl.searchParams.set("redirect_uri", server.redirectUri);
1719
+ authUrl.searchParams.set("scope", SCOPE);
1720
+ authUrl.searchParams.set("code_challenge", challenge);
1721
+ authUrl.searchParams.set("code_challenge_method", "S256");
1722
+ authUrl.searchParams.set("state", state);
1723
+ authUrl.searchParams.set("id_token_add_organizations", "true");
1724
+ authUrl.searchParams.set("codex_cli_simplified_flow", "true");
1725
+ authUrl.searchParams.set("originator", originator);
1726
+ options.onAuth({
1727
+ url: authUrl.toString(),
1728
+ instructions: "A browser window should open. Complete login to finish."
1729
+ });
1730
+ let code;
1731
+ try {
1732
+ if (options.onManualCodeInput) {
1733
+ let manualCode;
1734
+ let manualError;
1735
+ const manualPromise = options.onManualCodeInput().then((input) => {
1736
+ manualCode = input;
1737
+ server.cancelWait();
1738
+ }).catch((err) => {
1739
+ manualError = err instanceof Error ? err : new Error(String(err));
1740
+ server.cancelWait();
1741
+ });
1742
+ const result = await server.waitForCode();
1743
+ if (manualError) throw manualError;
1744
+ if (result?.code) code = result.code;
1745
+ else if (manualCode) {
1746
+ const parsed = parseAuthorizationInput(manualCode);
1747
+ if (parsed.state && parsed.state !== state) throw new Error("State mismatch");
1748
+ code = parsed.code;
1749
+ }
1750
+ if (!code) {
1751
+ await manualPromise;
1752
+ if (manualError) throw manualError;
1753
+ if (manualCode) {
1754
+ const parsed = parseAuthorizationInput(manualCode);
1755
+ if (parsed.state && parsed.state !== state) throw new Error("State mismatch");
1756
+ code = parsed.code;
1757
+ }
1758
+ }
1759
+ } else {
1760
+ const result = await server.waitForCode();
1761
+ if (result?.code) code = result.code;
1762
+ }
1763
+ if (!code) {
1764
+ const parsed = parseAuthorizationInput(await options.onPrompt({ message: "Paste the authorization code (or full redirect URL):" }));
1765
+ if (parsed.state && parsed.state !== state) throw new Error("State mismatch");
1766
+ code = parsed.code;
1767
+ }
1768
+ if (!code) throw new Error("Missing authorization code");
1769
+ const tokenResult = await exchangeAuthorizationCode(code, verifier, server.redirectUri);
1770
+ if (tokenResult.type !== "success") throw new Error(tokenResult.message);
1771
+ const accountId = getAccountId(tokenResult.access);
1772
+ if (!accountId) throw new Error("Failed to extract accountId from token");
1773
+ return {
1774
+ access: tokenResult.access,
1775
+ refresh: tokenResult.refresh,
1776
+ expires: tokenResult.expires,
1777
+ accountId
1778
+ };
1779
+ } finally {
1780
+ server.close();
1781
+ }
1782
+ }
1783
+ /**
1784
+ * Build an `OAuthProviderInterface` that behaves identically to pi-ai's
1785
+ * `openaiCodexOAuthProvider` except for the callback page HTML.
1786
+ */
1787
+ function createOpenAICodexOAuthProviderWithCustomPage(renderPage) {
1788
+ return {
1789
+ id: "openai-codex",
1790
+ name: "ChatGPT Plus/Pro (Codex Subscription)",
1791
+ usesCallbackServer: true,
1792
+ async login(callbacks) {
1793
+ return loginOpenAICodexWithCustomPage({
1794
+ renderPage,
1795
+ onAuth: callbacks.onAuth,
1796
+ onPrompt: callbacks.onPrompt,
1797
+ onProgress: callbacks.onProgress,
1798
+ onManualCodeInput: callbacks.onManualCodeInput
1799
+ });
1800
+ },
1801
+ async refreshToken(credentials) {
1802
+ return refreshOpenAICodexToken(credentials.refresh);
1803
+ },
1804
+ getApiKey(credentials) {
1805
+ return credentials.access;
1806
+ }
1807
+ };
1808
+ }
1809
+ //#endregion
1810
+ //#region src/chat/oauth-page/render.ts
1811
+ function escapeHtml(value) {
1812
+ return value.replaceAll("&", "&amp;").replaceAll("<", "&lt;").replaceAll(">", "&gt;").replaceAll("\"", "&quot;").replaceAll("'", "&#39;");
1813
+ }
1814
+ /**
1815
+ * Default zidane-themed page. Visually neutral but distinguishable from
1816
+ * pi-ai's stock page — dark background, mono headings, no logo (host can
1817
+ * pass a custom renderer to add one).
1818
+ */
1819
+ const renderDefaultCallbackPage = (page) => {
1820
+ const heading = escapeHtml(page.kind === "success" ? `Signed in to ${page.provider}` : `Could not sign in to ${page.provider}`);
1821
+ const message = escapeHtml(page.message);
1822
+ const details = page.details ? escapeHtml(page.details) : void 0;
1823
+ return `<!doctype html>
1824
+ <html lang="en">
1825
+ <head>
1826
+ <meta charset="utf-8" />
1827
+ <meta name="viewport" content="width=device-width, initial-scale=1" />
1828
+ <title>${heading}</title>
1829
+ <style>
1830
+ :root {
1831
+ --text: #f4f4f5;
1832
+ --text-dim: #a1a1aa;
1833
+ --accent: ${page.kind === "success" ? "#22d3ee" : "#f87171"};
1834
+ --page-bg: #0a0a0a;
1835
+ --panel-bg: #131316;
1836
+ --border: #27272a;
1837
+ --font-sans: ui-sans-serif, system-ui, -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, sans-serif;
1838
+ --font-mono: ui-monospace, SFMono-Regular, Menlo, Monaco, Consolas, monospace;
1839
+ }
1840
+ * { box-sizing: border-box; }
1841
+ html { color-scheme: dark; }
1842
+ body {
1843
+ margin: 0;
1844
+ min-height: 100vh;
1845
+ display: flex;
1846
+ align-items: center;
1847
+ justify-content: center;
1848
+ padding: 24px;
1849
+ background: var(--page-bg);
1850
+ color: var(--text);
1851
+ font-family: var(--font-sans);
1852
+ }
1853
+ main {
1854
+ width: 100%;
1855
+ max-width: 520px;
1856
+ padding: 32px;
1857
+ background: var(--panel-bg);
1858
+ border: 1px solid var(--border);
1859
+ border-radius: 12px;
1860
+ }
1861
+ .label {
1862
+ font-family: var(--font-mono);
1863
+ font-size: 12px;
1864
+ letter-spacing: 0.08em;
1865
+ text-transform: uppercase;
1866
+ color: var(--accent);
1867
+ margin-bottom: 12px;
1868
+ }
1869
+ h1 {
1870
+ margin: 0 0 12px;
1871
+ font-size: 22px;
1872
+ font-weight: 600;
1873
+ letter-spacing: -0.01em;
1874
+ color: var(--text);
1875
+ }
1876
+ p {
1877
+ margin: 0;
1878
+ line-height: 1.6;
1879
+ color: var(--text-dim);
1880
+ font-size: 14px;
1881
+ }
1882
+ .details {
1883
+ margin-top: 18px;
1884
+ padding: 12px 14px;
1885
+ background: var(--page-bg);
1886
+ border: 1px solid var(--border);
1887
+ border-radius: 8px;
1888
+ font-family: var(--font-mono);
1889
+ font-size: 12px;
1890
+ color: var(--text-dim);
1891
+ white-space: pre-wrap;
1892
+ word-break: break-word;
1893
+ }
1894
+ </style>
1895
+ </head>
1896
+ <body>
1897
+ <main>
1898
+ <div class="label">zidane · OAuth · ${escapeHtml(page.provider)}</div>
1899
+ <h1>${heading}</h1>
1900
+ <p>${message}</p>
1901
+ ${details ? `<div class="details">${details}</div>` : ""}
1902
+ </main>
1903
+ </body>
1904
+ </html>`;
1905
+ };
1906
+ //#endregion
1907
+ //#region src/chat/oauth-page/index.ts
1908
+ /**
1909
+ * Bundle helper — returns both Anthropic + OpenAI Codex providers wired
1910
+ * with the same renderer. Hosts that want different renderers per provider
1911
+ * should call the individual `create…WithCustomPage` factories instead.
1912
+ */
1913
+ function createCustomCallbackOAuthProviders(renderPage) {
1914
+ return {
1915
+ anthropic: createAnthropicOAuthProviderWithCustomPage(renderPage),
1916
+ openaiCodex: createOpenAICodexOAuthProviderWithCustomPage(renderPage)
1917
+ };
1918
+ }
1919
+ //#endregion
1274
1920
  //#region src/chat/providers.ts
1921
+ /**
1922
+ * pi-ai's stock Anthropic + Codex OAuth providers bake their own callback
1923
+ * HTML; we route both through `renderDefaultCallbackPage` so the post-redirect
1924
+ * page matches zidane's theme. The override lives in `src/chat/oauth-page/`
1925
+ * — see that folder's `index.ts` for the deletion path once pi-ai exposes
1926
+ * a `renderCallbackPage` hook upstream.
1927
+ */
1928
+ const { anthropic: anthropicOAuthProvider, openaiCodex: openaiCodexOAuthProvider } = createCustomCallbackOAuthProviders(renderDefaultCallbackPage);
1275
1929
  /** Convenience accessor — returns `credentialFileKey ?? key`. */
1276
1930
  function credKeyOf(desc) {
1277
1931
  return desc.credentialFileKey ?? desc.key;
@@ -5954,6 +6608,203 @@ const CRUSH_THEME = {
5954
6608
  }
5955
6609
  };
5956
6610
  //#endregion
6611
+ //#region src/chat/themes/gruvbox.ts
6612
+ const DARK = {
6613
+ dark: true,
6614
+ bg0: "#282828",
6615
+ bg1: "#3c3836",
6616
+ bg2: "#504945",
6617
+ bg3: "#665c54",
6618
+ bg4: "#7c6f64",
6619
+ fg0: "#fbf1c7",
6620
+ fg1: "#ebdbb2",
6621
+ fg2: "#d5c4a1",
6622
+ fg3: "#bdae93",
6623
+ fg4: "#a89984",
6624
+ red: "#fb4934",
6625
+ green: "#b8bb26",
6626
+ yellow: "#fabd2f",
6627
+ blue: "#83a598",
6628
+ purple: "#d3869b",
6629
+ aqua: "#8ec07c",
6630
+ orange: "#fe8019",
6631
+ gray: "#928374"
6632
+ };
6633
+ const LIGHT = {
6634
+ dark: false,
6635
+ bg0: "#fbf1c7",
6636
+ bg1: "#ebdbb2",
6637
+ bg2: "#d5c4a1",
6638
+ bg3: "#bdae93",
6639
+ bg4: "#a89984",
6640
+ fg0: "#282828",
6641
+ fg1: "#3c3836",
6642
+ fg2: "#504945",
6643
+ fg3: "#665c54",
6644
+ fg4: "#7c6f64",
6645
+ red: "#cc241d",
6646
+ green: "#98971a",
6647
+ yellow: "#d79921",
6648
+ blue: "#458588",
6649
+ purple: "#b16286",
6650
+ aqua: "#689d6a",
6651
+ orange: "#d65d0e",
6652
+ gray: "#928374"
6653
+ };
6654
+ function gruvboxTheme(id, label, p) {
6655
+ return {
6656
+ id,
6657
+ label,
6658
+ colors: {
6659
+ brand: p.yellow,
6660
+ accent: p.green,
6661
+ model: p.blue,
6662
+ warn: p.orange,
6663
+ error: p.red,
6664
+ dim: p.fg3,
6665
+ mute: p.gray,
6666
+ border: p.bg2,
6667
+ borderActive: p.bg4,
6668
+ throbber: {
6669
+ from: p.yellow,
6670
+ to: p.orange
6671
+ },
6672
+ money: p.yellow
6673
+ },
6674
+ select: {
6675
+ backgroundColor: "transparent",
6676
+ focusedBackgroundColor: "transparent",
6677
+ selectedBackgroundColor: "transparent",
6678
+ selectedTextColor: p.yellow,
6679
+ textColor: p.fg2,
6680
+ descriptionColor: p.fg4,
6681
+ selectedDescriptionColor: p.fg3
6682
+ },
6683
+ surfaces: {
6684
+ background: p.dark ? "#1d2021" : "#f9f5d7",
6685
+ modal: p.bg0,
6686
+ chips: {
6687
+ default: {
6688
+ bg: p.yellow,
6689
+ fg: p.dark ? p.bg0 : p.fg0
6690
+ },
6691
+ skills: {
6692
+ bg: p.yellow,
6693
+ fg: p.dark ? p.bg0 : p.fg0
6694
+ },
6695
+ files: {
6696
+ bg: p.blue,
6697
+ fg: p.dark ? p.bg0 : "#fbf1c7"
6698
+ }
6699
+ },
6700
+ selection: p.bg1,
6701
+ diff: {
6702
+ addBg: p.bg1,
6703
+ removeBg: p.bg1,
6704
+ addContentBg: p.bg2,
6705
+ removeContentBg: p.bg2,
6706
+ addFg: p.green,
6707
+ removeFg: p.red
6708
+ }
6709
+ },
6710
+ syntax: {
6711
+ "default": { fg: p.fg1 },
6712
+ "markup.heading": {
6713
+ fg: p.yellow,
6714
+ bold: true
6715
+ },
6716
+ "markup.heading.1": {
6717
+ fg: p.yellow,
6718
+ bold: true
6719
+ },
6720
+ "markup.heading.2": {
6721
+ fg: p.orange,
6722
+ bold: true
6723
+ },
6724
+ "markup.heading.3": {
6725
+ fg: p.blue,
6726
+ bold: true
6727
+ },
6728
+ "markup.bold": {
6729
+ fg: p.fg0,
6730
+ bold: true
6731
+ },
6732
+ "markup.strong": {
6733
+ fg: p.fg0,
6734
+ bold: true
6735
+ },
6736
+ "markup.italic": {
6737
+ fg: p.fg1,
6738
+ italic: true
6739
+ },
6740
+ "markup.link": {
6741
+ fg: p.blue,
6742
+ underline: true
6743
+ },
6744
+ "markup.link.url": {
6745
+ fg: p.blue,
6746
+ underline: true
6747
+ },
6748
+ "markup.list": { fg: p.orange },
6749
+ "markup.raw": { fg: p.green },
6750
+ "markup.raw.block": { fg: p.green },
6751
+ "markup.quote": {
6752
+ fg: p.fg4,
6753
+ italic: true
6754
+ },
6755
+ "keyword": {
6756
+ fg: p.red,
6757
+ bold: true
6758
+ },
6759
+ "keyword.import": {
6760
+ fg: p.red,
6761
+ bold: true
6762
+ },
6763
+ "keyword.operator": { fg: p.orange },
6764
+ "string": { fg: p.green },
6765
+ "string.escape": {
6766
+ fg: p.orange,
6767
+ bold: true
6768
+ },
6769
+ "character": { fg: p.aqua },
6770
+ "comment": {
6771
+ fg: p.gray,
6772
+ italic: true
6773
+ },
6774
+ "number": { fg: p.purple },
6775
+ "boolean": { fg: p.purple },
6776
+ "constant": { fg: p.purple },
6777
+ "constant.builtin": { fg: p.purple },
6778
+ "function": { fg: p.blue },
6779
+ "function.call": { fg: p.blue },
6780
+ "function.method": { fg: p.blue },
6781
+ "function.method.call": { fg: p.blue },
6782
+ "function.builtin": { fg: p.blue },
6783
+ "function.macro": { fg: p.aqua },
6784
+ "type": { fg: p.yellow },
6785
+ "type.builtin": { fg: p.yellow },
6786
+ "constructor": { fg: p.yellow },
6787
+ "attribute": { fg: p.aqua },
6788
+ "tag": { fg: p.aqua },
6789
+ "variable": { fg: p.fg1 },
6790
+ "variable.builtin": { fg: p.red },
6791
+ "variable.parameter": {
6792
+ fg: p.orange,
6793
+ italic: true
6794
+ },
6795
+ "variable.member": { fg: p.fg1 },
6796
+ "property": { fg: p.blue },
6797
+ "operator": { fg: p.orange },
6798
+ "punctuation": { fg: p.fg4 },
6799
+ "punctuation.bracket": { fg: p.fg2 },
6800
+ "punctuation.delimiter": { fg: p.fg4 },
6801
+ "label": { fg: p.aqua }
6802
+ }
6803
+ };
6804
+ }
6805
+ const GRUVBOX_DARK = gruvboxTheme("gruvbox-dark", "Gruvbox Dark", DARK);
6806
+ const GRUVBOX_LIGHT = gruvboxTheme("gruvbox-light", "Gruvbox Light", LIGHT);
6807
+ //#endregion
5957
6808
  //#region src/chat/themes/light.ts
5958
6809
  const TEXT = "#1f2328";
5959
6810
  const DIM = "#656d76";
@@ -6474,6 +7325,8 @@ const BUILTIN_THEMES = {
6474
7325
  [CATPPUCCIN_MACCHIATO.id]: CATPPUCCIN_MACCHIATO,
6475
7326
  [CATPPUCCIN_FRAPPE.id]: CATPPUCCIN_FRAPPE,
6476
7327
  [CATPPUCCIN_LATTE.id]: CATPPUCCIN_LATTE,
7328
+ [GRUVBOX_DARK.id]: GRUVBOX_DARK,
7329
+ [GRUVBOX_LIGHT.id]: GRUVBOX_LIGHT,
6477
7330
  [CRUSH_THEME.id]: CRUSH_THEME,
6478
7331
  [VAPORWAVE_THEME.id]: VAPORWAVE_THEME
6479
7332
  };
@@ -8817,10 +9670,22 @@ async function runOAuthLogin(descriptor, options) {
8817
9670
  options.onUrl(info.url, info.instructions);
8818
9671
  tryOpenBrowser(info.url);
8819
9672
  },
9673
+ onDeviceCode: (info) => {
9674
+ if (options.onDeviceCode) {
9675
+ options.onDeviceCode(info);
9676
+ return;
9677
+ }
9678
+ options.onProgress?.(`Device code: ${info.userCode} — open ${info.verificationUri}`);
9679
+ tryOpenBrowser(info.verificationUri);
9680
+ },
8820
9681
  onPrompt: async (prompt) => {
8821
9682
  if (!options.onPrompt) throw new Error(`OAuth provider "${descriptor.label}" requested user input ("${prompt.message}") but no onPrompt handler is wired.`);
8822
9683
  return options.onPrompt(prompt);
8823
9684
  },
9685
+ onSelect: async (prompt) => {
9686
+ if (options.onSelect) return options.onSelect(prompt);
9687
+ return prompt.options[0]?.id;
9688
+ },
8824
9689
  onProgress: options.onProgress,
8825
9690
  signal: options.signal
8826
9691
  };
@@ -10735,6 +11600,6 @@ function countNeighbors(turnIds, turnId) {
10735
11600
  };
10736
11601
  }
10737
11602
  //#endregion
10738
- export { mcpToolsCachePath as $, ensureKeybindingsFile as $n, modelsForDescriptor as $r, CATPPUCCIN_MACCHIATO as $t, getSafelist as A, COMMUNICATION_DOCTRINE as Ai, applyEditPayload as An, parseSemver as Ar, clipHintsToWidth as At, oauthUsesManualCodePaste as B, buildPlanSystem as Bi, tokenize as Bn, readProviderCredential as Br, SETTINGS_CATEGORIES as Bt, resolveSessionExportTarget as C, isTodoTool as Ci, stripSpawnTokensLine as Cn, bootTick as Cr, isInteractionTool as Ct, useSafeModeQueue as D, setTodosForRun as Di, toolResultText as Dn, compareSemver as Dr, useInteractionsActions as Dt, useSafeModeActions as E, selectActiveTodos as Ei, toolCallPreview as En, checkForUpdate as Er, serializeInteractionResponse as Et, suggestSafelistEntry as F, PLAN_MODE_DOCTRINE as Fi, extractEditPayload as Fn, shouldAutoCompact as Fr, buildHints as Ft, indexOfEntry as G, resolveApprovalForPayload as Gn, OUTPUT_RESERVE_TOKENS as Gr, useSettings as Gt, supportsOAuth as H, maskToOutcomeKinds as Hn, setProviderCredential as Hr, SETTINGS_TOGGLES as Ht, writeProjects as I, PLAN_MODE_DOCTRINE_NO_PROMPTS as Ii, filetypeFromPath as In, detectAuth as Ir, shortChord as It, discoverProjectMcps as J, summarizeOutcomes as Jn, credKeyOf as Jr, resolveChipColor as Jt, buildMcpServers as K, rewriteMultiEditHeader as Kn, anthropicDescriptor as Kr, BUILTIN_THEMES as Kt, splitPromptSegments as L, SUBAGENT_GUIDANCE as Li, previewEditPayload as Ln, applyApiKeyEnv as Lr, listProjectFiles as Lt, matchesSafelistEntry as M, IDENTITY_PREFIX as Mi, buildUnifiedDiff as Mn, performSelfUpdate as Mr, truncateTrailing as Mt, projectsFilePath as N, INTERACTION_GUIDANCE as Ni, computeInlineDiff as Nn, resolvePlatformPackage as Nr, cleanTitle as Nt, IMPLICITLY_SAFE_TOOLS as O, useActiveTodos as Oi, turnSelectionOwnership as On, detectLibc as Or, useInteractionsQueue as Ot, readProjects as P, INTERACTION_GUIDANCE_NO_PROMPTS as Pi, computeLineDiff as Pn, AUTO_COMPACT_MIN_GROWTH_FRACTION as Pr, generateSessionTitle as Pt, loadMcpToolsCache as Q, KEYBINDING_KEY_COL_WIDTH as Qn, modelSupportsReasoning as Qr, CATPPUCCIN_LATTE as Qt, formatPathForCwd as R, TOKEN_DISCIPLINE_DOCTRINE as Ri, splitLines as Rn, credentialsPath as Rr, useEnabledToggleSet as Rt, renderSession as S, getTodosForRun as Si, selectableTurnIds as Sn, bootProfileEnabled as Sr, createInteractionTools as St, SafeModeProvider as T, pruneTodosByRun as Ti, titleFromTurns as Tn, useUpdateCheck as Tr, pendingInteractionsFromTurns as Tt, buildModelCatalog as U, mergeApprovalAndBodyOutcomes as Un, writeCredentials as Ur, SettingsProvider as Ut, runOAuthLogin as V, envSection as Vi, buildEditOutcomesAnnotation as Vn, removeProviderCredential as Vr, SETTINGS_CHOICES as Vt, filterModelCatalog as W, parseEditOutcomesFromResult as Wn, BUILTIN_PROVIDERS as Wr, clampFps as Wt, projectUserPaths as X, KEYBINDING_DEFS as Xn, getContextWindow as Xr, VAPORWAVE_THEME as Xt, parseMcpsFile as Y, DEFAULT_KEYBINDINGS as Yn, effectiveContextWindow as Yr, resolveTheme as Yt, clearMcpToolsCache as Z, KEYBINDING_DEF_BY_ACTION as Zn, getModelInfo as Zr, CATPPUCCIN_FRAPPE as Zt, turnContextSize as _, TODOWRITE_TOOL as _i, lastContextSizeFromTurns as _n, mergeReferences as _r, splitMarkdownCodeBlocks as _t, computeTurnAnchors as a, findGitRoot$1 as ai, ConfigProvider as an, parseBindingSpec as ar, useMcpToolToggleSet as at, defaultSkillScanPaths as b, createTodoTools as bi, marginTopFor as bn, buildLinearRamp as br, PRESENT_PLAN_TOOL as bt, formatToolCall as c, DEFAULT_AGENT_ID as ci, resolveStoragePaths as cn, SKILLS_TRIGGER as cr, parentServerName as ct, useSelectStyle as d, PLAN_AGENT as di, createStateStore as dn, FILES_TRIGGER as dr, patchMcpCredential as dt, openaiDescriptor as ei, CATPPUCCIN_MOCHA as en, formatBindingForDisplay as er, refreshMcpToolsCatalog as et, useSurfaces as f, accentColor as fi, deriveSessionTitle as fn, createFilesCompletionProvider as fr, McpAuthProvider as ft, finalizeStreamingMarkdownForOwner as g, TODOS_METADATA_KEY as gi, isVisible as gn, findActiveTrigger as gr, reduceMcpAuth as gt, finalizeStreamingMarkdown as h, TODOREAD_TOOL as hi, isTurnHighlighted as hn, collectReferences as hr, getMcpAuthStatus as ht, turnAsText as i, renderAgentsMdBlock as ii, useDiscoveryOptional as in, mergeKeybindings as ir, useMcpToolToggleMap as it, isOnSafelist as j, DOING_TASKS_DOCTRINE as ji, buildContextualDiff as jn, performInPlaceSelfUpdate as jr, hintsLength as jt, addToSafelist as k, ACTIONS_WITH_CARE_DOCTRINE as ki, updateToolEventOutcomes as kn, detectPackageManager as kr, EMPTY_HINTS as kt, ThemeProvider as l, DEFAULT_BUDGET_EXCLUDE_TOOLS as li, resolveStorageDirs as ln, createSkillsCompletionProvider as lr, createFileMcpCredentialStore as lt, useTheme as m, singleAgentRegistry as mi, isEditErrorResult as mn, applyInsert as mr, useMcpAuthState as mt, deleteTurnSafely as n, piIdOf as ni, DiscoveryProvider as nn, keybindingsPath as nr, subscribeMcpToolsCache as nt, TOOL_DISPLAY as o, BUILD_AGENT as oi, useConfig as on, readKeybindings as or, buildVisibleMcpRows as ot, useSyntaxStyles as p, resolveAgentId as pi, eventsFromTurns as pn, uniqueFilesFromReferences as pr, useMcpAuthDispatch as pt, defaultMcpsConfigPaths as q, stripEditOutcomesAnnotation as qn, cerebrasDescriptor as qr, DEFAULT_THEME as qt, truncateTurnsAt as r, discoverAgentsMd as ri, useDiscovery as rn, matchesBinding as rr, buildToolToggle as rt, displayNameFor as s, BUILTIN_AGENTS as si, resolveConfig as sn, stripJsonComments as sr, indexOfServerRow as st, countNeighbors as t, openrouterDescriptor as ti, createDiscoverySlot as tn, groupBindings as tr, saveMcpToolsCache as tt, useColors as u, DEFAULT_PERSIST_EXCLUDE_TOOLS as ui, EDIT_TOOL_NAMES as un, uniqueSkillNamesFromReferences as ur, mcpCredentialsPath as ut, useStreamBuffer as v, TODO_STATUS_GLYPHS as vi, listSessionMeta as vn, useCompletion as vr, ASK_USER_TOOL as vt, writeSessionExport as w, pickActiveRunId as wi, sumRunCosts as wn, buildUpdateHint as wr, makeRequestInteraction as wt, discoverProjectSkills as x, getArchivedTodosForRun as xi, saveState as xn, tryOpenBrowser as xr, buildResumedToolResultsTurn as xt, buildSkillsConfig as y, TODO_WRITE_COUNTS_METADATA_KEY as yi, loadState as yn, blendHsl as yr, InteractionsProvider as yt, fetchOAuthRedirect as z, buildBuildSystem as zi, summarizeEditPayload as zn, readCredentials as zr, DEFAULT_SETTINGS as zt };
11603
+ export { mcpToolsCachePath as $, KEYBINDING_DEF_BY_ACTION as $n, getModelInfo as $r, CATPPUCCIN_FRAPPE as $t, getSafelist as A, useActiveTodos as Ai, turnSelectionOwnership as An, detectLibc as Ar, clipHintsToWidth as At, oauthUsesManualCodePaste as B, TOKEN_DISCIPLINE_DOCTRINE as Bi, splitLines as Bn, credentialsPath as Br, SETTINGS_CATEGORIES as Bt, resolveSessionExportTarget as C, getArchivedTodosForRun as Ci, saveState as Cn, tryOpenBrowser as Cr, isInteractionTool as Ct, useSafeModeQueue as D, pruneTodosByRun as Di, titleFromTurns as Dn, useUpdateCheck as Dr, useInteractionsActions as Dt, useSafeModeActions as E, pickActiveRunId as Ei, sumRunCosts as En, buildUpdateHint as Er, serializeInteractionResponse as Et, suggestSafelistEntry as F, INTERACTION_GUIDANCE as Fi, computeInlineDiff as Fn, resolvePlatformPackage as Fr, buildHints as Ft, indexOfEntry as G, mergeApprovalAndBodyOutcomes as Gn, writeCredentials as Gr, useSettings as Gt, supportsOAuth as H, buildPlanSystem as Hi, tokenize as Hn, readProviderCredential as Hr, SETTINGS_TOGGLES as Ht, writeProjects as I, INTERACTION_GUIDANCE_NO_PROMPTS as Ii, computeLineDiff as In, AUTO_COMPACT_MIN_GROWTH_FRACTION as Ir, shortChord as It, discoverProjectMcps as J, rewriteMultiEditHeader as Jn, anthropicDescriptor as Jr, resolveChipColor as Jt, buildMcpServers as K, parseEditOutcomesFromResult as Kn, BUILTIN_PROVIDERS as Kr, BUILTIN_THEMES as Kt, splitPromptSegments as L, PLAN_MODE_DOCTRINE as Li, extractEditPayload as Ln, shouldAutoCompact as Lr, listProjectFiles as Lt, matchesSafelistEntry as M, COMMUNICATION_DOCTRINE as Mi, applyEditPayload as Mn, parseSemver as Mr, truncateTrailing as Mt, projectsFilePath as N, DOING_TASKS_DOCTRINE as Ni, buildContextualDiff as Nn, performInPlaceSelfUpdate as Nr, cleanTitle as Nt, IMPLICITLY_SAFE_TOOLS as O, selectActiveTodos as Oi, toolCallPreview as On, checkForUpdate as Or, useInteractionsQueue as Ot, readProjects as P, IDENTITY_PREFIX as Pi, buildUnifiedDiff as Pn, performSelfUpdate as Pr, generateSessionTitle as Pt, loadMcpToolsCache as Q, KEYBINDING_DEFS as Qn, getContextWindow as Qr, GRUVBOX_LIGHT as Qt, formatPathForCwd as R, PLAN_MODE_DOCTRINE_NO_PROMPTS as Ri, filetypeFromPath as Rn, detectAuth as Rr, useEnabledToggleSet as Rt, renderSession as S, createTodoTools as Si, marginTopFor as Sn, buildLinearRamp as Sr, createInteractionTools as St, SafeModeProvider as T, isTodoTool as Ti, stripSpawnTokensLine as Tn, bootTick as Tr, pendingInteractionsFromTurns as Tt, buildModelCatalog as U, envSection as Ui, buildEditOutcomesAnnotation as Un, removeProviderCredential as Ur, SettingsProvider as Ut, runOAuthLogin as V, buildBuildSystem as Vi, summarizeEditPayload as Vn, readCredentials as Vr, SETTINGS_CHOICES as Vt, filterModelCatalog as W, maskToOutcomeKinds as Wn, setProviderCredential as Wr, clampFps as Wt, projectUserPaths as X, summarizeOutcomes as Xn, credKeyOf as Xr, VAPORWAVE_THEME as Xt, parseMcpsFile as Y, stripEditOutcomesAnnotation as Yn, cerebrasDescriptor as Yr, resolveTheme as Yt, clearMcpToolsCache as Z, DEFAULT_KEYBINDINGS as Zn, effectiveContextWindow as Zr, GRUVBOX_DARK as Zt, turnContextSize as _, TODOREAD_TOOL as _i, isTurnHighlighted as _n, collectReferences as _r, splitMarkdownCodeBlocks as _t, computeTurnAnchors as a, discoverAgentsMd as ai, useDiscovery as an, matchesBinding as ar, useMcpToolToggleSet as at, defaultSkillScanPaths as b, TODO_STATUS_GLYPHS as bi, listSessionMeta as bn, useCompletion as br, PRESENT_PLAN_TOOL as bt, formatToolCall as c, BUILD_AGENT as ci, useConfig as cn, readKeybindings as cr, parentServerName as ct, useSelectStyle as d, DEFAULT_BUDGET_EXCLUDE_TOOLS as di, resolveStorageDirs as dn, createSkillsCompletionProvider as dr, patchMcpCredential as dt, modelSupportsReasoning as ei, CATPPUCCIN_LATTE as en, KEYBINDING_KEY_COL_WIDTH as er, refreshMcpToolsCatalog as et, useSurfaces as f, DEFAULT_PERSIST_EXCLUDE_TOOLS as fi, EDIT_TOOL_NAMES as fn, uniqueSkillNamesFromReferences as fr, McpAuthProvider as ft, finalizeStreamingMarkdownForOwner as g, singleAgentRegistry as gi, isEditErrorResult as gn, applyInsert as gr, reduceMcpAuth as gt, finalizeStreamingMarkdown as h, resolveAgentId as hi, eventsFromTurns as hn, uniqueFilesFromReferences as hr, getMcpAuthStatus as ht, turnAsText as i, piIdOf as ii, DiscoveryProvider as in, keybindingsPath as ir, useMcpToolToggleMap as it, isOnSafelist as j, ACTIONS_WITH_CARE_DOCTRINE as ji, updateToolEventOutcomes as jn, detectPackageManager as jr, hintsLength as jt, addToSafelist as k, setTodosForRun as ki, toolResultText as kn, compareSemver as kr, EMPTY_HINTS as kt, ThemeProvider as l, BUILTIN_AGENTS as li, resolveConfig as ln, stripJsonComments as lr, createFileMcpCredentialStore as lt, useTheme as m, accentColor as mi, deriveSessionTitle as mn, createFilesCompletionProvider as mr, useMcpAuthState as mt, deleteTurnSafely as n, openaiDescriptor as ni, CATPPUCCIN_MOCHA as nn, formatBindingForDisplay as nr, subscribeMcpToolsCache as nt, TOOL_DISPLAY as o, renderAgentsMdBlock as oi, useDiscoveryOptional as on, mergeKeybindings as or, buildVisibleMcpRows as ot, useSyntaxStyles as p, PLAN_AGENT as pi, createStateStore as pn, FILES_TRIGGER as pr, useMcpAuthDispatch as pt, defaultMcpsConfigPaths as q, resolveApprovalForPayload as qn, OUTPUT_RESERVE_TOKENS as qr, DEFAULT_THEME as qt, truncateTurnsAt as r, openrouterDescriptor as ri, createDiscoverySlot as rn, groupBindings as rr, buildToolToggle as rt, displayNameFor as s, findGitRoot$1 as si, ConfigProvider as sn, parseBindingSpec as sr, indexOfServerRow as st, countNeighbors as t, modelsForDescriptor as ti, CATPPUCCIN_MACCHIATO as tn, ensureKeybindingsFile as tr, saveMcpToolsCache as tt, useColors as u, DEFAULT_AGENT_ID as ui, resolveStoragePaths as un, SKILLS_TRIGGER as ur, mcpCredentialsPath as ut, useStreamBuffer as v, TODOS_METADATA_KEY as vi, isVisible as vn, findActiveTrigger as vr, ASK_USER_TOOL as vt, writeSessionExport as w, getTodosForRun as wi, selectableTurnIds as wn, bootProfileEnabled as wr, makeRequestInteraction as wt, discoverProjectSkills as x, TODO_WRITE_COUNTS_METADATA_KEY as xi, loadState as xn, blendHsl as xr, buildResumedToolResultsTurn as xt, buildSkillsConfig as y, TODOWRITE_TOOL as yi, lastContextSizeFromTurns as yn, mergeReferences as yr, InteractionsProvider as yt, fetchOAuthRedirect as z, SUBAGENT_GUIDANCE as zi, previewEditPayload as zn, applyApiKeyEnv as zr, DEFAULT_SETTINGS as zt };
10739
11604
 
10740
- //# sourceMappingURL=turn-operations-CxE8BBau.js.map
11605
+ //# sourceMappingURL=turn-operations-6Yls2HuG.js.map