agent-yes 1.196.3 → 1.197.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/default.config.yaml +51 -12
- package/dist/{SUPPORTED_CLIS-D3knhJ6_.js → SUPPORTED_CLIS-BY83eM58.js} +2 -2
- package/dist/SUPPORTED_CLIS-D4Z--hGm.js +9 -0
- package/dist/{agent-yes.config-ugPYTPOd.js → agent-yes.config-whqkqq9P.js} +2 -2
- package/dist/{agentShare-M6FE9AsI.js → agentShare-DWioPObE.js} +8 -7
- package/dist/cli.js +8 -8
- package/dist/{configShared-0MnIQ652.js → configShared-aKTg-sa5.js} +1 -1
- package/dist/{e2e-BeKjLhmO.js → e2e-jb0Hp43q.js} +1 -1
- package/dist/{expose-B3AGhVhM.js → expose-k9l6ZJDA.js} +1 -1
- package/dist/{forkNested-C1kKsXi5.js → forkNested-DOyDAHnC.js} +1 -1
- package/dist/index.js +3 -2
- package/dist/messageLog-CxrKJj77.js +94 -0
- package/dist/{nodeRuntime-DZRjxpNp.js → nodeRuntime-CwNJuwH5.js} +1 -1
- package/dist/{notifyDaemon-DgxP6GAn.js → notifyDaemon-CRf_27kE.js} +7 -6
- package/dist/{openBrowser-DQVkxCyj.js → openBrowser-CSj21pZE.js} +1 -1
- package/dist/{reaper-CWF2_ATd.js → reaper-BUHCyxdF.js} +1 -1
- package/dist/{reaper-jfSmog15.js → reaper-CJSu8UVZ.js} +1 -1
- package/dist/{remotes-CW-yxSgW.js → remotes-CXqYR0p8.js} +3 -3
- package/dist/{remotes-oSmwSYaV.js → remotes-Cim0dBU7.js} +3 -3
- package/dist/{rustBinary-C9q17obe.js → rustBinary-5HCt7_0b.js} +2 -2
- package/dist/{schedule-SisMmURQ.js → schedule-CjBcOy-b.js} +7 -6
- package/dist/{serve-BOrkFR6H.js → serve-C3E8uicL.js} +26 -25
- package/dist/{setup-CFbckIlf.js → setup-C4SKqGy1.js} +3 -3
- package/dist/{share-29YbzpbU.js → share-BfIU8t_h.js} +2 -2
- package/dist/{share-nvqy64A2.js → share-Ciw1mWVN.js} +2 -2
- package/dist/{spawnGate-CWbdnJ5K.js → spawnGate-BkjeS4tg.js} +1 -1
- package/dist/{spawnGate-DRWa_Fzj.js → spawnGate-CXjEz6Pf.js} +2 -2
- package/dist/{subcommands-CMX-dej0.js → subcommands-B7UxIJRw.js} +17 -103
- package/dist/{subcommands-DSbYPDt5.js → subcommands-DzWBZ6HE.js} +6 -5
- package/dist/{tray-BKTZ5gIf.js → tray-BSFi35Qg.js} +1 -1
- package/dist/{ts-Bqa5HjY3.js → ts-B64pNQlI.js} +84 -7
- package/dist/{versionChecker-ZIgviJ62.js → versionChecker-DKiSuTSp.js} +2 -2
- package/dist/{webrtcLink-BG0Xc4-W.js → webrtcLink-B7REGtK2.js} +2 -2
- package/dist/{webrtcRemote-BmuVKcrx.js → webrtcRemote-CvIEfWX8.js} +3 -3
- package/dist/{workspaceConfig-_GtAZtsi.js → workspaceConfig-CjaRvTjf.js} +1 -1
- package/package.json +1 -1
- package/ts/autoRetry.spec.ts +90 -1
- package/ts/autoRetry.ts +76 -0
- package/ts/index.ts +37 -4
- package/ts/messageLog.ts +3 -2
- package/ts/subcommands.ts +9 -5
- package/dist/SUPPORTED_CLIS-L3rJRumq.js +0 -8
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { a as computeTranscriptHash, c as open$1, d as randomHex, f as seal, i as MAX_CHUNK, l as packEnvelope, n as FLAG_CONFIRM, o as deriveAuthToken, p as unpackEnvelope, r as MARKER, s as deriveDirKeys, t as CONFIRM_TIMEOUT_MS, u as parseSecret } from "./e2e-
|
|
1
|
+
import { a as computeTranscriptHash, c as open$1, d as randomHex, f as seal, i as MAX_CHUNK, l as packEnvelope, n as FLAG_CONFIRM, o as deriveAuthToken, p as unpackEnvelope, r as MARKER, s as deriveDirKeys, t as CONFIRM_TIMEOUT_MS, u as parseSecret } from "./e2e-jb0Hp43q.js";
|
|
2
2
|
import { mkdir, readFile, writeFile } from "fs/promises";
|
|
3
3
|
import { homedir } from "os";
|
|
4
4
|
import path from "path";
|
|
@@ -534,4 +534,4 @@ async function startShare(opts) {
|
|
|
534
534
|
|
|
535
535
|
//#endregion
|
|
536
536
|
export { shareLinkFromRoomUrl as n, startShare as r, loadOrCreateShareRoom as t };
|
|
537
|
-
//# sourceMappingURL=share-
|
|
537
|
+
//# sourceMappingURL=share-BfIU8t_h.js.map
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import "./e2e-
|
|
2
|
-
import { n as shareLinkFromRoomUrl, r as startShare, t as loadOrCreateShareRoom } from "./share-
|
|
1
|
+
import "./e2e-jb0Hp43q.js";
|
|
2
|
+
import { n as shareLinkFromRoomUrl, r as startShare, t as loadOrCreateShareRoom } from "./share-BfIU8t_h.js";
|
|
3
3
|
|
|
4
4
|
export { loadOrCreateShareRoom, shareLinkFromRoomUrl, startShare };
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import "./logger-CDIsZ-Pp.js";
|
|
2
2
|
import "./globalPidIndex-CoNr7tS8.js";
|
|
3
|
-
import { i as waitForSpawnCapacity, n as spawnGateEnabled, r as spawnRejectionReason, t as memAvailableMb } from "./spawnGate-
|
|
3
|
+
import { i as waitForSpawnCapacity, n as spawnGateEnabled, r as spawnRejectionReason, t as memAvailableMb } from "./spawnGate-CXjEz6Pf.js";
|
|
4
4
|
|
|
5
5
|
export { waitForSpawnCapacity };
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import { i as readGlobalPids } from "./globalPidIndex-CoNr7tS8.js";
|
|
2
|
-
import { n as getMinFreeMb, o as getSpawnWaitMs, t as getMaxAgents } from "./workspaceConfig-
|
|
2
|
+
import { n as getMinFreeMb, o as getSpawnWaitMs, t as getMaxAgents } from "./workspaceConfig-CjaRvTjf.js";
|
|
3
3
|
import { readFile } from "fs/promises";
|
|
4
4
|
|
|
5
5
|
//#region ts/spawnGate.ts
|
|
@@ -104,4 +104,4 @@ async function waitForSpawnCapacity(opts) {
|
|
|
104
104
|
|
|
105
105
|
//#endregion
|
|
106
106
|
export { waitForSpawnCapacity as i, spawnGateEnabled as n, spawnRejectionReason as r, memAvailableMb as t };
|
|
107
|
-
//# sourceMappingURL=spawnGate-
|
|
107
|
+
//# sourceMappingURL=spawnGate-CXjEz6Pf.js.map
|
|
@@ -2,9 +2,10 @@ import { t as invokedCliName } from "./invokedCli-uqM2YYA7.js";
|
|
|
2
2
|
import { n as logger } from "./logger-CDIsZ-Pp.js";
|
|
3
3
|
import { t as agentYesHome } from "./agentYesHome-CtHb5b71.js";
|
|
4
4
|
import { a as updateGlobalPidStatus, i as readGlobalPids } from "./globalPidIndex-CoNr7tS8.js";
|
|
5
|
-
import { t as
|
|
6
|
-
import {
|
|
7
|
-
import {
|
|
5
|
+
import { a as recordOutbox, i as recordMessage, n as readMailbox, t as partyMatches } from "./messageLog-CxrKJj77.js";
|
|
6
|
+
import { t as loadSharedCliDefaults } from "./configShared-aKTg-sa5.js";
|
|
7
|
+
import { n as isWebrtcSpec } from "./webrtcLink-B7REGtK2.js";
|
|
8
|
+
import { a as resolveRemoteSpec, i as readRemotes } from "./remotes-Cim0dBU7.js";
|
|
8
9
|
import ms from "ms";
|
|
9
10
|
import yargs from "yargs";
|
|
10
11
|
import { appendFile, mkdir, open, readFile, readdir, rename, rm, stat, writeFile } from "fs/promises";
|
|
@@ -161,94 +162,6 @@ function parseTaskCounts(lines) {
|
|
|
161
162
|
return best;
|
|
162
163
|
}
|
|
163
164
|
|
|
164
|
-
//#endregion
|
|
165
|
-
//#region ts/messageLog.ts
|
|
166
|
-
/**
|
|
167
|
-
* Durable inter-agent message log.
|
|
168
|
-
*
|
|
169
|
-
* Every `ay send` that carries a real body is recorded twice — from the two
|
|
170
|
-
* ends' points of view — as append-only JSONL colocated with each agent's
|
|
171
|
-
* project dir (the same `<cwd>/.agent-yes/` convention the session logs use):
|
|
172
|
-
*
|
|
173
|
-
* - the SENDER's `<from.cwd>/.agent-yes/outbox.jsonl`
|
|
174
|
-
* - the RECIPIENT's `<to.cwd>/.agent-yes/inbox.jsonl`
|
|
175
|
-
*
|
|
176
|
-
* A single cwd may host several agents, so records carry the stable `agent_id`
|
|
177
|
-
* (falling back to `pid`) of each end; `ay msgs` filters a mailbox down to one
|
|
178
|
-
* agent by that key. Reading needs no lock (last line wins isn't relevant — a
|
|
179
|
-
* message log keeps every entry); writing is best-effort and never blocks or
|
|
180
|
-
* fails a send.
|
|
181
|
-
*/
|
|
182
|
-
/** Keep at most this many lines per mailbox; older entries are compacted away. */
|
|
183
|
-
const MAILBOX_MAX_LINES = 2e3;
|
|
184
|
-
/** Path to a cwd's mailbox file (`<cwd>/.agent-yes/{inbox,outbox}.jsonl`). */
|
|
185
|
-
function mailboxPath(cwd, box) {
|
|
186
|
-
return path.join(cwd, ".agent-yes", `${box}.jsonl`);
|
|
187
|
-
}
|
|
188
|
-
/** Whether a mail party is the agent identified by (agentId, pid). Prefers the
|
|
189
|
-
* stable agent_id (survives restart); falls back to pid for legacy records. */
|
|
190
|
-
function partyMatches(party, agentId, pid) {
|
|
191
|
-
if (!party) return false;
|
|
192
|
-
if (agentId && party.agent_id && party.agent_id === agentId) return true;
|
|
193
|
-
if (typeof pid === "number" && party.pid === pid) return true;
|
|
194
|
-
return false;
|
|
195
|
-
}
|
|
196
|
-
async function appendCapped(filePath, record) {
|
|
197
|
-
await mkdir(path.dirname(filePath), { recursive: true });
|
|
198
|
-
await appendFile(filePath, JSON.stringify(record) + "\n");
|
|
199
|
-
const lines = (await readFile(filePath, "utf-8").catch(() => "")).split("\n").filter((l) => l.trim());
|
|
200
|
-
if (lines.length > MAILBOX_MAX_LINES) await writeFile(filePath, lines.slice(lines.length - MAILBOX_MAX_LINES).join("\n") + "\n");
|
|
201
|
-
}
|
|
202
|
-
/**
|
|
203
|
-
* Record the SENDER's view in its outbox. Best-effort — a filesystem error is
|
|
204
|
-
* logged and swallowed so persistence never breaks a send. The outbox lives
|
|
205
|
-
* under `from.cwd`; a human sender (from === null) writes under `process.cwd()`.
|
|
206
|
-
*/
|
|
207
|
-
async function recordOutbox(record) {
|
|
208
|
-
const outCwd = record.from?.cwd ?? process.cwd();
|
|
209
|
-
try {
|
|
210
|
-
await appendCapped(mailboxPath(outCwd, "outbox"), record);
|
|
211
|
-
} catch (err) {
|
|
212
|
-
logger.debug(`[messageLog] outbox append failed: ${err}`);
|
|
213
|
-
}
|
|
214
|
-
}
|
|
215
|
-
/** Record the RECIPIENT's view in its inbox (under `to.cwd`). Best-effort. */
|
|
216
|
-
async function recordInbox(record) {
|
|
217
|
-
try {
|
|
218
|
-
await appendCapped(mailboxPath(record.to.cwd, "inbox"), record);
|
|
219
|
-
} catch (err) {
|
|
220
|
-
logger.debug(`[messageLog] inbox append failed: ${err}`);
|
|
221
|
-
}
|
|
222
|
-
}
|
|
223
|
-
/**
|
|
224
|
-
* Record a same-host message in both mailboxes — the sender's outbox and the
|
|
225
|
-
* recipient's inbox both live on this machine. For a message that crossed the
|
|
226
|
-
* wire, each end calls `recordOutbox`/`recordInbox` on its own host instead.
|
|
227
|
-
*/
|
|
228
|
-
async function recordMessage(record) {
|
|
229
|
-
await recordOutbox(record);
|
|
230
|
-
await recordInbox(record);
|
|
231
|
-
}
|
|
232
|
-
/** Read and parse a cwd's mailbox, oldest first. Missing/corrupt lines skipped. */
|
|
233
|
-
async function readMailbox(cwd, box) {
|
|
234
|
-
let raw;
|
|
235
|
-
try {
|
|
236
|
-
raw = await readFile(mailboxPath(cwd, box), "utf-8");
|
|
237
|
-
} catch {
|
|
238
|
-
return [];
|
|
239
|
-
}
|
|
240
|
-
const out = [];
|
|
241
|
-
for (const line of raw.split("\n")) {
|
|
242
|
-
const t = line.trim();
|
|
243
|
-
if (!t) continue;
|
|
244
|
-
try {
|
|
245
|
-
const rec = JSON.parse(t);
|
|
246
|
-
if (rec && typeof rec.at === "number" && rec.to) out.push(rec);
|
|
247
|
-
} catch {}
|
|
248
|
-
}
|
|
249
|
-
return out;
|
|
250
|
-
}
|
|
251
|
-
|
|
252
165
|
//#endregion
|
|
253
166
|
//#region ts/badges.ts
|
|
254
167
|
const BADGE_DEFS = [
|
|
@@ -1271,27 +1184,27 @@ async function runSubcommand(argv) {
|
|
|
1271
1184
|
case "restart": return await cmdRestart(rest);
|
|
1272
1185
|
case "note": return await cmdNote(rest);
|
|
1273
1186
|
case "serve": {
|
|
1274
|
-
const { cmdServe } = await import("./serve-
|
|
1187
|
+
const { cmdServe } = await import("./serve-C3E8uicL.js");
|
|
1275
1188
|
return cmdServe(rest);
|
|
1276
1189
|
}
|
|
1277
1190
|
case "setup": {
|
|
1278
|
-
const { cmdSetup } = await import("./setup-
|
|
1191
|
+
const { cmdSetup } = await import("./setup-C4SKqGy1.js");
|
|
1279
1192
|
return cmdSetup(rest);
|
|
1280
1193
|
}
|
|
1281
1194
|
case "schedule": {
|
|
1282
|
-
const { cmdSchedule } = await import("./schedule-
|
|
1195
|
+
const { cmdSchedule } = await import("./schedule-CjBcOy-b.js");
|
|
1283
1196
|
return cmdSchedule(rest);
|
|
1284
1197
|
}
|
|
1285
1198
|
case "remote": {
|
|
1286
|
-
const { cmdRemote } = await import("./remotes-
|
|
1199
|
+
const { cmdRemote } = await import("./remotes-CXqYR0p8.js");
|
|
1287
1200
|
return cmdRemote(rest);
|
|
1288
1201
|
}
|
|
1289
1202
|
case "expose": {
|
|
1290
|
-
const { cmdExpose } = await import("./expose-
|
|
1203
|
+
const { cmdExpose } = await import("./expose-k9l6ZJDA.js");
|
|
1291
1204
|
return cmdExpose(rest);
|
|
1292
1205
|
}
|
|
1293
1206
|
case "reap":
|
|
1294
|
-
await (await import("./reaper-
|
|
1207
|
+
await (await import("./reaper-CJSu8UVZ.js")).sweep();
|
|
1295
1208
|
return 0;
|
|
1296
1209
|
case "help": return cmdHelp(managerCommands);
|
|
1297
1210
|
default: return null;
|
|
@@ -1703,7 +1616,7 @@ async function fetchRemoteRecordsRaw(url, token, opts) {
|
|
|
1703
1616
|
let base = url;
|
|
1704
1617
|
let bearer = token;
|
|
1705
1618
|
if (isWebrtcSpec(url)) {
|
|
1706
|
-
const { startWebrtcBridge } = await import("./webrtcRemote-
|
|
1619
|
+
const { startWebrtcBridge } = await import("./webrtcRemote-CvIEfWX8.js");
|
|
1707
1620
|
bridge = await startWebrtcBridge(url);
|
|
1708
1621
|
base = bridge.baseUrl;
|
|
1709
1622
|
bearer = bridge.token;
|
|
@@ -3222,7 +3135,8 @@ async function cmdMsgs(rest) {
|
|
|
3222
3135
|
}
|
|
3223
3136
|
for (const { dir, rec } of shown) {
|
|
3224
3137
|
const when = new Date(rec.at).toLocaleTimeString();
|
|
3225
|
-
const
|
|
3138
|
+
const fromLabel = rec.from ? `${rec.from.cli} #${rec.from.pid}` : rec.kind === "auto-retry" ? "agent-yes" : "human";
|
|
3139
|
+
const peer = dir === "out" ? `→ ${rec.to.cli} #${rec.to.pid}` : `← ${fromLabel}`;
|
|
3226
3140
|
const via = rec.remote ? ` (via ${rec.remote})` : "";
|
|
3227
3141
|
const flag = rec.confirmed === false ? " (unconfirmed)" : "";
|
|
3228
3142
|
const line = (rec.kind ? `[${rec.kind}] ` : "") + truncate(rec.body.replace(/\s+/g, " "), 100);
|
|
@@ -4297,7 +4211,7 @@ async function cmdNotify(rest) {
|
|
|
4297
4211
|
}
|
|
4298
4212
|
const ensure = async () => {
|
|
4299
4213
|
if (!argv["ensure-daemon"]) return;
|
|
4300
|
-
const { ensureDaemon } = await import("./notifyDaemon-
|
|
4214
|
+
const { ensureDaemon } = await import("./notifyDaemon-CRf_27kE.js");
|
|
4301
4215
|
await ensureDaemon().catch(() => null);
|
|
4302
4216
|
};
|
|
4303
4217
|
await heartbeatWatcher(parent, selfStartedAt);
|
|
@@ -4371,7 +4285,7 @@ async function cmdNotifyCursor(args) {
|
|
|
4371
4285
|
}
|
|
4372
4286
|
async function cmdNotifyd(rest) {
|
|
4373
4287
|
const sub = rest[0] ?? "status";
|
|
4374
|
-
const daemon = await import("./notifyDaemon-
|
|
4288
|
+
const daemon = await import("./notifyDaemon-CRf_27kE.js");
|
|
4375
4289
|
switch (sub) {
|
|
4376
4290
|
case "run": return daemon.runDaemon();
|
|
4377
4291
|
case "once": return daemon.runDaemon({ once: true });
|
|
@@ -4397,5 +4311,5 @@ async function cmdNotifyd(rest) {
|
|
|
4397
4311
|
}
|
|
4398
4312
|
|
|
4399
4313
|
//#endregion
|
|
4400
|
-
export { notifyDir as $, renderRawLog as A, submitAndConfirm as B, matchKeyword as C, recentMessageEdges as D, readPtysize as E, restartHintLines as F, gcInboxes as G, writeKeysPaced as H, runSubcommand as I, liveWatchers as J, hostId as K, snapshotStatus as L, resolveOne as M, resolveReadWindow as N, recentReadEdges as O, resolveResumeArgs as P, daemonLockOwnerPath as Q, stdinActivityPath as R, listRecords as S, readNotes as T, writeToIpc as U, waitForLogQuiet as V, appendEvent as W, shouldStealLock as X, readInbox as Y, daemonLockDir as Z, isPidAlive as _, cmdHelp as a, isUserTyping as b, deriveLiveState as c, extractMenu as d, TYPING_BADGE as et, extractNeedsInput as f, isExitRequest as g, isAgentStuck as h, backoffWhileTyping as i, renderRawLogLines as j, renderLogTailLines as k, deriveLiveStatus as l, finalizedLines as m, READ_PAGE_DEFAULT as n, controlCodeFromName as o, extractTaskCounts as p, listInboxParents as q, TYPING_WINDOW_MS as r, cursorAbs as s, GRACEFUL_EXIT_COMMANDS as t,
|
|
4401
|
-
//# sourceMappingURL=subcommands-
|
|
4314
|
+
export { notifyDir as $, renderRawLog as A, submitAndConfirm as B, matchKeyword as C, recentMessageEdges as D, readPtysize as E, restartHintLines as F, gcInboxes as G, writeKeysPaced as H, runSubcommand as I, liveWatchers as J, hostId as K, snapshotStatus as L, resolveOne as M, resolveReadWindow as N, recentReadEdges as O, resolveResumeArgs as P, daemonLockOwnerPath as Q, stdinActivityPath as R, listRecords as S, readNotes as T, writeToIpc as U, waitForLogQuiet as V, appendEvent as W, shouldStealLock as X, readInbox as Y, daemonLockDir as Z, isPidAlive as _, cmdHelp as a, isUserTyping as b, deriveLiveState as c, extractMenu as d, TYPING_BADGE as et, extractNeedsInput as f, isExitRequest as g, isAgentStuck as h, backoffWhileTyping as i, renderRawLogLines as j, renderLogTailLines as k, deriveLiveStatus as l, finalizedLines as m, READ_PAGE_DEFAULT as n, controlCodeFromName as o, extractTaskCounts as p, listInboxParents as q, TYPING_WINDOW_MS as r, cursorAbs as s, GRACEFUL_EXIT_COMMANDS as t, extractBadges as u, isSlashCommand as v, menuSelectKeys as w, lastStdinAt as x, isSubcommand as y, stopTipForCli as z };
|
|
4315
|
+
//# sourceMappingURL=subcommands-B7UxIJRw.js.map
|
|
@@ -1,9 +1,10 @@
|
|
|
1
1
|
import "./logger-CDIsZ-Pp.js";
|
|
2
2
|
import "./globalPidIndex-CoNr7tS8.js";
|
|
3
|
-
import "./
|
|
4
|
-
import
|
|
5
|
-
import "./
|
|
6
|
-
import "./
|
|
7
|
-
import "./
|
|
3
|
+
import "./messageLog-CxrKJj77.js";
|
|
4
|
+
import "./configShared-aKTg-sa5.js";
|
|
5
|
+
import { A as renderRawLog, B as submitAndConfirm, C as matchKeyword, D as recentMessageEdges, E as readPtysize, F as restartHintLines, H as writeKeysPaced, I as runSubcommand, L as snapshotStatus, M as resolveOne, N as resolveReadWindow, O as recentReadEdges, P as resolveResumeArgs, R as stdinActivityPath, S as listRecords, T as readNotes, U as writeToIpc, V as waitForLogQuiet, _ as isPidAlive, a as cmdHelp, b as isUserTyping, c as deriveLiveState, d as extractMenu, f as extractNeedsInput, g as isExitRequest, h as isAgentStuck, i as backoffWhileTyping, j as renderRawLogLines, k as renderLogTailLines, l as deriveLiveStatus, m as finalizedLines, n as READ_PAGE_DEFAULT, o as controlCodeFromName, p as extractTaskCounts, r as TYPING_WINDOW_MS, s as cursorAbs, t as GRACEFUL_EXIT_COMMANDS, u as extractBadges, v as isSlashCommand, w as menuSelectKeys, x as lastStdinAt, y as isSubcommand, z as stopTipForCli } from "./subcommands-B7UxIJRw.js";
|
|
6
|
+
import "./e2e-jb0Hp43q.js";
|
|
7
|
+
import "./webrtcLink-B7REGtK2.js";
|
|
8
|
+
import "./remotes-Cim0dBU7.js";
|
|
8
9
|
|
|
9
10
|
export { cmdHelp, isSubcommand, runSubcommand };
|
|
@@ -1,10 +1,11 @@
|
|
|
1
1
|
import { n as logger, t as addTransport } from "./logger-CDIsZ-Pp.js";
|
|
2
|
-
import { r as getInstalledPackage } from "./versionChecker-
|
|
2
|
+
import { r as getInstalledPackage } from "./versionChecker-DKiSuTSp.js";
|
|
3
3
|
import { t as agentYesHome } from "./agentYesHome-CtHb5b71.js";
|
|
4
4
|
import { i as shouldUseLock, r as releaseLock, t as acquireLock } from "./runningLock-CNMl13dC.js";
|
|
5
5
|
import { t as PidStore } from "./pidStore-BIvsBQ8X.js";
|
|
6
6
|
import { i as readGlobalPids } from "./globalPidIndex-CoNr7tS8.js";
|
|
7
|
-
import {
|
|
7
|
+
import { r as recordInbox } from "./messageLog-CxrKJj77.js";
|
|
8
|
+
import { n as register, r as sweep } from "./reaper-BUHCyxdF.js";
|
|
8
9
|
import { arch, platform } from "process";
|
|
9
10
|
import { execSync } from "child_process";
|
|
10
11
|
import { closeSync, constants, createReadStream, existsSync, mkdirSync, openSync } from "fs";
|
|
@@ -572,6 +573,64 @@ function autoRetryBackoffMs(streak) {
|
|
|
572
573
|
function shouldFireRetry(working, ready, idleMs, minIdleMs) {
|
|
573
574
|
return !working && ready && idleMs >= minIdleMs;
|
|
574
575
|
}
|
|
576
|
+
/** Fallback reason when no autoRetry match was captured before firing. */
|
|
577
|
+
const AUTO_RETRY_REASON_FALLBACK = "a transient server-side error";
|
|
578
|
+
/**
|
|
579
|
+
* All reason strings `classifyAutoRetryReason` can produce — used by the
|
|
580
|
+
* self-trigger guard spec to prove every possible typed message is inert
|
|
581
|
+
* against every CLI's screen-scrape patterns. Mirrors Rust's RETRY_REASONS.
|
|
582
|
+
*/
|
|
583
|
+
const AUTO_RETRY_REASONS = [
|
|
584
|
+
"the model backend reported it is temporarily busy",
|
|
585
|
+
"the response stream stalled mid-way",
|
|
586
|
+
"the connection dropped mid-response",
|
|
587
|
+
"a usage cap was reached (it may need time to reset)",
|
|
588
|
+
"requests are being throttled by the server",
|
|
589
|
+
AUTO_RETRY_REASON_FALLBACK
|
|
590
|
+
];
|
|
591
|
+
/**
|
|
592
|
+
* Paraphrase the matched recoverable-error banner into a short reason.
|
|
593
|
+
*
|
|
594
|
+
* Deliberately NEVER echoes the raw banner wording ("API Error", "Overloaded",
|
|
595
|
+
* "rate limit", …): the built message is typed into the PTY and stays visible
|
|
596
|
+
* in the transcript, where raw wording would re-match the autoRetry patterns —
|
|
597
|
+
* keeping the error state true forever, which blocks the streak reset on
|
|
598
|
+
* recovery. Mirrors Rust's classify_retry_reason.
|
|
599
|
+
*/
|
|
600
|
+
function classifyAutoRetryReason(screen) {
|
|
601
|
+
const s = screen.toLowerCase();
|
|
602
|
+
if (s.includes("overload")) return AUTO_RETRY_REASONS[0];
|
|
603
|
+
if (s.includes("stalled")) return AUTO_RETRY_REASONS[1];
|
|
604
|
+
if (s.includes("connection closed")) return AUTO_RETRY_REASONS[2];
|
|
605
|
+
if (s.includes("usage limit") || s.includes("session limit")) return AUTO_RETRY_REASONS[3];
|
|
606
|
+
if (s.includes("rate") && s.includes("limit")) return AUTO_RETRY_REASONS[4];
|
|
607
|
+
return AUTO_RETRY_REASON_FALLBACK;
|
|
608
|
+
}
|
|
609
|
+
/** Human-readable duration: "45s", "3m20s", "1h04m", "8h". Mirrors Rust's fmt_dur_secs. */
|
|
610
|
+
function formatDurationSecs(total) {
|
|
611
|
+
const t = Math.max(0, Math.floor(total));
|
|
612
|
+
if (t < 60) return `${t}s`;
|
|
613
|
+
if (t < 3600) {
|
|
614
|
+
const m = Math.floor(t / 60);
|
|
615
|
+
const s = t % 60;
|
|
616
|
+
return s === 0 ? `${m}m` : `${m}m${String(s).padStart(2, "0")}s`;
|
|
617
|
+
}
|
|
618
|
+
const h = Math.floor(t / 3600);
|
|
619
|
+
const m = Math.floor(t % 3600 / 60);
|
|
620
|
+
return m === 0 ? `${h}h` : `${h}h${String(m).padStart(2, "0")}m`;
|
|
621
|
+
}
|
|
622
|
+
/**
|
|
623
|
+
* The single line typed into the agent's prompt instead of a bare "retry":
|
|
624
|
+
* says WHO is nudging (agent-yes, automated), WHY (paraphrased reason), and
|
|
625
|
+
* the backoff state (attempt #, elapsed, next delay, give-up horizon), plus an
|
|
626
|
+
* explicit "ignore if nothing failed" clause so an agent that already
|
|
627
|
+
* recovered — or is mid-question — doesn't burn a turn asking what "retry"
|
|
628
|
+
* refers to. Must stay one line (it is submitted with a single "\r").
|
|
629
|
+
* Mirrors Rust's build_retry_message in rs/src/context.rs.
|
|
630
|
+
*/
|
|
631
|
+
function buildAutoRetryMessage(attempt, reason, sinceFirstSecs, nextBackoffSecs) {
|
|
632
|
+
return `retry [auto-retry #${attempt} by agent-yes: ${reason}; first seen ${formatDurationSecs(sinceFirstSecs)} ago; if this attempt fails too, the next nudge comes in ${formatDurationSecs(nextBackoffSecs)} (giving up after ${formatDurationSecs(AUTO_RETRY_GIVE_UP_MS / 1e3)}). This is an automated recovery nudge - if no request actually failed, ignore it and simply continue your previous task.]`;
|
|
633
|
+
}
|
|
575
634
|
|
|
576
635
|
//#endregion
|
|
577
636
|
//#region ts/core/logging.ts
|
|
@@ -1084,7 +1143,7 @@ async function notifyWebhook(status, details, cwd = process.cwd()) {
|
|
|
1084
1143
|
|
|
1085
1144
|
//#endregion
|
|
1086
1145
|
//#region ts/index.ts
|
|
1087
|
-
const config = await import("./agent-yes.config-
|
|
1146
|
+
const config = await import("./agent-yes.config-whqkqq9P.js").then((mod) => mod.default || mod);
|
|
1088
1147
|
const CLIS_CONFIG = config.clis;
|
|
1089
1148
|
/**
|
|
1090
1149
|
* Main function to run agent-cli with automatic yes/no responses
|
|
@@ -1517,6 +1576,7 @@ async function agentYes({ cli, cliArgs = [], skipPermissions = false, prompt, ro
|
|
|
1517
1576
|
let retryStartedAt = null;
|
|
1518
1577
|
let retryNextAt = null;
|
|
1519
1578
|
let autoRetryScreen = "";
|
|
1579
|
+
let retryReason = null;
|
|
1520
1580
|
const heartbeatInterval = setInterval(async () => {
|
|
1521
1581
|
try {
|
|
1522
1582
|
const rendered = removeControlCharacters(xtermProxy.tail(12));
|
|
@@ -1530,10 +1590,26 @@ async function agentYes({ cli, cliArgs = [], skipPermissions = false, prompt, ro
|
|
|
1530
1590
|
} else if (now >= retryNextAt) if (!shouldFireRetry(conf.working?.some((rx) => rx.test(autoRetryScreen)) ?? false, conf.ready?.some((rx) => rx.test(autoRetryScreen)) ?? false, ctx.idleWaiter.idleTimeMs(), AUTO_RETRY_MIN_IDLE_MS)) retryNextAt = now + 500;
|
|
1531
1591
|
else {
|
|
1532
1592
|
retryStreak += 1;
|
|
1533
|
-
|
|
1534
|
-
|
|
1593
|
+
const nextBackoffMs = autoRetryBackoffMs(retryStreak);
|
|
1594
|
+
const reason = retryReason ?? AUTO_RETRY_REASON_FALLBACK;
|
|
1595
|
+
const sinceFirstSecs = retryStartedAt === null ? 0 : (now - retryStartedAt) / 1e3;
|
|
1596
|
+
const line = buildAutoRetryMessage(retryStreak, reason, sinceFirstSecs, nextBackoffMs / 1e3);
|
|
1597
|
+
logger.warn(`[${cli}-yes] auto-retry: typing retry nudge (attempt ${retryStreak}, reason: ${reason})`);
|
|
1598
|
+
ctx.messageContext.shell.write(line + "\r");
|
|
1535
1599
|
ctx.idleWaiter.ping();
|
|
1536
|
-
|
|
1600
|
+
recordInbox({
|
|
1601
|
+
at: now,
|
|
1602
|
+
from: null,
|
|
1603
|
+
to: {
|
|
1604
|
+
pid: process.pid,
|
|
1605
|
+
cli,
|
|
1606
|
+
cwd: workingDir
|
|
1607
|
+
},
|
|
1608
|
+
kind: "auto-retry",
|
|
1609
|
+
body: `${reason} (attempt ${retryStreak}, next backoff ${formatDurationSecs(nextBackoffMs / 1e3)})`,
|
|
1610
|
+
wrapped: false
|
|
1611
|
+
});
|
|
1612
|
+
retryNextAt = now + nextBackoffMs;
|
|
1537
1613
|
}
|
|
1538
1614
|
}
|
|
1539
1615
|
if (rendered === lastHeartbeatRendered) return;
|
|
@@ -1738,6 +1814,7 @@ async function agentYes({ cli, cliArgs = [], skipPermissions = false, prompt, ro
|
|
|
1738
1814
|
const errVisible = conf.autoRetry.some((rx) => rx.test(rendered));
|
|
1739
1815
|
const readyVisible = conf.ready?.some((rx) => rx.test(rendered)) ?? false;
|
|
1740
1816
|
if (errVisible && readyVisible) {
|
|
1817
|
+
retryReason = classifyAutoRetryReason(rendered);
|
|
1741
1818
|
if (retryNextAt === null) {
|
|
1742
1819
|
if (retryStartedAt === null) retryStartedAt = Date.now();
|
|
1743
1820
|
const delayMs = autoRetryBackoffMs(retryStreak);
|
|
@@ -1824,4 +1901,4 @@ function sleep(ms) {
|
|
|
1824
1901
|
|
|
1825
1902
|
//#endregion
|
|
1826
1903
|
export { removeControlCharacters as a, AgentContext as i, agentYes as n, config as r, CLIS_CONFIG as t };
|
|
1827
|
-
//# sourceMappingURL=ts-
|
|
1904
|
+
//# sourceMappingURL=ts-B64pNQlI.js.map
|
|
@@ -7,7 +7,7 @@ import { fileURLToPath } from "url";
|
|
|
7
7
|
|
|
8
8
|
//#region package.json
|
|
9
9
|
var name = "agent-yes";
|
|
10
|
-
var version = "1.
|
|
10
|
+
var version = "1.197.0";
|
|
11
11
|
|
|
12
12
|
//#endregion
|
|
13
13
|
//#region ts/versionChecker.ts
|
|
@@ -215,4 +215,4 @@ async function displayVersion() {
|
|
|
215
215
|
|
|
216
216
|
//#endregion
|
|
217
217
|
export { versionString as i, displayVersion as n, getInstalledPackage as r, checkAndAutoUpdate as t };
|
|
218
|
-
//# sourceMappingURL=versionChecker-
|
|
218
|
+
//# sourceMappingURL=versionChecker-DKiSuTSp.js.map
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { u as parseSecret } from "./e2e-
|
|
1
|
+
import { u as parseSecret } from "./e2e-jb0Hp43q.js";
|
|
2
2
|
|
|
3
3
|
//#region ts/webrtcLink.ts
|
|
4
4
|
const SIGNAL_SUBPROTOCOL = "ay-signal-1";
|
|
@@ -42,4 +42,4 @@ function isWebrtcSpec(spec) {
|
|
|
42
42
|
|
|
43
43
|
//#endregion
|
|
44
44
|
export { isWebrtcSpec as n, parseWebrtcLink as r, SIGNAL_SUBPROTOCOL as t };
|
|
45
|
-
//# sourceMappingURL=webrtcLink-
|
|
45
|
+
//# sourceMappingURL=webrtcLink-B7REGtK2.js.map
|
|
@@ -1,5 +1,5 @@
|
|
|
1
|
-
import { a as computeTranscriptHash, c as open, d as randomHex, f as seal, l as packEnvelope, n as FLAG_CONFIRM, o as deriveAuthToken, p as unpackEnvelope, s as deriveDirKeys } from "./e2e-
|
|
2
|
-
import { n as isWebrtcSpec, r as parseWebrtcLink, t as SIGNAL_SUBPROTOCOL } from "./webrtcLink-
|
|
1
|
+
import { a as computeTranscriptHash, c as open, d as randomHex, f as seal, l as packEnvelope, n as FLAG_CONFIRM, o as deriveAuthToken, p as unpackEnvelope, s as deriveDirKeys } from "./e2e-jb0Hp43q.js";
|
|
2
|
+
import { n as isWebrtcSpec, r as parseWebrtcLink, t as SIGNAL_SUBPROTOCOL } from "./webrtcLink-B7REGtK2.js";
|
|
3
3
|
import { RTCPeerConnection } from "node-datachannel/polyfill";
|
|
4
4
|
|
|
5
5
|
//#region ts/webrtcRemote.ts
|
|
@@ -256,4 +256,4 @@ async function startWebrtcBridge(link) {
|
|
|
256
256
|
|
|
257
257
|
//#endregion
|
|
258
258
|
export { startWebrtcBridge };
|
|
259
|
-
//# sourceMappingURL=webrtcRemote-
|
|
259
|
+
//# sourceMappingURL=webrtcRemote-CvIEfWX8.js.map
|
|
@@ -193,4 +193,4 @@ function resolveSpawnCwd(input) {
|
|
|
193
193
|
|
|
194
194
|
//#endregion
|
|
195
195
|
export { getSpawnHook as a, hasProvisionHook as c, resolveSpawnCwd as d, setWorkspaceRoot as f, getProvisionRoot as i, hasSpawnHook as l, getMinFreeMb as n, getSpawnWaitMs as o, getProvisionHook as r, getWorkspaceRoot as s, getMaxAgents as t, isProvisionAllowed as u };
|
|
196
|
-
//# sourceMappingURL=workspaceConfig-
|
|
196
|
+
//# sourceMappingURL=workspaceConfig-CjaRvTjf.js.map
|
package/package.json
CHANGED
package/ts/autoRetry.spec.ts
CHANGED
|
@@ -1,5 +1,15 @@
|
|
|
1
1
|
import { describe, expect, it } from "vitest";
|
|
2
|
-
import {
|
|
2
|
+
import {
|
|
3
|
+
AUTO_RETRY_MAX_DELAY_SECS,
|
|
4
|
+
AUTO_RETRY_REASON_FALLBACK,
|
|
5
|
+
AUTO_RETRY_REASONS,
|
|
6
|
+
autoRetryBackoffMs,
|
|
7
|
+
buildAutoRetryMessage,
|
|
8
|
+
classifyAutoRetryReason,
|
|
9
|
+
formatDurationSecs,
|
|
10
|
+
shouldFireRetry,
|
|
11
|
+
} from "./autoRetry.ts";
|
|
12
|
+
import { loadSharedCliDefaults } from "./configShared.ts";
|
|
3
13
|
|
|
4
14
|
describe("autoRetryBackoffMs", () => {
|
|
5
15
|
it("doubles 8,16,32,…,256 then caps", () => {
|
|
@@ -32,3 +42,82 @@ describe("shouldFireRetry", () => {
|
|
|
32
42
|
expect(shouldFireRetry(false, true, 10_000, 5_000)).toBe(true);
|
|
33
43
|
});
|
|
34
44
|
});
|
|
45
|
+
|
|
46
|
+
describe("classifyAutoRetryReason", () => {
|
|
47
|
+
it("maps real banners to inert paraphrases", () => {
|
|
48
|
+
expect(classifyAutoRetryReason("● API Error: 529 Overloaded")).toBe(AUTO_RETRY_REASONS[0]);
|
|
49
|
+
expect(classifyAutoRetryReason("API Error: Response stalled mid-stream.")).toBe(
|
|
50
|
+
AUTO_RETRY_REASONS[1],
|
|
51
|
+
);
|
|
52
|
+
expect(classifyAutoRetryReason("API Error: Connection closed mid-response.")).toBe(
|
|
53
|
+
AUTO_RETRY_REASONS[2],
|
|
54
|
+
);
|
|
55
|
+
expect(classifyAutoRetryReason("Claude usage limit reached")).toBe(AUTO_RETRY_REASONS[3]);
|
|
56
|
+
expect(classifyAutoRetryReason("You are being rate-limited")).toBe(AUTO_RETRY_REASONS[4]);
|
|
57
|
+
expect(classifyAutoRetryReason("API Error: 503 Service Unavailable")).toBe(
|
|
58
|
+
AUTO_RETRY_REASON_FALLBACK,
|
|
59
|
+
);
|
|
60
|
+
});
|
|
61
|
+
});
|
|
62
|
+
|
|
63
|
+
describe("formatDurationSecs", () => {
|
|
64
|
+
it("renders s / m / h forms", () => {
|
|
65
|
+
expect(formatDurationSecs(0)).toBe("0s");
|
|
66
|
+
expect(formatDurationSecs(45)).toBe("45s");
|
|
67
|
+
expect(formatDurationSecs(60)).toBe("1m");
|
|
68
|
+
expect(formatDurationSecs(200)).toBe("3m20s");
|
|
69
|
+
expect(formatDurationSecs(3600)).toBe("1h");
|
|
70
|
+
expect(formatDurationSecs(3900)).toBe("1h05m");
|
|
71
|
+
expect(formatDurationSecs(8 * 3600)).toBe("8h");
|
|
72
|
+
});
|
|
73
|
+
});
|
|
74
|
+
|
|
75
|
+
describe("buildAutoRetryMessage", () => {
|
|
76
|
+
it("is one line and carries attempt/reason/backoff context + ignore clause", () => {
|
|
77
|
+
const msg = buildAutoRetryMessage(3, AUTO_RETRY_REASONS[0], 45, 64);
|
|
78
|
+
expect(msg).not.toContain("\n");
|
|
79
|
+
expect(msg.startsWith("retry [")).toBe(true);
|
|
80
|
+
expect(msg).toContain("#3");
|
|
81
|
+
expect(msg).toContain("45s ago");
|
|
82
|
+
expect(msg).toContain("in 1m04s");
|
|
83
|
+
expect(msg).toContain("giving up after 8h");
|
|
84
|
+
expect(msg).toContain("ignore it");
|
|
85
|
+
});
|
|
86
|
+
|
|
87
|
+
// Self-trigger guard: the message is typed into the PTY and stays visible in
|
|
88
|
+
// the transcript, so if it ever matched a CLI's own screen-scrape patterns
|
|
89
|
+
// it would re-arm the retry loop (autoRetry), block the streak reset, kill
|
|
90
|
+
// the session (fatal), or fake a state (working/enter/needsInput). Mirrors
|
|
91
|
+
// rs/src/config.rs test_auto_retry_message_is_inert_against_all_cli_patterns.
|
|
92
|
+
it("never matches any CLI's screen-scrape patterns", async () => {
|
|
93
|
+
const clis = await loadSharedCliDefaults();
|
|
94
|
+
expect(Object.keys(clis).length).toBeGreaterThan(0);
|
|
95
|
+
for (const [cliName, conf] of Object.entries(clis)) {
|
|
96
|
+
for (const reason of AUTO_RETRY_REASONS) {
|
|
97
|
+
const cases: [number, number, number][] = [
|
|
98
|
+
[1, 0, 8],
|
|
99
|
+
[5, 500, 128],
|
|
100
|
+
[12, 30_000, 256],
|
|
101
|
+
];
|
|
102
|
+
for (const [attempt, since, next] of cases) {
|
|
103
|
+
const msg = buildAutoRetryMessage(attempt, reason, since, next);
|
|
104
|
+
const groups: [string, RegExp[] | undefined][] = [
|
|
105
|
+
["autoRetry", conf.autoRetry],
|
|
106
|
+
["fatal", conf.fatal],
|
|
107
|
+
["enter", conf.enter],
|
|
108
|
+
["working", conf.working],
|
|
109
|
+
["needsInput", conf.needsInput],
|
|
110
|
+
];
|
|
111
|
+
for (const [kind, patterns] of groups) {
|
|
112
|
+
for (const rx of patterns ?? []) {
|
|
113
|
+
expect(
|
|
114
|
+
rx.test(msg),
|
|
115
|
+
`retry message must not match ${cliName}.${kind} /${rx}/: ${msg}`,
|
|
116
|
+
).toBe(false);
|
|
117
|
+
}
|
|
118
|
+
}
|
|
119
|
+
}
|
|
120
|
+
}
|
|
121
|
+
}
|
|
122
|
+
});
|
|
123
|
+
});
|
package/ts/autoRetry.ts
CHANGED
|
@@ -37,3 +37,79 @@ export function shouldFireRetry(
|
|
|
37
37
|
): boolean {
|
|
38
38
|
return !working && ready && idleMs >= minIdleMs;
|
|
39
39
|
}
|
|
40
|
+
|
|
41
|
+
/** Fallback reason when no autoRetry match was captured before firing. */
|
|
42
|
+
export const AUTO_RETRY_REASON_FALLBACK = "a transient server-side error";
|
|
43
|
+
|
|
44
|
+
/**
|
|
45
|
+
* All reason strings `classifyAutoRetryReason` can produce — used by the
|
|
46
|
+
* self-trigger guard spec to prove every possible typed message is inert
|
|
47
|
+
* against every CLI's screen-scrape patterns. Mirrors Rust's RETRY_REASONS.
|
|
48
|
+
*/
|
|
49
|
+
export const AUTO_RETRY_REASONS = [
|
|
50
|
+
"the model backend reported it is temporarily busy",
|
|
51
|
+
"the response stream stalled mid-way",
|
|
52
|
+
"the connection dropped mid-response",
|
|
53
|
+
"a usage cap was reached (it may need time to reset)",
|
|
54
|
+
"requests are being throttled by the server",
|
|
55
|
+
AUTO_RETRY_REASON_FALLBACK,
|
|
56
|
+
] as const;
|
|
57
|
+
|
|
58
|
+
/**
|
|
59
|
+
* Paraphrase the matched recoverable-error banner into a short reason.
|
|
60
|
+
*
|
|
61
|
+
* Deliberately NEVER echoes the raw banner wording ("API Error", "Overloaded",
|
|
62
|
+
* "rate limit", …): the built message is typed into the PTY and stays visible
|
|
63
|
+
* in the transcript, where raw wording would re-match the autoRetry patterns —
|
|
64
|
+
* keeping the error state true forever, which blocks the streak reset on
|
|
65
|
+
* recovery. Mirrors Rust's classify_retry_reason.
|
|
66
|
+
*/
|
|
67
|
+
export function classifyAutoRetryReason(screen: string): string {
|
|
68
|
+
const s = screen.toLowerCase();
|
|
69
|
+
if (s.includes("overload")) return AUTO_RETRY_REASONS[0];
|
|
70
|
+
if (s.includes("stalled")) return AUTO_RETRY_REASONS[1];
|
|
71
|
+
if (s.includes("connection closed")) return AUTO_RETRY_REASONS[2];
|
|
72
|
+
if (s.includes("usage limit") || s.includes("session limit")) return AUTO_RETRY_REASONS[3];
|
|
73
|
+
if (s.includes("rate") && s.includes("limit")) return AUTO_RETRY_REASONS[4];
|
|
74
|
+
return AUTO_RETRY_REASON_FALLBACK;
|
|
75
|
+
}
|
|
76
|
+
|
|
77
|
+
/** Human-readable duration: "45s", "3m20s", "1h04m", "8h". Mirrors Rust's fmt_dur_secs. */
|
|
78
|
+
export function formatDurationSecs(total: number): string {
|
|
79
|
+
const t = Math.max(0, Math.floor(total));
|
|
80
|
+
if (t < 60) return `${t}s`;
|
|
81
|
+
if (t < 3600) {
|
|
82
|
+
const m = Math.floor(t / 60);
|
|
83
|
+
const s = t % 60;
|
|
84
|
+
return s === 0 ? `${m}m` : `${m}m${String(s).padStart(2, "0")}s`;
|
|
85
|
+
}
|
|
86
|
+
const h = Math.floor(t / 3600);
|
|
87
|
+
const m = Math.floor((t % 3600) / 60);
|
|
88
|
+
return m === 0 ? `${h}h` : `${h}h${String(m).padStart(2, "0")}m`;
|
|
89
|
+
}
|
|
90
|
+
|
|
91
|
+
/**
|
|
92
|
+
* The single line typed into the agent's prompt instead of a bare "retry":
|
|
93
|
+
* says WHO is nudging (agent-yes, automated), WHY (paraphrased reason), and
|
|
94
|
+
* the backoff state (attempt #, elapsed, next delay, give-up horizon), plus an
|
|
95
|
+
* explicit "ignore if nothing failed" clause so an agent that already
|
|
96
|
+
* recovered — or is mid-question — doesn't burn a turn asking what "retry"
|
|
97
|
+
* refers to. Must stay one line (it is submitted with a single "\r").
|
|
98
|
+
* Mirrors Rust's build_retry_message in rs/src/context.rs.
|
|
99
|
+
*/
|
|
100
|
+
export function buildAutoRetryMessage(
|
|
101
|
+
attempt: number,
|
|
102
|
+
reason: string,
|
|
103
|
+
sinceFirstSecs: number,
|
|
104
|
+
nextBackoffSecs: number,
|
|
105
|
+
): string {
|
|
106
|
+
const since = formatDurationSecs(sinceFirstSecs);
|
|
107
|
+
const next = formatDurationSecs(nextBackoffSecs);
|
|
108
|
+
const giveUp = formatDurationSecs(AUTO_RETRY_GIVE_UP_MS / 1000);
|
|
109
|
+
return (
|
|
110
|
+
`retry [auto-retry #${attempt} by agent-yes: ${reason}; first seen ${since} ago; ` +
|
|
111
|
+
`if this attempt fails too, the next nudge comes in ${next} (giving up after ${giveUp}). ` +
|
|
112
|
+
`This is an automated recovery nudge - if no request actually failed, ignore it and ` +
|
|
113
|
+
`simply continue your previous task.]`
|
|
114
|
+
);
|
|
115
|
+
}
|