archymedes-cli 3.6.0 → 3.7.0

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.
@@ -7,7 +7,7 @@ import {
7
7
  eventJournalPath,
8
8
  readEventJournal,
9
9
  runtimeEventForJournal
10
- } from "./archymedes-qm9bwbe7.mjs";
10
+ } from "./archymedes-cfwtgnjx.mjs";
11
11
  import {
12
12
  APIConnectionError,
13
13
  BoundedAgentRuntime,
@@ -45,18 +45,18 @@ import {
45
45
  wanderGradeCounts,
46
46
  wanderPlannerInstructions,
47
47
  withOpenRouterCacheControl
48
- } from "./archymedes-2119874q.mjs";
48
+ } from "./archymedes-hr1bssj4.mjs";
49
49
  import {
50
50
  __require
51
51
  } from "./archymedes-x67v3n58.mjs";
52
52
 
53
- // packages/archymedes-cli/src/archymedes.ts
53
+ // src/archymedes.ts
54
54
  import { createInterface as createInterface5 } from "readline/promises";
55
- import path37 from "path";
55
+ import path40 from "path";
56
56
  import { realpathSync } from "fs";
57
57
  import { pathToFileURL as pathToFileURL3 } from "url";
58
58
 
59
- // packages/core/src/cli/acp.ts
59
+ // ../core/src/cli/acp.ts
60
60
  var ACP_PROTOCOL_VERSION = 1;
61
61
  var JSON_RPC = { parseError: -32700, invalidRequest: -32600, methodNotFound: -32601, invalidParams: -32602, internalError: -32603 };
62
62
  var ARCHYMEDES_EVENT_METHOD = "_archymedes/event";
@@ -411,7 +411,7 @@ function toolCallTitle(toolName, argumentsValue) {
411
411
  return subject ? `${label}: ${String(subject).slice(0, 120)}` : label;
412
412
  }
413
413
 
414
- // packages/core/src/cli/session.ts
414
+ // ../core/src/cli/session.ts
415
415
  import { createHash, randomUUID } from "node:crypto";
416
416
  import { promises as fs } from "node:fs";
417
417
  import path from "node:path";
@@ -826,7 +826,7 @@ ${summary.trim()}` },
826
826
  ];
827
827
  }
828
828
 
829
- // packages/core/src/money.ts
829
+ // ../core/src/money.ts
830
830
  var CURRENCIES = (() => {
831
831
  try {
832
832
  return Intl.supportedValuesOf("currency");
@@ -916,7 +916,7 @@ function priceUsage(usage, prices) {
916
916
  return money(micros, prices.currency);
917
917
  }
918
918
 
919
- // packages/core/src/pricing.ts
919
+ // ../core/src/pricing.ts
920
920
  var ISO_DATE = /^\d{4}-\d{2}-\d{2}$/;
921
921
  function assertDate(value, label) {
922
922
  if (!ISO_DATE.test(value) || Number.isNaN(Date.parse(value)))
@@ -1005,7 +1005,7 @@ function priceUnits(record, quantities) {
1005
1005
  return money(units * 1e6, record.currency);
1006
1006
  }
1007
1007
 
1008
- // packages/core/src/providers/price-catalog.ts
1008
+ // ../core/src/providers/price-catalog.ts
1009
1009
  var ANTHROPIC_VERIFIED = "2026-06-24";
1010
1010
  var ANTHROPIC_SOURCE = "anthropic.com/pricing, recorded 2026-06-24";
1011
1011
  var ANTHROPIC = [
@@ -1057,7 +1057,7 @@ var SERVICES = [
1057
1057
  ];
1058
1058
  var PRICE_CATALOG = definePrices([...ANTHROPIC, ...OAI_COMPAT_LABS, ...SERVICES]);
1059
1059
 
1060
- // packages/core/src/providers/free-catalog.ts
1060
+ // ../core/src/providers/free-catalog.ts
1061
1061
  var FREE_ROUTER = "openrouter/free";
1062
1062
  var FREE_BASE_URL = "https://openrouter.ai/api/v1";
1063
1063
  var FREE_GATEWAY_URL = "";
@@ -1065,15 +1065,10 @@ var FREE_DISCOVERY_URL = "https://raw.githubusercontent.com/ClawLabsAI/free-ai-m
1065
1065
  var FREE_CATALOG_TTL_MS = 6 * 60 * 60 * 1000;
1066
1066
  var FREE_CATALOG_MAX_RECORDS = 1e4;
1067
1067
  var FREE_ROUTER_PREFERENCE = [
1068
- "cohere/north-mini-code:free",
1069
- "nvidia/nemotron-3-super-120b-a12b:free",
1070
- "google/gemma-4-31b-it:free",
1068
+ "nvidia/nemotron-3.5-super-vl:free",
1071
1069
  "dots-studio/dots-3-note-preview:free",
1072
- "nex-agi/nex-n2.5-pro:free",
1073
- "nvidia/nemotron-3.5-lightning:free",
1074
- "google/gemma-4-26b-a4b-it:free",
1075
- "inclusionai/ling-3.0-flash-vl:free",
1076
- "poolside/laguna-xs-2.1:free"
1070
+ "nvidia/nemotron-3-super-120b-a12b:free",
1071
+ "cohere/north-mini-code:free"
1077
1072
  ];
1078
1073
  function gatewayUrl(value) {
1079
1074
  try {
@@ -1149,6 +1144,18 @@ function parseFreeDiscovery(body) {
1149
1144
  }
1150
1145
  return [...models.values()].filter((model) => !duplicates.has(model.id));
1151
1146
  }
1147
+ var EFFORT_WORDS = new Set(["none", "minimal", "low", "medium", "high", "xhigh", "max"]);
1148
+ function parseReasoning(value) {
1149
+ const row = record(value);
1150
+ if (typeof row.mandatory !== "boolean")
1151
+ return;
1152
+ const efforts = strings(row.supported_efforts).filter((effort) => EFFORT_WORDS.has(effort));
1153
+ return {
1154
+ mandatory: row.mandatory,
1155
+ ...typeof row.default_enabled === "boolean" ? { defaultEnabled: row.default_enabled } : {},
1156
+ ...efforts.length ? { efforts } : {}
1157
+ };
1158
+ }
1152
1159
  function parseFreeOpenRouterModels(body) {
1153
1160
  const models = new Map;
1154
1161
  const duplicates = new Set;
@@ -1164,9 +1171,11 @@ function parseFreeOpenRouterModels(body) {
1164
1171
  const context = limit(row.context_length);
1165
1172
  const output = limit(top.max_completion_tokens);
1166
1173
  const reason = !zero2(pricing.prompt) || !zero2(pricing.completion) || pricing.request !== undefined && !zero2(pricing.request) ? "Zero pricing not confirmed" : !strings(architecture.output_modalities).includes("text") ? "No text output" : !strings(row.supported_parameters).includes("tools") ? "Tool calling not confirmed" : !context ? "Context limit unknown" : undefined;
1174
+ const reasoning = parseReasoning(row.reasoning);
1167
1175
  if (models.has(id))
1168
1176
  duplicates.add(id);
1169
1177
  models.set(id, {
1178
+ ...reasoning ? { reasoning } : {},
1170
1179
  id,
1171
1180
  name: label(row.name, id),
1172
1181
  provider: id.split("/")[0],
@@ -1207,7 +1216,7 @@ function isSmallModel(model) {
1207
1216
  return size !== null && size <= SMALL_MODEL_BILLIONS;
1208
1217
  }
1209
1218
 
1210
- // packages/core/src/providers/provider-specs.ts
1219
+ // ../core/src/providers/provider-specs.ts
1211
1220
  var PROVIDER_IDS = [
1212
1221
  "anthropic",
1213
1222
  "openai",
@@ -1268,7 +1277,7 @@ var PROVIDER_INFO = {
1268
1277
  }
1269
1278
  };
1270
1279
 
1271
- // packages/core/src/providers/anthropic-agent.ts
1280
+ // ../core/src/providers/anthropic-agent.ts
1272
1281
  async function collectAnthropicStream(stream, onTextDelta, onOutputProgress) {
1273
1282
  let id = "";
1274
1283
  let model = "";
@@ -1432,7 +1441,7 @@ class AnthropicAgentTurnProvider {
1432
1441
  let client;
1433
1442
  this.call = async (body, signal) => {
1434
1443
  if (!client) {
1435
- const { default: Anthropic } = await import("./index-teq1cta4.mjs").catch(() => {
1444
+ const { default: Anthropic } = await import("./index-qvgcwsvm.mjs").catch(() => {
1436
1445
  throw new Error("The Anthropic provider needs the @anthropic-ai/sdk package. Install it with: npm install @anthropic-ai/sdk");
1437
1446
  });
1438
1447
  client = new Anthropic({ apiKey: options.apiKey, ...options.baseURL ? { baseURL: options.baseURL } : {}, maxRetries: 0 });
@@ -1494,10 +1503,10 @@ class AnthropicAgentTurnProvider {
1494
1503
  }
1495
1504
  }
1496
1505
 
1497
- // packages/core/src/providers/archymedes-cloud-agent.ts
1506
+ // ../core/src/providers/archymedes-cloud-agent.ts
1498
1507
  import { createHash as createHash2, randomUUID as randomUUID2 } from "node:crypto";
1499
1508
 
1500
- // packages/core/src/providers/routing-plan.ts
1509
+ // ../core/src/providers/routing-plan.ts
1501
1510
  function isRecord(value) {
1502
1511
  return typeof value === "object" && value !== null;
1503
1512
  }
@@ -1569,7 +1578,7 @@ function planCurrency(ranked) {
1569
1578
  return;
1570
1579
  }
1571
1580
 
1572
- // packages/core/src/providers/credit-balance.ts
1581
+ // ../core/src/providers/credit-balance.ts
1573
1582
  function isRecord2(value) {
1574
1583
  return typeof value === "object" && value !== null;
1575
1584
  }
@@ -1604,7 +1613,7 @@ function parseCreditBalance(value) {
1604
1613
  };
1605
1614
  }
1606
1615
 
1607
- // packages/core/src/providers/archymedes-cloud-agent.ts
1616
+ // ../core/src/providers/archymedes-cloud-agent.ts
1608
1617
  class ArchymedesCloudError extends Error {
1609
1618
  status;
1610
1619
  code;
@@ -1821,7 +1830,7 @@ async function readBody(response) {
1821
1830
  }
1822
1831
  }
1823
1832
 
1824
- // packages/core/src/providers/request-pacer.ts
1833
+ // ../core/src/providers/request-pacer.ts
1825
1834
  var MIN_LEARNABLE_CAP = 8;
1826
1835
  var LEARNED_CAP_LIFETIME_WINDOWS = 5;
1827
1836
  var DEFAULTS = { baseGapMs: 2000, maxGapMs: 120000, releaseMs: 500 };
@@ -1923,7 +1932,33 @@ class RequestPacer {
1923
1932
  }
1924
1933
  }
1925
1934
 
1926
- // packages/core/src/providers/free-catalog-fetch.ts
1935
+ // ../core/src/providers/free-model-profiles.ts
1936
+ var PROFILES = {
1937
+ "nvidia/nemotron-3.5-super-vl:free": { fit: "agent", note: "tool calls verified; finished the test task in 78s" },
1938
+ "dots-studio/dots-3-note-preview:free": { fit: "agent", note: "tool calls verified; finished the test task in 104s" },
1939
+ "nvidia/nemotron-3-super-120b-a12b:free": { fit: "agent", note: "tool calls verified; finished the test task in 140s" },
1940
+ "cohere/north-mini-code:free": { fit: "agent", note: "tool calls verified; first token in about a second" },
1941
+ "poolside/laguna-s-2.1:free": { fit: "busy", note: "tool calls verified; upstream rate-limits most requests" },
1942
+ "poolside/laguna-xs-2.1:free": { fit: "busy", note: "tool calls verified; upstream rate-limits most requests" },
1943
+ "google/gemma-4-31b-it:free": { fit: "busy", note: "upstream rate-limited 9 of 10 requests" },
1944
+ "google/gemma-4-26b-a4b-it:free": { fit: "busy", note: "upstream rate-limited 9 of 10 requests" },
1945
+ "nvidia/nemotron-3-nano-omni-30b-a3b-reasoning:free": { fit: "busy", note: "tool calls verified; upstream capacity often exhausted" },
1946
+ "nvidia/nemotron-3.5-lightning:free": { fit: "slow", note: "tool calls verified, but 15s-3min before the first token" },
1947
+ "nvidia/nemotron-3-ultra-550b-a55b:free": { fit: "slow", note: "every probe timed out upstream after 5 minutes" },
1948
+ "thinkingmachines/inkling:free": { fit: "apps-only", note: "OpenRouter serves it only to apps it lists; refused (403) here" },
1949
+ "thinkingmachines/inkling-small:free": { fit: "apps-only", note: "OpenRouter serves it only to apps it lists; refused (403) here" },
1950
+ "liquid/lfm-2.5-2.6b:free": { fit: "small", note: "2.6B: calls are well-formed, but 37 of 57 failed in the test task" }
1951
+ };
1952
+ function freeModelProfile(id) {
1953
+ return PROFILES[id] ?? (isSmallModel(id) ? { fit: "small", note: "chat-sized: fine for chat, but it often misuses tools" } : { fit: "unmeasured", note: "tool calling not measured yet" });
1954
+ }
1955
+ var TIER = { agent: 0, unmeasured: 1, busy: 2, slow: 3, small: 4, "apps-only": null };
1956
+ var FREE_ROUTABLE_TIER = 2;
1957
+ function freeRoutingTier(id) {
1958
+ return TIER[freeModelProfile(id).fit];
1959
+ }
1960
+
1961
+ // ../core/src/providers/free-catalog-fetch.ts
1927
1962
  var MAX_BYTES = 8 * 1024 * 1024;
1928
1963
  async function boundedJson(fetchImpl, url, signal) {
1929
1964
  const response = await fetchImpl(url, { signal, redirect: "error", headers: { accept: "application/json" } });
@@ -1987,11 +2022,11 @@ async function fetchFreeCatalog(options = {}) {
1987
2022
  }
1988
2023
  }
1989
2024
 
1990
- // packages/core/src/providers/free-install.ts
2025
+ // ../core/src/providers/free-install.ts
1991
2026
  import { promises as fs3 } from "node:fs";
1992
2027
  import path3 from "node:path";
1993
2028
 
1994
- // packages/core/src/cli/memory.ts
2029
+ // ../core/src/cli/memory.ts
1995
2030
  import { promises as fs2 } from "node:fs";
1996
2031
  import os from "node:os";
1997
2032
  import path2 from "node:path";
@@ -2157,7 +2192,7 @@ function memoryPromptBlock(entries2) {
2157
2192
  `);
2158
2193
  }
2159
2194
 
2160
- // packages/core/src/providers/free-install.ts
2195
+ // ../core/src/providers/free-install.ts
2161
2196
  var FREE_INSTALL_HEADER = "x-archymedes-install";
2162
2197
  var FREE_INSTALL_STATUS_HEADER = "x-archymedes-install-status";
2163
2198
  var FREE_INSTALL_TIMEOUT_MS = 5000;
@@ -2333,7 +2368,7 @@ class FreeInstallToken {
2333
2368
  }
2334
2369
  }
2335
2370
 
2336
- // packages/core/src/providers/free-usage.ts
2371
+ // ../core/src/providers/free-usage.ts
2337
2372
  import { promises as fs4 } from "node:fs";
2338
2373
  import path4 from "node:path";
2339
2374
  var FREE_REMAINING_TOKENS_HEADER = "x-free-remaining-tokens";
@@ -2473,7 +2508,7 @@ class FreeUsageMeter {
2473
2508
  }
2474
2509
  }
2475
2510
 
2476
- // packages/core/src/providers/free-errors.ts
2511
+ // ../core/src/providers/free-errors.ts
2477
2512
  var OPENROUTER_PRIVACY_SETTINGS_URL = "https://openrouter.ai/settings/privacy";
2478
2513
  var OPENROUTER_KEYS_URL = "https://openrouter.ai/keys";
2479
2514
  var OPENROUTER_CREDITS_URL = "https://openrouter.ai/settings/credits";
@@ -2549,7 +2584,7 @@ function describeFreeFailure(input) {
2549
2584
  return { message, retryable: transient, ...retryAfterMs2 ? { retryAfterMs: retryAfterMs2 } : {} };
2550
2585
  }
2551
2586
 
2552
- // packages/core/src/providers/free-health.ts
2587
+ // ../core/src/providers/free-health.ts
2553
2588
  import { promises as fs5 } from "node:fs";
2554
2589
  import path5 from "node:path";
2555
2590
  var FREE_HEALTH_WINDOW_MS = 7 * 24 * 60 * 60 * 1000;
@@ -2602,7 +2637,7 @@ function recordFreeOutcome(records, id, outcome, now) {
2602
2637
  return Object.fromEntries(newest);
2603
2638
  }
2604
2639
  function orderByFreeHealth(models, records, now) {
2605
- return models.map((model, index) => ({ model, index, small: isSmallModel(model.id), score: healthScore(records[model.id], now) })).sort((a, b) => Number(a.small) - Number(b.small) || b.score - a.score || a.index - b.index).map(({ model }) => model);
2640
+ return models.map((model, index) => ({ model, index, tier: freeRoutingTier(model.id) ?? Infinity, score: healthScore(records[model.id], now) })).sort((a, b) => a.tier - b.tier || b.score - a.score || a.index - b.index).map(({ model }) => model);
2606
2641
  }
2607
2642
  function fileFreeHealthStore(file) {
2608
2643
  return {
@@ -2658,7 +2693,81 @@ class FreeHealthTracker {
2658
2693
  }
2659
2694
  }
2660
2695
 
2661
- // packages/core/src/providers/openrouter-key-info.ts
2696
+ // ../core/src/providers/free-endpoint-uptime.ts
2697
+ var FREE_ENDPOINT_DOWN_PERCENT = 30;
2698
+ var FREE_UPTIME_TTL_MS = 5 * 60 * 1000;
2699
+ var FREE_UPTIME_TIMEOUT_MS = 2000;
2700
+ function percent(value) {
2701
+ return typeof value === "number" && Number.isFinite(value) && value >= 0 && value <= 100 ? value : undefined;
2702
+ }
2703
+ function parseEndpointUptime(body) {
2704
+ const data = body?.data;
2705
+ const endpoints = Array.isArray(data?.endpoints) ? data.endpoints : [];
2706
+ let best;
2707
+ for (const endpoint of endpoints.slice(0, 32)) {
2708
+ const row = endpoint;
2709
+ if (!row || typeof row !== "object")
2710
+ continue;
2711
+ const uptime = { last30m: percent(row.uptime_last_30m), last1d: percent(row.uptime_last_1d) };
2712
+ if (uptime.last30m === undefined && uptime.last1d === undefined)
2713
+ continue;
2714
+ const score = (u) => u.last30m ?? u.last1d ?? 0;
2715
+ if (!best || score(uptime) > score(best))
2716
+ best = uptime;
2717
+ }
2718
+ return best;
2719
+ }
2720
+ function isEndpointDown(uptime) {
2721
+ const latest = uptime?.last30m ?? uptime?.last1d;
2722
+ return latest !== undefined && latest < FREE_ENDPOINT_DOWN_PERCENT;
2723
+ }
2724
+
2725
+ class FreeEndpointUptime {
2726
+ options;
2727
+ cache = new Map;
2728
+ inFlight = new Map;
2729
+ constructor(options = {}) {
2730
+ this.options = options;
2731
+ }
2732
+ async down(ids, signal) {
2733
+ const now = (this.options.now ?? Date.now)();
2734
+ const unknown = [];
2735
+ for (const id of ids) {
2736
+ const hit = this.cache.get(id);
2737
+ if (hit && hit.at <= now && now - hit.at < FREE_UPTIME_TTL_MS)
2738
+ continue;
2739
+ const reading = this.read(id, now);
2740
+ if (!hit)
2741
+ unknown.push(reading);
2742
+ }
2743
+ if (unknown.length) {
2744
+ const deadline = AbortSignal.any([AbortSignal.timeout(this.options.timeoutMs ?? FREE_UPTIME_TIMEOUT_MS), ...signal ? [signal] : []]);
2745
+ await Promise.race([Promise.all(unknown), new Promise((resolve) => deadline.addEventListener("abort", resolve, { once: true }))]);
2746
+ }
2747
+ return new Set(ids.filter((id) => isEndpointDown(this.cache.get(id)?.uptime)));
2748
+ }
2749
+ read(id, now) {
2750
+ const pending = this.inFlight.get(id);
2751
+ if (pending)
2752
+ return pending;
2753
+ const fetchImpl = this.options.fetchImpl ?? globalThis.fetch;
2754
+ const base = this.options.baseUrl ?? FREE_BASE_URL;
2755
+ const signal = AbortSignal.timeout(Math.max(this.options.timeoutMs ?? FREE_UPTIME_TIMEOUT_MS, 1e4));
2756
+ const work = (async () => {
2757
+ try {
2758
+ const response = await fetchImpl(`${base}/models/${id.split("/").map(encodeURIComponent).join("/")}/endpoints`, { signal, redirect: "error", headers: { accept: "application/json" } });
2759
+ if (response.ok)
2760
+ this.cache.set(id, { at: now, uptime: parseEndpointUptime(await response.json()) });
2761
+ } catch {} finally {
2762
+ this.inFlight.delete(id);
2763
+ }
2764
+ })();
2765
+ this.inFlight.set(id, work);
2766
+ return work;
2767
+ }
2768
+ }
2769
+
2770
+ // ../core/src/providers/openrouter-key-info.ts
2662
2771
  var OPENROUTER_KEY_URL = `${FREE_BASE_URL}/key`;
2663
2772
  var OPENROUTER_KEY_INFO_TTL_MS = 60000;
2664
2773
  var KEY_INFO_TIMEOUT_MS = 5000;
@@ -2761,7 +2870,7 @@ function nextUtcMidnight(now) {
2761
2870
  return new Date(Date.UTC(date.getUTCFullYear(), date.getUTCMonth(), date.getUTCDate() + 1)).toISOString();
2762
2871
  }
2763
2872
 
2764
- // packages/core/src/providers/tool-call-repair.ts
2873
+ // ../core/src/providers/tool-call-repair.ts
2765
2874
  function isObject(value) {
2766
2875
  return typeof value === "object" && value !== null && !Array.isArray(value);
2767
2876
  }
@@ -2871,7 +2980,7 @@ function repairToolCalls(calls) {
2871
2980
  });
2872
2981
  }
2873
2982
 
2874
- // packages/core/src/providers/free-agent.ts
2983
+ // ../core/src/providers/free-agent.ts
2875
2984
  var RETRYABLE_UPSTREAM_TYPES = new Set(["upstream_timeout", "upstream_idle_timeout", "upstream_unreachable", "upstream_stream_failed"]);
2876
2985
  function freeFailureStatus(error) {
2877
2986
  const record2 = error;
@@ -2894,13 +3003,33 @@ function openRouterEffort(effort) {
2894
3003
  return;
2895
3004
  return effort === "xhigh" || effort === "max" ? "high" : effort;
2896
3005
  }
3006
+ var EFFORT_ORDER = ["none", "minimal", "low", "medium", "high", "xhigh", "max"];
3007
+ function freeReasoningFor(model, effort) {
3008
+ const wanted = openRouterEffort(effort);
3009
+ if (!wanted)
3010
+ return;
3011
+ const reasoning = model?.reasoning;
3012
+ if (reasoning?.defaultEnabled === false && (wanted === "minimal" || wanted === "low"))
3013
+ return;
3014
+ const named = reasoning?.efforts?.filter((level) => level !== "none");
3015
+ if (!named?.length || named.includes(wanted))
3016
+ return { effort: wanted };
3017
+ const at = EFFORT_ORDER.indexOf(wanted);
3018
+ const nearest = [...named].sort((a, b) => Math.abs(EFFORT_ORDER.indexOf(a) - at) - Math.abs(EFFORT_ORDER.indexOf(b) - at) || EFFORT_ORDER.indexOf(a) - EFFORT_ORDER.indexOf(b))[0];
3019
+ return { effort: nearest };
3020
+ }
2897
3021
  var FREE_REQUESTS_PER_MINUTE = 20;
2898
3022
  var MAX_ROUTER_ATTEMPTS = 4;
2899
3023
  var CATALOG_TIMEOUT_MS = 15000;
2900
3024
  var FREE_CANDIDATE_FIRST_BYTE_TIMEOUT_MS = 45000;
2901
3025
  function rank(id) {
2902
3026
  const index = FREE_ROUTER_PREFERENCE.indexOf(id);
2903
- return index === -1 ? FREE_ROUTER_PREFERENCE.length : index;
3027
+ return [freeRoutingTier(id) ?? Infinity, index === -1 ? FREE_ROUTER_PREFERENCE.length : index];
3028
+ }
3029
+ function byRank(a, b) {
3030
+ const [tierA, positionA] = rank(a);
3031
+ const [tierB, positionB] = rank(b);
3032
+ return tierA - tierB || positionA - positionB;
2904
3033
  }
2905
3034
  function freeModelCapabilities(router, model) {
2906
3035
  if (!model)
@@ -2940,6 +3069,7 @@ class FreeAgentTurnProvider {
2940
3069
  install;
2941
3070
  health;
2942
3071
  keyInfo;
3072
+ uptime;
2943
3073
  pacer;
2944
3074
  constructor(options, dependencies = {}) {
2945
3075
  this.options = options;
@@ -2971,6 +3101,9 @@ class FreeAgentTurnProvider {
2971
3101
  const keyInfo = dependencies.keyInfo === undefined ? injected ? undefined : new OpenRouterKeyInfoCache(apiKey, dependencies.now ? { now: dependencies.now } : {}) : dependencies.keyInfo ?? undefined;
2972
3102
  if (keyInfo)
2973
3103
  this.keyInfo = keyInfo;
3104
+ const uptime = dependencies.uptime === undefined ? injected ? undefined : new FreeEndpointUptime(dependencies.now ? { now: dependencies.now } : {}) : dependencies.uptime ?? undefined;
3105
+ if (uptime)
3106
+ this.uptime = uptime;
2974
3107
  }
2975
3108
  if (this.viaGateway) {
2976
3109
  const install = dependencies.install === undefined ? dependencies.call ? undefined : new FreeInstallToken({ gatewayUrl: options.gatewayUrl, store: fileFreeInstallStore(freeInstallFile(process.env)) }) : dependencies.install ?? undefined;
@@ -3006,8 +3139,9 @@ class FreeAgentTurnProvider {
3006
3139
  }
3007
3140
  const eligible = this.catalog.models.filter((model) => model.eligible && isFreeModelId(model.id));
3008
3141
  const selected = eligible.find((model) => model.id === this.options.model);
3009
- const routable = eligible.filter((model) => model.id !== FREE_ROUTER);
3010
- const capable = routable.filter((model) => !isSmallModel(model.id));
3142
+ const routable = eligible.filter((model) => model.id !== FREE_ROUTER && freeRoutingTier(model.id) !== null);
3143
+ const down = this.options.model === FREE_ROUTER && this.uptime ? await this.uptime.down(routable.map((model) => model.id), userSignal) : new Set;
3144
+ const capable = routable.filter((model) => (freeRoutingTier(model.id) ?? Infinity) <= FREE_ROUTABLE_TIER && !down.has(model.id));
3011
3145
  const candidates = this.options.model === FREE_ROUTER ? capable.length > 0 ? capable : routable : selected ? [selected] : [];
3012
3146
  if (!candidates.length)
3013
3147
  throw new FreeAccessError("No eligible free tool model is available. Run /models refresh or try again later.");
@@ -3021,7 +3155,7 @@ class FreeAgentTurnProvider {
3021
3155
  const outputFor = (model) => Math.min(output, (model.context_window ?? input + output) - input);
3022
3156
  if (!fitting.length)
3023
3157
  throw new FreeAccessError("The conversation exceeds the available free model context. Start a new session or select a larger free model.");
3024
- const preferred = this.options.model === FREE_ROUTER ? fitting.filter((model) => !this.refused.has(model.id)).sort((a, b) => rank(a.id) - rank(b.id) || (b.context_window ?? 0) - (a.context_window ?? 0) || a.id.localeCompare(b.id)) : fitting;
3158
+ const preferred = this.options.model === FREE_ROUTER ? fitting.filter((model) => !this.refused.has(model.id)).sort((a, b) => byRank(a.id, b.id) || (b.context_window ?? 0) - (a.context_window ?? 0) || a.id.localeCompare(b.id)) : fitting;
3025
3159
  const ordered = this.options.model === FREE_ROUTER && this.health ? orderByFreeHealth(preferred, await this.health.records(), now) : preferred;
3026
3160
  if (!ordered.length)
3027
3161
  throw new FreeAccessError("Every free model refused this key. Choose a specific model with /model or try again later.", 403);
@@ -3047,7 +3181,7 @@ class FreeAgentTurnProvider {
3047
3181
  signal: userSignal
3048
3182
  });
3049
3183
  try {
3050
- const turn = await this.attempt(candidate.id, messages, outputFor(candidate), request, onTextDelta, onOutputProgress, deadline);
3184
+ const turn = await this.attempt(candidate, messages, outputFor(candidate), request, onTextDelta, onOutputProgress, deadline);
3051
3185
  this.health?.record(candidate.id, { ok: true });
3052
3186
  this.pacer.reportSuccess();
3053
3187
  return turn;
@@ -3100,7 +3234,7 @@ class FreeAgentTurnProvider {
3100
3234
  throw new FreeAccessError(failure.message, status, failure.retryable, failure.retryAfterMs);
3101
3235
  }
3102
3236
  }
3103
- async attempt(model, messages, output, request, onTextDelta, onOutputProgress, deadline) {
3237
+ async attempt(candidate, messages, output, request, onTextDelta, onOutputProgress, deadline) {
3104
3238
  const token = this.install?.current();
3105
3239
  let allowance;
3106
3240
  const observe = (headers) => {
@@ -3108,6 +3242,8 @@ class FreeAgentTurnProvider {
3108
3242
  if (this.viaGateway)
3109
3243
  allowance = parseFreeAllowanceHeaders(headers) ?? allowance;
3110
3244
  };
3245
+ const model = candidate.id;
3246
+ const reasoning = freeReasoningFor(candidate, request.effort);
3111
3247
  let response;
3112
3248
  try {
3113
3249
  response = await deadline.race(this.call({
@@ -3115,7 +3251,7 @@ class FreeAgentTurnProvider {
3115
3251
  messages: withOpenRouterCacheControl(messages, model),
3116
3252
  max_tokens: output,
3117
3253
  ...request.tools.length ? { tools: request.tools.map((tool) => ({ type: "function", function: { name: tool.name, description: tool.description, parameters: tool.inputSchema } })) } : {},
3118
- ...openRouterEffort(request.effort) ? { reasoning: { effort: openRouterEffort(request.effort) } } : {},
3254
+ ...reasoning ? { reasoning } : {},
3119
3255
  provider: { require_parameters: true, max_price: { prompt: 0, completion: 0 }, allow_fallbacks: false },
3120
3256
  stream: true,
3121
3257
  stream_options: { include_usage: true }
@@ -3150,7 +3286,7 @@ class FreeAgentTurnProvider {
3150
3286
  }
3151
3287
  }
3152
3288
 
3153
- // packages/core/src/providers/openrouter-agent.ts
3289
+ // ../core/src/providers/openrouter-agent.ts
3154
3290
  var OPENROUTER_BASE_URL = "https://openrouter.ai/api/v1";
3155
3291
 
3156
3292
  class OpenRouterAgentTurnProvider {
@@ -3199,7 +3335,7 @@ function withHint(error) {
3199
3335
  return wrapped;
3200
3336
  }
3201
3337
 
3202
- // packages/core/src/cli/token-saver.ts
3338
+ // ../core/src/cli/token-saver.ts
3203
3339
  var TOKEN_SAVER_CONTEXT_LIMIT = 16384;
3204
3340
  var TOKEN_SAVER_TOOL_RESULT_CHARS = 3000;
3205
3341
  var TOKEN_SAVER_TOTAL_TOOL_RESULT_CHARS = 24000;
@@ -3304,7 +3440,7 @@ function restoreFullHistory(messages, original, sent) {
3304
3440
  return [messages[0], ...original, ...messages.slice(1 + sent.length)];
3305
3441
  }
3306
3442
 
3307
- // packages/core/src/providers/agent-matrix.ts
3443
+ // ../core/src/providers/agent-matrix.ts
3308
3444
  var OPENAI_COMPATIBLE_BASE_URL = {
3309
3445
  google: "https://generativelanguage.googleapis.com/v1beta/openai/",
3310
3446
  xai: "https://api.x.ai/v1",
@@ -3470,12 +3606,12 @@ function describeProviders(environment) {
3470
3606
  }
3471
3607
  var PRICE_ENVIRONMENT_HINT = "MODEL_INPUT_PER_MILLION, MODEL_OUTPUT_PER_MILLION (and optionally MODEL_CACHED_INPUT_PER_MILLION, MODEL_PRICE_CURRENCY, MODEL_PRICE_MODEL)";
3472
3608
 
3473
- // packages/archymedes-cli/src/platform/settings.ts
3609
+ // src/platform/settings.ts
3474
3610
  import { promises as fs7 } from "node:fs";
3475
3611
  import os2 from "node:os";
3476
3612
  import path7 from "node:path";
3477
3613
 
3478
- // packages/archymedes-cli/src/platform/i18n.ts
3614
+ // src/platform/i18n.ts
3479
3615
  var CONTROL_LANGUAGES = {
3480
3616
  en: "English",
3481
3617
  zh: "中文 (Mandarin)",
@@ -3942,11 +4078,11 @@ function t(language, key) {
3942
4078
  return MESSAGES[key][language] ?? MESSAGES[key].en;
3943
4079
  }
3944
4080
 
3945
- // packages/archymedes-cli/src/platform/local-currency.ts
4081
+ // src/platform/local-currency.ts
3946
4082
  import { promises as fs6 } from "node:fs";
3947
4083
  import path6 from "node:path";
3948
4084
 
3949
- // packages/archymedes-cli/src/platform/endpoints.ts
4085
+ // src/platform/endpoints.ts
3950
4086
  var FX_ENDPOINTS = [
3951
4087
  "https://cdn.jsdelivr.net/npm/@fawazahmed0/currency-api@latest/v1/currencies",
3952
4088
  "https://latest.currency-api.pages.dev/v1/currencies"
@@ -3990,7 +4126,7 @@ function hostOf(url) {
3990
4126
  }
3991
4127
  }
3992
4128
 
3993
- // packages/archymedes-cli/src/platform/network.ts
4129
+ // src/platform/network.ts
3994
4130
  var CODES = {
3995
4131
  dns: ["ENOTFOUND", "EAI_AGAIN", "EAI_NONAME", "EAI_FAIL", "EAI_MEMORY", "EAI_SYSTEM", "DNS_E_NAME_NOT_FOUND", "DNS_E_QUERY_PENDING"],
3996
4132
  timeout: ["ETIMEDOUT", "UND_ERR_CONNECT_TIMEOUT", "UND_ERR_HEADERS_TIMEOUT", "UND_ERR_BODY_TIMEOUT", "ERR_SOCKET_CONNECTION_TIMEOUT", "connection_timeout"],
@@ -4239,7 +4375,7 @@ function classifyNetworkError(error, options = {}) {
4239
4375
  return null;
4240
4376
  }
4241
4377
 
4242
- // packages/archymedes-cli/src/platform/local-currency.ts
4378
+ // src/platform/local-currency.ts
4243
4379
  var COUNTRY_CURRENCIES = {
4244
4380
  RW: "RWF",
4245
4381
  EG: "EGP",
@@ -4367,7 +4503,7 @@ async function startFxRateLookup(from, to, options) {
4367
4503
  return { immediate: cached?.rate ?? null, refresh };
4368
4504
  }
4369
4505
 
4370
- // packages/archymedes-cli/src/theme/theme.ts
4506
+ // src/theme/theme.ts
4371
4507
  var BLUEPRINT = `
4372
4508
  /* Blueprint navy, chalk-white ink and an amber pencil — the original drafting table. */
4373
4509
  @theme blueprint {
@@ -4669,7 +4805,7 @@ function roleCode(role, palette, depth) {
4669
4805
  }
4670
4806
  var NO_COLOR_PALETTE = buildPalette(findBuiltinTheme(DEFAULT_THEME_NAME) ?? { name: DEFAULT_THEME_NAME, description: "", tokens: DEFAULT_TOKENS }, "none");
4671
4807
 
4672
- // packages/archymedes-cli/src/platform/settings.ts
4808
+ // src/platform/settings.ts
4673
4809
  var LANGUAGE_NAMES = {
4674
4810
  en: "English",
4675
4811
  zh: "中文",
@@ -4789,9 +4925,11 @@ var REASONING_CHOICES = [
4789
4925
  { value: "high", label: "High — slowest" }
4790
4926
  ];
4791
4927
  var CLAUDE_PROVIDER_ID = "claude";
4928
+ var OUTSIDE_ENGINE_IDS = [CLAUDE_PROVIDER_ID, "codex"];
4792
4929
  var PROVIDER_CHOICES = [
4793
4930
  ...PROVIDER_IDS.map((id) => ({ value: id, label: PROVIDER_INFO[id].label })),
4794
- { value: CLAUDE_PROVIDER_ID, label: "Claude (your subscription, through Claude Code)" }
4931
+ { value: CLAUDE_PROVIDER_ID, label: "Claude (your subscription, through Claude Code)" },
4932
+ { value: "codex", label: "Codex (your ChatGPT plan, through the Codex CLI)" }
4795
4933
  ];
4796
4934
  var SETTING_FIELDS = [
4797
4935
  { key: "ARCHYMEDES_THEME", label: "Theme — colours for the whole CLI (/theme <name> also saves it)", choices: THEME_CHOICES, section: "Appearance" },
@@ -4820,6 +4958,7 @@ var SETTING_FIELDS = [
4820
4958
  { key: "ARCHYMEDES_PROVIDER", label: "Default provider", choices: PROVIDER_CHOICES, section: "Providers" },
4821
4959
  { key: "ARCHYMEDES_FALLBACK_MODEL", label: "Fallback after transient provider failure — ask, or provider:model" },
4822
4960
  { key: "CLAUDE_MODEL", label: "Claude model (your subscription) — opus, sonnet, haiku or a model id; empty for Claude Code's default" },
4961
+ { key: "CODEX_MODEL", label: "Codex model (your ChatGPT plan) — a model id from Codex's list; empty for Codex's default" },
4823
4962
  { key: "ANTHROPIC_API_KEY", label: "Anthropic API key", secret: true },
4824
4963
  { key: "ANTHROPIC_BASE_URL", label: "Anthropic base URL", url: true },
4825
4964
  { key: "ANTHROPIC_MODEL", label: "Anthropic model" },
@@ -5004,8 +5143,8 @@ function validateSetting(key, raw) {
5004
5143
  throw new Error("Currency must be a three-letter ISO code such as USD.");
5005
5144
  if (key === "ARCHYMEDES_LANGUAGE" && !CONTROL_LANGUAGE_CODES.has(value.toLowerCase()))
5006
5145
  throw new Error(`Choose one of: ${[...CONTROL_LANGUAGE_CODES].join(", ")}.`);
5007
- if (key === "ARCHYMEDES_PROVIDER" && !isProviderId(value.toLowerCase()) && value.toLowerCase() !== CLAUDE_PROVIDER_ID)
5008
- throw new Error(`Choose one of: ${[...PROVIDER_IDS, CLAUDE_PROVIDER_ID].join(", ")}.`);
5146
+ if (key === "ARCHYMEDES_PROVIDER" && !isProviderId(value.toLowerCase()) && !OUTSIDE_ENGINE_IDS.includes(value.toLowerCase()))
5147
+ throw new Error(`Choose one of: ${[...PROVIDER_IDS, ...OUTSIDE_ENGINE_IDS].join(", ")}.`);
5009
5148
  if (key === "ARCHYMEDES_PROVIDER")
5010
5149
  return value.toLowerCase();
5011
5150
  if (key === "ARCHYMEDES_JEV" && !["on", "off"].includes(value.toLowerCase()))
@@ -5185,7 +5324,7 @@ Archymedes ${controlLabel(language, "settings")}
5185
5324
  }
5186
5325
  }
5187
5326
 
5188
- // packages/archymedes-cli/src/app/free-setup.ts
5327
+ // src/app/free-setup.ts
5189
5328
  var OPENROUTER_KEYS_URL2 = "https://openrouter.ai/keys";
5190
5329
  var FREE_PRIVACY_NOTICE = "Free models may log your prompts and code; don't send secrets. Use a paid provider for private code.";
5191
5330
  function needsFreePrivacyNotice(environment) {
@@ -5204,11 +5343,11 @@ async function showFreePrivacyNoticeOnce(options) {
5204
5343
  return next;
5205
5344
  }
5206
5345
 
5207
- // packages/core/src/providers/model-fetch.ts
5346
+ // ../core/src/providers/model-fetch.ts
5208
5347
  import { promises as fs8 } from "node:fs";
5209
5348
  import path8 from "node:path";
5210
5349
 
5211
- // packages/core/src/providers/model-list.ts
5350
+ // ../core/src/providers/model-list.ts
5212
5351
  function modelsUrl(base) {
5213
5352
  const trimmed = base.replace(/\/+$/, "");
5214
5353
  return trimmed.endsWith("/v1") ? `${trimmed}/models` : `${trimmed}/v1/models`;
@@ -5319,7 +5458,7 @@ function mergeModelLists(known, live) {
5319
5458
  const added = live.filter((model) => !seen.has(model)).sort();
5320
5459
  return [...known, ...added];
5321
5460
  }
5322
- // packages/core/src/providers/model-fetch.ts
5461
+ // ../core/src/providers/model-fetch.ts
5323
5462
  async function fetchProviderModels(provider, environment, fetchImpl, timeoutMs = 4000) {
5324
5463
  if (provider === "free") {
5325
5464
  try {
@@ -5407,7 +5546,7 @@ function fetchableProviders(environment, providers) {
5407
5546
  return providers.filter((provider) => provider !== "archymedes-cloud" && missingRequirements(provider, environment).length === 0);
5408
5547
  }
5409
5548
 
5410
- // packages/archymedes-cli/src/app/acp-connect.ts
5549
+ // src/app/acp-connect.ts
5411
5550
  var DEFAULT_BASE_URLS = {
5412
5551
  anthropic: "https://api.anthropic.com",
5413
5552
  openai: "https://api.openai.com/v1",
@@ -5534,7 +5673,7 @@ async function rememberModel(params, deps) {
5534
5673
  return { ok: true };
5535
5674
  }
5536
5675
 
5537
- // packages/archymedes-cli/src/app/acp-setup.ts
5676
+ // src/app/acp-setup.ts
5538
5677
  var KEY_URLS = {
5539
5678
  free: OPENROUTER_KEYS_URL2,
5540
5679
  openrouter: OPENROUTER_KEYS_URL2,
@@ -5606,7 +5745,7 @@ function providerOverview(environment) {
5606
5745
  });
5607
5746
  }
5608
5747
 
5609
- // packages/archymedes-cli/src/session/models.ts
5748
+ // src/session/models.ts
5610
5749
  function modelsForProvider(provider, asOf) {
5611
5750
  const seen = new Set;
5612
5751
  for (const record2 of PRICE_CATALOG) {
@@ -5630,7 +5769,7 @@ function describePrice(prices, display, convert2) {
5630
5769
  return `${formatMoney(input)}/${formatMoney(output)} per Mtok`;
5631
5770
  }
5632
5771
 
5633
- // packages/archymedes-cli/src/app/acp-models.ts
5772
+ // src/app/acp-models.ts
5634
5773
  var PUBLIC_LISTINGS = ["free", "openrouter"];
5635
5774
  async function modelList(environment, params, options = {}) {
5636
5775
  const offline = /^(1|true|on|yes)$/i.test(environment.ARCHYMEDES_MODELS_OFFLINE?.trim() ?? "");
@@ -5676,13 +5815,17 @@ async function modelList(environment, params, options = {}) {
5676
5815
  missing,
5677
5816
  small: isSmallModel(model)
5678
5817
  };
5818
+ if (model !== FREE_ROUTER && isFreeModelId(model)) {
5819
+ const profile = freeModelProfile(model);
5820
+ Object.assign(choice, { fit: profile.fit, fitNote: profile.note });
5821
+ }
5679
5822
  (choice.locked ? locked : ready).push(choice);
5680
5823
  }
5681
5824
  }
5682
5825
  return { choices: [...ready, ...locked], unconfigured, errors, providers: PROVIDER_IDS.length };
5683
5826
  }
5684
5827
 
5685
- // packages/archymedes-cli/src/app/acp-settings.ts
5828
+ // src/app/acp-settings.ts
5686
5829
  async function listSettings(processEnvironment) {
5687
5830
  const stored = await loadSettings(processEnvironment);
5688
5831
  const rows = SETTING_FIELDS.map((field) => {
@@ -5734,7 +5877,7 @@ async function setSetting(params, deps) {
5734
5877
  return { ok: true, file, value: secret && stored ? maskSetting(stored) : stored, isSet: stored !== "" };
5735
5878
  }
5736
5879
 
5737
- // packages/core/src/cli/image-attachments.ts
5880
+ // ../core/src/cli/image-attachments.ts
5738
5881
  var MAX_IMAGES_PER_TURN = 4;
5739
5882
  var MAX_IMAGE_BYTES = 5 * 1024 * 1024;
5740
5883
  var MENTION = /(?:^|\s)@((?:[\w.-]+\/)*[\w.-]+\.(?:png|jpe?g|gif|webp))(?=$|[\s,;:!?)])/gi;
@@ -5799,7 +5942,7 @@ function withoutImageData(messages) {
5799
5942
  });
5800
5943
  }
5801
5944
 
5802
- // packages/core/src/cli/hosted-recovery.ts
5945
+ // ../core/src/cli/hosted-recovery.ts
5803
5946
  import { createHash as createHash3, randomUUID as randomUUID3 } from "node:crypto";
5804
5947
  import { promises as fs9 } from "node:fs";
5805
5948
  import path9 from "node:path";
@@ -6058,12 +6201,12 @@ ${entry.response.content}` });
6058
6201
  }
6059
6202
  }
6060
6203
 
6061
- // packages/core/src/cli/agent.ts
6204
+ // ../core/src/cli/agent.ts
6062
6205
  import { randomUUID as randomUUID6 } from "node:crypto";
6063
6206
  import path23 from "node:path";
6064
6207
  import { access as access3 } from "node:fs/promises";
6065
6208
 
6066
- // packages/core/src/cli/checkpoints.ts
6209
+ // ../core/src/cli/checkpoints.ts
6067
6210
  import { spawn } from "node:child_process";
6068
6211
  import { randomUUID as randomUUID4 } from "node:crypto";
6069
6212
  import { promises as fs10 } from "node:fs";
@@ -6238,10 +6381,10 @@ class CheckpointStore {
6238
6381
  }
6239
6382
  }
6240
6383
 
6241
- // packages/core/src/cli/permissions.ts
6384
+ // ../core/src/cli/permissions.ts
6242
6385
  import { createHash as createHash4 } from "node:crypto";
6243
6386
 
6244
- // packages/core/src/cli/os-sandbox.ts
6387
+ // ../core/src/cli/os-sandbox.ts
6245
6388
  import { spawnSync } from "node:child_process";
6246
6389
  import { existsSync } from "node:fs";
6247
6390
  import os3 from "node:os";
@@ -6295,7 +6438,7 @@ ${result.stdout}`);
6295
6438
  }
6296
6439
  var SANDBOX_ESCALATION_NOTE = "This may have been blocked by the sandbox (commands write only inside the project and the temporary directory, and have no network). If the command really needs more, call run_command again with escalate: true and say why in your message; the person will be asked to allow that one run outside the sandbox.";
6297
6440
 
6298
- // packages/core/src/cli/command.ts
6441
+ // ../core/src/cli/command.ts
6299
6442
  import { spawn as spawn2 } from "node:child_process";
6300
6443
  import { constants } from "node:os";
6301
6444
  var ARCHYMEDES_CREDENTIAL_ENV_NAMES = [
@@ -6655,7 +6798,7 @@ Command exceeded its ${options.timeoutMs >= 1000 ? `${Math.round(options.timeout
6655
6798
  });
6656
6799
  };
6657
6800
 
6658
- // packages/core/src/cli/safety.ts
6801
+ // ../core/src/cli/safety.ts
6659
6802
  var TASK_RULES = [
6660
6803
  { category: "credentials", reason: "credential disclosure or high-impact credential change", pattern: /\b(?:export|expose|reveal|print|show|share|send|upload|publish|rotate|revoke)\b.{0,60}\b(?:api[ _-]?key|api[ _-]?token|access[ _-]?token|auth[ _-]?token|password|private[ _-]?key|secret|credential)s?\b|\b(?:set|add|store|save|replace)\b.{0,45}\b(?:production|prod|live)\b.{0,45}\b(?:api[ _-]?key|api[ _-]?token|access[ _-]?token|auth[ _-]?token|password|private[ _-]?key|secret|credential)s?\b/i },
6661
6804
  { category: "destructive", reason: "destructive data or repository operation", pattern: /(?:(?<!git\s)\brm\s+[^\n]*(?:-[^\n]*[rf]\b|--recursive|--force)|\bfind\s+[^\n]*-delete\b|\b(?:delete|drop|erase|purge|truncate|wipe|destroy|reset)\b.{0,45}\b(?:all|database|table|records?|history|account|branch|repository|production|live)\b)/i },
@@ -6717,7 +6860,7 @@ function assessToolSafety(call, tool) {
6717
6860
  };
6718
6861
  }
6719
6862
 
6720
- // packages/core/src/cli/command-rules.ts
6863
+ // ../core/src/cli/command-rules.ts
6721
6864
  import { promises as fs11 } from "node:fs";
6722
6865
  import path12 from "node:path";
6723
6866
  var PROJECT_RULES_FILE = ".archymedes/rules.json";
@@ -6852,7 +6995,7 @@ function describePrefix(prefix) {
6852
6995
  return prefix.map((word) => Array.isArray(word) ? `{${word.join("|")}}` : word).join(" ");
6853
6996
  }
6854
6997
 
6855
- // packages/core/src/cli/permissions.ts
6998
+ // ../core/src/cli/permissions.ts
6856
6999
  var ARCHYMEDES_CAPABILITIES = {
6857
7000
  read: "workspace.files.read",
6858
7001
  write: "workspace.files",
@@ -7310,7 +7453,7 @@ function describeToolCall(call, tool) {
7310
7453
  }
7311
7454
  }
7312
7455
 
7313
- // packages/core/src/cli/environment.ts
7456
+ // ../core/src/cli/environment.ts
7314
7457
  import os4 from "node:os";
7315
7458
  var BASE_PROBES = ["git", "node", "bun", "npm", "pnpm", "yarn", "rg", "python3"];
7316
7459
  var MARKER_PROBES = [
@@ -7408,13 +7551,13 @@ function describeEnvironment(report) {
7408
7551
  `);
7409
7552
  }
7410
7553
 
7411
- // packages/core/src/cli/prompt.ts
7554
+ // ../core/src/cli/prompt.ts
7412
7555
  import { createHash as createHash6 } from "node:crypto";
7413
7556
  import { promises as fs13 } from "node:fs";
7414
7557
  import os5 from "node:os";
7415
7558
  import path14 from "node:path";
7416
7559
 
7417
- // packages/core/src/cli/workspace.ts
7560
+ // ../core/src/cli/workspace.ts
7418
7561
  import { constants as fsConstants, promises as fs12 } from "node:fs";
7419
7562
  import { spawn as spawn3 } from "node:child_process";
7420
7563
  import { createHash as createHash5 } from "node:crypto";
@@ -8077,7 +8220,7 @@ async function grepWorkspace(root, query, options = {}) {
8077
8220
  return matches;
8078
8221
  }
8079
8222
 
8080
- // packages/core/src/cli/defender-playbooks.ts
8223
+ // ../core/src/cli/defender-playbooks.ts
8081
8224
  var INJECTION_PLAYBOOK = `
8082
8225
  ## Injection
8083
8226
 
@@ -8779,7 +8922,7 @@ function playbookFor(id) {
8779
8922
  return DEFENDER_PLAYBOOK_CATALOG.find((entry) => entry.id === wanted || entry.title.toLowerCase() === wanted);
8780
8923
  }
8781
8924
 
8782
- // packages/core/src/cli/prompt.ts
8925
+ // ../core/src/cli/prompt.ts
8783
8926
  var INSTRUCTION_FILES = ["AGENTS.override.md", "ARCHYMEDES.md", "AGENTS.md", "CLAUDE.md", ".archymedesrules"];
8784
8927
  async function instructionBoundary(root) {
8785
8928
  const requestedRoot = path14.resolve(root);
@@ -9045,17 +9188,17 @@ ${instructions}`);
9045
9188
  `);
9046
9189
  }
9047
9190
 
9048
- // packages/core/src/hooks/command.ts
9191
+ // ../core/src/hooks/command.ts
9049
9192
  function hookCommand(scriptPath, payload, platform) {
9050
9193
  if (platform !== "win32")
9051
9194
  return `env ARCHYMEDES_HOOK_EVENT_B64=${payload} ${scriptPath}`;
9052
9195
  return `set "ARCHYMEDES_HOOK_EVENT_B64=${payload}"&& call "${scriptPath.split("/").join("\\")}"`;
9053
9196
  }
9054
9197
 
9055
- // packages/core/src/hooks/types.ts
9198
+ // ../core/src/hooks/types.ts
9056
9199
  var HOOK_PHASES = ["pre-session", "pre-turn", "pre-tool-use", "post-tool-use", "post-turn", "post-session"];
9057
9200
 
9058
- // packages/core/src/hooks/registry.ts
9201
+ // ../core/src/hooks/registry.ts
9059
9202
  var HOOKS_DIRECTORY = ".archymedes/hooks";
9060
9203
  var HOOK_TIMEOUT_MS = 5000;
9061
9204
  async function scriptsIn(workspace, phaseDirectory) {
@@ -9158,7 +9301,7 @@ class HookRegistry {
9158
9301
  return warnings;
9159
9302
  }
9160
9303
  }
9161
- // packages/core/src/mcp/config.ts
9304
+ // ../core/src/mcp/config.ts
9162
9305
  var MCP_CONFIG_PATH = ".archymedes/mcp.json";
9163
9306
  function isPlainObject(value) {
9164
9307
  return typeof value === "object" && value !== null && !Array.isArray(value);
@@ -9213,7 +9356,7 @@ async function discoverMcpServers(workspace) {
9213
9356
  }
9214
9357
  return servers;
9215
9358
  }
9216
- // packages/core/src/mcp/http-transport.ts
9359
+ // ../core/src/mcp/http-transport.ts
9217
9360
  class HttpMcpTransport {
9218
9361
  config;
9219
9362
  requestTimeoutMs;
@@ -9369,7 +9512,7 @@ function isJsonRpcNotification(value) {
9369
9512
  return isPlainObject2(value) && "jsonrpc" in value && !("id" in value) && typeof value.method === "string";
9370
9513
  }
9371
9514
 
9372
- // packages/core/src/mcp/stdio-transport.ts
9515
+ // ../core/src/mcp/stdio-transport.ts
9373
9516
  import { spawn as spawn4 } from "node:child_process";
9374
9517
  import { createInterface } from "node:readline";
9375
9518
  class StdioMcpTransport {
@@ -9471,7 +9614,7 @@ function isJsonRpcNotification2(value) {
9471
9614
  return typeof value === "object" && value !== null && "jsonrpc" in value && !("id" in value) && typeof value.method === "string";
9472
9615
  }
9473
9616
 
9474
- // packages/core/src/mcp/connection.ts
9617
+ // ../core/src/mcp/connection.ts
9475
9618
  class McpConnection {
9476
9619
  config;
9477
9620
  transport;
@@ -9533,7 +9676,7 @@ class McpConnection {
9533
9676
  }
9534
9677
  var MCP_PROTOCOL_VERSION = "2024-11-05";
9535
9678
  var REQUEST_TIMEOUT_MS = 15000;
9536
- // packages/core/src/mcp/client.ts
9679
+ // ../core/src/mcp/client.ts
9537
9680
  class McpToolProvider {
9538
9681
  connection;
9539
9682
  kind = "mcp";
@@ -9558,7 +9701,7 @@ class McpToolProvider {
9558
9701
  this.connection.close();
9559
9702
  }
9560
9703
  }
9561
- // packages/core/src/cli/plugins.ts
9704
+ // ../core/src/cli/plugins.ts
9562
9705
  var PLUGINS_DIRECTORY = ".archymedes/plugins";
9563
9706
  function isPlainObject3(value) {
9564
9707
  return typeof value === "object" && value !== null && !Array.isArray(value);
@@ -9607,7 +9750,7 @@ async function discoverPlugins(workspace) {
9607
9750
  return plugins;
9608
9751
  }
9609
9752
 
9610
- // packages/core/src/cli/tool-schema.ts
9753
+ // ../core/src/cli/tool-schema.ts
9611
9754
  class ToolArgumentError extends Error {
9612
9755
  constructor(message) {
9613
9756
  super(message);
@@ -9768,7 +9911,7 @@ function validateToolArguments(toolName, schema, args) {
9768
9911
  return normalizeToolArguments(toolName, schema, args).value;
9769
9912
  }
9770
9913
 
9771
- // packages/core/src/skills/manifest.ts
9914
+ // ../core/src/skills/manifest.ts
9772
9915
  var SKILLS_DIRECTORY = ".archymedes/skills";
9773
9916
  function isPlainObject5(value) {
9774
9917
  return typeof value === "object" && value !== null && !Array.isArray(value);
@@ -9824,7 +9967,7 @@ function shellQuote(value, platform) {
9824
9967
  return `'${value.replace(/'/g, `'\\''`)}'`;
9825
9968
  return `"${value.replace(/"/g, '""').replace(/%/g, "%%")}"`;
9826
9969
  }
9827
- // packages/core/src/skills/provider.ts
9970
+ // ../core/src/skills/provider.ts
9828
9971
  class SkillToolProvider {
9829
9972
  id;
9830
9973
  skillsDirectory;
@@ -9854,7 +9997,7 @@ ${body}`, isError: result.exitCode !== 0 };
9854
9997
  }
9855
9998
  }
9856
9999
  var DEFAULT_SKILL_TIMEOUT_MS = 60000;
9857
- // packages/core/src/cli/external-tools.ts
10000
+ // ../core/src/cli/external-tools.ts
9858
10001
  var IMPLICIT_SKILL_PROVIDER_ID = "local-skills";
9859
10002
  async function loadLocalExternalTooling(workspace) {
9860
10003
  const providers = [new SkillToolProvider(IMPLICIT_SKILL_PROVIDER_ID, SKILLS_DIRECTORY, workspace)];
@@ -9895,7 +10038,7 @@ async function loadLocalExternalTooling(workspace) {
9895
10038
  };
9896
10039
  }
9897
10040
 
9898
- // packages/core/src/cli/goal.ts
10041
+ // ../core/src/cli/goal.ts
9899
10042
  var GOAL_EVIDENCE_CHARS = 24000;
9900
10043
  function goalPrompt(condition) {
9901
10044
  return `Work until this condition is fully met: ${condition}
@@ -9955,7 +10098,7 @@ function parseGoalVerdict(text) {
9955
10098
  return { status: "continue", reason: text.trim().slice(0, 300) || "the verdict could not be read" };
9956
10099
  }
9957
10100
 
9958
- // packages/core/src/cli/review.ts
10101
+ // ../core/src/cli/review.ts
9959
10102
  import { randomUUID as randomUUID5 } from "node:crypto";
9960
10103
  import { promises as fs14 } from "node:fs";
9961
10104
  import path15 from "node:path";
@@ -10096,7 +10239,7 @@ function reviewTranscript(result) {
10096
10239
  `);
10097
10240
  }
10098
10241
 
10099
- // packages/core/src/cli/nested-instructions.ts
10242
+ // ../core/src/cli/nested-instructions.ts
10100
10243
  class NestedInstructionTracker {
10101
10244
  workspace;
10102
10245
  seenDirectories = new Set;
@@ -10145,7 +10288,7 @@ ${blocks}`;
10145
10288
  }
10146
10289
  }
10147
10290
 
10148
- // packages/core/src/cli/safe-fetch.ts
10291
+ // ../core/src/cli/safe-fetch.ts
10149
10292
  import { lookup } from "node:dns/promises";
10150
10293
  import { isIP } from "node:net";
10151
10294
  var defaultResolveHost = async (hostname) => (await lookup(hostname, { all: true, verbatim: true })).map((entry) => entry.address);
@@ -10307,7 +10450,7 @@ async function fetchPublicText(input, options = {}) {
10307
10450
  return { ok: false, error: "too many redirects" };
10308
10451
  }
10309
10452
 
10310
- // packages/core/src/cli/secret-scan.ts
10453
+ // ../core/src/cli/secret-scan.ts
10311
10454
  var SECRET_PATTERNS = [
10312
10455
  { name: "AWS access key", regex: /AKIA[0-9A-Z]{16}/, severity: "critical" },
10313
10456
  { name: "GitHub token", regex: /gh[pousr]_[A-Za-z0-9]{36,255}/, severity: "critical" },
@@ -10338,7 +10481,7 @@ function findSecretsInLine(line) {
10338
10481
  return found;
10339
10482
  }
10340
10483
 
10341
- // packages/core/src/cli/tool-providers.ts
10484
+ // ../core/src/cli/tool-providers.ts
10342
10485
  var DEFAULT_EXTERNAL_TOOL_TIMEOUT_MS = 30000;
10343
10486
  async function toolsFromProvider(provider, timeoutMs = DEFAULT_EXTERNAL_TOOL_TIMEOUT_MS) {
10344
10487
  const externalTools = await provider.listTools();
@@ -10380,7 +10523,7 @@ async function collectExternalTools(providers, timeoutMs) {
10380
10523
  return collected;
10381
10524
  }
10382
10525
 
10383
- // packages/core/src/cli/deploy.ts
10526
+ // ../core/src/cli/deploy.ts
10384
10527
  var DEPLOY_PROVIDERS = {
10385
10528
  vercel: {
10386
10529
  id: "vercel",
@@ -10504,7 +10647,7 @@ function deployPlan(target, detection, options = {}) {
10504
10647
  return { commands, notes };
10505
10648
  }
10506
10649
 
10507
- // packages/core/src/cli/compute.ts
10650
+ // ../core/src/cli/compute.ts
10508
10651
  var MAX_INPUT = 2000;
10509
10652
  var MAX_DEPTH = 64;
10510
10653
  var FACTORIAL_LIMIT = 170;
@@ -11056,11 +11199,11 @@ function compute(expression, options = {}) {
11056
11199
  return { value: result, exact, rendered, radixRendered, warnings };
11057
11200
  }
11058
11201
 
11059
- // packages/core/src/cli/tools-extra/git.ts
11202
+ // ../core/src/cli/tools-extra/git.ts
11060
11203
  import { execFile } from "node:child_process";
11061
11204
  import path16 from "node:path";
11062
11205
 
11063
- // packages/core/src/cli/tools-extra/shared.ts
11206
+ // ../core/src/cli/tools-extra/shared.ts
11064
11207
  import { promises as fs15 } from "node:fs";
11065
11208
  function resolvedLimits(options) {
11066
11209
  return options.limits ?? DEFAULT_WORKSPACE_LIMITS;
@@ -11115,7 +11258,7 @@ function truncateText(text, maxChars, hint) {
11115
11258
  [truncated: showed ${cut.length} of ${text.length} chars. ${hint}]`, truncated: true };
11116
11259
  }
11117
11260
 
11118
- // packages/core/src/cli/tools-extra/git.ts
11261
+ // ../core/src/cli/tools-extra/git.ts
11119
11262
  var GIT_BASE_ARGS = ["--no-pager", "-c", "core.fsmonitor=false", "-c", "core.quotepath=false", "-c", "color.ui=false"];
11120
11263
  var MAX_GIT_OUTPUT = 16 * 1024 * 1024;
11121
11264
  function runGit2(root, args, timeoutMs) {
@@ -11397,7 +11540,7 @@ function createGitTools(options) {
11397
11540
  ];
11398
11541
  }
11399
11542
 
11400
- // packages/core/src/cli/tools-extra/image.ts
11543
+ // ../core/src/cli/tools-extra/image.ts
11401
11544
  function createImageTools(options) {
11402
11545
  const { root } = options;
11403
11546
  const limits = { ...resolvedLimits(options), maxReadBytes: MAX_IMAGE_BYTES };
@@ -11432,7 +11575,7 @@ function createImageTools(options) {
11432
11575
  ];
11433
11576
  }
11434
11577
 
11435
- // packages/core/src/cli/tools-extra/notebook.ts
11578
+ // ../core/src/cli/tools-extra/notebook.ts
11436
11579
  import { randomBytes } from "node:crypto";
11437
11580
  function toSourceLines(source) {
11438
11581
  if (source === "")
@@ -11613,7 +11756,7 @@ function createNotebookTools(options) {
11613
11756
  ];
11614
11757
  }
11615
11758
 
11616
- // packages/core/src/cli/tools-extra/repo-map.ts
11759
+ // ../core/src/cli/tools-extra/repo-map.ts
11617
11760
  var REPO_MAP_DEFAULT_CHARS = 12000;
11618
11761
  var REPO_MAP_MAX_CHARS = 60000;
11619
11762
  var ENTRY_NAMES = /(?:^|\/)(?:index|main|app|lib|mod|__init__|server|cli|program)\.[^/]+$/i;
@@ -11883,7 +12026,7 @@ ${lines.join(`
11883
12026
  ];
11884
12027
  }
11885
12028
 
11886
- // packages/core/src/cli/tools-extra/symbols.ts
12029
+ // ../core/src/cli/tools-extra/symbols.ts
11887
12030
  import { promises as fs16 } from "node:fs";
11888
12031
  import path17 from "node:path";
11889
12032
  var EXTENSIONS = {
@@ -12217,13 +12360,13 @@ class SymbolIndex {
12217
12360
  }
12218
12361
  }
12219
12362
 
12220
- // packages/core/src/cli/tools-extra/index.ts
12363
+ // ../core/src/cli/tools-extra/index.ts
12221
12364
  function createExtraTools(options) {
12222
12365
  const index = options.symbolIndex ?? new SymbolIndex(options.root, resolvedLimits(options));
12223
12366
  return [...createRepoMapTools(options, index), ...createGitTools(options), ...createNotebookTools(options), ...createImageTools(options)];
12224
12367
  }
12225
12368
 
12226
- // packages/core/src/cli/tools.ts
12369
+ // ../core/src/cli/tools.ts
12227
12370
  class TodoList {
12228
12371
  items = [];
12229
12372
  nextId = 1;
@@ -13353,7 +13496,7 @@ function stripMarkup(html) {
13353
13496
  `).replace(/[ \t]{2,}/g, " ").trim();
13354
13497
  }
13355
13498
 
13356
- // packages/core/src/cli/cost.ts
13499
+ // ../core/src/cli/cost.ts
13357
13500
  function formatTokenFlow(usage) {
13358
13501
  return `↑${compactTokenCount(usage.inputTokens)} ↓${compactTokenCount(usage.outputTokens)} tok`;
13359
13502
  }
@@ -13629,10 +13772,10 @@ class CostLedger {
13629
13772
  }
13630
13773
  }
13631
13774
 
13632
- // packages/core/src/cli/backends.ts
13775
+ // ../core/src/cli/backends.ts
13633
13776
  import path19 from "node:path";
13634
13777
 
13635
- // packages/core/src/sandbox-search.ts
13778
+ // ../core/src/sandbox-search.ts
13636
13779
  var DEFAULT_IGNORED_DIRECTORIES = [".git", "node_modules", ".next", "dist", "build", "target", "__pycache__", ".venv", "venv", ".pytest_cache", ".mypy_cache", ".ruff_cache", ".turbo", "vendor", ".archymedes"];
13637
13780
  async function listSandboxFiles(sandbox, sandboxId, root, ignoredDirectories = DEFAULT_IGNORED_DIRECTORIES) {
13638
13781
  const result = await sandbox.runCommand(sandboxId, {
@@ -13706,7 +13849,7 @@ function relativeTo(root, absolute) {
13706
13849
  return absolute.startsWith(`${root}/`) ? absolute.slice(root.length + 1) : absolute;
13707
13850
  }
13708
13851
 
13709
- // packages/core/src/cli/applications.ts
13852
+ // ../core/src/cli/applications.ts
13710
13853
  import { spawn as spawn5 } from "node:child_process";
13711
13854
  import { stat } from "node:fs/promises";
13712
13855
  import http from "node:http";
@@ -13894,7 +14037,7 @@ The process produced no logs.`;
13894
14037
  }
13895
14038
  }
13896
14039
 
13897
- // packages/core/src/cli/backends.ts
14040
+ // ../core/src/cli/backends.ts
13898
14041
  var CONFIG_FILE_SCAN_LIMIT = 500;
13899
14042
  function localCommandGuidance(platform) {
13900
14043
  if (platform !== "win32")
@@ -14218,7 +14361,7 @@ async function downloadProject(workspace, localRoot, options = {}) {
14218
14361
  return { written, failed };
14219
14362
  }
14220
14363
 
14221
- // packages/core/src/cli/artifacts.ts
14364
+ // ../core/src/cli/artifacts.ts
14222
14365
  import { createHash as createHash7 } from "node:crypto";
14223
14366
  var ARTIFACT_DIRECTORY = ".archymedes/artifacts";
14224
14367
  var MAX_ARTIFACT_BYTES = 480000;
@@ -14262,12 +14405,12 @@ function countLines(content) {
14262
14405
  return lines;
14263
14406
  }
14264
14407
 
14265
- // packages/core/src/cli/defender-brain.ts
14408
+ // ../core/src/cli/defender-brain.ts
14266
14409
  import { access as access2 } from "node:fs/promises";
14267
14410
  import path22 from "node:path";
14268
14411
  import { fileURLToPath } from "node:url";
14269
14412
 
14270
- // packages/core/src/cli/state-client.ts
14413
+ // ../core/src/cli/state-client.ts
14271
14414
  import { spawn as spawn6 } from "node:child_process";
14272
14415
  import { access } from "node:fs/promises";
14273
14416
  import { createRequire } from "node:module";
@@ -14481,7 +14624,7 @@ async function tryConnectArchymedesState(options = {}) {
14481
14624
  return binary ? ArchymedesStateClient.connect({ ...options, binary }) : null;
14482
14625
  }
14483
14626
 
14484
- // packages/core/src/cli/defender-feed.ts
14627
+ // ../core/src/cli/defender-feed.ts
14485
14628
  import { createHash as createHash8, createPublicKey, verify as verifySignature } from "node:crypto";
14486
14629
  import { promises as fs17 } from "node:fs";
14487
14630
  import path21 from "node:path";
@@ -14710,7 +14853,7 @@ async function refreshDefenderFeed(options) {
14710
14853
  }
14711
14854
  }
14712
14855
 
14713
- // packages/core/src/cli/defender-brain.ts
14856
+ // ../core/src/cli/defender-brain.ts
14714
14857
  async function existingDirectory(candidates) {
14715
14858
  for (const candidate of candidates) {
14716
14859
  try {
@@ -14796,7 +14939,7 @@ class DefenderBrain {
14796
14939
  }
14797
14940
  }
14798
14941
 
14799
- // packages/core/src/cli/tool-profile.ts
14942
+ // ../core/src/cli/tool-profile.ts
14800
14943
  var MUTATING = /\b(create|write|edit|change|modify|fix|implement|build|install|delete|remove|rename|move|run|test|commit|push|deploy|publish|send|continue|resume|update|add|refactor|replace|rewrite|append|insert|generate|format|upgrade|bump|revert|patch|migrate|scaffold|save|set up|setup)\b/i;
14801
14944
  var REPOSITORY_READING = /\b(review|inspect|audit|analy[sz]e|diagnose|explain|find|locate|search|read|why|error|bug|code|repo(?:sitory)?|project|file|folder|function|class|test|diff)\b/i;
14802
14945
  var DIRECT_CHAT = /^(?:hi|hello|hey|thanks|thank you|who are you|what can you do)[.!?\s]*$/i;
@@ -14823,7 +14966,7 @@ function toolsForProfile(tools, profile) {
14823
14966
  return tools.filter((tool) => tool.effect === "none");
14824
14967
  }
14825
14968
 
14826
- // packages/core/src/cli/jev.ts
14969
+ // ../core/src/cli/jev.ts
14827
14970
  var JEV_API_URL = "https://api.typesafe.ai/v1/systemone";
14828
14971
  var JEV_DEFAULT_MODEL = "jev-latest";
14829
14972
  var JEV_TIMEOUT_MS = 30000;
@@ -15066,7 +15209,7 @@ function verdictFromResponse(response) {
15066
15209
  };
15067
15210
  }
15068
15211
 
15069
- // packages/core/src/cli/agent.ts
15212
+ // ../core/src/cli/agent.ts
15070
15213
  var DELEGATION_TOOLS = new Set(["delegate_task", "delegate_readonly_task"]);
15071
15214
  var EDIT_DIAGNOSTIC_TOOLS = new Set(["edit_file", "write_file"]);
15072
15215
  var EDIT_DIAGNOSTICS_TIMEOUT_MS = 3000;
@@ -16079,15 +16222,15 @@ class SteeringQueue {
16079
16222
  }
16080
16223
  }
16081
16224
 
16082
- // packages/core/src/cli/edit-diagnostics.ts
16225
+ // ../core/src/cli/edit-diagnostics.ts
16083
16226
  import { promises as fs18 } from "node:fs";
16084
16227
  import path24 from "node:path";
16085
16228
  import { fileURLToPath as fileURLToPath2, pathToFileURL } from "node:url";
16086
16229
 
16087
- // packages/core/src/lsp/client.ts
16230
+ // ../core/src/lsp/client.ts
16088
16231
  import { spawn as spawn7, spawnSync as spawnSync2 } from "node:child_process";
16089
16232
 
16090
- // packages/core/src/lsp/diagnostics.ts
16233
+ // ../core/src/lsp/diagnostics.ts
16091
16234
  function severityName(severity) {
16092
16235
  switch (severity) {
16093
16236
  case 1:
@@ -16193,7 +16336,7 @@ class DiagnosticsCollection {
16193
16336
  }
16194
16337
  }
16195
16338
 
16196
- // packages/core/src/lsp/protocol.ts
16339
+ // ../core/src/lsp/protocol.ts
16197
16340
  var HEADER_SEPARATOR = `\r
16198
16341
  \r
16199
16342
  `;
@@ -16239,7 +16382,7 @@ function isJsonRpcNotification3(value) {
16239
16382
  return typeof value === "object" && value !== null && "jsonrpc" in value && !("id" in value) && typeof value.method === "string";
16240
16383
  }
16241
16384
 
16242
- // packages/core/src/lsp/client.ts
16385
+ // ../core/src/lsp/client.ts
16243
16386
  class LspClientError extends Error {
16244
16387
  constructor(message) {
16245
16388
  super(message);
@@ -16450,7 +16593,7 @@ function toLocations(result) {
16450
16593
  return candidates.filter((candidate) => typeof candidate === "object" && candidate !== null && typeof candidate.uri === "string" && typeof candidate.range === "object");
16451
16594
  }
16452
16595
 
16453
- // packages/core/src/lsp/servers.ts
16596
+ // ../core/src/lsp/servers.ts
16454
16597
  import { spawnSync as spawnSync3 } from "node:child_process";
16455
16598
  var LANGUAGE_SERVERS = [
16456
16599
  {
@@ -16591,7 +16734,7 @@ function defaultProbe(command) {
16591
16734
  });
16592
16735
  }
16593
16736
 
16594
- // packages/core/src/cli/edit-diagnostics.ts
16737
+ // ../core/src/cli/edit-diagnostics.ts
16595
16738
  var DEFAULT_TIMEOUT_MS = 1500;
16596
16739
  var DEFAULT_REFINE_MS = 250;
16597
16740
  var DEFAULT_MAX_LINES = 20;
@@ -16812,7 +16955,7 @@ function createLspEditDiagnostics(root, options = {}) {
16812
16955
  });
16813
16956
  }
16814
16957
 
16815
- // packages/core/src/cli/session-fork.ts
16958
+ // ../core/src/cli/session-fork.ts
16816
16959
  import { randomUUID as randomUUID7 } from "node:crypto";
16817
16960
  import path25 from "node:path";
16818
16961
  async function forkSession(record2, git = runGit) {
@@ -16840,7 +16983,7 @@ function forkTitle(title) {
16840
16983
  return title.endsWith(" (fork)") ? title : `${title} (fork)`;
16841
16984
  }
16842
16985
 
16843
- // packages/core/src/providers/exa.ts
16986
+ // ../core/src/providers/exa.ts
16844
16987
  var LATENCY_CEILING = {
16845
16988
  instant: 5000,
16846
16989
  fast: 1e4,
@@ -17046,7 +17189,7 @@ function createExaClient(environment, fetchImpl) {
17046
17189
  });
17047
17190
  }
17048
17191
 
17049
- // packages/archymedes-cli/src/app/acp-commands.ts
17192
+ // src/app/acp-commands.ts
17050
17193
  async function runSessionCommand(command, argument, session) {
17051
17194
  switch (command) {
17052
17195
  case "cost":
@@ -17180,16 +17323,16 @@ async function memoryCommand(argument, root) {
17180
17323
  }
17181
17324
  }
17182
17325
 
17183
- // packages/archymedes-cli/src/app/claude/executable.ts
17326
+ // src/app/claude/executable.ts
17184
17327
  import { existsSync as existsSync3 } from "node:fs";
17185
17328
  import os6 from "node:os";
17186
17329
  import path27 from "node:path";
17187
17330
 
17188
- // packages/archymedes-cli/src/app/claude/install.ts
17331
+ // src/app/claude/install.ts
17189
17332
  import { spawn as spawn8 } from "node:child_process";
17190
17333
  import { mkdir } from "node:fs/promises";
17191
17334
 
17192
- // packages/archymedes-cli/src/app/claude/sdk.ts
17335
+ // src/app/claude/sdk.ts
17193
17336
  import { existsSync as existsSync2 } from "node:fs";
17194
17337
  import path26 from "node:path";
17195
17338
  import { pathToFileURL as pathToFileURL2 } from "node:url";
@@ -17221,7 +17364,7 @@ function loadSdk(entry) {
17221
17364
  return module;
17222
17365
  }
17223
17366
 
17224
- // packages/archymedes-cli/src/app/claude/install.ts
17367
+ // src/app/claude/install.ts
17225
17368
  var runCommand = (command, args, options) => new Promise((resolve) => {
17226
17369
  const child = spawn8(command, args, { stdio: ["ignore", "pipe", "pipe"], shell: process.platform === "win32", windowsHide: true, ...options.env ? { env: options.env } : {} });
17227
17370
  let output = "";
@@ -17262,7 +17405,7 @@ async function installSdk(environment, run = runCommand) {
17262
17405
  return { ok: false, error: `Could not install ${CLAUDE_SDK_PACKAGE} (${reason}). Install it yourself with: npm install --prefix "${prefix}" --omit=optional --legacy-peer-deps ${CLAUDE_SDK_PACKAGE}@${CLAUDE_SDK_VERSION}` };
17263
17406
  }
17264
17407
 
17265
- // packages/archymedes-cli/src/app/claude/executable.ts
17408
+ // src/app/claude/executable.ts
17266
17409
  function findClaude(environment, options = {}) {
17267
17410
  const exists = options.exists ?? existsSync3;
17268
17411
  const platform = options.platform ?? process.platform;
@@ -17311,11 +17454,11 @@ function claudeEnvironment(processEnvironment) {
17311
17454
  return environment;
17312
17455
  }
17313
17456
 
17314
- // packages/archymedes-cli/src/app/claude/identity.ts
17457
+ // src/app/claude/identity.ts
17315
17458
  var CLAUDE_PROVIDER = "claude";
17316
17459
  var CLAUDE_LABEL = "Claude (your subscription)";
17317
17460
 
17318
- // packages/archymedes-cli/src/app/claude/backend.ts
17461
+ // src/app/claude/backend.ts
17319
17462
  var CLAUDE_INSTALL_URL = "https://code.claude.com/docs/en/setup";
17320
17463
  var CLAUDE_LOGIN_ARGS = ["auth", "login"];
17321
17464
  var CLAUDE_MODEL_ALIASES = ["default", "opus", "sonnet", "haiku"];
@@ -17422,10 +17565,44 @@ async function claudeRuntime(deps) {
17422
17565
  }
17423
17566
  }
17424
17567
 
17425
- // packages/archymedes-cli/src/app/claude/session.ts
17568
+ // src/app/claude/session.ts
17426
17569
  import { randomUUID as randomUUID8 } from "node:crypto";
17427
17570
 
17428
- // packages/archymedes-cli/src/app/claude/translate.ts
17571
+ // src/app/handoff.ts
17572
+ var MAX_PREFACE_CHARS = 24000;
17573
+ function prefaceFrom(messages) {
17574
+ const lines = [];
17575
+ for (const message of messages) {
17576
+ if (message.internal || message.role !== "user" && message.role !== "assistant" || !message.content.trim())
17577
+ continue;
17578
+ lines.push(`${message.role === "user" ? "User" : "Assistant"}: ${message.content.trim()}`);
17579
+ }
17580
+ if (lines.length === 0)
17581
+ return "";
17582
+ let body = lines.join(`
17583
+
17584
+ `);
17585
+ if (body.length > MAX_PREFACE_CHARS)
17586
+ body = `…
17587
+ ${body.slice(body.length - MAX_PREFACE_CHARS)}`;
17588
+ return `<earlier_conversation note="This conversation began with another model. It is what was said, for context; the work it describes is in the files.">
17589
+ ${body}
17590
+ </earlier_conversation>`;
17591
+ }
17592
+ function handoffPreface(record2, engine, resumable) {
17593
+ if (!resumable)
17594
+ return prefaceFrom(record2.messages);
17595
+ const seen = record2.engineSeen?.[engine] ?? record2.messages.length;
17596
+ const missed = record2.messages.slice(Math.min(Math.max(seen, 0), record2.messages.length));
17597
+ if (!prefaceFrom(missed))
17598
+ return "";
17599
+ return prefaceFrom(missed).replace('note="This conversation began with another model.', 'note="While you were away, this conversation went on with another model.');
17600
+ }
17601
+ function markSeen(record2, engine) {
17602
+ record2.engineSeen = { ...record2.engineSeen, [engine]: record2.messages.length };
17603
+ }
17604
+
17605
+ // src/app/claude/translate.ts
17429
17606
  import path28 from "node:path";
17430
17607
  var TOOLS = {
17431
17608
  Bash: { name: "run_command", args: (input) => pick({ command: input.command, description: input.description }) },
@@ -17461,6 +17638,9 @@ class ClaudeTranslator {
17461
17638
  constructor(root) {
17462
17639
  this.root = root;
17463
17640
  }
17641
+ get toolRunning() {
17642
+ return this.open.size > 0 && !this.ended;
17643
+ }
17464
17644
  begin() {
17465
17645
  this.streamed.clear();
17466
17646
  this.open.clear();
@@ -17727,9 +17907,8 @@ function isRecord4(value) {
17727
17907
  return typeof value === "object" && value !== null && !Array.isArray(value);
17728
17908
  }
17729
17909
 
17730
- // packages/archymedes-cli/src/app/claude/session.ts
17910
+ // src/app/claude/session.ts
17731
17911
  var PERMISSION_MODES = { plan: "plan", build: "default", auto: "acceptEdits", defender: "default" };
17732
- var MAX_PREFACE_CHARS = 24000;
17733
17912
 
17734
17913
  class ClaudeSession {
17735
17914
  options;
@@ -17752,7 +17931,7 @@ class ClaudeSession {
17752
17931
  this.model = options.model;
17753
17932
  this.record = options.record ? structuredClone(options.record) : newRecord(options.root, options.mode);
17754
17933
  this.record.modelSelection = { provider: CLAUDE_PROVIDER, model: this.model ?? "default" };
17755
- this.preface = this.record.claudeSession ? "" : prefaceFrom(this.record.messages);
17934
+ this.preface = handoffPreface(this.record, CLAUDE_PROVIDER, Boolean(this.record.claudeSession));
17756
17935
  }
17757
17936
  get id() {
17758
17937
  return this.record.id;
@@ -17769,7 +17948,7 @@ class ClaudeSession {
17769
17948
  const done = new Promise((settle) => {
17770
17949
  resolve = settle;
17771
17950
  });
17772
- const turn = { resolve, prompt, cancelled: false };
17951
+ const turn = { resolve, prompt, cancelled: false, steers: [] };
17773
17952
  this.turn = turn;
17774
17953
  this.translator.begin();
17775
17954
  let input;
@@ -17790,6 +17969,14 @@ class ClaudeSession {
17790
17969
  ${prompt}` : prompt }, parent_tool_use_id: null, uuid: id });
17791
17970
  return done;
17792
17971
  }
17972
+ steer(text) {
17973
+ const turn = this.turn;
17974
+ if (!turn || turn.cancelled || !this.translator.toolRunning || !this.process)
17975
+ return false;
17976
+ turn.steers.push(text);
17977
+ this.process.then(({ input }) => input.push({ type: "user", message: { role: "user", content: text }, parent_tool_use_id: null, uuid: randomUUID8() }));
17978
+ return true;
17979
+ }
17793
17980
  cancel() {
17794
17981
  if (!this.turn || this.turn.cancelled)
17795
17982
  return;
@@ -17953,15 +18140,21 @@ ${this.translator.tools.join(`
17953
18140
  this.options.onEvent(event);
17954
18141
  if (outcome.status !== "failed" || outcome.text)
17955
18142
  this.preface = "";
17956
- this.remember(turn.prompt, outcome.text).finally(() => turn.resolve({ status: outcome.status, summary: outcome.summary }));
18143
+ this.remember(turn.prompt, outcome.text, turn.steers).finally(() => turn.resolve({ status: outcome.status, summary: outcome.summary }));
17957
18144
  }
17958
- async remember(prompt, reply) {
18145
+ async remember(prompt, reply, steers = []) {
17959
18146
  const record2 = this.record;
17960
- record2.messages = [...record2.messages, { role: "user", content: prompt }, ...reply.trim() ? [{ role: "assistant", content: reply }] : []];
18147
+ record2.messages = [
18148
+ ...record2.messages,
18149
+ { role: "user", content: prompt },
18150
+ ...steers.map((text) => ({ role: "user", content: `[Sent while you were working] ${text}` })),
18151
+ ...reply.trim() ? [{ role: "assistant", content: reply }] : []
18152
+ ];
17961
18153
  if (record2.title === "Untitled session")
17962
18154
  record2.title = titleFromObjective(prompt);
17963
18155
  if (this.translator.sessionId)
17964
18156
  record2.claudeSession = this.translator.sessionId;
18157
+ markSeen(record2, CLAUDE_PROVIDER);
17965
18158
  record2.mode = this.mode;
17966
18159
  const save = this.options.save ?? saveSession;
17967
18160
  try {
@@ -18038,25 +18231,6 @@ ${this.translator.tools.join(`
18038
18231
  function forThisSession(suggestions) {
18039
18232
  return suggestions?.map((suggestion) => ({ ...suggestion, destination: "session" }));
18040
18233
  }
18041
- function prefaceFrom(messages) {
18042
- const lines = [];
18043
- for (const message of messages) {
18044
- if (message.internal || message.role !== "user" && message.role !== "assistant" || !message.content.trim())
18045
- continue;
18046
- lines.push(`${message.role === "user" ? "User" : "Assistant"}: ${message.content.trim()}`);
18047
- }
18048
- if (lines.length === 0)
18049
- return "";
18050
- let body = lines.join(`
18051
-
18052
- `);
18053
- if (body.length > MAX_PREFACE_CHARS)
18054
- body = `…
18055
- ${body.slice(body.length - MAX_PREFACE_CHARS)}`;
18056
- return `<earlier_conversation note="This conversation began with another model. It is what was said, for context; the work it describes is in the files.">
18057
- ${body}
18058
- </earlier_conversation>`;
18059
- }
18060
18234
  function newRecord(root, mode) {
18061
18235
  const now = Date.now();
18062
18236
  return { schemaVersion: 2, revision: 0, id: newSessionId(), createdAt: now, updatedAt: now, root, title: "Untitled session", messages: [], mode, approvals: {}, totalRwf: 0 };
@@ -18103,81 +18277,1040 @@ class InputQueue {
18103
18277
  }
18104
18278
  }
18105
18279
 
18106
- // packages/archymedes-cli/src/app/jev.ts
18107
- function jevOptionsFromEnvironment(environment) {
18108
- if ((environment.ARCHYMEDES_JEV ?? "").trim().toLowerCase() === "off")
18109
- return;
18110
- const apiKey = environment.TYPESAFE_API_KEY?.trim();
18111
- if (!apiKey)
18112
- return;
18113
- const model = environment.TYPESAFE_MODEL?.trim();
18114
- return { apiKey, ...model ? { model } : {}, ...environment.ARCHYMEDES_JEV_REVIEW?.trim().toLowerCase() === "off" ? { review: false } : {} };
18115
- }
18280
+ // src/app/codex/backend.ts
18281
+ import { existsSync as existsSync4 } from "node:fs";
18282
+ import os7 from "node:os";
18283
+ import path29 from "node:path";
18116
18284
 
18117
- // packages/archymedes-cli/src/app/session-provider.ts
18118
- async function resolveSessionProvider(environment, options) {
18119
- if (options.provider || !options.resume)
18120
- return resolveProvider(environment, options);
18121
- const id = options.resume === "latest" ? (await listSessions(options.root, 1))[0]?.id : options.resume;
18122
- const selection = id ? (await loadSession(options.root, id))?.modelSelection : undefined;
18123
- return resolveProvider(environment, selection?.provider === "free" ? { provider: "free", model: options.model ?? selection.model } : options);
18124
- }
18285
+ // src/app/codex/identity.ts
18286
+ var CODEX_PROVIDER = "codex";
18287
+ var CODEX_LABEL = "Codex (your ChatGPT plan)";
18125
18288
 
18126
- // packages/archymedes-cli/src/app/acp-session.ts
18127
- async function openSession(context, resumeSessionId) {
18128
- let record2;
18129
- if (resumeSessionId) {
18130
- const id = resumeSessionId === "latest" ? (await listSessions(context.root, 1))[0]?.id : resumeSessionId;
18131
- record2 = id ? await loadSession(context.root, id) ?? undefined : undefined;
18132
- if (!record2)
18133
- throw new Error(resumeSessionId === "latest" ? `No earlier session in ${context.root}` : `No stored session ${resumeSessionId} in ${context.root}`);
18289
+ // src/app/codex/backend.ts
18290
+ var CODEX_INSTALL_URL = "https://developers.openai.com/codex/cli";
18291
+ var CODEX_LOGIN_ARGS = ["login"];
18292
+ function findCodex(environment, options = {}) {
18293
+ const exists = options.exists ?? existsSync4;
18294
+ const platform = options.platform ?? process.platform;
18295
+ const override = environment.ARCHYMEDES_CODEX_PATH?.trim();
18296
+ if (override)
18297
+ return exists(override) ? override : undefined;
18298
+ const names = platform === "win32" ? ["codex.exe", "codex.cmd", "codex"] : ["codex"];
18299
+ const directories = [
18300
+ ...(environment.PATH ?? environment.Path ?? "").split(platform === "win32" ? ";" : ":").filter(Boolean),
18301
+ path29.join(options.home ?? os7.homedir(), ".local", "bin"),
18302
+ "/opt/homebrew/bin",
18303
+ "/usr/local/bin"
18304
+ ];
18305
+ for (const directory of directories) {
18306
+ for (const name of names) {
18307
+ const candidate = path29.join(directory, name);
18308
+ if (exists(candidate))
18309
+ return candidate;
18310
+ }
18134
18311
  }
18135
- const mode = context.mode;
18136
- const engine = startsOnClaude(context, record2) ? await (context.claude ?? claudeEngine)(context, record2, record2?.mode ?? mode, context.provider === CLAUDE_PROVIDER ? context.model : claudeModel(context.environment)) : await archymedesEngine(context, { record: record2, mode, provider: context.provider, model: context.model, resume: resumeSessionId });
18137
- return new SwitchingSession(engine, context, mode);
18312
+ return;
18138
18313
  }
18139
- function startsOnClaude(context, record2) {
18140
- if (context.provider)
18141
- return context.provider === CLAUDE_PROVIDER;
18142
- if (record2)
18143
- return record2.modelSelection?.provider === CLAUDE_PROVIDER;
18144
- return claudeIsDefault(context.environment);
18314
+ function parseLoginStatus(code, output) {
18315
+ const text = output.trim();
18316
+ if (code === 0 && /logged in/i.test(text))
18317
+ return { loggedIn: true, method: /api key/i.test(text) ? "apiKey" : "chatgpt" };
18318
+ if (code === null)
18319
+ return { loggedIn: false, error: text || "codex did not start" };
18320
+ return { loggedIn: false };
18321
+ }
18322
+ var AUTH_TTL_MS2 = 30000;
18323
+ var authCache2;
18324
+ async function codexReadiness(deps, options = {}) {
18325
+ const executable2 = findCodex(deps.processEnvironment, deps.home ? { home: deps.home } : {});
18326
+ if (!executable2)
18327
+ return { ready: false, needs: "codex", missing: `Codex installed (${CODEX_INSTALL_URL})` };
18328
+ const now = (deps.now ?? Date.now)();
18329
+ let auth = authCache2?.executable === executable2 && now - authCache2.at < AUTH_TTL_MS2 && !options.fresh ? authCache2.auth : undefined;
18330
+ if (!auth) {
18331
+ const { code, output } = await (deps.run ?? runCommand)(executable2, ["login", "status"], { timeoutMs: 15000, env: codexEnvironment(deps.processEnvironment) });
18332
+ auth = parseLoginStatus(code, output);
18333
+ authCache2 = { executable: executable2, at: now, auth };
18334
+ }
18335
+ if (!auth.loggedIn)
18336
+ return { ready: false, needs: "sign-in", executable: executable2, auth, missing: "signing in to Codex (codex login)" };
18337
+ return { ready: true, executable: executable2, auth };
18338
+ }
18339
+ function codexEnvironment(environment) {
18340
+ const env = { ...environment };
18341
+ if (environment.ARCHYMEDES_CODEX_BILLING?.trim().toLowerCase() !== "api")
18342
+ delete env.OPENAI_API_KEY;
18343
+ return env;
18344
+ }
18345
+ function codexIsDefault(environment) {
18346
+ return environment.ARCHYMEDES_PROVIDER?.trim().toLowerCase() === CODEX_PROVIDER;
18347
+ }
18348
+ function codexModel(environment) {
18349
+ return environment.CODEX_MODEL?.trim() || "default";
18350
+ }
18351
+ async function codexOverview(deps) {
18352
+ const readiness = await codexReadiness(deps);
18353
+ const note = readiness.auth?.loggedIn ? readiness.auth.method === "apiKey" ? "Codex is signed in with an API key: turns are billed per token." : "Signed in with ChatGPT: turns use your plan's Codex limits." : undefined;
18354
+ return {
18355
+ id: CODEX_PROVIDER,
18356
+ label: CODEX_LABEL,
18357
+ configured: readiness.ready,
18358
+ missing: readiness.ready ? [] : [readiness.missing],
18359
+ keyUrl: readiness.ready || readiness.needs !== "codex" ? "" : CODEX_INSTALL_URL,
18360
+ model: codexModel(deps.environment),
18361
+ isDefault: codexIsDefault(deps.environment),
18362
+ fields: [],
18363
+ about: "Runs your own Codex on your ChatGPT plan. Signing in is Codex's own; Archymedes never sees your account.",
18364
+ ...readiness.executable && !readiness.ready && readiness.needs === "sign-in" ? { login: [readiness.executable, ...CODEX_LOGIN_ARGS] } : {},
18365
+ ...note ? { note } : {}
18366
+ };
18367
+ }
18368
+ async function connectCodex(deps) {
18369
+ const readiness = await codexReadiness(deps, { fresh: true });
18370
+ if (!readiness.ready) {
18371
+ return { ok: false, error: readiness.needs === "codex" ? `Install Codex first: ${CODEX_INSTALL_URL}` : "Sign in to Codex first: enter runs `codex login`, Codex's own sign-in." };
18372
+ }
18373
+ return { ok: true, file: "", models: codexModels() };
18374
+ }
18375
+ var learnedModels2;
18376
+ function codexModels() {
18377
+ return learnedModels2 && learnedModels2.length > 0 ? ["default", ...learnedModels2.filter((model) => model !== "default")] : ["default"];
18378
+ }
18379
+ function learnCodexModels(models) {
18380
+ learnedModels2 = models.filter((model) => typeof model === "string" && model.trim() !== "");
18381
+ }
18382
+ async function rememberCodexModel(model, deps) {
18383
+ const current = await (deps.load ?? (() => loadSettings(deps.processEnvironment)))();
18384
+ const next = { ...current };
18385
+ if (!model.trim() || model.trim() === "default")
18386
+ delete next.CODEX_MODEL;
18387
+ else
18388
+ next.CODEX_MODEL = model.trim();
18389
+ await (deps.save ?? ((settings) => saveSettings(settings, deps.processEnvironment)))(next);
18390
+ Object.assign(deps.environment, mergedEnvironment(next, deps.processEnvironment));
18391
+ if (!next.CODEX_MODEL)
18392
+ delete deps.environment.CODEX_MODEL;
18393
+ return { ok: true };
18394
+ }
18395
+ function withCodex(providers, codex) {
18396
+ const after = Math.max(providers.findIndex((provider) => provider.id === "claude"), providers.findIndex((provider) => provider.id === "free"));
18397
+ return [...providers.slice(0, after + 1), codex, ...providers.slice(after + 1)];
18398
+ }
18399
+ function withCodexModels(listing, codex) {
18400
+ const preferred = codex.model || "default";
18401
+ const choices = codexModels().map((model) => ({
18402
+ provider: CODEX_PROVIDER,
18403
+ providerLabel: CODEX_LABEL,
18404
+ model,
18405
+ isDefault: model === preferred,
18406
+ live: false,
18407
+ price: "your ChatGPT plan",
18408
+ free: false,
18409
+ locked: !codex.configured,
18410
+ missing: codex.missing,
18411
+ small: false
18412
+ }));
18413
+ const ready = listing.choices.filter((choice) => choice.locked !== true);
18414
+ const locked = listing.choices.filter((choice) => choice.locked === true);
18415
+ return {
18416
+ ...listing,
18417
+ choices: codex.configured ? [...ready, ...choices, ...locked] : [...ready, ...locked, ...choices],
18418
+ unconfigured: codex.configured ? listing.unconfigured : [...listing.unconfigured, { provider: CODEX_PROVIDER, label: CODEX_LABEL, missing: codex.missing }]
18419
+ };
18420
+ }
18421
+ async function codexSetupStatus(status, requested, deps) {
18422
+ const wanted = requested.provider ? requested.provider === CODEX_PROVIDER : codexIsDefault(deps.environment);
18423
+ if (!wanted)
18424
+ return status;
18425
+ const readiness = await codexReadiness(deps);
18426
+ if (readiness.ready)
18427
+ return { ...status, configured: true, reason: undefined };
18428
+ return { ...status, configured: false, reason: `Codex needs ${readiness.missing}.` };
18145
18429
  }
18146
18430
 
18147
- class SwitchingSession {
18148
- engine;
18149
- context;
18150
- mode;
18151
- constructor(engine, context, mode) {
18152
- this.engine = engine;
18153
- this.context = context;
18154
- this.mode = mode;
18431
+ // src/app/codex/session.ts
18432
+ import { randomUUID as randomUUID9 } from "node:crypto";
18433
+ import path31 from "node:path";
18434
+
18435
+ // src/app/codex/rpc.ts
18436
+ import { spawn as spawn9 } from "node:child_process";
18437
+
18438
+ class CodexRpcError extends Error {
18439
+ method;
18440
+ code;
18441
+ constructor(method, message2, code) {
18442
+ super(message2);
18443
+ this.method = method;
18444
+ this.code = code;
18445
+ this.name = "CodexRpcError";
18155
18446
  }
18156
- get id() {
18157
- return this.engine.id;
18447
+ }
18448
+
18449
+ class CodexRpc {
18450
+ transport;
18451
+ handlers;
18452
+ nextId = 1;
18453
+ pending = new Map;
18454
+ closed;
18455
+ constructor(transport, handlers) {
18456
+ this.transport = transport;
18457
+ this.handlers = handlers;
18458
+ transport.onLine((line) => this.receive(line));
18459
+ transport.onExit((reason) => this.gone(reason));
18460
+ }
18461
+ request(method, params, timeoutMs = 120000) {
18462
+ if (this.closed)
18463
+ return Promise.reject(new CodexRpcError(method, `Codex is not running (${this.closed})`));
18464
+ const id = this.nextId++;
18465
+ return new Promise((resolve, reject) => {
18466
+ const timer = setTimeout(() => {
18467
+ this.pending.delete(id);
18468
+ reject(new CodexRpcError(method, `Codex did not answer ${method} within ${Math.round(timeoutMs / 1000)} s`));
18469
+ }, timeoutMs);
18470
+ timer.unref?.();
18471
+ this.pending.set(id, {
18472
+ method,
18473
+ resolve: (value) => {
18474
+ clearTimeout(timer);
18475
+ resolve(value);
18476
+ },
18477
+ reject: (error) => {
18478
+ clearTimeout(timer);
18479
+ reject(error);
18480
+ }
18481
+ });
18482
+ this.send({ jsonrpc: "2.0", id, method, params });
18483
+ });
18158
18484
  }
18159
- send(prompt) {
18160
- return this.engine.send(prompt);
18485
+ notify(method, params) {
18486
+ if (!this.closed)
18487
+ this.send({ jsonrpc: "2.0", method, ...params === undefined ? {} : { params } });
18161
18488
  }
18162
- cancel() {
18163
- this.engine.cancel();
18489
+ get running() {
18490
+ return !this.closed;
18164
18491
  }
18165
- dispose() {
18166
- return this.engine.dispose();
18492
+ close() {
18493
+ if (this.closed)
18494
+ return;
18495
+ this.transport.close();
18496
+ this.gone("closed");
18167
18497
  }
18168
- history() {
18169
- return this.engine.history?.() ?? [];
18498
+ send(message2) {
18499
+ this.transport.write(`${JSON.stringify(message2)}
18500
+ `);
18170
18501
  }
18171
- info() {
18172
- return this.engine.info?.() ?? {};
18502
+ receive(line) {
18503
+ if (!line.trim())
18504
+ return;
18505
+ let message2;
18506
+ try {
18507
+ message2 = JSON.parse(line);
18508
+ } catch {
18509
+ return;
18510
+ }
18511
+ if (message2.method && message2.id !== undefined) {
18512
+ const id = message2.id;
18513
+ this.handlers.onRequest(message2.method, asRecord(message2.params)).then((result) => this.send({ jsonrpc: "2.0", id, result }), (error) => this.send({ jsonrpc: "2.0", id, error: { code: -32603, message: error instanceof Error ? error.message : String(error) } }));
18514
+ return;
18515
+ }
18516
+ if (message2.method) {
18517
+ this.handlers.onNotification(message2.method, asRecord(message2.params));
18518
+ return;
18519
+ }
18520
+ if (typeof message2.id !== "number")
18521
+ return;
18522
+ const waiting = this.pending.get(message2.id);
18523
+ if (!waiting)
18524
+ return;
18525
+ this.pending.delete(message2.id);
18526
+ if (message2.error)
18527
+ waiting.reject(new CodexRpcError(waiting.method, message2.error.message ?? "Codex refused the request", message2.error.code));
18528
+ else
18529
+ waiting.resolve(message2.result);
18173
18530
  }
18174
- steer(text) {
18175
- return this.engine.steer?.(text) ?? false;
18531
+ gone(reason) {
18532
+ if (this.closed)
18533
+ return;
18534
+ this.closed = reason;
18535
+ for (const [, waiting] of this.pending)
18536
+ waiting.reject(new CodexRpcError(waiting.method, `Codex stopped: ${reason}`));
18537
+ this.pending.clear();
18538
+ this.handlers.onExit?.(reason);
18176
18539
  }
18177
- async command(command, argument) {
18178
- if (command === "fork") {
18179
- const fork = await forkSession(this.engine.snapshot());
18180
- return { kind: "forked", id: fork.id, title: fork.title };
18540
+ }
18541
+ function asRecord(value) {
18542
+ return value && typeof value === "object" && !Array.isArray(value) ? value : {};
18543
+ }
18544
+ function spawnCodexAppServer(executable2, cwd, environment) {
18545
+ const child = spawn9(executable2, ["app-server"], { cwd, env: environment, stdio: ["pipe", "pipe", "pipe"] });
18546
+ const lineListeners = [];
18547
+ const exitListeners = [];
18548
+ let buffer = "";
18549
+ let stderr = "";
18550
+ let exited = false;
18551
+ const exit = (reason) => {
18552
+ if (exited)
18553
+ return;
18554
+ exited = true;
18555
+ for (const listener of exitListeners)
18556
+ listener(reason);
18557
+ };
18558
+ child.stdout.setEncoding("utf8");
18559
+ child.stdout.on("data", (chunk) => {
18560
+ buffer += chunk;
18561
+ let newline;
18562
+ while ((newline = buffer.indexOf(`
18563
+ `)) >= 0) {
18564
+ const line = buffer.slice(0, newline);
18565
+ buffer = buffer.slice(newline + 1);
18566
+ for (const listener of lineListeners)
18567
+ listener(line);
18568
+ }
18569
+ });
18570
+ child.stderr.setEncoding("utf8");
18571
+ child.stderr.on("data", (chunk) => {
18572
+ stderr = (stderr + chunk).slice(-4000);
18573
+ });
18574
+ child.on("error", (error) => exit(error.message));
18575
+ child.on("exit", (code, signal) => exit(signal ? `signal ${signal}` : `exit ${code}`));
18576
+ child.stdin.on("error", () => {
18577
+ return;
18578
+ });
18579
+ return {
18580
+ write: (line) => {
18581
+ if (!exited)
18582
+ child.stdin.write(line);
18583
+ },
18584
+ onLine: (listener) => {
18585
+ lineListeners.push(listener);
18586
+ },
18587
+ onExit: (listener) => {
18588
+ exitListeners.push(listener);
18589
+ },
18590
+ close: () => {
18591
+ child.stdin.end();
18592
+ child.kill();
18593
+ },
18594
+ stderr: () => stderr
18595
+ };
18596
+ }
18597
+
18598
+ // src/app/codex/translate.ts
18599
+ import path30 from "node:path";
18600
+ var MAX_RESULT_CHARS2 = 20000;
18601
+
18602
+ class CodexTranslator {
18603
+ root;
18604
+ model;
18605
+ todos = [];
18606
+ contextWindow;
18607
+ totals = { calls: 0, input: 0, output: 0, cacheRead: 0 };
18608
+ iteration = 0;
18609
+ toolCallsSinceUsage = 0;
18610
+ open = new Map;
18611
+ streamed = new Set;
18612
+ messages = new Map;
18613
+ order = [];
18614
+ finalAnswer;
18615
+ changePaths = new Map;
18616
+ failure;
18617
+ ended = false;
18618
+ constructor(root) {
18619
+ this.root = root;
18620
+ }
18621
+ begin() {
18622
+ this.open.clear();
18623
+ this.streamed.clear();
18624
+ this.messages.clear();
18625
+ this.order = [];
18626
+ this.finalAnswer = undefined;
18627
+ this.changePaths.clear();
18628
+ this.failure = undefined;
18629
+ this.toolCallsSinceUsage = 0;
18630
+ this.iteration = 0;
18631
+ this.ended = false;
18632
+ }
18633
+ feed(method, params) {
18634
+ if (this.ended)
18635
+ return [];
18636
+ switch (method) {
18637
+ case "item/agentMessage/delta": {
18638
+ const id = String(params.itemId ?? "");
18639
+ const delta = typeof params.delta === "string" ? params.delta : "";
18640
+ if (!delta)
18641
+ return [];
18642
+ this.streamed.add(id);
18643
+ this.say(id, delta);
18644
+ return [runtime2({ type: "assistant_delta", iteration: this.iteration, text: delta })];
18645
+ }
18646
+ case "item/started":
18647
+ return this.started(asRecord(params.item));
18648
+ case "item/completed":
18649
+ return this.completed(asRecord(params.item));
18650
+ case "thread/tokenUsage/updated":
18651
+ return this.usage(params);
18652
+ case "turn/plan/updated":
18653
+ this.todos = (Array.isArray(params.plan) ? params.plan : []).map(asRecord).map((step) => ({
18654
+ text: String(step.step ?? ""),
18655
+ status: step.status === "inProgress" ? "in_progress" : step.status === "completed" ? "done" : "pending"
18656
+ }));
18657
+ return [];
18658
+ case "error": {
18659
+ const error = asRecord(params.error);
18660
+ const text = codexErrorText(String(error.message ?? "Codex reported an error"));
18661
+ if (params.willRetry === true)
18662
+ return [runtime2({ type: "provider_retry", iteration: this.iteration, nextAttempt: 2, maxAttempts: 2, delayMs: 0, reason: failureKind2(text) })];
18663
+ this.failure = text;
18664
+ return [];
18665
+ }
18666
+ case "thread/compacted":
18667
+ return [{ type: "compaction", tokensBefore: 0, messagesBefore: 0, messagesAfter: 0, urgency: "required" }];
18668
+ default:
18669
+ return [];
18670
+ }
18671
+ }
18672
+ finish(turn, reason = {}) {
18673
+ if (this.ended)
18674
+ return { events: [], outcome: { status: "failed", summary: "The turn had already ended.", text: "" } };
18675
+ const events = [];
18676
+ for (const [id, name] of this.open) {
18677
+ events.push(runtime2({ type: "tool_result", toolCallId: id, toolName: name, isError: true, effect: "none", content: reason.cancelled ? "Stopped before it finished." : "Ended before it finished." }));
18678
+ }
18679
+ this.open.clear();
18680
+ const text = this.order.map((id) => this.messages.get(id) ?? "").filter((part) => part.trim()).join(`
18681
+
18682
+ `);
18683
+ const error = reason.error ?? (turn?.error ? codexErrorText(turn.error) : undefined) ?? this.failure;
18684
+ let outcome;
18685
+ if (reason.cancelled || turn?.status === "interrupted")
18686
+ outcome = { status: "cancelled", summary: "Stopped.", text };
18687
+ else if (turn?.status === "completed" && !reason.error)
18688
+ outcome = { status: "completed", summary: this.finalAnswer?.trim() || lastParagraph(text) || "Codex finished.", text };
18689
+ else
18690
+ outcome = { status: "failed", summary: error ?? "Codex stopped before finishing the turn.", text };
18691
+ events.push(runtime2({ type: "runtime_stop", status: outcome.status, summary: outcome.summary }));
18692
+ this.ended = true;
18693
+ return { events, outcome };
18694
+ }
18695
+ pathsOf(itemId) {
18696
+ return this.changePaths.get(itemId) ?? [];
18697
+ }
18698
+ started(item) {
18699
+ const id = String(item.id ?? "");
18700
+ if (!id || this.open.has(id))
18701
+ return [];
18702
+ if (item.type === "fileChange") {
18703
+ this.changePaths.set(id, (Array.isArray(item.changes) ? item.changes : []).map(asRecord).map((change) => relative2(String(change.path ?? ""), this.root)));
18704
+ }
18705
+ const call = this.describe(item);
18706
+ if (!call)
18707
+ return [];
18708
+ this.open.set(id, call.name);
18709
+ this.toolCallsSinceUsage += 1;
18710
+ return [runtime2({ type: "tool_call", toolCallId: id, toolName: call.name, effect: call.effect, arguments: call.args })];
18711
+ }
18712
+ completed(item) {
18713
+ const id = String(item.id ?? "");
18714
+ if (item.type === "agentMessage") {
18715
+ if (item.phase === "final_answer" && typeof item.text === "string")
18716
+ this.finalAnswer = item.text;
18717
+ if (this.streamed.has(id) || typeof item.text !== "string" || !item.text)
18718
+ return [];
18719
+ this.streamed.add(id);
18720
+ this.say(id, item.text);
18721
+ return [runtime2({ type: "assistant_delta", iteration: this.iteration, text: item.text })];
18722
+ }
18723
+ const name = this.open.get(id);
18724
+ if (!name)
18725
+ return [];
18726
+ this.open.delete(id);
18727
+ const { content, isError } = this.result(item);
18728
+ return [runtime2({ type: "tool_result", toolCallId: id, toolName: name, isError, effect: "none", content: bounded2(content) })];
18729
+ }
18730
+ usage(params) {
18731
+ const tokenUsage = asRecord(params.tokenUsage);
18732
+ const last = asRecord(tokenUsage.last);
18733
+ if (typeof tokenUsage.modelContextWindow === "number")
18734
+ this.contextWindow = tokenUsage.modelContextWindow;
18735
+ const usage = {
18736
+ inputTokens: count3(last.inputTokens),
18737
+ outputTokens: count3(last.outputTokens),
18738
+ totalTokens: count3(last.totalTokens),
18739
+ cachedInputTokens: count3(last.cachedInputTokens),
18740
+ cacheWriteTokens: count3(last.cacheWriteInputTokens),
18741
+ reasoningTokens: count3(last.reasoningOutputTokens)
18742
+ };
18743
+ this.totals.calls += 1;
18744
+ this.totals.input += usage.inputTokens;
18745
+ this.totals.output += usage.outputTokens;
18746
+ this.totals.cacheRead += usage.cachedInputTokens;
18747
+ this.iteration += 1;
18748
+ const event = runtime2({
18749
+ type: "model_turn",
18750
+ iteration: this.iteration,
18751
+ responseId: `${String(params.turnId ?? "turn")}:${this.iteration}`,
18752
+ model: this.model ?? "codex",
18753
+ toolCallCount: this.toolCallsSinceUsage,
18754
+ usage
18755
+ });
18756
+ this.toolCallsSinceUsage = 0;
18757
+ return [event];
18758
+ }
18759
+ say(id, text) {
18760
+ if (!this.messages.has(id))
18761
+ this.order.push(id);
18762
+ this.messages.set(id, (this.messages.get(id) ?? "") + text);
18763
+ }
18764
+ describe(item) {
18765
+ switch (item.type) {
18766
+ case "commandExecution":
18767
+ return { name: "run_command", effect: "workspace", args: { command: unwrapShell(String(item.command ?? "")) } };
18768
+ case "fileChange": {
18769
+ const changes = (Array.isArray(item.changes) ? item.changes : []).map(asRecord);
18770
+ const created = changes.length > 0 && changes.every((change) => asRecord(change.kind).type === "add");
18771
+ const first = changes[0] ? relative2(String(changes[0].path ?? ""), this.root) : "";
18772
+ return { name: created ? "write_file" : "edit_file", effect: "workspace", args: { path: first, ...changes.length > 1 ? { files: changes.map((change) => relative2(String(change.path ?? ""), this.root)) } : {} } };
18773
+ }
18774
+ case "mcpToolCall":
18775
+ return { name: `${String(item.server ?? "mcp")}.${String(item.tool ?? "tool")}`, effect: "external", args: asRecord(item.arguments) };
18776
+ case "dynamicToolCall":
18777
+ return { name: String(item.tool ?? "tool"), effect: "external", args: asRecord(item.arguments) };
18778
+ case "webSearch":
18779
+ return { name: "web_search", effect: "external", args: { query: String(item.query ?? "") } };
18780
+ default:
18781
+ return;
18782
+ }
18783
+ }
18784
+ result(item) {
18785
+ switch (item.type) {
18786
+ case "commandExecution": {
18787
+ const exitCode = typeof item.exitCode === "number" ? item.exitCode : null;
18788
+ const output = typeof item.aggregatedOutput === "string" && item.aggregatedOutput.trim() ? item.aggregatedOutput.trimEnd() : "(no output)";
18789
+ if (item.status === "declined")
18790
+ return { content: "Refused: the person did not allow this command.", isError: true };
18791
+ return { content: `exit ${exitCode ?? "?"}
18792
+ ${output}`, isError: item.status !== "completed" || exitCode !== null && exitCode !== 0 };
18793
+ }
18794
+ case "fileChange": {
18795
+ const changes = (Array.isArray(item.changes) ? item.changes : []).map(asRecord);
18796
+ if (item.status === "declined")
18797
+ return { content: "Refused: the person did not allow this change.", isError: true };
18798
+ const diff = changes.map((change) => `--- ${relative2(String(change.path ?? ""), this.root)}
18799
+ ${String(change.diff ?? "")}`).join(`
18800
+ `);
18801
+ return { content: diff || "(no change)", isError: item.status !== "completed" };
18802
+ }
18803
+ case "mcpToolCall": {
18804
+ const error = asRecord(item.error);
18805
+ if (typeof error.message === "string")
18806
+ return { content: error.message, isError: true };
18807
+ return { content: JSON.stringify(asRecord(item.result).content ?? item.result ?? null), isError: false };
18808
+ }
18809
+ default:
18810
+ return { content: item.status === "failed" ? "Failed." : "Done.", isError: item.status === "failed" };
18811
+ }
18812
+ }
18813
+ }
18814
+ function unwrapShell(command) {
18815
+ const match = /^(?:\S*\/)?(?:bash|sh|zsh)\s+-l?c\s+(['"])([\s\S]*)\1$/.exec(command.trim());
18816
+ if (!match)
18817
+ return command;
18818
+ return match[1] === '"' ? match[2].replace(/\\(["\\$`])/g, "$1") : match[2].replace(/'\\''/g, "'");
18819
+ }
18820
+ function codexErrorText(message2) {
18821
+ try {
18822
+ const parsed = asRecord(JSON.parse(message2));
18823
+ const inner = asRecord(parsed.error);
18824
+ if (typeof inner.message === "string" && inner.message)
18825
+ return inner.message;
18826
+ if (typeof parsed.message === "string" && parsed.message)
18827
+ return parsed.message;
18828
+ } catch {}
18829
+ return message2;
18830
+ }
18831
+ function failureKind2(text) {
18832
+ if (/rate.?limit|429|too many requests/i.test(text))
18833
+ return "rate_limit";
18834
+ if (/time(?:d)?.?out/i.test(text))
18835
+ return "timeout";
18836
+ if (/5\d\d|server error|overloaded/i.test(text))
18837
+ return "server";
18838
+ if (/network|connect|ECONN|stream disconnected/i.test(text))
18839
+ return "network";
18840
+ return "unknown";
18841
+ }
18842
+ function relative2(file, root) {
18843
+ if (!file)
18844
+ return file;
18845
+ const rel = path30.relative(root, file);
18846
+ return rel && !rel.startsWith("..") && !path30.isAbsolute(rel) ? rel.split(path30.sep).join("/") : file;
18847
+ }
18848
+ function bounded2(text) {
18849
+ return text.length > MAX_RESULT_CHARS2 ? `${text.slice(0, MAX_RESULT_CHARS2)}
18850
+ … (${text.length - MAX_RESULT_CHARS2} more characters)` : text;
18851
+ }
18852
+ function lastParagraph(text) {
18853
+ const parts = text.trim().split(/\n\s*\n/);
18854
+ return parts.at(-1)?.trim() ?? "";
18855
+ }
18856
+ function count3(value) {
18857
+ return typeof value === "number" && Number.isFinite(value) && value > 0 ? Math.round(value) : 0;
18858
+ }
18859
+ function runtime2(event) {
18860
+ return { type: "runtime", event };
18861
+ }
18862
+
18863
+ // src/app/codex/session.ts
18864
+ function codexPolicy(mode, root) {
18865
+ if (mode === "plan")
18866
+ return { approvalPolicy: "never", sandbox: "read-only", sandboxPolicy: { type: "readOnly", networkAccess: false } };
18867
+ const workspace = { type: "workspaceWrite", writableRoots: [path31.resolve(root)], networkAccess: false, excludeTmpdirEnvVar: false, excludeSlashTmp: false };
18868
+ return { approvalPolicy: mode === "auto" ? "on-request" : "untrusted", sandbox: "workspace-write", sandboxPolicy: workspace };
18869
+ }
18870
+
18871
+ class CodexSession {
18872
+ options;
18873
+ kind = "codex";
18874
+ translator;
18875
+ checkpoints;
18876
+ record;
18877
+ mode;
18878
+ model;
18879
+ process;
18880
+ turn;
18881
+ preface;
18882
+ refused = new Set;
18883
+ disposed = false;
18884
+ constructor(options) {
18885
+ this.options = options;
18886
+ this.translator = new CodexTranslator(options.root);
18887
+ this.mode = options.mode;
18888
+ this.model = options.model;
18889
+ this.record = options.record ? structuredClone(options.record) : newRecord2(options.root, options.mode);
18890
+ this.record.modelSelection = { provider: CODEX_PROVIDER, model: this.model ?? "default" };
18891
+ this.preface = handoffPreface(this.record, CODEX_PROVIDER, Boolean(this.record.codexThread));
18892
+ this.checkpoints = new CheckpointStore(options.root, path31.join(options.root, ".archymedes", `checkpoint-index-${randomUUID9()}`), options.git, () => this.record.id);
18893
+ this.checkpoints.load(this.record.checkpoints ?? []);
18894
+ }
18895
+ get id() {
18896
+ return this.record.id;
18897
+ }
18898
+ async open() {
18899
+ await this.start();
18900
+ }
18901
+ async send(prompt) {
18902
+ if (this.turn)
18903
+ throw new Error("A turn is already running in this session");
18904
+ if (this.disposed)
18905
+ throw new Error("This session is closed");
18906
+ let resolve;
18907
+ const done = new Promise((settle) => {
18908
+ resolve = settle;
18909
+ });
18910
+ const turn = { resolve, prompt, cancelled: false, steers: [] };
18911
+ this.turn = turn;
18912
+ this.translator.begin();
18913
+ let started;
18914
+ try {
18915
+ const [ready, checkpoint] = await Promise.all([this.start(), this.checkpoints.capture(titleFromObjective(prompt), `turn_${randomUUID9()}`, this.record.messages.length)]);
18916
+ started = ready;
18917
+ if (checkpoint)
18918
+ this.options.onEvent({ type: "checkpoint", checkpoint });
18919
+ } catch (error) {
18920
+ this.turn = undefined;
18921
+ throw error;
18922
+ }
18923
+ if (turn.cancelled || this.turn !== turn) {
18924
+ this.endTurn(undefined);
18925
+ return done;
18926
+ }
18927
+ const policy = codexPolicy(this.mode, this.options.root);
18928
+ const text = this.preface ? `${this.preface}
18929
+
18930
+ ${prompt}` : prompt;
18931
+ started.rpc.request("turn/start", {
18932
+ threadId: started.threadId,
18933
+ input: [{ type: "text", text, text_elements: [] }],
18934
+ approvalPolicy: policy.approvalPolicy,
18935
+ sandboxPolicy: policy.sandboxPolicy,
18936
+ ...this.model ? { model: this.model } : {}
18937
+ }).then((response) => {
18938
+ if (this.turn !== turn)
18939
+ return;
18940
+ turn.id = response.turn?.id;
18941
+ if (turn.cancelled && turn.id)
18942
+ started.rpc.request("turn/interrupt", { threadId: started.threadId, turnId: turn.id }).catch(() => {
18943
+ return;
18944
+ });
18945
+ }, (error) => this.endTurn(undefined, `Codex did not start the turn: ${message2(error)}`));
18946
+ return done;
18947
+ }
18948
+ cancel() {
18949
+ const turn = this.turn;
18950
+ if (!turn || turn.cancelled)
18951
+ return;
18952
+ turn.cancelled = true;
18953
+ if (!turn.id)
18954
+ return;
18955
+ this.process?.then(({ rpc, threadId }) => rpc.request("turn/interrupt", { threadId, turnId: turn.id })).catch(() => {
18956
+ return;
18957
+ });
18958
+ }
18959
+ steer(text) {
18960
+ const turn = this.turn;
18961
+ if (!turn?.id || turn.cancelled || !this.process)
18962
+ return false;
18963
+ const expectedTurnId = turn.id;
18964
+ turn.steers.push(text);
18965
+ this.process.then(({ rpc, threadId }) => rpc.request("turn/steer", { threadId, expectedTurnId, input: [{ type: "text", text, text_elements: [] }] })).catch(() => {
18966
+ turn.steers.splice(turn.steers.indexOf(text), 1);
18967
+ this.options.onEvent({ type: "steering_unread", messages: [text] });
18968
+ });
18969
+ return true;
18970
+ }
18971
+ async setMode(mode) {
18972
+ this.mode = mode;
18973
+ this.record.mode = mode;
18974
+ }
18975
+ async setModel(_provider, model) {
18976
+ this.model = !model || model === "default" ? undefined : model;
18977
+ this.record.modelSelection = { provider: CODEX_PROVIDER, model: this.model ?? "default" };
18978
+ }
18979
+ async command(command, argument) {
18980
+ switch (command) {
18981
+ case "undo":
18982
+ return this.undo(argument);
18983
+ case "turns":
18984
+ return { kind: "turns", turns: this.checkpoints.list().map((checkpoint, index) => ({ index, label: checkpoint.label, createdAt: checkpoint.createdAt })) };
18985
+ case "rewind": {
18986
+ const [position = "", scope = ""] = argument.trim().split(/\s+/);
18987
+ if (!/^\d+$/.test(position) || scope !== "" && scope !== "code")
18988
+ return { kind: "error", text: "Codex keeps its own conversation: /rewind <n> code puts the files back to before that turn." };
18989
+ return this.restore(Number(position));
18990
+ }
18991
+ case "diff": {
18992
+ const turn = /^turn\s+(\d+)$/.exec(argument.trim());
18993
+ const patch = turn ? await this.checkpoints.turnPatch(Number(turn[1])) : await this.checkpoints.diffPatch();
18994
+ return patch.trim() ? { kind: "diff", patch } : { kind: "text", text: turn ? "That turn changed no files." : "Nothing changed since the last checkpoint." };
18995
+ }
18996
+ case "cost": {
18997
+ const totals = this.translator.totals;
18998
+ return { kind: "text", text: `Codex on your plan: ${totals.calls} model calls, ${totals.input.toLocaleString("en-US")} input tokens (${totals.cacheRead.toLocaleString("en-US")} cached), ${totals.output.toLocaleString("en-US")} output.` };
18999
+ }
19000
+ case "todos":
19001
+ return this.translator.todos.length > 0 ? { kind: "todos", items: this.translator.todos } : { kind: "text", text: "No plan yet." };
19002
+ case "compact": {
19003
+ const { rpc, threadId } = await this.start();
19004
+ await rpc.request("thread/compact/start", { threadId });
19005
+ return { kind: "text", text: "Codex is summarizing its conversation." };
19006
+ }
19007
+ case "tools":
19008
+ return { kind: "text", text: "Codex runs its own tools: shell commands, patches, web search and its MCP servers, inside its sandbox." };
19009
+ case "memory":
19010
+ return { kind: "text", text: "Codex keeps its own instructions (AGENTS.md); /memory is for Archymedes's models." };
19011
+ default:
19012
+ return { kind: "error", text: `/${command} is not available while Codex runs this session.` };
19013
+ }
19014
+ }
19015
+ history() {
19016
+ return this.record.messages;
19017
+ }
19018
+ info() {
19019
+ return {
19020
+ sessionId: this.record.id,
19021
+ provider: CODEX_PROVIDER,
19022
+ providerLabel: CODEX_LABEL,
19023
+ model: this.model ?? this.translator.model ?? "default",
19024
+ mode: this.mode,
19025
+ root: this.options.root,
19026
+ workspace: "local",
19027
+ workspaceLabel: this.options.root
19028
+ };
19029
+ }
19030
+ snapshot() {
19031
+ return structuredClone({ ...this.record, checkpoints: this.checkpoints.list() });
19032
+ }
19033
+ async dispose() {
19034
+ this.disposed = true;
19035
+ this.cancel();
19036
+ const running = this.process;
19037
+ this.process = undefined;
19038
+ const started = await running?.catch(() => {
19039
+ return;
19040
+ });
19041
+ started?.rpc.close();
19042
+ }
19043
+ start() {
19044
+ this.process ??= this.spawn().catch((error) => {
19045
+ this.process = undefined;
19046
+ throw error;
19047
+ });
19048
+ return this.process;
19049
+ }
19050
+ async spawn() {
19051
+ const transport = this.options.transport?.() ?? spawnCodexAppServer(this.options.executable, this.options.root, this.options.environment);
19052
+ const stderr = () => transport.stderr?.() ?? "";
19053
+ let threadId = "";
19054
+ const rpc = new CodexRpc(transport, {
19055
+ onNotification: (method, params) => this.receive(method, params, threadId),
19056
+ onRequest: (method, params) => this.answer(method, params),
19057
+ onExit: (reason) => {
19058
+ this.endTurn(undefined, `Codex stopped (${reason}).${tail(stderr())}`);
19059
+ this.process?.then((started) => {
19060
+ if (started.rpc === rpc)
19061
+ this.process = undefined;
19062
+ }, () => {
19063
+ return;
19064
+ });
19065
+ }
19066
+ });
19067
+ try {
19068
+ await rpc.request("initialize", { clientInfo: { name: "archymedes", title: "Archymedes", version: "1" }, capabilities: { experimentalApi: true, requestAttestation: false } }, 120000);
19069
+ rpc.notify("initialized");
19070
+ threadId = await this.thread(rpc);
19071
+ } catch (error) {
19072
+ rpc.close();
19073
+ throw new Error(`Codex did not start: ${message2(error)}${tail(stderr())}`);
19074
+ }
19075
+ if (this.options.onModels) {
19076
+ rpc.request("model/list", {}).then((listed) => this.options.onModels?.((listed.data ?? []).filter((model) => !model.hidden && model.id).map((model) => String(model.id))), () => {
19077
+ return;
19078
+ });
19079
+ }
19080
+ return { rpc, threadId, stderr };
19081
+ }
19082
+ async thread(rpc) {
19083
+ const policy = codexPolicy(this.mode, this.options.root);
19084
+ const common = { cwd: this.options.root, approvalPolicy: policy.approvalPolicy, sandbox: policy.sandbox, ...this.model ? { model: this.model } : {} };
19085
+ const resume = this.record.codexThread;
19086
+ if (resume) {
19087
+ try {
19088
+ const resumed = asRecord(await rpc.request("thread/resume", { threadId: resume, ...common, excludeTurns: true }));
19089
+ this.translator.model = typeof resumed.model === "string" ? resumed.model : this.translator.model;
19090
+ return resume;
19091
+ } catch (error) {
19092
+ this.options.warn?.(`archymedes: Codex thread ${resume} could not be resumed (${message2(error)}); starting a new one`);
19093
+ delete this.record.codexThread;
19094
+ this.preface = prefaceFrom(this.record.messages);
19095
+ }
19096
+ }
19097
+ const started = asRecord(await rpc.request("thread/start", common));
19098
+ this.translator.model = typeof started.model === "string" ? started.model : undefined;
19099
+ const id = String(asRecord(started.thread).id ?? "");
19100
+ if (!id)
19101
+ throw new Error("Codex started no thread");
19102
+ this.record.codexThread = id;
19103
+ return id;
19104
+ }
19105
+ receive(method, params, threadId) {
19106
+ if (threadId && typeof params.threadId === "string" && params.threadId !== threadId)
19107
+ return;
19108
+ if (!this.turn) {
19109
+ this.translator.feed(method, params);
19110
+ return;
19111
+ }
19112
+ if (method === "turn/completed") {
19113
+ const turn = asRecord(params.turn);
19114
+ const error = asRecord(turn.error);
19115
+ this.endTurn({ status: String(turn.status ?? ""), ...typeof error.message === "string" ? { error: error.message } : {} });
19116
+ return;
19117
+ }
19118
+ for (const event of this.translator.feed(method, params))
19119
+ this.options.onEvent(event);
19120
+ }
19121
+ endTurn(result, error) {
19122
+ const turn = this.turn;
19123
+ if (!turn)
19124
+ return;
19125
+ this.turn = undefined;
19126
+ const { events, outcome } = this.translator.finish(result, { cancelled: turn.cancelled, ...error ? { error } : {} });
19127
+ for (const event of events)
19128
+ this.options.onEvent(event);
19129
+ if (outcome.status !== "failed" || outcome.text)
19130
+ this.preface = "";
19131
+ this.remember(turn.prompt, outcome.text, turn.steers).finally(() => turn.resolve({ status: outcome.status, summary: outcome.summary }));
19132
+ }
19133
+ async remember(prompt, reply, steers = []) {
19134
+ const record2 = this.record;
19135
+ record2.messages = [
19136
+ ...record2.messages,
19137
+ { role: "user", content: prompt },
19138
+ ...steers.map((text) => ({ role: "user", content: `[Sent while you were working] ${text}` })),
19139
+ ...reply.trim() ? [{ role: "assistant", content: reply }] : []
19140
+ ];
19141
+ if (record2.title === "Untitled session")
19142
+ record2.title = titleFromObjective(prompt);
19143
+ record2.checkpoints = this.checkpoints.list();
19144
+ markSeen(record2, CODEX_PROVIDER);
19145
+ record2.mode = this.mode;
19146
+ const save = this.options.save ?? saveSession;
19147
+ try {
19148
+ await save(record2);
19149
+ } catch (failure) {
19150
+ const stored = /revision conflict/i.test(message2(failure)) ? await loadSession(record2.root, record2.id).catch(() => null) : null;
19151
+ if (stored) {
19152
+ record2.revision = stored.revision;
19153
+ await save(record2).catch((retry) => this.options.warn?.(`archymedes: could not save the session: ${message2(retry)}`));
19154
+ } else
19155
+ this.options.warn?.(`archymedes: could not save the session: ${message2(failure)}`);
19156
+ }
19157
+ }
19158
+ async answer(method, params) {
19159
+ switch (method) {
19160
+ case "item/commandExecution/requestApproval": {
19161
+ const command = unwrapShell(String(params.command ?? ""));
19162
+ const decision = await this.ask("run_command", String(params.itemId ?? params.approvalId ?? randomUUID9()), { command }, params.reason);
19163
+ return { decision: decision === "allow" ? "accept" : decision === "allow_always" || decision === "allow_pattern" ? "acceptForSession" : "decline" };
19164
+ }
19165
+ case "item/fileChange/requestApproval": {
19166
+ const itemId = String(params.itemId ?? randomUUID9());
19167
+ const decision = await this.ask("edit_file", itemId, { path: this.changeTarget(this.translator.pathsOf(itemId)) }, params.reason);
19168
+ return { decision: decision === "allow" ? "accept" : decision === "allow_always" || decision === "allow_pattern" ? "acceptForSession" : "decline" };
19169
+ }
19170
+ case "execCommandApproval": {
19171
+ const argv = Array.isArray(params.command) ? params.command.map(String) : [String(params.command ?? "")];
19172
+ const decision = await this.ask("run_command", String(params.callId ?? randomUUID9()), { command: unwrapShell(argv.join(" ")) }, params.reason);
19173
+ return { decision: decision === "allow" ? "approved" : decision === "allow_always" || decision === "allow_pattern" ? "approved_for_session" : { denied: { rejection: "The person refused this." } } };
19174
+ }
19175
+ case "applyPatchApproval": {
19176
+ const decision = await this.ask("edit_file", String(params.callId ?? randomUUID9()), { path: this.changeTarget(Object.keys(asRecord(params.fileChanges)).map((file) => path31.relative(this.options.root, file) || file)) }, params.reason);
19177
+ return { decision: decision === "allow" ? "approved" : decision === "allow_always" || decision === "allow_pattern" ? "approved_for_session" : { denied: { rejection: "The person refused this." } } };
19178
+ }
19179
+ case "mcpServer/elicitation/request":
19180
+ return { action: "decline", content: null };
19181
+ default:
19182
+ throw new Error(`Archymedes does not answer ${method}; change the mode to give Codex more room.`);
19183
+ }
19184
+ }
19185
+ changeTarget(paths) {
19186
+ if (paths.length === 0)
19187
+ return "files in the project";
19188
+ return paths.length === 1 ? paths[0] : `${paths[0]} and ${paths.length - 1} more`;
19189
+ }
19190
+ async ask(toolName, toolCallId, args, reason) {
19191
+ if (this.refused.has(toolName))
19192
+ return "deny";
19193
+ const why = typeof reason === "string" && reason.trim() ? ` — ${reason.trim()}` : "";
19194
+ const decision = await this.options.approve({ toolName, toolCallId, summary: `${toolCallTitle(toolName, args)}${why}` });
19195
+ if (decision === "deny_always")
19196
+ this.refused.add(toolName);
19197
+ return decision;
19198
+ }
19199
+ async undo(argument) {
19200
+ if (argument === "conversation")
19201
+ return { kind: "text", text: "Codex keeps its conversation; /undo code puts back the files it changed." };
19202
+ if (argument !== "" && argument !== "code")
19203
+ return { kind: "error", text: `/undo takes no argument, "code", or "conversation" — not "${argument}".` };
19204
+ const count4 = this.checkpoints.list().length;
19205
+ return count4 === 0 ? { kind: "text", text: "Nothing to undo." } : this.restore(count4 - 1);
19206
+ }
19207
+ async restore(index) {
19208
+ if (this.turn)
19209
+ return { kind: "error", text: "Wait for the turn to finish, or stop it, before undoing." };
19210
+ const checkpoint = this.checkpoints.at(index);
19211
+ if (!checkpoint)
19212
+ return { kind: "error", text: `No turn ${index} to rewind to.` };
19213
+ if (!await this.checkpoints.restore(checkpoint.tree))
19214
+ return { kind: "error", text: "The files could not be put back." };
19215
+ await this.checkpoints.dropFrom(index);
19216
+ this.record.checkpoints = this.checkpoints.list();
19217
+ return { kind: "text", text: `Put the files back to before "${checkpoint.label}". Codex keeps its conversation: tell it what changed.` };
19218
+ }
19219
+ }
19220
+ function newRecord2(root, mode) {
19221
+ const now = Date.now();
19222
+ return { schemaVersion: 2, revision: 0, id: newSessionId(), createdAt: now, updatedAt: now, root, title: "Untitled session", messages: [], mode, approvals: {}, totalRwf: 0 };
19223
+ }
19224
+ function message2(error) {
19225
+ return error instanceof Error ? codexErrorText(error.message) : String(error);
19226
+ }
19227
+ function tail(stderr) {
19228
+ const lines = stderr.trim().split(`
19229
+ `).filter(Boolean).slice(-3).join(" ");
19230
+ return lines ? ` ${lines}` : "";
19231
+ }
19232
+
19233
+ // src/app/jev.ts
19234
+ function jevOptionsFromEnvironment(environment) {
19235
+ if ((environment.ARCHYMEDES_JEV ?? "").trim().toLowerCase() === "off")
19236
+ return;
19237
+ const apiKey = environment.TYPESAFE_API_KEY?.trim();
19238
+ if (!apiKey)
19239
+ return;
19240
+ const model = environment.TYPESAFE_MODEL?.trim();
19241
+ return { apiKey, ...model ? { model } : {}, ...environment.ARCHYMEDES_JEV_REVIEW?.trim().toLowerCase() === "off" ? { review: false } : {} };
19242
+ }
19243
+
19244
+ // src/app/session-provider.ts
19245
+ async function resolveSessionProvider(environment, options) {
19246
+ if (options.provider || !options.resume)
19247
+ return resolveProvider(environment, options);
19248
+ const id = options.resume === "latest" ? (await listSessions(options.root, 1))[0]?.id : options.resume;
19249
+ const selection = id ? (await loadSession(options.root, id))?.modelSelection : undefined;
19250
+ return resolveProvider(environment, selection?.provider === "free" ? { provider: "free", model: options.model ?? selection.model } : options);
19251
+ }
19252
+
19253
+ // src/app/acp-session.ts
19254
+ async function openSession(context, resumeSessionId) {
19255
+ let record2;
19256
+ if (resumeSessionId) {
19257
+ const id = resumeSessionId === "latest" ? (await listSessions(context.root, 1))[0]?.id : resumeSessionId;
19258
+ record2 = id ? await loadSession(context.root, id) ?? undefined : undefined;
19259
+ if (!record2)
19260
+ throw new Error(resumeSessionId === "latest" ? `No earlier session in ${context.root}` : `No stored session ${resumeSessionId} in ${context.root}`);
19261
+ }
19262
+ const mode = context.mode;
19263
+ const kind = startingEngine(context, record2);
19264
+ const engine = kind === "claude" ? await (context.claude ?? claudeEngine)(context, record2, record2?.mode ?? mode, context.provider === CLAUDE_PROVIDER ? context.model : claudeModel(context.environment)) : kind === "codex" ? await (context.codex ?? codexEngine)(context, record2, record2?.mode ?? mode, context.provider === CODEX_PROVIDER ? context.model : codexModel(context.environment)) : await archymedesEngine(context, { record: record2, mode, provider: context.provider, model: context.model, resume: resumeSessionId });
19265
+ return new SwitchingSession(engine, context, mode);
19266
+ }
19267
+ function engineFor(provider) {
19268
+ return provider === CLAUDE_PROVIDER ? "claude" : provider === CODEX_PROVIDER ? "codex" : "archymedes";
19269
+ }
19270
+ function startingEngine(context, record2) {
19271
+ if (context.provider)
19272
+ return engineFor(context.provider);
19273
+ if (record2)
19274
+ return engineFor(record2.modelSelection?.provider);
19275
+ if (claudeIsDefault(context.environment))
19276
+ return "claude";
19277
+ return codexIsDefault(context.environment) ? "codex" : "archymedes";
19278
+ }
19279
+
19280
+ class SwitchingSession {
19281
+ engine;
19282
+ context;
19283
+ mode;
19284
+ constructor(engine, context, mode) {
19285
+ this.engine = engine;
19286
+ this.context = context;
19287
+ this.mode = mode;
19288
+ }
19289
+ get id() {
19290
+ return this.engine.id;
19291
+ }
19292
+ send(prompt) {
19293
+ return this.engine.send(prompt);
19294
+ }
19295
+ cancel() {
19296
+ this.engine.cancel();
19297
+ }
19298
+ dispose() {
19299
+ return this.engine.dispose();
19300
+ }
19301
+ history() {
19302
+ return this.engine.history?.() ?? [];
19303
+ }
19304
+ info() {
19305
+ return this.engine.info?.() ?? {};
19306
+ }
19307
+ steer(text) {
19308
+ return this.engine.steer?.(text) ?? false;
19309
+ }
19310
+ async command(command, argument) {
19311
+ if (command === "fork") {
19312
+ const fork = await forkSession(this.engine.snapshot());
19313
+ return { kind: "forked", id: fork.id, title: fork.title };
18181
19314
  }
18182
19315
  if (!this.engine.command)
18183
19316
  throw new Error("This session has no commands");
@@ -18188,15 +19321,15 @@ class SwitchingSession {
18188
19321
  this.mode = mode;
18189
19322
  }
18190
19323
  async setModel(provider, model) {
18191
- const toClaude = provider === CLAUDE_PROVIDER;
18192
- if (toClaude === (this.engine.kind === "claude")) {
19324
+ const target = engineFor(provider);
19325
+ if (target === this.engine.kind) {
18193
19326
  if (!this.engine.setModel)
18194
19327
  throw new Error("This session cannot switch models");
18195
19328
  await this.engine.setModel(provider, model);
18196
19329
  return;
18197
19330
  }
18198
19331
  const record2 = this.engine.snapshot();
18199
- const next = toClaude ? await (this.context.claude ?? claudeEngine)(this.context, record2, this.mode, model) : await archymedesEngine({ ...this.context, createWorkspace: undefined }, { record: record2, mode: this.mode, provider, model });
19332
+ const next = target === "claude" ? await (this.context.claude ?? claudeEngine)(this.context, record2, this.mode, model) : target === "codex" ? await (this.context.codex ?? codexEngine)(this.context, record2, this.mode, model) : await archymedesEngine({ ...this.context, createWorkspace: undefined }, { record: record2, mode: this.mode, provider, model });
18200
19333
  const previous = this.engine;
18201
19334
  this.engine = next;
18202
19335
  await previous.dispose().catch((error) => this.context.warn?.(`archymedes: retiring the previous engine: ${error instanceof Error ? error.message : String(error)}`));
@@ -18205,10 +19338,10 @@ class SwitchingSession {
18205
19338
  async function claudeEngine(context, record2, mode, model) {
18206
19339
  if (context.createWorkspace)
18207
19340
  throw new Error("Claude runs Claude Code on this machine; it cannot work in a --sandbox workspace.");
18208
- const runtime2 = await claudeRuntime({ environment: context.environment, processEnvironment: process.env });
18209
- if ("error" in runtime2)
18210
- throw new Error(runtime2.error);
18211
- const { signedIn, ...start } = runtime2;
19341
+ const runtime3 = await claudeRuntime({ environment: context.environment, processEnvironment: process.env });
19342
+ if ("error" in runtime3)
19343
+ throw new Error(runtime3.error);
19344
+ const { signedIn, ...start } = runtime3;
18212
19345
  const session = new ClaudeSession({
18213
19346
  root: context.root,
18214
19347
  ...start,
@@ -18231,6 +19364,27 @@ async function claudeEngine(context, record2, mode, model) {
18231
19364
  throw failed;
18232
19365
  return session;
18233
19366
  }
19367
+ async function codexEngine(context, record2, mode, model) {
19368
+ if (context.createWorkspace)
19369
+ throw new Error("Codex runs on this machine in its own sandbox; it cannot work in a --sandbox workspace.");
19370
+ const readiness = await codexReadiness({ environment: context.environment, processEnvironment: process.env });
19371
+ if (!readiness.ready)
19372
+ throw new Error(`Codex is not ready: it needs ${readiness.missing}. Open Providers (alt+p) and choose Codex.`);
19373
+ const session = new CodexSession({
19374
+ root: context.root,
19375
+ executable: readiness.executable,
19376
+ environment: codexEnvironment(process.env),
19377
+ mode,
19378
+ record: record2,
19379
+ ...model && model !== "default" ? { model } : {},
19380
+ onEvent: context.onEvent,
19381
+ approve: context.approve,
19382
+ warn: context.warn,
19383
+ onModels: learnCodexModels
19384
+ });
19385
+ await session.open();
19386
+ return session;
19387
+ }
18234
19388
  async function archymedesEngine(context, start) {
18235
19389
  const { root, environment, onEvent, approve } = context;
18236
19390
  const first = await resolveSessionProvider(environment, { root, resume: start.resume, provider: start.provider, model: start.model });
@@ -18324,7 +19478,7 @@ function priceCatalogFor(prices) {
18324
19478
  };
18325
19479
  }
18326
19480
 
18327
- // packages/archymedes-cli/src/app/claude/listing.ts
19481
+ // src/app/claude/listing.ts
18328
19482
  function withClaude(providers, claude) {
18329
19483
  const free = providers.findIndex((provider) => provider.id === "free");
18330
19484
  return [...providers.slice(0, free + 1), claude, ...providers.slice(free + 1)];
@@ -18361,12 +19515,12 @@ async function claudeSetupStatus(status, requested, deps) {
18361
19515
  return { ...status, configured: false, reason: `Claude needs ${readiness.missing}.` };
18362
19516
  }
18363
19517
 
18364
- // packages/archymedes-cli/src/app/startup-update.ts
18365
- import { spawn as spawn9 } from "node:child_process";
19518
+ // src/app/startup-update.ts
19519
+ import { spawn as spawn10 } from "node:child_process";
18366
19520
 
18367
- // packages/archymedes-cli/src/platform/auto-update.ts
19521
+ // src/platform/auto-update.ts
18368
19522
  import { promises as fs19 } from "node:fs";
18369
- import path29 from "node:path";
19523
+ import path32 from "node:path";
18370
19524
  var AUTO_UPDATE_INTERVAL_MS = 24 * 60 * 60 * 1000;
18371
19525
  function isContinuousIntegration(environment) {
18372
19526
  return ["CI", "CONTINUOUS_INTEGRATION", "GITHUB_ACTIONS", "GITLAB_CI", "BUILDKITE", "JENKINS_URL", "TEAMCITY_VERSION"].some((name) => Boolean(environment[name]?.trim()));
@@ -18411,7 +19565,7 @@ function isNewer(candidate, current) {
18411
19565
  return false;
18412
19566
  }
18413
19567
  function stateFile(environment) {
18414
- return path29.join(archymedesConfigDirectory(environment), "update-check.json");
19568
+ return path32.join(archymedesConfigDirectory(environment), "update-check.json");
18415
19569
  }
18416
19570
  async function readAutoUpdateState(environment) {
18417
19571
  try {
@@ -18430,7 +19584,7 @@ async function readAutoUpdateState(environment) {
18430
19584
  async function writeAutoUpdateState(environment, state) {
18431
19585
  try {
18432
19586
  const file = stateFile(environment);
18433
- await fs19.mkdir(path29.dirname(file), { recursive: true });
19587
+ await fs19.mkdir(path32.dirname(file), { recursive: true });
18434
19588
  await fs19.writeFile(file, `${JSON.stringify(state, null, 2)}
18435
19589
  `, "utf8");
18436
19590
  } catch {}
@@ -18477,7 +19631,7 @@ async function runAutoUpdate(options) {
18477
19631
  return { notice: [], decision };
18478
19632
  }
18479
19633
 
18480
- // packages/archymedes-cli/src/platform/defender-feed-update.ts
19634
+ // src/platform/defender-feed-update.ts
18481
19635
  var OFFICIAL_DEFENDER_FEED_URL = "https://circuit-archymedes.vercel.app/api/defender-brain/manifest";
18482
19636
  var OFFICIAL_DEFENDER_FEED_KEYS = Object.freeze({
18483
19637
  "release-2026-01": "MCowBQYDK2VwAyEA4/jb1zd6f+jIPAFja1bPNtroXV8MtAZFrKt5BSY+ngI="
@@ -18505,12 +19659,12 @@ async function updateDefenderFeed(environment) {
18505
19659
  });
18506
19660
  }
18507
19661
 
18508
- // packages/archymedes-cli/src/platform/update.ts
19662
+ // src/platform/update.ts
18509
19663
  import { spawnSync as spawnSync4 } from "node:child_process";
18510
19664
  import { tmpdir } from "node:os";
18511
19665
  import { createInterface as createInterface3 } from "node:readline/promises";
18512
19666
 
18513
- // packages/archymedes-cli/src/catalog/commands.ts
19667
+ // src/catalog/commands.ts
18514
19668
  function defineCommands(table) {
18515
19669
  return Object.entries(table).map(([name, command]) => ({ name, ...command }));
18516
19670
  }
@@ -18583,7 +19737,7 @@ function renderCommandHelp(language = "en", shortcuts = new Map, options = {}) {
18583
19737
  `);
18584
19738
  }
18585
19739
 
18586
- // packages/archymedes-cli/src/terminal/keybindings.ts
19740
+ // src/terminal/keybindings.ts
18587
19741
  var FUNCTION_KEY = /^f([1-9]|1[0-2])$/;
18588
19742
  var NAMED_KEYS = new Set(["tab", "enter", "escape", "space", "up", "down", "left", "right", "home", "end", "pageup", "pagedown", "backspace", "delete"]);
18589
19743
  function isKeyName(value) {
@@ -18833,10 +19987,10 @@ function escapeCodeTimeoutMs(environment = process.env) {
18833
19987
  const parsed = raw ? Number(raw) : NaN;
18834
19988
  return Number.isInteger(parsed) && parsed >= 10 && parsed <= 2000 ? parsed : DEFAULT_ESCAPE_CODE_TIMEOUT_MS;
18835
19989
  }
18836
- // packages/archymedes-cli/package.json
19990
+ // package.json
18837
19991
  var package_default = {
18838
19992
  name: "archymedes-cli",
18839
- version: "3.6.0",
19993
+ version: "3.7.0",
18840
19994
  description: "Archymedes CLI — a coding agent that shows its work. Runs against your working tree or an isolated remote sandbox, with guarded approvals, git checkpoints, and exact, measured cost accounting.",
18841
19995
  keywords: [
18842
19996
  "cli",
@@ -18882,12 +20036,12 @@ var package_default = {
18882
20036
  prepublishOnly: "bun run ../../tooling/build/build-packages.ts"
18883
20037
  },
18884
20038
  optionalDependencies: {
18885
- "@archymedes/cli-darwin-arm64": "3.6.0",
18886
- "@archymedes/cli-darwin-x64": "3.6.0",
18887
- "@archymedes/cli-linux-arm64": "3.6.0",
18888
- "@archymedes/cli-linux-x64": "3.6.0",
18889
- "@archymedes/cli-win32-arm64": "3.6.0",
18890
- "@archymedes/cli-win32-x64": "3.6.0",
20039
+ "@archymedes/cli-darwin-arm64": "3.7.0",
20040
+ "@archymedes/cli-darwin-x64": "3.7.0",
20041
+ "@archymedes/cli-linux-arm64": "3.7.0",
20042
+ "@archymedes/cli-linux-x64": "3.7.0",
20043
+ "@archymedes/cli-win32-arm64": "3.7.0",
20044
+ "@archymedes/cli-win32-x64": "3.7.0",
18891
20045
  "@archymedes/state-darwin-arm64": "0.4.0",
18892
20046
  "@archymedes/state-darwin-x64": "0.4.0",
18893
20047
  "@archymedes/state-linux-arm64-gnu": "0.4.0",
@@ -18899,7 +20053,7 @@ var package_default = {
18899
20053
  }
18900
20054
  };
18901
20055
 
18902
- // packages/archymedes-cli/src/platform/update.ts
20056
+ // src/platform/update.ts
18903
20057
  var ARCHYMEDES_CLI_PACKAGE = "archymedes-cli";
18904
20058
  var ARCHYMEDES_CLI_VERSION = package_default.version;
18905
20059
  var DEFAULT_REGISTRY = "https://registry.npmjs.org";
@@ -19090,10 +20244,10 @@ async function runSelfUpdate(options = {}) {
19090
20244
  }
19091
20245
  stdout(`Updating Archymedes ${currentVersion} → ${latestVersion} with ${manager}…
19092
20246
  `);
19093
- const spawn9 = options.spawnImpl ?? ((executable2, args, spawnOptions) => spawnSync4(executable2, args, spawnOptions));
20247
+ const spawn10 = options.spawnImpl ?? ((executable2, args, spawnOptions) => spawnSync4(executable2, args, spawnOptions));
19094
20248
  let result;
19095
20249
  try {
19096
- result = spawn9(command.command, command.args, {
20250
+ result = spawn10(command.command, command.args, {
19097
20251
  cwd: tmpdir(),
19098
20252
  env: { ...process.env, ...environment },
19099
20253
  stdio: "inherit",
@@ -19117,7 +20271,7 @@ async function runSelfUpdate(options = {}) {
19117
20271
  return { status: "updated", currentVersion, latestVersion, code: 0 };
19118
20272
  }
19119
20273
 
19120
- // packages/archymedes-cli/src/text/ansi.ts
20274
+ // src/text/ansi.ts
19121
20275
  var RESET = "\x1B[0m";
19122
20276
  var BOLD = "\x1B[1m";
19123
20277
  var DIM = "\x1B[2m";
@@ -19130,7 +20284,7 @@ function paintAll(text, codes, depth) {
19130
20284
  return depth === "none" || codes.length === 0 ? text : `${codes.join("")}${text}${RESET}`;
19131
20285
  }
19132
20286
 
19133
- // packages/archymedes-cli/src/terminal/output.ts
20287
+ // src/terminal/output.ts
19134
20288
  var terminalStream = {
19135
20289
  write(text) {
19136
20290
  process.stdout.write(text);
@@ -19164,7 +20318,7 @@ class OutputRouter {
19164
20318
  }
19165
20319
  }
19166
20320
 
19167
- // packages/archymedes-cli/src/text/glyphs.ts
20321
+ // src/text/glyphs.ts
19168
20322
  var UNICODE_GLYPHS = {
19169
20323
  starGlyphs: [".", "·", "✧", "✦", "✶"],
19170
20324
  spinnerFrames: [" ◜ ", " ◠ ", " ◝ ", " ◞ ", " ◡ ", " ◟ "],
@@ -19271,7 +20425,7 @@ function borderGlyphsFor(style, glyphs) {
19271
20425
  return ROUND_BORDER;
19272
20426
  }
19273
20427
 
19274
- // packages/archymedes-cli/src/text/text-width.ts
20428
+ // src/text/text-width.ts
19275
20429
  var ANSI = /\x1b\[[0-9;]*m/g;
19276
20430
  var PRINTABLE_ASCII = /^[\x20-\x7e]*$/;
19277
20431
  function visibleWidth(text) {
@@ -19306,7 +20460,7 @@ function clipTo(text, width, glyphs = UNICODE_GLYPHS) {
19306
20460
  return `${out}${mark}`;
19307
20461
  }
19308
20462
 
19309
- // packages/archymedes-cli/src/render/syntax.ts
20463
+ // src/render/syntax.ts
19310
20464
  function newSyntaxState() {
19311
20465
  return {};
19312
20466
  }
@@ -19921,7 +21075,7 @@ function highlightSyntax(line, options) {
19921
21075
  const changedAt = [];
19922
21076
  if (options.segments)
19923
21077
  for (const segment of options.segments)
19924
- for (let count3 = 0;count3 < segment.text.length; count3 += 1)
21078
+ for (let count4 = 0;count4 < segment.text.length; count4 += 1)
19925
21079
  changedAt.push(segment.changed);
19926
21080
  let out = lineBackground;
19927
21081
  let offset = 0;
@@ -19953,7 +21107,7 @@ function highlightSyntax(line, options) {
19953
21107
  return `${out}${RESET}`;
19954
21108
  }
19955
21109
 
19956
- // packages/archymedes-cli/src/render/markdown.ts
21110
+ // src/render/markdown.ts
19957
21111
  var colour = (role, palette) => palette?.[role] || ANSI_PALETTE[role];
19958
21112
  var INLINE_PATTERN = /(?<!\\)(`[^`\n]+`)|(?<!\\)(\*\*[^*\n]+\*\*)|(?<!\\)(__[^_\n]+__)|(?<!\\)(~~[^~\n]+~~)|(?<!\\)(\*[^*\n]+\*)|(?<!\\)(_[^_\n]+_)|(!?\[([^\]\n]+)\]\((https?:\/\/[^\s)\n]+)\))|(<(https?:\/\/[^>\s]+)>)/g;
19959
21113
  var unescapeMarkdown = (text) => text.replace(/\\([\\`*_[\]{}()#+.!~>-])/g, "$1");
@@ -20174,7 +21328,7 @@ function renderMarkdown(text, options) {
20174
21328
  `);
20175
21329
  }
20176
21330
 
20177
- // packages/archymedes-cli/src/render/tui.ts
21331
+ // src/render/tui.ts
20178
21332
  function archymedesSpinnerFrame(index, glyphs = UNICODE_GLYPHS) {
20179
21333
  const frames = glyphs.spinnerFrames;
20180
21334
  const normalized = Number.isFinite(index) ? Math.max(0, Math.floor(index)) : 0;
@@ -20415,10 +21569,10 @@ function table(columns, rows, options) {
20415
21569
  const glyphs = options.glyphs ?? UNICODE_GLYPHS;
20416
21570
  const border = borderGlyphsFor(options.borderStyle ?? "round", glyphs);
20417
21571
  const terminalWidth = Math.max(1, options.width ?? process.stdout.columns ?? 80);
20418
- const count3 = columns.length;
21572
+ const count4 = columns.length;
20419
21573
  const widths = columns.map((header, index) => Math.max(visibleWidth(header), ...rows.map((row2) => visibleWidth(row2[index] ?? ""))));
20420
- const budget = terminalWidth - (count3 + 1) - count3 * 2;
20421
- while (widths.reduce((sum, width) => sum + width, 0) > Math.max(count3, budget)) {
21574
+ const budget = terminalWidth - (count4 + 1) - count4 * 2;
21575
+ while (widths.reduce((sum, width) => sum + width, 0) > Math.max(count4, budget)) {
20422
21576
  const widest = widths.indexOf(Math.max(...widths));
20423
21577
  if (widths[widest] <= 3)
20424
21578
  break;
@@ -20440,13 +21594,13 @@ function table(columns, rows, options) {
20440
21594
  `);
20441
21595
  }
20442
21596
  function paginator(current, total, options = {}) {
20443
- const count3 = Math.max(1, Math.floor(total));
20444
- const index = Math.max(0, Math.min(Math.floor(current), count3 - 1));
21597
+ const count4 = Math.max(1, Math.floor(total));
21598
+ const index = Math.max(0, Math.min(Math.floor(current), count4 - 1));
20445
21599
  if (options.style === "dots") {
20446
21600
  const glyphs = options.glyphs ?? UNICODE_GLYPHS;
20447
- return Array.from({ length: count3 }, (_, position) => position === index ? glyphs.bullet : glyphs.middot).join(" ");
21601
+ return Array.from({ length: count4 }, (_, position) => position === index ? glyphs.bullet : glyphs.middot).join(" ");
20448
21602
  }
20449
- return `${index + 1}/${count3}`;
21603
+ return `${index + 1}/${count4}`;
20450
21604
  }
20451
21605
  var VERBS = ["Thinking", "Reasoning", "Tracing", "Considering"];
20452
21606
  function thinkingVerb(elapsedMs) {
@@ -20762,7 +21916,7 @@ function wrapPlain(text, width) {
20762
21916
  return lines.length === 0 ? [""] : lines;
20763
21917
  }
20764
21918
 
20765
- // packages/archymedes-cli/src/render/transcript.ts
21919
+ // src/render/transcript.ts
20766
21920
  function truncateMiddle(value, maximum) {
20767
21921
  const clean = value.replace(/\s+/g, " ").trim();
20768
21922
  if (clean.length <= maximum)
@@ -20770,8 +21924,8 @@ function truncateMiddle(value, maximum) {
20770
21924
  if (maximum <= 1)
20771
21925
  return clean.slice(0, Math.max(0, maximum));
20772
21926
  const head = Math.ceil((maximum - 1) / 2);
20773
- const tail = Math.floor((maximum - 1) / 2);
20774
- return `${clean.slice(0, head)}…${tail > 0 ? clean.slice(-tail) : ""}`;
21927
+ const tail2 = Math.floor((maximum - 1) / 2);
21928
+ return `${clean.slice(0, head)}…${tail2 > 0 ? clean.slice(-tail2) : ""}`;
20775
21929
  }
20776
21930
  function asString(value) {
20777
21931
  return typeof value === "string" && value.trim() !== "" ? value.trim() : undefined;
@@ -20782,12 +21936,12 @@ function countOf(value) {
20782
21936
  function describeToolCall2(toolName, args) {
20783
21937
  switch (toolName) {
20784
21938
  case "read_file": {
20785
- const path30 = asString(args.path) ?? "";
21939
+ const path33 = asString(args.path) ?? "";
20786
21940
  const offset = typeof args.offset === "number" ? args.offset : undefined;
20787
21941
  const limit2 = typeof args.limit === "number" ? args.limit : undefined;
20788
21942
  if (offset === undefined)
20789
- return path30;
20790
- return limit2 === undefined ? `${path30}:${offset}+` : `${path30}:${offset}-${offset + limit2 - 1}`;
21943
+ return path33;
21944
+ return limit2 === undefined ? `${path33}:${offset}+` : `${path33}:${offset}-${offset + limit2 - 1}`;
20791
21945
  }
20792
21946
  case "write_file":
20793
21947
  case "edit_file":
@@ -20889,8 +22043,8 @@ function summarizeToolResult(toolName, content, isError) {
20889
22043
  case "scan_secrets": {
20890
22044
  if (/^No likely secrets/i.test(body))
20891
22045
  return "clean";
20892
- const count3 = /^(\d+) possible secret/.exec(body);
20893
- return count3 ? `${count3[1]} possible secret${count3[1] === "1" ? "" : "s"}` : "findings";
22046
+ const count4 = /^(\d+) possible secret/.exec(body);
22047
+ return count4 ? `${count4[1]} possible secret${count4[1] === "1" ? "" : "s"}` : "findings";
20894
22048
  }
20895
22049
  case "glob_files": {
20896
22050
  if (/^No files matched/i.test(body))
@@ -20925,7 +22079,7 @@ function summarizeToolResult(toolName, content, isError) {
20925
22079
  }
20926
22080
  }
20927
22081
 
20928
- // packages/archymedes-cli/src/render/sections.ts
22082
+ // src/render/sections.ts
20929
22083
  var TONE_ROLE = {
20930
22084
  good: "success",
20931
22085
  bad: "error",
@@ -21052,7 +22206,7 @@ function outcomeMark(outcome, style) {
21052
22206
  }
21053
22207
  }
21054
22208
 
21055
- // packages/archymedes-cli/src/render/code-view.ts
22209
+ // src/render/code-view.ts
21056
22210
  var MAX_DIFF_CELLS = 250000;
21057
22211
  function diffLines(before, after) {
21058
22212
  const left = before === "" ? [] : before.split(`
@@ -21349,7 +22503,7 @@ function renderFileChange(change, style, options = {}) {
21349
22503
  };
21350
22504
  }
21351
22505
 
21352
- // packages/archymedes-cli/src/render/test-report.ts
22506
+ // src/render/test-report.ts
21353
22507
  function toMs(value) {
21354
22508
  if (!value)
21355
22509
  return;
@@ -21389,8 +22543,8 @@ function parseJavaScript(lines) {
21389
22543
  currentSuite = file[1];
21390
22544
  const failed = /^[\s|]*(?:×|✕|✗|❯|FAIL)/.test(line);
21391
22545
  if (file[2]) {
21392
- const count3 = Number(file[2]);
21393
- for (let index = 0;index < count3; index += 1) {
22546
+ const count4 = Number(file[2]);
22547
+ for (let index = 0;index < count4; index += 1) {
21394
22548
  cases.push({ name: `${file[1]} #${index + 1}`, outcome: failed ? "fail" : "pass", suite: file[1] });
21395
22549
  }
21396
22550
  } else {
@@ -21624,7 +22778,7 @@ function renderTestReport(report, style, options = {}) {
21624
22778
  `);
21625
22779
  }
21626
22780
 
21627
- // packages/archymedes-cli/src/render/expandable.ts
22781
+ // src/render/expandable.ts
21628
22782
  var MAX_ENTRIES = 64;
21629
22783
 
21630
22784
  class ExpandableStore {
@@ -21658,7 +22812,7 @@ function expandHint(id, hidden, depth, glyphs = UNICODE_GLYPHS) {
21658
22812
  return paint(`${glyphs.collapsed} ${hidden} more line${hidden === 1 ? "" : "s"} hidden ${glyphs.middot} /expand ${id}`, DIM, depth);
21659
22813
  }
21660
22814
 
21661
- // packages/archymedes-cli/src/app/transcript.ts
22815
+ // src/app/transcript.ts
21662
22816
  var colorEnabled = false;
21663
22817
  var wrap = (code) => (value) => colorEnabled ? `${code}${value}${RESET}` : value;
21664
22818
  var palette = NO_COLOR_PALETTE;
@@ -21738,7 +22892,7 @@ function beginTranscriptTurn() {
21738
22892
  markdown.reset();
21739
22893
  }
21740
22894
 
21741
- // packages/archymedes-cli/src/app/startup-update.ts
22895
+ // src/app/startup-update.ts
21742
22896
  async function startupMaintenance(environment, interactive, install) {
21743
22897
  updateDefenderFeed(environment).catch(() => {
21744
22898
  return;
@@ -21768,7 +22922,7 @@ async function startupMaintenance(environment, interactive, install) {
21768
22922
  }
21769
22923
  function installInChildProcess(entry, environment) {
21770
22924
  return (version) => new Promise((resolve) => {
21771
- const child = spawn9(entry.execPath, [entry.script, "update", "--yes"], {
22925
+ const child = spawn10(entry.execPath, [entry.script, "update", "--yes"], {
21772
22926
  env: { ...process.env, ...environment, NO_COLOR: "1" },
21773
22927
  stdio: ["ignore", "ignore", "ignore"]
21774
22928
  });
@@ -21777,7 +22931,7 @@ function installInChildProcess(entry, environment) {
21777
22931
  });
21778
22932
  }
21779
22933
 
21780
- // packages/archymedes-cli/src/acp-server.ts
22934
+ // src/acp-server.ts
21781
22935
  function splitFrames(buffer) {
21782
22936
  const parts = buffer.split(`
21783
22937
  `);
@@ -21785,24 +22939,37 @@ function splitFrames(buffer) {
21785
22939
  return { messages: parts.map((line) => line.trim()).filter((line) => line.length > 0), rest };
21786
22940
  }
21787
22941
  async function runAcpServer(options) {
21788
- const warn = options.warn ?? ((message2) => process.stderr.write(`${message2}
22942
+ const warn = options.warn ?? ((message3) => process.stderr.write(`${message3}
21789
22943
  `));
21790
- const send = (message2) => options.write(`${JSON.stringify(message2)}
22944
+ const send = (message3) => options.write(`${JSON.stringify(message3)}
21791
22945
  `);
21792
22946
  let startup;
21793
22947
  const claude = { environment: options.environment, processEnvironment: process.env };
21794
22948
  const connection = new AcpConnection({
21795
22949
  send,
21796
22950
  extensions: {
21797
- "_archymedes/providers": async () => ({ providers: withClaude(providerOverview(options.environment), await claudeOverview(claude)) }),
21798
- "_archymedes/providers/connect": async (params) => params.provider === CLAUDE_PROVIDER ? connectClaude(claude) : connectProvider(params, { environment: options.environment, processEnvironment: process.env }),
21799
- "_archymedes/providers/models": async (params) => params.provider === CLAUDE_PROVIDER ? { models: claudeModels() } : providerModels(params, { environment: options.environment }),
21800
- "_archymedes/providers/remember-model": async (params) => params.provider === CLAUDE_PROVIDER ? rememberClaudeModel(typeof params.model === "string" ? params.model : "", claude) : rememberModel(params, { environment: options.environment, processEnvironment: process.env }),
21801
- "_archymedes/setup/status": async () => claudeSetupStatus(setupStatus(options.environment, { provider: options.provider, model: options.model }), options, claude),
22951
+ "_archymedes/providers": async () => {
22952
+ const [claudeRow, codexRow] = await Promise.all([claudeOverview(claude), codexOverview(claude)]);
22953
+ return { providers: withCodex(withClaude(providerOverview(options.environment), claudeRow), codexRow) };
22954
+ },
22955
+ "_archymedes/providers/connect": async (params) => params.provider === CLAUDE_PROVIDER ? connectClaude(claude) : params.provider === CODEX_PROVIDER ? connectCodex(claude) : connectProvider(params, { environment: options.environment, processEnvironment: process.env }),
22956
+ "_archymedes/providers/models": async (params) => params.provider === CLAUDE_PROVIDER ? { models: claudeModels() } : params.provider === CODEX_PROVIDER ? { models: codexModels() } : providerModels(params, { environment: options.environment }),
22957
+ "_archymedes/providers/remember-model": async (params) => {
22958
+ const model = typeof params.model === "string" ? params.model : "";
22959
+ if (params.provider === CLAUDE_PROVIDER)
22960
+ return rememberClaudeModel(model, claude);
22961
+ if (params.provider === CODEX_PROVIDER)
22962
+ return rememberCodexModel(model, claude);
22963
+ return rememberModel(params, { environment: options.environment, processEnvironment: process.env });
22964
+ },
22965
+ "_archymedes/setup/status": async () => codexSetupStatus(await claudeSetupStatus(setupStatus(options.environment, { provider: options.provider, model: options.model }), options, claude), options, claude),
21802
22966
  "_archymedes/setup/save": async (params) => saveSetup(params, { environment: options.environment, processEnvironment: process.env }),
21803
22967
  "_archymedes/settings/list": async () => listSettings(process.env),
21804
22968
  "_archymedes/settings/set": async (params) => setSetting(params, { environment: options.environment, processEnvironment: process.env }),
21805
- "_archymedes/models": async (params) => withClaudeModels(await modelList(options.environment, params), await claudeOverview(claude)),
22969
+ "_archymedes/models": async (params) => {
22970
+ const [listing, claudeRow, codexRow] = await Promise.all([modelList(options.environment, params), claudeOverview(claude), codexOverview(claude)]);
22971
+ return withCodexModels(withClaudeModels(listing, claudeRow), codexRow);
22972
+ },
21806
22973
  "_archymedes/startup": async () => ({
21807
22974
  notices: await (startup ??= startupMaintenance(options.environment, false, installInChildProcess({ execPath: process.execPath, script: process.argv[1] ?? "" }, options.environment)))
21808
22975
  }),
@@ -21821,7 +22988,7 @@ async function runAcpServer(options) {
21821
22988
  }, resumeSessionId)
21822
22989
  });
21823
22990
  try {
21824
- const { drain } = await pumpMessages(options.input, (message2) => connection.receive(message2), warn);
22991
+ const { drain } = await pumpMessages(options.input, (message3) => connection.receive(message3), warn);
21825
22992
  connection.endOfInput();
21826
22993
  await drain();
21827
22994
  return 0;
@@ -21860,14 +23027,14 @@ async function pumpMessages(input, receive, warn) {
21860
23027
  }
21861
23028
  };
21862
23029
  }
21863
- function isPrompt(message2) {
21864
- return typeof message2 === "object" && message2 !== null && message2.method === "session/prompt";
23030
+ function isPrompt(message3) {
23031
+ return typeof message3 === "object" && message3 !== null && message3.method === "session/prompt";
21865
23032
  }
21866
23033
 
21867
- // packages/archymedes-cli/src/app/args.ts
21868
- import path30 from "node:path";
23034
+ // src/app/args.ts
23035
+ import path33 from "node:path";
21869
23036
 
21870
- // packages/archymedes-cli/src/render/charts.ts
23037
+ // src/render/charts.ts
21871
23038
  function barChart(data, options) {
21872
23039
  if (data.length === 0)
21873
23040
  return [];
@@ -21892,9 +23059,9 @@ function barChart(data, options) {
21892
23059
  });
21893
23060
  }
21894
23061
 
21895
- // packages/archymedes-cli/src/commands/chat-history.ts
23062
+ // src/commands/chat-history.ts
21896
23063
  function countTurns(messages) {
21897
- return messages.filter((message2) => message2.role === "user" && !message2.internal).length;
23064
+ return messages.filter((message3) => message3.role === "user" && !message3.internal).length;
21898
23065
  }
21899
23066
  function relativeTime(timestamp, now = Date.now()) {
21900
23067
  const seconds = Math.max(0, Math.round((now - timestamp) / 1000));
@@ -21913,15 +23080,15 @@ function relativeTime(timestamp, now = Date.now()) {
21913
23080
  return months < 12 ? `${months}mo ago` : `${Math.round(months / 12)}y ago`;
21914
23081
  }
21915
23082
  function summarizeSession(record2) {
21916
- const searchText = record2.messages.flatMap((message2) => {
21917
- if (message2.role === "system" || message2.internal)
23083
+ const searchText = record2.messages.flatMap((message3) => {
23084
+ if (message3.role === "system" || message3.internal)
21918
23085
  return [];
21919
- if (message2.role === "tool")
21920
- return [message2.name, message2.content];
21921
- if (message2.role === "assistant" && "toolCalls" in message2) {
21922
- return [message2.content, ...message2.toolCalls.flatMap((call) => [call.name, JSON.stringify(call.arguments ?? {})])];
23086
+ if (message3.role === "tool")
23087
+ return [message3.name, message3.content];
23088
+ if (message3.role === "assistant" && "toolCalls" in message3) {
23089
+ return [message3.content, ...message3.toolCalls.flatMap((call) => [call.name, JSON.stringify(call.arguments ?? {})])];
21923
23090
  }
21924
- return [message2.content];
23091
+ return [message3.content];
21925
23092
  }).join(`
21926
23093
  `).toLowerCase();
21927
23094
  return {
@@ -22005,37 +23172,37 @@ function renderReplay(record2, style2, options = {}) {
22005
23172
  const out2 = [];
22006
23173
  out2.push(heading(record2.title || record2.id, 1, style2));
22007
23174
  out2.push(`${GUTTER}${paint(`${record2.id} ${glyphs2.middot} ${countTurns(record2.messages)} turns ${glyphs2.middot} ${relativeTime(record2.updatedAt)}`, DIM, style2.depth)}`);
22008
- const boundaries = record2.messages.flatMap((message2, index) => message2.role === "user" && !message2.internal ? [index] : []);
23175
+ const boundaries = record2.messages.flatMap((message3, index) => message3.role === "user" && !message3.internal ? [index] : []);
22009
23176
  const from = options.turns !== undefined && boundaries.length > options.turns ? boundaries[boundaries.length - options.turns] : 0;
22010
23177
  if (from > 0)
22011
23178
  out2.push(`${GUTTER}${paint(`${glyphs2.middot.repeat(3)} earlier turns omitted`, DIM, style2.depth)}`);
22012
23179
  let turn = 0;
22013
- for (const message2 of record2.messages.slice(from)) {
22014
- if (message2.role === "system" || message2.internal)
23180
+ for (const message3 of record2.messages.slice(from)) {
23181
+ if (message3.role === "system" || message3.internal)
22015
23182
  continue;
22016
- if (message2.role === "user") {
23183
+ if (message3.role === "user") {
22017
23184
  turn += 1;
22018
23185
  out2.push(rule(style2, { label: `turn ${turn}`, tone: "accent" }));
22019
- out2.push(message2.content.split(`
23186
+ out2.push(message3.content.split(`
22020
23187
  `).map((line) => `${GUTTER}${paint(glyphs2.prompt, toneCode("accent", style2), style2.depth)} ${paintAll(line, [BOLD], style2.depth)}`).join(`
22021
23188
  `));
22022
23189
  continue;
22023
23190
  }
22024
- if (message2.role === "tool")
23191
+ if (message3.role === "tool")
22025
23192
  continue;
22026
- if ("toolCalls" in message2 && message2.toolCalls.length > 0) {
23193
+ if ("toolCalls" in message3 && message3.toolCalls.length > 0) {
22027
23194
  if (options.tools === false)
22028
23195
  continue;
22029
- for (const call of message2.toolCalls) {
23196
+ for (const call of message3.toolCalls) {
22030
23197
  const detail = describeToolCall2(call.name, call.arguments ?? {});
22031
23198
  out2.push(`${GUTTER}${paint(glyphs2.check, DIM, style2.depth)} ${paint(call.name, toneCode("accent", style2), style2.depth)}${detail ? paint(` ${clip(detail, Math.max(8, style2.width - 24), glyphs2)}`, DIM, style2.depth) : ""}`);
22032
23199
  }
22033
- if (!message2.content.trim())
23200
+ if (!message3.content.trim())
22034
23201
  continue;
22035
23202
  }
22036
- if (message2.content.trim()) {
23203
+ if (message3.content.trim()) {
22037
23204
  out2.push(`${GUTTER}${paint(glyphs2.star, DIM, style2.depth)} ${paintAll("Archymedes", [BOLD], style2.depth)}`);
22038
- out2.push(renderMarkdown(message2.content.trim(), { width: style2.width, depth: style2.depth, palette: style2.palette }));
23205
+ out2.push(renderMarkdown(message3.content.trim(), { width: style2.width, depth: style2.depth, palette: style2.palette }));
22039
23206
  }
22040
23207
  }
22041
23208
  out2.push(rule(style2, { label: "end of session", tone: "neutral" }));
@@ -22085,7 +23252,7 @@ function parseHistoryCommand(input) {
22085
23252
  return { kind: "invalid", reason: `/history takes "list", a session id, "search <text>", "delete <id>", "status", or "resume" — not "${verb}".` };
22086
23253
  }
22087
23254
 
22088
- // packages/archymedes-cli/src/commands/pacing.ts
23255
+ // src/commands/pacing.ts
22089
23256
  var PACE_PROFILES = {
22090
23257
  off: {
22091
23258
  level: "off",
@@ -22147,7 +23314,7 @@ function paceBadge(level, glyphs2 = UNICODE_GLYPHS) {
22147
23314
  return `${glyphs2.paused} ${profileFor(level).label}`;
22148
23315
  }
22149
23316
 
22150
- // packages/archymedes-cli/src/app/args.ts
23317
+ // src/app/args.ts
22151
23318
  var SESSION_ID2 = /^\d{8}T\d{6}Z-[a-z0-9]{6}$/;
22152
23319
  var DEFAULT_DOCKER_IMAGE = "debian:stable-slim";
22153
23320
  function parseArgs(argv) {
@@ -22282,7 +23449,7 @@ function parseArgs(argv) {
22282
23449
  parsed.resumePick = true;
22283
23450
  }
22284
23451
  } else if (argument === "--cwd") {
22285
- parsed.root = path30.resolve(argv[index + 1] ?? ".");
23452
+ parsed.root = path33.resolve(argv[index + 1] ?? ".");
22286
23453
  index += 1;
22287
23454
  } else if (argument === "--sandbox") {
22288
23455
  const value = argv[index + 1];
@@ -22345,7 +23512,7 @@ function parseArgs(argv) {
22345
23512
  return parsed;
22346
23513
  }
22347
23514
 
22348
- // packages/archymedes-cli/src/app/session-provider.ts
23515
+ // src/app/session-provider.ts
22349
23516
  async function resolveSessionProvider2(environment, options) {
22350
23517
  if (options.provider || !options.resume)
22351
23518
  return resolveProvider(environment, options);
@@ -22354,7 +23521,7 @@ async function resolveSessionProvider2(environment, options) {
22354
23521
  return resolveProvider(environment, selection?.provider === "free" ? { provider: "free", model: options.model ?? selection.model } : options);
22355
23522
  }
22356
23523
 
22357
- // packages/archymedes-cli/src/app/transcript.ts
23524
+ // src/app/transcript.ts
22358
23525
  var colorEnabled2 = false;
22359
23526
  var liveTerminal2 = false;
22360
23527
  var wrap2 = (code) => (value) => colorEnabled2 ? `${code}${value}${RESET}` : value;
@@ -22445,8 +23612,8 @@ function toolLineText(mark, name, detail, summary, effect, lane) {
22445
23612
  const effectGlyph = effect === "workspace" ? style2.yellow(glyphs2.effectWorkspace) : effect === "external" ? style2.magenta(glyphs2.effectExternal) : "";
22446
23613
  const head = `${indent} ${mark}${effectGlyph ? ` ${effectGlyph}` : ""} ${style2.cyan(name)}`;
22447
23614
  const middle = detail ? ` ${detail}` : "";
22448
- const tail = summary ? style2.dim(` · ${summary}`) : "";
22449
- return `${head}${middle}${tail}`;
23615
+ const tail2 = summary ? style2.dim(` · ${summary}`) : "";
23616
+ return `${head}${middle}${tail2}`;
22450
23617
  }
22451
23618
  var FOLD_AFTER_LINES2 = 14;
22452
23619
  function writeFoldable(label3, rendered) {
@@ -22459,22 +23626,22 @@ function writeFoldable(label3, rendered) {
22459
23626
  }
22460
23627
  }
22461
23628
  function renderWrittenCode(toolName, args) {
22462
- const path31 = typeof args.path === "string" ? args.path : "";
22463
- if (!path31)
23629
+ const path34 = typeof args.path === "string" ? args.path : "";
23630
+ if (!path34)
22464
23631
  return;
22465
23632
  const style_ = sectionStyle2();
22466
23633
  if (toolName === "write_file") {
22467
23634
  const content = typeof args.content === "string" ? args.content : "";
22468
23635
  if (content === "")
22469
23636
  return;
22470
- writeFoldable(path31, renderFileChange({ path: path31, kind: "write", content }, style_, { maxLines: FOLD_AFTER_LINES2 }));
23637
+ writeFoldable(path34, renderFileChange({ path: path34, kind: "write", content }, style_, { maxLines: FOLD_AFTER_LINES2 }));
22471
23638
  return;
22472
23639
  }
22473
23640
  const before = typeof args.oldText === "string" ? args.oldText : "";
22474
23641
  const after = typeof args.newText === "string" ? args.newText : "";
22475
23642
  if (before === "" && after === "")
22476
23643
  return;
22477
- writeFoldable(path31, renderFileChange({ path: path31, kind: "edit", before, after }, style_, { maxLines: FOLD_AFTER_LINES2 }));
23644
+ writeFoldable(path34, renderFileChange({ path: path34, kind: "edit", before, after }, style_, { maxLines: FOLD_AFTER_LINES2 }));
22478
23645
  }
22479
23646
  function renderCommandOutput(content) {
22480
23647
  const style_ = sectionStyle2();
@@ -22558,22 +23725,22 @@ ${rule(sectionStyle2(), { label: label3, tone: "accent" })}
22558
23725
  }
22559
23726
  if (event.type !== "runtime")
22560
23727
  return;
22561
- const runtime2 = event.event;
22562
- if (runtime2.type === "provider_retry") {
23728
+ const runtime3 = event.event;
23729
+ if (runtime3.type === "provider_retry") {
22563
23730
  forgetToolLines2();
22564
- const reason = runtime2.reason === "rate_limit" ? "rate limited" : runtime2.reason === "server" ? "provider unavailable" : runtime2.reason === "network" ? "connection failed" : runtime2.reason === "timeout" ? "request timed out" : "temporary provider failure";
22565
- const wait2 = runtime2.delayMs >= 1000 ? `${(runtime2.delayMs / 1000).toFixed(runtime2.delayMs % 1000 === 0 ? 0 : 1)}s` : `${runtime2.delayMs}ms`;
22566
- out2.write(style2.yellow(` ${glyphs2.elbow} ${reason}; retrying model request ${runtime2.nextAttempt}/${runtime2.maxAttempts} in ${wait2}
23731
+ const reason = runtime3.reason === "rate_limit" ? "rate limited" : runtime3.reason === "server" ? "provider unavailable" : runtime3.reason === "network" ? "connection failed" : runtime3.reason === "timeout" ? "request timed out" : "temporary provider failure";
23732
+ const wait2 = runtime3.delayMs >= 1000 ? `${(runtime3.delayMs / 1000).toFixed(runtime3.delayMs % 1000 === 0 ? 0 : 1)}s` : `${runtime3.delayMs}ms`;
23733
+ out2.write(style2.yellow(` ${glyphs2.elbow} ${reason}; retrying model request ${runtime3.nextAttempt}/${runtime3.maxAttempts} in ${wait2}
22567
23734
  `));
22568
23735
  return;
22569
23736
  }
22570
- if (runtime2.type === "model_turn") {
22571
- activity2.tokens += runtime2.usage.inputTokens + runtime2.usage.outputTokens;
22572
- if (runtime2.allowance)
22573
- activity2.allowance = runtime2.allowance;
23737
+ if (runtime3.type === "model_turn") {
23738
+ activity2.tokens += runtime3.usage.inputTokens + runtime3.usage.outputTokens;
23739
+ if (runtime3.allowance)
23740
+ activity2.allowance = runtime3.allowance;
22574
23741
  return;
22575
23742
  }
22576
- if (runtime2.type === "tool_call") {
23743
+ if (runtime3.type === "tool_call") {
22577
23744
  if (!toolSectionAnnounced2) {
22578
23745
  out2.write(`
22579
23746
  ${rule(sectionStyle2(), { label: "tool activity", tone: "neutral" })}
@@ -22581,7 +23748,7 @@ ${rule(sectionStyle2(), { label: "tool activity", tone: "neutral" })}
22581
23748
  toolSectionAnnounced2 = true;
22582
23749
  }
22583
23750
  activity2.awaitingFirstDelta = true;
22584
- const detail = describeToolCall2(runtime2.toolName, runtime2.arguments);
23751
+ const detail = describeToolCall2(runtime3.toolName, runtime3.arguments);
22585
23752
  const lane = pendingCalls2.size > 0;
22586
23753
  if (lane) {
22587
23754
  for (const [id, entry] of pendingCalls2) {
@@ -22592,48 +23759,48 @@ ${rule(sectionStyle2(), { label: "tool activity", tone: "neutral" })}
22592
23759
  pendingCalls2.set(id, { ...entry, lane: true });
22593
23760
  }
22594
23761
  }
22595
- const line = liveTerminal2 ? toolLines2.append(toolLineText(style2.dim(glyphs2.pending), runtime2.toolName, style2.dim(detail), "", runtime2.effect, lane)) : -1;
22596
- pendingCalls2.set(runtime2.toolCallId, { line, detail, name: runtime2.toolName, arguments: runtime2.arguments, effect: runtime2.effect, lane });
23762
+ const line = liveTerminal2 ? toolLines2.append(toolLineText(style2.dim(glyphs2.pending), runtime3.toolName, style2.dim(detail), "", runtime3.effect, lane)) : -1;
23763
+ pendingCalls2.set(runtime3.toolCallId, { line, detail, name: runtime3.toolName, arguments: runtime3.arguments, effect: runtime3.effect, lane });
22597
23764
  activity2.phase = "operation";
22598
- activity2.operation = runtime2.toolName;
23765
+ activity2.operation = runtime3.toolName;
22599
23766
  spinner2?.start();
22600
23767
  return;
22601
23768
  }
22602
- if (runtime2.type === "tool_result") {
23769
+ if (runtime3.type === "tool_result") {
22603
23770
  activity2.toolCalls += 1;
22604
- const verificationKind = typeof runtime2.data?.verificationKind === "string" ? runtime2.data.verificationKind : undefined;
23771
+ const verificationKind = typeof runtime3.data?.verificationKind === "string" ? runtime3.data.verificationKind : undefined;
22605
23772
  if (verificationKind) {
22606
- verificationChecks2.set(verificationKind, !runtime2.isError);
22607
- sessionChecks2.set(verificationKind, !runtime2.isError);
23773
+ verificationChecks2.set(verificationKind, !runtime3.isError);
23774
+ sessionChecks2.set(verificationKind, !runtime3.isError);
22608
23775
  }
22609
- const items = Array.isArray(runtime2.data?.items) ? runtime2.data.items : undefined;
23776
+ const items = Array.isArray(runtime3.data?.items) ? runtime3.data.items : undefined;
22610
23777
  if (items) {
22611
23778
  activity2.steps = items.length > 0 ? { done: items.filter((item) => item.status === "done").length, total: items.length, label: "plan" } : undefined;
22612
23779
  }
22613
- const mark = runtime2.isError ? style2.red(glyphs2.cross) : style2.green(glyphs2.check);
22614
- const summary = summarizeToolResult(runtime2.toolName, runtime2.content, runtime2.isError);
22615
- const pending = pendingCalls2.get(runtime2.toolCallId);
22616
- const completed = toolLineText(mark, runtime2.toolName, style2.dim(pending?.detail ?? ""), summary, runtime2.effect, pending?.lane ?? false);
23780
+ const mark = runtime3.isError ? style2.red(glyphs2.cross) : style2.green(glyphs2.check);
23781
+ const summary = summarizeToolResult(runtime3.toolName, runtime3.content, runtime3.isError);
23782
+ const pending = pendingCalls2.get(runtime3.toolCallId);
23783
+ const completed = toolLineText(mark, runtime3.toolName, style2.dim(pending?.detail ?? ""), summary, runtime3.effect, pending?.lane ?? false);
22617
23784
  if (pending === undefined || pending.line < 0 || !toolLines2.update(pending.line, completed)) {
22618
23785
  out2.write(`${completed}
22619
23786
  `);
22620
23787
  }
22621
- pendingCalls2.delete(runtime2.toolCallId);
22622
- if (runtime2.artifact) {
22623
- const artifact = runtime2.artifact;
23788
+ pendingCalls2.delete(runtime3.toolCallId);
23789
+ if (runtime3.artifact) {
23790
+ const artifact = runtime3.artifact;
22624
23791
  out2.write(style2.dim(` ${glyphs2.elbow} ${artifact.lines.toLocaleString()} lines kept in ${artifact.path}
22625
23792
  `));
22626
23793
  }
22627
- if (!runtime2.isError && pending) {
22628
- if (runtime2.toolName === "write_file" || runtime2.toolName === "edit_file") {
23794
+ if (!runtime3.isError && pending) {
23795
+ if (runtime3.toolName === "write_file" || runtime3.toolName === "edit_file") {
22629
23796
  toolLines2.forget();
22630
- renderWrittenCode(runtime2.toolName, pending.arguments);
22631
- const path31 = typeof pending.arguments.path === "string" ? pending.arguments.path : undefined;
22632
- if (path31)
22633
- touchedFiles2.add(path31);
23797
+ renderWrittenCode(runtime3.toolName, pending.arguments);
23798
+ const path34 = typeof pending.arguments.path === "string" ? pending.arguments.path : undefined;
23799
+ if (path34)
23800
+ touchedFiles2.add(path34);
22634
23801
  let addedDelta = 0;
22635
23802
  let removedDelta = 0;
22636
- if (runtime2.toolName === "write_file") {
23803
+ if (runtime3.toolName === "write_file") {
22637
23804
  const content = typeof pending.arguments.content === "string" ? pending.arguments.content : "";
22638
23805
  if (content)
22639
23806
  addedDelta = content.split(`
@@ -22647,13 +23814,13 @@ ${rule(sectionStyle2(), { label: "tool activity", tone: "neutral" })}
22647
23814
  }
22648
23815
  turnLineDelta2.added += addedDelta;
22649
23816
  turnLineDelta2.removed += removedDelta;
22650
- if (path31) {
22651
- const prior = sessionFiles2.get(path31) ?? { added: 0, removed: 0 };
22652
- sessionFiles2.set(path31, { added: prior.added + addedDelta, removed: prior.removed + removedDelta });
23817
+ if (path34) {
23818
+ const prior = sessionFiles2.get(path34) ?? { added: 0, removed: 0 };
23819
+ sessionFiles2.set(path34, { added: prior.added + addedDelta, removed: prior.removed + removedDelta });
22653
23820
  }
22654
- } else if (runtime2.toolName === "run_command") {
23821
+ } else if (runtime3.toolName === "run_command") {
22655
23822
  toolLines2.forget();
22656
- renderCommandOutput(runtime2.content);
23823
+ renderCommandOutput(runtime3.content);
22657
23824
  }
22658
23825
  }
22659
23826
  const stillRunning = pendingCalls2.values().next().value;
@@ -22668,7 +23835,7 @@ ${rule(sectionStyle2(), { label: "tool activity", tone: "neutral" })}
22668
23835
  }
22669
23836
  }
22670
23837
 
22671
- // packages/core/src/cli/suggestions.ts
23838
+ // ../core/src/cli/suggestions.ts
22672
23839
  var CATEGORY_ORDER = ["blocked", "recovery", "next", "discover", "starter"];
22673
23840
  function defaultSignals(overrides = {}) {
22674
23841
  return {
@@ -22686,7 +23853,7 @@ function defaultSignals(overrides = {}) {
22686
23853
  ...overrides
22687
23854
  };
22688
23855
  }
22689
- var plural = (count3, noun) => `${count3} ${noun}${count3 === 1 ? "" : "s"}`;
23856
+ var plural = (count4, noun) => `${count4} ${noun}${count4 === 1 ? "" : "s"}`;
22690
23857
  function classifyFailure(signals) {
22691
23858
  if (signals.lastStatus === "cancelled")
22692
23859
  return "cancelled";
@@ -23065,7 +24232,7 @@ async function askModelForSuggestions(model, signals, context = {}) {
23065
24232
  }
23066
24233
  }
23067
24234
 
23068
- // packages/archymedes-cli/src/ui/chooser.ts
24235
+ // src/ui/chooser.ts
23069
24236
  var ALL_CHROME = { title: true, status: true, pagination: true, help: true };
23070
24237
  function chromeOf(chrome) {
23071
24238
  return { ...ALL_CHROME, ...chrome };
@@ -23088,11 +24255,11 @@ function filterItems(items, query) {
23088
24255
  }
23089
24256
  function windowStart(selected, total, height) {
23090
24257
  const rows = Math.max(1, Math.floor(height));
23091
- const count3 = Math.max(0, Math.floor(total));
23092
- if (count3 <= rows)
24258
+ const count4 = Math.max(0, Math.floor(total));
24259
+ if (count4 <= rows)
23093
24260
  return 0;
23094
- const cursor = Math.max(0, Math.min(Math.floor(selected), count3 - 1));
23095
- return Math.max(0, Math.min(cursor - Math.floor(rows / 2), count3 - rows));
24261
+ const cursor = Math.max(0, Math.min(Math.floor(selected), count4 - 1));
24262
+ return Math.max(0, Math.min(cursor - Math.floor(rows / 2), count4 - rows));
23096
24263
  }
23097
24264
  function terminalColumns(width, fallback = 80) {
23098
24265
  const value = width ?? fallback;
@@ -23194,8 +24361,8 @@ function renderChooser(state, items, options) {
23194
24361
  const illuminated = Math.round(graphemes.length * focus);
23195
24362
  const label3 = active ? paint2.cyan(graphemes.slice(0, illuminated).join("")) + graphemes.slice(illuminated).join("") : padded;
23196
24363
  const head = `${marker} ${paint2.dim(number)} ${label3} `;
23197
- const tail = `${item.hint ? ` ${item.hint}` : ""}${item.description ? ` ${item.description}` : ""}`;
23198
- lines.push(` ${head}${paint2.dim(clip2(tail, Math.max(0, room - visibleWidth(padded) - 2)))}`);
24364
+ const tail2 = `${item.hint ? ` ${item.hint}` : ""}${item.description ? ` ${item.description}` : ""}`;
24365
+ lines.push(` ${head}${paint2.dim(clip2(tail2, Math.max(0, room - visibleWidth(padded) - 2)))}`);
23199
24366
  }
23200
24367
  const position = visible.length > height && chrome.pagination ? ` ${paginator(state.selected, visible.length)}` : "";
23201
24368
  if (chrome.help) {
@@ -23293,7 +24460,7 @@ async function runChooser(keys, items, paint2, options) {
23293
24460
  return;
23294
24461
  }
23295
24462
 
23296
- // packages/archymedes-cli/src/ui/navigation.ts
24463
+ // src/ui/navigation.ts
23297
24464
  class CommandUsage {
23298
24465
  limit;
23299
24466
  order = [];
@@ -23316,19 +24483,19 @@ class CommandUsage {
23316
24483
  }
23317
24484
  }
23318
24485
 
23319
- // packages/core/src/cli/daemon.ts
23320
- import { randomUUID as randomUUID9 } from "node:crypto";
24486
+ // ../core/src/cli/daemon.ts
24487
+ import { randomUUID as randomUUID10 } from "node:crypto";
23321
24488
  var ARCHYMEDES_DAEMON_PROTOCOL_VERSION = 1;
23322
24489
  var PREVIEW_HUNK_SEPARATOR = `
23323
24490
  …
23324
24491
  `;
23325
24492
  function previewFor(request) {
23326
24493
  const args = request.call.arguments ?? {};
23327
- const path31 = typeof args.path === "string" ? args.path : undefined;
23328
- if (!path31)
24494
+ const path34 = typeof args.path === "string" ? args.path : undefined;
24495
+ if (!path34)
23329
24496
  return;
23330
24497
  if (request.tool.name === "write_file" && typeof args.content === "string") {
23331
- return { toolName: "write_file", path: path31, content: args.content };
24498
+ return { toolName: "write_file", path: path34, content: args.content };
23332
24499
  }
23333
24500
  if (request.tool.name === "edit_file" && args.edits !== undefined && args.edits !== null) {
23334
24501
  let edits;
@@ -23339,14 +24506,14 @@ function previewFor(request) {
23339
24506
  }
23340
24507
  return {
23341
24508
  toolName: "edit_file",
23342
- path: path31,
24509
+ path: path34,
23343
24510
  oldText: edits.map((edit) => edit.oldText).join(PREVIEW_HUNK_SEPARATOR),
23344
24511
  newText: edits.map((edit) => edit.newText).join(PREVIEW_HUNK_SEPARATOR),
23345
24512
  edits
23346
24513
  };
23347
24514
  }
23348
24515
  if (request.tool.name === "edit_file" && typeof args.oldText === "string" && typeof args.newText === "string") {
23349
- return { toolName: "edit_file", path: path31, oldText: args.oldText, newText: args.newText };
24516
+ return { toolName: "edit_file", path: path34, oldText: args.oldText, newText: args.newText };
23350
24517
  }
23351
24518
  return;
23352
24519
  }
@@ -23363,7 +24530,7 @@ class ArchymedesSessionDaemon {
23363
24530
  sessions = new Map;
23364
24531
  pendingApprovals = new Map;
23365
24532
  connect(options = {}) {
23366
- const id = options.id ?? `client_${randomUUID9()}`;
24533
+ const id = options.id ?? `client_${randomUUID10()}`;
23367
24534
  assertSafeId(id, "Client id");
23368
24535
  if (this.clients.has(id))
23369
24536
  throw new Error(`Daemon client already connected: ${id}`);
@@ -23382,7 +24549,7 @@ class ArchymedesSessionDaemon {
23382
24549
  }
23383
24550
  async requestApproval(sessionId, request) {
23384
24551
  const publicRequest = {
23385
- id: `approval_${randomUUID9()}`,
24552
+ id: `approval_${randomUUID10()}`,
23386
24553
  sessionId,
23387
24554
  summary: request.summary,
23388
24555
  actionDigest: request.actionDigest,
@@ -23655,7 +24822,7 @@ class ArchymedesDaemonClient {
23655
24822
  planRoute(objective, signal) {
23656
24823
  return this.daemon.planRoute(this.id, this.sessionId, objective, signal);
23657
24824
  }
23658
- send(objective, commandId = `turn_${randomUUID9()}`) {
24825
+ send(objective, commandId = `turn_${randomUUID10()}`) {
23659
24826
  return this.daemon.send(this.id, this.sessionId, objective, commandId);
23660
24827
  }
23661
24828
  cancel() {
@@ -23714,7 +24881,7 @@ class ArchymedesDaemonClient {
23714
24881
  }
23715
24882
  }
23716
24883
 
23717
- // packages/core/src/money.ts
24884
+ // ../core/src/money.ts
23718
24885
  var CURRENCIES2 = (() => {
23719
24886
  try {
23720
24887
  return Intl.supportedValuesOf("currency");
@@ -23757,8 +24924,8 @@ function formatMoney2(value) {
23757
24924
  return value.currency === "USD" ? `${format.symbol}${rendered}` : `${format.symbol} ${rendered}`;
23758
24925
  }
23759
24926
 
23760
- // packages/core/src/cli/backends.ts
23761
- import path31 from "node:path";
24927
+ // ../core/src/cli/backends.ts
24928
+ import path34 from "node:path";
23762
24929
  var CONFIG_FILE_SCAN_LIMIT2 = 500;
23763
24930
  function localCommandGuidance2(platform) {
23764
24931
  if (platform !== "win32")
@@ -23783,17 +24950,17 @@ class LocalWorkspace2 {
23783
24950
  get label() {
23784
24951
  return this.root;
23785
24952
  }
23786
- readFile(path32, options = {}) {
23787
- return readTextFile(this.root, path32, { ...options, limits: this.limits });
24953
+ readFile(path35, options = {}) {
24954
+ return readTextFile(this.root, path35, { ...options, limits: this.limits });
23788
24955
  }
23789
- readBytes(path32) {
23790
- return readBinaryFile(this.root, path32, this.limits);
24956
+ readBytes(path35) {
24957
+ return readBinaryFile(this.root, path35, this.limits);
23791
24958
  }
23792
- writeFile(path32, content) {
23793
- return writeTextFile(this.root, path32, content, this.limits);
24959
+ writeFile(path35, content) {
24960
+ return writeTextFile(this.root, path35, content, this.limits);
23794
24961
  }
23795
- editFile(path32, oldText, newText, options = {}) {
23796
- return editTextFile(this.root, path32, oldText, newText, { ...options, limits: this.limits });
24962
+ editFile(path35, oldText, newText, options = {}) {
24963
+ return editTextFile(this.root, path35, oldText, newText, { ...options, limits: this.limits });
23797
24964
  }
23798
24965
  async list(prefix, depth) {
23799
24966
  const entries2 = [];
@@ -23814,8 +24981,8 @@ class LocalWorkspace2 {
23814
24981
  }
23815
24982
  async listConfigFiles(prefix) {
23816
24983
  const files = [];
23817
- const base = path31.resolve(this.root, prefix);
23818
- if (!base.startsWith(path31.resolve(this.root)))
24984
+ const base = path34.resolve(this.root, prefix);
24985
+ if (!base.startsWith(path34.resolve(this.root)))
23819
24986
  return [];
23820
24987
  for await (const entry of walkWorkspace(base, { ...this.limits, ignoredDirectories: [] }, CONFIG_FILE_SCAN_LIMIT2)) {
23821
24988
  if (!entry.isDirectory)
@@ -23860,7 +25027,7 @@ class LocalWorkspace2 {
23860
25027
  }
23861
25028
  }
23862
25029
 
23863
- // packages/core/src/cli/cost.ts
25030
+ // ../core/src/cli/cost.ts
23864
25031
  function formatTokenFlow2(usage) {
23865
25032
  return `↑${compactTokenCount(usage.inputTokens)} ↓${compactTokenCount(usage.outputTokens)} tok`;
23866
25033
  }
@@ -24112,7 +25279,7 @@ class CostLedger2 {
24112
25279
  }
24113
25280
  }
24114
25281
 
24115
- // packages/archymedes-cli/src/headless.ts
25282
+ // src/headless.ts
24116
25283
  var HEADLESS_PROTOCOL_VERSION = 1;
24117
25284
  var EXIT_CODES = {
24118
25285
  completed: 0,
@@ -24193,12 +25360,12 @@ class HeadlessEmitter {
24193
25360
  }
24194
25361
  if (event.type !== "runtime")
24195
25362
  return;
24196
- const runtime2 = event.event;
24197
- if (runtime2.type === "assistant_delta") {
24198
- this.emit("text", { text: runtime2.text });
25363
+ const runtime3 = event.event;
25364
+ if (runtime3.type === "assistant_delta") {
25365
+ this.emit("text", { text: runtime3.text });
24199
25366
  return;
24200
25367
  }
24201
- const safe = runtimeEventForJournal(runtime2);
25368
+ const safe = runtimeEventForJournal(runtime3);
24202
25369
  if (safe.type === "tool_call") {
24203
25370
  this.emit("tool_call", { toolCallId: safe.toolCallId, tool: safe.toolName, effect: safe.effect, arguments: safe.arguments });
24204
25371
  return;
@@ -24245,12 +25412,12 @@ class HeadlessEmitter {
24245
25412
  turnEnd(result) {
24246
25413
  this.emit("turn_end", { ...result, exitCode: exitCodeForStatus(result.status) });
24247
25414
  }
24248
- error(message2, fields = {}) {
24249
- this.emit("error", { message: message2, ...fields });
25415
+ error(message3, fields = {}) {
25416
+ this.emit("error", { message: message3, ...fields });
24250
25417
  }
24251
25418
  }
24252
25419
 
24253
- // packages/core/src/providers/price-catalog.ts
25420
+ // ../core/src/providers/price-catalog.ts
24254
25421
  var ANTHROPIC_VERIFIED2 = "2026-06-24";
24255
25422
  var ANTHROPIC_SOURCE2 = "anthropic.com/pricing, recorded 2026-06-24";
24256
25423
  var ANTHROPIC2 = [
@@ -24302,7 +25469,7 @@ var SERVICES2 = [
24302
25469
  ];
24303
25470
  var PRICE_CATALOG2 = definePrices([...ANTHROPIC2, ...OAI_COMPAT_LABS2, ...SERVICES2]);
24304
25471
 
24305
- // packages/archymedes-cli/src/text/color-depth.ts
25472
+ // src/text/color-depth.ts
24306
25473
  function detectColorDepth(environment, isTTY) {
24307
25474
  if (!isTTY || environment.NO_COLOR !== undefined || environment.TERM === "dumb")
24308
25475
  return "none";
@@ -24311,7 +25478,7 @@ function detectColorDepth(environment, isTTY) {
24311
25478
  return "ansi256";
24312
25479
  }
24313
25480
 
24314
- // packages/archymedes-cli/src/terminal/keybindings.ts
25481
+ // src/terminal/keybindings.ts
24315
25482
  var FUNCTION_KEY2 = /^f([1-9]|1[0-2])$/;
24316
25483
  var NAMED_KEYS2 = new Set(["tab", "enter", "escape", "space", "up", "down", "left", "right", "home", "end", "pageup", "pagedown", "backspace", "delete"]);
24317
25484
  function isKeyName2(value) {
@@ -24553,7 +25720,7 @@ function escapeCodeTimeoutMs2(environment = process.env) {
24553
25720
  return Number.isInteger(parsed) && parsed >= 10 && parsed <= 2000 ? parsed : DEFAULT_ESCAPE_CODE_TIMEOUT_MS2;
24554
25721
  }
24555
25722
 
24556
- // packages/archymedes-cli/src/platform/local-currency.ts
25723
+ // src/platform/local-currency.ts
24557
25724
  var COUNTRY_CURRENCIES2 = {
24558
25725
  RW: "RWF",
24559
25726
  EG: "EGP",
@@ -24679,10 +25846,10 @@ function resolveCurrencyPreference(options) {
24679
25846
  }
24680
25847
  var FX_CACHE_MAX_AGE_MS2 = 24 * 60 * 60 * 1000;
24681
25848
 
24682
- // packages/archymedes-cli/src/platform/settings.ts
25849
+ // src/platform/settings.ts
24683
25850
  import { promises as fs20 } from "node:fs";
24684
- import os7 from "node:os";
24685
- import path32 from "node:path";
25851
+ import os8 from "node:os";
25852
+ import path35 from "node:path";
24686
25853
  var LANGUAGE_NAMES2 = {
24687
25854
  en: "English",
24688
25855
  zh: "中文",
@@ -24804,7 +25971,8 @@ var REASONING_CHOICES2 = [
24804
25971
  var CLAUDE_PROVIDER_ID2 = "claude";
24805
25972
  var PROVIDER_CHOICES2 = [
24806
25973
  ...PROVIDER_IDS.map((id) => ({ value: id, label: PROVIDER_INFO[id].label })),
24807
- { value: CLAUDE_PROVIDER_ID2, label: "Claude (your subscription, through Claude Code)" }
25974
+ { value: CLAUDE_PROVIDER_ID2, label: "Claude (your subscription, through Claude Code)" },
25975
+ { value: "codex", label: "Codex (your ChatGPT plan, through the Codex CLI)" }
24808
25976
  ];
24809
25977
  var SETTING_FIELDS2 = [
24810
25978
  { key: "ARCHYMEDES_THEME", label: "Theme — colours for the whole CLI (/theme <name> also saves it)", choices: THEME_CHOICES2, section: "Appearance" },
@@ -24833,6 +26001,7 @@ var SETTING_FIELDS2 = [
24833
26001
  { key: "ARCHYMEDES_PROVIDER", label: "Default provider", choices: PROVIDER_CHOICES2, section: "Providers" },
24834
26002
  { key: "ARCHYMEDES_FALLBACK_MODEL", label: "Fallback after transient provider failure — ask, or provider:model" },
24835
26003
  { key: "CLAUDE_MODEL", label: "Claude model (your subscription) — opus, sonnet, haiku or a model id; empty for Claude Code's default" },
26004
+ { key: "CODEX_MODEL", label: "Codex model (your ChatGPT plan) — a model id from Codex's list; empty for Codex's default" },
24836
26005
  { key: "ANTHROPIC_API_KEY", label: "Anthropic API key", secret: true },
24837
26006
  { key: "ANTHROPIC_BASE_URL", label: "Anthropic base URL", url: true },
24838
26007
  { key: "ANTHROPIC_MODEL", label: "Anthropic model" },
@@ -24891,15 +26060,15 @@ var SETTING_FIELDS2 = [
24891
26060
  var INTERNAL_SETTING_KEYS2 = ["ARCHYMEDES_FREE_PRIVACY_ACK"];
24892
26061
  function settingsDirectory2(environment = process.env, platform = process.platform) {
24893
26062
  if (environment.ARCHYMEDES_CONFIG_DIR?.trim())
24894
- return path32.resolve(environment.ARCHYMEDES_CONFIG_DIR);
26063
+ return path35.resolve(environment.ARCHYMEDES_CONFIG_DIR);
24895
26064
  if (platform === "win32")
24896
- return path32.join(environment.APPDATA?.trim() || path32.join(os7.homedir(), "AppData", "Roaming"), "Archymedes");
26065
+ return path35.join(environment.APPDATA?.trim() || path35.join(os8.homedir(), "AppData", "Roaming"), "Archymedes");
24897
26066
  if (platform === "darwin")
24898
- return path32.join(os7.homedir(), "Library", "Application Support", "Archymedes");
24899
- return path32.join(environment.XDG_CONFIG_HOME?.trim() || path32.join(os7.homedir(), ".config"), "archymedes");
26067
+ return path35.join(os8.homedir(), "Library", "Application Support", "Archymedes");
26068
+ return path35.join(environment.XDG_CONFIG_HOME?.trim() || path35.join(os8.homedir(), ".config"), "archymedes");
24900
26069
  }
24901
26070
  function settingsFile2(environment = process.env, platform = process.platform) {
24902
- return path32.join(settingsDirectory2(environment, platform), "settings.json");
26071
+ return path35.join(settingsDirectory2(environment, platform), "settings.json");
24903
26072
  }
24904
26073
  function cleanSettings2(value) {
24905
26074
  if (!value || typeof value !== "object" || Array.isArray(value))
@@ -24947,7 +26116,7 @@ function mergedEnvironment2(settings, environment = process.env) {
24947
26116
  var PROVIDER_REQUIRED_NAMES2 = new Set(PROVIDER_IDS.flatMap((id) => PROVIDER_INFO[id].requires));
24948
26117
  var PROVIDER_KEY_FIELDS2 = SETTING_FIELDS2.filter((field) => PROVIDER_REQUIRED_NAMES2.has(field.key));
24949
26118
 
24950
- // packages/archymedes-cli/src/app/free-setup.ts
26119
+ // src/app/free-setup.ts
24951
26120
  var OPENROUTER_KEYS_URL3 = "https://openrouter.ai/keys";
24952
26121
  var FREE_SETUP_INTRO = [
24953
26122
  "Free mode runs zero-priced models through OpenRouter and never falls back to a paid one.",
@@ -25013,7 +26182,7 @@ ${style3.bold("Free mode setup")}
25013
26182
  return;
25014
26183
  }
25015
26184
 
25016
- // packages/archymedes-cli/src/session/tabs.ts
26185
+ // src/session/tabs.ts
25017
26186
  class WorkspaceController {
25018
26187
  limit;
25019
26188
  tabs = [];
@@ -25102,7 +26271,7 @@ class WorkspaceController {
25102
26271
  }
25103
26272
  }
25104
26273
 
25105
- // packages/archymedes-cli/src/terminal/output.ts
26274
+ // src/terminal/output.ts
25106
26275
  var terminalStream2 = {
25107
26276
  write(text) {
25108
26277
  process.stdout.write(text);
@@ -25158,8 +26327,8 @@ class LineLog {
25158
26327
  get dropped() {
25159
26328
  return this.droppedLines;
25160
26329
  }
25161
- tail(count3) {
25162
- return count3 <= 0 ? [] : this.committed.slice(Math.max(0, this.committed.length - count3));
26330
+ tail(count4) {
26331
+ return count4 <= 0 ? [] : this.committed.slice(Math.max(0, this.committed.length - count4));
25163
26332
  }
25164
26333
  clear() {
25165
26334
  this.committed.length = 0;
@@ -25198,7 +26367,7 @@ class TabSink {
25198
26367
  }
25199
26368
  }
25200
26369
 
25201
- // packages/archymedes-cli/src/terminal/job-stream.ts
26370
+ // src/terminal/job-stream.ts
25202
26371
  var TERMINAL_STATUSES = new Set(["completed", "failed", "cancelled"]);
25203
26372
  class WatchRegistry {
25204
26373
  watched = new Map;
@@ -25233,7 +26402,7 @@ class WatchRegistry {
25233
26402
  }
25234
26403
  }
25235
26404
 
25236
- // packages/archymedes-cli/src/theme/theme.ts
26405
+ // src/theme/theme.ts
25237
26406
  var BLUEPRINT2 = `
25238
26407
  /* Blueprint navy, chalk-white ink and an amber pencil — the original drafting table. */
25239
26408
  @theme blueprint {
@@ -25534,11 +26703,11 @@ function detectPreferredTheme(environment) {
25534
26703
  return DEFAULT_THEME_NAME2;
25535
26704
  }
25536
26705
 
25537
- // packages/archymedes-cli/src/theme/theme-files.ts
26706
+ // src/theme/theme-files.ts
25538
26707
  import { promises as fs21 } from "node:fs";
25539
- import path33 from "node:path";
26708
+ import path36 from "node:path";
25540
26709
  function themeDirectory(scope, root, environment) {
25541
- return scope === "project" ? path33.join(root, ".archymedes", "themes") : path33.join(settingsDirectory(environment), "themes");
26710
+ return scope === "project" ? path36.join(root, ".archymedes", "themes") : path36.join(settingsDirectory(environment), "themes");
25542
26711
  }
25543
26712
  async function readThemeDirectory(directory, source) {
25544
26713
  let entries2;
@@ -25549,7 +26718,7 @@ async function readThemeDirectory(directory, source) {
25549
26718
  }
25550
26719
  const themes = [];
25551
26720
  for (const entry of entries2.filter((name) => name.endsWith(".tss")).sort()) {
25552
- const file = path33.join(directory, entry);
26721
+ const file = path36.join(directory, entry);
25553
26722
  try {
25554
26723
  const contents = await fs21.readFile(file, "utf8");
25555
26724
  for (const theme of parseThemeSource(contents))
@@ -25572,7 +26741,7 @@ async function findTheme(name, root, environment) {
25572
26741
  return (await discoverThemes(root, environment)).find((theme) => theme.name.toLowerCase() === wanted);
25573
26742
  }
25574
26743
 
25575
- // packages/archymedes-cli/src/render/syntax.ts
26744
+ // src/render/syntax.ts
25576
26745
  var DEFAULT_CODE_STYLE2 = { colors: "vscode", lineNumbers: true };
25577
26746
  var currentCodeStyle2 = { ...DEFAULT_CODE_STYLE2 };
25578
26747
  function codeStyleFromEnvironment(environment) {
@@ -25630,7 +26799,7 @@ var GRAMMARS2 = {
25630
26799
  };
25631
26800
  var PUNCTUATION2 = new Set([..."{}[]()<>;,.:=+-*/%!&|^~?\\"]);
25632
26801
 
25633
- // packages/archymedes-cli/src/platform/i18n.ts
26802
+ // src/platform/i18n.ts
25634
26803
  var CONTROL_LANGUAGES2 = {
25635
26804
  en: "English",
25636
26805
  zh: "中文 (Mandarin)",
@@ -25655,7 +26824,7 @@ function resolveControlLanguage2(value) {
25655
26824
  return base in CONTROL_LANGUAGES2 ? base : "en";
25656
26825
  }
25657
26826
 
25658
- // packages/archymedes-cli/src/text/glyphs.ts
26827
+ // src/text/glyphs.ts
25659
26828
  var UNICODE_GLYPHS2 = {
25660
26829
  starGlyphs: [".", "·", "✧", "✦", "✶"],
25661
26830
  spinnerFrames: [" ◜ ", " ◠ ", " ◝ ", " ◞ ", " ◡ ", " ◟ "],
@@ -25771,7 +26940,7 @@ function glyphsFor(mode) {
25771
26940
  function resolveGlyphs(environment, platform = process.platform) {
25772
26941
  return glyphsFor(detectGlyphMode(environment, platform));
25773
26942
  }
25774
- // packages/archymedes-cli/src/session/state-history.ts
26943
+ // src/session/state-history.ts
25775
26944
  class CliStateHistory {
25776
26945
  root;
25777
26946
  environment;
@@ -25882,7 +27051,7 @@ class CliStateHistory {
25882
27051
  }
25883
27052
  }
25884
27053
 
25885
- // packages/archymedes-cli/src/ui/borrowed-keyboard.ts
27054
+ // src/ui/borrowed-keyboard.ts
25886
27055
  function painter(output) {
25887
27056
  let rows = 0;
25888
27057
  const erase = () => {
@@ -25963,7 +27132,7 @@ async function openChooser(host, items, options, self) {
25963
27132
  return withBorrowedKeyboard(host, self, (keys, paint2) => runChooser(keys, items, paint2, sized));
25964
27133
  }
25965
27134
 
25966
- // packages/archymedes-cli/src/app/prompts.ts
27135
+ // src/app/prompts.ts
25967
27136
  function renderApprovalPreview(preview2) {
25968
27137
  if (!preview2)
25969
27138
  return;
@@ -26101,7 +27270,7 @@ async function hiddenQuestion(readline, question, signal) {
26101
27270
  }
26102
27271
  }
26103
27272
 
26104
- // packages/archymedes-cli/src/app/providers.ts
27273
+ // src/app/providers.ts
26105
27274
  function readFxRates(environment) {
26106
27275
  const genericRate = Number(environment.ARCHYMEDES_FX_RATE);
26107
27276
  const genericFrom = environment.ARCHYMEDES_FX_FROM?.trim().toUpperCase();
@@ -26118,11 +27287,11 @@ function readFxRates(environment) {
26118
27287
  }
26119
27288
  return configured;
26120
27289
  }
26121
- // packages/core/src/cli/job-store.ts
27290
+ // ../core/src/cli/job-store.ts
26122
27291
  import { promises as fs22 } from "node:fs";
26123
- import path34 from "node:path";
27292
+ import path37 from "node:path";
26124
27293
 
26125
- // packages/core/src/cli/jobs.ts
27294
+ // ../core/src/cli/jobs.ts
26126
27295
  var JOB_STATUSES = ["queued", "running", "paused", "completed", "failed", "cancelled"];
26127
27296
  var DECISIONS = ["allow", "allow_always", "allow_pattern", "deny", "deny_always"];
26128
27297
  function parseJobRecord(value, index) {
@@ -26297,12 +27466,12 @@ function recoverStale(store, now) {
26297
27466
  return { store: { jobs }, recovered };
26298
27467
  }
26299
27468
 
26300
- // packages/core/src/cli/job-store.ts
27469
+ // ../core/src/cli/job-store.ts
26301
27470
  function jobStoreFile(root) {
26302
- return path34.join(root, ".archymedes", "jobs.json");
27471
+ return path37.join(root, ".archymedes", "jobs.json");
26303
27472
  }
26304
27473
  function jobLogPath(root, id) {
26305
- return path34.join(root, ".archymedes", "jobs", `${id}.log`);
27474
+ return path37.join(root, ".archymedes", "jobs", `${id}.log`);
26306
27475
  }
26307
27476
  async function readStore(file) {
26308
27477
  try {
@@ -26352,7 +27521,7 @@ async function reapLegacyLock(lockFile) {
26352
27521
  async function withJobs(root, mutate) {
26353
27522
  const file = jobStoreFile(root);
26354
27523
  const lockFile = `${file}.lock`;
26355
- await fs22.mkdir(path34.dirname(file), { recursive: true });
27524
+ await fs22.mkdir(path37.dirname(file), { recursive: true });
26356
27525
  const release = await acquireLock(lockFile);
26357
27526
  try {
26358
27527
  const current = await readStore(file);
@@ -26413,14 +27582,14 @@ async function getJob(root, id) {
26413
27582
  }
26414
27583
  async function appendJobLog(root, id, line) {
26415
27584
  const file = jobLogPath(root, id);
26416
- await fs22.mkdir(path34.dirname(file), { recursive: true });
27585
+ await fs22.mkdir(path37.dirname(file), { recursive: true });
26417
27586
  await fs22.appendFile(file, `${line}
26418
27587
  `, "utf8");
26419
27588
  }
26420
- // packages/archymedes-cli/src/job-worker.ts
26421
- import os8 from "node:os";
27589
+ // src/job-worker.ts
27590
+ import os9 from "node:os";
26422
27591
 
26423
- // packages/archymedes-cli/src/commands/wander.ts
27592
+ // src/commands/wander.ts
26424
27593
  async function gatherWanderEvidence(topic, search, numResults = 8) {
26425
27594
  if (!search) {
26426
27595
  return {
@@ -26501,7 +27670,7 @@ function renderWanderResults(source, style3, width) {
26501
27670
  }
26502
27671
  function renderWanderChart(results, style3, width) {
26503
27672
  const counts = wanderGradeCounts(results);
26504
- const bars = barChart(counts.map(({ grade, count: count3 }) => ({ label: grade.replace("_", " "), value: count3 })), { width: Math.max(24, Math.min(width, 72)), depth: style3.depth, glyphs: style3.glyphs, max: results.claims.length });
27673
+ const bars = barChart(counts.map(({ grade, count: count4 }) => ({ label: grade.replace("_", " "), value: count4 })), { width: Math.max(24, Math.min(width, 72)), depth: style3.depth, glyphs: style3.glyphs, max: results.claims.length });
26505
27674
  const strongest = WANDER_GRADES.find((grade) => counts.find((entry) => entry.grade === grade)?.count);
26506
27675
  const lines = [
26507
27676
  ...bars,
@@ -26516,8 +27685,8 @@ function renderWanderChart(results, style3, width) {
26516
27685
  `);
26517
27686
  }
26518
27687
 
26519
- // packages/archymedes-cli/src/job-worker.ts
26520
- function workerId(hostname = os8.hostname(), pid = process.pid) {
27688
+ // src/job-worker.ts
27689
+ function workerId(hostname = os9.hostname(), pid = process.pid) {
26521
27690
  return `${hostname}:${pid}`;
26522
27691
  }
26523
27692
  var DEFAULT_LEASE_MS = 4 * 60000;
@@ -26653,10 +27822,10 @@ async function runJobWorkerOnce(deps) {
26653
27822
  await logChain.catch(() => {
26654
27823
  return;
26655
27824
  });
26656
- const message2 = error instanceof Error ? error.message : String(error);
26657
- await writeLogSafely(deps.root, job.id, `✗ ${message2}`);
26658
- await finishJob(deps.root, job.id, owner, "failed", { error: message2 });
26659
- return { outcome: "failed", error: message2 };
27825
+ const message3 = error instanceof Error ? error.message : String(error);
27826
+ await writeLogSafely(deps.root, job.id, `✗ ${message3}`);
27827
+ await finishJob(deps.root, job.id, owner, "failed", { error: message3 });
27828
+ return { outcome: "failed", error: message3 };
26660
27829
  } finally {
26661
27830
  clearInterval(heartbeat3);
26662
27831
  await workspaceDispose(deps.workspace);
@@ -26694,7 +27863,7 @@ async function runJobWorkerForever(deps) {
26694
27863
  }
26695
27864
  }
26696
27865
 
26697
- // packages/archymedes-cli/src/app/providers.ts
27866
+ // src/app/providers.ts
26698
27867
  function renderProviders(environment, depth) {
26699
27868
  const paint2 = (text, apply) => depth === "none" ? text : apply(text);
26700
27869
  const statuses = describeProviders(environment);
@@ -26765,7 +27934,7 @@ function modelPriceCatalogFor(prices, exact) {
26765
27934
  return { ...rates, ...prices.largeContext ? { largeContext: prices.largeContext } : {} };
26766
27935
  }
26767
27936
 
26768
- // packages/archymedes-cli/src/app/job-launch.ts
27937
+ // src/app/job-launch.ts
26769
27938
  async function runJobWorkerProcess(root, jobId) {
26770
27939
  const savedSettings = await loadSettings(process.env);
26771
27940
  const environment = mergedEnvironment(savedSettings, process.env);
@@ -26802,10 +27971,10 @@ async function runJobWorkerProcess(root, jobId) {
26802
27971
  }
26803
27972
  }
26804
27973
 
26805
- // packages/archymedes-cli/src/app/startup-commands.ts
27974
+ // src/app/startup-commands.ts
26806
27975
  import { createInterface as createInterface4 } from "node:readline/promises";
26807
27976
 
26808
- // packages/archymedes-cli/src/headless.ts
27977
+ // src/headless.ts
26809
27978
  var EXIT_CODES2 = {
26810
27979
  completed: 0,
26811
27980
  failed: 1,
@@ -26829,7 +27998,7 @@ function exitCodeForStatus2(status) {
26829
27998
  return STATUS_EXIT2[status];
26830
27999
  }
26831
28000
 
26832
- // packages/archymedes-cli/src/platform/doctor.ts
28001
+ // src/platform/doctor.ts
26833
28002
  var DEFAULT_PROBE_TIMEOUT_MS = 5000;
26834
28003
  function modelEndpoints(environment) {
26835
28004
  return providerEndpoints(environment).map((provider) => {
@@ -26959,7 +28128,7 @@ function doctorReport(probes, context) {
26959
28128
  };
26960
28129
  }
26961
28130
 
26962
- // packages/archymedes-cli/src/text/color-depth.ts
28131
+ // src/text/color-depth.ts
26963
28132
  function detectColorDepth2(environment, isTTY) {
26964
28133
  if (!isTTY || environment.NO_COLOR !== undefined || environment.TERM === "dumb")
26965
28134
  return "none";
@@ -26968,9 +28137,9 @@ function detectColorDepth2(environment, isTTY) {
26968
28137
  return "ansi256";
26969
28138
  }
26970
28139
 
26971
- // packages/archymedes-cli/src/session/delete-session.ts
28140
+ // src/session/delete-session.ts
26972
28141
  import { promises as fs23 } from "node:fs";
26973
- import path35 from "node:path";
28142
+ import path38 from "node:path";
26974
28143
  var SAFE_ID = /^[A-Za-z0-9][A-Za-z0-9._-]{0,127}$/;
26975
28144
  async function removeIfPresent(file) {
26976
28145
  try {
@@ -26983,12 +28152,12 @@ async function removeIfPresent(file) {
26983
28152
  }
26984
28153
  }
26985
28154
  async function clearProjection(root, id) {
26986
- const file = path35.join(root, ".archymedes", "projection.db");
28155
+ const file = path38.join(root, ".archymedes", "projection.db");
26987
28156
  const exists = await fs23.access(file).then(() => true, () => false);
26988
28157
  if (!exists)
26989
28158
  return;
26990
28159
  try {
26991
- const { SessionProjection } = await import("./projection-4jjdaf41.mjs");
28160
+ const { SessionProjection } = await import("./projection-ndr3t77h.mjs");
26992
28161
  const projection = await SessionProjection.open(root);
26993
28162
  try {
26994
28163
  await projection.rebuild(id);
@@ -27003,10 +28172,10 @@ async function deleteSession(root, id, options = {}) {
27003
28172
  if (id === options.activeId)
27004
28173
  return { deleted: false, reason: "That is the chat you are in — it can't be deleted while it is open. Use /clear to start a fresh one first." };
27005
28174
  const removed = await Promise.all([
27006
- removeIfPresent(path35.join(sessionDirectory(root), `${id}.json`)),
28175
+ removeIfPresent(path38.join(sessionDirectory(root), `${id}.json`)),
27007
28176
  removeIfPresent(sessionJournalPath(root, id)),
27008
28177
  removeIfPresent(eventJournalPath(root, id)),
27009
- removeIfPresent(path35.join(root, ".archymedes", "recovery", `${id}.json`))
28178
+ removeIfPresent(path38.join(root, ".archymedes", "recovery", `${id}.json`))
27010
28179
  ]);
27011
28180
  if (!removed.some(Boolean))
27012
28181
  return { deleted: false, reason: `No session ${id} in this project.` };
@@ -27014,7 +28183,7 @@ async function deleteSession(root, id, options = {}) {
27014
28183
  return { deleted: true };
27015
28184
  }
27016
28185
 
27017
- // packages/archymedes-cli/src/ui/navigation.ts
28186
+ // src/ui/navigation.ts
27018
28187
  var ESSENTIAL_COMMANDS = ["/help", "/history", "/model", "/mode", "/diff", "/undo", "/settings", "/exit"];
27019
28188
  function isEssential(command) {
27020
28189
  return ESSENTIAL_COMMANDS.includes(command);
@@ -27081,7 +28250,7 @@ function renderAsks(suggestions, style3) {
27081
28250
  `);
27082
28251
  }
27083
28252
 
27084
- // packages/archymedes-cli/src/app/help.ts
28253
+ // src/app/help.ts
27085
28254
  function helpText(language = "en", shortcuts = new Map) {
27086
28255
  const star = glyphs.star;
27087
28256
  const mark = (command) => isEssential(command) ? `${star} ` : " ";
@@ -27186,7 +28355,7 @@ ${renderCommandHelp(language, shortcuts, { mark })}
27186
28355
  `;
27187
28356
  }
27188
28357
 
27189
- // packages/archymedes-cli/src/app/prompts.ts
28358
+ // src/app/prompts.ts
27190
28359
  function settingsChooser(readline) {
27191
28360
  return (request) => openChooser({ readline, input: process.stdin, output: process.stdout }, request.items.map((item) => ({ ...item })), {
27192
28361
  title: request.title,
@@ -27259,7 +28428,7 @@ async function hiddenQuestion2(readline, question, signal) {
27259
28428
  }
27260
28429
  }
27261
28430
 
27262
- // packages/archymedes-cli/src/app/startup-commands.ts
28431
+ // src/app/startup-commands.ts
27263
28432
  function runInformationFlag(context) {
27264
28433
  const { args, environment, language, earlyDepth } = context;
27265
28434
  if (args.help) {
@@ -27463,7 +28632,7 @@ ${status.reason ?? "native state engine unavailable"}
27463
28632
  return 0;
27464
28633
  }
27465
28634
 
27466
- // packages/archymedes-cli/src/app/display-currency.ts
28635
+ // src/app/display-currency.ts
27467
28636
  async function settleDisplayCurrency(options) {
27468
28637
  const { args, environment, prices, rates, state } = options;
27469
28638
  if (prices && state.display !== prices.currency) {
@@ -27520,7 +28689,7 @@ async function settleDisplayCurrency(options) {
27520
28689
  return;
27521
28690
  }
27522
28691
 
27523
- // packages/archymedes-cli/src/app/workspaces.ts
28692
+ // src/app/workspaces.ts
27524
28693
  function createWorkspaceFactory(args, environment) {
27525
28694
  return async (request) => {
27526
28695
  const announce = (text) => {
@@ -27530,8 +28699,8 @@ function createWorkspaceFactory(args, environment) {
27530
28699
  };
27531
28700
  const minutes = Math.max(1, Math.min(args.sandboxMinutes, 60));
27532
28701
  if (request.backend === "e2b") {
27533
- const { findWorkspacePreset } = await import("./sandbox-templates-8sd1psez.mjs");
27534
- const { createE2BProvider } = await import("./factory-wc4p77tw.mjs");
28702
+ const { findWorkspacePreset } = await import("./sandbox-templates-9jcjws23.mjs");
28703
+ const { createE2BProvider } = await import("./factory-3a3bffgd.mjs");
27535
28704
  const preset = findWorkspacePreset(request.preset ?? args.preset);
27536
28705
  const sandbox = createE2BProvider(environment, preset.templateAlias);
27537
28706
  if (!sandbox)
@@ -27557,7 +28726,7 @@ function createWorkspaceFactory(args, environment) {
27557
28726
  return { workspace: created };
27558
28727
  }
27559
28728
  if (request.backend === "docker") {
27560
- const { createDockerProvider } = await import("./factory-wc4p77tw.mjs");
28729
+ const { createDockerProvider } = await import("./factory-3a3bffgd.mjs");
27561
28730
  const image = request.dockerImage || args.dockerImage || environment.DOCKER_CODING_IMAGE;
27562
28731
  const sandbox = createDockerProvider({ ...environment, DOCKER_CODING_IMAGE: image });
27563
28732
  if (!sandbox)
@@ -27586,11 +28755,11 @@ function createWorkspaceFactory(args, environment) {
27586
28755
  };
27587
28756
  }
27588
28757
 
27589
- // packages/core/src/cli/balance.ts
28758
+ // ../core/src/cli/balance.ts
27590
28759
  var CRITICAL_BALANCE_USD2 = 0.5;
27591
28760
  var LOW_BALANCE_USD2 = 2;
27592
28761
 
27593
- // packages/archymedes-cli/src/commands/balance.ts
28762
+ // src/commands/balance.ts
27594
28763
  function formatBalance(amount, currency) {
27595
28764
  return formatMoney(fromUnits(amount, currency));
27596
28765
  }
@@ -27674,11 +28843,11 @@ class BalanceWatch {
27674
28843
  const runway = turnsLeft !== undefined ? ` At this session's average, that is about ${turnsLeft} more turn${turnsLeft === 1 ? "" : "s"}.` : "";
27675
28844
  if (kind === "rapid") {
27676
28845
  const minutes = Math.max(1, Math.round(elapsed / 60000));
27677
- const percent = prior ? Math.round(drop / Math.max(0.000000001, prior.balance.amount) * 100) : 0;
28846
+ const percent2 = prior ? Math.round(drop / Math.max(0.000000001, prior.balance.amount) * 100) : 0;
27678
28847
  return {
27679
28848
  kind,
27680
28849
  lines: [
27681
- `Balance watch: down ${money4(drop)} (${percent}%) in about ${minutes} min; ${money4(balance.amount)} remains.${runway}`,
28850
+ `Balance watch: down ${money4(drop)} (${percent2}%) in about ${minutes} min; ${money4(balance.amount)} remains.${runway}`,
27682
28851
  "You are still in control: use /cost to review it, /slow to reduce the pace, or /balance when you choose."
27683
28852
  ]
27684
28853
  };
@@ -27693,7 +28862,7 @@ class BalanceWatch {
27693
28862
  }
27694
28863
  }
27695
28864
 
27696
- // packages/archymedes-cli/src/session/resumed-spend.ts
28865
+ // src/session/resumed-spend.ts
27697
28866
  async function readSessionModelTurns(root, sessionId) {
27698
28867
  const events = await readEventJournal(root, sessionId);
27699
28868
  const turns = [];
@@ -27725,7 +28894,7 @@ function priceSessionModelTurns(turns, options) {
27725
28894
  return { spent: total, unpriced: [...unpriced].sort() };
27726
28895
  }
27727
28896
 
27728
- // packages/archymedes-cli/src/app/balance-tracking.ts
28897
+ // src/app/balance-tracking.ts
27729
28898
  function createBalanceTracking(options) {
27730
28899
  const { environment, processEnvironment, rates, state } = options;
27731
28900
  const usdInDisplay = (usd) => {
@@ -27814,22 +28983,22 @@ function createBalanceTracking(options) {
27814
28983
  return { lowBalance, criticalBalance, balanceWatch, parseManualBalance, currentBalance, persistManualBalance, sessionSpend, balanceHeader, checkBalance, carryResumedSpend };
27815
28984
  }
27816
28985
 
27817
- // packages/archymedes-cli/src/app/agent-factory.ts
28986
+ // src/app/agent-factory.ts
27818
28987
  function createClientFactory(options) {
27819
28988
  const { args, environment, daemon, approvalPrompt, rates, approvedBudget, state } = options;
27820
- return async (record2, runtime2 = {}) => {
28989
+ return async (record2, runtime3 = {}) => {
27821
28990
  const client = daemon.connect({
27822
28991
  onNotification: options.onNotification,
27823
28992
  approve: (request) => approvalPrompt({ summary: request.summary, safety: request.safety, preview: request.preview, jev: request.jev, pattern: request.pattern })
27824
28993
  });
27825
- const editDiagnostics = (runtime2.workspace ?? state.workspace) instanceof LocalWorkspace && environment.ARCHYMEDES_EDIT_DIAGNOSTICS?.trim().toLowerCase() !== "off" ? createLspEditDiagnostics(args.root) : undefined;
28994
+ const editDiagnostics = (runtime3.workspace ?? state.workspace) instanceof LocalWorkspace && environment.ARCHYMEDES_EDIT_DIAGNOSTICS?.trim().toLowerCase() !== "off" ? createLspEditDiagnostics(args.root) : undefined;
27826
28995
  await client.open(({ onEvent, approve }) => new ArchymedesAgent({
27827
28996
  root: args.root,
27828
- model: runtime2.provider ?? state.model,
27829
- prices: modelPriceCatalogFor(runtime2.prices ?? state.prices, Boolean(approvedBudget)),
27830
- budgets: applyPacing(approvedBudget && (runtime2.prices ?? state.prices) ? { maxRwf: convertTo(approvedBudget, (runtime2.prices ?? state.prices).currency, rates)?.micros ?? approvedBudget.micros } : {}, state.pace),
28997
+ model: runtime3.provider ?? state.model,
28998
+ prices: modelPriceCatalogFor(runtime3.prices ?? state.prices, Boolean(approvedBudget)),
28999
+ budgets: applyPacing(approvedBudget && (runtime3.prices ?? state.prices) ? { maxRwf: convertTo(approvedBudget, (runtime3.prices ?? state.prices).currency, rates)?.micros ?? approvedBudget.micros } : {}, state.pace),
27831
29000
  mode: state.mode,
27832
- workspace: runtime2.workspace ?? state.workspace,
29001
+ workspace: runtime3.workspace ?? state.workspace,
27833
29002
  approve,
27834
29003
  search: createExaClient(environment),
27835
29004
  onExpense: (expense) => state.ledger.recordExpense(expense),
@@ -27855,14 +29024,14 @@ function closeWithClient(client, hook) {
27855
29024
  client.dispose = () => dispose().finally(close);
27856
29025
  }
27857
29026
 
27858
- // packages/archymedes-cli/src/app/live-models.ts
29027
+ // src/app/live-models.ts
27859
29028
  async function readCachedLiveModels(environment, state) {
27860
29029
  const cached = await readModelCache(environment);
27861
29030
  if (isCacheFresh(cached))
27862
29031
  state.liveModels = cached.models;
27863
29032
  }
27864
29033
 
27865
- // packages/archymedes-cli/src/app/session-hints.ts
29034
+ // src/app/session-hints.ts
27866
29035
  function createSessionHints(options) {
27867
29036
  const { state, watched, tabs, usage } = options;
27868
29037
  const navContext = () => ({
@@ -27891,7 +29060,7 @@ function createSessionHints(options) {
27891
29060
  return { navContext, writeHint };
27892
29061
  }
27893
29062
 
27894
- // packages/archymedes-cli/src/render/completion-card.ts
29063
+ // src/render/completion-card.ts
27895
29064
  var CHECK_LABELS = {
27896
29065
  tests: "tests",
27897
29066
  typecheck: "typecheck",
@@ -27921,7 +29090,7 @@ function renderCompletionCard(input, style3) {
27921
29090
  return panel(lines, style3, { title, tone: failed2 ? "bad" : verified ? "good" : "warn" });
27922
29091
  }
27923
29092
 
27924
- // packages/archymedes-cli/src/render/routing-receipt.ts
29093
+ // src/render/routing-receipt.ts
27925
29094
  function formatMicros(micros, currency = "USD") {
27926
29095
  if (micros === undefined || !Number.isFinite(micros))
27927
29096
  return "—";
@@ -28013,10 +29182,10 @@ ${rule(cell, { label: badge })}` : out3.join(`
28013
29182
  `);
28014
29183
  }
28015
29184
 
28016
- // packages/archymedes-cli/src/app/session-hints.ts
29185
+ // src/app/session-hints.ts
28017
29186
  var failureStatus = (status) => status === "needs_approval" ? "waiting_approval" : status;
28018
29187
 
28019
- // packages/archymedes-cli/src/app/turn-failure.ts
29188
+ // src/app/turn-failure.ts
28020
29189
  async function handleTurnFailure(error, request, context) {
28021
29190
  const { args, environment, interactive, headless, recoveryState, state, navContext } = context;
28022
29191
  if (recoveryState.last?.request === request) {
@@ -28031,15 +29200,15 @@ async function handleTurnFailure(error, request, context) {
28031
29200
  spinner?.stop();
28032
29201
  statusBar.clear();
28033
29202
  endStreamedLine();
28034
- const message2 = error instanceof Error ? error.message : String(error);
28035
- if (/exceeds the reserved model budget/i.test(message2) && args.budget) {
29203
+ const message3 = error instanceof Error ? error.message : String(error);
29204
+ if (/exceeds the reserved model budget/i.test(message3) && args.budget) {
28036
29205
  out.write(`
28037
29206
  ${style.yellow(`Stopped at the ${formatMoney(fromUnits(args.budget, state.display))} cap for this request.`)}
28038
29207
  `);
28039
29208
  out.write(style.dim(` Raise it with --budget, or ask for something smaller.
28040
29209
  `));
28041
29210
  state.lastTurnStatus = "iteration_limit";
28042
- state.lastFailure = { status: "iteration_limit", message: message2 };
29211
+ state.lastFailure = { status: "iteration_limit", message: message3 };
28043
29212
  const recovery2 = renderRecovery(navContext(), sectionStyle());
28044
29213
  if (recovery2 && interactive)
28045
29214
  out.write(`${recovery2}
@@ -28058,20 +29227,20 @@ ${style.yellow(`Stopped at the ${formatMoney(fromUnits(args.budget, state.displa
28058
29227
  out.write(` ${style.dim(diagnosis.hint)}
28059
29228
  `);
28060
29229
  } else {
28061
- out.write(`${style.red("error")} ${message2}
29230
+ out.write(`${style.red("error")} ${message3}
28062
29231
  `);
28063
29232
  }
28064
- state.lastFailure = { status: "failed", message: message2 };
29233
+ state.lastFailure = { status: "failed", message: message3 };
28065
29234
  const recovery = renderRecovery(navContext(), sectionStyle());
28066
29235
  if (recovery && interactive)
28067
29236
  out.write(`${recovery}
28068
29237
  `);
28069
29238
  state.lastTurnStatus = "failed";
28070
- headless?.error(message2, { status: "failed" });
29239
+ headless?.error(message3, { status: "failed" });
28071
29240
  return false;
28072
29241
  }
28073
29242
 
28074
- // packages/archymedes-cli/src/app/turn-preflight.ts
29243
+ // src/app/turn-preflight.ts
28075
29244
  async function runTurnPreflight(request, context) {
28076
29245
  const { args, readline, interactive, approvedBudget, rates, state } = context;
28077
29246
  const { currentBalance, balanceWatch, sessionSpend } = context.balance;
@@ -28169,7 +29338,7 @@ async function runTurnPreflight(request, context) {
28169
29338
  return true;
28170
29339
  }
28171
29340
 
28172
- // packages/archymedes-cli/src/app/turn-runner.ts
29341
+ // src/app/turn-runner.ts
28173
29342
  var addModelUsage2 = (left, right) => ({
28174
29343
  inputTokens: left.inputTokens + right.inputTokens,
28175
29344
  outputTokens: left.outputTokens + right.outputTokens,
@@ -28187,7 +29356,7 @@ function createTurnRunner(context) {
28187
29356
  const suggestions = await askModelForSuggestions({
28188
29357
  complete: async ({ messages, maxOutputTokens }) => {
28189
29358
  const turn = await state.model.complete({
28190
- messages: messages.map((message2) => ({ role: message2.role, content: message2.content })),
29359
+ messages: messages.map((message3) => ({ role: message3.role, content: message3.content })),
28191
29360
  tools: [],
28192
29361
  maxOutputTokens,
28193
29362
  safetyIdentifier: state.agent.sessionId
@@ -28257,7 +29426,7 @@ function createTurnRunner(context) {
28257
29426
  spinner?.stop();
28258
29427
  statusBar.clear();
28259
29428
  endStreamedLine();
28260
- const spoken = [...result.messages].reverse().find((message2) => message2.role === "assistant" && !("toolCalls" in message2) && message2.content.trim());
29429
+ const spoken = [...result.messages].reverse().find((message3) => message3.role === "assistant" && !("toolCalls" in message3) && message3.content.trim());
28261
29430
  const spokenText = spoken?.content.trim();
28262
29431
  const asMarkdown = (text) => renderMarkdown(text, { width: contentWidth(), depth, palette });
28263
29432
  if (!state.streamedAnswer) {
@@ -28377,7 +29546,7 @@ ${result.status === "completed" ? asMarkdown(result.summary) : style.yellow(resu
28377
29546
  };
28378
29547
  }
28379
29548
 
28380
- // packages/archymedes-cli/src/app/interrupts.ts
29549
+ // src/app/interrupts.ts
28381
29550
  function createSigintHandling(options) {
28382
29551
  const { readline, state } = options;
28383
29552
  const handleSigint = () => {
@@ -28418,8 +29587,8 @@ function createSigintHandling(options) {
28418
29587
  return { bindSigint, unbindSigint };
28419
29588
  }
28420
29589
 
28421
- // packages/archymedes-cli/src/app/session-start.ts
28422
- import path36 from "node:path";
29590
+ // src/app/session-start.ts
29591
+ import path39 from "node:path";
28423
29592
  async function resumeAtStartup(context) {
28424
29593
  const { args, interactive, stateHistory, state, openClient } = context;
28425
29594
  if (!args.resume)
@@ -28480,7 +29649,7 @@ async function runOneShot(context) {
28480
29649
  });
28481
29650
  const ran = await context.runTurn(prompt);
28482
29651
  if (ran && state.workspace.kind === "e2b") {
28483
- const destination = path36.resolve(args.root, "archymedes-pull");
29652
+ const destination = path39.resolve(args.root, "archymedes-pull");
28484
29653
  const pulled = await downloadProject(state.workspace, destination);
28485
29654
  out.write(style.dim(` pulled ${pulled.written.length} files into ${destination}
28486
29655
  `));
@@ -28491,7 +29660,7 @@ async function runOneShot(context) {
28491
29660
  return args.json ? exitCodeForStatus2(state.lastTurnStatus) : ran ? 0 : 1;
28492
29661
  }
28493
29662
 
28494
- // packages/archymedes-cli/src/archymedes.ts
29663
+ // src/archymedes.ts
28495
29664
  async function main() {
28496
29665
  process.stdout.on("error", (error) => {
28497
29666
  if (error.code === "EPIPE")
@@ -28913,7 +30082,7 @@ async function main() {
28913
30082
  let agent = await openClient();
28914
30083
  const tabs = new WorkspaceController;
28915
30084
  const firstSink = new TabSink(sessionStream, { live: true });
28916
- tabs.adopt(path37.basename(args.root) || "archymedes", {
30085
+ tabs.adopt(path40.basename(args.root) || "archymedes", {
28917
30086
  agent,
28918
30087
  ledger,
28919
30088
  mode,
@@ -28961,7 +30130,7 @@ async function main() {
28961
30130
  let sessionRequest;
28962
30131
  const recoveryState = { last: null };
28963
30132
  let lastTurnEndedAt;
28964
- const where = workspace.kind === "e2b" ? `sandbox ${workspace.label.split(":")[1]}` : path37.basename(args.root);
30133
+ const where = workspace.kind === "e2b" ? `sandbox ${workspace.label.split(":")[1]}` : path40.basename(args.root);
28965
30134
  const runTurn = createTurnRunner({
28966
30135
  args,
28967
30136
  environment,