zidane 5.6.12 → 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 (52) hide show
  1. package/README.md +3 -1
  2. package/dist/{agent-NwJU7c_W.d.ts → agent-ClkpElCZ.d.ts} +179 -39
  3. package/dist/agent-ClkpElCZ.d.ts.map +1 -0
  4. package/dist/chat.d.ts +19 -5
  5. package/dist/chat.d.ts.map +1 -1
  6. package/dist/chat.js +2 -2
  7. package/dist/{index-Bd0kbj4D.d.ts → index-CTDMMdIy.d.ts} +2 -2
  8. package/dist/{index-Bd0kbj4D.d.ts.map → index-CTDMMdIy.d.ts.map} +1 -1
  9. package/dist/{index-CZIOuAw7.d.ts → index-v3Tzobqr.d.ts} +2 -2
  10. package/dist/{index-CZIOuAw7.d.ts.map → index-v3Tzobqr.d.ts.map} +1 -1
  11. package/dist/index.d.ts +4 -4
  12. package/dist/index.js +7 -7
  13. package/dist/{login-BmPfubSA.js → login-DS3sf6b5.js} +3 -3
  14. package/dist/{login-BmPfubSA.js.map → login-DS3sf6b5.js.map} +1 -1
  15. package/dist/mcp.d.ts +1 -1
  16. package/dist/{messages-BRrEqWzH.js → messages-Dym8S_YH.js} +45 -4
  17. package/dist/messages-Dym8S_YH.js.map +1 -0
  18. package/dist/{presets-B0JQGmDK.js → presets-CZXS_87d.js} +2 -2
  19. package/dist/{presets-B0JQGmDK.js.map → presets-CZXS_87d.js.map} +1 -1
  20. package/dist/presets.d.ts +2 -2
  21. package/dist/presets.js +1 -1
  22. package/dist/{providers-f-2jAcUL.js → providers-beXyD9W9.js} +11 -7
  23. package/dist/providers-beXyD9W9.js.map +1 -0
  24. package/dist/providers.d.ts +1 -1
  25. package/dist/providers.js +2 -2
  26. package/dist/restate.d.ts +1 -1
  27. package/dist/session/sqlite.d.ts +1 -1
  28. package/dist/{session-CPZP-ZWO.js → session-BRIsmBSY.js} +5 -2
  29. package/dist/session-BRIsmBSY.js.map +1 -0
  30. package/dist/session.d.ts +2 -2
  31. package/dist/session.js +3 -3
  32. package/dist/skills.d.ts +2 -2
  33. package/dist/{tools-CQuDw7Sw.js → tools-DE9pR_NG.js} +4 -2
  34. package/dist/tools-DE9pR_NG.js.map +1 -0
  35. package/dist/tools.d.ts +2 -2
  36. package/dist/tools.js +1 -1
  37. package/dist/{transcript-anchors-awwdnicP.d.ts → transcript-anchors-D0TR6djV.d.ts} +4 -4
  38. package/dist/transcript-anchors-D0TR6djV.d.ts.map +1 -0
  39. package/dist/tui.d.ts +2 -2
  40. package/dist/tui.js +5 -5
  41. package/dist/{turn-operations-Dz_vp9En.js → turn-operations-6Yls2HuG.js} +645 -8
  42. package/dist/turn-operations-6Yls2HuG.js.map +1 -0
  43. package/dist/types.d.ts +2 -2
  44. package/docs/ARCHITECTURE.md +5 -3
  45. package/package.json +2 -2
  46. package/dist/agent-NwJU7c_W.d.ts.map +0 -1
  47. package/dist/messages-BRrEqWzH.js.map +0 -1
  48. package/dist/providers-f-2jAcUL.js.map +0 -1
  49. package/dist/session-CPZP-ZWO.js.map +0 -1
  50. package/dist/tools-CQuDw7Sw.js.map +0 -1
  51. package/dist/transcript-anchors-awwdnicP.d.ts.map +0 -1
  52. package/dist/turn-operations-Dz_vp9En.js.map +0 -1
@@ -1,12 +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$1, n as createSpawnTool, o as listFiles, t as writeFile$1, u as edit, y as shell } from "./tools-CQuDw7Sw.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 { D as joinSystemPrompt } from "./messages-BRrEqWzH.js";
4
+ import { O as joinSystemPrompt } from "./messages-Dym8S_YH.js";
5
5
  import { r as normalizeMcpServers, t as connectMcpServers } from "./mcp-DGeB7-3D.js";
6
6
  import { a as discoverSkills } from "./interpolate-DM1UcKeQ.js";
7
7
  import { n as formatTokenUsage } from "./stats-Lc3zL3RM.js";
8
- import { n as definePreset, t as composePresets } from "./presets-B0JQGmDK.js";
9
- import { i as anthropic, n as openai, o as writeFileAtomic, r as cerebras, t as openrouter } from "./providers-f-2jAcUL.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";
10
10
  import { createRequire } from "node:module";
11
11
  import { dirname, isAbsolute, join, posix, relative, resolve, sep } from "node:path";
12
12
  import { homedir, tmpdir } from "node:os";
@@ -14,9 +14,10 @@ import { spawn } from "node:child_process";
14
14
  import { chmodSync, createReadStream, createWriteStream, existsSync, mkdirSync, mkdtempSync, readFileSync, realpathSync, renameSync, rmSync, statSync, writeFileSync } from "node:fs";
15
15
  import { readdir, stat, writeFile } from "node:fs/promises";
16
16
  import { Buffer as Buffer$1 } from "node:buffer";
17
- import { getModel, getModels } from "@mariozechner/pi-ai";
18
- import { anthropicOAuthProvider, openaiCodexOAuthProvider } from "@mariozechner/pi-ai/oauth";
19
- 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";
20
21
  import { createGunzip } from "node:zlib";
21
22
  import { createContext, useCallback, useContext, useEffect, useMemo, useRef, useState } from "react";
22
23
  import { jsx } from "react/jsx-runtime";
@@ -1300,7 +1301,631 @@ function readIfPresent(path, source) {
1300
1301
  };
1301
1302
  }
1302
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
1303
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);
1304
1929
  /** Convenience accessor — returns `credentialFileKey ?? key`. */
1305
1930
  function credKeyOf(desc) {
1306
1931
  return desc.credentialFileKey ?? desc.key;
@@ -9045,10 +9670,22 @@ async function runOAuthLogin(descriptor, options) {
9045
9670
  options.onUrl(info.url, info.instructions);
9046
9671
  tryOpenBrowser(info.url);
9047
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
+ },
9048
9681
  onPrompt: async (prompt) => {
9049
9682
  if (!options.onPrompt) throw new Error(`OAuth provider "${descriptor.label}" requested user input ("${prompt.message}") but no onPrompt handler is wired.`);
9050
9683
  return options.onPrompt(prompt);
9051
9684
  },
9685
+ onSelect: async (prompt) => {
9686
+ if (options.onSelect) return options.onSelect(prompt);
9687
+ return prompt.options[0]?.id;
9688
+ },
9052
9689
  onProgress: options.onProgress,
9053
9690
  signal: options.signal
9054
9691
  };
@@ -10965,4 +11602,4 @@ function countNeighbors(turnIds, turnId) {
10965
11602
  //#endregion
10966
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 };
10967
11604
 
10968
- //# sourceMappingURL=turn-operations-Dz_vp9En.js.map
11605
+ //# sourceMappingURL=turn-operations-6Yls2HuG.js.map