mixdog 0.9.2 → 0.9.4

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 (417) hide show
  1. package/package.json +8 -3
  2. package/scripts/anthropic-maxtokens-test.mjs +119 -0
  3. package/scripts/bench/lead-review-tasks-r3.json +20 -0
  4. package/scripts/bench/lead-review-tasks.json +20 -0
  5. package/scripts/bench/r4-mixed-tasks.json +20 -0
  6. package/scripts/bench/review-tasks.json +20 -0
  7. package/scripts/bench/round-codex.json +114 -0
  8. package/scripts/bench/round-mixdog-lead-r3.json +269 -0
  9. package/scripts/bench/round-mixdog-lead.json +269 -0
  10. package/scripts/bench/round-mixdog.json +126 -0
  11. package/scripts/bench/round-r10-bigsample.json +679 -0
  12. package/scripts/bench/round-r11-codexalign.json +257 -0
  13. package/scripts/bench/round-r4-codex.json +114 -0
  14. package/scripts/bench/round-r4-mixed.json +225 -0
  15. package/scripts/bench/round-r5-gpt-lead.json +259 -0
  16. package/scripts/bench/round-r6-codex.json +114 -0
  17. package/scripts/bench/round-r6-solo.json +257 -0
  18. package/scripts/bench/round-r7-full.json +254 -0
  19. package/scripts/bench/round-r8-fulldefault.json +255 -0
  20. package/scripts/bench-run.mjs +215 -29
  21. package/scripts/build-tui.mjs +13 -1
  22. package/scripts/explore-bench.mjs +124 -0
  23. package/scripts/freevar-smoke.mjs +95 -0
  24. package/scripts/hook-bus-test.mjs +191 -0
  25. package/scripts/internal-comms-bench.mjs +1 -0
  26. package/scripts/internal-comms-smoke.mjs +10 -9
  27. package/scripts/mouse-probe.mjs +45 -0
  28. package/scripts/output-style-bench.mjs +13 -6
  29. package/scripts/output-style-smoke.mjs +4 -4
  30. package/scripts/path-suffix-test.mjs +57 -0
  31. package/scripts/provider-toolcall-test.mjs +7 -3
  32. package/scripts/recall-bench.mjs +207 -0
  33. package/scripts/recall-usecase-cases.json +18 -0
  34. package/scripts/recall-usecase-probe.json +6 -0
  35. package/scripts/session-bench.mjs +152 -6
  36. package/scripts/tool-smoke.mjs +30 -67
  37. package/scripts/tui-render-smoke.mjs +90 -0
  38. package/scripts/webhook-smoke.mjs +208 -0
  39. package/src/agents/debugger/AGENT.md +5 -2
  40. package/src/agents/heavy-worker/AGENT.md +21 -11
  41. package/src/agents/maintainer/AGENT.md +4 -0
  42. package/src/agents/reviewer/AGENT.md +3 -2
  43. package/src/agents/worker/AGENT.md +21 -11
  44. package/src/lib/rules-builder.cjs +4 -0
  45. package/src/mixdog-session-runtime.mjs +933 -3731
  46. package/src/output-styles/default.md +34 -9
  47. package/src/output-styles/{oneline.md → extreme-minimal.md} +5 -4
  48. package/src/output-styles/minimal.md +4 -1
  49. package/src/output-styles/simple.md +22 -7
  50. package/src/repl.mjs +5 -5
  51. package/src/rules/agent/00-common.md +2 -0
  52. package/src/rules/agent/30-explorer.md +8 -11
  53. package/src/rules/lead/lead-brief.md +12 -0
  54. package/src/rules/lead/lead-tool.md +2 -9
  55. package/src/rules/shared/01-tool.md +11 -5
  56. package/src/runtime/agent/orchestrator/agent-runtime/agent-loop-policy.mjs +25 -0
  57. package/src/runtime/agent/orchestrator/agent-runtime/cache-strategy.mjs +100 -23
  58. package/src/runtime/agent/orchestrator/agent-runtime/session-builder.mjs +6 -15
  59. package/src/runtime/agent/orchestrator/agent-trace-format.mjs +362 -0
  60. package/src/runtime/agent/orchestrator/agent-trace-io.mjs +410 -0
  61. package/src/runtime/agent/orchestrator/agent-trace.mjs +16 -735
  62. package/src/runtime/agent/orchestrator/config.mjs +69 -2
  63. package/src/runtime/agent/orchestrator/context/collect.mjs +51 -0
  64. package/src/runtime/agent/orchestrator/mcp/client.mjs +6 -2
  65. package/src/runtime/agent/orchestrator/providers/anthropic-effort.mjs +63 -21
  66. package/src/runtime/agent/orchestrator/providers/anthropic-max-tokens.mjs +93 -0
  67. package/src/runtime/agent/orchestrator/providers/anthropic-model-resolve.mjs +209 -0
  68. package/src/runtime/agent/orchestrator/providers/anthropic-oauth-credentials.mjs +489 -0
  69. package/src/runtime/agent/orchestrator/providers/anthropic-oauth.mjs +97 -1343
  70. package/src/runtime/agent/orchestrator/providers/anthropic-sse.mjs +607 -0
  71. package/src/runtime/agent/orchestrator/providers/anthropic.mjs +78 -10
  72. package/src/runtime/agent/orchestrator/providers/api-usage.mjs +1 -13
  73. package/src/runtime/agent/orchestrator/providers/codex-client-meta.mjs +81 -0
  74. package/src/runtime/agent/orchestrator/providers/gemini-cache.mjs +248 -0
  75. package/src/runtime/agent/orchestrator/providers/gemini-schema.mjs +303 -0
  76. package/src/runtime/agent/orchestrator/providers/gemini-stream.mjs +505 -0
  77. package/src/runtime/agent/orchestrator/providers/gemini.mjs +44 -1014
  78. package/src/runtime/agent/orchestrator/providers/grok-oauth.mjs +18 -4
  79. package/src/runtime/agent/orchestrator/providers/lib/usage-primitives.mjs +32 -0
  80. package/src/runtime/agent/orchestrator/providers/model-catalog.mjs +86 -11
  81. package/src/runtime/agent/orchestrator/providers/model-list-sanitize.mjs +348 -0
  82. package/src/runtime/agent/orchestrator/providers/oauth-usage.mjs +54 -20
  83. package/src/runtime/agent/orchestrator/providers/openai-codex-model.mjs +108 -0
  84. package/src/runtime/agent/orchestrator/providers/openai-compat-stream.mjs +19 -12
  85. package/src/runtime/agent/orchestrator/providers/openai-compat-trace.mjs +58 -0
  86. package/src/runtime/agent/orchestrator/providers/openai-compat-wire.mjs +368 -0
  87. package/src/runtime/agent/orchestrator/providers/openai-compat-xai.mjs +760 -0
  88. package/src/runtime/agent/orchestrator/providers/openai-compat.mjs +41 -1142
  89. package/src/runtime/agent/orchestrator/providers/openai-oauth-http-sse.mjs +732 -0
  90. package/src/runtime/agent/orchestrator/providers/openai-oauth-login.mjs +193 -0
  91. package/src/runtime/agent/orchestrator/providers/openai-oauth-ws.mjs +303 -2119
  92. package/src/runtime/agent/orchestrator/providers/openai-oauth.mjs +140 -995
  93. package/src/runtime/agent/orchestrator/providers/openai-ws-delta.mjs +227 -0
  94. package/src/runtime/agent/orchestrator/providers/openai-ws-events.mjs +67 -0
  95. package/src/runtime/agent/orchestrator/providers/openai-ws-pool.mjs +436 -0
  96. package/src/runtime/agent/orchestrator/providers/openai-ws-stream.mjs +1105 -0
  97. package/src/runtime/agent/orchestrator/providers/openai-ws.mjs +2 -1
  98. package/src/runtime/agent/orchestrator/providers/opencode-go-usage.mjs +38 -12
  99. package/src/runtime/agent/orchestrator/providers/retry-classifier.mjs +7 -8
  100. package/src/runtime/agent/orchestrator/session/compact/budget.mjs +288 -0
  101. package/src/runtime/agent/orchestrator/session/compact/constants.mjs +85 -0
  102. package/src/runtime/agent/orchestrator/session/compact/engine.mjs +749 -0
  103. package/src/runtime/agent/orchestrator/session/compact/messages.mjs +82 -0
  104. package/src/runtime/agent/orchestrator/session/compact/summary-schema.mjs +315 -0
  105. package/src/runtime/agent/orchestrator/session/compact/summary.mjs +643 -0
  106. package/src/runtime/agent/orchestrator/session/compact/text-utils.mjs +326 -0
  107. package/src/runtime/agent/orchestrator/session/compact.mjs +40 -2282
  108. package/src/runtime/agent/orchestrator/session/loop/compact-debug.mjs +28 -0
  109. package/src/runtime/agent/orchestrator/session/loop/compact-policy.mjs +274 -0
  110. package/src/runtime/agent/orchestrator/session/loop/completion-guards.mjs +61 -0
  111. package/src/runtime/agent/orchestrator/session/loop/context-overflow.mjs +38 -0
  112. package/src/runtime/agent/orchestrator/session/loop/env.mjs +14 -0
  113. package/src/runtime/agent/orchestrator/session/loop/hidden-agents.mjs +21 -0
  114. package/src/runtime/agent/orchestrator/session/loop/pre-dispatch-deny.mjs +47 -0
  115. package/src/runtime/agent/orchestrator/session/loop/recall-fasttrack.mjs +182 -0
  116. package/src/runtime/agent/orchestrator/session/loop/steering-ladder.mjs +173 -0
  117. package/src/runtime/agent/orchestrator/session/loop/steering.mjs +63 -0
  118. package/src/runtime/agent/orchestrator/session/loop/stored-tool-args.mjs +100 -0
  119. package/src/runtime/agent/orchestrator/session/loop/termination.mjs +58 -0
  120. package/src/runtime/agent/orchestrator/session/loop/tool-classify.mjs +52 -0
  121. package/src/runtime/agent/orchestrator/session/loop/tool-exec.mjs +239 -0
  122. package/src/runtime/agent/orchestrator/session/loop/tool-helpers.mjs +218 -0
  123. package/src/runtime/agent/orchestrator/session/loop/transcript-repair.mjs +101 -0
  124. package/src/runtime/agent/orchestrator/session/loop/usage.mjs +35 -0
  125. package/src/runtime/agent/orchestrator/session/loop.mjs +409 -1304
  126. package/src/runtime/agent/orchestrator/session/manager/compaction-runner.mjs +471 -0
  127. package/src/runtime/agent/orchestrator/session/manager/context-meta.mjs +230 -0
  128. package/src/runtime/agent/orchestrator/session/manager/pending-messages.mjs +235 -0
  129. package/src/runtime/agent/orchestrator/session/manager/prompt-utils.mjs +149 -0
  130. package/src/runtime/agent/orchestrator/session/manager/rules-cache.mjs +155 -0
  131. package/src/runtime/agent/orchestrator/session/manager/runtime-liveness.mjs +406 -0
  132. package/src/runtime/agent/orchestrator/session/manager/status-telemetry.mjs +80 -0
  133. package/src/runtime/agent/orchestrator/session/manager/tool-resolution.mjs +303 -0
  134. package/src/runtime/agent/orchestrator/session/manager/usage-metrics.mjs +210 -0
  135. package/src/runtime/agent/orchestrator/session/manager.mjs +226 -2114
  136. package/src/runtime/agent/orchestrator/session/store-summary-index.mjs +189 -0
  137. package/src/runtime/agent/orchestrator/session/store.mjs +74 -179
  138. package/src/runtime/agent/orchestrator/stall-policy.mjs +3 -3
  139. package/src/runtime/agent/orchestrator/tools/builtin/arg-guard.mjs +70 -20
  140. package/src/runtime/agent/orchestrator/tools/builtin/bash-tool.mjs +22 -2
  141. package/src/runtime/agent/orchestrator/tools/builtin/builtin-tools.mjs +33 -42
  142. package/src/runtime/agent/orchestrator/tools/builtin/external-tool-adapters.mjs +241 -0
  143. package/src/runtime/agent/orchestrator/tools/builtin/fuzzy-match.mjs +1 -1
  144. package/src/runtime/agent/orchestrator/tools/builtin/list-tool.mjs +40 -0
  145. package/src/runtime/agent/orchestrator/tools/builtin/path-diagnostics.mjs +42 -2
  146. package/src/runtime/agent/orchestrator/tools/builtin/read-formatting.mjs +1 -1
  147. package/src/runtime/agent/orchestrator/tools/builtin/read-single-tool.mjs +11 -4
  148. package/src/runtime/agent/orchestrator/tools/builtin/rg-runner.mjs +29 -0
  149. package/src/runtime/agent/orchestrator/tools/builtin/search-builders.mjs +8 -0
  150. package/src/runtime/agent/orchestrator/tools/builtin/search-path-diagnostics.mjs +126 -0
  151. package/src/runtime/agent/orchestrator/tools/builtin/search-tool.mjs +81 -87
  152. package/src/runtime/agent/orchestrator/tools/builtin/shell-job-paths.mjs +161 -0
  153. package/src/runtime/agent/orchestrator/tools/builtin/shell-job-process.mjs +108 -0
  154. package/src/runtime/agent/orchestrator/tools/builtin/shell-jobs.mjs +78 -304
  155. package/src/runtime/agent/orchestrator/tools/builtin.mjs +11 -6
  156. package/src/runtime/agent/orchestrator/tools/code-graph/build.mjs +303 -0
  157. package/src/runtime/agent/orchestrator/tools/code-graph/constants.mjs +43 -0
  158. package/src/runtime/agent/orchestrator/tools/code-graph/disk-cache.mjs +382 -0
  159. package/src/runtime/agent/orchestrator/tools/code-graph/dispatch.mjs +551 -0
  160. package/src/runtime/agent/orchestrator/tools/code-graph/graph-binary.mjs +295 -0
  161. package/src/runtime/agent/orchestrator/tools/code-graph/graph-model.mjs +158 -0
  162. package/src/runtime/agent/orchestrator/tools/code-graph/keyword-match.mjs +82 -0
  163. package/src/runtime/agent/orchestrator/tools/code-graph/lang-predicates.mjs +128 -0
  164. package/src/runtime/agent/orchestrator/tools/code-graph/memory-cache.mjs +66 -0
  165. package/src/runtime/agent/orchestrator/tools/code-graph/project-root.mjs +44 -0
  166. package/src/runtime/agent/orchestrator/tools/code-graph/search.mjs +1080 -0
  167. package/src/runtime/agent/orchestrator/tools/code-graph/source-access.mjs +81 -0
  168. package/src/runtime/agent/orchestrator/tools/code-graph/span.mjs +19 -0
  169. package/src/runtime/agent/orchestrator/tools/code-graph/symbol-index.mjs +280 -0
  170. package/src/runtime/agent/orchestrator/tools/code-graph/text-columns.mjs +45 -0
  171. package/src/runtime/agent/orchestrator/tools/code-graph/text-mask.mjs +347 -0
  172. package/src/runtime/agent/orchestrator/tools/code-graph-tool-defs.mjs +6 -6
  173. package/src/runtime/agent/orchestrator/tools/code-graph.mjs +36 -4277
  174. package/src/runtime/agent/orchestrator/tools/graph-binary-fetcher.mjs +6 -3
  175. package/src/runtime/agent/orchestrator/tools/patch/constants.mjs +9 -0
  176. package/src/runtime/agent/orchestrator/tools/patch/dispatch.mjs +171 -0
  177. package/src/runtime/agent/orchestrator/tools/patch/matcher.mjs +471 -0
  178. package/src/runtime/agent/orchestrator/tools/patch/native-server.mjs +436 -0
  179. package/src/runtime/agent/orchestrator/tools/patch/orchestrator.mjs +342 -0
  180. package/src/runtime/agent/orchestrator/tools/patch/parsing.mjs +359 -0
  181. package/src/runtime/agent/orchestrator/tools/patch/paths.mjs +340 -0
  182. package/src/runtime/agent/orchestrator/tools/patch/v4a-convert.mjs +643 -0
  183. package/src/runtime/agent/orchestrator/tools/patch.mjs +36 -2959
  184. package/src/runtime/agent/orchestrator/tools/progress-message.mjs +1 -23
  185. package/src/runtime/agent/orchestrator/tools/shell-command.mjs +10 -74
  186. package/src/runtime/agent/orchestrator/tools/shell-powershell.mjs +77 -0
  187. package/src/runtime/agent/orchestrator/tools/shell-snapshot.mjs +2 -4
  188. package/src/runtime/agent/orchestrator/tools/shell-state.mjs +154 -0
  189. package/src/runtime/channels/backends/discord-access.mjs +32 -0
  190. package/src/runtime/channels/backends/discord-attachments.mjs +65 -0
  191. package/src/runtime/channels/backends/discord-gateway.mjs +233 -0
  192. package/src/runtime/channels/backends/discord.mjs +12 -292
  193. package/src/runtime/channels/index.mjs +241 -894
  194. package/src/runtime/channels/lib/backend-dispatch.mjs +44 -0
  195. package/src/runtime/channels/lib/boot-profile.mjs +23 -0
  196. package/src/runtime/channels/lib/crash-log.mjs +106 -0
  197. package/src/runtime/channels/lib/event-pipeline.mjs +18 -1
  198. package/src/runtime/channels/lib/event-queue.mjs +63 -4
  199. package/src/runtime/channels/lib/inbound-routing.mjs +111 -0
  200. package/src/runtime/channels/lib/index-drop-trace.mjs +72 -0
  201. package/src/runtime/channels/lib/output-forwarder.mjs +9 -1
  202. package/src/runtime/channels/lib/owner-heartbeat.mjs +75 -0
  203. package/src/runtime/channels/lib/parent-bridge.mjs +88 -0
  204. package/src/runtime/channels/lib/runtime-paths.mjs +14 -4
  205. package/src/runtime/channels/lib/session-discovery.mjs +56 -4
  206. package/src/runtime/channels/lib/telegram-format.mjs +19 -22
  207. package/src/runtime/channels/lib/tool-dispatch.mjs +158 -0
  208. package/src/runtime/channels/lib/tool-format.mjs +1 -1
  209. package/src/runtime/channels/lib/transcript-discovery.mjs +4 -4
  210. package/src/runtime/channels/lib/voice-runtime-fetcher.mjs +6 -3
  211. package/src/runtime/channels/lib/voice-transcription.mjs +179 -0
  212. package/src/runtime/channels/lib/webhook/deliveries.mjs +312 -0
  213. package/src/runtime/channels/lib/webhook/log.mjs +42 -0
  214. package/src/runtime/channels/lib/webhook/ngrok.mjs +181 -0
  215. package/src/runtime/channels/lib/webhook/signature.mjs +60 -0
  216. package/src/runtime/channels/lib/webhook.mjs +36 -570
  217. package/src/runtime/channels/lib/whisper-language.mjs +42 -0
  218. package/src/runtime/channels/tool-defs.mjs +11 -130
  219. package/src/runtime/memory/index.mjs +258 -2050
  220. package/src/runtime/memory/lib/core-memory-store.mjs +351 -1
  221. package/src/runtime/memory/lib/cycle-llm-adapters.mjs +58 -0
  222. package/src/runtime/memory/lib/cycle-scheduler.mjs +497 -0
  223. package/src/runtime/memory/lib/cycle-signatures.mjs +34 -0
  224. package/src/runtime/memory/lib/embedding-warmup.mjs +58 -0
  225. package/src/runtime/memory/lib/http-wire.mjs +57 -0
  226. package/src/runtime/memory/lib/memory-config-flags.mjs +91 -0
  227. package/src/runtime/memory/lib/memory-cycle.mjs +1 -1
  228. package/src/runtime/memory/lib/memory-cycle2-gate.mjs +515 -0
  229. package/src/runtime/memory/lib/memory-cycle2-mutations.mjs +324 -0
  230. package/src/runtime/memory/lib/memory-cycle2-shared.mjs +18 -0
  231. package/src/runtime/memory/lib/memory-cycle2.mjs +72 -837
  232. package/src/runtime/memory/lib/memory-embed.mjs +149 -0
  233. package/src/runtime/memory/lib/memory-process-lock.mjs +162 -0
  234. package/src/runtime/memory/lib/memory-recall-scope-filter.mjs +24 -0
  235. package/src/runtime/memory/lib/memory-recall-store.mjs +22 -2
  236. package/src/runtime/memory/lib/memory-retrievers.mjs +8 -0
  237. package/src/runtime/memory/lib/memory.mjs +20 -0
  238. package/src/runtime/memory/lib/pg/supervisor.mjs +1 -1
  239. package/src/runtime/memory/lib/promotion-fingerprint.mjs +50 -0
  240. package/src/runtime/memory/lib/query-handlers.mjs +780 -0
  241. package/src/runtime/memory/lib/recall-format.mjs +238 -0
  242. package/src/runtime/memory/lib/runtime-fetcher.mjs +8 -3
  243. package/src/runtime/memory/lib/transcript-ingest.mjs +425 -0
  244. package/src/runtime/memory/tool-defs.mjs +6 -14
  245. package/src/runtime/search/lib/http-fetch.mjs +274 -0
  246. package/src/runtime/search/lib/ssrf-guard.mjs +333 -0
  247. package/src/runtime/search/lib/web-tools.mjs +24 -602
  248. package/src/runtime/shared/abort-controller.mjs +1 -1
  249. package/src/runtime/shared/atomic-file.mjs +26 -1
  250. package/src/runtime/shared/background-tasks.mjs +2 -3
  251. package/src/runtime/shared/buffered-appender.mjs +149 -0
  252. package/src/runtime/shared/launcher-control.mjs +2 -2
  253. package/src/runtime/shared/task-notification-envelope.mjs +98 -0
  254. package/src/runtime/shared/tool-execution-contract.mjs +2 -2
  255. package/src/runtime/shared/tool-primitives.mjs +308 -0
  256. package/src/runtime/shared/tool-result-summary.mjs +515 -0
  257. package/src/runtime/shared/tool-surface.mjs +80 -898
  258. package/src/runtime/shared/transcript-writer.mjs +52 -2
  259. package/src/runtime/shared/update-checker.mjs +7 -4
  260. package/src/session-runtime/config-helpers.mjs +291 -0
  261. package/src/session-runtime/config-lifecycle.mjs +232 -0
  262. package/src/session-runtime/cwd-plugins.mjs +226 -0
  263. package/src/session-runtime/effort.mjs +128 -0
  264. package/src/session-runtime/fs-utils.mjs +10 -0
  265. package/src/session-runtime/mcp-glue.mjs +177 -0
  266. package/src/session-runtime/model-capabilities.mjs +130 -0
  267. package/src/session-runtime/model-recency.mjs +111 -0
  268. package/src/session-runtime/native-search.mjs +247 -0
  269. package/src/session-runtime/output-styles.mjs +126 -0
  270. package/src/session-runtime/plugin-mcp.mjs +114 -0
  271. package/src/session-runtime/prewarm.mjs +142 -0
  272. package/src/session-runtime/provider-models.mjs +278 -0
  273. package/src/session-runtime/provider-usage.mjs +120 -0
  274. package/src/session-runtime/quick-model-rows.mjs +170 -0
  275. package/src/session-runtime/quick-search-models.mjs +46 -0
  276. package/src/session-runtime/session-hooks.mjs +93 -0
  277. package/src/session-runtime/session-text.mjs +100 -0
  278. package/src/session-runtime/settings-api.mjs +319 -0
  279. package/src/session-runtime/statusline-route.mjs +35 -0
  280. package/src/session-runtime/tool-catalog.mjs +720 -0
  281. package/src/session-runtime/tool-defs.mjs +84 -0
  282. package/src/session-runtime/warmup-schedulers.mjs +201 -0
  283. package/src/session-runtime/workflow.mjs +358 -0
  284. package/src/standalone/agent-tool/helpers.mjs +237 -0
  285. package/src/standalone/agent-tool/notify.mjs +107 -0
  286. package/src/standalone/agent-tool/provider-init.mjs +143 -0
  287. package/src/standalone/agent-tool/render.mjs +152 -0
  288. package/src/standalone/agent-tool/tool-def.mjs +55 -0
  289. package/src/standalone/agent-tool.mjs +155 -677
  290. package/src/standalone/channel-worker.mjs +7 -9
  291. package/src/standalone/explore-tool.mjs +40 -12
  292. package/src/standalone/hook-bus/config.mjs +207 -0
  293. package/src/standalone/hook-bus/constants.mjs +90 -0
  294. package/src/standalone/hook-bus/handlers.mjs +481 -0
  295. package/src/standalone/hook-bus/payload.mjs +31 -0
  296. package/src/standalone/hook-bus/rules.mjs +77 -0
  297. package/src/standalone/hook-bus.mjs +110 -746
  298. package/src/standalone/memory-runtime-proxy.mjs +7 -0
  299. package/src/standalone/opencode-go-login.mjs +125 -0
  300. package/src/standalone/provider-admin.mjs +15 -19
  301. package/src/standalone/usage-dashboard.mjs +3 -1
  302. package/src/tui/App.jsx +1163 -7571
  303. package/src/tui/app/app-format.mjs +206 -0
  304. package/src/tui/app/channel-pickers.mjs +510 -0
  305. package/src/tui/app/clipboard.mjs +67 -0
  306. package/src/tui/app/core-memory-picker.mjs +210 -0
  307. package/src/tui/app/extension-pickers.mjs +506 -0
  308. package/src/tui/app/input-parsers.mjs +193 -0
  309. package/src/tui/app/maintenance-pickers.mjs +324 -0
  310. package/src/tui/app/model-options.mjs +330 -0
  311. package/src/tui/app/model-picker.mjs +365 -0
  312. package/src/tui/app/onboarding-steps.mjs +400 -0
  313. package/src/tui/app/project-picker.mjs +247 -0
  314. package/src/tui/app/provider-setup-picker.mjs +580 -0
  315. package/src/tui/app/resume-picker.mjs +55 -0
  316. package/src/tui/app/route-pickers.mjs +419 -0
  317. package/src/tui/app/settings-picker.mjs +490 -0
  318. package/src/tui/app/slash-commands.mjs +101 -0
  319. package/src/tui/app/slash-dispatch.mjs +427 -0
  320. package/src/tui/app/text-layout.mjs +46 -0
  321. package/src/tui/app/theme-effort-pickers.mjs +154 -0
  322. package/src/tui/app/transcript-window.mjs +671 -0
  323. package/src/tui/app/use-mouse-input.mjs +460 -0
  324. package/src/tui/app/use-prompt-handlers.mjs +310 -0
  325. package/src/tui/app/use-transcript-scroll.mjs +510 -0
  326. package/src/tui/app/use-transcript-window.mjs +589 -0
  327. package/src/tui/components/ConfirmBar.jsx +1 -1
  328. package/src/tui/components/Picker.jsx +32 -4
  329. package/src/tui/components/PromptInput.jsx +259 -80
  330. package/src/tui/components/SlashCommandPalette.jsx +8 -1
  331. package/src/tui/components/StatusLine.jsx +63 -12
  332. package/src/tui/components/TextEntryPanel.jsx +11 -0
  333. package/src/tui/components/ToolExecution.jsx +56 -588
  334. package/src/tui/components/TranscriptItem.jsx +105 -0
  335. package/src/tui/components/UsagePanel.jsx +18 -4
  336. package/src/tui/components/prompt-input/edit-helpers.mjs +72 -0
  337. package/src/tui/components/prompt-input/voice-indicator.mjs +39 -0
  338. package/src/tui/components/tool-execution/ResultBody.jsx +56 -0
  339. package/src/tui/components/tool-execution/surface-detail.mjs +405 -0
  340. package/src/tui/components/tool-execution/text-format.mjs +161 -0
  341. package/src/tui/components/tool-output-format.mjs +2 -2
  342. package/src/tui/display-width.mjs +20 -3
  343. package/src/tui/dist/index.mjs +18034 -17188
  344. package/src/tui/engine/agent-envelope.mjs +296 -0
  345. package/src/tui/engine/agent-job-feed.mjs +133 -0
  346. package/src/tui/engine/boot-profile.mjs +21 -0
  347. package/src/tui/engine/labels.mjs +67 -0
  348. package/src/tui/engine/notice-text.mjs +112 -0
  349. package/src/tui/engine/notification-plan.mjs +76 -0
  350. package/src/tui/engine/queue-helpers.mjs +161 -0
  351. package/src/tui/engine/render-timing.mjs +17 -0
  352. package/src/tui/engine/session-stats.mjs +46 -0
  353. package/src/tui/engine/tool-approval.mjs +94 -0
  354. package/src/tui/engine/tool-call-fields.mjs +23 -0
  355. package/src/tui/engine/tool-card-results.mjs +234 -0
  356. package/src/tui/engine/tool-result-status.mjs +135 -0
  357. package/src/tui/engine/tool-result-text.mjs +126 -0
  358. package/src/tui/engine.mjs +405 -1385
  359. package/src/tui/figures.mjs +5 -0
  360. package/src/tui/index.jsx +105 -0
  361. package/src/tui/input-editing.mjs +60 -10
  362. package/src/tui/keyboard-protocol.mjs +2 -2
  363. package/src/tui/lib/voice-recorder.mjs +35 -19
  364. package/src/tui/markdown/format-token.mjs +11 -9
  365. package/src/tui/paste-attachments.mjs +38 -0
  366. package/src/tui/statusline-ansi-bridge.mjs +11 -3
  367. package/src/tui/theme.mjs +6 -0
  368. package/src/tui/themes/base.mjs +2 -2
  369. package/src/tui/themes/kanagawa.mjs +4 -4
  370. package/src/tui/themes/teal.mjs +4 -5
  371. package/src/tui/themes/utils.mjs +1 -1
  372. package/src/ui/statusline-agents.mjs +213 -0
  373. package/src/ui/statusline-format.mjs +146 -0
  374. package/src/ui/statusline-segments.mjs +148 -0
  375. package/src/ui/statusline.mjs +77 -462
  376. package/src/ui/tool-card.mjs +0 -1
  377. package/src/vendor/statusline/bin/statusline-route.mjs +15 -2
  378. package/src/workflows/default/WORKFLOW.md +16 -9
  379. package/src/workflows/sequential/WORKFLOW.md +16 -11
  380. package/src/workflows/solo/WORKFLOW.md +5 -1
  381. package/vendor/ink/build/display-width.js +19 -3
  382. package/vendor/ink/build/ink.js +103 -6
  383. package/vendor/ink/build/log-update.js +17 -3
  384. package/vendor/ink/build/wrap-text.js +125 -0
  385. package/scripts/_test-folder-dialog.mjs +0 -30
  386. package/scripts/fix-brief-fn.mjs +0 -35
  387. package/scripts/fix-format-tool-surface.mjs +0 -24
  388. package/scripts/fix-tool-exec-visible.mjs +0 -42
  389. package/scripts/patch-agent-brief.mjs +0 -48
  390. package/scripts/patch-app.mjs +0 -21
  391. package/scripts/patch-app2.mjs +0 -18
  392. package/scripts/patch-dist-brief.mjs +0 -96
  393. package/scripts/patch-tool-exec.mjs +0 -70
  394. package/src/examples/schedules/SCHEDULE.example.md +0 -32
  395. package/src/examples/webhooks/WEBHOOK.example.md +0 -40
  396. package/src/runtime/agent/orchestrator/session/manager.reactive-persist.test.mjs +0 -107
  397. package/src/runtime/agent/orchestrator/tools/builtin/arg-guard.test.mjs +0 -143
  398. package/src/runtime/agent/orchestrator/tools/builtin/diagnostics-tool.mjs +0 -285
  399. package/src/runtime/agent/orchestrator/tools/builtin/open-config-tool.mjs +0 -26
  400. package/src/runtime/shared/channel-notification-routing.test.mjs +0 -45
  401. package/src/runtime/shared/tool-execution-contract.test.mjs +0 -183
  402. package/src/standalone/agent-task-status.test.mjs +0 -76
  403. package/src/tui/components/tool-output-format.test.mjs +0 -399
  404. package/src/tui/display-width.test.mjs +0 -35
  405. package/src/tui/engine-runtime-notification.test.mjs +0 -115
  406. package/src/tui/engine-tool-result-text.test.mjs +0 -75
  407. package/src/tui/markdown/format-token.test.mjs +0 -354
  408. package/src/tui/markdown/render-ansi.test.mjs +0 -108
  409. package/src/tui/markdown/stream-fence.test.mjs +0 -26
  410. package/src/tui/markdown/streaming-markdown.test.mjs +0 -70
  411. package/src/tui/prompt-history-store.test.mjs +0 -52
  412. package/src/tui/statusline-ansi-bridge.test.mjs +0 -159
  413. package/src/tui/transcript-tool-failures.test.mjs +0 -111
  414. package/src/ui/markdown.test.mjs +0 -70
  415. package/src/ui/statusline-context-label.test.mjs +0 -15
  416. package/src/vendor/statusline/bin/statusline-lib.mjs +0 -186
  417. package/src/vendor/statusline/bin/statusline-route.test.mjs +0 -80
@@ -0,0 +1,230 @@
1
+ // Context-window sizing, compaction target math, and session context-meta
2
+ // resolution. Extracted verbatim from manager.mjs (behavior-preserving).
3
+ import { getModelMetadataSync } from '../../providers/model-catalog.mjs';
4
+ import { resolveCompactTriggerTokens } from '../context-utils.mjs';
5
+ import { normalizeCompactType, DEFAULT_COMPACT_TYPE } from '../compact.mjs';
6
+
7
+ // Known context windows for the current-generation models this plugin
8
+ // routes to. Anything not listed falls through to guessContextWindow() —
9
+ // local llama/mistral/phi default to 8192, everything else 128000. Keep
10
+ // this map trimmed to live models; older generations slow down reads
11
+ // without buying anything.
12
+ const CONTEXT_WINDOWS = {
13
+ // OpenAI GPT-5.x family (openai / openai-oauth)
14
+ 'gpt-5.5': 272000,
15
+ 'gpt-5.4': 272000,
16
+ 'gpt-5.4-mini': 272000,
17
+ 'gpt-5.4-nano': 272000,
18
+ // Anthropic Claude 4.x
19
+ 'claude-opus-4-8': 1000000,
20
+ 'claude-opus-4-7': 1000000,
21
+ 'claude-sonnet-4-6': 1000000,
22
+ 'claude-haiku-4-5-20251001': 200000,
23
+ // Google Gemini 3.x
24
+ 'gemini-3.1-pro': 1000000,
25
+ 'gemini-3-pro': 1000000,
26
+ 'gemini-3.5-flash': 1000000,
27
+ 'gemini-3-flash': 1000000,
28
+ // xAI Grok (catalog polyfill mirror — model-catalog PRICING_OVERRIDES)
29
+ 'grok-build-0.1': 256000,
30
+ 'grok-4.20': 1000000,
31
+ };
32
+ // Family-pattern fallback used only when both the provider catalog and the
33
+ // exact-id table miss (cold metadata, before the LiteLLM/models.dev catalog
34
+ // warms). Keep these aligned with the catalog so /context, gateway, and the
35
+ // runtime agree on the boundary the first time a model is routed. Local models
36
+ // (llama/mistral/phi/qwen/gemma) stay small so an unknown local id never claims
37
+ // a giant window.
38
+ const LOCAL_PROVIDERS = new Set(['ollama', 'lmstudio', 'llamacpp', 'llama.cpp', 'local', '']);
39
+ export function guessContextWindow(model, provider = null) {
40
+ if (CONTEXT_WINDOWS[model])
41
+ return CONTEXT_WINDOWS[model];
42
+ const m = String(model || '').toLowerCase();
43
+ const p = String(provider || '').toLowerCase();
44
+ const isLocalProvider = LOCAL_PROVIDERS.has(p);
45
+ // Local/self-hosted families — never inflate an unknown local id.
46
+ if (isLocalProvider && (m.includes('llama') || m.includes('mistral') || m.includes('mixtral')
47
+ || m.includes('phi') || m.includes('qwen') || m.includes('gemma')
48
+ || m.includes('deepseek-r1') || m.includes('codellama')))
49
+ return 8192;
50
+ // Current hosted families by name pattern.
51
+ if (m.startsWith('claude-opus') || m.startsWith('claude-sonnet')) return 1000000;
52
+ if (m.startsWith('claude-haiku') || m.startsWith('claude-')) return 200000;
53
+ if (m.startsWith('gemini-3') || m.startsWith('gemini-2')) return 1000000;
54
+ if (m.startsWith('gpt-5')) return 272000;
55
+ if (m.startsWith('grok-build')) return 256000;
56
+ if (m.startsWith('grok-')) return 1000000;
57
+ if (m.startsWith('deepseek-v')) return 1000000;
58
+ return 128000;
59
+ }
60
+ export function positiveContextWindow(value) {
61
+ const n = Number(value);
62
+ return Number.isFinite(n) && n > 0 ? Math.floor(n) : null;
63
+ }
64
+ export function envFlag(name, fallback = false) {
65
+ const v = process.env[name];
66
+ if (v === undefined) return fallback;
67
+ return !['0', 'false', 'off', 'no'].includes(String(v).trim().toLowerCase());
68
+ }
69
+ function boundedPercent(value, fallback = null) {
70
+ const n = Number(value);
71
+ if (Number.isFinite(n) && n > 0 && n <= 100) return n;
72
+ return fallback;
73
+ }
74
+ function providerNameOf(provider) {
75
+ if (typeof provider === 'string') return provider.toLowerCase();
76
+ return String(provider?.name || provider?.id || '').toLowerCase();
77
+ }
78
+ // Carry the percent/ratio-named buffer config from a compaction config object
79
+ // onto session.compaction so the shared compact-policy parser honors configured
80
+ // buffer
81
+ // percent/ratio. Only finite positive values are copied; absent fields stay
82
+ // undefined so the default-ratio fallback still applies.
83
+ export function preserveBufferConfigFields(cfg = {}) {
84
+ const out = {};
85
+ for (const key of ['bufferPercent', 'bufferPct', 'bufferRatio', 'bufferFraction']) {
86
+ const n = Number(cfg?.[key]);
87
+ if (Number.isFinite(n) && n > 0) out[key] = n;
88
+ }
89
+ return out;
90
+ }
91
+ const COMPACT_TARGET_RATIO = 0.02;
92
+ const COMPACT_TARGET_MIN_TOKENS = 4_000;
93
+ const COMPACT_TARGET_MAX_TOKENS = 16_000;
94
+ function compactTargetRatio() {
95
+ const raw = process.env.MIXDOG_AGENT_COMPACT_TARGET_PERCENT
96
+ ?? process.env.MIXDOG_COMPACT_TARGET_PERCENT
97
+ ?? COMPACT_TARGET_RATIO;
98
+ const n = Number(raw);
99
+ if (!Number.isFinite(n) || n <= 0) return COMPACT_TARGET_RATIO;
100
+ return n > 1 ? n / 100 : n;
101
+ }
102
+ function compactTargetTokensForBoundary(boundaryTokens) {
103
+ const boundary = positiveContextWindow(boundaryTokens);
104
+ if (!boundary) return null;
105
+ const explicit = positiveContextWindow(
106
+ process.env.MIXDOG_AGENT_COMPACT_TARGET_TOKENS
107
+ ?? process.env.MIXDOG_COMPACT_TARGET_TOKENS,
108
+ );
109
+ if (explicit) return Math.max(1, Math.min(boundary, explicit));
110
+ const minTarget = Math.min(boundary, positiveContextWindow(process.env.MIXDOG_COMPACT_TARGET_MIN_TOKENS) || COMPACT_TARGET_MIN_TOKENS);
111
+ const maxTarget = Math.min(boundary, positiveContextWindow(process.env.MIXDOG_COMPACT_TARGET_MAX_TOKENS) || COMPACT_TARGET_MAX_TOKENS);
112
+ const byRatio = Math.max(1, Math.floor(boundary * compactTargetRatio()));
113
+ return Math.max(1, Math.min(boundary, maxTarget, Math.max(minTarget, byRatio)));
114
+ }
115
+ function defaultEffectiveContextWindowPercent(provider) {
116
+ // Gateway/statusline route metadata reserves a universal 10% headroom from
117
+ // the raw catalog window. Keep session compaction on the same effective
118
+ // capacity so /context, the TUI statusline, and gateway telemetry agree.
119
+ return 90;
120
+ }
121
+ const PROVIDER_SYNTHETIC_CONTEXT_DEFAULT = 1_000_000;
122
+ function providerRawContextWindow(info, catalogInfo) {
123
+ if (!info || typeof info !== 'object') return null;
124
+ const fromApiFields = positiveContextWindow(info.context_window)
125
+ || positiveContextWindow(info.max_context_window);
126
+ if (fromApiFields) return fromApiFields;
127
+ const fromCache = positiveContextWindow(info.contextWindow)
128
+ || positiveContextWindow(info.maxContextWindow);
129
+ if (!fromCache) return null;
130
+ const catalogWindow = positiveContextWindow(catalogInfo?.contextWindow)
131
+ || positiveContextWindow(catalogInfo?.maxContextWindow)
132
+ || positiveContextWindow(catalogInfo?.context_window)
133
+ || positiveContextWindow(catalogInfo?.max_context_window);
134
+ if (fromCache === PROVIDER_SYNTHETIC_CONTEXT_DEFAULT
135
+ && catalogWindow
136
+ && fromCache !== catalogWindow) {
137
+ return null;
138
+ }
139
+ return fromCache;
140
+ }
141
+ export function resolveSessionContextMeta(provider, model, seed = {}) {
142
+ const info = typeof provider?.getCachedModelInfo === 'function'
143
+ ? provider.getCachedModelInfo(model)
144
+ : null;
145
+ const catalogInfo = getModelMetadataSync(model, providerNameOf(provider));
146
+ const rawContextWindow = providerRawContextWindow(info, catalogInfo)
147
+ || positiveContextWindow(catalogInfo?.contextWindow)
148
+ || positiveContextWindow(catalogInfo?.maxContextWindow)
149
+ || positiveContextWindow(catalogInfo?.context_window)
150
+ || positiveContextWindow(catalogInfo?.max_context_window)
151
+ || positiveContextWindow(seed.rawContextWindow)
152
+ || positiveContextWindow(seed.raw_context_window)
153
+ || positiveContextWindow(seed.contextWindow)
154
+ || guessContextWindow(model, providerNameOf(provider));
155
+ const effectiveContextWindowPercent = boundedPercent(
156
+ seed.effectiveContextWindowPercent
157
+ ?? seed.effective_context_window_percent
158
+ ?? info?.effectiveContextWindowPercent
159
+ ?? info?.effective_context_window_percent
160
+ ?? catalogInfo?.effectiveContextWindowPercent
161
+ ?? catalogInfo?.effective_context_window_percent,
162
+ defaultEffectiveContextWindowPercent(provider),
163
+ );
164
+ const pct = boundedPercent(effectiveContextWindowPercent, 100);
165
+ const contextWindow = Math.max(1, Math.floor(rawContextWindow * pct / 100));
166
+ const compactBoundaryTokens = contextWindow;
167
+ const rawCompactLimit = positiveContextWindow(
168
+ seed.autoCompactTokenLimit
169
+ ?? seed.auto_compact_token_limit
170
+ ?? info?.autoCompactTokenLimit
171
+ ?? info?.auto_compact_token_limit
172
+ ?? catalogInfo?.autoCompactTokenLimit
173
+ ?? catalogInfo?.auto_compact_token_limit,
174
+ );
175
+ // Legacy-data migration: old implementations derived autoCompactTokenLimit
176
+ // from the full effective/raw window and persisted it onto the session.
177
+ // A resumed session therefore re-seeds autoCompactTokenLimit == boundary
178
+ // (or the raw window), which compactTriggerForSession / loop policy used to
179
+ // honor as an explicit trigger, collapsing the compaction buffer to 0. Only
180
+ // accept an explicit limit that is STRICTLY BELOW the boundary; a value at
181
+ // or above the boundary is a derived full-window artifact and is dropped to
182
+ // null so the trigger falls back to the default boundary trigger.
183
+ const explicitCompactLimit = rawCompactLimit && rawCompactLimit < compactBoundaryTokens
184
+ ? rawCompactLimit
185
+ : null;
186
+ // Do NOT derive the auto-compact limit from the full effective window.
187
+ // Setting it to contextWindow makes autoTriggerTokens == boundary and the
188
+ // compaction buffer collapse to 0 (loop.mjs:708-713 / compactTriggerForSession),
189
+ // so auto-compact only fires when the context is already at the limit —
190
+ // at which point semantic compact fails ("result exceeds budget" /
191
+ // "summary cannot fit") and the turn can no longer be resumed.
192
+ // Leave it null unless the provider/catalog/seed supplies an explicit
193
+ // limit; the downstream buffer logic (default 10%, capped 25%) then
194
+ // triggers compaction with headroom, matching the reference auto-compact threshold.
195
+ const autoCompactTokenLimit = explicitCompactLimit || null;
196
+ return {
197
+ contextWindow,
198
+ rawContextWindow,
199
+ effectiveContextWindowPercent,
200
+ autoCompactTokenLimit: autoCompactTokenLimit || null,
201
+ compactBoundaryTokens,
202
+ };
203
+ }
204
+ export function compactTriggerForSession(session, boundaryTokens) {
205
+ return resolveCompactTriggerTokens(session, boundaryTokens);
206
+ }
207
+ export function compactTargetBudget(boundaryTokens, reserveTokens, _sourceTokens = null, _ratio = null) {
208
+ const boundary = positiveContextWindow(boundaryTokens);
209
+ if (!boundary) return null;
210
+ const reserve = Math.max(0, Number(reserveTokens) || 0);
211
+ const targetEffective = compactTargetTokensForBoundary(boundary) || boundary;
212
+ return Math.max(1, Math.min(boundary, targetEffective + reserve));
213
+ }
214
+ export function semanticCompactionEnabledForSession(session) {
215
+ const cfg = session?.compaction || {};
216
+ if (process.env.MIXDOG_AGENT_COMPACT_SEMANTIC !== undefined) return envFlag('MIXDOG_AGENT_COMPACT_SEMANTIC', true);
217
+ if (process.env.MIXDOG_COMPACT_SEMANTIC !== undefined) return envFlag('MIXDOG_COMPACT_SEMANTIC', true);
218
+ if (cfg.semantic === false || cfg.semantic === 'false' || cfg.semantic === 'off') return false;
219
+ if (cfg.semantic === true || cfg.semantic === 'true' || cfg.semantic === 'on' || cfg.semantic === 'auto') return true;
220
+ return true;
221
+ }
222
+ export function compactTypeForSession(session) {
223
+ const cfg = session?.compaction || {};
224
+ const configured = process.env.MIXDOG_AGENT_COMPACT_TYPE
225
+ ?? process.env.MIXDOG_COMPACT_TYPE
226
+ ?? cfg.type
227
+ ?? cfg.compactType
228
+ ?? cfg.compact_type;
229
+ return normalizeCompactType(configured, DEFAULT_COMPACT_TYPE);
230
+ }
@@ -0,0 +1,235 @@
1
+ // Steering / pending-message queue with sync buffered + atomic-file persistence.
2
+ // Extracted verbatim from manager.mjs (behavior-preserving).
3
+ import { join } from 'path';
4
+ import { resolvePluginData } from '../../../../shared/plugin-paths.mjs';
5
+ import { updateJsonAtomicSync } from '../../../../shared/atomic-file.mjs';
6
+ import { promptContentText, isInternalRuntimeNotificationText } from './prompt-utils.mjs';
7
+
8
+ const _sessionPendingMessages = new Map();
9
+ const PENDING_MESSAGES_FILE = 'session-pending-messages.json';
10
+ const PENDING_MESSAGES_MODE = 0o600;
11
+ const _pendingPersistBuffers = new Map();
12
+ let _pendingPersistImmediate = null;
13
+
14
+ function pendingMessagesPath() {
15
+ return join(resolvePluginData(), PENDING_MESSAGES_FILE);
16
+ }
17
+
18
+ function isValidPendingSessionId(sessionId) {
19
+ return typeof sessionId === 'string' && /^[A-Za-z0-9_-]+$/.test(sessionId);
20
+ }
21
+
22
+ function normalizePendingStore(raw) {
23
+ const sessions = raw && typeof raw === 'object' && raw.sessions && typeof raw.sessions === 'object'
24
+ ? raw.sessions
25
+ : {};
26
+ const out = { version: 1, updatedAt: Date.now(), sessions: {} };
27
+ for (const [sid, value] of Object.entries(sessions)) {
28
+ if (!isValidPendingSessionId(sid) || !Array.isArray(value)) continue;
29
+ const q = value
30
+ .map((entry) => {
31
+ if (typeof entry === 'string') return entry;
32
+ if (entry && typeof entry === 'object' && typeof entry.message === 'string') return entry.message;
33
+ return '';
34
+ })
35
+ .filter(Boolean);
36
+ if (q.length > 0) out.sessions[sid] = q;
37
+ }
38
+ return out;
39
+ }
40
+
41
+ function normalizePendingMessageEntry(entry) {
42
+ if (typeof entry === 'string') {
43
+ const text = entry.trim();
44
+ return text ? { content: text, text } : null;
45
+ }
46
+ if (Array.isArray(entry)) {
47
+ if (entry.length === 0) return null;
48
+ const text = promptContentText(entry).trim();
49
+ return { content: entry, text };
50
+ }
51
+ if (!entry || typeof entry !== 'object') return null;
52
+ const content = Object.prototype.hasOwnProperty.call(entry, 'content') ? entry.content : null;
53
+ if (content == null) return null;
54
+ const text = typeof entry.text === 'string' ? entry.text.trim() : promptContentText(content).trim();
55
+ if (Array.isArray(content)) return content.length > 0 ? { content, text } : null;
56
+ if (typeof content === 'string') {
57
+ const value = content.trim();
58
+ return value ? { content: value, text: text || value } : null;
59
+ }
60
+ const fallback = promptContentText(content).trim();
61
+ return fallback ? { content: fallback, text: text || fallback } : null;
62
+ }
63
+
64
+ function pendingMessageText(entry) {
65
+ const normalized = normalizePendingMessageEntry(entry);
66
+ return normalized ? String(normalized.text || promptContentText(normalized.content) || '').trim() : '';
67
+ }
68
+
69
+ function pendingMessageQueueEntry(entry) {
70
+ const normalized = normalizePendingMessageEntry(entry);
71
+ if (!normalized) return null;
72
+ if (typeof normalized.content === 'string' && normalized.content === normalized.text) return normalized.content;
73
+ return { content: normalized.content, text: normalized.text || promptContentText(normalized.content).trim() };
74
+ }
75
+
76
+ function persistPendingMessages(sessionId, messages) {
77
+ if (!isValidPendingSessionId(sessionId)) return 0;
78
+ const persistedMessages = (Array.isArray(messages) ? messages : [messages])
79
+ .map(pendingMessageText)
80
+ .filter(Boolean);
81
+ if (persistedMessages.length === 0) return 0;
82
+ let depth = 0;
83
+ try {
84
+ updateJsonAtomicSync(pendingMessagesPath(), (raw) => {
85
+ const next = normalizePendingStore(raw);
86
+ const q = Array.isArray(next.sessions[sessionId]) ? next.sessions[sessionId] : [];
87
+ q.push(...persistedMessages);
88
+ next.sessions[sessionId] = q;
89
+ next.updatedAt = Date.now();
90
+ depth = q.length;
91
+ return next;
92
+ }, { compact: true, lock: true, mode: PENDING_MESSAGES_MODE, fsync: false });
93
+ } catch (err) {
94
+ try { process.stderr.write(`[session] pending-message persist failed sessionId=${sessionId}: ${err?.message || err}\n`); } catch {}
95
+ }
96
+ return depth;
97
+ }
98
+
99
+ function flushPendingMessagePersistsSync() {
100
+ if (_pendingPersistImmediate) {
101
+ try { clearImmediate(_pendingPersistImmediate); } catch {}
102
+ _pendingPersistImmediate = null;
103
+ }
104
+ if (_pendingPersistBuffers.size === 0) return;
105
+ const batches = [..._pendingPersistBuffers.entries()];
106
+ _pendingPersistBuffers.clear();
107
+ for (const [sid, messages] of batches) {
108
+ persistPendingMessages(sid, messages);
109
+ }
110
+ }
111
+
112
+ function schedulePendingMessagePersist(sessionId, message) {
113
+ if (!isValidPendingSessionId(sessionId)) return 0;
114
+ const persistedMessage = pendingMessageText(message);
115
+ if (!persistedMessage) return 0;
116
+ const q = _pendingPersistBuffers.get(sessionId) || [];
117
+ q.push(persistedMessage);
118
+ _pendingPersistBuffers.set(sessionId, q);
119
+ if (!_pendingPersistImmediate) {
120
+ _pendingPersistImmediate = setImmediate(() => {
121
+ _pendingPersistImmediate = null;
122
+ flushPendingMessagePersistsSync();
123
+ });
124
+ }
125
+ return q.length;
126
+ }
127
+
128
+ function takeBufferedPendingMessages(sessionId) {
129
+ if (!isValidPendingSessionId(sessionId)) return [];
130
+ const buffered = _pendingPersistBuffers.get(sessionId);
131
+ if (!buffered || buffered.length === 0) return [];
132
+ _pendingPersistBuffers.delete(sessionId);
133
+ return buffered.slice();
134
+ }
135
+
136
+ function drainPersistedPendingMessages(sessionId) {
137
+ if (!isValidPendingSessionId(sessionId)) return [];
138
+ let drained = [];
139
+ try {
140
+ updateJsonAtomicSync(pendingMessagesPath(), (raw) => {
141
+ const next = normalizePendingStore(raw);
142
+ const q = Array.isArray(next.sessions[sessionId]) ? next.sessions[sessionId] : [];
143
+ drained = q.filter((m) => typeof m === 'string' && m.length > 0);
144
+ if (drained.length === 0) return undefined;
145
+ delete next.sessions[sessionId];
146
+ next.updatedAt = Date.now();
147
+ return next;
148
+ }, { compact: true, lock: true, mode: PENDING_MESSAGES_MODE, fsync: false });
149
+ } catch (err) {
150
+ try { process.stderr.write(`[session] pending-message drain failed sessionId=${sessionId}: ${err?.message || err}\n`); } catch {}
151
+ }
152
+ return drained;
153
+ }
154
+
155
+ function modelVisiblePendingMessages(messages) {
156
+ return (Array.isArray(messages) ? messages : [])
157
+ .map(pendingMessageQueueEntry)
158
+ .filter(Boolean)
159
+ .filter((message) => !isInternalRuntimeNotificationText(
160
+ message && typeof message === 'object' && Object.prototype.hasOwnProperty.call(message, 'content')
161
+ ? message.content
162
+ : message,
163
+ ));
164
+ }
165
+
166
+ export function _mergePendingMessageEntries(entries) {
167
+ const normalized = (Array.isArray(entries) ? entries : [])
168
+ .map(normalizePendingMessageEntry)
169
+ .filter(Boolean);
170
+ if (normalized.length === 0) return null;
171
+ const displayText = normalized.map((entry) => entry.text || promptContentText(entry.content))
172
+ .filter((text) => String(text || '').trim())
173
+ .join('\n');
174
+ if (normalized.every((entry) => typeof entry.content === 'string')) {
175
+ return {
176
+ content: normalized.map((entry) => entry.content).filter(Boolean).join('\n'),
177
+ text: displayText,
178
+ count: normalized.length,
179
+ };
180
+ }
181
+ const parts = [];
182
+ for (const entry of normalized) {
183
+ if (typeof entry.content === 'string') {
184
+ if (entry.content.trim()) parts.push({ type: 'text', text: entry.content });
185
+ } else if (Array.isArray(entry.content)) {
186
+ parts.push(...entry.content);
187
+ } else {
188
+ const text = promptContentText(entry.content);
189
+ if (text.trim()) parts.push({ type: 'text', text });
190
+ }
191
+ parts.push({ type: 'text', text: '\n' });
192
+ }
193
+ while (parts.length && parts[parts.length - 1]?.type === 'text' && parts[parts.length - 1]?.text === '\n') parts.pop();
194
+ return { content: parts, text: displayText || promptContentText(parts), count: normalized.length };
195
+ }
196
+
197
+ export function enqueuePendingMessage(sessionId, message) {
198
+ const entry = pendingMessageQueueEntry(message);
199
+ if (!sessionId || !entry) return 0;
200
+ let q = _sessionPendingMessages.get(sessionId);
201
+ if (!q) { q = []; _sessionPendingMessages.set(sessionId, q); }
202
+ q.push(entry);
203
+ const bufferedDepth = schedulePendingMessagePersist(sessionId, entry);
204
+ return Math.max(q.length, bufferedDepth || 0);
205
+ }
206
+
207
+ export function drainPendingMessages(sessionId) {
208
+ const q = _sessionPendingMessages.get(sessionId);
209
+ const memory = q && q.length > 0 ? q.slice() : [];
210
+ _sessionPendingMessages.delete(sessionId);
211
+ const persisted = [...takeBufferedPendingMessages(sessionId), ...drainPersistedPendingMessages(sessionId)];
212
+ const memoryVisible = modelVisiblePendingMessages(memory);
213
+ const persistedVisible = modelVisiblePendingMessages(persisted);
214
+ if (memoryVisible.length === 0) return persistedVisible;
215
+ if (persistedVisible.length === 0) return memoryVisible;
216
+ const persistedTexts = persistedVisible.map(pendingMessageText);
217
+ const prefixMatches = memoryVisible.every((m, i) => persistedTexts[i] === pendingMessageText(m));
218
+ if (prefixMatches) return [...memoryVisible, ...persistedVisible.slice(memoryVisible.length)];
219
+ const out = persistedVisible.slice();
220
+ const seen = new Set(persistedTexts);
221
+ for (const m of memoryVisible) {
222
+ const text = pendingMessageText(m);
223
+ if (!text || seen.has(text)) continue;
224
+ out.push(m);
225
+ seen.add(text);
226
+ }
227
+ return out;
228
+ }
229
+
230
+ // Cleanup hook for closeSession — drop the in-memory queue and buffered-persist
231
+ // entry so both Maps do not accumulate one entry per closed session.
232
+ export function _dropPendingMessageState(id) {
233
+ try { _sessionPendingMessages.delete(id); } catch { /* ignore */ }
234
+ try { _pendingPersistBuffers.delete(id); } catch { /* ignore */ }
235
+ }
@@ -0,0 +1,149 @@
1
+ // Prompt content + temporal helpers, extracted verbatim from manager.mjs
2
+ // (behavior-preserving). Pure string/date utilities with no session state.
3
+ import { isInternalRuntimeNotificationText as contractIsInternalRuntimeNotificationText } from '../../../../shared/tool-execution-contract.mjs';
4
+ import { SUMMARY_PREFIX } from '../compact.mjs';
5
+
6
+ export function promptContentText(content) {
7
+ if (typeof content === 'string') return content;
8
+ if (Array.isArray(content)) {
9
+ return content.map((part) => {
10
+ if (typeof part === 'string') return part;
11
+ if (part?.type === 'text') return part.text || '';
12
+ if (part?.type === 'image') return '[Image]';
13
+ return part?.text || '';
14
+ }).filter(Boolean).join('\n');
15
+ }
16
+ return String(content ?? '');
17
+ }
18
+
19
+ export function hasModelVisiblePromptContent(prompt) {
20
+ return !!promptContentText(prompt).trim();
21
+ }
22
+
23
+ export function promptContentBytes(content) {
24
+ try {
25
+ if (typeof content === 'string') return Buffer.byteLength(content, 'utf8');
26
+ return Buffer.byteLength(JSON.stringify(content), 'utf8');
27
+ } catch {
28
+ return Buffer.byteLength(promptContentText(content), 'utf8');
29
+ }
30
+ }
31
+
32
+ export function prefixUserTurnContent(content, contextBlock) {
33
+ if (!contextBlock) return content;
34
+ if (Array.isArray(content)) {
35
+ return [{ type: 'text', text: `${contextBlock}# Task\n` }, ...content];
36
+ }
37
+ return `${contextBlock}# Task\n${content}`;
38
+ }
39
+
40
+ export function prefixSessionStartContent(content, sessionBlock) {
41
+ if (!sessionBlock) return content;
42
+ if (Array.isArray(content)) {
43
+ return [{ type: 'text', text: `${sessionBlock}\n\n` }, ...content];
44
+ }
45
+ return `${sessionBlock}\n\n${content}`;
46
+ }
47
+
48
+ function localIsoDate(date = new Date()) {
49
+ const year = date.getFullYear();
50
+ const month = String(date.getMonth() + 1).padStart(2, '0');
51
+ const day = String(date.getDate()).padStart(2, '0');
52
+ return `${year}-${month}-${day}`;
53
+ }
54
+
55
+ function localDateTimeWithZone(date = new Date()) {
56
+ const datePart = localIsoDate(date);
57
+ const hh = String(date.getHours()).padStart(2, '0');
58
+ const mm = String(date.getMinutes()).padStart(2, '0');
59
+ const ss = String(date.getSeconds()).padStart(2, '0');
60
+ let zone = '';
61
+ try { zone = Intl.DateTimeFormat().resolvedOptions().timeZone || ''; } catch {}
62
+ return zone ? `${datePart} ${hh}:${mm}:${ss} ${zone}` : `${datePart} ${hh}:${mm}:${ss}`;
63
+ }
64
+
65
+ function temporalPromptText(content) {
66
+ const text = promptContentText(content)
67
+ .replace(/\s+/g, ' ')
68
+ .trim()
69
+ .toLowerCase();
70
+ return text;
71
+ }
72
+
73
+ function promptNeedsDateReminder(content) {
74
+ const text = temporalPromptText(content);
75
+ if (!text) return false;
76
+ return /(?:오늘|내일|어제|모레|그저께|요즘|최근|방금|아까|현재\s*(?:날짜|시간|시각)|지금\s*(?:몇\s*시|시간|날짜|요일)|몇\s*월\s*몇\s*일|몇\s*시|무슨\s*요일|요일|날짜|이번\s*(?:주|달|월|년)|지난\s*(?:주|달|월|년)|다음\s*(?:주|달|월|년)|올해|작년|내년|today|tomorrow|yesterday|recently|current\s+(?:date|time)|what\s+(?:date|time)|which\s+day|weekday|this\s+(?:week|month|year)|last\s+(?:week|month|year)|next\s+(?:week|month|year))/i.test(text);
77
+ }
78
+
79
+ function promptNeedsTimeReminder(content) {
80
+ const text = temporalPromptText(content);
81
+ if (!text) return false;
82
+ return /(?:현재\s*(?:시간|시각)|지금\s*(?:몇\s*시|시간)|몇\s*시|시각|시간|current\s+time|what\s+time|time\s+is\s+it)/i.test(text);
83
+ }
84
+
85
+ export function buildCurrentTimeBlock(content) {
86
+ const needsTime = promptNeedsTimeReminder(content);
87
+ if (!needsTime && !promptNeedsDateReminder(content)) return '';
88
+ return localDateTimeWithZone(new Date());
89
+ }
90
+
91
+ function sessionModelDisplay(model) {
92
+ const text = String(model || '').trim();
93
+ if (!text) return '';
94
+ return text
95
+ .replace(/-\d{4}-\d{2}-\d{2}$/, '')
96
+ .replace(/^gpt-/i, 'GPT-')
97
+ .replace(/(?:^|-)([a-z])/g, (m) => m.toUpperCase());
98
+ }
99
+
100
+ export function buildSessionStartBlock(session, cwd) {
101
+ if (!session || session.owner === 'agent') return '';
102
+ const lines = ['# Session'];
103
+ const effectiveCwd = String(cwd || session.cwd || '').trim();
104
+ if (effectiveCwd) lines.push(`Cwd: ${effectiveCwd}`);
105
+ const modelBits = [
106
+ sessionModelDisplay(session.model),
107
+ session.effort ? String(session.effort).trim().toUpperCase() : '',
108
+ session.fast === true ? 'FAST' : '',
109
+ ].filter(Boolean);
110
+ if (modelBits.length) lines.push(`Model: ${modelBits.join(' · ')}`);
111
+ const workflowName = String(session.workflow?.name || session.workflow?.id || '').trim();
112
+ if (workflowName) lines.push(`Workflow: ${workflowName}`);
113
+ return lines.length > 1 ? lines.join('\n') : '';
114
+ }
115
+
116
+ export function isReferenceFilesMessage(message) {
117
+ return message?.role === 'user'
118
+ && typeof message.content === 'string'
119
+ && /^Reference files:\s*/i.test(message.content.trimStart());
120
+ }
121
+
122
+ export function isProtectedContextUserMessage(message) {
123
+ return message?.role === 'user'
124
+ && typeof message.content === 'string'
125
+ && message.content.trimStart().startsWith('<system-reminder>');
126
+ }
127
+
128
+ // Compact summary messages (role:'user', content startsWith SUMMARY_PREFIX)
129
+ // are synthetic anchors, not a real human turn — they must not count as
130
+ // "user conversation" or the post-clear/post-compact session-start block
131
+ // would be wrongly suppressed on the next real user turn.
132
+ function isSummaryAnchorMessage(message) {
133
+ return message?.role === 'user'
134
+ && typeof message.content === 'string'
135
+ && message.content.startsWith(SUMMARY_PREFIX);
136
+ }
137
+
138
+ export function hasUserConversationMessage(messages) {
139
+ return (Array.isArray(messages) ? messages : []).some((message) => (
140
+ message?.role === 'user'
141
+ && !isProtectedContextUserMessage(message)
142
+ && !isReferenceFilesMessage(message)
143
+ && !isSummaryAnchorMessage(message)
144
+ ));
145
+ }
146
+
147
+ export function isInternalRuntimeNotificationText(content) {
148
+ return contractIsInternalRuntimeNotificationText(promptContentText(content));
149
+ }