mixdog 0.8.1 → 0.9.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (299) hide show
  1. package/README.md +47 -23
  2. package/package.json +34 -28
  3. package/scripts/_test-folder-dialog.mjs +30 -0
  4. package/scripts/agent-parallel-smoke.mjs +388 -0
  5. package/scripts/agent-tag-reuse-smoke.mjs +183 -0
  6. package/scripts/background-task-meta-smoke.mjs +38 -0
  7. package/scripts/boot-smoke.mjs +52 -9
  8. package/scripts/build-runtime-linux.sh +348 -0
  9. package/scripts/build-runtime-macos.sh +217 -0
  10. package/scripts/build-runtime-windows.ps1 +242 -0
  11. package/scripts/compact-active-turn-test.mjs +68 -0
  12. package/scripts/compact-smoke.mjs +859 -129
  13. package/scripts/compact-trigger-migration-smoke.mjs +187 -0
  14. package/scripts/fix-brief-fn.mjs +35 -0
  15. package/scripts/fix-format-tool-surface.mjs +24 -0
  16. package/scripts/fix-tool-exec-visible.mjs +42 -0
  17. package/scripts/generate-runtime-manifest.mjs +166 -0
  18. package/scripts/hook-bus-test.mjs +330 -0
  19. package/scripts/lead-workflow-smoke.mjs +33 -39
  20. package/scripts/live-worker-smoke.mjs +43 -37
  21. package/scripts/llm-trace-summary.mjs +315 -0
  22. package/scripts/memory-meta-concurrency-test.mjs +20 -0
  23. package/scripts/output-style-smoke.mjs +56 -15
  24. package/scripts/parent-abort-link-test.mjs +44 -0
  25. package/scripts/patch-agent-brief.mjs +48 -0
  26. package/scripts/patch-app.mjs +21 -0
  27. package/scripts/patch-app2.mjs +18 -0
  28. package/scripts/patch-dist-brief.mjs +96 -0
  29. package/scripts/patch-tool-exec.mjs +70 -0
  30. package/scripts/pretool-ask-runtime-test.mjs +54 -0
  31. package/scripts/provider-toolcall-test.mjs +376 -0
  32. package/scripts/reactive-compact-persist-smoke.mjs +124 -0
  33. package/scripts/sanitize-tool-pairs-test.mjs +260 -0
  34. package/scripts/session-context-bench.mjs +205 -33
  35. package/scripts/session-ingest-smoke.mjs +177 -0
  36. package/scripts/set-effort-config-test.mjs +41 -0
  37. package/scripts/smoke-runtime-negative.ps1 +106 -0
  38. package/scripts/smoke-runtime-negative.sh +97 -0
  39. package/scripts/smoke.mjs +25 -0
  40. package/scripts/tool-result-hook-test.mjs +48 -0
  41. package/scripts/tool-smoke.mjs +1222 -90
  42. package/scripts/toolcall-args-test.mjs +150 -0
  43. package/scripts/tui-background-failure-smoke.mjs +73 -0
  44. package/scripts/usage-metrics-epoch-smoke.mjs +114 -0
  45. package/src/agents/debugger/AGENT.md +8 -0
  46. package/src/agents/explore/AGENT.md +4 -0
  47. package/src/agents/heavy-worker/AGENT.md +9 -3
  48. package/src/agents/maintainer/AGENT.md +4 -0
  49. package/src/agents/reviewer/AGENT.md +8 -0
  50. package/src/agents/scheduler-task/AGENT.md +12 -0
  51. package/src/agents/scheduler-task/agent.json +6 -0
  52. package/src/agents/webhook-handler/AGENT.md +12 -0
  53. package/src/agents/webhook-handler/agent.json +6 -0
  54. package/src/agents/worker/AGENT.md +9 -3
  55. package/src/app.mjs +77 -3
  56. package/src/defaults/hidden-roles.json +17 -12
  57. package/src/headless-role.mjs +117 -0
  58. package/src/help.mjs +30 -0
  59. package/src/hooks/lib/permission-evaluator.cjs +11 -475
  60. package/src/lib/keychain-cjs.cjs +9 -1
  61. package/src/lib/mixdog-debug.cjs +0 -29
  62. package/src/lib/plugin-paths.cjs +1 -7
  63. package/src/lib/rules-builder.cjs +242 -98
  64. package/src/lib/text-utils.cjs +1 -1
  65. package/src/mixdog-session-runtime.mjs +2211 -446
  66. package/src/output-styles/default.md +12 -28
  67. package/src/output-styles/extreme-simple.md +9 -6
  68. package/src/output-styles/simple.md +22 -9
  69. package/src/repl.mjs +108 -59
  70. package/src/rules/agent/00-common.md +15 -0
  71. package/src/rules/{bridge → agent}/20-skip-protocol.md +1 -2
  72. package/src/rules/agent/30-explorer.md +22 -0
  73. package/src/rules/{bridge → agent}/40-cycle1-agent.md +7 -0
  74. package/src/rules/{bridge → agent}/41-cycle2-agent.md +7 -0
  75. package/src/rules/{bridge → agent}/42-cycle3-agent.md +7 -0
  76. package/src/rules/lead/01-general.md +9 -5
  77. package/src/rules/lead/04-workflow.md +51 -12
  78. package/src/rules/lead/lead-tool.md +6 -0
  79. package/src/rules/shared/01-tool.md +12 -1
  80. package/src/runtime/agent/orchestrator/activity-bus.mjs +7 -18
  81. package/src/runtime/agent/orchestrator/agent-owner.mjs +11 -0
  82. package/src/runtime/agent/orchestrator/{smart-bridge/bridge-llm.mjs → agent-runtime/agent-dispatch.mjs} +138 -111
  83. package/src/runtime/agent/orchestrator/agent-runtime/agent-progress-watchdog.mjs +94 -0
  84. package/src/runtime/agent/orchestrator/{smart-bridge → agent-runtime}/cache-strategy.mjs +32 -23
  85. package/src/runtime/agent/orchestrator/{smart-bridge → agent-runtime}/session-builder.mjs +33 -27
  86. package/src/runtime/agent/orchestrator/{bridge-trace.mjs → agent-trace.mjs} +132 -81
  87. package/src/runtime/agent/orchestrator/cache-mtime.mjs +0 -21
  88. package/src/runtime/agent/orchestrator/config.mjs +174 -55
  89. package/src/runtime/agent/orchestrator/context/collect.mjs +195 -487
  90. package/src/runtime/agent/orchestrator/dispatch-persist.mjs +1 -1
  91. package/src/runtime/agent/orchestrator/internal-roles.mjs +77 -29
  92. package/src/runtime/agent/orchestrator/internal-tools.mjs +5 -6
  93. package/src/runtime/agent/orchestrator/mcp/client.mjs +15 -9
  94. package/src/runtime/agent/orchestrator/providers/anthropic-betas.mjs +7 -1
  95. package/src/runtime/agent/orchestrator/providers/anthropic-oauth.mjs +380 -268
  96. package/src/runtime/agent/orchestrator/providers/anthropic.mjs +131 -93
  97. package/src/runtime/agent/orchestrator/providers/api-usage.mjs +236 -4
  98. package/src/runtime/agent/orchestrator/providers/custom-tool-wire.mjs +49 -0
  99. package/src/runtime/agent/orchestrator/providers/gemini.mjs +58 -13
  100. package/src/runtime/agent/orchestrator/providers/grok-oauth.mjs +67 -149
  101. package/src/runtime/agent/orchestrator/providers/media-normalization.mjs +132 -2
  102. package/src/runtime/agent/orchestrator/providers/model-catalog.mjs +4 -1
  103. package/src/runtime/agent/orchestrator/providers/oauth-credential-probes.mjs +45 -0
  104. package/src/runtime/agent/orchestrator/providers/oauth-usage.mjs +61 -116
  105. package/src/runtime/agent/orchestrator/providers/openai-compat-presets.mjs +25 -0
  106. package/src/runtime/agent/orchestrator/providers/openai-compat-stream.mjs +79 -255
  107. package/src/runtime/agent/orchestrator/providers/openai-compat.mjs +203 -71
  108. package/src/runtime/agent/orchestrator/providers/openai-oauth-ws.mjs +512 -312
  109. package/src/runtime/agent/orchestrator/providers/openai-oauth.mjs +343 -518
  110. package/src/runtime/agent/orchestrator/providers/openai-ws.mjs +6 -6
  111. package/src/runtime/agent/orchestrator/providers/registry.mjs +88 -51
  112. package/src/runtime/agent/orchestrator/providers/retry-classifier.mjs +289 -11
  113. package/src/runtime/agent/orchestrator/providers/statusline-route-meta.mjs +41 -8
  114. package/src/runtime/agent/orchestrator/session/compact.mjs +1173 -267
  115. package/src/runtime/agent/orchestrator/session/context-utils.mjs +199 -36
  116. package/src/runtime/agent/orchestrator/session/loop.mjs +840 -612
  117. package/src/runtime/agent/orchestrator/session/manager.mjs +1574 -459
  118. package/src/runtime/agent/orchestrator/session/manager.reactive-persist.test.mjs +107 -0
  119. package/src/runtime/agent/orchestrator/session/store.mjs +67 -29
  120. package/src/runtime/agent/orchestrator/session/tool-result-offload.mjs +2 -2
  121. package/src/runtime/agent/orchestrator/stall-policy.mjs +31 -16
  122. package/src/runtime/agent/orchestrator/tool-loop-guard.mjs +3 -219
  123. package/src/runtime/agent/orchestrator/tools/bash-session.mjs +35 -8
  124. package/src/runtime/agent/orchestrator/tools/builtin/arg-guard.mjs +19 -8
  125. package/src/runtime/agent/orchestrator/tools/builtin/bash-tool.mjs +10 -53
  126. package/src/runtime/agent/orchestrator/tools/builtin/builtin-tools.mjs +60 -37
  127. package/src/runtime/agent/orchestrator/tools/builtin/cache-layers.mjs +21 -2
  128. package/src/runtime/agent/orchestrator/tools/builtin/device-paths.mjs +1 -1
  129. package/src/runtime/agent/orchestrator/tools/builtin/diagnostics-tool.mjs +0 -7
  130. package/src/runtime/agent/orchestrator/tools/builtin/glob-walk.mjs +1 -3
  131. package/src/runtime/agent/orchestrator/tools/builtin/list-tool.mjs +36 -12
  132. package/src/runtime/agent/orchestrator/tools/builtin/read-args.mjs +2 -0
  133. package/src/runtime/agent/orchestrator/tools/builtin/read-constants.mjs +2 -2
  134. package/src/runtime/agent/orchestrator/tools/builtin/read-formatting.mjs +5 -12
  135. package/src/runtime/agent/orchestrator/tools/builtin/read-image-resize.mjs +1 -1
  136. package/src/runtime/agent/orchestrator/tools/builtin/read-single-tool.mjs +4 -36
  137. package/src/runtime/agent/orchestrator/tools/builtin/read-tool.mjs +2 -40
  138. package/src/runtime/agent/orchestrator/tools/builtin/rg-runner.mjs +148 -27
  139. package/src/runtime/agent/orchestrator/tools/builtin/search-builders.mjs +2 -2
  140. package/src/runtime/agent/orchestrator/tools/builtin/search-tool.mjs +43 -75
  141. package/src/runtime/agent/orchestrator/tools/builtin/shell-jobs.mjs +90 -20
  142. package/src/runtime/agent/orchestrator/tools/builtin.mjs +59 -5
  143. package/src/runtime/agent/orchestrator/tools/code-graph-state.mjs +86 -0
  144. package/src/runtime/agent/orchestrator/tools/code-graph-tool-defs.mjs +11 -11
  145. package/src/runtime/agent/orchestrator/tools/code-graph.mjs +4106 -4019
  146. package/src/runtime/agent/orchestrator/tools/patch-tool-defs.mjs +33 -4
  147. package/src/runtime/agent/orchestrator/tools/patch.mjs +90 -6
  148. package/src/runtime/agent/orchestrator/tools/progress-message.mjs +6 -4
  149. package/src/runtime/agent/orchestrator/tools/result-compression.mjs +4 -4
  150. package/src/runtime/agent/orchestrator/tools/shell-command.mjs +8 -1
  151. package/src/runtime/agent/orchestrator/tools/shell-snapshot.mjs +4 -4
  152. package/src/runtime/channels/index.mjs +149 -51
  153. package/src/runtime/channels/lib/cli-worker-host.mjs +1 -8
  154. package/src/runtime/channels/lib/config.mjs +0 -1
  155. package/src/runtime/channels/lib/drop-trace.mjs +1 -1
  156. package/src/runtime/channels/lib/executor.mjs +0 -3
  157. package/src/runtime/channels/lib/memory-client.mjs +0 -38
  158. package/src/runtime/channels/lib/output-forwarder.mjs +1 -8
  159. package/src/runtime/channels/lib/runtime-paths.mjs +0 -6
  160. package/src/runtime/channels/lib/scheduler.mjs +18 -14
  161. package/src/runtime/channels/lib/session-discovery.mjs +3 -6
  162. package/src/runtime/channels/lib/tool-format.mjs +0 -1
  163. package/src/runtime/channels/lib/transcript-discovery.mjs +4 -12
  164. package/src/runtime/channels/lib/webhook.mjs +1 -1
  165. package/src/runtime/channels/tool-defs.mjs +29 -29
  166. package/src/runtime/lib/keychain-cjs.cjs +0 -1
  167. package/src/runtime/memory/data/runtime-manifest.json +6 -7
  168. package/src/runtime/memory/index.mjs +519 -118
  169. package/src/runtime/memory/lib/agent-ipc.mjs +29 -12
  170. package/src/runtime/memory/lib/core-memory-store.mjs +2 -2
  171. package/src/runtime/memory/lib/embedding-model-config.mjs +55 -0
  172. package/src/runtime/memory/lib/embedding-provider.mjs +31 -4
  173. package/src/runtime/memory/lib/embedding-worker.mjs +19 -10
  174. package/src/runtime/memory/lib/llm-worker-host.mjs +0 -4
  175. package/src/runtime/memory/lib/memory-cycle1.mjs +28 -7
  176. package/src/runtime/memory/lib/memory-cycle2.mjs +4 -5
  177. package/src/runtime/memory/lib/memory-cycle3.mjs +2 -2
  178. package/src/runtime/memory/lib/memory-ops-policy.mjs +2 -2
  179. package/src/runtime/memory/lib/memory-session-merge.mjs +38 -0
  180. package/src/runtime/memory/lib/memory.mjs +88 -9
  181. package/src/runtime/memory/lib/model-profile.mjs +1 -1
  182. package/src/runtime/memory/lib/pg/adapter.mjs +1 -1
  183. package/src/runtime/memory/lib/pg/supervisor.mjs +12 -0
  184. package/src/runtime/memory/lib/runtime-fetcher.mjs +80 -21
  185. package/src/runtime/memory/lib/session-ingest.mjs +196 -0
  186. package/src/runtime/memory/lib/trace-store.mjs +96 -51
  187. package/src/runtime/memory/tool-defs.mjs +46 -37
  188. package/src/runtime/search/index.mjs +104 -473
  189. package/src/runtime/search/lib/config.mjs +0 -4
  190. package/src/runtime/search/lib/state.mjs +1 -15
  191. package/src/runtime/search/lib/web-tools.mjs +44 -25
  192. package/src/runtime/search/tool-defs.mjs +16 -23
  193. package/src/runtime/shared/abort-controller.mjs +1 -1
  194. package/src/runtime/shared/atomic-file.mjs +4 -3
  195. package/src/runtime/shared/background-tasks.mjs +122 -11
  196. package/src/runtime/shared/child-spawn-gate.mjs +139 -0
  197. package/src/runtime/shared/config.mjs +7 -4
  198. package/src/runtime/shared/err-text.mjs +131 -4
  199. package/src/runtime/shared/llm/cost.mjs +2 -2
  200. package/src/runtime/shared/llm/http-agent.mjs +23 -7
  201. package/src/runtime/shared/llm/index.mjs +34 -11
  202. package/src/runtime/shared/llm/usage-log.mjs +4 -4
  203. package/src/runtime/shared/markdown-frontmatter.mjs +56 -0
  204. package/src/runtime/shared/singleton-owner.mjs +104 -0
  205. package/src/runtime/shared/tool-execution-contract.mjs +199 -20
  206. package/src/runtime/shared/tool-execution-contract.test.mjs +183 -0
  207. package/src/runtime/shared/tool-surface.mjs +624 -95
  208. package/src/runtime/shared/user-data-guard.mjs +0 -2
  209. package/src/standalone/agent-task-status.mjs +203 -0
  210. package/src/standalone/agent-task-status.test.mjs +76 -0
  211. package/src/standalone/agent-tool.mjs +1913 -0
  212. package/src/standalone/channel-worker.mjs +370 -14
  213. package/src/standalone/explore-tool.mjs +165 -70
  214. package/src/standalone/folder-dialog.mjs +314 -0
  215. package/src/standalone/hook-bus.mjs +898 -22
  216. package/src/standalone/memory-runtime-proxy.mjs +320 -0
  217. package/src/standalone/projects.mjs +226 -0
  218. package/src/standalone/provider-admin.mjs +41 -24
  219. package/src/standalone/seeds.mjs +1 -78
  220. package/src/standalone/usage-dashboard.mjs +96 -8
  221. package/src/tui/App.jsx +4800 -2140
  222. package/src/tui/components/AnsiText.jsx +39 -28
  223. package/src/tui/components/ContextPanel.jsx +14 -4
  224. package/src/tui/components/Markdown.jsx +43 -77
  225. package/src/tui/components/MarkdownTable.jsx +9 -184
  226. package/src/tui/components/Message.jsx +28 -11
  227. package/src/tui/components/Picker.jsx +95 -56
  228. package/src/tui/components/PromptInput.jsx +428 -239
  229. package/src/tui/components/QueuedCommands.jsx +1 -1
  230. package/src/tui/components/SlashCommandPalette.jsx +27 -21
  231. package/src/tui/components/Spinner.jsx +67 -38
  232. package/src/tui/components/StatusLine.jsx +606 -38
  233. package/src/tui/components/TextEntryPanel.jsx +128 -9
  234. package/src/tui/components/ToolExecution.jsx +597 -362
  235. package/src/tui/components/TurnDone.jsx +3 -3
  236. package/src/tui/components/UsagePanel.jsx +3 -5
  237. package/src/tui/components/tool-output-format.mjs +499 -0
  238. package/src/tui/components/tool-output-format.test.mjs +312 -0
  239. package/src/tui/dist/index.mjs +9192 -2399
  240. package/src/tui/engine-runtime-notification.test.mjs +115 -0
  241. package/src/tui/engine-tool-result-text.test.mjs +75 -0
  242. package/src/tui/engine.mjs +1455 -279
  243. package/src/tui/figures.mjs +21 -40
  244. package/src/tui/index.jsx +75 -31
  245. package/src/tui/input-editing.mjs +25 -0
  246. package/src/tui/markdown/format-token.mjs +671 -69
  247. package/src/tui/markdown/format-token.test.mjs +312 -0
  248. package/src/tui/markdown/render-ansi.mjs +94 -0
  249. package/src/tui/markdown/render-ansi.test.mjs +108 -0
  250. package/src/tui/markdown/stream-fence.mjs +34 -0
  251. package/src/tui/markdown/stream-fence.test.mjs +26 -0
  252. package/src/tui/markdown/table-layout.mjs +250 -0
  253. package/src/tui/paste-attachments.mjs +0 -7
  254. package/src/tui/spinner-verbs.mjs +1 -2
  255. package/src/tui/statusline-ansi-bridge.mjs +172 -0
  256. package/src/tui/statusline-ansi-bridge.test.mjs +159 -0
  257. package/src/tui/theme.mjs +756 -24
  258. package/src/tui/time-format.mjs +1 -1
  259. package/src/tui/transcript-tool-failures.mjs +67 -0
  260. package/src/tui/transcript-tool-failures.test.mjs +111 -0
  261. package/src/ui/ansi.mjs +1 -2
  262. package/src/ui/markdown.mjs +85 -26
  263. package/src/ui/markdown.test.mjs +70 -0
  264. package/src/ui/model-display.mjs +121 -0
  265. package/src/ui/session-stats.mjs +44 -0
  266. package/src/ui/statusline-context-label.test.mjs +15 -0
  267. package/src/ui/statusline.mjs +386 -178
  268. package/src/ui/tool-card.mjs +2 -13
  269. package/src/vendor/statusline/bin/statusline-lib.mjs +1 -620
  270. package/src/vendor/statusline/bin/statusline-route.mjs +169 -37
  271. package/src/vendor/statusline/bin/statusline-route.test.mjs +80 -0
  272. package/src/vendor/statusline/scripts/lib/gateway-settings.mjs +3 -3
  273. package/src/vendor/statusline/src/gateway/claude-current.mjs +1 -1
  274. package/src/vendor/statusline/src/gateway/route-meta.mjs +44 -6
  275. package/src/vendor/statusline/src/gateway/session-routes.mjs +1 -1
  276. package/src/workflows/default/WORKFLOW.md +12 -5
  277. package/src/workflows/default/workflow.json +0 -1
  278. package/src/workflows/solo/WORKFLOW.md +15 -0
  279. package/src/workflows/solo/workflow.json +7 -0
  280. package/vendor/ink/build/ink.js +54 -8
  281. package/vendor/ink/build/output.js +6 -1
  282. package/src/agents/scheduler-task.md +0 -3
  283. package/src/agents/web-researcher/AGENT.md +0 -3
  284. package/src/agents/web-researcher/agent.json +0 -6
  285. package/src/agents/webhook-handler.md +0 -3
  286. package/src/hooks/lib/permission-rules.cjs +0 -170
  287. package/src/hooks/lib/settings-loader.cjs +0 -112
  288. package/src/lib/hook-pipe-path.cjs +0 -10
  289. package/src/rules/bridge/00-common.md +0 -5
  290. package/src/rules/bridge/30-explorer.md +0 -4
  291. package/src/rules/lead/00-tool-lead.md +0 -5
  292. package/src/rules/shared/00-language.md +0 -3
  293. package/src/runtime/agent/orchestrator/tools/builtin/native-edit-runner.mjs +0 -110
  294. package/src/runtime/agent/orchestrator/tools/mutation-content-cache.mjs +0 -67
  295. package/src/runtime/channels/lib/hook-pipe-server.mjs +0 -671
  296. package/src/runtime/memory/lib/bridge-trace-queries.mjs +0 -120
  297. package/src/runtime/shared/llm/pid-cleanup.mjs +0 -27
  298. package/src/standalone/bridge-tool.mjs +0 -1414
  299. package/src/tui/runtime/shared/process-shutdown.mjs +0 -1
@@ -24,7 +24,6 @@ const RECOVERY_NOTICE = 'RECOVERY-REQUIRED.txt';
24
24
 
25
25
  const USER_DATA_FILES = [
26
26
  'mixdog-config.json',
27
- 'user-workflow.json',
28
27
  'user-workflow.md',
29
28
  'history/user.md',
30
29
  'history/bot.md',
@@ -33,7 +32,6 @@ const USER_DATA_FILES = [
33
32
  const USER_DATA_DIRS = [
34
33
  'schedules',
35
34
  'webhooks',
36
- 'roles',
37
35
  'workflows',
38
36
  ];
39
37
 
@@ -0,0 +1,203 @@
1
+ import {
2
+ agentWatchdogPolicyActive,
3
+ evaluateAgentWatchdogAbort,
4
+ } from '../runtime/agent/orchestrator/agent-runtime/agent-progress-watchdog.mjs';
5
+
6
+ const ACTIVE_RUNTIME_STAGES = new Set([
7
+ 'connecting',
8
+ 'requesting',
9
+ 'streaming',
10
+ 'tool_running',
11
+ 'running',
12
+ 'cancelling',
13
+ ]);
14
+
15
+ function positiveSeconds(now, ts) {
16
+ const n = Number(ts);
17
+ if (!Number.isFinite(n) || n <= 0) return null;
18
+ return Math.max(0, Math.floor((now - n) / 1000));
19
+ }
20
+
21
+ export function formatAgentWatchdogSummary(policy) {
22
+ if (!policy || !agentWatchdogPolicyActive(policy)) return null;
23
+ const parts = [];
24
+ if (policy.firstResponseMs > 0) parts.push(`first=${Math.round(policy.firstResponseMs / 1000)}s`);
25
+ if (policy.idleStaleMs > 0) parts.push(`idle=${Math.round(policy.idleStaleMs / 1000)}s`);
26
+ if (policy.toolRunningMs > 0) parts.push(`tool=${Math.round(policy.toolRunningMs / 1000)}s`);
27
+ return parts.length ? `armed ${parts.join(' ')}` : null;
28
+ }
29
+
30
+ export function resolveSilentForSeconds(now, snapshot, runtime) {
31
+ const last = Math.max(
32
+ snapshot?.lastProgressAt || 0,
33
+ runtime?.lastProgressAt || 0,
34
+ snapshot?.lastStreamDeltaAt || 0,
35
+ runtime?.lastStreamDeltaAt || 0,
36
+ snapshot?.toolStartedAt || 0,
37
+ runtime?.toolStartedAt || 0,
38
+ snapshot?.askStartedAt || 0,
39
+ runtime?.askStartedAt || 0,
40
+ );
41
+ return positiveSeconds(now, last);
42
+ }
43
+
44
+ export function buildAgentTaskProgressFields({
45
+ now = Date.now(),
46
+ sessionStatus = null,
47
+ runtimeStage = null,
48
+ snapshot = null,
49
+ runtime = null,
50
+ policy = null,
51
+ queuedFollowups = null,
52
+ taskStatus = null,
53
+ lastToolCall = null,
54
+ } = {}) {
55
+ const stage = cleanStage(runtimeStage || snapshot?.stage || sessionStatus || 'unknown');
56
+ const workerStage = stage;
57
+ const silentFor = resolveSilentForSeconds(now, snapshot, runtime);
58
+ const watchdog = formatAgentWatchdogSummary(policy);
59
+ const queued = Number.isFinite(Number(queuedFollowups)) && Number(queuedFollowups) > 0
60
+ ? Math.floor(Number(queuedFollowups))
61
+ : null;
62
+
63
+ const lastProgress = describeLastProgress({
64
+ stage,
65
+ snapshot,
66
+ runtime,
67
+ silentFor,
68
+ lastToolCall: lastToolCall || runtime?.lastToolCall || null,
69
+ now,
70
+ });
71
+
72
+ const diagnostic = describeAgentDiagnostic({
73
+ taskStatus,
74
+ sessionStatus,
75
+ stage,
76
+ snapshot,
77
+ silentFor,
78
+ queued,
79
+ policy,
80
+ now,
81
+ });
82
+
83
+ const out = {
84
+ worker_stage: workerStage,
85
+ last_progress: lastProgress,
86
+ diagnostic,
87
+ };
88
+ if (silentFor != null && ACTIVE_RUNTIME_STAGES.has(stage)) out.silent_for = silentFor;
89
+ if (watchdog) out.watchdog = watchdog;
90
+ if (queued != null) out.queued_followups = queued;
91
+ return out;
92
+ }
93
+
94
+ function cleanStage(value) {
95
+ return String(value ?? '').trim() || 'unknown';
96
+ }
97
+
98
+ function describeLastProgress({ stage, snapshot, runtime, silentFor, lastToolCall, now }) {
99
+ if (snapshot?.waitingForFirstActivity) return 'awaiting first model response';
100
+ if (stage === 'tool_running') {
101
+ const tool = cleanStage(lastToolCall);
102
+ return tool && tool !== 'unknown' ? `tool: ${tool}` : 'tool running';
103
+ }
104
+ if (stage === 'connecting' || stage === 'requesting') return 'connecting to model';
105
+ if (stage === 'streaming') {
106
+ const streamSilent = positiveSeconds(
107
+ now,
108
+ snapshot?.lastStreamDeltaAt || runtime?.lastStreamDeltaAt || 0,
109
+ );
110
+ if (streamSilent != null && streamSilent >= 5) {
111
+ return `streaming (no stream delta for ${streamSilent}s)`;
112
+ }
113
+ return 'streaming';
114
+ }
115
+ if (stage === 'cancelling') return 'cancelling';
116
+ if (stage === 'done' || stage === 'idle') {
117
+ if (runtime?.emptyFinal) return 'finished empty';
118
+ return 'idle';
119
+ }
120
+ if (stage === 'error') return 'error';
121
+ if (stage === 'closed') return 'closed';
122
+ if (silentFor != null && ACTIVE_RUNTIME_STAGES.has(stage)) {
123
+ return `active (${silentFor}s since last progress)`;
124
+ }
125
+ return stage;
126
+ }
127
+
128
+ function describeAgentDiagnostic({
129
+ taskStatus,
130
+ sessionStatus,
131
+ stage,
132
+ snapshot,
133
+ silentFor,
134
+ queued,
135
+ policy,
136
+ now,
137
+ }) {
138
+ const normalizedTask = cleanStage(taskStatus).toLowerCase();
139
+ if (normalizedTask === 'cancelled' || normalizedTask === 'canceled') return 'task cancelled';
140
+ if (normalizedTask === 'failed' || normalizedTask === 'error') return 'task failed';
141
+ if (normalizedTask === 'completed') {
142
+ return sessionStatus === 'error' ? 'task completed (worker error)' : 'task completed; worker idle';
143
+ }
144
+
145
+ if (queued) {
146
+ if (ACTIVE_RUNTIME_STAGES.has(stage)) {
147
+ return `${stage}, ${queued} follow-up${queued === 1 ? '' : 's'} queued`;
148
+ }
149
+ return `idle, ${queued} follow-up${queued === 1 ? '' : 's'} queued`;
150
+ }
151
+
152
+ if (snapshot?.waitingForFirstActivity) return 'waiting for first response';
153
+
154
+ if (stage === 'streaming') {
155
+ const streamSilent = positiveSeconds(
156
+ now,
157
+ snapshot?.lastStreamDeltaAt || 0,
158
+ );
159
+ if (streamSilent != null && streamSilent >= 5) {
160
+ return `streaming, no visible output yet (${streamSilent}s)`;
161
+ }
162
+ return 'streaming';
163
+ }
164
+
165
+ if (stage === 'tool_running') return 'tool running';
166
+ if (stage === 'connecting' || stage === 'requesting') return 'waiting for first response';
167
+
168
+ if (silentFor != null && policy && ACTIVE_RUNTIME_STAGES.has(stage)) {
169
+ const abortErr = evaluateAgentWatchdogAbort(
170
+ snapshot || { stage, lastProgressAt: now - silentFor * 1000 },
171
+ now,
172
+ policy,
173
+ );
174
+ if (abortErr) {
175
+ return `stale: ${abortErr.message.replace(/^agent /i, '')}`;
176
+ }
177
+ if (silentFor >= 30) {
178
+ return `stale: no stream/tool progress for ${silentFor}s`;
179
+ }
180
+ }
181
+
182
+ if (normalizedTask === 'running' && (stage === 'idle' || stage === 'done')) {
183
+ return 'task running; worker turn finished, awaiting job reconciliation';
184
+ }
185
+
186
+ if (stage === 'idle' || stage === 'done') return 'idle';
187
+ if (stage === 'cancelling') return 'cancelling';
188
+ if (stage === 'error') return 'worker error';
189
+ if (stage === 'closed') return 'closed';
190
+
191
+ return stage;
192
+ }
193
+
194
+ export function appendAgentProgressKv(line, fields = {}) {
195
+ const parts = [line];
196
+ if (fields.worker_stage) parts.push(`stage=${fields.worker_stage}`);
197
+ if (fields.last_progress) parts.push(`last_progress=${fields.last_progress}`);
198
+ if (Number.isFinite(fields.silent_for)) parts.push(`silent_for=${fields.silent_for}s`);
199
+ if (fields.watchdog) parts.push(`watchdog=${fields.watchdog}`);
200
+ if (Number.isFinite(fields.queued_followups)) parts.push(`queued_followups=${fields.queued_followups}`);
201
+ if (fields.diagnostic) parts.push(`hint=${fields.diagnostic}`);
202
+ return parts.join(' ');
203
+ }
@@ -0,0 +1,76 @@
1
+ import assert from 'node:assert/strict';
2
+ import test from 'node:test';
3
+ import {
4
+ appendAgentProgressKv,
5
+ buildAgentTaskProgressFields,
6
+ resolveSilentForSeconds,
7
+ } from './agent-task-status.mjs';
8
+
9
+ test('waiting for first activity explains silence', () => {
10
+ const now = 1_000_000;
11
+ const fields = buildAgentTaskProgressFields({
12
+ now,
13
+ sessionStatus: 'running',
14
+ runtimeStage: 'requesting',
15
+ snapshot: {
16
+ stage: 'requesting',
17
+ waitingForFirstActivity: true,
18
+ modelRequestStartedAt: now - 12_000,
19
+ askStartedAt: now - 12_000,
20
+ lastProgressAt: now - 12_000,
21
+ },
22
+ policy: { firstResponseMs: 120_000, idleStaleMs: 1_800_000, toolRunningMs: 600_000 },
23
+ taskStatus: 'running',
24
+ });
25
+ assert.equal(fields.last_progress, 'awaiting first model response');
26
+ assert.match(fields.diagnostic, /waiting for first response/);
27
+ assert.match(fields.watchdog, /first=120s/);
28
+ });
29
+
30
+ test('streaming without recent deltas is called out', () => {
31
+ const now = 2_000_000;
32
+ const fields = buildAgentTaskProgressFields({
33
+ now,
34
+ sessionStatus: 'running',
35
+ runtimeStage: 'streaming',
36
+ snapshot: {
37
+ stage: 'streaming',
38
+ waitingForFirstActivity: false,
39
+ lastStreamDeltaAt: now - 23_000,
40
+ lastProgressAt: now - 23_000,
41
+ },
42
+ taskStatus: 'running',
43
+ });
44
+ assert.match(fields.last_progress, /no stream delta for 23s/);
45
+ assert.match(fields.diagnostic, /no visible output yet \(23s\)/);
46
+ assert.equal(fields.silent_for, 23);
47
+ });
48
+
49
+ test('queued follow-ups surface on idle worker', () => {
50
+ const fields = buildAgentTaskProgressFields({
51
+ sessionStatus: 'idle',
52
+ runtimeStage: 'streaming',
53
+ queuedFollowups: 2,
54
+ taskStatus: 'running',
55
+ });
56
+ assert.equal(fields.queued_followups, 2);
57
+ assert.match(fields.diagnostic, /2 follow-ups queued/);
58
+ });
59
+
60
+ test('resolveSilentForSeconds uses latest progress signal', () => {
61
+ const now = 10_000;
62
+ const silent = resolveSilentForSeconds(now, { lastProgressAt: now - 5_000 }, { lastStreamDeltaAt: now - 9_000 });
63
+ assert.equal(silent, 5);
64
+ });
65
+
66
+ test('appendAgentProgressKv keeps base line first', () => {
67
+ const line = appendAgentProgressKv('- job1 spawn running', {
68
+ worker_stage: 'streaming',
69
+ silent_for: 4,
70
+ diagnostic: 'streaming',
71
+ });
72
+ assert.match(line, /^- job1 spawn running /);
73
+ assert.match(line, /stage=streaming/);
74
+ assert.match(line, /hint=streaming/);
75
+ });
76
+