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.
- package/README.md +7 -3
- package/bin/archymedes.mjs +0 -0
- package/dist/{archymedes-qm9bwbe7.mjs → archymedes-cfwtgnjx.mjs} +3 -3
- package/dist/{archymedes-0jxzsh4z.mjs → archymedes-d5wqcmdr.mjs} +97 -97
- package/dist/{archymedes-1g1y6q74.mjs → archymedes-h13ap0za.mjs} +1 -1
- package/dist/{archymedes-2119874q.mjs → archymedes-hr1bssj4.mjs} +154 -154
- package/dist/archymedes-main.mjs +1692 -523
- package/dist/{factory-wc4p77tw.mjs → factory-3a3bffgd.mjs} +326 -326
- package/dist/{index-teq1cta4.mjs → index-qvgcwsvm.mjs} +1 -1
- package/dist/{node-0fh63p8z.mjs → node-mdgzx68h.mjs} +7 -7
- package/dist/{projection-4jjdaf41.mjs → projection-ndr3t77h.mjs} +2 -2
- package/dist/{sandbox-templates-8sd1psez.mjs → sandbox-templates-9jcjws23.mjs} +2 -2
- package/package.json +8 -8
package/dist/archymedes-main.mjs
CHANGED
|
@@ -7,7 +7,7 @@ import {
|
|
|
7
7
|
eventJournalPath,
|
|
8
8
|
readEventJournal,
|
|
9
9
|
runtimeEventForJournal
|
|
10
|
-
} from "./archymedes-
|
|
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-
|
|
48
|
+
} from "./archymedes-hr1bssj4.mjs";
|
|
49
49
|
import {
|
|
50
50
|
__require
|
|
51
51
|
} from "./archymedes-x67v3n58.mjs";
|
|
52
52
|
|
|
53
|
-
//
|
|
53
|
+
// src/archymedes.ts
|
|
54
54
|
import { createInterface as createInterface5 } from "readline/promises";
|
|
55
|
-
import
|
|
55
|
+
import path40 from "path";
|
|
56
56
|
import { realpathSync } from "fs";
|
|
57
57
|
import { pathToFileURL as pathToFileURL3 } from "url";
|
|
58
58
|
|
|
59
|
-
//
|
|
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
|
-
//
|
|
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
|
-
//
|
|
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
|
-
//
|
|
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
|
-
//
|
|
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
|
-
//
|
|
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
|
-
"
|
|
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
|
-
"
|
|
1073
|
-
"
|
|
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
|
-
//
|
|
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
|
-
//
|
|
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-
|
|
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
|
-
//
|
|
1506
|
+
// ../core/src/providers/archymedes-cloud-agent.ts
|
|
1498
1507
|
import { createHash as createHash2, randomUUID as randomUUID2 } from "node:crypto";
|
|
1499
1508
|
|
|
1500
|
-
//
|
|
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
|
-
//
|
|
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
|
-
//
|
|
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
|
-
//
|
|
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
|
-
//
|
|
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
|
-
//
|
|
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
|
-
//
|
|
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
|
-
//
|
|
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
|
-
//
|
|
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
|
-
//
|
|
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
|
-
//
|
|
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,
|
|
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
|
-
//
|
|
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
|
-
//
|
|
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
|
-
//
|
|
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
|
|
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) =>
|
|
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
|
|
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(
|
|
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
|
-
...
|
|
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
|
-
//
|
|
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
|
-
//
|
|
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
|
-
//
|
|
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
|
-
//
|
|
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
|
-
//
|
|
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
|
-
//
|
|
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
|
-
//
|
|
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
|
-
//
|
|
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
|
-
//
|
|
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
|
-
//
|
|
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
|
-
//
|
|
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()
|
|
5008
|
-
throw new Error(`Choose one of: ${[...PROVIDER_IDS,
|
|
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
|
-
//
|
|
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
|
-
//
|
|
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
|
-
//
|
|
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
|
-
//
|
|
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
|
-
//
|
|
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
|
-
//
|
|
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
|
-
//
|
|
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
|
-
//
|
|
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
|
-
//
|
|
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
|
-
//
|
|
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
|
-
//
|
|
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
|
-
//
|
|
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
|
-
//
|
|
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
|
-
//
|
|
6384
|
+
// ../core/src/cli/permissions.ts
|
|
6242
6385
|
import { createHash as createHash4 } from "node:crypto";
|
|
6243
6386
|
|
|
6244
|
-
//
|
|
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
|
-
//
|
|
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
|
-
//
|
|
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
|
-
//
|
|
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
|
-
//
|
|
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
|
-
//
|
|
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
|
-
//
|
|
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
|
-
//
|
|
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
|
-
//
|
|
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
|
-
//
|
|
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
|
-
//
|
|
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
|
-
//
|
|
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
|
-
//
|
|
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
|
-
//
|
|
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
|
-
//
|
|
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
|
-
//
|
|
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
|
-
//
|
|
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
|
-
//
|
|
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
|
-
//
|
|
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
|
-
//
|
|
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
|
-
//
|
|
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
|
-
//
|
|
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
|
-
//
|
|
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
|
-
//
|
|
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
|
-
//
|
|
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
|
-
//
|
|
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
|
-
//
|
|
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
|
-
//
|
|
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
|
-
//
|
|
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
|
-
//
|
|
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
|
-
//
|
|
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
|
-
//
|
|
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
|
-
//
|
|
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
|
-
//
|
|
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
|
-
//
|
|
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
|
-
//
|
|
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
|
-
//
|
|
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
|
-
//
|
|
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
|
-
//
|
|
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
|
-
//
|
|
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
|
-
//
|
|
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
|
-
//
|
|
13775
|
+
// ../core/src/cli/backends.ts
|
|
13633
13776
|
import path19 from "node:path";
|
|
13634
13777
|
|
|
13635
|
-
//
|
|
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
|
-
//
|
|
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
|
-
//
|
|
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
|
-
//
|
|
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
|
-
//
|
|
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
|
-
//
|
|
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
|
-
//
|
|
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
|
-
//
|
|
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
|
-
//
|
|
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
|
-
//
|
|
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
|
-
//
|
|
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
|
-
//
|
|
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
|
-
//
|
|
16230
|
+
// ../core/src/lsp/client.ts
|
|
16088
16231
|
import { spawn as spawn7, spawnSync as spawnSync2 } from "node:child_process";
|
|
16089
16232
|
|
|
16090
|
-
//
|
|
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
|
-
//
|
|
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
|
-
//
|
|
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
|
-
//
|
|
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
|
-
//
|
|
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
|
-
//
|
|
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
|
-
//
|
|
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
|
-
//
|
|
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
|
-
//
|
|
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
|
-
//
|
|
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
|
-
//
|
|
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
|
-
//
|
|
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
|
-
//
|
|
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
|
-
//
|
|
17457
|
+
// src/app/claude/identity.ts
|
|
17315
17458
|
var CLAUDE_PROVIDER = "claude";
|
|
17316
17459
|
var CLAUDE_LABEL = "Claude (your subscription)";
|
|
17317
17460
|
|
|
17318
|
-
//
|
|
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
|
-
//
|
|
17568
|
+
// src/app/claude/session.ts
|
|
17426
17569
|
import { randomUUID as randomUUID8 } from "node:crypto";
|
|
17427
17570
|
|
|
17428
|
-
//
|
|
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
|
-
//
|
|
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
|
|
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 = [
|
|
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
|
-
//
|
|
18107
|
-
|
|
18108
|
-
|
|
18109
|
-
|
|
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
|
-
//
|
|
18118
|
-
|
|
18119
|
-
|
|
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
|
-
//
|
|
18127
|
-
|
|
18128
|
-
|
|
18129
|
-
|
|
18130
|
-
|
|
18131
|
-
|
|
18132
|
-
|
|
18133
|
-
|
|
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
|
-
|
|
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
|
|
18140
|
-
|
|
18141
|
-
|
|
18142
|
-
|
|
18143
|
-
|
|
18144
|
-
|
|
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
|
-
|
|
18148
|
-
|
|
18149
|
-
|
|
18150
|
-
|
|
18151
|
-
|
|
18152
|
-
|
|
18153
|
-
|
|
18154
|
-
|
|
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
|
-
|
|
18157
|
-
|
|
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
|
-
|
|
18160
|
-
|
|
18485
|
+
notify(method, params) {
|
|
18486
|
+
if (!this.closed)
|
|
18487
|
+
this.send({ jsonrpc: "2.0", method, ...params === undefined ? {} : { params } });
|
|
18161
18488
|
}
|
|
18162
|
-
|
|
18163
|
-
this.
|
|
18489
|
+
get running() {
|
|
18490
|
+
return !this.closed;
|
|
18164
18491
|
}
|
|
18165
|
-
|
|
18166
|
-
|
|
18492
|
+
close() {
|
|
18493
|
+
if (this.closed)
|
|
18494
|
+
return;
|
|
18495
|
+
this.transport.close();
|
|
18496
|
+
this.gone("closed");
|
|
18167
18497
|
}
|
|
18168
|
-
|
|
18169
|
-
|
|
18498
|
+
send(message2) {
|
|
18499
|
+
this.transport.write(`${JSON.stringify(message2)}
|
|
18500
|
+
`);
|
|
18170
18501
|
}
|
|
18171
|
-
|
|
18172
|
-
|
|
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
|
-
|
|
18175
|
-
|
|
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
|
-
|
|
18178
|
-
|
|
18179
|
-
|
|
18180
|
-
|
|
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
|
|
18192
|
-
if (
|
|
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 =
|
|
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
|
|
18209
|
-
if ("error" in
|
|
18210
|
-
throw new Error(
|
|
18211
|
-
const { signedIn, ...start } =
|
|
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
|
-
//
|
|
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
|
-
//
|
|
18365
|
-
import { spawn as
|
|
19518
|
+
// src/app/startup-update.ts
|
|
19519
|
+
import { spawn as spawn10 } from "node:child_process";
|
|
18366
19520
|
|
|
18367
|
-
//
|
|
19521
|
+
// src/platform/auto-update.ts
|
|
18368
19522
|
import { promises as fs19 } from "node:fs";
|
|
18369
|
-
import
|
|
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
|
|
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(
|
|
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
|
-
//
|
|
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
|
-
//
|
|
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
|
-
//
|
|
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
|
-
//
|
|
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
|
-
//
|
|
19990
|
+
// package.json
|
|
18837
19991
|
var package_default = {
|
|
18838
19992
|
name: "archymedes-cli",
|
|
18839
|
-
version: "3.
|
|
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.
|
|
18886
|
-
"@archymedes/cli-darwin-x64": "3.
|
|
18887
|
-
"@archymedes/cli-linux-arm64": "3.
|
|
18888
|
-
"@archymedes/cli-linux-x64": "3.
|
|
18889
|
-
"@archymedes/cli-win32-arm64": "3.
|
|
18890
|
-
"@archymedes/cli-win32-x64": "3.
|
|
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
|
-
//
|
|
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
|
|
20247
|
+
const spawn10 = options.spawnImpl ?? ((executable2, args, spawnOptions) => spawnSync4(executable2, args, spawnOptions));
|
|
19094
20248
|
let result;
|
|
19095
20249
|
try {
|
|
19096
|
-
result =
|
|
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
|
-
//
|
|
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
|
-
//
|
|
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
|
-
//
|
|
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
|
-
//
|
|
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
|
-
//
|
|
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
|
|
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
|
-
//
|
|
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
|
-
//
|
|
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
|
|
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 - (
|
|
20421
|
-
while (widths.reduce((sum, width) => sum + width, 0) > Math.max(
|
|
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
|
|
20444
|
-
const index = Math.max(0, Math.min(Math.floor(current),
|
|
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:
|
|
21601
|
+
return Array.from({ length: count4 }, (_, position) => position === index ? glyphs.bullet : glyphs.middot).join(" ");
|
|
20448
21602
|
}
|
|
20449
|
-
return `${index + 1}/${
|
|
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
|
-
//
|
|
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
|
|
20774
|
-
return `${clean.slice(0, head)}…${
|
|
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
|
|
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
|
|
20790
|
-
return limit2 === undefined ? `${
|
|
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
|
|
20893
|
-
return
|
|
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
|
-
//
|
|
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
|
-
//
|
|
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
|
-
//
|
|
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
|
|
21393
|
-
for (let index = 0;index <
|
|
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
|
-
//
|
|
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
|
-
//
|
|
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
|
-
//
|
|
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 =
|
|
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
|
-
//
|
|
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 ?? ((
|
|
22942
|
+
const warn = options.warn ?? ((message3) => process.stderr.write(`${message3}
|
|
21789
22943
|
`));
|
|
21790
|
-
const send = (
|
|
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 () =>
|
|
21798
|
-
|
|
21799
|
-
|
|
21800
|
-
|
|
21801
|
-
"_archymedes/
|
|
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) =>
|
|
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, (
|
|
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(
|
|
21864
|
-
return typeof
|
|
23030
|
+
function isPrompt(message3) {
|
|
23031
|
+
return typeof message3 === "object" && message3 !== null && message3.method === "session/prompt";
|
|
21865
23032
|
}
|
|
21866
23033
|
|
|
21867
|
-
//
|
|
21868
|
-
import
|
|
23034
|
+
// src/app/args.ts
|
|
23035
|
+
import path33 from "node:path";
|
|
21869
23036
|
|
|
21870
|
-
//
|
|
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
|
-
//
|
|
23062
|
+
// src/commands/chat-history.ts
|
|
21896
23063
|
function countTurns(messages) {
|
|
21897
|
-
return messages.filter((
|
|
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((
|
|
21917
|
-
if (
|
|
23083
|
+
const searchText = record2.messages.flatMap((message3) => {
|
|
23084
|
+
if (message3.role === "system" || message3.internal)
|
|
21918
23085
|
return [];
|
|
21919
|
-
if (
|
|
21920
|
-
return [
|
|
21921
|
-
if (
|
|
21922
|
-
return [
|
|
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 [
|
|
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((
|
|
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
|
|
22014
|
-
if (
|
|
23180
|
+
for (const message3 of record2.messages.slice(from)) {
|
|
23181
|
+
if (message3.role === "system" || message3.internal)
|
|
22015
23182
|
continue;
|
|
22016
|
-
if (
|
|
23183
|
+
if (message3.role === "user") {
|
|
22017
23184
|
turn += 1;
|
|
22018
23185
|
out2.push(rule(style2, { label: `turn ${turn}`, tone: "accent" }));
|
|
22019
|
-
out2.push(
|
|
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 (
|
|
23191
|
+
if (message3.role === "tool")
|
|
22025
23192
|
continue;
|
|
22026
|
-
if ("toolCalls" in
|
|
23193
|
+
if ("toolCalls" in message3 && message3.toolCalls.length > 0) {
|
|
22027
23194
|
if (options.tools === false)
|
|
22028
23195
|
continue;
|
|
22029
|
-
for (const call of
|
|
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 (!
|
|
23200
|
+
if (!message3.content.trim())
|
|
22034
23201
|
continue;
|
|
22035
23202
|
}
|
|
22036
|
-
if (
|
|
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(
|
|
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
|
-
//
|
|
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
|
-
//
|
|
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 =
|
|
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
|
-
//
|
|
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
|
-
//
|
|
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
|
|
22449
|
-
return `${head}${middle}${
|
|
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
|
|
22463
|
-
if (!
|
|
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(
|
|
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(
|
|
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
|
|
22562
|
-
if (
|
|
23728
|
+
const runtime3 = event.event;
|
|
23729
|
+
if (runtime3.type === "provider_retry") {
|
|
22563
23730
|
forgetToolLines2();
|
|
22564
|
-
const reason =
|
|
22565
|
-
const wait2 =
|
|
22566
|
-
out2.write(style2.yellow(` ${glyphs2.elbow} ${reason}; retrying model request ${
|
|
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 (
|
|
22571
|
-
activity2.tokens +=
|
|
22572
|
-
if (
|
|
22573
|
-
activity2.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 (
|
|
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(
|
|
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),
|
|
22596
|
-
pendingCalls2.set(
|
|
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 =
|
|
23765
|
+
activity2.operation = runtime3.toolName;
|
|
22599
23766
|
spinner2?.start();
|
|
22600
23767
|
return;
|
|
22601
23768
|
}
|
|
22602
|
-
if (
|
|
23769
|
+
if (runtime3.type === "tool_result") {
|
|
22603
23770
|
activity2.toolCalls += 1;
|
|
22604
|
-
const verificationKind = typeof
|
|
23771
|
+
const verificationKind = typeof runtime3.data?.verificationKind === "string" ? runtime3.data.verificationKind : undefined;
|
|
22605
23772
|
if (verificationKind) {
|
|
22606
|
-
verificationChecks2.set(verificationKind, !
|
|
22607
|
-
sessionChecks2.set(verificationKind, !
|
|
23773
|
+
verificationChecks2.set(verificationKind, !runtime3.isError);
|
|
23774
|
+
sessionChecks2.set(verificationKind, !runtime3.isError);
|
|
22608
23775
|
}
|
|
22609
|
-
const items = Array.isArray(
|
|
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 =
|
|
22614
|
-
const summary = summarizeToolResult(
|
|
22615
|
-
const pending = pendingCalls2.get(
|
|
22616
|
-
const completed = toolLineText(mark,
|
|
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(
|
|
22622
|
-
if (
|
|
22623
|
-
const 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 (!
|
|
22628
|
-
if (
|
|
23794
|
+
if (!runtime3.isError && pending) {
|
|
23795
|
+
if (runtime3.toolName === "write_file" || runtime3.toolName === "edit_file") {
|
|
22629
23796
|
toolLines2.forget();
|
|
22630
|
-
renderWrittenCode(
|
|
22631
|
-
const
|
|
22632
|
-
if (
|
|
22633
|
-
touchedFiles2.add(
|
|
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 (
|
|
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 (
|
|
22651
|
-
const prior = sessionFiles2.get(
|
|
22652
|
-
sessionFiles2.set(
|
|
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 (
|
|
23821
|
+
} else if (runtime3.toolName === "run_command") {
|
|
22655
23822
|
toolLines2.forget();
|
|
22656
|
-
renderCommandOutput(
|
|
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
|
-
//
|
|
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 = (
|
|
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
|
-
//
|
|
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
|
|
23092
|
-
if (
|
|
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),
|
|
23095
|
-
return Math.max(0, Math.min(cursor - Math.floor(rows / 2),
|
|
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
|
|
23198
|
-
lines.push(` ${head}${paint2.dim(clip2(
|
|
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
|
-
//
|
|
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
|
-
//
|
|
23320
|
-
import { randomUUID as
|
|
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
|
|
23328
|
-
if (!
|
|
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:
|
|
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:
|
|
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:
|
|
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_${
|
|
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_${
|
|
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_${
|
|
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
|
-
//
|
|
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
|
-
//
|
|
23761
|
-
import
|
|
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(
|
|
23787
|
-
return readTextFile(this.root,
|
|
24953
|
+
readFile(path35, options = {}) {
|
|
24954
|
+
return readTextFile(this.root, path35, { ...options, limits: this.limits });
|
|
23788
24955
|
}
|
|
23789
|
-
readBytes(
|
|
23790
|
-
return readBinaryFile(this.root,
|
|
24956
|
+
readBytes(path35) {
|
|
24957
|
+
return readBinaryFile(this.root, path35, this.limits);
|
|
23791
24958
|
}
|
|
23792
|
-
writeFile(
|
|
23793
|
-
return writeTextFile(this.root,
|
|
24959
|
+
writeFile(path35, content) {
|
|
24960
|
+
return writeTextFile(this.root, path35, content, this.limits);
|
|
23794
24961
|
}
|
|
23795
|
-
editFile(
|
|
23796
|
-
return editTextFile(this.root,
|
|
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 =
|
|
23818
|
-
if (!base.startsWith(
|
|
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
|
-
//
|
|
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
|
-
//
|
|
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
|
|
24197
|
-
if (
|
|
24198
|
-
this.emit("text", { 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(
|
|
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(
|
|
24249
|
-
this.emit("error", { message:
|
|
25415
|
+
error(message3, fields = {}) {
|
|
25416
|
+
this.emit("error", { message: message3, ...fields });
|
|
24250
25417
|
}
|
|
24251
25418
|
}
|
|
24252
25419
|
|
|
24253
|
-
//
|
|
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
|
-
//
|
|
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
|
-
//
|
|
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
|
-
//
|
|
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
|
-
//
|
|
25849
|
+
// src/platform/settings.ts
|
|
24683
25850
|
import { promises as fs20 } from "node:fs";
|
|
24684
|
-
import
|
|
24685
|
-
import
|
|
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
|
|
26063
|
+
return path35.resolve(environment.ARCHYMEDES_CONFIG_DIR);
|
|
24895
26064
|
if (platform === "win32")
|
|
24896
|
-
return
|
|
26065
|
+
return path35.join(environment.APPDATA?.trim() || path35.join(os8.homedir(), "AppData", "Roaming"), "Archymedes");
|
|
24897
26066
|
if (platform === "darwin")
|
|
24898
|
-
return
|
|
24899
|
-
return
|
|
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
|
|
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
|
-
//
|
|
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
|
-
//
|
|
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
|
-
//
|
|
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(
|
|
25162
|
-
return
|
|
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
|
-
//
|
|
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
|
-
//
|
|
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
|
-
//
|
|
26706
|
+
// src/theme/theme-files.ts
|
|
25538
26707
|
import { promises as fs21 } from "node:fs";
|
|
25539
|
-
import
|
|
26708
|
+
import path36 from "node:path";
|
|
25540
26709
|
function themeDirectory(scope, root, environment) {
|
|
25541
|
-
return scope === "project" ?
|
|
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 =
|
|
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
|
-
//
|
|
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
|
-
//
|
|
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
|
-
//
|
|
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
|
-
//
|
|
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
|
-
//
|
|
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
|
-
//
|
|
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
|
-
//
|
|
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
|
-
//
|
|
27290
|
+
// ../core/src/cli/job-store.ts
|
|
26122
27291
|
import { promises as fs22 } from "node:fs";
|
|
26123
|
-
import
|
|
27292
|
+
import path37 from "node:path";
|
|
26124
27293
|
|
|
26125
|
-
//
|
|
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
|
-
//
|
|
27469
|
+
// ../core/src/cli/job-store.ts
|
|
26301
27470
|
function jobStoreFile(root) {
|
|
26302
|
-
return
|
|
27471
|
+
return path37.join(root, ".archymedes", "jobs.json");
|
|
26303
27472
|
}
|
|
26304
27473
|
function jobLogPath(root, id) {
|
|
26305
|
-
return
|
|
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(
|
|
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(
|
|
27585
|
+
await fs22.mkdir(path37.dirname(file), { recursive: true });
|
|
26417
27586
|
await fs22.appendFile(file, `${line}
|
|
26418
27587
|
`, "utf8");
|
|
26419
27588
|
}
|
|
26420
|
-
//
|
|
26421
|
-
import
|
|
27589
|
+
// src/job-worker.ts
|
|
27590
|
+
import os9 from "node:os";
|
|
26422
27591
|
|
|
26423
|
-
//
|
|
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:
|
|
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
|
-
//
|
|
26520
|
-
function workerId(hostname =
|
|
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
|
|
26657
|
-
await writeLogSafely(deps.root, job.id, `✗ ${
|
|
26658
|
-
await finishJob(deps.root, job.id, owner, "failed", { error:
|
|
26659
|
-
return { outcome: "failed", error:
|
|
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
|
-
//
|
|
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
|
-
//
|
|
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
|
-
//
|
|
27974
|
+
// src/app/startup-commands.ts
|
|
26806
27975
|
import { createInterface as createInterface4 } from "node:readline/promises";
|
|
26807
27976
|
|
|
26808
|
-
//
|
|
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
|
-
//
|
|
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
|
-
//
|
|
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
|
-
//
|
|
28140
|
+
// src/session/delete-session.ts
|
|
26972
28141
|
import { promises as fs23 } from "node:fs";
|
|
26973
|
-
import
|
|
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 =
|
|
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-
|
|
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(
|
|
28175
|
+
removeIfPresent(path38.join(sessionDirectory(root), `${id}.json`)),
|
|
27007
28176
|
removeIfPresent(sessionJournalPath(root, id)),
|
|
27008
28177
|
removeIfPresent(eventJournalPath(root, id)),
|
|
27009
|
-
removeIfPresent(
|
|
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
|
-
//
|
|
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
|
-
//
|
|
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
|
-
//
|
|
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
|
-
//
|
|
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
|
-
//
|
|
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
|
-
//
|
|
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-
|
|
27534
|
-
const { createE2BProvider } = await import("./factory-
|
|
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-
|
|
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
|
-
//
|
|
28758
|
+
// ../core/src/cli/balance.ts
|
|
27590
28759
|
var CRITICAL_BALANCE_USD2 = 0.5;
|
|
27591
28760
|
var LOW_BALANCE_USD2 = 2;
|
|
27592
28761
|
|
|
27593
|
-
//
|
|
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
|
|
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)} (${
|
|
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
|
-
//
|
|
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
|
-
//
|
|
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
|
-
//
|
|
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,
|
|
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 = (
|
|
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:
|
|
27829
|
-
prices: modelPriceCatalogFor(
|
|
27830
|
-
budgets: applyPacing(approvedBudget && (
|
|
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:
|
|
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
|
-
//
|
|
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
|
-
//
|
|
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
|
-
//
|
|
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
|
-
//
|
|
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
|
-
//
|
|
29185
|
+
// src/app/session-hints.ts
|
|
28017
29186
|
var failureStatus = (status) => status === "needs_approval" ? "waiting_approval" : status;
|
|
28018
29187
|
|
|
28019
|
-
//
|
|
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
|
|
28035
|
-
if (/exceeds the reserved model budget/i.test(
|
|
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:
|
|
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")} ${
|
|
29230
|
+
out.write(`${style.red("error")} ${message3}
|
|
28062
29231
|
`);
|
|
28063
29232
|
}
|
|
28064
|
-
state.lastFailure = { status: "failed", message:
|
|
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(
|
|
29239
|
+
headless?.error(message3, { status: "failed" });
|
|
28071
29240
|
return false;
|
|
28072
29241
|
}
|
|
28073
29242
|
|
|
28074
|
-
//
|
|
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
|
-
//
|
|
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((
|
|
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((
|
|
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
|
-
//
|
|
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
|
-
//
|
|
28422
|
-
import
|
|
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 =
|
|
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
|
-
//
|
|
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(
|
|
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]}` :
|
|
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,
|