archymedes-cli 3.1.0 → 3.3.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -7,9 +7,10 @@ import {
7
7
  eventJournalPath,
8
8
  readEventJournal,
9
9
  runtimeEventForJournal
10
- } from "./archymedes-qm9bwbe7.mjs";
10
+ } from "./archymedes-cfwtgnjx.mjs";
11
11
  import {
12
12
  APIConnectionError,
13
+ BoundedAgentRuntime,
13
14
  DEFAULT_OUTPUT_CEILING,
14
15
  DEFAULT_TOTAL_TIMEOUT_MS,
15
16
  OpenAI,
@@ -19,9 +20,8 @@ import {
19
20
  WANDER_GRADES,
20
21
  WANDER_LAB_FILES,
21
22
  WANDER_MARKER,
22
- addPart,
23
23
  affordableOutputTokens,
24
- affordableOutputTokensFor,
24
+ agentMessagePromptParts,
25
25
  approximateInputTokens,
26
26
  buildTaskProfile,
27
27
  buildWanderObjective,
@@ -31,29 +31,31 @@ import {
31
31
  isWanderObjective,
32
32
  isWanderRandomScheduleObjective,
33
33
  mergeRoutingReceipts,
34
- newPartTotals,
35
34
  parseWanderResults,
36
35
  pickWanderTopic,
36
+ predictEffort,
37
37
  priceActualModelUsage,
38
+ providerEffort,
39
+ providerRetryAfterMs,
40
+ reasoningFromEnvironment,
38
41
  streamTimeoutsFor,
39
42
  toWireMessages,
40
- tokenEstimateFrom,
41
43
  turnFromChatResponse,
42
44
  wanderGradeCounts,
43
45
  wanderPlannerInstructions,
44
46
  withOpenRouterCacheControl
45
- } from "./archymedes-95xvcvww.mjs";
47
+ } from "./archymedes-j0vrbr99.mjs";
46
48
  import {
47
49
  __require
48
50
  } from "./archymedes-x67v3n58.mjs";
49
51
 
50
- // packages/archymedes-cli/src/archymedes.ts
52
+ // src/archymedes.ts
51
53
  import { createInterface as createInterface5 } from "readline/promises";
52
54
  import path30 from "path";
53
55
  import { realpathSync } from "fs";
54
56
  import { pathToFileURL as pathToFileURL2 } from "url";
55
57
 
56
- // packages/core/src/cli/acp.ts
58
+ // ../core/src/cli/acp.ts
57
59
  var ACP_PROTOCOL_VERSION = 1;
58
60
  var JSON_RPC = { parseError: -32700, invalidRequest: -32600, methodNotFound: -32601, invalidParams: -32602, internalError: -32603 };
59
61
  var ARCHYMEDES_EVENT_METHOD = "_archymedes/event";
@@ -402,7 +404,7 @@ function toolCallTitle(toolName, argumentsValue) {
402
404
  return subject ? `${label}: ${String(subject).slice(0, 120)}` : label;
403
405
  }
404
406
 
405
- // packages/core/src/cli/image-attachments.ts
407
+ // ../core/src/cli/image-attachments.ts
406
408
  var MAX_IMAGES_PER_TURN = 4;
407
409
  var MAX_IMAGE_BYTES = 5 * 1024 * 1024;
408
410
  var MENTION = /(?:^|\s)@((?:[\w.-]+\/)*[\w.-]+\.(?:png|jpe?g|gif|webp))(?=$|[\s,;:!?)])/gi;
@@ -467,7 +469,7 @@ function withoutImageData(messages) {
467
469
  });
468
470
  }
469
471
 
470
- // packages/core/src/cli/hosted-recovery.ts
472
+ // ../core/src/cli/hosted-recovery.ts
471
473
  import { createHash, randomUUID } from "node:crypto";
472
474
  import { promises as fs } from "node:fs";
473
475
  import path from "node:path";
@@ -726,677 +728,14 @@ ${entry.response.content}` });
726
728
  }
727
729
  }
728
730
 
729
- // packages/core/src/cli/agent.ts
730
- import { randomUUID as randomUUID5 } from "node:crypto";
731
+ // ../core/src/cli/agent.ts
732
+ import { randomUUID as randomUUID4 } from "node:crypto";
731
733
  import path15 from "node:path";
732
734
  import { access as access3 } from "node:fs/promises";
733
735
 
734
- // packages/core/src/agent-runtime.ts
735
- import { createHash as createHash2, randomUUID as randomUUID2 } from "node:crypto";
736
- var IMAGE_TOKEN_ESTIMATE = 1600;
737
- var IMAGE_CHAR_ESTIMATE = IMAGE_TOKEN_ESTIMATE * 4;
738
- function agentMessagePromptParts(message) {
739
- const parts = [message.content ?? ""];
740
- if ("images" in message)
741
- for (const _image of message.images ?? [])
742
- parts.push(" ".repeat(IMAGE_CHAR_ESTIMATE));
743
- if ("toolCalls" in message && Array.isArray(message.toolCalls)) {
744
- for (const call of message.toolCalls)
745
- parts.push(call.id, call.name, JSON.stringify(call.arguments ?? {}));
746
- } else if (message.role === "tool") {
747
- parts.push(message.toolCallId, message.name);
748
- }
749
- return parts;
750
- }
751
- var EVIDENCE_RANK = { check: 1, tests: 2, smoke: 3, behavior: 4 };
752
- var MAX_VERIFICATION_NUDGES = 1;
753
- var MAX_UNAVAILABLE_TOOL_RECOVERIES = 1;
754
- var PROVIDER_BACKOFF_BASE_MS = 1000;
755
- var PROVIDER_BACKOFF_CAP_MS = 30000;
756
- var PROVIDER_RETRY_AFTER_CAP_MS = 60000;
757
- var MAX_TOOL_TURN_RECOVERIES = 2;
758
- var RETRY_POLICIES = {
759
- server: { maxAttempts: 4, baseDelayMs: 1000, maxDelayMs: 15000 },
760
- rate_limit: { maxAttempts: 6, baseDelayMs: 2000, maxDelayMs: 60000 },
761
- timeout: { maxAttempts: 2, baseDelayMs: 1000, maxDelayMs: 8000 },
762
- network: { maxAttempts: 3, baseDelayMs: 250, maxDelayMs: 4000 },
763
- unknown: { maxAttempts: 3, baseDelayMs: 1000, maxDelayMs: 8000 }
764
- };
765
-
766
- class ProviderRequestError extends Error {
767
- attempts;
768
- kind;
769
- waitedMs;
770
- retrySuppressed;
771
- constructor(cause, options) {
772
- const original = cause instanceof Error ? cause.message : String(cause);
773
- const kind = options.kind ? ` (${options.kind.replace("_", " ")} error)` : "";
774
- const waited = options.waitedMs && options.waitedMs > 0 ? ` over ${(options.waitedMs / 1000).toFixed(1)}s of retries` : "";
775
- const detail = options.retrySuppressed === "output_started" ? "The model connection failed after output began, so Archymedes did not retry to avoid duplicated output, charges, or tool actions." : `The model request failed after ${options.attempts} attempt${options.attempts === 1 ? "" : "s"}${kind}${waited}.`;
776
- super(`${detail} Provider message: ${original}`, { cause });
777
- this.name = "ProviderRequestError";
778
- this.attempts = options.attempts;
779
- if (options.kind)
780
- this.kind = options.kind;
781
- if (options.waitedMs !== undefined)
782
- this.waitedMs = options.waitedMs;
783
- this.retrySuppressed = options.retrySuppressed ?? null;
784
- }
785
- }
786
- var MAX_LENGTH_CONTINUATIONS = 3;
787
- var LENGTH_CONTINUATION_NUDGE = "Your previous message hit the output token limit and was cut off mid-answer. Continue from exactly where it stopped — do not repeat what you already wrote, and do not restart. If you were part-way through a tool call, send that tool call again from the beginning, since the truncated one was discarded. If the remaining work does not fit in one reply, do the part that fits and keep each reply shorter.";
788
- var VERIFICATION_NUDGE = "You changed the workspace but ended the turn without running anything that verifies it. Run the smallest relevant existing test, check, or smoke command and report the real result. Add a regression test only when the change would otherwise be unprotected. If this project genuinely has nothing relevant to run, say so explicitly instead of stopping silently.";
789
- var TEST_EVIDENCE_NUDGE = "That build/typecheck passing shows the code compiles, not that it behaves correctly. Run the smallest relevant existing test or smoke command for the changed behaviour and report the real result. Add a regression test only when the change would otherwise be unprotected. If the change genuinely has no behaviour to assert (documentation, formatting, configuration), say so explicitly and stop.";
790
- var TRANSIENT_PROVIDER_CODES = new Set([
791
- "ABORT_ERR",
792
- "ECONNREFUSED",
793
- "ECONNRESET",
794
- "EAI_AGAIN",
795
- "ENETDOWN",
796
- "ENETUNREACH",
797
- "ETIMEDOUT",
798
- "UND_ERR_CONNECT_TIMEOUT",
799
- "UND_ERR_HEADERS_TIMEOUT",
800
- "UND_ERR_SOCKET"
801
- ]);
802
- function errorRecord(value) {
803
- return typeof value === "object" && value !== null ? value : undefined;
804
- }
805
- function isRetryableProviderError(error) {
806
- let current = error;
807
- const visited = new Set;
808
- const messages = [];
809
- for (let depth = 0;current !== undefined && current !== null && depth < 5 && !visited.has(current); depth += 1) {
810
- visited.add(current);
811
- const record = errorRecord(current);
812
- if (current instanceof Error)
813
- messages.push(current.message);
814
- else if (typeof current === "string")
815
- messages.push(current);
816
- if (!record)
817
- break;
818
- if (typeof record.retryable === "boolean")
819
- return record.retryable;
820
- const status = Number(record.status ?? record.statusCode);
821
- if (Number.isInteger(status)) {
822
- if (status === 408 || status === 409 || status === 425 || status === 429 || status === 500 || status === 502 || status === 503 || status === 504)
823
- return true;
824
- if (status >= 400 && status < 600)
825
- return false;
826
- }
827
- const code = typeof record.code === "string" ? record.code.toUpperCase() : "";
828
- if (TRANSIENT_PROVIDER_CODES.has(code))
829
- return true;
830
- current = record.cause;
831
- }
832
- return messages.some((message) => /\b(?:timed?\s*out|timeout|rate\s*limit|overload(?:ed)?|temporar(?:y|ily)|connection\s*reset|socket\s*hang\s*up|fetch\s*failed|network\s*error)\b/i.test(message));
833
- }
834
- function providerFailureKind(error) {
835
- let current = error;
836
- const visited = new Set;
837
- const messages = [];
838
- for (let depth = 0;current !== undefined && current !== null && depth < 5 && !visited.has(current); depth += 1) {
839
- visited.add(current);
840
- const record = errorRecord(current);
841
- if (current instanceof Error)
842
- messages.push(current.message);
843
- else if (typeof current === "string")
844
- messages.push(current);
845
- if (!record)
846
- break;
847
- const status = Number(record.status ?? record.statusCode);
848
- if (status === 408)
849
- return "timeout";
850
- if (status === 429)
851
- return "rate_limit";
852
- if (status >= 500 && status < 600)
853
- return "server";
854
- const code = typeof record.code === "string" ? record.code.toUpperCase() : "";
855
- if (/TIMEOUT/.test(code) || code === "ETIMEDOUT")
856
- return "timeout";
857
- if (TRANSIENT_PROVIDER_CODES.has(code))
858
- return "network";
859
- current = record.cause;
860
- }
861
- const message = messages.join(" ");
862
- if (/rate\s*limit|too many requests/i.test(message))
863
- return "rate_limit";
864
- if (/timed?\s*out|timeout/i.test(message))
865
- return "timeout";
866
- if (/overload|service unavailable|bad gateway|gateway timeout/i.test(message))
867
- return "server";
868
- if (/connection|socket|fetch failed|network/i.test(message))
869
- return "network";
870
- return "unknown";
871
- }
872
- function providerRetryDelayMs(error, attempt, random = Math.random, policy = { baseDelayMs: PROVIDER_BACKOFF_BASE_MS, maxDelayMs: PROVIDER_BACKOFF_CAP_MS }) {
873
- const retryAfter = providerRetryAfterMs(error);
874
- const base = Math.min(policy.maxDelayMs, policy.baseDelayMs * 2 ** Math.min(10, Math.max(0, attempt)));
875
- const jitter = 0.75 + 0.5 * Math.min(1, Math.max(0, random()));
876
- const backoff = Math.min(policy.maxDelayMs, Math.round(base * jitter));
877
- return retryAfter !== undefined ? Math.max(backoff, Math.min(PROVIDER_RETRY_AFTER_CAP_MS, Math.round(retryAfter))) : backoff;
878
- }
879
- function providerRetryAfterMs(error) {
880
- let current = error;
881
- const visited = new Set;
882
- for (let depth = 0;depth < 5 && current !== undefined && current !== null && !visited.has(current); depth += 1) {
883
- visited.add(current);
884
- const record = errorRecord(current);
885
- if (!record)
886
- break;
887
- const explicit = record.retryAfterMs;
888
- if (typeof explicit === "number" && Number.isFinite(explicit) && explicit >= 0)
889
- return explicit;
890
- const headers = record.headers;
891
- const raw = typeof headers?.get === "function" ? headers.get("retry-after") : headers?.["retry-after"];
892
- if (typeof raw === "string" && raw.trim() !== "") {
893
- const text = raw.trim();
894
- if (/^\d+(?:\.\d+)?$/.test(text))
895
- return Number(text) * 1000;
896
- const at = Date.parse(text);
897
- if (Number.isFinite(at))
898
- return Math.max(0, at - Date.now());
899
- }
900
- current = record.cause;
901
- }
902
- return;
903
- }
904
- function providerRetryDelay(delayMs, signal) {
905
- if (signal?.aborted)
906
- return Promise.resolve(false);
907
- return new Promise((resolve) => {
908
- const abort = () => finish(false);
909
- const finish = (elapsed) => {
910
- clearTimeout(timer);
911
- signal?.removeEventListener("abort", abort);
912
- resolve(elapsed);
913
- };
914
- const timer = setTimeout(() => finish(true), delayMs);
915
- signal?.addEventListener("abort", abort, { once: true });
916
- });
917
- }
918
- var emptyUsage = { inputTokens: 0, outputTokens: 0, totalTokens: 0, cachedInputTokens: 0, cacheWriteTokens: 0, reasoningTokens: 0 };
919
- function addUsage(total, next) {
920
- const values = Object.values(next);
921
- if (values.some((value) => !Number.isSafeInteger(value) || value < 0))
922
- throw new Error("Model returned invalid usage accounting");
923
- return {
924
- inputTokens: total.inputTokens + next.inputTokens,
925
- outputTokens: total.outputTokens + next.outputTokens,
926
- totalTokens: total.totalTokens + next.totalTokens,
927
- cachedInputTokens: total.cachedInputTokens + next.cachedInputTokens,
928
- cacheWriteTokens: total.cacheWriteTokens + next.cacheWriteTokens,
929
- reasoningTokens: total.reasoningTokens + next.reasoningTokens
930
- };
931
- }
932
- function validateRequest(request) {
933
- if (!request.taskId.trim() || !request.runId.trim() || !request.stepId.trim())
934
- throw new Error("Task, run, and step identity are required");
935
- if (!request.objective.trim() || !request.systemPrompt.trim())
936
- throw new Error("Objective and systemPrompt are required");
937
- const bounded = [
938
- [request.maxIterations, 1, 100, "maxIterations"],
939
- [request.maxToolCalls, 0, 500, "maxToolCalls"],
940
- [request.maxToolCallsPerTurn, 1, 16, "maxToolCallsPerTurn"],
941
- [request.maxToolResultChars, 128, 1e6, "maxToolResultChars"],
942
- [request.maxTotalToolResultChars, 128, 4000000, "maxTotalToolResultChars"],
943
- [request.maxOutputTokens, 256, 128000, "maxOutputTokens"]
944
- ];
945
- for (const [value, minimum, maximum, name] of bounded) {
946
- if (!Number.isInteger(value) || value < minimum || value > maximum)
947
- throw new Error(`${name} must be between ${minimum} and ${maximum}`);
948
- }
949
- if (!Number.isSafeInteger(request.modelReservationRwf) || request.modelReservationRwf < 0)
950
- throw new Error("modelReservationRwf must be a non-negative integer");
951
- validateHistory(request.history ?? []);
952
- }
953
- function validateHistory(history) {
954
- const pending = new Map;
955
- const seen = new Set;
956
- for (const message of history) {
957
- if (message.role === "system")
958
- throw new Error("Runtime history must not contain system messages");
959
- if (message.role === "tool") {
960
- const expectedName = pending.get(message.toolCallId);
961
- if (!expectedName)
962
- throw new Error(`Runtime history contains orphaned tool result ${message.toolCallId}`);
963
- if (expectedName !== message.name)
964
- throw new Error(`Runtime history tool result ${message.toolCallId} does not match ${expectedName}`);
965
- pending.delete(message.toolCallId);
966
- continue;
967
- }
968
- if (pending.size > 0)
969
- throw new Error("Runtime history contains unresolved tool calls before the next message");
970
- if (message.role === "assistant" && "toolCalls" in message) {
971
- for (const call of message.toolCalls) {
972
- if (!call.id.trim() || seen.has(call.id))
973
- throw new Error(`Runtime history contains missing or duplicate tool-call id ${call.id}`);
974
- seen.add(call.id);
975
- pending.set(call.id, call.name);
976
- }
977
- }
978
- }
979
- if (pending.size > 0)
980
- throw new Error("Runtime history ends with unresolved tool calls");
981
- }
982
- function normalizeArguments(value) {
983
- return typeof value === "object" && value !== null && !Array.isArray(value) ? value : undefined;
984
- }
985
- function truncate(value, maximum) {
986
- if (maximum <= 0)
987
- return "";
988
- if (maximum <= 32)
989
- return value.slice(0, maximum);
990
- return value.length <= maximum ? value : `${value.slice(0, Math.max(0, maximum - 32))}
991
- ...[tool result truncated]`;
992
- }
993
- var MAX_IN_TURN_COMPACTIONS = 8;
994
- var COMPACTION_MIN_CHARS = 400;
995
- function compactedToolResult(toolName, chars, artifactPath, images = []) {
996
- if (images.length > 0) {
997
- const note = `[elided: ${images.length} image${images.length === 1 ? "" : "s"} (${images.map((image) => image.path).join(", ")}) from earlier ${toolName} output, to keep the conversation within its context budget; view ${images.length === 1 ? "it" : "them"} again if you need to]`;
998
- return chars > 0 ? `${compactedToolResult(toolName, chars, artifactPath)}
999
- ${note}` : note;
1000
- }
1001
- return artifactPath ? `[elided: ${chars} chars of earlier ${toolName} output, to keep the conversation within its context budget; see artifact ${artifactPath} (read it with read_file if you need it again)]` : `[elided: ${chars} chars of earlier ${toolName} output, to keep the conversation within its context budget; run the tool again if you need it]`;
1002
- }
1003
- var EVICTION_MIN_BUDGET = 400;
1004
- var ELISION_MARKER_BUDGET = 96;
1005
- var EXCERPT_SHARE = 0.1;
1006
- var EXCERPT_FLOOR = 1200;
1007
- var DEDUPE_MIN_CHARS = 2000;
1008
- function takeWithinBudget(lines, budget, fromEnd) {
1009
- const taken = [];
1010
- let used = 0;
1011
- for (let step = 0;step < lines.length; step += 1) {
1012
- const line = lines[fromEnd ? lines.length - 1 - step : step];
1013
- const cost = line.length + 1;
1014
- if (used + cost > budget)
1015
- break;
1016
- used += cost;
1017
- taken.push(line);
1018
- }
1019
- return fromEnd ? taken.reverse() : taken;
1020
- }
1021
- function evictedToolResult(content, artifact, budget) {
1022
- const header = `[Output too large for the transcript: ${artifact.lines} lines, ${artifact.bytes} bytes.` + ` The whole thing is saved at ${artifact.path} — read any window of it with read_file(path, offset, limit),` + ` or search it with run_command. The beginning and end are below.]`;
1023
- if (budget < EVICTION_MIN_BUDGET || header.length + ELISION_MARKER_BUDGET + 64 > budget)
1024
- return truncate(content, budget);
1025
- const lines = content.split(`
1026
- `);
1027
- const bodyBudget = budget - header.length - 2 - ELISION_MARKER_BUDGET;
1028
- const headBudget = Math.floor(bodyBudget * 0.6);
1029
- const head = takeWithinBudget(lines, headBudget, false);
1030
- const tail = takeWithinBudget(lines.slice(head.length), bodyBudget - headBudget, true);
1031
- const elided = lines.length - head.length - tail.length;
1032
- const marker = `...[${elided} line${elided === 1 ? "" : "s"} elided — full output at ${artifact.path}]...`;
1033
- const assembled = [header, "", ...head, marker, ...tail].join(`
1034
- `);
1035
- return assembled.length <= budget ? assembled : assembled.slice(0, budget);
1036
- }
1037
-
1038
- class BoundedAgentRuntime {
1039
- dependencies;
1040
- toolsByName;
1041
- constructor(dependencies) {
1042
- this.dependencies = dependencies;
1043
- this.toolsByName = new Map;
1044
- for (const tool of dependencies.tools) {
1045
- if (this.toolsByName.has(tool.name))
1046
- throw new Error(`Duplicate tool name: ${tool.name}`);
1047
- if (tool.effect === "external" && !tool.requiresApproval)
1048
- throw new Error(`External tool ${tool.name} must require approval`);
1049
- if (tool.effect !== "none" && tool.parallelSafe)
1050
- throw new Error(`Effectful tool ${tool.name} cannot be marked parallel-safe`);
1051
- this.toolsByName.set(tool.name, tool);
1052
- }
1053
- }
1054
- async execute(request) {
1055
- validateRequest(request);
1056
- const capabilities = new Set(request.allowedCapabilityIds);
1057
- const tools = [...this.toolsByName.values()].filter((tool) => capabilities.has(tool.capabilityId));
1058
- const definitions = tools.map(({ name, description, inputSchema }) => ({ name, description, inputSchema }));
1059
- const messages = [
1060
- { role: "system", content: request.systemPrompt },
1061
- ...request.history ?? [],
1062
- { role: "user", content: request.objective, ...request.images?.length ? { images: request.images } : {} }
1063
- ];
1064
- let usage = { ...emptyUsage };
1065
- let actualModelRwf = 0;
1066
- let toolCallsExecuted = 0;
1067
- let totalToolResultChars = 0;
1068
- const sentResults = new Map;
1069
- const runToolResults = [];
1070
- let inTurnCompactions = 0;
1071
- let workspaceNeedsVerification = false;
1072
- let verificationNudges = 0;
1073
- let unavailableToolRecoveries = 0;
1074
- let toolTurnRecoveries = 0;
1075
- let lengthContinuations = 0;
1076
- let strongestEvidence = 0;
1077
- let askedForTestEvidence = false;
1078
- const routingReceipts = [];
1079
- let promptTotals = addPart(newPartTotals(), JSON.stringify(definitions));
1080
- let measured = 0;
1081
- const compactOlderToolResults = async (currentIteration, incoming = 0) => {
1082
- if (inTurnCompactions >= MAX_IN_TURN_COMPACTIONS)
1083
- return false;
1084
- const target = Math.min(Math.floor(request.maxTotalToolResultChars / 2), request.maxTotalToolResultChars - incoming);
1085
- let changed = false;
1086
- for (const entry of runToolResults) {
1087
- if (totalToolResultChars <= target)
1088
- break;
1089
- if (entry.compacted || entry.iteration >= currentIteration || entry.chars < COMPACTION_MIN_CHARS)
1090
- continue;
1091
- const message = messages[entry.index];
1092
- if (message?.role !== "tool" || message.toolCallId !== entry.toolCallId)
1093
- continue;
1094
- const images = message.images ?? [];
1095
- const stubText = message.content.length >= COMPACTION_MIN_CHARS;
1096
- let path2 = entry.artifactPath;
1097
- if (stubText && !path2 && this.dependencies.artifacts) {
1098
- path2 = (await this.dependencies.artifacts.put({ toolName: entry.name, toolCallId: entry.toolCallId, content: message.content }).catch(() => {
1099
- return;
1100
- }))?.path;
1101
- }
1102
- const stub = images.length > 0 ? `${stubText ? compactedToolResult(entry.name, message.content.length, path2) : message.content}
1103
- ${compactedToolResult(entry.name, 0, undefined, images)}` : compactedToolResult(entry.name, message.content.length, path2);
1104
- if (images.length === 0 && stub.length >= message.content.length)
1105
- continue;
1106
- const { images: _dropped, ...withoutImages } = message;
1107
- messages[entry.index] = { ...withoutImages, content: stub };
1108
- totalToolResultChars -= entry.chars - stub.length;
1109
- entry.chars = stub.length;
1110
- entry.images = 0;
1111
- entry.compacted = true;
1112
- changed = true;
1113
- if (entry.digest) {
1114
- const sent = sentResults.get(entry.digest);
1115
- if (sent && !sent.path) {
1116
- if (path2)
1117
- sentResults.set(entry.digest, { ...sent, path: path2 });
1118
- else
1119
- sentResults.delete(entry.digest);
1120
- }
1121
- }
1122
- }
1123
- if (changed) {
1124
- inTurnCompactions += 1;
1125
- promptTotals = addPart(newPartTotals(), JSON.stringify(definitions));
1126
- measured = 0;
1127
- }
1128
- return changed;
1129
- };
1130
- const stop = async (status, summary, iterations) => {
1131
- await this.dependencies.control.persistEvent({ type: "runtime_stop", status, summary });
1132
- return {
1133
- status,
1134
- summary,
1135
- messages,
1136
- usage,
1137
- actualModelRwf,
1138
- iterations,
1139
- toolCallsExecuted,
1140
- ...routingReceipts.length > 0 ? { routingReceipts: [...routingReceipts] } : {}
1141
- };
1142
- };
1143
- for (let iteration = 1;iteration <= request.maxIterations; iteration += 1) {
1144
- await this.dependencies.control.heartbeat();
1145
- if (request.signal?.aborted || await this.dependencies.control.isCancellationRequested())
1146
- return stop("cancelled", "Run cancelled at a safe checkpoint.", iteration - 1);
1147
- const approvedRemaining = request.modelReservationRwf - actualModelRwf;
1148
- for (;measured < messages.length; measured += 1) {
1149
- for (const part of agentMessagePromptParts(messages[measured]))
1150
- addPart(promptTotals, part);
1151
- }
1152
- const maximumOutputTokens = affordableOutputTokensFor(tokenEstimateFrom(promptTotals), request.maxOutputTokens, Math.max(0, approvedRemaining), this.dependencies.prices);
1153
- if (maximumOutputTokens < 1)
1154
- return stop("iteration_limit", "Run reached its approved model budget before another provider call.", iteration - 1);
1155
- const modelRequest = {
1156
- requestId: `cli_${randomUUID2()}`,
1157
- ...request.effort ? { effort: request.effort } : {},
1158
- messages: [...messages],
1159
- tools: definitions,
1160
- maxOutputTokens: maximumOutputTokens,
1161
- safetyIdentifier: request.safetyIdentifier,
1162
- signal: request.signal,
1163
- onTextDelta: (text) => void this.dependencies.control.persistEvent({ type: "assistant_delta", iteration, text })
1164
- };
1165
- let turn;
1166
- let waitedMs = 0;
1167
- for (let attempt = 0;; attempt += 1) {
1168
- let emittedOutput = false;
1169
- const attemptRequest = {
1170
- ...modelRequest,
1171
- onTextDelta: modelRequest.onTextDelta ? (text) => {
1172
- emittedOutput = true;
1173
- modelRequest.onTextDelta(text);
1174
- } : undefined,
1175
- onOutputProgress: (kind) => {
1176
- emittedOutput = true;
1177
- modelRequest.onOutputProgress?.(kind);
1178
- }
1179
- };
1180
- try {
1181
- turn = await this.dependencies.model.complete(attemptRequest);
1182
- break;
1183
- } catch (error) {
1184
- if (request.signal?.aborted || await this.dependencies.control.isCancellationRequested()) {
1185
- return stop("cancelled", "Run cancelled while waiting for the model provider.", iteration - 1);
1186
- }
1187
- if (emittedOutput)
1188
- throw new ProviderRequestError(error, { attempts: attempt + 1, retrySuppressed: "output_started" });
1189
- if (!isRetryableProviderError(error))
1190
- throw error;
1191
- const reason = providerFailureKind(error);
1192
- const policy = RETRY_POLICIES[reason];
1193
- if (attempt + 1 >= policy.maxAttempts) {
1194
- throw new ProviderRequestError(error, { attempts: attempt + 1, kind: reason, waitedMs });
1195
- }
1196
- const delayMs = providerRetryDelayMs(error, attempt, this.dependencies.retry?.random, policy);
1197
- waitedMs += delayMs;
1198
- await this.dependencies.control.persistEvent({
1199
- type: "provider_retry",
1200
- iteration,
1201
- nextAttempt: attempt + 2,
1202
- maxAttempts: policy.maxAttempts,
1203
- delayMs,
1204
- reason
1205
- });
1206
- if (!await (this.dependencies.retry?.sleep ?? providerRetryDelay)(delayMs, request.signal)) {
1207
- return stop("cancelled", "Run cancelled while waiting to retry the model provider.", iteration - 1);
1208
- }
1209
- if (request.signal?.aborted || await this.dependencies.control.isCancellationRequested()) {
1210
- return stop("cancelled", "Run cancelled while waiting to retry the model provider.", iteration - 1);
1211
- }
1212
- }
1213
- }
1214
- if (!turn)
1215
- throw new Error("Model provider retry loop ended without a response");
1216
- if (turn.routingReceipt)
1217
- routingReceipts.push({ ...turn.routingReceipt, taskId: turn.routingReceipt.taskId ?? modelRequest.requestId });
1218
- usage = addUsage(usage, turn.usage);
1219
- actualModelRwf = priceActualModelUsage(usage.inputTokens, usage.outputTokens, this.dependencies.prices);
1220
- if (actualModelRwf > request.modelReservationRwf)
1221
- throw new Error("Actual model usage exceeds the reserved model budget");
1222
- await this.dependencies.control.persistEvent({ type: "model_turn", requestId: modelRequest.requestId, iteration, responseId: turn.responseId, model: turn.model, toolCallCount: turn.toolCalls.length, usage: turn.usage, ...turn.allowance ? { allowance: turn.allowance } : {} });
1223
- if (turn.finishReason === "refusal")
1224
- return stop("blocked", turn.refusal?.trim() || "Model refused the task.", iteration);
1225
- if (turn.finishReason === "length") {
1226
- const partial = turn.content.trim();
1227
- if (lengthContinuations >= MAX_LENGTH_CONTINUATIONS) {
1228
- return stop("iteration_limit", `Model kept exceeding its output limit: ${MAX_LENGTH_CONTINUATIONS} continuations were not enough to finish the answer.`, iteration);
1229
- }
1230
- lengthContinuations += 1;
1231
- messages.push({ role: "assistant", content: partial || "(no output before the limit was reached)" });
1232
- messages.push({ role: "user", content: LENGTH_CONTINUATION_NUDGE });
1233
- continue;
1234
- }
1235
- if (turn.finishReason === "stop") {
1236
- const summary = turn.content.trim() || "Model completed without a summary.";
1237
- messages.push({ role: "assistant", content: summary });
1238
- if (workspaceNeedsVerification && verificationNudges < MAX_VERIFICATION_NUDGES) {
1239
- verificationNudges += 1;
1240
- messages.push({ role: "user", content: VERIFICATION_NUDGE, internal: true });
1241
- continue;
1242
- }
1243
- if (strongestEvidence === EVIDENCE_RANK.check && !askedForTestEvidence) {
1244
- askedForTestEvidence = true;
1245
- messages.push({ role: "user", content: TEST_EVIDENCE_NUDGE, internal: true });
1246
- continue;
1247
- }
1248
- return workspaceNeedsVerification ? stop("needs_verification", `Workspace changes were made without passing verification evidence. The agent reported: ${summary}`, iteration) : stop("completed", summary, iteration);
1249
- }
1250
- const recoverToolTurn = (reason, instruction) => {
1251
- if (toolTurnRecoveries >= MAX_TOOL_TURN_RECOVERIES)
1252
- return false;
1253
- toolTurnRecoveries += 1;
1254
- messages.push({ role: "user", content: `${reason} ${instruction}`, internal: true });
1255
- return true;
1256
- };
1257
- if (turn.finishReason !== "tool_calls" || turn.toolCalls.length === 0) {
1258
- if (recoverToolTurn("Your previous response declared tool use but contained no usable tool calls.", "Try again with a valid tool call, or answer normally if no tool is needed."))
1259
- continue;
1260
- return stop("failed", "Model repeatedly returned an invalid tool-call turn.", iteration);
1261
- }
1262
- if (turn.toolCalls.length > request.maxToolCallsPerTurn) {
1263
- if (recoverToolTurn(`Your previous response requested ${turn.toolCalls.length} tools, above the per-turn limit of ${request.maxToolCallsPerTurn}; none were executed.`, `Retry with at most ${request.maxToolCallsPerTurn} calls, prioritizing the calls needed to make progress.`))
1264
- continue;
1265
- return stop("failed", `Model repeatedly exceeded the per-turn tool-call limit of ${request.maxToolCallsPerTurn}.`, iteration);
1266
- }
1267
- if (toolCallsExecuted + turn.toolCalls.length > request.maxToolCalls)
1268
- return stop("iteration_limit", "Run reached its tool-call budget.", iteration);
1269
- const seenCallIds = new Set;
1270
- const malformed = turn.toolCalls.find((call) => {
1271
- if (!call.id.trim() || seenCallIds.has(call.id))
1272
- return true;
1273
- seenCallIds.add(call.id);
1274
- return !normalizeArguments(call.arguments);
1275
- });
1276
- if (malformed) {
1277
- if (recoverToolTurn(`Your previous tool-call response was malformed (the ${JSON.stringify(malformed.name).slice(0, 80)} call); no tools were executed.`, 'Retry with a unique non-empty id for every call and a JSON object for every arguments value, for example {"path": "src/a.ts"}: double quotes, no trailing commas, no code fences.'))
1278
- continue;
1279
- return stop("failed", "Model repeatedly returned malformed tool calls.", iteration);
1280
- }
1281
- const unavailable = turn.toolCalls.find((call) => {
1282
- const tool = this.toolsByName.get(call.name);
1283
- return !tool || !capabilities.has(tool.capabilityId);
1284
- });
1285
- if (unavailable) {
1286
- if (unavailableToolRecoveries >= MAX_UNAVAILABLE_TOOL_RECOVERIES) {
1287
- return stop("failed", `Tool ${unavailable.name} is outside the run capability scope.`, iteration);
1288
- }
1289
- unavailableToolRecoveries += 1;
1290
- messages.push({ role: "assistant", content: turn.content, toolCalls: turn.toolCalls });
1291
- for (const call of turn.toolCalls) {
1292
- messages.push({
1293
- role: "tool",
1294
- toolCallId: call.id,
1295
- name: call.name,
1296
- internal: true,
1297
- content: call.id === unavailable.id ? `Tool ${call.name} is unavailable in the current mode. Continue with the listed tools, or answer without it.` : "This batch was not executed because another call was unavailable. Retry any still-needed available call separately."
1298
- });
1299
- }
1300
- continue;
1301
- }
1302
- const prepared = [];
1303
- for (const call of turn.toolCalls) {
1304
- const tool = this.toolsByName.get(call.name);
1305
- const argumentsValue = normalizeArguments(call.arguments);
1306
- if (tool.requiresApproval) {
1307
- const approval = await this.dependencies.control.isToolCallApproved(call, tool);
1308
- if (approval !== true && approval !== "approved") {
1309
- return approval === "denied" ? stop("blocked", `Tool ${call.name} was rejected by the user.`, iteration) : stop("needs_approval", `Tool ${call.name} requires approval before execution.`, iteration);
1310
- }
1311
- }
1312
- prepared.push({ call, tool, argumentsValue });
1313
- }
1314
- messages.push({ role: "assistant", content: turn.content, toolCalls: turn.toolCalls });
1315
- const runOne = async ({ call, tool, argumentsValue }) => {
1316
- if (request.signal?.aborted || await this.dependencies.control.isCancellationRequested())
1317
- return { call, tool, result: { content: "Tool execution cancelled before start.", isError: true } };
1318
- await this.dependencies.control.persistEvent({ type: "tool_call", toolCallId: call.id, toolName: call.name, effect: tool.effect, arguments: argumentsValue });
1319
- try {
1320
- return { call, tool, result: await tool.execute(argumentsValue, request) };
1321
- } catch (error) {
1322
- return { call, tool, result: { content: error instanceof Error ? error.message : "Tool execution failed", isError: true } };
1323
- }
1324
- };
1325
- const groups = [];
1326
- for (const item of prepared) {
1327
- const concurrent = item.tool.parallelSafe && item.tool.effect === "none";
1328
- const last = groups.at(-1);
1329
- const lastIsConcurrent = last !== undefined && last[0].tool.parallelSafe && last[0].tool.effect === "none";
1330
- if (concurrent && lastIsConcurrent)
1331
- last.push(item);
1332
- else
1333
- groups.push([item]);
1334
- }
1335
- const results = [];
1336
- for (const group of groups) {
1337
- if (group.length === 1)
1338
- results.push(await runOne(group[0]));
1339
- else
1340
- results.push(...await Promise.all(group.map(runOne)));
1341
- }
1342
- const iterationCharBudget = Math.max(request.maxToolResultChars, Math.floor(request.maxTotalToolResultChars / 4));
1343
- let iterationChars = 0;
1344
- const incoming = Math.min(iterationCharBudget, results.reduce((sum, { result }) => sum + Math.min((result.content || " ").length, request.maxToolResultChars) + (result.images?.length ?? 0) * IMAGE_CHAR_ESTIMATE, 0));
1345
- if (totalToolResultChars + incoming > request.maxTotalToolResultChars)
1346
- await compactOlderToolResults(iteration, incoming);
1347
- for (const { call, tool, result } of results) {
1348
- toolCallsExecuted += 1;
1349
- const remaining = request.maxTotalToolResultChars - totalToolResultChars;
1350
- const budget = Math.max(0, Math.min(request.maxToolResultChars, remaining, iterationCharBudget - iterationChars));
1351
- const raw = result.content || "(empty tool result)";
1352
- let artifact;
1353
- let content = truncate(raw, budget);
1354
- const withinTwiceBudget = raw.length <= budget * 2 && raw.length <= remaining;
1355
- const images = result.images?.length ? result.images : undefined;
1356
- const digest2 = !images && raw.length >= DEDUPE_MIN_CHARS ? createHash2("sha256").update(raw).digest("hex") : undefined;
1357
- const alreadySent = digest2 ? sentResults.get(digest2) : undefined;
1358
- if (alreadySent) {
1359
- content = `[Identical to the earlier ${alreadySent.toolName} result in this conversation${alreadySent.path ? `, saved at ${alreadySent.path}` : ""}. Unchanged since then; re-read it there if you need it again.]`;
1360
- } else if (withinTwiceBudget) {
1361
- content = raw;
1362
- } else if (this.dependencies.artifacts && raw.length > budget && budget > 0) {
1363
- const stored = await this.dependencies.artifacts.put({ toolName: call.name, toolCallId: call.id, content: raw }).catch(() => {
1364
- return;
1365
- });
1366
- if (stored) {
1367
- artifact = stored;
1368
- content = evictedToolResult(raw, stored, Math.min(budget, Math.max(EXCERPT_FLOOR, Math.round(budget * EXCERPT_SHARE))));
1369
- }
1370
- }
1371
- if (digest2 && !alreadySent)
1372
- sentResults.set(digest2, { toolName: call.name, path: artifact?.path });
1373
- const imageChars = (images?.length ?? 0) * IMAGE_CHAR_ESTIMATE;
1374
- totalToolResultChars += content.length + imageChars;
1375
- iterationChars += content.length + imageChars;
1376
- runToolResults.push({ index: messages.length, iteration, toolCallId: call.id, name: call.name, chars: content.length + imageChars, compacted: false, images: images?.length ?? 0, ...artifact ? { artifactPath: artifact.path } : {}, ...digest2 ? { digest: digest2 } : {} });
1377
- messages.push({ role: "tool", content, toolCallId: call.id, name: call.name, ...images ? { images } : {} });
1378
- const effect = tool.effect === "external" ? "external" : result.effect ?? tool.effect;
1379
- if (!result.isError && effect === "workspace")
1380
- workspaceNeedsVerification = true;
1381
- if (!result.isError && result.verification?.passed) {
1382
- workspaceNeedsVerification = false;
1383
- strongestEvidence = Math.max(strongestEvidence, EVIDENCE_RANK[result.verification.kind ?? "check"]);
1384
- }
1385
- await this.dependencies.control.persistEvent({ type: "tool_result", toolCallId: call.id, toolName: call.name, isError: result.isError ?? false, effect, content, ...result.data ? { data: result.data } : {}, ...artifact ? { artifact } : {} });
1386
- }
1387
- if (totalToolResultChars >= request.maxTotalToolResultChars)
1388
- await compactOlderToolResults(iteration);
1389
- await this.dependencies.control.checkpointMessages?.(messages);
1390
- if (totalToolResultChars >= request.maxTotalToolResultChars)
1391
- return stop("iteration_limit", "Run reached its total tool-result context budget.", iteration);
1392
- }
1393
- return stop("iteration_limit", "Run reached its model iteration budget.", request.maxIterations);
1394
- }
1395
- }
1396
-
1397
- // packages/core/src/cli/checkpoints.ts
736
+ // ../core/src/cli/checkpoints.ts
1398
737
  import { spawn } from "node:child_process";
1399
- import { randomUUID as randomUUID3 } from "node:crypto";
738
+ import { randomUUID as randomUUID2 } from "node:crypto";
1400
739
  import { promises as fs2 } from "node:fs";
1401
740
  import path2 from "node:path";
1402
741
  var EXCLUDE_ARCHYMEDES = [".", ":(exclude).archymedes"];
@@ -1501,7 +840,7 @@ class CheckpointStore {
1501
840
  const checkpoint = this.latest();
1502
841
  if (!checkpoint)
1503
842
  return "";
1504
- const index = `${this.indexFile}.diff-${randomUUID3()}`;
843
+ const index = `${this.indexFile}.diff-${randomUUID2()}`;
1505
844
  const options = { cwd: this.root, env: { GIT_INDEX_FILE: index } };
1506
845
  try {
1507
846
  if ((await this.git(["read-tree", checkpoint.tree], options)).exitCode !== 0)
@@ -1518,10 +857,10 @@ class CheckpointStore {
1518
857
  }
1519
858
  }
1520
859
 
1521
- // packages/core/src/cli/permissions.ts
1522
- import { createHash as createHash3 } from "node:crypto";
860
+ // ../core/src/cli/permissions.ts
861
+ import { createHash as createHash2 } from "node:crypto";
1523
862
 
1524
- // packages/core/src/cli/command.ts
863
+ // ../core/src/cli/command.ts
1525
864
  import { spawn as spawn2 } from "node:child_process";
1526
865
  import { constants } from "node:os";
1527
866
  var ARCHYMEDES_CREDENTIAL_ENV_NAMES = [
@@ -1876,7 +1215,7 @@ Command exceeded ${options.timeoutMs}ms and was killed.`;
1876
1215
  });
1877
1216
  };
1878
1217
 
1879
- // packages/core/src/cli/safety.ts
1218
+ // ../core/src/cli/safety.ts
1880
1219
  var TASK_RULES = [
1881
1220
  { 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 },
1882
1221
  { 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 },
@@ -1938,7 +1277,7 @@ function assessToolSafety(call, tool) {
1938
1277
  };
1939
1278
  }
1940
1279
 
1941
- // packages/core/src/cli/permissions.ts
1280
+ // ../core/src/cli/permissions.ts
1942
1281
  var ARCHYMEDES_CAPABILITIES = {
1943
1282
  read: "workspace.files.read",
1944
1283
  write: "workspace.files",
@@ -1991,7 +1330,7 @@ function actionDigest(call, tool) {
1991
1330
  provenance: tool.provenance ?? { kind: "built-in" },
1992
1331
  arguments: call.arguments ?? {}
1993
1332
  }));
1994
- return createHash3("sha256").update(action).digest("hex");
1333
+ return createHash2("sha256").update(action).digest("hex");
1995
1334
  }
1996
1335
  var SHELL_OPERATORS = /[;&|`$<>(){}\r\n]/;
1997
1336
  var NEVER_GENERALIZE = new Set([
@@ -2383,7 +1722,7 @@ function describeToolCall(call, tool) {
2383
1722
  }
2384
1723
  }
2385
1724
 
2386
- // packages/core/src/cli/memory.ts
1725
+ // ../core/src/cli/memory.ts
2387
1726
  import { promises as fs3 } from "node:fs";
2388
1727
  import os from "node:os";
2389
1728
  import path3 from "node:path";
@@ -2494,6 +1833,20 @@ async function addMemory(scope, text, root, environment, options = {}) {
2494
1833
  }
2495
1834
  return { entries: next, file: await writeScope(scope, next, root, environment), changed: true };
2496
1835
  }
1836
+ async function forgetMemory(scope, index, root, environment) {
1837
+ const existing = await readScope(scope, root, environment);
1838
+ const removed = existing.find((entry) => entry.index === index);
1839
+ if (!removed)
1840
+ return { entries: existing, file: memoryFile(scope, root, environment), changed: false };
1841
+ const next = existing.filter((entry) => entry.index !== index).map((entry, position) => ({ ...entry, index: position + 1 }));
1842
+ return { entries: next, file: await writeScope(scope, next, root, environment), changed: true, removed };
1843
+ }
1844
+ async function clearMemories(scope, root, environment) {
1845
+ const existing = await readScope(scope, root, environment);
1846
+ if (existing.length === 0)
1847
+ return { entries: [], file: memoryFile(scope, root, environment), changed: false };
1848
+ return { entries: [], file: await writeScope(scope, [], root, environment), changed: true };
1849
+ }
2497
1850
  function terms(text) {
2498
1851
  return new Set(text.toLowerCase().match(/[\p{L}\p{N}_-]{3,}/gu) ?? []);
2499
1852
  }
@@ -2535,7 +1888,7 @@ function memoryPromptBlock(entries) {
2535
1888
  `);
2536
1889
  }
2537
1890
 
2538
- // packages/core/src/cli/environment.ts
1891
+ // ../core/src/cli/environment.ts
2539
1892
  import os2 from "node:os";
2540
1893
  var BASE_PROBES = ["git", "node", "bun", "npm", "pnpm", "yarn", "rg", "python3"];
2541
1894
  var MARKER_PROBES = [
@@ -2633,16 +1986,16 @@ function describeEnvironment(report) {
2633
1986
  `);
2634
1987
  }
2635
1988
 
2636
- // packages/core/src/cli/prompt.ts
2637
- import { createHash as createHash5 } from "node:crypto";
1989
+ // ../core/src/cli/prompt.ts
1990
+ import { createHash as createHash4 } from "node:crypto";
2638
1991
  import { promises as fs5 } from "node:fs";
2639
1992
  import os3 from "node:os";
2640
1993
  import path5 from "node:path";
2641
1994
 
2642
- // packages/core/src/cli/workspace.ts
1995
+ // ../core/src/cli/workspace.ts
2643
1996
  import { constants as fsConstants, promises as fs4 } from "node:fs";
2644
1997
  import { spawn as spawn3 } from "node:child_process";
2645
- import { createHash as createHash4 } from "node:crypto";
1998
+ import { createHash as createHash3 } from "node:crypto";
2646
1999
  import path4 from "node:path";
2647
2000
  var DEFAULT_WORKSPACE_LIMITS = {
2648
2001
  maxReadBytes: 512000,
@@ -2662,7 +2015,7 @@ function resolveInWorkspace(root, candidate) {
2662
2015
  const absoluteRoot = path4.resolve(root);
2663
2016
  const resolved = path4.resolve(absoluteRoot, candidate);
2664
2017
  if (resolved !== absoluteRoot && !resolved.startsWith(absoluteRoot + path4.sep)) {
2665
- throw new WorkspaceViolation(`path escapes the workspace root: ${candidate}`);
2018
+ throw new WorkspaceViolation(`path escapes the workspace root: ${candidate}. Use a path inside the project; a relative path (like "notes/tmp.txt") is resolved from the project root.`);
2666
2019
  }
2667
2020
  return resolved;
2668
2021
  }
@@ -2937,7 +2290,7 @@ function parseEditArguments(args) {
2937
2290
  return [{ oldText: args.oldText, newText: args.newText, replaceAll: args.replaceAll === true }];
2938
2291
  }
2939
2292
  function fingerprintText(content) {
2940
- return createHash4("sha256").update(content, "utf8").digest("hex");
2293
+ return createHash3("sha256").update(content, "utf8").digest("hex");
2941
2294
  }
2942
2295
 
2943
2296
  class FileReadTracker {
@@ -3286,7 +2639,7 @@ async function grepWorkspace(root, query, options = {}) {
3286
2639
  return matches;
3287
2640
  }
3288
2641
 
3289
- // packages/core/src/cli/defender-playbooks.ts
2642
+ // ../core/src/cli/defender-playbooks.ts
3290
2643
  var INJECTION_PLAYBOOK = `
3291
2644
  ## Injection
3292
2645
 
@@ -3988,7 +3341,7 @@ function playbookFor(id) {
3988
3341
  return DEFENDER_PLAYBOOK_CATALOG.find((entry) => entry.id === wanted || entry.title.toLowerCase() === wanted);
3989
3342
  }
3990
3343
 
3991
- // packages/core/src/cli/token-saver.ts
3344
+ // ../core/src/cli/token-saver.ts
3992
3345
  var TOKEN_SAVER_CONTEXT_LIMIT = 16384;
3993
3346
  var TOKEN_SAVER_TOOL_RESULT_CHARS = 3000;
3994
3347
  var TOKEN_SAVER_TOTAL_TOOL_RESULT_CHARS = 24000;
@@ -4093,7 +3446,7 @@ function restoreFullHistory(messages, original, sent) {
4093
3446
  return [messages[0], ...original, ...messages.slice(1 + sent.length)];
4094
3447
  }
4095
3448
 
4096
- // packages/core/src/cli/prompt.ts
3449
+ // ../core/src/cli/prompt.ts
4097
3450
  var INSTRUCTION_FILES = ["AGENTS.override.md", "ARCHYMEDES.md", "AGENTS.md", "CLAUDE.md", ".archymedesrules"];
4098
3451
  async function instructionBoundary(root) {
4099
3452
  const requestedRoot = path5.resolve(root);
@@ -4137,7 +3490,7 @@ async function collectProjectContext(root, maxInstructionChars = 8000) {
4137
3490
  selected.unshift({
4138
3491
  path: item.relative,
4139
3492
  content,
4140
- sha256: createHash5("sha256").update(item.text).digest("hex"),
3493
+ sha256: createHash4("sha256").update(item.text).digest("hex"),
4141
3494
  truncated: content.length < item.text.length
4142
3495
  });
4143
3496
  remaining -= content.length;
@@ -4359,8 +3712,8 @@ ${instructions}`);
4359
3712
  `);
4360
3713
  }
4361
3714
 
4362
- // packages/core/src/cli/session.ts
4363
- import { createHash as createHash6, randomUUID as randomUUID4 } from "node:crypto";
3715
+ // ../core/src/cli/session.ts
3716
+ import { createHash as createHash5, randomUUID as randomUUID3 } from "node:crypto";
4364
3717
  import { promises as fs6 } from "node:fs";
4365
3718
  import path6 from "node:path";
4366
3719
  var SESSION_SCHEMA_VERSION = 2;
@@ -4387,7 +3740,7 @@ function canonicalize2(value) {
4387
3740
  return null;
4388
3741
  }
4389
3742
  function integrityFor(record) {
4390
- return createHash6("sha256").update(JSON.stringify(canonicalize2(record))).digest("hex");
3743
+ return createHash5("sha256").update(JSON.stringify(canonicalize2(record))).digest("hex");
4391
3744
  }
4392
3745
  async function acquireSessionLock(lockFile) {
4393
3746
  for (let attempt = 0;attempt < 40; attempt += 1) {
@@ -4423,7 +3776,7 @@ async function saveSession(record) {
4423
3776
  const file = path6.join(directory, `${record.id}.json`);
4424
3777
  const lockFile = `${file}.lock`;
4425
3778
  const release = await acquireSessionLock(lockFile);
4426
- const temporary = `${file}.${process.pid}.${randomUUID4()}.tmp`;
3779
+ const temporary = `${file}.${process.pid}.${randomUUID3()}.tmp`;
4427
3780
  try {
4428
3781
  const fileExists = await fs6.stat(file).then(() => true).catch(() => false);
4429
3782
  const current = (await loadSnapshot(record.root, record.id))?.record;
@@ -4482,10 +3835,10 @@ function sessionJournalPath(root, id) {
4482
3835
  return path6.join(sessionDirectory(root), `${id}.steps.jsonl`);
4483
3836
  }
4484
3837
  function journalEntryHash(entry) {
4485
- return createHash6("sha256").update(JSON.stringify(entry)).digest("hex");
3838
+ return createHash5("sha256").update(JSON.stringify(entry)).digest("hex");
4486
3839
  }
4487
3840
  function messageFingerprint(message) {
4488
- return message === undefined ? "" : createHash6("sha256").update(JSON.stringify(message)).digest("hex");
3841
+ return message === undefined ? "" : createHash5("sha256").update(JSON.stringify(message)).digest("hex");
4489
3842
  }
4490
3843
  var durableStates = new Map;
4491
3844
  var journalQueues = new Map;
@@ -4768,17 +4121,17 @@ ${summary.trim()}` },
4768
4121
  ];
4769
4122
  }
4770
4123
 
4771
- // packages/core/src/hooks/command.ts
4124
+ // ../core/src/hooks/command.ts
4772
4125
  function hookCommand(scriptPath, payload, platform) {
4773
4126
  if (platform !== "win32")
4774
4127
  return `env ARCHYMEDES_HOOK_EVENT_B64=${payload} ${scriptPath}`;
4775
4128
  return `set "ARCHYMEDES_HOOK_EVENT_B64=${payload}"&& call "${scriptPath.split("/").join("\\")}"`;
4776
4129
  }
4777
4130
 
4778
- // packages/core/src/hooks/types.ts
4131
+ // ../core/src/hooks/types.ts
4779
4132
  var HOOK_PHASES = ["pre-session", "pre-turn", "pre-tool-use", "post-tool-use", "post-turn", "post-session"];
4780
4133
 
4781
- // packages/core/src/hooks/registry.ts
4134
+ // ../core/src/hooks/registry.ts
4782
4135
  var HOOKS_DIRECTORY = ".archymedes/hooks";
4783
4136
  var HOOK_TIMEOUT_MS = 5000;
4784
4137
  async function scriptsIn(workspace, phaseDirectory) {
@@ -4881,7 +4234,7 @@ class HookRegistry {
4881
4234
  return warnings;
4882
4235
  }
4883
4236
  }
4884
- // packages/core/src/mcp/config.ts
4237
+ // ../core/src/mcp/config.ts
4885
4238
  var MCP_CONFIG_PATH = ".archymedes/mcp.json";
4886
4239
  function isPlainObject(value) {
4887
4240
  return typeof value === "object" && value !== null && !Array.isArray(value);
@@ -4936,7 +4289,7 @@ async function discoverMcpServers(workspace) {
4936
4289
  }
4937
4290
  return servers;
4938
4291
  }
4939
- // packages/core/src/mcp/http-transport.ts
4292
+ // ../core/src/mcp/http-transport.ts
4940
4293
  class HttpMcpTransport {
4941
4294
  config;
4942
4295
  requestTimeoutMs;
@@ -5092,7 +4445,7 @@ function isJsonRpcNotification(value) {
5092
4445
  return isPlainObject2(value) && "jsonrpc" in value && !("id" in value) && typeof value.method === "string";
5093
4446
  }
5094
4447
 
5095
- // packages/core/src/mcp/stdio-transport.ts
4448
+ // ../core/src/mcp/stdio-transport.ts
5096
4449
  import { spawn as spawn4 } from "node:child_process";
5097
4450
  import { createInterface } from "node:readline";
5098
4451
  class StdioMcpTransport {
@@ -5194,7 +4547,7 @@ function isJsonRpcNotification2(value) {
5194
4547
  return typeof value === "object" && value !== null && "jsonrpc" in value && !("id" in value) && typeof value.method === "string";
5195
4548
  }
5196
4549
 
5197
- // packages/core/src/mcp/connection.ts
4550
+ // ../core/src/mcp/connection.ts
5198
4551
  class McpConnection {
5199
4552
  config;
5200
4553
  transport;
@@ -5256,7 +4609,7 @@ class McpConnection {
5256
4609
  }
5257
4610
  var MCP_PROTOCOL_VERSION = "2024-11-05";
5258
4611
  var REQUEST_TIMEOUT_MS = 15000;
5259
- // packages/core/src/mcp/client.ts
4612
+ // ../core/src/mcp/client.ts
5260
4613
  class McpToolProvider {
5261
4614
  connection;
5262
4615
  kind = "mcp";
@@ -5281,7 +4634,7 @@ class McpToolProvider {
5281
4634
  this.connection.close();
5282
4635
  }
5283
4636
  }
5284
- // packages/core/src/cli/plugins.ts
4637
+ // ../core/src/cli/plugins.ts
5285
4638
  var PLUGINS_DIRECTORY = ".archymedes/plugins";
5286
4639
  function isPlainObject3(value) {
5287
4640
  return typeof value === "object" && value !== null && !Array.isArray(value);
@@ -5330,7 +4683,7 @@ async function discoverPlugins(workspace) {
5330
4683
  return plugins;
5331
4684
  }
5332
4685
 
5333
- // packages/core/src/cli/tool-schema.ts
4686
+ // ../core/src/cli/tool-schema.ts
5334
4687
  class ToolArgumentError extends Error {
5335
4688
  constructor(message) {
5336
4689
  super(message);
@@ -5421,7 +4774,7 @@ function validateToolArguments(toolName, schema, args) {
5421
4774
  return present;
5422
4775
  }
5423
4776
 
5424
- // packages/core/src/skills/manifest.ts
4777
+ // ../core/src/skills/manifest.ts
5425
4778
  var SKILLS_DIRECTORY = ".archymedes/skills";
5426
4779
  function isPlainObject5(value) {
5427
4780
  return typeof value === "object" && value !== null && !Array.isArray(value);
@@ -5477,7 +4830,7 @@ function shellQuote(value, platform) {
5477
4830
  return `'${value.replace(/'/g, `'\\''`)}'`;
5478
4831
  return `"${value.replace(/"/g, '""').replace(/%/g, "%%")}"`;
5479
4832
  }
5480
- // packages/core/src/skills/provider.ts
4833
+ // ../core/src/skills/provider.ts
5481
4834
  class SkillToolProvider {
5482
4835
  id;
5483
4836
  skillsDirectory;
@@ -5507,7 +4860,7 @@ ${body}`, isError: result.exitCode !== 0 };
5507
4860
  }
5508
4861
  }
5509
4862
  var DEFAULT_SKILL_TIMEOUT_MS = 60000;
5510
- // packages/core/src/cli/external-tools.ts
4863
+ // ../core/src/cli/external-tools.ts
5511
4864
  var IMPLICIT_SKILL_PROVIDER_ID = "local-skills";
5512
4865
  async function loadLocalExternalTooling(workspace) {
5513
4866
  const providers = [new SkillToolProvider(IMPLICIT_SKILL_PROVIDER_ID, SKILLS_DIRECTORY, workspace)];
@@ -5548,7 +4901,7 @@ async function loadLocalExternalTooling(workspace) {
5548
4901
  };
5549
4902
  }
5550
4903
 
5551
- // packages/core/src/cli/nested-instructions.ts
4904
+ // ../core/src/cli/nested-instructions.ts
5552
4905
  class NestedInstructionTracker {
5553
4906
  workspace;
5554
4907
  seenDirectories = new Set;
@@ -5597,7 +4950,7 @@ ${blocks}`;
5597
4950
  }
5598
4951
  }
5599
4952
 
5600
- // packages/core/src/cli/safe-fetch.ts
4953
+ // ../core/src/cli/safe-fetch.ts
5601
4954
  import { lookup } from "node:dns/promises";
5602
4955
  import { isIP } from "node:net";
5603
4956
  var defaultResolveHost = async (hostname) => (await lookup(hostname, { all: true, verbatim: true })).map((entry) => entry.address);
@@ -5759,7 +5112,7 @@ async function fetchPublicText(input, options = {}) {
5759
5112
  return { ok: false, error: "too many redirects" };
5760
5113
  }
5761
5114
 
5762
- // packages/core/src/cli/secret-scan.ts
5115
+ // ../core/src/cli/secret-scan.ts
5763
5116
  var SECRET_PATTERNS = [
5764
5117
  { name: "AWS access key", regex: /AKIA[0-9A-Z]{16}/, severity: "critical" },
5765
5118
  { name: "GitHub token", regex: /gh[pousr]_[A-Za-z0-9]{36,255}/, severity: "critical" },
@@ -5790,7 +5143,7 @@ function findSecretsInLine(line) {
5790
5143
  return found;
5791
5144
  }
5792
5145
 
5793
- // packages/core/src/cli/tool-providers.ts
5146
+ // ../core/src/cli/tool-providers.ts
5794
5147
  var DEFAULT_EXTERNAL_TOOL_TIMEOUT_MS = 30000;
5795
5148
  async function toolsFromProvider(provider, timeoutMs = DEFAULT_EXTERNAL_TOOL_TIMEOUT_MS) {
5796
5149
  const externalTools = await provider.listTools();
@@ -5832,7 +5185,7 @@ async function collectExternalTools(providers, timeoutMs) {
5832
5185
  return collected;
5833
5186
  }
5834
5187
 
5835
- // packages/core/src/cli/deploy.ts
5188
+ // ../core/src/cli/deploy.ts
5836
5189
  var DEPLOY_PROVIDERS = {
5837
5190
  vercel: {
5838
5191
  id: "vercel",
@@ -5956,7 +5309,7 @@ function deployPlan(target, detection, options = {}) {
5956
5309
  return { commands, notes };
5957
5310
  }
5958
5311
 
5959
- // packages/core/src/cli/compute.ts
5312
+ // ../core/src/cli/compute.ts
5960
5313
  var MAX_INPUT = 2000;
5961
5314
  var MAX_DEPTH = 64;
5962
5315
  var FACTORIAL_LIMIT = 170;
@@ -6508,11 +5861,11 @@ function compute(expression, options = {}) {
6508
5861
  return { value: result, exact, rendered, radixRendered, warnings };
6509
5862
  }
6510
5863
 
6511
- // packages/core/src/cli/tools-extra/git.ts
5864
+ // ../core/src/cli/tools-extra/git.ts
6512
5865
  import { execFile } from "node:child_process";
6513
5866
  import path7 from "node:path";
6514
5867
 
6515
- // packages/core/src/cli/tools-extra/shared.ts
5868
+ // ../core/src/cli/tools-extra/shared.ts
6516
5869
  import { promises as fs7 } from "node:fs";
6517
5870
  function resolvedLimits(options) {
6518
5871
  return options.limits ?? DEFAULT_WORKSPACE_LIMITS;
@@ -6567,7 +5920,7 @@ function truncateText(text, maxChars, hint) {
6567
5920
  [truncated: showed ${cut.length} of ${text.length} chars. ${hint}]`, truncated: true };
6568
5921
  }
6569
5922
 
6570
- // packages/core/src/cli/tools-extra/git.ts
5923
+ // ../core/src/cli/tools-extra/git.ts
6571
5924
  var GIT_BASE_ARGS = ["--no-pager", "-c", "core.fsmonitor=false", "-c", "core.quotepath=false", "-c", "color.ui=false"];
6572
5925
  var MAX_GIT_OUTPUT = 16 * 1024 * 1024;
6573
5926
  function runGit2(root, args, timeoutMs) {
@@ -6849,7 +6202,7 @@ function createGitTools(options) {
6849
6202
  ];
6850
6203
  }
6851
6204
 
6852
- // packages/core/src/cli/tools-extra/image.ts
6205
+ // ../core/src/cli/tools-extra/image.ts
6853
6206
  function createImageTools(options) {
6854
6207
  const { root } = options;
6855
6208
  const limits = { ...resolvedLimits(options), maxReadBytes: MAX_IMAGE_BYTES };
@@ -6884,7 +6237,7 @@ function createImageTools(options) {
6884
6237
  ];
6885
6238
  }
6886
6239
 
6887
- // packages/core/src/cli/tools-extra/notebook.ts
6240
+ // ../core/src/cli/tools-extra/notebook.ts
6888
6241
  import { randomBytes } from "node:crypto";
6889
6242
  function toSourceLines(source) {
6890
6243
  if (source === "")
@@ -7065,7 +6418,7 @@ function createNotebookTools(options) {
7065
6418
  ];
7066
6419
  }
7067
6420
 
7068
- // packages/core/src/cli/tools-extra/repo-map.ts
6421
+ // ../core/src/cli/tools-extra/repo-map.ts
7069
6422
  var REPO_MAP_DEFAULT_CHARS = 12000;
7070
6423
  var REPO_MAP_MAX_CHARS = 60000;
7071
6424
  var ENTRY_NAMES = /(?:^|\/)(?:index|main|app|lib|mod|__init__|server|cli|program)\.[^/]+$/i;
@@ -7335,7 +6688,7 @@ ${lines.join(`
7335
6688
  ];
7336
6689
  }
7337
6690
 
7338
- // packages/core/src/cli/tools-extra/symbols.ts
6691
+ // ../core/src/cli/tools-extra/symbols.ts
7339
6692
  import { promises as fs8 } from "node:fs";
7340
6693
  import path8 from "node:path";
7341
6694
  var EXTENSIONS = {
@@ -7669,13 +7022,13 @@ class SymbolIndex {
7669
7022
  }
7670
7023
  }
7671
7024
 
7672
- // packages/core/src/cli/tools-extra/index.ts
7025
+ // ../core/src/cli/tools-extra/index.ts
7673
7026
  function createExtraTools(options) {
7674
7027
  const index = options.symbolIndex ?? new SymbolIndex(options.root, resolvedLimits(options));
7675
7028
  return [...createRepoMapTools(options, index), ...createGitTools(options), ...createNotebookTools(options), ...createImageTools(options)];
7676
7029
  }
7677
7030
 
7678
- // packages/core/src/cli/tools.ts
7031
+ // ../core/src/cli/tools.ts
7679
7032
  class TodoList {
7680
7033
  items = [];
7681
7034
  nextId = 1;
@@ -7728,6 +7081,18 @@ var REFUSED_COMMAND_PATTERNS = [
7728
7081
  />\s*\/dev\/sd[a-z]/,
7729
7082
  /:\(\)\s*\{.*\|.*&\s*\}\s*;/
7730
7083
  ];
7084
+ var LIST_ENTRY_CAP = 400;
7085
+ var GLOB_SHOWN = 150;
7086
+ function listPrefix(raw, localRoot) {
7087
+ const trimmed = raw.trim();
7088
+ if (!trimmed || trimmed === ".")
7089
+ return "";
7090
+ if (localRoot !== null) {
7091
+ const relative = displayPath(localRoot, resolveInWorkspace(localRoot, trimmed));
7092
+ return relative === "." ? "" : relative;
7093
+ }
7094
+ return trimmed.replace(/^(\.\/)+/, "").replace(/\/+$/, "");
7095
+ }
7731
7096
  function isRefusedCommand(command) {
7732
7097
  return REFUSED_COMMAND_PATTERNS.some((pattern) => pattern.test(command)) || isRecursiveForceRemoval(command) || isFindDelete(command);
7733
7098
  }
@@ -7739,6 +7104,7 @@ async function scanWorkspaceForSecrets(workspace, include) {
7739
7104
  async function createArchymedesTools(options) {
7740
7105
  const { workspace, todos } = options;
7741
7106
  const commandTimeoutMs = options.commandTimeoutMs ?? 120000;
7107
+ const writtenFiles = new Set;
7742
7108
  const tools = [
7743
7109
  {
7744
7110
  name: "read_file",
@@ -7792,24 +7158,39 @@ async function createArchymedesTools(options) {
7792
7158
  requiresApproval: false,
7793
7159
  parallelSafe: true,
7794
7160
  async execute(args) {
7795
- const prefix = typeof args.path === "string" && args.path.trim() && args.path !== "." ? args.path.replace(/^\.\//, "").replace(/\/$/, "") : "";
7161
+ const prefix = listPrefix(typeof args.path === "string" ? args.path : "", workspace.kind === "local" ? workspace.label : null);
7796
7162
  const entries = await workspace.list(prefix, optionalInteger(args.depth, "depth") ?? 2);
7797
- return { content: entries.length > 0 ? entries.join(`
7798
- `) : "No entries.", data: { entries } };
7163
+ if (entries.length === 0) {
7164
+ return { content: prefix ? `${prefix} is empty, ignored, or does not exist. Paths are relative to the project root; list "." to see the top level.` : "No entries. The project root is empty (or everything in it is ignored).", data: { entries } };
7165
+ }
7166
+ const capped = entries.length >= LIST_ENTRY_CAP;
7167
+ return { content: capped ? `${entries.join(`
7168
+ `)}
7169
+ [Listing capped at ${LIST_ENTRY_CAP} entries; list a subdirectory or lower depth to see the rest.]` : entries.join(`
7170
+ `), data: { entries, truncated: capped } };
7799
7171
  }
7800
7172
  },
7801
7173
  {
7802
7174
  name: "glob_files",
7803
- description: "Find files whose path matches a glob, e.g. 'src/**/*.ts' or '**/*.{js,ts}'.",
7804
- inputSchema: { type: "object", properties: { pattern: { type: "string" } }, required: ["pattern"], additionalProperties: false },
7175
+ description: "Find files whose path matches a glob, e.g. 'src/**/*.ts' or '**/*.{js,ts}'. Optional path limits it to one directory (the pattern is then relative to it).",
7176
+ inputSchema: { type: "object", properties: { pattern: { type: "string" }, path: { type: "string", description: "Directory to search in. Default: project root." } }, required: ["pattern"], additionalProperties: false },
7805
7177
  capabilityId: ARCHYMEDES_CAPABILITIES.read,
7806
7178
  effect: "none",
7807
7179
  requiresApproval: false,
7808
7180
  parallelSafe: true,
7809
7181
  async execute(args) {
7810
- const matches = await workspace.glob(requiredString2(args.pattern, "pattern"));
7811
- return { content: matches.length > 0 ? matches.join(`
7812
- `) : "No files matched.", data: { matches } };
7182
+ const directory = listPrefix(typeof args.path === "string" ? args.path : "", workspace.kind === "local" ? workspace.label : null);
7183
+ const pattern = requiredString2(args.pattern, "pattern").replace(/^(\.\/)+/, "");
7184
+ const matches = await workspace.glob(directory ? `${directory}/${pattern}` : pattern);
7185
+ if (matches.length === 0) {
7186
+ return { content: directory ? `No files matched ${pattern} under ${directory}. Try "**/*" there, or list_files on it.` : "No files matched.", data: { matches } };
7187
+ }
7188
+ const shown = matches.slice(0, GLOB_SHOWN);
7189
+ const more = matches.length - shown.length;
7190
+ const note = more > 0 ? `
7191
+ [${more} more matched. Narrow it: pass path for one directory, or a more specific pattern.]` : "";
7192
+ return { content: shown.join(`
7193
+ `) + note, data: { matches, truncated: more > 0 } };
7813
7194
  }
7814
7195
  },
7815
7196
  {
@@ -7886,6 +7267,7 @@ ${content}`,
7886
7267
  async execute(args) {
7887
7268
  const result = await workspace.writeFile(requiredString2(args.path, "path"), args.content);
7888
7269
  fileReadTrackerFor(workspace).record(result.path, args.content);
7270
+ writtenFiles.add(result.path);
7889
7271
  return { content: `Wrote ${result.path} (${result.bytesWritten} bytes).`, data: { path: result.path, bytesWritten: result.bytesWritten } };
7890
7272
  }
7891
7273
  },
@@ -7963,7 +7345,7 @@ ${body}
7963
7345
  data: { command, exitCode: result.exitCode, stdout: result.stdout, stderr: result.stderr, missingProgram: missing }
7964
7346
  };
7965
7347
  }
7966
- const kind = classifyVerification(command);
7348
+ const kind = classifyVerification(command) ?? (runsWrittenScript(command, writtenFiles) ? "smoke" : null);
7967
7349
  const rejectedVerification = kind && result.exitCode === 0 ? rejectedVerificationReason(command, result) : null;
7968
7350
  const verificationNote = rejectedVerification ? `
7969
7351
 
@@ -8646,7 +8028,7 @@ var TEST_COMMAND = /\b(tests?|pytest|jest|vitest|mocha|jasmine|karma|ava|rspec|m
8646
8028
  var CHECK_COMMAND = /\b(typecheck|tsc|lint|eslint|pylint|ruff|flake8|mypy|pyright|check|build|compile|py_compile|vet|clippy|rubocop|audit|fmt|format)\b/i;
8647
8029
  var BEHAVIOR_COMMAND = /\b(e2e|end-to-end|playwright|cypress|puppeteer|selenium|webdriver|testcafe|nightwatch|integration|behave|cucumber|supertest|testcontainers)\b/i;
8648
8030
  var SMOKE_COMMAND = /\b(smoke|healthcheck|health[-_ ]?check|curl|wget|httpie|\bhttp\s+(?:get|post)\b|playwright\s+screenshot)\b/i;
8649
- var PERSISTENT_COMMAND = /(?:^|(?:&&|;|\|)\s*)(?:(?:npm|pnpm|yarn|bun)\s+(?:run\s+)?(?:dev|start|serve)(?:\s|$)|(?:vite|next\s+dev|webpack\s+serve)(?:\s|$)|bun\s+run\s+--hot\b)|(?:^|\s)(?:--watch|-w)(?:\s|$)/i;
8031
+ var PERSISTENT_COMMAND = /(?:^|(?:&&|;|\|)\s*)(?:(?:npm|pnpm|yarn|bun)\s+(?:run\s+)?(?:dev|start|serve)(?:\s|$)|(?:vite|next\s+dev|webpack\s+serve)(?:\s|$)|bun\s+run\s+--hot\b)|(?:^|\s)--watch(?:\s|$)|\b(?:tsc|jest|vitest|mocha|sass|tailwindcss|esbuild|webpack|rollup|nodemon|parcel|tsup|swc)\b[^|;&\n]*\s-w(?:\s|$)/i;
8650
8032
  var CD_PREFIX = /^\s*cd\s+(?:[^&\n|;]|\&(?!\&))+&&\s*/;
8651
8033
  function verificationTarget(command) {
8652
8034
  let target = command;
@@ -8678,6 +8060,24 @@ ${result.stderr}`.trim();
8678
8060
  }
8679
8061
  return null;
8680
8062
  }
8063
+ var SCRIPT_INTERPRETER = /^(?:python[\d.]*|py|node|bun|deno|tsx|ts-node|ruby|php|perl|bash|sh|zsh|lua|Rscript)$/;
8064
+ function runsWrittenScript(command, written) {
8065
+ if (written.size === 0)
8066
+ return false;
8067
+ let tokens;
8068
+ try {
8069
+ tokens = tokenizeCommand(verificationTarget(command));
8070
+ } catch {
8071
+ return false;
8072
+ }
8073
+ if (tokens.length < 2 || !SCRIPT_INTERPRETER.test(tokens[0].split("/").pop() ?? ""))
8074
+ return false;
8075
+ const file = tokens.slice(1).find((token) => !token.startsWith("-"));
8076
+ if (!file)
8077
+ return false;
8078
+ const clean = file.replace(/^(\.\/)+/, "");
8079
+ return [...written].some((path9) => clean === path9 || clean.endsWith(`/${path9}`));
8080
+ }
8681
8081
  function classifyVerification(command) {
8682
8082
  const target = verificationTarget(command);
8683
8083
  if (BEHAVIOR_COMMAND.test(target))
@@ -8695,7 +8095,7 @@ function stripMarkup(html) {
8695
8095
  `).replace(/[ \t]{2,}/g, " ").trim();
8696
8096
  }
8697
8097
 
8698
- // packages/core/src/money.ts
8098
+ // ../core/src/money.ts
8699
8099
  var CURRENCIES = (() => {
8700
8100
  try {
8701
8101
  return Intl.supportedValuesOf("currency");
@@ -8785,7 +8185,7 @@ function priceUsage(usage, prices) {
8785
8185
  return money(micros, prices.currency);
8786
8186
  }
8787
8187
 
8788
- // packages/core/src/pricing.ts
8188
+ // ../core/src/pricing.ts
8789
8189
  var ISO_DATE = /^\d{4}-\d{2}-\d{2}$/;
8790
8190
  function assertDate(value, label) {
8791
8191
  if (!ISO_DATE.test(value) || Number.isNaN(Date.parse(value)))
@@ -8874,7 +8274,7 @@ function priceUnits(record, quantities) {
8874
8274
  return money(units * 1e6, record.currency);
8875
8275
  }
8876
8276
 
8877
- // packages/core/src/providers/free-usage.ts
8277
+ // ../core/src/providers/free-usage.ts
8878
8278
  import { promises as fs9 } from "node:fs";
8879
8279
  import path9 from "node:path";
8880
8280
  var FREE_REMAINING_TOKENS_HEADER = "x-free-remaining-tokens";
@@ -9014,7 +8414,7 @@ class FreeUsageMeter {
9014
8414
  }
9015
8415
  }
9016
8416
 
9017
- // packages/core/src/cli/cost.ts
8417
+ // ../core/src/cli/cost.ts
9018
8418
  function formatTokenFlow(usage) {
9019
8419
  return `↑${compactTokenCount(usage.inputTokens)} ↓${compactTokenCount(usage.outputTokens)} tok`;
9020
8420
  }
@@ -9042,7 +8442,7 @@ function predictAgentUsage(input) {
9042
8442
  outputTokensHigh: Math.round(outputTokensExpected * 1.7)
9043
8443
  };
9044
8444
  }
9045
- var emptyUsage2 = { inputTokens: 0, outputTokens: 0, totalTokens: 0, cachedInputTokens: 0, cacheWriteTokens: 0, reasoningTokens: 0 };
8445
+ var emptyUsage = { inputTokens: 0, outputTokens: 0, totalTokens: 0, cachedInputTokens: 0, cacheWriteTokens: 0, reasoningTokens: 0 };
9046
8446
  var CACHE_CHURN_FLOOR_TOKENS = 20000;
9047
8447
  var CACHE_CHURN_HINT = [
9048
8448
  "The prompt cache is being rewritten more than it is read, which costs more than not caching at all.",
@@ -9130,7 +8530,7 @@ class CostLedger {
9130
8530
  cachedInputTokens: total.cachedInputTokens + turn.usage.cachedInputTokens,
9131
8531
  cacheWriteTokens: total.cacheWriteTokens + turn.usage.cacheWriteTokens,
9132
8532
  reasoningTokens: total.reasoningTokens + turn.usage.reasoningTokens
9133
- }), { ...emptyUsage2 });
8533
+ }), { ...emptyUsage });
9134
8534
  }
9135
8535
  get history() {
9136
8536
  return [...this.turns];
@@ -9290,10 +8690,10 @@ class CostLedger {
9290
8690
  }
9291
8691
  }
9292
8692
 
9293
- // packages/core/src/cli/backends.ts
8693
+ // ../core/src/cli/backends.ts
9294
8694
  import path11 from "node:path";
9295
8695
 
9296
- // packages/core/src/sandbox-search.ts
8696
+ // ../core/src/sandbox-search.ts
9297
8697
  var DEFAULT_IGNORED_DIRECTORIES = [".git", "node_modules", ".next", "dist", "build", "target", "__pycache__", ".venv", "venv", ".pytest_cache", ".mypy_cache", ".ruff_cache", ".turbo", "vendor", ".archymedes"];
9298
8698
  async function listSandboxFiles(sandbox, sandboxId, root, ignoredDirectories = DEFAULT_IGNORED_DIRECTORIES) {
9299
8699
  const result = await sandbox.runCommand(sandboxId, {
@@ -9367,7 +8767,7 @@ function relativeTo(root, absolute) {
9367
8767
  return absolute.startsWith(`${root}/`) ? absolute.slice(root.length + 1) : absolute;
9368
8768
  }
9369
8769
 
9370
- // packages/core/src/cli/applications.ts
8770
+ // ../core/src/cli/applications.ts
9371
8771
  import { spawn as spawn5 } from "node:child_process";
9372
8772
  import { stat } from "node:fs/promises";
9373
8773
  import http from "node:http";
@@ -9516,8 +8916,10 @@ class LocalApplicationSupervisor {
9516
8916
  }
9517
8917
  async stop(id) {
9518
8918
  const application = this.applications.get(id);
9519
- if (!application)
9520
- throw new Error(`Unknown application: ${id}`);
8919
+ if (!application) {
8920
+ const known = [...this.applications.keys()];
8921
+ throw new Error(`Unknown application: ${id}. ${known.length > 0 ? `Running or known: ${known.join(", ")}.` : "No applications are managed right now; start one with start_application."}`);
8922
+ }
9521
8923
  if (application.state !== "exited") {
9522
8924
  terminateProcessTree2(application.process);
9523
8925
  await Promise.race([
@@ -9553,7 +8955,7 @@ The process produced no logs.`;
9553
8955
  }
9554
8956
  }
9555
8957
 
9556
- // packages/core/src/cli/backends.ts
8958
+ // ../core/src/cli/backends.ts
9557
8959
  var CONFIG_FILE_SCAN_LIMIT = 500;
9558
8960
  function localCommandGuidance(platform) {
9559
8961
  if (platform !== "win32")
@@ -9726,7 +9128,7 @@ class SandboxWorkspace {
9726
9128
  const existing = await this.readFile(path12);
9727
9129
  const occurrences = existing.content.split(oldText).length - 1;
9728
9130
  if (occurrences === 0)
9729
- throw new WorkspaceViolation(`oldText was not found in ${existing.path}`);
9131
+ throw new WorkspaceViolation(`oldText was not found in ${existing.path}.${nearestLineHint(existing.content, oldText)}`);
9730
9132
  if (occurrences > 1 && !options.replaceAll) {
9731
9133
  throw new WorkspaceViolation(`oldText appears ${occurrences} times in ${existing.path}; include more surrounding context or set replaceAll`);
9732
9134
  }
@@ -9868,8 +9270,8 @@ async function downloadProject(workspace, localRoot, options = {}) {
9868
9270
  return { written, failed };
9869
9271
  }
9870
9272
 
9871
- // packages/core/src/cli/artifacts.ts
9872
- import { createHash as createHash7 } from "node:crypto";
9273
+ // ../core/src/cli/artifacts.ts
9274
+ import { createHash as createHash6 } from "node:crypto";
9873
9275
  var ARTIFACT_DIRECTORY = ".archymedes/artifacts";
9874
9276
  var MAX_ARTIFACT_BYTES = 480000;
9875
9277
  function safeToolName(toolName) {
@@ -9877,7 +9279,7 @@ function safeToolName(toolName) {
9877
9279
  return cleaned || "tool";
9878
9280
  }
9879
9281
  function artifactPathFor(toolName, content) {
9880
- const digest2 = createHash7("sha256").update(content).digest("hex").slice(0, 12);
9282
+ const digest2 = createHash6("sha256").update(content).digest("hex").slice(0, 12);
9881
9283
  return `${ARTIFACT_DIRECTORY}/${safeToolName(toolName)}-${digest2}.txt`;
9882
9284
  }
9883
9285
 
@@ -9912,12 +9314,12 @@ function countLines(content) {
9912
9314
  return lines;
9913
9315
  }
9914
9316
 
9915
- // packages/core/src/cli/defender-brain.ts
9317
+ // ../core/src/cli/defender-brain.ts
9916
9318
  import { access as access2 } from "node:fs/promises";
9917
9319
  import path14 from "node:path";
9918
9320
  import { fileURLToPath } from "node:url";
9919
9321
 
9920
- // packages/core/src/cli/state-client.ts
9322
+ // ../core/src/cli/state-client.ts
9921
9323
  import { spawn as spawn6 } from "node:child_process";
9922
9324
  import { access } from "node:fs/promises";
9923
9325
  import { createRequire } from "node:module";
@@ -10131,8 +9533,8 @@ async function tryConnectArchymedesState(options = {}) {
10131
9533
  return binary ? ArchymedesStateClient.connect({ ...options, binary }) : null;
10132
9534
  }
10133
9535
 
10134
- // packages/core/src/cli/defender-feed.ts
10135
- import { createHash as createHash8, createPublicKey, verify as verifySignature } from "node:crypto";
9536
+ // ../core/src/cli/defender-feed.ts
9537
+ import { createHash as createHash7, createPublicKey, verify as verifySignature } from "node:crypto";
10136
9538
  import { promises as fs10 } from "node:fs";
10137
9539
  import path13 from "node:path";
10138
9540
  var DEFENDER_FEED_INTERVAL_MS = 6 * 60 * 60 * 1000;
@@ -10326,7 +9728,7 @@ async function refreshDefenderFeed(options) {
10326
9728
  const bytes = await boundedResponseBody(corpusResponse, manifest.corpus.bytes, "corpus");
10327
9729
  if (bytes.byteLength !== manifest.corpus.bytes)
10328
9730
  throw new Error("corpus byte count does not match manifest");
10329
- if (createHash8("sha256").update(bytes).digest("hex") !== manifest.corpus.sha256)
9731
+ if (createHash7("sha256").update(bytes).digest("hex") !== manifest.corpus.sha256)
10330
9732
  throw new Error("corpus digest does not match manifest");
10331
9733
  const records = bytes.toString("utf8").split(`
10332
9734
  `).filter((line) => line.trim()).length;
@@ -10360,7 +9762,7 @@ async function refreshDefenderFeed(options) {
10360
9762
  }
10361
9763
  }
10362
9764
 
10363
- // packages/core/src/cli/defender-brain.ts
9765
+ // ../core/src/cli/defender-brain.ts
10364
9766
  async function existingDirectory(candidates) {
10365
9767
  for (const candidate of candidates) {
10366
9768
  try {
@@ -10446,7 +9848,7 @@ class DefenderBrain {
10446
9848
  }
10447
9849
  }
10448
9850
 
10449
- // packages/core/src/cli/tool-profile.ts
9851
+ // ../core/src/cli/tool-profile.ts
10450
9852
  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;
10451
9853
  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;
10452
9854
  var DIRECT_CHAT = /^(?:hi|hello|hey|thanks|thank you|who are you|what can you do)[.!?\s]*$/i;
@@ -10473,7 +9875,7 @@ function toolsForProfile(tools, profile) {
10473
9875
  return tools.filter((tool) => tool.effect === "none");
10474
9876
  }
10475
9877
 
10476
- // packages/core/src/cli/jev.ts
9878
+ // ../core/src/cli/jev.ts
10477
9879
  var JEV_API_URL = "https://api.typesafe.ai/v1/systemone";
10478
9880
  var JEV_DEFAULT_MODEL = "jev-latest";
10479
9881
  var JEV_TIMEOUT_MS = 30000;
@@ -10662,6 +10064,26 @@ async function requestJevVerdict(options) {
10662
10064
  throw new JevError(200, "Jev returned a body that could not be read as a verdict");
10663
10065
  }
10664
10066
  }
10067
+ var DIFFICULTY_QUESTIONS = {
10068
+ difficulty: {
10069
+ type: "choice",
10070
+ instructions: "How much careful reasoning does this coding task need from the person or agent doing it?",
10071
+ criteria: {
10072
+ routine: "A direct, well-specified change or question with an obvious approach: add a file, rename, run a command, explain something simple",
10073
+ involved: "Several steps or files, but each one is clear once the previous is done",
10074
+ hard: "Diagnosing a failure, an unclear cause, a design or architecture decision, a subtle bug, security or concurrency reasoning, or a large refactor"
10075
+ }
10076
+ }
10077
+ };
10078
+ function difficultyFromResponse(response) {
10079
+ const answer = response.answers.difficulty;
10080
+ if (!answer || answer.type !== "choice")
10081
+ return;
10082
+ if (answer.choice !== "routine" && answer.choice !== "involved" && answer.choice !== "hard")
10083
+ return;
10084
+ const probability2 = answer.probabilities[answer.choice] ?? answer.confidence;
10085
+ return probability2 >= 0.55 ? { difficulty: answer.choice, probability: probability2 } : undefined;
10086
+ }
10665
10087
  function turnVerdictState(input) {
10666
10088
  const tools = input.toolNames.length > 0 ? `Tools used: ${[...new Set(input.toolNames)].join(", ")}.` : "No tools used.";
10667
10089
  const lines = [
@@ -10696,7 +10118,7 @@ function verdictFromResponse(response) {
10696
10118
  };
10697
10119
  }
10698
10120
 
10699
- // packages/core/src/cli/agent.ts
10121
+ // ../core/src/cli/agent.ts
10700
10122
  var DELEGATION_TOOLS = new Set(["delegate_task", "delegate_readonly_task"]);
10701
10123
  var EDIT_DIAGNOSTIC_TOOLS = new Set(["edit_file", "write_file"]);
10702
10124
  var EDIT_DIAGNOSTICS_TIMEOUT_MS = 3000;
@@ -10799,6 +10221,9 @@ class ArchymedesAgent {
10799
10221
  const { maxRwf: _maxRwf, ...callerLimits } = this.options.budgets ?? {};
10800
10222
  Object.assign(this.budgets, this.modelBudgets(), callerLimits);
10801
10223
  }
10224
+ warmUp() {
10225
+ this.loadEnvironment();
10226
+ }
10802
10227
  loadEnvironment() {
10803
10228
  this.environment ??= probeEnvironment(this.workspace);
10804
10229
  return this.environment.catch(() => {
@@ -10812,7 +10237,7 @@ class ArchymedesAgent {
10812
10237
  this.nestedInstructions = new NestedInstructionTracker(this.workspace);
10813
10238
  this.artifacts = new WorkspaceArtifactStore(this.workspace);
10814
10239
  this.defenderBrain = new DefenderBrain(path15.join(options.root, ".archymedes", "security-brain"));
10815
- this.checkpoints = new CheckpointStore(options.root, path15.join(options.root, ".archymedes", `checkpoint-index-${randomUUID5()}`), options.git);
10240
+ this.checkpoints = new CheckpointStore(options.root, path15.join(options.root, ".archymedes", `checkpoint-index-${randomUUID4()}`), options.git);
10816
10241
  this.session = {
10817
10242
  schemaVersion: 2,
10818
10243
  revision: 0,
@@ -11102,7 +10527,7 @@ class ArchymedesAgent {
11102
10527
  this.delegatedInFlightRwf += reservation;
11103
10528
  try {
11104
10529
  const runtime = new BoundedAgentRuntime({
11105
- model: this.recovery.wrap(this.options.model, () => this.session, this.options.prices, `delegate:${randomUUID5()}`),
10530
+ model: this.recovery.wrap(this.options.model, () => this.session, this.options.prices, `delegate:${randomUUID4()}`),
11106
10531
  tools,
11107
10532
  prices: this.options.prices,
11108
10533
  artifacts: this.artifacts,
@@ -11116,7 +10541,7 @@ class ArchymedesAgent {
11116
10541
  const result = await runtime.execute({
11117
10542
  taskId: `${this.session.id}_delegate`,
11118
10543
  runId: this.session.id,
11119
- stepId: `delegate_${randomUUID5()}`,
10544
+ stepId: `delegate_${randomUUID4()}`,
11120
10545
  objective: task,
11121
10546
  history: [],
11122
10547
  systemPrompt,
@@ -11159,7 +10584,7 @@ class ArchymedesAgent {
11159
10584
  this.turnAbort = null;
11160
10585
  throw error;
11161
10586
  }
11162
- const turnId = `turn_${randomUUID5()}`;
10587
+ const turnId = `turn_${randomUUID4()}`;
11163
10588
  this.activeTurnId = turnId;
11164
10589
  let turnStatus = "queued";
11165
10590
  const transition = async (to, durable = false) => {
@@ -11218,17 +10643,27 @@ class ArchymedesAgent {
11218
10643
 
11219
10644
  ${requested}` : requested;
11220
10645
  let checkpoint;
10646
+ let checkpointReady = Promise.resolve();
11221
10647
  if (this.options.mode !== "plan" && this.workspace.kind === "local") {
11222
- checkpoint = await this.checkpoints.capture(titleFromObjective(objective), turnId, this.messages.length);
11223
- if (checkpoint)
11224
- this.options.onEvent?.({ type: "checkpoint", checkpoint });
10648
+ checkpointReady = this.checkpoints.capture(titleFromObjective(objective), turnId, this.messages.length).then((taken) => {
10649
+ checkpoint = taken;
10650
+ if (taken)
10651
+ this.options.onEvent?.({ type: "checkpoint", checkpoint: taken });
10652
+ }).catch(() => {
10653
+ return;
10654
+ });
11225
10655
  }
11226
10656
  const compaction = await this.compactIfNeeded(turnId, objective, turnAbort.signal, tokenSaver);
11227
10657
  compactionActualRwf = compaction.actualRwf;
11228
10658
  this.permissions.setTaskHint(objective);
10659
+ this.startDifficultyReading(objective, turnAbort.signal);
11229
10660
  const runtime = new BoundedAgentRuntime({
11230
10661
  model: this.recovery.wrap(this.options.model, () => this.session, this.options.prices, "main"),
11231
- tools: scoped,
10662
+ ...this.options.reasoning === "auto" ? { effortPolicy: (signals) => predictEffort({ ...signals, objective, ...this.jevDifficulty ? { difficultyHint: this.jevDifficulty } : {} }) } : {},
10663
+ tools: scoped.map((tool) => tool.effect === "none" ? tool : { ...tool, execute: async (args, context) => {
10664
+ await checkpointReady;
10665
+ return tool.execute(args, context);
10666
+ } }),
11232
10667
  prices: this.options.prices,
11233
10668
  artifacts: this.artifacts,
11234
10669
  control: {
@@ -11267,6 +10702,7 @@ ${requested}` : requested;
11267
10702
  runId: this.session.id,
11268
10703
  stepId: `turn_${this.messages.length}`,
11269
10704
  objective: turnObjective,
10705
+ ...this.options.reasoning && this.options.reasoning !== "auto" ? { effort: this.options.reasoning } : {},
11270
10706
  images: attached.images,
11271
10707
  history: sentHistory,
11272
10708
  systemPrompt,
@@ -11301,6 +10737,7 @@ ${hookWarnings.join(`
11301
10737
  hostedRecoveryBatchId: this.recovery.settledBatchId() ?? this.session.hostedRecoveryBatchId,
11302
10738
  updatedAt: Date.now()
11303
10739
  };
10740
+ await checkpointReady;
11304
10741
  const terminalStatus = runtimeStatusToTurnStatus(result.status);
11305
10742
  await transition(terminalStatus, true);
11306
10743
  await this.recordHostedUsage();
@@ -11351,6 +10788,8 @@ ${hookWarnings.join(`
11351
10788
  for (const call of message.toolCalls)
11352
10789
  toolNames.push(call.name);
11353
10790
  }
10791
+ if (toolNames.length === 0)
10792
+ return;
11354
10793
  try {
11355
10794
  const response = await requestJevVerdict({
11356
10795
  apiKey: this.jev.apiKey,
@@ -11385,6 +10824,29 @@ ${hookWarnings.join(`
11385
10824
  return (verdict.outcomeProbabilities[verdict.outcome] ?? 0) >= 0.5;
11386
10825
  }
11387
10826
  jevCorrectionDepth = 0;
10827
+ jevDifficulty;
10828
+ difficultyStamp = 0;
10829
+ startDifficultyReading(objective, signal) {
10830
+ this.jevDifficulty = undefined;
10831
+ const stamp = ++this.difficultyStamp;
10832
+ if (this.options.reasoning !== "auto" || !this.jev)
10833
+ return;
10834
+ requestJevVerdict({
10835
+ apiKey: this.jev.apiKey,
10836
+ ...this.jev.model ? { model: this.jev.model } : {},
10837
+ ...this.jev.fetchImpl ? { fetchImpl: this.jev.fetchImpl } : {},
10838
+ state: objective,
10839
+ questions: DIFFICULTY_QUESTIONS,
10840
+ timeoutMs: 5000,
10841
+ signal
10842
+ }).then((response) => {
10843
+ if (stamp !== this.difficultyStamp)
10844
+ return;
10845
+ this.jevDifficulty = difficultyFromResponse(response)?.difficulty;
10846
+ }).catch(() => {
10847
+ return;
10848
+ });
10849
+ }
11388
10850
  async correctivePrompt(objective, verdict, evidence) {
11389
10851
  const failures = evidence.filter((entry) => entry.exitCode !== undefined && entry.exitCode !== 0).slice(-6);
11390
10852
  const failureLine = failures.length > 0 ? `Failing commands from the last turn: ${failures.map((entry) => `\`${entry.command ?? entry.tool}\` exited ${entry.exitCode}`).join("; ")}.` : "The last turn produced no passing verification evidence.";
@@ -11484,15 +10946,15 @@ function isActiveTurnStatus(status) {
11484
10946
  return status === "queued" || status === "running" || status === "waiting_approval";
11485
10947
  }
11486
10948
 
11487
- // packages/core/src/cli/edit-diagnostics.ts
10949
+ // ../core/src/cli/edit-diagnostics.ts
11488
10950
  import { promises as fs11 } from "node:fs";
11489
10951
  import path16 from "node:path";
11490
10952
  import { fileURLToPath as fileURLToPath2, pathToFileURL } from "node:url";
11491
10953
 
11492
- // packages/core/src/lsp/client.ts
10954
+ // ../core/src/lsp/client.ts
11493
10955
  import { spawn as spawn7, spawnSync } from "node:child_process";
11494
10956
 
11495
- // packages/core/src/lsp/diagnostics.ts
10957
+ // ../core/src/lsp/diagnostics.ts
11496
10958
  function severityName(severity) {
11497
10959
  switch (severity) {
11498
10960
  case 1:
@@ -11598,7 +11060,7 @@ class DiagnosticsCollection {
11598
11060
  }
11599
11061
  }
11600
11062
 
11601
- // packages/core/src/lsp/protocol.ts
11063
+ // ../core/src/lsp/protocol.ts
11602
11064
  var HEADER_SEPARATOR = `\r
11603
11065
  \r
11604
11066
  `;
@@ -11644,7 +11106,7 @@ function isJsonRpcNotification3(value) {
11644
11106
  return typeof value === "object" && value !== null && "jsonrpc" in value && !("id" in value) && typeof value.method === "string";
11645
11107
  }
11646
11108
 
11647
- // packages/core/src/lsp/client.ts
11109
+ // ../core/src/lsp/client.ts
11648
11110
  class LspClientError extends Error {
11649
11111
  constructor(message) {
11650
11112
  super(message);
@@ -11855,7 +11317,7 @@ function toLocations(result) {
11855
11317
  return candidates.filter((candidate) => typeof candidate === "object" && candidate !== null && typeof candidate.uri === "string" && typeof candidate.range === "object");
11856
11318
  }
11857
11319
 
11858
- // packages/core/src/lsp/servers.ts
11320
+ // ../core/src/lsp/servers.ts
11859
11321
  import { spawnSync as spawnSync2 } from "node:child_process";
11860
11322
  var LANGUAGE_SERVERS = [
11861
11323
  {
@@ -11996,7 +11458,7 @@ function defaultProbe(command) {
11996
11458
  });
11997
11459
  }
11998
11460
 
11999
- // packages/core/src/cli/edit-diagnostics.ts
11461
+ // ../core/src/cli/edit-diagnostics.ts
12000
11462
  var DEFAULT_TIMEOUT_MS = 1500;
12001
11463
  var DEFAULT_REFINE_MS = 250;
12002
11464
  var DEFAULT_MAX_LINES = 20;
@@ -12217,7 +11679,7 @@ function createLspEditDiagnostics(root, options = {}) {
12217
11679
  });
12218
11680
  }
12219
11681
 
12220
- // packages/core/src/providers/price-catalog.ts
11682
+ // ../core/src/providers/price-catalog.ts
12221
11683
  var ANTHROPIC_VERIFIED = "2026-06-24";
12222
11684
  var ANTHROPIC_SOURCE = "anthropic.com/pricing, recorded 2026-06-24";
12223
11685
  var ANTHROPIC = [
@@ -12269,7 +11731,7 @@ var SERVICES = [
12269
11731
  ];
12270
11732
  var PRICE_CATALOG = definePrices([...ANTHROPIC, ...OAI_COMPAT_LABS, ...SERVICES]);
12271
11733
 
12272
- // packages/core/src/providers/free-catalog.ts
11734
+ // ../core/src/providers/free-catalog.ts
12273
11735
  var FREE_ROUTER = "openrouter/free";
12274
11736
  var FREE_BASE_URL = "https://openrouter.ai/api/v1";
12275
11737
  var FREE_GATEWAY_URL = "";
@@ -12278,6 +11740,7 @@ var FREE_CATALOG_TTL_MS = 6 * 60 * 60 * 1000;
12278
11740
  var FREE_CATALOG_MAX_RECORDS = 1e4;
12279
11741
  var FREE_ROUTER_PREFERENCE = [
12280
11742
  "cohere/north-mini-code:free",
11743
+ "nvidia/nemotron-3-super-120b-a12b:free",
12281
11744
  "google/gemma-4-31b-it:free",
12282
11745
  "dots-studio/dots-3-note-preview:free",
12283
11746
  "nex-agi/nex-n2.5-pro:free",
@@ -12404,8 +11867,21 @@ function mergeFreeCatalog(live, discovery, fetchedAt, warnings = []) {
12404
11867
  }
12405
11868
  return { fetchedAt, models: [...byId.values()].sort((a, b) => a.id.localeCompare(b.id)), warnings };
12406
11869
  }
11870
+ var SMALL_MODEL_BILLIONS = 4;
11871
+ function modelSizeBillions(model) {
11872
+ const id = model.toLowerCase();
11873
+ const billions = [...id.matchAll(/(?:^|[^a-z0-9.])(\d+(?:\.\d+)?)b(?![a-z])/g)];
11874
+ if (billions.length > 0)
11875
+ return Math.min(...billions.map((match) => Number(match[1])));
11876
+ const millions = id.match(/(?:^|[^a-z0-9.])(\d+(?:\.\d+)?)m(?![a-z])/);
11877
+ return millions ? Number(millions[1]) / 1000 : null;
11878
+ }
11879
+ function isSmallModel(model) {
11880
+ const size = modelSizeBillions(model);
11881
+ return size !== null && size <= SMALL_MODEL_BILLIONS;
11882
+ }
12407
11883
 
12408
- // packages/core/src/providers/provider-specs.ts
11884
+ // ../core/src/providers/provider-specs.ts
12409
11885
  var PROVIDER_IDS = [
12410
11886
  "anthropic",
12411
11887
  "openai",
@@ -12466,7 +11942,7 @@ var PROVIDER_INFO = {
12466
11942
  }
12467
11943
  };
12468
11944
 
12469
- // packages/core/src/providers/anthropic-agent.ts
11945
+ // ../core/src/providers/anthropic-agent.ts
12470
11946
  async function collectAnthropicStream(stream, onTextDelta, onOutputProgress) {
12471
11947
  let id = "";
12472
11948
  let model = "";
@@ -12625,7 +12101,7 @@ class AnthropicAgentTurnProvider {
12625
12101
  let client;
12626
12102
  this.call = async (body, signal) => {
12627
12103
  if (!client) {
12628
- const { default: Anthropic } = await import("./index-teq1cta4.mjs").catch(() => {
12104
+ const { default: Anthropic } = await import("./index-qvgcwsvm.mjs").catch(() => {
12629
12105
  throw new Error("The Anthropic provider needs the @anthropic-ai/sdk package. Install it with: npm install @anthropic-ai/sdk");
12630
12106
  });
12631
12107
  client = new Anthropic({ apiKey: options.apiKey, ...options.baseURL ? { baseURL: options.baseURL } : {}, maxRetries: 0 });
@@ -12645,7 +12121,7 @@ class AnthropicAgentTurnProvider {
12645
12121
  ...system ? { system } : {},
12646
12122
  messages,
12647
12123
  tools,
12648
- ...request.effort && this.capabilities.supportsEffort ? { output_config: { effort: request.effort } } : {},
12124
+ ...request.effort && this.capabilities.supportsEffort ? { output_config: { effort: providerEffort(request.effort) } } : {},
12649
12125
  metadata: { user_id: request.safetyIdentifier },
12650
12126
  stream: true
12651
12127
  };
@@ -12687,10 +12163,10 @@ class AnthropicAgentTurnProvider {
12687
12163
  }
12688
12164
  }
12689
12165
 
12690
- // packages/core/src/providers/archymedes-cloud-agent.ts
12691
- import { createHash as createHash9, randomUUID as randomUUID6 } from "node:crypto";
12166
+ // ../core/src/providers/archymedes-cloud-agent.ts
12167
+ import { createHash as createHash8, randomUUID as randomUUID5 } from "node:crypto";
12692
12168
 
12693
- // packages/core/src/providers/routing-plan.ts
12169
+ // ../core/src/providers/routing-plan.ts
12694
12170
  function isRecord2(value) {
12695
12171
  return typeof value === "object" && value !== null;
12696
12172
  }
@@ -12762,7 +12238,7 @@ function planCurrency(ranked) {
12762
12238
  return;
12763
12239
  }
12764
12240
 
12765
- // packages/core/src/providers/credit-balance.ts
12241
+ // ../core/src/providers/credit-balance.ts
12766
12242
  function isRecord3(value) {
12767
12243
  return typeof value === "object" && value !== null;
12768
12244
  }
@@ -12797,7 +12273,7 @@ function parseCreditBalance(value) {
12797
12273
  };
12798
12274
  }
12799
12275
 
12800
- // packages/core/src/providers/archymedes-cloud-agent.ts
12276
+ // ../core/src/providers/archymedes-cloud-agent.ts
12801
12277
  class ArchymedesCloudError extends Error {
12802
12278
  status;
12803
12279
  code;
@@ -12863,7 +12339,7 @@ class ArchymedesCloudTurnProvider {
12863
12339
  this.planUrl = base.endsWith("/v1") ? `${base}/routes/plan` : `${base}/v1/routes/plan`;
12864
12340
  this.balanceUrl = base.endsWith("/v1") ? `${base}/credits/balance` : `${base}/v1/credits/balance`;
12865
12341
  this.capabilities = capabilitiesFor(this.model);
12866
- this.recoveryScope = createHash9("sha256").update(JSON.stringify({
12342
+ this.recoveryScope = createHash8("sha256").update(JSON.stringify({
12867
12343
  version: 1,
12868
12344
  endpoint: this.completionUrl,
12869
12345
  token: options.token,
@@ -12946,7 +12422,7 @@ class ArchymedesCloudTurnProvider {
12946
12422
  async exchange(request, recoveryOnly) {
12947
12423
  if (!request.safetyIdentifier.trim())
12948
12424
  throw new Error("safetyIdentifier is required");
12949
- const taskId = request.requestId ?? `cli_${randomUUID6()}`;
12425
+ const taskId = request.requestId ?? `cli_${randomUUID5()}`;
12950
12426
  if (!/^[A-Za-z0-9_-]{1,160}$/.test(taskId))
12951
12427
  throw new Error("requestId must contain 1 to 160 letters, numbers, underscores or hyphens");
12952
12428
  request.signal?.throwIfAborted();
@@ -12973,7 +12449,7 @@ class ArchymedesCloudTurnProvider {
12973
12449
  max_completion_tokens: request.maxOutputTokens,
12974
12450
  safety_identifier: request.safetyIdentifier,
12975
12451
  prompt_cache_key: request.safetyIdentifier,
12976
- ...request.effort ? { reasoning_effort: request.effort } : {},
12452
+ ...request.effort ? { reasoning_effort: providerEffort(request.effort) } : {},
12977
12453
  stream: false,
12978
12454
  archymedes: {
12979
12455
  task_id: taskId,
@@ -13014,7 +12490,7 @@ async function readBody(response) {
13014
12490
  }
13015
12491
  }
13016
12492
 
13017
- // packages/core/src/providers/request-pacer.ts
12493
+ // ../core/src/providers/request-pacer.ts
13018
12494
  var DEFAULTS = { baseGapMs: 2000, maxGapMs: 120000, releaseMs: 500 };
13019
12495
 
13020
12496
  class RequestPacer {
@@ -13023,12 +12499,17 @@ class RequestPacer {
13023
12499
  baseGapMs;
13024
12500
  maxGapMs;
13025
12501
  releaseMs;
12502
+ windowMs;
12503
+ cap;
12504
+ starts = [];
13026
12505
  now;
13027
12506
  sleep;
13028
12507
  constructor(options = {}) {
13029
12508
  this.baseGapMs = options.baseGapMs ?? DEFAULTS.baseGapMs;
13030
12509
  this.maxGapMs = options.maxGapMs ?? DEFAULTS.maxGapMs;
13031
12510
  this.releaseMs = options.releaseMs ?? DEFAULTS.releaseMs;
12511
+ this.windowMs = options.windowMs ?? 60000;
12512
+ this.cap = options.windowCap !== undefined && options.windowCap >= 1 ? Math.floor(options.windowCap) : undefined;
13032
12513
  this.now = options.now ?? Date.now;
13033
12514
  this.sleep = options.sleep ?? ((delayMs, signal) => new Promise((resolve, reject) => {
13034
12515
  if (signal?.aborted) {
@@ -13046,33 +12527,61 @@ class RequestPacer {
13046
12527
  signal?.addEventListener("abort", onAbort, { once: true });
13047
12528
  }));
13048
12529
  }
12530
+ get windowBudget() {
12531
+ return this.cap;
12532
+ }
12533
+ get inWindow() {
12534
+ this.prune(this.now());
12535
+ return this.starts.length;
12536
+ }
12537
+ prune(now) {
12538
+ let drop = 0;
12539
+ while (drop < this.starts.length && now - this.starts[drop] >= this.windowMs)
12540
+ drop += 1;
12541
+ if (drop > 0)
12542
+ this.starts.splice(0, drop);
12543
+ }
13049
12544
  get paceMs() {
13050
12545
  return this.gapMs;
13051
12546
  }
13052
12547
  async wait(signal) {
13053
12548
  signal?.throwIfAborted();
13054
- if (this.gapMs <= 0) {
13055
- this.lastStart = this.now();
13056
- return;
12549
+ if (this.cap !== undefined) {
12550
+ for (;; ) {
12551
+ const now = this.now();
12552
+ this.prune(now);
12553
+ if (this.starts.length < this.cap)
12554
+ break;
12555
+ await this.sleep(this.starts[0] + this.windowMs - now + 25, signal);
12556
+ signal?.throwIfAborted();
12557
+ }
12558
+ }
12559
+ if (this.gapMs > 0) {
12560
+ const remaining = this.gapMs - (this.now() - this.lastStart);
12561
+ if (remaining > 0)
12562
+ await this.sleep(remaining, signal);
12563
+ signal?.throwIfAborted();
13057
12564
  }
13058
- const elapsed = this.now() - this.lastStart;
13059
- const remaining = this.gapMs - elapsed;
13060
- if (remaining > 0)
13061
- await this.sleep(remaining, signal);
13062
- signal?.throwIfAborted();
13063
12565
  this.lastStart = this.now();
12566
+ this.starts.push(this.lastStart);
12567
+ if (this.starts.length > 512)
12568
+ this.starts.splice(0, this.starts.length - 512);
13064
12569
  }
13065
12570
  reportSuccess() {
13066
12571
  this.gapMs = Math.max(0, this.gapMs - this.releaseMs);
13067
12572
  }
13068
12573
  reportRateLimited(retryAfterMs) {
12574
+ this.prune(this.now());
12575
+ const admitted = this.starts.length - 1;
12576
+ if (admitted >= 2)
12577
+ this.cap = Math.min(this.cap ?? Number.POSITIVE_INFINITY, admitted);
13069
12578
  const doubled = this.gapMs <= 0 ? this.baseGapMs : Math.min(this.maxGapMs, this.gapMs * 2);
13070
12579
  const asked = typeof retryAfterMs === "number" && Number.isFinite(retryAfterMs) && retryAfterMs > 0 ? retryAfterMs : 0;
13071
12580
  this.gapMs = Math.min(this.maxGapMs, Math.max(doubled, Math.min(this.maxGapMs, asked)));
13072
12581
  }
13073
12582
  }
13074
12583
 
13075
- // packages/core/src/providers/free-catalog-fetch.ts
12584
+ // ../core/src/providers/free-catalog-fetch.ts
13076
12585
  var MAX_BYTES = 8 * 1024 * 1024;
13077
12586
  async function boundedJson(fetchImpl, url, signal) {
13078
12587
  const response = await fetchImpl(url, { signal, redirect: "error", headers: { accept: "application/json" } });
@@ -13136,7 +12645,7 @@ async function fetchFreeCatalog(options = {}) {
13136
12645
  }
13137
12646
  }
13138
12647
 
13139
- // packages/core/src/providers/free-install.ts
12648
+ // ../core/src/providers/free-install.ts
13140
12649
  import { promises as fs12 } from "node:fs";
13141
12650
  import path17 from "node:path";
13142
12651
  var FREE_INSTALL_HEADER = "x-archymedes-install";
@@ -13314,7 +12823,7 @@ class FreeInstallToken {
13314
12823
  }
13315
12824
  }
13316
12825
 
13317
- // packages/core/src/providers/free-errors.ts
12826
+ // ../core/src/providers/free-errors.ts
13318
12827
  var OPENROUTER_PRIVACY_SETTINGS_URL = "https://openrouter.ai/settings/privacy";
13319
12828
  var OPENROUTER_KEYS_URL = "https://openrouter.ai/keys";
13320
12829
  var OPENROUTER_CREDITS_URL = "https://openrouter.ai/settings/credits";
@@ -13390,7 +12899,7 @@ function describeFreeFailure(input) {
13390
12899
  return { message, retryable: transient, ...retryAfterMs2 ? { retryAfterMs: retryAfterMs2 } : {} };
13391
12900
  }
13392
12901
 
13393
- // packages/core/src/providers/free-health.ts
12902
+ // ../core/src/providers/free-health.ts
13394
12903
  import { promises as fs13 } from "node:fs";
13395
12904
  import path18 from "node:path";
13396
12905
  var FREE_HEALTH_WINDOW_MS = 7 * 24 * 60 * 60 * 1000;
@@ -13443,7 +12952,7 @@ function recordFreeOutcome(records, id, outcome, now) {
13443
12952
  return Object.fromEntries(newest);
13444
12953
  }
13445
12954
  function orderByFreeHealth(models, records, now) {
13446
- return models.map((model, index) => ({ model, index, score: healthScore(records[model.id], now) })).sort((a, b) => b.score - a.score || a.index - b.index).map(({ model }) => model);
12955
+ return models.map((model, index) => ({ model, index, small: isSmallModel(model.id), score: healthScore(records[model.id], now) })).sort((a, b) => Number(a.small) - Number(b.small) || b.score - a.score || a.index - b.index).map(({ model }) => model);
13447
12956
  }
13448
12957
  function fileFreeHealthStore(file) {
13449
12958
  return {
@@ -13499,7 +13008,7 @@ class FreeHealthTracker {
13499
13008
  }
13500
13009
  }
13501
13010
 
13502
- // packages/core/src/providers/openrouter-key-info.ts
13011
+ // ../core/src/providers/openrouter-key-info.ts
13503
13012
  var OPENROUTER_KEY_URL = `${FREE_BASE_URL}/key`;
13504
13013
  var OPENROUTER_KEY_INFO_TTL_MS = 60000;
13505
13014
  var KEY_INFO_TIMEOUT_MS = 5000;
@@ -13602,7 +13111,7 @@ function nextUtcMidnight(now) {
13602
13111
  return new Date(Date.UTC(date.getUTCFullYear(), date.getUTCMonth(), date.getUTCDate() + 1)).toISOString();
13603
13112
  }
13604
13113
 
13605
- // packages/core/src/providers/tool-call-repair.ts
13114
+ // ../core/src/providers/tool-call-repair.ts
13606
13115
  function isObject(value) {
13607
13116
  return typeof value === "object" && value !== null && !Array.isArray(value);
13608
13117
  }
@@ -13712,7 +13221,7 @@ function repairToolCalls(calls) {
13712
13221
  });
13713
13222
  }
13714
13223
 
13715
- // packages/core/src/providers/free-agent.ts
13224
+ // ../core/src/providers/free-agent.ts
13716
13225
  var RETRYABLE_UPSTREAM_TYPES = new Set(["upstream_timeout", "upstream_idle_timeout", "upstream_unreachable", "upstream_stream_failed"]);
13717
13226
  function freeFailureStatus(error) {
13718
13227
  const record2 = error;
@@ -13730,6 +13239,12 @@ function isTransportFailure(error) {
13730
13239
  const name = error?.name;
13731
13240
  return error instanceof APIConnectionError || name === "APIConnectionError" || name === "APIConnectionTimeoutError" || error instanceof TypeError || Boolean(error?.cause);
13732
13241
  }
13242
+ function openRouterEffort(effort) {
13243
+ if (!effort)
13244
+ return;
13245
+ return effort === "xhigh" || effort === "max" ? "high" : effort;
13246
+ }
13247
+ var FREE_REQUESTS_PER_MINUTE = 20;
13733
13248
  var MAX_ROUTER_ATTEMPTS = 4;
13734
13249
  var CATALOG_TIMEOUT_MS = 15000;
13735
13250
  var FREE_CANDIDATE_FIRST_BYTE_TIMEOUT_MS = 45000;
@@ -13778,7 +13293,7 @@ class FreeAgentTurnProvider {
13778
13293
  this.options = options;
13779
13294
  this.dependencies = dependencies;
13780
13295
  this.selection = { provider: "free", model: options.model };
13781
- this.pacer = dependencies.pacer ?? new RequestPacer;
13296
+ this.pacer = dependencies.pacer ?? new RequestPacer({ windowCap: FREE_REQUESTS_PER_MINUTE });
13782
13297
  this.tokenSaver = options.tokenSaver ?? true;
13783
13298
  const usage = dependencies.usage === undefined ? dependencies.call || dependencies.catalog ? undefined : new FreeUsageMeter(fileFreeUsageStore(freeUsageFile(process.env)), dependencies.now) : dependencies.usage ?? undefined;
13784
13299
  if (usage)
@@ -13839,7 +13354,9 @@ class FreeAgentTurnProvider {
13839
13354
  }
13840
13355
  const eligible = this.catalog.models.filter((model) => model.eligible && isFreeModelId(model.id));
13841
13356
  const selected = eligible.find((model) => model.id === this.options.model);
13842
- const candidates = this.options.model === FREE_ROUTER ? eligible.filter((model) => model.id !== FREE_ROUTER) : selected ? [selected] : [];
13357
+ const routable = eligible.filter((model) => model.id !== FREE_ROUTER);
13358
+ const capable = routable.filter((model) => !isSmallModel(model.id));
13359
+ const candidates = this.options.model === FREE_ROUTER ? capable.length > 0 ? capable : routable : selected ? [selected] : [];
13843
13360
  if (!candidates.length)
13844
13361
  throw new FreeAccessError("No eligible free tool model is available. Run /models refresh or try again later.");
13845
13362
  const messages = toWireMessages(request.messages);
@@ -13944,6 +13461,7 @@ class FreeAgentTurnProvider {
13944
13461
  messages: withOpenRouterCacheControl(messages, model),
13945
13462
  max_tokens: output,
13946
13463
  ...request.tools.length ? { tools: request.tools.map((tool) => ({ type: "function", function: { name: tool.name, description: tool.description, parameters: tool.inputSchema } })) } : {},
13464
+ ...openRouterEffort(request.effort) ? { reasoning: { effort: openRouterEffort(request.effort) } } : {},
13947
13465
  provider: { require_parameters: true, max_price: { prompt: 0, completion: 0 }, allow_fallbacks: false },
13948
13466
  stream: true,
13949
13467
  stream_options: { include_usage: true }
@@ -13978,7 +13496,7 @@ class FreeAgentTurnProvider {
13978
13496
  }
13979
13497
  }
13980
13498
 
13981
- // packages/core/src/providers/openrouter-agent.ts
13499
+ // ../core/src/providers/openrouter-agent.ts
13982
13500
  var OPENROUTER_BASE_URL = "https://openrouter.ai/api/v1";
13983
13501
 
13984
13502
  class OpenRouterAgentTurnProvider {
@@ -14027,7 +13545,7 @@ function withHint(error) {
14027
13545
  return wrapped;
14028
13546
  }
14029
13547
 
14030
- // packages/core/src/providers/agent-matrix.ts
13548
+ // ../core/src/providers/agent-matrix.ts
14031
13549
  var OPENAI_COMPATIBLE_BASE_URL = {
14032
13550
  google: "https://generativelanguage.googleapis.com/v1beta/openai/",
14033
13551
  xai: "https://api.x.ai/v1",
@@ -14192,7 +13710,7 @@ function describeProviders(environment) {
14192
13710
  }
14193
13711
  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)";
14194
13712
 
14195
- // packages/archymedes-cli/src/app/session-provider.ts
13713
+ // src/app/session-provider.ts
14196
13714
  async function resolveSessionProvider(environment, options) {
14197
13715
  if (options.provider || !options.resume)
14198
13716
  return resolveProvider(environment, options);
@@ -14201,12 +13719,12 @@ async function resolveSessionProvider(environment, options) {
14201
13719
  return resolveProvider(environment, selection?.provider === "free" ? { provider: "free", model: options.model ?? selection.model } : options);
14202
13720
  }
14203
13721
 
14204
- // packages/archymedes-cli/src/platform/settings.ts
13722
+ // src/platform/settings.ts
14205
13723
  import { promises as fs15 } from "node:fs";
14206
13724
  import os4 from "node:os";
14207
13725
  import path20 from "node:path";
14208
13726
 
14209
- // packages/archymedes-cli/src/platform/i18n.ts
13727
+ // src/platform/i18n.ts
14210
13728
  var CONTROL_LANGUAGES = {
14211
13729
  en: "English",
14212
13730
  zh: "中文 (Mandarin)",
@@ -14673,11 +14191,11 @@ function t(language, key) {
14673
14191
  return MESSAGES[key][language] ?? MESSAGES[key].en;
14674
14192
  }
14675
14193
 
14676
- // packages/archymedes-cli/src/platform/local-currency.ts
14194
+ // src/platform/local-currency.ts
14677
14195
  import { promises as fs14 } from "node:fs";
14678
14196
  import path19 from "node:path";
14679
14197
 
14680
- // packages/archymedes-cli/src/platform/endpoints.ts
14198
+ // src/platform/endpoints.ts
14681
14199
  var FX_ENDPOINTS = [
14682
14200
  "https://cdn.jsdelivr.net/npm/@fawazahmed0/currency-api@latest/v1/currencies",
14683
14201
  "https://latest.currency-api.pages.dev/v1/currencies"
@@ -14721,7 +14239,7 @@ function hostOf(url) {
14721
14239
  }
14722
14240
  }
14723
14241
 
14724
- // packages/archymedes-cli/src/platform/network.ts
14242
+ // src/platform/network.ts
14725
14243
  var CODES = {
14726
14244
  dns: ["ENOTFOUND", "EAI_AGAIN", "EAI_NONAME", "EAI_FAIL", "EAI_MEMORY", "EAI_SYSTEM", "DNS_E_NAME_NOT_FOUND", "DNS_E_QUERY_PENDING"],
14727
14245
  timeout: ["ETIMEDOUT", "UND_ERR_CONNECT_TIMEOUT", "UND_ERR_HEADERS_TIMEOUT", "UND_ERR_BODY_TIMEOUT", "ERR_SOCKET_CONNECTION_TIMEOUT", "connection_timeout"],
@@ -14970,7 +14488,7 @@ function classifyNetworkError(error, options = {}) {
14970
14488
  return null;
14971
14489
  }
14972
14490
 
14973
- // packages/archymedes-cli/src/platform/local-currency.ts
14491
+ // src/platform/local-currency.ts
14974
14492
  var COUNTRY_CURRENCIES = {
14975
14493
  RW: "RWF",
14976
14494
  EG: "EGP",
@@ -15098,7 +14616,7 @@ async function startFxRateLookup(from, to, options) {
15098
14616
  return { immediate: cached?.rate ?? null, refresh };
15099
14617
  }
15100
14618
 
15101
- // packages/archymedes-cli/src/theme/theme.ts
14619
+ // src/theme/theme.ts
15102
14620
  var BLUEPRINT = `
15103
14621
  /* Blueprint navy, chalk-white ink and an amber pencil — the original drafting table. */
15104
14622
  @theme blueprint {
@@ -15400,7 +14918,7 @@ function roleCode(role, palette, depth) {
15400
14918
  }
15401
14919
  var NO_COLOR_PALETTE = buildPalette(findBuiltinTheme(DEFAULT_THEME_NAME) ?? { name: DEFAULT_THEME_NAME, description: "", tokens: DEFAULT_TOKENS }, "none");
15402
14920
 
15403
- // packages/archymedes-cli/src/platform/settings.ts
14921
+ // src/platform/settings.ts
15404
14922
  var LANGUAGE_NAMES = {
15405
14923
  en: "English",
15406
14924
  zh: "中文",
@@ -15504,6 +15022,13 @@ var AUTOSAVE_CHOICES = [
15504
15022
  { value: "off", label: "Off — save with Ctrl+S (default)" },
15505
15023
  { value: "on", label: "On — save a second after you stop typing" }
15506
15024
  ];
15025
+ var REASONING_CHOICES = [
15026
+ { value: "auto", label: "Auto (experimental) — lower the effort on routine steps, leave hard ones to the model" },
15027
+ { value: "minimal", label: "Minimal — never think more than needed" },
15028
+ { value: "low", label: "Low" },
15029
+ { value: "medium", label: "Medium" },
15030
+ { value: "high", label: "High — slowest" }
15031
+ ];
15507
15032
  var PROVIDER_CHOICES = PROVIDER_IDS.map((id) => ({ value: id, label: PROVIDER_INFO[id].label }));
15508
15033
  var SETTING_FIELDS = [
15509
15034
  { key: "ARCHYMEDES_THEME", label: "Theme — colours for the whole CLI (/theme <name> also saves it)", choices: THEME_CHOICES, section: "Appearance" },
@@ -15511,6 +15036,7 @@ var SETTING_FIELDS = [
15511
15036
  { key: "ARCHYMEDES_CODE_LINE_NUMBERS", label: "Line numbers in code blocks", choices: SHOW_HIDE_CHOICES },
15512
15037
  { key: "ARCHYMEDES_SIMPLE", label: "Simple mode — quiet start, short help", choices: SIMPLE_CHOICES },
15513
15038
  { key: "ARCHYMEDES_LANGUAGE", label: "Control language", choices: LANGUAGE_CHOICES },
15039
+ { key: "ARCHYMEDES_REASONING", label: "Reasoning effort — clear it to leave each model at its own default", choices: REASONING_CHOICES },
15514
15040
  { key: "ARCHYMEDES_RESUME", label: "Continue your last chat at startup", choices: RESUME_CHOICES, section: "Behaviour" },
15515
15041
  { key: "ARCHYMEDES_EDITOR_AUTOSAVE", label: "Auto-save in /edit", choices: AUTOSAVE_CHOICES },
15516
15042
  { key: "ARCHYMEDES_SUGGEST_MODEL", label: "Ask the model for extra suggestions (a small extra call per turn)", choices: ON_OFF_CHOICES },
@@ -15655,7 +15181,8 @@ var ENUM_SETTINGS = {
15655
15181
  ARCHYMEDES_RESUME: ["ask", "always", "never"],
15656
15182
  ARCHYMEDES_EDITOR_AUTOSAVE: ["on", "off"],
15657
15183
  ARCHYMEDES_SUGGEST_MODEL: ["on", "off"],
15658
- ARCHYMEDES_AUTO_UPDATE: ["install", "check", "off"]
15184
+ ARCHYMEDES_AUTO_UPDATE: ["install", "check", "off"],
15185
+ ARCHYMEDES_REASONING: ["auto", "minimal", "low", "medium", "high"]
15659
15186
  };
15660
15187
  var SETTING_CANCELLED = "\x00cancelled";
15661
15188
  function validateSetting(key, raw) {
@@ -15892,7 +15419,7 @@ Archymedes ${controlLabel(language, "settings")}
15892
15419
  }
15893
15420
  }
15894
15421
 
15895
- // packages/archymedes-cli/src/app/free-setup.ts
15422
+ // src/app/free-setup.ts
15896
15423
  var OPENROUTER_KEYS_URL2 = "https://openrouter.ai/keys";
15897
15424
  var FREE_PRIVACY_NOTICE = "Free models may log your prompts and code; don't send secrets. Use a paid provider for private code.";
15898
15425
  function needsFreePrivacyNotice(environment) {
@@ -15911,7 +15438,7 @@ async function showFreePrivacyNoticeOnce(options) {
15911
15438
  return next;
15912
15439
  }
15913
15440
 
15914
- // packages/archymedes-cli/src/app/acp-setup.ts
15441
+ // src/app/acp-setup.ts
15915
15442
  var KEY_URLS = {
15916
15443
  free: OPENROUTER_KEYS_URL2,
15917
15444
  openrouter: OPENROUTER_KEYS_URL2,
@@ -15970,61 +15497,11 @@ async function saveSetup(params, deps) {
15970
15497
  return { ok: true, file, ...note ? { note } : {} };
15971
15498
  }
15972
15499
 
15973
- // packages/archymedes-cli/src/session/models.ts
15974
- function modelsForProvider(provider, asOf) {
15975
- const seen = new Set;
15976
- for (const record2 of PRICE_CATALOG) {
15977
- if (record2.provider !== provider || record2.modality !== "text" || record2.billingUnit !== "tokens")
15978
- continue;
15979
- seen.add(record2.model);
15980
- }
15981
- const fallback = PROVIDERS[provider].defaultModel;
15982
- seen.add(fallback);
15983
- const models = [...seen].filter((model) => model !== fallback).sort();
15984
- return [fallback, ...models];
15985
- }
15986
- function buildModelCatalog(environment, asOf, live) {
15987
- const choices = [];
15988
- const unconfigured = [];
15989
- for (const provider of PROVIDER_IDS) {
15990
- const spec = PROVIDERS[provider];
15991
- const missing = missingRequirements(provider, environment);
15992
- if (missing.length > 0) {
15993
- unconfigured.push({ provider, label: spec.label, missing });
15994
- continue;
15995
- }
15996
- const known = modelsForProvider(provider, asOf);
15997
- const knownSet = new Set(known);
15998
- const extra = (live?.[provider] ?? []).filter((model) => !knownSet.has(model) && (provider !== "free" || isFreeModelId(model))).slice().sort();
15999
- for (const model of [...known, ...extra]) {
16000
- choices.push({
16001
- provider,
16002
- providerLabel: spec.label,
16003
- model,
16004
- isProviderDefault: model === (environment[`${providerEnvPrefix(provider)}_MODEL`]?.trim() || spec.defaultModel),
16005
- prices: catalogPrices(provider, model, asOf),
16006
- ...knownSet.has(model) ? {} : { live: true }
16007
- });
16008
- }
16009
- }
16010
- return { choices, unconfigured };
16011
- }
16012
- function describePrice(prices, display, convert2) {
16013
- if (!prices)
16014
- return "unpriced";
16015
- const input = convert2({ currency: prices.currency, micros: prices.inputPerMillion });
16016
- const output = convert2({ currency: prices.currency, micros: prices.outputPerMillion });
16017
- if (!input || !output) {
16018
- return `${formatMoney(money(prices.inputPerMillion, prices.currency))}/${formatMoney(money(prices.outputPerMillion, prices.currency))} per Mtok`;
16019
- }
16020
- return `${formatMoney(input)}/${formatMoney(output)} per Mtok`;
16021
- }
16022
-
16023
- // packages/core/src/providers/model-fetch.ts
15500
+ // ../core/src/providers/model-fetch.ts
16024
15501
  import { promises as fs16 } from "node:fs";
16025
15502
  import path21 from "node:path";
16026
15503
 
16027
- // packages/core/src/providers/model-list.ts
15504
+ // ../core/src/providers/model-list.ts
16028
15505
  function modelsUrl(base) {
16029
15506
  const trimmed = base.replace(/\/+$/, "");
16030
15507
  return trimmed.endsWith("/v1") ? `${trimmed}/models` : `${trimmed}/v1/models`;
@@ -16036,10 +15513,8 @@ function modelsEndpoint(provider, environment) {
16036
15513
  return { url: `${FREE_BASE_URL}/models`, headers: {} };
16037
15514
  case "openrouter": {
16038
15515
  const key = trimmed(environment.OPENROUTER_API_KEY);
16039
- if (!key)
16040
- return;
16041
15516
  const base = trimmed(environment.OPENROUTER_BASE_URL) ?? "https://openrouter.ai/api/v1";
16042
- return { url: modelsUrl(base), headers: { authorization: `Bearer ${key}` } };
15517
+ return { url: modelsUrl(base), headers: key ? { authorization: `Bearer ${key}` } : {} };
16043
15518
  }
16044
15519
  case "archymedes-cloud":
16045
15520
  return;
@@ -16136,7 +15611,7 @@ function mergeModelLists(known, live) {
16136
15611
  const added = live.filter((model) => !seen.has(model)).sort();
16137
15612
  return [...known, ...added];
16138
15613
  }
16139
- // packages/core/src/providers/model-fetch.ts
15614
+ // ../core/src/providers/model-fetch.ts
16140
15615
  async function fetchProviderModels(provider, environment, fetchImpl, timeoutMs = 4000) {
16141
15616
  if (provider === "free") {
16142
15617
  try {
@@ -16224,51 +15699,83 @@ function fetchableProviders(environment, providers) {
16224
15699
  return providers.filter((provider) => provider !== "archymedes-cloud" && missingRequirements(provider, environment).length === 0);
16225
15700
  }
16226
15701
 
16227
- // packages/archymedes-cli/src/app/live-models.ts
16228
- function createLiveModelRefresh(environment, state) {
16229
- return async (options = {}) => {
16230
- const providers = fetchableProviders(environment, PROVIDER_IDS);
16231
- if (providers.length === 0)
16232
- return { errors: [] };
16233
- const loaded = await loadLiveModels(providers, environment, options);
16234
- if (Object.keys(loaded.models).length > 0)
16235
- state.liveModels = loaded.models;
16236
- return {
16237
- errors: loaded.errors.filter((error) => error.provider !== "ollama" || Boolean(environment.OLLAMA_BASE_URL?.trim())).map((error) => `${error.provider}: ${error.error}`)
16238
- };
16239
- };
15702
+ // src/session/models.ts
15703
+ function modelsForProvider(provider, asOf) {
15704
+ const seen = new Set;
15705
+ for (const record2 of PRICE_CATALOG) {
15706
+ if (record2.provider !== provider || record2.modality !== "text" || record2.billingUnit !== "tokens")
15707
+ continue;
15708
+ seen.add(record2.model);
15709
+ }
15710
+ const fallback = PROVIDERS[provider].defaultModel;
15711
+ seen.add(fallback);
15712
+ const models = [...seen].filter((model) => model !== fallback).sort();
15713
+ return [fallback, ...models];
16240
15714
  }
16241
- async function readCachedLiveModels(environment, state) {
16242
- const cached = await readModelCache(environment);
16243
- if (isCacheFresh(cached))
16244
- state.liveModels = cached.models;
15715
+ function describePrice(prices, display, convert2) {
15716
+ if (!prices)
15717
+ return "unpriced";
15718
+ const input = convert2({ currency: prices.currency, micros: prices.inputPerMillion });
15719
+ const output = convert2({ currency: prices.currency, micros: prices.outputPerMillion });
15720
+ if (!input || !output) {
15721
+ return `${formatMoney(money(prices.inputPerMillion, prices.currency))}/${formatMoney(money(prices.outputPerMillion, prices.currency))} per Mtok`;
15722
+ }
15723
+ return `${formatMoney(input)}/${formatMoney(output)} per Mtok`;
16245
15724
  }
16246
15725
 
16247
- // packages/archymedes-cli/src/app/acp-models.ts
16248
- async function modelList(environment, params) {
16249
- const state = {};
15726
+ // src/app/acp-models.ts
15727
+ var PUBLIC_LISTINGS = ["free", "openrouter"];
15728
+ async function modelList(environment, params, options = {}) {
15729
+ const offline = /^(1|true|on|yes)$/i.test(environment.ARCHYMEDES_MODELS_OFFLINE?.trim() ?? "");
15730
+ const cached = await readModelCache(environment);
15731
+ let live = isCacheFresh(cached, options.now) ? cached.models : undefined;
16250
15732
  let errors = [];
16251
- if (params.refresh === true)
16252
- errors = (await createLiveModelRefresh(environment, state)({ refresh: true })).errors;
16253
- else
16254
- await readCachedLiveModels(environment, state);
16255
- const catalog = buildModelCatalog(environment, undefined, state.liveModels);
16256
- return {
16257
- choices: catalog.choices.map((choice) => ({
16258
- provider: choice.provider,
16259
- providerLabel: choice.providerLabel,
16260
- model: choice.model,
16261
- isDefault: choice.isProviderDefault,
16262
- live: Boolean(choice.live),
16263
- price: choice.prices ? describePrice(choice.prices, choice.prices.currency, (value) => value) : "unpriced"
16264
- })),
16265
- unconfigured: catalog.unconfigured.map(({ provider, label: label3, missing }) => ({ provider, label: label3, missing })),
16266
- errors,
16267
- providers: PROVIDER_IDS.length
16268
- };
15733
+ if (params.refresh === true || !live && !offline) {
15734
+ const asked = new Set(fetchableProviders(environment, PROVIDER_IDS));
15735
+ if (!offline)
15736
+ for (const provider of PUBLIC_LISTINGS)
15737
+ asked.add(provider);
15738
+ const loaded = await loadLiveModels([...asked], environment, { refresh: true, ...options });
15739
+ if (Object.keys(loaded.models).length > 0)
15740
+ live = { ...live ?? cached?.models ?? {}, ...loaded.models };
15741
+ errors = loaded.errors.filter((error) => error.provider !== "ollama" || Boolean(environment.OLLAMA_BASE_URL?.trim())).map((error) => `${error.provider}: ${error.error}`);
15742
+ }
15743
+ live ??= cached?.models;
15744
+ const ready = [];
15745
+ const locked = [];
15746
+ const unconfigured = [];
15747
+ const freeIds = new Set(live?.free ?? []);
15748
+ for (const provider of PROVIDER_IDS) {
15749
+ const spec = PROVIDERS[provider];
15750
+ const missing = missingRequirements(provider, environment);
15751
+ if (missing.length > 0)
15752
+ unconfigured.push({ provider, label: spec.label, missing });
15753
+ const known = modelsForProvider(provider);
15754
+ const knownSet = new Set(known);
15755
+ const extra = (live?.[provider] ?? []).filter((model) => !knownSet.has(model) && (provider !== "free" || isFreeModelId(model)) && !(provider === "openrouter" && freeIds.has(model))).sort();
15756
+ const preferred = environment[`${providerEnvPrefix(provider)}_MODEL`]?.trim() || spec.defaultModel;
15757
+ for (const model of [...known, ...extra]) {
15758
+ const prices = catalogPrices(provider, model);
15759
+ const free = provider === "free" || model.endsWith(":free") || Boolean(prices && prices.inputPerMillion === 0 && prices.outputPerMillion === 0);
15760
+ const choice = {
15761
+ provider,
15762
+ providerLabel: spec.label,
15763
+ model,
15764
+ isDefault: model === preferred,
15765
+ live: !knownSet.has(model),
15766
+ price: free ? "free" : prices ? describePrice(prices, prices.currency, (value) => value) : "unpriced",
15767
+ free,
15768
+ locked: missing.length > 0,
15769
+ missing,
15770
+ small: isSmallModel(model)
15771
+ };
15772
+ (choice.locked ? locked : ready).push(choice);
15773
+ }
15774
+ }
15775
+ return { choices: [...ready, ...locked], unconfigured, errors, providers: PROVIDER_IDS.length };
16269
15776
  }
16270
15777
 
16271
- // packages/archymedes-cli/src/app/acp-commands.ts
15778
+ // src/app/acp-commands.ts
16272
15779
  async function runSessionCommand(command, argument, session) {
16273
15780
  switch (command) {
16274
15781
  case "cost":
@@ -16293,15 +15800,115 @@ async function runSessionCommand(command, argument, session) {
16293
15800
  const items = session.agent.todos.map((item) => ({ text: item.text, status: item.status }));
16294
15801
  return items.length > 0 ? { kind: "todos", items } : { kind: "text", text: "No plan yet." };
16295
15802
  }
15803
+ case "memory":
15804
+ return memoryCommand(argument, session.agent.workspaceLabel);
15805
+ case "tools": {
15806
+ const { tools } = await session.agent.inspectTools();
15807
+ const lines = tools.map((tool) => `${tool.name.padEnd(24)} ${tool.requiresApproval ? "asks first " : " "}${tool.description.split(/(?<=\.)\s/)[0]}`);
15808
+ return { kind: "text", text: `${tools.length} tools
15809
+ ${lines.join(`
15810
+ `)}` };
15811
+ }
16296
15812
  default:
16297
15813
  return { kind: "error", text: `Unknown session command "${command}".` };
16298
15814
  }
16299
15815
  }
15816
+ var MEMORY_KINDS = ["preference", "convention", "decision", "lesson", "fact"];
15817
+ async function memoryCommand(argument, root) {
15818
+ const environment = process.env;
15819
+ const [verb = "", ...rest] = argument.trim().split(/\s+/);
15820
+ try {
15821
+ if (verb === "" || verb === "list") {
15822
+ const entries2 = await loadMemories(root, environment);
15823
+ if (entries2.length === 0)
15824
+ return { kind: "text", text: "Nothing remembered yet. /memory add <text> saves a fact for this project." };
15825
+ return { kind: "text", text: entries2.map((entry) => `${entry.scope === "user" ? "user " : "project"} ${entry.index}. [${entry.kind}${entry.pinned ? ", pinned" : ""}] ${entry.text}`).join(`
15826
+ `) };
15827
+ }
15828
+ if (verb === "add") {
15829
+ const scope = rest[0] === "user" ? "user" : "project";
15830
+ if (rest[0] === "user" || rest[0] === "project")
15831
+ rest.shift();
15832
+ const kind = MEMORY_KINDS.find((candidate) => candidate === rest[0]);
15833
+ if (kind)
15834
+ rest.shift();
15835
+ const change = await addMemory(scope, rest.join(" "), root, environment, kind ? { kind } : {});
15836
+ return { kind: "text", text: change.changed ? `Remembered (${scope}).` : "Already remembered, or nothing to add." };
15837
+ }
15838
+ if (verb === "forget") {
15839
+ const scope = rest[0] === "user" ? "user" : "project";
15840
+ const index = Number(rest[rest.length - 1]);
15841
+ if (!Number.isInteger(index))
15842
+ return { kind: "error", text: "/memory forget [user] <number> — see /memory for the numbers." };
15843
+ const change = await forgetMemory(scope, index, root, environment);
15844
+ return { kind: "text", text: change.removed ? `Forgot: ${change.removed.text}` : `No ${scope} memory numbered ${index}.` };
15845
+ }
15846
+ if (verb === "clear") {
15847
+ const scope = rest[0] === "user" ? "user" : "project";
15848
+ return { kind: "text", text: (await clearMemories(scope, root, environment)).changed ? `Cleared ${scope} memory.` : `${scope} memory was already empty.` };
15849
+ }
15850
+ return { kind: "error", text: `/memory takes list, add <text>, forget <n> or clear — not "${verb}".` };
15851
+ } catch (error) {
15852
+ return { kind: "error", text: error instanceof Error ? error.message : String(error) };
15853
+ }
15854
+ }
15855
+
15856
+ // src/app/acp-settings.ts
15857
+ async function listSettings(processEnvironment) {
15858
+ const stored = await loadSettings(processEnvironment);
15859
+ const rows = SETTING_FIELDS.map((field) => {
15860
+ const secret = "secret" in field && field.secret === true;
15861
+ const choices = "choices" in field ? field.choices : undefined;
15862
+ const value = stored[field.key] ?? "";
15863
+ return {
15864
+ key: field.key,
15865
+ label: field.label,
15866
+ section: sectionOf(field.key) ?? "General",
15867
+ kind: secret ? "secret" : choices ? "choice" : "text",
15868
+ ...choices ? { choices: choices.map((choice) => ({ value: choice.value, label: choice.label })) } : {},
15869
+ value: secret && value ? maskSetting(value) : value,
15870
+ isSet: value !== "",
15871
+ overriddenByEnvironment: Boolean(processEnvironment[field.key]) && processEnvironment[field.key] !== value
15872
+ };
15873
+ });
15874
+ return { file: settingsFile(processEnvironment), rows };
15875
+ }
15876
+ async function setSetting(params, deps) {
15877
+ const key = typeof params.key === "string" ? params.key : "";
15878
+ const raw = typeof params.value === "string" ? params.value : "";
15879
+ const field = SETTING_FIELDS.find((candidate) => candidate.key === key);
15880
+ if (!field)
15881
+ return { ok: false, error: `No setting named ${key || "(none)"}.` };
15882
+ const current = await loadSettings(deps.processEnvironment);
15883
+ const next = { ...current };
15884
+ let stored = "";
15885
+ if (raw.trim() === "") {
15886
+ delete next[field.key];
15887
+ } else {
15888
+ try {
15889
+ stored = validateSetting(field.key, raw);
15890
+ } catch (error) {
15891
+ return { ok: false, error: error instanceof Error ? error.message : String(error) };
15892
+ }
15893
+ next[field.key] = stored;
15894
+ }
15895
+ let file;
15896
+ try {
15897
+ file = await saveSettings(next, deps.processEnvironment);
15898
+ } catch (error) {
15899
+ return { ok: false, error: `Could not save: ${error instanceof Error ? error.message : String(error)}` };
15900
+ }
15901
+ if (raw.trim() === "" && deps.environment[field.key] === current[field.key])
15902
+ delete deps.environment[field.key];
15903
+ Object.assign(deps.environment, mergedEnvironment(next, deps.processEnvironment));
15904
+ const secret = "secret" in field && field.secret === true;
15905
+ return { ok: true, file, value: secret && stored ? maskSetting(stored) : stored, isSet: stored !== "" };
15906
+ }
16300
15907
 
16301
- // packages/archymedes-cli/src/app/startup-update.ts
15908
+ // src/app/startup-update.ts
16302
15909
  import { spawn as spawn8 } from "node:child_process";
16303
15910
 
16304
- // packages/archymedes-cli/src/platform/auto-update.ts
15911
+ // src/platform/auto-update.ts
16305
15912
  import { promises as fs17 } from "node:fs";
16306
15913
  import path22 from "node:path";
16307
15914
  var AUTO_UPDATE_INTERVAL_MS = 24 * 60 * 60 * 1000;
@@ -16414,7 +16021,7 @@ async function runAutoUpdate(options) {
16414
16021
  return { notice: [], decision };
16415
16022
  }
16416
16023
 
16417
- // packages/archymedes-cli/src/platform/defender-feed-update.ts
16024
+ // src/platform/defender-feed-update.ts
16418
16025
  var OFFICIAL_DEFENDER_FEED_URL = "https://circuit-archymedes.vercel.app/api/defender-brain/manifest";
16419
16026
  var OFFICIAL_DEFENDER_FEED_KEYS = Object.freeze({
16420
16027
  "release-2026-01": "MCowBQYDK2VwAyEA4/jb1zd6f+jIPAFja1bPNtroXV8MtAZFrKt5BSY+ngI="
@@ -16442,12 +16049,12 @@ async function updateDefenderFeed(environment) {
16442
16049
  });
16443
16050
  }
16444
16051
 
16445
- // packages/archymedes-cli/src/platform/update.ts
16052
+ // src/platform/update.ts
16446
16053
  import { spawnSync as spawnSync3 } from "node:child_process";
16447
16054
  import { tmpdir } from "node:os";
16448
16055
  import { createInterface as createInterface3 } from "node:readline/promises";
16449
16056
 
16450
- // packages/archymedes-cli/src/catalog/commands.ts
16057
+ // src/catalog/commands.ts
16451
16058
  function defineCommands(table) {
16452
16059
  return Object.entries(table).map(([name, command]) => ({ name, ...command }));
16453
16060
  }
@@ -16520,7 +16127,7 @@ function renderCommandHelp(language = "en", shortcuts = new Map, options = {}) {
16520
16127
  `);
16521
16128
  }
16522
16129
 
16523
- // packages/archymedes-cli/src/terminal/keybindings.ts
16130
+ // src/terminal/keybindings.ts
16524
16131
  var FUNCTION_KEY = /^f([1-9]|1[0-2])$/;
16525
16132
  var NAMED_KEYS = new Set(["tab", "enter", "escape", "space", "up", "down", "left", "right", "home", "end", "pageup", "pagedown", "backspace", "delete"]);
16526
16133
  function isKeyName(value) {
@@ -16770,10 +16377,10 @@ function escapeCodeTimeoutMs(environment = process.env) {
16770
16377
  const parsed = raw ? Number(raw) : NaN;
16771
16378
  return Number.isInteger(parsed) && parsed >= 10 && parsed <= 2000 ? parsed : DEFAULT_ESCAPE_CODE_TIMEOUT_MS;
16772
16379
  }
16773
- // packages/archymedes-cli/package.json
16380
+ // package.json
16774
16381
  var package_default = {
16775
16382
  name: "archymedes-cli",
16776
- version: "3.1.0",
16383
+ version: "3.3.0",
16777
16384
  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.",
16778
16385
  keywords: [
16779
16386
  "cli",
@@ -16819,12 +16426,12 @@ var package_default = {
16819
16426
  prepublishOnly: "bun run ../../tooling/build/build-packages.ts"
16820
16427
  },
16821
16428
  optionalDependencies: {
16822
- "@archymedes/cli-darwin-arm64": "3.1.0",
16823
- "@archymedes/cli-darwin-x64": "3.1.0",
16824
- "@archymedes/cli-linux-arm64": "3.1.0",
16825
- "@archymedes/cli-linux-x64": "3.1.0",
16826
- "@archymedes/cli-win32-arm64": "3.1.0",
16827
- "@archymedes/cli-win32-x64": "3.1.0",
16429
+ "@archymedes/cli-darwin-arm64": "3.3.0",
16430
+ "@archymedes/cli-darwin-x64": "3.3.0",
16431
+ "@archymedes/cli-linux-arm64": "3.3.0",
16432
+ "@archymedes/cli-linux-x64": "3.3.0",
16433
+ "@archymedes/cli-win32-arm64": "3.3.0",
16434
+ "@archymedes/cli-win32-x64": "3.3.0",
16828
16435
  "@archymedes/state-darwin-arm64": "0.4.0",
16829
16436
  "@archymedes/state-darwin-x64": "0.4.0",
16830
16437
  "@archymedes/state-linux-arm64-gnu": "0.4.0",
@@ -16836,7 +16443,7 @@ var package_default = {
16836
16443
  }
16837
16444
  };
16838
16445
 
16839
- // packages/archymedes-cli/src/platform/update.ts
16446
+ // src/platform/update.ts
16840
16447
  var ARCHYMEDES_CLI_PACKAGE = "archymedes-cli";
16841
16448
  var ARCHYMEDES_CLI_VERSION = package_default.version;
16842
16449
  var DEFAULT_REGISTRY = "https://registry.npmjs.org";
@@ -17054,7 +16661,7 @@ async function runSelfUpdate(options = {}) {
17054
16661
  return { status: "updated", currentVersion, latestVersion, code: 0 };
17055
16662
  }
17056
16663
 
17057
- // packages/archymedes-cli/src/text/ansi.ts
16664
+ // src/text/ansi.ts
17058
16665
  var RESET = "\x1B[0m";
17059
16666
  var BOLD = "\x1B[1m";
17060
16667
  var DIM = "\x1B[2m";
@@ -17067,7 +16674,7 @@ function paintAll(text, codes, depth) {
17067
16674
  return depth === "none" || codes.length === 0 ? text : `${codes.join("")}${text}${RESET}`;
17068
16675
  }
17069
16676
 
17070
- // packages/archymedes-cli/src/terminal/output.ts
16677
+ // src/terminal/output.ts
17071
16678
  var terminalStream = {
17072
16679
  write(text) {
17073
16680
  process.stdout.write(text);
@@ -17101,7 +16708,7 @@ class OutputRouter {
17101
16708
  }
17102
16709
  }
17103
16710
 
17104
- // packages/archymedes-cli/src/text/glyphs.ts
16711
+ // src/text/glyphs.ts
17105
16712
  var UNICODE_GLYPHS = {
17106
16713
  starGlyphs: [".", "·", "✧", "✦", "✶"],
17107
16714
  spinnerFrames: [" ◜ ", " ◠ ", " ◝ ", " ◞ ", " ◡ ", " ◟ "],
@@ -17208,7 +16815,7 @@ function borderGlyphsFor(style, glyphs) {
17208
16815
  return ROUND_BORDER;
17209
16816
  }
17210
16817
 
17211
- // packages/archymedes-cli/src/text/text-width.ts
16818
+ // src/text/text-width.ts
17212
16819
  var ANSI = /\x1b\[[0-9;]*m/g;
17213
16820
  var PRINTABLE_ASCII = /^[\x20-\x7e]*$/;
17214
16821
  function visibleWidth(text) {
@@ -17243,7 +16850,7 @@ function clipTo(text, width, glyphs = UNICODE_GLYPHS) {
17243
16850
  return `${out}${mark}`;
17244
16851
  }
17245
16852
 
17246
- // packages/archymedes-cli/src/render/syntax.ts
16853
+ // src/render/syntax.ts
17247
16854
  function newSyntaxState() {
17248
16855
  return {};
17249
16856
  }
@@ -17890,7 +17497,7 @@ function highlightSyntax(line, options) {
17890
17497
  return `${out}${RESET}`;
17891
17498
  }
17892
17499
 
17893
- // packages/archymedes-cli/src/render/markdown.ts
17500
+ // src/render/markdown.ts
17894
17501
  var colour = (role, palette) => palette?.[role] || ANSI_PALETTE[role];
17895
17502
  var INLINE_PATTERN = /(?<!\\)(`[^`\n]+`)|(?<!\\)(\*\*[^*\n]+\*\*)|(?<!\\)(__[^_\n]+__)|(?<!\\)(~~[^~\n]+~~)|(?<!\\)(\*[^*\n]+\*)|(?<!\\)(_[^_\n]+_)|(!?\[([^\]\n]+)\]\((https?:\/\/[^\s)\n]+)\))|(<(https?:\/\/[^>\s]+)>)/g;
17896
17503
  var unescapeMarkdown = (text) => text.replace(/\\([\\`*_[\]{}()#+.!~>-])/g, "$1");
@@ -18111,7 +17718,7 @@ function renderMarkdown(text, options) {
18111
17718
  `);
18112
17719
  }
18113
17720
 
18114
- // packages/archymedes-cli/src/render/tui.ts
17721
+ // src/render/tui.ts
18115
17722
  function archymedesSpinnerFrame(index, glyphs = UNICODE_GLYPHS) {
18116
17723
  const frames = glyphs.spinnerFrames;
18117
17724
  const normalized = Number.isFinite(index) ? Math.max(0, Math.floor(index)) : 0;
@@ -18699,7 +18306,7 @@ function wrapPlain(text, width) {
18699
18306
  return lines.length === 0 ? [""] : lines;
18700
18307
  }
18701
18308
 
18702
- // packages/archymedes-cli/src/render/transcript.ts
18309
+ // src/render/transcript.ts
18703
18310
  function truncateMiddle(value, maximum) {
18704
18311
  const clean = value.replace(/\s+/g, " ").trim();
18705
18312
  if (clean.length <= maximum)
@@ -18862,7 +18469,7 @@ function summarizeToolResult(toolName, content, isError) {
18862
18469
  }
18863
18470
  }
18864
18471
 
18865
- // packages/archymedes-cli/src/render/sections.ts
18472
+ // src/render/sections.ts
18866
18473
  var TONE_ROLE = {
18867
18474
  good: "success",
18868
18475
  bad: "error",
@@ -18989,7 +18596,7 @@ function outcomeMark(outcome, style) {
18989
18596
  }
18990
18597
  }
18991
18598
 
18992
- // packages/archymedes-cli/src/render/code-view.ts
18599
+ // src/render/code-view.ts
18993
18600
  var MAX_DIFF_CELLS = 250000;
18994
18601
  function diffLines(before, after) {
18995
18602
  const left = before === "" ? [] : before.split(`
@@ -19286,7 +18893,7 @@ function renderFileChange(change, style, options = {}) {
19286
18893
  };
19287
18894
  }
19288
18895
 
19289
- // packages/archymedes-cli/src/render/test-report.ts
18896
+ // src/render/test-report.ts
19290
18897
  function toMs(value) {
19291
18898
  if (!value)
19292
18899
  return;
@@ -19561,7 +19168,7 @@ function renderTestReport(report, style, options = {}) {
19561
19168
  `);
19562
19169
  }
19563
19170
 
19564
- // packages/archymedes-cli/src/render/expandable.ts
19171
+ // src/render/expandable.ts
19565
19172
  var MAX_ENTRIES = 64;
19566
19173
 
19567
19174
  class ExpandableStore {
@@ -19595,7 +19202,7 @@ function expandHint(id, hidden, depth, glyphs = UNICODE_GLYPHS) {
19595
19202
  return paint(`${glyphs.collapsed} ${hidden} more line${hidden === 1 ? "" : "s"} hidden ${glyphs.middot} /expand ${id}`, DIM, depth);
19596
19203
  }
19597
19204
 
19598
- // packages/archymedes-cli/src/app/transcript.ts
19205
+ // src/app/transcript.ts
19599
19206
  var colorEnabled = false;
19600
19207
  var wrap = (code) => (value) => colorEnabled ? `${code}${value}${RESET}` : value;
19601
19208
  var palette = NO_COLOR_PALETTE;
@@ -19675,7 +19282,7 @@ function beginTranscriptTurn() {
19675
19282
  markdown.reset();
19676
19283
  }
19677
19284
 
19678
- // packages/archymedes-cli/src/app/startup-update.ts
19285
+ // src/app/startup-update.ts
19679
19286
  async function startupMaintenance(environment, interactive, install) {
19680
19287
  updateDefenderFeed(environment).catch(() => {
19681
19288
  return;
@@ -19714,7 +19321,7 @@ function installInChildProcess(entry, environment) {
19714
19321
  });
19715
19322
  }
19716
19323
 
19717
- // packages/archymedes-cli/src/app/jev.ts
19324
+ // src/app/jev.ts
19718
19325
  function jevOptionsFromEnvironment(environment) {
19719
19326
  if ((environment.ARCHYMEDES_JEV ?? "").trim().toLowerCase() === "off")
19720
19327
  return;
@@ -19725,7 +19332,7 @@ function jevOptionsFromEnvironment(environment) {
19725
19332
  return { apiKey, ...model ? { model } : {}, ...environment.ARCHYMEDES_JEV_REVIEW?.trim().toLowerCase() === "off" ? { review: false } : {} };
19726
19333
  }
19727
19334
 
19728
- // packages/core/src/providers/exa.ts
19335
+ // ../core/src/providers/exa.ts
19729
19336
  var LATENCY_CEILING = {
19730
19337
  instant: 5000,
19731
19338
  fast: 1e4,
@@ -19931,7 +19538,7 @@ function createExaClient(environment, fetchImpl) {
19931
19538
  });
19932
19539
  }
19933
19540
 
19934
- // packages/archymedes-cli/src/acp-server.ts
19541
+ // src/acp-server.ts
19935
19542
  function splitFrames(buffer) {
19936
19543
  const parts = buffer.split(`
19937
19544
  `);
@@ -19949,6 +19556,8 @@ async function runAcpServer(options) {
19949
19556
  extensions: {
19950
19557
  "_archymedes/setup/status": async () => setupStatus(options.environment, { provider: options.provider, model: options.model }),
19951
19558
  "_archymedes/setup/save": async (params) => saveSetup(params, { environment: options.environment, processEnvironment: process.env }),
19559
+ "_archymedes/settings/list": async () => listSettings(process.env),
19560
+ "_archymedes/settings/set": async (params) => setSetting(params, { environment: options.environment, processEnvironment: process.env }),
19952
19561
  "_archymedes/models": async (params) => modelList(options.environment, params),
19953
19562
  "_archymedes/startup": async () => ({
19954
19563
  notices: await (startup ??= startupMaintenance(options.environment, false, installInChildProcess({ execPath: process.execPath, script: process.argv[1] ?? "" }, options.environment)))
@@ -19969,7 +19578,7 @@ async function runAcpServer(options) {
19969
19578
  const workspace = started.workspace;
19970
19579
  let agent = build(mode);
19971
19580
  function build(currentMode) {
19972
- return new ArchymedesAgent({
19581
+ const built = new ArchymedesAgent({
19973
19582
  root,
19974
19583
  model: resolved.provider,
19975
19584
  prices: priceCatalogFor(resolved.prices),
@@ -19978,9 +19587,12 @@ async function runAcpServer(options) {
19978
19587
  search: createExaClient(options.environment),
19979
19588
  onEvent,
19980
19589
  jev: jevOptionsFromEnvironment(options.environment),
19590
+ ...reasoningFromEnvironment(options.environment) ? { reasoning: reasoningFromEnvironment(options.environment) } : {},
19981
19591
  afterEdit: acpEditDiagnostics(root, workspace, options.environment),
19982
19592
  approve: async (request) => approve({ toolName: request.tool.name, summary: request.summary, toolCallId: request.call.id, ...request.pattern ? { pattern: request.pattern } : {} })
19983
19593
  });
19594
+ built.warmUp();
19595
+ return built;
19984
19596
  }
19985
19597
  async function rebuild(change) {
19986
19598
  const carried = await loadSession(root, agent.sessionId);
@@ -20093,10 +19705,10 @@ function priceCatalogFor(prices) {
20093
19705
  };
20094
19706
  }
20095
19707
 
20096
- // packages/archymedes-cli/src/app/args.ts
19708
+ // src/app/args.ts
20097
19709
  import path23 from "node:path";
20098
19710
 
20099
- // packages/archymedes-cli/src/render/charts.ts
19711
+ // src/render/charts.ts
20100
19712
  function barChart(data, options) {
20101
19713
  if (data.length === 0)
20102
19714
  return [];
@@ -20121,7 +19733,7 @@ function barChart(data, options) {
20121
19733
  });
20122
19734
  }
20123
19735
 
20124
- // packages/archymedes-cli/src/commands/chat-history.ts
19736
+ // src/commands/chat-history.ts
20125
19737
  function countTurns(messages) {
20126
19738
  return messages.filter((message) => message.role === "user" && !message.internal).length;
20127
19739
  }
@@ -20314,7 +19926,7 @@ function parseHistoryCommand(input) {
20314
19926
  return { kind: "invalid", reason: `/history takes "list", a session id, "search <text>", "delete <id>", "status", or "resume" — not "${verb}".` };
20315
19927
  }
20316
19928
 
20317
- // packages/archymedes-cli/src/commands/pacing.ts
19929
+ // src/commands/pacing.ts
20318
19930
  var PACE_PROFILES = {
20319
19931
  off: {
20320
19932
  level: "off",
@@ -20376,7 +19988,7 @@ function paceBadge(level, glyphs2 = UNICODE_GLYPHS) {
20376
19988
  return `${glyphs2.paused} ${profileFor(level).label}`;
20377
19989
  }
20378
19990
 
20379
- // packages/archymedes-cli/src/app/args.ts
19991
+ // src/app/args.ts
20380
19992
  var SESSION_ID2 = /^\d{8}T\d{6}Z-[a-z0-9]{6}$/;
20381
19993
  var DEFAULT_DOCKER_IMAGE = "debian:stable-slim";
20382
19994
  function parseArgs(argv) {
@@ -20574,7 +20186,7 @@ function parseArgs(argv) {
20574
20186
  return parsed;
20575
20187
  }
20576
20188
 
20577
- // packages/archymedes-cli/src/app/session-provider.ts
20189
+ // src/app/session-provider.ts
20578
20190
  async function resolveSessionProvider2(environment, options) {
20579
20191
  if (options.provider || !options.resume)
20580
20192
  return resolveProvider(environment, options);
@@ -20583,7 +20195,7 @@ async function resolveSessionProvider2(environment, options) {
20583
20195
  return resolveProvider(environment, selection?.provider === "free" ? { provider: "free", model: options.model ?? selection.model } : options);
20584
20196
  }
20585
20197
 
20586
- // packages/archymedes-cli/src/app/transcript.ts
20198
+ // src/app/transcript.ts
20587
20199
  var colorEnabled2 = false;
20588
20200
  var liveTerminal2 = false;
20589
20201
  var wrap2 = (code) => (value) => colorEnabled2 ? `${code}${value}${RESET}` : value;
@@ -20897,7 +20509,7 @@ ${rule(sectionStyle2(), { label: "tool activity", tone: "neutral" })}
20897
20509
  }
20898
20510
  }
20899
20511
 
20900
- // packages/core/src/cli/suggestions.ts
20512
+ // ../core/src/cli/suggestions.ts
20901
20513
  var CATEGORY_ORDER = ["blocked", "recovery", "next", "discover", "starter"];
20902
20514
  function defaultSignals(overrides = {}) {
20903
20515
  return {
@@ -21294,7 +20906,7 @@ async function askModelForSuggestions(model, signals, context = {}) {
21294
20906
  }
21295
20907
  }
21296
20908
 
21297
- // packages/archymedes-cli/src/ui/chooser.ts
20909
+ // src/ui/chooser.ts
21298
20910
  var ALL_CHROME = { title: true, status: true, pagination: true, help: true };
21299
20911
  function chromeOf(chrome) {
21300
20912
  return { ...ALL_CHROME, ...chrome };
@@ -21522,7 +21134,7 @@ async function runChooser(keys, items, paint2, options) {
21522
21134
  return;
21523
21135
  }
21524
21136
 
21525
- // packages/archymedes-cli/src/ui/navigation.ts
21137
+ // src/ui/navigation.ts
21526
21138
  class CommandUsage {
21527
21139
  limit;
21528
21140
  order = [];
@@ -21545,8 +21157,8 @@ class CommandUsage {
21545
21157
  }
21546
21158
  }
21547
21159
 
21548
- // packages/core/src/cli/daemon.ts
21549
- import { randomUUID as randomUUID7 } from "node:crypto";
21160
+ // ../core/src/cli/daemon.ts
21161
+ import { randomUUID as randomUUID6 } from "node:crypto";
21550
21162
  var ARCHYMEDES_DAEMON_PROTOCOL_VERSION = 1;
21551
21163
  var PREVIEW_HUNK_SEPARATOR = `
21552
21164
  …
@@ -21592,7 +21204,7 @@ class ArchymedesSessionDaemon {
21592
21204
  sessions = new Map;
21593
21205
  pendingApprovals = new Map;
21594
21206
  connect(options = {}) {
21595
- const id = options.id ?? `client_${randomUUID7()}`;
21207
+ const id = options.id ?? `client_${randomUUID6()}`;
21596
21208
  assertSafeId(id, "Client id");
21597
21209
  if (this.clients.has(id))
21598
21210
  throw new Error(`Daemon client already connected: ${id}`);
@@ -21611,7 +21223,7 @@ class ArchymedesSessionDaemon {
21611
21223
  }
21612
21224
  async requestApproval(sessionId, request) {
21613
21225
  const publicRequest = {
21614
- id: `approval_${randomUUID7()}`,
21226
+ id: `approval_${randomUUID6()}`,
21615
21227
  sessionId,
21616
21228
  summary: request.summary,
21617
21229
  actionDigest: request.actionDigest,
@@ -21884,7 +21496,7 @@ class ArchymedesDaemonClient {
21884
21496
  planRoute(objective, signal) {
21885
21497
  return this.daemon.planRoute(this.id, this.sessionId, objective, signal);
21886
21498
  }
21887
- send(objective, commandId = `turn_${randomUUID7()}`) {
21499
+ send(objective, commandId = `turn_${randomUUID6()}`) {
21888
21500
  return this.daemon.send(this.id, this.sessionId, objective, commandId);
21889
21501
  }
21890
21502
  cancel() {
@@ -21943,7 +21555,7 @@ class ArchymedesDaemonClient {
21943
21555
  }
21944
21556
  }
21945
21557
 
21946
- // packages/core/src/money.ts
21558
+ // ../core/src/money.ts
21947
21559
  var CURRENCIES2 = (() => {
21948
21560
  try {
21949
21561
  return Intl.supportedValuesOf("currency");
@@ -21986,7 +21598,7 @@ function formatMoney2(value) {
21986
21598
  return value.currency === "USD" ? `${format.symbol}${rendered}` : `${format.symbol} ${rendered}`;
21987
21599
  }
21988
21600
 
21989
- // packages/core/src/cli/backends.ts
21601
+ // ../core/src/cli/backends.ts
21990
21602
  import path24 from "node:path";
21991
21603
  var CONFIG_FILE_SCAN_LIMIT2 = 500;
21992
21604
  function localCommandGuidance2(platform) {
@@ -22080,11 +21692,11 @@ class LocalWorkspace2 {
22080
21692
  }
22081
21693
  }
22082
21694
 
22083
- // packages/core/src/cli/cost.ts
21695
+ // ../core/src/cli/cost.ts
22084
21696
  function formatTokenFlow2(usage) {
22085
21697
  return `↑${compactTokenCount(usage.inputTokens)} ↓${compactTokenCount(usage.outputTokens)} tok`;
22086
21698
  }
22087
- var emptyUsage3 = { inputTokens: 0, outputTokens: 0, totalTokens: 0, cachedInputTokens: 0, cacheWriteTokens: 0, reasoningTokens: 0 };
21699
+ var emptyUsage2 = { inputTokens: 0, outputTokens: 0, totalTokens: 0, cachedInputTokens: 0, cacheWriteTokens: 0, reasoningTokens: 0 };
22088
21700
  var CACHE_CHURN_FLOOR_TOKENS2 = 20000;
22089
21701
  var CACHE_CHURN_HINT2 = [
22090
21702
  "The prompt cache is being rewritten more than it is read, which costs more than not caching at all.",
@@ -22172,7 +21784,7 @@ class CostLedger2 {
22172
21784
  cachedInputTokens: total.cachedInputTokens + turn.usage.cachedInputTokens,
22173
21785
  cacheWriteTokens: total.cacheWriteTokens + turn.usage.cacheWriteTokens,
22174
21786
  reasoningTokens: total.reasoningTokens + turn.usage.reasoningTokens
22175
- }), { ...emptyUsage3 });
21787
+ }), { ...emptyUsage2 });
22176
21788
  }
22177
21789
  get history() {
22178
21790
  return [...this.turns];
@@ -22332,7 +21944,7 @@ class CostLedger2 {
22332
21944
  }
22333
21945
  }
22334
21946
 
22335
- // packages/archymedes-cli/src/headless.ts
21947
+ // src/headless.ts
22336
21948
  var HEADLESS_PROTOCOL_VERSION = 1;
22337
21949
  var EXIT_CODES = {
22338
21950
  completed: 0,
@@ -22435,7 +22047,7 @@ class HeadlessEmitter {
22435
22047
  return;
22436
22048
  }
22437
22049
  if (safe.type === "model_turn") {
22438
- this.emit("model_turn", { iteration: safe.iteration, model: safe.model, toolCalls: safe.toolCallCount, usage: safe.usage });
22050
+ this.emit("model_turn", { iteration: safe.iteration, model: safe.model, toolCalls: safe.toolCallCount, usage: safe.usage, ...safe.effort ? { effort: safe.effort, effortReason: safe.effortReason } : {} });
22439
22051
  return;
22440
22052
  }
22441
22053
  if (safe.type === "provider_retry") {
@@ -22458,7 +22070,7 @@ class HeadlessEmitter {
22458
22070
  }
22459
22071
  }
22460
22072
 
22461
- // packages/core/src/providers/price-catalog.ts
22073
+ // ../core/src/providers/price-catalog.ts
22462
22074
  var ANTHROPIC_VERIFIED2 = "2026-06-24";
22463
22075
  var ANTHROPIC_SOURCE2 = "anthropic.com/pricing, recorded 2026-06-24";
22464
22076
  var ANTHROPIC2 = [
@@ -22510,7 +22122,7 @@ var SERVICES2 = [
22510
22122
  ];
22511
22123
  var PRICE_CATALOG2 = definePrices([...ANTHROPIC2, ...OAI_COMPAT_LABS2, ...SERVICES2]);
22512
22124
 
22513
- // packages/archymedes-cli/src/text/color-depth.ts
22125
+ // src/text/color-depth.ts
22514
22126
  function detectColorDepth(environment, isTTY) {
22515
22127
  if (!isTTY || environment.NO_COLOR !== undefined || environment.TERM === "dumb")
22516
22128
  return "none";
@@ -22519,7 +22131,7 @@ function detectColorDepth(environment, isTTY) {
22519
22131
  return "ansi256";
22520
22132
  }
22521
22133
 
22522
- // packages/archymedes-cli/src/terminal/keybindings.ts
22134
+ // src/terminal/keybindings.ts
22523
22135
  var FUNCTION_KEY2 = /^f([1-9]|1[0-2])$/;
22524
22136
  var NAMED_KEYS2 = new Set(["tab", "enter", "escape", "space", "up", "down", "left", "right", "home", "end", "pageup", "pagedown", "backspace", "delete"]);
22525
22137
  function isKeyName2(value) {
@@ -22761,7 +22373,7 @@ function escapeCodeTimeoutMs2(environment = process.env) {
22761
22373
  return Number.isInteger(parsed) && parsed >= 10 && parsed <= 2000 ? parsed : DEFAULT_ESCAPE_CODE_TIMEOUT_MS2;
22762
22374
  }
22763
22375
 
22764
- // packages/archymedes-cli/src/platform/local-currency.ts
22376
+ // src/platform/local-currency.ts
22765
22377
  var COUNTRY_CURRENCIES2 = {
22766
22378
  RW: "RWF",
22767
22379
  EG: "EGP",
@@ -22887,7 +22499,7 @@ function resolveCurrencyPreference(options) {
22887
22499
  }
22888
22500
  var FX_CACHE_MAX_AGE_MS2 = 24 * 60 * 60 * 1000;
22889
22501
 
22890
- // packages/archymedes-cli/src/platform/settings.ts
22502
+ // src/platform/settings.ts
22891
22503
  import { promises as fs18 } from "node:fs";
22892
22504
  import os5 from "node:os";
22893
22505
  import path25 from "node:path";
@@ -22994,6 +22606,13 @@ var AUTOSAVE_CHOICES2 = [
22994
22606
  { value: "off", label: "Off — save with Ctrl+S (default)" },
22995
22607
  { value: "on", label: "On — save a second after you stop typing" }
22996
22608
  ];
22609
+ var REASONING_CHOICES2 = [
22610
+ { value: "auto", label: "Auto (experimental) — lower the effort on routine steps, leave hard ones to the model" },
22611
+ { value: "minimal", label: "Minimal — never think more than needed" },
22612
+ { value: "low", label: "Low" },
22613
+ { value: "medium", label: "Medium" },
22614
+ { value: "high", label: "High — slowest" }
22615
+ ];
22997
22616
  var PROVIDER_CHOICES2 = PROVIDER_IDS.map((id) => ({ value: id, label: PROVIDER_INFO[id].label }));
22998
22617
  var SETTING_FIELDS2 = [
22999
22618
  { key: "ARCHYMEDES_THEME", label: "Theme — colours for the whole CLI (/theme <name> also saves it)", choices: THEME_CHOICES2, section: "Appearance" },
@@ -23001,6 +22620,7 @@ var SETTING_FIELDS2 = [
23001
22620
  { key: "ARCHYMEDES_CODE_LINE_NUMBERS", label: "Line numbers in code blocks", choices: SHOW_HIDE_CHOICES2 },
23002
22621
  { key: "ARCHYMEDES_SIMPLE", label: "Simple mode — quiet start, short help", choices: SIMPLE_CHOICES2 },
23003
22622
  { key: "ARCHYMEDES_LANGUAGE", label: "Control language", choices: LANGUAGE_CHOICES2 },
22623
+ { key: "ARCHYMEDES_REASONING", label: "Reasoning effort — clear it to leave each model at its own default", choices: REASONING_CHOICES2 },
23004
22624
  { key: "ARCHYMEDES_RESUME", label: "Continue your last chat at startup", choices: RESUME_CHOICES2, section: "Behaviour" },
23005
22625
  { key: "ARCHYMEDES_EDITOR_AUTOSAVE", label: "Auto-save in /edit", choices: AUTOSAVE_CHOICES2 },
23006
22626
  { key: "ARCHYMEDES_SUGGEST_MODEL", label: "Ask the model for extra suggestions (a small extra call per turn)", choices: ON_OFF_CHOICES2 },
@@ -23132,7 +22752,7 @@ function mergedEnvironment2(settings, environment = process.env) {
23132
22752
  var PROVIDER_REQUIRED_NAMES2 = new Set(PROVIDER_IDS.flatMap((id) => PROVIDER_INFO[id].requires));
23133
22753
  var PROVIDER_KEY_FIELDS2 = SETTING_FIELDS2.filter((field) => PROVIDER_REQUIRED_NAMES2.has(field.key));
23134
22754
 
23135
- // packages/archymedes-cli/src/app/free-setup.ts
22755
+ // src/app/free-setup.ts
23136
22756
  var OPENROUTER_KEYS_URL3 = "https://openrouter.ai/keys";
23137
22757
  var FREE_SETUP_INTRO = [
23138
22758
  "Free mode runs zero-priced models through OpenRouter and never falls back to a paid one.",
@@ -23198,7 +22818,7 @@ ${style3.bold("Free mode setup")}
23198
22818
  return;
23199
22819
  }
23200
22820
 
23201
- // packages/archymedes-cli/src/session/tabs.ts
22821
+ // src/session/tabs.ts
23202
22822
  class WorkspaceController {
23203
22823
  limit;
23204
22824
  tabs = [];
@@ -23287,7 +22907,7 @@ class WorkspaceController {
23287
22907
  }
23288
22908
  }
23289
22909
 
23290
- // packages/archymedes-cli/src/terminal/output.ts
22910
+ // src/terminal/output.ts
23291
22911
  var terminalStream2 = {
23292
22912
  write(text) {
23293
22913
  process.stdout.write(text);
@@ -23383,7 +23003,7 @@ class TabSink {
23383
23003
  }
23384
23004
  }
23385
23005
 
23386
- // packages/archymedes-cli/src/terminal/job-stream.ts
23006
+ // src/terminal/job-stream.ts
23387
23007
  var TERMINAL_STATUSES = new Set(["completed", "failed", "cancelled"]);
23388
23008
  class WatchRegistry {
23389
23009
  watched = new Map;
@@ -23418,7 +23038,7 @@ class WatchRegistry {
23418
23038
  }
23419
23039
  }
23420
23040
 
23421
- // packages/archymedes-cli/src/theme/theme.ts
23041
+ // src/theme/theme.ts
23422
23042
  var BLUEPRINT2 = `
23423
23043
  /* Blueprint navy, chalk-white ink and an amber pencil — the original drafting table. */
23424
23044
  @theme blueprint {
@@ -23719,7 +23339,7 @@ function detectPreferredTheme(environment) {
23719
23339
  return DEFAULT_THEME_NAME2;
23720
23340
  }
23721
23341
 
23722
- // packages/archymedes-cli/src/theme/theme-files.ts
23342
+ // src/theme/theme-files.ts
23723
23343
  import { promises as fs19 } from "node:fs";
23724
23344
  import path26 from "node:path";
23725
23345
  function themeDirectory(scope, root, environment) {
@@ -23757,7 +23377,7 @@ async function findTheme(name, root, environment) {
23757
23377
  return (await discoverThemes(root, environment)).find((theme) => theme.name.toLowerCase() === wanted);
23758
23378
  }
23759
23379
 
23760
- // packages/archymedes-cli/src/render/syntax.ts
23380
+ // src/render/syntax.ts
23761
23381
  var DEFAULT_CODE_STYLE2 = { colors: "vscode", lineNumbers: true };
23762
23382
  var currentCodeStyle2 = { ...DEFAULT_CODE_STYLE2 };
23763
23383
  function codeStyleFromEnvironment(environment) {
@@ -23815,7 +23435,7 @@ var GRAMMARS2 = {
23815
23435
  };
23816
23436
  var PUNCTUATION2 = new Set([..."{}[]()<>;,.:=+-*/%!&|^~?\\"]);
23817
23437
 
23818
- // packages/archymedes-cli/src/platform/i18n.ts
23438
+ // src/platform/i18n.ts
23819
23439
  var CONTROL_LANGUAGES2 = {
23820
23440
  en: "English",
23821
23441
  zh: "中文 (Mandarin)",
@@ -23840,7 +23460,7 @@ function resolveControlLanguage2(value) {
23840
23460
  return base in CONTROL_LANGUAGES2 ? base : "en";
23841
23461
  }
23842
23462
 
23843
- // packages/archymedes-cli/src/text/glyphs.ts
23463
+ // src/text/glyphs.ts
23844
23464
  var UNICODE_GLYPHS2 = {
23845
23465
  starGlyphs: [".", "·", "✧", "✦", "✶"],
23846
23466
  spinnerFrames: [" ◜ ", " ◠ ", " ◝ ", " ◞ ", " ◡ ", " ◟ "],
@@ -23956,7 +23576,7 @@ function glyphsFor(mode) {
23956
23576
  function resolveGlyphs(environment, platform = process.platform) {
23957
23577
  return glyphsFor(detectGlyphMode(environment, platform));
23958
23578
  }
23959
- // packages/archymedes-cli/src/session/state-history.ts
23579
+ // src/session/state-history.ts
23960
23580
  class CliStateHistory {
23961
23581
  root;
23962
23582
  environment;
@@ -24067,7 +23687,7 @@ class CliStateHistory {
24067
23687
  }
24068
23688
  }
24069
23689
 
24070
- // packages/archymedes-cli/src/ui/borrowed-keyboard.ts
23690
+ // src/ui/borrowed-keyboard.ts
24071
23691
  function painter(output) {
24072
23692
  let rows = 0;
24073
23693
  const erase = () => {
@@ -24148,7 +23768,7 @@ async function openChooser(host, items, options, self) {
24148
23768
  return withBorrowedKeyboard(host, self, (keys, paint2) => runChooser(keys, items, paint2, sized));
24149
23769
  }
24150
23770
 
24151
- // packages/archymedes-cli/src/app/prompts.ts
23771
+ // src/app/prompts.ts
24152
23772
  function renderApprovalPreview(preview2) {
24153
23773
  if (!preview2)
24154
23774
  return;
@@ -24286,7 +23906,7 @@ async function hiddenQuestion(readline, question, signal) {
24286
23906
  }
24287
23907
  }
24288
23908
 
24289
- // packages/archymedes-cli/src/app/providers.ts
23909
+ // src/app/providers.ts
24290
23910
  function readFxRates(environment) {
24291
23911
  const genericRate = Number(environment.ARCHYMEDES_FX_RATE);
24292
23912
  const genericFrom = environment.ARCHYMEDES_FX_FROM?.trim().toUpperCase();
@@ -24303,11 +23923,11 @@ function readFxRates(environment) {
24303
23923
  }
24304
23924
  return configured;
24305
23925
  }
24306
- // packages/core/src/cli/job-store.ts
23926
+ // ../core/src/cli/job-store.ts
24307
23927
  import { promises as fs20 } from "node:fs";
24308
23928
  import path27 from "node:path";
24309
23929
 
24310
- // packages/core/src/cli/jobs.ts
23930
+ // ../core/src/cli/jobs.ts
24311
23931
  var JOB_STATUSES = ["queued", "running", "paused", "completed", "failed", "cancelled"];
24312
23932
  var DECISIONS = ["allow", "allow_always", "allow_pattern", "deny", "deny_always"];
24313
23933
  function parseJobRecord(value, index) {
@@ -24482,7 +24102,7 @@ function recoverStale(store, now) {
24482
24102
  return { store: { jobs }, recovered };
24483
24103
  }
24484
24104
 
24485
- // packages/core/src/cli/job-store.ts
24105
+ // ../core/src/cli/job-store.ts
24486
24106
  function jobStoreFile(root) {
24487
24107
  return path27.join(root, ".archymedes", "jobs.json");
24488
24108
  }
@@ -24602,10 +24222,10 @@ async function appendJobLog(root, id, line) {
24602
24222
  await fs20.appendFile(file, `${line}
24603
24223
  `, "utf8");
24604
24224
  }
24605
- // packages/archymedes-cli/src/job-worker.ts
24225
+ // src/job-worker.ts
24606
24226
  import os6 from "node:os";
24607
24227
 
24608
- // packages/archymedes-cli/src/commands/wander.ts
24228
+ // src/commands/wander.ts
24609
24229
  async function gatherWanderEvidence(topic, search, numResults = 8) {
24610
24230
  if (!search) {
24611
24231
  return {
@@ -24701,7 +24321,7 @@ function renderWanderChart(results, style3, width) {
24701
24321
  `);
24702
24322
  }
24703
24323
 
24704
- // packages/archymedes-cli/src/job-worker.ts
24324
+ // src/job-worker.ts
24705
24325
  function workerId(hostname = os6.hostname(), pid = process.pid) {
24706
24326
  return `${hostname}:${pid}`;
24707
24327
  }
@@ -24879,7 +24499,7 @@ async function runJobWorkerForever(deps) {
24879
24499
  }
24880
24500
  }
24881
24501
 
24882
- // packages/archymedes-cli/src/app/providers.ts
24502
+ // src/app/providers.ts
24883
24503
  function renderProviders(environment, depth) {
24884
24504
  const paint2 = (text, apply) => depth === "none" ? text : apply(text);
24885
24505
  const statuses = describeProviders(environment);
@@ -24950,7 +24570,7 @@ function modelPriceCatalogFor(prices, exact) {
24950
24570
  return { ...rates, ...prices.largeContext ? { largeContext: prices.largeContext } : {} };
24951
24571
  }
24952
24572
 
24953
- // packages/archymedes-cli/src/app/job-launch.ts
24573
+ // src/app/job-launch.ts
24954
24574
  async function runJobWorkerProcess(root, jobId) {
24955
24575
  const savedSettings = await loadSettings(process.env);
24956
24576
  const environment = mergedEnvironment(savedSettings, process.env);
@@ -24987,10 +24607,10 @@ async function runJobWorkerProcess(root, jobId) {
24987
24607
  }
24988
24608
  }
24989
24609
 
24990
- // packages/archymedes-cli/src/app/startup-commands.ts
24610
+ // src/app/startup-commands.ts
24991
24611
  import { createInterface as createInterface4 } from "node:readline/promises";
24992
24612
 
24993
- // packages/archymedes-cli/src/headless.ts
24613
+ // src/headless.ts
24994
24614
  var EXIT_CODES2 = {
24995
24615
  completed: 0,
24996
24616
  failed: 1,
@@ -25014,7 +24634,7 @@ function exitCodeForStatus2(status) {
25014
24634
  return STATUS_EXIT2[status];
25015
24635
  }
25016
24636
 
25017
- // packages/archymedes-cli/src/platform/doctor.ts
24637
+ // src/platform/doctor.ts
25018
24638
  var DEFAULT_PROBE_TIMEOUT_MS = 5000;
25019
24639
  function modelEndpoints(environment) {
25020
24640
  return providerEndpoints(environment).map((provider) => {
@@ -25144,7 +24764,7 @@ function doctorReport(probes, context) {
25144
24764
  };
25145
24765
  }
25146
24766
 
25147
- // packages/archymedes-cli/src/text/color-depth.ts
24767
+ // src/text/color-depth.ts
25148
24768
  function detectColorDepth2(environment, isTTY) {
25149
24769
  if (!isTTY || environment.NO_COLOR !== undefined || environment.TERM === "dumb")
25150
24770
  return "none";
@@ -25153,7 +24773,7 @@ function detectColorDepth2(environment, isTTY) {
25153
24773
  return "ansi256";
25154
24774
  }
25155
24775
 
25156
- // packages/archymedes-cli/src/session/delete-session.ts
24776
+ // src/session/delete-session.ts
25157
24777
  import { promises as fs21 } from "node:fs";
25158
24778
  import path28 from "node:path";
25159
24779
  var SAFE_ID = /^[A-Za-z0-9][A-Za-z0-9._-]{0,127}$/;
@@ -25173,7 +24793,7 @@ async function clearProjection(root, id) {
25173
24793
  if (!exists)
25174
24794
  return;
25175
24795
  try {
25176
- const { SessionProjection } = await import("./projection-4jjdaf41.mjs");
24796
+ const { SessionProjection } = await import("./projection-ndr3t77h.mjs");
25177
24797
  const projection = await SessionProjection.open(root);
25178
24798
  try {
25179
24799
  await projection.rebuild(id);
@@ -25199,7 +24819,7 @@ async function deleteSession(root, id, options = {}) {
25199
24819
  return { deleted: true };
25200
24820
  }
25201
24821
 
25202
- // packages/archymedes-cli/src/ui/navigation.ts
24822
+ // src/ui/navigation.ts
25203
24823
  var ESSENTIAL_COMMANDS = ["/help", "/history", "/model", "/mode", "/diff", "/undo", "/settings", "/exit"];
25204
24824
  function isEssential(command) {
25205
24825
  return ESSENTIAL_COMMANDS.includes(command);
@@ -25266,7 +24886,7 @@ function renderAsks(suggestions, style3) {
25266
24886
  `);
25267
24887
  }
25268
24888
 
25269
- // packages/archymedes-cli/src/app/help.ts
24889
+ // src/app/help.ts
25270
24890
  function helpText(language = "en", shortcuts = new Map) {
25271
24891
  const star = glyphs.star;
25272
24892
  const mark = (command) => isEssential(command) ? `${star} ` : " ";
@@ -25371,7 +24991,7 @@ ${renderCommandHelp(language, shortcuts, { mark })}
25371
24991
  `;
25372
24992
  }
25373
24993
 
25374
- // packages/archymedes-cli/src/app/prompts.ts
24994
+ // src/app/prompts.ts
25375
24995
  function settingsChooser(readline) {
25376
24996
  return (request) => openChooser({ readline, input: process.stdin, output: process.stdout }, request.items.map((item) => ({ ...item })), {
25377
24997
  title: request.title,
@@ -25444,7 +25064,7 @@ async function hiddenQuestion2(readline, question, signal) {
25444
25064
  }
25445
25065
  }
25446
25066
 
25447
- // packages/archymedes-cli/src/app/startup-commands.ts
25067
+ // src/app/startup-commands.ts
25448
25068
  function runInformationFlag(context) {
25449
25069
  const { args, environment, language, earlyDepth } = context;
25450
25070
  if (args.help) {
@@ -25648,7 +25268,7 @@ ${status.reason ?? "native state engine unavailable"}
25648
25268
  return 0;
25649
25269
  }
25650
25270
 
25651
- // packages/archymedes-cli/src/app/display-currency.ts
25271
+ // src/app/display-currency.ts
25652
25272
  async function settleDisplayCurrency(options) {
25653
25273
  const { args, environment, prices, rates, state } = options;
25654
25274
  if (prices && state.display !== prices.currency) {
@@ -25705,7 +25325,7 @@ async function settleDisplayCurrency(options) {
25705
25325
  return;
25706
25326
  }
25707
25327
 
25708
- // packages/archymedes-cli/src/app/workspaces.ts
25328
+ // src/app/workspaces.ts
25709
25329
  function createWorkspaceFactory(args, environment) {
25710
25330
  return async (request) => {
25711
25331
  const announce = (text) => {
@@ -25715,8 +25335,8 @@ function createWorkspaceFactory(args, environment) {
25715
25335
  };
25716
25336
  const minutes = Math.max(1, Math.min(args.sandboxMinutes, 60));
25717
25337
  if (request.backend === "e2b") {
25718
- const { findWorkspacePreset } = await import("./sandbox-templates-8sd1psez.mjs");
25719
- const { createE2BProvider } = await import("./factory-8xk6y60m.mjs");
25338
+ const { findWorkspacePreset } = await import("./sandbox-templates-9jcjws23.mjs");
25339
+ const { createE2BProvider } = await import("./factory-r6r0ypm6.mjs");
25720
25340
  const preset = findWorkspacePreset(request.preset ?? args.preset);
25721
25341
  const sandbox = createE2BProvider(environment, preset.templateAlias);
25722
25342
  if (!sandbox)
@@ -25742,7 +25362,7 @@ function createWorkspaceFactory(args, environment) {
25742
25362
  return { workspace: created };
25743
25363
  }
25744
25364
  if (request.backend === "docker") {
25745
- const { createDockerProvider } = await import("./factory-8xk6y60m.mjs");
25365
+ const { createDockerProvider } = await import("./factory-r6r0ypm6.mjs");
25746
25366
  const image = request.dockerImage || args.dockerImage || environment.DOCKER_CODING_IMAGE;
25747
25367
  const sandbox = createDockerProvider({ ...environment, DOCKER_CODING_IMAGE: image });
25748
25368
  if (!sandbox)
@@ -25771,11 +25391,11 @@ function createWorkspaceFactory(args, environment) {
25771
25391
  };
25772
25392
  }
25773
25393
 
25774
- // packages/core/src/cli/balance.ts
25394
+ // ../core/src/cli/balance.ts
25775
25395
  var CRITICAL_BALANCE_USD2 = 0.5;
25776
25396
  var LOW_BALANCE_USD2 = 2;
25777
25397
 
25778
- // packages/archymedes-cli/src/commands/balance.ts
25398
+ // src/commands/balance.ts
25779
25399
  function formatBalance(amount, currency) {
25780
25400
  return formatMoney(fromUnits(amount, currency));
25781
25401
  }
@@ -25878,7 +25498,7 @@ class BalanceWatch {
25878
25498
  }
25879
25499
  }
25880
25500
 
25881
- // packages/archymedes-cli/src/session/resumed-spend.ts
25501
+ // src/session/resumed-spend.ts
25882
25502
  async function readSessionModelTurns(root, sessionId) {
25883
25503
  const events = await readEventJournal(root, sessionId);
25884
25504
  const turns = [];
@@ -25910,7 +25530,7 @@ function priceSessionModelTurns(turns, options) {
25910
25530
  return { spent: total, unpriced: [...unpriced].sort() };
25911
25531
  }
25912
25532
 
25913
- // packages/archymedes-cli/src/app/balance-tracking.ts
25533
+ // src/app/balance-tracking.ts
25914
25534
  function createBalanceTracking(options) {
25915
25535
  const { environment, processEnvironment, rates, state } = options;
25916
25536
  const usdInDisplay = (usd) => {
@@ -25999,7 +25619,7 @@ function createBalanceTracking(options) {
25999
25619
  return { lowBalance, criticalBalance, balanceWatch, parseManualBalance, currentBalance, persistManualBalance, sessionSpend, balanceHeader, checkBalance, carryResumedSpend };
26000
25620
  }
26001
25621
 
26002
- // packages/archymedes-cli/src/app/agent-factory.ts
25622
+ // src/app/agent-factory.ts
26003
25623
  function createClientFactory(options) {
26004
25624
  const { args, environment, daemon, approvalPrompt, rates, approvedBudget, state } = options;
26005
25625
  return async (record2, runtime = {}) => {
@@ -26020,6 +25640,7 @@ function createClientFactory(options) {
26020
25640
  onExpense: (expense) => state.ledger.recordExpense(expense),
26021
25641
  onEvent,
26022
25642
  jev: jevOptionsFromEnvironment(environment),
25643
+ ...reasoningFromEnvironment(environment) ? { reasoning: reasoningFromEnvironment(environment) } : {},
26023
25644
  afterEdit: editDiagnostics
26024
25645
  }), record2);
26025
25646
  if (editDiagnostics)
@@ -26039,14 +25660,14 @@ function closeWithClient(client, hook) {
26039
25660
  client.dispose = () => dispose().finally(close);
26040
25661
  }
26041
25662
 
26042
- // packages/archymedes-cli/src/app/live-models.ts
26043
- async function readCachedLiveModels2(environment, state) {
25663
+ // src/app/live-models.ts
25664
+ async function readCachedLiveModels(environment, state) {
26044
25665
  const cached = await readModelCache(environment);
26045
25666
  if (isCacheFresh(cached))
26046
25667
  state.liveModels = cached.models;
26047
25668
  }
26048
25669
 
26049
- // packages/archymedes-cli/src/app/session-hints.ts
25670
+ // src/app/session-hints.ts
26050
25671
  function createSessionHints(options) {
26051
25672
  const { state, watched, tabs, usage } = options;
26052
25673
  const navContext = () => ({
@@ -26075,7 +25696,7 @@ function createSessionHints(options) {
26075
25696
  return { navContext, writeHint };
26076
25697
  }
26077
25698
 
26078
- // packages/archymedes-cli/src/render/completion-card.ts
25699
+ // src/render/completion-card.ts
26079
25700
  var CHECK_LABELS = {
26080
25701
  tests: "tests",
26081
25702
  typecheck: "typecheck",
@@ -26105,7 +25726,7 @@ function renderCompletionCard(input, style3) {
26105
25726
  return panel(lines, style3, { title, tone: failed2 ? "bad" : verified ? "good" : "warn" });
26106
25727
  }
26107
25728
 
26108
- // packages/archymedes-cli/src/render/routing-receipt.ts
25729
+ // src/render/routing-receipt.ts
26109
25730
  function formatMicros(micros, currency = "USD") {
26110
25731
  if (micros === undefined || !Number.isFinite(micros))
26111
25732
  return "—";
@@ -26197,10 +25818,10 @@ ${rule(cell, { label: badge })}` : out3.join(`
26197
25818
  `);
26198
25819
  }
26199
25820
 
26200
- // packages/archymedes-cli/src/app/session-hints.ts
25821
+ // src/app/session-hints.ts
26201
25822
  var failureStatus = (status) => status === "needs_approval" ? "waiting_approval" : status;
26202
25823
 
26203
- // packages/archymedes-cli/src/app/turn-failure.ts
25824
+ // src/app/turn-failure.ts
26204
25825
  async function handleTurnFailure(error, request, context) {
26205
25826
  const { args, environment, interactive, headless, recoveryState, state, navContext } = context;
26206
25827
  if (recoveryState.last?.request === request) {
@@ -26255,7 +25876,7 @@ ${style.yellow(`Stopped at the ${formatMoney(fromUnits(args.budget, state.displa
26255
25876
  return false;
26256
25877
  }
26257
25878
 
26258
- // packages/archymedes-cli/src/app/turn-preflight.ts
25879
+ // src/app/turn-preflight.ts
26259
25880
  async function runTurnPreflight(request, context) {
26260
25881
  const { args, readline, interactive, approvedBudget, rates, state } = context;
26261
25882
  const { currentBalance, balanceWatch, sessionSpend } = context.balance;
@@ -26353,7 +25974,7 @@ async function runTurnPreflight(request, context) {
26353
25974
  return true;
26354
25975
  }
26355
25976
 
26356
- // packages/archymedes-cli/src/app/turn-runner.ts
25977
+ // src/app/turn-runner.ts
26357
25978
  var addModelUsage2 = (left, right) => ({
26358
25979
  inputTokens: left.inputTokens + right.inputTokens,
26359
25980
  outputTokens: left.outputTokens + right.outputTokens,
@@ -26561,7 +26182,7 @@ ${result.status === "completed" ? asMarkdown(result.summary) : style.yellow(resu
26561
26182
  };
26562
26183
  }
26563
26184
 
26564
- // packages/archymedes-cli/src/app/interrupts.ts
26185
+ // src/app/interrupts.ts
26565
26186
  function createSigintHandling(options) {
26566
26187
  const { readline, state } = options;
26567
26188
  const handleSigint = () => {
@@ -26602,7 +26223,7 @@ function createSigintHandling(options) {
26602
26223
  return { bindSigint, unbindSigint };
26603
26224
  }
26604
26225
 
26605
- // packages/archymedes-cli/src/app/session-start.ts
26226
+ // src/app/session-start.ts
26606
26227
  import path29 from "node:path";
26607
26228
  async function resumeAtStartup(context) {
26608
26229
  const { args, interactive, stateHistory, state, openClient } = context;
@@ -26675,7 +26296,7 @@ async function runOneShot(context) {
26675
26296
  return args.json ? exitCodeForStatus2(state.lastTurnStatus) : ran ? 0 : 1;
26676
26297
  }
26677
26298
 
26678
- // packages/archymedes-cli/src/archymedes.ts
26299
+ // src/archymedes.ts
26679
26300
  async function main() {
26680
26301
  process.stdout.on("error", (error) => {
26681
26302
  if (error.code === "EPIPE")
@@ -27113,7 +26734,7 @@ async function main() {
27113
26734
  out2.route(firstSink);
27114
26735
  let explainedTabs = false;
27115
26736
  let liveModels = {};
27116
- await readCachedLiveModels2(environment, state);
26737
+ await readCachedLiveModels(environment, state);
27117
26738
  if (args.estimateOnly) {
27118
26739
  out2.write(`${ledger.formatPrediction(await agent.estimate(args.prompt))}
27119
26740
  `);