@runtypelabs/cli 2.1.0 → 2.2.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/index.js +699 -173
- package/dist/index.js.map +1 -1
- package/package.json +3 -3
package/dist/index.js
CHANGED
|
@@ -5,10 +5,16 @@ var __getOwnPropDesc = Object.getOwnPropertyDescriptor;
|
|
|
5
5
|
var __getOwnPropNames = Object.getOwnPropertyNames;
|
|
6
6
|
var __getProtoOf = Object.getPrototypeOf;
|
|
7
7
|
var __hasOwnProp = Object.prototype.hasOwnProperty;
|
|
8
|
+
var __require = /* @__PURE__ */ ((x) => typeof require !== "undefined" ? require : typeof Proxy !== "undefined" ? new Proxy(x, {
|
|
9
|
+
get: (a, b) => (typeof require !== "undefined" ? require : a)[b]
|
|
10
|
+
}) : x)(function(x) {
|
|
11
|
+
if (typeof require !== "undefined") return require.apply(this, arguments);
|
|
12
|
+
throw Error('Dynamic require of "' + x + '" is not supported');
|
|
13
|
+
});
|
|
8
14
|
var __esm = (fn, res) => function __init() {
|
|
9
15
|
return fn && (res = (0, fn[__getOwnPropNames(fn)[0]])(fn = 0)), res;
|
|
10
16
|
};
|
|
11
|
-
var __commonJS = (cb, mod) => function
|
|
17
|
+
var __commonJS = (cb, mod) => function __require2() {
|
|
12
18
|
return mod || (0, cb[__getOwnPropNames(cb)[0]])((mod = { exports: {} }).exports, mod), mod.exports;
|
|
13
19
|
};
|
|
14
20
|
var __export = (target, all) => {
|
|
@@ -147,6 +153,33 @@ var init_credential_store = __esm({
|
|
|
147
153
|
}
|
|
148
154
|
});
|
|
149
155
|
|
|
156
|
+
// ../shared/dist/integration-providers.js
|
|
157
|
+
var require_integration_providers = __commonJS({
|
|
158
|
+
"../shared/dist/integration-providers.js"(exports) {
|
|
159
|
+
"use strict";
|
|
160
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
161
|
+
exports.NON_AI_PROVIDER_IDS = void 0;
|
|
162
|
+
exports.isNonAIProvider = isNonAIProvider2;
|
|
163
|
+
exports.NON_AI_PROVIDER_IDS = /* @__PURE__ */ new Set([
|
|
164
|
+
// Analytics & Tracking
|
|
165
|
+
"posthog",
|
|
166
|
+
// Development Tools
|
|
167
|
+
"daytona",
|
|
168
|
+
// Web Scraping & Data Extraction
|
|
169
|
+
"firecrawl",
|
|
170
|
+
// Search Providers
|
|
171
|
+
"exa",
|
|
172
|
+
// Email Services
|
|
173
|
+
"resend",
|
|
174
|
+
// Audio / Speech
|
|
175
|
+
"elevenlabs"
|
|
176
|
+
]);
|
|
177
|
+
function isNonAIProvider2(id) {
|
|
178
|
+
return exports.NON_AI_PROVIDER_IDS.has(id.trim().toLowerCase());
|
|
179
|
+
}
|
|
180
|
+
}
|
|
181
|
+
});
|
|
182
|
+
|
|
150
183
|
// ../shared/dist/builtin-tools-registry.js
|
|
151
184
|
var require_builtin_tools_registry = __commonJS({
|
|
152
185
|
"../shared/dist/builtin-tools-registry.js"(exports) {
|
|
@@ -4565,8 +4598,8 @@ var CallbackServer = class {
|
|
|
4565
4598
|
expectedState;
|
|
4566
4599
|
constructor() {
|
|
4567
4600
|
this.app = express();
|
|
4568
|
-
this.codePromise = new Promise((
|
|
4569
|
-
this.codeResolve =
|
|
4601
|
+
this.codePromise = new Promise((resolve8, reject) => {
|
|
4602
|
+
this.codeResolve = resolve8;
|
|
4570
4603
|
this.codeReject = reject;
|
|
4571
4604
|
});
|
|
4572
4605
|
this.app.get("/callback", (req, res) => {
|
|
@@ -5761,14 +5794,14 @@ async function promptConfirm(message, options) {
|
|
|
5761
5794
|
output: process.stdout,
|
|
5762
5795
|
terminal: true
|
|
5763
5796
|
});
|
|
5764
|
-
return new Promise((
|
|
5797
|
+
return new Promise((resolve8) => {
|
|
5765
5798
|
rl.question(chalk2.cyan(`${message} (${hint}): `), (answer) => {
|
|
5766
5799
|
rl.close();
|
|
5767
5800
|
const trimmed = answer.trim().toLowerCase();
|
|
5768
5801
|
if (trimmed === "") {
|
|
5769
|
-
|
|
5802
|
+
resolve8(defaultYes);
|
|
5770
5803
|
} else {
|
|
5771
|
-
|
|
5804
|
+
resolve8(trimmed === "y" || trimmed === "yes");
|
|
5772
5805
|
}
|
|
5773
5806
|
});
|
|
5774
5807
|
});
|
|
@@ -5843,13 +5876,24 @@ function getCliVersion() {
|
|
|
5843
5876
|
if (cachedCliVersion) {
|
|
5844
5877
|
return cachedCliVersion;
|
|
5845
5878
|
}
|
|
5846
|
-
|
|
5847
|
-
|
|
5848
|
-
|
|
5849
|
-
|
|
5850
|
-
|
|
5851
|
-
|
|
5879
|
+
const base = dirname(fileURLToPath(import.meta.url));
|
|
5880
|
+
const candidates = [
|
|
5881
|
+
join(base, "..", "..", "package.json"),
|
|
5882
|
+
// src/lib/ → package root
|
|
5883
|
+
join(base, "..", "package.json")
|
|
5884
|
+
// dist/ → package root
|
|
5885
|
+
];
|
|
5886
|
+
for (const pkgPath of candidates) {
|
|
5887
|
+
try {
|
|
5888
|
+
const pkg = JSON.parse(readFileSync(pkgPath, "utf-8"));
|
|
5889
|
+
if (pkg.name === CLI_PACKAGE_NAME && pkg.version) {
|
|
5890
|
+
cachedCliVersion = pkg.version;
|
|
5891
|
+
return cachedCliVersion;
|
|
5892
|
+
}
|
|
5893
|
+
} catch {
|
|
5894
|
+
}
|
|
5852
5895
|
}
|
|
5896
|
+
cachedCliVersion = "0.0.0";
|
|
5853
5897
|
return cachedCliVersion;
|
|
5854
5898
|
}
|
|
5855
5899
|
|
|
@@ -5869,9 +5913,9 @@ var ApiClient = class {
|
|
|
5869
5913
|
this.baseUrl = baseUrl || getApiUrl();
|
|
5870
5914
|
}
|
|
5871
5915
|
/** Prefix path with API version (e.g. /v1) so all requests hit versioned routes. */
|
|
5872
|
-
path(
|
|
5916
|
+
path(path14) {
|
|
5873
5917
|
const version = getApiVersion();
|
|
5874
|
-
const p =
|
|
5918
|
+
const p = path14.startsWith("/") ? path14 : `/${path14}`;
|
|
5875
5919
|
return p.startsWith(`/${version}/`) ? p : `/${version}${p}`;
|
|
5876
5920
|
}
|
|
5877
5921
|
headers(extra) {
|
|
@@ -5904,8 +5948,8 @@ var ApiClient = class {
|
|
|
5904
5948
|
}
|
|
5905
5949
|
return response.json();
|
|
5906
5950
|
}
|
|
5907
|
-
async get(
|
|
5908
|
-
const url = new URL(this.path(
|
|
5951
|
+
async get(path14, params) {
|
|
5952
|
+
const url = new URL(this.path(path14), this.baseUrl);
|
|
5909
5953
|
if (params) {
|
|
5910
5954
|
for (const [key, value] of Object.entries(params)) {
|
|
5911
5955
|
if (value !== void 0 && value !== "") {
|
|
@@ -5918,32 +5962,32 @@ var ApiClient = class {
|
|
|
5918
5962
|
});
|
|
5919
5963
|
return this.handleResponse(response);
|
|
5920
5964
|
}
|
|
5921
|
-
async post(
|
|
5922
|
-
const response = await fetch(new URL(this.path(
|
|
5965
|
+
async post(path14, body) {
|
|
5966
|
+
const response = await fetch(new URL(this.path(path14), this.baseUrl).toString(), {
|
|
5923
5967
|
method: "POST",
|
|
5924
5968
|
headers: this.headers(),
|
|
5925
5969
|
body: body !== void 0 ? JSON.stringify(body) : void 0
|
|
5926
5970
|
});
|
|
5927
5971
|
return this.handleResponse(response);
|
|
5928
5972
|
}
|
|
5929
|
-
async put(
|
|
5930
|
-
const response = await fetch(new URL(this.path(
|
|
5973
|
+
async put(path14, body) {
|
|
5974
|
+
const response = await fetch(new URL(this.path(path14), this.baseUrl).toString(), {
|
|
5931
5975
|
method: "PUT",
|
|
5932
5976
|
headers: this.headers(),
|
|
5933
5977
|
body: body !== void 0 ? JSON.stringify(body) : void 0
|
|
5934
5978
|
});
|
|
5935
5979
|
return this.handleResponse(response);
|
|
5936
5980
|
}
|
|
5937
|
-
async patch(
|
|
5938
|
-
const response = await fetch(new URL(this.path(
|
|
5981
|
+
async patch(path14, body) {
|
|
5982
|
+
const response = await fetch(new URL(this.path(path14), this.baseUrl).toString(), {
|
|
5939
5983
|
method: "PATCH",
|
|
5940
5984
|
headers: this.headers(),
|
|
5941
5985
|
body: body !== void 0 ? JSON.stringify(body) : void 0
|
|
5942
5986
|
});
|
|
5943
5987
|
return this.handleResponse(response);
|
|
5944
5988
|
}
|
|
5945
|
-
async delete(
|
|
5946
|
-
const response = await fetch(new URL(this.path(
|
|
5989
|
+
async delete(path14) {
|
|
5990
|
+
const response = await fetch(new URL(this.path(path14), this.baseUrl).toString(), {
|
|
5947
5991
|
method: "DELETE",
|
|
5948
5992
|
headers: this.headers()
|
|
5949
5993
|
});
|
|
@@ -5957,8 +6001,8 @@ var ApiClient = class {
|
|
|
5957
6001
|
throw new ApiError(response.status, message);
|
|
5958
6002
|
}
|
|
5959
6003
|
}
|
|
5960
|
-
async stream(
|
|
5961
|
-
const response = await fetch(new URL(this.path(
|
|
6004
|
+
async stream(path14, body) {
|
|
6005
|
+
const response = await fetch(new URL(this.path(path14), this.baseUrl).toString(), {
|
|
5962
6006
|
method: "POST",
|
|
5963
6007
|
headers: this.headers({ Accept: "text/event-stream" }),
|
|
5964
6008
|
body: body !== void 0 ? JSON.stringify(body) : void 0
|
|
@@ -8346,6 +8390,7 @@ import React9 from "react";
|
|
|
8346
8390
|
import { useState as useState22, useEffect as useEffect19, useRef as useRef9, useCallback as useCallback8, useMemo as useMemo11 } from "react";
|
|
8347
8391
|
import { execSync } from "child_process";
|
|
8348
8392
|
import * as fs4 from "fs";
|
|
8393
|
+
import * as path4 from "path";
|
|
8349
8394
|
import open4 from "open";
|
|
8350
8395
|
import { Box as Box28, Text as Text28, useApp as useApp4, useInput as useInput11, useStdout as useStdout5 } from "ink";
|
|
8351
8396
|
import { StatusBar, theme as theme31 } from "@runtypelabs/ink-components";
|
|
@@ -8469,8 +8514,12 @@ function setStoredToolPayloadField(payloads, toolId, field, value) {
|
|
|
8469
8514
|
const next = { ...payloads.get(toolId) ?? {} };
|
|
8470
8515
|
if (value === void 0) {
|
|
8471
8516
|
delete next[field];
|
|
8517
|
+
} else if (field === "parameters") {
|
|
8518
|
+
next.parameters = value;
|
|
8519
|
+
} else if (field === "result") {
|
|
8520
|
+
next.result = value;
|
|
8472
8521
|
} else {
|
|
8473
|
-
next
|
|
8522
|
+
next.streamedInput = value;
|
|
8474
8523
|
}
|
|
8475
8524
|
if (next.parameters === void 0 && next.result === void 0 && next.streamedInput === void 0) {
|
|
8476
8525
|
payloads.delete(toolId);
|
|
@@ -8566,7 +8615,8 @@ function formatContextCompactionCompleteLine(event) {
|
|
|
8566
8615
|
return thresholdLabel ? `[context] History compacted ${modeLabel}${modelLabel}${strategyLabel} at ${thresholdLabel}${breakdownLabel}.` : `[context] History compacted ${modeLabel}${modelLabel}${strategyLabel}${breakdownLabel}.`;
|
|
8567
8616
|
}
|
|
8568
8617
|
function formatContextNoticeLine(event) {
|
|
8569
|
-
|
|
8618
|
+
const prefix = event.kind === "server_network_retry" ? "[retry]" : "[context]";
|
|
8619
|
+
return `${prefix} ${event.message}`;
|
|
8570
8620
|
}
|
|
8571
8621
|
var toolCounter = 0;
|
|
8572
8622
|
function useMarathonStream() {
|
|
@@ -8574,6 +8624,7 @@ function useMarathonStream() {
|
|
|
8574
8624
|
const rawEventsRef = useRef3([]);
|
|
8575
8625
|
const stateRef = useRef3(state);
|
|
8576
8626
|
stateRef.current = state;
|
|
8627
|
+
const costOffsetRef = useRef3(0);
|
|
8577
8628
|
const eventWriterRef = useRef3(null);
|
|
8578
8629
|
const toolPayloadsRef = useRef3(/* @__PURE__ */ new Map());
|
|
8579
8630
|
const hydrateToolEntry = useCallback2((tool) => {
|
|
@@ -8954,7 +9005,7 @@ function useMarathonStream() {
|
|
|
8954
9005
|
executionTime: Date.now() - (t.localExecutionStartedAt ?? t.startedAt)
|
|
8955
9006
|
} : t
|
|
8956
9007
|
),
|
|
8957
|
-
totalCost: event.totalCost != null ? event.totalCost : prev.totalCost,
|
|
9008
|
+
totalCost: event.totalCost != null ? costOffsetRef.current + event.totalCost : prev.totalCost,
|
|
8958
9009
|
totalInputTokens: event.totalTokens?.input ?? prev.totalInputTokens,
|
|
8959
9010
|
totalOutputTokens: event.totalTokens?.output ?? prev.totalOutputTokens
|
|
8960
9011
|
}));
|
|
@@ -9036,18 +9087,21 @@ function useMarathonStream() {
|
|
|
9036
9087
|
}, []);
|
|
9037
9088
|
const resetForNewSession = useCallback2(() => {
|
|
9038
9089
|
toolPayloadsRef.current.clear();
|
|
9039
|
-
setState((prev) =>
|
|
9040
|
-
|
|
9041
|
-
|
|
9042
|
-
|
|
9043
|
-
|
|
9044
|
-
|
|
9045
|
-
|
|
9046
|
-
|
|
9047
|
-
|
|
9048
|
-
|
|
9049
|
-
|
|
9050
|
-
|
|
9090
|
+
setState((prev) => {
|
|
9091
|
+
costOffsetRef.current = prev.totalCost;
|
|
9092
|
+
return {
|
|
9093
|
+
...prev,
|
|
9094
|
+
phase: "idle",
|
|
9095
|
+
content: "",
|
|
9096
|
+
reasoning: "",
|
|
9097
|
+
lastTranscriptKind: null,
|
|
9098
|
+
thinkingStartedAt: null,
|
|
9099
|
+
tools: [],
|
|
9100
|
+
currentIteration: 0,
|
|
9101
|
+
error: null,
|
|
9102
|
+
contextCompaction: null
|
|
9103
|
+
};
|
|
9104
|
+
});
|
|
9051
9105
|
}, []);
|
|
9052
9106
|
const showError = useCallback2((error) => {
|
|
9053
9107
|
pushRawEvent(rawEventsRef, "agent_error", { message: error.message }, eventWriterRef);
|
|
@@ -10353,6 +10407,10 @@ function FileContentView({
|
|
|
10353
10407
|
separator,
|
|
10354
10408
|
"from tool call"
|
|
10355
10409
|
] }),
|
|
10410
|
+
/* @__PURE__ */ jsxs14(Text16, { color: theme17.textSubtle, children: [
|
|
10411
|
+
separator,
|
|
10412
|
+
"o: open"
|
|
10413
|
+
] }),
|
|
10356
10414
|
/* @__PURE__ */ jsxs14(Text16, { color: theme17.textSubtle, children: [
|
|
10357
10415
|
separator,
|
|
10358
10416
|
"c: copy"
|
|
@@ -10455,6 +10513,14 @@ function FilesPanel({
|
|
|
10455
10513
|
separator,
|
|
10456
10514
|
files.length,
|
|
10457
10515
|
" files"
|
|
10516
|
+
] }),
|
|
10517
|
+
/* @__PURE__ */ jsxs14(Text16, { color: theme17.border, children: [
|
|
10518
|
+
separator,
|
|
10519
|
+
"Enter: view",
|
|
10520
|
+
separator,
|
|
10521
|
+
"o: open",
|
|
10522
|
+
separator,
|
|
10523
|
+
"c: copy path"
|
|
10458
10524
|
] })
|
|
10459
10525
|
] }),
|
|
10460
10526
|
/* @__PURE__ */ jsxs14(Box15, { flexDirection: "row", children: [
|
|
@@ -11955,7 +12021,7 @@ function CheckpointRecap({
|
|
|
11955
12021
|
`Tools: ${toolCallsMade}`,
|
|
11956
12022
|
`Tokens: ${formatTokenCount2(tokensInput)} in${separator}${formatTokenCount2(tokensOutput)} out`,
|
|
11957
12023
|
...reasoningTokens && reasoningTokens > 0 ? [`Reasoning: ${formatTokenCount2(reasoningTokens)}`] : [],
|
|
11958
|
-
`
|
|
12024
|
+
`Total cost: $${cost.toFixed(4)}`
|
|
11959
12025
|
].join(separator);
|
|
11960
12026
|
return /* @__PURE__ */ jsxs19(
|
|
11961
12027
|
Box20,
|
|
@@ -13086,8 +13152,8 @@ function MarathonApp({
|
|
|
13086
13152
|
setIsTerminalCheckpoint(true);
|
|
13087
13153
|
isTerminalCheckpointRef.current = true;
|
|
13088
13154
|
}
|
|
13089
|
-
return new Promise((
|
|
13090
|
-
checkpointResolveRef.current =
|
|
13155
|
+
return new Promise((resolve8) => {
|
|
13156
|
+
checkpointResolveRef.current = resolve8;
|
|
13091
13157
|
});
|
|
13092
13158
|
},
|
|
13093
13159
|
updateMilestone: (milestone) => {
|
|
@@ -13413,7 +13479,7 @@ function MarathonApp({
|
|
|
13413
13479
|
setUpgradeModalDismissed(true);
|
|
13414
13480
|
return;
|
|
13415
13481
|
}
|
|
13416
|
-
if (_input === "o" && agentPageUrl && !(state.phase === "checkpoint" && checkpointRecap)) {
|
|
13482
|
+
if (_input === "o" && agentPageUrl && !showFilesPanel && !(state.phase === "checkpoint" && checkpointRecap)) {
|
|
13417
13483
|
void open4(agentPageUrl);
|
|
13418
13484
|
return;
|
|
13419
13485
|
}
|
|
@@ -13569,6 +13635,19 @@ function MarathonApp({
|
|
|
13569
13635
|
setReasoningCollapsed((prev) => !prev);
|
|
13570
13636
|
return;
|
|
13571
13637
|
}
|
|
13638
|
+
if (_input === "o" && showFilesPanel) {
|
|
13639
|
+
const file = detailFile ?? trackedFiles[fileCursor];
|
|
13640
|
+
if (file) {
|
|
13641
|
+
const filePath = path4.resolve(file.path);
|
|
13642
|
+
if (fs4.existsSync(filePath)) {
|
|
13643
|
+
void open4(filePath);
|
|
13644
|
+
showFlash(`Opening ${file.path}`);
|
|
13645
|
+
} else {
|
|
13646
|
+
showFlash("File not found on disk");
|
|
13647
|
+
}
|
|
13648
|
+
}
|
|
13649
|
+
return;
|
|
13650
|
+
}
|
|
13572
13651
|
if (_input === "c" && showFilesPanel) {
|
|
13573
13652
|
if (detailFile && detailFileContent) {
|
|
13574
13653
|
const ok = copyToClipboard(detailFileContent);
|
|
@@ -14321,7 +14400,8 @@ function buildPromptShellModel(prompt, columnWidth) {
|
|
|
14321
14400
|
}
|
|
14322
14401
|
function MarathonStartupShell({
|
|
14323
14402
|
startupRef,
|
|
14324
|
-
marathonAppProps
|
|
14403
|
+
marathonAppProps,
|
|
14404
|
+
version
|
|
14325
14405
|
}) {
|
|
14326
14406
|
const { exit } = useApp5();
|
|
14327
14407
|
const { stdout } = useStdout6();
|
|
@@ -14347,16 +14427,16 @@ function MarathonStartupShell({
|
|
|
14347
14427
|
latestAppPropsRef.current = marathonAppProps;
|
|
14348
14428
|
useEffect20(() => {
|
|
14349
14429
|
if (scene !== "app" || !appReadyResolverRef.current) return;
|
|
14350
|
-
const
|
|
14430
|
+
const resolve8 = appReadyResolverRef.current;
|
|
14351
14431
|
appReadyResolverRef.current = null;
|
|
14352
|
-
|
|
14432
|
+
resolve8();
|
|
14353
14433
|
}, [scene]);
|
|
14354
14434
|
const beginTransition = (target) => {
|
|
14355
14435
|
if (transitionPromiseRef.current) return transitionPromiseRef.current;
|
|
14356
14436
|
if (target === "app" && !latestAppPropsRef.current) {
|
|
14357
14437
|
throw new Error("Cannot complete startup before marathon app props are ready.");
|
|
14358
14438
|
}
|
|
14359
|
-
const promise = new Promise((
|
|
14439
|
+
const promise = new Promise((resolve8) => {
|
|
14360
14440
|
globalThis.setTimeout(() => {
|
|
14361
14441
|
setPrompt(null);
|
|
14362
14442
|
setModelChoices(null);
|
|
@@ -14377,12 +14457,12 @@ function MarathonStartupShell({
|
|
|
14377
14457
|
if (target === "app") {
|
|
14378
14458
|
appReadyResolverRef.current = () => {
|
|
14379
14459
|
transitionPromiseRef.current = null;
|
|
14380
|
-
|
|
14460
|
+
resolve8();
|
|
14381
14461
|
};
|
|
14382
14462
|
} else {
|
|
14383
14463
|
dismissResolverRef.current = () => {
|
|
14384
14464
|
transitionPromiseRef.current = null;
|
|
14385
|
-
|
|
14465
|
+
resolve8();
|
|
14386
14466
|
};
|
|
14387
14467
|
}
|
|
14388
14468
|
}, Math.max(0, MIN_HOLD_MS - (Date.now() - mountedAtRef.current)));
|
|
@@ -14399,8 +14479,8 @@ function MarathonStartupShell({
|
|
|
14399
14479
|
setModelChoices(null);
|
|
14400
14480
|
setPrompt(nextPrompt);
|
|
14401
14481
|
setSelectedPromptIndex(0);
|
|
14402
|
-
return new Promise((
|
|
14403
|
-
promptResolverRef.current =
|
|
14482
|
+
return new Promise((resolve8) => {
|
|
14483
|
+
promptResolverRef.current = resolve8;
|
|
14404
14484
|
});
|
|
14405
14485
|
},
|
|
14406
14486
|
requestModelChoice: async (nextCurrentModel, models) => {
|
|
@@ -14408,8 +14488,8 @@ function MarathonStartupShell({
|
|
|
14408
14488
|
setPlaybookConfirm(null);
|
|
14409
14489
|
setCurrentModel(nextCurrentModel);
|
|
14410
14490
|
setModelChoices(models);
|
|
14411
|
-
return new Promise((
|
|
14412
|
-
modelResolverRef.current =
|
|
14491
|
+
return new Promise((resolve8) => {
|
|
14492
|
+
modelResolverRef.current = resolve8;
|
|
14413
14493
|
});
|
|
14414
14494
|
},
|
|
14415
14495
|
requestPlaybookModelConfirm: async (playbookName, milestoneModels) => {
|
|
@@ -14424,8 +14504,8 @@ function MarathonStartupShell({
|
|
|
14424
14504
|
// Default selection is the "Confirm" action (first item after milestones)
|
|
14425
14505
|
selectedIndex: names.length
|
|
14426
14506
|
});
|
|
14427
|
-
return new Promise((
|
|
14428
|
-
playbookConfirmResolverRef.current =
|
|
14507
|
+
return new Promise((resolve8) => {
|
|
14508
|
+
playbookConfirmResolverRef.current = resolve8;
|
|
14429
14509
|
});
|
|
14430
14510
|
},
|
|
14431
14511
|
completeStartup: () => beginTransition("app"),
|
|
@@ -14575,6 +14655,22 @@ function MarathonStartupShell({
|
|
|
14575
14655
|
children: /* @__PURE__ */ jsx34(StartupGridIconCompactLightInverted, { autoplay: false })
|
|
14576
14656
|
}
|
|
14577
14657
|
),
|
|
14658
|
+
version && /* @__PURE__ */ jsx34(
|
|
14659
|
+
Box29,
|
|
14660
|
+
{
|
|
14661
|
+
position: "absolute",
|
|
14662
|
+
width: terminalWidth,
|
|
14663
|
+
height: terminalRows,
|
|
14664
|
+
alignItems: "flex-end",
|
|
14665
|
+
justifyContent: "flex-end",
|
|
14666
|
+
paddingX: 2,
|
|
14667
|
+
paddingY: 1,
|
|
14668
|
+
children: /* @__PURE__ */ jsxs27(Text29, { color: theme32.border, children: [
|
|
14669
|
+
"v",
|
|
14670
|
+
version
|
|
14671
|
+
] })
|
|
14672
|
+
}
|
|
14673
|
+
),
|
|
14578
14674
|
/* @__PURE__ */ jsx34(
|
|
14579
14675
|
Box29,
|
|
14580
14676
|
{
|
|
@@ -14676,23 +14772,25 @@ function MarathonStartupShell({
|
|
|
14676
14772
|
}
|
|
14677
14773
|
|
|
14678
14774
|
// src/commands/agents-task.ts
|
|
14775
|
+
var import_integration_providers = __toESM(require_integration_providers(), 1);
|
|
14679
14776
|
import * as fs13 from "fs";
|
|
14680
14777
|
import {
|
|
14681
14778
|
RuntypeClient as RuntypeClient2,
|
|
14682
14779
|
defaultWorkflow,
|
|
14683
14780
|
deployWorkflow,
|
|
14684
|
-
gameWorkflow
|
|
14781
|
+
gameWorkflow,
|
|
14782
|
+
getDefaultPlanPath
|
|
14685
14783
|
} from "@runtypelabs/sdk";
|
|
14686
14784
|
|
|
14687
14785
|
// src/lib/terminal-title.ts
|
|
14688
|
-
import
|
|
14786
|
+
import path5 from "path";
|
|
14689
14787
|
function setTerminalTitle(title) {
|
|
14690
14788
|
if (process.stdout.isTTY) {
|
|
14691
14789
|
process.stdout.write(`\x1B]0;${title}\x07`);
|
|
14692
14790
|
}
|
|
14693
14791
|
}
|
|
14694
14792
|
function getFolderName() {
|
|
14695
|
-
return
|
|
14793
|
+
return path5.basename(process.cwd());
|
|
14696
14794
|
}
|
|
14697
14795
|
function setCliTitle() {
|
|
14698
14796
|
setTerminalTitle(`Runtype (${getFolderName()})`);
|
|
@@ -14703,21 +14801,21 @@ function setMarathonTitle() {
|
|
|
14703
14801
|
|
|
14704
14802
|
// src/marathon/checkpoint.ts
|
|
14705
14803
|
import * as fs6 from "fs";
|
|
14706
|
-
import * as
|
|
14804
|
+
import * as path7 from "path";
|
|
14707
14805
|
|
|
14708
14806
|
// src/config/paths.ts
|
|
14709
14807
|
import * as os3 from "os";
|
|
14710
|
-
import * as
|
|
14808
|
+
import * as path6 from "path";
|
|
14711
14809
|
import * as crypto3 from "crypto";
|
|
14712
14810
|
import * as fs5 from "fs";
|
|
14713
14811
|
function getRuntypeHomeDir() {
|
|
14714
|
-
return process.env.RUNTYPE_STATE_DIR ||
|
|
14812
|
+
return process.env.RUNTYPE_STATE_DIR || path6.join(os3.homedir(), ".runtype");
|
|
14715
14813
|
}
|
|
14716
14814
|
function getProjectStateDir(projectDir) {
|
|
14717
14815
|
const dir = projectDir || process.cwd();
|
|
14718
14816
|
const hash = crypto3.createHash("sha256").update(dir).digest("hex").slice(0, 12);
|
|
14719
|
-
const stateDir =
|
|
14720
|
-
const breadcrumb =
|
|
14817
|
+
const stateDir = path6.join(getRuntypeHomeDir(), "projects", hash);
|
|
14818
|
+
const breadcrumb = path6.join(stateDir, "project-path.txt");
|
|
14721
14819
|
if (!fs5.existsSync(breadcrumb)) {
|
|
14722
14820
|
fs5.mkdirSync(stateDir, { recursive: true });
|
|
14723
14821
|
fs5.writeFileSync(breadcrumb, dir);
|
|
@@ -14725,7 +14823,7 @@ function getProjectStateDir(projectDir) {
|
|
|
14725
14823
|
return stateDir;
|
|
14726
14824
|
}
|
|
14727
14825
|
function getMarathonStateDir(projectDir) {
|
|
14728
|
-
return
|
|
14826
|
+
return path6.join(getProjectStateDir(projectDir), "marathons");
|
|
14729
14827
|
}
|
|
14730
14828
|
|
|
14731
14829
|
// src/marathon/checkpoint.ts
|
|
@@ -14736,12 +14834,12 @@ function stateSafeName(name) {
|
|
|
14736
14834
|
return name.replace(/[^a-zA-Z0-9_-]/g, "_");
|
|
14737
14835
|
}
|
|
14738
14836
|
function marathonArtifactsDir(taskName, stateDir) {
|
|
14739
|
-
return
|
|
14837
|
+
return path7.join(stateDir || defaultStateDir(), stateSafeName(taskName));
|
|
14740
14838
|
}
|
|
14741
14839
|
function resolveMarathonCheckpointPath(taskName, targetPath, stateDir) {
|
|
14742
|
-
const normalizedTargetPath =
|
|
14743
|
-
const relativeTargetPath =
|
|
14744
|
-
return
|
|
14840
|
+
const normalizedTargetPath = path7.normalize(targetPath);
|
|
14841
|
+
const relativeTargetPath = path7.isAbsolute(normalizedTargetPath) ? path7.relative(process.cwd(), normalizedTargetPath) : normalizedTargetPath;
|
|
14842
|
+
return path7.join(
|
|
14745
14843
|
marathonArtifactsDir(taskName, stateDir),
|
|
14746
14844
|
"checkpoints",
|
|
14747
14845
|
"original",
|
|
@@ -14749,23 +14847,23 @@ function resolveMarathonCheckpointPath(taskName, targetPath, stateDir) {
|
|
|
14749
14847
|
);
|
|
14750
14848
|
}
|
|
14751
14849
|
function ensureMarathonFileCheckpoint(taskName, targetPath, stateDir) {
|
|
14752
|
-
const normalizedTargetPath =
|
|
14753
|
-
const relativeTargetPath =
|
|
14850
|
+
const normalizedTargetPath = path7.resolve(targetPath);
|
|
14851
|
+
const relativeTargetPath = path7.relative(process.cwd(), normalizedTargetPath);
|
|
14754
14852
|
if (!relativeTargetPath || relativeTargetPath.startsWith("..")) return void 0;
|
|
14755
14853
|
if (!fs6.existsSync(normalizedTargetPath)) return void 0;
|
|
14756
14854
|
if (!fs6.statSync(normalizedTargetPath).isFile()) return void 0;
|
|
14757
14855
|
const normalizedRelativeTargetPath = relativeTargetPath.replace(/\\/g, "/");
|
|
14758
14856
|
if (normalizedRelativeTargetPath.startsWith(".runtype/")) return void 0;
|
|
14759
|
-
if (normalizedTargetPath.startsWith(getRuntypeHomeDir() +
|
|
14857
|
+
if (normalizedTargetPath.startsWith(getRuntypeHomeDir() + path7.sep)) return void 0;
|
|
14760
14858
|
const checkpointPath = resolveMarathonCheckpointPath(taskName, normalizedTargetPath, stateDir);
|
|
14761
14859
|
if (fs6.existsSync(checkpointPath)) return checkpointPath;
|
|
14762
|
-
fs6.mkdirSync(
|
|
14860
|
+
fs6.mkdirSync(path7.dirname(checkpointPath), { recursive: true });
|
|
14763
14861
|
fs6.copyFileSync(normalizedTargetPath, checkpointPath);
|
|
14764
14862
|
return checkpointPath;
|
|
14765
14863
|
}
|
|
14766
14864
|
function restoreMarathonFileCheckpoint(taskName, targetPath, stateDir) {
|
|
14767
|
-
const normalizedTargetPath =
|
|
14768
|
-
const relativeTargetPath =
|
|
14865
|
+
const normalizedTargetPath = path7.resolve(targetPath);
|
|
14866
|
+
const relativeTargetPath = path7.relative(process.cwd(), normalizedTargetPath);
|
|
14769
14867
|
if (!relativeTargetPath || relativeTargetPath.startsWith("..")) {
|
|
14770
14868
|
return {
|
|
14771
14869
|
restored: false,
|
|
@@ -14780,7 +14878,7 @@ function restoreMarathonFileCheckpoint(taskName, targetPath, stateDir) {
|
|
|
14780
14878
|
error: `No checkpoint found for ${normalizedTargetPath}`
|
|
14781
14879
|
};
|
|
14782
14880
|
}
|
|
14783
|
-
fs6.mkdirSync(
|
|
14881
|
+
fs6.mkdirSync(path7.dirname(normalizedTargetPath), { recursive: true });
|
|
14784
14882
|
fs6.copyFileSync(checkpointPath, normalizedTargetPath);
|
|
14785
14883
|
return { restored: true, checkpointPath };
|
|
14786
14884
|
}
|
|
@@ -14797,6 +14895,8 @@ var NETWORK_ERROR_PATTERNS = [
|
|
|
14797
14895
|
"econnrefused",
|
|
14798
14896
|
"econnaborted",
|
|
14799
14897
|
"etimedout",
|
|
14898
|
+
"timeout",
|
|
14899
|
+
"request timeout",
|
|
14800
14900
|
"enetunreach",
|
|
14801
14901
|
"enetdown",
|
|
14802
14902
|
"ehostunreach",
|
|
@@ -14816,12 +14916,78 @@ var NETWORK_ERROR_PATTERNS = [
|
|
|
14816
14916
|
"unable to connect",
|
|
14817
14917
|
"err_network"
|
|
14818
14918
|
];
|
|
14919
|
+
var LOCAL_NETWORK_PATTERNS = [
|
|
14920
|
+
"enetunreach",
|
|
14921
|
+
"enetdown",
|
|
14922
|
+
"enotfound",
|
|
14923
|
+
"network error",
|
|
14924
|
+
"network request failed",
|
|
14925
|
+
"networkerror",
|
|
14926
|
+
"err_network"
|
|
14927
|
+
];
|
|
14928
|
+
var SERVER_UNREACHABLE_PATTERNS = [
|
|
14929
|
+
"econnrefused",
|
|
14930
|
+
"econnreset",
|
|
14931
|
+
"connection refused",
|
|
14932
|
+
"connection reset",
|
|
14933
|
+
"ehostunreach"
|
|
14934
|
+
];
|
|
14935
|
+
function collectErrorSignals(error, seen = /* @__PURE__ */ new Set()) {
|
|
14936
|
+
if (error == null || seen.has(error)) return [];
|
|
14937
|
+
if (typeof error === "string") return [error];
|
|
14938
|
+
if (typeof error !== "object") return [String(error)];
|
|
14939
|
+
seen.add(error);
|
|
14940
|
+
const parts = [];
|
|
14941
|
+
if ("message" in error && typeof error.message === "string") {
|
|
14942
|
+
parts.push(error.message);
|
|
14943
|
+
}
|
|
14944
|
+
if ("code" in error && typeof error.code === "string") {
|
|
14945
|
+
parts.push(error.code);
|
|
14946
|
+
}
|
|
14947
|
+
if ("cause" in error) {
|
|
14948
|
+
parts.push(...collectErrorSignals(error.cause, seen));
|
|
14949
|
+
}
|
|
14950
|
+
return parts;
|
|
14951
|
+
}
|
|
14952
|
+
function getNetworkErrorContext(error) {
|
|
14953
|
+
const signals = collectErrorSignals(error);
|
|
14954
|
+
const fallbackMessage = error instanceof Error ? error.message : String(error);
|
|
14955
|
+
const uniqueSignals = [...new Set(signals.map((signal) => signal.trim()).filter(Boolean))];
|
|
14956
|
+
const searchText = uniqueSignals.join(" ").toLowerCase();
|
|
14957
|
+
const detailMessage = uniqueSignals.find((signal) => signal.toLowerCase() !== "fetch failed") ?? fallbackMessage;
|
|
14958
|
+
return {
|
|
14959
|
+
searchText,
|
|
14960
|
+
detailMessage
|
|
14961
|
+
};
|
|
14962
|
+
}
|
|
14963
|
+
function describeNetworkError(error) {
|
|
14964
|
+
const { searchText, detailMessage } = getNetworkErrorContext(error);
|
|
14965
|
+
const isLocalNetwork = LOCAL_NETWORK_PATTERNS.some((p) => searchText.includes(p));
|
|
14966
|
+
const isServerUnreachable = SERVER_UNREACHABLE_PATTERNS.some((p) => searchText.includes(p));
|
|
14967
|
+
const isTimeout = searchText.includes("etimedout") || searchText.includes("timeout");
|
|
14968
|
+
const lines = [];
|
|
14969
|
+
if (isLocalNetwork) {
|
|
14970
|
+
lines.push("Could not reach the Runtype API \u2014 your network appears to be offline.");
|
|
14971
|
+
lines.push("Check your internet connection and try again.");
|
|
14972
|
+
} else if (isServerUnreachable) {
|
|
14973
|
+
lines.push("Could not reach the Runtype API \u2014 the server is not responding.");
|
|
14974
|
+
lines.push("The service may be temporarily unavailable. Try again in a few minutes.");
|
|
14975
|
+
} else if (isTimeout) {
|
|
14976
|
+
lines.push("Could not reach the Runtype API \u2014 the request timed out.");
|
|
14977
|
+
lines.push("This could be a network issue or the server may be under heavy load.");
|
|
14978
|
+
} else {
|
|
14979
|
+
lines.push("Could not reach the Runtype API \u2014 a network error occurred.");
|
|
14980
|
+
lines.push("Check your internet connection or try again in a few minutes.");
|
|
14981
|
+
}
|
|
14982
|
+
lines.push(`Details: ${detailMessage}`);
|
|
14983
|
+
return lines;
|
|
14984
|
+
}
|
|
14819
14985
|
function isTransientNetworkError(error) {
|
|
14820
14986
|
if (error instanceof RuntypeApiError) return false;
|
|
14821
|
-
const
|
|
14822
|
-
if (error instanceof TypeError &&
|
|
14987
|
+
const { searchText } = getNetworkErrorContext(error);
|
|
14988
|
+
if (error instanceof TypeError && searchText.includes("fetch")) return true;
|
|
14823
14989
|
if (error instanceof DOMException && error.name === "AbortError") return true;
|
|
14824
|
-
return NETWORK_ERROR_PATTERNS.some((pattern) =>
|
|
14990
|
+
return NETWORK_ERROR_PATTERNS.some((pattern) => searchText.includes(pattern));
|
|
14825
14991
|
}
|
|
14826
14992
|
async function retryOnNetworkError(fn, opts = {}) {
|
|
14827
14993
|
const maxRetries = opts.maxRetries ?? 3;
|
|
@@ -14838,7 +15004,7 @@ async function retryOnNetworkError(fn, opts = {}) {
|
|
|
14838
15004
|
}
|
|
14839
15005
|
const delay = Math.min(baseDelay * 2 ** attempt, maxDelay);
|
|
14840
15006
|
opts.onRetry?.(attempt + 1, delay, error);
|
|
14841
|
-
await new Promise((
|
|
15007
|
+
await new Promise((resolve8) => setTimeout(resolve8, delay));
|
|
14842
15008
|
}
|
|
14843
15009
|
}
|
|
14844
15010
|
throw lastError;
|
|
@@ -14898,9 +15064,8 @@ function describeMarathonApiError(error) {
|
|
|
14898
15064
|
if (!(error instanceof Error)) {
|
|
14899
15065
|
return ["Task failed: Unknown error"];
|
|
14900
15066
|
}
|
|
14901
|
-
if (
|
|
14902
|
-
|
|
14903
|
-
return [`Task failed: ${message}`];
|
|
15067
|
+
if (isTransientNetworkError(error)) {
|
|
15068
|
+
return describeNetworkError(error);
|
|
14904
15069
|
}
|
|
14905
15070
|
return [`Task failed: ${error.message}`];
|
|
14906
15071
|
}
|
|
@@ -14913,6 +15078,72 @@ function formatMarathonApiError(error) {
|
|
|
14913
15078
|
}
|
|
14914
15079
|
|
|
14915
15080
|
// src/marathon/verification.ts
|
|
15081
|
+
var SAFE_ENV_KEYS = [
|
|
15082
|
+
"PATH",
|
|
15083
|
+
"HOME",
|
|
15084
|
+
"USER",
|
|
15085
|
+
"SHELL",
|
|
15086
|
+
"TERM",
|
|
15087
|
+
"COLORTERM",
|
|
15088
|
+
"FORCE_COLOR",
|
|
15089
|
+
"CI",
|
|
15090
|
+
"NODE_PATH",
|
|
15091
|
+
"NODE_ENV",
|
|
15092
|
+
"NPM_CONFIG_CACHE",
|
|
15093
|
+
"LANG",
|
|
15094
|
+
"LC_ALL",
|
|
15095
|
+
"LC_CTYPE",
|
|
15096
|
+
"TMPDIR",
|
|
15097
|
+
"TMP",
|
|
15098
|
+
"TEMP",
|
|
15099
|
+
"HOSTNAME",
|
|
15100
|
+
"PNPM_HOME",
|
|
15101
|
+
"XDG_CONFIG_HOME",
|
|
15102
|
+
"XDG_DATA_HOME",
|
|
15103
|
+
"XDG_CACHE_HOME"
|
|
15104
|
+
];
|
|
15105
|
+
function getSanitizedEnv() {
|
|
15106
|
+
const clean = {};
|
|
15107
|
+
for (const key of SAFE_ENV_KEYS) {
|
|
15108
|
+
if (process.env[key]) clean[key] = process.env[key];
|
|
15109
|
+
}
|
|
15110
|
+
return clean;
|
|
15111
|
+
}
|
|
15112
|
+
function redactSecrets(output, secrets) {
|
|
15113
|
+
const sorted = secrets.filter((s) => s.length >= 8).sort((a, b) => b.length - a.length);
|
|
15114
|
+
let redacted = output;
|
|
15115
|
+
for (const secret of sorted) {
|
|
15116
|
+
redacted = redacted.replaceAll(secret, "[REDACTED]");
|
|
15117
|
+
}
|
|
15118
|
+
return redacted;
|
|
15119
|
+
}
|
|
15120
|
+
function collectSecretValues() {
|
|
15121
|
+
const secretPatterns = [
|
|
15122
|
+
/key/i,
|
|
15123
|
+
/secret/i,
|
|
15124
|
+
/token/i,
|
|
15125
|
+
/password/i,
|
|
15126
|
+
/credential/i,
|
|
15127
|
+
/auth/i,
|
|
15128
|
+
/api_key/i,
|
|
15129
|
+
/apikey/i,
|
|
15130
|
+
/private/i,
|
|
15131
|
+
/signing/i,
|
|
15132
|
+
/encryption/i,
|
|
15133
|
+
/database_url/i,
|
|
15134
|
+
/connection_string/i,
|
|
15135
|
+
/dsn/i
|
|
15136
|
+
];
|
|
15137
|
+
const secrets = [];
|
|
15138
|
+
for (const [key, value] of Object.entries(process.env)) {
|
|
15139
|
+
if (!value || value.length < 8) continue;
|
|
15140
|
+
if (SAFE_ENV_KEYS.includes(key)) continue;
|
|
15141
|
+
if (secretPatterns.some((pattern) => pattern.test(key))) {
|
|
15142
|
+
secrets.push(value);
|
|
15143
|
+
}
|
|
15144
|
+
}
|
|
15145
|
+
return secrets;
|
|
15146
|
+
}
|
|
14916
15147
|
var BLOCKED_VERIFICATION_PATTERNS = [
|
|
14917
15148
|
/&&/,
|
|
14918
15149
|
/\|\|/,
|
|
@@ -14951,10 +15182,54 @@ function isSafeVerificationCommand(command) {
|
|
|
14951
15182
|
if (BLOCKED_VERIFICATION_PATTERNS.some((pattern) => pattern.test(trimmed))) return false;
|
|
14952
15183
|
return ALLOWED_VERIFICATION_COMMANDS.some((pattern) => pattern.test(trimmed));
|
|
14953
15184
|
}
|
|
15185
|
+
var SCRIPT_COMMAND_PATTERNS = [
|
|
15186
|
+
{
|
|
15187
|
+
pattern: /^(?:pnpm|npm|yarn|bun)\s+(?:test|lint|build|check|typecheck|run\s+\w+)\b/i,
|
|
15188
|
+
files: ["package.json"]
|
|
15189
|
+
},
|
|
15190
|
+
{
|
|
15191
|
+
pattern: /^make\s+/i,
|
|
15192
|
+
files: ["Makefile", "makefile", "GNUmakefile"]
|
|
15193
|
+
},
|
|
15194
|
+
{
|
|
15195
|
+
pattern: /^just\s+/i,
|
|
15196
|
+
files: ["justfile", "Justfile"]
|
|
15197
|
+
}
|
|
15198
|
+
];
|
|
15199
|
+
function getScriptConfigFiles(command) {
|
|
15200
|
+
const trimmed = command.trim();
|
|
15201
|
+
for (const { pattern, files } of SCRIPT_COMMAND_PATTERNS) {
|
|
15202
|
+
if (pattern.test(trimmed)) return files;
|
|
15203
|
+
}
|
|
15204
|
+
return [];
|
|
15205
|
+
}
|
|
15206
|
+
function isScriptConfigModified(cwd, files) {
|
|
15207
|
+
try {
|
|
15208
|
+
const { spawnSync: spawnSync2 } = __require("child_process");
|
|
15209
|
+
const spawnOpts = { cwd, encoding: "utf-8", timeout: 5e3 };
|
|
15210
|
+
const failed = (r) => r.error != null || r.status === null || r.status !== 0;
|
|
15211
|
+
const diff = spawnSync2("git", ["diff", "--name-only", "HEAD", "--", ...files], spawnOpts);
|
|
15212
|
+
if (failed(diff)) return true;
|
|
15213
|
+
if (diff.stdout?.trim()) return true;
|
|
15214
|
+
const staged = spawnSync2("git", ["diff", "--name-only", "--cached", "--", ...files], spawnOpts);
|
|
15215
|
+
if (failed(staged)) return true;
|
|
15216
|
+
if (staged.stdout?.trim()) return true;
|
|
15217
|
+
const lsFiles = spawnSync2(
|
|
15218
|
+
"git",
|
|
15219
|
+
["ls-files", "--others", "--exclude-standard", "--", ...files],
|
|
15220
|
+
spawnOpts
|
|
15221
|
+
);
|
|
15222
|
+
if (lsFiles.error != null || lsFiles.status === null) return true;
|
|
15223
|
+
if (lsFiles.stdout?.trim()) return true;
|
|
15224
|
+
return false;
|
|
15225
|
+
} catch {
|
|
15226
|
+
return true;
|
|
15227
|
+
}
|
|
15228
|
+
}
|
|
14954
15229
|
|
|
14955
15230
|
// src/marathon/state.ts
|
|
14956
15231
|
import * as fs7 from "fs";
|
|
14957
|
-
import * as
|
|
15232
|
+
import * as path8 from "path";
|
|
14958
15233
|
import chalk15 from "chalk";
|
|
14959
15234
|
|
|
14960
15235
|
// src/lib/select-prompt.ts
|
|
@@ -14989,14 +15264,14 @@ async function promptNumericSelect(choices, promptLabel) {
|
|
|
14989
15264
|
output: process.stdout,
|
|
14990
15265
|
terminal: true
|
|
14991
15266
|
});
|
|
14992
|
-
return new Promise((
|
|
15267
|
+
return new Promise((resolve8) => {
|
|
14993
15268
|
const ask = () => {
|
|
14994
15269
|
rl.question(chalk14.cyan(`
|
|
14995
15270
|
${promptLabel} (1-${choices.length}): `), (answer) => {
|
|
14996
15271
|
const value = parseInt(answer.trim(), 10);
|
|
14997
15272
|
if (value >= 1 && value <= choices.length) {
|
|
14998
15273
|
rl.close();
|
|
14999
|
-
|
|
15274
|
+
resolve8(choices[value - 1].value);
|
|
15000
15275
|
return;
|
|
15001
15276
|
}
|
|
15002
15277
|
console.log(chalk14.red(`Please enter a number between 1 and ${choices.length}.`));
|
|
@@ -15024,7 +15299,7 @@ ${message}`));
|
|
|
15024
15299
|
const previousRawMode = input.isRaw === true;
|
|
15025
15300
|
let selectedIndex = 0;
|
|
15026
15301
|
let renderedLineCount = 0;
|
|
15027
|
-
return new Promise((
|
|
15302
|
+
return new Promise((resolve8) => {
|
|
15028
15303
|
const renderMenu = () => {
|
|
15029
15304
|
if (renderedLineCount > 0) {
|
|
15030
15305
|
clearRenderedLines(output, renderedLineCount);
|
|
@@ -15046,7 +15321,7 @@ ${message}`));
|
|
|
15046
15321
|
};
|
|
15047
15322
|
const finish = (value) => {
|
|
15048
15323
|
cleanup();
|
|
15049
|
-
|
|
15324
|
+
resolve8(value);
|
|
15050
15325
|
};
|
|
15051
15326
|
const onKeypress = (_, key) => {
|
|
15052
15327
|
if (key.ctrl && key.name === "c") {
|
|
@@ -15092,7 +15367,7 @@ function stateSafeName2(name) {
|
|
|
15092
15367
|
}
|
|
15093
15368
|
function stateFilePath(name, stateDir) {
|
|
15094
15369
|
const dir = stateDir || defaultStateDir2();
|
|
15095
|
-
return
|
|
15370
|
+
return path8.join(dir, `${stateSafeName2(name)}.json`);
|
|
15096
15371
|
}
|
|
15097
15372
|
function normalizeMarathonStatePath(candidatePath) {
|
|
15098
15373
|
if (!candidatePath) return void 0;
|
|
@@ -15101,15 +15376,15 @@ function normalizeMarathonStatePath(candidatePath) {
|
|
|
15101
15376
|
}
|
|
15102
15377
|
function marathonStatePathExists(candidatePath) {
|
|
15103
15378
|
if (!candidatePath) return false;
|
|
15104
|
-
return fs7.existsSync(
|
|
15379
|
+
return fs7.existsSync(path8.resolve(candidatePath));
|
|
15105
15380
|
}
|
|
15106
15381
|
function isMarathonArtifactStatePath(candidatePath) {
|
|
15107
15382
|
if (!candidatePath) return false;
|
|
15108
15383
|
const normalized = normalizeMarathonStatePath(candidatePath)?.toLowerCase();
|
|
15109
15384
|
if (normalized === ".runtype" || normalized?.startsWith(".runtype/") === true) return true;
|
|
15110
|
-
const resolved =
|
|
15385
|
+
const resolved = path8.resolve(candidatePath);
|
|
15111
15386
|
const homeStateDir = getRuntypeHomeDir();
|
|
15112
|
-
return resolved.startsWith(homeStateDir +
|
|
15387
|
+
return resolved.startsWith(homeStateDir + path8.sep) || resolved === homeStateDir;
|
|
15113
15388
|
}
|
|
15114
15389
|
function scoreMarathonCandidatePath(candidatePath) {
|
|
15115
15390
|
const normalized = candidatePath.toLowerCase();
|
|
@@ -15282,7 +15557,7 @@ function loadState(filePath) {
|
|
|
15282
15557
|
}
|
|
15283
15558
|
}
|
|
15284
15559
|
function saveState(filePath, state, options) {
|
|
15285
|
-
const dir =
|
|
15560
|
+
const dir = path8.dirname(filePath);
|
|
15286
15561
|
fs7.mkdirSync(dir, { recursive: true });
|
|
15287
15562
|
const stateToWrite = options?.stripSnapshotEvents && state.sessionSnapshots?.length ? {
|
|
15288
15563
|
...state,
|
|
@@ -15322,9 +15597,9 @@ function findStateFile(name, stateDir) {
|
|
|
15322
15597
|
}
|
|
15323
15598
|
const homePath = stateFilePath(name, getMarathonStateDir());
|
|
15324
15599
|
if (fs7.existsSync(homePath)) return homePath;
|
|
15325
|
-
const oldMarathonPath = stateFilePath(name,
|
|
15600
|
+
const oldMarathonPath = stateFilePath(name, path8.join(process.cwd(), ".runtype", "marathons"));
|
|
15326
15601
|
if (fs7.existsSync(oldMarathonPath)) return oldMarathonPath;
|
|
15327
|
-
const oldTaskPath = stateFilePath(name,
|
|
15602
|
+
const oldTaskPath = stateFilePath(name, path8.join(process.cwd(), ".runtype", "tasks"));
|
|
15328
15603
|
if (fs7.existsSync(oldTaskPath)) return oldTaskPath;
|
|
15329
15604
|
return homePath;
|
|
15330
15605
|
}
|
|
@@ -15333,15 +15608,15 @@ function findLatestStateFile(stateDir) {
|
|
|
15333
15608
|
// New home-dir location
|
|
15334
15609
|
getMarathonStateDir(),
|
|
15335
15610
|
// Old project-dir locations (backward compat)
|
|
15336
|
-
|
|
15337
|
-
|
|
15611
|
+
path8.join(process.cwd(), ".runtype", "marathons"),
|
|
15612
|
+
path8.join(process.cwd(), ".runtype", "tasks")
|
|
15338
15613
|
];
|
|
15339
15614
|
let latest = null;
|
|
15340
15615
|
for (const dir of dirs) {
|
|
15341
15616
|
if (!fs7.existsSync(dir)) continue;
|
|
15342
15617
|
const files = fs7.readdirSync(dir).filter((f) => f.endsWith(".json"));
|
|
15343
15618
|
for (const file of files) {
|
|
15344
|
-
const fullPath =
|
|
15619
|
+
const fullPath = path8.join(dir, file);
|
|
15345
15620
|
const stat = fs7.statSync(fullPath);
|
|
15346
15621
|
if (!latest || stat.mtimeMs > latest.mtime) {
|
|
15347
15622
|
latest = { name: file.replace(".json", ""), filePath: fullPath, mtime: stat.mtimeMs };
|
|
@@ -15463,12 +15738,12 @@ function buildResumeCommand(agent, options, parsedSandbox) {
|
|
|
15463
15738
|
|
|
15464
15739
|
// src/marathon/local-tools.ts
|
|
15465
15740
|
import * as fs9 from "fs";
|
|
15466
|
-
import * as
|
|
15741
|
+
import * as path10 from "path";
|
|
15467
15742
|
import { spawnSync } from "child_process";
|
|
15468
15743
|
|
|
15469
15744
|
// src/marathon/repo-discovery.ts
|
|
15470
15745
|
import * as fs8 from "fs";
|
|
15471
|
-
import * as
|
|
15746
|
+
import * as path9 from "path";
|
|
15472
15747
|
var IGNORED_REPO_DIRS = /* @__PURE__ */ new Set([
|
|
15473
15748
|
".git",
|
|
15474
15749
|
".next",
|
|
@@ -15566,8 +15841,8 @@ var SEARCH_STOP_WORDS = /* @__PURE__ */ new Set([
|
|
|
15566
15841
|
"do"
|
|
15567
15842
|
]);
|
|
15568
15843
|
function normalizeToolPath(toolPath) {
|
|
15569
|
-
const resolved =
|
|
15570
|
-
return
|
|
15844
|
+
const resolved = path9.resolve(toolPath || ".");
|
|
15845
|
+
return path9.relative(process.cwd(), resolved) || ".";
|
|
15571
15846
|
}
|
|
15572
15847
|
function tokenizeSearchQuery(query) {
|
|
15573
15848
|
return query.toLowerCase().split(/[^a-z0-9./_-]+/g).map((token) => token.trim()).filter((token) => token.length >= 2 && !SEARCH_STOP_WORDS.has(token));
|
|
@@ -15576,7 +15851,7 @@ function scoreSearchPath(relativePath) {
|
|
|
15576
15851
|
const normalized = relativePath.replace(/\\/g, "/");
|
|
15577
15852
|
const segments = normalized.split("/");
|
|
15578
15853
|
const fileName = segments[segments.length - 1] || normalized;
|
|
15579
|
-
const extension =
|
|
15854
|
+
const extension = path9.extname(fileName).toLowerCase();
|
|
15580
15855
|
let score = 0;
|
|
15581
15856
|
if (LOW_SIGNAL_FILE_NAMES.has(fileName)) score -= 50;
|
|
15582
15857
|
if (segments.some((segment) => LOW_SIGNAL_PATH_SEGMENTS.has(segment))) score -= 15;
|
|
@@ -15592,7 +15867,7 @@ function shouldIgnoreRepoEntry(entryPath) {
|
|
|
15592
15867
|
const normalized = normalizeToolPath(entryPath).replace(/\\/g, "/");
|
|
15593
15868
|
if (normalized === ".") return false;
|
|
15594
15869
|
if (isSensitivePath(normalized)) return true;
|
|
15595
|
-
return normalized.split(
|
|
15870
|
+
return normalized.split(path9.sep).some((segment) => IGNORED_REPO_DIRS.has(segment));
|
|
15596
15871
|
}
|
|
15597
15872
|
function safeReadTextFile(filePath) {
|
|
15598
15873
|
try {
|
|
@@ -15620,7 +15895,7 @@ function walkRepo(startPath, visitor) {
|
|
|
15620
15895
|
continue;
|
|
15621
15896
|
}
|
|
15622
15897
|
for (const entry of entries) {
|
|
15623
|
-
const entryPath =
|
|
15898
|
+
const entryPath = path9.join(currentDir, entry.name);
|
|
15624
15899
|
if (shouldIgnoreRepoEntry(entryPath)) continue;
|
|
15625
15900
|
const shouldStop = visitor(entryPath, entry);
|
|
15626
15901
|
if (shouldStop === true) return;
|
|
@@ -15665,7 +15940,7 @@ function buildTree(dirPath, maxDepth, depth = 0) {
|
|
|
15665
15940
|
}
|
|
15666
15941
|
const lines = [];
|
|
15667
15942
|
for (const entry of entries.sort((a, b) => a.name.localeCompare(b.name))) {
|
|
15668
|
-
const entryPath =
|
|
15943
|
+
const entryPath = path9.join(dirPath, entry.name);
|
|
15669
15944
|
if (shouldIgnoreRepoEntry(entryPath)) continue;
|
|
15670
15945
|
lines.push(`${" ".repeat(depth)}- ${entry.name}${entry.isDirectory() ? "/" : ""}`);
|
|
15671
15946
|
if (entry.isDirectory() && depth < maxDepth) {
|
|
@@ -15681,14 +15956,14 @@ function stateSafeName3(name) {
|
|
|
15681
15956
|
return name.replace(/[^a-zA-Z0-9_-]/g, "_");
|
|
15682
15957
|
}
|
|
15683
15958
|
function getOffloadedOutputDir(taskName, stateDir) {
|
|
15684
|
-
return
|
|
15959
|
+
return path10.join(stateDir || getMarathonStateDir(), stateSafeName3(taskName), "outputs");
|
|
15685
15960
|
}
|
|
15686
15961
|
function isPathWithinRoot(targetPath, rootPath) {
|
|
15687
|
-
const relativePath =
|
|
15688
|
-
return relativePath === "" || !relativePath.startsWith("..") && !
|
|
15962
|
+
const relativePath = path10.relative(rootPath, targetPath);
|
|
15963
|
+
return relativePath === "" || !relativePath.startsWith("..") && !path10.isAbsolute(relativePath);
|
|
15689
15964
|
}
|
|
15690
15965
|
function resolveCanonicalToolPath(toolPath, allowMissing) {
|
|
15691
|
-
const absolutePath =
|
|
15966
|
+
const absolutePath = path10.resolve(toolPath);
|
|
15692
15967
|
if (fs9.existsSync(absolutePath)) {
|
|
15693
15968
|
return {
|
|
15694
15969
|
canonicalPath: fs9.realpathSync.native(absolutePath),
|
|
@@ -15699,22 +15974,22 @@ function resolveCanonicalToolPath(toolPath, allowMissing) {
|
|
|
15699
15974
|
const missingSegments = [];
|
|
15700
15975
|
let currentPath = absolutePath;
|
|
15701
15976
|
while (!fs9.existsSync(currentPath)) {
|
|
15702
|
-
const parentPath =
|
|
15977
|
+
const parentPath = path10.dirname(currentPath);
|
|
15703
15978
|
if (parentPath === currentPath) return null;
|
|
15704
|
-
missingSegments.unshift(
|
|
15979
|
+
missingSegments.unshift(path10.basename(currentPath));
|
|
15705
15980
|
currentPath = parentPath;
|
|
15706
15981
|
}
|
|
15707
15982
|
return {
|
|
15708
|
-
canonicalPath:
|
|
15983
|
+
canonicalPath: path10.join(fs9.realpathSync.native(currentPath), ...missingSegments),
|
|
15709
15984
|
exists: false
|
|
15710
15985
|
};
|
|
15711
15986
|
}
|
|
15712
15987
|
function canonicalizeAllowedRoot(rootPath) {
|
|
15713
|
-
return resolveCanonicalToolPath(rootPath, true)?.canonicalPath ||
|
|
15988
|
+
return resolveCanonicalToolPath(rootPath, true)?.canonicalPath || path10.resolve(rootPath);
|
|
15714
15989
|
}
|
|
15715
15990
|
function findBlockedWorkspaceSegment(targetPath, workspaceRoot) {
|
|
15716
15991
|
if (!isPathWithinRoot(targetPath, workspaceRoot)) return void 0;
|
|
15717
|
-
const relativePath =
|
|
15992
|
+
const relativePath = path10.relative(workspaceRoot, targetPath).replace(/\\/g, "/");
|
|
15718
15993
|
if (!relativePath) return void 0;
|
|
15719
15994
|
return relativePath.split("/").find((segment) => DIRECT_TOOL_BLOCKED_SEGMENTS.has(segment));
|
|
15720
15995
|
}
|
|
@@ -15743,19 +16018,24 @@ function resolveToolPath(toolPath, options = {}) {
|
|
|
15743
16018
|
};
|
|
15744
16019
|
}
|
|
15745
16020
|
if (matchedRoot === workspaceRoot) {
|
|
15746
|
-
const
|
|
15747
|
-
|
|
15748
|
-
|
|
15749
|
-
|
|
15750
|
-
|
|
15751
|
-
|
|
15752
|
-
|
|
15753
|
-
|
|
15754
|
-
|
|
15755
|
-
|
|
15756
|
-
|
|
15757
|
-
|
|
15758
|
-
|
|
16021
|
+
const isUnderExtraRoot = extraRoots.some(
|
|
16022
|
+
(extraRoot) => isPathWithinRoot(resolved.canonicalPath, extraRoot)
|
|
16023
|
+
);
|
|
16024
|
+
if (!isUnderExtraRoot) {
|
|
16025
|
+
const blockedSegment = findBlockedWorkspaceSegment(resolved.canonicalPath, workspaceRoot);
|
|
16026
|
+
if (blockedSegment) {
|
|
16027
|
+
return {
|
|
16028
|
+
ok: false,
|
|
16029
|
+
error: `Access denied: ${requestedPath} is inside restricted workspace state (${blockedSegment})`
|
|
16030
|
+
};
|
|
16031
|
+
}
|
|
16032
|
+
const relativeFromWorkspace = path10.relative(workspaceRoot, resolved.canonicalPath).replace(/\\/g, "/");
|
|
16033
|
+
if (isSensitivePath(relativeFromWorkspace)) {
|
|
16034
|
+
return {
|
|
16035
|
+
ok: false,
|
|
16036
|
+
error: `Access denied: ${requestedPath} is a sensitive path and cannot be read or written`
|
|
16037
|
+
};
|
|
16038
|
+
}
|
|
15759
16039
|
}
|
|
15760
16040
|
}
|
|
15761
16041
|
if (resolved.exists) {
|
|
@@ -15778,15 +16058,17 @@ function resolveToolPath(toolPath, options = {}) {
|
|
|
15778
16058
|
return { ok: true, resolvedPath: resolved.canonicalPath };
|
|
15779
16059
|
}
|
|
15780
16060
|
function getTaskStateRoot(taskName, stateDir) {
|
|
15781
|
-
return
|
|
16061
|
+
return path10.join(stateDir || getMarathonStateDir(), stateSafeName3(taskName));
|
|
15782
16062
|
}
|
|
15783
16063
|
function createDefaultLocalTools(context) {
|
|
15784
16064
|
const taskStateRoot = context?.taskName ? getTaskStateRoot(context.taskName, context.stateDir) : void 0;
|
|
15785
|
-
const planDir = context?.taskName ?
|
|
16065
|
+
const planDir = context?.taskName ? path10.resolve(`.runtype/marathons/${stateSafeName3(context.taskName)}`) : void 0;
|
|
16066
|
+
const planPathDir = context?.planPath ? path10.resolve(path10.dirname(context.planPath)) : void 0;
|
|
15786
16067
|
const allowedReadRoots = context?.taskName ? [
|
|
15787
16068
|
getOffloadedOutputDir(context.taskName, context.stateDir),
|
|
15788
16069
|
...taskStateRoot ? [taskStateRoot] : [],
|
|
15789
|
-
...planDir ? [planDir] : []
|
|
16070
|
+
...planDir ? [planDir] : [],
|
|
16071
|
+
...planPathDir ? [planPathDir] : []
|
|
15790
16072
|
] : [];
|
|
15791
16073
|
return {
|
|
15792
16074
|
read_file: {
|
|
@@ -15824,7 +16106,7 @@ function createDefaultLocalTools(context) {
|
|
|
15824
16106
|
if (fs9.existsSync(resolvedPath.resolvedPath) && fs9.statSync(resolvedPath.resolvedPath).isDirectory()) {
|
|
15825
16107
|
return `Error: Path is a directory: ${String(args.path || "")}`;
|
|
15826
16108
|
}
|
|
15827
|
-
const dir =
|
|
16109
|
+
const dir = path10.dirname(resolvedPath.resolvedPath);
|
|
15828
16110
|
fs9.mkdirSync(dir, { recursive: true });
|
|
15829
16111
|
fs9.writeFileSync(resolvedPath.resolvedPath, content);
|
|
15830
16112
|
return "ok";
|
|
@@ -15841,7 +16123,7 @@ function createDefaultLocalTools(context) {
|
|
|
15841
16123
|
requireDirectory: true
|
|
15842
16124
|
});
|
|
15843
16125
|
if (!resolvedPath.ok) return `Error: ${resolvedPath.error}`;
|
|
15844
|
-
return fs9.readdirSync(resolvedPath.resolvedPath, { withFileTypes: true }).filter((entry) => !shouldIgnoreRepoEntry(
|
|
16126
|
+
return fs9.readdirSync(resolvedPath.resolvedPath, { withFileTypes: true }).filter((entry) => !shouldIgnoreRepoEntry(path10.join(resolvedPath.resolvedPath, entry.name))).map((entry) => entry.name).join("\n");
|
|
15845
16127
|
}
|
|
15846
16128
|
},
|
|
15847
16129
|
search_repo: {
|
|
@@ -15988,9 +16270,12 @@ function createDefaultLocalTools(context) {
|
|
|
15988
16270
|
}
|
|
15989
16271
|
};
|
|
15990
16272
|
}
|
|
15991
|
-
function createCheckpointedWriteFileTool(taskName, stateDir) {
|
|
16273
|
+
function createCheckpointedWriteFileTool(taskName, stateDir, planPath) {
|
|
15992
16274
|
const taskStateRoot = getTaskStateRoot(taskName, stateDir);
|
|
15993
|
-
const planDir =
|
|
16275
|
+
const planDir = path10.resolve(`.runtype/marathons/${stateSafeName3(taskName)}`);
|
|
16276
|
+
const marathonPlanRoot = path10.resolve(".runtype/marathons");
|
|
16277
|
+
const planPathDir = planPath ? path10.resolve(path10.dirname(planPath)) : void 0;
|
|
16278
|
+
const writeAllowedRoots = [taskStateRoot, planDir, marathonPlanRoot, ...planPathDir ? [planPathDir] : []];
|
|
15994
16279
|
return {
|
|
15995
16280
|
description: "Write content to a file, creating directories as needed and checkpointing original repo files",
|
|
15996
16281
|
parametersSchema: {
|
|
@@ -16004,14 +16289,102 @@ function createCheckpointedWriteFileTool(taskName, stateDir) {
|
|
|
16004
16289
|
execute: async (args) => {
|
|
16005
16290
|
const resolvedPath = resolveToolPath(String(args.path || ""), {
|
|
16006
16291
|
allowMissing: true,
|
|
16007
|
-
allowedRoots:
|
|
16292
|
+
allowedRoots: writeAllowedRoots
|
|
16008
16293
|
});
|
|
16009
16294
|
if (!resolvedPath.ok) return `Error: ${resolvedPath.error}`;
|
|
16010
16295
|
const content = String(args.content || "");
|
|
16011
16296
|
ensureMarathonFileCheckpoint(taskName, resolvedPath.resolvedPath, stateDir);
|
|
16012
|
-
const dir =
|
|
16297
|
+
const dir = path10.dirname(resolvedPath.resolvedPath);
|
|
16013
16298
|
fs9.mkdirSync(dir, { recursive: true });
|
|
16014
16299
|
fs9.writeFileSync(resolvedPath.resolvedPath, content);
|
|
16300
|
+
const ext = path10.extname(resolvedPath.resolvedPath).toLowerCase();
|
|
16301
|
+
if (ext === ".js" || ext === ".jsx") {
|
|
16302
|
+
const syntaxError = quickJsSyntaxCheck(content);
|
|
16303
|
+
if (syntaxError) {
|
|
16304
|
+
return `ok
|
|
16305
|
+
|
|
16306
|
+
\u26A0\uFE0F Syntax error detected in written file: ${syntaxError}. The file was written but contains errors. Read it back and fix the issues.`;
|
|
16307
|
+
}
|
|
16308
|
+
}
|
|
16309
|
+
return "ok";
|
|
16310
|
+
}
|
|
16311
|
+
};
|
|
16312
|
+
}
|
|
16313
|
+
function quickJsSyntaxCheck(content) {
|
|
16314
|
+
try {
|
|
16315
|
+
new Function(content);
|
|
16316
|
+
return void 0;
|
|
16317
|
+
} catch (err) {
|
|
16318
|
+
if (err instanceof SyntaxError) {
|
|
16319
|
+
return err.message;
|
|
16320
|
+
}
|
|
16321
|
+
return void 0;
|
|
16322
|
+
}
|
|
16323
|
+
}
|
|
16324
|
+
function createEditFileTool(taskName, stateDir, planPath) {
|
|
16325
|
+
const taskStateRoot = getTaskStateRoot(taskName, stateDir);
|
|
16326
|
+
const planDir = path10.resolve(`.runtype/marathons/${stateSafeName3(taskName)}`);
|
|
16327
|
+
const marathonPlanRoot = path10.resolve(".runtype/marathons");
|
|
16328
|
+
const planPathDir = planPath ? path10.resolve(path10.dirname(planPath)) : void 0;
|
|
16329
|
+
const writeAllowedRoots = [taskStateRoot, planDir, marathonPlanRoot, ...planPathDir ? [planPathDir] : []];
|
|
16330
|
+
return {
|
|
16331
|
+
description: "Make a targeted edit to a file by replacing a specific string. Much more efficient than rewriting the entire file with write_file. The old_string must appear exactly once in the file (or use replace_all for global replacement). Always read the file first to get the exact text to replace.",
|
|
16332
|
+
parametersSchema: {
|
|
16333
|
+
type: "object",
|
|
16334
|
+
properties: {
|
|
16335
|
+
path: { type: "string", description: "File path to edit" },
|
|
16336
|
+
// @snake-case-ok: Parameter names use snake_case to match agent tool conventions
|
|
16337
|
+
old_string: { type: "string", description: "Exact string to find and replace (must be unique in the file)" },
|
|
16338
|
+
// @snake-case-ok: Parameter names use snake_case to match agent tool conventions
|
|
16339
|
+
new_string: { type: "string", description: "Replacement string" },
|
|
16340
|
+
// @snake-case-ok: Parameter names use snake_case to match agent tool conventions
|
|
16341
|
+
replace_all: {
|
|
16342
|
+
type: "boolean",
|
|
16343
|
+
description: "If true, replace all occurrences instead of requiring uniqueness. Default: false"
|
|
16344
|
+
}
|
|
16345
|
+
},
|
|
16346
|
+
required: ["path", "old_string", "new_string"]
|
|
16347
|
+
},
|
|
16348
|
+
execute: async (args) => {
|
|
16349
|
+
const resolvedPath = resolveToolPath(String(args.path || ""), {
|
|
16350
|
+
requireFile: true,
|
|
16351
|
+
allowedRoots: writeAllowedRoots
|
|
16352
|
+
});
|
|
16353
|
+
if (!resolvedPath.ok) return `Error: ${resolvedPath.error}`;
|
|
16354
|
+
const oldString = String(args.old_string ?? "");
|
|
16355
|
+
const newString = String(args.new_string ?? "");
|
|
16356
|
+
const replaceAll = Boolean(args.replace_all);
|
|
16357
|
+
if (!oldString) return "Error: old_string cannot be empty";
|
|
16358
|
+
if (oldString === newString) return "Error: old_string and new_string are identical";
|
|
16359
|
+
let content;
|
|
16360
|
+
try {
|
|
16361
|
+
content = fs9.readFileSync(resolvedPath.resolvedPath, "utf-8");
|
|
16362
|
+
} catch {
|
|
16363
|
+
return `Error: Could not read file: ${String(args.path || "")}`;
|
|
16364
|
+
}
|
|
16365
|
+
if (!content.includes(oldString)) {
|
|
16366
|
+
return `Error: old_string not found in ${String(args.path || "")}. Read the file first to get the exact text.`;
|
|
16367
|
+
}
|
|
16368
|
+
if (!replaceAll) {
|
|
16369
|
+
const firstIndex = content.indexOf(oldString);
|
|
16370
|
+
const secondIndex = content.indexOf(oldString, firstIndex + 1);
|
|
16371
|
+
if (secondIndex !== -1) {
|
|
16372
|
+
const count = content.split(oldString).length - 1;
|
|
16373
|
+
return `Error: old_string appears ${count} times in the file. Provide more surrounding context to make it unique, or set replace_all to true.`;
|
|
16374
|
+
}
|
|
16375
|
+
}
|
|
16376
|
+
ensureMarathonFileCheckpoint(taskName, resolvedPath.resolvedPath, stateDir);
|
|
16377
|
+
const updated = replaceAll ? content.split(oldString).join(newString) : content.replace(oldString, newString);
|
|
16378
|
+
fs9.writeFileSync(resolvedPath.resolvedPath, updated);
|
|
16379
|
+
const ext = path10.extname(resolvedPath.resolvedPath).toLowerCase();
|
|
16380
|
+
if (ext === ".js" || ext === ".jsx") {
|
|
16381
|
+
const syntaxError = quickJsSyntaxCheck(updated);
|
|
16382
|
+
if (syntaxError) {
|
|
16383
|
+
return `ok
|
|
16384
|
+
|
|
16385
|
+
\u26A0\uFE0F Syntax error detected after edit: ${syntaxError}. The edit was applied but the file now contains errors.`;
|
|
16386
|
+
}
|
|
16387
|
+
}
|
|
16015
16388
|
return "ok";
|
|
16016
16389
|
}
|
|
16017
16390
|
};
|
|
@@ -16058,7 +16431,7 @@ function createReadOffloadedOutputTool(taskName, stateDir) {
|
|
|
16058
16431
|
if (!/^[a-zA-Z0-9_-]+$/.test(outputId)) {
|
|
16059
16432
|
return `Error: invalid offloaded output id: ${outputId}`;
|
|
16060
16433
|
}
|
|
16061
|
-
const outputPath =
|
|
16434
|
+
const outputPath = path10.join(getOffloadedOutputDir(taskName, stateDir), `${outputId}.txt`);
|
|
16062
16435
|
if (!fs9.existsSync(outputPath) || !fs9.statSync(outputPath).isFile()) {
|
|
16063
16436
|
return `Error: offloaded output not found: ${outputId}`;
|
|
16064
16437
|
}
|
|
@@ -16100,6 +16473,15 @@ function createRunCheckTool() {
|
|
|
16100
16473
|
error: "Blocked unsafe verification command. Use a single non-destructive lint/test/typecheck/build command."
|
|
16101
16474
|
});
|
|
16102
16475
|
}
|
|
16476
|
+
const configFiles = getScriptConfigFiles(command);
|
|
16477
|
+
if (configFiles.length > 0 && isScriptConfigModified(process.cwd(), configFiles)) {
|
|
16478
|
+
return JSON.stringify({
|
|
16479
|
+
success: false,
|
|
16480
|
+
blocked: true,
|
|
16481
|
+
command,
|
|
16482
|
+
error: `Blocked: ${configFiles.join("/")} has been modified since last commit. Cannot run script commands when config files have uncommitted changes.`
|
|
16483
|
+
});
|
|
16484
|
+
}
|
|
16103
16485
|
const timeoutMs = Math.max(
|
|
16104
16486
|
1e3,
|
|
16105
16487
|
Math.min(
|
|
@@ -16113,9 +16495,11 @@ function createRunCheckTool() {
|
|
|
16113
16495
|
encoding: "utf-8",
|
|
16114
16496
|
shell: true,
|
|
16115
16497
|
timeout: timeoutMs,
|
|
16116
|
-
maxBuffer: 1024 * 1024 * 4
|
|
16498
|
+
maxBuffer: 1024 * 1024 * 4,
|
|
16499
|
+
env: getSanitizedEnv()
|
|
16117
16500
|
});
|
|
16118
|
-
const
|
|
16501
|
+
const rawOutput = `${result.stdout || ""}${result.stderr || ""}`.trim().slice(0, 12e3);
|
|
16502
|
+
const output = redactSecrets(rawOutput, collectSecretValues());
|
|
16119
16503
|
return JSON.stringify({
|
|
16120
16504
|
success: result.status === 0 && !result.error,
|
|
16121
16505
|
command,
|
|
@@ -16134,12 +16518,64 @@ function createRunCheckTool() {
|
|
|
16134
16518
|
}
|
|
16135
16519
|
};
|
|
16136
16520
|
}
|
|
16521
|
+
function createSearchSessionHistoryTool(client, taskName) {
|
|
16522
|
+
return {
|
|
16523
|
+
description: "Search across all prior marathon sessions for specific information, decisions, findings, or tool outputs. Use this when you need to recall something from earlier sessions that may have been compacted away. Returns ranked results with content snippets from matching sessions.",
|
|
16524
|
+
parametersSchema: {
|
|
16525
|
+
type: "object",
|
|
16526
|
+
properties: {
|
|
16527
|
+
query: {
|
|
16528
|
+
type: "string",
|
|
16529
|
+
description: 'What to search for (e.g. "authentication flow decisions", "test failures in auth module")'
|
|
16530
|
+
},
|
|
16531
|
+
limit: {
|
|
16532
|
+
type: "number",
|
|
16533
|
+
description: "Maximum number of results to return (default 5, max 20)"
|
|
16534
|
+
},
|
|
16535
|
+
types: {
|
|
16536
|
+
type: "array",
|
|
16537
|
+
items: { type: "string", enum: ["response", "reasoning", "tool_output"] },
|
|
16538
|
+
description: "Filter by content type (default: all types)"
|
|
16539
|
+
}
|
|
16540
|
+
},
|
|
16541
|
+
required: ["query"]
|
|
16542
|
+
},
|
|
16543
|
+
execute: async (args) => {
|
|
16544
|
+
const query = String(args.query || "").trim();
|
|
16545
|
+
if (!query) return "Error: query is required";
|
|
16546
|
+
const limit = Math.max(1, Math.min(20, Number(args.limit) || 5));
|
|
16547
|
+
const types = Array.isArray(args.types) ? args.types : void 0;
|
|
16548
|
+
try {
|
|
16549
|
+
const response = await client.post("/session-context/search", {
|
|
16550
|
+
query,
|
|
16551
|
+
taskName,
|
|
16552
|
+
limit,
|
|
16553
|
+
...types ? { types } : {}
|
|
16554
|
+
});
|
|
16555
|
+
if (!response.success || !response.results || response.results.length === 0) {
|
|
16556
|
+
return "No matching session context found for your query.";
|
|
16557
|
+
}
|
|
16558
|
+
const formatted = response.results.map((r, i) => {
|
|
16559
|
+
const header = `[Result ${i + 1}] Session ${r.sessionIndex} | ${r.type}${r.toolName ? ` (${r.toolName})` : ""} | Score: ${r.score.toFixed(3)}`;
|
|
16560
|
+
return `${header}
|
|
16561
|
+
${r.content}`;
|
|
16562
|
+
});
|
|
16563
|
+
return `Found ${response.count} matching results:
|
|
16564
|
+
|
|
16565
|
+
${formatted.join("\n\n---\n\n")}`;
|
|
16566
|
+
} catch (error) {
|
|
16567
|
+
return `Session search unavailable: ${error instanceof Error ? error.message : String(error)}`;
|
|
16568
|
+
}
|
|
16569
|
+
}
|
|
16570
|
+
};
|
|
16571
|
+
}
|
|
16137
16572
|
function buildLocalTools(client, sandboxProvider, options, context) {
|
|
16138
16573
|
const enabledTools = {};
|
|
16139
16574
|
if (!options.noLocalTools) {
|
|
16140
16575
|
Object.assign(enabledTools, createDefaultLocalTools(context));
|
|
16141
16576
|
if (context) {
|
|
16142
|
-
enabledTools.write_file = createCheckpointedWriteFileTool(context.taskName, context.stateDir);
|
|
16577
|
+
enabledTools.write_file = createCheckpointedWriteFileTool(context.taskName, context.stateDir, context.planPath);
|
|
16578
|
+
enabledTools.edit_file = createEditFileTool(context.taskName, context.stateDir, context.planPath);
|
|
16143
16579
|
enabledTools.restore_file_checkpoint = createRestoreFileCheckpointTool(
|
|
16144
16580
|
context.taskName,
|
|
16145
16581
|
context.stateDir
|
|
@@ -16149,6 +16585,9 @@ function buildLocalTools(client, sandboxProvider, options, context) {
|
|
|
16149
16585
|
context.stateDir
|
|
16150
16586
|
);
|
|
16151
16587
|
enabledTools.run_check = createRunCheckTool();
|
|
16588
|
+
if (options.sessionSearch === true) {
|
|
16589
|
+
enabledTools.search_session_history = createSearchSessionHistoryTool(client, context.taskName);
|
|
16590
|
+
}
|
|
16152
16591
|
}
|
|
16153
16592
|
}
|
|
16154
16593
|
if (sandboxProvider) {
|
|
@@ -16160,6 +16599,62 @@ function buildLocalTools(client, sandboxProvider, options, context) {
|
|
|
16160
16599
|
return Object.keys(enabledTools).length > 0 ? enabledTools : void 0;
|
|
16161
16600
|
}
|
|
16162
16601
|
|
|
16602
|
+
// src/marathon/session-chunker.ts
|
|
16603
|
+
var DEFAULT_MAX_CHUNK_CHARS = 2e3;
|
|
16604
|
+
var MIN_CONTENT_LENGTH = 50;
|
|
16605
|
+
function extractSessionChunks(snapshot, maxChunkChars = DEFAULT_MAX_CHUNK_CHARS) {
|
|
16606
|
+
const chunks = [];
|
|
16607
|
+
if (snapshot.content && snapshot.content.length >= MIN_CONTENT_LENGTH) {
|
|
16608
|
+
chunks.push(...chunkText(snapshot.content, "response", maxChunkChars));
|
|
16609
|
+
}
|
|
16610
|
+
if (snapshot.reasoning && snapshot.reasoning.length >= MIN_CONTENT_LENGTH) {
|
|
16611
|
+
chunks.push(...chunkText(snapshot.reasoning, "reasoning", maxChunkChars));
|
|
16612
|
+
}
|
|
16613
|
+
for (const tool of snapshot.tools) {
|
|
16614
|
+
const result = typeof tool.result === "string" ? tool.result : JSON.stringify(tool.result ?? "");
|
|
16615
|
+
if (result.length >= MIN_CONTENT_LENGTH) {
|
|
16616
|
+
chunks.push(
|
|
16617
|
+
...chunkText(result, "tool_output", maxChunkChars, tool.name)
|
|
16618
|
+
);
|
|
16619
|
+
}
|
|
16620
|
+
}
|
|
16621
|
+
return chunks;
|
|
16622
|
+
}
|
|
16623
|
+
function chunkText(text, type, maxChars, toolName) {
|
|
16624
|
+
if (text.length <= maxChars) {
|
|
16625
|
+
return [{ content: text, type, ...toolName ? { toolName } : {} }];
|
|
16626
|
+
}
|
|
16627
|
+
const chunks = [];
|
|
16628
|
+
const paragraphs = text.split(/\n\n+/);
|
|
16629
|
+
let current = "";
|
|
16630
|
+
for (const paragraph of paragraphs) {
|
|
16631
|
+
if (paragraph.length > maxChars) {
|
|
16632
|
+
if (current.length >= MIN_CONTENT_LENGTH) {
|
|
16633
|
+
chunks.push({ content: current.trim(), type, ...toolName ? { toolName } : {} });
|
|
16634
|
+
current = "";
|
|
16635
|
+
}
|
|
16636
|
+
const sentences = paragraph.match(/[^.!?]+[.!?]+\s*|[^.!?]+$/g) || [paragraph];
|
|
16637
|
+
for (const sentence of sentences) {
|
|
16638
|
+
if (current.length + sentence.length > maxChars && current.length > 0) {
|
|
16639
|
+
chunks.push({ content: current.trim(), type, ...toolName ? { toolName } : {} });
|
|
16640
|
+
current = "";
|
|
16641
|
+
}
|
|
16642
|
+
current += sentence;
|
|
16643
|
+
}
|
|
16644
|
+
continue;
|
|
16645
|
+
}
|
|
16646
|
+
if (current.length + paragraph.length + 2 > maxChars && current.length >= MIN_CONTENT_LENGTH) {
|
|
16647
|
+
chunks.push({ content: current.trim(), type, ...toolName ? { toolName } : {} });
|
|
16648
|
+
current = "";
|
|
16649
|
+
}
|
|
16650
|
+
current += (current ? "\n\n" : "") + paragraph;
|
|
16651
|
+
}
|
|
16652
|
+
if (current.length >= MIN_CONTENT_LENGTH) {
|
|
16653
|
+
chunks.push({ content: current.trim(), type, ...toolName ? { toolName } : {} });
|
|
16654
|
+
}
|
|
16655
|
+
return chunks;
|
|
16656
|
+
}
|
|
16657
|
+
|
|
16163
16658
|
// src/marathon/loop-detector.ts
|
|
16164
16659
|
var DEFAULT_MAX_HISTORY = 30;
|
|
16165
16660
|
var DEFAULT_MIN_PATTERN_LENGTH = 2;
|
|
@@ -16241,7 +16736,7 @@ function createLoopDetector(options = {}) {
|
|
|
16241
16736
|
|
|
16242
16737
|
// src/marathon/context-offload.ts
|
|
16243
16738
|
import * as fs10 from "fs";
|
|
16244
|
-
import * as
|
|
16739
|
+
import * as path11 from "path";
|
|
16245
16740
|
var DEFAULT_OUTPUT_THRESHOLD = 2e3;
|
|
16246
16741
|
var DEFAULT_PREVIEW_LENGTH = 200;
|
|
16247
16742
|
function buildOffloadedOutputId(toolName) {
|
|
@@ -16273,13 +16768,13 @@ function offloadToolOutput(taskName, toolName, output, options = {}, stateDir) {
|
|
|
16273
16768
|
return { offloaded: false, content: output };
|
|
16274
16769
|
}
|
|
16275
16770
|
const outputId = buildOffloadedOutputId(toolName);
|
|
16276
|
-
const dir =
|
|
16771
|
+
const dir = path11.join(
|
|
16277
16772
|
stateDir || defaultStateDir3(),
|
|
16278
16773
|
stateSafeName4(taskName),
|
|
16279
16774
|
"outputs"
|
|
16280
16775
|
);
|
|
16281
16776
|
const fileName = `${outputId}.txt`;
|
|
16282
|
-
const filePath =
|
|
16777
|
+
const filePath = path11.join(dir, fileName);
|
|
16283
16778
|
fs10.mkdirSync(dir, { recursive: true });
|
|
16284
16779
|
fs10.writeFileSync(filePath, output, "utf-8");
|
|
16285
16780
|
const preview = output.slice(0, previewLength).replace(/\n/g, " ");
|
|
@@ -16454,17 +16949,17 @@ function resolveAutoCompactTokenThreshold(modelContextLength, rawThreshold, stra
|
|
|
16454
16949
|
|
|
16455
16950
|
// src/marathon/recipes.ts
|
|
16456
16951
|
import * as fs11 from "fs";
|
|
16457
|
-
import * as
|
|
16952
|
+
import * as path12 from "path";
|
|
16458
16953
|
var RULES_DIR = ".marathon/rules";
|
|
16459
16954
|
function loadRules(cwd) {
|
|
16460
16955
|
const baseCwd = cwd || process.cwd();
|
|
16461
|
-
const rulesDir =
|
|
16956
|
+
const rulesDir = path12.resolve(baseCwd, RULES_DIR);
|
|
16462
16957
|
if (!fs11.existsSync(rulesDir)) return [];
|
|
16463
16958
|
const rules = [];
|
|
16464
16959
|
try {
|
|
16465
16960
|
const entries = fs11.readdirSync(rulesDir).filter((f) => f.endsWith(".md"));
|
|
16466
16961
|
for (const entry of entries) {
|
|
16467
|
-
const filePath =
|
|
16962
|
+
const filePath = path12.join(rulesDir, entry);
|
|
16468
16963
|
try {
|
|
16469
16964
|
const raw = fs11.readFileSync(filePath, "utf-8");
|
|
16470
16965
|
const parsed = parseRuleFile(raw);
|
|
@@ -16541,7 +17036,7 @@ function resolveErrorHandlingForPhase(phase, cliFallbackModel, milestoneFallback
|
|
|
16541
17036
|
|
|
16542
17037
|
// src/marathon/playbook-loader.ts
|
|
16543
17038
|
import * as fs12 from "fs";
|
|
16544
|
-
import * as
|
|
17039
|
+
import * as path13 from "path";
|
|
16545
17040
|
import * as os4 from "os";
|
|
16546
17041
|
import micromatch from "micromatch";
|
|
16547
17042
|
import { parse as parseYaml } from "yaml";
|
|
@@ -16556,20 +17051,20 @@ function getCandidatePaths(nameOrPath, cwd) {
|
|
|
16556
17051
|
const home = os4.homedir();
|
|
16557
17052
|
return [
|
|
16558
17053
|
// Exact path
|
|
16559
|
-
|
|
17054
|
+
path13.resolve(cwd, nameOrPath),
|
|
16560
17055
|
// Repo-level
|
|
16561
|
-
|
|
16562
|
-
|
|
16563
|
-
|
|
17056
|
+
path13.resolve(cwd, PLAYBOOKS_DIR, `${nameOrPath}.yaml`),
|
|
17057
|
+
path13.resolve(cwd, PLAYBOOKS_DIR, `${nameOrPath}.yml`),
|
|
17058
|
+
path13.resolve(cwd, PLAYBOOKS_DIR, `${nameOrPath}.json`),
|
|
16564
17059
|
// User-level
|
|
16565
|
-
|
|
16566
|
-
|
|
16567
|
-
|
|
17060
|
+
path13.resolve(home, PLAYBOOKS_DIR, `${nameOrPath}.yaml`),
|
|
17061
|
+
path13.resolve(home, PLAYBOOKS_DIR, `${nameOrPath}.yml`),
|
|
17062
|
+
path13.resolve(home, PLAYBOOKS_DIR, `${nameOrPath}.json`)
|
|
16568
17063
|
];
|
|
16569
17064
|
}
|
|
16570
17065
|
function parsePlaybookFile(filePath) {
|
|
16571
17066
|
const raw = fs12.readFileSync(filePath, "utf-8");
|
|
16572
|
-
const ext =
|
|
17067
|
+
const ext = path13.extname(filePath).toLowerCase();
|
|
16573
17068
|
if (ext === ".json") {
|
|
16574
17069
|
return JSON.parse(raw);
|
|
16575
17070
|
}
|
|
@@ -16835,6 +17330,8 @@ function buildMarathonStartupModelOptions(configuredModels, availableModels) {
|
|
|
16835
17330
|
for (const model of configuredModels) {
|
|
16836
17331
|
const modelId = model.modelId?.trim();
|
|
16837
17332
|
if (!modelId) continue;
|
|
17333
|
+
const provider = model.provider?.trim().toLowerCase() ?? "";
|
|
17334
|
+
if ((0, import_integration_providers.isNonAIProvider)(provider) || (0, import_integration_providers.isNonAIProvider)(modelId)) continue;
|
|
16838
17335
|
pushOption({
|
|
16839
17336
|
label: model.displayName?.trim() || modelId,
|
|
16840
17337
|
value: modelId,
|
|
@@ -16846,6 +17343,8 @@ function buildMarathonStartupModelOptions(configuredModels, availableModels) {
|
|
|
16846
17343
|
for (const provider of model.providers) {
|
|
16847
17344
|
const modelId2 = provider.modelId?.trim() || model.baseModel?.trim();
|
|
16848
17345
|
if (!modelId2) continue;
|
|
17346
|
+
const providerName = provider.provider?.trim().toLowerCase() ?? "";
|
|
17347
|
+
if ((0, import_integration_providers.isNonAIProvider)(providerName) || (0, import_integration_providers.isNonAIProvider)(modelId2)) continue;
|
|
16849
17348
|
pushOption({
|
|
16850
17349
|
label: provider.displayName?.trim() || model.displayName?.trim() || model.baseModel?.trim() || modelId2,
|
|
16851
17350
|
value: modelId2,
|
|
@@ -16856,6 +17355,7 @@ function buildMarathonStartupModelOptions(configuredModels, availableModels) {
|
|
|
16856
17355
|
}
|
|
16857
17356
|
const modelId = model.baseModel?.trim();
|
|
16858
17357
|
if (!modelId) continue;
|
|
17358
|
+
if ((0, import_integration_providers.isNonAIProvider)(modelId)) continue;
|
|
16859
17359
|
pushOption({
|
|
16860
17360
|
label: model.displayName?.trim() || modelId,
|
|
16861
17361
|
value: modelId,
|
|
@@ -17021,14 +17521,15 @@ async function taskAction(agent, options) {
|
|
|
17021
17521
|
const renderedShell = render9(
|
|
17022
17522
|
React9.createElement(MarathonStartupShell, {
|
|
17023
17523
|
startupRef: startupShellRef,
|
|
17024
|
-
marathonAppProps: null
|
|
17524
|
+
marathonAppProps: null,
|
|
17525
|
+
version: getCliVersion()
|
|
17025
17526
|
}),
|
|
17026
17527
|
{ exitOnCtrlC: false }
|
|
17027
17528
|
);
|
|
17028
17529
|
waitForUiExit = renderedShell.waitUntilExit;
|
|
17029
17530
|
rerenderUi = renderedShell.rerender;
|
|
17030
17531
|
unmountUi = renderedShell.unmount;
|
|
17031
|
-
await new Promise((
|
|
17532
|
+
await new Promise((resolve8) => setTimeout(resolve8, 0));
|
|
17032
17533
|
if (!startupShellRef.current) {
|
|
17033
17534
|
exitAltScreen();
|
|
17034
17535
|
unmountUi?.();
|
|
@@ -17090,6 +17591,9 @@ async function taskAction(agent, options) {
|
|
|
17090
17591
|
console.log(chalk16.green(`Created agent: ${agentId}`));
|
|
17091
17592
|
}
|
|
17092
17593
|
} catch (createErr) {
|
|
17594
|
+
if (isTransientNetworkError(createErr)) {
|
|
17595
|
+
await failBeforeMain(formatMarathonApiError(createErr));
|
|
17596
|
+
}
|
|
17093
17597
|
const errMsg = createErr instanceof Error ? createErr.message : String(createErr);
|
|
17094
17598
|
await failBeforeMain([
|
|
17095
17599
|
chalk16.red(`Failed to create agent "${normalizedAgent}"`),
|
|
@@ -17098,6 +17602,9 @@ async function taskAction(agent, options) {
|
|
|
17098
17602
|
}
|
|
17099
17603
|
}
|
|
17100
17604
|
} catch (error) {
|
|
17605
|
+
if (isTransientNetworkError(error)) {
|
|
17606
|
+
await failBeforeMain(formatMarathonApiError(error));
|
|
17607
|
+
}
|
|
17101
17608
|
const errMsg = error instanceof Error ? error.message : String(error);
|
|
17102
17609
|
await failBeforeMain([
|
|
17103
17610
|
chalk16.red("Failed to list agents"),
|
|
@@ -17402,9 +17909,11 @@ ${rulesContext}`;
|
|
|
17402
17909
|
});
|
|
17403
17910
|
}
|
|
17404
17911
|
}
|
|
17912
|
+
const resolvedPlanPath = resumeLoadedState?.planPath ?? resumeState?.planPath ?? getDefaultPlanPath(taskName);
|
|
17405
17913
|
let localTools = buildLocalTools(client, parsedSandbox, options, {
|
|
17406
17914
|
taskName,
|
|
17407
|
-
stateDir: options.stateDir
|
|
17915
|
+
stateDir: options.stateDir,
|
|
17916
|
+
planPath: resolvedPlanPath
|
|
17408
17917
|
});
|
|
17409
17918
|
const resolveContextLimitForModel = (modelId) => resolveModelContextLength(modelId, configuredModels, availableModels);
|
|
17410
17919
|
const resolveCompactStrategyForModel = (modelId) => {
|
|
@@ -17464,7 +17973,8 @@ ${rulesContext}`;
|
|
|
17464
17973
|
const checkpointHasConfigChanges = (result) => Boolean(result.model) || result.tools === true || result.sandbox !== void 0;
|
|
17465
17974
|
const rebuildCheckpointTools = () => buildLocalTools(client, parsedSandbox, options, {
|
|
17466
17975
|
taskName,
|
|
17467
|
-
stateDir: options.stateDir
|
|
17976
|
+
stateDir: options.stateDir,
|
|
17977
|
+
planPath: resolvedPlanPath
|
|
17468
17978
|
});
|
|
17469
17979
|
const applyCheckpointConfig = (result) => {
|
|
17470
17980
|
if (result.model) {
|
|
@@ -17551,7 +18061,8 @@ Saving state... done. Session saved to ${filePath}`);
|
|
|
17551
18061
|
rerenderUi?.(
|
|
17552
18062
|
React9.createElement(MarathonStartupShell, {
|
|
17553
18063
|
startupRef: startupShellRef,
|
|
17554
|
-
marathonAppProps
|
|
18064
|
+
marathonAppProps,
|
|
18065
|
+
version: getCliVersion()
|
|
17555
18066
|
})
|
|
17556
18067
|
);
|
|
17557
18068
|
await startupShellRef.current?.completeStartup();
|
|
@@ -17564,7 +18075,7 @@ Saving state... done. Session saved to ${filePath}`);
|
|
|
17564
18075
|
waitForUiExit = renderedApp.waitUntilExit;
|
|
17565
18076
|
unmountUi = renderedApp.unmount;
|
|
17566
18077
|
}
|
|
17567
|
-
await new Promise((
|
|
18078
|
+
await new Promise((resolve8) => setTimeout(resolve8, 0));
|
|
17568
18079
|
const streamActions = streamRef.current;
|
|
17569
18080
|
if (!streamActions) {
|
|
17570
18081
|
exitAltScreen();
|
|
@@ -17689,7 +18200,7 @@ Saving state... done. Session saved to ${filePath}`);
|
|
|
17689
18200
|
};
|
|
17690
18201
|
if (event.phase === "start") {
|
|
17691
18202
|
currentActions.startContextCompaction(absoluteEvent);
|
|
17692
|
-
await new Promise((
|
|
18203
|
+
await new Promise((resolve8) => setTimeout(resolve8, 0));
|
|
17693
18204
|
return;
|
|
17694
18205
|
}
|
|
17695
18206
|
currentActions.finishContextCompaction(absoluteEvent);
|
|
@@ -17752,6 +18263,21 @@ Saving state... done. Session saved to ${filePath}`);
|
|
|
17752
18263
|
resumeState = extractRunTaskResumeState(adjustedState);
|
|
17753
18264
|
lastSessionMessages = state.messages ?? [];
|
|
17754
18265
|
saveState(filePath, adjustedState, { stripSnapshotEvents: !!eventLogWriter });
|
|
18266
|
+
if (options.sessionSearch === true) {
|
|
18267
|
+
const latestSnapshot = persistedSessionSnapshots[persistedSessionSnapshots.length - 1];
|
|
18268
|
+
if (latestSnapshot) {
|
|
18269
|
+
const chunks = extractSessionChunks(latestSnapshot);
|
|
18270
|
+
if (chunks.length > 0) {
|
|
18271
|
+
const sessionIdx = currentSessionOffset + state.sessionCount - 1;
|
|
18272
|
+
client.post("/session-context/index", {
|
|
18273
|
+
taskName,
|
|
18274
|
+
sessionIndex: sessionIdx,
|
|
18275
|
+
chunks
|
|
18276
|
+
}).catch(() => {
|
|
18277
|
+
});
|
|
18278
|
+
}
|
|
18279
|
+
}
|
|
18280
|
+
}
|
|
17755
18281
|
if (resumeState?.workflowPhase) {
|
|
17756
18282
|
const displayMilestone = detectedVariant === "external" && resumeState.workflowPhase === "research" && adjustedState.planWritten ? "report" : resumeState.workflowPhase;
|
|
17757
18283
|
streamRef.current?.updateMilestone(displayMilestone);
|
|
@@ -17916,7 +18442,7 @@ Saving state... done. Session saved to ${filePath}`);
|
|
|
17916
18442
|
toolCallsMade: accumulatedToolCalls,
|
|
17917
18443
|
tokensInput: accumulatedTokens.input,
|
|
17918
18444
|
tokensOutput: accumulatedTokens.output,
|
|
17919
|
-
cost: accumulatedCost,
|
|
18445
|
+
cost: priorCost + accumulatedCost,
|
|
17920
18446
|
outputPreview: terminalPreview.slice(0, 100)
|
|
17921
18447
|
};
|
|
17922
18448
|
const checkpointResult = await currentActions.requestCheckpoint(recap, true);
|
|
@@ -18127,7 +18653,7 @@ ${details}`);
|
|
|
18127
18653
|
}
|
|
18128
18654
|
return resolved;
|
|
18129
18655
|
}
|
|
18130
|
-
function detectDeployWorkflow(_message,
|
|
18656
|
+
function detectDeployWorkflow(_message, _sandboxProvider, resumeState) {
|
|
18131
18657
|
if (resumeState?.workflowVariant === "game") return gameWorkflow;
|
|
18132
18658
|
if (resumeState?.workflowPhase === "design" || resumeState?.workflowPhase === "build" || resumeState?.workflowPhase === "verify") {
|
|
18133
18659
|
return gameWorkflow;
|
|
@@ -18158,7 +18684,7 @@ function resolveSandboxWorkflowSelection(message, sandboxProvider, resumeState)
|
|
|
18158
18684
|
};
|
|
18159
18685
|
}
|
|
18160
18686
|
function applyTaskOptions(cmd) {
|
|
18161
|
-
return cmd.argument("<agent>", "Agent ID or name").option("-g, --goal <text>", "Goal message for the agent").option("--max-sessions <n>", "Maximum sessions", "50").option("--max-cost <n>", "Budget in USD").option("--model <modelId>", "Model ID to use (overrides agent config)").option("--name <name>", "Task name (used for state file, defaults to agent name)").option("--session <name>", "Resume a specific session by name").option("--state-dir <path>", "Directory for state files (default: ~/.runtype/projects/<hash>/marathons/)").option("--resume [message]", "Resume from existing local state, optionally with a new message").option("--fresh", "Start a new run and ignore any existing local state for this task").option("--compact", "Force compact-summary resume mode instead of replaying full history").option("--compact-strategy <strategy>", "Compaction strategy: auto (default), provider_native, or summary_fallback").option("--compact-threshold <value>", "Auto-compact when estimated context crosses this threshold (default: 80% fallback, 90% native; accepts percent like 90% or absolute token count like 120000)").option("--compact-instructions <text>", "Extra instructions for what a compact summary must preserve").option("--no-auto-compact", "Disable automatic context-aware history compaction").option("--track", "Sync progress to a Runtype record (visible in dashboard)").option("--debug", "Show debug output from each session").option("--json", "Output final result as JSON").option("--sandbox <provider>", "Enable sandbox code execution tool (cloudflare-worker, quickjs, or daytona)").option("--no-local-tools", "Disable built-in local tool execution (read_file, write_file, list_directory)").option("-t, --tools <tools...>", "Enable built-in tools (e.g., exa, firecrawl, dalle, openai_web_search, anthropic_web_search)").option("--plain-text", "Disable markdown rendering in output").option("--no-reasoning", "Disable model reasoning/thinking (enabled by default for supported models)").option("--no-checkpoint", "Run all iterations without checkpoint pauses (fully autonomous)").option("--checkpoint-timeout <seconds>", "Auto-continue timeout in seconds (default: 10)", "10").option("--planning-model <modelId>", "Model to use during research/planning phases").option("--execution-model <modelId>", "Model to use during execution phase").option("--fallback-model <modelId>", "Model to fall back to when primary model fails").option("--playbook <name>", "Load a playbook from .runtype/marathons/playbooks/").option("--offload-threshold <chars>", 'Offload tool outputs larger than this to files (default: 100000; use "off" or "0" to disable guardrails)').option("--tool-context <mode>", "Tool result storage: hot-tail (default), observation-mask, or full-inline").option("--tool-window <window>", 'Compaction window: "session" (default) or a number for last-N tool results (e.g. 10)').option("--runner-char <char>", "Custom runner emoji (default: \u{1F3C3})").option("--finish-char <char>", "Custom finish line emoji (default: \u{1F3C1})").option("--no-runner", "Hide the runner emoji from the header border").option("--no-finish", "Hide the finish line emoji from the header border").action(taskAction);
|
|
18687
|
+
return cmd.argument("<agent>", "Agent ID or name").option("-g, --goal <text>", "Goal message for the agent").option("--max-sessions <n>", "Maximum sessions", "50").option("--max-cost <n>", "Budget in USD").option("--model <modelId>", "Model ID to use (overrides agent config)").option("--name <name>", "Task name (used for state file, defaults to agent name)").option("--session <name>", "Resume a specific session by name").option("--state-dir <path>", "Directory for state files (default: ~/.runtype/projects/<hash>/marathons/)").option("--resume [message]", "Resume from existing local state, optionally with a new message").option("--fresh", "Start a new run and ignore any existing local state for this task").option("--compact", "Force compact-summary resume mode instead of replaying full history").option("--compact-strategy <strategy>", "Compaction strategy: auto (default), provider_native, or summary_fallback").option("--compact-threshold <value>", "Auto-compact when estimated context crosses this threshold (default: 80% fallback, 90% native; accepts percent like 90% or absolute token count like 120000)").option("--compact-instructions <text>", "Extra instructions for what a compact summary must preserve").option("--no-auto-compact", "Disable automatic context-aware history compaction").option("--track", "Sync progress to a Runtype record (visible in dashboard)").option("--debug", "Show debug output from each session").option("--json", "Output final result as JSON").option("--sandbox <provider>", "Enable sandbox code execution tool (cloudflare-worker, quickjs, or daytona)").option("--no-local-tools", "Disable built-in local tool execution (read_file, write_file, list_directory)").option("--session-search", "Enable session context indexing and search_session_history tool").option("-t, --tools <tools...>", "Enable built-in tools (e.g., exa, firecrawl, dalle, openai_web_search, anthropic_web_search)").option("--plain-text", "Disable markdown rendering in output").option("--no-reasoning", "Disable model reasoning/thinking (enabled by default for supported models)").option("--no-checkpoint", "Run all iterations without checkpoint pauses (fully autonomous)").option("--checkpoint-timeout <seconds>", "Auto-continue timeout in seconds (default: 10)", "10").option("--planning-model <modelId>", "Model to use during research/planning phases").option("--execution-model <modelId>", "Model to use during execution phase").option("--fallback-model <modelId>", "Model to fall back to when primary model fails").option("--playbook <name>", "Load a playbook from .runtype/marathons/playbooks/").option("--offload-threshold <chars>", 'Offload tool outputs larger than this to files (default: 100000; use "off" or "0" to disable guardrails)').option("--tool-context <mode>", "Tool result storage: hot-tail (default), observation-mask, or full-inline").option("--tool-window <window>", 'Compaction window: "session" (default) or a number for last-N tool results (e.g. 10)').option("--runner-char <char>", "Custom runner emoji (default: \u{1F3C3})").option("--finish-char <char>", "Custom finish line emoji (default: \u{1F3C1})").option("--no-runner", "Hide the runner emoji from the header border").option("--no-finish", "Hide the finish line emoji from the header border").action(taskAction);
|
|
18162
18688
|
}
|
|
18163
18689
|
var taskCommand = applyTaskOptions(
|
|
18164
18690
|
new Command10("task").description("Run a multi-session agent task")
|
|
@@ -19646,13 +20172,13 @@ apiKeysCommand.command("delete <id>").description("Delete an API key").option("-
|
|
|
19646
20172
|
await waitUntilExit2();
|
|
19647
20173
|
return;
|
|
19648
20174
|
}
|
|
19649
|
-
const confirmed = await new Promise((
|
|
20175
|
+
const confirmed = await new Promise((resolve8) => {
|
|
19650
20176
|
const { unmount } = render14(
|
|
19651
20177
|
React14.createElement(ConfirmPrompt, {
|
|
19652
20178
|
message: `Delete API key ${id}?`,
|
|
19653
20179
|
defaultValue: false,
|
|
19654
20180
|
onConfirm: (result) => {
|
|
19655
|
-
|
|
20181
|
+
resolve8(result);
|
|
19656
20182
|
unmount();
|
|
19657
20183
|
}
|
|
19658
20184
|
})
|
|
@@ -19757,8 +20283,8 @@ apiKeysCommand.command("analytics").description("Show API key usage analytics").
|
|
|
19757
20283
|
const client = new ApiClient(apiKey);
|
|
19758
20284
|
if (!isTTY(options) || options.json) {
|
|
19759
20285
|
try {
|
|
19760
|
-
const
|
|
19761
|
-
const data = await client.get(
|
|
20286
|
+
const path14 = options.key ? `/api-keys/${options.key}/analytics` : "/api-keys/analytics";
|
|
20287
|
+
const data = await client.get(path14);
|
|
19762
20288
|
printJson(data);
|
|
19763
20289
|
} catch (error) {
|
|
19764
20290
|
const message = error instanceof Error ? error.message : "Unknown error";
|
|
@@ -19775,8 +20301,8 @@ apiKeysCommand.command("analytics").description("Show API key usage analytics").
|
|
|
19775
20301
|
useEffect25(() => {
|
|
19776
20302
|
const run = async () => {
|
|
19777
20303
|
try {
|
|
19778
|
-
const
|
|
19779
|
-
const data = await client.get(
|
|
20304
|
+
const path14 = options.key ? `/api-keys/${options.key}/analytics` : "/api-keys/analytics";
|
|
20305
|
+
const data = await client.get(path14);
|
|
19780
20306
|
printJson(data);
|
|
19781
20307
|
setSuccess(true);
|
|
19782
20308
|
setLoading(false);
|