mixdog 0.9.93 → 0.9.95

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 (186) hide show
  1. package/NOTICE.md +72 -0
  2. package/package.json +16 -6
  3. package/scripts/lib/isolated-root-cleanup.mjs +19 -0
  4. package/scripts/run-suite.mjs +100 -0
  5. package/src/lib/rules-builder.cjs +4 -3
  6. package/src/output-styles/detailed.md +13 -15
  7. package/src/output-styles/extreme-minimal.md +7 -11
  8. package/src/output-styles/minimal.md +4 -7
  9. package/src/output-styles/simple.md +11 -13
  10. package/src/rules/agent/30-explorer.md +22 -16
  11. package/src/rules/lead/01-general.md +1 -2
  12. package/src/rules/lead/lead-brief.md +4 -5
  13. package/src/rules/lead/lead-tool.md +3 -2
  14. package/src/rules/shared/01-tool.md +28 -29
  15. package/src/runtime/agent/orchestrator/agent-runtime/cache-strategy.mjs +2 -2
  16. package/src/runtime/agent/orchestrator/agent-trace-format.mjs +23 -7
  17. package/src/runtime/agent/orchestrator/agent-trace-io.mjs +4 -0
  18. package/src/runtime/agent/orchestrator/agent-trace.mjs +29 -0
  19. package/src/runtime/agent/orchestrator/context/collect.mjs +6 -2
  20. package/src/runtime/agent/orchestrator/mcp/client.mjs +3 -3
  21. package/src/runtime/agent/orchestrator/providers/anthropic-effort.mjs +97 -7
  22. package/src/runtime/agent/orchestrator/providers/anthropic-model-resolve.mjs +11 -4
  23. package/src/runtime/agent/orchestrator/providers/anthropic-oauth.mjs +2 -3
  24. package/src/runtime/agent/orchestrator/providers/anthropic.mjs +1 -1
  25. package/src/runtime/agent/orchestrator/providers/codex-client-meta.mjs +4 -6
  26. package/src/runtime/agent/orchestrator/providers/lib/stream-outcome.mjs +1 -1
  27. package/src/runtime/agent/orchestrator/providers/oauth-usage.mjs +75 -15
  28. package/src/runtime/agent/orchestrator/providers/openai-codex-metadata.mjs +8 -8
  29. package/src/runtime/agent/orchestrator/providers/openai-oauth-http-sse.mjs +7 -8
  30. package/src/runtime/agent/orchestrator/providers/openai-oauth-ws.mjs +2 -2
  31. package/src/runtime/agent/orchestrator/providers/openai-responses-payload.mjs +14 -16
  32. package/src/runtime/agent/orchestrator/providers/openai-ws-headers.mjs +5 -4
  33. package/src/runtime/agent/orchestrator/providers/openai-ws-pool.mjs +10 -10
  34. package/src/runtime/agent/orchestrator/providers/openai-ws-stream.mjs +2 -3
  35. package/src/runtime/agent/orchestrator/providers/registry.mjs +51 -4
  36. package/src/runtime/agent/orchestrator/providers/retry-classifier.mjs +15 -15
  37. package/src/runtime/agent/orchestrator/session/agent-loop.mjs +12 -32
  38. package/src/runtime/agent/orchestrator/session/cache/read-cache.mjs +7 -0
  39. package/src/runtime/agent/orchestrator/session/cache/scoped-cache.mjs +42 -2
  40. package/src/runtime/agent/orchestrator/session/context-compaction-policy.mjs +10 -3
  41. package/src/runtime/agent/orchestrator/session/context-utils.mjs +10 -11
  42. package/src/runtime/agent/orchestrator/session/eager-dispatch.mjs +1 -0
  43. package/src/runtime/agent/orchestrator/session/loop/compact-policy.mjs +3 -3
  44. package/src/runtime/agent/orchestrator/session/loop/completion-guards.mjs +0 -14
  45. package/src/runtime/agent/orchestrator/session/loop/stop-hooks.mjs +9 -8
  46. package/src/runtime/agent/orchestrator/session/manager/ask-session.mjs +7 -4
  47. package/src/runtime/agent/orchestrator/session/manager/context-meta.mjs +1 -1
  48. package/src/runtime/agent/orchestrator/session/manager/idle-cleanup.mjs +1 -1
  49. package/src/runtime/agent/orchestrator/session/manager/pending-messages.mjs +40 -13
  50. package/src/runtime/agent/orchestrator/session/manager/session-close.mjs +3 -0
  51. package/src/runtime/agent/orchestrator/session/manager/session-lifecycle.mjs +8 -1
  52. package/src/runtime/agent/orchestrator/session/manager/turn-interruption.mjs +5 -9
  53. package/src/runtime/agent/orchestrator/session/send-with-recovery.mjs +3 -3
  54. package/src/runtime/agent/orchestrator/session/tool-batch.mjs +103 -92
  55. package/src/runtime/agent/orchestrator/session/tool-result-offload.mjs +98 -3
  56. package/src/runtime/agent/orchestrator/stall-policy.mjs +2 -3
  57. package/src/runtime/agent/orchestrator/tools/bash-session.mjs +3 -3
  58. package/src/runtime/agent/orchestrator/tools/builtin/arg-guard.mjs +25 -3
  59. package/src/runtime/agent/orchestrator/tools/builtin/bash-tool.mjs +10 -2
  60. package/src/runtime/agent/orchestrator/tools/builtin/builtin-tools.mjs +5 -5
  61. package/src/runtime/agent/orchestrator/tools/builtin/fuzzy-match.mjs +12 -3
  62. package/src/runtime/agent/orchestrator/tools/builtin/grep-formatting.mjs +22 -0
  63. package/src/runtime/agent/orchestrator/tools/builtin/lib/grep-context-expander.mjs +491 -0
  64. package/src/runtime/agent/orchestrator/tools/builtin/lib/grep-output.mjs +91 -13
  65. package/src/runtime/agent/orchestrator/tools/builtin/list-tool.mjs +90 -27
  66. package/src/runtime/agent/orchestrator/tools/builtin/path-utils.mjs +20 -1
  67. package/src/runtime/agent/orchestrator/tools/builtin/read-batch.mjs +1 -1
  68. package/src/runtime/agent/orchestrator/tools/builtin/read-constants.mjs +4 -4
  69. package/src/runtime/agent/orchestrator/tools/builtin/read-image-resize.mjs +2 -2
  70. package/src/runtime/agent/orchestrator/tools/builtin/read-single-tool.mjs +19 -9
  71. package/src/runtime/agent/orchestrator/tools/builtin/read-snapshot-runtime.mjs +2 -1
  72. package/src/runtime/agent/orchestrator/tools/builtin/read-special-files.mjs +3 -3
  73. package/src/runtime/agent/orchestrator/tools/builtin/read-streaming.mjs +7 -2
  74. package/src/runtime/agent/orchestrator/tools/builtin/read-tool.mjs +32 -4
  75. package/src/runtime/agent/orchestrator/tools/builtin/rg-runner.mjs +1 -1
  76. package/src/runtime/agent/orchestrator/tools/builtin/search-builders.mjs +16 -1
  77. package/src/runtime/agent/orchestrator/tools/builtin/search-tool.mjs +546 -23
  78. package/src/runtime/agent/orchestrator/tools/builtin/shell-analysis.mjs +8 -5
  79. package/src/runtime/agent/orchestrator/tools/builtin/shell-job-paths.mjs +12 -0
  80. package/src/runtime/agent/orchestrator/tools/builtin/shell-job-spawn.mjs +10 -3
  81. package/src/runtime/agent/orchestrator/tools/builtin/shell-jobs.mjs +35 -5
  82. package/src/runtime/agent/orchestrator/tools/builtin/shell-output.mjs +3 -3
  83. package/src/runtime/agent/orchestrator/tools/builtin/snapshot-store.mjs +98 -0
  84. package/src/runtime/agent/orchestrator/tools/builtin/tool-output-limit.mjs +48 -0
  85. package/src/runtime/agent/orchestrator/tools/builtin.mjs +71 -1
  86. package/src/runtime/agent/orchestrator/tools/code-graph/build.mjs +7 -3
  87. package/src/runtime/agent/orchestrator/tools/code-graph/dispatch.mjs +104 -14
  88. package/src/runtime/agent/orchestrator/tools/code-graph/project-root.mjs +47 -2
  89. package/src/runtime/agent/orchestrator/tools/code-graph/search-references.mjs +6 -17
  90. package/src/runtime/agent/orchestrator/tools/code-graph/search.mjs +2 -4
  91. package/src/runtime/agent/orchestrator/tools/code-graph/trusted-roots.mjs +3 -1
  92. package/src/runtime/agent/orchestrator/tools/env-scrub.mjs +9 -2
  93. package/src/runtime/agent/orchestrator/tools/patch/dispatch.mjs +5 -5
  94. package/src/runtime/agent/orchestrator/tools/patch/matcher.mjs +1 -1
  95. package/src/runtime/agent/orchestrator/tools/patch/native-server.mjs +57 -2
  96. package/src/runtime/agent/orchestrator/tools/patch/orchestrator.mjs +151 -18
  97. package/src/runtime/agent/orchestrator/tools/patch/parsing.mjs +5 -1
  98. package/src/runtime/agent/orchestrator/tools/patch/v4a-convert.mjs +109 -13
  99. package/src/runtime/agent/orchestrator/tools/patch-manifest.json +10 -10
  100. package/src/runtime/agent/orchestrator/tools/patch-tool-defs.mjs +4 -5
  101. package/src/runtime/agent/orchestrator/tools/shell-command.mjs +4 -0
  102. package/src/runtime/agent/orchestrator/tools/shell-exec-output.mjs +1 -1
  103. package/src/runtime/channels/backends/discord.mjs +5 -14
  104. package/src/runtime/channels/backends/telegram.mjs +0 -5
  105. package/src/runtime/channels/lib/inbound-handler.mjs +0 -1
  106. package/src/runtime/channels/lib/output-forwarder.mjs +24 -5
  107. package/src/runtime/channels/lib/scheduler.mjs +1 -1
  108. package/src/runtime/channels/lib/worker-main.mjs +1 -1
  109. package/src/runtime/media/renditions.mjs +21 -2
  110. package/src/runtime/memory/lib/tool-call-handler.mjs +16 -1
  111. package/src/runtime/memory/tool-defs.mjs +6 -6
  112. package/src/runtime/shared/atomic-file.mjs +53 -0
  113. package/src/runtime/shared/background-tasks.mjs +16 -6
  114. package/src/runtime/shared/child-spawn-gate.mjs +50 -26
  115. package/src/runtime/shared/resource-admission.mjs +40 -1
  116. package/src/runtime/shared/task-notification-envelope.mjs +11 -2
  117. package/src/runtime/shared/tool-card-model.mjs +6 -2
  118. package/src/runtime/shared/tool-status.mjs +10 -1
  119. package/src/runtime/shared/tool-surface.mjs +5 -0
  120. package/src/runtime/shared/turn-snapshot.mjs +385 -21
  121. package/src/runtime/shared/turn-worktree-snapshot.mjs +540 -0
  122. package/src/session-runtime/context-status.mjs +9 -3
  123. package/src/session-runtime/lifecycle-api.mjs +35 -5
  124. package/src/session-runtime/mcp-glue.mjs +11 -6
  125. package/src/session-runtime/provider-models.mjs +94 -43
  126. package/src/session-runtime/provider-usage.mjs +26 -2
  127. package/src/session-runtime/runtime-core.mjs +46 -8
  128. package/src/session-runtime/runtime-tunables.mjs +4 -0
  129. package/src/session-runtime/self-update.mjs +33 -4
  130. package/src/session-runtime/session-lifecycle.mjs +2 -0
  131. package/src/session-runtime/session-text.mjs +2 -1
  132. package/src/session-runtime/session-turn-api.mjs +106 -22
  133. package/src/session-runtime/workflow.mjs +11 -4
  134. package/src/standalone/agent-tool.mjs +4 -4
  135. package/src/standalone/backend-daemon.mjs +570 -0
  136. package/src/standalone/channel-daemon-transport.mjs +141 -1
  137. package/src/standalone/channel-worker.mjs +3 -2
  138. package/src/standalone/engine-daemon-client.mjs +894 -0
  139. package/src/standalone/engine-daemon-local-bridge.mjs +20 -0
  140. package/src/standalone/engine-daemon-protocol.mjs +33 -0
  141. package/src/standalone/engine-daemon-service.mjs +864 -0
  142. package/src/standalone/engine-daemon-transport.mjs +603 -0
  143. package/src/standalone/explore-tool.mjs +1 -1
  144. package/src/tui/App.jsx +62 -47
  145. package/src/tui/app/app-format.mjs +4 -2
  146. package/src/tui/app/app-view.jsx +5 -0
  147. package/src/tui/app/channel-pickers.mjs +7 -6
  148. package/src/tui/app/core-memory-picker.mjs +4 -4
  149. package/src/tui/app/extension-pickers.mjs +20 -18
  150. package/src/tui/app/maintenance-pickers.mjs +27 -27
  151. package/src/tui/app/onboarding-steps.mjs +23 -18
  152. package/src/tui/app/prompt-submit.mjs +13 -2
  153. package/src/tui/app/route-pickers.mjs +13 -8
  154. package/src/tui/app/settings-picker.mjs +55 -58
  155. package/src/tui/app/slash-dispatch.mjs +32 -28
  156. package/src/tui/app/transcript-window.mjs +19 -0
  157. package/src/tui/app/usage-context-panels.mjs +22 -7
  158. package/src/tui/app/use-mouse-input.mjs +25 -3
  159. package/src/tui/app/use-prompt-queue-history.mjs +31 -16
  160. package/src/tui/app/use-transcript-scroll.mjs +30 -8
  161. package/src/tui/app/use-transcript-window.mjs +22 -1
  162. package/src/tui/app/use-welcome-prompt-hint.mjs +2 -2
  163. package/src/tui/components/PromptInput.jsx +10 -0
  164. package/src/tui/components/Spinner.jsx +89 -86
  165. package/src/tui/components/TextEntryPanel.jsx +14 -0
  166. package/src/tui/components/ToolExecution.jsx +2 -2
  167. package/src/tui/dist/index.mjs +1537 -9718
  168. package/src/tui/engine/agent-job-feed.mjs +2 -2
  169. package/src/tui/engine/live-share.mjs +97 -4
  170. package/src/tui/engine/session-api-ext.mjs +23 -1
  171. package/src/tui/engine/session-api.mjs +88 -41
  172. package/src/tui/engine/session-flow.mjs +43 -4
  173. package/src/tui/engine/tool-card-results.mjs +6 -0
  174. package/src/tui/engine/turn.mjs +84 -4
  175. package/src/tui/engine-local-session.mjs +1108 -0
  176. package/src/tui/engine.mjs +16 -1057
  177. package/src/tui/index.jsx +47 -2
  178. package/src/tui/markdown/stream-fence.mjs +1 -1
  179. package/src/tui/spinner-meta.mjs +80 -0
  180. package/src/tui/spinner-verbs.mjs +35 -0
  181. package/src/ui/statusline-segments.mjs +43 -10
  182. package/src/ui/statusline.mjs +10 -1
  183. package/scripts/tmp-cdp-errors.mjs +0 -41
  184. package/scripts/tmp-cdp-inspect.mjs +0 -41
  185. package/src/runtime/agent/orchestrator/session/loop/steering-ladder.mjs +0 -176
  186. package/src/standalone/channel-daemon.mjs +0 -226
@@ -1,226 +0,0 @@
1
- // Machine-global channels daemon entry.
2
- //
3
- // One process per machine hosts the channels runtime (worker-main) and exposes
4
- // it to many TUIs over a local HTTP + SSE transport (see
5
- // channel-daemon-transport.mjs) instead of the old per-TUI fork + node-IPC.
6
- // Spawned (or attached-to) by createStandaloneChannelWorker; ownership is a
7
- // pid-verified singleton lock (singleton-owner.mjs) — NOT the try-once
8
- // active-instance lock that starved under 6 contending workers. A stale daemon
9
- // (dead owner pid) is reclaimed by the next claim; a live peer that wins the
10
- // race makes this process exit(0) so the spawner attaches to the winner.
11
- //
12
- // Boot order matters: MIXDOG_CHANNEL_DAEMON must be set BEFORE importing the
13
- // channels runtime so worker-main skips its parent-IPC loop (runWorkerIpc) and
14
- // lets this entry own start()/stop() + the transport.
15
- process.env.MIXDOG_CHANNEL_DAEMON = '1';
16
- process.env.MIXDOG_WORKER_MODE = process.env.MIXDOG_WORKER_MODE || '1';
17
-
18
- // V8 compile cache: the daemon is a standalone child entry (not via cli.mjs);
19
- // caching compiled bytecode across restarts removes the channels+memory
20
- // runtime parse cost from every daemon boot. Best-effort.
21
- try {
22
- const { enableCompileCache } = await import('node:module');
23
- enableCompileCache?.();
24
- } catch { /* launch-speed optimization only */ }
25
-
26
- import os from 'node:os';
27
- import path from 'node:path';
28
- import { mkdirSync, appendFileSync } from 'node:fs';
29
- import { fileURLToPath } from 'node:url';
30
- import { performance } from 'node:perf_hooks';
31
- import { claimSingletonOwner, releaseSingletonOwner } from '../runtime/shared/singleton-owner.mjs';
32
- import { setChannelNotifySink } from '../runtime/channels/lib/parent-bridge.mjs';
33
- import { setOwnerContext } from '../runtime/channels/lib/runtime-paths.mjs';
34
- import { safeIpcSend } from '../runtime/shared/safe-ipc-send.mjs';
35
- import { createChannelDaemonTransport } from './channel-daemon-transport.mjs';
36
- import { createStandaloneMemoryRuntime } from './memory-runtime-proxy.mjs';
37
-
38
- function runtimeRoot() {
39
- return process.env.MIXDOG_RUNTIME_ROOT
40
- ? path.resolve(process.env.MIXDOG_RUNTIME_ROOT)
41
- : path.join(os.tmpdir(), 'mixdog');
42
- }
43
-
44
- const RUNTIME_ROOT = runtimeRoot();
45
- const DATA_DIR = process.env.MIXDOG_DATA_DIR ? path.resolve(process.env.MIXDOG_DATA_DIR) : RUNTIME_ROOT;
46
- const CWD = process.cwd();
47
- const DISCOVERY_PATH = path.join(RUNTIME_ROOT, 'channel-daemon.json');
48
- // Owner-election lock, separate from the channels seat/bridge state. Reused
49
- // pid-verified claim primitive with real claim-lock retry inside.
50
- const OWNER_PATH = path.join(DATA_DIR, 'channel-daemon-owner.json');
51
- // Memory runtime is folded into the daemon: the ONE machine-global process is
52
- // responsible for starting BOTH the channels runtime and the memory runtime.
53
- // The memory proxy is spawn-or-attach + singleton (memory-runtime-owner lock)
54
- // and advertises memory_port to active-instance.json exactly as before, so
55
- // external readers / memory-client.mjs discovery is UNCHANGED.
56
- const MEMORY_ENTRY = fileURLToPath(new URL('../runtime/memory/index.mjs', import.meta.url));
57
-
58
- // The spawning TUI mirrors our stderr into this file ONLY until it sees our
59
- // 'ready' message; after that its pipe consumer dies on parent exit and later
60
- // lines would be lost. So once ready we append here ourselves (fileLogging on),
61
- // keyed to the SAME ready event the spawner detaches on — no loss, no dup.
62
- const LOG_PATH = path.join(DATA_DIR, 'channels-worker-standalone.log');
63
- let fileLogging = false;
64
- function log(line) {
65
- const text = `[channel-daemon] ${line}`;
66
- // Exactly ONE sink per line: before ready the spawner mirrors our stderr into
67
- // the log, so write stderr only; after ready we own the file, so write the
68
- // file only — never both (no duplicate around the ready handoff).
69
- if (!fileLogging) {
70
- try { process.stderr.write(`${text}\n`); } catch {}
71
- return;
72
- }
73
- try {
74
- mkdirSync(path.dirname(LOG_PATH), { recursive: true });
75
- appendFileSync(LOG_PATH, `[${new Date().toISOString()}] ${text}\n`);
76
- } catch {}
77
- }
78
-
79
- // Redirect raw process.stderr/stdout writes and console.* from ANY module in
80
- // this process to the daemon log file. Installed at the ready boundary (same
81
- // point fileLogging flips) so pre-ready lines still reach the spawner mirror.
82
- function installDaemonLogRedirect() {
83
- if (process.env.MIXDOG_DAEMON_ALLOW_STDERR === '1') return;
84
- const file = (chunk) => {
85
- try {
86
- const text = String(chunk ?? '').trimEnd();
87
- if (text) {
88
- mkdirSync(path.dirname(LOG_PATH), { recursive: true });
89
- appendFileSync(LOG_PATH, `[${new Date().toISOString()}] ${text}\n`);
90
- }
91
- } catch { /* logging must never fail the daemon */ }
92
- };
93
- const patch = (stream) => {
94
- stream.write = ((chunk, encoding, callback) => {
95
- const done = typeof encoding === 'function' ? encoding : callback;
96
- file(chunk);
97
- if (typeof done === 'function') { try { done(); } catch {} }
98
- return true;
99
- });
100
- };
101
- patch(process.stderr);
102
- patch(process.stdout);
103
- for (const m of ['log', 'info', 'warn', 'error', 'debug', 'trace']) {
104
- console[m] = (...args) => file(`[console.${m}] ${args.map((a) => {
105
- if (typeof a === 'string') return a;
106
- try { return JSON.stringify(a); } catch { return String(a); }
107
- }).join(' ')}`);
108
- }
109
- }
110
-
111
- let channels = null;
112
- let transport = null;
113
- let memoryRuntime = null;
114
- let shuttingDown = false;
115
-
116
- async function shutdown(reason, code = 0) {
117
- if (shuttingDown) return;
118
- shuttingDown = true;
119
- log(`shutting down (${reason})`);
120
- try { setChannelNotifySink(null); } catch {}
121
- try { await channels?.stop?.(); } catch (e) { log(`channels.stop failed: ${e?.message || e}`); }
122
- // Detach the memory client (never hard-kills the shared memory daemon).
123
- try { await memoryRuntime?.stop?.(); } catch (e) { log(`memory.stop failed: ${e?.message || e}`); }
124
- try { await transport?.stop?.(); } catch (e) { log(`transport.stop failed: ${e?.message || e}`); }
125
- try { releaseSingletonOwner(OWNER_PATH, process.pid); } catch {}
126
- process.exit(code);
127
- }
128
-
129
- async function main() {
130
- const startedAt = performance.now();
131
- try { mkdirSync(RUNTIME_ROOT, { recursive: true }); } catch {}
132
-
133
- // Pid-verified singleton claim (claimSingletonOwner reclaims a dead-pid owner
134
- // file and refuses only a LIVE peer). Loser exits so the spawner attaches to
135
- // the winner instead of running a second daemon.
136
- const claim = claimSingletonOwner(OWNER_PATH, { kind: 'channel-runtime-daemon', pid: process.pid, meta: { cwd: CWD } });
137
- if (!claim.owned) {
138
- log(`live peer holds owner lock (pid=${claim.owner?.pid}) — exiting for attach`);
139
- process.exit(0);
140
- }
141
- process.on('exit', () => { try { releaseSingletonOwner(OWNER_PATH, process.pid); } catch {} });
142
-
143
- // Import the channels runtime AFTER the daemon env is set so worker-main
144
- // skips runWorkerIpc; this triggers its boot side effects (config/backend).
145
- channels = await import('../runtime/channels/index.mjs');
146
-
147
- // Accepted owner controls refresh active-instance context. The transport
148
- // admits rebind only for the current manual owner.
149
- const POINTER_TOOLS = new Set(['activate_channel_bridge', 'rebind_current_transcript']);
150
- const handleCall = (name, args, ctx) => {
151
- if (ctx && POINTER_TOOLS.has(name)) {
152
- try { setOwnerContext({ leadPid: ctx.leadPid, cwd: ctx.cwd }); } catch {}
153
- }
154
- return channels.handleToolCallWithBridgeRetry(name, args || {});
155
- };
156
- transport = createChannelDaemonTransport({
157
- handleCall,
158
- // If a manual owner disappears without sending OFF, deactivate the backend.
159
- // No survivor is selected or rebound.
160
- dispatchControl: (name, args, ctx) => handleCall(name, args, ctx),
161
- discoveryPath: DISCOVERY_PATH,
162
- log,
163
- // Self-shutdown when the last attached TUI leaves (reuses the SSE/client
164
- // registry as the liveness signal — mirrors the memory daemon grace).
165
- onClientsEmpty: () => { void shutdown('no live clients'); },
166
- });
167
- setChannelNotifySink((method, params) => transport.notify(method, params));
168
- const { port, token } = await transport.start();
169
-
170
- // Ready handshake for the spawner FIRST (mirrors the memory daemon's ready
171
- // port). Transport is already listening; signal ready before the heavy
172
- // Discord connect so the spawner's ready wait never blocks on backend I/O.
173
- // Take over file logging from the spawner at the ready boundary. No rotate
174
- // here: the spawner already bounds the file at its own boot (channel-worker
175
- // rotateBoundedLog), and rotating now would race other processes' buffered
176
- // appends into the same log.
177
- fileLogging = true;
178
- // Global stderr/console redirect: runtime modules hosted in this daemon
179
- // (session sweeps, scheduler, inbound handlers, providers…) write raw
180
- // process.stderr lines. With the current pipe stdio those bytes die with the
181
- // spawner, and a daemon inherited from an older spawn path prints them into
182
- // whatever terminal originally launched it — the "[session-sweep] …" text
183
- // observed inside the TUI composer. Route EVERY stderr/console line to the
184
- // daemon log so no spawn mode can ever reach a user terminal.
185
- installDaemonLogRedirect();
186
- // Guard the ready handshake against a dead/closing parent pipe. process.send
187
- // delivery is async: if the spawner TUI already exited, the write fails with
188
- // an async 'error' (EPIPE) that a sync try/catch cannot catch — it would
189
- // surface as uncaughtException and (pre-fix) flip the daemon degraded
190
- // forever. process.connected gates the obvious-dead case; the send callback
191
- // swallows the async delivery error so it never reaches uncaughtException.
192
- if (process.connected && process.send) {
193
- safeIpcSend(process, { type: 'ready', port, token });
194
- }
195
- log(`ready port=${port} pid=${process.pid} in ${(performance.now() - startedAt).toFixed(0)}ms`);
196
-
197
- // Boot messaging only for an explicit manual remote request. Automation may
198
- // spawn the shared daemon with no remote intent; keep schedules/webhooks live
199
- // without connecting the channel backend until a manual Remote ON arrives.
200
- // Legacy `auto` intent is deliberately ignored.
201
- const remoteIntent = String(process.env.MIXDOG_REMOTE_INTENT || '');
202
- const messaging = remoteIntent === 'explicit';
203
- void Promise.resolve().then(() => channels.start({ messaging }))
204
- .catch((e) => log(`channels.start failed (non-fatal): ${e?.message || e}`));
205
-
206
- // Fold memory startup in: eagerly ensure the memory runtime is up under the
207
- // daemon's lifecycle (spawn-or-attach singleton). Fire-and-forget — memory
208
- // boot is heavy (DB/embeddings) and must NOT delay the ready handshake below
209
- // (the spawner's ready wait would time out); the proxy publishes memory_port
210
- // to active-instance.json when ready and memory-client buffers until then.
211
- try {
212
- memoryRuntime = createStandaloneMemoryRuntime({ entry: MEMORY_ENTRY, dataDir: DATA_DIR, cwd: CWD });
213
- void memoryRuntime.start().catch((e) => log(`memory.start failed (non-fatal): ${e?.message || e}`));
214
- } catch (e) { log(`memory.start setup failed (non-fatal): ${e?.message || e}`); }
215
- }
216
-
217
- process.on('SIGTERM', () => { void shutdown('SIGTERM'); });
218
- process.on('SIGINT', () => { void shutdown('SIGINT'); });
219
- process.on('message', (msg) => {
220
- if (msg && msg.type === 'shutdown') void shutdown('IPC shutdown');
221
- });
222
-
223
- main().catch((err) => {
224
- log(`fatal boot error: ${err?.stack || err?.message || err}`);
225
- void shutdown('fatal boot error', 2);
226
- });