@duckmind/dm-windows-x64 0.61.4 → 0.61.9

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 (279) hide show
  1. package/dm.exe +0 -0
  2. package/extensions/.dm-extensions.json +211 -67
  3. package/extensions/dm-subagents/agents/claude-code-writer.md +15 -0
  4. package/extensions/dm-subagents/agents/claude-code.md +15 -0
  5. package/extensions/dm-subagents/agents/codex-exec-writer.md +15 -0
  6. package/extensions/dm-subagents/agents/codex-exec.md +15 -0
  7. package/extensions/dm-subagents/agents/cursor-agent-writer.md +14 -0
  8. package/extensions/dm-subagents/agents/cursor-agent.md +14 -0
  9. package/extensions/dm-subagents/agents/delegate.md +3 -2
  10. package/extensions/dm-subagents/agents/oracle.md +10 -5
  11. package/extensions/dm-subagents/agents/researcher.md +2 -2
  12. package/extensions/dm-subagents/agents/reviewer.md +17 -7
  13. package/extensions/dm-subagents/agents/scout.md +5 -5
  14. package/extensions/dm-subagents/agents/worker.md +6 -2
  15. package/extensions/dm-subagents/async-retention-discovery-worker.mjs +167 -0
  16. package/extensions/dm-subagents/index.js +4 -0
  17. package/extensions/dm-subagents/inspector-runner.mjs +10 -0
  18. package/extensions/dm-subagents/install.mjs +3 -2
  19. package/extensions/dm-subagents/package.json +2 -2
  20. package/extensions/dm-subagents/prompts/council.md +60 -0
  21. package/extensions/dm-subagents/prompts/parallel-review.md +5 -1
  22. package/extensions/dm-subagents/prompts/review-loop.md +13 -7
  23. package/extensions/dm-subagents/skills/council-mode/SKILL.md +59 -0
  24. package/extensions/dm-subagents/skills/council-mode/references/pass-contracts.md +150 -0
  25. package/extensions/dm-subagents/skills/dm-subagents/SKILL.md +96 -911
  26. package/extensions/dm-subagents/skills/dm-subagents/references/constraints-and-recipes.md +70 -0
  27. package/extensions/dm-subagents/skills/dm-subagents/references/execution-controls.md +539 -0
  28. package/extensions/dm-subagents/skills/dm-subagents/references/management-authoring-rpc.md +161 -0
  29. package/extensions/dm-subagents/skills/dm-subagents/references/multi-lane-orchestration.md +51 -0
  30. package/extensions/dm-subagents/skills/dm-subagents/references/prompting-and-roles.md +295 -0
  31. package/extensions/dm-subagents/skills/dm-subagents/references/review-and-validation.md +73 -0
  32. package/extensions/dm-subagents/src/agents/agent-management.js +716 -484
  33. package/extensions/dm-subagents/src/agents/agent-refinements.js +563 -0
  34. package/extensions/dm-subagents/src/agents/agent-serializer.js +70 -5
  35. package/extensions/dm-subagents/src/agents/agents.js +1447 -299
  36. package/extensions/dm-subagents/src/agents/builtin-names.js +15 -0
  37. package/extensions/dm-subagents/src/agents/chain-serializer.js +12 -7
  38. package/extensions/dm-subagents/src/agents/frontmatter.js +64 -14
  39. package/extensions/dm-subagents/src/agents/identity.js +1 -1
  40. package/extensions/dm-subagents/src/agents/proactive-skills.js +14 -11
  41. package/extensions/dm-subagents/src/agents/runtime-agent-events.js +49 -0
  42. package/extensions/dm-subagents/src/agents/runtime-agent-registry.js +412 -0
  43. package/extensions/dm-subagents/src/agents/skills.js +52 -35
  44. package/extensions/dm-subagents/src/api/agents.js +6 -0
  45. package/extensions/dm-subagents/src/api/background-work.js +151 -0
  46. package/extensions/dm-subagents/src/api/capability-ceiling.js +12 -0
  47. package/extensions/dm-subagents/src/api/control-channel.js +3 -0
  48. package/extensions/dm-subagents/src/api/delegation.js +5 -0
  49. package/extensions/dm-subagents/src/api/dm-args.js +3 -0
  50. package/extensions/dm-subagents/src/api/external-job-provider.js +137 -0
  51. package/extensions/dm-subagents/src/api/external-runs.js +233 -0
  52. package/extensions/dm-subagents/src/api/intercom-bridge.js +3 -0
  53. package/extensions/dm-subagents/src/api/preflight.js +322 -0
  54. package/extensions/dm-subagents/src/api/project-panes.js +11 -0
  55. package/extensions/dm-subagents/src/api/shared-types.js +4 -0
  56. package/extensions/dm-subagents/src/extension/config.js +175 -0
  57. package/extensions/dm-subagents/src/extension/control-notices.js +4 -43
  58. package/extensions/dm-subagents/src/extension/doctor.js +71 -17
  59. package/extensions/dm-subagents/src/extension/fanout-child.js +49 -32
  60. package/extensions/dm-subagents/src/extension/index.js +711 -265
  61. package/extensions/dm-subagents/src/extension/public-execution.js +114 -0
  62. package/extensions/dm-subagents/src/extension/rpc.js +427 -22
  63. package/extensions/dm-subagents/src/extension/schemas.js +158 -65
  64. package/extensions/dm-subagents/src/extension/steering-notices.js +23 -0
  65. package/extensions/dm-subagents/src/extension/subagent-guide.js +31 -0
  66. package/extensions/dm-subagents/src/extension/tool-description.js +92 -74
  67. package/extensions/dm-subagents/src/extension/tool-result.js +7 -0
  68. package/extensions/dm-subagents/src/inspectors/herdr/actions.js +218 -0
  69. package/extensions/dm-subagents/src/inspectors/herdr/client.js +123 -0
  70. package/extensions/dm-subagents/src/inspectors/herdr/focus.js +47 -0
  71. package/extensions/dm-subagents/src/inspectors/herdr/inspector-runner.js +160 -0
  72. package/extensions/dm-subagents/src/inspectors/herdr/project-panes.js +618 -0
  73. package/extensions/dm-subagents/src/inspectors/herdr/session-roots-codec.js +21 -0
  74. package/extensions/dm-subagents/src/inspectors/herdr/shell-command.js +15 -0
  75. package/extensions/dm-subagents/src/integrations/herdr-status.js +377 -0
  76. package/extensions/dm-subagents/src/intercom/intercom-bridge.js +17 -13
  77. package/extensions/dm-subagents/src/intercom/native-supervisor-channel.js +371 -79
  78. package/extensions/dm-subagents/src/intercom/result-intercom.js +47 -7
  79. package/extensions/dm-subagents/src/missions/actions.js +394 -0
  80. package/extensions/dm-subagents/src/missions/goal-driver.js +149 -0
  81. package/extensions/dm-subagents/src/missions/lifecycle.js +331 -0
  82. package/extensions/dm-subagents/src/missions/store.js +548 -0
  83. package/extensions/dm-subagents/src/missions/types.js +9 -0
  84. package/extensions/dm-subagents/src/missions/workflow-state.js +245 -0
  85. package/extensions/dm-subagents/src/policy/authority.js +37 -0
  86. package/extensions/dm-subagents/src/profiles/profiles.js +36 -18
  87. package/extensions/dm-subagents/src/runs/background/active-async-capacity.js +427 -0
  88. package/extensions/dm-subagents/src/runs/background/active-run-index.js +122 -0
  89. package/extensions/dm-subagents/src/runs/background/async-execution.js +925 -137
  90. package/extensions/dm-subagents/src/runs/background/async-job-tracker.js +515 -148
  91. package/extensions/dm-subagents/src/runs/background/async-resume.js +378 -51
  92. package/extensions/dm-subagents/src/runs/background/async-retention.js +828 -0
  93. package/extensions/dm-subagents/src/runs/background/async-status-snapshot.js +31 -0
  94. package/extensions/dm-subagents/src/runs/background/async-status.js +259 -22
  95. package/extensions/dm-subagents/src/runs/background/auto-drain.js +46 -0
  96. package/extensions/dm-subagents/src/runs/background/chain-append.js +50 -15
  97. package/extensions/dm-subagents/src/runs/background/chain-root-attachment.js +67 -12
  98. package/extensions/dm-subagents/src/runs/background/completion-batcher.js +5 -1
  99. package/extensions/dm-subagents/src/runs/background/completion-dedupe.js +3 -11
  100. package/extensions/dm-subagents/src/runs/background/completion-replay.js +245 -0
  101. package/extensions/dm-subagents/src/runs/background/control-channel.js +423 -36
  102. package/extensions/dm-subagents/src/runs/background/fleet-view.js +132 -59
  103. package/extensions/dm-subagents/src/runs/background/index-segment.js +38 -0
  104. package/extensions/dm-subagents/src/runs/background/inspect-rpc.js +373 -0
  105. package/extensions/dm-subagents/src/runs/background/notify.js +357 -57
  106. package/extensions/dm-subagents/src/runs/background/owned-process-tree.js +86 -0
  107. package/extensions/dm-subagents/src/runs/background/process-terminal.js +269 -0
  108. package/extensions/dm-subagents/src/runs/background/result-delivery-ownership.js +34 -0
  109. package/extensions/dm-subagents/src/runs/background/result-files.js +469 -0
  110. package/extensions/dm-subagents/src/runs/background/result-watcher.js +540 -75
  111. package/extensions/dm-subagents/src/runs/background/resume-guidance.js +44 -0
  112. package/extensions/dm-subagents/src/runs/background/retained-children.js +119 -0
  113. package/extensions/dm-subagents/src/runs/background/run-id-query.js +5 -0
  114. package/extensions/dm-subagents/src/runs/background/run-id-resolver.js +93 -9
  115. package/extensions/dm-subagents/src/runs/background/run-status.js +303 -32
  116. package/extensions/dm-subagents/src/runs/background/scheduled-runs.js +784 -376
  117. package/extensions/dm-subagents/src/runs/background/stale-run-reconciler.js +75 -34
  118. package/extensions/dm-subagents/src/runs/background/steering.js +221 -0
  119. package/extensions/dm-subagents/src/runs/background/subagent-runner.js +3106 -844
  120. package/extensions/dm-subagents/src/runs/background/subagent-wait.js +529 -0
  121. package/extensions/dm-subagents/src/runs/background/terminal-run-index.js +106 -0
  122. package/extensions/dm-subagents/src/runs/background/top-level-async.js +1 -1
  123. package/extensions/dm-subagents/src/runs/background/wait-completions.js +155 -0
  124. package/extensions/dm-subagents/src/runs/background/wait-config.js +46 -0
  125. package/extensions/dm-subagents/src/runs/background/wait-subscriptions.js +278 -0
  126. package/extensions/dm-subagents/src/runs/background/wait-tool.js +47 -0
  127. package/extensions/dm-subagents/src/runs/foreground/async-dismiss-action.js +81 -0
  128. package/extensions/dm-subagents/src/runs/foreground/async-steering-action.js +245 -0
  129. package/extensions/dm-subagents/src/runs/foreground/async-stop-action.js +74 -0
  130. package/extensions/dm-subagents/src/runs/foreground/execution.js +1401 -342
  131. package/extensions/dm-subagents/src/runs/foreground/foreground-control.js +133 -0
  132. package/extensions/dm-subagents/src/runs/foreground/foreground-history.js +148 -0
  133. package/extensions/dm-subagents/src/runs/foreground/prompt-audit.js +139 -0
  134. package/extensions/dm-subagents/src/runs/foreground/subagent-executor.js +4278 -1441
  135. package/extensions/dm-subagents/src/runs/foreground/workflow-detach-reconcile.js +278 -0
  136. package/extensions/dm-subagents/src/runs/foreground/workflow-foreground-steering.js +155 -0
  137. package/extensions/dm-subagents/src/runs/shared/abort-recovery.js +97 -0
  138. package/extensions/dm-subagents/src/runs/shared/acceptance.js +597 -148
  139. package/extensions/dm-subagents/src/runs/shared/agent-contract.js +35 -0
  140. package/extensions/dm-subagents/src/runs/shared/async-status-projection.js +472 -0
  141. package/extensions/dm-subagents/src/runs/shared/background-process-options.js +6 -0
  142. package/extensions/dm-subagents/src/runs/shared/capability-ceiling.js +175 -0
  143. package/extensions/dm-subagents/src/runs/shared/child-identity.js +32 -0
  144. package/extensions/dm-subagents/src/runs/shared/child-launch-plan.js +65 -0
  145. package/extensions/dm-subagents/src/runs/shared/child-protocol.js +447 -0
  146. package/extensions/dm-subagents/src/runs/shared/claude-code-adapter.js +120 -0
  147. package/extensions/dm-subagents/src/runs/shared/codex-exec-adapter.js +129 -0
  148. package/extensions/dm-subagents/src/runs/shared/completion-evidence.js +40 -0
  149. package/extensions/dm-subagents/src/runs/shared/completion-guard.js +140 -83
  150. package/extensions/dm-subagents/src/runs/shared/context-mode.js +38 -0
  151. package/extensions/dm-subagents/src/runs/shared/cursor-agent-adapter.js +101 -0
  152. package/extensions/dm-subagents/src/runs/shared/dm-args.js +445 -72
  153. package/extensions/dm-subagents/src/runs/shared/dm-spawn.js +27 -16
  154. package/extensions/dm-subagents/src/runs/shared/dynamic-fanout.js +19 -6
  155. package/extensions/dm-subagents/src/runs/shared/extension-bindings.js +81 -0
  156. package/extensions/dm-subagents/src/runs/shared/external-cli-contract.js +134 -0
  157. package/extensions/dm-subagents/src/runs/shared/external-cli-preflight.js +98 -0
  158. package/extensions/dm-subagents/src/runs/shared/external-cli-runner.js +419 -0
  159. package/extensions/dm-subagents/src/runs/shared/external-job-bridge.js +404 -0
  160. package/extensions/dm-subagents/src/runs/shared/external-job-runner.js +334 -0
  161. package/extensions/dm-subagents/src/runs/shared/fast-mode-extension.js +8 -0
  162. package/extensions/dm-subagents/src/runs/shared/host-step-status.js +228 -0
  163. package/extensions/dm-subagents/src/runs/shared/lane-metadata.js +104 -0
  164. package/extensions/dm-subagents/src/runs/shared/launch-cwd.js +17 -0
  165. package/extensions/dm-subagents/src/runs/shared/llm-intent-arbiter.js +190 -0
  166. package/extensions/dm-subagents/src/runs/shared/long-running-guard.js +48 -3
  167. package/extensions/dm-subagents/src/runs/shared/mcp-config-sources.js +387 -0
  168. package/extensions/dm-subagents/src/runs/shared/mcp-direct-tool-allowlist.js +212 -137
  169. package/extensions/dm-subagents/src/runs/shared/mcp-direct-tool-grant.js +131 -0
  170. package/extensions/dm-subagents/src/runs/shared/model-exclusions.js +207 -0
  171. package/extensions/dm-subagents/src/runs/shared/model-fallback.js +225 -55
  172. package/extensions/dm-subagents/src/runs/shared/model-scope.js +85 -28
  173. package/extensions/dm-subagents/src/runs/shared/mutation-evidence.js +182 -0
  174. package/extensions/dm-subagents/src/runs/shared/nested-events.js +264 -102
  175. package/extensions/dm-subagents/src/runs/shared/nested-render.js +15 -5
  176. package/extensions/dm-subagents/src/runs/shared/orca-progress-tabs.js +505 -0
  177. package/extensions/dm-subagents/src/runs/shared/parallel-handoff.js +653 -0
  178. package/extensions/dm-subagents/src/runs/shared/parallel-utils.js +29 -12
  179. package/extensions/dm-subagents/src/runs/shared/permissions.js +108 -0
  180. package/extensions/dm-subagents/src/runs/shared/process-signal.js +13 -0
  181. package/extensions/dm-subagents/src/runs/shared/run-fanout-budget.js +257 -0
  182. package/extensions/dm-subagents/src/runs/shared/run-history.js +133 -13
  183. package/extensions/dm-subagents/src/runs/shared/runtime-acknowledged-extensions.js +62 -0
  184. package/extensions/dm-subagents/src/runs/shared/session-lease.js +225 -0
  185. package/extensions/dm-subagents/src/runs/shared/single-output.js +129 -27
  186. package/extensions/dm-subagents/src/runs/shared/spawn-budget.js +95 -0
  187. package/extensions/dm-subagents/src/runs/shared/structured-output.js +129 -10
  188. package/extensions/dm-subagents/src/runs/shared/subagent-control.js +66 -11
  189. package/extensions/dm-subagents/src/runs/shared/subagent-prompt-runtime.js +548 -70
  190. package/extensions/dm-subagents/src/runs/shared/subagent-startup-retry.js +50 -0
  191. package/extensions/dm-subagents/src/runs/shared/task-intent.js +130 -0
  192. package/extensions/dm-subagents/src/runs/shared/tool-availability.js +59 -0
  193. package/extensions/dm-subagents/src/runs/shared/tool-budget.js +7 -5
  194. package/extensions/dm-subagents/src/runs/shared/tool-timeout.js +64 -0
  195. package/extensions/dm-subagents/src/runs/shared/usage-budget.js +74 -0
  196. package/extensions/dm-subagents/src/runs/shared/workflow-graph.js +22 -0
  197. package/extensions/dm-subagents/src/runs/shared/worktree-cleanup-plan.js +721 -0
  198. package/extensions/dm-subagents/src/runs/shared/worktree.js +168 -19
  199. package/extensions/dm-subagents/src/shared/accessible-dir.js +35 -0
  200. package/extensions/dm-subagents/src/shared/agent-stream-options.js +3 -0
  201. package/extensions/dm-subagents/src/shared/artifacts.js +170 -11
  202. package/extensions/dm-subagents/src/shared/atomic-json.js +36 -38
  203. package/extensions/dm-subagents/src/shared/capacity-resilient-json.js +77 -0
  204. package/extensions/dm-subagents/src/shared/child-session-name.js +15 -0
  205. package/extensions/dm-subagents/src/shared/child-transcript.js +57 -2
  206. package/extensions/dm-subagents/src/shared/completion-owner.js +7 -0
  207. package/extensions/dm-subagents/src/shared/display-text.js +142 -0
  208. package/extensions/dm-subagents/src/shared/extension-context.js +17 -0
  209. package/extensions/dm-subagents/src/shared/file-coalescer.js +9 -0
  210. package/extensions/dm-subagents/src/shared/file-system-retry.js +56 -0
  211. package/extensions/dm-subagents/src/shared/fork-context.js +96 -33
  212. package/extensions/dm-subagents/src/shared/formatters.js +21 -7
  213. package/extensions/dm-subagents/src/shared/launch-contract.js +94 -0
  214. package/extensions/dm-subagents/src/shared/model-info.js +10 -5
  215. package/extensions/dm-subagents/src/shared/node-executable.js +19 -0
  216. package/extensions/dm-subagents/src/shared/prompt-resources.js +10 -0
  217. package/extensions/dm-subagents/src/shared/pruned-fork.js +427 -0
  218. package/extensions/dm-subagents/src/shared/session-file-trust.js +19 -0
  219. package/extensions/dm-subagents/src/shared/session-tokens.js +14 -3
  220. package/extensions/dm-subagents/src/shared/settings.js +39 -47
  221. package/extensions/dm-subagents/src/shared/shortcuts.js +16 -0
  222. package/extensions/dm-subagents/src/shared/status-format.js +9 -2
  223. package/extensions/dm-subagents/src/shared/thinking-ceiling.js +41 -0
  224. package/extensions/dm-subagents/src/shared/types.js +47 -7
  225. package/extensions/dm-subagents/src/shared/utf8.js +12 -0
  226. package/extensions/dm-subagents/src/shared/utils.js +151 -131
  227. package/extensions/dm-subagents/src/shared/watch-strategy.js +3 -0
  228. package/extensions/dm-subagents/src/shared/workflow-child-permit.js +84 -0
  229. package/extensions/dm-subagents/src/slash/delegation-adapters.js +274 -0
  230. package/extensions/dm-subagents/src/slash/delegation-json.js +113 -0
  231. package/extensions/dm-subagents/src/slash/delegation-request.js +152 -0
  232. package/extensions/dm-subagents/src/slash/prompt-template-bridge.js +294 -243
  233. package/extensions/dm-subagents/src/slash/prompt-workflows.js +35 -73
  234. package/extensions/dm-subagents/src/slash/selector.js +101 -0
  235. package/extensions/dm-subagents/src/slash/slash-bridge.js +17 -1
  236. package/extensions/dm-subagents/src/slash/slash-commands.js +722 -732
  237. package/extensions/dm-subagents/src/slash/slash-live-state.js +37 -19
  238. package/extensions/dm-subagents/src/slash/subagents-admin.js +410 -0
  239. package/extensions/dm-subagents/src/tui/fleet-status.js +824 -0
  240. package/extensions/dm-subagents/src/tui/fleet-transcript.js +479 -0
  241. package/extensions/dm-subagents/src/tui/fleet.js +1326 -0
  242. package/extensions/dm-subagents/src/tui/render-helpers.js +22 -0
  243. package/extensions/dm-subagents/src/tui/render.js +1511 -261
  244. package/extensions/dm-subagents/src/watchdog/change-signature.js +220 -0
  245. package/extensions/dm-subagents/src/watchdog/child-status.js +151 -0
  246. package/extensions/dm-subagents/src/watchdog/emission-guard.js +90 -0
  247. package/extensions/dm-subagents/src/watchdog/lsp-diagnostics.js +484 -0
  248. package/extensions/dm-subagents/src/watchdog/model-selection.js +154 -0
  249. package/extensions/dm-subagents/src/watchdog/permission-arbiter.js +138 -0
  250. package/extensions/dm-subagents/src/watchdog/register-child.js +112 -0
  251. package/extensions/dm-subagents/src/watchdog/register-main.js +419 -0
  252. package/extensions/dm-subagents/src/watchdog/render.js +54 -0
  253. package/extensions/dm-subagents/src/watchdog/review.js +251 -0
  254. package/extensions/dm-subagents/src/watchdog/runtime.js +803 -0
  255. package/extensions/dm-subagents/src/watchdog/scope.js +56 -0
  256. package/extensions/dm-subagents/src/watchdog/settings.js +515 -0
  257. package/extensions/dm-subagents/src/watchdog/tool-actions.js +151 -0
  258. package/extensions/dm-subagents/src/watchdog/turn-delta.js +169 -0
  259. package/extensions/dm-subagents/src/watchdog/types.js +29 -0
  260. package/extensions/dm-subagents/src/watchdog/warning-format.js +58 -0
  261. package/extensions/dm-subagents/src/workflows/chat-progress.js +116 -0
  262. package/extensions/dm-subagents/src/workflows/host-command.js +227 -0
  263. package/extensions/dm-subagents/src/workflows/scripted-workflow.js +2011 -0
  264. package/extensions/dm-subagents/src/workflows/workflow-child-summary.js +116 -0
  265. package/extensions/dm-subagents/src/workflows/workflow-preflight.js +243 -0
  266. package/extensions/dm-subagents/src/workflows/workflow-receipt.js +387 -0
  267. package/extensions/dm-subagents/src/workflows/workflow-settlement.js +189 -0
  268. package/package.json +4 -3
  269. package/extensions/dm-fff/package.json +0 -21
  270. package/extensions/dm-fff/src/index.js +0 -691
  271. package/extensions/dm-fff/src/query.js +0 -60
  272. package/extensions/dm-subagents/agents/context-builder.md +0 -46
  273. package/extensions/dm-subagents/agents/planner.md +0 -55
  274. package/extensions/dm-subagents/prompts/parallel-context-build.md +0 -55
  275. package/extensions/dm-subagents/prompts/parallel-handoff-plan.md +0 -61
  276. package/extensions/dm-subagents/src/runs/background/wait.js +0 -206
  277. package/extensions/dm-subagents/src/runs/foreground/chain-clarify.js +0 -1013
  278. package/extensions/dm-subagents/src/runs/foreground/chain-execution.js +0 -981
  279. package/extensions/dm-subagents/src/runs/shared/turn-budget.js +0 -50
@@ -1,14 +1,16 @@
1
1
  import * as fs from "node:fs";
2
2
  import * as path from "node:path";
3
+ import { safeTerminalText } from "../../shared/display-text.js";
3
4
  import { formatDuration, formatModelThinking, formatTokens, shortenPath } from "../../shared/formatters.js";
4
5
  import { formatActivityLabel } from "../../shared/status-format.js";
5
6
  import {
6
- ASYNC_DIR,
7
- RESULTS_DIR
7
+ DIRS
8
8
  } from "../../shared/types.js";
9
9
  import { readStatus } from "../../shared/utils.js";
10
10
  import { formatNestedRunStatusLines } from "../shared/nested-render.js";
11
+ import { contextModeLabel, summarizeContextModes } from "../shared/context-mode.js";
11
12
  import { formatAsyncRunOutputPath, formatAsyncRunProgressLabel, listAsyncRuns } from "./async-status.js";
13
+ import { isTrustedRecordedSessionFile } from "../../shared/session-file-trust.js";
12
14
  const DEFAULT_TRANSCRIPT_LINES = 80;
13
15
  const MAX_TRANSCRIPT_LINES = 500;
14
16
  const TRANSCRIPT_TAIL_BYTES = 256 * 1024;
@@ -30,6 +32,12 @@ function uniqueStrings(values) {
30
32
  }
31
33
  return result;
32
34
  }
35
+ function fleetChildDisplayName(child, fallback = "subagent") {
36
+ return child.sessionName?.trim() || child.agent || fallback;
37
+ }
38
+ function fleetStepDisplayName(step) {
39
+ return step.sessionName?.trim() || (step.label ? `${step.label} (${step.agent})` : step.agent);
40
+ }
33
41
  function resolveMaybeRelative(asyncDir, filePath) {
34
42
  if (!filePath)
35
43
  return;
@@ -78,11 +86,12 @@ function readTextTail(filePath, maxLines) {
78
86
  fs.closeSync(fd);
79
87
  }
80
88
  }
81
- function readContainedTextTail(filePath, maxLines, trustedRoots, label) {
82
- if (trustedRoots.length === 0)
89
+ function readContainedTextTail(filePath, maxLines, trustedRoots, label, trustedFiles = [], trustedFileRoot) {
90
+ if (trustedRoots.length === 0 && (!trustedFileRoot || trustedFiles.length === 0))
83
91
  return { path: filePath, lines: [], truncated: false, error: `Refusing to read ${label} transcript path without a trusted root: ${filePath}` };
84
92
  const resolvedPath = path.resolve(filePath);
85
- if (!trustedRoots.some((root) => pathWithin(root, resolvedPath))) {
93
+ const recordedCandidate = trustedFileRoot && pathWithin(trustedFileRoot, resolvedPath) && trustedFiles.some((file) => path.resolve(file) === resolvedPath);
94
+ if (!trustedRoots.some((root) => pathWithin(root, resolvedPath)) && !recordedCandidate) {
86
95
  return { path: filePath, lines: [], truncated: false, error: `Refusing to read ${label} transcript path outside trusted roots: ${filePath}` };
87
96
  }
88
97
  let lstat;
@@ -105,7 +114,7 @@ function readContainedTextTail(filePath, maxLines, trustedRoots, label) {
105
114
  } catch (error) {
106
115
  return { path: filePath, lines: [], truncated: false, error: getErrorMessage(error) };
107
116
  }
108
- if (!realRoots.some((root) => pathWithin(root, realPath))) {
117
+ if (!realRoots.some((root) => pathWithin(root, realPath)) && !isTrustedRecordedSessionFile(realPath, trustedFiles, trustedFileRoot)) {
109
118
  return { path: filePath, lines: [], truncated: false, error: `Refusing to read ${label} transcript path outside trusted roots: ${filePath}` };
110
119
  }
111
120
  return readTextTail(resolvedPath, maxLines);
@@ -118,45 +127,84 @@ function stringifyJsonPreview(value, maxLength = 240) {
118
127
  raw = JSON.stringify(value);
119
128
  return raw.length > maxLength ? `${raw.slice(0, maxLength)}…` : raw;
120
129
  }
121
- function contentText(content) {
130
+ function sessionMessageParts(record) {
131
+ if (!record || typeof record !== "object")
132
+ return [];
133
+ const outer = record;
134
+ const message = outer.message && typeof outer.message === "object" ? outer.message : outer;
135
+ const role = typeof message.role === "string" ? message.role : undefined;
136
+ if (!role)
137
+ return [];
138
+ const content = message.content;
122
139
  if (typeof content === "string")
123
- return content;
140
+ return content.trim() ? [{ role, kind: "text", text: content }] : [];
124
141
  if (!Array.isArray(content))
125
- return "";
126
- return content.map((part) => {
142
+ return [];
143
+ const parts = [];
144
+ for (const part of content) {
127
145
  if (!part || typeof part !== "object")
128
- return "";
146
+ continue;
129
147
  const entry = part;
130
- if (typeof entry.text === "string")
131
- return entry.text;
148
+ if (typeof entry.text === "string") {
149
+ if (entry.text.trim())
150
+ parts.push({ role, kind: "text", text: entry.text });
151
+ continue;
152
+ }
132
153
  if (entry.type === "toolCall" || entry.type === "tool_call") {
133
154
  const name = typeof entry.name === "string" ? entry.name : typeof entry.toolName === "string" ? entry.toolName : "tool";
134
- return `[tool: ${name}${entry.args === undefined ? "" : ` ${stringifyJsonPreview(entry.args)}`}]`;
155
+ parts.push({ role, kind: "toolCall", name, text: `[tool: ${name}${entry.args === undefined ? "" : ` ${stringifyJsonPreview(entry.args)}`}]` });
156
+ continue;
135
157
  }
136
158
  if (entry.type === "toolResult" || entry.type === "tool_result") {
137
- return `[tool result${entry.result === undefined ? "" : `: ${stringifyJsonPreview(entry.result)}`}]`;
159
+ const name = typeof entry.name === "string" ? entry.name : typeof entry.toolName === "string" ? entry.toolName : undefined;
160
+ parts.push({ role, kind: "toolResult", ...name ? { name } : {}, ...entry.isError === true ? { isError: true } : {}, text: `[tool result${entry.result === undefined ? "" : `: ${stringifyJsonPreview(entry.result)}`}]` });
161
+ continue;
138
162
  }
139
- if (entry.content !== undefined)
140
- return stringifyJsonPreview(entry.content);
141
- return "";
142
- }).filter(Boolean).join(`
143
- `);
163
+ if (entry.content !== undefined) {
164
+ const preview = stringifyJsonPreview(entry.content);
165
+ if (preview.trim())
166
+ parts.push({ role, kind: "text", text: preview });
167
+ }
168
+ }
169
+ return parts;
144
170
  }
145
171
  function sessionMessageLine(record) {
146
- if (!record || typeof record !== "object")
172
+ const parts = sessionMessageParts(record);
173
+ if (parts.length === 0)
147
174
  return;
148
- const outer = record;
149
- const message = outer.message && typeof outer.message === "object" ? outer.message : outer;
150
- const role = typeof message.role === "string" ? message.role : undefined;
151
- if (!role)
152
- return;
153
- const text = contentText(message.content).trim();
175
+ const text = parts.map((part) => part.text).join(`
176
+ `).trim();
154
177
  if (!text)
155
178
  return;
156
- return `${role}: ${text}`;
179
+ return `${parts[0].role}: ${text}`;
157
180
  }
158
- function readSessionTranscriptTail(sessionFile, maxLines, trustedRoots) {
159
- const tail = readContainedTextTail(sessionFile, Math.max(maxLines * 4, maxLines), trustedRoots, "session");
181
+ export function readSessionMessagesTail(sessionFile, maxMessages, trustedRoots, trustedFiles = [], trustedFileRoot) {
182
+ const tail = readContainedTextTail(sessionFile, Math.max(maxMessages * 4, maxMessages), trustedRoots, "session", trustedFiles, trustedFileRoot);
183
+ const warnings = [];
184
+ if (tail.error)
185
+ warnings.push(`Session read failed for ${sessionFile}: ${tail.error}`);
186
+ const parsedMessages = [];
187
+ let malformed = 0;
188
+ let recordIndex = 0;
189
+ for (const line of tail.lines) {
190
+ if (!line.trim())
191
+ continue;
192
+ try {
193
+ const parsed = JSON.parse(line);
194
+ for (const part of sessionMessageParts(parsed))
195
+ parsedMessages.push({ ...part, recordIndex });
196
+ } catch {
197
+ malformed++;
198
+ }
199
+ recordIndex++;
200
+ }
201
+ if (malformed > 0)
202
+ warnings.push(`Skipped ${malformed} malformed session tail line${malformed === 1 ? "" : "s"}.`);
203
+ const messages = parsedMessages.slice(-maxMessages);
204
+ return { messages, warnings, truncated: tail.truncated || parsedMessages.length > messages.length };
205
+ }
206
+ function readSessionTranscriptTail(sessionFile, maxLines, trustedRoots, trustedFiles = [], trustedFileRoot) {
207
+ const tail = readContainedTextTail(sessionFile, Math.max(maxLines * 4, maxLines), trustedRoots, "session", trustedFiles, trustedFileRoot);
160
208
  const warnings = [];
161
209
  if (tail.error)
162
210
  warnings.push(`Session read failed for ${sessionFile}: ${tail.error}`);
@@ -169,7 +217,7 @@ function readSessionTranscriptTail(sessionFile, maxLines, trustedRoots) {
169
217
  const parsed = JSON.parse(line);
170
218
  const messageLine = sessionMessageLine(parsed);
171
219
  if (messageLine)
172
- lines.push(messageLine);
220
+ lines.push(...messageLine.split(/\r?\n/));
173
221
  } catch {
174
222
  malformed++;
175
223
  }
@@ -196,8 +244,9 @@ function formatActivityFacts(input) {
196
244
  return activity || facts.length ? [activity, ...facts].filter(Boolean).join(" | ") : undefined;
197
245
  }
198
246
  function foregroundModeName(control) {
199
- if (control.mode === "single" && control.currentAgent)
200
- return control.currentAgent;
247
+ const currentDisplayName = control.sessionName?.trim() || control.currentAgent;
248
+ if (control.mode === "single" && currentDisplayName)
249
+ return currentDisplayName;
201
250
  return control.mode;
202
251
  }
203
252
  function formatForegroundFleetLines(controls) {
@@ -216,7 +265,8 @@ function formatForegroundFleetLines(controls) {
216
265
  toolCount: control.toolCount,
217
266
  ...control.tokens !== undefined ? { tokens: { total: control.tokens } } : {}
218
267
  });
219
- const current = control.currentAgent ? ` | ${control.currentAgent}${control.currentIndex !== undefined ? ` #${control.currentIndex}` : ""}` : "";
268
+ const currentDisplayName = control.sessionName?.trim() || control.currentAgent;
269
+ const current = currentDisplayName ? ` | ${currentDisplayName}${control.currentIndex !== undefined ? ` #${control.currentIndex}` : ""}` : "";
220
270
  lines.push(`- ${control.runId} | running | ${foregroundModeName(control)}${current}${activity ? ` | ${activity}` : ""}`);
221
271
  lines.push(` status: subagent({ action: "status", id: "${control.runId}" })`);
222
272
  lines.push(" transcript: live in the expanded foreground result; persisted session transcript appears after completion when sessions are enabled.");
@@ -224,6 +274,20 @@ function formatForegroundFleetLines(controls) {
224
274
  }
225
275
  return lines;
226
276
  }
277
+ function formatDetachedForegroundFleetLines(runs) {
278
+ if (runs.length === 0)
279
+ return [];
280
+ const lines = ["Detached foreground runs:"];
281
+ const ordered = [...runs].sort((left, right) => right.updatedAt - left.updatedAt);
282
+ for (const run of ordered) {
283
+ const detachedChildren = run.children.filter((child) => child.status === "detached");
284
+ const childSummary = detachedChildren.map((child) => `${fleetChildDisplayName(child)} #${child.index}`).join(", ");
285
+ lines.push(`- ${run.runId} | detached | ${run.mode}${childSummary ? ` | ${childSummary}` : ""}`);
286
+ lines.push(` status: subagent({ action: "status", id: "${run.runId}" })`);
287
+ lines.push(` recovery: reply to the supervisor request first, then wait with bg_wait({ id: "${run.runId}" }); do not resume or launch a replacement while any child remains detached.`);
288
+ }
289
+ return lines;
290
+ }
227
291
  function formatAsyncFleetLines(runs) {
228
292
  if (runs.length === 0)
229
293
  return [];
@@ -233,15 +297,17 @@ function formatAsyncFleetLines(runs) {
233
297
  const activity = formatActivityFacts(run);
234
298
  const cwd = run.cwd ? shortenPath(run.cwd) : shortenPath(run.asyncDir);
235
299
  const pending = run.pendingAppends ? ` | ${run.pendingAppends} pending append${run.pendingAppends === 1 ? "" : "s"}` : "";
236
- lines.push(`- ${run.id} | ${run.state}${activity ? ` | ${activity}` : ""} | ${run.mode} | ${progress}${pending} | ${cwd}`);
300
+ const runContext = contextModeLabel(run.context);
301
+ lines.push(`- ${run.id} | ${run.state}${activity ? ` | ${activity}` : ""} | ${run.mode}${runContext ? ` ${runContext}` : ""} | ${progress}${pending} | ${cwd}`);
237
302
  lines.push(` status: subagent({ action: "status", id: "${run.id}" })`);
238
303
  lines.push(` transcript: subagent({ action: "status", id: "${run.id}", view: "transcript" })`);
239
304
  for (const step of run.steps) {
240
- const display = step.label ? `${step.label} (${step.agent})` : step.agent;
305
+ const display = fleetStepDisplayName(step);
306
+ const stepContext = contextModeLabel(step.context);
241
307
  const phase = step.phase ? `[${step.phase}] ` : "";
242
308
  const stepActivity = formatActivityFacts(step);
243
309
  const modelThinking = formatModelThinking(step.model, step.thinking);
244
- const parts = [`${step.index}. ${phase}${display}`, step.status, stepActivity, modelThinking].filter(Boolean);
310
+ const parts = [`${step.index}. ${phase}${display}${stepContext ? ` ${stepContext}` : ""}`, step.status, stepActivity, modelThinking].filter(Boolean);
245
311
  lines.push(` ${parts.join(" | ")}`);
246
312
  const output = path.join(run.asyncDir, `output-${step.index}.log`);
247
313
  if (fs.existsSync(output))
@@ -278,10 +344,10 @@ export function inspectSubagentFleet(_params, deps = {}) {
278
344
  }
279
345
  let asyncRuns;
280
346
  try {
281
- asyncRuns = listAsyncRuns(deps.asyncDirRoot ?? ASYNC_DIR, {
347
+ asyncRuns = listAsyncRuns(deps.asyncDirRoot ?? DIRS.async, {
282
348
  states: ["queued", "running"],
283
349
  sessionId: deps.state?.currentSessionId ?? undefined,
284
- resultsDir: deps.resultsDir ?? RESULTS_DIR,
350
+ resultsDir: deps.resultsDir ?? DIRS.results,
285
351
  kill: deps.kill,
286
352
  now: deps.now
287
353
  });
@@ -290,17 +356,22 @@ export function inspectSubagentFleet(_params, deps = {}) {
290
356
  return { content: [{ type: "text", text: message }], isError: true, details: { mode: "management", results: [] } };
291
357
  }
292
358
  const foregroundControls = deps.state ? [...deps.state.foregroundControls.values()] : [];
293
- const total = foregroundControls.length + asyncRuns.length;
359
+ const activeForegroundIds = new Set(foregroundControls.map((control) => control.runId));
360
+ const detachedForegroundRuns = deps.state?.foregroundRuns ? [...deps.state.foregroundRuns.values()].filter((run) => run.sessionId === deps.state?.currentSessionId && !activeForegroundIds.has(run.runId) && run.children.some((child) => child.status === "detached")) : [];
361
+ const total = foregroundControls.length + detachedForegroundRuns.length + asyncRuns.length;
294
362
  if (total === 0) {
295
363
  return {
296
364
  content: [{ type: "text", text: 'No active subagent fleet. Background runs that already finished are available through completion notifications or subagent({ action: "status", id: "..." }).' }],
297
365
  details: { mode: "management", results: [] }
298
366
  };
299
367
  }
300
- const lines = [`Subagent fleet: ${total} active`, ""];
368
+ const lines = [`Subagent fleet: ${total} tracked`, ""];
301
369
  const foregroundLines = formatForegroundFleetLines(foregroundControls);
302
370
  if (foregroundLines.length)
303
371
  lines.push(...foregroundLines, "");
372
+ const detachedForegroundLines = formatDetachedForegroundFleetLines(detachedForegroundRuns);
373
+ if (detachedForegroundLines.length)
374
+ lines.push(...detachedForegroundLines, "");
304
375
  const asyncLines = formatAsyncFleetLines(asyncRuns);
305
376
  if (asyncLines.length)
306
377
  lines.push(...asyncLines, "");
@@ -331,15 +402,16 @@ function selectTranscriptStep(status, options) {
331
402
  }
332
403
  }
333
404
  const step = selectedIndex !== undefined ? steps[selectedIndex] : undefined;
334
- const hint = options.index === undefined && steps.length > 1 ? `Tip: pass index to inspect a specific child transcript (${steps.map((candidate, index) => `${index}=${candidate.agent}`).join(", ")}).` : undefined;
405
+ const hint = options.index === undefined && steps.length > 1 ? `Tip: pass index to inspect a specific child transcript (${steps.map((candidate, index) => `${index}=${fleetChildDisplayName(candidate)}`).join(", ")}).` : undefined;
335
406
  return { index: selectedIndex, step, hint };
336
407
  }
337
408
  function stepStateLine(mode, index, step) {
338
409
  if (index === undefined || !step)
339
410
  return;
340
411
  const modelThinking = formatModelThinking(step.model, step.thinking);
412
+ const context = contextModeLabel(step.context);
341
413
  const parts = [
342
- `${mode === "parallel" ? "Agent" : "Step"}: ${index} (${step.agent})`,
414
+ `${mode === "parallel" ? "Agent" : "Step"}: ${index} (${fleetChildDisplayName(step)})${context ? ` ${context}` : ""}`,
343
415
  step.status,
344
416
  formatActivityFacts(step),
345
417
  modelThinking,
@@ -365,6 +437,10 @@ function appendKnownArtifacts(lines, input) {
365
437
  for (const artifact of artifacts)
366
438
  lines.push(` ${artifact}`);
367
439
  }
440
+ function safeTranscriptLines(lines) {
441
+ return lines.map((line) => safeTerminalText(line)).join(`
442
+ `);
443
+ }
368
444
  function appendTranscriptBody(lines, sourceLabel, sourceLines, truncated) {
369
445
  lines.push(`${sourceLabel}${truncated ? " (tail truncated)" : ""}:`);
370
446
  if (sourceLines.length === 0) {
@@ -383,14 +459,15 @@ export function formatAsyncRunTranscript(status, asyncDir, options = {}) {
383
459
  const outputPaths = selected.index !== undefined ? uniqueStrings([stepOutputPath, runOutputPath && stepOutputPath && path.resolve(runOutputPath) === path.resolve(stepOutputPath) ? runOutputPath : undefined]) : uniqueStrings([runOutputPath]);
384
460
  const sessionFile = selected.index !== undefined ? selected.step?.sessionFile : status.sessionFile;
385
461
  const eventsPath = path.join(asyncDir, "events.jsonl");
462
+ const context = contextModeLabel(status.context ?? summarizeContextModes((status.steps ?? []).map((step) => step.context)));
386
463
  const lines = [
387
464
  `Run: ${status.runId}`,
388
465
  `State: ${status.state}`,
389
- `Mode: ${status.mode}`,
466
+ `Mode: ${status.mode}${context ? ` ${context}` : ""}`,
390
467
  stepStateLine(status.mode, selected.index, selected.step),
391
468
  selected.hint
392
469
  ].filter((line) => Boolean(line));
393
- appendKnownArtifacts(lines, { outputPaths, sessionFile, eventsPath: fs.existsSync(eventsPath) ? eventsPath : undefined, logPath: fs.existsSync(logPath) ? logPath : undefined });
470
+ appendKnownArtifacts(lines, { outputPaths: outputPaths.filter((outputPath) => fs.existsSync(outputPath)), sessionFile, eventsPath: fs.existsSync(eventsPath) ? eventsPath : undefined, logPath: fs.existsSync(logPath) ? logPath : undefined });
394
471
  const warnings = [];
395
472
  let transcriptLines = [];
396
473
  let transcriptSource = "Transcript tail";
@@ -411,7 +488,7 @@ export function formatAsyncRunTranscript(status, asyncDir, options = {}) {
411
488
  transcriptSource = "Recent output from status.json";
412
489
  }
413
490
  if (transcriptLines.length === 0 && sessionFile) {
414
- const sessionTail = readSessionTranscriptTail(sessionFile, lineLimit, options.sessionRoots ?? []);
491
+ const sessionTail = readSessionTranscriptTail(sessionFile, lineLimit, options.sessionRoots ?? [], options.trustedSessionFiles, options.trustedSessionFileRoot);
415
492
  transcriptLines = sessionTail.lines;
416
493
  warnings.push(...sessionTail.warnings);
417
494
  if (transcriptLines.length > 0)
@@ -423,8 +500,7 @@ export function formatAsyncRunTranscript(status, asyncDir, options = {}) {
423
500
  lines.push(` ${warning}`);
424
501
  }
425
502
  appendTranscriptBody(lines, transcriptSource, transcriptLines, truncated);
426
- return lines.join(`
427
- `);
503
+ return safeTranscriptLines(lines);
428
504
  }
429
505
  export function formatNestedRunTranscript(run, options = {}) {
430
506
  if (run.asyncDir) {
@@ -437,28 +513,26 @@ export function formatNestedRunTranscript(run, options = {}) {
437
513
  `Nested run: ${run.id}`,
438
514
  `State: ${run.state}`,
439
515
  run.mode ? `Mode: ${run.mode}` : undefined,
440
- run.agent ? `Agent: ${run.agent}` : run.agents?.length ? `Agents: ${run.agents.join(", ")}` : undefined
516
+ run.sessionName?.trim() ? `Agent: ${run.sessionName.trim()}` : run.agent ? `Agent: ${run.agent}` : run.agents?.length ? `Agents: ${run.agents.join(", ")}` : undefined
441
517
  ].filter((line) => Boolean(line));
442
518
  appendKnownArtifacts(lines, { outputPaths: [], sessionFile: run.sessionFile });
443
519
  if (!run.sessionFile) {
444
520
  appendTranscriptBody(lines, "Transcript tail", [], false);
445
- return lines.join(`
446
- `);
521
+ return safeTranscriptLines(lines);
447
522
  }
448
- const sessionTail = readSessionTranscriptTail(run.sessionFile, lineLimit, options.sessionRoots ?? []);
523
+ const sessionTail = readSessionTranscriptTail(run.sessionFile, lineLimit, options.sessionRoots ?? [], options.trustedSessionFiles, options.trustedSessionFileRoot);
449
524
  if (sessionTail.warnings.length) {
450
525
  lines.push("Warnings:");
451
526
  for (const warning of sessionTail.warnings)
452
527
  lines.push(` ${warning}`);
453
528
  }
454
529
  appendTranscriptBody(lines, `Session transcript tail from ${run.sessionFile}`, sessionTail.lines, false);
455
- return lines.join(`
456
- `);
530
+ return safeTranscriptLines(lines);
457
531
  }
458
532
  export function formatAsyncResultTranscript(data, resultPath, options = {}) {
459
533
  const lineLimit = transcriptLineLimit(options.lines);
460
534
  const runId = data.runId ?? data.id ?? path.basename(resultPath, ".json");
461
- const children = Array.isArray(data.results) ? data.results : data.agent ? [{ agent: data.agent, output: data.output, summary: data.summary, sessionFile: data.sessionFile, state: data.state, success: data.success, exitCode: data.exitCode }] : [];
535
+ const children = Array.isArray(data.results) ? data.results : data.agent ? [{ agent: data.agent, sessionName: undefined, output: data.output, summary: data.summary, sessionFile: data.sessionFile, state: data.state, success: data.success, exitCode: data.exitCode }] : [];
462
536
  let index = options.index;
463
537
  if (index !== undefined && !Number.isInteger(index))
464
538
  throw new Error("Transcript index must be an integer.");
@@ -473,11 +547,10 @@ export function formatAsyncResultTranscript(data, resultPath, options = {}) {
473
547
  const lines = [
474
548
  `Run: ${runId}`,
475
549
  `State: ${data.state ?? (data.success ? "complete" : "failed")}`,
476
- index !== undefined && child ? `Child: ${index} (${child.agent ?? "subagent"})` : undefined,
477
- index === undefined && children.length > 1 ? `Tip: pass index to inspect a specific child transcript (${children.map((candidate, childIndex) => `${childIndex}=${candidate.agent ?? "subagent"}`).join(", ")}).` : undefined
550
+ index !== undefined && child ? `Child: ${index} (${fleetChildDisplayName(child)})` : undefined,
551
+ index === undefined && children.length > 1 ? `Tip: pass index to inspect a specific child transcript (${children.map((candidate, childIndex) => `${childIndex}=${fleetChildDisplayName(candidate)}`).join(", ")}).` : undefined
478
552
  ].filter((line) => Boolean(line));
479
553
  appendKnownArtifacts(lines, { outputPaths: [], sessionFile, resultPath });
480
554
  appendTranscriptBody(lines, "Result transcript tail", transcriptLines.filter((line) => line.trim()), output.split(/\r?\n/).length > lineLimit);
481
- return lines.join(`
482
- `);
555
+ return safeTranscriptLines(lines);
483
556
  }
@@ -0,0 +1,38 @@
1
+ import { createHash } from "node:crypto";
2
+ export const MAX_INDEX_SEGMENT_BYTES = 255;
3
+ const HASHED_INDEX_SEGMENT_PREFIX = "~sha256-";
4
+ const WINDOWS_RESERVED_NAME = /^(?:con|prn|aux|nul|com[1-9]|lpt[1-9])(?:\.|$)/i;
5
+ function hashedSegment(value) {
6
+ return `${HASHED_INDEX_SEGMENT_PREFIX}${createHash("sha256").update(value).digest("hex")}`;
7
+ }
8
+ function isPortableSegment(value) {
9
+ return value.length > 0 && value !== "." && value !== ".." && !value.endsWith(".") && !WINDOWS_RESERVED_NAME.test(value) && !/%5C/i.test(value) && !/\.[A-Za-z][A-Za-z0-9]{0,7}$/.test(value);
10
+ }
11
+ export function encodeIndexSegment(value, maxBytes = MAX_INDEX_SEGMENT_BYTES) {
12
+ let encoded;
13
+ try {
14
+ encoded = encodeURIComponent(value);
15
+ } catch {
16
+ return hashedSegment(value);
17
+ }
18
+ if (Buffer.byteLength(encoded, "utf-8") <= maxBytes && isPortableSegment(encoded))
19
+ return encoded;
20
+ return hashedSegment(value);
21
+ }
22
+ function historicallyReadableSegment(value, maxBytes) {
23
+ try {
24
+ const encoded = encodeURIComponent(value);
25
+ if (encoded.length === 0 || encoded === "." || encoded === ".." || encoded.endsWith(".") || WINDOWS_RESERVED_NAME.test(encoded))
26
+ return;
27
+ if (Buffer.byteLength(encoded, "utf-8") > maxBytes)
28
+ return;
29
+ return encoded;
30
+ } catch {
31
+ return;
32
+ }
33
+ }
34
+ export function indexSegmentAliases(value, maxBytes = MAX_INDEX_SEGMENT_BYTES) {
35
+ const current = encodeIndexSegment(value, maxBytes);
36
+ const historical = historicallyReadableSegment(value, maxBytes);
37
+ return historical && historical !== current ? [current, historical] : [current];
38
+ }