nolo-cli 0.16.0-alpha.11 → 0.16.0-alpha.13

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.
Files changed (38) hide show
  1. package/{_executeModel-LGHYA5XS.js → _executeModel-Y43RS7DI.js} +10 -9
  2. package/{agentEmailCommands-KC2QCPQV.js → agentEmailCommands-AJFG5HBG.js} +10 -9
  3. package/{agentMachineCommands-P7S5WAVA.js → agentMachineCommands-2ACKQSTE.js} +1 -1
  4. package/{agentRecordCommands-JKZR2TZJ.js → agentRecordCommands-RQTFDKKJ.js} +10 -9
  5. package/{agentRunCommand-2ELKCBWR.js → agentRunCommand-FAHV33ML.js} +14 -10
  6. package/{agentRunControl-6ADXVYMR.js → agentRunControl-YVLQ4BW5.js} +24 -1
  7. package/{agentSlice-NXLMDKR7.js → agentSlice-XEQOAOC6.js} +1 -1
  8. package/{chunk-ZH62XNZE.js → chunk-6IZTYSJK.js} +641 -31
  9. package/{chunk-EQTZPEID.js → chunk-A3S7ITGN.js} +17 -40
  10. package/{chunk-HSASSW4L.js → chunk-BE3IKV5X.js} +5 -5
  11. package/{chunk-ZXQ52OY2.js → chunk-COIZN2AL.js} +3 -3
  12. package/{chunk-MYMMNKYL.js → chunk-E5CLOOG5.js} +153 -6
  13. package/{chunk-47QUPRHO.js → chunk-EPCM5E6C.js} +1 -1
  14. package/chunk-FKXFCYVO.js +221 -0
  15. package/{chunk-2WXMU64E.js → chunk-IQJOXIYF.js} +2 -2
  16. package/{chunk-4LXOVZ4W.js → chunk-KEPSRWXW.js} +8 -8
  17. package/{chunk-BXXECNBU.js → chunk-MDNZEE6B.js} +1 -1
  18. package/{chunk-22NGXGN3.js → chunk-N3IKEYV7.js} +1 -1
  19. package/{chunk-WBA6SIBB.js → chunk-PFAN32FJ.js} +1 -1
  20. package/{chunk-XEWUC53Z.js → chunk-QSJNZQFD.js} +4 -4
  21. package/{chunk-6CRACQXW.js → chunk-SPHXTAG6.js} +2 -2
  22. package/{chunk-D4NCEGGR.js → chunk-TSGUTKW2.js} +83 -233
  23. package/{createAgentAutomationAction-RKWIMKHW.js → createAgentAutomationAction-G5ZEEBMB.js} +2 -2
  24. package/{createDialogAction-VN7MEBS3.js → createDialogAction-3EJL6XK6.js} +3 -3
  25. package/{createPageAction-JBUWBHHC.js → createPageAction-FIUQXAI4.js} +2 -2
  26. package/{dialogSlice-NNYBU35K.js → dialogSlice-H24LGUPG.js} +2 -2
  27. package/{docStore-DR22GGF3.js → docStore-EFEUV2FP.js} +3 -3
  28. package/{handleSendMessageAction-FHITDM7H.js → handleSendMessageAction-PMKHJZGP.js} +2 -2
  29. package/index.js +20 -20
  30. package/{machineCommands-3B4IHHVK.js → machineCommands-U3N22344.js} +10 -9
  31. package/{machineWsRunDispatch-AXZ6OL3V.js → machineWsRunDispatch-BGYPVQPM.js} +10 -9
  32. package/package.json +1 -1
  33. package/{readlineWorkspace-DBZCXS7Y.js → readlineWorkspace-HWYYQ2KL.js} +18 -16
  34. package/{runAgentClientLoop-AB7PQ3TB.js → runAgentClientLoop-3NESZLYZ.js} +8 -7
  35. package/{spaceSlice-TEJDCU6A.js → spaceSlice-YBORJR2H.js} +2 -2
  36. package/{streamAgentChatTurn-NZ3HLAXS.js → streamAgentChatTurn-XAPKGQ6M.js} +10 -9
  37. package/{tools-M4USUF3R.js → tools-RXIDHJ2C.js} +6 -5
  38. package/{updateDialogTitleAction-SMPQVKWT.js → updateDialogTitleAction-S7AG3P44.js} +2 -2
@@ -21,7 +21,7 @@ import {
21
21
  import {
22
22
  inlineImageUrlsForCustomProvider,
23
23
  prepareTools
24
- } from "./chunk-47QUPRHO.js";
24
+ } from "./chunk-EPCM5E6C.js";
25
25
  import {
26
26
  getAntigravityUserAgent,
27
27
  isAntigravityOAuthAgent,
@@ -30,27 +30,19 @@ import {
30
30
  } from "./chunk-L6NVTGI7.js";
31
31
  import {
32
32
  FORCED_TOOLS,
33
- TASK_PREVIEW_MAX,
34
33
  addDefaultLightWebToolsForConfiguredAgents,
35
34
  addDefaultSystemCapabilityTools,
36
35
  appendEnabledPackPromptPatches,
37
36
  applyDisabledTools,
38
37
  applySystemBuiltinSkillFilter,
39
38
  expandEnabledPacks,
40
- formatListRunsCard,
41
- formatNotFoundRunCard,
42
- formatStartRunCard,
43
- formatStatusRunCard,
44
- formatStopRunCard,
45
- isAgentNameFallback,
46
39
  readXPostFunc,
47
40
  readXPostFunctionSchema,
48
41
  readXhsProfileFunc,
49
42
  readXhsProfileFunctionSchema,
50
43
  rememberMemoryFunctionSchema,
51
- resolveEffectiveEnabledPacks,
52
- resolveRunLabel
53
- } from "./chunk-D4NCEGGR.js";
44
+ resolveEffectiveEnabledPacks
45
+ } from "./chunk-TSGUTKW2.js";
54
46
  import {
55
47
  createDrainExhaustedResponse,
56
48
  isCoreDrainingBody,
@@ -62,13 +54,13 @@ import {
62
54
  } from "./chunk-OWOHNGCN.js";
63
55
  import {
64
56
  prepareTokenUsageData
65
- } from "./chunk-4LXOVZ4W.js";
57
+ } from "./chunk-KEPSRWXW.js";
66
58
  import {
67
59
  BUILTIN_TITLE_LLM_CONFIG
68
60
  } from "./chunk-XOIF2TNF.js";
69
61
  import {
70
62
  fetchServerSyncedCredential
71
- } from "./chunk-ZXQ52OY2.js";
63
+ } from "./chunk-COIZN2AL.js";
72
64
  import {
73
65
  buildAgentRuntimeDialogWritePlan,
74
66
  buildLocalWorkspaceOpenAiTools,
@@ -138,9 +130,22 @@ import {
138
130
  checkStaleRun,
139
131
  finalizeRunRecord,
140
132
  findRunRecord,
141
- listRunRecords,
133
+ gcRunRecords,
134
+ queryRunRecords,
142
135
  spawnLocalBackgroundRun
143
- } from "./chunk-MYMMNKYL.js";
136
+ } from "./chunk-E5CLOOG5.js";
137
+ import {
138
+ TASK_PREVIEW_MAX,
139
+ formatListRunsCard,
140
+ formatNotFoundRunCard,
141
+ formatStartRunCard,
142
+ formatStatusRunCard,
143
+ formatStopRunCard,
144
+ isAgentNameFallback,
145
+ isAgentRunTerminalStatus,
146
+ resolveRunLabel,
147
+ shortRunId
148
+ } from "./chunk-FKXFCYVO.js";
144
149
  import {
145
150
  CliProviderQuotaError,
146
151
  executeCli
@@ -950,6 +955,185 @@ function agentRunCardLabels() {
950
955
  };
951
956
  }
952
957
 
958
+ // packages/ai/tools/agent/quotaCircuitBreaker.ts
959
+ var QUOTA_ERROR_PATTERNS = [
960
+ /429/,
961
+ /quota/i,
962
+ /rate\s*limit/i,
963
+ /too\s*many\s*requests/i,
964
+ /额度/,
965
+ /上限/,
966
+ /用尽/,
967
+ /CliProviderQuotaError/i
968
+ ];
969
+ var AGENT_NOT_FOUND_PATTERNS = [
970
+ /agent[^\n]{0,80}not\s+found/i,
971
+ /agent[^\n]{0,80}not\s+exist/i,
972
+ /agent[^\n]{0,80}不存在/i,
973
+ /agent[^\n]{0,80}未找到/i,
974
+ /AGENT_NOT_FOUND/
975
+ ];
976
+ var AUTH_ERROR_PATTERNS = [
977
+ /401/,
978
+ /403/,
979
+ /unauthori[sz]ed/i,
980
+ /authentication\s+failed/i,
981
+ /invalid\s+(api[-\s]?key|token|credential)/i,
982
+ /api[-\s]?key\s+(invalid|missing|not\s+found)/i,
983
+ /token\s*(invalid|expired|失效|过期)/i,
984
+ /未授权/,
985
+ /认证失败/,
986
+ /凭证/
987
+ ];
988
+ var NETWORK_ERROR_PATTERNS = [
989
+ /ECONNREFUSED|ECONNRESET|ETIMEDOUT|ENOTFOUND|EHOSTUNREACH|EPIPE/i,
990
+ /getaddrinfo|DNS\s+(error|fail)/i,
991
+ /fetch\s+failed/i,
992
+ /network\s+(error|unreachable|down|disconnected)/i,
993
+ /socket\s+(hang|closed|error)/i,
994
+ /连接失败/,
995
+ /网络错误|网络异常|无法连接/
996
+ ];
997
+ function findActiveBreaker(now, table, target) {
998
+ if (!Number.isFinite(now)) return void 0;
999
+ for (const entry of table) {
1000
+ if (entry.target !== target) continue;
1001
+ if (typeof entry.until === "number" && entry.until > now) return entry;
1002
+ }
1003
+ return void 0;
1004
+ }
1005
+ function isCliProviderQuotaErrorShape(error) {
1006
+ if (!error || typeof error !== "object") return false;
1007
+ const e = error;
1008
+ return e.name === "CliProviderQuotaError" || typeof e.message === "string" && e.message.startsWith("[QUOTA_LIMITED:");
1009
+ }
1010
+ function isQuotaExhaustedError(error) {
1011
+ if (!error) return false;
1012
+ if (isCliProviderQuotaErrorShape(error)) return true;
1013
+ if (error instanceof Error) {
1014
+ return QUOTA_ERROR_PATTERNS.some((pattern) => pattern.test(error.message));
1015
+ }
1016
+ if (typeof error === "string") {
1017
+ return QUOTA_ERROR_PATTERNS.some((pattern) => pattern.test(error));
1018
+ }
1019
+ const maybeStatus = error.status;
1020
+ if (typeof maybeStatus === "number" && maybeStatus === 429) return true;
1021
+ const maybeStatusCode = error.statusCode;
1022
+ if (typeof maybeStatusCode === "number" && maybeStatusCode === 429) return true;
1023
+ return false;
1024
+ }
1025
+ function isAgentNotFoundError(error) {
1026
+ if (!error) return false;
1027
+ const e = error;
1028
+ if (e.code === "AGENT_NOT_FOUND") return true;
1029
+ const message = typeof e.message === "string" ? e.message : typeof error === "string" ? error : "";
1030
+ if (AGENT_NOT_FOUND_PATTERNS.some((pattern) => pattern.test(message))) return true;
1031
+ if ((e.status === 404 || e.statusCode === 404) && /agent|不存在|未找到/i.test(message)) {
1032
+ return true;
1033
+ }
1034
+ return false;
1035
+ }
1036
+ function extractMessage(error) {
1037
+ if (typeof error === "string") return error;
1038
+ if (error instanceof Error) return error.message;
1039
+ if (error && typeof error === "object") {
1040
+ const m = error.message;
1041
+ return typeof m === "string" ? m : "";
1042
+ }
1043
+ return "";
1044
+ }
1045
+ function extractStatus(error) {
1046
+ if (!error || typeof error !== "object") return void 0;
1047
+ const e = error;
1048
+ const s = typeof e.status === "number" ? e.status : void 0;
1049
+ const sc = typeof e.statusCode === "number" ? e.statusCode : void 0;
1050
+ return s ?? sc;
1051
+ }
1052
+ function parseResetsInMs(text) {
1053
+ if (!text) return void 0;
1054
+ const unitMs = (n, unit) => {
1055
+ if (/^d/.test(unit)) return n * 24 * 3600 * 1e3;
1056
+ if (/^h/.test(unit)) return n * 3600 * 1e3;
1057
+ if (/^m/.test(unit)) return n * 60 * 1e3;
1058
+ if (/^s/.test(unit)) return n * 1e3;
1059
+ return NaN;
1060
+ };
1061
+ const re = /(\d+)\s*(d(?:ay|ays)?|h(?:r|rs|our|ours)?|m(?:in|ins|inute|inutes)?|s(?:ec|ecs|econd|econds)?)/gi;
1062
+ let totalMs = 0;
1063
+ let matched = false;
1064
+ let m;
1065
+ while ((m = re.exec(text)) !== null) {
1066
+ const ms = unitMs(Number(m[1]), m[2]);
1067
+ if (!Number.isFinite(ms)) continue;
1068
+ totalMs += ms;
1069
+ matched = true;
1070
+ }
1071
+ return matched ? totalMs : void 0;
1072
+ }
1073
+ function extractProvider(error) {
1074
+ if (!error || typeof error !== "object") return void 0;
1075
+ const p = error.provider;
1076
+ if (typeof p === "string" && p.length > 0) return p;
1077
+ const message = extractMessage(error);
1078
+ const tag = /\[QUOTA_LIMITED:([^\]]+)\]/i.exec(message);
1079
+ if (tag?.[1]) return tag[1];
1080
+ return void 0;
1081
+ }
1082
+ function extractRetryAfterHeader(error) {
1083
+ if (!error || typeof error !== "object") return void 0;
1084
+ const headers = error.headers;
1085
+ if (!headers || typeof headers !== "object") return void 0;
1086
+ const raw = headers["retry-after"];
1087
+ if (typeof raw !== "string" && typeof raw !== "number") return void 0;
1088
+ const secs = Number(raw);
1089
+ return Number.isFinite(secs) && secs > 0 ? secs * 1e3 : void 0;
1090
+ }
1091
+ function classifyRunFailure(error) {
1092
+ const message = extractMessage(error);
1093
+ const status = extractStatus(error);
1094
+ const retryAfterMs = extractRetryAfterHeader(error) ?? parseResetsInMs(message);
1095
+ if (isQuotaExhaustedError(error)) {
1096
+ return {
1097
+ reason: "quota",
1098
+ provider: extractProvider(error),
1099
+ // 纯函数不取时钟:resetsAt 由调用方用 now + retryAfterMs 计算后补齐
1100
+ retryAfterMs,
1101
+ message
1102
+ };
1103
+ }
1104
+ if (isAgentNotFoundError(error)) {
1105
+ return { reason: "agent-not-found", message };
1106
+ }
1107
+ if (isAuthError(error)) {
1108
+ return { reason: "auth", message };
1109
+ }
1110
+ if (isNetworkError(error)) {
1111
+ return { reason: "network", message };
1112
+ }
1113
+ return { reason: "other", message };
1114
+ }
1115
+ function isAuthError(error) {
1116
+ const message = extractMessage(error);
1117
+ const status = extractStatus(error);
1118
+ if (status === 401 || status === 403) return true;
1119
+ return AUTH_ERROR_PATTERNS.some((pattern) => pattern.test(message));
1120
+ }
1121
+ function isNetworkError(error) {
1122
+ const message = extractMessage(error);
1123
+ return NETWORK_ERROR_PATTERNS.some((pattern) => pattern.test(message));
1124
+ }
1125
+ var DEFAULT_QUOTA_BREAKER_MS = 6 * 3600 * 1e3;
1126
+ var DEFAULT_AGENT_NOT_FOUND_BREAKER_MS = 15 * 60 * 1e3;
1127
+ function buildBreakerEntry(now, target, kind, retryAfterMs) {
1128
+ const ttl = kind === "quota" ? typeof retryAfterMs === "number" && retryAfterMs > 0 ? retryAfterMs : DEFAULT_QUOTA_BREAKER_MS : DEFAULT_AGENT_NOT_FOUND_BREAKER_MS;
1129
+ return {
1130
+ target,
1131
+ kind,
1132
+ until: now + ttl,
1133
+ ...kind === "quota" && typeof retryAfterMs === "number" ? { resetsAt: now + retryAfterMs } : {}
1134
+ };
1135
+ }
1136
+
953
1137
  // packages/cli/client/localRuntimeDiagnostics.ts
954
1138
  import { ulid } from "ulid";
955
1139
  var preparedAgentRuntimeCache = /* @__PURE__ */ new Map();
@@ -989,7 +1173,7 @@ function summarizeOpenAiToolNames(tools) {
989
1173
  }
990
1174
 
991
1175
  // packages/cli/client/localRuntimeAdapter.ts
992
- import { dirname, extname, join } from "node:path";
1176
+ import { dirname as dirname2, extname, join as join2 } from "node:path";
993
1177
  import { fileURLToPath } from "node:url";
994
1178
 
995
1179
  // packages/core/summarizeEndpoint.ts
@@ -2048,7 +2232,7 @@ function buildServerPlatformToolExecutors(args) {
2048
2232
  }
2049
2233
 
2050
2234
  // packages/cli/client/cliAgentRunToolExecutors.ts
2051
- import { existsSync, readFileSync } from "node:fs";
2235
+ import { existsSync as existsSync2, readFileSync as readFileSync2 } from "node:fs";
2052
2236
 
2053
2237
  // packages/cli/client/agentRunSnapshot.ts
2054
2238
  function readString(value) {
@@ -2135,6 +2319,276 @@ function parseAgentRunEvent(event) {
2135
2319
  return { kind: "status", snapshot };
2136
2320
  }
2137
2321
 
2322
+ // packages/ai/tools/agent/batchAggregation.ts
2323
+ var DEFAULT_BATCH_TIMEOUT_MS = 30 * 60 * 1e3;
2324
+ var MAX_ERROR_SUMMARY_CHARS = 120;
2325
+ var MAX_FAILURE_LINES = 10;
2326
+ function isFailureStatus(status) {
2327
+ return isAgentRunTerminalStatus(status) && status !== "done";
2328
+ }
2329
+ function isOrphanedStatus(status) {
2330
+ return status === "orphaned";
2331
+ }
2332
+ function toEpochMs(iso) {
2333
+ if (!iso) return void 0;
2334
+ const ms = Date.parse(iso);
2335
+ return Number.isFinite(ms) ? ms : void 0;
2336
+ }
2337
+ function isOverTimeout(run, nowMs, timeoutMs) {
2338
+ const startedMs = toEpochMs(run.startedAt);
2339
+ if (startedMs === void 0) return false;
2340
+ return nowMs - startedMs >= timeoutMs;
2341
+ }
2342
+ function truncateErrorSummary(text, max = MAX_ERROR_SUMMARY_CHARS) {
2343
+ if (!text) return "";
2344
+ const collapsed = text.replace(/\s+/g, " ").trim();
2345
+ if (collapsed.length <= max) return collapsed;
2346
+ return `${collapsed.slice(0, max - 1)}\u2026`;
2347
+ }
2348
+ function formatFailureLine(run) {
2349
+ const label = shortRunId(run.runId);
2350
+ const agent = run.agentName ? ` (${run.agentName})` : "";
2351
+ const error = truncateErrorSummary(run.errorSummary);
2352
+ return error ? `\u5931\u8D25: ${label}${agent} \u2014 ${error}` : `\u5931\u8D25: ${label}${agent}`;
2353
+ }
2354
+ function formatBatchSummary(batchId, counts, runs, reason) {
2355
+ const failures = runs.filter((r) => isFailureStatus(r.status));
2356
+ const unfinished = runs.filter((r) => !isAgentRunTerminalStatus(r.status));
2357
+ if (reason === "timeout") {
2358
+ const lines2 = [
2359
+ `\u6279\u6B21 ${batchId}: \u8D85\u65F6\u672A\u6536\u655B \u2014 ${counts.running} \u4E2A\u672A\u5B8C\u6210`
2360
+ ];
2361
+ for (const run of unfinished.slice(0, MAX_FAILURE_LINES)) {
2362
+ const agent = run.agentName ? ` (${run.agentName})` : "";
2363
+ lines2.push(`\u672A\u5B8C\u6210: ${shortRunId(run.runId)}${agent}`);
2364
+ }
2365
+ const rest2 = unfinished.length - MAX_FAILURE_LINES;
2366
+ if (rest2 > 0) lines2.push(`\u2026 \u53CA\u53E6\u5916 ${rest2} \u4E2A\u672A\u5B8C\u6210`);
2367
+ return lines2.join("\n");
2368
+ }
2369
+ const tail = counts.running > 0 ? `, ${counts.running} \u4ECD\u5728\u8DD1` : "";
2370
+ const lines = [
2371
+ `\u6279\u6B21 ${batchId}: ${counts.success + counts.failed}/${counts.total} \u5B8C\u6210 \u2014 ${counts.success} \u6210\u529F, ${counts.failed} \u5931\u8D25${tail}`
2372
+ ];
2373
+ for (const run of failures.slice(0, MAX_FAILURE_LINES)) {
2374
+ lines.push(formatFailureLine(run));
2375
+ }
2376
+ const rest = failures.length - MAX_FAILURE_LINES;
2377
+ if (rest > 0) lines.push(`\u2026 \u53CA\u53E6\u5916 ${rest} \u4E2A\u5931\u8D25`);
2378
+ return lines.join("\n");
2379
+ }
2380
+ function aggregateBatch(input) {
2381
+ const { batchId, runs, now } = input;
2382
+ const timeoutMs = input.timeoutMs ?? DEFAULT_BATCH_TIMEOUT_MS;
2383
+ const notified = new Set(input.notifiedReasons ?? []);
2384
+ const nowMs = toEpochMs(now) ?? 0;
2385
+ let success = 0;
2386
+ let failed = 0;
2387
+ let orphaned = 0;
2388
+ let running = 0;
2389
+ const unfinished = [];
2390
+ let timedOut = false;
2391
+ for (const run of runs) {
2392
+ if (isAgentRunTerminalStatus(run.status)) {
2393
+ if (run.status === "done") success += 1;
2394
+ else {
2395
+ failed += 1;
2396
+ if (isOrphanedStatus(run.status)) orphaned += 1;
2397
+ }
2398
+ } else {
2399
+ running += 1;
2400
+ unfinished.push(run.runId);
2401
+ if (isOverTimeout(run, nowMs, timeoutMs)) timedOut = true;
2402
+ }
2403
+ }
2404
+ const converged = running === 0;
2405
+ let newWakeReasons = [];
2406
+ if (failed > 0 && !notified.has("failure")) {
2407
+ newWakeReasons = ["failure"];
2408
+ } else if (converged && !notified.has("converged")) {
2409
+ newWakeReasons = ["converged"];
2410
+ } else if (timedOut && !notified.has("timeout")) {
2411
+ newWakeReasons = ["timeout"];
2412
+ }
2413
+ const counts = {
2414
+ total: runs.length,
2415
+ success,
2416
+ failed,
2417
+ orphaned,
2418
+ running
2419
+ };
2420
+ const summary = newWakeReasons.length > 0 ? formatBatchSummary(batchId, counts, runs, newWakeReasons[0]) : "";
2421
+ return {
2422
+ batchId,
2423
+ converged,
2424
+ timedOut,
2425
+ counts,
2426
+ newWakeReasons,
2427
+ unfinished,
2428
+ summary
2429
+ };
2430
+ }
2431
+
2432
+ // packages/ai/tools/agent/runtimeTodo.ts
2433
+ var TODO_REVIEW_BLOCK = "BLOCK";
2434
+ function deriveTodoStatus(input) {
2435
+ const { todo, runs } = input;
2436
+ if (todo.status === "abandoned") {
2437
+ return { status: "abandoned" };
2438
+ }
2439
+ if (runs.length === 0) {
2440
+ return { status: todo.status };
2441
+ }
2442
+ if (runs.some((r) => !isAgentRunTerminalStatus(r.status))) {
2443
+ return { status: "running", latestRun: latestRun(runs) };
2444
+ }
2445
+ const latest = latestRun(runs);
2446
+ if (latest?.reviewVerdict === TODO_REVIEW_BLOCK) {
2447
+ return {
2448
+ status: "blocked",
2449
+ latestRun: latest,
2450
+ blockedReason: clipSummary(latest.reviewReason, MAX_ERROR_SUMMARY_CHARS)
2451
+ };
2452
+ }
2453
+ if (latest?.status === "done") {
2454
+ return { status: "done", latestRun: latest };
2455
+ }
2456
+ return { status: "pending", latestRun: latest };
2457
+ }
2458
+ function toEpochMs2(iso) {
2459
+ if (!iso) return void 0;
2460
+ const ms = Date.parse(iso);
2461
+ return Number.isFinite(ms) ? ms : void 0;
2462
+ }
2463
+ function latestRun(runs) {
2464
+ let latest;
2465
+ let latestScore = -1;
2466
+ for (const run of runs) {
2467
+ const score = toEpochMs2(run.finishedAt) ?? toEpochMs2(run.startedAt) ?? -1;
2468
+ if (latest === void 0 || score >= latestScore) {
2469
+ latest = run;
2470
+ latestScore = score;
2471
+ }
2472
+ }
2473
+ return latest;
2474
+ }
2475
+ function clipSummary(text, max) {
2476
+ if (!text) return void 0;
2477
+ const folded = text.replace(/\s+/g, " ").trim();
2478
+ if (folded.length === 0) return void 0;
2479
+ return folded.length > max ? `${folded.slice(0, max - 1)}\u2026` : folded;
2480
+ }
2481
+
2482
+ // packages/cli/client/fileSystemStores.ts
2483
+ import { existsSync, mkdirSync, readFileSync, writeFileSync } from "node:fs";
2484
+ import { homedir as nodeHomedir } from "node:os";
2485
+ import { dirname, join } from "node:path";
2486
+ var nodeFs = {
2487
+ mkdirSync,
2488
+ writeFileSync,
2489
+ readFileSync,
2490
+ existsSync
2491
+ };
2492
+ function resolveNoloHome(env, homedir) {
2493
+ const custom = env?.NOLO_HOME;
2494
+ if (custom && custom.trim()) return custom.trim();
2495
+ return join(homedir(), ".nolo");
2496
+ }
2497
+ function ensureDir(dir, fs) {
2498
+ fs.mkdirSync(dir, { recursive: true });
2499
+ }
2500
+ function readJson(path, fs) {
2501
+ if (!fs.existsSync(path)) return void 0;
2502
+ try {
2503
+ const raw = fs.readFileSync(path, "utf8");
2504
+ return JSON.parse(String(raw));
2505
+ } catch {
2506
+ return void 0;
2507
+ }
2508
+ }
2509
+ function writeJson(path, data, fs) {
2510
+ fs.writeFileSync(path, JSON.stringify(data, null, 2));
2511
+ }
2512
+ function createFileSystemCircuitBreakerStore(opts = {}) {
2513
+ const env = opts.env ?? process.env;
2514
+ const homedir = opts.homedir ?? nodeHomedir;
2515
+ const fs = opts.fs ?? nodeFs;
2516
+ const filePath = opts.filePath ?? join(resolveNoloHome(env, homedir), "breakers.json");
2517
+ const loadMap = () => {
2518
+ const data = readJson(filePath, fs);
2519
+ const map = /* @__PURE__ */ new Map();
2520
+ if (data?.entries) {
2521
+ for (const entry of data.entries) {
2522
+ if (entry && typeof entry.target === "string") map.set(entry.target, entry);
2523
+ }
2524
+ }
2525
+ return map;
2526
+ };
2527
+ const persist = (map) => {
2528
+ ensureDir(dirname(filePath), fs);
2529
+ writeJson(filePath, { entries: [...map.values()] }, fs);
2530
+ };
2531
+ return {
2532
+ get(target) {
2533
+ return loadMap().get(target);
2534
+ },
2535
+ set(entry) {
2536
+ const map = loadMap();
2537
+ map.set(entry.target, entry);
2538
+ persist(map);
2539
+ },
2540
+ clear(target) {
2541
+ const map = loadMap();
2542
+ map.delete(target);
2543
+ persist(map);
2544
+ },
2545
+ clearAll() {
2546
+ persist(/* @__PURE__ */ new Map());
2547
+ }
2548
+ };
2549
+ }
2550
+ function createFileSystemTodoStore(opts = {}) {
2551
+ const env = opts.env ?? process.env;
2552
+ const homedir = opts.homedir ?? nodeHomedir;
2553
+ const fs = opts.fs ?? nodeFs;
2554
+ const filePath = opts.filePath ?? join(resolveNoloHome(env, homedir), "todos.json");
2555
+ const loadMap = () => {
2556
+ const data = readJson(filePath, fs);
2557
+ const map = /* @__PURE__ */ new Map();
2558
+ if (data?.todos) {
2559
+ for (const todo of data.todos) {
2560
+ if (todo && typeof todo.id === "string") map.set(todo.id, todo);
2561
+ }
2562
+ }
2563
+ return map;
2564
+ };
2565
+ const persist = (map) => {
2566
+ ensureDir(dirname(filePath), fs);
2567
+ writeJson(filePath, { todos: [...map.values()] }, fs);
2568
+ };
2569
+ return {
2570
+ async getTodo(id) {
2571
+ return loadMap().get(id);
2572
+ },
2573
+ async listTodos(filter) {
2574
+ const all = [...loadMap().values()];
2575
+ if (!filter?.statuses || filter.statuses.length === 0) return all;
2576
+ const wanted = new Set(filter.statuses);
2577
+ return all.filter((t2) => wanted.has(t2.status));
2578
+ },
2579
+ async putTodo(todo) {
2580
+ const map = loadMap();
2581
+ map.set(todo.id, todo);
2582
+ persist(map);
2583
+ },
2584
+ async deleteTodo(id) {
2585
+ const map = loadMap();
2586
+ map.delete(id);
2587
+ persist(map);
2588
+ }
2589
+ };
2590
+ }
2591
+
2138
2592
  // packages/cli/client/cliAgentRunToolExecutors.ts
2139
2593
  var noopOutput = { write: () => {
2140
2594
  } };
@@ -2145,7 +2599,7 @@ var parseCallArgs = (call) => {
2145
2599
  return {};
2146
2600
  }
2147
2601
  };
2148
- var resolveFs = (deps) => deps.fs ?? { existsSync, readFileSync };
2602
+ var resolveFs = (deps) => deps.fs ?? { existsSync: existsSync2, readFileSync: readFileSync2 };
2149
2603
  var tailFile = (logPath, tailLines, fs) => {
2150
2604
  if (!fs.existsSync(logPath)) return void 0;
2151
2605
  try {
@@ -2157,6 +2611,10 @@ var tailFile = (logPath, tailLines, fs) => {
2157
2611
  return void 0;
2158
2612
  }
2159
2613
  };
2614
+ var resolveBreakerStore = (deps) => deps.breakerStore ?? createFileSystemCircuitBreakerStore({ env: deps.env, homedir: deps.homedir, fs: deps.fs });
2615
+ var resolveTodoStore = (deps) => deps.todoStore ?? createFileSystemTodoStore({ env: deps.env, homedir: deps.homedir, fs: deps.fs });
2616
+ var resolveNowMs = (deps) => typeof deps.nowMs === "function" ? deps.nowMs() : Date.now();
2617
+ var resolveTarget = (deps, agentKey) => typeof deps.resolveProviderTarget === "function" ? deps.resolveProviderTarget(agentKey) : agentKey;
2160
2618
  function createCliStartAgentRunExecutor(deps = {}) {
2161
2619
  return async (call) => {
2162
2620
  const args = parseCallArgs(call);
@@ -2164,6 +2622,29 @@ function createCliStartAgentRunExecutor(deps = {}) {
2164
2622
  const task = typeof args.task === "string" ? args.task.trim() : "";
2165
2623
  if (!agentKey) throw new Error("startAgentRun: \u7F3A\u5C11 agentKey \u53C2\u6570\u3002");
2166
2624
  if (!task) throw new Error("startAgentRun: \u7F3A\u5C11\u6709\u6548\u7684 task \u6587\u672C\u63CF\u8FF0\u3002");
2625
+ const breakerStore = resolveBreakerStore(deps);
2626
+ const target = resolveTarget(deps, agentKey);
2627
+ const nowMs = resolveNowMs(deps);
2628
+ const activeBreaker = findActiveBreaker(
2629
+ nowMs,
2630
+ [breakerStore.get(target)].filter(Boolean),
2631
+ target
2632
+ );
2633
+ if (activeBreaker) {
2634
+ const resetsAt = activeBreaker.resetsAt;
2635
+ return {
2636
+ content: JSON.stringify({
2637
+ rejected: true,
2638
+ reason: "quota",
2639
+ provider: target,
2640
+ ...resetsAt !== void 0 ? { resetsAt } : {},
2641
+ message: `provider ${target} \u5904\u4E8E\u7194\u65AD\u671F\uFF08\u914D\u989D\u8017\u5C3D\uFF09\uFF0C\u5EFA\u8BAE\u6539\u6D3E\u5176\u5B83 provider\u3002`
2642
+ }),
2643
+ metadata: {
2644
+ displayData: `Run rejected \xB7 quota breaker active on ${target}`
2645
+ }
2646
+ };
2647
+ }
2167
2648
  const message = typeof args.input === "undefined" ? task : `${task}
2168
2649
 
2169
2650
  --- \u9644\u52A0\u8F93\u5165 ---
@@ -2179,13 +2660,15 @@ ${JSON.stringify(args.input)}`;
2179
2660
  ...args.ephemeral === true ? ["--ephemeral"] : []
2180
2661
  ];
2181
2662
  const agentName = typeof args.agentName === "string" && args.agentName.trim() ? args.agentName.trim() : typeof args.name === "string" && args.name.trim() ? args.name.trim() : void 0;
2182
- const { runId } = await spawnLocalBackgroundRun(
2663
+ const batchId = typeof args.batchId === "string" && args.batchId.trim() ? args.batchId.trim() : void 0;
2664
+ const { runId, batchId: resolvedBatchId } = await spawnLocalBackgroundRun(
2183
2665
  {
2184
2666
  rawArgs,
2185
2667
  commandPath: ["agent", "run"],
2186
2668
  cliEntrypointPath: deps.cliEntrypoint,
2187
2669
  agentKey,
2188
2670
  ...agentName ? { agentName } : {},
2671
+ ...batchId ? { batchId } : {},
2189
2672
  cwd: deps.cwd ?? process.cwd(),
2190
2673
  message,
2191
2674
  output: noopOutput
@@ -2195,10 +2678,39 @@ ${JSON.stringify(args.input)}`;
2195
2678
  const displayName = resolveRunLabel({ agentName, agentKey, runId });
2196
2679
  const labels = agentRunCardLabels();
2197
2680
  const taskPreview = typeof args.task === "string" ? args.task.replace(/\s+/g, " ").trim().slice(0, TASK_PREVIEW_MAX) : "";
2681
+ const trackTodo = typeof args.trackTodo === "boolean" ? args.trackTodo : Boolean(resolvedBatchId);
2682
+ if (trackTodo) {
2683
+ try {
2684
+ const todoStore = resolveTodoStore(deps);
2685
+ const todoId = `todo-${resolvedBatchId}`;
2686
+ const existing = await todoStore.getTodo(todoId);
2687
+ const nowIso = new Date(nowMs).toISOString();
2688
+ const runIds = existing?.runIds ?? [];
2689
+ if (!runIds.includes(runId)) runIds.push(runId);
2690
+ const todo = {
2691
+ id: todoId,
2692
+ title: taskPreview || task.slice(0, 100),
2693
+ status: existing?.status ?? "pending",
2694
+ ...existing?.specPath ? { specPath: existing.specPath } : {},
2695
+ ...existing?.worktree ? { worktree: existing.worktree } : {},
2696
+ ...existing?.branch ? { branch: existing.branch } : {},
2697
+ ...deps.cwd ? { worktree: deps.cwd } : {},
2698
+ runIds,
2699
+ deps: existing?.deps ?? [],
2700
+ createdAt: existing?.createdAt ?? nowIso,
2701
+ updatedAt: nowIso
2702
+ };
2703
+ await todoStore.putTodo(todo);
2704
+ } catch {
2705
+ }
2706
+ }
2198
2707
  return {
2199
2708
  content: JSON.stringify({
2200
2709
  runId,
2201
2710
  status: "running",
2711
+ // batchId is always returned so the caller can later filter by it —
2712
+ // even when the caller didn't supply one, a fresh id was generated.
2713
+ batchId: resolvedBatchId,
2202
2714
  ...agentName ? { agentName } : {},
2203
2715
  ...taskPreview ? { taskPreview } : {}
2204
2716
  }),
@@ -2218,26 +2730,103 @@ function createCliControlAgentRunExecutor(deps = {}) {
2218
2730
  const action = typeof args.action === "string" ? args.action : "";
2219
2731
  const tailLines = typeof args.tailLines === "number" ? args.tailLines : 0;
2220
2732
  if (action === "list") {
2221
- const records = listRunRecords(deps);
2733
+ gcRunRecords(deps);
2734
+ const { runs: records, total, hasMore } = queryRunRecords(
2735
+ {
2736
+ ...typeof args.batchId === "string" && args.batchId.trim() ? { batchId: args.batchId.trim() } : {},
2737
+ ...typeof args.status === "string" && args.status.trim() ? { status: args.status.trim() } : typeof args.statusFilter === "string" && args.statusFilter.trim() ? { status: args.statusFilter.trim() } : {},
2738
+ ...typeof args.limit === "number" ? { limit: args.limit } : {},
2739
+ ...typeof args.offset === "number" ? { offset: args.offset } : {}
2740
+ },
2741
+ deps
2742
+ );
2222
2743
  const runs = records.map((record2) => {
2223
- const reconciled = checkStaleRun(record2.runId, deps) ?? record2;
2224
2744
  return {
2225
- runId: reconciled.runId,
2226
- status: reconciled.status,
2227
- agentKey: reconciled.agentKey,
2228
- ...reconciled.agentName ? { agentName: reconciled.agentName } : {},
2229
- pid: reconciled.pid ?? null,
2230
- startedAt: reconciled.startedAt
2745
+ runId: record2.runId,
2746
+ status: record2.status,
2747
+ agentKey: record2.agentKey,
2748
+ ...record2.agentName ? { agentName: record2.agentName } : {},
2749
+ ...record2.batchId ? { batchId: record2.batchId } : {},
2750
+ pid: record2.pid ?? null,
2751
+ startedAt: record2.startedAt
2231
2752
  };
2232
2753
  });
2233
2754
  const labels2 = agentRunCardLabels();
2755
+ const batchIdArg = typeof args.batchId === "string" && args.batchId.trim() ? args.batchId.trim() : void 0;
2756
+ let batchSummary;
2757
+ if (batchIdArg && records.length > 0) {
2758
+ const batchRuns = records.map((r) => ({
2759
+ runId: r.runId,
2760
+ status: r.status,
2761
+ startedAt: r.startedAt,
2762
+ ...r.agentName ? { agentName: r.agentName } : {}
2763
+ }));
2764
+ batchSummary = aggregateBatch({
2765
+ batchId: batchIdArg,
2766
+ runs: batchRuns,
2767
+ now: new Date(resolveNowMs(deps)).toISOString()
2768
+ });
2769
+ }
2234
2770
  return {
2235
- content: JSON.stringify({ runs, count: runs.length }),
2771
+ content: JSON.stringify({
2772
+ runs,
2773
+ count: runs.length,
2774
+ total,
2775
+ hasMore,
2776
+ ...batchSummary?.summary ? { batchSummary: batchSummary.summary } : {}
2777
+ }),
2236
2778
  metadata: {
2237
2779
  displayData: formatListRunsCard(runs, labels2)
2238
2780
  }
2239
2781
  };
2240
2782
  }
2783
+ if (action === "todo") {
2784
+ const todoStore = resolveTodoStore(deps);
2785
+ const statusesRaw = typeof args.status === "string" ? args.status.trim() : void 0;
2786
+ const filter = statusesRaw && statusesRaw !== "all" ? { statuses: statusesRaw.split(",").map((s) => s.trim()).filter(Boolean) } : void 0;
2787
+ const todos = await todoStore.listTodos(filter);
2788
+ const enriched = await Promise.all(
2789
+ todos.map(async (todo) => {
2790
+ const runSummaries = [];
2791
+ for (const rid of todo.runIds) {
2792
+ const rec = findRunRecord(rid, deps);
2793
+ if (!rec) continue;
2794
+ runSummaries.push({
2795
+ runId: rec.runId,
2796
+ status: rec.status,
2797
+ startedAt: rec.startedAt,
2798
+ ...rec.agentName ? { agentName: rec.agentName } : {}
2799
+ });
2800
+ }
2801
+ const derived = deriveTodoStatus({
2802
+ todo: { id: todo.id, status: todo.status },
2803
+ runs: runSummaries,
2804
+ now: new Date(resolveNowMs(deps)).toISOString()
2805
+ });
2806
+ return {
2807
+ id: todo.id,
2808
+ title: todo.title,
2809
+ status: derived.status,
2810
+ runIds: todo.runIds,
2811
+ ...derived.blockedReason ? { blockedReason: derived.blockedReason } : {},
2812
+ ...derived.latestRun ? { latestRunId: derived.latestRun.runId, latestRunStatus: derived.latestRun.status } : {},
2813
+ updatedAt: todo.updatedAt
2814
+ };
2815
+ })
2816
+ );
2817
+ return {
2818
+ content: JSON.stringify({ todos: enriched, count: enriched.length }),
2819
+ metadata: {
2820
+ displayData: formatListRunsCard(
2821
+ enriched.map((t2) => ({
2822
+ agentName: t2.title,
2823
+ status: t2.status
2824
+ })),
2825
+ agentRunCardLabels()
2826
+ )
2827
+ }
2828
+ };
2829
+ }
2241
2830
  if (action !== "status" && action !== "stop") {
2242
2831
  throw new Error(`controlAgentRun: \u672A\u77E5 action "${action}"\u3002`);
2243
2832
  }
@@ -2256,6 +2845,25 @@ function createCliControlAgentRunExecutor(deps = {}) {
2256
2845
  const name = resolveRunLabel(reconciled);
2257
2846
  const logLines = logTail ? logTail.split("\n") : void 0;
2258
2847
  const labels2 = agentRunCardLabels();
2848
+ if (reconciled.status === "failed" || reconciled.status === "orphaned") {
2849
+ try {
2850
+ const failureSource = logTail ?? reconciled.note ?? "";
2851
+ const failureInfo = classifyRunFailure(
2852
+ failureSource || reconciled.status
2853
+ );
2854
+ if (failureInfo.reason === "quota") {
2855
+ const t2 = resolveTarget(deps, reconciled.agentKey);
2856
+ const entry = buildBreakerEntry(
2857
+ resolveNowMs(deps),
2858
+ t2,
2859
+ "quota",
2860
+ failureInfo.retryAfterMs
2861
+ );
2862
+ resolveBreakerStore(deps).set(entry);
2863
+ }
2864
+ } catch {
2865
+ }
2866
+ }
2259
2867
  return {
2260
2868
  content: JSON.stringify({
2261
2869
  runId: reconciled.runId,
@@ -10420,9 +11028,9 @@ function createCliCallAgentToolExecutor(deps, ctx) {
10420
11028
  // packages/cli/client/localRuntimeAdapter.ts
10421
11029
  var currentMetaFile = fileURLToPath(import.meta.url);
10422
11030
  var isJsBundle = extname(currentMetaFile) === ".js";
10423
- var SOURCE_CLI_DIR = isJsBundle ? dirname(currentMetaFile) : dirname(dirname(currentMetaFile));
10424
- var CLI_DIR = isCompiledBinary() ? dirname(process.execPath) : SOURCE_CLI_DIR;
10425
- var CLI_ENTRYPOINT = isCompiledBinary() ? process.execPath : isJsBundle ? currentMetaFile : join(SOURCE_CLI_DIR, "index.ts");
11031
+ var SOURCE_CLI_DIR = isJsBundle ? dirname2(currentMetaFile) : dirname2(dirname2(currentMetaFile));
11032
+ var CLI_DIR = isCompiledBinary() ? dirname2(process.execPath) : SOURCE_CLI_DIR;
11033
+ var CLI_ENTRYPOINT = isCompiledBinary() ? process.execPath : isJsBundle ? currentMetaFile : join2(SOURCE_CLI_DIR, "index.ts");
10426
11034
  function createCliLocalRuntimeAdapter(deps) {
10427
11035
  const now = deps.now ?? Date.now;
10428
11036
  const createId = deps.createId ?? createFallbackId;
@@ -11262,6 +11870,8 @@ export {
11262
11870
  t,
11263
11871
  toolLabel,
11264
11872
  agentRunCardLabels,
11873
+ QUOTA_ERROR_PATTERNS,
11874
+ isQuotaExhaustedError,
11265
11875
  clearCliLocalRuntimePreparedAgentCache,
11266
11876
  createCliCallAgentToolExecutor,
11267
11877
  createCliLocalRuntimeAdapter