mixdog 0.8.0 → 0.9.0

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 (285) hide show
  1. package/README.md +47 -23
  2. package/package.json +33 -27
  3. package/scripts/_test-folder-dialog.mjs +30 -0
  4. package/scripts/agent-parallel-smoke.mjs +388 -0
  5. package/scripts/agent-tag-reuse-smoke.mjs +183 -0
  6. package/scripts/background-task-meta-smoke.mjs +38 -0
  7. package/scripts/boot-smoke.mjs +52 -9
  8. package/scripts/build-runtime-linux.sh +348 -0
  9. package/scripts/build-runtime-macos.sh +217 -0
  10. package/scripts/build-runtime-windows.ps1 +242 -0
  11. package/scripts/compact-active-turn-test.mjs +68 -0
  12. package/scripts/compact-smoke.mjs +859 -129
  13. package/scripts/compact-trigger-migration-smoke.mjs +187 -0
  14. package/scripts/fix-brief-fn.mjs +35 -0
  15. package/scripts/fix-format-tool-surface.mjs +24 -0
  16. package/scripts/fix-tool-exec-visible.mjs +42 -0
  17. package/scripts/generate-runtime-manifest.mjs +166 -0
  18. package/scripts/hook-bus-test.mjs +330 -0
  19. package/scripts/lead-workflow-smoke.mjs +33 -39
  20. package/scripts/live-worker-smoke.mjs +43 -37
  21. package/scripts/llm-trace-summary.mjs +315 -0
  22. package/scripts/memory-meta-concurrency-test.mjs +20 -0
  23. package/scripts/output-style-smoke.mjs +56 -15
  24. package/scripts/parent-abort-link-test.mjs +44 -0
  25. package/scripts/patch-agent-brief.mjs +48 -0
  26. package/scripts/patch-app.mjs +21 -0
  27. package/scripts/patch-app2.mjs +18 -0
  28. package/scripts/patch-dist-brief.mjs +96 -0
  29. package/scripts/patch-tool-exec.mjs +70 -0
  30. package/scripts/pretool-ask-runtime-test.mjs +54 -0
  31. package/scripts/provider-toolcall-test.mjs +376 -0
  32. package/scripts/reactive-compact-persist-smoke.mjs +124 -0
  33. package/scripts/sanitize-tool-pairs-test.mjs +260 -0
  34. package/scripts/session-context-bench.mjs +344 -0
  35. package/scripts/session-ingest-smoke.mjs +177 -0
  36. package/scripts/set-effort-config-test.mjs +41 -0
  37. package/scripts/smoke-runtime-negative.ps1 +106 -0
  38. package/scripts/smoke-runtime-negative.sh +97 -0
  39. package/scripts/smoke.mjs +25 -0
  40. package/scripts/tool-result-hook-test.mjs +48 -0
  41. package/scripts/tool-smoke.mjs +1223 -95
  42. package/scripts/toolcall-args-test.mjs +150 -0
  43. package/scripts/tui-background-failure-smoke.mjs +73 -0
  44. package/scripts/usage-metrics-epoch-smoke.mjs +114 -0
  45. package/src/agents/debugger/AGENT.md +8 -0
  46. package/src/agents/explore/AGENT.md +4 -0
  47. package/src/agents/heavy-worker/AGENT.md +9 -3
  48. package/src/agents/maintainer/AGENT.md +4 -0
  49. package/src/agents/reviewer/AGENT.md +8 -0
  50. package/src/agents/scheduler-task/AGENT.md +12 -0
  51. package/src/agents/scheduler-task/agent.json +6 -0
  52. package/src/agents/webhook-handler/AGENT.md +12 -0
  53. package/src/agents/webhook-handler/agent.json +6 -0
  54. package/src/agents/worker/AGENT.md +9 -3
  55. package/src/app.mjs +77 -3
  56. package/src/defaults/hidden-roles.json +17 -12
  57. package/src/headless-role.mjs +117 -0
  58. package/src/help.mjs +30 -0
  59. package/src/hooks/lib/permission-evaluator.cjs +11 -475
  60. package/src/lib/keychain-cjs.cjs +9 -1
  61. package/src/lib/mixdog-debug.cjs +0 -7
  62. package/src/lib/rules-builder.cjs +240 -96
  63. package/src/lib/text-utils.cjs +1 -1
  64. package/src/mixdog-session-runtime.mjs +2325 -450
  65. package/src/output-styles/default.md +12 -28
  66. package/src/output-styles/extreme-simple.md +9 -6
  67. package/src/output-styles/simple.md +22 -9
  68. package/src/repl.mjs +118 -59
  69. package/src/rules/agent/00-common.md +15 -0
  70. package/src/rules/{bridge → agent}/20-skip-protocol.md +1 -2
  71. package/src/rules/agent/30-explorer.md +22 -0
  72. package/src/rules/{bridge → agent}/40-cycle1-agent.md +7 -0
  73. package/src/rules/{bridge → agent}/41-cycle2-agent.md +7 -0
  74. package/src/rules/{bridge → agent}/42-cycle3-agent.md +7 -0
  75. package/src/rules/lead/01-general.md +9 -5
  76. package/src/rules/lead/04-workflow.md +51 -12
  77. package/src/rules/lead/lead-tool.md +6 -0
  78. package/src/rules/shared/01-tool.md +12 -1
  79. package/src/runtime/agent/orchestrator/activity-bus.mjs +7 -18
  80. package/src/runtime/agent/orchestrator/agent-owner.mjs +11 -0
  81. package/src/runtime/agent/orchestrator/{smart-bridge/bridge-llm.mjs → agent-runtime/agent-dispatch.mjs} +138 -111
  82. package/src/runtime/agent/orchestrator/agent-runtime/agent-progress-watchdog.mjs +94 -0
  83. package/src/runtime/agent/orchestrator/{smart-bridge → agent-runtime}/cache-strategy.mjs +32 -23
  84. package/src/runtime/agent/orchestrator/{smart-bridge → agent-runtime}/session-builder.mjs +33 -27
  85. package/src/runtime/agent/orchestrator/{bridge-trace.mjs → agent-trace.mjs} +132 -81
  86. package/src/runtime/agent/orchestrator/cache-mtime.mjs +0 -21
  87. package/src/runtime/agent/orchestrator/config.mjs +174 -55
  88. package/src/runtime/agent/orchestrator/context/collect.mjs +195 -487
  89. package/src/runtime/agent/orchestrator/dispatch-persist.mjs +1 -1
  90. package/src/runtime/agent/orchestrator/internal-roles.mjs +77 -29
  91. package/src/runtime/agent/orchestrator/internal-tools.mjs +5 -6
  92. package/src/runtime/agent/orchestrator/mcp/client.mjs +15 -9
  93. package/src/runtime/agent/orchestrator/providers/anthropic-betas.mjs +7 -1
  94. package/src/runtime/agent/orchestrator/providers/anthropic-oauth.mjs +377 -243
  95. package/src/runtime/agent/orchestrator/providers/anthropic.mjs +146 -93
  96. package/src/runtime/agent/orchestrator/providers/api-usage.mjs +236 -4
  97. package/src/runtime/agent/orchestrator/providers/custom-tool-wire.mjs +49 -0
  98. package/src/runtime/agent/orchestrator/providers/gemini.mjs +58 -13
  99. package/src/runtime/agent/orchestrator/providers/grok-oauth.mjs +67 -149
  100. package/src/runtime/agent/orchestrator/providers/media-normalization.mjs +132 -2
  101. package/src/runtime/agent/orchestrator/providers/model-catalog.mjs +4 -1
  102. package/src/runtime/agent/orchestrator/providers/oauth-credential-probes.mjs +45 -0
  103. package/src/runtime/agent/orchestrator/providers/oauth-usage.mjs +61 -116
  104. package/src/runtime/agent/orchestrator/providers/openai-compat-presets.mjs +25 -0
  105. package/src/runtime/agent/orchestrator/providers/openai-compat-stream.mjs +79 -255
  106. package/src/runtime/agent/orchestrator/providers/openai-compat.mjs +221 -70
  107. package/src/runtime/agent/orchestrator/providers/openai-oauth-ws.mjs +477 -147
  108. package/src/runtime/agent/orchestrator/providers/openai-oauth.mjs +343 -496
  109. package/src/runtime/agent/orchestrator/providers/openai-ws.mjs +6 -6
  110. package/src/runtime/agent/orchestrator/providers/registry.mjs +88 -51
  111. package/src/runtime/agent/orchestrator/providers/retry-classifier.mjs +31 -11
  112. package/src/runtime/agent/orchestrator/providers/statusline-route-meta.mjs +41 -8
  113. package/src/runtime/agent/orchestrator/session/cache/post-edit-marks.mjs +4 -4
  114. package/src/runtime/agent/orchestrator/session/cache/read-cache.mjs +1 -1
  115. package/src/runtime/agent/orchestrator/session/compact.mjs +1173 -267
  116. package/src/runtime/agent/orchestrator/session/context-utils.mjs +199 -36
  117. package/src/runtime/agent/orchestrator/session/loop.mjs +851 -674
  118. package/src/runtime/agent/orchestrator/session/manager.mjs +1593 -466
  119. package/src/runtime/agent/orchestrator/session/manager.reactive-persist.test.mjs +107 -0
  120. package/src/runtime/agent/orchestrator/session/store.mjs +291 -46
  121. package/src/runtime/agent/orchestrator/session/tool-result-offload.mjs +2 -2
  122. package/src/runtime/agent/orchestrator/stall-policy.mjs +31 -16
  123. package/src/runtime/agent/orchestrator/tool-loop-guard.mjs +3 -219
  124. package/src/runtime/agent/orchestrator/tools/bash-session.mjs +34 -7
  125. package/src/runtime/agent/orchestrator/tools/builtin/advisory-lock.mjs +1 -1
  126. package/src/runtime/agent/orchestrator/tools/builtin/arg-guard.mjs +19 -0
  127. package/src/runtime/agent/orchestrator/tools/builtin/bash-tool.mjs +9 -9
  128. package/src/runtime/agent/orchestrator/tools/builtin/builtin-tools.mjs +60 -37
  129. package/src/runtime/agent/orchestrator/tools/builtin/cache-layers.mjs +21 -2
  130. package/src/runtime/agent/orchestrator/tools/builtin/device-paths.mjs +1 -1
  131. package/src/runtime/agent/orchestrator/tools/builtin/diagnostics-tool.mjs +0 -7
  132. package/src/runtime/agent/orchestrator/tools/builtin/glob-walk.mjs +1 -3
  133. package/src/runtime/agent/orchestrator/tools/builtin/list-tool.mjs +36 -12
  134. package/src/runtime/agent/orchestrator/tools/builtin/read-args.mjs +2 -0
  135. package/src/runtime/agent/orchestrator/tools/builtin/read-constants.mjs +2 -2
  136. package/src/runtime/agent/orchestrator/tools/builtin/read-formatting.mjs +5 -12
  137. package/src/runtime/agent/orchestrator/tools/builtin/read-image-resize.mjs +1 -1
  138. package/src/runtime/agent/orchestrator/tools/builtin/read-single-tool.mjs +4 -36
  139. package/src/runtime/agent/orchestrator/tools/builtin/read-tool.mjs +2 -40
  140. package/src/runtime/agent/orchestrator/tools/builtin/rg-runner.mjs +148 -27
  141. package/src/runtime/agent/orchestrator/tools/builtin/search-builders.mjs +2 -2
  142. package/src/runtime/agent/orchestrator/tools/builtin/search-tool.mjs +43 -75
  143. package/src/runtime/agent/orchestrator/tools/builtin/shell-jobs.mjs +90 -20
  144. package/src/runtime/agent/orchestrator/tools/builtin/snapshot-store.mjs +1 -1
  145. package/src/runtime/agent/orchestrator/tools/builtin.mjs +59 -5
  146. package/src/runtime/agent/orchestrator/tools/code-graph-state.mjs +86 -0
  147. package/src/runtime/agent/orchestrator/tools/code-graph-tool-defs.mjs +11 -11
  148. package/src/runtime/agent/orchestrator/tools/code-graph.mjs +4106 -4019
  149. package/src/runtime/agent/orchestrator/tools/patch-tool-defs.mjs +33 -4
  150. package/src/runtime/agent/orchestrator/tools/patch.mjs +90 -6
  151. package/src/runtime/agent/orchestrator/tools/progress-message.mjs +6 -4
  152. package/src/runtime/agent/orchestrator/tools/result-compression.mjs +4 -4
  153. package/src/runtime/agent/orchestrator/tools/shell-command.mjs +8 -1
  154. package/src/runtime/agent/orchestrator/tools/shell-snapshot.mjs +4 -4
  155. package/src/runtime/channels/index.mjs +152 -24
  156. package/src/runtime/channels/lib/scheduler.mjs +18 -14
  157. package/src/runtime/channels/lib/session-discovery.mjs +3 -2
  158. package/src/runtime/channels/lib/tool-format.mjs +0 -2
  159. package/src/runtime/channels/lib/transcript-discovery.mjs +3 -2
  160. package/src/runtime/channels/lib/webhook.mjs +1 -1
  161. package/src/runtime/channels/tool-defs.mjs +29 -29
  162. package/src/runtime/memory/index.mjs +635 -107
  163. package/src/runtime/memory/lib/agent-ipc.mjs +29 -12
  164. package/src/runtime/memory/lib/core-memory-store.mjs +2 -2
  165. package/src/runtime/memory/lib/embedding-model-config.mjs +55 -0
  166. package/src/runtime/memory/lib/embedding-provider.mjs +31 -4
  167. package/src/runtime/memory/lib/embedding-worker.mjs +19 -10
  168. package/src/runtime/memory/lib/memory-cycle1.mjs +38 -17
  169. package/src/runtime/memory/lib/memory-cycle2.mjs +6 -7
  170. package/src/runtime/memory/lib/memory-cycle3.mjs +4 -4
  171. package/src/runtime/memory/lib/memory-ops-policy.mjs +2 -1
  172. package/src/runtime/memory/lib/memory-session-merge.mjs +38 -0
  173. package/src/runtime/memory/lib/memory.mjs +88 -9
  174. package/src/runtime/memory/lib/model-profile.mjs +1 -1
  175. package/src/runtime/memory/lib/pg/adapter.mjs +15 -1
  176. package/src/runtime/memory/lib/pg/supervisor.mjs +12 -0
  177. package/src/runtime/memory/lib/runtime-fetcher.mjs +37 -3
  178. package/src/runtime/memory/lib/session-ingest.mjs +194 -0
  179. package/src/runtime/memory/lib/trace-store.mjs +96 -51
  180. package/src/runtime/memory/tool-defs.mjs +46 -37
  181. package/src/runtime/search/index.mjs +102 -466
  182. package/src/runtime/search/lib/web-tools.mjs +45 -25
  183. package/src/runtime/search/tool-defs.mjs +16 -23
  184. package/src/runtime/shared/abort-controller.mjs +1 -1
  185. package/src/runtime/shared/atomic-file.mjs +4 -3
  186. package/src/runtime/shared/background-tasks.mjs +122 -11
  187. package/src/runtime/shared/child-spawn-gate.mjs +145 -0
  188. package/src/runtime/shared/config.mjs +7 -4
  189. package/src/runtime/shared/err-text.mjs +131 -4
  190. package/src/runtime/shared/llm/cost.mjs +2 -2
  191. package/src/runtime/shared/llm/http-agent.mjs +23 -7
  192. package/src/runtime/shared/llm/index.mjs +34 -11
  193. package/src/runtime/shared/llm/usage-log.mjs +4 -4
  194. package/src/runtime/shared/markdown-frontmatter.mjs +56 -0
  195. package/src/runtime/shared/singleton-owner.mjs +104 -0
  196. package/src/runtime/shared/tool-execution-contract.mjs +199 -20
  197. package/src/runtime/shared/tool-execution-contract.test.mjs +183 -0
  198. package/src/runtime/shared/tool-surface.mjs +624 -98
  199. package/src/runtime/shared/user-data-guard.mjs +0 -2
  200. package/src/standalone/agent-task-status.mjs +203 -0
  201. package/src/standalone/agent-task-status.test.mjs +76 -0
  202. package/src/standalone/agent-tool.mjs +1913 -0
  203. package/src/standalone/channel-worker.mjs +370 -14
  204. package/src/standalone/explore-tool.mjs +165 -70
  205. package/src/standalone/folder-dialog.mjs +314 -0
  206. package/src/standalone/hook-bus.mjs +898 -22
  207. package/src/standalone/memory-runtime-proxy.mjs +320 -0
  208. package/src/standalone/projects.mjs +226 -0
  209. package/src/standalone/provider-admin.mjs +41 -24
  210. package/src/standalone/seeds.mjs +2 -69
  211. package/src/standalone/usage-dashboard.mjs +96 -8
  212. package/src/tui/App.jsx +4798 -2153
  213. package/src/tui/components/AnsiText.jsx +39 -28
  214. package/src/tui/components/ContextPanel.jsx +87 -29
  215. package/src/tui/components/Markdown.jsx +43 -77
  216. package/src/tui/components/MarkdownTable.jsx +9 -184
  217. package/src/tui/components/Message.jsx +28 -11
  218. package/src/tui/components/Picker.jsx +95 -56
  219. package/src/tui/components/PromptInput.jsx +367 -239
  220. package/src/tui/components/QueuedCommands.jsx +1 -1
  221. package/src/tui/components/SlashCommandPalette.jsx +27 -21
  222. package/src/tui/components/Spinner.jsx +67 -38
  223. package/src/tui/components/StatusLine.jsx +606 -38
  224. package/src/tui/components/TextEntryPanel.jsx +128 -9
  225. package/src/tui/components/ToolExecution.jsx +617 -368
  226. package/src/tui/components/TurnDone.jsx +3 -3
  227. package/src/tui/components/UsagePanel.jsx +3 -5
  228. package/src/tui/components/tool-output-format.mjs +365 -0
  229. package/src/tui/components/tool-output-format.test.mjs +220 -0
  230. package/src/tui/dist/index.mjs +8915 -2418
  231. package/src/tui/engine-runtime-notification.test.mjs +115 -0
  232. package/src/tui/engine-tool-result-text.test.mjs +75 -0
  233. package/src/tui/engine.mjs +1455 -279
  234. package/src/tui/figures.mjs +21 -40
  235. package/src/tui/index.jsx +75 -31
  236. package/src/tui/input-editing.mjs +25 -0
  237. package/src/tui/markdown/format-token.mjs +511 -68
  238. package/src/tui/markdown/format-token.test.mjs +216 -0
  239. package/src/tui/markdown/render-ansi.mjs +94 -0
  240. package/src/tui/markdown/render-ansi.test.mjs +108 -0
  241. package/src/tui/markdown/stream-fence.mjs +34 -0
  242. package/src/tui/markdown/stream-fence.test.mjs +26 -0
  243. package/src/tui/markdown/table-layout.mjs +250 -0
  244. package/src/tui/paste-attachments.mjs +0 -7
  245. package/src/tui/spinner-verbs.mjs +1 -2
  246. package/src/tui/statusline-ansi-bridge.mjs +172 -0
  247. package/src/tui/statusline-ansi-bridge.test.mjs +159 -0
  248. package/src/tui/theme.mjs +746 -24
  249. package/src/tui/time-format.mjs +1 -1
  250. package/src/tui/transcript-tool-failures.mjs +67 -0
  251. package/src/tui/transcript-tool-failures.test.mjs +111 -0
  252. package/src/ui/ansi.mjs +1 -2
  253. package/src/ui/markdown.mjs +85 -26
  254. package/src/ui/markdown.test.mjs +70 -0
  255. package/src/ui/model-display.mjs +121 -0
  256. package/src/ui/session-stats.mjs +44 -0
  257. package/src/ui/statusline-context-label.test.mjs +15 -0
  258. package/src/ui/statusline.mjs +386 -178
  259. package/src/ui/tool-card.mjs +3 -16
  260. package/src/vendor/statusline/bin/statusline-lib.mjs +8 -4
  261. package/src/vendor/statusline/bin/statusline-route.mjs +169 -37
  262. package/src/vendor/statusline/bin/statusline-route.test.mjs +80 -0
  263. package/src/vendor/statusline/scripts/lib/gateway-settings.mjs +3 -3
  264. package/src/vendor/statusline/src/gateway/claude-current.mjs +1 -1
  265. package/src/vendor/statusline/src/gateway/route-meta.mjs +44 -6
  266. package/src/vendor/statusline/src/gateway/session-routes.mjs +1 -1
  267. package/src/workflows/default/WORKFLOW.md +12 -5
  268. package/src/workflows/default/workflow.json +0 -1
  269. package/src/workflows/solo/WORKFLOW.md +15 -0
  270. package/src/workflows/solo/workflow.json +7 -0
  271. package/vendor/ink/build/output.js +6 -1
  272. package/src/agents/scheduler-task.md +0 -3
  273. package/src/agents/web-researcher/AGENT.md +0 -3
  274. package/src/agents/web-researcher/agent.json +0 -6
  275. package/src/agents/webhook-handler.md +0 -3
  276. package/src/rules/bridge/00-common.md +0 -5
  277. package/src/rules/bridge/30-explorer.md +0 -4
  278. package/src/rules/lead/00-tool-lead.md +0 -5
  279. package/src/rules/shared/00-language.md +0 -3
  280. package/src/runtime/agent/orchestrator/tools/builtin/native-edit-runner.mjs +0 -110
  281. package/src/runtime/agent/orchestrator/tools/mutation-content-cache.mjs +0 -67
  282. package/src/runtime/memory/lib/bridge-trace-queries.mjs +0 -120
  283. package/src/runtime/shared/llm/pid-cleanup.mjs +0 -27
  284. package/src/standalone/bridge-tool.mjs +0 -1414
  285. package/src/tui/runtime/shared/process-shutdown.mjs +0 -1
@@ -8,7 +8,7 @@ function __mixdogMemoryLog(...args) {
8
8
  // Uses pg-adapter (schema='trace') so trace_events live in the trace schema.
9
9
  // Isolated from memory schema; shares the same PG instance.
10
10
 
11
- import { ensurePgInstance, checkedConnect } from './pg/adapter.mjs'
11
+ import { ensurePgInstance, checkedConnect, closePgInstance } from './pg/adapter.mjs'
12
12
  import { resolve } from 'path'
13
13
 
14
14
  const dbs = new Map()
@@ -123,15 +123,15 @@ async function init(client) {
123
123
  }
124
124
 
125
125
  // ---------------------------------------------------------------------------
126
- // Bridge-specific analytic tables (added post-init via initBridgeTables)
126
+ // Agent-specific analytic tables (added post-init via initAgentTables)
127
127
  // ---------------------------------------------------------------------------
128
128
 
129
129
  // Called once per openTraceDatabase boot (after the advisory lock is released).
130
130
  // Safe to call concurrently — all DDL is IF NOT EXISTS.
131
- export async function initBridgeTables(client) {
132
- // ── bridge_calls: one row per tool invocation ─────────────────────────────
131
+ export async function initAgentTables(client) {
132
+ // ── agent_calls: one row per tool invocation ─────────────────────────────
133
133
  await client.query(`
134
- CREATE TABLE IF NOT EXISTS bridge_calls (
134
+ CREATE TABLE IF NOT EXISTS agent_calls (
135
135
  id BIGSERIAL PRIMARY KEY,
136
136
  session_id TEXT NOT NULL,
137
137
  iteration INT,
@@ -142,18 +142,18 @@ export async function initBridgeTables(client) {
142
142
  tool_args JSONB
143
143
  )
144
144
  `)
145
- await client.query(`CREATE INDEX IF NOT EXISTS idx_bc_session ON bridge_calls (session_id, iteration)`)
146
- await client.query(`CREATE INDEX IF NOT EXISTS idx_bc_ts ON bridge_calls USING BRIN (ts)`)
147
- await client.query(`CREATE INDEX IF NOT EXISTS idx_bc_tool_name ON bridge_calls (tool_name)`)
145
+ await client.query(`CREATE INDEX IF NOT EXISTS idx_ac_session ON agent_calls (session_id, iteration)`)
146
+ await client.query(`CREATE INDEX IF NOT EXISTS idx_ac_ts ON agent_calls USING BRIN (ts)`)
147
+ await client.query(`CREATE INDEX IF NOT EXISTS idx_ac_tool_name ON agent_calls (tool_name)`)
148
148
  // Expression indexes covering the two actual query patterns (md5 dedup + path lookup).
149
149
  // The old GIN index had no @> callers and was write-heavy; dropped in favour of these.
150
- await client.query(`CREATE INDEX IF NOT EXISTS idx_bc_args_md5 ON bridge_calls (session_id, tool_name, md5(tool_args::text))`)
151
- await client.query(`CREATE INDEX IF NOT EXISTS idx_bc_args_path ON bridge_calls (session_id, tool_name, (tool_args->>'path'), ts, id)`)
152
- await client.query(`DROP INDEX IF EXISTS idx_bc_args`)
150
+ await client.query(`CREATE INDEX IF NOT EXISTS idx_ac_args_md5 ON agent_calls (session_id, tool_name, md5(tool_args::text))`)
151
+ await client.query(`CREATE INDEX IF NOT EXISTS idx_ac_args_path ON agent_calls (session_id, tool_name, (tool_args->>'path'), ts, id)`)
152
+ await client.query(`DROP INDEX IF EXISTS idx_ac_args`)
153
153
 
154
- // ── bridge_llm: one row per LLM usage event ───────────────────────────────
154
+ // ── agent_llm: one row per LLM usage event ───────────────────────────────
155
155
  await client.query(`
156
- CREATE TABLE IF NOT EXISTS bridge_llm (
156
+ CREATE TABLE IF NOT EXISTS agent_llm (
157
157
  id BIGSERIAL PRIMARY KEY,
158
158
  session_id TEXT NOT NULL,
159
159
  iteration INT,
@@ -167,13 +167,13 @@ export async function initBridgeTables(client) {
167
167
  response_id TEXT
168
168
  )
169
169
  `)
170
- await client.query(`CREATE INDEX IF NOT EXISTS idx_bl_session ON bridge_llm (session_id, iteration)`)
171
- await client.query(`CREATE INDEX IF NOT EXISTS idx_bl_ts ON bridge_llm USING BRIN (ts)`)
172
- await client.query(`CREATE INDEX IF NOT EXISTS idx_bl_model ON bridge_llm (model)`)
170
+ await client.query(`CREATE INDEX IF NOT EXISTS idx_al_session ON agent_llm (session_id, iteration)`)
171
+ await client.query(`CREATE INDEX IF NOT EXISTS idx_al_ts ON agent_llm USING BRIN (ts)`)
172
+ await client.query(`CREATE INDEX IF NOT EXISTS idx_al_model ON agent_llm (model)`)
173
173
 
174
- // ── bridge_sessions: denormalised summary upserted on each insert ─────────
174
+ // ── agent_sessions: denormalised summary upserted on each insert ─────────
175
175
  await client.query(`
176
- CREATE TABLE IF NOT EXISTS bridge_sessions (
176
+ CREATE TABLE IF NOT EXISTS agent_sessions (
177
177
  session_id TEXT PRIMARY KEY,
178
178
  role TEXT,
179
179
  model TEXT,
@@ -189,7 +189,7 @@ export async function initBridgeTables(client) {
189
189
  }
190
190
 
191
191
  // ---------------------------------------------------------------------------
192
- // insertBridgeCalls — batch insert tool rows + upsert session summary
192
+ // insertAgentCalls — batch insert tool rows + upsert session summary
193
193
  // ---------------------------------------------------------------------------
194
194
  const TOOL_ARGS_MAX_BYTES = 65536 // 64 KB cap; oversized → sha256 + truncated preview
195
195
 
@@ -207,7 +207,7 @@ function _capToolArgsSync(args) {
207
207
  return { _oversized: true, sha256: _createHash('sha256').update(raw).digest('hex'), preview: raw.slice(0, 512) }
208
208
  }
209
209
 
210
- export async function insertBridgeCalls(db, events) {
210
+ export async function insertAgentCalls(db, events) {
211
211
  if (!Array.isArray(events) || events.length === 0) return { calls: 0, llm: 0 }
212
212
  const toolRows = []
213
213
  const llmRows = []
@@ -237,7 +237,7 @@ export async function insertBridgeCalls(db, events) {
237
237
 
238
238
  // Wrap all three inserts in a single transaction — one flush/fsync.
239
239
  // checkedConnect ensures search_path = trace, public on fresh connections;
240
- // raw _pool.connect() leaves search_path at PG default and bridge_* lookups
240
+ // raw _pool.connect() leaves search_path at PG default and agent_* lookups
241
241
  // resolve in the wrong schema.
242
242
  const client = await checkedConnect(db._pool, 'trace')
243
243
  try {
@@ -245,7 +245,7 @@ export async function insertBridgeCalls(db, events) {
245
245
 
246
246
  if (toolRows.length > 0) {
247
247
  await client.query(
248
- `INSERT INTO bridge_calls (session_id,iteration,ts,tool_name,tool_kind,tool_ms,tool_args)
248
+ `INSERT INTO agent_calls (session_id,iteration,ts,tool_name,tool_kind,tool_ms,tool_args)
249
249
  SELECT u.session_id, u.iteration::int, u.ts::timestamptz,
250
250
  u.tool_name, u.tool_kind, u.tool_ms::int, u.tool_args::jsonb
251
251
  FROM unnest($1::text[],$2::int[],$3::text[],$4::text[],$5::text[],$6::int[],$7::text[])
@@ -264,7 +264,7 @@ export async function insertBridgeCalls(db, events) {
264
264
 
265
265
  if (llmRows.length > 0) {
266
266
  await client.query(
267
- `INSERT INTO bridge_llm (session_id,iteration,ts,model,input_tokens,output_tokens,cached_tokens,cache_write_tokens,prompt_tokens,response_id)
267
+ `INSERT INTO agent_llm (session_id,iteration,ts,model,input_tokens,output_tokens,cached_tokens,cache_write_tokens,prompt_tokens,response_id)
268
268
  SELECT u.session_id, u.iteration::int, u.ts::timestamptz,
269
269
  u.model, u.input_tokens::int, u.output_tokens::int,
270
270
  u.cached_tokens::int, u.cache_write_tokens::int,
@@ -330,7 +330,7 @@ export async function insertBridgeCalls(db, events) {
330
330
  })
331
331
  }
332
332
 
333
- // Coalesce bridge_sessions upserts: batch all sessions in one unnest INSERT.
333
+ // Coalesce agent_sessions upserts: batch all sessions in one unnest INSERT.
334
334
  // Also within the same transaction.
335
335
  if (sessionMap.size > 0) {
336
336
  const sids = [], roles = [], models = [], ts0s = [], ts1s = [],
@@ -343,7 +343,7 @@ export async function insertBridgeCalls(db, events) {
343
343
  tinputs.push(String(s.total_input)); toutputs.push(String(s.total_output))
344
344
  }
345
345
  await client.query(`
346
- INSERT INTO bridge_sessions (session_id, role, model, started_at, last_seen_at, tool_calls, llm_calls, max_iteration, total_input_tokens, total_output_tokens)
346
+ INSERT INTO agent_sessions (session_id, role, model, started_at, last_seen_at, tool_calls, llm_calls, max_iteration, total_input_tokens, total_output_tokens)
347
347
  SELECT u.session_id, u.role, u.model,
348
348
  u.started_at::timestamptz, u.last_seen_at::timestamptz,
349
349
  u.tool_calls::int, u.llm_calls::int, u.max_iteration::int,
@@ -351,15 +351,15 @@ export async function insertBridgeCalls(db, events) {
351
351
  FROM unnest($1::text[],$2::text[],$3::text[],$4::text[],$5::text[],$6::int[],$7::int[],$8::int[],$9::text[],$10::text[])
352
352
  AS u(session_id,role,model,started_at,last_seen_at,tool_calls,llm_calls,max_iteration,total_input_tokens,total_output_tokens)
353
353
  ON CONFLICT (session_id) DO UPDATE SET
354
- role = COALESCE(EXCLUDED.role, bridge_sessions.role),
355
- model = COALESCE(EXCLUDED.model, bridge_sessions.model),
356
- started_at = LEAST(bridge_sessions.started_at, EXCLUDED.started_at),
357
- last_seen_at = GREATEST(bridge_sessions.last_seen_at, EXCLUDED.last_seen_at),
358
- tool_calls = bridge_sessions.tool_calls + EXCLUDED.tool_calls,
359
- llm_calls = bridge_sessions.llm_calls + EXCLUDED.llm_calls,
360
- max_iteration = GREATEST(bridge_sessions.max_iteration, EXCLUDED.max_iteration),
361
- total_input_tokens = bridge_sessions.total_input_tokens + EXCLUDED.total_input_tokens,
362
- total_output_tokens = bridge_sessions.total_output_tokens + EXCLUDED.total_output_tokens
354
+ role = COALESCE(EXCLUDED.role, agent_sessions.role),
355
+ model = COALESCE(EXCLUDED.model, agent_sessions.model),
356
+ started_at = LEAST(agent_sessions.started_at, EXCLUDED.started_at),
357
+ last_seen_at = GREATEST(agent_sessions.last_seen_at, EXCLUDED.last_seen_at),
358
+ tool_calls = agent_sessions.tool_calls + EXCLUDED.tool_calls,
359
+ llm_calls = agent_sessions.llm_calls + EXCLUDED.llm_calls,
360
+ max_iteration = GREATEST(agent_sessions.max_iteration, EXCLUDED.max_iteration),
361
+ total_input_tokens = agent_sessions.total_input_tokens + EXCLUDED.total_input_tokens,
362
+ total_output_tokens = agent_sessions.total_output_tokens + EXCLUDED.total_output_tokens
363
363
  `, [sids, roles, models, ts0s, ts1s, tcalls, lcalls, maxiters, tinputs, toutputs])
364
364
  }
365
365
 
@@ -519,8 +519,8 @@ export async function openTraceDatabase(dataDir) {
519
519
  } finally {
520
520
  client.release()
521
521
  }
522
- // Bridge-specific analytic tables — idempotent, no advisory lock needed.
523
- await initBridgeTables(db)
522
+ // Agent-specific analytic tables — idempotent, no advisory lock needed.
523
+ await initAgentTables(db)
524
524
 
525
525
  dbs.set(key, db)
526
526
 
@@ -633,32 +633,77 @@ function _scheduleFlush(db, q) {
633
633
  // Issue 4: timer is unref()'d so it won't prevent exit; register drain handlers.
634
634
  // ---------------------------------------------------------------------------
635
635
 
636
- const _registeredExitDbs = new WeakSet()
636
+ const _registeredExitDbs = new WeakMap()
637
+
638
+ async function drainTraceQueue(db) {
639
+ const q = _traceQueues.get(db)
640
+ if (!q) return
641
+ try {
642
+ if (q.timer) { clearTimeout(q.timer); q.timer = null }
643
+ if (q.flushPromise) await q.flushPromise.catch(() => {})
644
+ if (q.pending.length > 0) await _insertTraceEventsDirect(db, q.pending.splice(0).flatMap(w => w.events))
645
+ } catch (e) {
646
+ __mixdogMemoryLog(`[trace-queue] drain failed: ${e?.message}\n`)
647
+ }
648
+ }
649
+
650
+ function clearTraceQueue(db) {
651
+ const q = _traceQueues.get(db)
652
+ if (!q) return
653
+ if (q.timer) { clearTimeout(q.timer); q.timer = null }
654
+ q.pending.length = 0
655
+ q.flushPromise = null
656
+ _traceQueues.delete(db)
657
+ }
658
+
659
+ function unregisterTraceExitDrain(db) {
660
+ const handlers = _registeredExitDbs.get(db)
661
+ if (!handlers) return
662
+ try { process.off('exit', handlers.onExit) } catch {}
663
+ try { process.off('SIGTERM', handlers.onSigterm) } catch {}
664
+ try { process.off('beforeExit', handlers.onBeforeExit) } catch {}
665
+ _registeredExitDbs.delete(db)
666
+ }
637
667
 
638
668
  export function registerTraceExitDrain(db) {
639
669
  if (_registeredExitDbs.has(db)) return
640
- _registeredExitDbs.add(db)
641
670
 
642
671
  async function drainOnExit() {
643
- const q = _traceQueues.get(db)
644
- if (!q || q.pending.length === 0) return
645
- try {
646
- if (q.timer) { clearTimeout(q.timer); q.timer = null }
647
- if (q.flushPromise) await q.flushPromise.catch(() => {})
648
- if (q.pending.length > 0) await _insertTraceEventsDirect(db, q.pending.splice(0).flatMap(w => w.events))
649
- } catch (e) {
650
- __mixdogMemoryLog(`[trace-queue] exit drain failed: ${e?.message}\n`)
651
- }
672
+ await drainTraceQueue(db)
652
673
  }
653
674
 
654
- process.on('exit', () => {
675
+ const onExit = () => {
655
676
  const q = _traceQueues.get(db)
656
677
  if (q?.pending.length) __mixdogMemoryLog(`[trace-queue] exit with ${q.pending.length} unflushed events\n`)
657
- })
658
-
659
- process.on('SIGTERM', async () => { await drainOnExit(); process.exit(0) })
678
+ }
679
+ const onSigterm = async () => { await drainOnExit(); process.exit(0) }
660
680
 
681
+ process.on('exit', onExit)
682
+ process.on('SIGTERM', onSigterm)
661
683
  process.once('beforeExit', drainOnExit)
684
+
685
+ _registeredExitDbs.set(db, { onExit, onSigterm, onBeforeExit: drainOnExit })
686
+ }
687
+
688
+ export async function closeTraceDatabase(dataDir) {
689
+ const key = resolve(dataDir)
690
+ let db = dbs.get(key)
691
+ if (!db && opening.has(key)) {
692
+ try { db = await opening.get(key) } catch { db = null }
693
+ }
694
+ if (!db) return false
695
+ const timer = partitionTimers.get(key)
696
+ if (timer) {
697
+ try { clearInterval(timer) } catch {}
698
+ partitionTimers.delete(key)
699
+ }
700
+ await drainTraceQueue(db)
701
+ clearTraceQueue(db)
702
+ unregisterTraceExitDrain(db)
703
+ dbs.delete(key)
704
+ try { await db.close?.() } catch {}
705
+ try { await closePgInstance(dataDir, { schema: 'trace' }) } catch {}
706
+ return true
662
707
  }
663
708
 
664
709
  /**
@@ -8,24 +8,28 @@ export const TOOL_DEFS = [
8
8
  name: 'memory',
9
9
  title: 'Memory Cycle',
10
10
  annotations: { title: 'Memory Cycle', readOnlyHint: false, destructiveHint: true, idempotentHint: false, openWorldHint: false },
11
- description: 'Persistent memory maintenance and explicit mutation. Use recall for retrieval; use memory only for status, lifecycle jobs, or intentional add/edit/delete/purge. Destructive jobs require exact confirm strings.',
11
+ description: 'Memory status/lifecycle or explicit mutation. Use recall for retrieval. Destructive jobs require exact confirm strings.',
12
12
  inputSchema: {
13
13
  type: 'object',
14
14
  properties: {
15
- action: { type: 'string', enum: ['core','manage','status','sleep','cycle1','cycle2','cycle3','flush','backfill','prune','rebuild','purge','retro_eval_active'], description: 'Operation to run: status/read jobs, core/manage mutation, lifecycle cycles, or destructive maintenance.' },
16
- op: { type: 'string', enum: ['add','edit','delete','list'], description: 'Mutation operation for action=core/manage.' },
17
- id: { type: 'number', description: 'Exact memory id for edit/delete; get it from recall or list output first.' },
18
- element: { type: 'string', description: 'Stable memory key/title for add/edit.' },
19
- summary: { type: 'string', description: 'Memory content to store or replace.' },
20
- category: { type: 'string', enum: ['rule','constraint','decision','fact','goal','preference','task','issue'], description: 'Memory category.' },
21
- status: { type: 'string', enum: ['pending','active','archived'], description: 'Entry lifecycle status for manage edit.' },
22
- maxDays: { type: 'number', description: 'Prune threshold in days for old unclassified entries.' },
23
- window: { type: 'string', description: 'Backfill/search window, e.g. 24h, 7d, 30d.' },
24
- limit: { type: 'number', description: 'Maximum rows/items for list, search, or backfill.' },
25
- concurrency: { type: 'number', description: 'Worker concurrency for supported maintenance jobs.' },
26
- confirm: { type: 'string', description: 'Exact confirmation phrase for destructive actions such as prune, rebuild, purge, or retro_eval_active.' },
27
- cycle3Mode: { type: 'string', enum: ['proposal','conservative'], description: 'cycle3 application mode; conservative applies only safe automatic changes.' },
28
- project_id: { type: 'string', description: 'Core memory project pool: common, owner/repo slug, or * for core list across pools.' },
15
+ action: { type: 'string', enum: ['core','manage','status','sleep','cycle1','cycle2','cycle3','flush','backfill','prune','rebuild','purge','retro_eval_active'], description: 'Operation.' },
16
+ op: { type: 'string', enum: ['add','edit','delete','list'], description: 'Mutation op.' },
17
+ id: { type: 'number', description: 'Exact memory id.' },
18
+ element: { type: 'string', description: 'Memory key/title.' },
19
+ summary: { type: 'string', description: 'Memory content.' },
20
+ category: { type: 'string', enum: ['rule','constraint','decision','fact','goal','preference','task','issue'], description: 'Category.' },
21
+ status: { type: 'string', enum: ['pending','active','archived'], description: 'Lifecycle status.' },
22
+ maxDays: { type: 'number', description: 'Prune age days.' },
23
+ window: { type: 'string', description: 'Window, e.g. 24h/7d/30d.' },
24
+ limit: { type: 'number', description: 'Max rows/items.' },
25
+ concurrency: { type: 'number', description: 'Worker concurrency.' },
26
+ sessionId: { type: 'string', description: 'Scoped session id.' },
27
+ session_id: { type: 'string', description: 'Alias for sessionId.' },
28
+ window_size: { type: 'number', description: 'cycle1 rows/window.' },
29
+ rows_per_session: { type: 'number', description: 'cycle1 rows/session.' },
30
+ confirm: { type: 'string', description: 'Exact confirmation phrase for destructive actions.' },
31
+ cycle3Mode: { type: 'string', enum: ['proposal','conservative'], description: 'cycle3 mode.' },
32
+ project_id: { type: 'string', description: 'Core pool: common, slug, or *.' },
29
33
  },
30
34
  required: ['action'],
31
35
  },
@@ -34,20 +38,23 @@ export const TOOL_DEFS = [
34
38
  name: 'recall',
35
39
  title: 'Recall',
36
40
  annotations: { title: 'Recall', readOnlyHint: true, destructiveHint: false, idempotentHint: true, openWorldHint: false },
37
- description: 'Retrieve stored memory (past decisions, prior work, session history). Use when the user references earlier work, when resuming a paused task, or before re-proposing something possibly already decided. query (string|array fan-out) and/or id; category/period/projectScope filters. Read-only and cheap — when in doubt, recall first.',
41
+ description: 'Retrieve stored memory/session history. Use for earlier work, resumes, or possibly decided items; when in doubt, recall first.',
38
42
  inputSchema: {
39
43
  type: 'object',
40
44
  properties: {
41
45
  query: { anyOf: [{ type: 'string' }, { type: 'array', items: { type: 'string' }, minItems: 1 }], description: 'Search text, or array for independent fan-out queries.' },
42
- id: { anyOf: [{ type: 'number' }, { type: 'array', items: { type: 'number' }, minItems: 1 }], description: 'Only use exact #ids from a prior recall result. Do not invent ids, and do not combine id with query.' },
43
- period: { type: 'string', description: 'Time scope: last, 24h/7d/30d, all, YYYY-MM-DD, or YYYY-MM-DD~YYYY-MM-DD.' },
44
- limit: { type: 'number', description: 'Maximum entries to return.' },
45
- offset: { type: 'number', description: 'Skip N entries for paging.' },
46
- sort: { type: 'string', enum: ['importance', 'date'], description: 'importance (default relevance) or date (newest first).' },
47
- category: { anyOf: [{ type: 'string', enum: ['rule','constraint','decision','fact','goal','preference','task','issue'] }, { type: 'array', items: { type: 'string', enum: ['rule','constraint','decision','fact','goal','preference','task','issue'] }, minItems: 1 }], description: 'Optional category filter, string or array.' },
48
- includeArchived: { type: 'boolean', description: 'Include archived/historical entries; set false to focus on live/pending memory.' },
49
- projectScope: { type: 'string', description: 'Project pool selector: inferred from cwd, common, all, or a project slug.' },
50
- cwd: { type: 'string', description: 'Workspace path used to infer projectScope when projectScope is omitted.' },
46
+ id: { anyOf: [{ type: 'number' }, { type: 'array', items: { type: 'number' }, minItems: 1 }], description: 'Exact #id(s) from recall. Do not invent ids.' },
47
+ period: { type: 'string', description: 'last, 24h/7d/30d, all, date, or range.' },
48
+ limit: { type: 'number', description: 'Max entries.' },
49
+ offset: { type: 'number', description: 'Skip entries.' },
50
+ sort: { type: 'string', enum: ['importance', 'date'], description: 'importance or date.' },
51
+ category: { anyOf: [{ type: 'string', enum: ['rule','constraint','decision','fact','goal','preference','task','issue'] }, { type: 'array', items: { type: 'string', enum: ['rule','constraint','decision','fact','goal','preference','task','issue'] }, minItems: 1 }], description: 'Category filter.' },
52
+ includeArchived: { type: 'boolean', description: 'Include archived entries.' },
53
+ sessionId: { type: 'string', description: 'Scoped session id.' },
54
+ session_id: { type: 'string', description: 'Alias for sessionId.' },
55
+ includeMembers: { type: 'boolean', description: 'Include chunk members.' },
56
+ projectScope: { type: 'string', description: 'Project pool selector: inferred from cwd, common, all, or slug.' },
57
+ cwd: { type: 'string', description: 'Infer projectScope.' },
51
58
  },
52
59
  additionalProperties: false,
53
60
  },
@@ -57,21 +64,23 @@ export const TOOL_DEFS = [
57
64
  title: 'Search Memories',
58
65
  public: false,
59
66
  annotations: { title: 'Search Memories', readOnlyHint: true, destructiveHint: false, idempotentHint: true, openWorldHint: false },
60
- description: 'Search past context and memory. Returns root entries by default. Use when user references prior work, decisions, or preferences. Storage is automatic — only retrieval is manual.',
67
+ description: 'Search past context/memory. Returns root entries.',
61
68
  inputSchema: {
62
69
  type: 'object',
63
70
  properties: {
64
- query: { type: 'string', description: 'Search text. Triggers hybrid search (vector KNN + full-text BM25).' },
65
- period: { type: 'string', description: 'Time scope: "last" (before this session), "24h"/"3d"/"7d"/"30d" (relative), "all", "2026-04-05" (single day), "2026-04-01~2026-04-05" (range). Default: 30d when query set, latest entries otherwise.' },
66
- sort: { type: 'string', enum: ['date', 'importance'], description: 'date (newest first) or importance (score desc).' },
67
- category: { anyOf: [{ type: 'string', enum: ['rule','constraint','decision','fact','goal','preference','task','issue'] }, { type: 'array', items: { type: 'string', enum: ['rule','constraint','decision','fact','goal','preference','task','issue'] }, minItems: 1 }], description: 'Optional category filter for timeline/category recall.' },
68
- limit: { type: 'number', default: 30, description: 'Maximum entries to return.' },
69
- offset: { type: 'number', default: 0, description: 'Skip N entries for paging.' },
70
- includeMembers: { type: 'boolean', description: 'Include chunk member entries inline.' },
71
- includeRaw: { type: 'boolean', description: 'When true, fetch unclassified raw rows in the requested period window and merge into results. Caller-driven only — no auto-trigger.' },
72
- includeArchived: { type: 'boolean', description: 'Default true for historical recall. Set false to restrict to live/pending roots.' },
73
- projectScope: { type: 'string', description: 'Project pool selector. Omitted = infer from cwd, or `common` if unknown. Use `all` only when intentionally searching every project.' },
74
- cwd: { type: 'string', description: 'Optional workspace path, used only when `projectScope` is not set.' },
71
+ query: { type: 'string', description: 'Search text.' },
72
+ period: { type: 'string', description: 'last, 24h/7d/30d, all, date, or range.' },
73
+ sort: { type: 'string', enum: ['date', 'importance'], description: 'date or importance.' },
74
+ category: { anyOf: [{ type: 'string', enum: ['rule','constraint','decision','fact','goal','preference','task','issue'] }, { type: 'array', items: { type: 'string', enum: ['rule','constraint','decision','fact','goal','preference','task','issue'] }, minItems: 1 }], description: 'Category filter.' },
75
+ limit: { type: 'number', default: 30, description: 'Max entries.' },
76
+ offset: { type: 'number', default: 0, description: 'Skip entries.' },
77
+ includeMembers: { type: 'boolean', description: 'Include members.' },
78
+ includeRaw: { type: 'boolean', description: 'Include raw rows.' },
79
+ includeArchived: { type: 'boolean', description: 'Include archived.' },
80
+ sessionId: { type: 'string', description: 'Scoped session id.' },
81
+ session_id: { type: 'string', description: 'Alias for sessionId.' },
82
+ projectScope: { type: 'string', description: 'Project pool selector.' },
83
+ cwd: { type: 'string', description: 'Infer projectScope.' },
75
84
  },
76
85
  required: [],
77
86
  },