@armadra/agent 0.7.5 → 0.7.6

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 (62) hide show
  1. package/CHANGELOG.md +21 -0
  2. package/CHANGELOG.zh-CN.md +21 -0
  3. package/dist/agent/session-classifier.d.ts +2 -0
  4. package/dist/agent/session-classifier.js +24 -3
  5. package/dist/agent/session-extensions.d.ts +1 -0
  6. package/dist/agent/session-extensions.js +3 -0
  7. package/dist/agent/session-subagent.js +9 -2
  8. package/dist/agent/subagent-background.d.ts +5 -1
  9. package/dist/agent/subagent-background.js +13 -1
  10. package/dist/agent/subagent-fork.d.ts +10 -5
  11. package/dist/agent/subagent-fork.js +22 -6
  12. package/dist/agent/subagent-registry.d.ts +1 -1
  13. package/dist/agent/subagent-registry.js +1 -1
  14. package/dist/agents/catalog.d.ts +1 -0
  15. package/dist/ai/apis/max-tokens.d.ts +4 -0
  16. package/dist/ai/apis/max-tokens.js +12 -1
  17. package/dist/ai/providers/enrich.d.ts +6 -2
  18. package/dist/ai/providers/enrich.js +13 -2
  19. package/dist/ai/providers/max-tokens-cache.d.ts +22 -0
  20. package/dist/ai/providers/max-tokens-cache.js +95 -0
  21. package/dist/ai/providers/registry.js +3 -3
  22. package/dist/bundle/ama.cjs +296 -103
  23. package/dist/checkpoints/shadow-git.d.ts +5 -1
  24. package/dist/checkpoints/shadow-git.js +11 -2
  25. package/dist/cli/compose-providers.d.ts +1 -0
  26. package/dist/cli/compose-providers.js +3 -0
  27. package/dist/cli/compose-store.d.ts +6 -2
  28. package/dist/cli/compose-store.js +8 -4
  29. package/dist/cli/compose.js +2 -2
  30. package/dist/config/json-schema.js +1 -0
  31. package/dist/config/key-docs.js +1 -0
  32. package/dist/config/schema-w5.js +2 -0
  33. package/dist/config/settings-registry.js +1 -0
  34. package/dist/config/types-w5.d.ts +5 -0
  35. package/dist/i18n/catalog.d.ts +2 -0
  36. package/dist/i18n/messages/config-keys.d.ts +2 -0
  37. package/dist/i18n/messages/config-keys.js +2 -0
  38. package/dist/i18n/messages/config.d.ts +2 -0
  39. package/dist/i18n/messages/interactive.d.ts +2 -0
  40. package/dist/i18n/messages/interactive.js +2 -0
  41. package/dist/i18n/messages/session.js +2 -2
  42. package/dist/modes/interactive/agent-ui.js +7 -0
  43. package/dist/session/manager.d.ts +3 -1
  44. package/dist/session/manager.js +5 -0
  45. package/dist/session/offload.d.ts +10 -2
  46. package/dist/session/offload.js +27 -4
  47. package/dist/tools/background-jobs.d.ts +4 -4
  48. package/dist/tools/background-jobs.js +4 -4
  49. package/dist/tools/bash.d.ts +2 -1
  50. package/dist/tools/bash.js +4 -3
  51. package/dist/tools/task-ctl.js +2 -1
  52. package/dist/tools/task.d.ts +9 -0
  53. package/dist/tools/task.js +27 -1
  54. package/docs/agents.md +14 -3
  55. package/docs/en/permissions.md +1 -1
  56. package/docs/en/providers.md +4 -4
  57. package/docs/en/sessions.md +2 -1
  58. package/docs/permissions.md +1 -1
  59. package/docs/providers.md +9 -5
  60. package/docs/session-format.md +2 -1
  61. package/docs/sessions.md +2 -1
  62. package/package.json +1 -1
@@ -41,7 +41,7 @@ var AMA_VERSION;
41
41
  var init_version = __esm({
42
42
  "src/version.ts"() {
43
43
  "use strict";
44
- AMA_VERSION = true ? "0.7.5" : readPackageVersion();
44
+ AMA_VERSION = true ? "0.7.6" : readPackageVersion();
45
45
  }
46
46
  });
47
47
 
@@ -1969,7 +1969,8 @@ var init_interactive = __esm({
1969
1969
  backgroundExited: (jobId, code, command3) => `Background command ${jobId} exited (code ${code}): ${command3}`,
1970
1970
  backgroundStopped: (jobId, command3) => `Background command ${jobId} stopped: ${command3}`,
1971
1971
  compactionFailed: (error2) => `Compaction failed: ${error2}`,
1972
- compactionCancelled: "Compaction cancelled"
1972
+ compactionCancelled: "Compaction cancelled",
1973
+ imageReadBackFailed: (id) => `Session image for entry ${id} could not be read back (file changed); it stays empty`
1973
1974
  },
1974
1975
  statusLine: {
1975
1976
  usage: "Usage: /statusline [full|compact]",
@@ -2104,7 +2105,8 @@ var init_interactive = __esm({
2104
2105
  backgroundExited: (jobId, code, command3) => `后台命令 ${jobId} 已退出(码 ${code}):${command3}`,
2105
2106
  backgroundStopped: (jobId, command3) => `后台命令 ${jobId} 已停止:${command3}`,
2106
2107
  compactionFailed: (error2) => `压缩失败:${error2}`,
2107
- compactionCancelled: "压缩已取消"
2108
+ compactionCancelled: "压缩已取消",
2109
+ imageReadBackFailed: (id) => `会话文件已改变,条目 ${id} 的图片读不回,保留为空`
2108
2110
  },
2109
2111
  statusLine: {
2110
2112
  usage: "用法:/statusline [full|compact]",
@@ -3814,6 +3816,7 @@ var init_config_keys = __esm({
3814
3816
  "subagents.maxPending": "Queue limit; task fails beyond it",
3815
3817
  "subagents.retainSessions": "Finished sub-sessions kept in memory (least recently used first out); others reopen from their session file when continued",
3816
3818
  "subagents.defaultModel": "Default sub-session model provider/model[@channel]",
3819
+ "subagents.forkMaxContextRatio": "Fork sub-agents fall back to fresh when the parent's last request used more than this share of (window − compaction.reserveTokens); 0.05–0.95, default 0.5",
3817
3820
  "subagents.background": "Whether task runs in the background by default: auto = background in the TUI / RPC / ACP and foreground with -p; always / never are fixed. The call argument and the agent type's background: override it; user, project and host level",
3818
3821
  "subagents.autoBackgroundAfterMs": "Move a foreground task to the background after it has run this many milliseconds; 0 disables; user and project level",
3819
3822
  models: "Model aliases and the model picker list; user level only (effective from wave 5 W5-G)",
@@ -3942,6 +3945,7 @@ var init_config_keys = __esm({
3942
3945
  "subagents.maxPending": "排队上限,超出时 task 报错",
3943
3946
  "subagents.retainSessions": "内存中保留的已结束子会话数(最久未用的先释放),其余续聊时从会话文件重开",
3944
3947
  "subagents.defaultModel": "子会话缺省模型 provider/model[@channel]",
3948
+ "subagents.forkMaxContextRatio": "fork 子 Agent 的回落比例:父上一次请求超过(窗口 − compaction.reserveTokens)的这一比例时改为 fresh;0.05–0.95,缺省 0.5",
3945
3949
  "subagents.background": "task 缺省是否后台:auto = TUI / RPC / ACP 下后台、-p 下前台;always / never 固定。调用参数与类型定义的 background: 优先;用户、项目、宿主级都认",
3946
3950
  "subagents.autoBackgroundAfterMs": "前台任务运行超过该毫秒数自动转后台;0 关闭;用户、项目级都认",
3947
3951
  models: "模型别名与模型选择器清单;只认用户级(第五波 W5-G 起生效)",
@@ -4439,7 +4443,7 @@ var init_session = __esm({
4439
4443
  cwdIsHome: "the working directory is the home directory",
4440
4444
  gitNotFound: "git not found",
4441
4445
  tooManyFiles: (files, max) => `the working directory has ${plural(files, "file")} (limit ${max})`,
4442
- tooSlow: (seconds2, max) => `the snapshot took ${seconds2} s (limit ${max} s)`,
4446
+ tooSlow: (seconds2, max) => `snapshots took longer than ${max} s twice in a row (latest ${seconds2} s)`,
4443
4447
  gitFailed: (command3, code, detail) => `git ${command3} failed (${code})${detail === "" ? "" : `: ${detail}`}`,
4444
4448
  noShadowCommit: "the checkpoint has no shadow commit",
4445
4449
  shadowCommitMissing: (commit) => `shadow commit ${commit} does not exist`,
@@ -4571,7 +4575,7 @@ var init_session = __esm({
4571
4575
  cwdIsHome: "工作目录是家目录",
4572
4576
  gitNotFound: "找不到 git",
4573
4577
  tooManyFiles: (files, max) => `工作目录有 ${files} 个文件(上限 ${max})`,
4574
- tooSlow: (seconds2, max) => `快照用了 ${seconds2} 秒(上限 ${max} 秒)`,
4578
+ tooSlow: (seconds2, max) => `连续两次快照超过 ${max} 秒(最近一次 ${seconds2} 秒)`,
4575
4579
  gitFailed: (command3, code, detail) => `git ${command3} 失败(${code})${detail === "" ? "" : `:${detail}`}`,
4576
4580
  noShadowCommit: "检查点没有影子提交",
4577
4581
  shadowCommitMissing: (commit) => `影子提交 ${commit} 不存在`,
@@ -6776,7 +6780,8 @@ function validateConfigW5(c, config) {
6776
6780
  "defaultModel",
6777
6781
  "background",
6778
6782
  "autoBackgroundAfterMs",
6779
- "retainSessions"
6783
+ "retainSessions",
6784
+ "forkMaxContextRatio"
6780
6785
  ],
6781
6786
  (s2, p) => {
6782
6787
  c.number(s2, "maxConcurrent", p, 1, 64);
@@ -6785,6 +6790,7 @@ function validateConfigW5(c, config) {
6785
6790
  c.oneOf(s2, "background", p, SUBAGENT_BACKGROUND_MODES);
6786
6791
  c.number(s2, "autoBackgroundAfterMs", p, 0);
6787
6792
  c.number(s2, "retainSessions", p, 0);
6793
+ c.number(s2, "forkMaxContextRatio", p, 0.05, 0.95);
6788
6794
  }
6789
6795
  );
6790
6796
  checkSection(c, config, "models", ["aliases", "enabled"], (s2, p) => {
@@ -12382,7 +12388,7 @@ function fillLimits(out, hit, sources) {
12382
12388
  sources.maxTokens = "catalog-alias";
12383
12389
  }
12384
12390
  }
12385
- function enrichEntry(entry, index2) {
12391
+ function enrichEntry(entry, index2, providerId) {
12386
12392
  const { modelsDev, channels: _channels, catalog: _catalog, ...rest } = entry;
12387
12393
  const sources = {};
12388
12394
  for (const field2 of ENRICHABLE_FIELDS) {
@@ -12390,10 +12396,14 @@ function enrichEntry(entry, index2) {
12390
12396
  }
12391
12397
  const metadata = { sources, looked: false };
12392
12398
  const out = { ...rest };
12399
+ const warnings = [];
12400
+ const ref = `${providerId ?? "?"}/${entry.id}`;
12393
12401
  const hit = catalogHit(entry);
12394
12402
  if (hit !== void 0) {
12395
12403
  metadata.catalog = hit.ref;
12396
12404
  inheritIntrinsic(out, hit, sources);
12405
+ } else if (typeof entry.catalog === "string") {
12406
+ warnings.push(`catalog "${entry.catalog}" for "${ref}" not found; ignored`);
12397
12407
  }
12398
12408
  const explicit = typeof modelsDev === "string" ? modelsDev : hit?.snapshotRef;
12399
12409
  const lookup = modelsDev !== false && (needsLookup(out) || modelsDev !== void 0);
@@ -12420,7 +12430,10 @@ function enrichEntry(entry, index2) {
12420
12430
  }
12421
12431
  }
12422
12432
  }
12433
+ if (typeof modelsDev === "string" && metadata.looked && metadata.match === void 0)
12434
+ warnings.push(`modelsDev "${modelsDev}" for "${ref}" not found`);
12423
12435
  if (hit !== void 0 && modelsDev !== false) fillLimits(out, hit, sources);
12436
+ if (warnings.length > 0) metadata.warnings = warnings;
12424
12437
  return { entry: out, metadata };
12425
12438
  }
12426
12439
  function catalogMetadata(model) {
@@ -17599,8 +17612,8 @@ var init_background_jobs = __esm({
17599
17612
  await entry.done;
17600
17613
  return entry.job;
17601
17614
  }
17602
- /** 输出文件尾部(2000 行 / 50 KB)。 */
17603
- output(id) {
17615
+ /** 输出文件尾部(缺省 2000 行 / 50 KB;调用方可传字节上限,#155)。 */
17616
+ output(id, options = {}) {
17604
17617
  const entry = this.entries.get(id);
17605
17618
  if (entry === void 0) return void 0;
17606
17619
  let text = "";
@@ -17609,7 +17622,7 @@ var init_background_jobs = __esm({
17609
17622
  text = (0, import_node_fs24.readFileSync)(entry.job.outputPath, "utf8");
17610
17623
  } catch {
17611
17624
  }
17612
- return { ...truncateTail(text), job: entry.job };
17625
+ return { ...truncateTail(text, options), job: entry.job };
17613
17626
  }
17614
17627
  /** 取走已退出、尚未通知的任务(提醒通道用;wait 已看到退出的也算已知)。 */
17615
17628
  takeExited() {
@@ -18097,7 +18110,7 @@ async function jobAction(input2, ctx, options) {
18097
18110
  }
18098
18111
  if (action === "stop") {
18099
18112
  const job2 = await jobs.stop(id);
18100
- const out2 = jobs.output(id);
18113
+ const out2 = jobs.output(id, { maxBytes: toolOutputBytes(ctx.maxResultChars) });
18101
18114
  const text = out2?.content ?? "";
18102
18115
  return {
18103
18116
  content: `${text === "" ? "(no output)" : text}
@@ -18112,7 +18125,7 @@ ${jobStatusLine(job2)}`,
18112
18125
  return fail2(`timeoutMs must be between 1 and ${MAX_BASH_TIMEOUT_MS}`);
18113
18126
  await jobs.wait(id, waitMs, ctx.signal);
18114
18127
  }
18115
- const out = jobs.output(id);
18128
+ const out = jobs.output(id, { maxBytes: toolOutputBytes(ctx.maxResultChars) });
18116
18129
  if (out === void 0) return fail2(`Unknown background job ${id}`);
18117
18130
  const job = out.job;
18118
18131
  if (job.status !== "running") jobs.markSeen(id);
@@ -18799,6 +18812,16 @@ var init_result = __esm({
18799
18812
  });
18800
18813
 
18801
18814
  // src/tools/task.ts
18815
+ function taskLine(prompt) {
18816
+ const line2 = prompt.split("\n").find((text) => text.trim() !== "")?.trim() ?? "";
18817
+ return line2.length > TASK_LINE_MAX ? `${line2.slice(0, TASK_LINE_MAX - 1)}…` : line2;
18818
+ }
18819
+ function nestedTaskRejection(ctx, tool, fallback) {
18820
+ const mark = ctx.session.lastCustom(FORK_CUSTOM_TYPE);
18821
+ if (mark === void 0 || mark === null) return fallback;
18822
+ const line2 = typeof mark.taskLine === "string" ? mark.taskLine : "";
18823
+ return `${tool} rejected: you are a sub-agent forked from the main conversation and cannot start or manage sub-agents. Do not call task or task_ctl again; complete the sub-task in your <task> block yourself with your other tools, then end with your report.` + (line2 !== "" ? ` Your task begins: "${line2}"` : "");
18824
+ }
18802
18825
  function bindTaskAgents(tool, catalog) {
18803
18826
  if (tool !== void 0) catalogs.set(tool, catalog);
18804
18827
  }
@@ -18878,7 +18901,13 @@ function createTaskTool(_options = {}) {
18878
18901
  promptGuidelines: [TASK_NOTIFICATION_RULE],
18879
18902
  async execute(input2, ctx) {
18880
18903
  if (ctx.depth >= MAX_TASK_DEPTH || ctx.spawnSubagent === void 0) {
18881
- return fail3("Sub-agents are not available here (nested tasks are not allowed).");
18904
+ return fail3(
18905
+ nestedTaskRejection(
18906
+ ctx,
18907
+ "task",
18908
+ "Sub-agents are not available here (nested tasks are not allowed)."
18909
+ )
18910
+ );
18882
18911
  }
18883
18912
  const request = buildSubagentRequest(input2, ctx);
18884
18913
  if (typeof request === "string") return fail3(request);
@@ -18907,13 +18936,15 @@ function createTaskTool(_options = {}) {
18907
18936
  };
18908
18937
  return tool;
18909
18938
  }
18910
- var MAX_TASK_DEPTH, THINKING, TASK_CONTEXT_LEAD, FOREGROUND_DESCRIPTION, BACKGROUND_DESCRIPTION, TASK_AGENTS_HEADING, catalogs, backgrounds;
18939
+ var MAX_TASK_DEPTH, TASK_LINE_MAX, FORK_CUSTOM_TYPE, THINKING, TASK_CONTEXT_LEAD, FOREGROUND_DESCRIPTION, BACKGROUND_DESCRIPTION, TASK_AGENTS_HEADING, catalogs, backgrounds;
18911
18940
  var init_task = __esm({
18912
18941
  "src/tools/task.ts"() {
18913
18942
  "use strict";
18914
18943
  init_catalog3();
18915
18944
  init_result();
18916
18945
  MAX_TASK_DEPTH = 1;
18946
+ TASK_LINE_MAX = 120;
18947
+ FORK_CUSTOM_TYPE = "ama.fork";
18917
18948
  THINKING = [
18918
18949
  "off",
18919
18950
  "minimal",
@@ -18995,7 +19026,10 @@ function createTaskCtlTool() {
18995
19026
  executionMode: "parallel",
18996
19027
  annotations: { pollable: true },
18997
19028
  async execute(input2, ctx) {
18998
- if (ctx.depth >= 1) return fail4("task_ctl is not available inside a sub-agent.");
19029
+ if (ctx.depth >= 1)
19030
+ return fail4(
19031
+ nestedTaskRejection(ctx, "task_ctl", "task_ctl is not available inside a sub-agent.")
19032
+ );
18999
19033
  const control = taskControl(ctx.sessionId);
19000
19034
  if (input2.action === "list") {
19001
19035
  const tasks = control?.list() ?? ctx.tasks?.list() ?? [];
@@ -19052,6 +19086,7 @@ var init_task_ctl = __esm({
19052
19086
  "use strict";
19053
19087
  init_result();
19054
19088
  init_task_control();
19089
+ init_task();
19055
19090
  TASK_CTL_TOOL = "task_ctl";
19056
19091
  DEFAULT_WAIT_MS = 3e4;
19057
19092
  MAX_WAIT_MS = 6e5;
@@ -20522,8 +20557,23 @@ var init_classifier = __esm({
20522
20557
  function textOf(message) {
20523
20558
  return message.content.filter((block) => block.type === "text").map((block) => block.type === "text" ? block.text : "").join("").trim();
20524
20559
  }
20560
+ function inferredSmall(core, session) {
20561
+ const hit = catalogByAlias(session.id);
20562
+ if (hit === void 0) return void 0;
20563
+ const origin = hit.ref.slice(0, hit.ref.indexOf("/"));
20564
+ const small = catalogSmall(origin);
20565
+ const target = `${origin}/${small}`;
20566
+ if (small === void 0 || hit.ref === target) return void 0;
20567
+ const listed2 = core.options.providers.get(session.provider)?.models ?? [];
20568
+ return listed2.find((m) => {
20569
+ if (m.id === small) return true;
20570
+ const entry = catalogByAlias(m.id);
20571
+ return entry?.ref === target && entry.tier === void 0;
20572
+ })?.id;
20573
+ }
20525
20574
  async function smallModel(core, session) {
20526
- const small = catalogSmall(session.provider);
20575
+ const own = catalogSmall(session.provider);
20576
+ const small = own === void 0 ? inferredSmall(core, session) : own;
20527
20577
  if (small === void 0 || small === session.id) return void 0;
20528
20578
  const lookup = core.options.providers.findModel(`${session.provider}/${small}`);
20529
20579
  if (!lookup.ok) return void 0;
@@ -25219,7 +25269,7 @@ function editTargets(file) {
25219
25269
  }
25220
25270
  return targets;
25221
25271
  }
25222
- var import_node_fs31, placeholder, ImageOffload;
25272
+ var import_node_fs31, placeholder, PENDING_WARNINGS, ImageOffload;
25223
25273
  var init_offload = __esm({
25224
25274
  "src/session/offload.ts"() {
25225
25275
  "use strict";
@@ -25228,15 +25278,26 @@ var init_offload = __esm({
25228
25278
  init_line_reader();
25229
25279
  init_projection();
25230
25280
  placeholder = (block) => ({ ...block, data: "" });
25281
+ PENDING_WARNINGS = 16;
25231
25282
  ImageOffload = class {
25232
- constructor(warn2 = () => {
25233
- }) {
25234
- this.warn = warn2;
25235
- }
25236
- warn;
25237
25283
  /** 含图 message 条目的行位置(只有已落盘的条目才有)。 */
25238
25284
  locators = /* @__PURE__ */ new Map();
25239
25285
  offloaded = /* @__PURE__ */ new Set();
25286
+ warn;
25287
+ pending = [];
25288
+ dropped = 0;
25289
+ constructor(warn2) {
25290
+ this.warn = warn2;
25291
+ }
25292
+ /** 接上(或换掉)告警通道;之前缓冲的告警按序冲出。 */
25293
+ setWarn(warn2) {
25294
+ this.warn = warn2;
25295
+ const pending = this.pending.splice(0);
25296
+ if (this.dropped > 0)
25297
+ pending.push(`and ${this.dropped} more session entries could not be read back`);
25298
+ this.dropped = 0;
25299
+ for (const message of pending) warn2(message);
25300
+ }
25240
25301
  /** 记下条目的行位置;不含图片的条目忽略。 */
25241
25302
  track(entry, locator) {
25242
25303
  if (hasImages(entry)) this.locators.set(entry.id, locator);
@@ -25326,10 +25387,15 @@ var init_offload = __esm({
25326
25387
  } catch (error2) {
25327
25388
  this.locators.delete(entry.id);
25328
25389
  this.offloaded.delete(entry.id);
25329
- this.warn(`cannot read session entry ${entry.id} back from ${file}: ${String(error2)}`);
25390
+ this.report(`cannot read session entry ${entry.id} back from ${file}: ${String(error2)}`);
25330
25391
  return void 0;
25331
25392
  }
25332
25393
  }
25394
+ report(message) {
25395
+ if (this.warn !== void 0) this.warn(message);
25396
+ else if (this.pending.length < PENDING_WARNINGS) this.pending.push(message);
25397
+ else this.dropped++;
25398
+ }
25333
25399
  };
25334
25400
  }
25335
25401
  });
@@ -25514,6 +25580,11 @@ var init_manager = __esm({
25514
25580
  if (this.storage.kind !== "file" || !this.images.isOffloaded(entry.id)) return entry;
25515
25581
  return this.images.original(this.storage.file, entry);
25516
25582
  }
25583
+ /** 接上(或换掉)告警出口(图片读不回等);`open()` 期间缓冲的告警随即按序冲出(#183)。 */
25584
+ setWarn(warn2) {
25585
+ this.warn = warn2;
25586
+ this.images.setWarn(warn2);
25587
+ }
25517
25588
  /** 已卸载图片的条目数(测试与诊断用)。 */
25518
25589
  offloadedCount() {
25519
25590
  return this.images.size;
@@ -25626,6 +25697,9 @@ var init_manager = __esm({
25626
25697
  });
25627
25698
 
25628
25699
  // src/agent/subagent-fork.ts
25700
+ function forkRatio(ratio) {
25701
+ return ratio !== void 0 && Number.isFinite(ratio) && ratio > 0 && ratio < 1 ? ratio : FORK_MAX_CONTEXT_RATIO;
25702
+ }
25629
25703
  function requestedContext(request, agent) {
25630
25704
  return request.context ?? agent.context ?? "fresh";
25631
25705
  }
@@ -25637,7 +25711,7 @@ function forkPoint(branch, parentToolCallId) {
25637
25711
  );
25638
25712
  return index2 > 0 ? branch[index2 - 1]?.id : void 0;
25639
25713
  }
25640
- function forkPlan(parent, spec2, model, thinking) {
25714
+ function forkPlan(parent, spec2, model, thinking, ratio) {
25641
25715
  const base4 = parent.childBase();
25642
25716
  if (model.provider !== base4.model.provider || model.id !== base4.model.id)
25643
25717
  return { fallback: `model ${model.provider}/${model.id} differs from the parent` };
@@ -25647,7 +25721,7 @@ function forkPlan(parent, spec2, model, thinking) {
25647
25721
  if (last === void 0) return { fallback: "the parent has not sent a request yet" };
25648
25722
  const reserve = parent.options.compaction?.reserveTokens ?? DEFAULT_COMPACTION_SETTINGS.reserveTokens;
25649
25723
  const window2 = model.contextWindow;
25650
- const limit = window2 === void 0 ? Infinity : FORK_MAX_CONTEXT_RATIO * Math.max(0, window2 - reserve);
25724
+ const limit = window2 === void 0 ? Infinity : forkRatio(ratio) * Math.max(0, window2 - reserve);
25651
25725
  if (last.promptTokens > limit)
25652
25726
  return { fallback: `parent context ${last.promptTokens} tokens exceeds ${Math.floor(limit)}` };
25653
25727
  const forkedFrom = forkPoint(parent.manager.branch(), spec2.request.parentToolCallId);
@@ -25670,12 +25744,17 @@ function forkPlan(parent, spec2, model, thinking) {
25670
25744
  }
25671
25745
  }
25672
25746
  });
25747
+ manager.append({
25748
+ type: "custom",
25749
+ customType: FORK_CUSTOM_TYPE,
25750
+ data: { taskLine: taskLine(spec2.request.prompt) }
25751
+ });
25673
25752
  return { manager, forkedFrom };
25674
25753
  }
25675
25754
  function forkBrief(input2) {
25676
25755
  const lines2 = [
25677
25756
  "<task>",
25678
- "You are a sub-agent forked from the conversation above at this point. The main agent cannot see your work, only your final reply; do not delegate further. Requests above were for the main agent: do only this task. Instructions in this block take precedence over earlier plans or reminders above."
25757
+ "You are a sub-agent forked from the conversation above at this point. The main agent cannot see your work, only your final reply; do not delegate further. Requests above were for the main agent: do only this task. Instructions in this block take precedence over earlier plans or reminders above. Do not call task or task_ctl."
25679
25758
  ];
25680
25759
  if (input2.worktree !== void 0)
25681
25760
  lines2.push(
@@ -25702,6 +25781,7 @@ var FORK_MAX_CONTEXT_RATIO, TASK_CUSTOM_TYPE2;
25702
25781
  var init_subagent_fork = __esm({
25703
25782
  "src/agent/subagent-fork.ts"() {
25704
25783
  "use strict";
25784
+ init_task();
25705
25785
  init_session_compaction();
25706
25786
  FORK_MAX_CONTEXT_RATIO = 0.5;
25707
25787
  TASK_CUSTOM_TYPE2 = "ama.task";
@@ -26613,9 +26693,15 @@ function runningResult(record3, text, outputFile) {
26613
26693
  status: "running"
26614
26694
  };
26615
26695
  if (outputFile !== void 0) result.outputFile = outputFile;
26696
+ if (record3.info.context !== void 0) result.context = record3.info.context;
26616
26697
  return result;
26617
26698
  }
26618
- function startedResult(record3, outputFile) {
26699
+ async function startedResult(record3, outputFile, settle) {
26700
+ if (settle !== void 0)
26701
+ await Promise.race([
26702
+ settle.catch(() => void 0),
26703
+ new Promise((resolve41) => setImmediate(resolve41))
26704
+ ]);
26619
26705
  return runningResult(record3, startedText(record3.info.taskId, record3.agent.name), outputFile);
26620
26706
  }
26621
26707
  function backgroundedResult(record3, reason, afterMs, outputFile) {
@@ -26997,7 +27083,7 @@ var init_subagent_registry = __esm({
26997
27083
  const file = this.outputFileFor(record3.info.taskId);
26998
27084
  if (background) {
26999
27085
  void running.then((result) => this.notifier.notify(record3, result));
27000
- return Promise.resolve(startedResult(record3, file));
27086
+ return startedResult(record3, file, running);
27001
27087
  }
27002
27088
  const { waiter, promise } = foregroundWaiter(() => {
27003
27089
  clearTimeout(timer);
@@ -27302,7 +27388,8 @@ async function startAmaChild(parent, spec2, createChild, run7) {
27302
27388
  }
27303
27389
  const thinkingLevel = spec2.request.thinkingLevel ?? spec2.agent.thinking ?? base4.thinkingLevel ?? "off";
27304
27390
  const wanted = spec2.resumeFile === void 0 && requestedContext(spec2.request, spec2.agent) === "fork";
27305
- const plan2 = wanted ? forkPlan(parent, spec2, model, thinkingLevel) : void 0;
27391
+ const ratio = subagentRegistryFor(parent).env.modelConfig?.subagents?.forkMaxContextRatio;
27392
+ const plan2 = wanted ? forkPlan(parent, spec2, model, thinkingLevel, ratio) : void 0;
27306
27393
  if (plan2 !== void 0 && "fallback" in plan2)
27307
27394
  parent.log("info", `task ${spec2.taskId}: fork falls back to fresh (${plan2.fallback})`);
27308
27395
  const forked = plan2 !== void 0 && "manager" in plan2 ? plan2.manager : void 0;
@@ -27315,6 +27402,10 @@ async function startAmaChild(parent, spec2, createChild, run7) {
27315
27402
  const unavailable = inherited.filter(
27316
27403
  (name) => !allowed.includes(name) && !PARENT_ONLY_TOOLS.includes(name)
27317
27404
  );
27405
+ const briefUnavailable = [
27406
+ ...unavailable,
27407
+ ...PARENT_ONLY_TOOLS.filter((name) => inherited.includes(name))
27408
+ ];
27318
27409
  const options = {
27319
27410
  ...parent.options,
27320
27411
  ...base4,
@@ -27403,7 +27494,7 @@ async function startAmaChild(parent, spec2, createChild, run7) {
27403
27494
  const first = forked === void 0 ? run7.prompt : forkBrief({
27404
27495
  prompt: run7.prompt,
27405
27496
  role: spec2.agent.prompt,
27406
- unavailable,
27497
+ unavailable: briefUnavailable,
27407
27498
  ...spec2.isolated ? { worktree: { cwd: spec2.cwd, parentCwd: parent.cwd } } : {}
27408
27499
  });
27409
27500
  let current3 = runOnce(first, spec2.origin);
@@ -27631,6 +27722,7 @@ var init_session_extensions = __esm({
27631
27722
  if (extension !== void 0) list5.push(extension);
27632
27723
  }
27633
27724
  this.list = list5;
27725
+ core.manager.setWarn((message) => core.log("warn", message));
27634
27726
  }
27635
27727
  get size() {
27636
27728
  return this.list.length;
@@ -30292,7 +30384,7 @@ async function readSmall2(path) {
30292
30384
  return void 0;
30293
30385
  }
30294
30386
  }
30295
- var import_node_child_process7, import_node_crypto10, import_promises16, import_node_os10, import_node_path51, SHADOW_DIR, SHADOW_MAX_FILES, SHADOW_MAX_SNAPSHOT_MS, GIT_TIMEOUT_MS, CONFIG_VERSION, IDENTITY, ATTRIBUTES, GitMissingError, MODE_ABSENT, MODE_SYMLINK, MODE_GITLINK, MODE_EXECUTABLE, queues2, ShadowRepo;
30387
+ var import_node_child_process7, import_node_crypto10, import_promises16, import_node_os10, import_node_path51, SHADOW_DIR, SHADOW_MAX_FILES, SHADOW_MAX_SNAPSHOT_MS, SHADOW_SLOW_STREAK, GIT_TIMEOUT_MS, CONFIG_VERSION, IDENTITY, ATTRIBUTES, GitMissingError, MODE_ABSENT, MODE_SYMLINK, MODE_GITLINK, MODE_EXECUTABLE, queues2, ShadowRepo;
30296
30388
  var init_shadow_git = __esm({
30297
30389
  "src/checkpoints/shadow-git.ts"() {
30298
30390
  "use strict";
@@ -30306,6 +30398,7 @@ var init_shadow_git = __esm({
30306
30398
  SHADOW_DIR = "shadow";
30307
30399
  SHADOW_MAX_FILES = 2e4;
30308
30400
  SHADOW_MAX_SNAPSHOT_MS = 3e3;
30401
+ SHADOW_SLOW_STREAK = 2;
30309
30402
  GIT_TIMEOUT_MS = 6e4;
30310
30403
  CONFIG_VERSION = "1";
30311
30404
  IDENTITY = { name: "ama", email: "ama@localhost" };
@@ -30331,6 +30424,8 @@ var init_shadow_git = __esm({
30331
30424
  indexFile;
30332
30425
  initialized;
30333
30426
  counted = false;
30427
+ /** 连续超时的快照数(首次快照不计),到 `SHADOW_SLOW_STREAK` 才降级。 */
30428
+ slowStreak = 0;
30334
30429
  constructor(dataDir, cwd, options = {}) {
30335
30430
  this.cwd = (0, import_node_path51.resolve)(cwd);
30336
30431
  this.dir = shadowRepoDir(dataDir, this.cwd);
@@ -30450,6 +30545,7 @@ var init_shadow_git = __esm({
30450
30545
  return exclusive(this.dir, async () => {
30451
30546
  await this.init();
30452
30547
  const started = this.now();
30548
+ const first = !this.counted;
30453
30549
  await this.writeExcludes();
30454
30550
  if (!this.counted) {
30455
30551
  const out = await this.run([
@@ -30475,7 +30571,9 @@ var init_shadow_git = __esm({
30475
30571
  if (parent !== void 0 && await this.hasCommitUnlocked(parent)) args.push("-p", parent);
30476
30572
  const commit = await this.text(args);
30477
30573
  const elapsed = this.now() - started;
30478
- if (elapsed > this.maxSnapshotMs) {
30574
+ if (first) return { commit };
30575
+ this.slowStreak = elapsed > this.maxSnapshotMs ? this.slowStreak + 1 : 0;
30576
+ if (this.slowStreak >= SHADOW_SLOW_STREAK) {
30479
30577
  return {
30480
30578
  commit,
30481
30579
  degrade: {
@@ -31390,7 +31488,9 @@ function openSession(request, context) {
31390
31488
  return file === void 0 ? SessionManager.create(dir, cwd, { id: request.id }) : SessionManager.open(file);
31391
31489
  }
31392
31490
  case "fork": {
31393
- const source = SessionManager.open(requireFile(root, request.id, cwd));
31491
+ const source = SessionManager.open(requireFile(root, request.id, cwd), {
31492
+ warn: (message) => context.log?.("warn", message)
31493
+ });
31394
31494
  try {
31395
31495
  const leaf = source.leafId();
31396
31496
  if (leaf === null)
@@ -31406,9 +31506,9 @@ function listSessions(context) {
31406
31506
  const dirs = context.cwd === void 0 ? cwdDirs(context.sessionDir) : [sessionDirForCwd(context.sessionDir, context.cwd)];
31407
31507
  return dirs.flatMap((dir) => SessionManager.list(dir)).filter((item) => context.includeSubagents === true || item.subagent !== true).sort((a, b) => b.modifiedAt.localeCompare(a.modifiedAt));
31408
31508
  }
31409
- function createSessionStore() {
31509
+ function createSessionStore(log) {
31410
31510
  return {
31411
- open: (request, context) => openSession(request, context),
31511
+ open: (request, context) => openSession(request, { ...context, log }),
31412
31512
  list: async (context) => listSessions(context),
31413
31513
  show: async (id, context) => {
31414
31514
  const file = requireFile(context.sessionDir, id);
@@ -43981,7 +44081,8 @@ var init_key_docs = __esm({
43981
44081
  maxPending: 16,
43982
44082
  background: "auto",
43983
44083
  autoBackgroundAfterMs: 0,
43984
- retainSessions: 4
44084
+ retainSessions: 4,
44085
+ forkMaxContextRatio: 0.5
43985
44086
  },
43986
44087
  reminders: { todo: true, fileChanges: true, contextPressure: true, budget: true },
43987
44088
  todo: { reminder: 10 }
@@ -44246,7 +44347,8 @@ function w5Sections() {
44246
44347
  defaultModel: str3(),
44247
44348
  background: oneOf(SUBAGENT_BACKGROUND_MODES),
44248
44349
  autoBackgroundAfterMs: num2(0),
44249
- retainSessions: num2(0)
44350
+ retainSessions: num2(0),
44351
+ forkMaxContextRatio: num2(0.05, 0.95)
44250
44352
  }),
44251
44353
  models: object({ aliases: object({ fast: str3(), strong: str3() }), enabled: strings2 }),
44252
44354
  fallbackModel: str3(),
@@ -46233,6 +46335,10 @@ var init_sse = __esm({
46233
46335
  });
46234
46336
 
46235
46337
  // src/ai/apis/max-tokens.ts
46338
+ function onMaxTokensCap(listener) {
46339
+ capListeners.add(listener);
46340
+ return () => capListeners.delete(listener);
46341
+ }
46236
46342
  function clampMaxTokens(requested, window2, estimatedInput, fixed) {
46237
46343
  if (fixed || window2 === void 0 || !Number.isFinite(window2) || window2 <= 0) return requested;
46238
46344
  const room = Math.floor(window2 - estimatedInput - OUTPUT_HEADROOM_TOKENS);
@@ -46293,14 +46399,17 @@ async function postWithMaxTokensFallback(model, url, options, field2) {
46293
46399
  const sent = body[field2];
46294
46400
  if (rejection === void 0 || !("cap" in rejection) || typeof sent !== "number") throw error2;
46295
46401
  if (rejection.cap >= sent || !budgetFits(body, rejection.cap)) throw error2;
46296
- if (!rejection.transient) maxTokensCaps.set(key, rejection.cap);
46402
+ if (!rejection.transient) {
46403
+ maxTokensCaps.set(key, rejection.cap);
46404
+ for (const listener of capListeners) listener(key, rejection.cap);
46405
+ }
46297
46406
  return postWithCacheFallback(model, url, {
46298
46407
  ...options,
46299
46408
  body: withCap(body, field2, rejection.cap)
46300
46409
  });
46301
46410
  }
46302
46411
  }
46303
- var MIN_OUTPUT_TOKENS, OUTPUT_HEADROOM_TOKENS, maxTokensCaps, NUM, toInt, RANGE, LIMIT, CONTEXT_LIMIT;
46412
+ var MIN_OUTPUT_TOKENS, OUTPUT_HEADROOM_TOKENS, maxTokensCaps, capListeners, NUM, toInt, RANGE, LIMIT, CONTEXT_LIMIT;
46304
46413
  var init_max_tokens = __esm({
46305
46414
  "src/ai/apis/max-tokens.ts"() {
46306
46415
  "use strict";
@@ -46309,6 +46418,7 @@ var init_max_tokens = __esm({
46309
46418
  MIN_OUTPUT_TOKENS = 1024;
46310
46419
  OUTPUT_HEADROOM_TOKENS = 2048;
46311
46420
  maxTokensCaps = /* @__PURE__ */ new Map();
46421
+ capListeners = /* @__PURE__ */ new Set();
46312
46422
  NUM = "`?(\\d[\\d,_]*)`?";
46313
46423
  toInt = (text) => Number((text ?? "").replace(/[,_]/g, ""));
46314
46424
  RANGE = new RegExp(
@@ -47852,9 +47962,8 @@ var init_registry2 = __esm({
47852
47962
  (name) => this.warn(`model "${id}/${modelId}" channel "${name}" not found; ignored`)
47853
47963
  );
47854
47964
  for (const entry of config.models ?? []) {
47855
- const { entry: filled, metadata } = enrichEntry(entry, this.modelsDev);
47856
- if (typeof entry.modelsDev === "string" && metadata.looked && metadata.match === void 0)
47857
- this.warn(`modelsDev "${entry.modelsDev}" for "${id}/${entry.id}" not found`);
47965
+ const { entry: filled, metadata } = enrichEntry(entry, this.modelsDev, id);
47966
+ for (const warning of metadata.warnings ?? []) this.warn(warning);
47858
47967
  const model = withCustomDefaults(filled, id, entry.api ?? provider.api);
47859
47968
  if (provider.channels !== void 0 && (entry.channels !== void 0 || !provider.builtin))
47860
47969
  model.channels = channelsOf(entry.channels, entry.id);
@@ -48207,6 +48316,7 @@ var init_settings_registry = __esm({
48207
48316
  row2("subagents.maxConcurrent", "agents", "nextSession", "deny"),
48208
48317
  row2("subagents.maxPending", "agents", "nextSession", "deny"),
48209
48318
  row2("subagents.retainSessions", "agents", "nextSession", "deny"),
48319
+ row2("subagents.forkMaxContextRatio", "agents", "nextSession", "deny"),
48210
48320
  // [W7-B2] 改 task 工具描述(缺省后台 / 前台两种文案),故 prefix
48211
48321
  row2("subagents.background", "agents", "restart", "any", { prefix: true }),
48212
48322
  row2("subagents.autoBackgroundAfterMs", "agents", "restart", "any"),
@@ -50046,7 +50156,7 @@ function statusCost(stats) {
50046
50156
  function layoutRow(parts, width2, theme, style = {}) {
50047
50157
  const sep5 = style.sep ?? " · ";
50048
50158
  const dim = (text) => text === "" ? "" : theme.fg("dim", text);
50049
- const join66 = (list5) => {
50159
+ const join67 = (list5) => {
50050
50160
  let out = "";
50051
50161
  let size = 0;
50052
50162
  const add3 = (text, measured = visibleWidth(text)) => {
@@ -50078,7 +50188,7 @@ function layoutRow(parts, width2, theme, style = {}) {
50078
50188
  const left = zone("left");
50079
50189
  const right = zone("right");
50080
50190
  const between = left.length === 0 || right.length === 0 ? 0 : sep5.length;
50081
- return join66(left)[1] + between + join66(right)[1] <= width2;
50191
+ return join67(left)[1] + between + join67(right)[1] <= width2;
50082
50192
  };
50083
50193
  while (!fits()) {
50084
50194
  const droppable = kept.filter((p) => p.priority !== void 0);
@@ -50086,8 +50196,8 @@ function layoutRow(parts, width2, theme, style = {}) {
50086
50196
  const drop = droppable.reduce((worst, p) => p.priority > worst.priority ? p : worst);
50087
50197
  kept = kept.filter((p) => p !== drop);
50088
50198
  }
50089
- const l = join66(zone("left"))[0];
50090
- const r = join66(zone("right"))[0];
50199
+ const l = join67(zone("left"))[0];
50200
+ const r = join67(zone("right"))[0];
50091
50201
  const gap = width2 - visibleWidth(l) - visibleWidth(r);
50092
50202
  const line2 = r === "" ? l : l === "" ? " ".repeat(Math.max(0, gap)) + r : gap >= MIN_GAP ? l + " ".repeat(gap) + r : l + dim(sep5) + r;
50093
50203
  return truncateToWidth(line2, width2);
@@ -52519,6 +52629,81 @@ var init_codemode_notice = __esm({
52519
52629
  }
52520
52630
  });
52521
52631
 
52632
+ // src/ai/providers/max-tokens-cache.ts
52633
+ function maxTokensCachePath(dataDir) {
52634
+ return (0, import_node_path91.join)(dataDir, "models", "max-tokens-caps.json");
52635
+ }
52636
+ function readEntries(dataDir, now) {
52637
+ const out = /* @__PURE__ */ new Map();
52638
+ let parsed;
52639
+ try {
52640
+ parsed = JSON.parse((0, import_node_fs69.readFileSync)(maxTokensCachePath(dataDir), "utf8"));
52641
+ } catch {
52642
+ return out;
52643
+ }
52644
+ if (typeof parsed !== "object" || parsed === null) return out;
52645
+ const file = parsed;
52646
+ if (file.version !== MAX_TOKENS_CACHE_VERSION) return out;
52647
+ if (typeof file.caps !== "object" || file.caps === null) return out;
52648
+ for (const [key, value] of Object.entries(file.caps)) {
52649
+ if (typeof value !== "object" || value === null) continue;
52650
+ const { cap, learnedAt } = value;
52651
+ if (typeof cap !== "number" || !Number.isInteger(cap) || cap < 1) continue;
52652
+ if (typeof learnedAt !== "number" || !Number.isFinite(learnedAt)) continue;
52653
+ if (now - learnedAt > MAX_TOKENS_CACHE_TTL_MS) continue;
52654
+ out.set(key, { cap, learnedAt });
52655
+ }
52656
+ return out;
52657
+ }
52658
+ function loadMaxTokensCaps(dataDir, now = Date.now) {
52659
+ let loaded = 0;
52660
+ for (const [key, entry] of readEntries(dataDir, now())) {
52661
+ if (maxTokensCaps.has(key)) continue;
52662
+ maxTokensCaps.set(key, entry.cap);
52663
+ loaded++;
52664
+ }
52665
+ return loaded;
52666
+ }
52667
+ function recordMaxTokensCap(dataDir, key, cap, now = Date.now) {
52668
+ const at = now();
52669
+ const entries = readEntries(dataDir, at);
52670
+ entries.set(key, { cap, learnedAt: at });
52671
+ const path = maxTokensCachePath(dataDir);
52672
+ const tmp = `${path}.${process.pid}.tmp`;
52673
+ try {
52674
+ (0, import_node_fs69.mkdirSync)((0, import_node_path91.dirname)(path), { recursive: true, mode: 448 });
52675
+ const caps = Object.fromEntries([...entries].sort(([a], [b]) => a < b ? -1 : 1));
52676
+ (0, import_node_fs69.writeFileSync)(tmp, `${JSON.stringify({ version: MAX_TOKENS_CACHE_VERSION, caps }, null, 2)}
52677
+ `);
52678
+ (0, import_node_fs69.renameSync)(tmp, path);
52679
+ } catch {
52680
+ }
52681
+ }
52682
+ function attachMaxTokensCache(dataDir, now = Date.now) {
52683
+ const existing = attached2.get(dataDir);
52684
+ if (existing !== void 0) return existing;
52685
+ loadMaxTokensCaps(dataDir, now);
52686
+ const unsubscribe = onMaxTokensCap((key, cap) => recordMaxTokensCap(dataDir, key, cap, now));
52687
+ const detach = () => {
52688
+ unsubscribe();
52689
+ attached2.delete(dataDir);
52690
+ };
52691
+ attached2.set(dataDir, detach);
52692
+ return detach;
52693
+ }
52694
+ var import_node_fs69, import_node_path91, MAX_TOKENS_CACHE_VERSION, MAX_TOKENS_CACHE_TTL_MS, attached2;
52695
+ var init_max_tokens_cache = __esm({
52696
+ "src/ai/providers/max-tokens-cache.ts"() {
52697
+ "use strict";
52698
+ import_node_fs69 = require("node:fs");
52699
+ import_node_path91 = require("node:path");
52700
+ init_max_tokens();
52701
+ MAX_TOKENS_CACHE_VERSION = 1;
52702
+ MAX_TOKENS_CACHE_TTL_MS = 30 * 24 * 60 * 60 * 1e3;
52703
+ attached2 = /* @__PURE__ */ new Map();
52704
+ }
52705
+ });
52706
+
52522
52707
  // src/cli/compose-providers.ts
52523
52708
  function modelConfigOf(model, providerApi) {
52524
52709
  const { provider: _p, api, ...rest } = model;
@@ -52599,6 +52784,7 @@ async function buildProviderRegistry(input2, options = {}) {
52599
52784
  if (provider !== void 0) registry3 = make({ provider, apiKey: cli.apiKey });
52600
52785
  }
52601
52786
  if (dataDir !== void 0) {
52787
+ attachMaxTokensCache(dataDir);
52602
52788
  const flavor = readOAuthEntry(input2.authFile, CHATGPT_PROVIDER_ID)?.flavor;
52603
52789
  mergeDiscoveredModels(
52604
52790
  registry3,
@@ -52639,6 +52825,7 @@ var init_compose_providers = __esm({
52639
52825
  init_models_dev_cache();
52640
52826
  init_registry2();
52641
52827
  init_discovered_cache();
52828
+ init_max_tokens_cache();
52642
52829
  init_enrich();
52643
52830
  init_token_store();
52644
52831
  init_presets();
@@ -52650,7 +52837,7 @@ var init_compose_providers = __esm({
52650
52837
  // src/modes/image-input.ts
52651
52838
  function expand(path, cwd) {
52652
52839
  const home = path === "~" || path.startsWith("~/") ? (0, import_node_os14.homedir)() + path.slice(1) : path;
52653
- return (0, import_node_path91.resolve)(cwd, home);
52840
+ return (0, import_node_path92.resolve)(cwd, home);
52654
52841
  }
52655
52842
  function findImageRefs(text, cwd) {
52656
52843
  const out = [];
@@ -52662,7 +52849,7 @@ function findImageRefs(text, cwd) {
52662
52849
  token = token.replace(/\\ /g, " ").replace(/[,\uFF0C\u3002;\uFF1B)\uFF09]+$/, "");
52663
52850
  if (token === "" || imageMimeFromPath(token) === void 0) continue;
52664
52851
  const path = expand(token, cwd);
52665
- if (!explicit && !(0, import_node_fs69.existsSync)(path)) continue;
52852
+ if (!explicit && !(0, import_node_fs70.existsSync)(path)) continue;
52666
52853
  if (seen.has(path)) continue;
52667
52854
  seen.add(path);
52668
52855
  out.push({ path, explicit });
@@ -52721,13 +52908,13 @@ async function loadLocalized(path, options) {
52721
52908
  throw error2;
52722
52909
  }
52723
52910
  }
52724
- var import_node_fs69, import_node_os14, import_node_path91, TOKEN;
52911
+ var import_node_fs70, import_node_os14, import_node_path92, TOKEN;
52725
52912
  var init_image_input = __esm({
52726
52913
  "src/modes/image-input.ts"() {
52727
52914
  "use strict";
52728
- import_node_fs69 = require("node:fs");
52915
+ import_node_fs70 = require("node:fs");
52729
52916
  import_node_os14 = require("node:os");
52730
- import_node_path91 = require("node:path");
52917
+ import_node_path92 = require("node:path");
52731
52918
  init_channels();
52732
52919
  init_errors();
52733
52920
  init_image_limits();
@@ -53524,7 +53711,7 @@ function trustChoices() {
53524
53711
  }
53525
53712
  function isDirectory2(path) {
53526
53713
  try {
53527
- return (0, import_node_fs70.statSync)(path).isDirectory();
53714
+ return (0, import_node_fs71.statSync)(path).isDirectory();
53528
53715
  } catch {
53529
53716
  return false;
53530
53717
  }
@@ -53578,8 +53765,8 @@ function createStartupUi(options = {}) {
53578
53765
  placeholder: m.cwdPlaceholder,
53579
53766
  validate: (text) => {
53580
53767
  if (text === "") return m.cwdEmpty;
53581
- const abs = (0, import_node_path92.resolve)(text);
53582
- if (!(0, import_node_fs70.existsSync)(abs)) return m.cwdMissing(abs);
53768
+ const abs = (0, import_node_path93.resolve)(text);
53769
+ if (!(0, import_node_fs71.existsSync)(abs)) return m.cwdMissing(abs);
53583
53770
  if (!isDirectory2(abs)) return m.cwdNotDir(abs);
53584
53771
  return void 0;
53585
53772
  }
@@ -53587,13 +53774,13 @@ function createStartupUi(options = {}) {
53587
53774
  }
53588
53775
  };
53589
53776
  }
53590
- var import_node_fs70, import_node_path92;
53777
+ var import_node_fs71, import_node_path93;
53591
53778
  var init_startup_ui = __esm({
53592
53779
  "src/modes/interactive/startup-ui.ts"() {
53593
53780
  "use strict";
53594
53781
  init_i18n();
53595
- import_node_fs70 = require("node:fs");
53596
- import_node_path92 = require("node:path");
53782
+ import_node_fs71 = require("node:fs");
53783
+ import_node_path93 = require("node:path");
53597
53784
  init_tui2();
53598
53785
  init_model_items();
53599
53786
  init_model_items();
@@ -53731,7 +53918,7 @@ function oneLine7(text, max = 60) {
53731
53918
  const flat2 = text.replace(/\s+/g, " ").trim();
53732
53919
  return flat2.length > max ? `${flat2.slice(0, max - 1)}…` : flat2;
53733
53920
  }
53734
- function taskLine(task, now) {
53921
+ function taskLine2(task, now) {
53735
53922
  const m = msg().panels.tasks;
53736
53923
  const agent = task.runner !== "ama" && task.runner !== task.agent ? m.agentRunner(task.agent, task.runner) : task.agent;
53737
53924
  const parts = [agent, ...taskFacts(task, now)];
@@ -53745,7 +53932,7 @@ function describeTasks(sessionId, now) {
53745
53932
  if (tasks.length === 0) return m.none;
53746
53933
  return [
53747
53934
  m.heading(tasks.length),
53748
- ...tasks.map((task) => ` ${taskLine(task, now)}`),
53935
+ ...tasks.map((task) => ` ${taskLine2(task, now)}`),
53749
53936
  m.footer
53750
53937
  ].join("\n");
53751
53938
  }
@@ -53756,7 +53943,7 @@ function describeTaskOutput(sessionId, taskId, now) {
53756
53943
  const output = taskOutput(sessionId, taskId) ?? "";
53757
53944
  const file = task.outputFile !== void 0 ? `
53758
53945
  ${m.fullOutput(task.outputFile)}` : "";
53759
- return `${taskLine(task, now)}${file}
53946
+ return `${taskLine2(task, now)}${file}
53760
53947
  ${output.trim() === "" ? m.noOutput : output.trimEnd()}`;
53761
53948
  }
53762
53949
  function sourceText3(source) {
@@ -54010,7 +54197,7 @@ function startupInfo(runtime, home) {
54010
54197
  permissionMode: runtime.session.state.permissionMode,
54011
54198
  preset: runtime.config.tools?.preset ?? "default",
54012
54199
  codemode: effectiveCodemodeMode(runtime.config),
54013
- contextFiles: resources.contextFiles.map((f) => (0, import_node_path93.basename)(f.path)),
54200
+ contextFiles: resources.contextFiles.map((f) => (0, import_node_path94.basename)(f.path)),
54014
54201
  skills: resources.skills.filter((s2) => !isBuiltinSkill(s2)).length,
54015
54202
  prompts: resources.prompts.length,
54016
54203
  hooks: runtime.hooks.list().length,
@@ -54019,12 +54206,12 @@ function startupInfo(runtime, home) {
54019
54206
  if (runtime.host !== void 0) info2.host = runtime.host.adapter.id;
54020
54207
  return info2;
54021
54208
  }
54022
- var import_node_path93;
54209
+ var import_node_path94;
54023
54210
  var init_startup_screen = __esm({
54024
54211
  "src/cli/startup-screen.ts"() {
54025
54212
  "use strict";
54026
54213
  init_builtin3();
54027
- import_node_path93 = require("node:path");
54214
+ import_node_path94 = require("node:path");
54028
54215
  init_channels();
54029
54216
  init_presets2();
54030
54217
  init_i18n();
@@ -56767,29 +56954,29 @@ function runEditor(command3, file) {
56767
56954
  return result.status;
56768
56955
  }
56769
56956
  async function editExternally(text, name, deps) {
56770
- const dir = (0, import_node_fs71.mkdtempSync)((0, import_node_path94.join)((0, import_node_os15.tmpdir)(), "ama-edit-"));
56771
- const file = (0, import_node_path94.join)(dir, name);
56772
- (0, import_node_fs71.writeFileSync)(file, text);
56957
+ const dir = (0, import_node_fs72.mkdtempSync)((0, import_node_path95.join)((0, import_node_os15.tmpdir)(), "ama-edit-"));
56958
+ const file = (0, import_node_path95.join)(dir, name);
56959
+ (0, import_node_fs72.writeFileSync)(file, text);
56773
56960
  deps.suspend();
56774
56961
  try {
56775
56962
  const code = (deps.run ?? runEditor)(editorCommand(deps.env, deps.platform), file);
56776
56963
  if (code !== 0) return void 0;
56777
- return (0, import_node_fs71.readFileSync)(file, "utf8");
56964
+ return (0, import_node_fs72.readFileSync)(file, "utf8");
56778
56965
  } catch {
56779
56966
  return void 0;
56780
56967
  } finally {
56781
56968
  deps.resume();
56782
- (0, import_node_fs71.rmSync)(dir, { recursive: true, force: true });
56969
+ (0, import_node_fs72.rmSync)(dir, { recursive: true, force: true });
56783
56970
  }
56784
56971
  }
56785
- var import_node_child_process17, import_node_fs71, import_node_os15, import_node_path94;
56972
+ var import_node_child_process17, import_node_fs72, import_node_os15, import_node_path95;
56786
56973
  var init_external_editor = __esm({
56787
56974
  "src/modes/interactive/external-editor.ts"() {
56788
56975
  "use strict";
56789
56976
  import_node_child_process17 = require("node:child_process");
56790
- import_node_fs71 = require("node:fs");
56977
+ import_node_fs72 = require("node:fs");
56791
56978
  import_node_os15 = require("node:os");
56792
- import_node_path94 = require("node:path");
56979
+ import_node_path95 = require("node:path");
56793
56980
  }
56794
56981
  });
56795
56982
 
@@ -59753,7 +59940,7 @@ function agentCommandHooks(get) {
59753
59940
  agentView: (taskId) => get().openView(taskId)
59754
59941
  };
59755
59942
  }
59756
- var TASK_NOTICE, AgentUi;
59943
+ var TASK_NOTICE, SESSION_NOTICE, AgentUi;
59757
59944
  var init_agent_ui = __esm({
59758
59945
  "src/modes/interactive/agent-ui.ts"() {
59759
59946
  "use strict";
@@ -59773,6 +59960,7 @@ var init_agent_ui = __esm({
59773
59960
  init_task_background();
59774
59961
  init_tasks_report();
59775
59962
  TASK_NOTICE = /^\[task (t\d+)\] ([\s\S]*)$/;
59963
+ SESSION_NOTICE = /^cannot read session entry (\S+) back from /;
59776
59964
  AgentUi = class {
59777
59965
  constructor(deps) {
59778
59966
  this.deps = deps;
@@ -59883,6 +60071,11 @@ var init_agent_ui = __esm({
59883
60071
  this.deps.notice(level === "warn" ? "warn" : "info", `[${agent} · ${taskId}] ${match[2]}`);
59884
60072
  return;
59885
60073
  }
60074
+ const image = level === "warn" ? SESSION_NOTICE.exec(message) : null;
60075
+ if (image !== null) {
60076
+ this.deps.notice("warn", msg().interactive.events.imageReadBackFailed(image[1]));
60077
+ return;
60078
+ }
59886
60079
  original?.call(holder, level, message);
59887
60080
  };
59888
60081
  holder.log = routed;
@@ -60107,7 +60300,7 @@ var init_agent_ui = __esm({
60107
60300
  const now = this.deps.now();
60108
60301
  const picked = await this.deps.pick({
60109
60302
  title: msg().interactive.agentUi.tasksTitle,
60110
- items: [...tasks].reverse().map((task2) => ({ value: task2.taskId, label: taskLine(task2, now) })),
60303
+ items: [...tasks].reverse().map((task2) => ({ value: task2.taskId, label: taskLine2(task2, now) })),
60111
60304
  footer: msg().interactive.agentUi.tasksFooter("↑↓")
60112
60305
  });
60113
60306
  if (picked === void 0) return;
@@ -60803,9 +60996,9 @@ function parseLeftRight(text) {
60803
60996
  async function hasUpstream(cwd, branch) {
60804
60997
  const gitDir = await findGitDir(cwd);
60805
60998
  if (gitDir === void 0) return false;
60806
- const common = (await (0, import_promises25.readFile)((0, import_node_path95.join)(gitDir, "commondir"), "utf8").catch(() => void 0))?.trim();
60807
- const dir = common === void 0 ? gitDir : (0, import_node_path95.isAbsolute)(common) ? common : (0, import_node_path95.resolve)(gitDir, common);
60808
- const text = await (0, import_promises25.readFile)((0, import_node_path95.join)(dir, "config"), "utf8").catch(() => "");
60999
+ const common = (await (0, import_promises25.readFile)((0, import_node_path96.join)(gitDir, "commondir"), "utf8").catch(() => void 0))?.trim();
61000
+ const dir = common === void 0 ? gitDir : (0, import_node_path96.isAbsolute)(common) ? common : (0, import_node_path96.resolve)(gitDir, common);
61001
+ const text = await (0, import_promises25.readFile)((0, import_node_path96.join)(dir, "config"), "utf8").catch(() => "");
60809
61002
  let inBranch = false;
60810
61003
  for (const raw of text.split("\n")) {
60811
61004
  const line2 = raw.trim();
@@ -60828,17 +61021,17 @@ async function readHead2(cwd) {
60828
61021
  }
60829
61022
  const gitDir = await findGitDir(cwd);
60830
61023
  if (gitDir === void 0) return void 0;
60831
- const text = await (0, import_promises25.readFile)((0, import_node_path95.join)(gitDir, "HEAD"), "utf8").catch(() => void 0);
61024
+ const text = await (0, import_promises25.readFile)((0, import_node_path96.join)(gitDir, "HEAD"), "utf8").catch(() => void 0);
60832
61025
  const match = /^ref:\s*refs\/heads\/(\S+)\s*$/.exec(text ?? "");
60833
61026
  return match?.[1] !== void 0 ? { branch: match[1] } : void 0;
60834
61027
  }
60835
- var import_node_child_process18, import_promises25, import_node_path95, GIT_NUMSTAT_INTERVAL_MS, GIT_NUMSTAT_TIMEOUT_MS, sameInfo, GitInfoWatcher;
61028
+ var import_node_child_process18, import_promises25, import_node_path96, GIT_NUMSTAT_INTERVAL_MS, GIT_NUMSTAT_TIMEOUT_MS, sameInfo, GitInfoWatcher;
60836
61029
  var init_info = __esm({
60837
61030
  "src/git/info.ts"() {
60838
61031
  "use strict";
60839
61032
  import_node_child_process18 = require("node:child_process");
60840
61033
  import_promises25 = require("node:fs/promises");
60841
- import_node_path95 = require("node:path");
61034
+ import_node_path96 = require("node:path");
60842
61035
  init_git_head();
60843
61036
  GIT_NUMSTAT_INTERVAL_MS = 1e4;
60844
61037
  GIT_NUMSTAT_TIMEOUT_MS = 2e3;
@@ -61134,12 +61327,12 @@ function statusLineText(mode) {
61134
61327
  const m = msg().interactive.statusLine;
61135
61328
  return mode === "full" ? m.full : m.compact;
61136
61329
  }
61137
- var import_node_path96, WRITE_TOOLS3, QUOTA_TICK_MS, HintLine, StatusArea;
61330
+ var import_node_path97, WRITE_TOOLS3, QUOTA_TICK_MS, HintLine, StatusArea;
61138
61331
  var init_status_area = __esm({
61139
61332
  "src/modes/interactive/status-area.ts"() {
61140
61333
  "use strict";
61141
61334
  init_i18n();
61142
- import_node_path96 = require("node:path");
61335
+ import_node_path97 = require("node:path");
61143
61336
  init_capability();
61144
61337
  init_channels();
61145
61338
  init_info();
@@ -61329,7 +61522,7 @@ var init_status_area = __esm({
61329
61522
  gitView() {
61330
61523
  const cwd = this.gitCwd;
61331
61524
  if (cwd === void 0) return void 0;
61332
- return { dir: (0, import_node_path96.basename)(cwd), info: this.git?.current() };
61525
+ return { dir: (0, import_node_path97.basename)(cwd), info: this.git?.current() };
61333
61526
  }
61334
61527
  watchGit() {
61335
61528
  const cwd = this.deps.session().state.cwd;
@@ -63656,15 +63849,15 @@ function processTerminal() {
63656
63849
  return new ProcessTerminal();
63657
63850
  }
63658
63851
  function loadKeys(runtime, warn2) {
63659
- const parsed = loadKeybindingsFile((0, import_node_path97.join)(runtime.paths.configDir, KEYBINDINGS_FILE));
63852
+ const parsed = loadKeybindingsFile((0, import_node_path98.join)(runtime.paths.configDir, KEYBINDINGS_FILE));
63660
63853
  for (const w of parsed.warnings) warn2(msg().interactive.app.keybindingsWarning(w));
63661
63854
  return new Keybindings(parsed.overrides);
63662
63855
  }
63663
- var import_node_path97;
63856
+ var import_node_path98;
63664
63857
  var init_terminal_setup = __esm({
63665
63858
  "src/modes/interactive/terminal-setup.ts"() {
63666
63859
  "use strict";
63667
- import_node_path97 = require("node:path");
63860
+ import_node_path98 = require("node:path");
63668
63861
  init_paths();
63669
63862
  init_errors();
63670
63863
  init_i18n();
@@ -63730,7 +63923,7 @@ function runInteractiveMode(runtime, context, options = {}) {
63730
63923
  ...options.statusLine !== void 0 ? { layout: options.statusLine } : {}
63731
63924
  });
63732
63925
  const { hint, bar: status } = area;
63733
- const historyFile = options.historyFile === false ? void 0 : options.historyFile ?? (0, import_node_path98.join)(runtime.paths.dataDir, "history");
63926
+ const historyFile = options.historyFile === false ? void 0 : options.historyFile ?? (0, import_node_path99.join)(runtime.paths.dataDir, "history");
63734
63927
  const completion = new InteractiveCompletion({
63735
63928
  commands: () => ALL_COMMANDS,
63736
63929
  prompts: () => runtime.resources.prompts,
@@ -64094,12 +64287,12 @@ function runInteractiveMode(runtime, context, options = {}) {
64094
64287
  }
64095
64288
  });
64096
64289
  }
64097
- var import_node_path98, HINT_MS;
64290
+ var import_node_path99, HINT_MS;
64098
64291
  var init_interactive_mode = __esm({
64099
64292
  "src/modes/interactive/interactive-mode.ts"() {
64100
64293
  "use strict";
64101
64294
  init_image_input();
64102
- import_node_path98 = require("node:path");
64295
+ import_node_path99 = require("node:path");
64103
64296
  init_session2();
64104
64297
  init_compose_session();
64105
64298
  init_startup_screen();
@@ -64175,7 +64368,7 @@ function choose(answer, values) {
64175
64368
  }
64176
64369
  function isDirectory3(path) {
64177
64370
  try {
64178
- return (0, import_node_fs72.statSync)(path).isDirectory();
64371
+ return (0, import_node_fs73.statSync)(path).isDirectory();
64179
64372
  } catch {
64180
64373
  return false;
64181
64374
  }
@@ -64219,8 +64412,8 @@ function createTextStartupUi(io) {
64219
64412
  const m = msg().report.startup;
64220
64413
  const answer = (await ask([m.cwdMissing(missing)], m.cwdAnswer))?.trim();
64221
64414
  if (answer === void 0 || answer === "") return void 0;
64222
- const abs = (0, import_node_path99.resolve)(answer);
64223
- if (!(0, import_node_fs72.existsSync)(abs) || !isDirectory3(abs)) {
64415
+ const abs = (0, import_node_path100.resolve)(answer);
64416
+ if (!(0, import_node_fs73.existsSync)(abs) || !isDirectory3(abs)) {
64224
64417
  io.write(m.notDirectory(abs));
64225
64418
  return void 0;
64226
64419
  }
@@ -64228,12 +64421,12 @@ function createTextStartupUi(io) {
64228
64421
  }
64229
64422
  };
64230
64423
  }
64231
- var import_node_fs72, import_node_path99;
64424
+ var import_node_fs73, import_node_path100;
64232
64425
  var init_startup_ui_text = __esm({
64233
64426
  "src/modes/startup-ui-text.ts"() {
64234
64427
  "use strict";
64235
- import_node_fs72 = require("node:fs");
64236
- import_node_path99 = require("node:path");
64428
+ import_node_fs73 = require("node:fs");
64429
+ import_node_path100 = require("node:path");
64237
64430
  init_i18n();
64238
64431
  }
64239
64432
  });
@@ -64258,7 +64451,7 @@ function createTools(input2, options, state) {
64258
64451
  const osStatus = configureOsSandbox(input2.config.sandbox?.enabled);
64259
64452
  const bashSandbox = options.bashSandbox ?? resolveBashSandbox(input2.config.sandbox, {
64260
64453
  status: osStatus,
64261
- ...input2.paths === void 0 ? {} : { hidden: [(0, import_node_path100.join)(input2.paths.configDir, AUTH_FILE)] }
64454
+ ...input2.paths === void 0 ? {} : { hidden: [(0, import_node_path101.join)(input2.paths.configDir, AUTH_FILE)] }
64262
64455
  });
64263
64456
  state.bashSandbox = bashSandbox;
64264
64457
  const capability = options.sandboxCapability ?? sandboxCapabilityFor(input2.config);
@@ -64379,7 +64572,7 @@ function createRuntimeDeps(options = {}) {
64379
64572
  return registry3;
64380
64573
  }
64381
64574
  },
64382
- sessions: createSessionStore(),
64575
+ sessions: createSessionStore(options.log ?? stderrLog()),
64383
64576
  tools: { create: (input2) => createTools(input2, options, state) },
64384
64577
  permissions: {
64385
64578
  create: (input2) => new PermissionPipeline({
@@ -64448,7 +64641,7 @@ function createRuntimeDeps(options = {}) {
64448
64641
  if (ui !== void 0) deps.ui = ui;
64449
64642
  return deps;
64450
64643
  }
64451
- var import_node_path100, DEFAULT_TOOL_FACTORIES, DEFAULT_MODES;
64644
+ var import_node_path101, DEFAULT_TOOL_FACTORIES, DEFAULT_MODES;
64452
64645
  var init_compose2 = __esm({
64453
64646
  "src/cli/compose.ts"() {
64454
64647
  "use strict";
@@ -64463,7 +64656,7 @@ var init_compose2 = __esm({
64463
64656
  init_tool();
64464
64657
  init_detect();
64465
64658
  init_bash();
64466
- import_node_path100 = require("node:path");
64659
+ import_node_path101 = require("node:path");
64467
64660
  init_paths();
64468
64661
  init_presets2();
64469
64662
  init_registry();
@@ -64678,8 +64871,8 @@ var init_memory3 = __esm({
64678
64871
  });
64679
64872
 
64680
64873
  // src/cli/main.ts
64681
- var import_node_fs73 = require("node:fs");
64682
- var import_node_path101 = require("node:path");
64874
+ var import_node_fs74 = require("node:fs");
64875
+ var import_node_path102 = require("node:path");
64683
64876
  var import_node_url3 = require("node:url");
64684
64877
  init_version();
64685
64878
  init_args();
@@ -70254,7 +70447,7 @@ function readStdinDefault() {
70254
70447
  }
70255
70448
  function stdinKindDefault() {
70256
70449
  try {
70257
- const stat16 = (0, import_node_fs73.fstatSync)(0);
70450
+ const stat16 = (0, import_node_fs74.fstatSync)(0);
70258
70451
  if (stat16.isFile()) return "file";
70259
70452
  if (stat16.isFIFO()) return "fifo";
70260
70453
  if (stat16.isSocket()) return "socket";
@@ -70314,7 +70507,7 @@ function defaultIo() {
70314
70507
  }
70315
70508
  function peekUserLanguage(env) {
70316
70509
  try {
70317
- const raw = (0, import_node_fs73.readFileSync)((0, import_node_path101.join)(resolveConfigDir({ env }), "config.json"), "utf8");
70510
+ const raw = (0, import_node_fs74.readFileSync)((0, import_node_path102.join)(resolveConfigDir({ env }), "config.json"), "utf8");
70318
70511
  const value = JSON.parse(raw).ui?.language;
70319
70512
  return value === "auto" || value === "zh" || value === "en" ? value : void 0;
70320
70513
  } catch {
@@ -70402,7 +70595,7 @@ function isDirectRun() {
70402
70595
  const entry = process.argv[1];
70403
70596
  if (entry === void 0) return false;
70404
70597
  try {
70405
- return (0, import_node_fs73.realpathSync)(entry) === (0, import_node_fs73.realpathSync)((0, import_node_url3.fileURLToPath)(__ama_import_meta_url));
70598
+ return (0, import_node_fs74.realpathSync)(entry) === (0, import_node_fs74.realpathSync)((0, import_node_url3.fileURLToPath)(__ama_import_meta_url));
70406
70599
  } catch {
70407
70600
  return false;
70408
70601
  }