zidane 5.13.1 → 5.13.2

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 (40) hide show
  1. package/README.md +5 -1
  2. package/dist/{acp-CEE6C0m_.js → acp-BqIU2mo-.js} +109 -12
  3. package/dist/acp-BqIU2mo-.js.map +1 -0
  4. package/dist/acp-cli.js +163 -25
  5. package/dist/acp-cli.js.map +1 -1
  6. package/dist/acp.d.ts +41 -2
  7. package/dist/acp.d.ts.map +1 -1
  8. package/dist/acp.js +1 -1
  9. package/dist/chat.js +3 -2
  10. package/dist/chat.js.map +1 -1
  11. package/dist/eval.js +2 -2
  12. package/dist/{headless-Bz1SpTB-.js → headless-0O6HMNBQ.js} +5 -5
  13. package/dist/{headless-Bz1SpTB-.js.map → headless-0O6HMNBQ.js.map} +1 -1
  14. package/dist/headless.js +1 -1
  15. package/dist/index.js +6 -6
  16. package/dist/{messages-CGazSyTL.js → messages-DEsLGBB9.js} +2 -2
  17. package/dist/{messages-CGazSyTL.js.map → messages-DEsLGBB9.js.map} +1 -1
  18. package/dist/output/stream-json.js +1 -1
  19. package/dist/{presets-5IGiSsxm.js → presets-HDIxliiq.js} +2 -2
  20. package/dist/{presets-5IGiSsxm.js.map → presets-HDIxliiq.js.map} +1 -1
  21. package/dist/presets.js +1 -1
  22. package/dist/{providers-DMPAmUuY.js → providers-Cz-RNYZO.js} +2 -2
  23. package/dist/{providers-DMPAmUuY.js.map → providers-Cz-RNYZO.js.map} +1 -1
  24. package/dist/providers.js +2 -2
  25. package/dist/{session-B69BQSn1.js → session-BDWZZaYa.js} +2 -2
  26. package/dist/{session-B69BQSn1.js.map → session-BDWZZaYa.js.map} +1 -1
  27. package/dist/session.js +2 -2
  28. package/dist/{tools-CfS7rjuh.js → tools-DhzKzB1y.js} +2 -2
  29. package/dist/{tools-CfS7rjuh.js.map → tools-DhzKzB1y.js.map} +1 -1
  30. package/dist/tools.js +1 -1
  31. package/dist/{transcript-anchors-B_a1edre.js → transcript-anchors-Cq-8gx8u.js} +7 -1415
  32. package/dist/transcript-anchors-Cq-8gx8u.js.map +1 -0
  33. package/dist/tui.js +6 -5
  34. package/dist/tui.js.map +1 -1
  35. package/dist/xdg-zlSeVBhQ.js +1417 -0
  36. package/dist/xdg-zlSeVBhQ.js.map +1 -0
  37. package/docs/ACP.md +68 -0
  38. package/package.json +1 -1
  39. package/dist/acp-CEE6C0m_.js.map +0 -1
  40. package/dist/transcript-anchors-B_a1edre.js.map +0 -1
@@ -1,5 +1,5 @@
1
- import { A as joinSystemPrompt } from "./messages-CGazSyTL.js";
2
- import { a as multiEdit, c as grep, 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-CfS7rjuh.js";
1
+ import { A as joinSystemPrompt } from "./messages-DEsLGBB9.js";
2
+ import { a as multiEdit, c as grep, 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-DhzKzB1y.js";
3
3
  import { c as shortId, r as fmtTokens, s as previewLine } from "./format-BNOXpl-1.js";
4
4
  import { o as toolResultToText } from "./types-DxHDaqN7.js";
5
5
  import { l as errorMessage } from "./errors-BpPfMo_4.js";
@@ -7,8 +7,8 @@ import { t as writeFileAtomic } from "./atomic-write-Bgtr5JPu.js";
7
7
  import { o as discoverSkills } from "./interpolate-ConAiXGy.js";
8
8
  import { r as formatTokenUsage } from "./stats-DAKBEKjc.js";
9
9
  import { a as normalizeMcpServers, n as connectMcpServers } from "./mcp-C_TIj91j.js";
10
- import { n as definePreset, t as composePresets } from "./presets-5IGiSsxm.js";
11
- import { a as local, c as generatePkce, f as arcee, g as FAST_MODE_OPTIONS, h as ANTHROPIC_EXTRA_MODELS, i as openai, l as cerebras, n as createXaiOAuthProvider, p as anthropic, r as openrouter, s as createCursorOAuthProvider, t as xai, u as baseten } from "./providers-DMPAmUuY.js";
10
+ import { n as definePreset, t as composePresets } from "./presets-HDIxliiq.js";
11
+ import { C as restoreModelOptions, f as credKeyOf, i as readCredentials, l as BUILTIN_PROVIDERS, n as applyApiKeyEnv, r as credentialsPath, t as resolveStorageDirs, v as modelSupportsReasoning, y as modelsForDescriptor } from "./xdg-zlSeVBhQ.js";
12
12
  import { r as fetchUrl } from "./fetch-url-Cgbq-HYx.js";
13
13
  import { webSearch } from "./tools/web-search.js";
14
14
  import { D as extractEditPayload, I as parseEditOutcomesFromResult, J as collectReferences, Y as findActiveTrigger, q as applyInsert, u as ownerOf } from "./turn-operations-DLWN2J7f.js";
@@ -17,11 +17,8 @@ import { dirname, isAbsolute, join, posix, relative, resolve, sep } from "node:p
17
17
  import { chmodSync, createReadStream, createWriteStream, existsSync, mkdirSync, mkdtempSync, readFileSync, realpathSync, renameSync, rmSync, statSync, writeFileSync } from "node:fs";
18
18
  import { readdir, stat, writeFile } from "node:fs/promises";
19
19
  import { Buffer as Buffer$1 } from "node:buffer";
20
- import { createHash, randomBytes } from "node:crypto";
21
- import { getModel, getModels } from "@earendil-works/pi-ai";
20
+ import { createHash } from "node:crypto";
22
21
  import { homedir, tmpdir } from "node:os";
23
- import { createServer } from "node:http";
24
- import { refreshAnthropicToken, refreshOpenAICodexToken, registerOAuthProvider } from "@earendil-works/pi-ai/oauth";
25
22
  import { spawn } from "node:child_process";
26
23
  import { createGunzip } from "node:zlib";
27
24
  import { createContext, useCallback, useContext, useEffect, useMemo, useRef, useState } from "react";
@@ -1389,1331 +1386,6 @@ function readIfPresent(path, source) {
1389
1386
  };
1390
1387
  }
1391
1388
  //#endregion
1392
- //#region src/chat/oauth-page/server.ts
1393
- const CALLBACK_HOST = process.env.PI_OAUTH_CALLBACK_HOST || "127.0.0.1";
1394
- function buildRequestHandler(opts, pending) {
1395
- return (req, res) => {
1396
- try {
1397
- const url = new URL(req.url || "", "http://localhost");
1398
- if (url.pathname !== opts.path) {
1399
- respondError(res, 404, opts, "Callback route not found.");
1400
- return;
1401
- }
1402
- const error = url.searchParams.get("error");
1403
- if (error) {
1404
- respondError(res, 400, opts, `${opts.providerName} authentication did not complete.`, `Error: ${error}`);
1405
- return;
1406
- }
1407
- const code = url.searchParams.get("code");
1408
- const state = url.searchParams.get("state");
1409
- if (!code || !state) {
1410
- respondError(res, 400, opts, "Missing code or state parameter.");
1411
- return;
1412
- }
1413
- if (state !== opts.expectedState) {
1414
- respondError(res, 400, opts, "State mismatch.");
1415
- return;
1416
- }
1417
- res.writeHead(200, { "Content-Type": "text/html; charset=utf-8" });
1418
- res.end(opts.renderPage({
1419
- kind: "success",
1420
- provider: opts.providerName,
1421
- message: opts.successMessage
1422
- }));
1423
- pending.settle({
1424
- code,
1425
- state
1426
- });
1427
- } catch {
1428
- respondError(res, 500, opts, "Internal error while processing the callback.");
1429
- }
1430
- };
1431
- }
1432
- function respondError(res, status, opts, message, details) {
1433
- res.writeHead(status, { "Content-Type": "text/html; charset=utf-8" });
1434
- res.end(opts.renderPage({
1435
- kind: "error",
1436
- provider: opts.providerName,
1437
- message,
1438
- details
1439
- }));
1440
- }
1441
- function buildStubHandle(redirectUri, server) {
1442
- return {
1443
- redirectUri,
1444
- waitForCode: async () => null,
1445
- cancelWait: () => {},
1446
- close: () => {
1447
- try {
1448
- server?.close();
1449
- } catch {}
1450
- }
1451
- };
1452
- }
1453
- /**
1454
- * Start the loopback HTTP callback server. The returned handle is the
1455
- * caller's contract — they `await waitForCode()`, race it against manual
1456
- * paste, then `close()` in a `finally`.
1457
- */
1458
- async function startCallbackServer(opts) {
1459
- const onListenError = opts.onListenError ?? "reject";
1460
- const redirectUri = `http://localhost:${opts.port}${opts.path}`;
1461
- return new Promise((resolve, reject) => {
1462
- let settled = false;
1463
- const pending = { settle: () => {} };
1464
- const waitPromise = new Promise((resolveWait) => {
1465
- pending.settle = (value) => {
1466
- if (settled) return;
1467
- settled = true;
1468
- resolveWait(value);
1469
- };
1470
- });
1471
- const server = createServer(buildRequestHandler(opts, pending));
1472
- server.on("error", (err) => {
1473
- if (onListenError === "resolveWithStub") {
1474
- pending.settle(null);
1475
- resolve(buildStubHandle(redirectUri, server));
1476
- return;
1477
- }
1478
- reject(err);
1479
- });
1480
- server.listen(opts.port, CALLBACK_HOST, () => {
1481
- resolve({
1482
- redirectUri,
1483
- waitForCode: () => waitPromise,
1484
- cancelWait: () => pending.settle(null),
1485
- close: () => {
1486
- try {
1487
- server.close();
1488
- } catch {}
1489
- }
1490
- });
1491
- });
1492
- });
1493
- }
1494
- //#endregion
1495
- //#region src/chat/oauth-page/anthropic.ts
1496
- const CLIENT_ID$1 = atob("OWQxYzI1MGEtZTYxYi00NGQ5LTg4ZWQtNTk0NGQxOTYyZjVl");
1497
- const AUTHORIZE_URL$1 = "https://claude.ai/oauth/authorize";
1498
- const TOKEN_URL$1 = "https://platform.claude.com/v1/oauth/token";
1499
- const CALLBACK_PORT$1 = 53692;
1500
- const CALLBACK_PATH$1 = "/callback";
1501
- const SCOPES = "org:create_api_key user:profile user:inference user:sessions:claude_code user:mcp_servers user:file_upload";
1502
- const PROVIDER_NAME$1 = "Anthropic";
1503
- /**
1504
- * Parse what the user pasted into the manual-code prompt. Accepts:
1505
- * - the bare authorization code
1506
- * - the full `redirect_uri?code=...&state=...` URL
1507
- * - the `code#state` shorthand Anthropic surfaces on the page
1508
- * - a raw query string with `code=...&state=...`
1509
- *
1510
- * Matches pi-ai's parse table so an existing user's muscle memory still works.
1511
- */
1512
- function parseAuthorizationInput$1(input) {
1513
- const value = input.trim();
1514
- if (!value) return {};
1515
- try {
1516
- const url = new URL(value);
1517
- return {
1518
- code: url.searchParams.get("code") ?? void 0,
1519
- state: url.searchParams.get("state") ?? void 0
1520
- };
1521
- } catch {}
1522
- if (value.includes("#")) {
1523
- const [code, state] = value.split("#", 2);
1524
- return {
1525
- code,
1526
- state
1527
- };
1528
- }
1529
- if (value.includes("code=")) {
1530
- const params = new URLSearchParams(value);
1531
- return {
1532
- code: params.get("code") ?? void 0,
1533
- state: params.get("state") ?? void 0
1534
- };
1535
- }
1536
- return { code: value };
1537
- }
1538
- async function postJson(url, body) {
1539
- const response = await fetch(url, {
1540
- method: "POST",
1541
- headers: {
1542
- "Content-Type": "application/json",
1543
- "Accept": "application/json"
1544
- },
1545
- body: JSON.stringify(body),
1546
- signal: AbortSignal.timeout(3e4)
1547
- });
1548
- const responseBody = await response.text();
1549
- if (!response.ok) throw new Error(`HTTP request failed. status=${response.status}; url=${url}; body=${responseBody}`);
1550
- return responseBody;
1551
- }
1552
- async function exchangeAuthorizationCode$1(code, state, verifier, redirectUri) {
1553
- const responseBody = await postJson(TOKEN_URL$1, {
1554
- grant_type: "authorization_code",
1555
- client_id: CLIENT_ID$1,
1556
- code,
1557
- state,
1558
- redirect_uri: redirectUri,
1559
- code_verifier: verifier
1560
- });
1561
- const tokenData = JSON.parse(responseBody);
1562
- return {
1563
- refresh: tokenData.refresh_token,
1564
- access: tokenData.access_token,
1565
- expires: Date.now() + tokenData.expires_in * 1e3 - 300 * 1e3
1566
- };
1567
- }
1568
- async function loginAnthropicWithCustomPage(options) {
1569
- const { verifier, challenge } = await generatePkce();
1570
- const server = await startCallbackServer({
1571
- port: CALLBACK_PORT$1,
1572
- path: CALLBACK_PATH$1,
1573
- expectedState: verifier,
1574
- providerName: PROVIDER_NAME$1,
1575
- renderPage: options.renderPage,
1576
- successMessage: "Anthropic authentication completed. You can close this window.",
1577
- onListenError: "reject"
1578
- });
1579
- let code;
1580
- let state;
1581
- try {
1582
- const authParams = new URLSearchParams({
1583
- code: "true",
1584
- client_id: CLIENT_ID$1,
1585
- response_type: "code",
1586
- redirect_uri: server.redirectUri,
1587
- scope: SCOPES,
1588
- code_challenge: challenge,
1589
- code_challenge_method: "S256",
1590
- state: verifier
1591
- });
1592
- options.onAuth({
1593
- url: `${AUTHORIZE_URL$1}?${authParams.toString()}`,
1594
- instructions: "Complete login in your browser. If the browser is on another machine, paste the final redirect URL here."
1595
- });
1596
- if (options.onManualCodeInput) {
1597
- let manualInput;
1598
- let manualError;
1599
- const manualPromise = options.onManualCodeInput().then((input) => {
1600
- manualInput = input;
1601
- server.cancelWait();
1602
- }).catch((err) => {
1603
- manualError = err instanceof Error ? err : new Error(String(err));
1604
- server.cancelWait();
1605
- });
1606
- const result = await server.waitForCode();
1607
- if (manualError) throw manualError;
1608
- if (result?.code) {
1609
- code = result.code;
1610
- state = result.state;
1611
- } else if (manualInput) {
1612
- const parsed = parseAuthorizationInput$1(manualInput);
1613
- if (parsed.state && parsed.state !== verifier) throw new Error("OAuth state mismatch");
1614
- code = parsed.code;
1615
- state = parsed.state ?? verifier;
1616
- }
1617
- if (!code) {
1618
- await manualPromise;
1619
- if (manualError) throw manualError;
1620
- if (manualInput) {
1621
- const parsed = parseAuthorizationInput$1(manualInput);
1622
- if (parsed.state && parsed.state !== verifier) throw new Error("OAuth state mismatch");
1623
- code = parsed.code;
1624
- state = parsed.state ?? verifier;
1625
- }
1626
- }
1627
- } else {
1628
- const result = await server.waitForCode();
1629
- if (result?.code) {
1630
- code = result.code;
1631
- state = result.state;
1632
- }
1633
- }
1634
- if (!code) {
1635
- const parsed = parseAuthorizationInput$1(await options.onPrompt({
1636
- message: "Paste the authorization code or full redirect URL:",
1637
- placeholder: server.redirectUri
1638
- }));
1639
- if (parsed.state && parsed.state !== verifier) throw new Error("OAuth state mismatch");
1640
- code = parsed.code;
1641
- state = parsed.state ?? verifier;
1642
- }
1643
- if (!code) throw new Error("Missing authorization code");
1644
- if (!state) throw new Error("Missing OAuth state");
1645
- options.onProgress?.("Exchanging authorization code for tokens...");
1646
- return await exchangeAuthorizationCode$1(code, state, verifier, server.redirectUri);
1647
- } finally {
1648
- server.close();
1649
- }
1650
- }
1651
- /**
1652
- * Build an `OAuthProviderInterface` that behaves identically to pi-ai's
1653
- * `anthropicOAuthProvider` except for the callback page HTML. Drop this
1654
- * onto `ProviderDescriptor.oauthProvider` to override.
1655
- */
1656
- function createAnthropicOAuthProviderWithCustomPage(renderPage) {
1657
- return {
1658
- id: "anthropic",
1659
- name: "Anthropic (Claude Pro/Max)",
1660
- usesCallbackServer: true,
1661
- async login(callbacks) {
1662
- return loginAnthropicWithCustomPage({
1663
- renderPage,
1664
- onAuth: callbacks.onAuth,
1665
- onPrompt: callbacks.onPrompt,
1666
- onProgress: callbacks.onProgress,
1667
- onManualCodeInput: callbacks.onManualCodeInput
1668
- });
1669
- },
1670
- async refreshToken(credentials) {
1671
- return refreshAnthropicToken(credentials.refresh);
1672
- },
1673
- getApiKey(credentials) {
1674
- return credentials.access;
1675
- }
1676
- };
1677
- }
1678
- //#endregion
1679
- //#region src/chat/oauth-page/openai-codex.ts
1680
- const CLIENT_ID = "app_EMoamEEZ73f0CkXaXp7hrann";
1681
- const AUTHORIZE_URL = "https://auth.openai.com/oauth/authorize";
1682
- const TOKEN_URL = "https://auth.openai.com/oauth/token";
1683
- const CALLBACK_PORT = 1455;
1684
- const CALLBACK_PATH = "/auth/callback";
1685
- const SCOPE = "openid profile email offline_access";
1686
- const JWT_CLAIM_PATH = "https://api.openai.com/auth";
1687
- const PROVIDER_NAME = "OpenAI";
1688
- function createState() {
1689
- return randomBytes(16).toString("hex");
1690
- }
1691
- function parseAuthorizationInput(input) {
1692
- const value = input.trim();
1693
- if (!value) return {};
1694
- try {
1695
- const url = new URL(value);
1696
- return {
1697
- code: url.searchParams.get("code") ?? void 0,
1698
- state: url.searchParams.get("state") ?? void 0
1699
- };
1700
- } catch {}
1701
- if (value.includes("#")) {
1702
- const [code, state] = value.split("#", 2);
1703
- return {
1704
- code,
1705
- state
1706
- };
1707
- }
1708
- if (value.includes("code=")) {
1709
- const params = new URLSearchParams(value);
1710
- return {
1711
- code: params.get("code") ?? void 0,
1712
- state: params.get("state") ?? void 0
1713
- };
1714
- }
1715
- return { code: value };
1716
- }
1717
- function decodeJwt(token) {
1718
- try {
1719
- const parts = token.split(".");
1720
- if (parts.length !== 3) return null;
1721
- const payload = parts[1] ?? "";
1722
- const decoded = atob(payload);
1723
- return JSON.parse(decoded);
1724
- } catch {
1725
- return null;
1726
- }
1727
- }
1728
- function getAccountId(accessToken) {
1729
- const accountId = (decodeJwt(accessToken)?.[JWT_CLAIM_PATH])?.chatgpt_account_id;
1730
- return typeof accountId === "string" && accountId.length > 0 ? accountId : null;
1731
- }
1732
- async function exchangeAuthorizationCode(code, verifier, redirectUri) {
1733
- const response = await fetch(TOKEN_URL, {
1734
- method: "POST",
1735
- headers: { "Content-Type": "application/x-www-form-urlencoded" },
1736
- body: new URLSearchParams({
1737
- grant_type: "authorization_code",
1738
- client_id: CLIENT_ID,
1739
- code,
1740
- code_verifier: verifier,
1741
- redirect_uri: redirectUri
1742
- })
1743
- });
1744
- if (!response.ok) {
1745
- const text = await response.text().catch(() => "");
1746
- return {
1747
- type: "failed",
1748
- message: `OpenAI Codex token exchange failed (${response.status}): ${text || response.statusText}`
1749
- };
1750
- }
1751
- const json = await response.json();
1752
- if (!json.access_token || !json.refresh_token || typeof json.expires_in !== "number") return {
1753
- type: "failed",
1754
- message: `OpenAI Codex token exchange response missing fields: ${JSON.stringify(json)}`
1755
- };
1756
- return {
1757
- type: "success",
1758
- access: json.access_token,
1759
- refresh: json.refresh_token,
1760
- expires: Date.now() + json.expires_in * 1e3
1761
- };
1762
- }
1763
- async function loginOpenAICodexWithCustomPage(options) {
1764
- const { verifier, challenge } = await generatePkce();
1765
- const state = createState();
1766
- const originator = options.originator ?? "pi";
1767
- const server = await startCallbackServer({
1768
- port: CALLBACK_PORT,
1769
- path: CALLBACK_PATH,
1770
- expectedState: state,
1771
- providerName: PROVIDER_NAME,
1772
- renderPage: options.renderPage,
1773
- successMessage: "OpenAI authentication completed. You can close this window.",
1774
- onListenError: "resolveWithStub"
1775
- });
1776
- const authUrl = new URL(AUTHORIZE_URL);
1777
- authUrl.searchParams.set("response_type", "code");
1778
- authUrl.searchParams.set("client_id", CLIENT_ID);
1779
- authUrl.searchParams.set("redirect_uri", server.redirectUri);
1780
- authUrl.searchParams.set("scope", SCOPE);
1781
- authUrl.searchParams.set("code_challenge", challenge);
1782
- authUrl.searchParams.set("code_challenge_method", "S256");
1783
- authUrl.searchParams.set("state", state);
1784
- authUrl.searchParams.set("id_token_add_organizations", "true");
1785
- authUrl.searchParams.set("codex_cli_simplified_flow", "true");
1786
- authUrl.searchParams.set("originator", originator);
1787
- options.onAuth({
1788
- url: authUrl.toString(),
1789
- instructions: "A browser window should open. Complete login to finish."
1790
- });
1791
- let code;
1792
- try {
1793
- if (options.onManualCodeInput) {
1794
- let manualCode;
1795
- let manualError;
1796
- const manualPromise = options.onManualCodeInput().then((input) => {
1797
- manualCode = input;
1798
- server.cancelWait();
1799
- }).catch((err) => {
1800
- manualError = err instanceof Error ? err : new Error(String(err));
1801
- server.cancelWait();
1802
- });
1803
- const result = await server.waitForCode();
1804
- if (manualError) throw manualError;
1805
- if (result?.code) code = result.code;
1806
- else if (manualCode) {
1807
- const parsed = parseAuthorizationInput(manualCode);
1808
- if (parsed.state && parsed.state !== state) throw new Error("State mismatch");
1809
- code = parsed.code;
1810
- }
1811
- if (!code) {
1812
- await manualPromise;
1813
- if (manualError) throw manualError;
1814
- if (manualCode) {
1815
- const parsed = parseAuthorizationInput(manualCode);
1816
- if (parsed.state && parsed.state !== state) throw new Error("State mismatch");
1817
- code = parsed.code;
1818
- }
1819
- }
1820
- } else {
1821
- const result = await server.waitForCode();
1822
- if (result?.code) code = result.code;
1823
- }
1824
- if (!code) {
1825
- const parsed = parseAuthorizationInput(await options.onPrompt({ message: "Paste the authorization code (or full redirect URL):" }));
1826
- if (parsed.state && parsed.state !== state) throw new Error("State mismatch");
1827
- code = parsed.code;
1828
- }
1829
- if (!code) throw new Error("Missing authorization code");
1830
- const tokenResult = await exchangeAuthorizationCode(code, verifier, server.redirectUri);
1831
- if (tokenResult.type !== "success") throw new Error(tokenResult.message);
1832
- const accountId = getAccountId(tokenResult.access);
1833
- if (!accountId) throw new Error("Failed to extract accountId from token");
1834
- return {
1835
- access: tokenResult.access,
1836
- refresh: tokenResult.refresh,
1837
- expires: tokenResult.expires,
1838
- accountId
1839
- };
1840
- } finally {
1841
- server.close();
1842
- }
1843
- }
1844
- /**
1845
- * Build an `OAuthProviderInterface` that behaves identically to pi-ai's
1846
- * `openaiCodexOAuthProvider` except for the callback page HTML.
1847
- */
1848
- function createOpenAICodexOAuthProviderWithCustomPage(renderPage) {
1849
- return {
1850
- id: "openai-codex",
1851
- name: "ChatGPT Plus/Pro (Codex Subscription)",
1852
- usesCallbackServer: true,
1853
- async login(callbacks) {
1854
- return loginOpenAICodexWithCustomPage({
1855
- renderPage,
1856
- onAuth: callbacks.onAuth,
1857
- onPrompt: callbacks.onPrompt,
1858
- onProgress: callbacks.onProgress,
1859
- onManualCodeInput: callbacks.onManualCodeInput
1860
- });
1861
- },
1862
- async refreshToken(credentials) {
1863
- return refreshOpenAICodexToken(credentials.refresh);
1864
- },
1865
- getApiKey(credentials) {
1866
- return credentials.access;
1867
- }
1868
- };
1869
- }
1870
- //#endregion
1871
- //#region src/chat/oauth-page/render.ts
1872
- function escapeHtml(value) {
1873
- return value.replaceAll("&", "&amp;").replaceAll("<", "&lt;").replaceAll(">", "&gt;").replaceAll("\"", "&quot;").replaceAll("'", "&#39;");
1874
- }
1875
- /**
1876
- * Default zidane-themed page. Visually neutral but distinguishable from
1877
- * pi-ai's stock page — dark background, mono headings, no logo (host can
1878
- * pass a custom renderer to add one).
1879
- */
1880
- const renderDefaultCallbackPage = (page) => {
1881
- const heading = escapeHtml(page.kind === "success" ? `Signed in to ${page.provider}` : `Could not sign in to ${page.provider}`);
1882
- const message = escapeHtml(page.message);
1883
- const details = page.details ? escapeHtml(page.details) : void 0;
1884
- return `<!doctype html>
1885
- <html lang="en">
1886
- <head>
1887
- <meta charset="utf-8" />
1888
- <meta name="viewport" content="width=device-width, initial-scale=1" />
1889
- <title>${heading}</title>
1890
- <style>
1891
- :root {
1892
- --text: #f4f4f5;
1893
- --text-dim: #a1a1aa;
1894
- --accent: ${page.kind === "success" ? "#22d3ee" : "#f87171"};
1895
- --page-bg: #0a0a0a;
1896
- --panel-bg: #131316;
1897
- --border: #27272a;
1898
- --font-sans: ui-sans-serif, system-ui, -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, sans-serif;
1899
- --font-mono: ui-monospace, SFMono-Regular, Menlo, Monaco, Consolas, monospace;
1900
- }
1901
- * { box-sizing: border-box; }
1902
- html { color-scheme: dark; }
1903
- body {
1904
- margin: 0;
1905
- min-height: 100vh;
1906
- display: flex;
1907
- align-items: center;
1908
- justify-content: center;
1909
- padding: 24px;
1910
- background: var(--page-bg);
1911
- color: var(--text);
1912
- font-family: var(--font-sans);
1913
- }
1914
- main {
1915
- width: 100%;
1916
- max-width: 520px;
1917
- padding: 32px;
1918
- background: var(--panel-bg);
1919
- border: 1px solid var(--border);
1920
- border-radius: 12px;
1921
- }
1922
- .label {
1923
- font-family: var(--font-mono);
1924
- font-size: 12px;
1925
- letter-spacing: 0.08em;
1926
- text-transform: uppercase;
1927
- color: var(--accent);
1928
- margin-bottom: 12px;
1929
- }
1930
- h1 {
1931
- margin: 0 0 12px;
1932
- font-size: 22px;
1933
- font-weight: 600;
1934
- letter-spacing: -0.01em;
1935
- color: var(--text);
1936
- }
1937
- p {
1938
- margin: 0;
1939
- line-height: 1.6;
1940
- color: var(--text-dim);
1941
- font-size: 14px;
1942
- }
1943
- .details {
1944
- margin-top: 18px;
1945
- padding: 12px 14px;
1946
- background: var(--page-bg);
1947
- border: 1px solid var(--border);
1948
- border-radius: 8px;
1949
- font-family: var(--font-mono);
1950
- font-size: 12px;
1951
- color: var(--text-dim);
1952
- white-space: pre-wrap;
1953
- word-break: break-word;
1954
- }
1955
- </style>
1956
- </head>
1957
- <body>
1958
- <main>
1959
- <div class="label">zidane · OAuth · ${escapeHtml(page.provider)}</div>
1960
- <h1>${heading}</h1>
1961
- <p>${message}</p>
1962
- ${details ? `<div class="details">${details}</div>` : ""}
1963
- </main>
1964
- </body>
1965
- </html>`;
1966
- };
1967
- //#endregion
1968
- //#region src/chat/oauth-page/index.ts
1969
- /**
1970
- * Bundle helper — returns both Anthropic + OpenAI Codex providers wired
1971
- * with the same renderer. Hosts that want different renderers per provider
1972
- * should call the individual `create…WithCustomPage` factories instead.
1973
- */
1974
- function createCustomCallbackOAuthProviders(renderPage) {
1975
- return {
1976
- anthropic: createAnthropicOAuthProviderWithCustomPage(renderPage),
1977
- openaiCodex: createOpenAICodexOAuthProviderWithCustomPage(renderPage)
1978
- };
1979
- }
1980
- let cursorRegistered = false;
1981
- /**
1982
- * Register Cursor with pi-ai's OAuth registry.
1983
- *
1984
- * Unlike Anthropic / Codex, Cursor is **not** built into pi-ai, so
1985
- * `getOAuthApiKey('cursor', …)` (used by `resolveOAuthApiKey` for lazy
1986
- * token refresh) would throw "Unknown OAuth provider" until we register it.
1987
- * Call once at startup from both the CLI auth path and the TUI. Idempotent.
1988
- */
1989
- function registerCursorOAuthProvider() {
1990
- const provider = createCursorOAuthProvider();
1991
- if (!cursorRegistered) {
1992
- registerOAuthProvider(provider);
1993
- cursorRegistered = true;
1994
- }
1995
- return provider;
1996
- }
1997
- let xaiRegistered = false;
1998
- /**
1999
- * Register xAI Grok with pi-ai's OAuth registry.
2000
- *
2001
- * pi-ai ships an xAI *API-key* provider, but no OAuth one — so
2002
- * `getOAuthApiKey('xai-oauth', …)` (lazy refresh in `resolveOAuthApiKey`)
2003
- * would throw "Unknown OAuth provider" until we register our flow. The
2004
- * callback page routes through the supplied renderer so the post-redirect
2005
- * HTML matches zidane's theme. Call once at startup; idempotent.
2006
- */
2007
- function registerXaiOAuthProvider(renderPage) {
2008
- const provider = createXaiOAuthProvider(renderPage);
2009
- if (!xaiRegistered) {
2010
- registerOAuthProvider(provider);
2011
- xaiRegistered = true;
2012
- }
2013
- return provider;
2014
- }
2015
- //#endregion
2016
- //#region src/chat/providers.ts
2017
- /**
2018
- * pi-ai's stock Anthropic + Codex OAuth providers bake their own callback
2019
- * HTML; we route both through `renderDefaultCallbackPage` so the post-redirect
2020
- * page matches zidane's theme. The override lives in `src/chat/oauth-page/`
2021
- * — see that folder's `index.ts` for the deletion path once pi-ai exposes
2022
- * a `renderCallbackPage` hook upstream.
2023
- */
2024
- const { anthropic: anthropicOAuthProvider, openaiCodex: openaiCodexOAuthProvider } = createCustomCallbackOAuthProviders(renderDefaultCallbackPage);
2025
- registerCursorOAuthProvider();
2026
- /**
2027
- * xAI Grok isn't built into pi-ai's OAuth registry (only its API-key provider
2028
- * is), so register our loopback-PKCE flow — themed via `renderDefaultCallbackPage`
2029
- * — so lazy token refresh resolves through it.
2030
- */
2031
- const xaiOAuthProvider = registerXaiOAuthProvider(renderDefaultCallbackPage);
2032
- /** Convenience accessor — returns `credentialFileKey ?? key`. */
2033
- function credKeyOf(desc) {
2034
- return desc.credentialFileKey ?? desc.key;
2035
- }
2036
- /** Convenience accessor — returns `piProviderId ?? key`. */
2037
- function piIdOf(desc) {
2038
- return desc.piProviderId ?? desc.key;
2039
- }
2040
- const anthropicDescriptor = {
2041
- key: "anthropic",
2042
- label: "Anthropic",
2043
- factory: anthropic,
2044
- defaultModel: "claude-opus-4-8",
2045
- envKey: "ANTHROPIC_API_KEY",
2046
- apiKeyPlaceholder: "sk-ant-…",
2047
- oauthProvider: anthropicOAuthProvider,
2048
- oauthHint: "Claude Pro/Max subscription",
2049
- extraModels: ANTHROPIC_EXTRA_MODELS,
2050
- optionsFor: (id) => FAST_MODE_OPTIONS[id] ? [FAST_MODE_OPTIONS[id]] : void 0
2051
- };
2052
- const openaiDescriptor = {
2053
- key: "openai",
2054
- label: "OpenAI",
2055
- factory: openai,
2056
- defaultModel: "gpt-5.5",
2057
- envKey: "OPENAI_CODEX_API_KEY",
2058
- credentialFileKey: "openai-codex",
2059
- piProviderId: "openai-codex",
2060
- apiKeyPlaceholder: "sk-… or eyJ… (Codex)",
2061
- oauthProvider: openaiCodexOAuthProvider
2062
- };
2063
- const openrouterDescriptor = {
2064
- key: "openrouter",
2065
- label: "OpenRouter",
2066
- factory: openrouter,
2067
- defaultModel: "anthropic/claude-sonnet-4-6",
2068
- envKey: "OPENROUTER_API_KEY",
2069
- apiKeyPlaceholder: "sk-or-…"
2070
- };
2071
- const cerebrasDescriptor = {
2072
- key: "cerebras",
2073
- label: "Cerebras",
2074
- factory: cerebras,
2075
- defaultModel: "zai-glm-4.7",
2076
- envKey: "CEREBRAS_API_KEY",
2077
- apiKeyPlaceholder: "csk-…"
2078
- };
2079
- /**
2080
- * xAI Grok. Supports BOTH a static API key (`XAI_API_KEY`) and the
2081
- * SuperGrok / X Premium+ OAuth subscription, so the wizard offers both methods.
2082
- * Models come from pi-ai's `xai` registry (OpenAI-compatible endpoint), shared
2083
- * by both auth modes.
2084
- */
2085
- const xaiDescriptor = {
2086
- key: "xai",
2087
- label: "xAI Grok",
2088
- factory: xai,
2089
- defaultModel: "grok-4.3",
2090
- envKey: "XAI_API_KEY",
2091
- apiKeyPlaceholder: "xai-…",
2092
- oauthProvider: xaiOAuthProvider,
2093
- oauthHint: "SuperGrok / X Premium+ subscription",
2094
- credentialFileKey: "xai-oauth",
2095
- piProviderId: "xai"
2096
- };
2097
- const arceeDescriptor = {
2098
- key: "arcee",
2099
- label: "Arcee",
2100
- factory: arcee,
2101
- defaultModel: "trinity-large-thinking",
2102
- envKey: "ARCEE_API_KEY",
2103
- apiKeyPlaceholder: "arcee-…",
2104
- models: [
2105
- {
2106
- id: "trinity-large-thinking",
2107
- name: "Trinity Large (Thinking)",
2108
- contextWindow: 256e3,
2109
- reasoning: true,
2110
- input: ["text"],
2111
- cost: {
2112
- input: .25,
2113
- output: .8
2114
- }
2115
- },
2116
- {
2117
- id: "trinity-large-preview",
2118
- name: "Trinity Large (Preview)",
2119
- contextWindow: 128e3,
2120
- input: ["text"],
2121
- cost: {
2122
- input: .45,
2123
- output: .15
2124
- }
2125
- },
2126
- {
2127
- id: "trinity-mini",
2128
- name: "Trinity Mini",
2129
- contextWindow: 128e3,
2130
- input: ["text"],
2131
- cost: {
2132
- input: .045,
2133
- output: .15
2134
- }
2135
- }
2136
- ]
2137
- };
2138
- const basetenDescriptor = {
2139
- key: "baseten",
2140
- label: "Baseten",
2141
- factory: baseten,
2142
- defaultModel: "zai-org/GLM-5.1",
2143
- envKey: "BASETEN_API_KEY",
2144
- apiKeyPlaceholder: "baseten-api-key…",
2145
- models: [
2146
- {
2147
- id: "zai-org/GLM-5.2",
2148
- name: "GLM 5.2",
2149
- contextWindow: 1e6,
2150
- reasoning: true,
2151
- input: ["text"]
2152
- },
2153
- {
2154
- id: "zai-org/GLM-5.1",
2155
- name: "GLM 5.1",
2156
- contextWindow: 2e5,
2157
- reasoning: true,
2158
- input: ["text"]
2159
- },
2160
- {
2161
- id: "zai-org/GLM-5",
2162
- name: "GLM 5",
2163
- contextWindow: 2e5,
2164
- reasoning: true,
2165
- input: ["text"],
2166
- cost: {
2167
- input: .95,
2168
- output: 3.15,
2169
- cacheRead: .2
2170
- }
2171
- },
2172
- {
2173
- id: "zai-org/GLM-4.7",
2174
- name: "GLM 4.7",
2175
- contextWindow: 2e5,
2176
- reasoning: true,
2177
- input: ["text"],
2178
- cost: {
2179
- input: .6,
2180
- output: 2.2,
2181
- cacheRead: .12
2182
- }
2183
- },
2184
- {
2185
- id: "moonshotai/Kimi-K2.6",
2186
- name: "Kimi K2.6",
2187
- contextWindow: 256e3,
2188
- reasoning: true,
2189
- input: ["text", "image"],
2190
- cost: {
2191
- input: 1,
2192
- output: 3.9,
2193
- cacheRead: .2
2194
- }
2195
- },
2196
- {
2197
- id: "moonshotai/Kimi-K2.5",
2198
- name: "Kimi K2.5",
2199
- contextWindow: 256e3,
2200
- reasoning: true,
2201
- input: ["text", "image"],
2202
- cost: {
2203
- input: .6,
2204
- output: 3,
2205
- cacheRead: .12
2206
- }
2207
- },
2208
- {
2209
- id: "deepseek-ai/DeepSeek-V4-Pro",
2210
- name: "DeepSeek V4 Pro",
2211
- contextWindow: 131e3,
2212
- reasoning: true,
2213
- input: ["text"],
2214
- cost: {
2215
- input: 1.74,
2216
- output: 3.48,
2217
- cacheRead: .145
2218
- }
2219
- },
2220
- {
2221
- id: "nvidia/Nemotron-120B-A12B",
2222
- name: "Nemotron Super",
2223
- contextWindow: 2e5,
2224
- reasoning: true,
2225
- input: ["text"],
2226
- cost: {
2227
- input: .3,
2228
- output: .75,
2229
- cacheRead: .06
2230
- }
2231
- },
2232
- {
2233
- id: "nvidia/NVIDIA-Nemotron-3-Ultra-550B-A55B",
2234
- name: "Nemotron Ultra",
2235
- contextWindow: 2e5,
2236
- reasoning: true,
2237
- input: ["text"]
2238
- },
2239
- {
2240
- id: "openai/gpt-oss-120b",
2241
- name: "GPT-OSS 120B",
2242
- contextWindow: 128e3,
2243
- reasoning: true,
2244
- input: ["text"],
2245
- cost: {
2246
- input: .1,
2247
- output: .5
2248
- }
2249
- }
2250
- ]
2251
- };
2252
- /**
2253
- * Conservative context-window assumption for a user-configured local model.
2254
- * Local runtimes expose no reliable per-model metadata over the OpenAI-compat
2255
- * `/v1/models` endpoint (it returns ids, not context sizes), so we pick a
2256
- * floor that fits the smallest mainstream OSS models (Llama 3.x 8B, Qwen2.5)
2257
- * without over-promising. The footer's context indicator and auto-compaction
2258
- * lean on this — under-estimating is the safe direction (compact early rather
2259
- * than overflow the server's real window).
2260
- *
2261
- * Users running larger-context models (e.g. a 128K Qwen) can raise it via the
2262
- * `LOCAL_LLM_CONTEXT_WINDOW` env var / customField, which supports a single
2263
- * value or a per-model map (resolved by {@link resolveContextWindow}).
2264
- */
2265
- const LOCAL_DEFAULT_CONTEXT_WINDOW = 8192;
2266
- /**
2267
- * Local OpenAI-compatible LLM (Ollama, vLLM, LM Studio, Lemonade, llama.cpp).
2268
- *
2269
- * No fixed base URL or model catalogue — both come from the user via
2270
- * `customFields`. No `envKey`, so the wizard skips the API-key prompt and
2271
- * treats the customFields-only credential as the auth signal (see
2272
- * `applyApiKeyEnv` + `detectAuth`).
2273
- *
2274
- * Vision / cache / reasoning are off by default in the factory — flip them
2275
- * by passing a custom descriptor that calls `local({ capabilities })`.
2276
- *
2277
- * `models` is a getter, not a static list: there's no fixed catalogue to ship,
2278
- * but once the user has configured a default model (mirrored into
2279
- * `LOCAL_LLM_DEFAULT_MODEL` by `applyApiKeyEnv`), we surface it as a
2280
- * single-entry list so the model picker + footer aren't empty. Resolved at
2281
- * access time because the env var is populated at TUI launch, after this
2282
- * module loads. Empty list when unconfigured — the picker hides, the user
2283
- * types a slug at the prompt as before.
2284
- */
2285
- const localDescriptor = {
2286
- key: "local",
2287
- label: "Local LLM",
2288
- factory: local,
2289
- get models() {
2290
- const configured = process.env.LOCAL_LLM_DEFAULT_MODEL?.trim();
2291
- if (!configured) return [];
2292
- return [{
2293
- id: configured,
2294
- name: configured,
2295
- contextWindow: resolveContextWindow(process.env.LOCAL_LLM_CONTEXT_WINDOW, configured) ?? LOCAL_DEFAULT_CONTEXT_WINDOW,
2296
- input: ["text"]
2297
- }];
2298
- },
2299
- customFields: [
2300
- {
2301
- key: "baseURL",
2302
- label: "Base URL",
2303
- envVar: "LOCAL_LLM_BASE_URL",
2304
- placeholder: "http://localhost:11434/v1",
2305
- hint: "Where your local LLM server is reachable. Examples: Ollama → http://localhost:11434/v1, vLLM → http://localhost:8000/v1, LM Studio → http://localhost:1234/v1.",
2306
- required: true
2307
- },
2308
- {
2309
- key: "apiKey",
2310
- label: "API key",
2311
- envVar: "LOCAL_LLM_API_KEY",
2312
- placeholder: "leave blank if your server is unauthenticated"
2313
- },
2314
- {
2315
- key: "model",
2316
- label: "Default model",
2317
- envVar: "LOCAL_LLM_DEFAULT_MODEL",
2318
- placeholder: "e.g. llama3.1:8b, qwen2.5-coder, mistral-small",
2319
- hint: "Model id your server exposes. Leave blank to pick later from the model picker."
2320
- },
2321
- {
2322
- key: "contextWindow",
2323
- label: "Context window (tokens)",
2324
- envVar: "LOCAL_LLM_CONTEXT_WINDOW",
2325
- placeholder: "e.g. 32768 · or per-model: llama3.1:8b=32768, qwen2.5-coder=128k, 64k",
2326
- hint: "Context length(s) for your local model(s) — local runtimes don't advertise this, so set it here to enable the context indicator and auto-compaction. Use a single value (32768, 128k) for all models, or a comma-separated map of `model=window` with an optional bare value as the fallback. Press"
2327
- }
2328
- ],
2329
- contextWindowEnvVar: "LOCAL_LLM_CONTEXT_WINDOW"
2330
- };
2331
- /**
2332
- * Default provider registry. Passed verbatim when `runTui` is invoked without
2333
- * an explicit `providers` option. Hosts that want to override per-provider
2334
- * metadata can spread this and replace specific entries:
2335
- *
2336
- * ```ts
2337
- * runTui({ providers: { ...BUILTIN_PROVIDERS, anthropic: myOwnAnthropicDescriptor } })
2338
- * ```
2339
- *
2340
- * `cursor` is intentionally NOT registered here: OAuth works, but inference
2341
- * isn't wired (`cursor.stream()` throws — Cursor speaks a protobuf/HTTP-2
2342
- * agent protocol, not OpenAI-compat). Shipping it in the picker only lets users
2343
- * select a model that errors every turn. The descriptor stays exported so a
2344
- * host can opt in (`{ ...BUILTIN_PROVIDERS, cursor: cursorDescriptor }`) once
2345
- * the transport lands — re-add it here at that point.
2346
- */
2347
- const BUILTIN_PROVIDERS = {
2348
- anthropic: anthropicDescriptor,
2349
- openai: openaiDescriptor,
2350
- openrouter: openrouterDescriptor,
2351
- cerebras: cerebrasDescriptor,
2352
- xai: xaiDescriptor,
2353
- arcee: arceeDescriptor,
2354
- baseten: basetenDescriptor,
2355
- local: localDescriptor
2356
- };
2357
- /**
2358
- * Resolve the model list for a given provider. Honors `descriptor.models`
2359
- * when set; otherwise queries pi-ai via `descriptor.piProviderId`. Returns
2360
- * `[]` for descriptors with no known mapping (custom providers without a
2361
- * model list) — callers should hide the model picker in that case.
2362
- */
2363
- function modelsForDescriptor(descriptor) {
2364
- if (descriptor.models) return descriptor.models;
2365
- let bundled;
2366
- try {
2367
- bundled = getModels(piIdOf(descriptor));
2368
- } catch {
2369
- bundled = [];
2370
- }
2371
- if (descriptor.extraModels?.length) {
2372
- const bundledIds = new Set(bundled.map((m) => m.id));
2373
- bundled = [...descriptor.extraModels.filter((m) => !bundledIds.has(m.id)), ...bundled];
2374
- }
2375
- return descriptor.optionsFor ? bundled.map((m) => decorateOptions(m, descriptor)) : bundled;
2376
- }
2377
- /**
2378
- * Attach descriptor-resolved {@link ModelOption}s to a model that doesn't
2379
- * already declare its own. Pure / non-mutating — returns the input untouched
2380
- * when the model has options already or the descriptor resolves none.
2381
- */
2382
- function decorateOptions(model, descriptor) {
2383
- if (model.options?.length || !descriptor.optionsFor) return model;
2384
- const options = descriptor.optionsFor(model.id);
2385
- return options?.length ? {
2386
- ...model,
2387
- options
2388
- } : model;
2389
- }
2390
- /**
2391
- * Resolve a single model's metadata via the descriptor's model source.
2392
- * Mirrors {@link modelsForDescriptor} routing: descriptor's own list wins,
2393
- * pi-ai's registry is the fallback. Returns `null` when the model isn't
2394
- * known.
2395
- */
2396
- function getModelInfo(descriptor, modelId) {
2397
- if (descriptor.models) return descriptor.models.find((m) => m.id === modelId) ?? null;
2398
- try {
2399
- const bundled = getModel(piIdOf(descriptor), modelId);
2400
- if (bundled) return decorateOptions(bundled, descriptor);
2401
- } catch {}
2402
- const extra = descriptor.extraModels?.find((m) => m.id === modelId);
2403
- return extra ? decorateOptions(extra, descriptor) : null;
2404
- }
2405
- /**
2406
- * Parse a user-supplied context-window string into a token count.
2407
- *
2408
- * Accepts a plain integer (`"32768"`), or a number with a `k`/`m` suffix
2409
- * (×1_000 / ×1_000_000, case-insensitive, optional space) — so `"128k"`
2410
- * yields `128_000`, matching how the footer renders windows (`fmtTokens`).
2411
- * Fractional values are floored (`"1.5k"` → `1500`). Returns `null` for
2412
- * empty, malformed, zero, or negative input so callers fall through to
2413
- * "window unknown" rather than a bogus ceiling.
2414
- */
2415
- function parseContextWindow(raw) {
2416
- if (raw == null) return null;
2417
- const trimmed = raw.trim();
2418
- if (trimmed.length === 0) return null;
2419
- const match = /^(\d+(?:\.\d+)?)\s*([km])?$/i.exec(trimmed);
2420
- if (!match) return null;
2421
- const suffix = match[2]?.toLowerCase();
2422
- const mult = suffix === "k" ? 1e3 : suffix === "m" ? 1e6 : 1;
2423
- const value = Math.floor(Number.parseFloat(match[1]) * mult);
2424
- return value >= 1 ? value : null;
2425
- }
2426
- /**
2427
- * Resolve a per-model context window from a user-supplied spec string.
2428
- *
2429
- * A spec is a comma-separated list of entries. An entry is either:
2430
- * - `model=window` — a per-model override (split on the **first** `=`, so
2431
- * model ids containing `:` or `/` survive), or
2432
- * - a bare `window` — the fallback applied to any model not named above.
2433
- *
2434
- * Windows are parsed by {@link parseContextWindow} (plain int or k/m suffix).
2435
- * Whitespace around entries and the `=` is ignored; malformed entries are
2436
- * skipped rather than poisoning the whole spec; a later duplicate key wins.
2437
- *
2438
- * Lookup order for `modelId`: exact map entry → bare fallback → `null`. A
2439
- * lone bare value (`"32768"`) therefore behaves as a single global window,
2440
- * keeping the simple single-model config working.
2441
- *
2442
- * @example resolveContextWindow('llama3.1:8b=32768, qwen=128k, 64k', 'qwen') // 128000
2443
- */
2444
- function resolveContextWindow(spec, modelId) {
2445
- if (spec == null) return null;
2446
- const overrides = /* @__PURE__ */ new Map();
2447
- let fallback = null;
2448
- for (const entry of spec.split(",")) {
2449
- const eq = entry.indexOf("=");
2450
- if (eq === -1) {
2451
- const bare = parseContextWindow(entry);
2452
- if (bare != null) fallback = bare;
2453
- continue;
2454
- }
2455
- const key = entry.slice(0, eq).trim();
2456
- const window = parseContextWindow(entry.slice(eq + 1));
2457
- if (key.length > 0 && window != null) overrides.set(key, window);
2458
- }
2459
- return overrides.get(modelId) ?? fallback;
2460
- }
2461
- /**
2462
- * Look up the model's max context window via the descriptor's model source.
2463
- * Falls back to {@link ProviderDescriptor.contextWindowEnvVar} (parsed via
2464
- * {@link resolveContextWindow}, which supports a per-model map) when the
2465
- * registry has nothing — this is how local LLMs, whose runtimes don't
2466
- * advertise a window, get one. Returns `null` when neither source resolves a
2467
- * value (custom slugs, providers without a registry or a configured window);
2468
- * callers should hide the context indicator and skip auto-compaction then.
2469
- */
2470
- function getContextWindow(descriptor, modelId) {
2471
- const fromRegistry = getModelInfo(descriptor, modelId)?.contextWindow;
2472
- if (fromRegistry != null) return fromRegistry;
2473
- if (descriptor.contextWindowEnvVar) return resolveContextWindow(process.env[descriptor.contextWindowEnvVar], modelId);
2474
- return null;
2475
- }
2476
- /**
2477
- * Whether the given model exposes a reasoning / extended-thinking knob.
2478
- * Drives the TUI's effort picker visibility — the `ctrl+n` shortcut and
2479
- * the bottom-bar `effortName` segment only surface when this is `true`.
2480
- * Returns `false` for unknown models (no registry entry → no advertised
2481
- * capability).
2482
- */
2483
- function modelSupportsReasoning(descriptor, modelId) {
2484
- return getModelInfo(descriptor, modelId)?.reasoning === true;
2485
- }
2486
- /**
2487
- * Custom {@link ModelOption}s the given model supports (e.g. fast mode), or `[]`
2488
- * when none. Drives the TUI/GUI options picker visibility — surfaces only when
2489
- * non-empty.
2490
- */
2491
- function modelOptionsFor(descriptor, modelId) {
2492
- return getModelInfo(descriptor, modelId)?.options ?? [];
2493
- }
2494
- /**
2495
- * Normalize a model-options blob to an enabled-only `{ id: true }` map.
2496
- *
2497
- * Single source of truth shared by every surface that reads or persists model
2498
- * options — the TUI run path, the GUI engine reader, and the GUI IPC handlers.
2499
- * Tolerant of arbitrary input (persisted JSON metadata may be anything): a
2500
- * non-object → `{}`, and only entries strictly equal to `true` survive. This
2501
- * keeps the persisted shape minimal (no `{ fast: false }` noise) and means
2502
- * callers never forward a disabled option to `agent.run`.
2503
- */
2504
- function enabledModelOptions(raw) {
2505
- if (!raw || typeof raw !== "object") return {};
2506
- const out = {};
2507
- for (const [id, on] of Object.entries(raw)) if (on === true) out[id] = true;
2508
- return out;
2509
- }
2510
- /**
2511
- * Resolve a model's remembered options for a (re)pick: keep only options the
2512
- * model still declares AND that are enabled, dropping any that no longer apply
2513
- * (e.g. switching away from a model that supported `fast`). Returns `undefined`
2514
- * when nothing applies so callers can omit the field entirely.
2515
- *
2516
- * Shared by the TUI pick path and the launch-time resume path so "which options
2517
- * survive a model switch / relaunch" is decided in exactly one place.
2518
- */
2519
- function restoreModelOptions(descriptor, modelId, remembered) {
2520
- const validIds = new Set(modelOptionsFor(descriptor, modelId).map((o) => o.id));
2521
- if (validIds.size === 0) return void 0;
2522
- const enabled = enabledModelOptions(remembered?.[modelId]);
2523
- const filtered = {};
2524
- for (const id of Object.keys(enabled)) if (validIds.has(id)) filtered[id] = true;
2525
- return Object.keys(filtered).length > 0 ? filtered : void 0;
2526
- }
2527
- //#endregion
2528
- //#region src/chat/credentials.ts
2529
- /** POSIX mode for the credentials file. Ignored on Windows. */
2530
- const FILE_MODE$1 = 384;
2531
- /**
2532
- * Resolve the credentials file path given the resolved TUI data directory
2533
- * (typically `~/.zidane`, i.e. `config.paths.dir`).
2534
- *
2535
- * Matches the convention used elsewhere in the TUI (sessions.db, state.json)
2536
- * so a single `ZIDANE_STORAGE_DIR` override moves the entire data root.
2537
- */
2538
- function credentialsPath(dataDir) {
2539
- return resolve(dataDir, "credentials.json");
2540
- }
2541
- /**
2542
- * Read credentials from disk.
2543
- *
2544
- * Returns `{}` when the file is missing or corrupt (last-ditch tolerance —
2545
- * a hand-edit gone wrong shouldn't lock the user out of re-authing). On first
2546
- * call with no file present, attempts a migration from `cwd/.credentials.json`
2547
- * (the legacy location used by `bun run auth`).
2548
- */
2549
- function readCredentials(dataDir) {
2550
- const path = credentialsPath(dataDir);
2551
- if (!existsSync(path)) {
2552
- const migrated = migrateLegacyFile(path);
2553
- if (migrated) return migrated;
2554
- return {};
2555
- }
2556
- try {
2557
- const raw = readFileSync(path, "utf-8");
2558
- const parsed = JSON.parse(raw);
2559
- if (!parsed || typeof parsed !== "object" || Array.isArray(parsed)) return {};
2560
- return parsed;
2561
- } catch {
2562
- return {};
2563
- }
2564
- }
2565
- /** Read a single provider's credential (translating via the descriptor). */
2566
- function readProviderCredential(dataDir, descriptor) {
2567
- return readCredentials(dataDir)[credKeyOf(descriptor)];
2568
- }
2569
- /**
2570
- * Write credentials atomically (write-then-rename) with mode 0o600.
2571
- *
2572
- * Atomic on the same filesystem — readers either see the previous file or the
2573
- * new one, never a half-written intermediate. Creates the parent dir if needed
2574
- * (first launch on a fresh machine: `~/.zidane/` may not exist yet).
2575
- */
2576
- function writeCredentials(dataDir, creds) {
2577
- writeFileAtomic(credentialsPath(dataDir), `${JSON.stringify(creds, null, 2)}\n`, {
2578
- ensureDir: true,
2579
- mode: FILE_MODE$1
2580
- });
2581
- }
2582
- function setProviderCredential(dataDir, descriptor, cred) {
2583
- const all = readCredentials(dataDir);
2584
- all[credKeyOf(descriptor)] = cred;
2585
- writeCredentials(dataDir, all);
2586
- }
2587
- function removeProviderCredential(dataDir, descriptor) {
2588
- const all = readCredentials(dataDir);
2589
- const fileKey = credKeyOf(descriptor);
2590
- if (!(fileKey in all)) return;
2591
- delete all[fileKey];
2592
- writeCredentials(dataDir, all);
2593
- }
2594
- /**
2595
- * Reconcile `process.env` with the stored credentials so the harness providers
2596
- * pick up the wizard's output via their existing env-var resolution. Called
2597
- * once at TUI launch after the credentials file has been resolved.
2598
- *
2599
- * **Precedence: stored credential > ambient env.** When the user has run the
2600
- * auth wizard, that's an explicit, recent action — it MUST win over whatever
2601
- * ambient value is already in `process.env`. Otherwise an upgrade reliably
2602
- * breaks for anyone whose cwd happens to contain a stale `.env`: Bun
2603
- * auto-loads `.env` from cwd into `process.env` before our code runs, the
2604
- * legacy `bun run auth` used to upsert tokens into `cwd/.env`, and even a
2605
- * one-off `export ANTHROPIC_API_KEY=…` from months ago in the user's shell rc
2606
- * can outlive its usefulness. None of those should override a key the user
2607
- * just typed into the wizard.
2608
- *
2609
- * Three cases per registered provider with an `envKey`:
2610
- *
2611
- * 1. Stored `kind: 'apikey'` → overwrite env with the stored value. Wizard
2612
- * wins. A debug log fires when this clobbers a different ambient value.
2613
- * 2. Stored `kind: 'oauth'` → DELETE env, so {@link resolveOAuthApiKey}
2614
- * falls through to the file-read + refresh path. A stale OAuth token
2615
- * in env would otherwise short-circuit refresh and get rejected.
2616
- * 3. No stored entry → leave env alone. Env-only setups (no wizard run yet,
2617
- * hosts driving zidane purely from secrets management) keep working.
2618
- *
2619
- * To opt out and force ambient env to win again, the user signs the provider
2620
- * out via the wizard (which deletes the stored entry → case 3 above).
2621
- *
2622
- * Descriptors without an `envKey` (OAuth-only providers, custom providers
2623
- * that bypass env-var resolution) are skipped silently.
2624
- */
2625
- function applyApiKeyEnv(dataDir, registry) {
2626
- const creds = readCredentials(dataDir);
2627
- for (const descriptor of Object.values(registry)) {
2628
- const cred = creds[credKeyOf(descriptor)];
2629
- if (cred?.kind === "apikey" && descriptor.customFields) {
2630
- const stored = cred.customFields ?? {};
2631
- for (const field of descriptor.customFields) {
2632
- const value = stored[field.key];
2633
- if (typeof value === "string" && value.length > 0) {
2634
- const ambient = process.env[field.envVar];
2635
- if (ambient && ambient !== value && process.env.ZIDANE_DEBUG) process.stderr.write(`[zidane/chat] applyApiKeyEnv: overriding ambient \`${field.envVar}\` with stored value from credentials.json (provider=${descriptor.key}, field=${field.key}).\n`);
2636
- process.env[field.envVar] = value;
2637
- }
2638
- }
2639
- }
2640
- if (!descriptor.envKey) continue;
2641
- const envKey = descriptor.envKey;
2642
- const ambient = process.env[envKey];
2643
- if (cred?.kind === "apikey" && cred.value) {
2644
- if (ambient && ambient !== cred.value && process.env.ZIDANE_DEBUG) process.stderr.write(`[zidane/chat] applyApiKeyEnv: overriding ambient \`${envKey}\` with stored API key from credentials.json (provider=${descriptor.key}). Sign out via the auth wizard if the ambient value was intended to win.\n`);
2645
- process.env[envKey] = cred.value;
2646
- continue;
2647
- }
2648
- if (cred?.kind === "oauth" && cred.access) {
2649
- if (ambient !== void 0 && process.env.ZIDANE_DEBUG) process.stderr.write(`[zidane/chat] applyApiKeyEnv: clearing ambient \`${envKey}\` because credentials.json has stored OAuth for ${descriptor.key} — refresh path needs the file.\n`);
2650
- delete process.env[envKey];
2651
- continue;
2652
- }
2653
- }
2654
- }
2655
- /**
2656
- * `bun run auth` (pre-TUI) wrote `cwd/.credentials.json` with an entry per
2657
- * provider mapping directly to an OAuthCredentials payload, e.g.:
2658
- *
2659
- * {
2660
- * "anthropic": { "access": "...", "refresh": "...", "expires": 123 },
2661
- * "openai-codex": { "access": "...", "refresh": "...", "expires": 123, "accountId": "..." }
2662
- * }
2663
- *
2664
- * We don't delete the legacy file — it might still be used by a host that
2665
- * imports the harness directly. We just copy its contents into the new
2666
- * location under the kind-tagged shape so the TUI picks them up.
2667
- *
2668
- * Migration is provider-agnostic: any top-level entry with an `access` field
2669
- * is preserved verbatim (extras included), under the same key. The TUI's
2670
- * detection then looks them up via the matching descriptor's `credentialFileKey`.
2671
- *
2672
- * Returns the migrated credentials when the migration ran, or `null` when
2673
- * there's no legacy file to migrate.
2674
- */
2675
- function migrateLegacyFile(targetPath) {
2676
- const legacyPath = resolve(process.cwd(), ".credentials.json");
2677
- const real = (p) => {
2678
- try {
2679
- return realpathSync(p);
2680
- } catch {
2681
- return p;
2682
- }
2683
- };
2684
- const legacyDir = real(dirname(legacyPath));
2685
- if (legacyDir !== real(homedir()) && legacyDir !== real(dirname(resolve(targetPath)))) return null;
2686
- if (!existsSync(legacyPath)) return null;
2687
- let legacy;
2688
- try {
2689
- legacy = JSON.parse(readFileSync(legacyPath, "utf-8"));
2690
- } catch {
2691
- return null;
2692
- }
2693
- if (!legacy || typeof legacy !== "object" || Array.isArray(legacy)) return null;
2694
- const migrated = {};
2695
- for (const [fileKey, value] of Object.entries(legacy)) {
2696
- if (!isOAuthLegacy(value)) continue;
2697
- const { access, refresh, expires, ...extras } = value;
2698
- migrated[fileKey] = {
2699
- kind: "oauth",
2700
- access,
2701
- ...typeof refresh === "string" ? { refresh } : {},
2702
- ...typeof expires === "number" ? { expires } : {},
2703
- ...extras
2704
- };
2705
- }
2706
- if (Object.keys(migrated).length === 0) return null;
2707
- writeFileAtomic(targetPath, `${JSON.stringify(migrated, null, 2)}\n`, {
2708
- ensureDir: true,
2709
- mode: FILE_MODE$1
2710
- });
2711
- return migrated;
2712
- }
2713
- function isOAuthLegacy(value) {
2714
- return typeof value === "object" && value !== null && "access" in value && typeof value.access === "string";
2715
- }
2716
- //#endregion
2717
1389
  //#region src/chat/auth.ts
2718
1390
  /**
2719
1391
  * Detect available auth for every registered provider.
@@ -4971,86 +3643,6 @@ function normalizeUserConfig(raw) {
4971
3643
  return out;
4972
3644
  }
4973
3645
  //#endregion
4974
- //#region src/chat/xdg.ts
4975
- /**
4976
- * XDG Base Directory resolution for zidane.
4977
- *
4978
- * The user's storage spreads across four logical slots:
4979
- *
4980
- * - **config** — credentials, mcp-credentials, keybindings, config.json,
4981
- * mcps.json, skills (user-editable surface).
4982
- * - **data** — sessions.db (long-lived chat history).
4983
- * - **state** — state.json (per-machine UI bookkeeping: last provider,
4984
- * last model, last agent, settings).
4985
- * - **cache** — persisted tool results, background-task logs,
4986
- * auto-update registry cache (regenerable, safe to wipe).
4987
- *
4988
- * Historically all four collapsed to `~/.zidane/`. This module adds
4989
- * XDG-Base-Directory-aware defaults so a Linux user with no existing
4990
- * `~/.zidane/` lands in the conventional `~/.config/zidane/`,
4991
- * `~/.local/share/zidane/`, `~/.local/state/zidane/`, `~/.cache/zidane/`
4992
- * trio. Existing setups (anyone with a `~/.zidane/` directory or
4993
- * `ZIDANE_STORAGE_DIR` set) keep the single-dir layout unchanged.
4994
- *
4995
- * Resolution flow — applied in this order:
4996
- *
4997
- * 1. Caller passed an explicit `storageDir` (`ChatOptions.storageDir`)
4998
- * OR `ZIDANE_STORAGE_DIR` is set → **legacy-explicit** single-dir
4999
- * layout under `<storageDir>/<prefix>`. The four slots collapse to
5000
- * the same directory.
5001
- * 2. `~/<prefix>/` already exists on disk → **legacy-existing**
5002
- * single-dir layout under that directory. Upgrades never silently
5003
- * shift files around.
5004
- * 3. Any `XDG_*_HOME` env var is set OR `process.platform === 'linux'`
5005
- * → **XDG split**. Each slot lands in its own dir. Per-slot
5006
- * `ZIDANE_{CONFIG,DATA,CACHE,STATE}_DIR` overrides beat the XDG
5007
- * vars for surgical tweaks.
5008
- * 4. Otherwise (macOS/Windows, no XDG signal, no existing dir) →
5009
- * **default** single-dir layout at `~/<prefix>/`. Day-one behavior
5010
- * on these platforms is unchanged.
5011
- *
5012
- * The `userDir` legacy alias always equals `configDir` so existing code
5013
- * that still references `paths.userDir` keeps reading credentials, mcps,
5014
- * etc. from the right place.
5015
- */
5016
- /**
5017
- * Resolve the four storage slots according to the precedence above.
5018
- *
5019
- * Pure aside from one filesystem probe (`existsSync(home/<prefix>)`)
5020
- * — the same probe `resolveStoragePaths` would do anyway when
5021
- * computing `paths.userDir`. No directories are created here; the
5022
- * relevant write paths (state store, credentials writer, persist
5023
- * dir) handle their own lazy `mkdirSync` already.
5024
- */
5025
- function resolveStorageDirs(opts = {}) {
5026
- const env = opts.env ?? process.env;
5027
- const home = opts.home ?? homedir();
5028
- const platform = opts.platform ?? process.platform;
5029
- const rawPrefix = opts.prefix ?? ".zidane";
5030
- const prefixBare = rawPrefix.replace(/^\./, "");
5031
- const explicitStorageDir = opts.storageDir ?? env.ZIDANE_STORAGE_DIR;
5032
- if (explicitStorageDir) return single(resolve(explicitStorageDir, rawPrefix), "legacy-explicit");
5033
- const legacyHomeDir = resolve(home, rawPrefix);
5034
- if (existsSync(legacyHomeDir)) return single(legacyHomeDir, "legacy-existing");
5035
- if (platform === "linux" || !!env.XDG_CONFIG_HOME || !!env.XDG_DATA_HOME || !!env.XDG_CACHE_HOME || !!env.XDG_STATE_HOME || !!env.ZIDANE_CONFIG_DIR || !!env.ZIDANE_DATA_DIR || !!env.ZIDANE_STATE_DIR || !!env.ZIDANE_CACHE_DIR) return {
5036
- configDir: env.ZIDANE_CONFIG_DIR ?? resolve(env.XDG_CONFIG_HOME || resolve(home, ".config"), prefixBare),
5037
- dataDir: env.ZIDANE_DATA_DIR ?? resolve(env.XDG_DATA_HOME || resolve(home, ".local", "share"), prefixBare),
5038
- stateDir: env.ZIDANE_STATE_DIR ?? resolve(env.XDG_STATE_HOME || resolve(home, ".local", "state"), prefixBare),
5039
- cacheDir: env.ZIDANE_CACHE_DIR ?? resolve(env.XDG_CACHE_HOME || resolve(home, ".cache"), prefixBare),
5040
- mode: "xdg"
5041
- };
5042
- return single(legacyHomeDir, "default");
5043
- }
5044
- function single(base, mode) {
5045
- return {
5046
- configDir: base,
5047
- dataDir: base,
5048
- stateDir: base,
5049
- cacheDir: base,
5050
- mode
5051
- };
5052
- }
5053
- //#endregion
5054
3646
  //#region src/chat/config.ts
5055
3647
  /**
5056
3648
  * Resolve user options into a fully-bound runtime config.
@@ -10378,6 +8970,6 @@ function computeTurnAnchors(items) {
10378
8970
  };
10379
8971
  }
10380
8972
  //#endregion
10381
- export { useMcpAuthDispatch as $, cerebrasDescriptor as $n, compactSummaryEvent as $t, runOAuthLogin as A, bootProfileEnabled as An, getArchivedTodosForRun as Ar, SettingsProvider as At, refreshMcpToolsCatalog as B, performSelfUpdate as Bn, DOING_TASKS_DOCTRINE as Br, CATPPUCCIN_FRAPPE as Bt, projectsFilePath as C, matchesBinding as Cn, singleAgentRegistry as Cr, shortChord as Ct, formatPathForCwd as D, stripJsonComments as Dn, TODO_STATUS_GLYPHS as Dr, SETTINGS_CATEGORIES as Dt, writeProjects as E, readKeybindings as En, TODOWRITE_TOOL as Er, DEFAULT_SETTINGS as Et, parseMcpsFile as F, compareSemver as Fn, selectActiveTodos as Fr, resolveChipColor as Ft, useMcpToolToggleSet as G, credentialsPath as Gn, PLAN_MODE_DOCTRINE_NO_PROMPTS as Gr, DiscoveryProvider as Gt, subscribeMcpToolsCache as H, chatAutoCompactBehavior as Hn, INTERACTION_GUIDANCE as Hr, CATPPUCCIN_MACCHIATO as Ht, projectUserPaths as I, detectLibc as In, setTodosForRun as Ir, resolveTheme as It, parentServerName as J, removeProviderCredential as Jn, buildBuildSystem as Jr, ConfigProvider as Jt, buildVisibleMcpRows as K, readCredentials as Kn, SUBAGENT_GUIDANCE as Kr, useDiscovery as Kt, clearMcpToolsCache as L, detectPackageManager as Ln, useActiveTodos as Lr, VAPORWAVE_THEME as Lt, buildMcpServers as M, buildUpdateHint as Mn, isTodoTool as Mr, useSettings as Mt, defaultMcpsConfigPaths as N, useUpdateCheck as Nn, pickActiveRunId as Nr, BUILTIN_THEMES as Nt, fetchOAuthRedirect as O, useCompletion as On, TODO_WRITE_COUNTS_METADATA_KEY as Or, SETTINGS_CHOICES as Ot, discoverProjectMcps as P, checkForUpdate as Pn, pruneTodosByRun as Pr, DEFAULT_THEME as Pt, McpAuthProvider as Q, anthropicDescriptor as Qn, resolveStorageDirs as Qt, loadMcpToolsCache as R, parseSemver as Rn, ACTIONS_WITH_CARE_DOCTRINE as Rr, GRUVBOX_DARK as Rt, matchesSafelistEntry as S, keybindingsPath as Sn, resolveAgentId as Sr, buildHints as St, suggestSafelistEntry as T, parseBindingSpec as Tn, TODOS_METADATA_KEY as Tr, useEnabledToggleSet as Tt, buildToolToggle as U, detectAuth as Un, INTERACTION_GUIDANCE_NO_PROMPTS as Ur, CATPPUCCIN_MOCHA as Ut, saveMcpToolsCache as V, resolvePlatformPackage as Vn, IDENTITY_PREFIX as Vr, CATPPUCCIN_LATTE as Vt, useMcpToolToggleMap as W, applyApiKeyEnv as Wn, PLAN_MODE_DOCTRINE as Wr, createDiscoverySlot as Wt, mcpCredentialsPath as X, writeCredentials as Xn, envSection as Xr, resolveConfig as Xt, createFileMcpCredentialStore as Y, setProviderCredential as Yn, buildPlanSystem as Yr, useConfig as Yt, patchMcpCredential as Z, BUILTIN_PROVIDERS as Zn, resolveStoragePaths as Zt, useSafeModeQueue as _, KEYBINDING_DEF_BY_ACTION as _n, DEFAULT_AGENT_ID as _r, clipHintsToWidth as _t, useSurfaces as a, loadState as an, modelOptionsFor as ar, ASK_USER_TOOL as at, getSafelist as b, formatBindingForDisplay as bn, PLAN_AGENT as br, cleanTitle as bt, useStreamBuffer as c, serverToolResultSummary as cn, openaiDescriptor as cr, buildResumedToolResultsTurn as ct, discoverProjectSkills as d, titleFromTurns as dn, restoreModelOptions as dr, makeRequestInteraction as dt, createStateStore as en, credKeyOf as er, useMcpAuthState as et, renderSession as f, toolCallPreview as fn, discoverAgentsMd as fr, pendingInteractionsFromTurns as ft, useSafeModeActions as g, KEYBINDING_DEFS as gn, BUILTIN_AGENTS as gr, EMPTY_HINTS as gt, SafeModeProvider as h, DEFAULT_KEYBINDINGS as hn, BUILD_AGENT as hr, useInteractionsQueue as ht, useSelectStyle as i, listSessionMeta as in, localDescriptor as ir, splitMarkdownCodeBlocks as it, supportsOAuth as j, bootTick as jn, getTodosForRun as jr, clampFps as jt, oauthUsesManualCodePaste as k, tryOpenBrowser as kn, createTodoTools as kr, SETTINGS_TOGGLES as kt, buildSkillsConfig as l, stripSpawnTokensLine as ln, openrouterDescriptor as lr, createInteractionTools as lt, writeSessionExport as m, updateToolEventOutcomes as mn, findGitRoot$1 as mr, useInteractionsActions as mt, ThemeProvider as n, eventsFromTurns as nn, getContextWindow as nr, reduceMcpAuth as nt, useSyntaxStyles as o, marginTopFor as on, modelSupportsReasoning as or, InteractionsProvider as ot, resolveSessionExportTarget as p, toolResultText as pn, renderAgentsMdBlock as pr, serializeInteractionResponse as pt, indexOfServerRow as q, readProviderCredential as qn, TOKEN_DISCIPLINE_DOCTRINE as qr, useDiscoveryOptional as qt, useColors as r, lastContextSizeFromTurns as rn, getModelInfo as rr, normalizeMarkdownCodeFences as rt, useTheme as s, saveState as sn, modelsForDescriptor as sr, PRESENT_PLAN_TOOL as st, computeTurnAnchors as t, deriveSessionTitle as tn, enabledModelOptions as tr, getMcpAuthStatus as tt, defaultSkillScanPaths as u, sumRunCosts as un, piIdOf as ur, isInteractionTool as ut, IMPLICITLY_SAFE_TOOLS as v, KEYBINDING_KEY_COL_WIDTH as vn, DEFAULT_BUDGET_EXCLUDE_TOOLS as vr, hintsLength as vt, readProjects as w, mergeKeybindings as wn, TODOREAD_TOOL as wr, listProjectFiles as wt, isOnSafelist as x, groupBindings as xn, accentColor as xr, generateSessionTitle as xt, addToSafelist as y, ensureKeybindingsFile as yn, DEFAULT_PERSIST_EXCLUDE_TOOLS as yr, truncateTrailing as yt, mcpToolsCachePath as z, performInPlaceSelfUpdate as zn, COMMUNICATION_DOCTRINE as zr, GRUVBOX_LIGHT as zt };
8973
+ export { useMcpAuthDispatch as $, resolveAgentId as $n, createStateStore as $t, runOAuthLogin as A, bootTick as An, SettingsProvider as At, refreshMcpToolsCatalog as B, resolvePlatformPackage as Bn, CATPPUCCIN_FRAPPE as Bt, projectsFilePath as C, mergeKeybindings as Cn, SUBAGENT_GUIDANCE as Cr, shortChord as Ct, formatPathForCwd as D, useCompletion as Dn, envSection as Dr, SETTINGS_CATEGORIES as Dt, writeProjects as E, stripJsonComments as En, buildPlanSystem as Er, DEFAULT_SETTINGS as Et, parseMcpsFile as F, detectLibc as Fn, resolveChipColor as Ft, useMcpToolToggleSet as G, findGitRoot$1 as Gn, DiscoveryProvider as Gt, subscribeMcpToolsCache as H, detectAuth as Hn, CATPPUCCIN_MACCHIATO as Ht, projectUserPaths as I, detectPackageManager as In, resolveTheme as It, parentServerName as J, DEFAULT_AGENT_ID as Jn, ConfigProvider as Jt, buildVisibleMcpRows as K, BUILD_AGENT as Kn, useDiscovery as Kt, clearMcpToolsCache as L, parseSemver as Ln, VAPORWAVE_THEME as Lt, buildMcpServers as M, useUpdateCheck as Mn, useSettings as Mt, defaultMcpsConfigPaths as N, checkForUpdate as Nn, BUILTIN_THEMES as Nt, fetchOAuthRedirect as O, tryOpenBrowser as On, SETTINGS_CHOICES as Ot, discoverProjectMcps as P, compareSemver as Pn, DEFAULT_THEME as Pt, McpAuthProvider as Q, accentColor as Qn, compactSummaryEvent as Qt, loadMcpToolsCache as R, performInPlaceSelfUpdate as Rn, GRUVBOX_DARK as Rt, matchesSafelistEntry as S, matchesBinding as Sn, PLAN_MODE_DOCTRINE_NO_PROMPTS as Sr, buildHints as St, suggestSafelistEntry as T, readKeybindings as Tn, buildBuildSystem as Tr, useEnabledToggleSet as Tt, buildToolToggle as U, discoverAgentsMd as Un, CATPPUCCIN_MOCHA as Ut, saveMcpToolsCache as V, chatAutoCompactBehavior as Vn, CATPPUCCIN_LATTE as Vt, useMcpToolToggleMap as W, renderAgentsMdBlock as Wn, createDiscoverySlot as Wt, mcpCredentialsPath as X, DEFAULT_PERSIST_EXCLUDE_TOOLS as Xn, resolveConfig as Xt, createFileMcpCredentialStore as Y, DEFAULT_BUDGET_EXCLUDE_TOOLS as Yn, useConfig as Yt, patchMcpCredential as Z, PLAN_AGENT as Zn, resolveStoragePaths as Zt, useSafeModeQueue as _, KEYBINDING_KEY_COL_WIDTH as _n, DOING_TASKS_DOCTRINE as _r, clipHintsToWidth as _t, useSurfaces as a, marginTopFor as an, TODO_WRITE_COUNTS_METADATA_KEY as ar, ASK_USER_TOOL as at, getSafelist as b, groupBindings as bn, INTERACTION_GUIDANCE_NO_PROMPTS as br, cleanTitle as bt, useStreamBuffer as c, stripSpawnTokensLine as cn, getTodosForRun as cr, buildResumedToolResultsTurn as ct, discoverProjectSkills as d, toolCallPreview as dn, pruneTodosByRun as dr, makeRequestInteraction as dt, deriveSessionTitle as en, singleAgentRegistry as er, useMcpAuthState as et, renderSession as f, toolResultText as fn, selectActiveTodos as fr, pendingInteractionsFromTurns as ft, useSafeModeActions as g, KEYBINDING_DEF_BY_ACTION as gn, COMMUNICATION_DOCTRINE as gr, EMPTY_HINTS as gt, SafeModeProvider as h, KEYBINDING_DEFS as hn, ACTIONS_WITH_CARE_DOCTRINE as hr, useInteractionsQueue as ht, useSelectStyle as i, loadState as in, TODO_STATUS_GLYPHS as ir, splitMarkdownCodeBlocks as it, supportsOAuth as j, buildUpdateHint as jn, clampFps as jt, oauthUsesManualCodePaste as k, bootProfileEnabled as kn, SETTINGS_TOGGLES as kt, buildSkillsConfig as l, sumRunCosts as ln, isTodoTool as lr, createInteractionTools as lt, writeSessionExport as m, DEFAULT_KEYBINDINGS as mn, useActiveTodos as mr, useInteractionsActions as mt, ThemeProvider as n, lastContextSizeFromTurns as nn, TODOS_METADATA_KEY as nr, reduceMcpAuth as nt, useSyntaxStyles as o, saveState as on, createTodoTools as or, InteractionsProvider as ot, resolveSessionExportTarget as p, updateToolEventOutcomes as pn, setTodosForRun as pr, serializeInteractionResponse as pt, indexOfServerRow as q, BUILTIN_AGENTS as qn, useDiscoveryOptional as qt, useColors as r, listSessionMeta as rn, TODOWRITE_TOOL as rr, normalizeMarkdownCodeFences as rt, useTheme as s, serverToolResultSummary as sn, getArchivedTodosForRun as sr, PRESENT_PLAN_TOOL as st, computeTurnAnchors as t, eventsFromTurns as tn, TODOREAD_TOOL as tr, getMcpAuthStatus as tt, defaultSkillScanPaths as u, titleFromTurns as un, pickActiveRunId as ur, isInteractionTool as ut, IMPLICITLY_SAFE_TOOLS as v, ensureKeybindingsFile as vn, IDENTITY_PREFIX as vr, hintsLength as vt, readProjects as w, parseBindingSpec as wn, TOKEN_DISCIPLINE_DOCTRINE as wr, listProjectFiles as wt, isOnSafelist as x, keybindingsPath as xn, PLAN_MODE_DOCTRINE as xr, generateSessionTitle as xt, addToSafelist as y, formatBindingForDisplay as yn, INTERACTION_GUIDANCE as yr, truncateTrailing as yt, mcpToolsCachePath as z, performSelfUpdate as zn, GRUVBOX_LIGHT as zt };
10382
8974
 
10383
- //# sourceMappingURL=transcript-anchors-B_a1edre.js.map
8975
+ //# sourceMappingURL=transcript-anchors-Cq-8gx8u.js.map