@armadra/agent 0.7.1 → 0.7.2

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 (97) hide show
  1. package/CHANGELOG.md +57 -0
  2. package/CHANGELOG.zh-CN.md +34 -0
  3. package/dist/agent/session-cache.d.ts +4 -2
  4. package/dist/agent/session-cache.js +5 -3
  5. package/dist/agent/session-compaction.d.ts +13 -1
  6. package/dist/agent/session-compaction.js +36 -0
  7. package/dist/agent/session-core.d.ts +2 -0
  8. package/dist/agent/session-state.d.ts +9 -3
  9. package/dist/agent/session-state.js +8 -6
  10. package/dist/agent/session.js +3 -3
  11. package/dist/agent/types-w5.d.ts +3 -0
  12. package/dist/agent/types.d.ts +18 -0
  13. package/dist/agents/task-record.d.ts +4 -0
  14. package/dist/agents/task-record.js +18 -0
  15. package/dist/ai/context.d.ts +7 -2
  16. package/dist/ai/context.js +45 -7
  17. package/dist/ai/providers/catalog-data.js +1 -1
  18. package/dist/bundle/ama.cjs +1643 -486
  19. package/dist/compaction/breakdown.d.ts +51 -0
  20. package/dist/compaction/breakdown.js +159 -0
  21. package/dist/compaction/estimate.d.ts +6 -0
  22. package/dist/compaction/estimate.js +1 -1
  23. package/dist/compaction/prefix-estimate.d.ts +23 -0
  24. package/dist/compaction/prefix-estimate.js +34 -0
  25. package/dist/config/json-schema.js +1 -0
  26. package/dist/config/key-docs.js +1 -0
  27. package/dist/config/schema.js +2 -0
  28. package/dist/config/settings-registry.js +1 -0
  29. package/dist/config/types.d.ts +2 -0
  30. package/dist/drivers/native/codex-app-server.js +6 -0
  31. package/dist/drivers/runner.js +17 -0
  32. package/dist/drivers/store.d.ts +3 -0
  33. package/dist/drivers/store.js +4 -0
  34. package/dist/i18n/catalog.d.ts +53 -0
  35. package/dist/i18n/messages/agents.d.ts +3 -0
  36. package/dist/i18n/messages/agents.js +3 -0
  37. package/dist/i18n/messages/config-keys.d.ts +2 -0
  38. package/dist/i18n/messages/config-keys.js +2 -0
  39. package/dist/i18n/messages/config.d.ts +2 -0
  40. package/dist/i18n/messages/interactive.d.ts +6 -0
  41. package/dist/i18n/messages/interactive.js +6 -0
  42. package/dist/i18n/messages/panels.d.ts +3 -0
  43. package/dist/i18n/messages/panels.js +5 -2
  44. package/dist/i18n/messages/report-context.d.ts +94 -0
  45. package/dist/i18n/messages/report-context.js +95 -0
  46. package/dist/i18n/messages/report.d.ts +96 -0
  47. package/dist/i18n/messages/report.js +13 -2
  48. package/dist/i18n/messages/settings.d.ts +2 -0
  49. package/dist/i18n/messages/settings.js +2 -0
  50. package/dist/i18n/messages/subcommands.js +2 -2
  51. package/dist/modes/acp/acp-events.d.ts +17 -4
  52. package/dist/modes/acp/acp-events.js +32 -6
  53. package/dist/modes/commands-core.js +4 -0
  54. package/dist/modes/context-report.d.ts +35 -0
  55. package/dist/modes/context-report.js +172 -0
  56. package/dist/modes/interactive/agent-bar.d.ts +2 -0
  57. package/dist/modes/interactive/agent-bar.js +6 -0
  58. package/dist/modes/interactive/commands.d.ts +2 -1
  59. package/dist/modes/interactive/commands.js +10 -1
  60. package/dist/modes/interactive/interactive-mode.d.ts +3 -0
  61. package/dist/modes/interactive/interactive-mode.js +6 -1
  62. package/dist/modes/interactive/panels.d.ts +2 -1
  63. package/dist/modes/interactive/panels.js +13 -5
  64. package/dist/modes/interactive/program-status-ui.d.ts +72 -0
  65. package/dist/modes/interactive/program-status-ui.js +229 -0
  66. package/dist/modes/interactive/session-events.d.ts +4 -0
  67. package/dist/modes/interactive/session-events.js +24 -2
  68. package/dist/modes/interactive/status-bar.d.ts +21 -3
  69. package/dist/modes/interactive/status-bar.js +64 -25
  70. package/dist/modes/interactive/status-ctx.d.ts +55 -0
  71. package/dist/modes/interactive/status-ctx.js +125 -0
  72. package/dist/modes/interactive/status-line.d.ts +2 -1
  73. package/dist/modes/interactive/status-line.js +3 -2
  74. package/dist/modes/interactive/tasks-report.js +4 -0
  75. package/dist/modes/print/print-mode.js +12 -0
  76. package/dist/modes/session-report.d.ts +10 -2
  77. package/dist/modes/session-report.js +26 -2
  78. package/dist/sdk.d.ts +1 -1
  79. package/dist/tools/types.d.ts +6 -0
  80. package/dist/tui/components/meter.d.ts +2 -0
  81. package/dist/tui/components/meter.js +1 -1
  82. package/dist/tui/program-status.d.ts +90 -0
  83. package/dist/tui/program-status.js +259 -0
  84. package/docs/acp.md +4 -4
  85. package/docs/agents.md +1 -1
  86. package/docs/en/acp.md +4 -4
  87. package/docs/en/providers.md +5 -3
  88. package/docs/en/rpc.md +3 -0
  89. package/docs/en/sessions.md +23 -0
  90. package/docs/en/tui.md +79 -22
  91. package/docs/providers.md +8 -5
  92. package/docs/rpc.md +1 -0
  93. package/docs/session-format.md +3 -3
  94. package/docs/sessions.md +22 -0
  95. package/docs/tui-design.md +2 -2
  96. package/docs/tui.md +79 -22
  97. 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.1" : readPackageVersion();
44
+ AMA_VERSION = true ? "0.7.2" : readPackageVersion();
45
45
  }
46
46
  });
47
47
 
@@ -1274,7 +1274,7 @@ Will write ${s2.path}: ${s2.id} new channels ${s2.channels.join(", ") || "none"}
1274
1274
  cacheProbe: {
1275
1275
  adviceSilent: (provider, cacheFieldPresent) => `Set providers.${provider}.compat.cacheReporting: "silent" in config and the status bar will show not reported` + (cacheFieldPresent ? " (the response has cache fields but they stay 0; cache writes may also be delayed, try a larger --gap-ms)" : ""),
1276
1276
  adviceInconclusive: "The second request read only a little cache or only wrote: maybe cache granularity or TTL; retry with a larger --tokens or a shorter --gap-ms",
1277
- advicePromptCache: (provider, id) => `Fill in promptCache.short yourself to enable warming (providers.${provider}.modelOverrides: [{ "id": "${id}", "promptCache": { "short": 300 } }])`,
1277
+ advicePromptCache: (provider, id) => `The catalog has no cache lifetime for this model, so ama neither warms it nor prunes early. Fill in promptCache only with a lifetime the upstream documents (providers.${provider}.modelOverrides: [{ "id": "${id}", "promptCache": { "short": <seconds> } }]); a guessed short value fires warming and early pruning while the cache is still valid`,
1278
1278
  intAtLeast: (name, min) => `--${name} must be an integer ≥ ${min}`,
1279
1279
  yes: "yes",
1280
1280
  no: "no",
@@ -1526,7 +1526,7 @@ Estimated cost: ${estimate} (2 × ${tokens} tokens × catalog price)
1526
1526
  cacheProbe: {
1527
1527
  adviceSilent: (provider, cacheFieldPresent) => `可在 config 里设 providers.${provider}.compat.cacheReporting: "silent",状态栏将显示未报告` + (cacheFieldPresent ? "(响应里有缓存字段但恒为 0;也可能是缓存写入有延迟,可加大 --gap-ms 再测一次)" : ""),
1528
1528
  adviceInconclusive: "第二次只读到少量缓存或只有写入:可能是缓存粒度或 TTL 问题,可加大 --tokens 或缩短 --gap-ms 重试",
1529
- advicePromptCache: (provider, id) => `可自填 promptCache.short 以启用保温(providers.${provider}.modelOverrides: [{ "id": "${id}", "promptCache": { "short": 300 } }])`,
1529
+ advicePromptCache: (provider, id) => `目录里没有这个模型的缓存寿命,ama 不保温、也不提前裁剪。只有上游文档写明了寿命时才填 promptCache(providers.${provider}.modelOverrides: [{ "id": "${id}", "promptCache": { "short": <秒数> } }]);猜一个偏短的值会在缓存仍有效时触发保温和提前裁剪`,
1530
1530
  intAtLeast: (name, min) => `--${name} 应为不小于 ${min} 的整数`,
1531
1531
  yes: "有",
1532
1532
  no: "无",
@@ -1974,6 +1974,10 @@ var init_interactive = __esm({
1974
1974
  cacheSilent: "n/a",
1975
1975
  sandbox: "sandbox",
1976
1976
  cycleHint: "shift+tab to cycle",
1977
+ /** full 本行 Ctx 段末尾:自动压缩开着。 */
1978
+ autoCompact: "auto",
1979
+ /** 模型没有上下文窗口(状态栏 `ctx ?`)时提示一次。 */
1980
+ noWindow: (model) => `Context window unknown for ${model}: the status bar shows ctx ? and auto-compaction is off. Run \`ama models discover\` or set contextWindow for this model in the config.`,
1977
1981
  /** [W6] 订阅配额行(full 第三行)与窄屏 / compact 短格式;标签自带分隔(英文冒号后空格)。 */
1978
1982
  quota: {
1979
1983
  session: "Session: ",
@@ -2105,6 +2109,8 @@ var init_interactive = __esm({
2105
2109
  cacheSilent: "未报告",
2106
2110
  sandbox: "沙箱",
2107
2111
  cycleHint: "shift+tab 切换",
2112
+ autoCompact: "auto",
2113
+ noWindow: (model) => `${model} 没有上下文窗口信息:状态栏显示 ctx ?,自动压缩关闭。运行 \`ama models discover\`,或在配置里为该模型设置 contextWindow。`,
2108
2114
  quota: {
2109
2115
  session: "5 小时:",
2110
2116
  reset: "重置:",
@@ -2604,7 +2610,7 @@ var init_panels = __esm({
2604
2610
  user: (n) => `user ${n}`,
2605
2611
  assistant: (n) => `assistant ${n}`,
2606
2612
  toolCalls: (n) => `tool calls ${n}`,
2607
- usage: "Usage",
2613
+ usage: "Total usage",
2608
2614
  input: (tokens) => `input ${tokens}`,
2609
2615
  output: (tokens) => `output ${tokens}`,
2610
2616
  cacheRead: (tokens) => `cache read ${tokens}`,
@@ -2693,6 +2699,8 @@ var init_panels = __esm({
2693
2699
  interrupted: "interrupted"
2694
2700
  },
2695
2701
  turns: (n) => plural(n, "turn"),
2702
+ /** 外部 Agent 报告的上下文占用(`34%`、`0.4%`)。 */
2703
+ context: (percent3) => `ctx ${percent3}`,
2696
2704
  agentRunner: (agent, runner) => `${agent} (${runner})`,
2697
2705
  background: "background",
2698
2706
  none: "No subagent tasks yet",
@@ -2715,7 +2723,7 @@ var init_panels = __esm({
2715
2723
  user: (n) => `用户 ${n}`,
2716
2724
  assistant: (n) => `助手 ${n}`,
2717
2725
  toolCalls: (n) => `工具调用 ${n}`,
2718
- usage: "用量",
2726
+ usage: "累计用量",
2719
2727
  input: (tokens) => `输入 ${tokens}`,
2720
2728
  output: (tokens) => `输出 ${tokens}`,
2721
2729
  cacheRead: (tokens) => `缓存读 ${tokens}`,
@@ -2803,6 +2811,7 @@ var init_panels = __esm({
2803
2811
  interrupted: "已中断"
2804
2812
  },
2805
2813
  turns: (n) => `${n} 轮`,
2814
+ context: (percent3) => `ctx ${percent3}`,
2806
2815
  agentRunner: (agent, runner) => `${agent}(${runner})`,
2807
2816
  background: "后台",
2808
2817
  none: "还没有子 Agent 任务",
@@ -2968,13 +2977,111 @@ var init_permissions = __esm({
2968
2977
  }
2969
2978
  });
2970
2979
 
2971
- // src/i18n/messages/report.ts
2980
+ // src/i18n/messages/report-context.ts
2972
2981
  var en16, zh16;
2982
+ var init_report_context = __esm({
2983
+ "src/i18n/messages/report-context.ts"() {
2984
+ "use strict";
2985
+ init_format();
2986
+ en16 = {
2987
+ title: "Context",
2988
+ keyUsed: "Used",
2989
+ used: (tokens, window2, percent3) => `${tokens} / ${window2}${percent3 !== void 0 ? ` (${percent3})` : ""}`,
2990
+ keyLeft: "Left",
2991
+ left: (tokens) => `${tokens} tokens`,
2992
+ keySource: "Source",
2993
+ sourceUsage: (usage, estimate) => `reported usage ${usage} + estimated ${estimate}`,
2994
+ sourceEstimate: "estimated in full (no usable usage yet)",
2995
+ keyAutoCompact: "Auto-compact",
2996
+ autoCompact: (at, distance2) => `at ${at}, ≈ ${distance2} to go`,
2997
+ autoCompactReached: (at) => `at ${at}, reached`,
2998
+ autoCompactOff: "off",
2999
+ windowUnknown: "window unknown",
3000
+ keyPrune: "Pruning",
3001
+ prune: (at) => `old tool results trimmed from ${at}`,
3002
+ breakdown: (total) => `By category (estimated, ${total})`,
3003
+ prefixCounted: "System prompt and tool declarations are counted from the stored system messages; the full estimate can differ from reported usage.",
3004
+ prefixPending: "System prompt and tool declarations are not counted until the first request.",
3005
+ categories: {
3006
+ system: "System prompt",
3007
+ tools: "Tool declarations",
3008
+ user: "User messages",
3009
+ assistant: "Assistant text",
3010
+ reasoning: "Reasoning",
3011
+ toolCalls: "Tool-call arguments",
3012
+ toolResults: "Tool results",
3013
+ images: "Images",
3014
+ summaries: "Summaries",
3015
+ custom: "Custom messages"
3016
+ },
3017
+ partNames: {
3018
+ compaction: "compaction",
3019
+ branch: "branch",
3020
+ user: "user",
3021
+ toolResult: "tool result",
3022
+ custom: "custom"
3023
+ },
3024
+ images: (count2) => plural(count2, "image"),
3025
+ more: (count2) => `+${count2} more`,
3026
+ largest: (count2) => `Largest tool results (top ${count2})`,
3027
+ noToolResults: "no tool results",
3028
+ empty: "No messages in the context yet."
3029
+ };
3030
+ zh16 = {
3031
+ title: "上下文",
3032
+ keyUsed: "已用",
3033
+ used: (tokens, window2, percent3) => `${tokens} / ${window2}${percent3 !== void 0 ? `(${percent3})` : ""}`,
3034
+ keyLeft: "剩余",
3035
+ left: (tokens) => `${tokens} token`,
3036
+ keySource: "来源",
3037
+ sourceUsage: (usage, estimate) => `usage 实测 ${usage} + 估算 ${estimate}`,
3038
+ sourceEstimate: "全量估算(还没有可用的 usage)",
3039
+ keyAutoCompact: "自动压缩",
3040
+ autoCompact: (at, distance2) => `${at} 触发,还差 ≈ ${distance2}`,
3041
+ autoCompactReached: (at) => `${at} 触发,已达到`,
3042
+ autoCompactOff: "关闭",
3043
+ windowUnknown: "窗口未知",
3044
+ keyPrune: "裁剪",
3045
+ prune: (at) => `超过 ${at} 时清理旧工具结果`,
3046
+ breakdown: (total) => `按类别(估算,${total})`,
3047
+ prefixCounted: "系统提示与工具声明按会话里存的 system 消息计入;全量估算可能与 usage 实测不同。",
3048
+ prefixPending: "系统提示与工具声明在首次请求后才计入。",
3049
+ categories: {
3050
+ system: "系统提示",
3051
+ tools: "工具声明",
3052
+ user: "用户消息",
3053
+ assistant: "助手文本",
3054
+ reasoning: "推理",
3055
+ toolCalls: "工具调用参数",
3056
+ toolResults: "工具结果",
3057
+ images: "附件图片",
3058
+ summaries: "摘要",
3059
+ custom: "自定义消息"
3060
+ },
3061
+ partNames: {
3062
+ compaction: "压缩",
3063
+ branch: "分支",
3064
+ user: "用户",
3065
+ toolResult: "工具结果",
3066
+ custom: "自定义"
3067
+ },
3068
+ images: (count2) => `${count2} 张`,
3069
+ more: (count2) => `另有 ${count2} 项`,
3070
+ largest: (count2) => `最大的工具结果(前 ${count2})`,
3071
+ noToolResults: "没有工具结果",
3072
+ empty: "上下文里还没有消息。"
3073
+ };
3074
+ }
3075
+ });
3076
+
3077
+ // src/i18n/messages/report.ts
3078
+ var en17, zh17;
2973
3079
  var init_report = __esm({
2974
3080
  "src/i18n/messages/report.ts"() {
2975
3081
  "use strict";
2976
3082
  init_format();
2977
- en16 = {
3083
+ init_report_context();
3084
+ en17 = {
2978
3085
  /** 内置斜杠命令:`args` 只写需要翻译的占位,说明整句。 */
2979
3086
  commands: {
2980
3087
  help: "List commands",
@@ -2994,6 +3101,7 @@ var init_report = __esm({
2994
3101
  tools: "List / set active tools",
2995
3102
  hooks: "List loaded hooks",
2996
3103
  session: "Session info, usage and cache",
3104
+ context: "Context breakdown by category (estimated), source, window and auto-compact threshold",
2997
3105
  cache: "Cache stats; switch warming for this session; print the prefix fingerprint",
2998
3106
  statusline: "Footer info two lines / one line (Ctrl+G)",
2999
3107
  planArgs: "[goal] | approve [mode|fresh] | reject",
@@ -3053,6 +3161,8 @@ Available: ${inactive.join(", ")}` : ""}`,
3053
3161
  notDirectory: (path) => `Not a directory: ${path}
3054
3162
  `
3055
3163
  },
3164
+ /** `/context`:上下文分类明细(report-context.ts)。 */
3165
+ contextReport: en16,
3056
3166
  /** `/session`、`/cache` 报告与缓存提示行。 */
3057
3167
  cache: {
3058
3168
  unreported: "not reported",
@@ -3153,10 +3263,13 @@ Available: ${inactive.join(", ")}` : ""}`,
3153
3263
  model: (model, thinking, permission) => `${model} · thinking ${thinking} · permission ${permission}`,
3154
3264
  keyMessages: "Messages",
3155
3265
  messages: (user, assistant, tools) => `user ${user} · assistant ${assistant} · tool calls ${tools}`,
3156
- keyUsage: "Usage",
3266
+ keyUsage: "Total usage",
3157
3267
  usage: (input2, output, cacheRead, cacheWrite, cost2) => `input ${input2} · output ${output} · cache read ${cacheRead} · cache write ${cacheWrite} · ${cost2}`,
3158
3268
  keyContext: "Context",
3159
3269
  context: (tokens, window2, percent3) => `${tokens} / ${window2} (${percent3}%)`,
3270
+ /** 上下文行末尾:距自动压缩(档二)还有多少 token;自动压缩关闭 / 熔断时换成 autoCompactOff。 */
3271
+ toAutoCompact: (tokens) => `≈ ${tokens} until auto-compaction`,
3272
+ autoCompactOff: "auto-compaction off",
3160
3273
  keyAgents: "Sub-agents",
3161
3274
  externalTitle: "External agents",
3162
3275
  noFingerprint: "This session does not provide a prefix fingerprint",
@@ -3218,7 +3331,7 @@ Available: ${inactive.join(", ")}` : ""}`,
3218
3331
  userHooks: (path) => `User hooks.json: ${path}`
3219
3332
  }
3220
3333
  };
3221
- zh16 = {
3334
+ zh17 = {
3222
3335
  commands: {
3223
3336
  help: "列出命令",
3224
3337
  new: "新建会话",
@@ -3237,6 +3350,7 @@ Available: ${inactive.join(", ")}` : ""}`,
3237
3350
  tools: "列出 / 设置活动工具",
3238
3351
  hooks: "列出已加载的 Hook",
3239
3352
  session: "会话信息、用量与缓存",
3353
+ context: "上下文按类别拆分(估算)、来源、窗口与自动压缩阈值",
3240
3354
  cache: "缓存统计;切换本会话保温;打印前缀指纹",
3241
3355
  statusline: "底部信息行两行 / 一行(Ctrl+G)",
3242
3356
  planArgs: "[目标] | approve [模式|fresh] | reject",
@@ -3294,6 +3408,7 @@ Available: ${inactive.join(", ")}` : ""}`,
3294
3408
  notDirectory: (path) => `不是目录:${path}
3295
3409
  `
3296
3410
  },
3411
+ contextReport: zh16,
3297
3412
  cache: {
3298
3413
  unreported: "未报告",
3299
3414
  unknown: "未知",
@@ -3389,10 +3504,12 @@ Available: ${inactive.join(", ")}` : ""}`,
3389
3504
  model: (model, thinking, permission) => `${model} · 思考 ${thinking} · 权限 ${permission}`,
3390
3505
  keyMessages: "消息",
3391
3506
  messages: (user, assistant, tools) => `用户 ${user} · 助手 ${assistant} · 工具调用 ${tools}`,
3392
- keyUsage: "用量",
3507
+ keyUsage: "累计用量",
3393
3508
  usage: (input2, output, cacheRead, cacheWrite, cost2) => `输入 ${input2} · 输出 ${output} · 缓存读 ${cacheRead} · 缓存写 ${cacheWrite} · ${cost2}`,
3394
3509
  keyContext: "上下文",
3395
3510
  context: (tokens, window2, percent3) => `${tokens} / ${window2}(${percent3}%)`,
3511
+ toAutoCompact: (tokens) => `距自动压缩 ≈ ${tokens}`,
3512
+ autoCompactOff: "自动压缩关闭",
3396
3513
  keyAgents: "子 Agent",
3397
3514
  externalTitle: "外部 Agent",
3398
3515
  noFingerprint: "当前会话不提供前缀指纹",
@@ -3457,12 +3574,12 @@ Available: ${inactive.join(", ")}` : ""}`,
3457
3574
  });
3458
3575
 
3459
3576
  // src/i18n/messages/print.ts
3460
- var en17, zh17;
3577
+ var en18, zh18;
3461
3578
  var init_print = __esm({
3462
3579
  "src/i18n/messages/print.ts"() {
3463
3580
  "use strict";
3464
3581
  init_format();
3465
- en17 = {
3582
+ en18 = {
3466
3583
  /** `ama -p` 的 stderr。 */
3467
3584
  print: {
3468
3585
  stdinIgnored: (wait) => `ama: no piped input within ${wait}, ignored; to wait for it, end the arguments with -
@@ -3493,7 +3610,7 @@ var init_print = __esm({
3493
3610
  badMode: (modes) => `mode must be ${modes.join(" | ")}`
3494
3611
  }
3495
3612
  };
3496
- zh17 = {
3613
+ zh18 = {
3497
3614
  print: {
3498
3615
  stdinIgnored: (wait) => `ama: 未在 ${wait}内收到管道输入,已忽略;需要等待请在末尾加 -
3499
3616
  `,
@@ -3526,7 +3643,7 @@ var init_print = __esm({
3526
3643
  });
3527
3644
 
3528
3645
  // src/i18n/messages/config-keys.ts
3529
- var dynamicDefaultsEn, dynamicDefaultsZh, keysEn, keysZh, en18, zh18;
3646
+ var dynamicDefaultsEn, dynamicDefaultsZh, keysEn, keysZh, en19, zh19;
3530
3647
  var init_config_keys = __esm({
3531
3648
  "src/i18n/messages/config-keys.ts"() {
3532
3649
  "use strict";
@@ -3626,6 +3743,7 @@ var init_config_keys = __esm({
3626
3743
  "ui.animation": "false: the spinner stays still while running and redraws only when seconds change; the startup logo does not animate",
3627
3744
  "ui.restoreOnCancel": "When Esc interrupts a run before any reply or tool call in this turn, withdraw the turn and put the message back into the input box",
3628
3745
  "ui.enterWhileRunning": "Enter while a run is in progress: queue (default) queues the message as a steer delivered at the next delivery point; interrupt stops the current turn and sends it at once. The app.message.interrupt key (default Ctrl+X) does the other one",
3746
+ "ui.programStatus": "Report run state to the terminal with OSC 7501 (idle / working / blocked / done / error): auto (default) sends only after the terminal answers the detection query (off inside tmux), on sends without detecting (inside tmux wrapped in passthrough; needs allow-passthrough on), off never sends. Interactive TUI only",
3629
3747
  "ui.statusLine": "Bottom info line: full shows two lines (rate line + status line), compact one; toggle at runtime with Ctrl+G or /statusline (effective from wave 5 W5-A)",
3630
3748
  "ui.language": "Interface language: auto decides from LC_ALL / LC_MESSAGES / LANG (zh* is Chinese, anything else English), zh, en; AMA_LANG and --lang override; affects the interface only, text sent to the model is always English",
3631
3749
  "ui.replyLanguage": "Reply language for the model (e.g. Chinese): one English rule is appended to the end of the rules section at session start; zero-byte change when unset; user level only (effective from wave 6)",
@@ -3749,6 +3867,7 @@ var init_config_keys = __esm({
3749
3867
  "ui.animation": "false:运行中 spinner 静止,只在秒数变化时重绘;启动字符画不播放动画",
3750
3868
  "ui.restoreOnCancel": "运行中 Esc 中断、本回合还没有任何回复或工具调用时,撤回该回合并把原消息放回输入框",
3751
3869
  "ui.enterWhileRunning": "运行中按 Enter:queue(缺省)排队插话,等下一个投递点送达;interrupt 打断当前回合并立即发送。app.message.interrupt 键(缺省 Ctrl+X)做另一种",
3870
+ "ui.programStatus": "用 OSC 7501 把运行状态报告给终端(idle / working / blocked / done / error):auto(缺省)终端回应检测查询后才发(tmux 里不发),on 不检测直接发(tmux 里经 passthrough 包裹,需要 allow-passthrough on),off 不发。只用于交互界面",
3752
3871
  "ui.statusLine": "底部信息行:full 两行(速率行 + 状态行),compact 一行;运行时 Ctrl+G 或 /statusline 切换(第五波 W5-A 起生效)",
3753
3872
  "ui.language": "界面语言:auto 按 LC_ALL / LC_MESSAGES / LANG 判断(zh* 为中文,其余英文),zh,en;AMA_LANG、--lang 覆盖;只影响界面,发给模型的文本固定英文",
3754
3873
  "ui.replyLanguage": "模型回复语言(如 Chinese):会话开始在 rules 节末尾追加一句英文规则;不设时零字节变化;只认用户级(第六波起生效)",
@@ -3822,8 +3941,8 @@ var init_config_keys = __esm({
3822
3941
  todo: "todo 工具;只认用户级(第五波 W5-F / W5-H2 起生效)",
3823
3942
  "todo.reminder": "连续 N 回合未更新且有未完成项时提醒复述;0 关闭"
3824
3943
  };
3825
- en18 = { keys: keysEn, dynamicDefaults: dynamicDefaultsEn };
3826
- zh18 = {
3944
+ en19 = { keys: keysEn, dynamicDefaults: dynamicDefaultsEn };
3945
+ zh19 = {
3827
3946
  keys: keysZh,
3828
3947
  dynamicDefaults: dynamicDefaultsZh
3829
3948
  };
@@ -3831,12 +3950,12 @@ var init_config_keys = __esm({
3831
3950
  });
3832
3951
 
3833
3952
  // src/i18n/messages/config.ts
3834
- var en19, zh19;
3953
+ var en20, zh20;
3835
3954
  var init_config = __esm({
3836
3955
  "src/i18n/messages/config.ts"() {
3837
3956
  "use strict";
3838
3957
  init_config_keys();
3839
- en19 = {
3958
+ en20 = {
3840
3959
  /** 配置校验诊断(checker.ts、schema.ts、schema-w5.ts、schema-w6.ts)。 */
3841
3960
  schema: {
3842
3961
  enumArray: (choices) => `should be an array of ${choices.join(" | ")}`,
@@ -3957,9 +4076,9 @@ var init_config = __esm({
3957
4076
  models: "Custom models (an entry with the same id replaces the whole model)",
3958
4077
  modelOverrides: "Only changes metadata of existing models"
3959
4078
  },
3960
- ...en18
4079
+ ...en19
3961
4080
  };
3962
- zh19 = {
4081
+ zh20 = {
3963
4082
  schema: {
3964
4083
  enumArray: (choices) => `应为 ${choices.join(" | ")} 组成的数组`,
3965
4084
  ports: "应为端口号数组(0–65535;0 = 任意空闲端口)",
@@ -4071,18 +4190,18 @@ var init_config = __esm({
4071
4190
  models: "自定义模型(同 id 整条替换)",
4072
4191
  modelOverrides: "只改已有模型的元数据"
4073
4192
  },
4074
- ...zh18
4193
+ ...zh19
4075
4194
  };
4076
4195
  }
4077
4196
  });
4078
4197
 
4079
4198
  // src/i18n/messages/drivers.ts
4080
- var en20, zh20;
4199
+ var en21, zh21;
4081
4200
  var init_drivers = __esm({
4082
4201
  "src/i18n/messages/drivers.ts"() {
4083
4202
  "use strict";
4084
4203
  init_format();
4085
- en20 = {
4204
+ en21 = {
4086
4205
  /** 外部 Agent 的 notice 与日志。 */
4087
4206
  agent: {
4088
4207
  reapedOrphans: (pids) => `cleaned up external agent processes left over from last time: ${pids}`,
@@ -4142,7 +4261,7 @@ var init_drivers = __esm({
4142
4261
  noId: (label2) => `${label2}: the adapter returned by create() has no id`
4143
4262
  }
4144
4263
  };
4145
- zh20 = {
4264
+ zh21 = {
4146
4265
  agent: {
4147
4266
  reapedOrphans: (pids) => `清理了上次遗留的外部 Agent 进程:${pids}`,
4148
4267
  unverifiedVersion: (program, version, verified) => `${program} ${version} 不在已验证区间 ${verified},协议可能有变化`,
@@ -4203,12 +4322,12 @@ var init_drivers = __esm({
4203
4322
  });
4204
4323
 
4205
4324
  // src/i18n/messages/session.ts
4206
- var en21, zh21;
4325
+ var en22, zh22;
4207
4326
  var init_session = __esm({
4208
4327
  "src/i18n/messages/session.ts"() {
4209
4328
  "use strict";
4210
4329
  init_format();
4211
- en21 = {
4330
+ en22 = {
4212
4331
  /** `ama sessions list / show / prune`(search / export / trace 各有自己的文案)。 */
4213
4332
  cli: {
4214
4333
  usage: `Usage: ama sessions list [--all] [--session-dir <dir>]
@@ -4344,7 +4463,7 @@ var init_session = __esm({
4344
4463
  }
4345
4464
  }
4346
4465
  };
4347
- zh21 = {
4466
+ zh22 = {
4348
4467
  cli: {
4349
4468
  usage: `用法:ama sessions list [--all] [--session-dir <目录>]
4350
4469
  ama sessions show <id> [--session-dir <目录>]
@@ -4477,7 +4596,7 @@ var init_session = __esm({
4477
4596
  });
4478
4597
 
4479
4598
  // src/i18n/messages/errors.ts
4480
- var EN_HINT, ZH_HINT, en22, zh22;
4599
+ var EN_HINT, ZH_HINT, en23, zh23;
4481
4600
  var init_errors2 = __esm({
4482
4601
  "src/i18n/messages/errors.ts"() {
4483
4602
  "use strict";
@@ -4492,7 +4611,7 @@ var init_errors2 = __esm({
4492
4611
  no_tool: "(没找到缩放工具:装 ImageMagick,macOS 自带 sips)",
4493
4612
  still_too_large: "(缩放后仍超限)"
4494
4613
  };
4495
- en22 = {
4614
+ en23 = {
4496
4615
  /** [W6-I3] 图片附件:当前模型不收图片(`--image` / `@路径`)。 */
4497
4616
  imageUnsupported: (model) => `${model ?? "The current model"} does not accept image input (the model's input has no image); switch to a model that supports images (marked "image" in ama models list, or give the model "input": ["text", "image"] in the config)`,
4498
4617
  /** [W6-I3] SDK(`createRuntime` / `createAgentSession`)。 */
@@ -4564,7 +4683,7 @@ var init_errors2 = __esm({
4564
4683
  }
4565
4684
  }
4566
4685
  };
4567
- zh22 = {
4686
+ zh23 = {
4568
4687
  imageUnsupported: (model) => `${model ?? "当前模型"} 不接受图片输入(模型 input 没有 image);换一个支持图像的模型再试(ama models list 里标「图片」的,或在配置里给该模型写 "input": ["text", "image"])`,
4569
4688
  cacheParamsStripped: (key, field2, suggestion) => `${key}:该端点不支持 ${field2},已自动去掉缓存参数重发;可在 config 里设 ${suggestion ?? "相应的 compat 开关"}`,
4570
4689
  sdk: {
@@ -4635,12 +4754,12 @@ var init_errors2 = __esm({
4635
4754
  });
4636
4755
 
4637
4756
  // src/i18n/messages/agents.ts
4638
- var en23, zh23;
4757
+ var en24, zh24;
4639
4758
  var init_agents = __esm({
4640
4759
  "src/i18n/messages/agents.ts"() {
4641
4760
  "use strict";
4642
4761
  init_format();
4643
- en23 = {
4762
+ en24 = {
4644
4763
  status: {
4645
4764
  queued: "queued",
4646
4765
  running: "running",
@@ -4652,6 +4771,8 @@ var init_agents = __esm({
4652
4771
  interrupted: "interrupted"
4653
4772
  },
4654
4773
  turns: (n) => plural(n, "turn"),
4774
+ /** 外部 Agent 报告的上下文占用(`34%`、`0.4%`)。 */
4775
+ context: (percent3) => `ctx ${percent3}`,
4655
4776
  bar: {
4656
4777
  more: (n) => `${n} more`,
4657
4778
  keys: (up, down) => `${up}${down} select · Enter open · Esc back`,
@@ -4710,7 +4831,7 @@ var init_agents = __esm({
4710
4831
  externalNoSession: (runner) => `Live output is kept only in memory; ${runner} did not report a session to resume.`
4711
4832
  }
4712
4833
  };
4713
- zh23 = {
4834
+ zh24 = {
4714
4835
  status: {
4715
4836
  queued: "排队",
4716
4837
  running: "运行中",
@@ -4722,6 +4843,7 @@ var init_agents = __esm({
4722
4843
  interrupted: "已中断"
4723
4844
  },
4724
4845
  turns: (n) => `${n} 轮`,
4846
+ context: (percent3) => `ctx ${percent3}`,
4725
4847
  bar: {
4726
4848
  more: (n) => `另 ${n} 个`,
4727
4849
  keys: (up, down) => `${up}${down} 选择 · Enter 打开 · Esc 返回`,
@@ -4778,12 +4900,12 @@ var init_agents = __esm({
4778
4900
  });
4779
4901
 
4780
4902
  // src/i18n/messages/trace.ts
4781
- var en24, zh24;
4903
+ var en25, zh25;
4782
4904
  var init_trace = __esm({
4783
4905
  "src/i18n/messages/trace.ts"() {
4784
4906
  "use strict";
4785
4907
  init_format();
4786
- en24 = {
4908
+ en25 = {
4787
4909
  title: "Trace",
4788
4910
  /** 顶部汇总行。 */
4789
4911
  summary: (turns, requests, tools) => `${plural(turns, "turn")} · ${plural(requests, "request")} · ${plural(tools, "tool call")}`,
@@ -4902,7 +5024,7 @@ var init_trace = __esm({
4902
5024
  badNow: (raw) => `--now expects epoch milliseconds (got ${raw})`
4903
5025
  }
4904
5026
  };
4905
- zh24 = {
5027
+ zh25 = {
4906
5028
  title: "轨迹",
4907
5029
  summary: (turns, requests, tools) => `${turns} 回合 · ${requests} 请求 · ${tools} 次工具`,
4908
5030
  cache: (percent3) => `缓存 ${percent3}`,
@@ -5021,12 +5143,12 @@ var init_trace = __esm({
5021
5143
  });
5022
5144
 
5023
5145
  // src/i18n/messages/memory.ts
5024
- var en25, zh25;
5146
+ var en26, zh26;
5025
5147
  var init_memory = __esm({
5026
5148
  "src/i18n/messages/memory.ts"() {
5027
5149
  "use strict";
5028
5150
  init_format();
5029
- en25 = {
5151
+ en26 = {
5030
5152
  scope: { user: "user", project: "project", workspace: "workspace" },
5031
5153
  panel: {
5032
5154
  title: "Memory",
@@ -5086,7 +5208,7 @@ var init_memory = __esm({
5086
5208
  untrusted: (cwd) => `Project scope skipped: ${cwd} is not trusted (run ama --trust there once).`
5087
5209
  }
5088
5210
  };
5089
- zh25 = {
5211
+ zh26 = {
5090
5212
  scope: { user: "用户", project: "项目", workspace: "工作空间" },
5091
5213
  panel: {
5092
5214
  title: "记忆",
@@ -5150,11 +5272,11 @@ var init_memory = __esm({
5150
5272
  });
5151
5273
 
5152
5274
  // src/i18n/messages/auth.ts
5153
- var en26, zh26;
5275
+ var en27, zh27;
5154
5276
  var init_auth = __esm({
5155
5277
  "src/i18n/messages/auth.ts"() {
5156
5278
  "use strict";
5157
- en26 = {
5279
+ en27 = {
5158
5280
  usage: `Usage: ama auth set [<provider>] [--auth-file <file>] read the key from stdin (no provider on a TTY: pick one)
5159
5281
  ama auth list [--auth-file <file>]
5160
5282
  ama auth remove <provider> [--auth-file <file>]
@@ -5262,7 +5384,7 @@ Then paste the full URL from the address bar (http://127.0.0.1:…/auth/callback
5262
5384
  relogin: (provider) => `${provider}: sign-in expired, run ama auth login ${provider}`
5263
5385
  }
5264
5386
  };
5265
- zh26 = {
5387
+ zh27 = {
5266
5388
  usage: `用法:ama auth set [<provider>] [--auth-file <文件>] 从 stdin 读取 key(TTY 下不给供应商则先选)
5267
5389
  ama auth list [--auth-file <文件>]
5268
5390
  ama auth remove <provider> [--auth-file <文件>]
@@ -5374,11 +5496,11 @@ Then paste the full URL from the address bar (http://127.0.0.1:…/auth/callback
5374
5496
  });
5375
5497
 
5376
5498
  // src/i18n/messages/settings.ts
5377
- var en27, zh27;
5499
+ var en28, zh28;
5378
5500
  var init_settings = __esm({
5379
5501
  "src/i18n/messages/settings.ts"() {
5380
5502
  "use strict";
5381
- en27 = {
5503
+ en28 = {
5382
5504
  groups: {
5383
5505
  ui: "Interface",
5384
5506
  model: "Model",
@@ -5402,6 +5524,7 @@ var init_settings = __esm({
5402
5524
  "ui.statusLine": "Status line",
5403
5525
  "ui.restoreOnCancel": "Restore on cancel",
5404
5526
  "ui.enterWhileRunning": "Enter while running",
5527
+ "ui.programStatus": "Terminal program status",
5405
5528
  "ui.quietStartup": "Startup screen",
5406
5529
  "ui.ascii": "ASCII glyphs",
5407
5530
  "ui.language": "Interface language",
@@ -5551,7 +5674,7 @@ var init_settings = __esm({
5551
5674
  missingValue: "missing <value>"
5552
5675
  }
5553
5676
  };
5554
- zh27 = {
5677
+ zh28 = {
5555
5678
  groups: {
5556
5679
  ui: "界面",
5557
5680
  model: "模型",
@@ -5574,6 +5697,7 @@ var init_settings = __esm({
5574
5697
  "ui.statusLine": "底部信息行",
5575
5698
  "ui.restoreOnCancel": "中断后放回原消息",
5576
5699
  "ui.enterWhileRunning": "运行中按 Enter",
5700
+ "ui.programStatus": "终端程序状态",
5577
5701
  "ui.quietStartup": "启动画面",
5578
5702
  "ui.ascii": "ASCII 字形",
5579
5703
  "ui.language": "界面语言",
@@ -5726,12 +5850,12 @@ var init_settings = __esm({
5726
5850
  });
5727
5851
 
5728
5852
  // src/i18n/messages/acp.ts
5729
- var en28, zh28;
5853
+ var en29, zh29;
5730
5854
  var init_acp = __esm({
5731
5855
  "src/i18n/messages/acp.ts"() {
5732
5856
  "use strict";
5733
5857
  init_format();
5734
- en28 = {
5858
+ en29 = {
5735
5859
  /** ACP 服务端(`ama --mode acp`)的现有文案。 */
5736
5860
  core: {
5737
5861
  missingParam: (key) => `missing ${key}`,
@@ -5793,7 +5917,7 @@ var init_acp = __esm({
5793
5917
  authTerminal: (name, command3) => `${name} (run in a terminal: ${command3})`
5794
5918
  }
5795
5919
  };
5796
- zh28 = {
5920
+ zh29 = {
5797
5921
  core: {
5798
5922
  missingParam: (key) => `缺少 ${key}`,
5799
5923
  promptNotArray: "prompt 应为内容块数组",
@@ -5852,7 +5976,7 @@ var init_acp = __esm({
5852
5976
  });
5853
5977
 
5854
5978
  // src/i18n/catalog.ts
5855
- var en29, zh29, CATALOGS, MESSAGE_DOMAINS;
5979
+ var en30, zh30, CATALOGS, MESSAGE_DOMAINS;
5856
5980
  var init_catalog = __esm({
5857
5981
  "src/i18n/catalog.ts"() {
5858
5982
  "use strict";
@@ -5876,7 +6000,7 @@ var init_catalog = __esm({
5876
6000
  init_auth();
5877
6001
  init_settings();
5878
6002
  init_acp();
5879
- en29 = {
6003
+ en30 = {
5880
6004
  cli: en3,
5881
6005
  subcommands: en6,
5882
6006
  interactive: en10,
@@ -5885,20 +6009,20 @@ var init_catalog = __esm({
5885
6009
  rewind: en13,
5886
6010
  panels: en14,
5887
6011
  permissions: en15,
5888
- report: en16,
5889
- print: en17,
5890
- config: en19,
5891
- drivers: en20,
5892
- session: en21,
5893
- errors: en22,
5894
- agents: en23,
5895
- trace: en24,
5896
- memory: en25,
5897
- auth: en26,
5898
- settings: en27,
5899
- acp: en28
6012
+ report: en17,
6013
+ print: en18,
6014
+ config: en20,
6015
+ drivers: en21,
6016
+ session: en22,
6017
+ errors: en23,
6018
+ agents: en24,
6019
+ trace: en25,
6020
+ memory: en26,
6021
+ auth: en27,
6022
+ settings: en28,
6023
+ acp: en29
5900
6024
  };
5901
- zh29 = {
6025
+ zh30 = {
5902
6026
  cli: zh3,
5903
6027
  subcommands: zh6,
5904
6028
  interactive: zh10,
@@ -5907,21 +6031,21 @@ var init_catalog = __esm({
5907
6031
  rewind: zh13,
5908
6032
  panels: zh14,
5909
6033
  permissions: zh15,
5910
- report: zh16,
5911
- print: zh17,
5912
- config: zh19,
5913
- drivers: zh20,
5914
- session: zh21,
5915
- errors: zh22,
5916
- agents: zh23,
5917
- trace: zh24,
5918
- memory: zh25,
5919
- auth: zh26,
5920
- settings: zh27,
5921
- acp: zh28
6034
+ report: zh17,
6035
+ print: zh18,
6036
+ config: zh20,
6037
+ drivers: zh21,
6038
+ session: zh22,
6039
+ errors: zh23,
6040
+ agents: zh24,
6041
+ trace: zh25,
6042
+ memory: zh26,
6043
+ auth: zh27,
6044
+ settings: zh28,
6045
+ acp: zh29
5922
6046
  };
5923
- CATALOGS = { en: en29, zh: zh29 };
5924
- MESSAGE_DOMAINS = Object.keys(en29);
6047
+ CATALOGS = { en: en30, zh: zh30 };
6048
+ MESSAGE_DOMAINS = Object.keys(en30);
5925
6049
  }
5926
6050
  });
5927
6051
 
@@ -7300,6 +7424,7 @@ function validateConfig(value) {
7300
7424
  "animation",
7301
7425
  "restoreOnCancel",
7302
7426
  "enterWhileRunning",
7427
+ "programStatus",
7303
7428
  ...W5_UI_KEYS,
7304
7429
  ...W6_UI_KEYS
7305
7430
  ],
@@ -7315,6 +7440,7 @@ function validateConfig(value) {
7315
7440
  c.boolean(s2, "animation", p);
7316
7441
  c.boolean(s2, "restoreOnCancel", p);
7317
7442
  c.oneOf(s2, "enterWhileRunning", p, ["queue", "interrupt"]);
7443
+ c.oneOf(s2, "programStatus", p, ["auto", "on", "off"]);
7318
7444
  checkUiW5(c, s2, p);
7319
7445
  checkUiW6(c, s2, p);
7320
7446
  }
@@ -10993,7 +11119,7 @@ var init_catalog_data = __esm({
10993
11119
  anthropic: '{"version":1,"provider":"anthropic","modelsDev":"anthropic","models":[{"id":"claude-fable-5-1","thinkingLevelMap":{"off":null,"xhigh":"xhigh"},"promptCache":{"short":300,"long":3600,"minTokens":512},"compat":{"adaptiveThinking":true}},{"id":"claude-fable-5","thinkingLevelMap":{"off":null,"xhigh":"xhigh"},"promptCache":{"short":300,"long":3600,"minTokens":512},"compat":{"adaptiveThinking":true}},{"id":"claude-opus-5-5","thinkingLevelMap":{"off":null,"minimal":null,"low":"low","medium":"medium","high":"high","xhigh":"xhigh"},"promptCache":{"short":300,"long":3600,"minTokens":512},"compat":{"adaptiveThinking":true}},{"id":"claude-opus-5","thinkingLevelMap":{"off":null,"xhigh":"xhigh"},"promptCache":{"short":300,"long":3600,"minTokens":512},"compat":{"adaptiveThinking":true}},{"id":"claude-opus-4-8","thinkingLevelMap":{"xhigh":"xhigh"},"promptCache":{"short":300,"long":3600,"minTokens":1024},"compat":{"adaptiveThinking":true}},{"id":"claude-opus-4-7","thinkingLevelMap":{"xhigh":"xhigh"},"promptCache":{"short":300,"long":3600,"minTokens":2048},"compat":{"adaptiveThinking":true}},{"id":"claude-opus-4-6","promptCache":{"short":300,"long":3600,"minTokens":4096},"compat":{"adaptiveThinking":true}},{"id":"claude-sonnet-5-5","thinkingLevelMap":{"off":null,"minimal":null,"low":"low","medium":"medium","high":"high","xhigh":"xhigh"},"promptCache":{"short":300,"long":3600,"minTokens":512},"compat":{"adaptiveThinking":true}},{"id":"claude-sonnet-5","thinkingLevelMap":{"xhigh":"xhigh"},"promptCache":{"short":300,"long":3600,"minTokens":1024},"compat":{"adaptiveThinking":true}},{"id":"claude-sonnet-4-6","promptCache":{"short":300,"long":3600,"minTokens":1024},"compat":{"adaptiveThinking":true}},{"id":"claude-sonnet-4-5","promptCache":{"short":300,"long":3600,"minTokens":1024}},{"id":"claude-haiku-4-5","promptCache":{"short":300,"long":3600,"minTokens":4096}},{"id":"claude-opus-4-5","promptCache":{"short":300,"long":3600,"minTokens":4096}}]}',
10994
11120
  chatgpt: '{"version":1,"provider":"chatgpt","modelsDev":false,"models":[]}',
10995
11121
  dashscope: '{"version":1,"provider":"dashscope","modelsDev":"alibaba-cn","models":[{"id":"qwen3.8-max","thinkingLevelMap":{"off":null,"minimal":null,"low":"low","medium":"medium","high":null,"xhigh":"xhigh"},"compat":{"supportsReasoningEffort":true}},{"id":"qwen3.8-flash","thinkingLevelMap":{"off":null,"minimal":null,"low":"low","medium":"medium","high":null,"xhigh":"xhigh"},"compat":{"supportsReasoningEffort":true}},{"id":"qwen3.7-max","maxTokens":131072,"compat":{"supportsReasoningEffort":false}},{"id":"qwen3.7-plus","maxTokens":65536,"compat":{"supportsReasoningEffort":false}},{"id":"qwen3.6-plus","compat":{"supportsReasoningEffort":false}},{"id":"qwen3.6-flash","compat":{"supportsReasoningEffort":false}}]}',
10996
- deepseek: '{"version":1,"provider":"deepseek","modelsDev":"deepseek","models":[{"id":"deepseek-v4-pro","maxTokens":384000,"cost":{"input":1.32,"output":3.96,"cacheRead":0.044},"thinkingLevelMap":{"minimal":null,"low":null,"medium":null,"high":"high"},"_reason":"按官方价格页的高峰价(UTC 工作日 01–04、06–10 时)计,作上界;平峰为半价。快照价是平峰价或旧价"},{"id":"deepseek-flash","maxTokens":384000,"cost":{"input":0.3,"output":1.2,"cacheRead":0.006},"thinkingLevelMap":{"minimal":null,"low":"low","medium":null,"high":"high"},"_reason":"按官方价格页的高峰价(UTC 工作日 01–04、06–10 时)计,作上界;平峰为半价。快照价是平峰价或旧价"}]}',
11122
+ deepseek: '{"version":1,"provider":"deepseek","modelsDev":"deepseek","models":[{"id":"deepseek-v4-pro","maxTokens":384000,"cost":{"input":1.32,"output":3.96,"cacheRead":0.044},"thinkingLevelMap":{"minimal":null,"low":null,"medium":null,"high":"high"},"_reason":"按官方价格页的高峰价(UTC 工作日 01–04、06–10 时)计,作上界;平峰为半价。快照价是平峰价或旧价","promptCache":{"short":3600,"minTokens":2048}},{"id":"deepseek-flash","maxTokens":384000,"cost":{"input":0.3,"output":1.2,"cacheRead":0.006},"thinkingLevelMap":{"minimal":null,"low":"low","medium":null,"high":"high"},"_reason":"按官方价格页的高峰价(UTC 工作日 01–04、06–10 时)计,作上界;平峰为半价。快照价是平峰价或旧价","promptCache":{"short":3600,"minTokens":2048}}]}',
10997
11123
  google: '{"version":1,"provider":"google","modelsDev":"google","models":[{"id":"gemini-3.8-flash","thinkingLevelMap":{"off":null,"minimal":null,"low":"low","medium":"medium","high":"high","xhigh":null}},{"id":"gemini-3.7-flash","thinkingLevelMap":{"off":null,"minimal":null,"low":"low","medium":"medium","high":"high","xhigh":null}},{"id":"gemini-3.5-flash","thinkingLevelMap":{"off":null,"minimal":"minimal","low":"low","medium":"medium","high":"high","xhigh":null}},{"id":"gemini-3.5-flash-lite","thinkingLevelMap":{"off":null,"minimal":"minimal","low":"low","medium":"medium","high":"high","xhigh":null}},{"id":"gemini-3.1-pro-preview","thinkingLevelMap":{"off":null,"minimal":null,"low":"low","medium":"medium","high":"high","xhigh":null}},{"id":"gemini-3-flash-preview","thinkingLevelMap":{"off":null,"minimal":"minimal","low":"low","medium":"medium","high":"high","xhigh":null}},{"id":"gemini-2.5-pro","thinkingLevelMap":{"off":null,"minimal":128,"low":2048,"medium":8192,"high":32768}},{"id":"gemini-2.5-flash","thinkingLevelMap":{"minimal":128,"low":2048,"medium":8192,"high":24576}},{"id":"gemini-2.5-flash-lite","thinkingLevelMap":{"minimal":512,"low":2048,"medium":8192,"high":24576}}]}',
10998
11124
  groq: '{"version":1,"provider":"groq","modelsDev":"groq","models":[{"id":"openai/gpt-oss-120b","thinkingLevelMap":{"off":null,"minimal":null,"low":"low","medium":"medium","high":"high","xhigh":null}},{"id":"openai/gpt-oss-20b","thinkingLevelMap":{"off":null,"minimal":null,"low":"low","medium":"medium","high":"high","xhigh":null}},{"id":"openai/gpt-oss-safeguard-20b","thinkingLevelMap":{"off":null,"minimal":null,"low":"low","medium":"medium","high":"high","xhigh":null}},{"id":"qwen/qwen3.8-27b","thinkingLevelMap":{"off":"none","minimal":null,"low":"low","medium":"medium","high":"high","xhigh":null}},{"id":"qwen/qwen3.6-27b","thinkingLevelMap":{"off":"none","minimal":null,"low":null,"medium":null,"high":"default","xhigh":null}},{"id":"llama-3.3-70b-versatile"},{"id":"llama-3.1-8b-instant"}]}',
10999
11125
  lmstudio: '{"version":1,"provider":"lmstudio","modelsDev":false,"models":[]}',
@@ -12532,14 +12658,48 @@ function allowsImages(options) {
12532
12658
  return options?.model === void 0 || options.model.input.includes("image");
12533
12659
  }
12534
12660
  function normalizeContext(context, options) {
12535
- const state = new SystemState();
12661
+ const head = new SystemState();
12662
+ const tools = new SystemState();
12536
12663
  const messages = [];
12537
12664
  const allowImages = allowsImages(options);
12665
+ const fold = removesToolsMidway(context.messages);
12666
+ let pending = [];
12667
+ const flush = () => {
12668
+ if (pending.length === 0) return;
12669
+ messages.push({ role: "user", content: systemReminderText(pending), timestamp: 0 });
12670
+ pending = [];
12671
+ };
12538
12672
  for (const message of context.messages) {
12539
- if (message.role === "system") state.apply(message);
12540
- else messages.push(filterMessage(message, allowImages));
12673
+ if (message.role !== "system") {
12674
+ flush();
12675
+ messages.push(filterMessage(message, allowImages));
12676
+ continue;
12677
+ }
12678
+ tools.apply(message);
12679
+ if (fold || messages.length === 0) {
12680
+ head.apply(message);
12681
+ continue;
12682
+ }
12683
+ const text = renderSystemUpdate(message);
12684
+ if (text.length > 0) pending.push(text);
12541
12685
  }
12542
- return { ...state.snapshot(), messages };
12686
+ flush();
12687
+ return { ...head.snapshot(), tools: tools.snapshot().tools, messages };
12688
+ }
12689
+ function removesToolsMidway(transcript) {
12690
+ let started = false;
12691
+ for (const message of transcript) {
12692
+ if (message.role !== "system") started = true;
12693
+ else if (started && (message.toolsRemoved?.length ?? 0) > 0) return true;
12694
+ }
12695
+ return false;
12696
+ }
12697
+ function systemReminderText(updates) {
12698
+ return `<system-reminder>
12699
+ ${updates.join("\n\n")}
12700
+
12701
+ These updates replace the earlier versions of those system prompt sections.
12702
+ </system-reminder>`;
12543
12703
  }
12544
12704
  function renderSystemUpdate(message) {
12545
12705
  const parts = [];
@@ -21588,8 +21748,10 @@ var init_session_cache = __esm({
21588
21748
  return stats;
21589
21749
  }
21590
21750
  /**
21591
- * [W5-H1] 缓存是否已冷(wave5 §8.2 C3):上一次请求(含保温)距今超过 TTL(目录无承诺时按隐式
21592
- * 10 分钟)。只对 `reported` 端点判断;`silent` / `unknown` 返回 false,档一退化为只按阈值。
21751
+ * [W5-H1] 缓存是否已冷(wave5 §8.2 C3):上一次请求(含保温)距今超过目录承诺的 TTL。
21752
+ * 目录没有承诺 TTL 时返回 false:隐式缓存的寿命可能远长于 10 分钟,按猜测的寿命提前裁剪会改写
21753
+ * 仍然有效的缓存(隐式 600 s 只用于未命中归因)。只对 `reported` 端点判断;`silent` / `unknown`
21754
+ * 返回 false,档一退化为只按阈值。
21593
21755
  */
21594
21756
  isCold() {
21595
21757
  const prev = this.prev;
@@ -21598,7 +21760,7 @@ var init_session_cache = __esm({
21598
21760
  const endpoint = endpointKey({ model, baseUrl: model.baseUrl ?? "" });
21599
21761
  if (this.tracker.get(endpoint, model.compat?.cacheReporting) !== "reported") return false;
21600
21762
  const ttl = cacheTtlMs(this.modelOf(prev), prev.options.cacheRetention);
21601
- return this.now() - prev.at > (ttl ?? IMPLICIT_CACHE_TTL_MS);
21763
+ return ttl !== void 0 && this.now() - prev.at > ttl;
21602
21764
  }
21603
21765
  /**
21604
21766
  * 摘要续写的前缀(第三波 §1.8):当前转录按回合同样的方式转换,且上一次 turn 请求的消息
@@ -21888,6 +22050,30 @@ var init_breaker = __esm({
21888
22050
  }
21889
22051
  });
21890
22052
 
22053
+ // src/compaction/prefix-estimate.ts
22054
+ function estimatePrefixTokens(sections, tools) {
22055
+ const message = { role: "system", sections: { ...sections }, timestamp: 0 };
22056
+ if (tools.length > 0) message.toolsAdded = [...tools];
22057
+ return estimateMessageTokens(message);
22058
+ }
22059
+ function withPrefixBaseline(estimate, hasSystemMessage, prefix) {
22060
+ if (estimate.usageTokens > 0) return { ...estimate, source: "usage" };
22061
+ if (hasSystemMessage) return { ...estimate, source: "estimate" };
22062
+ const base4 = prefix();
22063
+ return {
22064
+ ...estimate,
22065
+ tokens: estimate.tokens + base4,
22066
+ trailingTokens: estimate.trailingTokens + base4,
22067
+ source: "prefix"
22068
+ };
22069
+ }
22070
+ var init_prefix_estimate = __esm({
22071
+ "src/compaction/prefix-estimate.ts"() {
22072
+ "use strict";
22073
+ init_estimate();
22074
+ }
22075
+ });
22076
+
21891
22077
  // src/compaction/protect.ts
21892
22078
  function unescapeXml(text) {
21893
22079
  return text.replace(/&lt;/g, "<").replace(/&gt;/g, ">").replace(/&quot;/g, '"').replace(/&apos;/g, "'").replace(/&amp;/g, "&");
@@ -22080,6 +22266,142 @@ Keep it concise; preserve exact file paths and error messages.`;
22080
22266
  }
22081
22267
  });
22082
22268
 
22269
+ // src/agent/prompt-rules.ts
22270
+ function baseRules(tools) {
22271
+ if (tools.length === 0) return [];
22272
+ const rules = [DESTRUCTIVE_RULE];
22273
+ const has = (name) => tools.some((tool) => tool.name === name);
22274
+ if (has("read")) rules.push(PARALLEL_READS_RULE);
22275
+ if (has("grep") && has("glob")) rules.push(LOCATE_RULE);
22276
+ return rules;
22277
+ }
22278
+ var DESTRUCTIVE_RULE, PARALLEL_READS_RULE, LOCATE_RULE;
22279
+ var init_prompt_rules = __esm({
22280
+ "src/agent/prompt-rules.ts"() {
22281
+ "use strict";
22282
+ DESTRUCTIVE_RULE = "Ask before destructive commands (rm -rf, git reset --hard, force push, deleting branches) unless the user requested them.";
22283
+ PARALLEL_READS_RULE = "Batch independent read-only tool calls into one turn.";
22284
+ LOCATE_RULE = "Locate code with grep/glob before reading; do not guess file paths.";
22285
+ }
22286
+ });
22287
+
22288
+ // src/agent/system-prompt.ts
22289
+ function firstLine(text) {
22290
+ return text.split("\n", 1)[0]?.trim() ?? "";
22291
+ }
22292
+ function toolDecl(tool) {
22293
+ return { name: tool.name, description: tool.description, parameters: tool.parameters };
22294
+ }
22295
+ function toolDecls(tools) {
22296
+ return [...tools].sort((a, b) => a.name.localeCompare(b.name)).map(toolDecl);
22297
+ }
22298
+ function assembleSections(input2) {
22299
+ const tools = [...input2.tools].sort((a, b) => a.name.localeCompare(b.name));
22300
+ const toolLines = tools.map((tool) => {
22301
+ const snippet = tool.promptSnippet ?? firstLine(tool.description);
22302
+ return snippet.startsWith(`${tool.name}:`) ? `- ${snippet}` : `- ${tool.name}: ${snippet}`;
22303
+ });
22304
+ const rules = [
22305
+ ...baseRules(tools),
22306
+ ...tools.flatMap((tool) => tool.promptGuidelines ?? []),
22307
+ ...input2.extraRules ?? []
22308
+ ];
22309
+ const projectContext = (input2.contextFiles ?? []).map((file) => `<file path="${escapeXml(file.path)}">
22310
+ ${file.content.trim()}
22311
+ </file>`).join("\n\n");
22312
+ let skills = input2.skillsIndex;
22313
+ if (skills === void 0 && input2.skills !== void 0 && input2.skills.length > 0) {
22314
+ skills = [
22315
+ "<available_skills>",
22316
+ ...input2.skills.map(
22317
+ (skill) => `<skill name="${escapeXml(skill.name)}" location="${escapeXml(skill.location)}">${escapeXml(skill.description)}</skill>`
22318
+ ),
22319
+ "</available_skills>"
22320
+ ].join("\n");
22321
+ }
22322
+ const host = (input2.hostInstructions ?? []).map((text) => text.trim()).filter(Boolean);
22323
+ const nonEmpty3 = (text) => text === void 0 || text.trim() === "" ? void 0 : text;
22324
+ return {
22325
+ preamble: input2.preamble ?? DEFAULT_PREAMBLE,
22326
+ tools: toolLines.length > 0 ? `Available tools:
22327
+ ${toolLines.join("\n")}` : void 0,
22328
+ rules: rules.length > 0 ? `Rules:
22329
+ ${rules.map((rule) => `- ${rule}`).join("\n")}` : void 0,
22330
+ project_context: nonEmpty3(projectContext),
22331
+ skills: nonEmpty3(skills),
22332
+ memory: nonEmpty3(input2.memory),
22333
+ hooks: nonEmpty3(input2.hookContext),
22334
+ cwd: `Current working directory: ${input2.cwd}`,
22335
+ host: host.length > 0 ? host.join("\n\n") : void 0,
22336
+ role: nonEmpty3(input2.role)
22337
+ };
22338
+ }
22339
+ function definedSections(sections) {
22340
+ const out = {};
22341
+ for (const name of SECTION_ORDER) {
22342
+ const text = sections[name];
22343
+ if (text !== void 0) out[name] = text;
22344
+ }
22345
+ return out;
22346
+ }
22347
+ function currentSystemState(messages) {
22348
+ return replaySystem(messages);
22349
+ }
22350
+ function sameTool(a, b) {
22351
+ return a.description === b.description && JSON.stringify(a.parameters) === JSON.stringify(b.parameters);
22352
+ }
22353
+ function diffSystem(current3, sections, tools, timestamp = Date.now()) {
22354
+ if (current3 === void 0) {
22355
+ const full = { role: "system", sections: { ...sections }, timestamp };
22356
+ if (tools.length > 0) full.toolsAdded = tools.map((tool) => ({ ...tool }));
22357
+ return full;
22358
+ }
22359
+ const patch = {};
22360
+ for (const [name, text] of Object.entries(sections)) {
22361
+ if (current3.sections[name] !== text) patch[name] = text;
22362
+ }
22363
+ for (const name of Object.keys(current3.sections)) {
22364
+ if (!(name in sections)) patch[name] = null;
22365
+ }
22366
+ const before2 = new Map(current3.tools.map((tool) => [tool.name, tool]));
22367
+ const after = new Map(tools.map((tool) => [tool.name, tool]));
22368
+ const toolsAdded = tools.filter((tool) => {
22369
+ const old = before2.get(tool.name);
22370
+ return old === void 0 || !sameTool(old, tool);
22371
+ });
22372
+ const toolsRemoved = [...before2.keys()].filter((name) => !after.has(name));
22373
+ if (Object.keys(patch).length === 0 && toolsAdded.length === 0 && toolsRemoved.length === 0) {
22374
+ return void 0;
22375
+ }
22376
+ const message = { role: "system", sections: patch, timestamp };
22377
+ if (toolsAdded.length > 0) message.toolsAdded = toolsAdded.map((tool) => ({ ...tool }));
22378
+ if (toolsRemoved.length > 0) message.toolsRemoved = toolsRemoved;
22379
+ return message;
22380
+ }
22381
+ var SECTION_ORDER, DEFAULT_PREAMBLE;
22382
+ var init_system_prompt = __esm({
22383
+ "src/agent/system-prompt.ts"() {
22384
+ "use strict";
22385
+ init_projection();
22386
+ init_index_prompt();
22387
+ init_prompt_rules();
22388
+ SECTION_ORDER = [
22389
+ "preamble",
22390
+ "tools",
22391
+ "rules",
22392
+ "project_context",
22393
+ "skills",
22394
+ // [W6-C0] 记忆索引(W6-M,docs/wave6-plan.md §3.4);未开启时为 undefined,字节不变
22395
+ "memory",
22396
+ "hooks",
22397
+ "cwd",
22398
+ "host",
22399
+ "role"
22400
+ ];
22401
+ DEFAULT_PREAMBLE = "You are ama, a coding agent working in the user's project through tools. Read files before changing them, make focused changes, and verify your work. Be concise.";
22402
+ }
22403
+ });
22404
+
22083
22405
  // src/agent/session-compaction.ts
22084
22406
  var DEFAULT_COMPACTION_SETTINGS, KEEP_RECENT_MAX_RATIO, HOOK_CONTEXT_CUSTOM_TYPE, CompactionController;
22085
22407
  var init_session_compaction = __esm({
@@ -22088,12 +22410,14 @@ var init_session_compaction = __esm({
22088
22410
  init_errors();
22089
22411
  init_breaker();
22090
22412
  init_estimate();
22413
+ init_prefix_estimate();
22091
22414
  init_prune_tier();
22092
22415
  init_protect();
22093
22416
  init_post_compact();
22094
22417
  init_summarize_tier();
22095
22418
  init_branch_summary();
22096
22419
  init_projection();
22420
+ init_system_prompt();
22097
22421
  DEFAULT_COMPACTION_SETTINGS = {
22098
22422
  enabled: true,
22099
22423
  reserveTokens: 16384,
@@ -22133,6 +22457,39 @@ var init_session_compaction = __esm({
22133
22457
  const branch = this.core.manager.branch();
22134
22458
  return estimateProjectedTokens(buildProjection(branch).items, branch);
22135
22459
  }
22460
+ /**
22461
+ * 统计用的上下文量(`getStats`):投影估算标上来源;系统消息还没落盘时加上前缀基线(系统提示 +
22462
+ * 工具声明,只统计不写分支)。附自动压缩的两个阈值(自动压缩可用时)。压缩调度仍用 `estimate()`。
22463
+ */
22464
+ contextStats() {
22465
+ const branch = this.core.manager.branch();
22466
+ const items = buildProjection(branch).items;
22467
+ const sourced = withPrefixBaseline(
22468
+ estimateProjectedTokens(items, branch),
22469
+ items.some((item) => item.message.role === "system"),
22470
+ () => this.prefixTokens()
22471
+ );
22472
+ const detail = {
22473
+ source: sourced.source,
22474
+ usageTokens: sourced.usageTokens,
22475
+ trailingTokens: sourced.trailingTokens
22476
+ };
22477
+ const budget = this.budget();
22478
+ const policy = this.prunePolicy();
22479
+ if (this.breaker.autoEnabled && budget !== void 0 && policy !== void 0) {
22480
+ detail.autoCompactAt = budget;
22481
+ detail.pruneAt = Math.floor(policy.triggerTokens);
22482
+ }
22483
+ return { tokens: sourced.tokens, detail };
22484
+ }
22485
+ /** 将要发送的系统提示 + 工具声明的估算(与首条 system 消息同样装配,不落盘)。 */
22486
+ prefixTokens() {
22487
+ const core = this.core;
22488
+ const tools = core.activeToolNames().map((name) => core.activeTool(name)).filter((tool) => tool !== void 0);
22489
+ const system = core.childBase?.().system ?? core.options.system ?? {};
22490
+ const sections = definedSections(assembleSections({ ...system, tools, cwd: core.cwd }));
22491
+ return estimatePrefixTokens(sections, toolDecls(tools));
22492
+ }
22136
22493
  budget() {
22137
22494
  const window2 = this.core.model().contextWindow;
22138
22495
  return window2 === void 0 ? void 0 : Math.max(0, window2 - this.settings.reserveTokens);
@@ -22498,7 +22855,8 @@ function computeStats(input2) {
22498
22855
  addCost(entry.usage);
22499
22856
  }
22500
22857
  }
22501
- const contextPercent = input2.contextWindow === void 0 || input2.contextTokens === void 0 || input2.contextWindow <= 0 ? void 0 : Math.min(100, Math.round(input2.contextTokens / input2.contextWindow * 1e3) / 10);
22858
+ const contextTokens = input2.context?.tokens ?? input2.contextTokens;
22859
+ const contextPercent = input2.contextWindow === void 0 || contextTokens === void 0 || input2.contextWindow <= 0 ? void 0 : Math.min(100, Math.round(contextTokens / input2.contextWindow * 1e3) / 10);
22502
22860
  const stats = {
22503
22861
  sessionId: input2.sessionId,
22504
22862
  sessionFile: input2.sessionFile,
@@ -22508,10 +22866,11 @@ function computeStats(input2) {
22508
22866
  toolResults,
22509
22867
  tokens,
22510
22868
  cost: cost2,
22511
- contextTokens: input2.contextTokens,
22869
+ contextTokens,
22512
22870
  contextWindow: input2.contextWindow,
22513
22871
  contextPercent
22514
22872
  };
22873
+ if (input2.context !== void 0) stats.context = { ...input2.context.detail };
22515
22874
  const rate = cacheHitRate(tokens);
22516
22875
  if (rate !== void 0) stats.cacheHitRate = rate;
22517
22876
  if (input2.cache !== void 0) stats.cache = input2.cache;
@@ -24113,6 +24472,8 @@ function applyRunnerEvent(record3, event, sink) {
24113
24472
  case "usage":
24114
24473
  if (event.usage !== void 0) record3.info.usage = event.usage;
24115
24474
  if (event.unit === "usd" && event.amount !== void 0) record3.info.costUsd = event.amount;
24475
+ if (event.contextTokens !== void 0) record3.info.contextTokens = event.contextTokens;
24476
+ if (event.contextWindow !== void 0) record3.info.contextWindow = event.contextWindow;
24116
24477
  return;
24117
24478
  case "notice":
24118
24479
  sink.log(event.level, `[task ${taskId}] ${event.text}`);
@@ -24157,6 +24518,16 @@ function recordDisplay(record3, event, now) {
24157
24518
  return;
24158
24519
  }
24159
24520
  }
24521
+ function taskContextPercent(info2) {
24522
+ const { contextTokens: used, contextWindow: size } = info2;
24523
+ if (used === void 0 || size === void 0 || size <= 0) return void 0;
24524
+ return Math.min(100, Math.round(used / size * 1e3) / 10);
24525
+ }
24526
+ function formatTaskContext(info2) {
24527
+ const percent3 = taskContextPercent(info2);
24528
+ if (percent3 === void 0) return void 0;
24529
+ return `${percent3 >= 10 ? Math.round(percent3) : percent3.toFixed(1)}%`;
24530
+ }
24160
24531
  function startEvent(record3, handle) {
24161
24532
  const event = {
24162
24533
  type: "subagent_start",
@@ -25770,142 +26141,6 @@ var init_session_subagent = __esm({
25770
26141
  }
25771
26142
  });
25772
26143
 
25773
- // src/agent/prompt-rules.ts
25774
- function baseRules(tools) {
25775
- if (tools.length === 0) return [];
25776
- const rules = [DESTRUCTIVE_RULE];
25777
- const has = (name) => tools.some((tool) => tool.name === name);
25778
- if (has("read")) rules.push(PARALLEL_READS_RULE);
25779
- if (has("grep") && has("glob")) rules.push(LOCATE_RULE);
25780
- return rules;
25781
- }
25782
- var DESTRUCTIVE_RULE, PARALLEL_READS_RULE, LOCATE_RULE;
25783
- var init_prompt_rules = __esm({
25784
- "src/agent/prompt-rules.ts"() {
25785
- "use strict";
25786
- DESTRUCTIVE_RULE = "Ask before destructive commands (rm -rf, git reset --hard, force push, deleting branches) unless the user requested them.";
25787
- PARALLEL_READS_RULE = "Batch independent read-only tool calls into one turn.";
25788
- LOCATE_RULE = "Locate code with grep/glob before reading; do not guess file paths.";
25789
- }
25790
- });
25791
-
25792
- // src/agent/system-prompt.ts
25793
- function firstLine(text) {
25794
- return text.split("\n", 1)[0]?.trim() ?? "";
25795
- }
25796
- function toolDecl(tool) {
25797
- return { name: tool.name, description: tool.description, parameters: tool.parameters };
25798
- }
25799
- function toolDecls(tools) {
25800
- return [...tools].sort((a, b) => a.name.localeCompare(b.name)).map(toolDecl);
25801
- }
25802
- function assembleSections(input2) {
25803
- const tools = [...input2.tools].sort((a, b) => a.name.localeCompare(b.name));
25804
- const toolLines = tools.map((tool) => {
25805
- const snippet = tool.promptSnippet ?? firstLine(tool.description);
25806
- return snippet.startsWith(`${tool.name}:`) ? `- ${snippet}` : `- ${tool.name}: ${snippet}`;
25807
- });
25808
- const rules = [
25809
- ...baseRules(tools),
25810
- ...tools.flatMap((tool) => tool.promptGuidelines ?? []),
25811
- ...input2.extraRules ?? []
25812
- ];
25813
- const projectContext = (input2.contextFiles ?? []).map((file) => `<file path="${escapeXml(file.path)}">
25814
- ${file.content.trim()}
25815
- </file>`).join("\n\n");
25816
- let skills = input2.skillsIndex;
25817
- if (skills === void 0 && input2.skills !== void 0 && input2.skills.length > 0) {
25818
- skills = [
25819
- "<available_skills>",
25820
- ...input2.skills.map(
25821
- (skill) => `<skill name="${escapeXml(skill.name)}" location="${escapeXml(skill.location)}">${escapeXml(skill.description)}</skill>`
25822
- ),
25823
- "</available_skills>"
25824
- ].join("\n");
25825
- }
25826
- const host = (input2.hostInstructions ?? []).map((text) => text.trim()).filter(Boolean);
25827
- const nonEmpty3 = (text) => text === void 0 || text.trim() === "" ? void 0 : text;
25828
- return {
25829
- preamble: input2.preamble ?? DEFAULT_PREAMBLE,
25830
- tools: toolLines.length > 0 ? `Available tools:
25831
- ${toolLines.join("\n")}` : void 0,
25832
- rules: rules.length > 0 ? `Rules:
25833
- ${rules.map((rule) => `- ${rule}`).join("\n")}` : void 0,
25834
- project_context: nonEmpty3(projectContext),
25835
- skills: nonEmpty3(skills),
25836
- memory: nonEmpty3(input2.memory),
25837
- hooks: nonEmpty3(input2.hookContext),
25838
- cwd: `Current working directory: ${input2.cwd}`,
25839
- host: host.length > 0 ? host.join("\n\n") : void 0,
25840
- role: nonEmpty3(input2.role)
25841
- };
25842
- }
25843
- function definedSections(sections) {
25844
- const out = {};
25845
- for (const name of SECTION_ORDER) {
25846
- const text = sections[name];
25847
- if (text !== void 0) out[name] = text;
25848
- }
25849
- return out;
25850
- }
25851
- function currentSystemState(messages) {
25852
- return replaySystem(messages);
25853
- }
25854
- function sameTool(a, b) {
25855
- return a.description === b.description && JSON.stringify(a.parameters) === JSON.stringify(b.parameters);
25856
- }
25857
- function diffSystem(current3, sections, tools, timestamp = Date.now()) {
25858
- if (current3 === void 0) {
25859
- const full = { role: "system", sections: { ...sections }, timestamp };
25860
- if (tools.length > 0) full.toolsAdded = tools.map((tool) => ({ ...tool }));
25861
- return full;
25862
- }
25863
- const patch = {};
25864
- for (const [name, text] of Object.entries(sections)) {
25865
- if (current3.sections[name] !== text) patch[name] = text;
25866
- }
25867
- for (const name of Object.keys(current3.sections)) {
25868
- if (!(name in sections)) patch[name] = null;
25869
- }
25870
- const before2 = new Map(current3.tools.map((tool) => [tool.name, tool]));
25871
- const after = new Map(tools.map((tool) => [tool.name, tool]));
25872
- const toolsAdded = tools.filter((tool) => {
25873
- const old = before2.get(tool.name);
25874
- return old === void 0 || !sameTool(old, tool);
25875
- });
25876
- const toolsRemoved = [...before2.keys()].filter((name) => !after.has(name));
25877
- if (Object.keys(patch).length === 0 && toolsAdded.length === 0 && toolsRemoved.length === 0) {
25878
- return void 0;
25879
- }
25880
- const message = { role: "system", sections: patch, timestamp };
25881
- if (toolsAdded.length > 0) message.toolsAdded = toolsAdded.map((tool) => ({ ...tool }));
25882
- if (toolsRemoved.length > 0) message.toolsRemoved = toolsRemoved;
25883
- return message;
25884
- }
25885
- var SECTION_ORDER, DEFAULT_PREAMBLE;
25886
- var init_system_prompt = __esm({
25887
- "src/agent/system-prompt.ts"() {
25888
- "use strict";
25889
- init_projection();
25890
- init_index_prompt();
25891
- init_prompt_rules();
25892
- SECTION_ORDER = [
25893
- "preamble",
25894
- "tools",
25895
- "rules",
25896
- "project_context",
25897
- "skills",
25898
- // [W6-C0] 记忆索引(W6-M,docs/wave6-plan.md §3.4);未开启时为 undefined,字节不变
25899
- "memory",
25900
- "hooks",
25901
- "cwd",
25902
- "host",
25903
- "role"
25904
- ];
25905
- DEFAULT_PREAMBLE = "You are ama, a coding agent working in the user's project through tools. Read files before changing them, make focused changes, and verify your work. Be concise.";
25906
- }
25907
- });
25908
-
25909
26144
  // src/agent/session-sync.ts
25910
26145
  function persistMessage(core, message) {
25911
26146
  if (message.role === "custom") {
@@ -27805,15 +28040,15 @@ var init_session2 = __esm({
27805
28040
  return lastAssistantText(this.manager.branch());
27806
28041
  }
27807
28042
  getStats() {
27808
- const contextTokens = this.compaction.estimate().tokens;
28043
+ const context = this.compaction.contextStats();
27809
28044
  const contextWindow = this.settings.model.contextWindow;
27810
28045
  const stats = computeStats({
27811
28046
  sessionId: this.manager.id,
27812
28047
  sessionFile: this.manager.file(),
27813
28048
  branch: this.manager.branch(),
27814
- contextTokens,
28049
+ context,
27815
28050
  contextWindow,
27816
- cache: this.cache.stats({ tokens: contextTokens, window: contextWindow }),
28051
+ cache: this.cache.stats({ tokens: context.tokens, window: contextWindow }),
27817
28052
  quota: this.lastQuota
27818
28053
  });
27819
28054
  return this.extensions.contributeStats(stats);
@@ -33351,6 +33586,8 @@ var init_codex_app_server = __esm({
33351
33586
  tokensTotal;
33352
33587
  tokensAtStart;
33353
33588
  contextWindow;
33589
+ /** 最近一次请求的总量(`tokenUsage.last.totalTokens`)= 线程当前的上下文占用。 */
33590
+ contextTokens;
33354
33591
  closed = false;
33355
33592
  interruptRequested = false;
33356
33593
  get sessionId() {
@@ -33490,6 +33727,7 @@ var init_codex_app_server = __esm({
33490
33727
  input: end.inputTokens - (start?.inputTokens ?? 0),
33491
33728
  output: end.outputTokens - (start?.outputTokens ?? 0),
33492
33729
  cacheRead: end.cachedInputTokens - (start?.cachedInputTokens ?? 0),
33730
+ ...this.contextTokens !== void 0 ? { contextTokens: this.contextTokens } : {},
33493
33731
  ...this.contextWindow !== void 0 ? { contextWindow: this.contextWindow } : {}
33494
33732
  });
33495
33733
  }
@@ -33513,6 +33751,8 @@ var init_codex_app_server = __esm({
33513
33751
  const usage = params["tokenUsage"] ?? {};
33514
33752
  const total = readTokens(usage["total"]);
33515
33753
  if (total !== void 0) this.tokensTotal = total;
33754
+ const last = readTokens(usage["last"]);
33755
+ if (last !== void 0 && last.totalTokens > 0) this.contextTokens = last.totalTokens;
33516
33756
  if (typeof usage["modelContextWindow"] === "number")
33517
33757
  this.contextWindow = usage["modelContextWindow"];
33518
33758
  return;
@@ -34431,6 +34671,8 @@ var init_runner2 = __esm({
34431
34671
  agent;
34432
34672
  /** [W6-C0] 本回合工具的骨架(id → 种类 / 状态 / 起止),回合结束写 `turn_trace`。 */
34433
34673
  turnTools = /* @__PURE__ */ new Map();
34674
+ /** 外部 Agent 最近报告的上下文占用与窗口(只记数字,跨回合保留)。 */
34675
+ context = {};
34434
34676
  get id() {
34435
34677
  return this.sessionId;
34436
34678
  }
@@ -34538,6 +34780,7 @@ var init_runner2 = __esm({
34538
34780
  this.emit({ type: "notice", level: event.level, text: event.text });
34539
34781
  return;
34540
34782
  case "usage":
34783
+ this.noteContext(event.contextTokens, event.contextWindow);
34541
34784
  if (event.costUsd !== void 0) {
34542
34785
  turnCost.usd = event.costUsd;
34543
34786
  const budget = this.request.budgetUsd;
@@ -34557,6 +34800,16 @@ var init_runner2 = __esm({
34557
34800
  return;
34558
34801
  }
34559
34802
  }
34803
+ /** 上下文占用或窗口变了:记下并单独发一条只带它们的 usage(任务记录 / Agent 栏即时更新)。 */
34804
+ noteContext(tokens, window2) {
34805
+ const next = { ...this.context };
34806
+ if (tokens !== void 0) next.contextTokens = tokens;
34807
+ if (window2 !== void 0) next.contextWindow = window2;
34808
+ if (next.contextTokens === this.context.contextTokens && next.contextWindow === this.context.contextWindow)
34809
+ return;
34810
+ this.context = next;
34811
+ this.emit({ type: "usage", ...next });
34812
+ }
34560
34813
  now() {
34561
34814
  return (this.deps.now ?? Date.now)();
34562
34815
  }
@@ -34645,7 +34898,8 @@ var init_runner2 = __esm({
34645
34898
  sessionId: this.sessionId,
34646
34899
  unit,
34647
34900
  amount,
34648
- ...usage.totalTokens > 0 ? { tokens: usage.totalTokens } : {}
34901
+ ...usage.totalTokens > 0 ? { tokens: usage.totalTokens } : {},
34902
+ ...this.context
34649
34903
  });
34650
34904
  this.emit({ type: "usage", usage, unit, amount });
34651
34905
  }
@@ -34886,6 +35140,8 @@ function aggregateExternal(entries) {
34886
35140
  }
34887
35141
  row3.amount += u.amount;
34888
35142
  if (typeof u.tokens === "number") row3.tokens = (row3.tokens ?? 0) + u.tokens;
35143
+ if (typeof u.contextTokens === "number") row3.contextTokens = u.contextTokens;
35144
+ if (typeof u.contextWindow === "number") row3.contextWindow = u.contextWindow;
34889
35145
  }
34890
35146
  return { byAgent };
34891
35147
  }
@@ -35890,11 +36146,17 @@ var init_acp_events = __esm({
35890
36146
  usedWireIds = /* @__PURE__ */ new Set();
35891
36147
  /** 上次 `session_info_update` 带出的标题。 */
35892
36148
  lastTitle = null;
35893
- /** 会话 new / load / resume 的响应发出后由服务端调:`available_commands_update` 与 `config_option_update`。 */
36149
+ /** 上次 `usage_update` 的内容(同值不重发)。 */
36150
+ lastUsage;
36151
+ /**
36152
+ * 会话 new / load / resume 的响应发出后由服务端调:`available_commands_update`、`config_option_update`
36153
+ * 与 `usage_update`(客户端可能刚重建线程,用量不去重,照发一次)。
36154
+ */
35894
36155
  announce() {
35895
36156
  const { configOptions, commands: commands2 } = this.extras();
35896
36157
  this.emit({ sessionUpdate: "available_commands_update", availableCommands: commands2 });
35897
36158
  this.emit({ sessionUpdate: "config_option_update", configOptions });
36159
+ this.emitUsage(true);
35898
36160
  }
35899
36161
  /** 回合结束后由服务端调:`session_info_update`(标题与上次不同才带 title)。 */
35900
36162
  emitSessionInfo(title, updatedAt) {
@@ -36009,6 +36271,9 @@ var init_acp_events = __esm({
36009
36271
  }))
36010
36272
  });
36011
36273
  return;
36274
+ case "message_end":
36275
+ if ("role" in event.message && event.message.role === "assistant") this.emitUsage();
36276
+ return;
36012
36277
  case "turn_end":
36013
36278
  this.emitUsage();
36014
36279
  return;
@@ -36025,20 +36290,29 @@ var init_acp_events = __esm({
36025
36290
  sessionUpdate: "config_option_update",
36026
36291
  configOptions: this.extras().configOptions
36027
36292
  });
36293
+ if (event.type === "model_changed") this.emitUsage(true);
36028
36294
  return;
36029
36295
  default:
36030
36296
  return;
36031
36297
  }
36032
36298
  }
36033
- emitUsage() {
36299
+ /**
36300
+ * 发一条 `usage_update`:上下文用量、窗口与会话累计美元。窗口未知时不发(`size` 必填);
36301
+ * 与上次内容相同时不发,`force` 时照发。
36302
+ */
36303
+ emitUsage(force = false) {
36034
36304
  const stats = this.session().getStats();
36035
36305
  if (stats.contextWindow === void 0) return;
36036
- this.emit({
36306
+ const update = {
36037
36307
  sessionUpdate: "usage_update",
36038
36308
  used: stats.contextTokens ?? 0,
36039
36309
  size: stats.contextWindow,
36040
36310
  ...stats.cost !== void 0 ? { cost: { amount: stats.cost, currency: "USD" } } : {}
36041
- });
36311
+ };
36312
+ const key = JSON.stringify(update);
36313
+ if (!force && key === this.lastUsage) return;
36314
+ this.lastUsage = key;
36315
+ this.emit(update);
36042
36316
  }
36043
36317
  /** `session/load`:按消息回放历史(工具结果带前 4 KB 文本;diff 不落盘,回放没有)。 */
36044
36318
  replay(messages) {
@@ -41791,7 +42065,7 @@ var init_meter = __esm({
41791
42065
  render(width2) {
41792
42066
  const { theme, label: label2, cells = 10 } = this.options;
41793
42067
  const ratio = this.value === void 0 ? void 0 : Math.min(1, Math.max(0, this.value));
41794
- const percent3 = ratio === void 0 ? "—" : `${Math.round(ratio * 100)}%`;
42068
+ const percent3 = ratio === void 0 ? "—" : this.options.percent?.(ratio) ?? `${Math.round(ratio * 100)}%`;
41795
42069
  const color = (c, text) => theme ? theme.fg(c, text) : text;
41796
42070
  const head = label2 === void 0 || label2 === "" ? "" : `${label2} `;
41797
42071
  const filled = ratio === void 0 ? 0 : Math.round(ratio * cells);
@@ -42223,6 +42497,7 @@ var init_key_docs = __esm({
42223
42497
  animation: true,
42224
42498
  restoreOnCancel: true,
42225
42499
  enterWhileRunning: "queue",
42500
+ programStatus: "auto",
42226
42501
  language: "auto"
42227
42502
  },
42228
42503
  // [W6-C0] 第六波键的缺省(行为由各批次实现)
@@ -42628,6 +42903,7 @@ function buildBaseSchema() {
42628
42903
  animation: bool(),
42629
42904
  restoreOnCancel: bool(),
42630
42905
  enterWhileRunning: oneOf(["queue", "interrupt"]),
42906
+ programStatus: oneOf(["auto", "on", "off"]),
42631
42907
  statusLine: oneOf(STATUS_LINE_MODES),
42632
42908
  language: oneOf(LANGUAGE_SETTINGS),
42633
42909
  replyLanguage: str3(),
@@ -45321,12 +45597,12 @@ function handleChunk2(chunk, state, tracker) {
45321
45597
  }
45322
45598
  const usage = obj6(chunk["usageMetadata"]);
45323
45599
  if (usage) output.usage = parseGoogleUsage(usage);
45324
- const blocked = str7(obj6(chunk["promptFeedback"])?.["blockReason"]);
45600
+ const blocked2 = str7(obj6(chunk["promptFeedback"])?.["blockReason"]);
45325
45601
  const candidates2 = Array.isArray(chunk["candidates"]) ? chunk["candidates"] : [];
45326
45602
  const candidate = obj6(candidates2[0]);
45327
- if (!candidate && blocked) {
45328
- output.rawStopReason = blocked;
45329
- throw new ProviderStopError(`Prompt blocked: ${blocked}`);
45603
+ if (!candidate && blocked2) {
45604
+ output.rawStopReason = blocked2;
45605
+ throw new ProviderStopError(`Prompt blocked: ${blocked2}`);
45330
45606
  }
45331
45607
  const parts = obj6(candidate?.["content"])?.["parts"];
45332
45608
  if (Array.isArray(parts)) {
@@ -46281,6 +46557,7 @@ var init_settings_registry = __esm({
46281
46557
  row2("ui.statusLine", "ui", "now", "any"),
46282
46558
  row2("ui.restoreOnCancel", "ui", "nextSession", "any"),
46283
46559
  row2("ui.enterWhileRunning", "ui", "now", "any"),
46560
+ row2("ui.programStatus", "ui", "restart", "any"),
46284
46561
  row2("ui.quietStartup", "ui", "restart", "any"),
46285
46562
  row2("ui.ascii", "ui", "restart", "any", { envOverride: "AMA_ASCII" }),
46286
46563
  row2("ui.language", "ui", "restart", "any", { envOverride: "AMA_LANG" }),
@@ -47589,6 +47866,17 @@ function formatTokenCount(count2) {
47589
47866
  if (count2 < 1e6) return `${Math.round(count2 / 1e3)}k`;
47590
47867
  return `${trim((count2 / 1e6).toFixed(2))}M`;
47591
47868
  }
47869
+ function contextEstimated(stats) {
47870
+ const source = stats.context?.source;
47871
+ return source !== void 0 && source !== "usage";
47872
+ }
47873
+ function autoCompactText(stats) {
47874
+ const m = msg().report.session;
47875
+ if (stats.context === void 0 || stats.contextWindow === void 0) return void 0;
47876
+ const at = stats.context.autoCompactAt;
47877
+ if (at === void 0) return m.autoCompactOff;
47878
+ return m.toAutoCompact(formatTokenCount(Math.max(0, at - (stats.contextTokens ?? 0))));
47879
+ }
47592
47880
  function formatUsd2(cost2) {
47593
47881
  if (cost2 === void 0) return "$?";
47594
47882
  return cost2 >= 0.01 || cost2 === 0 ? `$${cost2.toFixed(2)}` : `$${cost2.toFixed(3)}`;
@@ -47825,11 +48113,14 @@ function describeSession(session, now = Date.now()) {
47825
48113
  },
47826
48114
  {
47827
48115
  key: m.keyContext,
47828
- value: m.context(
47829
- String(stats.contextTokens ?? "?"),
47830
- String(stats.contextWindow ?? "?"),
47831
- percent3
47832
- )
48116
+ value: [
48117
+ m.context(
48118
+ `${contextEstimated(stats) ? "≈" : ""}${stats.contextTokens ?? "?"}`,
48119
+ String(stats.contextWindow ?? "?"),
48120
+ percent3
48121
+ ),
48122
+ autoCompactText(stats)
48123
+ ].filter((part) => part !== void 0).join(" · ")
47833
48124
  }
47834
48125
  ];
47835
48126
  const tasks = taskStatsText(session);
@@ -47998,6 +48289,111 @@ var init_status_quota = __esm({
47998
48289
  }
47999
48290
  });
48000
48291
 
48292
+ // src/modes/interactive/status-ctx.ts
48293
+ function ctxPercentText(percent3, decimal) {
48294
+ return decimal || percent3 > 0 && percent3 < 1 ? `${percent3.toFixed(1)}%` : `${Math.round(percent3)}%`;
48295
+ }
48296
+ function ctxWarnAt(stats) {
48297
+ const at = stats.context?.pruneAt;
48298
+ const window2 = stats.contextWindow;
48299
+ return at === void 0 || window2 === void 0 || window2 <= 0 ? 0.7 : Math.min(0.9, at / window2);
48300
+ }
48301
+ function ctxApprox(stats, ascii = false) {
48302
+ return contextEstimated(stats) ? ascii ? "~" : "≈" : "";
48303
+ }
48304
+ function compactCtxText(ctx, width2, theme) {
48305
+ const { percent: percent3 } = ctx;
48306
+ if (percent3 === void 0) return theme.fg("dim", "ctx ?");
48307
+ if (width2 >= METER_WIDTH) {
48308
+ const meter = new Meter(percent3 / 100, {
48309
+ label: theme.fg("dim", "ctx"),
48310
+ theme,
48311
+ warnAt: ctx.warnAt,
48312
+ percent: (ratio) => ctx.approx + ctxPercentText(ratio * 100, false)
48313
+ });
48314
+ return meter.render(80)[0] ?? "";
48315
+ }
48316
+ const color = levelColor(percent3 / 100, { warnAt: ctx.warnAt });
48317
+ return theme.fg("dim", "ctx ") + theme.fg(color, ctx.approx + ctxPercentText(percent3, false));
48318
+ }
48319
+ function fullCtxParts(ctx, theme, p) {
48320
+ const dim = (text) => theme.fg("dim", text);
48321
+ const out = [];
48322
+ const add3 = (text, priority, reserve) => void out.push({
48323
+ text,
48324
+ priority,
48325
+ zone: "right",
48326
+ group: "ctx",
48327
+ ...reserve === void 0 ? {} : { reserve }
48328
+ });
48329
+ const { percent: percent3 } = ctx;
48330
+ if (percent3 === void 0) add3(dim("Ctx ?"), p.ctx, 10);
48331
+ else {
48332
+ const color = levelColor(percent3 / 100, { warnAt: ctx.warnAt });
48333
+ add3(dim("Ctx ") + theme.fg(color, ctx.approx + ctxPercentText(percent3, true)), p.ctx, 10);
48334
+ }
48335
+ if (ctx.tokens !== void 0) add3(dim(` ${formatTokens3(ctx.tokens)}`), p.ctxUsed, 5);
48336
+ if (ctx.tokens !== void 0 && ctx.window !== void 0)
48337
+ add3(dim(`/${formatTokens3(ctx.window)}`), p.ctxWindow, 5);
48338
+ if (ctx.auto) add3(dim(` ${msg().interactive.statusLine.autoCompact}`), p.ctxAuto);
48339
+ return out;
48340
+ }
48341
+ var LIVE_CTX_INTERVAL_MS, METER_WIDTH, LiveContext;
48342
+ var init_status_ctx = __esm({
48343
+ "src/modes/interactive/status-ctx.ts"() {
48344
+ "use strict";
48345
+ init_estimate();
48346
+ init_i18n();
48347
+ init_tui2();
48348
+ init_session_report();
48349
+ init_status_bar();
48350
+ LIVE_CTX_INTERVAL_MS = 500;
48351
+ METER_WIDTH = 110;
48352
+ LiveContext = class {
48353
+ constructor(now) {
48354
+ this.now = now;
48355
+ }
48356
+ now;
48357
+ tokens;
48358
+ at = Number.NEGATIVE_INFINITY;
48359
+ /** 记下 usage 给出的上下文量;距上次采样 < 500 ms 时不更新。返回是否更新了。 */
48360
+ note(usage) {
48361
+ if (usage === void 0) return false;
48362
+ const tokens = calculateContextTokens(usage);
48363
+ if (tokens <= 0) return false;
48364
+ const now = this.now();
48365
+ if (this.tokens !== void 0 && now - this.at < LIVE_CTX_INTERVAL_MS) return false;
48366
+ this.tokens = tokens;
48367
+ this.at = now;
48368
+ return true;
48369
+ }
48370
+ clear() {
48371
+ this.tokens = void 0;
48372
+ this.at = Number.NEGATIVE_INFINITY;
48373
+ }
48374
+ /** 统计值 → 显示值(有在途值时以它为准,不再是估算)。 */
48375
+ view(stats, ascii) {
48376
+ const view = {
48377
+ tokens: stats.contextTokens,
48378
+ window: stats.contextWindow,
48379
+ percent: stats.contextPercent,
48380
+ approx: ctxApprox(stats, ascii),
48381
+ warnAt: ctxWarnAt(stats),
48382
+ auto: stats.context?.autoCompactAt !== void 0
48383
+ };
48384
+ const window2 = stats.contextWindow;
48385
+ if (this.tokens !== void 0) {
48386
+ view.tokens = this.tokens;
48387
+ view.approx = "";
48388
+ if (window2 !== void 0 && window2 > 0)
48389
+ view.percent = Math.min(100, Math.round(this.tokens / window2 * 1e3) / 10);
48390
+ }
48391
+ return view;
48392
+ }
48393
+ };
48394
+ }
48395
+ });
48396
+
48001
48397
  // src/modes/interactive/status-bar.ts
48002
48398
  function formatTokens3(count2) {
48003
48399
  if (count2 < 1e3) return String(count2);
@@ -48021,6 +48417,16 @@ function cacheText(cache3, warm = "♨") {
48021
48417
  const value = cache3.reporting === "silent" ? msg().interactive.statusLine.cacheSilent : cache3.reporting === "reported" && rate !== void 0 ? `${Math.round(rate * 100)}%` : "—";
48022
48418
  return `cache ${value}${cache3.warming.state === "scheduled" ? ` ${warm}` : ""}`;
48023
48419
  }
48420
+ function tokensText(t, g) {
48421
+ const prompt = t.input + t.cacheRead + t.cacheWrite;
48422
+ const parts = [
48423
+ `${g.ascii ? "sum " : "Σ"}${g.arrowUp}${formatTokens3(prompt)}`,
48424
+ `${g.arrowDown}${formatTokens3(t.output)}`
48425
+ ];
48426
+ if (t.cacheRead > 0) parts.push(`R${formatTokens3(t.cacheRead)}`);
48427
+ if (t.cacheWrite > 0) parts.push(`W${formatTokens3(t.cacheWrite)}`);
48428
+ return parts.join(" ");
48429
+ }
48024
48430
  function statusCost(stats) {
48025
48431
  if (stats.cost === void 0) return void 0;
48026
48432
  let external = 0;
@@ -48085,7 +48491,7 @@ function abbreviateModel(ref, width2) {
48085
48491
  if (width2 < 48) out = out.replace(/-\d.*$/, "");
48086
48492
  return out;
48087
48493
  }
48088
- function usageItems(stats, queue, source, theme) {
48494
+ function usageItems(stats, queue, source, theme, cumulative = false) {
48089
48495
  const g = theme.glyphs;
48090
48496
  const dim = (text) => theme.fg("dim", text);
48091
48497
  const out = {};
@@ -48093,7 +48499,9 @@ function usageItems(stats, queue, source, theme) {
48093
48499
  const prompt = t.input + t.cacheRead + t.cacheWrite;
48094
48500
  const cache3 = stats.cache;
48095
48501
  if (prompt + t.output > 0) {
48096
- out.tokens = dim(`${g.arrowUp}${formatTokens3(prompt)} ${g.arrowDown}${formatTokens3(t.output)}`);
48502
+ out.tokens = dim(
48503
+ cumulative ? tokensText(t, g) : `${g.arrowUp}${formatTokens3(prompt)} ${g.arrowDown}${formatTokens3(t.output)}`
48504
+ );
48097
48505
  if (cache3 !== void 0) out.cache = dim(cacheText(cache3, g.warm));
48098
48506
  else if (stats.cacheHitRate !== void 0) {
48099
48507
  out.cache = dim(`cache ${Math.round(stats.cacheHitRate * 100)}%`);
@@ -48123,7 +48531,7 @@ function usageItems(stats, queue, source, theme) {
48123
48531
  }
48124
48532
  return out;
48125
48533
  }
48126
- var MIN_GAP, METER_WIDTH, COMPACT, FULL, FULL_HINT, FULL_STYLE, StatusBar;
48534
+ var MIN_GAP, COMPACT, FULL, FULL_HINT, FULL_STYLE, StatusBar;
48127
48535
  var init_status_bar = __esm({
48128
48536
  "src/modes/interactive/status-bar.ts"() {
48129
48537
  "use strict";
@@ -48131,9 +48539,9 @@ var init_status_bar = __esm({
48131
48539
  init_modes();
48132
48540
  init_channels();
48133
48541
  init_status_quota();
48542
+ init_status_ctx();
48134
48543
  init_tui2();
48135
48544
  MIN_GAP = 4;
48136
- METER_WIDTH = 110;
48137
48545
  COMPACT = {
48138
48546
  model: 0,
48139
48547
  ctx: 1,
@@ -48153,9 +48561,21 @@ var init_status_bar = __esm({
48153
48561
  hint: 16,
48154
48562
  quota: 17
48155
48563
  };
48156
- FULL = { model: 0, ctx: 1, cost: 2, duration: 3, branch: 4, dir: 5, diff: 6, thinking: 7 };
48564
+ FULL = {
48565
+ model: 0,
48566
+ ctx: 1,
48567
+ cost: 2,
48568
+ duration: 3,
48569
+ ctxUsed: 4,
48570
+ branch: 5,
48571
+ dir: 6,
48572
+ diff: 7,
48573
+ ctxWindow: 8,
48574
+ thinking: 9,
48575
+ ctxAuto: 13
48576
+ };
48157
48577
  FULL_HINT = 12;
48158
- FULL_STYLE = { sep: " | " };
48578
+ FULL_STYLE = { sep: " | ", groups: { ctx: { glue: "" } } };
48159
48579
  StatusBar = class {
48160
48580
  constructor(source, theme) {
48161
48581
  this.source = source;
@@ -48165,10 +48585,23 @@ var init_status_bar = __esm({
48165
48585
  theme;
48166
48586
  stats;
48167
48587
  queue = 0;
48588
+ /** 在途请求的上下文量(message_update 的 usage,≤ 2 Hz 采样);该消息结束后清掉。 */
48589
+ live = new LiveContext(() => this.source.now?.() ?? Date.now());
48168
48590
  /** 用量、模型等变化后调用(事件驱动;流式期间不必每帧算)。 */
48169
48591
  refresh() {
48170
48592
  this.stats = this.source.session().getStats();
48171
48593
  }
48594
+ /**
48595
+ * 流式中的助手消息带了 usage(有的协议在开头就给输入量):记下在途请求的上下文量,距上次采样
48596
+ * ≥ 500 ms 才更新(≤ 2 Hz)。返回是否更新了。
48597
+ */
48598
+ noteStreaming(usage) {
48599
+ return this.live.note(usage);
48600
+ }
48601
+ /** 助手消息结束:丢掉在途值,回到统计值。 */
48602
+ clearStreaming() {
48603
+ this.live.clear();
48604
+ }
48172
48605
  setQueue(steering, followUp) {
48173
48606
  this.queue = steering + followUp;
48174
48607
  }
@@ -48219,7 +48652,9 @@ var init_status_bar = __esm({
48219
48652
  if (usage.cache !== void 0) right(usage.cache, COMPACT.cache);
48220
48653
  if (usage.cost !== void 0) right(usage.cost, COMPACT.cost);
48221
48654
  if (usage.rebill !== void 0) right(usage.rebill, COMPACT.rebill);
48222
- right(this.ctxText(stats.contextPercent, width2, full), p.ctx, full ? { reserve: 10 } : {});
48655
+ const ctx = this.live.view(stats, g.ascii);
48656
+ if (full) fullCtxParts(ctx, theme, FULL).forEach((part) => parts.push(part));
48657
+ else right(compactCtxText(ctx, width2, theme), p.ctx);
48223
48658
  this.gitParts(p, full).forEach((part) => parts.push(part));
48224
48659
  if (full) {
48225
48660
  const cost2 = statusCost(stats);
@@ -48280,17 +48715,6 @@ var init_status_bar = __esm({
48280
48715
  }
48281
48716
  return out;
48282
48717
  }
48283
- ctxText(percent3, width2, decimal) {
48284
- const theme = this.theme;
48285
- if (percent3 === void 0) return theme.fg("dim", decimal ? "Ctx ?" : "ctx ?");
48286
- const color = levelColor(percent3 / 100);
48287
- if (!decimal && width2 >= METER_WIDTH) {
48288
- const meter = new Meter(percent3 / 100, { label: theme.fg("dim", "ctx"), theme });
48289
- return meter.render(80)[0] ?? "";
48290
- }
48291
- if (decimal) return theme.fg("dim", "Ctx ") + theme.fg(color, `${percent3.toFixed(1)}%`);
48292
- return theme.fg("dim", "ctx ") + theme.fg(color, `${Math.round(percent3)}%`);
48293
- }
48294
48718
  render(width2) {
48295
48719
  const style = this.layout() === "full" ? FULL_STYLE : {};
48296
48720
  return [layoutRow(this.parts(width2), width2, this.theme, style)];
@@ -48648,7 +49072,7 @@ function usageOf(node) {
48648
49072
  return void 0;
48649
49073
  }
48650
49074
  }
48651
- function tokensText(node, theme) {
49075
+ function tokensText2(node, theme) {
48652
49076
  const { arrowUp, arrowDown } = theme.glyphs;
48653
49077
  if (node.kind === "turn") {
48654
49078
  const u = node.usage;
@@ -48792,7 +49216,7 @@ function formatRow(row3, width2, ctx, selected = false) {
48792
49216
  if (width2 < NARROW_WIDTH) right = ` ${duration}`;
48793
49217
  else {
48794
49218
  const bar = barText(row3, barWidth(width2), theme);
48795
- const tokens = padToWidth(tokensText(row3.node, theme), TOKENS_WIDTH);
49219
+ const tokens = padToWidth(tokensText2(row3.node, theme), TOKENS_WIDTH);
48796
49220
  const cache3 = cacheText2(row3.node).padStart(CACHE_WIDTH);
48797
49221
  right = ` ${duration} ${bar} ${tokens} ${cache3}`;
48798
49222
  }
@@ -48843,7 +49267,7 @@ function formatTraceText(trace, ctx, rows = expandAll((expanded) => flattenTrace
48843
49267
  for (const row3 of rows) {
48844
49268
  const indent2 = " ".repeat(row3.depth);
48845
49269
  const mark = statusMark(row3.node, theme);
48846
- const cols = [durationText(row3.node, theme), tokensText(row3.node, theme), cacheText2(row3.node)].filter((c) => c !== "").join(" ");
49270
+ const cols = [durationText(row3.node, theme), tokensText2(row3.node, theme), cacheText2(row3.node)].filter((c) => c !== "").join(" ");
48847
49271
  const label2 = `${indent2}${mark === "" ? "" : `${mark} `}${rowLabel(row3, ctx)}`;
48848
49272
  lines2.push(cols === "" ? label2 : `${label2} ${cols}`);
48849
49273
  }
@@ -50921,6 +51345,7 @@ async function runPrintMode(runtime, context) {
50921
51345
  usage: stats.tokens,
50922
51346
  cost: stats.cost,
50923
51347
  cacheHitRate: stats.cacheHitRate,
51348
+ ...contextOf(stats),
50924
51349
  ...stats.cache !== void 0 ? { cache: stats.cache } : {},
50925
51350
  ...stats.tasks !== void 0 ? { tasks: stats.tasks } : {},
50926
51351
  ...denied.length > 0 ? { deniedTools: denied } : {},
@@ -51003,6 +51428,13 @@ function describeDenied(denied) {
51003
51428
  const tools = [...counts].map(([name, n]) => `${name} ×${n}`);
51004
51429
  return msg().print.print.denied(denied.length, tools, denied[0]?.reason ?? "");
51005
51430
  }
51431
+ function contextOf(stats) {
51432
+ const context = {};
51433
+ if (stats.contextTokens !== void 0) context["tokens"] = stats.contextTokens;
51434
+ if (stats.contextWindow !== void 0) context["window"] = stats.contextWindow;
51435
+ if (stats.contextPercent !== void 0) context["percent"] = stats.contextPercent;
51436
+ return Object.keys(context).length > 0 ? { context } : {};
51437
+ }
51006
51438
  var STDIN_WAIT_HINT_MS, STDIN_FIRST_BYTE_MS;
51007
51439
  var init_print_mode = __esm({
51008
51440
  "src/modes/print/print-mode.ts"() {
@@ -51630,6 +52062,8 @@ function taskFacts(task, now) {
51630
52062
  facts.push(
51631
52063
  `↑${formatTokenCount(usage.input + usage.cacheRead + usage.cacheWrite)} ↓${formatTokenCount(usage.output)}`
51632
52064
  );
52065
+ const context = formatTaskContext(task);
52066
+ if (context !== void 0) facts.push(msg().panels.tasks.context(context));
51633
52067
  if (task.costUsd !== void 0 && task.costUsd > 0) facts.push(formatUsd2(task.costUsd));
51634
52068
  return facts;
51635
52069
  }
@@ -51696,6 +52130,7 @@ var init_tasks_report = __esm({
51696
52130
  "src/modes/interactive/tasks-report.ts"() {
51697
52131
  "use strict";
51698
52132
  init_external();
52133
+ init_task_record();
51699
52134
  init_subagent_registry();
51700
52135
  init_i18n();
51701
52136
  init_session_report();
@@ -51756,6 +52191,514 @@ var init_task_background = __esm({
51756
52191
  }
51757
52192
  });
51758
52193
 
52194
+ // src/compaction/breakdown.ts
52195
+ function textPiece(category, name, text) {
52196
+ const count2 = countText(text, { wide: 0, narrow: 0 });
52197
+ return { category, name, wide: count2.wide, narrow: count2.narrow, fixed: 0 };
52198
+ }
52199
+ function imagePiece(name) {
52200
+ return { category: "images", name, wide: 0, narrow: 0, fixed: IMAGE_TOKENS };
52201
+ }
52202
+ function contentPieces(content, category, name, imageSource) {
52203
+ if (typeof content === "string") return [textPiece(category, name, content)];
52204
+ return content.map(
52205
+ (block) => block.type === "text" ? textPiece(category, name, block.text) : imagePiece(imageSource)
52206
+ );
52207
+ }
52208
+ function messagePieces(message) {
52209
+ switch (message.role) {
52210
+ case "system": {
52211
+ const pieces2 = Object.entries(message.sections).map(
52212
+ ([name, text]) => textPiece("system", name, text ?? "")
52213
+ );
52214
+ const tools = message.toolsAdded;
52215
+ if (tools !== void 0) {
52216
+ for (const tool of tools) pieces2.push(textPiece("tools", tool.name, JSON.stringify(tool)));
52217
+ pieces2.push({
52218
+ category: "tools",
52219
+ name: void 0,
52220
+ wide: 0,
52221
+ narrow: 2 + Math.max(0, tools.length - 1),
52222
+ fixed: 0
52223
+ });
52224
+ }
52225
+ return pieces2;
52226
+ }
52227
+ case "user":
52228
+ return contentPieces(message.content, "user", void 0, "user");
52229
+ case "toolResult":
52230
+ return contentPieces(message.content, "toolResults", message.toolName, "toolResult");
52231
+ case "custom":
52232
+ return contentPieces(message.content, "custom", message.customType, "custom");
52233
+ case "assistant":
52234
+ return message.content.map(
52235
+ (block) => block.type === "text" ? textPiece("assistant", void 0, block.text) : block.type === "thinking" ? textPiece("reasoning", void 0, block.thinking) : textPiece("toolCalls", block.name, block.name + JSON.stringify(block.arguments))
52236
+ );
52237
+ case "compactionSummary":
52238
+ return [textPiece("summaries", "compaction", message.summary)];
52239
+ case "branchSummary":
52240
+ return [textPiece("summaries", "branch", message.summary)];
52241
+ }
52242
+ }
52243
+ function allocate(pieces2, total) {
52244
+ const tokens = pieces2.map((p) => p.fixed + p.wide + Math.floor(p.narrow / CHARS_PER_TOKEN2));
52245
+ let left = total - tokens.reduce((sum, n) => sum + n, 0);
52246
+ const order = pieces2.map((p, i) => ({ i, rest: p.narrow % CHARS_PER_TOKEN2 })).filter((x) => x.rest > 0).sort((a, b) => b.rest - a.rest || a.i - b.i);
52247
+ for (const { i } of order) {
52248
+ if (left <= 0) break;
52249
+ tokens[i] = (tokens[i] ?? 0) + 1;
52250
+ left--;
52251
+ }
52252
+ if (left !== 0 && tokens.length > 0) {
52253
+ let max = 0;
52254
+ for (let i = 1; i < tokens.length; i++) if ((tokens[i] ?? 0) > (tokens[max] ?? 0)) max = i;
52255
+ tokens[max] = (tokens[max] ?? 0) + left;
52256
+ }
52257
+ return tokens;
52258
+ }
52259
+ function breakdownMessages(messages) {
52260
+ const totals2 = /* @__PURE__ */ new Map();
52261
+ const parts = /* @__PURE__ */ new Map();
52262
+ for (const category of BREAKDOWN_CATEGORIES) {
52263
+ totals2.set(category, { tokens: 0, count: 0 });
52264
+ parts.set(category, /* @__PURE__ */ new Map());
52265
+ }
52266
+ const toolResults = [];
52267
+ let total = 0;
52268
+ let hasSystem = false;
52269
+ for (const message of messages) {
52270
+ const messageTokens = estimateMessageTokens(message);
52271
+ total += messageTokens;
52272
+ if (message.role === "system") hasSystem = true;
52273
+ if (message.role === "toolResult") {
52274
+ toolResults.push({
52275
+ ordinal: toolResults.length + 1,
52276
+ toolName: message.toolName,
52277
+ tokens: messageTokens
52278
+ });
52279
+ }
52280
+ const pieces2 = messagePieces(message);
52281
+ const allocated = allocate(pieces2, messageTokens);
52282
+ pieces2.forEach((piece, i) => {
52283
+ const tokens = allocated[i] ?? 0;
52284
+ const entry = totals2.get(piece.category);
52285
+ if (entry === void 0) return;
52286
+ entry.tokens += tokens;
52287
+ if (piece.name !== void 0 || piece.category !== "tools") entry.count++;
52288
+ if (piece.name !== void 0) {
52289
+ const named = parts.get(piece.category);
52290
+ named?.set(piece.name, (named.get(piece.name) ?? 0) + tokens);
52291
+ }
52292
+ });
52293
+ }
52294
+ const entries = BREAKDOWN_CATEGORIES.map((category) => {
52295
+ const entry = totals2.get(category) ?? { tokens: 0, count: 0 };
52296
+ const named = [...parts.get(category) ?? /* @__PURE__ */ new Map()].map(([name, tokens]) => ({ name, tokens })).sort((a, b) => b.tokens - a.tokens || a.name.localeCompare(b.name));
52297
+ return { category, tokens: entry.tokens, count: entry.count, parts: named };
52298
+ });
52299
+ const prefixTokens = entries.filter((e) => e.category === "system" || e.category === "tools").reduce((sum, e) => sum + e.tokens, 0);
52300
+ return { entries, total, prefixTokens, hasSystem, toolResults };
52301
+ }
52302
+ function largestToolResults(breakdown, n) {
52303
+ return [...breakdown.toolResults].sort((a, b) => b.tokens - a.tokens || a.ordinal - b.ordinal).slice(0, Math.max(0, n));
52304
+ }
52305
+ var CHARS_PER_TOKEN2, BREAKDOWN_CATEGORIES;
52306
+ var init_breakdown = __esm({
52307
+ "src/compaction/breakdown.ts"() {
52308
+ "use strict";
52309
+ init_estimate();
52310
+ CHARS_PER_TOKEN2 = 4;
52311
+ BREAKDOWN_CATEGORIES = [
52312
+ "system",
52313
+ "tools",
52314
+ "user",
52315
+ "assistant",
52316
+ "reasoning",
52317
+ "toolCalls",
52318
+ "toolResults",
52319
+ "images",
52320
+ "summaries",
52321
+ "custom"
52322
+ ];
52323
+ }
52324
+ });
52325
+
52326
+ // src/cli/startup-screen.ts
52327
+ function startupScreenLevel(runtime) {
52328
+ return runtime.config.ui?.quietStartup ?? "normal";
52329
+ }
52330
+ function trustSourceLabel(source) {
52331
+ const labels2 = msg().cli.startupScreen.trustSource;
52332
+ return source === "trust-file" ? labels2.trustFile : labels2[source];
52333
+ }
52334
+ function tildePath(path, home) {
52335
+ if (home === void 0 || home === "") return path;
52336
+ const trimmed = /[\\/]$/.test(home) ? home.slice(0, -1) : home;
52337
+ if (path === trimmed) return "~";
52338
+ const next = path[trimmed.length];
52339
+ return path.startsWith(trimmed) && (next === "/" || next === "\\") ? `~${path.slice(trimmed.length)}` : path;
52340
+ }
52341
+ function startupInfo(runtime, home) {
52342
+ const { resources } = runtime;
52343
+ const info2 = {
52344
+ version: AMA_VERSION,
52345
+ model: formatModelRef(runtime.model),
52346
+ thinking: runtime.thinkingLevel,
52347
+ cwd: tildePath(runtime.paths.cwd, home),
52348
+ trusted: runtime.trust.trusted,
52349
+ trustSource: trustSourceLabel(runtime.trust.source),
52350
+ permissionMode: runtime.session.state.permissionMode,
52351
+ preset: runtime.config.tools?.preset ?? "default",
52352
+ codemode: effectiveCodemodeMode(runtime.config),
52353
+ contextFiles: resources.contextFiles.map((f) => (0, import_node_path93.basename)(f.path)),
52354
+ skills: resources.skills.filter((s2) => !isBuiltinSkill(s2)).length,
52355
+ prompts: resources.prompts.length,
52356
+ hooks: runtime.hooks.list().length,
52357
+ warnings: runtime.warnings.length
52358
+ };
52359
+ if (runtime.host !== void 0) info2.host = runtime.host.adapter.id;
52360
+ return info2;
52361
+ }
52362
+ var import_node_path93;
52363
+ var init_startup_screen = __esm({
52364
+ "src/cli/startup-screen.ts"() {
52365
+ "use strict";
52366
+ init_builtin3();
52367
+ import_node_path93 = require("node:path");
52368
+ init_channels();
52369
+ init_presets2();
52370
+ init_i18n();
52371
+ init_version();
52372
+ }
52373
+ });
52374
+
52375
+ // src/modes/interactive/panels.ts
52376
+ function keyValue(rows, theme, wrap2 = false) {
52377
+ return new KeyValue(rows, { theme, maxKeyRatio: 0.3, ...wrap2 ? { wrap: true } : {} });
52378
+ }
52379
+ function cacheSection(session, theme, now) {
52380
+ return [
52381
+ theme.bold(msg().panels.cache.title),
52382
+ new Indent(keyValue(cacheRows(session, now), theme), 2)
52383
+ ];
52384
+ }
52385
+ function sessionPanel(session, theme, options = {}) {
52386
+ const m = msg().panels.session;
52387
+ const now = options.now ?? Date.now();
52388
+ const state = session.state;
52389
+ const stats = session.getStats();
52390
+ const t = stats.tokens;
52391
+ const sep5 = theme.fg("dim", " · ");
52392
+ const model = state.model === void 0 ? "?" : `${state.model.provider}/${state.model.id}`;
52393
+ const ratio = stats.contextPercent === void 0 ? void 0 : stats.contextPercent / 100;
52394
+ const approx = contextEstimated(stats) ? "≈" : "";
52395
+ const meter = new Meter(ratio, {
52396
+ theme,
52397
+ warnAt: ctxWarnAt(stats),
52398
+ percent: (r) => approx + ctxPercentText(r * 100, false)
52399
+ }).render(40)[0] ?? "";
52400
+ const window2 = stats.contextTokens === void 0 && stats.contextWindow === void 0 ? "" : sep5 + `${stats.contextTokens === void 0 ? "?" : approx + formatTokenCount(stats.contextTokens)} / ${stats.contextWindow === void 0 ? "?" : formatTokenCount(stats.contextWindow)}`;
52401
+ const auto = autoCompactText(stats);
52402
+ const rows = [
52403
+ {
52404
+ key: m.model,
52405
+ value: theme.fg("accent", model) + sep5 + m.thinking(state.thinkingLevel) + sep5 + m.permission(permissionModeLabel(state.permissionMode))
52406
+ },
52407
+ {
52408
+ key: m.messages,
52409
+ value: [
52410
+ m.user(stats.userMessages),
52411
+ m.assistant(stats.assistantMessages),
52412
+ m.toolCalls(stats.toolCalls)
52413
+ ].join(sep5)
52414
+ },
52415
+ {
52416
+ key: m.usage,
52417
+ value: [
52418
+ m.input(formatTokenCount(t.input)),
52419
+ m.output(formatTokenCount(t.output)),
52420
+ m.cacheRead(formatTokenCount(t.cacheRead)),
52421
+ m.cacheWrite(formatTokenCount(t.cacheWrite)),
52422
+ formatUsd2(stats.cost)
52423
+ ].join(sep5)
52424
+ },
52425
+ { key: m.context, value: meter + window2 + (auto === void 0 ? "" : sep5 + auto) }
52426
+ ];
52427
+ const tasks = taskStatsText(session);
52428
+ if (tasks !== void 0) rows.push({ key: m.subagents, value: tasks });
52429
+ const external = externalRows(session);
52430
+ const externalSection = external.length > 0 ? ["", theme.bold(m.external), new Indent(keyValue(external, theme), 2)] : [];
52431
+ const file = state.sessionFile === void 0 ? m.notSaved : tildePath(state.sessionFile, options.home);
52432
+ const parts = [
52433
+ keyValue(rows, theme),
52434
+ "",
52435
+ ...cacheSection(session, theme, now),
52436
+ ...externalSection
52437
+ ];
52438
+ return new Card(new Stack(parts), {
52439
+ theme,
52440
+ title: m.title(state.sessionId.slice(0, 8)),
52441
+ subtitle: file
52442
+ });
52443
+ }
52444
+ function cachePanel(session, theme, now = Date.now()) {
52445
+ return new Card(keyValue(cacheRows(session, now), theme), {
52446
+ theme,
52447
+ title: msg().panels.cache.title
52448
+ });
52449
+ }
52450
+ function permissionsPanel(runtime, session, theme) {
52451
+ const m = msg().panels.permissions;
52452
+ const rules = runtime.permission.rules;
52453
+ const mode = session.state.permissionMode;
52454
+ const order = mode === "auto" ? m.orderAuto : mode === "allowlist" ? m.orderAllowlist : m.orderDefault;
52455
+ const effect = (decision) => theme.fg(DECISION_COLOR[decision] ?? "text", decision.padEnd(5));
52456
+ const parts = [
52457
+ keyValue(
52458
+ [
52459
+ {
52460
+ key: m.mode,
52461
+ value: m.modeValue(permissionModeLabel(mode), mode, (t) => theme.fg("dim", t))
52462
+ },
52463
+ { key: m.order, value: order }
52464
+ ],
52465
+ theme,
52466
+ true
52467
+ ),
52468
+ theme.bold(rules.length === 0 ? m.rulesNone : m.rules(rules.length))
52469
+ ];
52470
+ if (rules.length > 0) {
52471
+ parts.push(
52472
+ new Indent(
52473
+ keyValue(
52474
+ rules.map((r) => ({
52475
+ key: effect(r.effect),
52476
+ value: `${r.raw} ${theme.fg("dim", `[${r.source}]`)}`
52477
+ })),
52478
+ theme
52479
+ ),
52480
+ 2
52481
+ )
52482
+ );
52483
+ }
52484
+ const recent = runtime.permission.autoDecisions?.() ?? [];
52485
+ if (recent.length > 0) {
52486
+ parts.push(theme.bold(m.recentAuto(recent.length)));
52487
+ parts.push(
52488
+ new Indent(
52489
+ keyValue(
52490
+ recent.map((d) => ({
52491
+ key: autoLayerText(d.layer),
52492
+ value: `${effect(d.decision)} ${d.toolName} ${d.summary} — ${d.reason}` + (d.cached === true ? theme.fg("dim", m.cached) : "")
52493
+ })),
52494
+ theme
52495
+ ),
52496
+ 2
52497
+ )
52498
+ );
52499
+ }
52500
+ return new Card(new Stack(parts), { theme });
52501
+ }
52502
+ var Stack, Indent, DECISION_COLOR;
52503
+ var init_panels2 = __esm({
52504
+ "src/modes/interactive/panels.ts"() {
52505
+ "use strict";
52506
+ init_i18n();
52507
+ init_modes();
52508
+ init_tui2();
52509
+ init_status_ctx();
52510
+ init_session_report();
52511
+ init_startup_screen();
52512
+ Stack = class {
52513
+ constructor(parts) {
52514
+ this.parts = parts;
52515
+ }
52516
+ parts;
52517
+ render(width2) {
52518
+ return this.parts.flatMap((part) => typeof part === "string" ? [part] : part.render(width2));
52519
+ }
52520
+ invalidate() {
52521
+ for (const part of this.parts) if (typeof part !== "string") part.invalidate();
52522
+ }
52523
+ };
52524
+ Indent = class {
52525
+ constructor(child, columns) {
52526
+ this.child = child;
52527
+ this.columns = columns;
52528
+ }
52529
+ child;
52530
+ columns;
52531
+ render(width2) {
52532
+ const pad3 = " ".repeat(this.columns);
52533
+ return this.child.render(Math.max(1, width2 - this.columns)).map((line2) => line2 === "" ? "" : pad3 + line2);
52534
+ }
52535
+ invalidate() {
52536
+ this.child.invalidate();
52537
+ }
52538
+ };
52539
+ DECISION_COLOR = {
52540
+ allow: "success",
52541
+ deny: "error",
52542
+ ask: "warning"
52543
+ };
52544
+ }
52545
+ });
52546
+
52547
+ // src/modes/context-report.ts
52548
+ function contextSnapshot(session) {
52549
+ let messages = session.messages;
52550
+ let estimate;
52551
+ let compaction = {};
52552
+ if (session instanceof AgentSessionImpl) {
52553
+ const branch = session.manager.branch();
52554
+ const items = buildProjection(branch).items;
52555
+ messages = items.map((item) => item.message);
52556
+ estimate = estimateProjectedTokens(items, branch);
52557
+ compaction = session.options.compaction ?? {};
52558
+ } else estimate = estimateContextTokens(messages);
52559
+ const window2 = session.getStats().contextWindow;
52560
+ const reserve = compaction.reserveTokens ?? DEFAULT_COMPACTION_SETTINGS.reserveTokens;
52561
+ const budget = window2 === void 0 ? void 0 : Math.max(0, window2 - reserve);
52562
+ const percent3 = window2 === void 0 || window2 <= 0 ? void 0 : Math.min(100, Math.round(estimate.tokens / window2 * 1e3) / 10);
52563
+ return {
52564
+ breakdown: breakdownMessages(messages),
52565
+ estimate,
52566
+ window: window2,
52567
+ percent: percent3,
52568
+ autoCompaction: session.state.autoCompaction,
52569
+ compactAt: budget,
52570
+ pruneAt: budget === void 0 ? void 0 : prunePolicy(budget, compaction.prune).triggerTokens
52571
+ };
52572
+ }
52573
+ function headerRows(snap) {
52574
+ const m = msg().report.contextReport;
52575
+ const { estimate, window: window2 } = snap;
52576
+ const rows = [
52577
+ {
52578
+ key: m.keyUsed,
52579
+ value: m.used(
52580
+ formatTokenCount(estimate.tokens),
52581
+ window2 === void 0 ? "?" : formatTokenCount(window2),
52582
+ snap.percent === void 0 ? void 0 : `${snap.percent.toFixed(1)}%`
52583
+ )
52584
+ }
52585
+ ];
52586
+ if (window2 !== void 0)
52587
+ rows.push({
52588
+ key: m.keyLeft,
52589
+ value: m.left(formatTokenCount(Math.max(0, window2 - estimate.tokens)))
52590
+ });
52591
+ rows.push({
52592
+ key: m.keySource,
52593
+ value: estimate.lastUsageIndex === null ? m.sourceEstimate : m.sourceUsage(
52594
+ formatTokenCount(estimate.usageTokens),
52595
+ formatTokenCount(estimate.trailingTokens)
52596
+ )
52597
+ });
52598
+ let auto;
52599
+ if (!snap.autoCompaction) auto = m.autoCompactOff;
52600
+ else if (snap.compactAt === void 0) auto = m.windowUnknown;
52601
+ else if (estimate.tokens >= snap.compactAt)
52602
+ auto = m.autoCompactReached(formatTokenCount(snap.compactAt));
52603
+ else
52604
+ auto = m.autoCompact(
52605
+ formatTokenCount(snap.compactAt),
52606
+ formatTokenCount(snap.compactAt - estimate.tokens)
52607
+ );
52608
+ rows.push({ key: m.keyAutoCompact, value: auto });
52609
+ if (snap.autoCompaction && snap.pruneAt !== void 0)
52610
+ rows.push({ key: m.keyPrune, value: m.prune(formatTokenCount(Math.floor(snap.pruneAt))) });
52611
+ return rows;
52612
+ }
52613
+ function partName(entry, name) {
52614
+ const names = msg().report.contextReport.partNames;
52615
+ if (entry.category === "summaries" || entry.category === "images")
52616
+ return names[name] ?? name;
52617
+ return name;
52618
+ }
52619
+ function partsText(entry) {
52620
+ const m = msg().report.contextReport;
52621
+ const shown2 = entry.parts.slice(0, MAX_PARTS).map((part) => `${partName(entry, part.name)} ${formatTokenCount(part.tokens)}`);
52622
+ if (entry.parts.length > MAX_PARTS) shown2.push(m.more(entry.parts.length - MAX_PARTS));
52623
+ if (entry.category === "images" && entry.count > 0) shown2.unshift(m.images(entry.count));
52624
+ return shown2.join(" · ");
52625
+ }
52626
+ function categoryRows(breakdown, theme) {
52627
+ const m = msg().report.contextReport;
52628
+ const dim = (text) => theme === void 0 ? text : theme.fg("dim", text);
52629
+ return breakdown.entries.map((entry) => {
52630
+ const ratio = breakdown.total > 0 ? entry.tokens / breakdown.total : void 0;
52631
+ const meter = new Meter(ratio, { warnAt: 2, dangerAt: 2, ...theme ? { theme } : {} });
52632
+ const parts = partsText(entry);
52633
+ return {
52634
+ key: m.categories[entry.category],
52635
+ value: `${formatTokenCount(entry.tokens).padStart(6)} ${meter.render(40)[0] ?? ""}` + (parts === "" ? "" : ` ${dim(parts)}`)
52636
+ };
52637
+ });
52638
+ }
52639
+ function largestRows(breakdown) {
52640
+ return largestToolResults(breakdown, CONTEXT_TOP_RESULTS).map((result) => ({
52641
+ key: `#${result.ordinal}`,
52642
+ value: `${formatTokenCount(result.tokens).padStart(6)} ${result.toolName}`
52643
+ }));
52644
+ }
52645
+ function describeContext(session) {
52646
+ const m = msg().report.contextReport;
52647
+ const snap = contextSnapshot(session);
52648
+ const { breakdown } = snap;
52649
+ const lines2 = [m.title, ...renderRows(headerRows(snap), " "), ""];
52650
+ lines2.push(m.breakdown(formatTokenCount(breakdown.total)));
52651
+ lines2.push(` ${breakdown.hasSystem ? m.prefixCounted : m.prefixPending}`);
52652
+ if (breakdown.total === 0) lines2.push(` ${m.empty}`);
52653
+ else lines2.push(...renderRows(categoryRows(breakdown, void 0), " "));
52654
+ lines2.push("", m.largest(CONTEXT_TOP_RESULTS));
52655
+ const largest = largestRows(breakdown);
52656
+ if (largest.length === 0) lines2.push(` ${m.noToolResults}`);
52657
+ else lines2.push(...renderRows(largest, " "));
52658
+ return lines2.join("\n");
52659
+ }
52660
+ function contextPanel(session, theme) {
52661
+ const m = msg().report.contextReport;
52662
+ const snap = contextSnapshot(session);
52663
+ const { breakdown } = snap;
52664
+ const table2 = (rows, wrap2 = false) => new KeyValue(rows, { theme, maxKeyRatio: 0.3, ...wrap2 ? { wrap: true } : {} });
52665
+ const largest = largestRows(breakdown);
52666
+ const parts = [
52667
+ table2(headerRows(snap)),
52668
+ "",
52669
+ theme.bold(m.breakdown(formatTokenCount(breakdown.total))),
52670
+ new Indent(
52671
+ new Stack([
52672
+ new Text(theme.fg("dim", breakdown.hasSystem ? m.prefixCounted : m.prefixPending)),
52673
+ breakdown.total === 0 ? theme.fg("dim", m.empty) : table2(categoryRows(breakdown, theme), true)
52674
+ ]),
52675
+ 2
52676
+ ),
52677
+ "",
52678
+ theme.bold(m.largest(CONTEXT_TOP_RESULTS)),
52679
+ largest.length === 0 ? new Indent(new Stack([theme.fg("dim", m.noToolResults)]), 2) : new Indent(table2(largest), 2)
52680
+ ];
52681
+ return new Card(new Stack(parts), { theme, title: m.title });
52682
+ }
52683
+ var CONTEXT_TOP_RESULTS, MAX_PARTS;
52684
+ var init_context_report = __esm({
52685
+ "src/modes/context-report.ts"() {
52686
+ "use strict";
52687
+ init_session2();
52688
+ init_session_compaction();
52689
+ init_breakdown();
52690
+ init_estimate();
52691
+ init_prune_tier();
52692
+ init_i18n();
52693
+ init_projection();
52694
+ init_tui2();
52695
+ init_panels2();
52696
+ init_session_report();
52697
+ CONTEXT_TOP_RESULTS = 5;
52698
+ MAX_PARTS = 6;
52699
+ }
52700
+ });
52701
+
51759
52702
  // src/modes/commands-core.ts
51760
52703
  function command(name, key, args) {
51761
52704
  const info2 = {
@@ -51927,6 +52870,8 @@ async function runSlashCommand(line2, ctx) {
51927
52870
  }
51928
52871
  case "session":
51929
52872
  return { kind: "handled", message: describeSession(session) };
52873
+ case "context":
52874
+ return { kind: "handled", message: describeContext(session) };
51930
52875
  case "cache":
51931
52876
  return { kind: "handled", message: cacheCommand(session, args) };
51932
52877
  case "rewind":
@@ -51977,6 +52922,7 @@ var init_commands_core = __esm({
51977
52922
  init_tasks_report();
51978
52923
  init_task_background();
51979
52924
  init_session_report();
52925
+ init_context_report();
51980
52926
  init_i18n();
51981
52927
  BUILTIN_COMMANDS = [
51982
52928
  command("help", "help"),
@@ -51991,6 +52937,7 @@ var init_commands_core = __esm({
51991
52937
  command("tools", "tools", { key: "toolsArgs" }),
51992
52938
  command("hooks", "hooks"),
51993
52939
  command("session", "session"),
52940
+ command("context", "context"),
51994
52941
  command("cache", "cache", "[warm off|streaming|idle | fingerprint]"),
51995
52942
  command("statusline", "statusline", "[full|compact]"),
51996
52943
  command("plan", "plan", { key: "planArgs" }),
@@ -52539,55 +53486,6 @@ var init_bypass = __esm({
52539
53486
  }
52540
53487
  });
52541
53488
 
52542
- // src/cli/startup-screen.ts
52543
- function startupScreenLevel(runtime) {
52544
- return runtime.config.ui?.quietStartup ?? "normal";
52545
- }
52546
- function trustSourceLabel(source) {
52547
- const labels2 = msg().cli.startupScreen.trustSource;
52548
- return source === "trust-file" ? labels2.trustFile : labels2[source];
52549
- }
52550
- function tildePath(path, home) {
52551
- if (home === void 0 || home === "") return path;
52552
- const trimmed = /[\\/]$/.test(home) ? home.slice(0, -1) : home;
52553
- if (path === trimmed) return "~";
52554
- const next = path[trimmed.length];
52555
- return path.startsWith(trimmed) && (next === "/" || next === "\\") ? `~${path.slice(trimmed.length)}` : path;
52556
- }
52557
- function startupInfo(runtime, home) {
52558
- const { resources } = runtime;
52559
- const info2 = {
52560
- version: AMA_VERSION,
52561
- model: formatModelRef(runtime.model),
52562
- thinking: runtime.thinkingLevel,
52563
- cwd: tildePath(runtime.paths.cwd, home),
52564
- trusted: runtime.trust.trusted,
52565
- trustSource: trustSourceLabel(runtime.trust.source),
52566
- permissionMode: runtime.session.state.permissionMode,
52567
- preset: runtime.config.tools?.preset ?? "default",
52568
- codemode: effectiveCodemodeMode(runtime.config),
52569
- contextFiles: resources.contextFiles.map((f) => (0, import_node_path93.basename)(f.path)),
52570
- skills: resources.skills.filter((s2) => !isBuiltinSkill(s2)).length,
52571
- prompts: resources.prompts.length,
52572
- hooks: runtime.hooks.list().length,
52573
- warnings: runtime.warnings.length
52574
- };
52575
- if (runtime.host !== void 0) info2.host = runtime.host.adapter.id;
52576
- return info2;
52577
- }
52578
- var import_node_path93;
52579
- var init_startup_screen = __esm({
52580
- "src/cli/startup-screen.ts"() {
52581
- "use strict";
52582
- init_builtin3();
52583
- import_node_path93 = require("node:path");
52584
- init_channels();
52585
- init_presets2();
52586
- init_i18n();
52587
- init_version();
52588
- }
52589
- });
52590
-
52591
53489
  // src/modes/interactive/config-panel.ts
52592
53490
  function settingValueText(spec2, value, theme) {
52593
53491
  if (value.value === void 0 && spec2.key === "ui.ascii")
@@ -52850,8 +53748,8 @@ var init_config_panel = __esm({
52850
53748
  const dynamic = value.value === void 0 ? settingDynamicDefault(row3.id) : void 0;
52851
53749
  const text = dynamic === void 0 ? doc : `${doc} · ${dynamic}`;
52852
53750
  const spec2 = settingsRegistry().find((x) => x.key === row3.id);
52853
- const blocked = spec2 === void 0 ? void 0 : blockedReason(spec2, value, this.scope);
52854
- const lines2 = blocked === void 0 ? [] : [theme.fg("warning", truncateToWidth(blocked, width2))];
53751
+ const blocked2 = spec2 === void 0 ? void 0 : blockedReason(spec2, value, this.scope);
53752
+ const lines2 = blocked2 === void 0 ? [] : [theme.fg("warning", truncateToWidth(blocked2, width2))];
52855
53753
  if (text !== "") lines2.push(...wrapTextWithAnsi(theme.fg("dim", text), width2));
52856
53754
  return lines2.slice(0, 3);
52857
53755
  }
@@ -53894,8 +54792,8 @@ var init_config_ui = __esm({
53894
54792
  const spec2 = settingSpec(key);
53895
54793
  if (panel === void 0 || spec2 === void 0) return;
53896
54794
  const current3 = getConfigValue(readSnapshot(this.input()), key);
53897
- const blocked = blockedReason(spec2, current3, scope);
53898
- if (blocked !== void 0) return panel.setNotice(blocked, "warning");
54795
+ const blocked2 = blockedReason(spec2, current3, scope);
54796
+ if (blocked2 !== void 0) return panel.setNotice(blocked2, "warning");
53899
54797
  const value = current3.value;
53900
54798
  switch (spec2.kind) {
53901
54799
  case "bool":
@@ -56005,6 +56903,8 @@ function taskRow(info2, ctx) {
56005
56903
  status,
56006
56904
  turns: Math.max(state?.turns ?? 0, info2.turns ?? 0)
56007
56905
  };
56906
+ const context = formatTaskContext(info2);
56907
+ if (context !== void 0) row3.context = context;
56008
56908
  const tool = state?.tools.at(-1);
56009
56909
  if (tool !== void 0 && status !== "queued") row3.tool = tool;
56010
56910
  if (status !== "queued") {
@@ -56020,6 +56920,7 @@ function rowFacts(row3, withTool = true) {
56020
56920
  const facts = [statusLabel(row3.status)];
56021
56921
  if (row3.elapsedMs !== void 0) facts[0] += ` ${formatElapsed(row3.elapsedMs)}`;
56022
56922
  if (row3.turns > 0) facts.push(msg().agents.turns(row3.turns));
56923
+ if (row3.context !== void 0) facts.push(msg().agents.context(row3.context));
56023
56924
  if (withTool && row3.tool !== void 0 && row3.status === "running") facts.push(row3.tool);
56024
56925
  return facts.join(" · ");
56025
56926
  }
@@ -56027,6 +56928,7 @@ var BAR_ROWS, BAR_RETAIN_MS, ACTIVE, AgentBar;
56027
56928
  var init_agent_bar = __esm({
56028
56929
  "src/modes/interactive/agent-bar.ts"() {
56029
56930
  "use strict";
56931
+ init_task_record();
56030
56932
  init_i18n();
56031
56933
  init_tui2();
56032
56934
  BAR_ROWS = 3;
@@ -56611,171 +57513,6 @@ var init_agent_view = __esm({
56611
57513
  }
56612
57514
  });
56613
57515
 
56614
- // src/modes/interactive/panels.ts
56615
- function keyValue(rows, theme, wrap2 = false) {
56616
- return new KeyValue(rows, { theme, maxKeyRatio: 0.3, ...wrap2 ? { wrap: true } : {} });
56617
- }
56618
- function cacheSection(session, theme, now) {
56619
- return [
56620
- theme.bold(msg().panels.cache.title),
56621
- new Indent(keyValue(cacheRows(session, now), theme), 2)
56622
- ];
56623
- }
56624
- function sessionPanel(session, theme, options = {}) {
56625
- const m = msg().panels.session;
56626
- const now = options.now ?? Date.now();
56627
- const state = session.state;
56628
- const stats = session.getStats();
56629
- const t = stats.tokens;
56630
- const sep5 = theme.fg("dim", " · ");
56631
- const model = state.model === void 0 ? "?" : `${state.model.provider}/${state.model.id}`;
56632
- const ratio = stats.contextPercent === void 0 ? void 0 : stats.contextPercent / 100;
56633
- const meter = new Meter(ratio, { theme }).render(40)[0] ?? "";
56634
- const window2 = stats.contextTokens === void 0 && stats.contextWindow === void 0 ? "" : sep5 + `${stats.contextTokens === void 0 ? "?" : formatTokenCount(stats.contextTokens)} / ${stats.contextWindow === void 0 ? "?" : formatTokenCount(stats.contextWindow)}`;
56635
- const rows = [
56636
- {
56637
- key: m.model,
56638
- value: theme.fg("accent", model) + sep5 + m.thinking(state.thinkingLevel) + sep5 + m.permission(permissionModeLabel(state.permissionMode))
56639
- },
56640
- {
56641
- key: m.messages,
56642
- value: [
56643
- m.user(stats.userMessages),
56644
- m.assistant(stats.assistantMessages),
56645
- m.toolCalls(stats.toolCalls)
56646
- ].join(sep5)
56647
- },
56648
- {
56649
- key: m.usage,
56650
- value: [
56651
- m.input(formatTokenCount(t.input)),
56652
- m.output(formatTokenCount(t.output)),
56653
- m.cacheRead(formatTokenCount(t.cacheRead)),
56654
- m.cacheWrite(formatTokenCount(t.cacheWrite)),
56655
- formatUsd2(stats.cost)
56656
- ].join(sep5)
56657
- },
56658
- { key: m.context, value: meter + window2 }
56659
- ];
56660
- const tasks = taskStatsText(session);
56661
- if (tasks !== void 0) rows.push({ key: m.subagents, value: tasks });
56662
- const external = externalRows(session);
56663
- const externalSection = external.length > 0 ? ["", theme.bold(m.external), new Indent(keyValue(external, theme), 2)] : [];
56664
- const file = state.sessionFile === void 0 ? m.notSaved : tildePath(state.sessionFile, options.home);
56665
- const parts = [
56666
- keyValue(rows, theme),
56667
- "",
56668
- ...cacheSection(session, theme, now),
56669
- ...externalSection
56670
- ];
56671
- return new Card(new Stack(parts), {
56672
- theme,
56673
- title: m.title(state.sessionId.slice(0, 8)),
56674
- subtitle: file
56675
- });
56676
- }
56677
- function cachePanel(session, theme, now = Date.now()) {
56678
- return new Card(keyValue(cacheRows(session, now), theme), {
56679
- theme,
56680
- title: msg().panels.cache.title
56681
- });
56682
- }
56683
- function permissionsPanel(runtime, session, theme) {
56684
- const m = msg().panels.permissions;
56685
- const rules = runtime.permission.rules;
56686
- const mode = session.state.permissionMode;
56687
- const order = mode === "auto" ? m.orderAuto : mode === "allowlist" ? m.orderAllowlist : m.orderDefault;
56688
- const effect = (decision) => theme.fg(DECISION_COLOR[decision] ?? "text", decision.padEnd(5));
56689
- const parts = [
56690
- keyValue(
56691
- [
56692
- {
56693
- key: m.mode,
56694
- value: m.modeValue(permissionModeLabel(mode), mode, (t) => theme.fg("dim", t))
56695
- },
56696
- { key: m.order, value: order }
56697
- ],
56698
- theme,
56699
- true
56700
- ),
56701
- theme.bold(rules.length === 0 ? m.rulesNone : m.rules(rules.length))
56702
- ];
56703
- if (rules.length > 0) {
56704
- parts.push(
56705
- new Indent(
56706
- keyValue(
56707
- rules.map((r) => ({
56708
- key: effect(r.effect),
56709
- value: `${r.raw} ${theme.fg("dim", `[${r.source}]`)}`
56710
- })),
56711
- theme
56712
- ),
56713
- 2
56714
- )
56715
- );
56716
- }
56717
- const recent = runtime.permission.autoDecisions?.() ?? [];
56718
- if (recent.length > 0) {
56719
- parts.push(theme.bold(m.recentAuto(recent.length)));
56720
- parts.push(
56721
- new Indent(
56722
- keyValue(
56723
- recent.map((d) => ({
56724
- key: autoLayerText(d.layer),
56725
- value: `${effect(d.decision)} ${d.toolName} ${d.summary} — ${d.reason}` + (d.cached === true ? theme.fg("dim", m.cached) : "")
56726
- })),
56727
- theme
56728
- ),
56729
- 2
56730
- )
56731
- );
56732
- }
56733
- return new Card(new Stack(parts), { theme });
56734
- }
56735
- var Stack, Indent, DECISION_COLOR;
56736
- var init_panels2 = __esm({
56737
- "src/modes/interactive/panels.ts"() {
56738
- "use strict";
56739
- init_i18n();
56740
- init_modes();
56741
- init_tui2();
56742
- init_session_report();
56743
- init_startup_screen();
56744
- Stack = class {
56745
- constructor(parts) {
56746
- this.parts = parts;
56747
- }
56748
- parts;
56749
- render(width2) {
56750
- return this.parts.flatMap((part) => typeof part === "string" ? [part] : part.render(width2));
56751
- }
56752
- invalidate() {
56753
- for (const part of this.parts) if (typeof part !== "string") part.invalidate();
56754
- }
56755
- };
56756
- Indent = class {
56757
- constructor(child, columns) {
56758
- this.child = child;
56759
- this.columns = columns;
56760
- }
56761
- child;
56762
- columns;
56763
- render(width2) {
56764
- const pad3 = " ".repeat(this.columns);
56765
- return this.child.render(Math.max(1, width2 - this.columns)).map((line2) => line2 === "" ? "" : pad3 + line2);
56766
- }
56767
- invalidate() {
56768
- this.child.invalidate();
56769
- }
56770
- };
56771
- DECISION_COLOR = {
56772
- allow: "success",
56773
- deny: "error",
56774
- ask: "warning"
56775
- };
56776
- }
56777
- });
56778
-
56779
57516
  // src/modes/interactive/agent-panels.ts
56780
57517
  function planPanel(controller, session, theme, displayPath3 = (p) => p) {
56781
57518
  const m = msg().panels.plan;
@@ -58141,6 +58878,7 @@ var init_commands2 = __esm({
58141
58878
  init_fake_visibility();
58142
58879
  init_modes();
58143
58880
  init_commands_core();
58881
+ init_context_report();
58144
58882
  init_message_view();
58145
58883
  init_panels2();
58146
58884
  init_pickers();
@@ -58160,6 +58898,12 @@ var init_commands2 = __esm({
58160
58898
  await ui.traceView(args === "" ? void 0 : args);
58161
58899
  return true;
58162
58900
  },
58901
+ context: async (ui) => {
58902
+ const theme = ui.theme?.();
58903
+ if (ui.panel === void 0 || theme === void 0) return false;
58904
+ ui.panel(contextPanel(ui.session(), theme));
58905
+ return true;
58906
+ },
58163
58907
  memory: async (ui, args) => {
58164
58908
  if (ui.memoryPanel === void 0) return false;
58165
58909
  await ui.memoryPanel(args);
@@ -58657,7 +59401,7 @@ var init_status_line = __esm({
58657
59401
  stat16
58658
59402
  );
58659
59403
  }
58660
- const usage = usageItems(stats, this.bar.queued(), this.source, theme);
59404
+ const usage = usageItems(stats, this.bar.queued(), this.source, theme, true);
58661
59405
  add3("right", usage.tokens, PRIORITY.tokens);
58662
59406
  add3("right", usage.cache, PRIORITY.cache);
58663
59407
  add3("right", usage.rebill, PRIORITY.rebill);
@@ -60717,6 +61461,16 @@ var init_run_indicator = __esm({
60717
61461
  function createSessionEventHandler(deps) {
60718
61462
  const { view, tools, status, area, agentUi, indicator, session, setQueue, notice, render } = deps;
60719
61463
  const images = new ImageBudgetNotices((text) => notice("info", text));
61464
+ const windowHinted = /* @__PURE__ */ new Set();
61465
+ const refresh = () => {
61466
+ status.refresh();
61467
+ if (status.current().contextWindow !== void 0) return;
61468
+ const model = session().state.model;
61469
+ const ref = model === void 0 ? void 0 : formatModelRef(model);
61470
+ if (ref === void 0 || windowHinted.has(ref)) return;
61471
+ windowHinted.add(ref);
61472
+ view.addNotice("info", msg().interactive.statusLine.noWindow(ref));
61473
+ };
60720
61474
  return (event) => {
60721
61475
  if (area.onEvent(event) || agentUi().onEvent(event)) {
60722
61476
  if (event.type === "subagent_start" || event.type === "subagent_end") indicator.sync();
@@ -60727,7 +61481,7 @@ function createSessionEventHandler(deps) {
60727
61481
  case "agent_settled":
60728
61482
  if (event.warning !== void 0 && event.warning !== LIMIT_WARNING)
60729
61483
  view.addNotice("warn", event.warning);
60730
- status.refresh();
61484
+ refresh();
60731
61485
  break;
60732
61486
  case "message_start": {
60733
61487
  const message = event.message;
@@ -60740,12 +61494,14 @@ function createSessionEventHandler(deps) {
60740
61494
  }
60741
61495
  case "message_update":
60742
61496
  view.updateAssistant(event.message);
61497
+ status.noteStreaming(event.message.usage);
60743
61498
  break;
60744
61499
  case "message_end":
60745
61500
  if (event.message.role === "assistant") {
60746
61501
  view.endAssistant(event.message);
61502
+ status.clearStreaming();
60747
61503
  status.refresh();
60748
- }
61504
+ } else if (event.message.role === "user") refresh();
60749
61505
  break;
60750
61506
  case "tool_execution_start": {
60751
61507
  const started = tools.start(event);
@@ -60792,7 +61548,7 @@ function createSessionEventHandler(deps) {
60792
61548
  case "model_changed":
60793
61549
  case "thinking_level_changed":
60794
61550
  case "session_changed":
60795
- status.refresh();
61551
+ refresh();
60796
61552
  render();
60797
61553
  return;
60798
61554
  default:
@@ -60806,11 +61562,407 @@ var init_session_events = __esm({
60806
61562
  "src/modes/interactive/session-events.ts"() {
60807
61563
  "use strict";
60808
61564
  init_i18n();
61565
+ init_channels();
60809
61566
  init_session_report();
60810
61567
  init_event_notices();
60811
61568
  }
60812
61569
  });
60813
61570
 
61571
+ // src/tui/program-status.ts
61572
+ function sanitizeLine(text) {
61573
+ return text.replace(/[\x00-\x1f\x7f-\x9f]/g, " ").replace(/\s+/g, " ").trim();
61574
+ }
61575
+ function truncateUtf8(text, maxBytes) {
61576
+ if (Buffer.byteLength(text, "utf8") <= maxBytes) return text;
61577
+ const budget = maxBytes - Buffer.byteLength(ELLIPSIS, "utf8");
61578
+ let used = 0;
61579
+ let out = "";
61580
+ for (const ch2 of text) {
61581
+ const n = Buffer.byteLength(ch2, "utf8");
61582
+ if (used + n > budget) break;
61583
+ used += n;
61584
+ out += ch2;
61585
+ }
61586
+ return out + ELLIPSIS;
61587
+ }
61588
+ function validId(id) {
61589
+ if (Buffer.byteLength(id, "utf8") > MAX_ID_BYTES) return false;
61590
+ const segments3 = id.split("/");
61591
+ return segments3.length <= MAX_ID_DEPTH && segments3.every((s2) => SEGMENT.test(s2));
61592
+ }
61593
+ function idSegment(raw, n = 12) {
61594
+ const s2 = raw.replace(/[^A-Za-z0-9_.+-]/g, "_").slice(0, n);
61595
+ return s2 === "" ? "x" : s2;
61596
+ }
61597
+ function encodeText(text, maxBytes) {
61598
+ if (text === void 0) return void 0;
61599
+ const line2 = sanitizeLine(text);
61600
+ if (line2 === "") return void 0;
61601
+ return Buffer.from(truncateUtf8(line2, maxBytes), "utf8").toString("base64");
61602
+ }
61603
+ function encodePairs(report2) {
61604
+ if (!STATES.includes(report2.state)) return void 0;
61605
+ if (report2.id !== void 0 && !validId(report2.id)) return void 0;
61606
+ const pairs = [`state=${report2.state}`];
61607
+ if (report2.id !== void 0) pairs.push(`id=${report2.id}`);
61608
+ if (report2.state === "blocked" && report2.kind !== void 0 && KINDS4.includes(report2.kind))
61609
+ pairs.push(`kind=${report2.kind}`);
61610
+ const p = report2.progress;
61611
+ if ((report2.state === "working" || report2.state === "blocked") && p !== void 0 && Number.isInteger(p) && p >= 0 && p <= 100)
61612
+ pairs.push(`progress=${p}`);
61613
+ if (report2.app !== void 0 && SEGMENT.test(report2.app)) pairs.push(`app=${report2.app}`);
61614
+ if (report2.state !== "clear") {
61615
+ const title = encodeText(report2.title, MAX_TITLE_BYTES);
61616
+ if (title !== void 0) pairs.push(`title=${title}`);
61617
+ const msg2 = encodeText(report2.msg, MAX_MSG_BYTES);
61618
+ if (msg2 !== void 0) pairs.push(`msg=${msg2}`);
61619
+ }
61620
+ while (osc(pairs.join(":")).length > MAX_SEQUENCE_BYTES) {
61621
+ const last = pairs.at(-1);
61622
+ if (!last.startsWith("msg=") && !last.startsWith("title=")) return void 0;
61623
+ pairs.pop();
61624
+ }
61625
+ return pairs.join(":");
61626
+ }
61627
+ function osc(body) {
61628
+ return `\x1B]7501;${body}\x1B\\`;
61629
+ }
61630
+ function tmuxPassthrough(sequence) {
61631
+ return `\x1BPtmux;${sequence.replaceAll("\x1B", "\x1B\x1B")}\x1B\\`;
61632
+ }
61633
+ function isProgramStatusReply(sequence) {
61634
+ return /^\x1b\]7501;\?/.test(sequence);
61635
+ }
61636
+ function isDa1Reply(sequence) {
61637
+ return /^\x1b\[\?[\d;]*c$/.test(sequence);
61638
+ }
61639
+ var PROGRAM_STATUS_QUERY, DA1_QUERY, MAX_SEQUENCE_BYTES, MAX_TITLE_BYTES, MAX_MSG_BYTES, MAX_ID_BYTES, MAX_ID_DEPTH, SEGMENT, STATES, KINDS4, ELLIPSIS, ProgramStatusEmitter;
61640
+ var init_program_status = __esm({
61641
+ "src/tui/program-status.ts"() {
61642
+ "use strict";
61643
+ PROGRAM_STATUS_QUERY = "\x1B]7501;?\x1B\\";
61644
+ DA1_QUERY = "\x1B[c";
61645
+ MAX_SEQUENCE_BYTES = 4096;
61646
+ MAX_TITLE_BYTES = 192;
61647
+ MAX_MSG_BYTES = 2048;
61648
+ MAX_ID_BYTES = 128;
61649
+ MAX_ID_DEPTH = 8;
61650
+ SEGMENT = /^[A-Za-z0-9_.+-]{1,32}$/;
61651
+ STATES = ["idle", "working", "done", "blocked", "error", "clear"];
61652
+ KINDS4 = ["permission", "question", "auth"];
61653
+ ELLIPSIS = "…";
61654
+ ProgramStatusEmitter = class {
61655
+ constructor(options) {
61656
+ this.options = options;
61657
+ this.tmux = Boolean(options.env?.["TMUX"]);
61658
+ this.now = options.now ?? Date.now;
61659
+ }
61660
+ options;
61661
+ support = "unknown";
61662
+ tmux;
61663
+ desired = /* @__PURE__ */ new Map();
61664
+ sent = /* @__PURE__ */ new Map();
61665
+ timers = /* @__PURE__ */ new Map();
61666
+ probeTimer;
61667
+ now;
61668
+ /** 当前是否会写出报告。 */
61669
+ get supported() {
61670
+ return this.support === "yes";
61671
+ }
61672
+ /** 按模式开始:on 直接启用,auto 发检测查询(tmux 里不检测,等同 off),off 什么都不做。 */
61673
+ start() {
61674
+ if (this.support !== "unknown") return;
61675
+ const mode = this.options.mode;
61676
+ if (mode === "off" || mode === "auto" && this.tmux) {
61677
+ this.support = "no";
61678
+ return;
61679
+ }
61680
+ if (mode === "on") return this.enable();
61681
+ this.support = "probing";
61682
+ this.options.write(PROGRAM_STATUS_QUERY + DA1_QUERY);
61683
+ this.probeTimer = setTimeout(() => this.settleProbe(false), this.options.timeoutMs ?? 300);
61684
+ this.probeTimer.unref?.();
61685
+ }
61686
+ /** 输入解析里调用:吞掉检测回复(返回 true),其余返回 false。 */
61687
+ handleInput(sequence) {
61688
+ if (isProgramStatusReply(sequence)) {
61689
+ if (this.support === "probing") this.settleProbe(true);
61690
+ return true;
61691
+ }
61692
+ if (isDa1Reply(sequence)) {
61693
+ if (this.support === "probing") this.settleProbe(false);
61694
+ return true;
61695
+ }
61696
+ return false;
61697
+ }
61698
+ /** 设定某个 id(缺省根记录)的状态;`clear` 同时忘掉该 id 及其下层的记录。 */
61699
+ report(report2) {
61700
+ const key = report2.id ?? "";
61701
+ const full = this.options.app !== void 0 ? { ...report2, app: this.options.app } : report2;
61702
+ if (report2.state === "clear") {
61703
+ for (const id of [...this.desired.keys()])
61704
+ if (key === "" || id === key || id.startsWith(`${key}/`)) this.forget(id);
61705
+ }
61706
+ this.desired.set(key, full);
61707
+ if (this.support === "yes") this.flush(key);
61708
+ if (report2.state === "clear") this.desired.delete(key);
61709
+ }
61710
+ /** 停止计时器(不再写任何东西)。 */
61711
+ dispose() {
61712
+ if (this.probeTimer !== void 0) clearTimeout(this.probeTimer);
61713
+ this.probeTimer = void 0;
61714
+ for (const timer of this.timers.values()) clearTimeout(timer);
61715
+ this.timers.clear();
61716
+ if (this.support === "probing") this.support = "no";
61717
+ }
61718
+ settleProbe(ok) {
61719
+ if (this.probeTimer !== void 0) clearTimeout(this.probeTimer);
61720
+ this.probeTimer = void 0;
61721
+ if (ok) this.enable();
61722
+ else this.support = "no";
61723
+ }
61724
+ enable() {
61725
+ this.support = "yes";
61726
+ for (const key of this.desired.keys()) this.flush(key);
61727
+ }
61728
+ forget(id) {
61729
+ this.desired.delete(id);
61730
+ this.sent.delete(id);
61731
+ const timer = this.timers.get(id);
61732
+ if (timer !== void 0) clearTimeout(timer);
61733
+ this.timers.delete(id);
61734
+ }
61735
+ flush(key) {
61736
+ const report2 = this.desired.get(key);
61737
+ if (report2 === void 0) return;
61738
+ const body = encodePairs(report2);
61739
+ if (body === void 0) return;
61740
+ const last = this.sent.get(key);
61741
+ if (last?.body === body) return;
61742
+ const throttle = this.options.throttleMs ?? 500;
61743
+ const head = `${report2.state}:${report2.kind ?? ""}`;
61744
+ const wait = last !== void 0 && last.head === head ? last.at + throttle - this.now() : 0;
61745
+ if (wait > 0 && report2.state !== "clear") {
61746
+ if (!this.timers.has(key)) {
61747
+ const timer2 = setTimeout(() => {
61748
+ this.timers.delete(key);
61749
+ this.flush(key);
61750
+ }, wait);
61751
+ timer2.unref?.();
61752
+ this.timers.set(key, timer2);
61753
+ }
61754
+ return;
61755
+ }
61756
+ const timer = this.timers.get(key);
61757
+ if (timer !== void 0) clearTimeout(timer);
61758
+ this.timers.delete(key);
61759
+ const sequence = osc(body);
61760
+ this.options.write(this.tmux ? tmuxPassthrough(sequence) : sequence);
61761
+ if (report2.state === "clear") this.sent.delete(key);
61762
+ else this.sent.set(key, { body, head, at: this.now() });
61763
+ }
61764
+ };
61765
+ }
61766
+ });
61767
+
61768
+ // src/modes/interactive/program-status-ui.ts
61769
+ function blocked(kind, text) {
61770
+ return { state: "blocked", kind, msg: text };
61771
+ }
61772
+ function programStatusUi(deps) {
61773
+ const { terminal, programStatus } = deps.options;
61774
+ const mode = deps.runtime.config.ui?.programStatus ?? "auto";
61775
+ if (terminal !== void 0 && programStatus === void 0 || mode === "off")
61776
+ return { wrap: (handler) => handler, start: () => void 0, exit: () => void 0 };
61777
+ const emitter = new ProgramStatusEmitter({
61778
+ write: (data2) => deps.tui.terminal.write(data2),
61779
+ mode,
61780
+ env: deps.env,
61781
+ app: "ama",
61782
+ ...programStatus
61783
+ });
61784
+ const tracker = new ProgramStatusTracker(emitter, deps.cwd);
61785
+ deps.tui.addInputListener((data2) => {
61786
+ if (emitter.handleInput(data2)) return true;
61787
+ tracker.acknowledge();
61788
+ return false;
61789
+ });
61790
+ return {
61791
+ wrap: (handler) => (event) => {
61792
+ tracker.onEvent(event);
61793
+ handler(event);
61794
+ },
61795
+ start: () => {
61796
+ emitter.start();
61797
+ tracker.sync();
61798
+ },
61799
+ exit: () => {
61800
+ emitter.report({ state: "clear" });
61801
+ emitter.dispose();
61802
+ }
61803
+ };
61804
+ }
61805
+ var AUTH_EXPIRED, ProgramStatusTracker;
61806
+ var init_program_status_ui = __esm({
61807
+ "src/modes/interactive/program-status-ui.ts"() {
61808
+ "use strict";
61809
+ init_i18n();
61810
+ init_program_status();
61811
+ init_tool_summary();
61812
+ AUTH_EXPIRED = /\bauth_expired\b/;
61813
+ ProgramStatusTracker = class {
61814
+ constructor(emitter, cwd = () => void 0) {
61815
+ this.emitter = emitter;
61816
+ this.cwd = cwd;
61817
+ }
61818
+ emitter;
61819
+ cwd;
61820
+ running = false;
61821
+ activity;
61822
+ approvals = /* @__PURE__ */ new Map();
61823
+ plan;
61824
+ auth;
61825
+ outcome;
61826
+ lastError;
61827
+ tasks = /* @__PURE__ */ new Map();
61828
+ onEvent(event) {
61829
+ const run7 = msg().interactive.view.run;
61830
+ switch (event.type) {
61831
+ case "session_start":
61832
+ if (event.reason !== "startup") this.reset();
61833
+ break;
61834
+ case "agent_start":
61835
+ this.running = true;
61836
+ this.activity = run7.thinking;
61837
+ this.outcome = this.auth = this.lastError = void 0;
61838
+ for (const [key, task] of this.tasks)
61839
+ if (task.state !== "working" && task.state !== "blocked") this.dropTask(key);
61840
+ break;
61841
+ case "message_update": {
61842
+ const last = event.message.content.at(-1);
61843
+ this.activity = last?.type === "text" ? run7.replying : run7.thinking;
61844
+ break;
61845
+ }
61846
+ case "tool_execution_start":
61847
+ if (event.parentToolCallId === void 0) this.activity = run7.runningTool(event.toolName);
61848
+ break;
61849
+ case "compaction_start":
61850
+ this.activity = run7.compacting;
61851
+ break;
61852
+ case "message_end":
61853
+ if (event.message.role === "assistant" && event.message.stopReason === "error")
61854
+ this.lastError = event.message.errorMessage ?? "error";
61855
+ break;
61856
+ case "agent_end":
61857
+ if (!event.willRetry) this.outcome = this.outcomeOf(event.stopReason);
61858
+ break;
61859
+ case "agent_settled":
61860
+ this.running = false;
61861
+ this.outcome ??= { state: "done" };
61862
+ break;
61863
+ case "permission_request": {
61864
+ const summary2 = flat(toolSummary(event.toolName, event.input, this.cwd()), 80);
61865
+ const text = summary2 === "" ? event.toolName : `${event.toolName} · ${summary2}`;
61866
+ const taskId = event.context?.taskId;
61867
+ this.approvals.set(event.requestId, { msg: text, ...taskId ? { taskId } : {} });
61868
+ const task = taskId !== void 0 ? this.tasks.get(taskId) : void 0;
61869
+ if (task !== void 0) Object.assign(task, { state: "blocked", msg: text });
61870
+ break;
61871
+ }
61872
+ case "permission_resolved": {
61873
+ const taskId = this.approvals.get(event.requestId)?.taskId;
61874
+ this.approvals.delete(event.requestId);
61875
+ const task = taskId !== void 0 ? this.tasks.get(taskId) : void 0;
61876
+ const still = [...this.approvals.values()].some((p) => p.taskId === taskId);
61877
+ if (task?.state === "blocked" && !still) Object.assign(task, { state: "working" });
61878
+ break;
61879
+ }
61880
+ case "plan_proposed":
61881
+ this.plan = msg().plan.dialog.title;
61882
+ break;
61883
+ case "plan_resolved":
61884
+ this.plan = void 0;
61885
+ break;
61886
+ case "model_changed":
61887
+ this.auth = void 0;
61888
+ break;
61889
+ case "subagent_start":
61890
+ this.tasks.set(event.taskId, {
61891
+ id: `task/${idSegment(event.taskId)}`,
61892
+ title: event.description,
61893
+ state: "working",
61894
+ msg: event.agent
61895
+ });
61896
+ break;
61897
+ case "subagent_update": {
61898
+ const task = this.tasks.get(event.taskId);
61899
+ if (task?.state === "working" && event.toolName !== void 0)
61900
+ task.msg = run7.runningTool(event.toolName);
61901
+ break;
61902
+ }
61903
+ case "subagent_end": {
61904
+ const task = this.tasks.get(event.taskId);
61905
+ if (task === void 0) break;
61906
+ const s2 = event.status;
61907
+ task.state = s2 === "completed" ? "done" : s2 === "aborted" || s2 === "interrupted" ? "idle" : "error";
61908
+ delete task.msg;
61909
+ break;
61910
+ }
61911
+ default:
61912
+ return;
61913
+ }
61914
+ this.sync();
61915
+ }
61916
+ /** 用户按键:已看到的 done / error 回到 idle。 */
61917
+ acknowledge() {
61918
+ if (this.running || this.outcome === void 0 || this.outcome.state === "idle") return;
61919
+ this.outcome = { state: "idle" };
61920
+ this.sync();
61921
+ }
61922
+ /** 当前根记录(测试也用)。 */
61923
+ root() {
61924
+ const pending = [...this.approvals.values()].at(-1);
61925
+ if (pending !== void 0) return blocked("permission", pending.msg);
61926
+ if (this.plan !== void 0) return blocked("question", this.plan);
61927
+ if (this.auth !== void 0) return blocked("auth", this.auth);
61928
+ if (this.running)
61929
+ return { state: "working", ...this.activity !== void 0 ? { msg: this.activity } : {} };
61930
+ return this.outcome ?? { state: "idle" };
61931
+ }
61932
+ /** 写出全部记录(发射器同值去重)。 */
61933
+ sync() {
61934
+ this.emitter.report(this.root());
61935
+ for (const task of this.tasks.values())
61936
+ this.emitter.report({
61937
+ id: task.id,
61938
+ state: task.state,
61939
+ title: task.title,
61940
+ ...task.state === "blocked" ? { kind: "permission" } : {},
61941
+ ...task.msg !== void 0 ? { msg: task.msg } : {}
61942
+ });
61943
+ }
61944
+ outcomeOf(stopReason) {
61945
+ if (stopReason === "aborted") return { state: "idle" };
61946
+ if (stopReason !== "error") return { state: "done" };
61947
+ const text = flat(this.lastError ?? "error", 200);
61948
+ if (AUTH_EXPIRED.test(text)) this.auth = text;
61949
+ return { state: "error", msg: text };
61950
+ }
61951
+ dropTask(key) {
61952
+ const task = this.tasks.get(key);
61953
+ this.tasks.delete(key);
61954
+ if (task !== void 0) this.emitter.report({ id: task.id, state: "clear" });
61955
+ }
61956
+ reset() {
61957
+ this.running = false;
61958
+ this.approvals.clear();
61959
+ this.plan = this.auth = this.outcome = this.lastError = this.activity = void 0;
61960
+ for (const key of [...this.tasks.keys()]) this.dropTask(key);
61961
+ }
61962
+ };
61963
+ }
61964
+ });
61965
+
60814
61966
  // src/modes/interactive/terminal-setup.ts
60815
61967
  function processTerminal() {
60816
61968
  const stdin = process.stdin;
@@ -60855,6 +62007,7 @@ function runInteractiveMode(runtime, context, options = {}) {
60855
62007
  const keys = options.keybindings ?? loadKeys(runtime, (m) => startupWarnings.push(m));
60856
62008
  let session = currentSession(runtime);
60857
62009
  const tui = new TUI(terminal);
62010
+ const osc7501 = programStatusUi({ tui, runtime, env, options, cwd: () => session.state.cwd });
60858
62011
  const view = new MessageView({
60859
62012
  theme,
60860
62013
  ...ui.showThinking !== void 0 ? { showThinking: ui.showThinking } : {},
@@ -60954,7 +62107,7 @@ function runInteractiveMode(runtime, context, options = {}) {
60954
62107
  queueView.setQueue(steering, followUp);
60955
62108
  render();
60956
62109
  };
60957
- const onEvent = createSessionEventHandler({
62110
+ const handleEvent3 = createSessionEventHandler({
60958
62111
  view,
60959
62112
  tools,
60960
62113
  status,
@@ -60966,6 +62119,7 @@ function runInteractiveMode(runtime, context, options = {}) {
60966
62119
  notice,
60967
62120
  render
60968
62121
  });
62122
+ const onEvent = osc7501.wrap(handleEvent3);
60969
62123
  let unsubscribe = session.subscribe(onEvent);
60970
62124
  const startupLevel = startupScreenLevel(runtime);
60971
62125
  const home = env["HOME"] ?? env["USERPROFILE"];
@@ -61193,6 +62347,7 @@ function runInteractiveMode(runtime, context, options = {}) {
61193
62347
  if (hintTimer !== void 0) clearTimeout(hintTimer);
61194
62348
  loader.stop();
61195
62349
  area.dispose();
62350
+ osc7501.exit();
61196
62351
  view.addExitSummary(exitSummaryLines(session.getStats(), now() - startedAt, theme));
61197
62352
  tui.clear();
61198
62353
  tui.addChild(view);
@@ -61231,6 +62386,7 @@ function runInteractiveMode(runtime, context, options = {}) {
61231
62386
  );
61232
62387
  return;
61233
62388
  }
62389
+ osc7501.start();
61234
62390
  runtime.approvals.setUiBroker(broker);
61235
62391
  runtime.notifier.set((message, level) => notice(level, message));
61236
62392
  agentUi.attach(session);
@@ -61287,6 +62443,7 @@ var init_interactive_mode = __esm({
61287
62443
  init_run_indicator();
61288
62444
  init_status_area();
61289
62445
  init_session_events();
62446
+ init_program_status_ui();
61290
62447
  init_subagent_view();
61291
62448
  init_terminal_setup();
61292
62449
  init_tool_view();
@@ -67009,7 +68166,7 @@ function traceHtmlData(source, opts) {
67009
68166
  }
67010
68167
  const du = durationText(node, theme);
67011
68168
  if (du !== "") out.du = du;
67012
- const tk = tokensText(node, theme);
68169
+ const tk = tokensText2(node, theme);
67013
68170
  if (tk !== "") out.tk = tk;
67014
68171
  const ca = cacheText2(node);
67015
68172
  if (ca !== "") out.ca = ca;