archymedes-cli 3.2.0 → 3.4.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-ds224xzf.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|vitest|mocha|tailwindcss|webpack|rollup|nodemon|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,
@@ -10961,10 +10386,12 @@ class ArchymedesAgent {
10961
10386
  get todos() {
10962
10387
  return this.todoList.list();
10963
10388
  }
10964
- diffPatch() {
10389
+ async diffPatch() {
10390
+ await this.pendingCheckpoint;
10965
10391
  return this.checkpoints.diffPatch();
10966
10392
  }
10967
- diffStat() {
10393
+ async diffStat() {
10394
+ await this.pendingCheckpoint;
10968
10395
  return this.checkpoints.diffStat();
10969
10396
  }
10970
10397
  listFiles(pattern = "**/*") {
@@ -11076,6 +10503,7 @@ class ArchymedesAgent {
11076
10503
  if (this.turnAbort)
11077
10504
  throw new Error("Cannot undo while a turn is running");
11078
10505
  await this.acquireOwnership();
10506
+ await this.pendingCheckpoint;
11079
10507
  const checkpoint = this.checkpoints.latest();
11080
10508
  if (!checkpoint)
11081
10509
  return;
@@ -11102,7 +10530,7 @@ class ArchymedesAgent {
11102
10530
  this.delegatedInFlightRwf += reservation;
11103
10531
  try {
11104
10532
  const runtime = new BoundedAgentRuntime({
11105
- model: this.recovery.wrap(this.options.model, () => this.session, this.options.prices, `delegate:${randomUUID5()}`),
10533
+ model: this.recovery.wrap(this.options.model, () => this.session, this.options.prices, `delegate:${randomUUID4()}`),
11106
10534
  tools,
11107
10535
  prices: this.options.prices,
11108
10536
  artifacts: this.artifacts,
@@ -11116,7 +10544,7 @@ class ArchymedesAgent {
11116
10544
  const result = await runtime.execute({
11117
10545
  taskId: `${this.session.id}_delegate`,
11118
10546
  runId: this.session.id,
11119
- stepId: `delegate_${randomUUID5()}`,
10547
+ stepId: `delegate_${randomUUID4()}`,
11120
10548
  objective: task,
11121
10549
  history: [],
11122
10550
  systemPrompt,
@@ -11159,7 +10587,7 @@ class ArchymedesAgent {
11159
10587
  this.turnAbort = null;
11160
10588
  throw error;
11161
10589
  }
11162
- const turnId = `turn_${randomUUID5()}`;
10590
+ const turnId = `turn_${randomUUID4()}`;
11163
10591
  this.activeTurnId = turnId;
11164
10592
  let turnStatus = "queued";
11165
10593
  const transition = async (to, durable = false) => {
@@ -11218,17 +10646,28 @@ class ArchymedesAgent {
11218
10646
 
11219
10647
  ${requested}` : requested;
11220
10648
  let checkpoint;
10649
+ let checkpointReady = Promise.resolve();
11221
10650
  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 });
10651
+ checkpointReady = this.checkpoints.capture(titleFromObjective(objective), turnId, this.messages.length).then((taken) => {
10652
+ checkpoint = taken;
10653
+ if (taken)
10654
+ this.options.onEvent?.({ type: "checkpoint", checkpoint: taken });
10655
+ }).catch(() => {
10656
+ return;
10657
+ });
10658
+ this.pendingCheckpoint = checkpointReady;
11225
10659
  }
11226
10660
  const compaction = await this.compactIfNeeded(turnId, objective, turnAbort.signal, tokenSaver);
11227
10661
  compactionActualRwf = compaction.actualRwf;
11228
10662
  this.permissions.setTaskHint(objective);
10663
+ this.startDifficultyReading(objective, turnAbort.signal);
11229
10664
  const runtime = new BoundedAgentRuntime({
11230
10665
  model: this.recovery.wrap(this.options.model, () => this.session, this.options.prices, "main"),
11231
- tools: scoped,
10666
+ ...this.options.reasoning === "auto" ? { effortPolicy: (signals) => predictEffort({ ...signals, objective, ...this.jevDifficulty ? { difficultyHint: this.jevDifficulty } : {} }) } : {},
10667
+ tools: scoped.map((tool) => ({ ...tool, execute: async (args, context) => {
10668
+ await checkpointReady;
10669
+ return tool.execute(args, context);
10670
+ } })),
11232
10671
  prices: this.options.prices,
11233
10672
  artifacts: this.artifacts,
11234
10673
  control: {
@@ -11267,6 +10706,7 @@ ${requested}` : requested;
11267
10706
  runId: this.session.id,
11268
10707
  stepId: `turn_${this.messages.length}`,
11269
10708
  objective: turnObjective,
10709
+ ...this.options.reasoning && this.options.reasoning !== "auto" ? { effort: this.options.reasoning } : {},
11270
10710
  images: attached.images,
11271
10711
  history: sentHistory,
11272
10712
  systemPrompt,
@@ -11301,6 +10741,7 @@ ${hookWarnings.join(`
11301
10741
  hostedRecoveryBatchId: this.recovery.settledBatchId() ?? this.session.hostedRecoveryBatchId,
11302
10742
  updatedAt: Date.now()
11303
10743
  };
10744
+ await checkpointReady;
11304
10745
  const terminalStatus = runtimeStatusToTurnStatus(result.status);
11305
10746
  await transition(terminalStatus, true);
11306
10747
  await this.recordHostedUsage();
@@ -11351,6 +10792,8 @@ ${hookWarnings.join(`
11351
10792
  for (const call of message.toolCalls)
11352
10793
  toolNames.push(call.name);
11353
10794
  }
10795
+ if (toolNames.length === 0)
10796
+ return;
11354
10797
  try {
11355
10798
  const response = await requestJevVerdict({
11356
10799
  apiKey: this.jev.apiKey,
@@ -11385,6 +10828,30 @@ ${hookWarnings.join(`
11385
10828
  return (verdict.outcomeProbabilities[verdict.outcome] ?? 0) >= 0.5;
11386
10829
  }
11387
10830
  jevCorrectionDepth = 0;
10831
+ pendingCheckpoint = Promise.resolve();
10832
+ jevDifficulty;
10833
+ difficultyStamp = 0;
10834
+ startDifficultyReading(objective, signal) {
10835
+ this.jevDifficulty = undefined;
10836
+ const stamp = ++this.difficultyStamp;
10837
+ if (this.options.reasoning !== "auto" || !this.jev)
10838
+ return;
10839
+ requestJevVerdict({
10840
+ apiKey: this.jev.apiKey,
10841
+ ...this.jev.model ? { model: this.jev.model } : {},
10842
+ ...this.jev.fetchImpl ? { fetchImpl: this.jev.fetchImpl } : {},
10843
+ state: objective,
10844
+ questions: DIFFICULTY_QUESTIONS,
10845
+ timeoutMs: 5000,
10846
+ signal
10847
+ }).then((response) => {
10848
+ if (stamp !== this.difficultyStamp)
10849
+ return;
10850
+ this.jevDifficulty = difficultyFromResponse(response)?.difficulty;
10851
+ }).catch(() => {
10852
+ return;
10853
+ });
10854
+ }
11388
10855
  async correctivePrompt(objective, verdict, evidence) {
11389
10856
  const failures = evidence.filter((entry) => entry.exitCode !== undefined && entry.exitCode !== 0).slice(-6);
11390
10857
  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 +10951,15 @@ function isActiveTurnStatus(status) {
11484
10951
  return status === "queued" || status === "running" || status === "waiting_approval";
11485
10952
  }
11486
10953
 
11487
- // packages/core/src/cli/edit-diagnostics.ts
10954
+ // ../core/src/cli/edit-diagnostics.ts
11488
10955
  import { promises as fs11 } from "node:fs";
11489
10956
  import path16 from "node:path";
11490
10957
  import { fileURLToPath as fileURLToPath2, pathToFileURL } from "node:url";
11491
10958
 
11492
- // packages/core/src/lsp/client.ts
10959
+ // ../core/src/lsp/client.ts
11493
10960
  import { spawn as spawn7, spawnSync } from "node:child_process";
11494
10961
 
11495
- // packages/core/src/lsp/diagnostics.ts
10962
+ // ../core/src/lsp/diagnostics.ts
11496
10963
  function severityName(severity) {
11497
10964
  switch (severity) {
11498
10965
  case 1:
@@ -11598,7 +11065,7 @@ class DiagnosticsCollection {
11598
11065
  }
11599
11066
  }
11600
11067
 
11601
- // packages/core/src/lsp/protocol.ts
11068
+ // ../core/src/lsp/protocol.ts
11602
11069
  var HEADER_SEPARATOR = `\r
11603
11070
  \r
11604
11071
  `;
@@ -11644,7 +11111,7 @@ function isJsonRpcNotification3(value) {
11644
11111
  return typeof value === "object" && value !== null && "jsonrpc" in value && !("id" in value) && typeof value.method === "string";
11645
11112
  }
11646
11113
 
11647
- // packages/core/src/lsp/client.ts
11114
+ // ../core/src/lsp/client.ts
11648
11115
  class LspClientError extends Error {
11649
11116
  constructor(message) {
11650
11117
  super(message);
@@ -11855,7 +11322,7 @@ function toLocations(result) {
11855
11322
  return candidates.filter((candidate) => typeof candidate === "object" && candidate !== null && typeof candidate.uri === "string" && typeof candidate.range === "object");
11856
11323
  }
11857
11324
 
11858
- // packages/core/src/lsp/servers.ts
11325
+ // ../core/src/lsp/servers.ts
11859
11326
  import { spawnSync as spawnSync2 } from "node:child_process";
11860
11327
  var LANGUAGE_SERVERS = [
11861
11328
  {
@@ -11996,7 +11463,7 @@ function defaultProbe(command) {
11996
11463
  });
11997
11464
  }
11998
11465
 
11999
- // packages/core/src/cli/edit-diagnostics.ts
11466
+ // ../core/src/cli/edit-diagnostics.ts
12000
11467
  var DEFAULT_TIMEOUT_MS = 1500;
12001
11468
  var DEFAULT_REFINE_MS = 250;
12002
11469
  var DEFAULT_MAX_LINES = 20;
@@ -12217,7 +11684,7 @@ function createLspEditDiagnostics(root, options = {}) {
12217
11684
  });
12218
11685
  }
12219
11686
 
12220
- // packages/core/src/providers/price-catalog.ts
11687
+ // ../core/src/providers/price-catalog.ts
12221
11688
  var ANTHROPIC_VERIFIED = "2026-06-24";
12222
11689
  var ANTHROPIC_SOURCE = "anthropic.com/pricing, recorded 2026-06-24";
12223
11690
  var ANTHROPIC = [
@@ -12269,7 +11736,7 @@ var SERVICES = [
12269
11736
  ];
12270
11737
  var PRICE_CATALOG = definePrices([...ANTHROPIC, ...OAI_COMPAT_LABS, ...SERVICES]);
12271
11738
 
12272
- // packages/core/src/providers/free-catalog.ts
11739
+ // ../core/src/providers/free-catalog.ts
12273
11740
  var FREE_ROUTER = "openrouter/free";
12274
11741
  var FREE_BASE_URL = "https://openrouter.ai/api/v1";
12275
11742
  var FREE_GATEWAY_URL = "";
@@ -12278,6 +11745,7 @@ var FREE_CATALOG_TTL_MS = 6 * 60 * 60 * 1000;
12278
11745
  var FREE_CATALOG_MAX_RECORDS = 1e4;
12279
11746
  var FREE_ROUTER_PREFERENCE = [
12280
11747
  "cohere/north-mini-code:free",
11748
+ "nvidia/nemotron-3-super-120b-a12b:free",
12281
11749
  "google/gemma-4-31b-it:free",
12282
11750
  "dots-studio/dots-3-note-preview:free",
12283
11751
  "nex-agi/nex-n2.5-pro:free",
@@ -12404,8 +11872,21 @@ function mergeFreeCatalog(live, discovery, fetchedAt, warnings = []) {
12404
11872
  }
12405
11873
  return { fetchedAt, models: [...byId.values()].sort((a, b) => a.id.localeCompare(b.id)), warnings };
12406
11874
  }
11875
+ var SMALL_MODEL_BILLIONS = 4;
11876
+ function modelSizeBillions(model) {
11877
+ const id = model.toLowerCase();
11878
+ const billions = [...id.matchAll(/(?:^|[^a-z0-9.])(\d+(?:\.\d+)?)b(?![a-z])/g)];
11879
+ if (billions.length > 0)
11880
+ return Math.min(...billions.map((match) => Number(match[1])));
11881
+ const millions = id.match(/(?:^|[^a-z0-9.])(\d+(?:\.\d+)?)m(?![a-z])/);
11882
+ return millions ? Number(millions[1]) / 1000 : null;
11883
+ }
11884
+ function isSmallModel(model) {
11885
+ const size = modelSizeBillions(model);
11886
+ return size !== null && size <= SMALL_MODEL_BILLIONS;
11887
+ }
12407
11888
 
12408
- // packages/core/src/providers/provider-specs.ts
11889
+ // ../core/src/providers/provider-specs.ts
12409
11890
  var PROVIDER_IDS = [
12410
11891
  "anthropic",
12411
11892
  "openai",
@@ -12466,7 +11947,7 @@ var PROVIDER_INFO = {
12466
11947
  }
12467
11948
  };
12468
11949
 
12469
- // packages/core/src/providers/anthropic-agent.ts
11950
+ // ../core/src/providers/anthropic-agent.ts
12470
11951
  async function collectAnthropicStream(stream, onTextDelta, onOutputProgress) {
12471
11952
  let id = "";
12472
11953
  let model = "";
@@ -12625,7 +12106,7 @@ class AnthropicAgentTurnProvider {
12625
12106
  let client;
12626
12107
  this.call = async (body, signal) => {
12627
12108
  if (!client) {
12628
- const { default: Anthropic } = await import("./index-teq1cta4.mjs").catch(() => {
12109
+ const { default: Anthropic } = await import("./index-qvgcwsvm.mjs").catch(() => {
12629
12110
  throw new Error("The Anthropic provider needs the @anthropic-ai/sdk package. Install it with: npm install @anthropic-ai/sdk");
12630
12111
  });
12631
12112
  client = new Anthropic({ apiKey: options.apiKey, ...options.baseURL ? { baseURL: options.baseURL } : {}, maxRetries: 0 });
@@ -12645,7 +12126,7 @@ class AnthropicAgentTurnProvider {
12645
12126
  ...system ? { system } : {},
12646
12127
  messages,
12647
12128
  tools,
12648
- ...request.effort && this.capabilities.supportsEffort ? { output_config: { effort: request.effort } } : {},
12129
+ ...request.effort && this.capabilities.supportsEffort ? { output_config: { effort: providerEffort(request.effort) } } : {},
12649
12130
  metadata: { user_id: request.safetyIdentifier },
12650
12131
  stream: true
12651
12132
  };
@@ -12687,10 +12168,10 @@ class AnthropicAgentTurnProvider {
12687
12168
  }
12688
12169
  }
12689
12170
 
12690
- // packages/core/src/providers/archymedes-cloud-agent.ts
12691
- import { createHash as createHash9, randomUUID as randomUUID6 } from "node:crypto";
12171
+ // ../core/src/providers/archymedes-cloud-agent.ts
12172
+ import { createHash as createHash8, randomUUID as randomUUID5 } from "node:crypto";
12692
12173
 
12693
- // packages/core/src/providers/routing-plan.ts
12174
+ // ../core/src/providers/routing-plan.ts
12694
12175
  function isRecord2(value) {
12695
12176
  return typeof value === "object" && value !== null;
12696
12177
  }
@@ -12762,7 +12243,7 @@ function planCurrency(ranked) {
12762
12243
  return;
12763
12244
  }
12764
12245
 
12765
- // packages/core/src/providers/credit-balance.ts
12246
+ // ../core/src/providers/credit-balance.ts
12766
12247
  function isRecord3(value) {
12767
12248
  return typeof value === "object" && value !== null;
12768
12249
  }
@@ -12797,7 +12278,7 @@ function parseCreditBalance(value) {
12797
12278
  };
12798
12279
  }
12799
12280
 
12800
- // packages/core/src/providers/archymedes-cloud-agent.ts
12281
+ // ../core/src/providers/archymedes-cloud-agent.ts
12801
12282
  class ArchymedesCloudError extends Error {
12802
12283
  status;
12803
12284
  code;
@@ -12863,7 +12344,7 @@ class ArchymedesCloudTurnProvider {
12863
12344
  this.planUrl = base.endsWith("/v1") ? `${base}/routes/plan` : `${base}/v1/routes/plan`;
12864
12345
  this.balanceUrl = base.endsWith("/v1") ? `${base}/credits/balance` : `${base}/v1/credits/balance`;
12865
12346
  this.capabilities = capabilitiesFor(this.model);
12866
- this.recoveryScope = createHash9("sha256").update(JSON.stringify({
12347
+ this.recoveryScope = createHash8("sha256").update(JSON.stringify({
12867
12348
  version: 1,
12868
12349
  endpoint: this.completionUrl,
12869
12350
  token: options.token,
@@ -12946,7 +12427,7 @@ class ArchymedesCloudTurnProvider {
12946
12427
  async exchange(request, recoveryOnly) {
12947
12428
  if (!request.safetyIdentifier.trim())
12948
12429
  throw new Error("safetyIdentifier is required");
12949
- const taskId = request.requestId ?? `cli_${randomUUID6()}`;
12430
+ const taskId = request.requestId ?? `cli_${randomUUID5()}`;
12950
12431
  if (!/^[A-Za-z0-9_-]{1,160}$/.test(taskId))
12951
12432
  throw new Error("requestId must contain 1 to 160 letters, numbers, underscores or hyphens");
12952
12433
  request.signal?.throwIfAborted();
@@ -12973,7 +12454,7 @@ class ArchymedesCloudTurnProvider {
12973
12454
  max_completion_tokens: request.maxOutputTokens,
12974
12455
  safety_identifier: request.safetyIdentifier,
12975
12456
  prompt_cache_key: request.safetyIdentifier,
12976
- ...request.effort ? { reasoning_effort: request.effort } : {},
12457
+ ...request.effort ? { reasoning_effort: providerEffort(request.effort) } : {},
12977
12458
  stream: false,
12978
12459
  archymedes: {
12979
12460
  task_id: taskId,
@@ -13014,7 +12495,9 @@ async function readBody(response) {
13014
12495
  }
13015
12496
  }
13016
12497
 
13017
- // packages/core/src/providers/request-pacer.ts
12498
+ // ../core/src/providers/request-pacer.ts
12499
+ var MIN_LEARNABLE_CAP = 8;
12500
+ var LEARNED_CAP_LIFETIME_WINDOWS = 5;
13018
12501
  var DEFAULTS = { baseGapMs: 2000, maxGapMs: 120000, releaseMs: 500 };
13019
12502
 
13020
12503
  class RequestPacer {
@@ -13023,12 +12506,20 @@ class RequestPacer {
13023
12506
  baseGapMs;
13024
12507
  maxGapMs;
13025
12508
  releaseMs;
12509
+ windowMs;
12510
+ cap;
12511
+ configuredCap;
12512
+ learnedAt = 0;
12513
+ starts = [];
13026
12514
  now;
13027
12515
  sleep;
13028
12516
  constructor(options = {}) {
13029
12517
  this.baseGapMs = options.baseGapMs ?? DEFAULTS.baseGapMs;
13030
12518
  this.maxGapMs = options.maxGapMs ?? DEFAULTS.maxGapMs;
13031
12519
  this.releaseMs = options.releaseMs ?? DEFAULTS.releaseMs;
12520
+ this.windowMs = options.windowMs ?? 60000;
12521
+ this.cap = options.windowCap !== undefined && options.windowCap >= 1 ? Math.floor(options.windowCap) : undefined;
12522
+ this.configuredCap = this.cap;
13032
12523
  this.now = options.now ?? Date.now;
13033
12524
  this.sleep = options.sleep ?? ((delayMs, signal) => new Promise((resolve, reject) => {
13034
12525
  if (signal?.aborted) {
@@ -13046,33 +12537,67 @@ class RequestPacer {
13046
12537
  signal?.addEventListener("abort", onAbort, { once: true });
13047
12538
  }));
13048
12539
  }
12540
+ get windowBudget() {
12541
+ return this.cap;
12542
+ }
12543
+ get inWindow() {
12544
+ this.prune(this.now());
12545
+ return this.starts.length;
12546
+ }
12547
+ prune(now) {
12548
+ let drop = 0;
12549
+ while (drop < this.starts.length && now - this.starts[drop] >= this.windowMs)
12550
+ drop += 1;
12551
+ if (drop > 0)
12552
+ this.starts.splice(0, drop);
12553
+ }
13049
12554
  get paceMs() {
13050
12555
  return this.gapMs;
13051
12556
  }
13052
12557
  async wait(signal) {
13053
12558
  signal?.throwIfAborted();
13054
- if (this.gapMs <= 0) {
13055
- this.lastStart = this.now();
13056
- return;
12559
+ if (this.learnedAt > 0 && this.now() - this.learnedAt > LEARNED_CAP_LIFETIME_WINDOWS * this.windowMs) {
12560
+ this.cap = this.configuredCap;
12561
+ this.learnedAt = 0;
12562
+ }
12563
+ if (this.cap !== undefined) {
12564
+ for (;; ) {
12565
+ const now = this.now();
12566
+ this.prune(now);
12567
+ if (this.starts.length < this.cap)
12568
+ break;
12569
+ await this.sleep(this.starts[0] + this.windowMs - now + 25, signal);
12570
+ signal?.throwIfAborted();
12571
+ }
12572
+ }
12573
+ if (this.gapMs > 0) {
12574
+ const remaining = this.gapMs - (this.now() - this.lastStart);
12575
+ if (remaining > 0)
12576
+ await this.sleep(remaining, signal);
12577
+ signal?.throwIfAborted();
13057
12578
  }
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
12579
  this.lastStart = this.now();
12580
+ this.starts.push(this.lastStart);
12581
+ if (this.starts.length > 512)
12582
+ this.starts.splice(0, this.starts.length - 512);
13064
12583
  }
13065
12584
  reportSuccess() {
13066
12585
  this.gapMs = Math.max(0, this.gapMs - this.releaseMs);
13067
12586
  }
13068
12587
  reportRateLimited(retryAfterMs) {
12588
+ this.prune(this.now());
12589
+ const admitted = this.starts.length - 1;
12590
+ if (admitted >= MIN_LEARNABLE_CAP && admitted < (this.cap ?? Number.POSITIVE_INFINITY)) {
12591
+ this.cap = admitted;
12592
+ this.learnedAt = this.now();
12593
+ }
13069
12594
  const doubled = this.gapMs <= 0 ? this.baseGapMs : Math.min(this.maxGapMs, this.gapMs * 2);
13070
12595
  const asked = typeof retryAfterMs === "number" && Number.isFinite(retryAfterMs) && retryAfterMs > 0 ? retryAfterMs : 0;
13071
12596
  this.gapMs = Math.min(this.maxGapMs, Math.max(doubled, Math.min(this.maxGapMs, asked)));
13072
12597
  }
13073
12598
  }
13074
12599
 
13075
- // packages/core/src/providers/free-catalog-fetch.ts
12600
+ // ../core/src/providers/free-catalog-fetch.ts
13076
12601
  var MAX_BYTES = 8 * 1024 * 1024;
13077
12602
  async function boundedJson(fetchImpl, url, signal) {
13078
12603
  const response = await fetchImpl(url, { signal, redirect: "error", headers: { accept: "application/json" } });
@@ -13136,7 +12661,7 @@ async function fetchFreeCatalog(options = {}) {
13136
12661
  }
13137
12662
  }
13138
12663
 
13139
- // packages/core/src/providers/free-install.ts
12664
+ // ../core/src/providers/free-install.ts
13140
12665
  import { promises as fs12 } from "node:fs";
13141
12666
  import path17 from "node:path";
13142
12667
  var FREE_INSTALL_HEADER = "x-archymedes-install";
@@ -13314,7 +12839,7 @@ class FreeInstallToken {
13314
12839
  }
13315
12840
  }
13316
12841
 
13317
- // packages/core/src/providers/free-errors.ts
12842
+ // ../core/src/providers/free-errors.ts
13318
12843
  var OPENROUTER_PRIVACY_SETTINGS_URL = "https://openrouter.ai/settings/privacy";
13319
12844
  var OPENROUTER_KEYS_URL = "https://openrouter.ai/keys";
13320
12845
  var OPENROUTER_CREDITS_URL = "https://openrouter.ai/settings/credits";
@@ -13390,7 +12915,7 @@ function describeFreeFailure(input) {
13390
12915
  return { message, retryable: transient, ...retryAfterMs2 ? { retryAfterMs: retryAfterMs2 } : {} };
13391
12916
  }
13392
12917
 
13393
- // packages/core/src/providers/free-health.ts
12918
+ // ../core/src/providers/free-health.ts
13394
12919
  import { promises as fs13 } from "node:fs";
13395
12920
  import path18 from "node:path";
13396
12921
  var FREE_HEALTH_WINDOW_MS = 7 * 24 * 60 * 60 * 1000;
@@ -13443,7 +12968,7 @@ function recordFreeOutcome(records, id, outcome, now) {
13443
12968
  return Object.fromEntries(newest);
13444
12969
  }
13445
12970
  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);
12971
+ 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
12972
  }
13448
12973
  function fileFreeHealthStore(file) {
13449
12974
  return {
@@ -13499,7 +13024,7 @@ class FreeHealthTracker {
13499
13024
  }
13500
13025
  }
13501
13026
 
13502
- // packages/core/src/providers/openrouter-key-info.ts
13027
+ // ../core/src/providers/openrouter-key-info.ts
13503
13028
  var OPENROUTER_KEY_URL = `${FREE_BASE_URL}/key`;
13504
13029
  var OPENROUTER_KEY_INFO_TTL_MS = 60000;
13505
13030
  var KEY_INFO_TIMEOUT_MS = 5000;
@@ -13602,7 +13127,7 @@ function nextUtcMidnight(now) {
13602
13127
  return new Date(Date.UTC(date.getUTCFullYear(), date.getUTCMonth(), date.getUTCDate() + 1)).toISOString();
13603
13128
  }
13604
13129
 
13605
- // packages/core/src/providers/tool-call-repair.ts
13130
+ // ../core/src/providers/tool-call-repair.ts
13606
13131
  function isObject(value) {
13607
13132
  return typeof value === "object" && value !== null && !Array.isArray(value);
13608
13133
  }
@@ -13712,7 +13237,7 @@ function repairToolCalls(calls) {
13712
13237
  });
13713
13238
  }
13714
13239
 
13715
- // packages/core/src/providers/free-agent.ts
13240
+ // ../core/src/providers/free-agent.ts
13716
13241
  var RETRYABLE_UPSTREAM_TYPES = new Set(["upstream_timeout", "upstream_idle_timeout", "upstream_unreachable", "upstream_stream_failed"]);
13717
13242
  function freeFailureStatus(error) {
13718
13243
  const record2 = error;
@@ -13730,6 +13255,12 @@ function isTransportFailure(error) {
13730
13255
  const name = error?.name;
13731
13256
  return error instanceof APIConnectionError || name === "APIConnectionError" || name === "APIConnectionTimeoutError" || error instanceof TypeError || Boolean(error?.cause);
13732
13257
  }
13258
+ function openRouterEffort(effort) {
13259
+ if (!effort)
13260
+ return;
13261
+ return effort === "xhigh" || effort === "max" ? "high" : effort;
13262
+ }
13263
+ var FREE_REQUESTS_PER_MINUTE = 20;
13733
13264
  var MAX_ROUTER_ATTEMPTS = 4;
13734
13265
  var CATALOG_TIMEOUT_MS = 15000;
13735
13266
  var FREE_CANDIDATE_FIRST_BYTE_TIMEOUT_MS = 45000;
@@ -13778,7 +13309,7 @@ class FreeAgentTurnProvider {
13778
13309
  this.options = options;
13779
13310
  this.dependencies = dependencies;
13780
13311
  this.selection = { provider: "free", model: options.model };
13781
- this.pacer = dependencies.pacer ?? new RequestPacer;
13312
+ this.pacer = dependencies.pacer ?? new RequestPacer({ windowCap: FREE_REQUESTS_PER_MINUTE });
13782
13313
  this.tokenSaver = options.tokenSaver ?? true;
13783
13314
  const usage = dependencies.usage === undefined ? dependencies.call || dependencies.catalog ? undefined : new FreeUsageMeter(fileFreeUsageStore(freeUsageFile(process.env)), dependencies.now) : dependencies.usage ?? undefined;
13784
13315
  if (usage)
@@ -13839,7 +13370,9 @@ class FreeAgentTurnProvider {
13839
13370
  }
13840
13371
  const eligible = this.catalog.models.filter((model) => model.eligible && isFreeModelId(model.id));
13841
13372
  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] : [];
13373
+ const routable = eligible.filter((model) => model.id !== FREE_ROUTER);
13374
+ const capable = routable.filter((model) => !isSmallModel(model.id));
13375
+ const candidates = this.options.model === FREE_ROUTER ? capable.length > 0 ? capable : routable : selected ? [selected] : [];
13843
13376
  if (!candidates.length)
13844
13377
  throw new FreeAccessError("No eligible free tool model is available. Run /models refresh or try again later.");
13845
13378
  const messages = toWireMessages(request.messages);
@@ -13944,6 +13477,7 @@ class FreeAgentTurnProvider {
13944
13477
  messages: withOpenRouterCacheControl(messages, model),
13945
13478
  max_tokens: output,
13946
13479
  ...request.tools.length ? { tools: request.tools.map((tool) => ({ type: "function", function: { name: tool.name, description: tool.description, parameters: tool.inputSchema } })) } : {},
13480
+ ...openRouterEffort(request.effort) ? { reasoning: { effort: openRouterEffort(request.effort) } } : {},
13947
13481
  provider: { require_parameters: true, max_price: { prompt: 0, completion: 0 }, allow_fallbacks: false },
13948
13482
  stream: true,
13949
13483
  stream_options: { include_usage: true }
@@ -13978,7 +13512,7 @@ class FreeAgentTurnProvider {
13978
13512
  }
13979
13513
  }
13980
13514
 
13981
- // packages/core/src/providers/openrouter-agent.ts
13515
+ // ../core/src/providers/openrouter-agent.ts
13982
13516
  var OPENROUTER_BASE_URL = "https://openrouter.ai/api/v1";
13983
13517
 
13984
13518
  class OpenRouterAgentTurnProvider {
@@ -14027,7 +13561,7 @@ function withHint(error) {
14027
13561
  return wrapped;
14028
13562
  }
14029
13563
 
14030
- // packages/core/src/providers/agent-matrix.ts
13564
+ // ../core/src/providers/agent-matrix.ts
14031
13565
  var OPENAI_COMPATIBLE_BASE_URL = {
14032
13566
  google: "https://generativelanguage.googleapis.com/v1beta/openai/",
14033
13567
  xai: "https://api.x.ai/v1",
@@ -14192,7 +13726,7 @@ function describeProviders(environment) {
14192
13726
  }
14193
13727
  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
13728
 
14195
- // packages/archymedes-cli/src/app/session-provider.ts
13729
+ // src/app/session-provider.ts
14196
13730
  async function resolveSessionProvider(environment, options) {
14197
13731
  if (options.provider || !options.resume)
14198
13732
  return resolveProvider(environment, options);
@@ -14201,12 +13735,12 @@ async function resolveSessionProvider(environment, options) {
14201
13735
  return resolveProvider(environment, selection?.provider === "free" ? { provider: "free", model: options.model ?? selection.model } : options);
14202
13736
  }
14203
13737
 
14204
- // packages/archymedes-cli/src/platform/settings.ts
13738
+ // src/platform/settings.ts
14205
13739
  import { promises as fs15 } from "node:fs";
14206
13740
  import os4 from "node:os";
14207
13741
  import path20 from "node:path";
14208
13742
 
14209
- // packages/archymedes-cli/src/platform/i18n.ts
13743
+ // src/platform/i18n.ts
14210
13744
  var CONTROL_LANGUAGES = {
14211
13745
  en: "English",
14212
13746
  zh: "中文 (Mandarin)",
@@ -14673,11 +14207,11 @@ function t(language, key) {
14673
14207
  return MESSAGES[key][language] ?? MESSAGES[key].en;
14674
14208
  }
14675
14209
 
14676
- // packages/archymedes-cli/src/platform/local-currency.ts
14210
+ // src/platform/local-currency.ts
14677
14211
  import { promises as fs14 } from "node:fs";
14678
14212
  import path19 from "node:path";
14679
14213
 
14680
- // packages/archymedes-cli/src/platform/endpoints.ts
14214
+ // src/platform/endpoints.ts
14681
14215
  var FX_ENDPOINTS = [
14682
14216
  "https://cdn.jsdelivr.net/npm/@fawazahmed0/currency-api@latest/v1/currencies",
14683
14217
  "https://latest.currency-api.pages.dev/v1/currencies"
@@ -14721,7 +14255,7 @@ function hostOf(url) {
14721
14255
  }
14722
14256
  }
14723
14257
 
14724
- // packages/archymedes-cli/src/platform/network.ts
14258
+ // src/platform/network.ts
14725
14259
  var CODES = {
14726
14260
  dns: ["ENOTFOUND", "EAI_AGAIN", "EAI_NONAME", "EAI_FAIL", "EAI_MEMORY", "EAI_SYSTEM", "DNS_E_NAME_NOT_FOUND", "DNS_E_QUERY_PENDING"],
14727
14261
  timeout: ["ETIMEDOUT", "UND_ERR_CONNECT_TIMEOUT", "UND_ERR_HEADERS_TIMEOUT", "UND_ERR_BODY_TIMEOUT", "ERR_SOCKET_CONNECTION_TIMEOUT", "connection_timeout"],
@@ -14970,7 +14504,7 @@ function classifyNetworkError(error, options = {}) {
14970
14504
  return null;
14971
14505
  }
14972
14506
 
14973
- // packages/archymedes-cli/src/platform/local-currency.ts
14507
+ // src/platform/local-currency.ts
14974
14508
  var COUNTRY_CURRENCIES = {
14975
14509
  RW: "RWF",
14976
14510
  EG: "EGP",
@@ -15098,7 +14632,7 @@ async function startFxRateLookup(from, to, options) {
15098
14632
  return { immediate: cached?.rate ?? null, refresh };
15099
14633
  }
15100
14634
 
15101
- // packages/archymedes-cli/src/theme/theme.ts
14635
+ // src/theme/theme.ts
15102
14636
  var BLUEPRINT = `
15103
14637
  /* Blueprint navy, chalk-white ink and an amber pencil — the original drafting table. */
15104
14638
  @theme blueprint {
@@ -15400,7 +14934,7 @@ function roleCode(role, palette, depth) {
15400
14934
  }
15401
14935
  var NO_COLOR_PALETTE = buildPalette(findBuiltinTheme(DEFAULT_THEME_NAME) ?? { name: DEFAULT_THEME_NAME, description: "", tokens: DEFAULT_TOKENS }, "none");
15402
14936
 
15403
- // packages/archymedes-cli/src/platform/settings.ts
14937
+ // src/platform/settings.ts
15404
14938
  var LANGUAGE_NAMES = {
15405
14939
  en: "English",
15406
14940
  zh: "中文",
@@ -15504,6 +15038,13 @@ var AUTOSAVE_CHOICES = [
15504
15038
  { value: "off", label: "Off — save with Ctrl+S (default)" },
15505
15039
  { value: "on", label: "On — save a second after you stop typing" }
15506
15040
  ];
15041
+ var REASONING_CHOICES = [
15042
+ { value: "auto", label: "Auto (experimental) — lower the effort on routine steps, leave hard ones to the model" },
15043
+ { value: "minimal", label: "Minimal — never think more than needed" },
15044
+ { value: "low", label: "Low" },
15045
+ { value: "medium", label: "Medium" },
15046
+ { value: "high", label: "High — slowest" }
15047
+ ];
15507
15048
  var PROVIDER_CHOICES = PROVIDER_IDS.map((id) => ({ value: id, label: PROVIDER_INFO[id].label }));
15508
15049
  var SETTING_FIELDS = [
15509
15050
  { key: "ARCHYMEDES_THEME", label: "Theme — colours for the whole CLI (/theme <name> also saves it)", choices: THEME_CHOICES, section: "Appearance" },
@@ -15511,6 +15052,7 @@ var SETTING_FIELDS = [
15511
15052
  { key: "ARCHYMEDES_CODE_LINE_NUMBERS", label: "Line numbers in code blocks", choices: SHOW_HIDE_CHOICES },
15512
15053
  { key: "ARCHYMEDES_SIMPLE", label: "Simple mode — quiet start, short help", choices: SIMPLE_CHOICES },
15513
15054
  { key: "ARCHYMEDES_LANGUAGE", label: "Control language", choices: LANGUAGE_CHOICES },
15055
+ { key: "ARCHYMEDES_REASONING", label: "Reasoning effort — clear it to leave each model at its own default", choices: REASONING_CHOICES },
15514
15056
  { key: "ARCHYMEDES_RESUME", label: "Continue your last chat at startup", choices: RESUME_CHOICES, section: "Behaviour" },
15515
15057
  { key: "ARCHYMEDES_EDITOR_AUTOSAVE", label: "Auto-save in /edit", choices: AUTOSAVE_CHOICES },
15516
15058
  { key: "ARCHYMEDES_SUGGEST_MODEL", label: "Ask the model for extra suggestions (a small extra call per turn)", choices: ON_OFF_CHOICES },
@@ -15655,7 +15197,8 @@ var ENUM_SETTINGS = {
15655
15197
  ARCHYMEDES_RESUME: ["ask", "always", "never"],
15656
15198
  ARCHYMEDES_EDITOR_AUTOSAVE: ["on", "off"],
15657
15199
  ARCHYMEDES_SUGGEST_MODEL: ["on", "off"],
15658
- ARCHYMEDES_AUTO_UPDATE: ["install", "check", "off"]
15200
+ ARCHYMEDES_AUTO_UPDATE: ["install", "check", "off"],
15201
+ ARCHYMEDES_REASONING: ["auto", "minimal", "low", "medium", "high"]
15659
15202
  };
15660
15203
  var SETTING_CANCELLED = "\x00cancelled";
15661
15204
  function validateSetting(key, raw) {
@@ -15892,7 +15435,7 @@ Archymedes ${controlLabel(language, "settings")}
15892
15435
  }
15893
15436
  }
15894
15437
 
15895
- // packages/archymedes-cli/src/app/free-setup.ts
15438
+ // src/app/free-setup.ts
15896
15439
  var OPENROUTER_KEYS_URL2 = "https://openrouter.ai/keys";
15897
15440
  var FREE_PRIVACY_NOTICE = "Free models may log your prompts and code; don't send secrets. Use a paid provider for private code.";
15898
15441
  function needsFreePrivacyNotice(environment) {
@@ -15911,70 +15454,11 @@ async function showFreePrivacyNoticeOnce(options) {
15911
15454
  return next;
15912
15455
  }
15913
15456
 
15914
- // packages/archymedes-cli/src/app/acp-setup.ts
15915
- var KEY_URLS = {
15916
- free: OPENROUTER_KEYS_URL2,
15917
- openrouter: OPENROUTER_KEYS_URL2,
15918
- anthropic: "https://console.anthropic.com/settings/keys",
15919
- openai: "https://platform.openai.com/api-keys",
15920
- google: "https://aistudio.google.com/apikey",
15921
- xai: "https://console.x.ai",
15922
- deepseek: "https://platform.deepseek.com/api_keys",
15923
- mistral: "https://console.mistral.ai/api-keys",
15924
- groq: "https://console.groq.com/keys"
15925
- };
15926
- var ORDER = ["free", ...PROVIDER_IDS.filter((id) => id !== "free")];
15927
- function setupStatus(environment, requested = {}) {
15928
- const resolved = resolveProvider(environment, requested);
15929
- const providers = ORDER.map((id) => {
15930
- const info = PROVIDER_INFO[id];
15931
- const keyOnly = info.requires.length === 1 && SETTING_FIELDS.some((field) => field.key === info.requires[0]);
15932
- return {
15933
- id,
15934
- label: info.label,
15935
- ...keyOnly ? { keyVariable: info.requires[0] } : {},
15936
- ...KEY_URLS[id] ? { keyUrl: KEY_URLS[id] } : {},
15937
- configured: missingRequirements(id, environment).length === 0
15938
- };
15939
- });
15940
- return "error" in resolved ? { configured: false, reason: resolved.error, providers } : { configured: true, providers };
15941
- }
15942
- async function saveSetup(params, deps) {
15943
- const provider = typeof params.provider === "string" ? params.provider : "";
15944
- const raw = typeof params.key === "string" ? params.key.trim() : "";
15945
- if (!isProviderId(provider))
15946
- return { ok: false, error: `Unknown provider "${provider}".` };
15947
- const target = setupStatus(deps.environment).providers.find((candidate) => candidate.id === provider);
15948
- if (!target?.keyVariable)
15949
- return { ok: false, error: `${PROVIDER_INFO[provider].label} needs more than a key; run archymedes settings.` };
15950
- if (!raw)
15951
- return { ok: false, error: "The key is empty." };
15952
- let key;
15953
- try {
15954
- key = validateSetting(target.keyVariable, raw);
15955
- } catch (error) {
15956
- return { ok: false, error: error instanceof Error ? error.message : String(error) };
15957
- }
15958
- let note;
15959
- if (provider === "free" || provider === "openrouter") {
15960
- const result = await (deps.checkOpenRouter ?? ((value) => checkOpenRouterKey(value)))(key);
15961
- if (!result.ok && result.reason === "invalid")
15962
- return { ok: false, error: describeKeyCheckFailure(result) };
15963
- if (!result.ok)
15964
- note = `${describeKeyCheckFailure(result)} Saved anyway; the first request will confirm it.`;
15965
- }
15966
- const current = await (deps.load ?? (() => loadSettings(deps.processEnvironment)))();
15967
- const settings = { ...current, [target.keyVariable]: key, ARCHYMEDES_PROVIDER: provider };
15968
- const file = await (deps.save ?? ((value) => saveSettings(value, deps.processEnvironment)))(settings);
15969
- Object.assign(deps.environment, mergedEnvironment(settings, deps.processEnvironment));
15970
- return { ok: true, file, ...note ? { note } : {} };
15971
- }
15972
-
15973
- // packages/core/src/providers/model-fetch.ts
15457
+ // ../core/src/providers/model-fetch.ts
15974
15458
  import { promises as fs16 } from "node:fs";
15975
15459
  import path21 from "node:path";
15976
15460
 
15977
- // packages/core/src/providers/model-list.ts
15461
+ // ../core/src/providers/model-list.ts
15978
15462
  function modelsUrl(base) {
15979
15463
  const trimmed = base.replace(/\/+$/, "");
15980
15464
  return trimmed.endsWith("/v1") ? `${trimmed}/models` : `${trimmed}/v1/models`;
@@ -16084,7 +15568,7 @@ function mergeModelLists(known, live) {
16084
15568
  const added = live.filter((model) => !seen.has(model)).sort();
16085
15569
  return [...known, ...added];
16086
15570
  }
16087
- // packages/core/src/providers/model-fetch.ts
15571
+ // ../core/src/providers/model-fetch.ts
16088
15572
  async function fetchProviderModels(provider, environment, fetchImpl, timeoutMs = 4000) {
16089
15573
  if (provider === "free") {
16090
15574
  try {
@@ -16172,7 +15656,206 @@ function fetchableProviders(environment, providers) {
16172
15656
  return providers.filter((provider) => provider !== "archymedes-cloud" && missingRequirements(provider, environment).length === 0);
16173
15657
  }
16174
15658
 
16175
- // packages/archymedes-cli/src/session/models.ts
15659
+ // src/app/acp-connect.ts
15660
+ var DEFAULT_BASE_URLS = {
15661
+ anthropic: "https://api.anthropic.com",
15662
+ openai: "https://api.openai.com/v1",
15663
+ openrouter: "https://openrouter.ai/api/v1",
15664
+ google: "https://generativelanguage.googleapis.com/v1beta/openai",
15665
+ xai: "https://api.x.ai/v1",
15666
+ deepseek: "https://api.deepseek.com/v1",
15667
+ mistral: "https://api.mistral.ai/v1",
15668
+ groq: "https://api.groq.com/openai/v1",
15669
+ ollama: "http://localhost:11434/v1"
15670
+ };
15671
+ var fieldSpec = (key) => SETTING_FIELDS.find((field) => field.key === key);
15672
+ function providerFields(provider, environment) {
15673
+ const required = PROVIDER_INFO[provider].requires;
15674
+ const baseUrl = provider === "free" ? undefined : `${providerEnvPrefix(provider)}_BASE_URL`;
15675
+ const keys = [...required, ...baseUrl && !required.includes(baseUrl) && fieldSpec(baseUrl) ? [baseUrl] : []];
15676
+ return keys.flatMap((key) => {
15677
+ const spec = fieldSpec(key);
15678
+ if (!spec)
15679
+ return [];
15680
+ const secret = "secret" in spec && spec.secret === true;
15681
+ const url = "url" in spec && spec.url === true;
15682
+ const value = environment[key]?.trim() ?? "";
15683
+ const placeholder = url ? DEFAULT_BASE_URLS[provider] ?? "https://…/v1" : undefined;
15684
+ return [{
15685
+ key: spec.key,
15686
+ label: spec.label,
15687
+ kind: secret ? "secret" : url ? "url" : "text",
15688
+ required: required.includes(key),
15689
+ isSet: value !== "",
15690
+ value: secret && value ? maskSetting(value) : value,
15691
+ ...placeholder ? { placeholder } : {}
15692
+ }];
15693
+ });
15694
+ }
15695
+ var refused = (error) => /\b40[13]\b/.test(error ?? "");
15696
+ async function connectProvider(params, deps) {
15697
+ const provider = typeof params.provider === "string" ? params.provider : "";
15698
+ if (!isProviderId(provider))
15699
+ return { ok: false, error: `Unknown provider "${provider}".` };
15700
+ const label3 = PROVIDER_INFO[provider].label;
15701
+ const values = params.values && typeof params.values === "object" ? params.values : {};
15702
+ const fields = providerFields(provider, deps.environment);
15703
+ const current = await (deps.load ?? (() => loadSettings(deps.processEnvironment)))();
15704
+ const next = { ...current };
15705
+ for (const field of fields) {
15706
+ const raw = typeof values[field.key] === "string" ? values[field.key].trim() : undefined;
15707
+ if (raw === undefined || raw === "") {
15708
+ if (field.kind === "url" && raw === "")
15709
+ delete next[field.key];
15710
+ continue;
15711
+ }
15712
+ try {
15713
+ next[field.key] = validateSetting(field.key, raw);
15714
+ } catch (error) {
15715
+ return { ok: false, field: field.key, error: error instanceof Error ? error.message : String(error) };
15716
+ }
15717
+ }
15718
+ const candidate = mergedEnvironment(next, deps.processEnvironment);
15719
+ const missing = missingRequirements(provider, candidate);
15720
+ if (missing.length > 0)
15721
+ return { ok: false, field: missing[0], error: `${label3} still needs ${missing.join(" and ")}.` };
15722
+ const keyField = fields.find((field) => field.kind === "secret")?.key;
15723
+ let note;
15724
+ if ((provider === "free" || provider === "openrouter") && candidate.OPENROUTER_API_KEY) {
15725
+ const check = await (deps.checkOpenRouter ?? ((value) => checkOpenRouterKey(value)))(candidate.OPENROUTER_API_KEY);
15726
+ if (!check.ok && check.reason === "invalid")
15727
+ return { ok: false, field: keyField, error: describeKeyCheckFailure(check) };
15728
+ if (!check.ok)
15729
+ note = `${describeKeyCheckFailure(check)} Saved anyway; the first request will confirm it.`;
15730
+ }
15731
+ const fetchImpl = deps.fetchImpl ?? globalThis.fetch;
15732
+ const listed = await fetchProviderModels(provider, candidate, fetchImpl, 8000);
15733
+ if (listed.models.length === 0 && refused(listed.error)) {
15734
+ return { ok: false, field: keyField, error: `${label3} refused the key (${listed.error}). Check it and paste it again.` };
15735
+ }
15736
+ if (listed.models.length === 0) {
15737
+ note = note ?? `Could not list ${label3}'s models (${listed.error ?? "none returned"}). Saved; check the base URL if this persists.`;
15738
+ }
15739
+ const file = await (deps.save ?? ((value) => saveSettings(value, deps.processEnvironment)))(next);
15740
+ Object.assign(deps.environment, mergedEnvironment(next, deps.processEnvironment));
15741
+ if (listed.models.length > 0)
15742
+ await rememberModels(provider, listed.models, deps.environment);
15743
+ return { ok: true, file, models: listed.models, ...note ? { note } : {} };
15744
+ }
15745
+ async function providerModels(params, deps) {
15746
+ const provider = typeof params.provider === "string" ? params.provider : "";
15747
+ if (!isProviderId(provider))
15748
+ return { models: [], error: `Unknown provider "${provider}".` };
15749
+ const fetchImpl = deps.fetchImpl ?? globalThis.fetch;
15750
+ const listed = await fetchProviderModels(provider, deps.environment, fetchImpl, 8000);
15751
+ if (listed.models.length > 0)
15752
+ await rememberModels(provider, listed.models, deps.environment);
15753
+ return { models: listed.models, ...listed.error ? { error: listed.error } : {} };
15754
+ }
15755
+ async function rememberModels(provider, models, environment) {
15756
+ try {
15757
+ const cache = await readModelCache(environment);
15758
+ await writeModelCache(environment, {
15759
+ fetchedAt: cache?.fetchedAt ?? Date.now(),
15760
+ ...cache?.freeCatalog ? { freeCatalog: cache.freeCatalog } : {},
15761
+ models: { ...cache?.models ?? {}, [provider]: models }
15762
+ });
15763
+ } catch {}
15764
+ }
15765
+ async function rememberModel(params, deps) {
15766
+ const provider = typeof params.provider === "string" ? params.provider : "";
15767
+ const model = typeof params.model === "string" ? params.model.trim() : "";
15768
+ if (!isProviderId(provider) || !model)
15769
+ return { ok: false, error: "A provider and a model are needed." };
15770
+ const key = `${providerEnvPrefix(provider)}_MODEL`;
15771
+ if (!fieldSpec(key))
15772
+ return { ok: true };
15773
+ let value;
15774
+ try {
15775
+ value = validateSetting(key, model);
15776
+ } catch (error) {
15777
+ return { ok: false, error: error instanceof Error ? error.message : String(error) };
15778
+ }
15779
+ const current = await (deps.load ?? (() => loadSettings(deps.processEnvironment)))();
15780
+ const next = { ...current, [key]: value };
15781
+ await (deps.save ?? ((settings) => saveSettings(settings, deps.processEnvironment)))(next);
15782
+ Object.assign(deps.environment, mergedEnvironment(next, deps.processEnvironment));
15783
+ return { ok: true };
15784
+ }
15785
+
15786
+ // src/app/acp-setup.ts
15787
+ var KEY_URLS = {
15788
+ free: OPENROUTER_KEYS_URL2,
15789
+ openrouter: OPENROUTER_KEYS_URL2,
15790
+ anthropic: "https://console.anthropic.com/settings/keys",
15791
+ openai: "https://platform.openai.com/api-keys",
15792
+ google: "https://aistudio.google.com/apikey",
15793
+ xai: "https://console.x.ai",
15794
+ deepseek: "https://platform.deepseek.com/api_keys",
15795
+ mistral: "https://console.mistral.ai/api-keys",
15796
+ groq: "https://console.groq.com/keys"
15797
+ };
15798
+ var ORDER = ["free", ...PROVIDER_IDS.filter((id) => id !== "free")];
15799
+ function setupStatus(environment, requested = {}) {
15800
+ const resolved = resolveProvider(environment, requested);
15801
+ const providers = ORDER.map((id) => {
15802
+ const info = PROVIDER_INFO[id];
15803
+ const keyOnly = info.requires.length === 1 && SETTING_FIELDS.some((field) => field.key === info.requires[0]);
15804
+ return {
15805
+ id,
15806
+ label: info.label,
15807
+ ...keyOnly ? { keyVariable: info.requires[0] } : {},
15808
+ ...KEY_URLS[id] ? { keyUrl: KEY_URLS[id] } : {},
15809
+ configured: missingRequirements(id, environment).length === 0
15810
+ };
15811
+ });
15812
+ return "error" in resolved ? { configured: false, reason: resolved.error, providers } : { configured: true, providers };
15813
+ }
15814
+ async function saveSetup(params, deps) {
15815
+ const provider = typeof params.provider === "string" ? params.provider : "";
15816
+ const raw = typeof params.key === "string" ? params.key.trim() : "";
15817
+ if (!isProviderId(provider))
15818
+ return { ok: false, error: `Unknown provider "${provider}".` };
15819
+ const target = setupStatus(deps.environment).providers.find((candidate) => candidate.id === provider);
15820
+ if (!target?.keyVariable)
15821
+ return { ok: false, error: `${PROVIDER_INFO[provider].label} needs more than a key; run archymedes settings.` };
15822
+ if (!raw)
15823
+ return { ok: false, error: "The key is empty." };
15824
+ let key;
15825
+ try {
15826
+ key = validateSetting(target.keyVariable, raw);
15827
+ } catch (error) {
15828
+ return { ok: false, error: error instanceof Error ? error.message : String(error) };
15829
+ }
15830
+ let note;
15831
+ if (provider === "free" || provider === "openrouter") {
15832
+ const result = await (deps.checkOpenRouter ?? ((value) => checkOpenRouterKey(value)))(key);
15833
+ if (!result.ok && result.reason === "invalid")
15834
+ return { ok: false, error: describeKeyCheckFailure(result) };
15835
+ if (!result.ok)
15836
+ note = `${describeKeyCheckFailure(result)} Saved anyway; the first request will confirm it.`;
15837
+ }
15838
+ const current = await (deps.load ?? (() => loadSettings(deps.processEnvironment)))();
15839
+ const settings = { ...current, [target.keyVariable]: key, ARCHYMEDES_PROVIDER: provider };
15840
+ const file = await (deps.save ?? ((value) => saveSettings(value, deps.processEnvironment)))(settings);
15841
+ Object.assign(deps.environment, mergedEnvironment(settings, deps.processEnvironment));
15842
+ return { ok: true, file, ...note ? { note } : {} };
15843
+ }
15844
+ function providerOverview(environment) {
15845
+ const preferred = environment.ARCHYMEDES_PROVIDER?.trim().toLowerCase();
15846
+ return setupStatus(environment).providers.map((provider) => {
15847
+ const info = PROVIDER_INFO[provider.id];
15848
+ return {
15849
+ ...provider,
15850
+ missing: missingRequirements(provider.id, environment),
15851
+ model: environment[`${providerEnvPrefix(provider.id)}_MODEL`]?.trim() || info.defaultModel,
15852
+ isDefault: preferred === provider.id,
15853
+ fields: providerFields(provider.id, environment)
15854
+ };
15855
+ });
15856
+ }
15857
+
15858
+ // src/session/models.ts
16176
15859
  function modelsForProvider(provider, asOf) {
16177
15860
  const seen = new Set;
16178
15861
  for (const record2 of PRICE_CATALOG) {
@@ -16196,7 +15879,7 @@ function describePrice(prices, display, convert2) {
16196
15879
  return `${formatMoney(input)}/${formatMoney(output)} per Mtok`;
16197
15880
  }
16198
15881
 
16199
- // packages/archymedes-cli/src/app/acp-models.ts
15882
+ // src/app/acp-models.ts
16200
15883
  var PUBLIC_LISTINGS = ["free", "openrouter"];
16201
15884
  async function modelList(environment, params, options = {}) {
16202
15885
  const offline = /^(1|true|on|yes)$/i.test(environment.ARCHYMEDES_MODELS_OFFLINE?.trim() ?? "");
@@ -16239,7 +15922,8 @@ async function modelList(environment, params, options = {}) {
16239
15922
  price: free ? "free" : prices ? describePrice(prices, prices.currency, (value) => value) : "unpriced",
16240
15923
  free,
16241
15924
  locked: missing.length > 0,
16242
- missing
15925
+ missing,
15926
+ small: isSmallModel(model)
16243
15927
  };
16244
15928
  (choice.locked ? locked : ready).push(choice);
16245
15929
  }
@@ -16247,7 +15931,7 @@ async function modelList(environment, params, options = {}) {
16247
15931
  return { choices: [...ready, ...locked], unconfigured, errors, providers: PROVIDER_IDS.length };
16248
15932
  }
16249
15933
 
16250
- // packages/archymedes-cli/src/app/acp-commands.ts
15934
+ // src/app/acp-commands.ts
16251
15935
  async function runSessionCommand(command, argument, session) {
16252
15936
  switch (command) {
16253
15937
  case "cost":
@@ -16272,15 +15956,115 @@ async function runSessionCommand(command, argument, session) {
16272
15956
  const items = session.agent.todos.map((item) => ({ text: item.text, status: item.status }));
16273
15957
  return items.length > 0 ? { kind: "todos", items } : { kind: "text", text: "No plan yet." };
16274
15958
  }
15959
+ case "memory":
15960
+ return memoryCommand(argument, session.agent.workspaceLabel);
15961
+ case "tools": {
15962
+ const { tools } = await session.agent.inspectTools();
15963
+ const lines = tools.map((tool) => `${tool.name.padEnd(24)} ${tool.requiresApproval ? "asks first " : " "}${tool.description.split(/(?<=\.)\s/)[0]}`);
15964
+ return { kind: "text", text: `${tools.length} tools
15965
+ ${lines.join(`
15966
+ `)}` };
15967
+ }
16275
15968
  default:
16276
15969
  return { kind: "error", text: `Unknown session command "${command}".` };
16277
15970
  }
16278
15971
  }
15972
+ var MEMORY_KINDS = ["preference", "convention", "decision", "lesson", "fact"];
15973
+ async function memoryCommand(argument, root) {
15974
+ const environment = process.env;
15975
+ const [verb = "", ...rest] = argument.trim().split(/\s+/);
15976
+ try {
15977
+ if (verb === "" || verb === "list") {
15978
+ const entries2 = await loadMemories(root, environment);
15979
+ if (entries2.length === 0)
15980
+ return { kind: "text", text: "Nothing remembered yet. /memory add <text> saves a fact for this project." };
15981
+ return { kind: "text", text: entries2.map((entry) => `${entry.scope === "user" ? "user " : "project"} ${entry.index}. [${entry.kind}${entry.pinned ? ", pinned" : ""}] ${entry.text}`).join(`
15982
+ `) };
15983
+ }
15984
+ if (verb === "add") {
15985
+ const scope = rest[0] === "user" ? "user" : "project";
15986
+ if (rest[0] === "user" || rest[0] === "project")
15987
+ rest.shift();
15988
+ const kind = MEMORY_KINDS.find((candidate) => candidate === rest[0]);
15989
+ if (kind)
15990
+ rest.shift();
15991
+ const change = await addMemory(scope, rest.join(" "), root, environment, kind ? { kind } : {});
15992
+ return { kind: "text", text: change.changed ? `Remembered (${scope}).` : "Already remembered, or nothing to add." };
15993
+ }
15994
+ if (verb === "forget") {
15995
+ const scope = rest[0] === "user" ? "user" : "project";
15996
+ const index = Number(rest[rest.length - 1]);
15997
+ if (!Number.isInteger(index))
15998
+ return { kind: "error", text: "/memory forget [user] <number> — see /memory for the numbers." };
15999
+ const change = await forgetMemory(scope, index, root, environment);
16000
+ return { kind: "text", text: change.removed ? `Forgot: ${change.removed.text}` : `No ${scope} memory numbered ${index}.` };
16001
+ }
16002
+ if (verb === "clear") {
16003
+ const scope = rest[0] === "user" ? "user" : "project";
16004
+ return { kind: "text", text: (await clearMemories(scope, root, environment)).changed ? `Cleared ${scope} memory.` : `${scope} memory was already empty.` };
16005
+ }
16006
+ return { kind: "error", text: `/memory takes list, add <text>, forget <n> or clear — not "${verb}".` };
16007
+ } catch (error) {
16008
+ return { kind: "error", text: error instanceof Error ? error.message : String(error) };
16009
+ }
16010
+ }
16279
16011
 
16280
- // packages/archymedes-cli/src/app/startup-update.ts
16012
+ // src/app/acp-settings.ts
16013
+ async function listSettings(processEnvironment) {
16014
+ const stored = await loadSettings(processEnvironment);
16015
+ const rows = SETTING_FIELDS.map((field) => {
16016
+ const secret = "secret" in field && field.secret === true;
16017
+ const choices = "choices" in field ? field.choices : undefined;
16018
+ const value = stored[field.key] ?? "";
16019
+ return {
16020
+ key: field.key,
16021
+ label: field.label,
16022
+ section: sectionOf(field.key) ?? "General",
16023
+ kind: secret ? "secret" : choices ? "choice" : "text",
16024
+ ...choices ? { choices: choices.map((choice) => ({ value: choice.value, label: choice.label })) } : {},
16025
+ value: secret && value ? maskSetting(value) : value,
16026
+ isSet: value !== "",
16027
+ overriddenByEnvironment: Boolean(processEnvironment[field.key]) && processEnvironment[field.key] !== value
16028
+ };
16029
+ });
16030
+ return { file: settingsFile(processEnvironment), rows };
16031
+ }
16032
+ async function setSetting(params, deps) {
16033
+ const key = typeof params.key === "string" ? params.key : "";
16034
+ const raw = typeof params.value === "string" ? params.value : "";
16035
+ const field = SETTING_FIELDS.find((candidate) => candidate.key === key);
16036
+ if (!field)
16037
+ return { ok: false, error: `No setting named ${key || "(none)"}.` };
16038
+ const current = await loadSettings(deps.processEnvironment);
16039
+ const next = { ...current };
16040
+ let stored = "";
16041
+ if (raw.trim() === "") {
16042
+ delete next[field.key];
16043
+ } else {
16044
+ try {
16045
+ stored = validateSetting(field.key, raw);
16046
+ } catch (error) {
16047
+ return { ok: false, error: error instanceof Error ? error.message : String(error) };
16048
+ }
16049
+ next[field.key] = stored;
16050
+ }
16051
+ let file;
16052
+ try {
16053
+ file = await saveSettings(next, deps.processEnvironment);
16054
+ } catch (error) {
16055
+ return { ok: false, error: `Could not save: ${error instanceof Error ? error.message : String(error)}` };
16056
+ }
16057
+ if (raw.trim() === "" && deps.environment[field.key] === current[field.key])
16058
+ delete deps.environment[field.key];
16059
+ Object.assign(deps.environment, mergedEnvironment(next, deps.processEnvironment));
16060
+ const secret = "secret" in field && field.secret === true;
16061
+ return { ok: true, file, value: secret && stored ? maskSetting(stored) : stored, isSet: stored !== "" };
16062
+ }
16063
+
16064
+ // src/app/startup-update.ts
16281
16065
  import { spawn as spawn8 } from "node:child_process";
16282
16066
 
16283
- // packages/archymedes-cli/src/platform/auto-update.ts
16067
+ // src/platform/auto-update.ts
16284
16068
  import { promises as fs17 } from "node:fs";
16285
16069
  import path22 from "node:path";
16286
16070
  var AUTO_UPDATE_INTERVAL_MS = 24 * 60 * 60 * 1000;
@@ -16393,7 +16177,7 @@ async function runAutoUpdate(options) {
16393
16177
  return { notice: [], decision };
16394
16178
  }
16395
16179
 
16396
- // packages/archymedes-cli/src/platform/defender-feed-update.ts
16180
+ // src/platform/defender-feed-update.ts
16397
16181
  var OFFICIAL_DEFENDER_FEED_URL = "https://circuit-archymedes.vercel.app/api/defender-brain/manifest";
16398
16182
  var OFFICIAL_DEFENDER_FEED_KEYS = Object.freeze({
16399
16183
  "release-2026-01": "MCowBQYDK2VwAyEA4/jb1zd6f+jIPAFja1bPNtroXV8MtAZFrKt5BSY+ngI="
@@ -16421,12 +16205,12 @@ async function updateDefenderFeed(environment) {
16421
16205
  });
16422
16206
  }
16423
16207
 
16424
- // packages/archymedes-cli/src/platform/update.ts
16208
+ // src/platform/update.ts
16425
16209
  import { spawnSync as spawnSync3 } from "node:child_process";
16426
16210
  import { tmpdir } from "node:os";
16427
16211
  import { createInterface as createInterface3 } from "node:readline/promises";
16428
16212
 
16429
- // packages/archymedes-cli/src/catalog/commands.ts
16213
+ // src/catalog/commands.ts
16430
16214
  function defineCommands(table) {
16431
16215
  return Object.entries(table).map(([name, command]) => ({ name, ...command }));
16432
16216
  }
@@ -16499,7 +16283,7 @@ function renderCommandHelp(language = "en", shortcuts = new Map, options = {}) {
16499
16283
  `);
16500
16284
  }
16501
16285
 
16502
- // packages/archymedes-cli/src/terminal/keybindings.ts
16286
+ // src/terminal/keybindings.ts
16503
16287
  var FUNCTION_KEY = /^f([1-9]|1[0-2])$/;
16504
16288
  var NAMED_KEYS = new Set(["tab", "enter", "escape", "space", "up", "down", "left", "right", "home", "end", "pageup", "pagedown", "backspace", "delete"]);
16505
16289
  function isKeyName(value) {
@@ -16749,10 +16533,10 @@ function escapeCodeTimeoutMs(environment = process.env) {
16749
16533
  const parsed = raw ? Number(raw) : NaN;
16750
16534
  return Number.isInteger(parsed) && parsed >= 10 && parsed <= 2000 ? parsed : DEFAULT_ESCAPE_CODE_TIMEOUT_MS;
16751
16535
  }
16752
- // packages/archymedes-cli/package.json
16536
+ // package.json
16753
16537
  var package_default = {
16754
16538
  name: "archymedes-cli",
16755
- version: "3.2.0",
16539
+ version: "3.4.0",
16756
16540
  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.",
16757
16541
  keywords: [
16758
16542
  "cli",
@@ -16798,12 +16582,12 @@ var package_default = {
16798
16582
  prepublishOnly: "bun run ../../tooling/build/build-packages.ts"
16799
16583
  },
16800
16584
  optionalDependencies: {
16801
- "@archymedes/cli-darwin-arm64": "3.2.0",
16802
- "@archymedes/cli-darwin-x64": "3.2.0",
16803
- "@archymedes/cli-linux-arm64": "3.2.0",
16804
- "@archymedes/cli-linux-x64": "3.2.0",
16805
- "@archymedes/cli-win32-arm64": "3.2.0",
16806
- "@archymedes/cli-win32-x64": "3.2.0",
16585
+ "@archymedes/cli-darwin-arm64": "3.4.0",
16586
+ "@archymedes/cli-darwin-x64": "3.4.0",
16587
+ "@archymedes/cli-linux-arm64": "3.4.0",
16588
+ "@archymedes/cli-linux-x64": "3.4.0",
16589
+ "@archymedes/cli-win32-arm64": "3.4.0",
16590
+ "@archymedes/cli-win32-x64": "3.4.0",
16807
16591
  "@archymedes/state-darwin-arm64": "0.4.0",
16808
16592
  "@archymedes/state-darwin-x64": "0.4.0",
16809
16593
  "@archymedes/state-linux-arm64-gnu": "0.4.0",
@@ -16815,7 +16599,7 @@ var package_default = {
16815
16599
  }
16816
16600
  };
16817
16601
 
16818
- // packages/archymedes-cli/src/platform/update.ts
16602
+ // src/platform/update.ts
16819
16603
  var ARCHYMEDES_CLI_PACKAGE = "archymedes-cli";
16820
16604
  var ARCHYMEDES_CLI_VERSION = package_default.version;
16821
16605
  var DEFAULT_REGISTRY = "https://registry.npmjs.org";
@@ -17033,7 +16817,7 @@ async function runSelfUpdate(options = {}) {
17033
16817
  return { status: "updated", currentVersion, latestVersion, code: 0 };
17034
16818
  }
17035
16819
 
17036
- // packages/archymedes-cli/src/text/ansi.ts
16820
+ // src/text/ansi.ts
17037
16821
  var RESET = "\x1B[0m";
17038
16822
  var BOLD = "\x1B[1m";
17039
16823
  var DIM = "\x1B[2m";
@@ -17046,7 +16830,7 @@ function paintAll(text, codes, depth) {
17046
16830
  return depth === "none" || codes.length === 0 ? text : `${codes.join("")}${text}${RESET}`;
17047
16831
  }
17048
16832
 
17049
- // packages/archymedes-cli/src/terminal/output.ts
16833
+ // src/terminal/output.ts
17050
16834
  var terminalStream = {
17051
16835
  write(text) {
17052
16836
  process.stdout.write(text);
@@ -17080,7 +16864,7 @@ class OutputRouter {
17080
16864
  }
17081
16865
  }
17082
16866
 
17083
- // packages/archymedes-cli/src/text/glyphs.ts
16867
+ // src/text/glyphs.ts
17084
16868
  var UNICODE_GLYPHS = {
17085
16869
  starGlyphs: [".", "·", "✧", "✦", "✶"],
17086
16870
  spinnerFrames: [" ◜ ", " ◠ ", " ◝ ", " ◞ ", " ◡ ", " ◟ "],
@@ -17187,7 +16971,7 @@ function borderGlyphsFor(style, glyphs) {
17187
16971
  return ROUND_BORDER;
17188
16972
  }
17189
16973
 
17190
- // packages/archymedes-cli/src/text/text-width.ts
16974
+ // src/text/text-width.ts
17191
16975
  var ANSI = /\x1b\[[0-9;]*m/g;
17192
16976
  var PRINTABLE_ASCII = /^[\x20-\x7e]*$/;
17193
16977
  function visibleWidth(text) {
@@ -17222,7 +17006,7 @@ function clipTo(text, width, glyphs = UNICODE_GLYPHS) {
17222
17006
  return `${out}${mark}`;
17223
17007
  }
17224
17008
 
17225
- // packages/archymedes-cli/src/render/syntax.ts
17009
+ // src/render/syntax.ts
17226
17010
  function newSyntaxState() {
17227
17011
  return {};
17228
17012
  }
@@ -17869,7 +17653,7 @@ function highlightSyntax(line, options) {
17869
17653
  return `${out}${RESET}`;
17870
17654
  }
17871
17655
 
17872
- // packages/archymedes-cli/src/render/markdown.ts
17656
+ // src/render/markdown.ts
17873
17657
  var colour = (role, palette) => palette?.[role] || ANSI_PALETTE[role];
17874
17658
  var INLINE_PATTERN = /(?<!\\)(`[^`\n]+`)|(?<!\\)(\*\*[^*\n]+\*\*)|(?<!\\)(__[^_\n]+__)|(?<!\\)(~~[^~\n]+~~)|(?<!\\)(\*[^*\n]+\*)|(?<!\\)(_[^_\n]+_)|(!?\[([^\]\n]+)\]\((https?:\/\/[^\s)\n]+)\))|(<(https?:\/\/[^>\s]+)>)/g;
17875
17659
  var unescapeMarkdown = (text) => text.replace(/\\([\\`*_[\]{}()#+.!~>-])/g, "$1");
@@ -18090,7 +17874,7 @@ function renderMarkdown(text, options) {
18090
17874
  `);
18091
17875
  }
18092
17876
 
18093
- // packages/archymedes-cli/src/render/tui.ts
17877
+ // src/render/tui.ts
18094
17878
  function archymedesSpinnerFrame(index, glyphs = UNICODE_GLYPHS) {
18095
17879
  const frames = glyphs.spinnerFrames;
18096
17880
  const normalized = Number.isFinite(index) ? Math.max(0, Math.floor(index)) : 0;
@@ -18678,7 +18462,7 @@ function wrapPlain(text, width) {
18678
18462
  return lines.length === 0 ? [""] : lines;
18679
18463
  }
18680
18464
 
18681
- // packages/archymedes-cli/src/render/transcript.ts
18465
+ // src/render/transcript.ts
18682
18466
  function truncateMiddle(value, maximum) {
18683
18467
  const clean = value.replace(/\s+/g, " ").trim();
18684
18468
  if (clean.length <= maximum)
@@ -18841,7 +18625,7 @@ function summarizeToolResult(toolName, content, isError) {
18841
18625
  }
18842
18626
  }
18843
18627
 
18844
- // packages/archymedes-cli/src/render/sections.ts
18628
+ // src/render/sections.ts
18845
18629
  var TONE_ROLE = {
18846
18630
  good: "success",
18847
18631
  bad: "error",
@@ -18968,7 +18752,7 @@ function outcomeMark(outcome, style) {
18968
18752
  }
18969
18753
  }
18970
18754
 
18971
- // packages/archymedes-cli/src/render/code-view.ts
18755
+ // src/render/code-view.ts
18972
18756
  var MAX_DIFF_CELLS = 250000;
18973
18757
  function diffLines(before, after) {
18974
18758
  const left = before === "" ? [] : before.split(`
@@ -19265,7 +19049,7 @@ function renderFileChange(change, style, options = {}) {
19265
19049
  };
19266
19050
  }
19267
19051
 
19268
- // packages/archymedes-cli/src/render/test-report.ts
19052
+ // src/render/test-report.ts
19269
19053
  function toMs(value) {
19270
19054
  if (!value)
19271
19055
  return;
@@ -19540,7 +19324,7 @@ function renderTestReport(report, style, options = {}) {
19540
19324
  `);
19541
19325
  }
19542
19326
 
19543
- // packages/archymedes-cli/src/render/expandable.ts
19327
+ // src/render/expandable.ts
19544
19328
  var MAX_ENTRIES = 64;
19545
19329
 
19546
19330
  class ExpandableStore {
@@ -19574,7 +19358,7 @@ function expandHint(id, hidden, depth, glyphs = UNICODE_GLYPHS) {
19574
19358
  return paint(`${glyphs.collapsed} ${hidden} more line${hidden === 1 ? "" : "s"} hidden ${glyphs.middot} /expand ${id}`, DIM, depth);
19575
19359
  }
19576
19360
 
19577
- // packages/archymedes-cli/src/app/transcript.ts
19361
+ // src/app/transcript.ts
19578
19362
  var colorEnabled = false;
19579
19363
  var wrap = (code) => (value) => colorEnabled ? `${code}${value}${RESET}` : value;
19580
19364
  var palette = NO_COLOR_PALETTE;
@@ -19654,7 +19438,7 @@ function beginTranscriptTurn() {
19654
19438
  markdown.reset();
19655
19439
  }
19656
19440
 
19657
- // packages/archymedes-cli/src/app/startup-update.ts
19441
+ // src/app/startup-update.ts
19658
19442
  async function startupMaintenance(environment, interactive, install) {
19659
19443
  updateDefenderFeed(environment).catch(() => {
19660
19444
  return;
@@ -19693,7 +19477,7 @@ function installInChildProcess(entry, environment) {
19693
19477
  });
19694
19478
  }
19695
19479
 
19696
- // packages/archymedes-cli/src/app/jev.ts
19480
+ // src/app/jev.ts
19697
19481
  function jevOptionsFromEnvironment(environment) {
19698
19482
  if ((environment.ARCHYMEDES_JEV ?? "").trim().toLowerCase() === "off")
19699
19483
  return;
@@ -19704,7 +19488,7 @@ function jevOptionsFromEnvironment(environment) {
19704
19488
  return { apiKey, ...model ? { model } : {}, ...environment.ARCHYMEDES_JEV_REVIEW?.trim().toLowerCase() === "off" ? { review: false } : {} };
19705
19489
  }
19706
19490
 
19707
- // packages/core/src/providers/exa.ts
19491
+ // ../core/src/providers/exa.ts
19708
19492
  var LATENCY_CEILING = {
19709
19493
  instant: 5000,
19710
19494
  fast: 1e4,
@@ -19910,7 +19694,7 @@ function createExaClient(environment, fetchImpl) {
19910
19694
  });
19911
19695
  }
19912
19696
 
19913
- // packages/archymedes-cli/src/acp-server.ts
19697
+ // src/acp-server.ts
19914
19698
  function splitFrames(buffer) {
19915
19699
  const parts = buffer.split(`
19916
19700
  `);
@@ -19926,8 +19710,14 @@ async function runAcpServer(options) {
19926
19710
  const connection = new AcpConnection({
19927
19711
  send,
19928
19712
  extensions: {
19713
+ "_archymedes/providers": async () => ({ providers: providerOverview(options.environment) }),
19714
+ "_archymedes/providers/connect": async (params) => connectProvider(params, { environment: options.environment, processEnvironment: process.env }),
19715
+ "_archymedes/providers/models": async (params) => providerModels(params, { environment: options.environment }),
19716
+ "_archymedes/providers/remember-model": async (params) => rememberModel(params, { environment: options.environment, processEnvironment: process.env }),
19929
19717
  "_archymedes/setup/status": async () => setupStatus(options.environment, { provider: options.provider, model: options.model }),
19930
19718
  "_archymedes/setup/save": async (params) => saveSetup(params, { environment: options.environment, processEnvironment: process.env }),
19719
+ "_archymedes/settings/list": async () => listSettings(process.env),
19720
+ "_archymedes/settings/set": async (params) => setSetting(params, { environment: options.environment, processEnvironment: process.env }),
19931
19721
  "_archymedes/models": async (params) => modelList(options.environment, params),
19932
19722
  "_archymedes/startup": async () => ({
19933
19723
  notices: await (startup ??= startupMaintenance(options.environment, false, installInChildProcess({ execPath: process.execPath, script: process.argv[1] ?? "" }, options.environment)))
@@ -19948,7 +19738,7 @@ async function runAcpServer(options) {
19948
19738
  const workspace = started.workspace;
19949
19739
  let agent = build(mode);
19950
19740
  function build(currentMode) {
19951
- return new ArchymedesAgent({
19741
+ const built = new ArchymedesAgent({
19952
19742
  root,
19953
19743
  model: resolved.provider,
19954
19744
  prices: priceCatalogFor(resolved.prices),
@@ -19957,9 +19747,12 @@ async function runAcpServer(options) {
19957
19747
  search: createExaClient(options.environment),
19958
19748
  onEvent,
19959
19749
  jev: jevOptionsFromEnvironment(options.environment),
19750
+ ...reasoningFromEnvironment(options.environment) ? { reasoning: reasoningFromEnvironment(options.environment) } : {},
19960
19751
  afterEdit: acpEditDiagnostics(root, workspace, options.environment),
19961
19752
  approve: async (request) => approve({ toolName: request.tool.name, summary: request.summary, toolCallId: request.call.id, ...request.pattern ? { pattern: request.pattern } : {} })
19962
19753
  });
19754
+ built.warmUp();
19755
+ return built;
19963
19756
  }
19964
19757
  async function rebuild(change) {
19965
19758
  const carried = await loadSession(root, agent.sessionId);
@@ -20072,10 +19865,10 @@ function priceCatalogFor(prices) {
20072
19865
  };
20073
19866
  }
20074
19867
 
20075
- // packages/archymedes-cli/src/app/args.ts
19868
+ // src/app/args.ts
20076
19869
  import path23 from "node:path";
20077
19870
 
20078
- // packages/archymedes-cli/src/render/charts.ts
19871
+ // src/render/charts.ts
20079
19872
  function barChart(data, options) {
20080
19873
  if (data.length === 0)
20081
19874
  return [];
@@ -20100,7 +19893,7 @@ function barChart(data, options) {
20100
19893
  });
20101
19894
  }
20102
19895
 
20103
- // packages/archymedes-cli/src/commands/chat-history.ts
19896
+ // src/commands/chat-history.ts
20104
19897
  function countTurns(messages) {
20105
19898
  return messages.filter((message) => message.role === "user" && !message.internal).length;
20106
19899
  }
@@ -20293,7 +20086,7 @@ function parseHistoryCommand(input) {
20293
20086
  return { kind: "invalid", reason: `/history takes "list", a session id, "search <text>", "delete <id>", "status", or "resume" — not "${verb}".` };
20294
20087
  }
20295
20088
 
20296
- // packages/archymedes-cli/src/commands/pacing.ts
20089
+ // src/commands/pacing.ts
20297
20090
  var PACE_PROFILES = {
20298
20091
  off: {
20299
20092
  level: "off",
@@ -20355,7 +20148,7 @@ function paceBadge(level, glyphs2 = UNICODE_GLYPHS) {
20355
20148
  return `${glyphs2.paused} ${profileFor(level).label}`;
20356
20149
  }
20357
20150
 
20358
- // packages/archymedes-cli/src/app/args.ts
20151
+ // src/app/args.ts
20359
20152
  var SESSION_ID2 = /^\d{8}T\d{6}Z-[a-z0-9]{6}$/;
20360
20153
  var DEFAULT_DOCKER_IMAGE = "debian:stable-slim";
20361
20154
  function parseArgs(argv) {
@@ -20553,7 +20346,7 @@ function parseArgs(argv) {
20553
20346
  return parsed;
20554
20347
  }
20555
20348
 
20556
- // packages/archymedes-cli/src/app/session-provider.ts
20349
+ // src/app/session-provider.ts
20557
20350
  async function resolveSessionProvider2(environment, options) {
20558
20351
  if (options.provider || !options.resume)
20559
20352
  return resolveProvider(environment, options);
@@ -20562,7 +20355,7 @@ async function resolveSessionProvider2(environment, options) {
20562
20355
  return resolveProvider(environment, selection?.provider === "free" ? { provider: "free", model: options.model ?? selection.model } : options);
20563
20356
  }
20564
20357
 
20565
- // packages/archymedes-cli/src/app/transcript.ts
20358
+ // src/app/transcript.ts
20566
20359
  var colorEnabled2 = false;
20567
20360
  var liveTerminal2 = false;
20568
20361
  var wrap2 = (code) => (value) => colorEnabled2 ? `${code}${value}${RESET}` : value;
@@ -20876,7 +20669,7 @@ ${rule(sectionStyle2(), { label: "tool activity", tone: "neutral" })}
20876
20669
  }
20877
20670
  }
20878
20671
 
20879
- // packages/core/src/cli/suggestions.ts
20672
+ // ../core/src/cli/suggestions.ts
20880
20673
  var CATEGORY_ORDER = ["blocked", "recovery", "next", "discover", "starter"];
20881
20674
  function defaultSignals(overrides = {}) {
20882
20675
  return {
@@ -21273,7 +21066,7 @@ async function askModelForSuggestions(model, signals, context = {}) {
21273
21066
  }
21274
21067
  }
21275
21068
 
21276
- // packages/archymedes-cli/src/ui/chooser.ts
21069
+ // src/ui/chooser.ts
21277
21070
  var ALL_CHROME = { title: true, status: true, pagination: true, help: true };
21278
21071
  function chromeOf(chrome) {
21279
21072
  return { ...ALL_CHROME, ...chrome };
@@ -21501,7 +21294,7 @@ async function runChooser(keys, items, paint2, options) {
21501
21294
  return;
21502
21295
  }
21503
21296
 
21504
- // packages/archymedes-cli/src/ui/navigation.ts
21297
+ // src/ui/navigation.ts
21505
21298
  class CommandUsage {
21506
21299
  limit;
21507
21300
  order = [];
@@ -21524,8 +21317,8 @@ class CommandUsage {
21524
21317
  }
21525
21318
  }
21526
21319
 
21527
- // packages/core/src/cli/daemon.ts
21528
- import { randomUUID as randomUUID7 } from "node:crypto";
21320
+ // ../core/src/cli/daemon.ts
21321
+ import { randomUUID as randomUUID6 } from "node:crypto";
21529
21322
  var ARCHYMEDES_DAEMON_PROTOCOL_VERSION = 1;
21530
21323
  var PREVIEW_HUNK_SEPARATOR = `
21531
21324
  …
@@ -21571,7 +21364,7 @@ class ArchymedesSessionDaemon {
21571
21364
  sessions = new Map;
21572
21365
  pendingApprovals = new Map;
21573
21366
  connect(options = {}) {
21574
- const id = options.id ?? `client_${randomUUID7()}`;
21367
+ const id = options.id ?? `client_${randomUUID6()}`;
21575
21368
  assertSafeId(id, "Client id");
21576
21369
  if (this.clients.has(id))
21577
21370
  throw new Error(`Daemon client already connected: ${id}`);
@@ -21590,7 +21383,7 @@ class ArchymedesSessionDaemon {
21590
21383
  }
21591
21384
  async requestApproval(sessionId, request) {
21592
21385
  const publicRequest = {
21593
- id: `approval_${randomUUID7()}`,
21386
+ id: `approval_${randomUUID6()}`,
21594
21387
  sessionId,
21595
21388
  summary: request.summary,
21596
21389
  actionDigest: request.actionDigest,
@@ -21863,7 +21656,7 @@ class ArchymedesDaemonClient {
21863
21656
  planRoute(objective, signal) {
21864
21657
  return this.daemon.planRoute(this.id, this.sessionId, objective, signal);
21865
21658
  }
21866
- send(objective, commandId = `turn_${randomUUID7()}`) {
21659
+ send(objective, commandId = `turn_${randomUUID6()}`) {
21867
21660
  return this.daemon.send(this.id, this.sessionId, objective, commandId);
21868
21661
  }
21869
21662
  cancel() {
@@ -21922,7 +21715,7 @@ class ArchymedesDaemonClient {
21922
21715
  }
21923
21716
  }
21924
21717
 
21925
- // packages/core/src/money.ts
21718
+ // ../core/src/money.ts
21926
21719
  var CURRENCIES2 = (() => {
21927
21720
  try {
21928
21721
  return Intl.supportedValuesOf("currency");
@@ -21965,7 +21758,7 @@ function formatMoney2(value) {
21965
21758
  return value.currency === "USD" ? `${format.symbol}${rendered}` : `${format.symbol} ${rendered}`;
21966
21759
  }
21967
21760
 
21968
- // packages/core/src/cli/backends.ts
21761
+ // ../core/src/cli/backends.ts
21969
21762
  import path24 from "node:path";
21970
21763
  var CONFIG_FILE_SCAN_LIMIT2 = 500;
21971
21764
  function localCommandGuidance2(platform) {
@@ -22059,11 +21852,11 @@ class LocalWorkspace2 {
22059
21852
  }
22060
21853
  }
22061
21854
 
22062
- // packages/core/src/cli/cost.ts
21855
+ // ../core/src/cli/cost.ts
22063
21856
  function formatTokenFlow2(usage) {
22064
21857
  return `↑${compactTokenCount(usage.inputTokens)} ↓${compactTokenCount(usage.outputTokens)} tok`;
22065
21858
  }
22066
- var emptyUsage3 = { inputTokens: 0, outputTokens: 0, totalTokens: 0, cachedInputTokens: 0, cacheWriteTokens: 0, reasoningTokens: 0 };
21859
+ var emptyUsage2 = { inputTokens: 0, outputTokens: 0, totalTokens: 0, cachedInputTokens: 0, cacheWriteTokens: 0, reasoningTokens: 0 };
22067
21860
  var CACHE_CHURN_FLOOR_TOKENS2 = 20000;
22068
21861
  var CACHE_CHURN_HINT2 = [
22069
21862
  "The prompt cache is being rewritten more than it is read, which costs more than not caching at all.",
@@ -22151,7 +21944,7 @@ class CostLedger2 {
22151
21944
  cachedInputTokens: total.cachedInputTokens + turn.usage.cachedInputTokens,
22152
21945
  cacheWriteTokens: total.cacheWriteTokens + turn.usage.cacheWriteTokens,
22153
21946
  reasoningTokens: total.reasoningTokens + turn.usage.reasoningTokens
22154
- }), { ...emptyUsage3 });
21947
+ }), { ...emptyUsage2 });
22155
21948
  }
22156
21949
  get history() {
22157
21950
  return [...this.turns];
@@ -22311,7 +22104,7 @@ class CostLedger2 {
22311
22104
  }
22312
22105
  }
22313
22106
 
22314
- // packages/archymedes-cli/src/headless.ts
22107
+ // src/headless.ts
22315
22108
  var HEADLESS_PROTOCOL_VERSION = 1;
22316
22109
  var EXIT_CODES = {
22317
22110
  completed: 0,
@@ -22414,7 +22207,7 @@ class HeadlessEmitter {
22414
22207
  return;
22415
22208
  }
22416
22209
  if (safe.type === "model_turn") {
22417
- this.emit("model_turn", { iteration: safe.iteration, model: safe.model, toolCalls: safe.toolCallCount, usage: safe.usage });
22210
+ this.emit("model_turn", { iteration: safe.iteration, model: safe.model, toolCalls: safe.toolCallCount, usage: safe.usage, ...safe.effort ? { effort: safe.effort, effortReason: safe.effortReason } : {} });
22418
22211
  return;
22419
22212
  }
22420
22213
  if (safe.type === "provider_retry") {
@@ -22437,7 +22230,7 @@ class HeadlessEmitter {
22437
22230
  }
22438
22231
  }
22439
22232
 
22440
- // packages/core/src/providers/price-catalog.ts
22233
+ // ../core/src/providers/price-catalog.ts
22441
22234
  var ANTHROPIC_VERIFIED2 = "2026-06-24";
22442
22235
  var ANTHROPIC_SOURCE2 = "anthropic.com/pricing, recorded 2026-06-24";
22443
22236
  var ANTHROPIC2 = [
@@ -22489,7 +22282,7 @@ var SERVICES2 = [
22489
22282
  ];
22490
22283
  var PRICE_CATALOG2 = definePrices([...ANTHROPIC2, ...OAI_COMPAT_LABS2, ...SERVICES2]);
22491
22284
 
22492
- // packages/archymedes-cli/src/text/color-depth.ts
22285
+ // src/text/color-depth.ts
22493
22286
  function detectColorDepth(environment, isTTY) {
22494
22287
  if (!isTTY || environment.NO_COLOR !== undefined || environment.TERM === "dumb")
22495
22288
  return "none";
@@ -22498,7 +22291,7 @@ function detectColorDepth(environment, isTTY) {
22498
22291
  return "ansi256";
22499
22292
  }
22500
22293
 
22501
- // packages/archymedes-cli/src/terminal/keybindings.ts
22294
+ // src/terminal/keybindings.ts
22502
22295
  var FUNCTION_KEY2 = /^f([1-9]|1[0-2])$/;
22503
22296
  var NAMED_KEYS2 = new Set(["tab", "enter", "escape", "space", "up", "down", "left", "right", "home", "end", "pageup", "pagedown", "backspace", "delete"]);
22504
22297
  function isKeyName2(value) {
@@ -22740,7 +22533,7 @@ function escapeCodeTimeoutMs2(environment = process.env) {
22740
22533
  return Number.isInteger(parsed) && parsed >= 10 && parsed <= 2000 ? parsed : DEFAULT_ESCAPE_CODE_TIMEOUT_MS2;
22741
22534
  }
22742
22535
 
22743
- // packages/archymedes-cli/src/platform/local-currency.ts
22536
+ // src/platform/local-currency.ts
22744
22537
  var COUNTRY_CURRENCIES2 = {
22745
22538
  RW: "RWF",
22746
22539
  EG: "EGP",
@@ -22866,7 +22659,7 @@ function resolveCurrencyPreference(options) {
22866
22659
  }
22867
22660
  var FX_CACHE_MAX_AGE_MS2 = 24 * 60 * 60 * 1000;
22868
22661
 
22869
- // packages/archymedes-cli/src/platform/settings.ts
22662
+ // src/platform/settings.ts
22870
22663
  import { promises as fs18 } from "node:fs";
22871
22664
  import os5 from "node:os";
22872
22665
  import path25 from "node:path";
@@ -22973,6 +22766,13 @@ var AUTOSAVE_CHOICES2 = [
22973
22766
  { value: "off", label: "Off — save with Ctrl+S (default)" },
22974
22767
  { value: "on", label: "On — save a second after you stop typing" }
22975
22768
  ];
22769
+ var REASONING_CHOICES2 = [
22770
+ { value: "auto", label: "Auto (experimental) — lower the effort on routine steps, leave hard ones to the model" },
22771
+ { value: "minimal", label: "Minimal — never think more than needed" },
22772
+ { value: "low", label: "Low" },
22773
+ { value: "medium", label: "Medium" },
22774
+ { value: "high", label: "High — slowest" }
22775
+ ];
22976
22776
  var PROVIDER_CHOICES2 = PROVIDER_IDS.map((id) => ({ value: id, label: PROVIDER_INFO[id].label }));
22977
22777
  var SETTING_FIELDS2 = [
22978
22778
  { key: "ARCHYMEDES_THEME", label: "Theme — colours for the whole CLI (/theme <name> also saves it)", choices: THEME_CHOICES2, section: "Appearance" },
@@ -22980,6 +22780,7 @@ var SETTING_FIELDS2 = [
22980
22780
  { key: "ARCHYMEDES_CODE_LINE_NUMBERS", label: "Line numbers in code blocks", choices: SHOW_HIDE_CHOICES2 },
22981
22781
  { key: "ARCHYMEDES_SIMPLE", label: "Simple mode — quiet start, short help", choices: SIMPLE_CHOICES2 },
22982
22782
  { key: "ARCHYMEDES_LANGUAGE", label: "Control language", choices: LANGUAGE_CHOICES2 },
22783
+ { key: "ARCHYMEDES_REASONING", label: "Reasoning effort — clear it to leave each model at its own default", choices: REASONING_CHOICES2 },
22983
22784
  { key: "ARCHYMEDES_RESUME", label: "Continue your last chat at startup", choices: RESUME_CHOICES2, section: "Behaviour" },
22984
22785
  { key: "ARCHYMEDES_EDITOR_AUTOSAVE", label: "Auto-save in /edit", choices: AUTOSAVE_CHOICES2 },
22985
22786
  { key: "ARCHYMEDES_SUGGEST_MODEL", label: "Ask the model for extra suggestions (a small extra call per turn)", choices: ON_OFF_CHOICES2 },
@@ -23111,7 +22912,7 @@ function mergedEnvironment2(settings, environment = process.env) {
23111
22912
  var PROVIDER_REQUIRED_NAMES2 = new Set(PROVIDER_IDS.flatMap((id) => PROVIDER_INFO[id].requires));
23112
22913
  var PROVIDER_KEY_FIELDS2 = SETTING_FIELDS2.filter((field) => PROVIDER_REQUIRED_NAMES2.has(field.key));
23113
22914
 
23114
- // packages/archymedes-cli/src/app/free-setup.ts
22915
+ // src/app/free-setup.ts
23115
22916
  var OPENROUTER_KEYS_URL3 = "https://openrouter.ai/keys";
23116
22917
  var FREE_SETUP_INTRO = [
23117
22918
  "Free mode runs zero-priced models through OpenRouter and never falls back to a paid one.",
@@ -23177,7 +22978,7 @@ ${style3.bold("Free mode setup")}
23177
22978
  return;
23178
22979
  }
23179
22980
 
23180
- // packages/archymedes-cli/src/session/tabs.ts
22981
+ // src/session/tabs.ts
23181
22982
  class WorkspaceController {
23182
22983
  limit;
23183
22984
  tabs = [];
@@ -23266,7 +23067,7 @@ class WorkspaceController {
23266
23067
  }
23267
23068
  }
23268
23069
 
23269
- // packages/archymedes-cli/src/terminal/output.ts
23070
+ // src/terminal/output.ts
23270
23071
  var terminalStream2 = {
23271
23072
  write(text) {
23272
23073
  process.stdout.write(text);
@@ -23362,7 +23163,7 @@ class TabSink {
23362
23163
  }
23363
23164
  }
23364
23165
 
23365
- // packages/archymedes-cli/src/terminal/job-stream.ts
23166
+ // src/terminal/job-stream.ts
23366
23167
  var TERMINAL_STATUSES = new Set(["completed", "failed", "cancelled"]);
23367
23168
  class WatchRegistry {
23368
23169
  watched = new Map;
@@ -23397,7 +23198,7 @@ class WatchRegistry {
23397
23198
  }
23398
23199
  }
23399
23200
 
23400
- // packages/archymedes-cli/src/theme/theme.ts
23201
+ // src/theme/theme.ts
23401
23202
  var BLUEPRINT2 = `
23402
23203
  /* Blueprint navy, chalk-white ink and an amber pencil — the original drafting table. */
23403
23204
  @theme blueprint {
@@ -23698,7 +23499,7 @@ function detectPreferredTheme(environment) {
23698
23499
  return DEFAULT_THEME_NAME2;
23699
23500
  }
23700
23501
 
23701
- // packages/archymedes-cli/src/theme/theme-files.ts
23502
+ // src/theme/theme-files.ts
23702
23503
  import { promises as fs19 } from "node:fs";
23703
23504
  import path26 from "node:path";
23704
23505
  function themeDirectory(scope, root, environment) {
@@ -23736,7 +23537,7 @@ async function findTheme(name, root, environment) {
23736
23537
  return (await discoverThemes(root, environment)).find((theme) => theme.name.toLowerCase() === wanted);
23737
23538
  }
23738
23539
 
23739
- // packages/archymedes-cli/src/render/syntax.ts
23540
+ // src/render/syntax.ts
23740
23541
  var DEFAULT_CODE_STYLE2 = { colors: "vscode", lineNumbers: true };
23741
23542
  var currentCodeStyle2 = { ...DEFAULT_CODE_STYLE2 };
23742
23543
  function codeStyleFromEnvironment(environment) {
@@ -23794,7 +23595,7 @@ var GRAMMARS2 = {
23794
23595
  };
23795
23596
  var PUNCTUATION2 = new Set([..."{}[]()<>;,.:=+-*/%!&|^~?\\"]);
23796
23597
 
23797
- // packages/archymedes-cli/src/platform/i18n.ts
23598
+ // src/platform/i18n.ts
23798
23599
  var CONTROL_LANGUAGES2 = {
23799
23600
  en: "English",
23800
23601
  zh: "中文 (Mandarin)",
@@ -23819,7 +23620,7 @@ function resolveControlLanguage2(value) {
23819
23620
  return base in CONTROL_LANGUAGES2 ? base : "en";
23820
23621
  }
23821
23622
 
23822
- // packages/archymedes-cli/src/text/glyphs.ts
23623
+ // src/text/glyphs.ts
23823
23624
  var UNICODE_GLYPHS2 = {
23824
23625
  starGlyphs: [".", "·", "✧", "✦", "✶"],
23825
23626
  spinnerFrames: [" ◜ ", " ◠ ", " ◝ ", " ◞ ", " ◡ ", " ◟ "],
@@ -23935,7 +23736,7 @@ function glyphsFor(mode) {
23935
23736
  function resolveGlyphs(environment, platform = process.platform) {
23936
23737
  return glyphsFor(detectGlyphMode(environment, platform));
23937
23738
  }
23938
- // packages/archymedes-cli/src/session/state-history.ts
23739
+ // src/session/state-history.ts
23939
23740
  class CliStateHistory {
23940
23741
  root;
23941
23742
  environment;
@@ -24046,7 +23847,7 @@ class CliStateHistory {
24046
23847
  }
24047
23848
  }
24048
23849
 
24049
- // packages/archymedes-cli/src/ui/borrowed-keyboard.ts
23850
+ // src/ui/borrowed-keyboard.ts
24050
23851
  function painter(output) {
24051
23852
  let rows = 0;
24052
23853
  const erase = () => {
@@ -24127,7 +23928,7 @@ async function openChooser(host, items, options, self) {
24127
23928
  return withBorrowedKeyboard(host, self, (keys, paint2) => runChooser(keys, items, paint2, sized));
24128
23929
  }
24129
23930
 
24130
- // packages/archymedes-cli/src/app/prompts.ts
23931
+ // src/app/prompts.ts
24131
23932
  function renderApprovalPreview(preview2) {
24132
23933
  if (!preview2)
24133
23934
  return;
@@ -24265,7 +24066,7 @@ async function hiddenQuestion(readline, question, signal) {
24265
24066
  }
24266
24067
  }
24267
24068
 
24268
- // packages/archymedes-cli/src/app/providers.ts
24069
+ // src/app/providers.ts
24269
24070
  function readFxRates(environment) {
24270
24071
  const genericRate = Number(environment.ARCHYMEDES_FX_RATE);
24271
24072
  const genericFrom = environment.ARCHYMEDES_FX_FROM?.trim().toUpperCase();
@@ -24282,11 +24083,11 @@ function readFxRates(environment) {
24282
24083
  }
24283
24084
  return configured;
24284
24085
  }
24285
- // packages/core/src/cli/job-store.ts
24086
+ // ../core/src/cli/job-store.ts
24286
24087
  import { promises as fs20 } from "node:fs";
24287
24088
  import path27 from "node:path";
24288
24089
 
24289
- // packages/core/src/cli/jobs.ts
24090
+ // ../core/src/cli/jobs.ts
24290
24091
  var JOB_STATUSES = ["queued", "running", "paused", "completed", "failed", "cancelled"];
24291
24092
  var DECISIONS = ["allow", "allow_always", "allow_pattern", "deny", "deny_always"];
24292
24093
  function parseJobRecord(value, index) {
@@ -24461,7 +24262,7 @@ function recoverStale(store, now) {
24461
24262
  return { store: { jobs }, recovered };
24462
24263
  }
24463
24264
 
24464
- // packages/core/src/cli/job-store.ts
24265
+ // ../core/src/cli/job-store.ts
24465
24266
  function jobStoreFile(root) {
24466
24267
  return path27.join(root, ".archymedes", "jobs.json");
24467
24268
  }
@@ -24581,10 +24382,10 @@ async function appendJobLog(root, id, line) {
24581
24382
  await fs20.appendFile(file, `${line}
24582
24383
  `, "utf8");
24583
24384
  }
24584
- // packages/archymedes-cli/src/job-worker.ts
24385
+ // src/job-worker.ts
24585
24386
  import os6 from "node:os";
24586
24387
 
24587
- // packages/archymedes-cli/src/commands/wander.ts
24388
+ // src/commands/wander.ts
24588
24389
  async function gatherWanderEvidence(topic, search, numResults = 8) {
24589
24390
  if (!search) {
24590
24391
  return {
@@ -24680,7 +24481,7 @@ function renderWanderChart(results, style3, width) {
24680
24481
  `);
24681
24482
  }
24682
24483
 
24683
- // packages/archymedes-cli/src/job-worker.ts
24484
+ // src/job-worker.ts
24684
24485
  function workerId(hostname = os6.hostname(), pid = process.pid) {
24685
24486
  return `${hostname}:${pid}`;
24686
24487
  }
@@ -24858,7 +24659,7 @@ async function runJobWorkerForever(deps) {
24858
24659
  }
24859
24660
  }
24860
24661
 
24861
- // packages/archymedes-cli/src/app/providers.ts
24662
+ // src/app/providers.ts
24862
24663
  function renderProviders(environment, depth) {
24863
24664
  const paint2 = (text, apply) => depth === "none" ? text : apply(text);
24864
24665
  const statuses = describeProviders(environment);
@@ -24929,7 +24730,7 @@ function modelPriceCatalogFor(prices, exact) {
24929
24730
  return { ...rates, ...prices.largeContext ? { largeContext: prices.largeContext } : {} };
24930
24731
  }
24931
24732
 
24932
- // packages/archymedes-cli/src/app/job-launch.ts
24733
+ // src/app/job-launch.ts
24933
24734
  async function runJobWorkerProcess(root, jobId) {
24934
24735
  const savedSettings = await loadSettings(process.env);
24935
24736
  const environment = mergedEnvironment(savedSettings, process.env);
@@ -24966,10 +24767,10 @@ async function runJobWorkerProcess(root, jobId) {
24966
24767
  }
24967
24768
  }
24968
24769
 
24969
- // packages/archymedes-cli/src/app/startup-commands.ts
24770
+ // src/app/startup-commands.ts
24970
24771
  import { createInterface as createInterface4 } from "node:readline/promises";
24971
24772
 
24972
- // packages/archymedes-cli/src/headless.ts
24773
+ // src/headless.ts
24973
24774
  var EXIT_CODES2 = {
24974
24775
  completed: 0,
24975
24776
  failed: 1,
@@ -24993,7 +24794,7 @@ function exitCodeForStatus2(status) {
24993
24794
  return STATUS_EXIT2[status];
24994
24795
  }
24995
24796
 
24996
- // packages/archymedes-cli/src/platform/doctor.ts
24797
+ // src/platform/doctor.ts
24997
24798
  var DEFAULT_PROBE_TIMEOUT_MS = 5000;
24998
24799
  function modelEndpoints(environment) {
24999
24800
  return providerEndpoints(environment).map((provider) => {
@@ -25123,7 +24924,7 @@ function doctorReport(probes, context) {
25123
24924
  };
25124
24925
  }
25125
24926
 
25126
- // packages/archymedes-cli/src/text/color-depth.ts
24927
+ // src/text/color-depth.ts
25127
24928
  function detectColorDepth2(environment, isTTY) {
25128
24929
  if (!isTTY || environment.NO_COLOR !== undefined || environment.TERM === "dumb")
25129
24930
  return "none";
@@ -25132,7 +24933,7 @@ function detectColorDepth2(environment, isTTY) {
25132
24933
  return "ansi256";
25133
24934
  }
25134
24935
 
25135
- // packages/archymedes-cli/src/session/delete-session.ts
24936
+ // src/session/delete-session.ts
25136
24937
  import { promises as fs21 } from "node:fs";
25137
24938
  import path28 from "node:path";
25138
24939
  var SAFE_ID = /^[A-Za-z0-9][A-Za-z0-9._-]{0,127}$/;
@@ -25152,7 +24953,7 @@ async function clearProjection(root, id) {
25152
24953
  if (!exists)
25153
24954
  return;
25154
24955
  try {
25155
- const { SessionProjection } = await import("./projection-4jjdaf41.mjs");
24956
+ const { SessionProjection } = await import("./projection-ndr3t77h.mjs");
25156
24957
  const projection = await SessionProjection.open(root);
25157
24958
  try {
25158
24959
  await projection.rebuild(id);
@@ -25178,7 +24979,7 @@ async function deleteSession(root, id, options = {}) {
25178
24979
  return { deleted: true };
25179
24980
  }
25180
24981
 
25181
- // packages/archymedes-cli/src/ui/navigation.ts
24982
+ // src/ui/navigation.ts
25182
24983
  var ESSENTIAL_COMMANDS = ["/help", "/history", "/model", "/mode", "/diff", "/undo", "/settings", "/exit"];
25183
24984
  function isEssential(command) {
25184
24985
  return ESSENTIAL_COMMANDS.includes(command);
@@ -25245,7 +25046,7 @@ function renderAsks(suggestions, style3) {
25245
25046
  `);
25246
25047
  }
25247
25048
 
25248
- // packages/archymedes-cli/src/app/help.ts
25049
+ // src/app/help.ts
25249
25050
  function helpText(language = "en", shortcuts = new Map) {
25250
25051
  const star = glyphs.star;
25251
25052
  const mark = (command) => isEssential(command) ? `${star} ` : " ";
@@ -25350,7 +25151,7 @@ ${renderCommandHelp(language, shortcuts, { mark })}
25350
25151
  `;
25351
25152
  }
25352
25153
 
25353
- // packages/archymedes-cli/src/app/prompts.ts
25154
+ // src/app/prompts.ts
25354
25155
  function settingsChooser(readline) {
25355
25156
  return (request) => openChooser({ readline, input: process.stdin, output: process.stdout }, request.items.map((item) => ({ ...item })), {
25356
25157
  title: request.title,
@@ -25423,7 +25224,7 @@ async function hiddenQuestion2(readline, question, signal) {
25423
25224
  }
25424
25225
  }
25425
25226
 
25426
- // packages/archymedes-cli/src/app/startup-commands.ts
25227
+ // src/app/startup-commands.ts
25427
25228
  function runInformationFlag(context) {
25428
25229
  const { args, environment, language, earlyDepth } = context;
25429
25230
  if (args.help) {
@@ -25627,7 +25428,7 @@ ${status.reason ?? "native state engine unavailable"}
25627
25428
  return 0;
25628
25429
  }
25629
25430
 
25630
- // packages/archymedes-cli/src/app/display-currency.ts
25431
+ // src/app/display-currency.ts
25631
25432
  async function settleDisplayCurrency(options) {
25632
25433
  const { args, environment, prices, rates, state } = options;
25633
25434
  if (prices && state.display !== prices.currency) {
@@ -25684,7 +25485,7 @@ async function settleDisplayCurrency(options) {
25684
25485
  return;
25685
25486
  }
25686
25487
 
25687
- // packages/archymedes-cli/src/app/workspaces.ts
25488
+ // src/app/workspaces.ts
25688
25489
  function createWorkspaceFactory(args, environment) {
25689
25490
  return async (request) => {
25690
25491
  const announce = (text) => {
@@ -25694,8 +25495,8 @@ function createWorkspaceFactory(args, environment) {
25694
25495
  };
25695
25496
  const minutes = Math.max(1, Math.min(args.sandboxMinutes, 60));
25696
25497
  if (request.backend === "e2b") {
25697
- const { findWorkspacePreset } = await import("./sandbox-templates-8sd1psez.mjs");
25698
- const { createE2BProvider } = await import("./factory-8xk6y60m.mjs");
25498
+ const { findWorkspacePreset } = await import("./sandbox-templates-9jcjws23.mjs");
25499
+ const { createE2BProvider } = await import("./factory-xpqqcyqq.mjs");
25699
25500
  const preset = findWorkspacePreset(request.preset ?? args.preset);
25700
25501
  const sandbox = createE2BProvider(environment, preset.templateAlias);
25701
25502
  if (!sandbox)
@@ -25721,7 +25522,7 @@ function createWorkspaceFactory(args, environment) {
25721
25522
  return { workspace: created };
25722
25523
  }
25723
25524
  if (request.backend === "docker") {
25724
- const { createDockerProvider } = await import("./factory-8xk6y60m.mjs");
25525
+ const { createDockerProvider } = await import("./factory-xpqqcyqq.mjs");
25725
25526
  const image = request.dockerImage || args.dockerImage || environment.DOCKER_CODING_IMAGE;
25726
25527
  const sandbox = createDockerProvider({ ...environment, DOCKER_CODING_IMAGE: image });
25727
25528
  if (!sandbox)
@@ -25750,11 +25551,11 @@ function createWorkspaceFactory(args, environment) {
25750
25551
  };
25751
25552
  }
25752
25553
 
25753
- // packages/core/src/cli/balance.ts
25554
+ // ../core/src/cli/balance.ts
25754
25555
  var CRITICAL_BALANCE_USD2 = 0.5;
25755
25556
  var LOW_BALANCE_USD2 = 2;
25756
25557
 
25757
- // packages/archymedes-cli/src/commands/balance.ts
25558
+ // src/commands/balance.ts
25758
25559
  function formatBalance(amount, currency) {
25759
25560
  return formatMoney(fromUnits(amount, currency));
25760
25561
  }
@@ -25857,7 +25658,7 @@ class BalanceWatch {
25857
25658
  }
25858
25659
  }
25859
25660
 
25860
- // packages/archymedes-cli/src/session/resumed-spend.ts
25661
+ // src/session/resumed-spend.ts
25861
25662
  async function readSessionModelTurns(root, sessionId) {
25862
25663
  const events = await readEventJournal(root, sessionId);
25863
25664
  const turns = [];
@@ -25889,7 +25690,7 @@ function priceSessionModelTurns(turns, options) {
25889
25690
  return { spent: total, unpriced: [...unpriced].sort() };
25890
25691
  }
25891
25692
 
25892
- // packages/archymedes-cli/src/app/balance-tracking.ts
25693
+ // src/app/balance-tracking.ts
25893
25694
  function createBalanceTracking(options) {
25894
25695
  const { environment, processEnvironment, rates, state } = options;
25895
25696
  const usdInDisplay = (usd) => {
@@ -25978,7 +25779,7 @@ function createBalanceTracking(options) {
25978
25779
  return { lowBalance, criticalBalance, balanceWatch, parseManualBalance, currentBalance, persistManualBalance, sessionSpend, balanceHeader, checkBalance, carryResumedSpend };
25979
25780
  }
25980
25781
 
25981
- // packages/archymedes-cli/src/app/agent-factory.ts
25782
+ // src/app/agent-factory.ts
25982
25783
  function createClientFactory(options) {
25983
25784
  const { args, environment, daemon, approvalPrompt, rates, approvedBudget, state } = options;
25984
25785
  return async (record2, runtime = {}) => {
@@ -25999,6 +25800,7 @@ function createClientFactory(options) {
25999
25800
  onExpense: (expense) => state.ledger.recordExpense(expense),
26000
25801
  onEvent,
26001
25802
  jev: jevOptionsFromEnvironment(environment),
25803
+ ...reasoningFromEnvironment(environment) ? { reasoning: reasoningFromEnvironment(environment) } : {},
26002
25804
  afterEdit: editDiagnostics
26003
25805
  }), record2);
26004
25806
  if (editDiagnostics)
@@ -26018,14 +25820,14 @@ function closeWithClient(client, hook) {
26018
25820
  client.dispose = () => dispose().finally(close);
26019
25821
  }
26020
25822
 
26021
- // packages/archymedes-cli/src/app/live-models.ts
25823
+ // src/app/live-models.ts
26022
25824
  async function readCachedLiveModels(environment, state) {
26023
25825
  const cached = await readModelCache(environment);
26024
25826
  if (isCacheFresh(cached))
26025
25827
  state.liveModels = cached.models;
26026
25828
  }
26027
25829
 
26028
- // packages/archymedes-cli/src/app/session-hints.ts
25830
+ // src/app/session-hints.ts
26029
25831
  function createSessionHints(options) {
26030
25832
  const { state, watched, tabs, usage } = options;
26031
25833
  const navContext = () => ({
@@ -26054,7 +25856,7 @@ function createSessionHints(options) {
26054
25856
  return { navContext, writeHint };
26055
25857
  }
26056
25858
 
26057
- // packages/archymedes-cli/src/render/completion-card.ts
25859
+ // src/render/completion-card.ts
26058
25860
  var CHECK_LABELS = {
26059
25861
  tests: "tests",
26060
25862
  typecheck: "typecheck",
@@ -26084,7 +25886,7 @@ function renderCompletionCard(input, style3) {
26084
25886
  return panel(lines, style3, { title, tone: failed2 ? "bad" : verified ? "good" : "warn" });
26085
25887
  }
26086
25888
 
26087
- // packages/archymedes-cli/src/render/routing-receipt.ts
25889
+ // src/render/routing-receipt.ts
26088
25890
  function formatMicros(micros, currency = "USD") {
26089
25891
  if (micros === undefined || !Number.isFinite(micros))
26090
25892
  return "—";
@@ -26176,10 +25978,10 @@ ${rule(cell, { label: badge })}` : out3.join(`
26176
25978
  `);
26177
25979
  }
26178
25980
 
26179
- // packages/archymedes-cli/src/app/session-hints.ts
25981
+ // src/app/session-hints.ts
26180
25982
  var failureStatus = (status) => status === "needs_approval" ? "waiting_approval" : status;
26181
25983
 
26182
- // packages/archymedes-cli/src/app/turn-failure.ts
25984
+ // src/app/turn-failure.ts
26183
25985
  async function handleTurnFailure(error, request, context) {
26184
25986
  const { args, environment, interactive, headless, recoveryState, state, navContext } = context;
26185
25987
  if (recoveryState.last?.request === request) {
@@ -26234,7 +26036,7 @@ ${style.yellow(`Stopped at the ${formatMoney(fromUnits(args.budget, state.displa
26234
26036
  return false;
26235
26037
  }
26236
26038
 
26237
- // packages/archymedes-cli/src/app/turn-preflight.ts
26039
+ // src/app/turn-preflight.ts
26238
26040
  async function runTurnPreflight(request, context) {
26239
26041
  const { args, readline, interactive, approvedBudget, rates, state } = context;
26240
26042
  const { currentBalance, balanceWatch, sessionSpend } = context.balance;
@@ -26332,7 +26134,7 @@ async function runTurnPreflight(request, context) {
26332
26134
  return true;
26333
26135
  }
26334
26136
 
26335
- // packages/archymedes-cli/src/app/turn-runner.ts
26137
+ // src/app/turn-runner.ts
26336
26138
  var addModelUsage2 = (left, right) => ({
26337
26139
  inputTokens: left.inputTokens + right.inputTokens,
26338
26140
  outputTokens: left.outputTokens + right.outputTokens,
@@ -26540,7 +26342,7 @@ ${result.status === "completed" ? asMarkdown(result.summary) : style.yellow(resu
26540
26342
  };
26541
26343
  }
26542
26344
 
26543
- // packages/archymedes-cli/src/app/interrupts.ts
26345
+ // src/app/interrupts.ts
26544
26346
  function createSigintHandling(options) {
26545
26347
  const { readline, state } = options;
26546
26348
  const handleSigint = () => {
@@ -26581,7 +26383,7 @@ function createSigintHandling(options) {
26581
26383
  return { bindSigint, unbindSigint };
26582
26384
  }
26583
26385
 
26584
- // packages/archymedes-cli/src/app/session-start.ts
26386
+ // src/app/session-start.ts
26585
26387
  import path29 from "node:path";
26586
26388
  async function resumeAtStartup(context) {
26587
26389
  const { args, interactive, stateHistory, state, openClient } = context;
@@ -26654,7 +26456,7 @@ async function runOneShot(context) {
26654
26456
  return args.json ? exitCodeForStatus2(state.lastTurnStatus) : ran ? 0 : 1;
26655
26457
  }
26656
26458
 
26657
- // packages/archymedes-cli/src/archymedes.ts
26459
+ // src/archymedes.ts
26658
26460
  async function main() {
26659
26461
  process.stdout.on("error", (error) => {
26660
26462
  if (error.code === "EPIPE")