svamp-cli 0.2.264 → 0.2.265

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 (26) hide show
  1. package/dist/{agentCommands-BZPDL-1B.mjs → agentCommands-CKd1jXfX.mjs} +5 -5
  2. package/dist/{auth-Dv8z9Dg0.mjs → auth-Ctsyrv9N.mjs} +2 -2
  3. package/dist/{cli-CNorTj26.mjs → cli-DtMJLEig.mjs} +63 -63
  4. package/dist/cli.mjs +3 -3
  5. package/dist/{commands-D_YqjDY4.mjs → commands-BI7IDVIE.mjs} +3 -3
  6. package/dist/{commands-CSNOKRVn.mjs → commands-BSBxY9iQ.mjs} +2 -2
  7. package/dist/{commands-BmpHRl5s.mjs → commands-BUhsW3bf.mjs} +2 -2
  8. package/dist/{commands-DNyzbWPw.mjs → commands-Bx4WYv3I.mjs} +6 -6
  9. package/dist/{commands-Dg_nTA2I.mjs → commands-CMZS3jBt.mjs} +2 -2
  10. package/dist/{commands-DuPRCS2G.mjs → commands-C_74tXoO.mjs} +1 -1
  11. package/dist/{commands-Kk7AH5dR.mjs → commands-De9crCdY.mjs} +2 -2
  12. package/dist/{fleet-DkFOlkxS.mjs → fleet-D3ycNACq.mjs} +1 -1
  13. package/dist/{frpc-DmaWRu0M.mjs → frpc-Cwn_s0Fs.mjs} +2 -2
  14. package/dist/{headlessCli-DFsJaJ2p.mjs → headlessCli-nWnqozcZ.mjs} +3 -3
  15. package/dist/index.mjs +2 -2
  16. package/dist/package-BHHMByEa.mjs +64 -0
  17. package/dist/{rpc-DswYjrCh.mjs → rpc-BnHMvDxQ.mjs} +2 -2
  18. package/dist/{rpc-Db3BR76v.mjs → rpc-DwlZTOtK.mjs} +2 -2
  19. package/dist/{run-C3qTQEDV.mjs → run-DW-5GWjY.mjs} +1 -1
  20. package/dist/{run-kkeXP2d_.mjs → run-b-b_szy6.mjs} +718 -341
  21. package/dist/{scheduler-DhKDP3hC.mjs → scheduler-BxijQDI8.mjs} +2 -2
  22. package/dist/{serveCommands-BVJR3K9P.mjs → serveCommands-CL6B6kEf.mjs} +5 -5
  23. package/dist/{serveManager-a_dIQuK4.mjs → serveManager-Dy1ZZNaY.mjs} +3 -3
  24. package/dist/{sideband-CIJUNKUA.mjs → sideband-DywLL2xs.mjs} +2 -2
  25. package/package.json +2 -2
  26. package/dist/package-Cz5Z0buw.mjs +0 -64
@@ -5,13 +5,13 @@ import path__default, { join as join$1, resolve as resolve$1, dirname as dirname
5
5
  import { fileURLToPath } from 'url';
6
6
  import { execFile, spawn as spawn$1, execSync as execSync$1, exec as exec$1 } from 'child_process';
7
7
  import { randomUUID as randomUUID$1 } from 'crypto';
8
- import { randomBytes, createHash, randomUUID } from 'node:crypto';
9
- import { existsSync, readFileSync, realpathSync, readdirSync, statSync, mkdirSync, writeFileSync, renameSync, rmSync, appendFileSync, unlinkSync } from 'node:fs';
8
+ import { randomUUID, randomBytes, createHash } from 'node:crypto';
9
+ import { existsSync, readFileSync, mkdirSync, writeFileSync, chmodSync, realpathSync, readdirSync, statSync, renameSync, rmSync, appendFileSync, unlinkSync } from 'node:fs';
10
10
  import { exec, execSync, spawn, execFile as execFile$1, execFileSync } from 'node:child_process';
11
11
  import { promisify } from 'util';
12
- import { extname, resolve, join, sep, basename, dirname } from 'node:path';
13
- import { EventEmitter } from 'node:events';
14
12
  import os, { homedir, platform } from 'node:os';
13
+ import { join, extname, resolve, sep, basename, dirname } from 'node:path';
14
+ import { EventEmitter } from 'node:events';
15
15
  import { ndJsonStream, ClientSideConnection } from '@agentclientprotocol/sdk';
16
16
  import { createInterface } from 'node:readline';
17
17
  import { mkdir, rm, chmod, access, mkdtemp, copyFile, writeFile, readdir, stat, readFile as readFile$1 } from 'node:fs/promises';
@@ -1218,6 +1218,547 @@ async function killDescendant(rootPid, targetPid, signal = "SIGTERM") {
1218
1218
  }
1219
1219
  }
1220
1220
 
1221
+ const EXAMPLE_HYPHA_PROXY_URL = "https://proxy.hypha.aicell.io";
1222
+ const MODE_KEY = "SVAMP_CLAUDE_PROXY";
1223
+ const HYPHA_PROXY_URL_KEY = "SVAMP_HYPHA_PROXY_URL";
1224
+ const MANAGED_KEYS = /* @__PURE__ */ new Set([
1225
+ MODE_KEY,
1226
+ HYPHA_PROXY_URL_KEY,
1227
+ "ANTHROPIC_BASE_URL",
1228
+ "ANTHROPIC_API_KEY"
1229
+ ]);
1230
+ function normalizeProxyUrl(raw) {
1231
+ const trimmed = raw.trim().replace(/\/+$/, "");
1232
+ if (trimmed.endsWith("/v1")) {
1233
+ throw new Error(
1234
+ "Hypha proxy URL must not end in /v1 \u2014 the SDK appends it, producing double /v1/v1/messages"
1235
+ );
1236
+ }
1237
+ return trimmed;
1238
+ }
1239
+ function resolveHyphaProxyUrl() {
1240
+ const configured = (process.env[HYPHA_PROXY_URL_KEY] || "").trim();
1241
+ if (!configured) return void 0;
1242
+ try {
1243
+ return normalizeProxyUrl(configured);
1244
+ } catch {
1245
+ return void 0;
1246
+ }
1247
+ }
1248
+ function envFilePath() {
1249
+ const svampHome = process.env.SVAMP_HOME || join(homedir(), ".svamp");
1250
+ return join(svampHome, ".env");
1251
+ }
1252
+ function readEnvLines() {
1253
+ const file = envFilePath();
1254
+ if (!existsSync(file)) return [];
1255
+ return readFileSync(file, "utf-8").split("\n");
1256
+ }
1257
+ function writeEnvLines(lines) {
1258
+ const file = envFilePath();
1259
+ const dir = join(file, "..");
1260
+ if (!existsSync(dir)) mkdirSync(dir, { recursive: true });
1261
+ while (lines.length > 0 && lines[lines.length - 1].trim() === "") {
1262
+ lines.pop();
1263
+ }
1264
+ writeFileSync(file, lines.join("\n") + "\n", "utf-8");
1265
+ }
1266
+ function updateEnvFile(updates) {
1267
+ const lines = readEnvLines();
1268
+ const kept = [];
1269
+ const remainingUpdates = new Map(Object.entries(updates));
1270
+ for (const line of lines) {
1271
+ const trimmed = line.trim();
1272
+ if (!trimmed || trimmed.startsWith("#")) {
1273
+ kept.push(line);
1274
+ continue;
1275
+ }
1276
+ const eq = trimmed.indexOf("=");
1277
+ const key = eq === -1 ? trimmed : trimmed.slice(0, eq);
1278
+ if (remainingUpdates.has(key)) {
1279
+ const v = remainingUpdates.get(key);
1280
+ remainingUpdates.delete(key);
1281
+ if (v === void 0) continue;
1282
+ kept.push(`${key}=${v}`);
1283
+ continue;
1284
+ }
1285
+ kept.push(line);
1286
+ }
1287
+ for (const [key, v] of remainingUpdates) {
1288
+ if (v === void 0) continue;
1289
+ kept.push(`${key}=${v}`);
1290
+ }
1291
+ writeEnvLines(kept);
1292
+ }
1293
+ function currentMode() {
1294
+ const raw = (process.env[MODE_KEY] || "").trim().toLowerCase();
1295
+ if (raw === "hypha" || raw === "hypha-proxy") return "hypha";
1296
+ if (raw === "custom") return "custom";
1297
+ return "login";
1298
+ }
1299
+ function redactKey(key) {
1300
+ if (!key) return void 0;
1301
+ if (key.length <= 12) return "***";
1302
+ return `${key.slice(0, 8)}\u2026${key.slice(-4)}`;
1303
+ }
1304
+ function getClaudeAuthStatus() {
1305
+ const mode = currentMode();
1306
+ if (mode === "hypha") {
1307
+ const token = process.env.HYPHA_TOKEN;
1308
+ const proxyUrl = resolveHyphaProxyUrl();
1309
+ return {
1310
+ mode,
1311
+ baseUrl: proxyUrl,
1312
+ apiKeyPreview: redactKey(token),
1313
+ hyphaTokenMissing: !token,
1314
+ hyphaProxyUrlMissing: !proxyUrl
1315
+ };
1316
+ }
1317
+ if (mode === "custom") {
1318
+ return {
1319
+ mode,
1320
+ baseUrl: process.env.ANTHROPIC_BASE_URL,
1321
+ apiKeyPreview: redactKey(process.env.ANTHROPIC_API_KEY)
1322
+ };
1323
+ }
1324
+ return { mode: "login" };
1325
+ }
1326
+ function setClaudeAuthHyphaProxy(url) {
1327
+ if (!url || !url.trim()) {
1328
+ throw new Error(
1329
+ `A Hypha proxy URL is required (no default). Example: svamp daemon auth use-hypha-proxy ${EXAMPLE_HYPHA_PROXY_URL}`
1330
+ );
1331
+ }
1332
+ updateEnvFile({
1333
+ [MODE_KEY]: "hypha",
1334
+ [HYPHA_PROXY_URL_KEY]: normalizeProxyUrl(url),
1335
+ // Hypha mode resolves token live at spawn — no stored copy.
1336
+ ANTHROPIC_BASE_URL: void 0,
1337
+ ANTHROPIC_API_KEY: void 0
1338
+ });
1339
+ }
1340
+ function setClaudeAuthLogin() {
1341
+ updateEnvFile({
1342
+ [MODE_KEY]: void 0,
1343
+ [HYPHA_PROXY_URL_KEY]: void 0,
1344
+ ANTHROPIC_BASE_URL: void 0,
1345
+ ANTHROPIC_API_KEY: void 0
1346
+ });
1347
+ }
1348
+ function setClaudeAuthCustom(baseUrl, apiKey) {
1349
+ if (!baseUrl) throw new Error("ANTHROPIC_BASE_URL is required for custom mode");
1350
+ if (!apiKey) throw new Error("ANTHROPIC_API_KEY is required for custom mode");
1351
+ const trimmed = baseUrl.replace(/\/+$/, "");
1352
+ if (trimmed.endsWith("/v1")) {
1353
+ throw new Error(
1354
+ "ANTHROPIC_BASE_URL must not end in /v1 \u2014 the SDK appends it, producing double /v1/v1/messages"
1355
+ );
1356
+ }
1357
+ updateEnvFile({
1358
+ [MODE_KEY]: "custom",
1359
+ [HYPHA_PROXY_URL_KEY]: void 0,
1360
+ ANTHROPIC_BASE_URL: trimmed,
1361
+ ANTHROPIC_API_KEY: apiKey
1362
+ });
1363
+ }
1364
+ function applyClaudeProxyEnv(spawnEnv) {
1365
+ const mode = currentMode();
1366
+ if (mode === "hypha") {
1367
+ const proxyUrl = resolveHyphaProxyUrl();
1368
+ if (!proxyUrl) {
1369
+ delete spawnEnv.ANTHROPIC_BASE_URL;
1370
+ delete spawnEnv.ANTHROPIC_API_KEY;
1371
+ return `hypha mode but ${HYPHA_PROXY_URL_KEY} not configured \u2014 run \`svamp daemon auth use-hypha-proxy <URL>\`; falling back to login`;
1372
+ }
1373
+ const token = process.env.HYPHA_TOKEN;
1374
+ if (!token) {
1375
+ delete spawnEnv.ANTHROPIC_BASE_URL;
1376
+ delete spawnEnv.ANTHROPIC_API_KEY;
1377
+ return "hypha mode but HYPHA_TOKEN missing \u2014 falling back to login";
1378
+ }
1379
+ spawnEnv.ANTHROPIC_BASE_URL = proxyUrl;
1380
+ spawnEnv.ANTHROPIC_API_KEY = token;
1381
+ const cacheOverride = spawnEnv.ENABLE_PROMPT_CACHING_1H ?? process.env.ENABLE_PROMPT_CACHING_1H ?? spawnEnv.FORCE_PROMPT_CACHING_5M ?? process.env.FORCE_PROMPT_CACHING_5M;
1382
+ if (cacheOverride === void 0) spawnEnv.ENABLE_PROMPT_CACHING_1H = "1";
1383
+ return `hypha proxy (${proxyUrl}, 1h cache TTL)`;
1384
+ }
1385
+ if (mode === "custom") {
1386
+ const url = process.env.ANTHROPIC_BASE_URL;
1387
+ const key = process.env.ANTHROPIC_API_KEY;
1388
+ if (!url || !key) {
1389
+ delete spawnEnv.ANTHROPIC_BASE_URL;
1390
+ delete spawnEnv.ANTHROPIC_API_KEY;
1391
+ return "custom mode but ANTHROPIC_BASE_URL/API_KEY missing \u2014 falling back to login";
1392
+ }
1393
+ spawnEnv.ANTHROPIC_BASE_URL = url;
1394
+ spawnEnv.ANTHROPIC_API_KEY = key;
1395
+ return `custom proxy (${url})`;
1396
+ }
1397
+ delete spawnEnv.ANTHROPIC_BASE_URL;
1398
+ delete spawnEnv.ANTHROPIC_API_KEY;
1399
+ return null;
1400
+ }
1401
+ const MANAGED_ENV_KEYS = Array.from(MANAGED_KEYS);
1402
+
1403
+ var claudeAuth = /*#__PURE__*/Object.freeze({
1404
+ __proto__: null,
1405
+ EXAMPLE_HYPHA_PROXY_URL: EXAMPLE_HYPHA_PROXY_URL,
1406
+ MANAGED_ENV_KEYS: MANAGED_ENV_KEYS,
1407
+ applyClaudeProxyEnv: applyClaudeProxyEnv,
1408
+ getClaudeAuthStatus: getClaudeAuthStatus,
1409
+ resolveHyphaProxyUrl: resolveHyphaProxyUrl,
1410
+ setClaudeAuthCustom: setClaudeAuthCustom,
1411
+ setClaudeAuthHyphaProxy: setClaudeAuthHyphaProxy,
1412
+ setClaudeAuthLogin: setClaudeAuthLogin,
1413
+ updateEnvFile: updateEnvFile
1414
+ });
1415
+
1416
+ function codexPermissionSettings(mode) {
1417
+ switch (mode) {
1418
+ case "yolo":
1419
+ case "bypassPermissions":
1420
+ case "danger-full-access":
1421
+ return { approvalPolicy: "never", sandbox: "danger-full-access" };
1422
+ case "safe-yolo":
1423
+ case "acceptEdits":
1424
+ return { approvalPolicy: "on-failure", sandbox: "workspace-write" };
1425
+ case "read-only":
1426
+ case "plan":
1427
+ return { approvalPolicy: "on-request", sandbox: "read-only" };
1428
+ case "default":
1429
+ default:
1430
+ return { approvalPolicy: "on-request", sandbox: "workspace-write" };
1431
+ }
1432
+ }
1433
+ const CODEX_PROVIDER_KEY_ENV = "SVAMP_CODEX_API_KEY";
1434
+ const PROVIDER_NAME = "svamp";
1435
+ function resolveCodexProvider(env = process.env) {
1436
+ return {
1437
+ apiBase: env.SVAMP_CODEX_API_BASE || env.LLM_API_BASE || void 0,
1438
+ apiKey: env.SVAMP_CODEX_API_KEY || env.LLM_API_KEY || void 0,
1439
+ model: env.SVAMP_CODEX_MODEL || env.LLM_MODEL || void 0
1440
+ };
1441
+ }
1442
+ function hasCustomProvider(cfg) {
1443
+ return !!(cfg.apiBase && cfg.apiKey);
1444
+ }
1445
+ function buildCodexProviderArgs(cfg) {
1446
+ if (!hasCustomProvider(cfg)) {
1447
+ return { configArgs: cfg.model ? ["-c", `model=${JSON.stringify(cfg.model)}`] : [], env: {}, model: cfg.model };
1448
+ }
1449
+ const configArgs = [
1450
+ "-c",
1451
+ `model_provider=${JSON.stringify(PROVIDER_NAME)}`,
1452
+ "-c",
1453
+ `model_providers.${PROVIDER_NAME}.name=${JSON.stringify(PROVIDER_NAME)}`,
1454
+ "-c",
1455
+ `model_providers.${PROVIDER_NAME}.base_url=${JSON.stringify(cfg.apiBase)}`,
1456
+ "-c",
1457
+ `model_providers.${PROVIDER_NAME}.env_key=${JSON.stringify(CODEX_PROVIDER_KEY_ENV)}`,
1458
+ "-c",
1459
+ `model_providers.${PROVIDER_NAME}.wire_api=${JSON.stringify("responses")}`
1460
+ ];
1461
+ if (cfg.model) configArgs.push("-c", `model=${JSON.stringify(cfg.model)}`);
1462
+ return { configArgs, env: { [CODEX_PROVIDER_KEY_ENV]: cfg.apiKey }, model: cfg.model };
1463
+ }
1464
+
1465
+ var codexProvider = /*#__PURE__*/Object.freeze({
1466
+ __proto__: null,
1467
+ CODEX_PROVIDER_KEY_ENV: CODEX_PROVIDER_KEY_ENV,
1468
+ buildCodexProviderArgs: buildCodexProviderArgs,
1469
+ codexPermissionSettings: codexPermissionSettings,
1470
+ hasCustomProvider: hasCustomProvider,
1471
+ resolveCodexProvider: resolveCodexProvider
1472
+ });
1473
+
1474
+ const METHODS_BY_VENDOR = {
1475
+ anthropic: ["anthropic-api-key", "claude-oauth-token", "hypha-proxy", "anthropic-gateway"],
1476
+ openai: ["openai-api-key", "chatgpt-oauth", "openai-gateway"]
1477
+ };
1478
+ function accountsFilePath(svampHome) {
1479
+ const home = svampHome || process.env.SVAMP_HOME || join(homedir(), ".svamp");
1480
+ return join(home, "accounts.json");
1481
+ }
1482
+ function accountCodexHomeDir(accountId, svampHome) {
1483
+ const home = svampHome || process.env.SVAMP_HOME || join(homedir(), ".svamp");
1484
+ return join(home, "codex-accounts", accountId);
1485
+ }
1486
+ function loadAccounts(svampHome) {
1487
+ const file = accountsFilePath(svampHome);
1488
+ if (!existsSync(file)) return [];
1489
+ try {
1490
+ const parsed = JSON.parse(readFileSync(file, "utf-8"));
1491
+ if (!parsed || !Array.isArray(parsed.accounts)) return [];
1492
+ return parsed.accounts;
1493
+ } catch {
1494
+ return [];
1495
+ }
1496
+ }
1497
+ function saveAccounts(accounts, svampHome) {
1498
+ const file = accountsFilePath(svampHome);
1499
+ const dir = join(file, "..");
1500
+ if (!existsSync(dir)) mkdirSync(dir, { recursive: true });
1501
+ const payload = { version: 1, accounts };
1502
+ writeFileSync(file, JSON.stringify(payload, null, 2) + "\n", { encoding: "utf-8", mode: 384 });
1503
+ try {
1504
+ chmodSync(file, 384);
1505
+ } catch {
1506
+ }
1507
+ }
1508
+ function previewSecret(secret) {
1509
+ if (!secret) return void 0;
1510
+ if (secret.length <= 8) return "\u2022\u2022\u2022\u2022";
1511
+ return `${secret.slice(0, 4)}\u2026${secret.slice(-4)}`;
1512
+ }
1513
+ function redactAccount(a) {
1514
+ const { secret, ...rest } = a;
1515
+ return { ...rest, secretPreview: previewSecret(secret) };
1516
+ }
1517
+ function redactAccounts(accounts) {
1518
+ return accounts.map(redactAccount);
1519
+ }
1520
+ function defaultAccountIds(accounts) {
1521
+ const out = { anthropic: null, openai: null };
1522
+ for (const a of accounts) {
1523
+ if (a.isDefault) out[a.vendor] = a.id;
1524
+ }
1525
+ return out;
1526
+ }
1527
+ function validateAccountInput(input) {
1528
+ const vendor = input.vendor;
1529
+ const method = input.method;
1530
+ if (vendor !== "anthropic" && vendor !== "openai") {
1531
+ throw new Error(`Invalid account vendor: ${String(vendor)}`);
1532
+ }
1533
+ if (!method || !METHODS_BY_VENDOR[vendor].includes(method)) {
1534
+ throw new Error(`Invalid method "${String(method)}" for vendor "${vendor}"`);
1535
+ }
1536
+ if (!input.label || !input.label.trim()) {
1537
+ throw new Error("Account label is required");
1538
+ }
1539
+ const needsSecret = method !== "hypha-proxy";
1540
+ if (needsSecret && !input.secret && !input.id) {
1541
+ throw new Error(`A credential is required for method "${method}"`);
1542
+ }
1543
+ if ((method === "anthropic-gateway" || method === "openai-gateway") && !input.baseUrl) {
1544
+ throw new Error(`A base URL is required for a custom gateway`);
1545
+ }
1546
+ return { vendor, method };
1547
+ }
1548
+ function upsertAccount(input, svampHome) {
1549
+ const { vendor, method } = validateAccountInput(input);
1550
+ const accounts = loadAccounts(svampHome);
1551
+ const now = Date.now();
1552
+ const existing = input.id ? accounts.find((a) => a.id === input.id) : void 0;
1553
+ const account = {
1554
+ id: existing?.id || input.id || randomUUID(),
1555
+ label: input.label.trim(),
1556
+ vendor,
1557
+ method,
1558
+ // Keep the old secret if the update left it blank (edit label/model without re-entry).
1559
+ secret: input.secret && input.secret.length > 0 ? input.secret : existing?.secret,
1560
+ baseUrl: input.baseUrl?.trim() || void 0,
1561
+ model: input.model?.trim() || void 0,
1562
+ isDefault: input.isDefault ?? existing?.isDefault ?? false,
1563
+ createdBy: existing?.createdBy || input.createdBy,
1564
+ createdAt: existing?.createdAt || now,
1565
+ updatedAt: now
1566
+ };
1567
+ let next = accounts.filter((a) => a.id !== account.id);
1568
+ if (account.isDefault) {
1569
+ next = next.map((a) => a.vendor === vendor ? { ...a, isDefault: false } : a);
1570
+ }
1571
+ if (!next.some((a) => a.vendor === vendor && a.isDefault) && !account.isDefault) {
1572
+ account.isDefault = true;
1573
+ }
1574
+ next.push(account);
1575
+ saveAccounts(next, svampHome);
1576
+ return account;
1577
+ }
1578
+ function deleteAccount(id, svampHome) {
1579
+ const accounts = loadAccounts(svampHome);
1580
+ const removed = accounts.find((a) => a.id === id);
1581
+ let next = accounts.filter((a) => a.id !== id);
1582
+ if (removed?.isDefault) {
1583
+ const idx = next.findIndex((a) => a.vendor === removed.vendor);
1584
+ if (idx >= 0) next[idx] = { ...next[idx], isDefault: true };
1585
+ }
1586
+ saveAccounts(next, svampHome);
1587
+ }
1588
+ function setDefaultAccount(vendor, id, svampHome) {
1589
+ const accounts = loadAccounts(svampHome);
1590
+ if (!accounts.some((a) => a.id === id && a.vendor === vendor)) {
1591
+ throw new Error(`No ${vendor} account with id ${id}`);
1592
+ }
1593
+ const next = accounts.map(
1594
+ (a) => a.vendor === vendor ? { ...a, isDefault: a.id === id, updatedAt: Date.now() } : a
1595
+ );
1596
+ saveAccounts(next, svampHome);
1597
+ }
1598
+ function getAccount(id, svampHome) {
1599
+ return loadAccounts(svampHome).find((a) => a.id === id);
1600
+ }
1601
+ function stripV1(url) {
1602
+ const trimmed = url.trim().replace(/\/+$/, "");
1603
+ if (trimmed.endsWith("/v1")) {
1604
+ throw new Error("Base URL must not end in /v1 \u2014 the SDK appends it (double /v1/v1)");
1605
+ }
1606
+ return trimmed;
1607
+ }
1608
+ function resolveAccountEnv(account, env = process.env, svampHome) {
1609
+ switch (account.method) {
1610
+ case "anthropic-api-key":
1611
+ return {
1612
+ vendor: "anthropic",
1613
+ claudeEnv: {
1614
+ ANTHROPIC_API_KEY: account.secret,
1615
+ ANTHROPIC_BASE_URL: account.baseUrl ? stripV1(account.baseUrl) : void 0,
1616
+ CLAUDE_CODE_OAUTH_TOKEN: void 0
1617
+ },
1618
+ describe: `anthropic API key (${account.label})`
1619
+ };
1620
+ case "anthropic-gateway": {
1621
+ const url = stripV1(account.baseUrl || "");
1622
+ return {
1623
+ vendor: "anthropic",
1624
+ claudeEnv: {
1625
+ ANTHROPIC_BASE_URL: url,
1626
+ ANTHROPIC_API_KEY: account.secret,
1627
+ CLAUDE_CODE_OAUTH_TOKEN: void 0
1628
+ },
1629
+ describe: `anthropic gateway ${url} (${account.label})`
1630
+ };
1631
+ }
1632
+ case "hypha-proxy": {
1633
+ const url = account.baseUrl ? stripV1(account.baseUrl) : resolveHyphaProxyUrl();
1634
+ const token = env.HYPHA_TOKEN;
1635
+ if (!url) throw new Error("hypha-proxy account has no URL (set SVAMP_HYPHA_PROXY_URL or an account base URL)");
1636
+ if (!token) throw new Error("hypha-proxy account requires HYPHA_TOKEN on the machine");
1637
+ return {
1638
+ vendor: "anthropic",
1639
+ claudeEnv: {
1640
+ ANTHROPIC_BASE_URL: url,
1641
+ ANTHROPIC_API_KEY: token,
1642
+ CLAUDE_CODE_OAUTH_TOKEN: void 0,
1643
+ // #0203: subscription backend qualifies for the free 1h prompt-cache TTL.
1644
+ ENABLE_PROMPT_CACHING_1H: env.FORCE_PROMPT_CACHING_5M ? void 0 : "1"
1645
+ },
1646
+ describe: `hypha proxy ${url} (${account.label})`
1647
+ };
1648
+ }
1649
+ case "claude-oauth-token":
1650
+ return {
1651
+ vendor: "anthropic",
1652
+ claudeEnv: {
1653
+ CLAUDE_CODE_OAUTH_TOKEN: account.secret,
1654
+ ANTHROPIC_BASE_URL: void 0,
1655
+ ANTHROPIC_API_KEY: void 0
1656
+ },
1657
+ describe: `claude subscription token (${account.label})`
1658
+ };
1659
+ case "openai-api-key":
1660
+ return {
1661
+ vendor: "openai",
1662
+ codexProvider: { model: account.model },
1663
+ extraEnv: { OPENAI_API_KEY: account.secret || "" },
1664
+ describe: `openai API key (${account.label})`
1665
+ };
1666
+ case "openai-gateway": {
1667
+ const url = stripV1(account.baseUrl || "");
1668
+ const provider = { apiBase: url, apiKey: account.secret, model: account.model };
1669
+ return {
1670
+ vendor: "openai",
1671
+ codexProvider: provider,
1672
+ extraEnv: { [CODEX_PROVIDER_KEY_ENV]: account.secret || "" },
1673
+ describe: `openai gateway ${url} (${account.label})`
1674
+ };
1675
+ }
1676
+ case "chatgpt-oauth": {
1677
+ const codexHome = accountCodexHomeDir(account.id, svampHome);
1678
+ return {
1679
+ vendor: "openai",
1680
+ codexProvider: { model: account.model },
1681
+ codexHome,
1682
+ extraEnv: { CODEX_HOME: codexHome },
1683
+ describe: `chatgpt subscription (${account.label})`
1684
+ };
1685
+ }
1686
+ default:
1687
+ throw new Error(`Unknown account method: ${account.method}`);
1688
+ }
1689
+ }
1690
+ function ensureCodexHome(account, svampHome) {
1691
+ const dir = accountCodexHomeDir(account.id, svampHome);
1692
+ if (!existsSync(dir)) mkdirSync(dir, { recursive: true });
1693
+ const authPath = join(dir, "auth.json");
1694
+ const body = JSON.stringify({ OPENAI_API_KEY: null, tokens: { access_token: account.secret || "" } }, null, 2);
1695
+ writeFileSync(authPath, body + "\n", { encoding: "utf-8", mode: 384 });
1696
+ try {
1697
+ chmodSync(authPath, 384);
1698
+ } catch {
1699
+ }
1700
+ return dir;
1701
+ }
1702
+ async function probeAccount(account, env = process.env) {
1703
+ let resolved;
1704
+ try {
1705
+ resolved = resolveAccountEnv(account, env);
1706
+ } catch (e) {
1707
+ return { ok: false, detail: `Misconfigured: ${e?.message || e}` };
1708
+ }
1709
+ const withTimeout = async (url, init) => {
1710
+ const ctrl = new AbortController();
1711
+ const t = setTimeout(() => ctrl.abort(), 8e3);
1712
+ try {
1713
+ return await fetch(url, { ...init, signal: ctrl.signal });
1714
+ } finally {
1715
+ clearTimeout(t);
1716
+ }
1717
+ };
1718
+ try {
1719
+ switch (account.method) {
1720
+ case "anthropic-api-key":
1721
+ case "anthropic-gateway":
1722
+ case "hypha-proxy": {
1723
+ const base = resolved.claudeEnv?.ANTHROPIC_BASE_URL || "https://api.anthropic.com";
1724
+ const key = resolved.claudeEnv?.ANTHROPIC_API_KEY;
1725
+ if (!key) return { ok: false, detail: "No credential resolved" };
1726
+ const res = await withTimeout(`${base}/v1/messages`, {
1727
+ method: "POST",
1728
+ headers: {
1729
+ "content-type": "application/json",
1730
+ "x-api-key": key,
1731
+ "authorization": `Bearer ${key}`,
1732
+ "anthropic-version": "2023-06-01"
1733
+ },
1734
+ body: JSON.stringify({ model: "claude-3-5-haiku-20241022", max_tokens: 1, messages: [{ role: "user", content: "hi" }] })
1735
+ });
1736
+ if (res.status === 401 || res.status === 403) return { ok: false, detail: `Auth rejected (HTTP ${res.status})` };
1737
+ if (res.ok || res.status === 400 || res.status === 429) return { ok: true, detail: `Reachable (HTTP ${res.status})` };
1738
+ return { ok: true, detail: `Reachable, unexpected HTTP ${res.status}` };
1739
+ }
1740
+ case "openai-api-key":
1741
+ case "openai-gateway": {
1742
+ const base = account.method === "openai-gateway" ? stripV1(account.baseUrl || "") : "https://api.openai.com/v1";
1743
+ const key = account.secret || "";
1744
+ if (!key) return { ok: false, detail: "No credential resolved" };
1745
+ const res = await withTimeout(`${base}/models`, { headers: { authorization: `Bearer ${key}` } });
1746
+ if (res.status === 401 || res.status === 403) return { ok: false, detail: `Auth rejected (HTTP ${res.status})` };
1747
+ if (res.ok || res.status === 400 || res.status === 429) return { ok: true, detail: `Reachable (HTTP ${res.status})` };
1748
+ return { ok: true, detail: `Reachable, unexpected HTTP ${res.status}` };
1749
+ }
1750
+ case "claude-oauth-token":
1751
+ return { ok: !!account.secret, detail: account.secret ? "Token stored \u2014 verified when a session starts (Claude CLI uses CLAUDE_CODE_OAUTH_TOKEN)" : "No token stored" };
1752
+ case "chatgpt-oauth":
1753
+ return { ok: !!account.secret, detail: account.secret ? "Token stored \u2014 verified when a Codex session starts (per-account CODEX_HOME)" : "No token stored" };
1754
+ default:
1755
+ return { ok: false, detail: "Unknown method" };
1756
+ }
1757
+ } catch (e) {
1758
+ return { ok: false, detail: `Unreachable: ${e?.message || e}` };
1759
+ }
1760
+ }
1761
+
1221
1762
  const FIELD_RANGES = [[0, 59], [0, 23], [1, 31], [1, 12], [0, 6]];
1222
1763
  function parseField(token, [min, max]) {
1223
1764
  const set = /* @__PURE__ */ new Set();
@@ -2600,6 +3141,43 @@ async function registerMachineService(server, machineId, metadata, daemonState,
2600
3141
  });
2601
3142
  return { success: true };
2602
3143
  },
3144
+ // ── Backend Accounts (per-machine credentials; admin-gated) ──
3145
+ // Secrets live on the machine (~/.svamp/accounts.json, 0600) and are NEVER returned —
3146
+ // only redacted previews. The composer sends an opaque accountId; the daemon resolves it
3147
+ // to concrete auth at spawn (daemon/backendAccounts.ts). See docs/sharing-and-security.md.
3148
+ listBackendAccounts: async (context) => {
3149
+ authorizeRequest(context, currentMetadata.sharing, "admin");
3150
+ const accounts = loadAccounts(metadata.svampHomeDir);
3151
+ return {
3152
+ accounts: redactAccounts(accounts),
3153
+ defaults: defaultAccountIds(accounts)
3154
+ };
3155
+ },
3156
+ setBackendAccount: async (account, context) => {
3157
+ authorizeRequest(context, currentMetadata.sharing, "admin");
3158
+ const createdBy = context?.user?.email;
3159
+ const saved = upsertAccount({ ...account, createdBy: account.createdBy || createdBy }, metadata.svampHomeDir);
3160
+ notifyListeners({ type: "update-machine", machineId, backendAccountsChanged: true });
3161
+ return { success: true, account: redactAccount(saved) };
3162
+ },
3163
+ deleteBackendAccount: async (id, context) => {
3164
+ authorizeRequest(context, currentMetadata.sharing, "admin");
3165
+ deleteAccount(id, metadata.svampHomeDir);
3166
+ notifyListeners({ type: "update-machine", machineId, backendAccountsChanged: true });
3167
+ return { success: true };
3168
+ },
3169
+ setDefaultBackendAccount: async (vendor, id, context) => {
3170
+ authorizeRequest(context, currentMetadata.sharing, "admin");
3171
+ setDefaultAccount(vendor, id, metadata.svampHomeDir);
3172
+ notifyListeners({ type: "update-machine", machineId, backendAccountsChanged: true });
3173
+ return { success: true };
3174
+ },
3175
+ testBackendAccount: async (id, context) => {
3176
+ authorizeRequest(context, currentMetadata.sharing, "admin");
3177
+ const acct = getAccount(id, metadata.svampHomeDir);
3178
+ if (!acct) return { ok: false, detail: "Account not found" };
3179
+ return await probeAccount(acct);
3180
+ },
2603
3181
  // Register a listener for real-time updates (app calls this with _rintf callback)
2604
3182
  registerListener: async (callback, context) => {
2605
3183
  trackInbound();
@@ -3041,7 +3619,7 @@ async function registerMachineService(server, machineId, metadata, daemonState,
3041
3619
  const tunnels = handlers.tunnels;
3042
3620
  if (!tunnels) throw new Error("Tunnel management not available");
3043
3621
  if (tunnels.has(params.name)) throw new Error(`Tunnel '${params.name}' already running`);
3044
- const { FrpcTunnel } = await import('./frpc-DmaWRu0M.mjs');
3622
+ const { FrpcTunnel } = await import('./frpc-Cwn_s0Fs.mjs');
3045
3623
  const tunnel = new FrpcTunnel({
3046
3624
  name: params.name,
3047
3625
  ports: params.ports,
@@ -3508,7 +4086,7 @@ QUESTION: ${params.question || "Summarize this concisely."}` }
3508
4086
  }
3509
4087
  const deps = buildSessionDeps(rpc, { cwd, ownerEmail: owner });
3510
4088
  const sender = { name: context?.user?.email || context?.user?.id || "user", kind: "user", verified: true };
3511
- const { toolsForRole } = await import('./sideband-CIJUNKUA.mjs');
4089
+ const { toolsForRole } = await import('./sideband-DywLL2xs.mjs');
3512
4090
  const r2 = await runWiseAgent({ message: params.message, sender, config: { tools: toolsForRole(role2) }, deps, transport, model: resolved.model });
3513
4091
  return fmt(r2);
3514
4092
  }
@@ -3607,7 +4185,7 @@ QUESTION: ${params.question || "Summarize this concisely."}` }
3607
4185
  if (r.error || !r.sender) return { error: r.error || "unauthorized" };
3608
4186
  const callId = "call_" + Math.random().toString(16).slice(2, 12);
3609
4187
  const rendered = renderMessage(c, { sender: r.sender, body: { message: kwargs.message }, callId });
3610
- const { queryCore } = await import('./commands-DuPRCS2G.mjs');
4188
+ const { queryCore } = await import('./commands-C_74tXoO.mjs');
3611
4189
  const timeout = c.reply?.timeout_sec || 120;
3612
4190
  let result;
3613
4191
  try {
@@ -3967,278 +4545,83 @@ function classifyInbound(message, now = Date.now(), opts) {
3967
4545
  if (isBreakerOpenAt(now)) {
3968
4546
  return { action: "queue", reason: "circuit-breaker-open" };
3969
4547
  }
3970
- if (!takeUrgentToken(senderKey(message), now)) {
3971
- return { action: "queue", reason: "rate-limited" };
3972
- }
3973
- const tripped = recordWake(now);
3974
- return { action: "wake", reason: "urgent", breakerTripped: tripped };
3975
- }
3976
- function applyInboxClear(inbox, opts) {
3977
- let kept;
3978
- if (opts?.messageId) kept = inbox.filter((m) => m.messageId !== opts.messageId);
3979
- else if (opts?.all) kept = [];
3980
- else kept = inbox.filter((m) => !m.read);
3981
- return { kept, removed: inbox.length - kept.length };
3982
- }
3983
-
3984
- function inboxFilePath(projectDir, sessionId) {
3985
- return join(projectDir, ".svamp", sessionId, "inbox.json");
3986
- }
3987
- function loadInbox(projectDir, sessionId) {
3988
- try {
3989
- const p = inboxFilePath(projectDir, sessionId);
3990
- if (!existsSync(p)) return [];
3991
- const data = JSON.parse(readFileSync(p, "utf8"));
3992
- if (Array.isArray(data)) return data;
3993
- if (data && Array.isArray(data.messages)) return data.messages;
3994
- return [];
3995
- } catch {
3996
- return [];
3997
- }
3998
- }
3999
- function saveInbox(projectDir, sessionId, inbox) {
4000
- try {
4001
- const p = inboxFilePath(projectDir, sessionId);
4002
- mkdirSync(dirname(p), { recursive: true });
4003
- const tmp = `${p}.tmp-${process.pid}`;
4004
- writeFileSync(tmp, JSON.stringify(inbox));
4005
- renameSync(tmp, p);
4006
- } catch {
4007
- }
4008
- }
4009
-
4010
- function envInt(name, fallback) {
4011
- const raw = process.env[name];
4012
- if (raw === void 0 || raw === "") return fallback;
4013
- const n = parseInt(raw, 10);
4014
- return Number.isFinite(n) && n >= 0 ? n : fallback;
4015
- }
4016
- function getRateLimitRetryConfig() {
4017
- return {
4018
- maxRetries: envInt("SVAMP_RATELIMIT_MAX_RETRIES", 200),
4019
- baseMs: envInt("SVAMP_RATELIMIT_BASE_MS", 5e3),
4020
- capMs: envInt("SVAMP_RATELIMIT_CAP_MS", 12e4)
4021
- };
4022
- }
4023
- function computeRetryDelayMs(attempt, cfg = getRateLimitRetryConfig(), rng = Math.random) {
4024
- const a = Math.max(0, Math.floor(attempt));
4025
- const expo = Math.min(a, 30);
4026
- const nominal = Math.min(cfg.capMs, cfg.baseMs * Math.pow(2, expo));
4027
- const half = nominal / 2;
4028
- return Math.round(half + rng() * half);
4029
- }
4030
- function isRetryableRateLimit(text, apiErrorStatus) {
4031
- const t = (text || "").toLowerCase();
4032
- const hardLimit = t.includes("quota") || // The Hypha LLM proxy emits a 429 "Daily spending limit reached … Resets
4033
- // at 00:00 UTC. Contact the admin to raise your limit." That is a hard cap
4034
- // that resets at a fixed time / needs human action — NOT a transient
4035
- // upstream throttle. Retrying it 10× just delays surfacing the real error.
4036
- t.includes("spending limit") || t.includes("credit") || t.includes("balance") || t.includes("billing") || t.includes("payment") || t.includes("subscription") || t.includes("1m-context") || t.includes("1m context") || t.includes("insufficient") || t.includes("usage limit") && !t.includes("not your usage limit");
4037
- if (hardLimit) return false;
4038
- const authIssue = apiErrorStatus === 401 || apiErrorStatus === 403 || t.includes("unauthorized") || t.includes("invalid api key") || t.includes("authentication") || t.includes("forbidden");
4039
- if (authIssue) return false;
4040
- const clientError = apiErrorStatus === 400 || apiErrorStatus === 404 || apiErrorStatus === 422 || t.includes("invalid_request") || t.includes("invalid request") || t.includes("bad request");
4041
- if (clientError) return false;
4042
- const serverTransient = typeof apiErrorStatus === "number" && apiErrorStatus >= 500 && apiErrorStatus <= 599 || apiErrorStatus === 429 || /\b(429|500|502|503|504|520|521|522|523|524|529)\b/.test(t) || t.includes("temporarily limiting") || t.includes("not your usage limit") || t.includes("overloaded") || t.includes("overload") || t.includes("too many requests") || t.includes("server is busy") || t.includes("internal server error") || t.includes("bad gateway") || t.includes("service unavailable") || t.includes("gateway timeout") || t.includes("rate limit") || t.includes("rate-limit") || t.includes("rate limited");
4043
- if (serverTransient) return true;
4044
- const networkTransient = t.includes("socket hang up") || t.includes("closed unexpectedly") || t.includes("connection closed") || t.includes("connection reset") || t.includes("connection error") || t.includes("econnreset") || t.includes("etimedout") || t.includes("econnaborted") || t.includes("epipe") || t.includes("enetunreach") || t.includes("enetreset") || t.includes("ehostunreach") || t.includes("eai_again") || t.includes("network error") || t.includes("fetch failed") || t.includes("request timed out") || t.includes("timed out");
4045
- return networkTransient;
4046
- }
4047
-
4048
- const EXAMPLE_HYPHA_PROXY_URL = "https://proxy.hypha.aicell.io";
4049
- const MODE_KEY = "SVAMP_CLAUDE_PROXY";
4050
- const HYPHA_PROXY_URL_KEY = "SVAMP_HYPHA_PROXY_URL";
4051
- const MANAGED_KEYS = /* @__PURE__ */ new Set([
4052
- MODE_KEY,
4053
- HYPHA_PROXY_URL_KEY,
4054
- "ANTHROPIC_BASE_URL",
4055
- "ANTHROPIC_API_KEY"
4056
- ]);
4057
- function normalizeProxyUrl(raw) {
4058
- const trimmed = raw.trim().replace(/\/+$/, "");
4059
- if (trimmed.endsWith("/v1")) {
4060
- throw new Error(
4061
- "Hypha proxy URL must not end in /v1 \u2014 the SDK appends it, producing double /v1/v1/messages"
4062
- );
4063
- }
4064
- return trimmed;
4065
- }
4066
- function resolveHyphaProxyUrl() {
4067
- const configured = (process.env[HYPHA_PROXY_URL_KEY] || "").trim();
4068
- if (!configured) return void 0;
4069
- try {
4070
- return normalizeProxyUrl(configured);
4071
- } catch {
4072
- return void 0;
4073
- }
4074
- }
4075
- function envFilePath() {
4076
- const svampHome = process.env.SVAMP_HOME || join(homedir(), ".svamp");
4077
- return join(svampHome, ".env");
4078
- }
4079
- function readEnvLines() {
4080
- const file = envFilePath();
4081
- if (!existsSync(file)) return [];
4082
- return readFileSync(file, "utf-8").split("\n");
4083
- }
4084
- function writeEnvLines(lines) {
4085
- const file = envFilePath();
4086
- const dir = join(file, "..");
4087
- if (!existsSync(dir)) mkdirSync(dir, { recursive: true });
4088
- while (lines.length > 0 && lines[lines.length - 1].trim() === "") {
4089
- lines.pop();
4090
- }
4091
- writeFileSync(file, lines.join("\n") + "\n", "utf-8");
4092
- }
4093
- function updateEnvFile(updates) {
4094
- const lines = readEnvLines();
4095
- const kept = [];
4096
- const remainingUpdates = new Map(Object.entries(updates));
4097
- for (const line of lines) {
4098
- const trimmed = line.trim();
4099
- if (!trimmed || trimmed.startsWith("#")) {
4100
- kept.push(line);
4101
- continue;
4102
- }
4103
- const eq = trimmed.indexOf("=");
4104
- const key = eq === -1 ? trimmed : trimmed.slice(0, eq);
4105
- if (remainingUpdates.has(key)) {
4106
- const v = remainingUpdates.get(key);
4107
- remainingUpdates.delete(key);
4108
- if (v === void 0) continue;
4109
- kept.push(`${key}=${v}`);
4110
- continue;
4111
- }
4112
- kept.push(line);
4113
- }
4114
- for (const [key, v] of remainingUpdates) {
4115
- if (v === void 0) continue;
4116
- kept.push(`${key}=${v}`);
4117
- }
4118
- writeEnvLines(kept);
4548
+ if (!takeUrgentToken(senderKey(message), now)) {
4549
+ return { action: "queue", reason: "rate-limited" };
4550
+ }
4551
+ const tripped = recordWake(now);
4552
+ return { action: "wake", reason: "urgent", breakerTripped: tripped };
4119
4553
  }
4120
- function currentMode() {
4121
- const raw = (process.env[MODE_KEY] || "").trim().toLowerCase();
4122
- if (raw === "hypha" || raw === "hypha-proxy") return "hypha";
4123
- if (raw === "custom") return "custom";
4124
- return "login";
4554
+ function applyInboxClear(inbox, opts) {
4555
+ let kept;
4556
+ if (opts?.messageId) kept = inbox.filter((m) => m.messageId !== opts.messageId);
4557
+ else if (opts?.all) kept = [];
4558
+ else kept = inbox.filter((m) => !m.read);
4559
+ return { kept, removed: inbox.length - kept.length };
4125
4560
  }
4126
- function redactKey(key) {
4127
- if (!key) return void 0;
4128
- if (key.length <= 12) return "***";
4129
- return `${key.slice(0, 8)}\u2026${key.slice(-4)}`;
4561
+
4562
+ function inboxFilePath(projectDir, sessionId) {
4563
+ return join(projectDir, ".svamp", sessionId, "inbox.json");
4130
4564
  }
4131
- function getClaudeAuthStatus() {
4132
- const mode = currentMode();
4133
- if (mode === "hypha") {
4134
- const token = process.env.HYPHA_TOKEN;
4135
- const proxyUrl = resolveHyphaProxyUrl();
4136
- return {
4137
- mode,
4138
- baseUrl: proxyUrl,
4139
- apiKeyPreview: redactKey(token),
4140
- hyphaTokenMissing: !token,
4141
- hyphaProxyUrlMissing: !proxyUrl
4142
- };
4143
- }
4144
- if (mode === "custom") {
4145
- return {
4146
- mode,
4147
- baseUrl: process.env.ANTHROPIC_BASE_URL,
4148
- apiKeyPreview: redactKey(process.env.ANTHROPIC_API_KEY)
4149
- };
4565
+ function loadInbox(projectDir, sessionId) {
4566
+ try {
4567
+ const p = inboxFilePath(projectDir, sessionId);
4568
+ if (!existsSync(p)) return [];
4569
+ const data = JSON.parse(readFileSync(p, "utf8"));
4570
+ if (Array.isArray(data)) return data;
4571
+ if (data && Array.isArray(data.messages)) return data.messages;
4572
+ return [];
4573
+ } catch {
4574
+ return [];
4150
4575
  }
4151
- return { mode: "login" };
4152
4576
  }
4153
- function setClaudeAuthHyphaProxy(url) {
4154
- if (!url || !url.trim()) {
4155
- throw new Error(
4156
- `A Hypha proxy URL is required (no default). Example: svamp daemon auth use-hypha-proxy ${EXAMPLE_HYPHA_PROXY_URL}`
4157
- );
4577
+ function saveInbox(projectDir, sessionId, inbox) {
4578
+ try {
4579
+ const p = inboxFilePath(projectDir, sessionId);
4580
+ mkdirSync(dirname(p), { recursive: true });
4581
+ const tmp = `${p}.tmp-${process.pid}`;
4582
+ writeFileSync(tmp, JSON.stringify(inbox));
4583
+ renameSync(tmp, p);
4584
+ } catch {
4158
4585
  }
4159
- updateEnvFile({
4160
- [MODE_KEY]: "hypha",
4161
- [HYPHA_PROXY_URL_KEY]: normalizeProxyUrl(url),
4162
- // Hypha mode resolves token live at spawn — no stored copy.
4163
- ANTHROPIC_BASE_URL: void 0,
4164
- ANTHROPIC_API_KEY: void 0
4165
- });
4166
4586
  }
4167
- function setClaudeAuthLogin() {
4168
- updateEnvFile({
4169
- [MODE_KEY]: void 0,
4170
- [HYPHA_PROXY_URL_KEY]: void 0,
4171
- ANTHROPIC_BASE_URL: void 0,
4172
- ANTHROPIC_API_KEY: void 0
4173
- });
4587
+
4588
+ function envInt(name, fallback) {
4589
+ const raw = process.env[name];
4590
+ if (raw === void 0 || raw === "") return fallback;
4591
+ const n = parseInt(raw, 10);
4592
+ return Number.isFinite(n) && n >= 0 ? n : fallback;
4174
4593
  }
4175
- function setClaudeAuthCustom(baseUrl, apiKey) {
4176
- if (!baseUrl) throw new Error("ANTHROPIC_BASE_URL is required for custom mode");
4177
- if (!apiKey) throw new Error("ANTHROPIC_API_KEY is required for custom mode");
4178
- const trimmed = baseUrl.replace(/\/+$/, "");
4179
- if (trimmed.endsWith("/v1")) {
4180
- throw new Error(
4181
- "ANTHROPIC_BASE_URL must not end in /v1 \u2014 the SDK appends it, producing double /v1/v1/messages"
4182
- );
4183
- }
4184
- updateEnvFile({
4185
- [MODE_KEY]: "custom",
4186
- [HYPHA_PROXY_URL_KEY]: void 0,
4187
- ANTHROPIC_BASE_URL: trimmed,
4188
- ANTHROPIC_API_KEY: apiKey
4189
- });
4594
+ function getRateLimitRetryConfig() {
4595
+ return {
4596
+ maxRetries: envInt("SVAMP_RATELIMIT_MAX_RETRIES", 200),
4597
+ baseMs: envInt("SVAMP_RATELIMIT_BASE_MS", 5e3),
4598
+ capMs: envInt("SVAMP_RATELIMIT_CAP_MS", 12e4)
4599
+ };
4190
4600
  }
4191
- function applyClaudeProxyEnv(spawnEnv) {
4192
- const mode = currentMode();
4193
- if (mode === "hypha") {
4194
- const proxyUrl = resolveHyphaProxyUrl();
4195
- if (!proxyUrl) {
4196
- delete spawnEnv.ANTHROPIC_BASE_URL;
4197
- delete spawnEnv.ANTHROPIC_API_KEY;
4198
- return `hypha mode but ${HYPHA_PROXY_URL_KEY} not configured \u2014 run \`svamp daemon auth use-hypha-proxy <URL>\`; falling back to login`;
4199
- }
4200
- const token = process.env.HYPHA_TOKEN;
4201
- if (!token) {
4202
- delete spawnEnv.ANTHROPIC_BASE_URL;
4203
- delete spawnEnv.ANTHROPIC_API_KEY;
4204
- return "hypha mode but HYPHA_TOKEN missing \u2014 falling back to login";
4205
- }
4206
- spawnEnv.ANTHROPIC_BASE_URL = proxyUrl;
4207
- spawnEnv.ANTHROPIC_API_KEY = token;
4208
- const cacheOverride = spawnEnv.ENABLE_PROMPT_CACHING_1H ?? process.env.ENABLE_PROMPT_CACHING_1H ?? spawnEnv.FORCE_PROMPT_CACHING_5M ?? process.env.FORCE_PROMPT_CACHING_5M;
4209
- if (cacheOverride === void 0) spawnEnv.ENABLE_PROMPT_CACHING_1H = "1";
4210
- return `hypha proxy (${proxyUrl}, 1h cache TTL)`;
4211
- }
4212
- if (mode === "custom") {
4213
- const url = process.env.ANTHROPIC_BASE_URL;
4214
- const key = process.env.ANTHROPIC_API_KEY;
4215
- if (!url || !key) {
4216
- delete spawnEnv.ANTHROPIC_BASE_URL;
4217
- delete spawnEnv.ANTHROPIC_API_KEY;
4218
- return "custom mode but ANTHROPIC_BASE_URL/API_KEY missing \u2014 falling back to login";
4219
- }
4220
- spawnEnv.ANTHROPIC_BASE_URL = url;
4221
- spawnEnv.ANTHROPIC_API_KEY = key;
4222
- return `custom proxy (${url})`;
4223
- }
4224
- delete spawnEnv.ANTHROPIC_BASE_URL;
4225
- delete spawnEnv.ANTHROPIC_API_KEY;
4226
- return null;
4601
+ function computeRetryDelayMs(attempt, cfg = getRateLimitRetryConfig(), rng = Math.random) {
4602
+ const a = Math.max(0, Math.floor(attempt));
4603
+ const expo = Math.min(a, 30);
4604
+ const nominal = Math.min(cfg.capMs, cfg.baseMs * Math.pow(2, expo));
4605
+ const half = nominal / 2;
4606
+ return Math.round(half + rng() * half);
4607
+ }
4608
+ function isRetryableRateLimit(text, apiErrorStatus) {
4609
+ const t = (text || "").toLowerCase();
4610
+ const hardLimit = t.includes("quota") || // The Hypha LLM proxy emits a 429 "Daily spending limit reached … Resets
4611
+ // at 00:00 UTC. Contact the admin to raise your limit." That is a hard cap
4612
+ // that resets at a fixed time / needs human action — NOT a transient
4613
+ // upstream throttle. Retrying it 10× just delays surfacing the real error.
4614
+ t.includes("spending limit") || t.includes("credit") || t.includes("balance") || t.includes("billing") || t.includes("payment") || t.includes("subscription") || t.includes("1m-context") || t.includes("1m context") || t.includes("insufficient") || t.includes("usage limit") && !t.includes("not your usage limit");
4615
+ if (hardLimit) return false;
4616
+ const authIssue = apiErrorStatus === 401 || apiErrorStatus === 403 || t.includes("unauthorized") || t.includes("invalid api key") || t.includes("authentication") || t.includes("forbidden");
4617
+ if (authIssue) return false;
4618
+ const clientError = apiErrorStatus === 400 || apiErrorStatus === 404 || apiErrorStatus === 422 || t.includes("invalid_request") || t.includes("invalid request") || t.includes("bad request");
4619
+ if (clientError) return false;
4620
+ const serverTransient = typeof apiErrorStatus === "number" && apiErrorStatus >= 500 && apiErrorStatus <= 599 || apiErrorStatus === 429 || /\b(429|500|502|503|504|520|521|522|523|524|529)\b/.test(t) || t.includes("temporarily limiting") || t.includes("not your usage limit") || t.includes("overloaded") || t.includes("overload") || t.includes("too many requests") || t.includes("server is busy") || t.includes("internal server error") || t.includes("bad gateway") || t.includes("service unavailable") || t.includes("gateway timeout") || t.includes("rate limit") || t.includes("rate-limit") || t.includes("rate limited");
4621
+ if (serverTransient) return true;
4622
+ const networkTransient = t.includes("socket hang up") || t.includes("closed unexpectedly") || t.includes("connection closed") || t.includes("connection reset") || t.includes("connection error") || t.includes("econnreset") || t.includes("etimedout") || t.includes("econnaborted") || t.includes("epipe") || t.includes("enetunreach") || t.includes("enetreset") || t.includes("ehostunreach") || t.includes("eai_again") || t.includes("network error") || t.includes("fetch failed") || t.includes("request timed out") || t.includes("timed out");
4623
+ return networkTransient;
4227
4624
  }
4228
- const MANAGED_ENV_KEYS = Array.from(MANAGED_KEYS);
4229
-
4230
- var claudeAuth = /*#__PURE__*/Object.freeze({
4231
- __proto__: null,
4232
- EXAMPLE_HYPHA_PROXY_URL: EXAMPLE_HYPHA_PROXY_URL,
4233
- MANAGED_ENV_KEYS: MANAGED_ENV_KEYS,
4234
- applyClaudeProxyEnv: applyClaudeProxyEnv,
4235
- getClaudeAuthStatus: getClaudeAuthStatus,
4236
- resolveHyphaProxyUrl: resolveHyphaProxyUrl,
4237
- setClaudeAuthCustom: setClaudeAuthCustom,
4238
- setClaudeAuthHyphaProxy: setClaudeAuthHyphaProxy,
4239
- setClaudeAuthLogin: setClaudeAuthLogin,
4240
- updateEnvFile: updateEnvFile
4241
- });
4242
4625
 
4243
4626
  const PARTICIPANTS_CHANNEL_ID = "sys-participants";
4244
4627
  function channelPublicView(c) {
@@ -8720,64 +9103,6 @@ function mapDecisionToWire(d) {
8720
9103
  }
8721
9104
  }
8722
9105
 
8723
- function codexPermissionSettings(mode) {
8724
- switch (mode) {
8725
- case "yolo":
8726
- case "bypassPermissions":
8727
- case "danger-full-access":
8728
- return { approvalPolicy: "never", sandbox: "danger-full-access" };
8729
- case "safe-yolo":
8730
- case "acceptEdits":
8731
- return { approvalPolicy: "on-failure", sandbox: "workspace-write" };
8732
- case "read-only":
8733
- case "plan":
8734
- return { approvalPolicy: "on-request", sandbox: "read-only" };
8735
- case "default":
8736
- default:
8737
- return { approvalPolicy: "on-request", sandbox: "workspace-write" };
8738
- }
8739
- }
8740
- const CODEX_PROVIDER_KEY_ENV = "SVAMP_CODEX_API_KEY";
8741
- const PROVIDER_NAME = "svamp";
8742
- function resolveCodexProvider(env = process.env) {
8743
- return {
8744
- apiBase: env.SVAMP_CODEX_API_BASE || env.LLM_API_BASE || void 0,
8745
- apiKey: env.SVAMP_CODEX_API_KEY || env.LLM_API_KEY || void 0,
8746
- model: env.SVAMP_CODEX_MODEL || env.LLM_MODEL || void 0
8747
- };
8748
- }
8749
- function hasCustomProvider(cfg) {
8750
- return !!(cfg.apiBase && cfg.apiKey);
8751
- }
8752
- function buildCodexProviderArgs(cfg) {
8753
- if (!hasCustomProvider(cfg)) {
8754
- return { configArgs: cfg.model ? ["-c", `model=${JSON.stringify(cfg.model)}`] : [], env: {}, model: cfg.model };
8755
- }
8756
- const configArgs = [
8757
- "-c",
8758
- `model_provider=${JSON.stringify(PROVIDER_NAME)}`,
8759
- "-c",
8760
- `model_providers.${PROVIDER_NAME}.name=${JSON.stringify(PROVIDER_NAME)}`,
8761
- "-c",
8762
- `model_providers.${PROVIDER_NAME}.base_url=${JSON.stringify(cfg.apiBase)}`,
8763
- "-c",
8764
- `model_providers.${PROVIDER_NAME}.env_key=${JSON.stringify(CODEX_PROVIDER_KEY_ENV)}`,
8765
- "-c",
8766
- `model_providers.${PROVIDER_NAME}.wire_api=${JSON.stringify("responses")}`
8767
- ];
8768
- if (cfg.model) configArgs.push("-c", `model=${JSON.stringify(cfg.model)}`);
8769
- return { configArgs, env: { [CODEX_PROVIDER_KEY_ENV]: cfg.apiKey }, model: cfg.model };
8770
- }
8771
-
8772
- var codexProvider = /*#__PURE__*/Object.freeze({
8773
- __proto__: null,
8774
- CODEX_PROVIDER_KEY_ENV: CODEX_PROVIDER_KEY_ENV,
8775
- buildCodexProviderArgs: buildCodexProviderArgs,
8776
- codexPermissionSettings: codexPermissionSettings,
8777
- hasCustomProvider: hasCustomProvider,
8778
- resolveCodexProvider: resolveCodexProvider
8779
- });
8780
-
8781
9106
  class CodexAppServerBackend {
8782
9107
  constructor(opts) {
8783
9108
  this.opts = opts;
@@ -13940,7 +14265,7 @@ async function startDaemon(options) {
13940
14265
  try {
13941
14266
  const dir = loadSessionIndex()[sessionId]?.directory;
13942
14267
  if (!dir) return;
13943
- const { reconcileServiceLinks } = await import('./agentCommands-BZPDL-1B.mjs');
14268
+ const { reconcileServiceLinks } = await import('./agentCommands-CKd1jXfX.mjs');
13944
14269
  const configPath = getSvampConfigPath(dir, sessionId);
13945
14270
  const config = readSvampConfig(configPath);
13946
14271
  const entries = Array.from(urls.entries());
@@ -13958,7 +14283,7 @@ async function startDaemon(options) {
13958
14283
  }
13959
14284
  }
13960
14285
  async function createExposedTunnel(spec) {
13961
- const { FrpcTunnel } = await import('./frpc-DmaWRu0M.mjs');
14286
+ const { FrpcTunnel } = await import('./frpc-Cwn_s0Fs.mjs');
13962
14287
  const tunnel = new FrpcTunnel({
13963
14288
  name: spec.name,
13964
14289
  ports: spec.ports,
@@ -13979,7 +14304,7 @@ async function startDaemon(options) {
13979
14304
  }
13980
14305
  const tunnelRecreateState = /* @__PURE__ */ new Map();
13981
14306
  const tunnelRecreateInFlight = /* @__PURE__ */ new Set();
13982
- const { ServeManager } = await import('./serveManager-a_dIQuK4.mjs');
14307
+ const { ServeManager } = await import('./serveManager-Dy1ZZNaY.mjs');
13983
14308
  const serveManager = new ServeManager(SVAMP_HOME, (msg) => logger.log(`[SERVE] ${msg}`), hyphaServerUrl);
13984
14309
  ensureAutoInstalledSkills(logger).catch(() => {
13985
14310
  });
@@ -14416,7 +14741,10 @@ ${v.guidance ? v.guidance + "\n" : ""}Criteria: ${v.criteria || "(none)"}
14416
14741
  ...sessionMetadata,
14417
14742
  // Derive the handle name from the id (new-format ids embed it) so the two
14418
14743
  // never diverge; fall back to persisted name (legacy ids) then a fresh one.
14419
- friendlyName: persisted?.metadata?.friendlyName || friendlyNameFromId(sessionId) || generateFriendlyName(collectKnownFriendlyNames())
14744
+ friendlyName: persisted?.metadata?.friendlyName || friendlyNameFromId(sessionId) || generateFriendlyName(collectKnownFriendlyNames()),
14745
+ // Selected backend account (composer). Sticky across respawns + restart via the
14746
+ // persisted metadata; the daemon resolves it to concrete auth at each Claude spawn.
14747
+ accountId: options2.accountId ?? persisted?.metadata?.accountId ?? void 0
14420
14748
  };
14421
14749
  let currentPermissionMode = options2.permissionMode || persisted?.permissionMode || "bypassPermissions";
14422
14750
  const sessionCreatedAt = persisted?.createdAt || Date.now();
@@ -14916,6 +15244,28 @@ ${parts.join("\n")}`);
14916
15244
  if (proxyDesc) {
14917
15245
  logger.log(`[Session ${sessionId}] Claude auth: ${proxyDesc}`);
14918
15246
  }
15247
+ {
15248
+ const acctId = sessionMetadata.accountId;
15249
+ if (acctId) {
15250
+ try {
15251
+ const acct = getAccount(acctId, SVAMP_HOME);
15252
+ if (!acct) {
15253
+ logger.log(`[Session ${sessionId}] account ${acctId} not found \u2014 using machine default`);
15254
+ } else if (acct.vendor !== "anthropic") {
15255
+ logger.log(`[Session ${sessionId}] account ${acctId} is a ${acct.vendor} account, not usable for Claude \u2014 using machine default`);
15256
+ } else {
15257
+ const resolved = resolveAccountEnv(acct, process.env, SVAMP_HOME);
15258
+ for (const [k, v] of Object.entries(resolved.claudeEnv || {})) {
15259
+ if (v === void 0) delete spawnEnv[k];
15260
+ else spawnEnv[k] = v;
15261
+ }
15262
+ logger.log(`[Session ${sessionId}] Claude auth (account): ${resolved.describe}`);
15263
+ }
15264
+ } catch (e) {
15265
+ logger.log(`[Session ${sessionId}] account ${acctId} resolve failed (${e?.message || e}) \u2014 using machine default`);
15266
+ }
15267
+ }
15268
+ }
14919
15269
  if (isoConfig && stagedCredentials) {
14920
15270
  Object.assign(spawnEnv, stagedCredentials.env);
14921
15271
  const filtered = {};
@@ -16101,11 +16451,11 @@ ${capturedError}${buildClaudeErrorHint(capturedError)}`;
16101
16451
  });
16102
16452
  },
16103
16453
  onIssue: async (params) => {
16104
- const { issueRpc } = await import('./rpc-DswYjrCh.mjs');
16454
+ const { issueRpc } = await import('./rpc-BnHMvDxQ.mjs');
16105
16455
  return issueRpc(params?.cwd || directory, params || {}, { notifySession: notifyIssueOwner, rekickLoopOwner });
16106
16456
  },
16107
16457
  onWorkflow: async (params) => {
16108
- const { workflowRpc } = await import('./rpc-Db3BR76v.mjs');
16458
+ const { workflowRpc } = await import('./rpc-DwlZTOtK.mjs');
16109
16459
  return workflowRpc(params?.cwd || directory, params || {});
16110
16460
  },
16111
16461
  onRipgrep: async (args, cwd) => {
@@ -16396,7 +16746,9 @@ ${capturedError}${buildClaudeErrorHint(capturedError)}`;
16396
16746
  sharing: options2.sharing,
16397
16747
  securityContext: options2.securityContext,
16398
16748
  tags: options2.tags,
16399
- parentSessionId: options2.parentSessionId
16749
+ parentSessionId: options2.parentSessionId,
16750
+ // Selected backend account — sticky across restart via persisted metadata.
16751
+ accountId: options2.accountId ?? acpPersisted?.metadata?.accountId ?? void 0
16400
16752
  };
16401
16753
  let currentPermissionMode = options2.permissionMode || "bypassPermissions";
16402
16754
  const allowedTools = /* @__PURE__ */ new Set();
@@ -16663,11 +17015,11 @@ ${capturedError}${buildClaudeErrorHint(capturedError)}`;
16663
17015
  });
16664
17016
  },
16665
17017
  onIssue: async (params) => {
16666
- const { issueRpc } = await import('./rpc-DswYjrCh.mjs');
17018
+ const { issueRpc } = await import('./rpc-BnHMvDxQ.mjs');
16667
17019
  return issueRpc(params?.cwd || directory, params || {}, { notifySession: notifyIssueOwner, rekickLoopOwner });
16668
17020
  },
16669
17021
  onWorkflow: async (params) => {
16670
- const { workflowRpc } = await import('./rpc-Db3BR76v.mjs');
17022
+ const { workflowRpc } = await import('./rpc-DwlZTOtK.mjs');
16671
17023
  return workflowRpc(params?.cwd || directory, params || {});
16672
17024
  },
16673
17025
  onRipgrep: async (args, cwd) => {
@@ -16818,16 +17170,41 @@ ${capturedError}${buildClaudeErrorHint(capturedError)}`;
16818
17170
  const avail = codexAppServerAvailable();
16819
17171
  if (avail === null) throw new Error("Codex CLI not found \u2014 install it (`npm i -g @openai/codex`) and authenticate (`svamp daemon codex-auth`).");
16820
17172
  if (avail === false) throw new Error("Installed Codex is too old for app-server \u2014 upgrade to >= 0.100 (`npm i -g @openai/codex@latest`).");
16821
- const provider = resolveCodexProvider();
17173
+ let provider = resolveCodexProvider();
17174
+ let accountCodexEnv;
17175
+ let accountDesc;
17176
+ {
17177
+ const acctId = sessionMetadata.accountId;
17178
+ if (acctId) {
17179
+ try {
17180
+ const acct = getAccount(acctId, SVAMP_HOME);
17181
+ if (!acct) {
17182
+ logger.log(`[Agent Session ${sessionId}] account ${acctId} not found \u2014 using machine default`);
17183
+ } else if (acct.vendor !== "openai") {
17184
+ logger.log(`[Agent Session ${sessionId}] account ${acctId} is a ${acct.vendor} account, not usable for Codex \u2014 using machine default`);
17185
+ } else {
17186
+ const resolved = resolveAccountEnv(acct, process.env, SVAMP_HOME);
17187
+ if (resolved.codexHome) ensureCodexHome(acct, SVAMP_HOME);
17188
+ provider = resolved.codexProvider || {};
17189
+ accountCodexEnv = resolved.extraEnv;
17190
+ accountDesc = resolved.describe;
17191
+ }
17192
+ } catch (e) {
17193
+ logger.log(`[Agent Session ${sessionId}] account ${acctId} resolve failed (${e?.message || e}) \u2014 using machine default`);
17194
+ }
17195
+ }
17196
+ }
16822
17197
  const codexModel = options2.model || agentConfig?.default_model || provider.model || void 0;
16823
17198
  const codexPerm = codexPermissionSettings(currentPermissionMode);
16824
- logger.log(`[Agent Session ${sessionId}] Codex backend: app-server (model=${codexModel ?? "default"}${provider.apiBase ? `, provider=${provider.apiBase}` : ""}, mode=${currentPermissionMode} \u2192 approval=${codexPerm.approvalPolicy}/sandbox=${codexPerm.sandbox})`);
17199
+ logger.log(`[Agent Session ${sessionId}] Codex backend: app-server (model=${codexModel ?? "default"}${provider.apiBase ? `, provider=${provider.apiBase}` : ""}${accountDesc ? `, account=${accountDesc}` : ""}, mode=${currentPermissionMode} \u2192 approval=${codexPerm.approvalPolicy}/sandbox=${codexPerm.sandbox})`);
16825
17200
  if (acpResumeThreadId) logger.log(`[Agent Session ${sessionId}] Resuming Codex thread ${acpResumeThreadId} (restart recovery)`);
16826
17201
  agentBackend = new CodexAppServerBackend({
16827
17202
  cwd: directory,
16828
17203
  model: codexModel,
16829
17204
  provider,
16830
- env: options2.environmentVariables,
17205
+ // Account extra env (OPENAI_API_KEY / SVAMP_CODEX_API_KEY / CODEX_HOME) layered
17206
+ // over any per-session environmentVariables from the composer.
17207
+ env: { ...options2.environmentVariables || {}, ...accountCodexEnv || {} },
16831
17208
  log: logger.log,
16832
17209
  isolationConfig: agentIsoConfig,
16833
17210
  approvalPolicy: codexPerm.approvalPolicy,
@@ -17678,7 +18055,7 @@ ${oracle.output.trim().slice(0, 500)}`, "\u{1F501} Continuing loop");
17678
18055
  const PING_TIMEOUT_MS = 15e3;
17679
18056
  const POST_RECONNECT_GRACE_MS = 2e4;
17680
18057
  const RECONNECT_JITTER_MS = 2500;
17681
- const { WorkflowScheduler } = await import('./scheduler-DhKDP3hC.mjs');
18058
+ const { WorkflowScheduler } = await import('./scheduler-BxijQDI8.mjs');
17682
18059
  const workflowScheduler = new WorkflowScheduler({
17683
18060
  projectRoots: () => {
17684
18061
  const dirs = /* @__PURE__ */ new Set();
@@ -18289,4 +18666,4 @@ var run = /*#__PURE__*/Object.freeze({
18289
18666
  writeStopMarker: writeStopMarker
18290
18667
  });
18291
18668
 
18292
- export { resolveModel as $, runWorkflow as A, setWorkflowEnabled as B, removeWorkflow as C, saveWorkflow as D, rawWorkflow as E, listWorkflows as F, isWorkflowEnabled as G, workflowCrons as H, cronMatches as I, summarize as J, workflowSteps as K, loadMachineContext as L, buildMachineInstructions as M, machineToolsForRole as N, buildMachineTools as O, parseFrontmatter as P, getSkillsServer as Q, READ_ONLY_TOOLS as R, ServeAuth as S, getSkillsWorkspaceName as T, getSkillsCollectionName as U, fetchWithTimeout as V, searchSkills as W, SKILLS_DIR as X, getSkillInfo as Y, downloadSkillFile as Z, listSkillFiles as _, createSessionStore as a, clearStopMarker as a0, stopMarkerExists as a1, formatHandle as a2, normalizeAllowedUser as a3, loadSecurityContextConfig as a4, resolveSecurityContext as a5, buildSecurityContextFromFlags as a6, mergeSecurityContexts as a7, buildSessionShareUrl as a8, computeOutboundHop as a9, registerAwaitingReply as aa, buildMachineShareUrl as ab, parseHandle as ac, handleMatchesMetadata as ad, describeMisconfiguration as ae, buildMachineDeps as af, applyClaudeProxyEnv as ag, composeSessionId as ah, generateFriendlyName as ai, generateHookSettings as aj, claudeAuth as ak, projectInfo as al, DefaultTransport$1 as am, acpBackend as an, acpAgentConfig as ao, codexProvider as ap, codexAppServerBackend as aq, GeminiTransport$1 as ar, instanceConfig as as, api as at, run as au, stopDaemon as b, connectToHypha as c, daemonStatus as d, getFrpsSubdomainHost as e, getFrpsServerPort as f, getHyphaServerUrl$1 as g, getFrpsServerAddr as h, shortId as i, getHyphaServerUrl as j, hasCookieToken as k, resolveProjectRoot as l, getIssue as m, resumeIssue as n, addComment as o, pauseIssue as p, addIssue as q, registerMachineService as r, startDaemon as s, listIssues as t, updateIssue as u, searchIssues as v, isVisibleTo as w, getRun as x, listRuns as y, getWorkflow as z };
18669
+ export { resolveModel as $, runWorkflow as A, setWorkflowEnabled as B, removeWorkflow as C, saveWorkflow as D, rawWorkflow as E, listWorkflows as F, isWorkflowEnabled as G, workflowCrons as H, cronMatches as I, summarize as J, workflowSteps as K, loadMachineContext as L, buildMachineInstructions as M, machineToolsForRole as N, buildMachineTools as O, parseFrontmatter as P, getSkillsServer as Q, READ_ONLY_TOOLS as R, ServeAuth as S, getSkillsWorkspaceName as T, getSkillsCollectionName as U, fetchWithTimeout as V, searchSkills as W, SKILLS_DIR as X, getSkillInfo as Y, downloadSkillFile as Z, listSkillFiles as _, createSessionStore as a, clearStopMarker as a0, stopMarkerExists as a1, formatHandle as a2, normalizeAllowedUser as a3, loadSecurityContextConfig as a4, resolveSecurityContext as a5, buildSecurityContextFromFlags as a6, mergeSecurityContexts as a7, buildSessionShareUrl as a8, computeOutboundHop as a9, registerAwaitingReply as aa, buildMachineShareUrl as ab, parseHandle as ac, handleMatchesMetadata as ad, describeMisconfiguration as ae, buildMachineDeps as af, applyClaudeProxyEnv as ag, composeSessionId as ah, generateFriendlyName as ai, generateHookSettings as aj, claudeAuth as ak, codexProvider as al, projectInfo as am, DefaultTransport$1 as an, acpBackend as ao, acpAgentConfig as ap, codexAppServerBackend as aq, GeminiTransport$1 as ar, instanceConfig as as, api as at, run as au, stopDaemon as b, connectToHypha as c, daemonStatus as d, getFrpsSubdomainHost as e, getFrpsServerPort as f, getHyphaServerUrl$1 as g, getFrpsServerAddr as h, shortId as i, getHyphaServerUrl as j, hasCookieToken as k, resolveProjectRoot as l, getIssue as m, resumeIssue as n, addComment as o, pauseIssue as p, addIssue as q, registerMachineService as r, startDaemon as s, listIssues as t, updateIssue as u, searchIssues as v, isVisibleTo as w, getRun as x, listRuns as y, getWorkflow as z };