mixdog 0.9.1 → 0.9.2
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.
- package/package.json +8 -1
- package/scripts/_bench-cwc.json +20 -0
- package/scripts/agent-loop-policy-test.mjs +37 -0
- package/scripts/agent-parallel-smoke.mjs +54 -10
- package/scripts/background-task-meta-smoke.mjs +1 -1
- package/scripts/bench-run.mjs +262 -0
- package/scripts/compact-smoke.mjs +12 -0
- package/scripts/compact-trigger-migration-smoke.mjs +67 -1
- package/scripts/ingest-pure-conversation-smoke.mjs +148 -0
- package/scripts/internal-comms-bench.mjs +727 -0
- package/scripts/internal-comms-smoke.mjs +75 -0
- package/scripts/lead-workflow-smoke.mjs +4 -4
- package/scripts/live-worker-smoke.mjs +9 -9
- package/scripts/output-style-bench.mjs +285 -0
- package/scripts/output-style-smoke.mjs +13 -10
- package/scripts/patch-replay.mjs +90 -0
- package/scripts/provider-stream-stall-test.mjs +276 -0
- package/scripts/provider-toolcall-test.mjs +599 -1
- package/scripts/routing-corpus.mjs +281 -0
- package/scripts/session-bench.mjs +1526 -0
- package/scripts/session-diag.mjs +595 -0
- package/scripts/task-bench.mjs +207 -0
- package/scripts/tool-failures.mjs +6 -6
- package/scripts/tool-smoke.mjs +306 -66
- package/scripts/toolcall-args-test.mjs +81 -0
- package/src/agents/debugger/AGENT.md +4 -4
- package/src/agents/heavy-worker/AGENT.md +4 -2
- package/src/agents/reviewer/AGENT.md +4 -4
- package/src/agents/worker/AGENT.md +4 -2
- package/src/app.mjs +10 -6
- package/src/defaults/{hidden-roles.json → agents.json} +7 -7
- package/src/examples/schedules/SCHEDULE.example.md +32 -0
- package/src/examples/webhooks/WEBHOOK.example.md +40 -0
- package/src/headless-role.mjs +14 -14
- package/src/help.mjs +1 -0
- package/src/lib/rules-builder.cjs +32 -54
- package/src/mixdog-session-runtime.mjs +710 -318
- package/src/output-styles/default.md +12 -7
- package/src/output-styles/minimal.md +25 -0
- package/src/output-styles/oneline.md +21 -0
- package/src/output-styles/simple.md +10 -9
- package/src/repl.mjs +12 -2
- package/src/rules/agent/00-common.md +7 -5
- package/src/rules/agent/30-explorer.md +7 -8
- package/src/rules/lead/01-general.md +3 -1
- package/src/rules/lead/lead-tool.md +7 -0
- package/src/rules/shared/01-tool.md +17 -12
- package/src/runtime/agent/orchestrator/agent-runtime/agent-dispatch.mjs +90 -32
- package/src/runtime/agent/orchestrator/agent-runtime/agent-loop-policy.mjs +32 -0
- package/src/runtime/agent/orchestrator/agent-runtime/agent-progress-watchdog.mjs +18 -6
- package/src/runtime/agent/orchestrator/agent-runtime/cache-strategy.mjs +23 -20
- package/src/runtime/agent/orchestrator/agent-runtime/session-builder.mjs +48 -14
- package/src/runtime/agent/orchestrator/agent-trace.mjs +87 -12
- package/src/runtime/agent/orchestrator/config.mjs +3 -0
- package/src/runtime/agent/orchestrator/context/collect.mjs +131 -67
- package/src/runtime/agent/orchestrator/{internal-roles.mjs → internal-agents.mjs} +72 -72
- package/src/runtime/agent/orchestrator/internal-tools.mjs +13 -26
- package/src/runtime/agent/orchestrator/mcp/client.mjs +94 -16
- package/src/runtime/agent/orchestrator/providers/anthropic-betas.mjs +7 -0
- package/src/runtime/agent/orchestrator/providers/anthropic-effort.mjs +188 -0
- package/src/runtime/agent/orchestrator/providers/anthropic-leaked-toolcall.mjs +444 -0
- package/src/runtime/agent/orchestrator/providers/anthropic-oauth.mjs +332 -57
- package/src/runtime/agent/orchestrator/providers/anthropic.mjs +59 -32
- package/src/runtime/agent/orchestrator/providers/api-usage.mjs +27 -20
- package/src/runtime/agent/orchestrator/providers/gemini.mjs +184 -17
- package/src/runtime/agent/orchestrator/providers/grok-oauth.mjs +8 -1
- package/src/runtime/agent/orchestrator/providers/model-catalog.mjs +18 -8
- package/src/runtime/agent/orchestrator/providers/openai-compat-stream.mjs +210 -21
- package/src/runtime/agent/orchestrator/providers/openai-compat.mjs +78 -3
- package/src/runtime/agent/orchestrator/providers/openai-oauth-ws.mjs +202 -98
- package/src/runtime/agent/orchestrator/providers/openai-oauth.mjs +183 -20
- package/src/runtime/agent/orchestrator/providers/openai-ws.mjs +18 -0
- package/src/runtime/agent/orchestrator/providers/opencode-go-usage.mjs +11 -5
- package/src/runtime/agent/orchestrator/providers/registry.mjs +2 -1
- package/src/runtime/agent/orchestrator/providers/retry-classifier.mjs +15 -9
- package/src/runtime/agent/orchestrator/session/compact.mjs +560 -51
- package/src/runtime/agent/orchestrator/session/context-utils.mjs +250 -3
- package/src/runtime/agent/orchestrator/session/loop.mjs +394 -132
- package/src/runtime/agent/orchestrator/session/manager.mjs +217 -170
- package/src/runtime/agent/orchestrator/session/store.mjs +4 -4
- package/src/runtime/agent/orchestrator/session/tool-envelope.mjs +61 -0
- package/src/runtime/agent/orchestrator/session/tool-result-offload.mjs +5 -0
- package/src/runtime/agent/orchestrator/stall-policy.mjs +63 -15
- package/src/runtime/agent/orchestrator/tools/builtin/arg-guard.mjs +194 -24
- package/src/runtime/agent/orchestrator/tools/builtin/arg-guard.test.mjs +143 -0
- package/src/runtime/agent/orchestrator/tools/builtin/builtin-tools.mjs +34 -18
- package/src/runtime/agent/orchestrator/tools/builtin/external-tool-adapters.mjs +0 -0
- package/src/runtime/agent/orchestrator/tools/builtin/list-formatting.mjs +10 -0
- package/src/runtime/agent/orchestrator/tools/builtin/list-tool.mjs +5 -4
- package/src/runtime/agent/orchestrator/tools/builtin/path-utils.mjs +15 -0
- package/src/runtime/agent/orchestrator/tools/builtin/read-args.mjs +9 -44
- package/src/runtime/agent/orchestrator/tools/builtin/read-constants.mjs +2 -1
- package/src/runtime/agent/orchestrator/tools/builtin/read-formatting.mjs +13 -4
- package/src/runtime/agent/orchestrator/tools/builtin/read-tool.mjs +10 -17
- package/src/runtime/agent/orchestrator/tools/builtin/search-tool.mjs +18 -2
- package/src/runtime/agent/orchestrator/tools/builtin/shell-output.mjs +3 -2
- package/src/runtime/agent/orchestrator/tools/builtin/tool-output-limit.mjs +10 -0
- package/src/runtime/agent/orchestrator/tools/builtin.mjs +59 -1
- package/src/runtime/agent/orchestrator/tools/code-graph-tool-defs.mjs +5 -5
- package/src/runtime/agent/orchestrator/tools/code-graph.mjs +4076 -3985
- package/src/runtime/agent/orchestrator/tools/patch.mjs +116 -2
- package/src/runtime/channels/backends/discord.mjs +99 -9
- package/src/runtime/channels/backends/telegram.mjs +501 -0
- package/src/runtime/channels/index.mjs +441 -1224
- package/src/runtime/channels/lib/config.mjs +54 -2
- package/src/runtime/channels/lib/format.mjs +4 -2
- package/src/runtime/channels/lib/output-forwarder.mjs +80 -67
- package/src/runtime/channels/lib/runtime-paths.mjs +29 -0
- package/src/runtime/channels/lib/scheduler.mjs +1 -1
- package/src/runtime/channels/lib/telegram-format.mjs +283 -0
- package/src/runtime/channels/lib/tool-format.mjs +1 -1
- package/src/runtime/channels/lib/transcript-discovery.mjs +19 -1
- package/src/runtime/channels/lib/webhook.mjs +59 -31
- package/src/runtime/channels/tool-defs.mjs +1 -1
- package/src/runtime/memory/index.mjs +184 -19
- package/src/runtime/memory/lib/agent-ipc.mjs +2 -2
- package/src/runtime/memory/lib/core-memory-store.mjs +1 -1
- package/src/runtime/memory/lib/memory-cycle1.mjs +1 -1
- package/src/runtime/memory/lib/memory-cycle2.mjs +9 -6
- package/src/runtime/memory/lib/memory-cycle3.mjs +1 -1
- package/src/runtime/memory/lib/memory.mjs +101 -4
- package/src/runtime/memory/lib/pg/adapter.mjs +139 -15
- package/src/runtime/memory/lib/session-ingest.mjs +107 -0
- package/src/runtime/memory/lib/trace-store.mjs +69 -22
- package/src/runtime/memory/tool-defs.mjs +6 -3
- package/src/runtime/shared/channel-notification-routing.mjs +12 -0
- package/src/runtime/shared/channel-notification-routing.test.mjs +45 -0
- package/src/runtime/shared/config.mjs +9 -0
- package/src/runtime/shared/llm/http-agent.mjs +12 -5
- package/src/runtime/shared/schedules-store.mjs +21 -19
- package/src/runtime/shared/tool-surface.mjs +98 -13
- package/src/runtime/shared/transcript-writer.mjs +129 -0
- package/src/runtime/shared/update-checker.mjs +214 -0
- package/src/standalone/agent-tool.mjs +255 -109
- package/src/standalone/channel-admin.mjs +133 -40
- package/src/standalone/channel-worker.mjs +8 -291
- package/src/standalone/explore-tool.mjs +2 -2
- package/src/standalone/memory-runtime-proxy.mjs +3 -1
- package/src/standalone/provider-admin.mjs +11 -0
- package/src/standalone/usage-dashboard.mjs +1 -1
- package/src/tui/App.jsx +2096 -732
- package/src/tui/components/ConfirmBar.jsx +47 -0
- package/src/tui/components/ContextPanel.jsx +5 -3
- package/src/tui/components/ItemRightHintOverprint.jsx +54 -0
- package/src/tui/components/Markdown.jsx +22 -98
- package/src/tui/components/Message.jsx +14 -35
- package/src/tui/components/Picker.jsx +87 -12
- package/src/tui/components/PromptInput.jsx +83 -7
- package/src/tui/components/QueuedCommands.jsx +1 -1
- package/src/tui/components/SlashCommandPalette.jsx +8 -5
- package/src/tui/components/Spinner.jsx +7 -7
- package/src/tui/components/StatusLine.jsx +40 -21
- package/src/tui/components/TextEntryPanel.jsx +51 -7
- package/src/tui/components/ToolExecution.jsx +170 -98
- package/src/tui/components/TurnDone.jsx +4 -4
- package/src/tui/components/UsagePanel.jsx +1 -1
- package/src/tui/components/tool-output-format.mjs +159 -21
- package/src/tui/components/tool-output-format.test.mjs +87 -0
- package/src/tui/display-width.mjs +69 -0
- package/src/tui/display-width.test.mjs +35 -0
- package/src/tui/dist/index.mjs +6965 -2391
- package/src/tui/engine.mjs +287 -126
- package/src/tui/index.jsx +117 -7
- package/src/tui/keyboard-protocol.mjs +42 -0
- package/src/tui/lib/voice-recorder.mjs +453 -0
- package/src/tui/markdown/format-token.mjs +129 -76
- package/src/tui/markdown/format-token.test.mjs +61 -19
- package/src/tui/markdown/measure-rendered-rows.mjs +85 -0
- package/src/tui/markdown/render-ansi.test.mjs +1 -1
- package/src/tui/markdown/streaming-markdown.mjs +167 -0
- package/src/tui/markdown/streaming-markdown.test.mjs +70 -0
- package/src/tui/markdown/table-layout.mjs +9 -9
- package/src/tui/paste-attachments.mjs +0 -11
- package/src/tui/prompt-history-store.mjs +129 -0
- package/src/tui/prompt-history-store.test.mjs +52 -0
- package/src/tui/statusline-ansi-bridge.test.mjs +3 -3
- package/src/tui/theme.mjs +41 -657
- package/src/tui/themes/base.mjs +86 -0
- package/src/tui/themes/basic.mjs +85 -0
- package/src/tui/themes/catppuccin.mjs +72 -0
- package/src/tui/themes/dracula.mjs +70 -0
- package/src/tui/themes/everforest.mjs +71 -0
- package/src/tui/themes/gruvbox.mjs +71 -0
- package/src/tui/themes/index.mjs +71 -0
- package/src/tui/themes/indigo.mjs +78 -0
- package/src/tui/themes/kanagawa.mjs +80 -0
- package/src/tui/themes/light.mjs +81 -0
- package/src/tui/themes/nord.mjs +72 -0
- package/src/tui/themes/onedark.mjs +16 -0
- package/src/tui/themes/rosepine.mjs +70 -0
- package/src/tui/themes/teal.mjs +81 -0
- package/src/tui/themes/tokyonight.mjs +79 -0
- package/src/tui/themes/utils.mjs +106 -0
- package/src/tui/themes/warm.mjs +79 -0
- package/src/tui/transcript-tool-failures.mjs +13 -2
- package/src/ui/markdown.mjs +1 -1
- package/src/ui/model-display.mjs +2 -2
- package/src/ui/statusline.mjs +26 -27
- package/src/vendor/statusline/bin/statusline-route.mjs +5 -12
- package/src/vendor/statusline/src/gateway/claude-current.mjs +3 -3
- package/src/vendor/statusline/src/gateway/route-meta.mjs +30 -16
- package/src/workflows/default/WORKFLOW.md +39 -12
- package/src/workflows/sequential/WORKFLOW.md +46 -0
- package/src/workflows/solo/WORKFLOW.md +7 -0
- package/vendor/ink/build/display-width.js +62 -0
- package/vendor/ink/build/ink.js +100 -12
- package/vendor/ink/build/measure-text.js +4 -1
- package/vendor/ink/build/output.js +115 -9
- package/vendor/ink/build/render-node-to-output.js +4 -1
- package/vendor/ink/build/render.js +4 -0
- package/src/output-styles/extreme-simple.md +0 -20
- package/src/rules/lead/04-workflow.md +0 -51
- package/src/workflows/default/workflow.json +0 -13
- package/src/workflows/solo/workflow.json +0 -7
|
@@ -1,11 +1,9 @@
|
|
|
1
1
|
import { execFile, fork, spawnSync } from 'node:child_process';
|
|
2
|
-
import { appendFileSync, mkdirSync,
|
|
3
|
-
import http from 'node:http';
|
|
2
|
+
import { appendFileSync, mkdirSync, rmSync, writeFileSync } from 'node:fs';
|
|
4
3
|
import { tmpdir } from 'node:os';
|
|
5
4
|
import { dirname, join, resolve } from 'node:path';
|
|
6
5
|
import { fileURLToPath, pathToFileURL } from 'node:url';
|
|
7
6
|
import { startChildGuardian } from '../runtime/shared/child-guardian.mjs';
|
|
8
|
-
import { claimSingletonOwner, readSingletonOwner, releaseSingletonOwner } from '../runtime/shared/singleton-owner.mjs';
|
|
9
7
|
|
|
10
8
|
const CHANNEL_TOOLS = new Set([
|
|
11
9
|
'reply',
|
|
@@ -56,67 +54,6 @@ function runtimeRoot() {
|
|
|
56
54
|
return process.env.MIXDOG_RUNTIME_ROOT ? resolve(process.env.MIXDOG_RUNTIME_ROOT) : join(tmpdir(), 'mixdog');
|
|
57
55
|
}
|
|
58
56
|
|
|
59
|
-
function readJson(path) {
|
|
60
|
-
try { return JSON.parse(readFileSync(path, 'utf8')); } catch { return null; }
|
|
61
|
-
}
|
|
62
|
-
|
|
63
|
-
function parsePort(value) {
|
|
64
|
-
const port = Number(value);
|
|
65
|
-
return Number.isInteger(port) && port > 0 && port < 65536 ? port : null;
|
|
66
|
-
}
|
|
67
|
-
|
|
68
|
-
function readActiveInstance() {
|
|
69
|
-
return readJson(join(runtimeRoot(), 'active-instance.json'));
|
|
70
|
-
}
|
|
71
|
-
|
|
72
|
-
function readOwnerSecret(instanceId) {
|
|
73
|
-
const parsed = readJson(join(runtimeRoot(), `owner-secret-${String(instanceId)}.json`));
|
|
74
|
-
return typeof parsed?.secret === 'string' ? parsed.secret : '';
|
|
75
|
-
}
|
|
76
|
-
|
|
77
|
-
function requestJson({ port, method = 'GET', path = '/', body = null, headers = {}, timeoutMs = 120_000 }) {
|
|
78
|
-
return new Promise((resolvePromise, reject) => {
|
|
79
|
-
const payload = body == null ? null : JSON.stringify(body);
|
|
80
|
-
const req = http.request({
|
|
81
|
-
hostname: '127.0.0.1',
|
|
82
|
-
port,
|
|
83
|
-
path,
|
|
84
|
-
method,
|
|
85
|
-
headers: {
|
|
86
|
-
...headers,
|
|
87
|
-
...(payload
|
|
88
|
-
? { 'Content-Type': 'application/json', 'Content-Length': Buffer.byteLength(payload) }
|
|
89
|
-
: {}),
|
|
90
|
-
},
|
|
91
|
-
timeout: timeoutMs,
|
|
92
|
-
}, (res) => {
|
|
93
|
-
let data = '';
|
|
94
|
-
res.setEncoding('utf8');
|
|
95
|
-
res.on('data', chunk => { data += chunk; });
|
|
96
|
-
res.on('end', () => {
|
|
97
|
-
let parsed = null;
|
|
98
|
-
try { parsed = data ? JSON.parse(data) : null; } catch {}
|
|
99
|
-
if (res.statusCode && res.statusCode >= 400) {
|
|
100
|
-
reject(new Error(parsed?.error || data || `HTTP ${res.statusCode}`));
|
|
101
|
-
return;
|
|
102
|
-
}
|
|
103
|
-
resolvePromise(parsed ?? { raw: data });
|
|
104
|
-
});
|
|
105
|
-
});
|
|
106
|
-
req.on('error', reject);
|
|
107
|
-
req.on('timeout', () => {
|
|
108
|
-
req.destroy();
|
|
109
|
-
reject(new Error(`channel owner request timed out: ${method} ${path}`));
|
|
110
|
-
});
|
|
111
|
-
if (payload) req.write(payload);
|
|
112
|
-
req.end();
|
|
113
|
-
});
|
|
114
|
-
}
|
|
115
|
-
|
|
116
|
-
function textResult(text, isError = false) {
|
|
117
|
-
return { content: [{ type: 'text', text: String(text ?? '') }], ...(isError ? { isError: true } : {}) };
|
|
118
|
-
}
|
|
119
|
-
|
|
120
57
|
export function createStandaloneChannelWorker({
|
|
121
58
|
entry,
|
|
122
59
|
rootDir,
|
|
@@ -141,16 +78,8 @@ export function createStandaloneChannelWorker({
|
|
|
141
78
|
const ownedChildPids = new Set();
|
|
142
79
|
const logPath = join(dataDir, 'channels-worker-standalone.log');
|
|
143
80
|
const useProcessWorker = process.env.MIXDOG_CHANNEL_WORKER_PROCESS !== '0';
|
|
144
|
-
const singletonEnabled = process.env.MIXDOG_CHANNEL_SINGLETON !== '0';
|
|
145
|
-
const daemonEnabled = process.env.MIXDOG_CHANNEL_DAEMON !== '0';
|
|
146
|
-
const ownerPath = join(dataDir, 'channels-worker-owner.json');
|
|
147
|
-
const notifyBusPath = join(runtimeRoot(), 'channel-notifications.jsonl');
|
|
148
81
|
const clientDir = join(runtimeRoot(), 'channel-clients');
|
|
149
82
|
const clientPath = join(clientDir, `${process.pid}.json`);
|
|
150
|
-
let ownerClaim = null;
|
|
151
|
-
let ownerPid = process.pid;
|
|
152
|
-
let notifyBusTimer = null;
|
|
153
|
-
let notifyBusOffset = 0;
|
|
154
83
|
let clientHeartbeatTimer = null;
|
|
155
84
|
let clientHeartbeatExitCleanup = null;
|
|
156
85
|
|
|
@@ -186,56 +115,9 @@ export function createStandaloneChannelWorker({
|
|
|
186
115
|
try { rmSync(clientPath, { force: true }); } catch {}
|
|
187
116
|
}
|
|
188
117
|
|
|
189
|
-
function startNotifyBus() {
|
|
190
|
-
if (!onNotify || notifyBusTimer) return;
|
|
191
|
-
try { notifyBusOffset = statSync(notifyBusPath).size; } catch { notifyBusOffset = 0; }
|
|
192
|
-
notifyBusTimer = setInterval(pollNotifyBus, 500);
|
|
193
|
-
notifyBusTimer.unref?.();
|
|
194
|
-
}
|
|
195
|
-
|
|
196
|
-
function stopNotifyBus() {
|
|
197
|
-
if (!notifyBusTimer) return;
|
|
198
|
-
clearInterval(notifyBusTimer);
|
|
199
|
-
notifyBusTimer = null;
|
|
200
|
-
}
|
|
201
|
-
|
|
202
|
-
function pollNotifyBus() {
|
|
203
|
-
let size = 0;
|
|
204
|
-
try { size = statSync(notifyBusPath).size; } catch { return; }
|
|
205
|
-
if (size < notifyBusOffset) notifyBusOffset = 0;
|
|
206
|
-
if (size === notifyBusOffset) return;
|
|
207
|
-
let chunk;
|
|
208
|
-
try {
|
|
209
|
-
const data = readFileSync(notifyBusPath);
|
|
210
|
-
chunk = data.subarray(notifyBusOffset, size).toString('utf8');
|
|
211
|
-
notifyBusOffset = size;
|
|
212
|
-
} catch {
|
|
213
|
-
return;
|
|
214
|
-
}
|
|
215
|
-
for (const line of chunk.split(/\r?\n/)) {
|
|
216
|
-
if (!line.trim()) continue;
|
|
217
|
-
try {
|
|
218
|
-
const msg = JSON.parse(line);
|
|
219
|
-
if (!msg || typeof msg !== 'object') continue;
|
|
220
|
-
onNotify({ type: 'notify', method: msg.method, params: msg.params, busId: msg.id, busPid: msg.pid });
|
|
221
|
-
} catch {}
|
|
222
|
-
}
|
|
223
|
-
}
|
|
224
|
-
|
|
225
|
-
startNotifyBus();
|
|
226
118
|
startClientHeartbeat();
|
|
227
119
|
|
|
228
120
|
function status() {
|
|
229
|
-
const external = externalOwner();
|
|
230
|
-
if (external) {
|
|
231
|
-
return {
|
|
232
|
-
running: false,
|
|
233
|
-
pid: null,
|
|
234
|
-
pending: pending.size,
|
|
235
|
-
mode: 'external-owner',
|
|
236
|
-
ownerPid: Number(external.pid) || null,
|
|
237
|
-
};
|
|
238
|
-
}
|
|
239
121
|
if (!useProcessWorker) {
|
|
240
122
|
return {
|
|
241
123
|
running: Boolean(inProcessMod),
|
|
@@ -252,36 +134,6 @@ export function createStandaloneChannelWorker({
|
|
|
252
134
|
};
|
|
253
135
|
}
|
|
254
136
|
|
|
255
|
-
function externalOwner() {
|
|
256
|
-
if (!singletonEnabled || ownerClaim?.owned) return null;
|
|
257
|
-
const current = readSingletonOwner(ownerPath);
|
|
258
|
-
if (current.alive && current.owner?.pid && Number(current.owner.pid) !== process.pid) return current.owner;
|
|
259
|
-
return null;
|
|
260
|
-
}
|
|
261
|
-
|
|
262
|
-
function ensureOwner() {
|
|
263
|
-
if (!singletonEnabled || ownerClaim?.owned) return true;
|
|
264
|
-
ownerClaim = claimSingletonOwner(ownerPath, {
|
|
265
|
-
kind: 'channel-worker-host',
|
|
266
|
-
pid: process.pid,
|
|
267
|
-
meta: { cwd },
|
|
268
|
-
});
|
|
269
|
-
if (!ownerClaim.owned) {
|
|
270
|
-
logLine(logPath, `runtime owned by pid ${ownerClaim.owner?.pid || 'unknown'}; skipping local channel worker`);
|
|
271
|
-
return false;
|
|
272
|
-
}
|
|
273
|
-
installParentExitHook();
|
|
274
|
-
return true;
|
|
275
|
-
}
|
|
276
|
-
|
|
277
|
-
function releaseOwner() {
|
|
278
|
-
if (!singletonEnabled || !ownerClaim?.owned) return;
|
|
279
|
-
releaseSingletonOwner(ownerPath, ownerPid);
|
|
280
|
-
ownerClaim = null;
|
|
281
|
-
ownerPid = process.pid;
|
|
282
|
-
uninstallParentExitHook();
|
|
283
|
-
}
|
|
284
|
-
|
|
285
137
|
function rejectPending(error) {
|
|
286
138
|
for (const [, item] of pending) {
|
|
287
139
|
clearTimeout(item.timer);
|
|
@@ -292,7 +144,6 @@ export function createStandaloneChannelWorker({
|
|
|
292
144
|
|
|
293
145
|
function start() {
|
|
294
146
|
if (!useProcessWorker) return startInProcess();
|
|
295
|
-
if (!ensureOwner()) return Promise.resolve(status());
|
|
296
147
|
if (stopPromise) {
|
|
297
148
|
return stopPromise.then(() => start());
|
|
298
149
|
}
|
|
@@ -306,35 +157,21 @@ export function createStandaloneChannelWorker({
|
|
|
306
157
|
cwd,
|
|
307
158
|
execArgv: ['--require', WORKER_PRELOAD],
|
|
308
159
|
stdio: ['ignore', 'ignore', 'pipe', 'ipc'],
|
|
309
|
-
detached:
|
|
160
|
+
detached: false,
|
|
310
161
|
env: {
|
|
311
162
|
...process.env,
|
|
312
163
|
MIXDOG_ROOT: rootDir,
|
|
313
164
|
MIXDOG_DATA_DIR: dataDir,
|
|
314
165
|
MIXDOG_STANDALONE: '1',
|
|
315
166
|
MIXDOG_WORKER_MODE: '1',
|
|
316
|
-
MIXDOG_CLI_OWNED:
|
|
317
|
-
MIXDOG_CHANNEL_DAEMON: daemonEnabled ? '1' : '0',
|
|
318
|
-
MIXDOG_CHANNELS_AUTO_BOOT: '0',
|
|
319
|
-
MIXDOG_CHANNEL_FLAG: '1',
|
|
167
|
+
MIXDOG_CLI_OWNED: '0',
|
|
320
168
|
MIXDOG_QUIET_SESSION_LOG: process.env.MIXDOG_QUIET_SESSION_LOG ?? '1',
|
|
321
169
|
},
|
|
322
170
|
windowsHide: true,
|
|
323
171
|
});
|
|
324
172
|
const spawnedPid = child.pid;
|
|
325
|
-
|
|
326
|
-
|
|
327
|
-
ownerClaim = claimSingletonOwner(ownerPath, {
|
|
328
|
-
kind: 'channel-worker-daemon',
|
|
329
|
-
pid: spawnedPid,
|
|
330
|
-
meta: { cwd, launcherPid: process.pid },
|
|
331
|
-
});
|
|
332
|
-
ownerPid = spawnedPid;
|
|
333
|
-
}
|
|
334
|
-
if (!daemonEnabled) {
|
|
335
|
-
startChildGuardian({ childPid: spawnedPid, label: 'channel-worker', orphanGraceMs: 8000, forceGraceMs: 3000 });
|
|
336
|
-
if (spawnedPid) ownedChildPids.add(spawnedPid);
|
|
337
|
-
}
|
|
173
|
+
startChildGuardian({ childPid: spawnedPid, label: 'channel-worker', orphanGraceMs: 8000, forceGraceMs: 3000 });
|
|
174
|
+
if (spawnedPid) ownedChildPids.add(spawnedPid);
|
|
338
175
|
installParentExitHook();
|
|
339
176
|
|
|
340
177
|
child.stderr?.on('data', (chunk) => {
|
|
@@ -360,19 +197,12 @@ export function createStandaloneChannelWorker({
|
|
|
360
197
|
return;
|
|
361
198
|
}
|
|
362
199
|
if (msg.type === 'notify') {
|
|
363
|
-
|
|
364
|
-
try { onNotify?.(msg); } catch {}
|
|
365
|
-
}
|
|
200
|
+
try { onNotify?.(msg); } catch {}
|
|
366
201
|
}
|
|
367
202
|
});
|
|
368
203
|
|
|
369
204
|
child.on('exit', (code, signal) => {
|
|
370
205
|
if (spawnedPid) ownedChildPids.delete(spawnedPid);
|
|
371
|
-
if (daemonEnabled && spawnedPid) releaseSingletonOwner(ownerPath, spawnedPid);
|
|
372
|
-
if (daemonEnabled && ownerPid === spawnedPid) {
|
|
373
|
-
ownerClaim = null;
|
|
374
|
-
ownerPid = process.pid;
|
|
375
|
-
}
|
|
376
206
|
const error = new Error(`channels runtime exited (${signal || (code ?? 'unknown')})`);
|
|
377
207
|
if (readyReject) readyReject(error);
|
|
378
208
|
readyResolve = null;
|
|
@@ -394,16 +224,12 @@ export function createStandaloneChannelWorker({
|
|
|
394
224
|
}
|
|
395
225
|
|
|
396
226
|
async function startInProcess() {
|
|
397
|
-
if (!ensureOwner()) return status();
|
|
398
227
|
if (inProcessMod) return status();
|
|
399
228
|
if (inProcessStartPromise) return inProcessStartPromise;
|
|
400
229
|
inProcessStartPromise = (async () => {
|
|
401
230
|
process.env.MIXDOG_ROOT = rootDir;
|
|
402
231
|
process.env.MIXDOG_DATA_DIR = dataDir;
|
|
403
232
|
process.env.MIXDOG_STANDALONE ??= '1';
|
|
404
|
-
process.env.MIXDOG_CHANNEL_FLAG ??= '1';
|
|
405
|
-
process.env.MIXDOG_CHANNELS_AUTO_BOOT = '0';
|
|
406
|
-
process.env.MIXDOG_CLI_OWNED = '1';
|
|
407
233
|
const mod = await import(pathToFileURL(entry).href);
|
|
408
234
|
if (typeof mod?.start !== 'function') throw new Error('channels runtime does not export start()');
|
|
409
235
|
await mod.start();
|
|
@@ -417,9 +243,6 @@ export function createStandaloneChannelWorker({
|
|
|
417
243
|
|
|
418
244
|
async function execute(name, args = {}, { timeoutMs = 120_000 } = {}) {
|
|
419
245
|
if (!CHANNEL_TOOLS.has(name)) throw new Error(`unknown channel tool: ${name}`);
|
|
420
|
-
if (!ensureOwner()) {
|
|
421
|
-
return await executeViaOwnerHttp(name, args || {}, timeoutMs);
|
|
422
|
-
}
|
|
423
246
|
await start();
|
|
424
247
|
if (!useProcessWorker) {
|
|
425
248
|
const call = inProcessMod?.handleToolCallWithBridgeRetry || inProcessMod?.handleToolCall;
|
|
@@ -453,91 +276,6 @@ export function createStandaloneChannelWorker({
|
|
|
453
276
|
});
|
|
454
277
|
}
|
|
455
278
|
|
|
456
|
-
async function executeViaOwnerHttp(name, args, timeoutMs) {
|
|
457
|
-
const active = readActiveInstance();
|
|
458
|
-
const port = parsePort(active?.httpPort);
|
|
459
|
-
const instanceId = active?.instanceId;
|
|
460
|
-
const token = readOwnerSecret(instanceId);
|
|
461
|
-
if (!port || !token) {
|
|
462
|
-
const owner = externalOwner();
|
|
463
|
-
throw new Error(`channels runtime is owned by pid ${owner?.pid || 'unknown'} but owner HTTP is unavailable`);
|
|
464
|
-
}
|
|
465
|
-
const headers = {
|
|
466
|
-
'x-owner-token': token,
|
|
467
|
-
'x-owner-instance': String(instanceId || ''),
|
|
468
|
-
};
|
|
469
|
-
const post = (path, body) => requestJson({ port, method: 'POST', path, body, headers, timeoutMs });
|
|
470
|
-
const get = (path) => requestJson({ port, method: 'GET', path, headers, timeoutMs });
|
|
471
|
-
|
|
472
|
-
switch (name) {
|
|
473
|
-
case 'reply': {
|
|
474
|
-
const result = await post('/send', {
|
|
475
|
-
chatId: args.chat_id,
|
|
476
|
-
text: args.text,
|
|
477
|
-
opts: {
|
|
478
|
-
replyTo: args.reply_to,
|
|
479
|
-
files: args.files ?? [],
|
|
480
|
-
embeds: args.embeds ?? [],
|
|
481
|
-
components: args.components ?? [],
|
|
482
|
-
},
|
|
483
|
-
});
|
|
484
|
-
const ids = Array.isArray(result?.sentIds) ? result.sentIds : [];
|
|
485
|
-
return textResult(ids.length === 1 ? `sent (id: ${ids[0]})` : `sent ${ids.length} parts (ids: ${ids.join(', ')})`);
|
|
486
|
-
}
|
|
487
|
-
case 'fetch': {
|
|
488
|
-
const channel = encodeURIComponent(String(args.channel || ''));
|
|
489
|
-
const limit = Math.max(1, Number(args.limit) || 20);
|
|
490
|
-
const result = await get(`/fetch?channel=${channel}&limit=${limit}`);
|
|
491
|
-
const msgs = Array.isArray(result?.messages) ? result.messages : [];
|
|
492
|
-
const text = msgs.length === 0 ? '(no messages)' : msgs.map((m) => {
|
|
493
|
-
const atts = Number(m.attachmentCount) > 0 ? ` +${m.attachmentCount}att` : '';
|
|
494
|
-
return `[${m.ts}] ${m.user}: ${m.text} (id: ${m.id}${atts})`;
|
|
495
|
-
}).join('\n');
|
|
496
|
-
return textResult(text);
|
|
497
|
-
}
|
|
498
|
-
case 'react':
|
|
499
|
-
await post('/react', { chatId: args.chat_id, messageId: args.message_id, emoji: args.emoji });
|
|
500
|
-
return textResult('reacted');
|
|
501
|
-
case 'edit_message': {
|
|
502
|
-
const result = await post('/edit', {
|
|
503
|
-
chatId: args.chat_id,
|
|
504
|
-
messageId: args.message_id,
|
|
505
|
-
text: args.text,
|
|
506
|
-
opts: { embeds: args.embeds ?? [], components: args.components ?? [] },
|
|
507
|
-
});
|
|
508
|
-
return textResult(`edited (id: ${result?.id ?? ''})`);
|
|
509
|
-
}
|
|
510
|
-
case 'download_attachment': {
|
|
511
|
-
const result = await post('/download', { chatId: args.chat_id, messageId: args.message_id });
|
|
512
|
-
const files = Array.isArray(result?.files) ? result.files : [];
|
|
513
|
-
if (files.length === 0) return textResult('message has no attachments');
|
|
514
|
-
return textResult(`downloaded ${files.length} attachment(s):\n${files.map((f) => ` ${f.path} (${f.name}, ${f.contentType}, ${(Number(f.size || 0) / 1024).toFixed(0)}KB)`).join('\n')}`);
|
|
515
|
-
}
|
|
516
|
-
case 'schedule_status': {
|
|
517
|
-
const result = await get('/schedule-status');
|
|
518
|
-
return result?.result ?? textResult('no schedules configured');
|
|
519
|
-
}
|
|
520
|
-
case 'trigger_schedule': {
|
|
521
|
-
const result = await post('/trigger-schedule', { name: args.name });
|
|
522
|
-
return textResult(result?.result == null ? '' : String(result.result));
|
|
523
|
-
}
|
|
524
|
-
case 'schedule_control': {
|
|
525
|
-
const result = await post('/schedule-control', { name: args.name, action: args.action, minutes: args.minutes });
|
|
526
|
-
return result?.result ?? textResult(`unknown action: ${args.action}`, true);
|
|
527
|
-
}
|
|
528
|
-
case 'activate_channel_bridge':
|
|
529
|
-
await post('/bridge/activate', { active: args.active === true });
|
|
530
|
-
return textResult(`channel bridge ${args.active ? 'activated' : 'deactivated'}`);
|
|
531
|
-
case 'inject_command':
|
|
532
|
-
await post('/inject', { content: `/${String(args.command || '').trim()}`, source: 'mixdog-agent', type: 'command' });
|
|
533
|
-
return textResult(`queued /${String(args.command || '').trim()} via channel owner`);
|
|
534
|
-
case 'reload_config':
|
|
535
|
-
return textResult('reload_config must run in the channel owner process', true);
|
|
536
|
-
default:
|
|
537
|
-
return textResult(`unknown channel tool: ${name}`, true);
|
|
538
|
-
}
|
|
539
|
-
}
|
|
540
|
-
|
|
541
279
|
function forceKillTree(pid) {
|
|
542
280
|
if (!pid) return;
|
|
543
281
|
if (process.platform === 'win32') {
|
|
@@ -565,11 +303,8 @@ export function createStandaloneChannelWorker({
|
|
|
565
303
|
if (parentExitCleanup) return;
|
|
566
304
|
parentExitCleanup = registerChannelWorkerExitCleanup(() => {
|
|
567
305
|
parentExitCleanup = null;
|
|
568
|
-
|
|
569
|
-
|
|
570
|
-
forceKillTreeSync(pid);
|
|
571
|
-
}
|
|
572
|
-
releaseOwner();
|
|
306
|
+
for (const pid of Array.from(ownedChildPids)) {
|
|
307
|
+
forceKillTreeSync(pid);
|
|
573
308
|
}
|
|
574
309
|
ownedChildPids.clear();
|
|
575
310
|
});
|
|
@@ -592,12 +327,10 @@ export function createStandaloneChannelWorker({
|
|
|
592
327
|
|
|
593
328
|
function stop(reason = 'standalone shutdown', options = {}) {
|
|
594
329
|
const waitForExit = options?.waitForExit !== false;
|
|
595
|
-
stopNotifyBus();
|
|
596
330
|
stopClientHeartbeat();
|
|
597
331
|
if (stopPromise) return stopPromise;
|
|
598
332
|
if (!useProcessWorker) {
|
|
599
333
|
if (!inProcessMod && !inProcessStartPromise) {
|
|
600
|
-
releaseOwner();
|
|
601
334
|
return Promise.resolve(false);
|
|
602
335
|
}
|
|
603
336
|
stopPromise = Promise.resolve(inProcessStartPromise)
|
|
@@ -605,7 +338,6 @@ export function createStandaloneChannelWorker({
|
|
|
605
338
|
.then(async () => {
|
|
606
339
|
try { await inProcessMod?.stop?.(reason); } catch {}
|
|
607
340
|
inProcessMod = null;
|
|
608
|
-
releaseOwner();
|
|
609
341
|
return true;
|
|
610
342
|
})
|
|
611
343
|
.finally(() => {
|
|
@@ -614,23 +346,11 @@ export function createStandaloneChannelWorker({
|
|
|
614
346
|
return stopPromise;
|
|
615
347
|
}
|
|
616
348
|
if (!child) {
|
|
617
|
-
stopNotifyBus();
|
|
618
|
-
releaseOwner();
|
|
619
349
|
return Promise.resolve(false);
|
|
620
350
|
}
|
|
621
351
|
const target = child;
|
|
622
352
|
const targetPid = target.pid;
|
|
623
353
|
child = null;
|
|
624
|
-
if (daemonEnabled && options?.force !== true) {
|
|
625
|
-
rejectPending(new Error(`channels runtime detached (${reason})`));
|
|
626
|
-
if (targetPid) ownedChildPids.delete(targetPid);
|
|
627
|
-
try { target.disconnect?.(); } catch {}
|
|
628
|
-
unrefChildHandles(target);
|
|
629
|
-
ownerClaim = null;
|
|
630
|
-
ownerPid = process.pid;
|
|
631
|
-
uninstallParentExitHook();
|
|
632
|
-
return Promise.resolve(true);
|
|
633
|
-
}
|
|
634
354
|
if (!waitForExit) {
|
|
635
355
|
rejectPending(new Error(`channels runtime shutdown requested (${reason})`));
|
|
636
356
|
if (targetPid) ownedChildPids.delete(targetPid);
|
|
@@ -642,7 +362,6 @@ export function createStandaloneChannelWorker({
|
|
|
642
362
|
clearTimeout(sendTimer);
|
|
643
363
|
stopPromise = null;
|
|
644
364
|
unrefChildHandles(target);
|
|
645
|
-
releaseOwner();
|
|
646
365
|
uninstallParentExitHook();
|
|
647
366
|
resolve(ok);
|
|
648
367
|
};
|
|
@@ -667,8 +386,6 @@ export function createStandaloneChannelWorker({
|
|
|
667
386
|
clearTimeout(termTimer);
|
|
668
387
|
clearTimeout(killTimer);
|
|
669
388
|
stopPromise = null;
|
|
670
|
-
stopNotifyBus();
|
|
671
|
-
releaseOwner();
|
|
672
389
|
uninstallParentExitHook();
|
|
673
390
|
resolve(ok);
|
|
674
391
|
};
|
|
@@ -14,7 +14,7 @@ export const EXPLORE_TOOL = {
|
|
|
14
14
|
name: 'explore',
|
|
15
15
|
title: 'Explore',
|
|
16
16
|
annotations: { title: 'Explore', readOnlyHint: true, destructiveHint: false, idempotentHint: true, openWorldHint: false },
|
|
17
|
-
description: 'Repo anchor locator.
|
|
17
|
+
description: 'Repo anchor locator. LLM-backed; broad/uncertain only. Array only for independent targets.',
|
|
18
18
|
inputSchema: {
|
|
19
19
|
type: 'object',
|
|
20
20
|
properties: {
|
|
@@ -305,7 +305,7 @@ async function runExploreSync(args = {}, ctx = {}) {
|
|
|
305
305
|
await ensureExploreProviderReady(config);
|
|
306
306
|
const presetName = config?.maintenance?.explore || '';
|
|
307
307
|
const llm = makeAgentDispatch({
|
|
308
|
-
|
|
308
|
+
agent: 'explorer',
|
|
309
309
|
cwd: resolvedCwd,
|
|
310
310
|
brief: true,
|
|
311
311
|
parentSessionId: ctx.callerSessionId || null,
|
|
@@ -188,12 +188,14 @@ export function createStandaloneMemoryRuntime({
|
|
|
188
188
|
releaseOwnerIfSelf();
|
|
189
189
|
}
|
|
190
190
|
|
|
191
|
+
const daemonEnv = { ...process.env };
|
|
192
|
+
delete daemonEnv.MIXDOG_QUIET_MEMORY_LOG;
|
|
191
193
|
child = fork(entry, [], {
|
|
192
194
|
cwd,
|
|
193
195
|
stdio: ['ignore', 'ignore', 'pipe', 'ipc'],
|
|
194
196
|
detached: true,
|
|
195
197
|
env: {
|
|
196
|
-
...
|
|
198
|
+
...daemonEnv,
|
|
197
199
|
MIXDOG_DATA_DIR: dataDir,
|
|
198
200
|
MIXDOG_WORKER_MODE: '1',
|
|
199
201
|
MIXDOG_STANDALONE: '1',
|
|
@@ -52,6 +52,17 @@ const API_PROVIDER_IDS = new Set(API_PROVIDERS.map((p) => p.id));
|
|
|
52
52
|
const OAUTH_BY_ID = new Map(OAUTH_PROVIDERS.map((p) => [p.id, p]));
|
|
53
53
|
const LOCAL_BY_ID = new Map(LOCAL_PROVIDERS.map((p) => [p.id, p]));
|
|
54
54
|
|
|
55
|
+
const ALL_PROVIDER_IDS = new Set([
|
|
56
|
+
...API_PROVIDERS.map((p) => p.id),
|
|
57
|
+
...OAUTH_PROVIDERS.map((p) => p.id),
|
|
58
|
+
...LOCAL_PROVIDERS.map((p) => p.id),
|
|
59
|
+
]);
|
|
60
|
+
|
|
61
|
+
export function isKnownProvider(provider) {
|
|
62
|
+
const id = String(provider || '').trim();
|
|
63
|
+
return id !== '' && ALL_PROVIDER_IDS.has(id);
|
|
64
|
+
}
|
|
65
|
+
|
|
55
66
|
async function detectLocalProvider(baseURL) {
|
|
56
67
|
const url = String(baseURL || '').replace(/\/+$/, '') + '/models';
|
|
57
68
|
const controller = new AbortController();
|
|
@@ -29,7 +29,7 @@ function clean(value) {
|
|
|
29
29
|
|
|
30
30
|
function money(value) {
|
|
31
31
|
const n = Number(value);
|
|
32
|
-
if (!Number.isFinite(n)) return '
|
|
32
|
+
if (!Number.isFinite(n)) return 'N/A';
|
|
33
33
|
if (n === 0) return '$0';
|
|
34
34
|
if (n >= 10) return `$${n.toFixed(0)}`;
|
|
35
35
|
if (n >= 1) return `$${n.toFixed(2)}`;
|