mixdog 0.9.147 → 0.9.148
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 +23 -3
- package/scripts/build-voice-runtime-unix.sh +101 -0
- package/scripts/embedding-quality-probe.mjs +96 -0
- package/scripts/generate-voice-runtime-manifest.mjs +300 -0
- package/scripts/generate-voice-runtime-manifest.test.mjs +135 -0
- package/scripts/native-binary-arch.mjs +30 -13
- package/scripts/native-binary-arch.test.mjs +40 -4
- package/scripts/plan-voice-runtime-release.mjs +73 -0
- package/scripts/prepare-native-assets.mjs +50 -9
- package/scripts/prune-embedding-runtime.mjs +3 -3
- package/scripts/sync-voice-runtime-manifest.mjs +46 -0
- package/scripts/verify-release-assets.mjs +106 -6
- package/scripts/verify-voice-runtime-release.mjs +89 -0
- package/scripts/voice-runtime-config.json +52 -0
- package/src/app.mjs +42 -5
- package/src/headless-exec.mjs +58 -32
- package/src/headless-exec.test.mjs +74 -13
- package/src/rules/lead/01-general.md +2 -0
- package/src/rules/shared/10-tool-workflow.md +7 -4
- package/src/rules/shared/30-exploration.md +2 -1
- package/src/runtime/agent/orchestrator/agent-runtime/cache-strategy.mjs +28 -9
- package/src/runtime/agent/orchestrator/agent-trace-format.mjs +6 -1
- package/src/runtime/agent/orchestrator/config.mjs +53 -18
- package/src/runtime/agent/orchestrator/mcp/cancellation.test.mjs +139 -0
- package/src/runtime/agent/orchestrator/mcp/child-tree.mjs +34 -5
- package/src/runtime/agent/orchestrator/mcp/client.mjs +94 -10
- package/src/runtime/agent/orchestrator/providers/anthropic-betas.mjs +5 -0
- package/src/runtime/agent/orchestrator/providers/anthropic-fast-mode.mjs +98 -0
- package/src/runtime/agent/orchestrator/providers/anthropic-fast-mode.test.mjs +90 -0
- package/src/runtime/agent/orchestrator/providers/anthropic-leaked-toolcall.mjs +67 -16
- package/src/runtime/agent/orchestrator/providers/anthropic-messages.mjs +4 -82
- package/src/runtime/agent/orchestrator/providers/anthropic-oauth-credentials.mjs +0 -5
- package/src/runtime/agent/orchestrator/providers/anthropic-oauth.mjs +62 -121
- package/src/runtime/agent/orchestrator/providers/anthropic-server-fallback.mjs +48 -0
- package/src/runtime/agent/orchestrator/providers/anthropic-server-fallback.test.mjs +215 -0
- package/src/runtime/agent/orchestrator/providers/anthropic-sse.mjs +44 -48
- package/src/runtime/agent/orchestrator/providers/anthropic.mjs +54 -31
- package/src/runtime/agent/orchestrator/providers/antigravity-oauth.mjs +56 -2
- package/src/runtime/agent/orchestrator/providers/cursor-auth.mjs +34 -2
- package/src/runtime/agent/orchestrator/providers/cursor-wire.mjs +61 -1
- package/src/runtime/agent/orchestrator/providers/gemini-stream.mjs +6 -1
- package/src/runtime/agent/orchestrator/providers/gemini.mjs +61 -15
- package/src/runtime/agent/orchestrator/providers/lib/anthropic-request-utils.mjs +120 -1
- package/src/runtime/agent/orchestrator/providers/lib/env-utils.mjs +9 -0
- package/src/runtime/agent/orchestrator/providers/lib/stream-outcome.mjs +0 -3
- package/src/runtime/agent/orchestrator/providers/oauth-credential-probes.mjs +187 -84
- package/src/runtime/agent/orchestrator/providers/openai-compat-stream.mjs +36 -36
- package/src/runtime/agent/orchestrator/providers/openai-compat-wire.mjs +16 -6
- package/src/runtime/agent/orchestrator/providers/openai-compat-xai.mjs +50 -213
- package/src/runtime/agent/orchestrator/providers/openai-compat.mjs +135 -126
- package/src/runtime/agent/orchestrator/providers/openai-oauth-http-sse.mjs +17 -11
- package/src/runtime/agent/orchestrator/providers/openai-oauth-ws.mjs +20 -7
- package/src/runtime/agent/orchestrator/providers/openai-ws-delta.mjs +11 -0
- package/src/runtime/agent/orchestrator/providers/openai-ws-pool.mjs +132 -42
- package/src/runtime/agent/orchestrator/providers/openai-ws-stream.mjs +2 -2
- package/src/runtime/agent/orchestrator/providers/openai-ws.mjs +28 -10
- package/src/runtime/agent/orchestrator/providers/registry.mjs +122 -95
- package/src/runtime/agent/orchestrator/providers/retry-classifier.mjs +58 -3
- package/src/runtime/agent/orchestrator/providers/tool-stream-state.mjs +4 -22
- package/src/runtime/agent/orchestrator/session/agent-loop.mjs +7 -14
- package/src/runtime/agent/orchestrator/session/compact-policy.test.mjs +128 -0
- package/src/runtime/agent/orchestrator/session/context-compaction-policy.mjs +5 -0
- package/src/runtime/agent/orchestrator/session/loop/compact-policy.mjs +27 -48
- package/src/runtime/agent/orchestrator/session/loop/steering.mjs +19 -14
- package/src/runtime/agent/orchestrator/session/loop/stored-tool-args.mjs +9 -43
- package/src/runtime/agent/orchestrator/session/loop/tool-exec.mjs +5 -1
- package/src/runtime/agent/orchestrator/session/manager/compaction-runner.mjs +15 -2
- package/src/runtime/agent/orchestrator/session/manager/pending-messages-persistence.test.mjs +325 -0
- package/src/runtime/agent/orchestrator/session/manager/pending-messages.mjs +599 -195
- package/src/runtime/agent/orchestrator/session/manager/session-close.mjs +22 -0
- package/src/runtime/agent/orchestrator/session/manager/session-crud.mjs +9 -2
- package/src/runtime/agent/orchestrator/session/manager.mjs +3 -0
- package/src/runtime/agent/orchestrator/session/pre-send-compact.mjs +26 -16
- package/src/runtime/agent/orchestrator/session/send-with-recovery.mjs +23 -11
- package/src/runtime/agent/orchestrator/session/store-summary-reader.mjs +118 -3
- package/src/runtime/agent/orchestrator/session/store-summary-reader.test.mjs +332 -0
- package/src/runtime/agent/orchestrator/session/store.mjs +38 -1
- package/src/runtime/agent/orchestrator/tools/builtin/advisory-lock.mjs +27 -6
- package/src/runtime/agent/orchestrator/tools/builtin/arg-guard.mjs +12 -20
- package/src/runtime/agent/orchestrator/tools/builtin/bash-tool.mjs +291 -128
- package/src/runtime/agent/orchestrator/tools/builtin/builtin-tools.mjs +45 -8
- package/src/runtime/agent/orchestrator/tools/builtin/edit-encoding-eol.test.mjs +224 -0
- package/src/runtime/agent/orchestrator/tools/builtin/edit-sequential-occupation.test.mjs +53 -3
- package/src/runtime/agent/orchestrator/tools/builtin/external-tool-adapters.mjs +255 -25
- package/src/runtime/agent/orchestrator/tools/builtin/find-search-budget.test.mjs +87 -0
- package/src/runtime/agent/orchestrator/tools/builtin/git-command-policy.mjs +1 -1
- package/src/runtime/agent/orchestrator/tools/builtin/git-command-tool.mjs +132 -9
- package/src/runtime/agent/orchestrator/tools/builtin/git-command-tool.test.mjs +10 -0
- package/src/runtime/agent/orchestrator/tools/builtin/lib/grep-pattern-fanout.mjs +29 -3
- package/src/runtime/agent/orchestrator/tools/builtin/lib/shell-job-records.mjs +1 -1
- package/src/runtime/agent/orchestrator/tools/builtin/list-tool.mjs +178 -29
- package/src/runtime/agent/orchestrator/tools/builtin/local-search-telemetry.mjs +4 -1
- package/src/runtime/agent/orchestrator/tools/builtin/native-search-client.mjs +32 -5
- package/src/runtime/agent/orchestrator/tools/builtin/native-search-health.test.mjs +5 -2
- package/src/runtime/agent/orchestrator/tools/builtin/native-search-runner.mjs +10 -1
- package/src/runtime/agent/orchestrator/tools/builtin/read-batch.mjs +38 -10
- package/src/runtime/agent/orchestrator/tools/builtin/read-single-tool.mjs +12 -0
- package/src/runtime/agent/orchestrator/tools/builtin/read-snapshot-runtime.mjs +31 -1
- package/src/runtime/agent/orchestrator/tools/builtin/read-tool.mjs +11 -1
- package/src/runtime/agent/orchestrator/tools/builtin/read-windows.mjs +22 -4
- package/src/runtime/agent/orchestrator/tools/builtin/runtime-capabilities.mjs +6 -8
- package/src/runtime/agent/orchestrator/tools/builtin/shell-analysis.mjs +25 -24
- package/src/runtime/agent/orchestrator/tools/builtin/shell-jobs.mjs +152 -133
- package/src/runtime/agent/orchestrator/tools/builtin/task-tool.mjs +114 -42
- package/src/runtime/agent/orchestrator/tools/code-graph/dispatch.mjs +129 -15
- package/src/runtime/agent/orchestrator/tools/code-graph/loose-file-target.test.mjs +72 -0
- package/src/runtime/agent/orchestrator/tools/code-graph/project-root.mjs +7 -8
- package/src/runtime/agent/orchestrator/tools/code-graph/search-references.mjs +4 -4
- package/src/runtime/agent/orchestrator/tools/code-graph/search.mjs +201 -19
- package/src/runtime/agent/orchestrator/tools/code-graph/span.mjs +26 -3
- package/src/runtime/agent/orchestrator/tools/code-graph/symbol-index.mjs +10 -1
- package/src/runtime/agent/orchestrator/tools/code-graph/symbol-search-scope.test.mjs +55 -0
- package/src/runtime/agent/orchestrator/tools/code-graph/text-mask.mjs +115 -25
- package/src/runtime/agent/orchestrator/tools/code-graph/text-span-mask.test.mjs +268 -0
- package/src/runtime/agent/orchestrator/tools/code-graph-tool-defs.mjs +3 -3
- package/src/runtime/agent/orchestrator/tools/lib/native-spawn-client.mjs +296 -46
- package/src/runtime/agent/orchestrator/tools/lib/native-spawn-client.test.mjs +104 -0
- package/src/runtime/agent/orchestrator/tools/lib/shell-descendants.mjs +136 -0
- package/src/runtime/agent/orchestrator/tools/patch/dispatch.mjs +127 -38
- package/src/runtime/agent/orchestrator/tools/patch/engine-contract-gate.test.mjs +661 -0
- package/src/runtime/agent/orchestrator/tools/patch/matcher.mjs +350 -9
- package/src/runtime/agent/orchestrator/tools/patch/native-server.mjs +418 -44
- package/src/runtime/agent/orchestrator/tools/patch/orchestrator.mjs +27 -3
- package/src/runtime/agent/orchestrator/tools/patch/patch-codec-eol.test.mjs +552 -0
- package/src/runtime/agent/orchestrator/tools/patch/v4a-convert.mjs +65 -15
- package/src/runtime/agent/orchestrator/tools/patch-manifest.json +11 -11
- package/src/runtime/agent/orchestrator/tools/shell-command.mjs +284 -109
- package/src/runtime/agent/orchestrator/tools/shell-exec-output.mjs +51 -21
- package/src/runtime/agent/orchestrator/tools/shell-exec-policy.mjs +44 -27
- package/src/runtime/agent/orchestrator/tools/shell-policy.mjs +6 -1
- package/src/runtime/agent/orchestrator/tools/spawn-manifest.json +11 -11
- package/src/runtime/attachments/store.mjs +11 -3
- package/src/runtime/channels/data/voice-runtime-manifest.json +46 -70
- package/src/runtime/channels/lib/event-queue.mjs +51 -41
- package/src/runtime/channels/lib/scheduler.mjs +23 -28
- package/src/runtime/channels/lib/voice-runtime-fetcher.mjs +6 -17
- package/src/runtime/channels/lib/voice-runtime-fetcher.test.mjs +17 -0
- package/src/runtime/channels/lib/webhook.mjs +39 -20
- package/src/runtime/channels/lib/whisper-server.mjs +46 -1
- package/src/runtime/channels/lib/worker-main.mjs +3 -1
- package/src/runtime/media/adapters/gemini-video.mjs +20 -4
- package/src/runtime/media/jobs.mjs +13 -0
- package/src/runtime/media/renditions.mjs +7 -1
- package/src/runtime/media/store.mjs +42 -22
- package/src/runtime/memory/index.mjs +4 -9
- package/src/runtime/memory/lib/agent-ipc.mjs +1 -6
- package/src/runtime/memory/lib/archive-security.test.mjs +21 -1
- package/src/runtime/memory/lib/core-memory-store.mjs +28 -50
- package/src/runtime/memory/lib/embedding-model-config.mjs +37 -1
- package/src/runtime/memory/lib/embedding-model-config.test.mjs +33 -0
- package/src/runtime/memory/lib/embedding-provider.mjs +34 -12
- package/src/runtime/memory/lib/embedding-worker.mjs +37 -5
- package/src/runtime/memory/lib/ko-morph.mjs +46 -15
- package/src/runtime/memory/lib/memory-action-handlers.mjs +18 -1
- package/src/runtime/memory/lib/memory-cycle1.mjs +1 -18
- package/src/runtime/memory/lib/memory-cycle2-gate.mjs +1 -18
- package/src/runtime/memory/lib/memory-cycle2-mutations.mjs +98 -45
- package/src/runtime/memory/lib/memory-cycle2-shared.mjs +71 -0
- package/src/runtime/memory/lib/memory-cycle2-shared.test.mjs +78 -0
- package/src/runtime/memory/lib/memory-cycle2.mjs +37 -3
- package/src/runtime/memory/lib/memory-cycle3.mjs +1 -1
- package/src/runtime/memory/lib/memory-embedding-migration.test.mjs +50 -0
- package/src/runtime/memory/lib/memory-recall-store.mjs +9 -24
- package/src/runtime/memory/lib/memory.mjs +50 -12
- package/src/runtime/memory/lib/pg/process.mjs +1 -10
- package/src/runtime/memory/lib/pg/supervisor.mjs +1 -9
- package/src/runtime/memory/lib/query-handlers.mjs +2 -4
- package/src/runtime/memory/lib/session-ingest-runtime.mjs +2 -2
- package/src/runtime/shared/background-tasks.mjs +15 -0
- package/src/runtime/shared/background-tasks.test.mjs +78 -0
- package/src/runtime/shared/child-guardian.mjs +2 -10
- package/src/runtime/shared/pid-liveness.mjs +27 -0
- package/src/runtime/shared/runtime-root.mjs +1 -11
- package/src/runtime/shared/service-discovery.mjs +1 -17
- package/src/runtime/shared/singleton-owner.mjs +1 -11
- package/src/runtime/shared/staged-update.mjs +2 -12
- package/src/runtime/shared/tool-status.mjs +5 -1
- package/src/runtime/shared/tool-surface.mjs +15 -7
- package/src/runtime/shared/turn-snapshot-store.mjs +168 -0
- package/src/runtime/shared/turn-snapshot.mjs +137 -13
- package/src/runtime/shared/turn-worktree-snapshot.mjs +110 -18
- package/src/runtime/shared/webhook-session-run.mjs +16 -2
- package/src/runtime/web-search/lib/http-fetch.mjs +34 -4
- package/src/runtime/web-search/lib/state.mjs +15 -15
- package/src/runtime/web-search/lib/web-tools.mjs +46 -5
- package/src/session-runtime/context-status.mjs +4 -12
- package/src/session-runtime/runtime-core.mjs +20 -1
- package/src/standalone/agent-dispatch-broker.mjs +33 -2
- package/src/standalone/agent-tool/job-task-reconcile.mjs +78 -0
- package/src/standalone/agent-tool/notify.mjs +3 -1
- package/src/standalone/agent-tool/spawn-flow.mjs +48 -66
- package/src/standalone/agent-tool/tag-registry.mjs +116 -33
- package/src/standalone/agent-tool/tool-def.mjs +2 -2
- package/src/standalone/agent-tool/worker-index.mjs +8 -11
- package/src/standalone/agent-tool/worker-rows.mjs +4 -1
- package/src/standalone/agent-tool.mjs +20 -55
- package/src/standalone/agent-watchdog-registry.mjs +28 -4
- package/src/standalone/channel-client.mjs +47 -15
- package/src/standalone/channel-transport.mjs +44 -25
- package/src/standalone/daemon.mjs +31 -2
- package/src/standalone/hook-bus/config.mjs +25 -9
- package/src/standalone/hook-bus/handlers.mjs +16 -52
- package/src/standalone/hook-bus.mjs +29 -23
- package/src/standalone/memory-runtime-proxy.mjs +1 -16
- package/src/standalone/session-client.mjs +86 -23
- package/src/standalone/session-protocol.mjs +0 -2
- package/src/standalone/session-runtime-host-health.test.mjs +5 -4
- package/src/standalone/session-runtime-host.mjs +101 -6
- package/src/standalone/session-runtime-record.mjs +1 -5
- package/src/standalone/session-runtime-worker.mjs +52 -57
- package/src/standalone/session-service.mjs +4 -1
- package/src/standalone/session-transport.mjs +56 -25
- package/src/tui/App.jsx +40 -49
- package/src/tui/app/app-view.jsx +14 -21
- package/src/tui/app/core-memory-picker.mjs +29 -16
- package/src/tui/app/create-app-pickers.mjs +15 -36
- package/src/tui/app/extension-pickers.mjs +60 -49
- package/src/tui/app/maintenance-pickers.mjs +100 -57
- package/src/tui/app/message-selector.mjs +6 -5
- package/src/tui/app/model-picker.mjs +30 -13
- package/src/tui/app/onboarding-steps.mjs +21 -12
- package/src/tui/app/panel-epoch.mjs +56 -0
- package/src/tui/app/panel-epoch.test.mjs +1009 -0
- package/src/tui/app/panel-surface.mjs +98 -0
- package/src/tui/app/project-picker.mjs +30 -29
- package/src/tui/app/prompt-submit.mjs +192 -97
- package/src/tui/app/prompt-submit.test.mjs +206 -0
- package/src/tui/app/provider-setup-picker.mjs +74 -43
- package/src/tui/app/resume-picker.mjs +11 -8
- package/src/tui/app/route-pickers.mjs +45 -28
- package/src/tui/app/settings-picker.mjs +69 -18
- package/src/tui/app/shell-layout.mjs +1 -3
- package/src/tui/app/slash-commands.mjs +0 -2
- package/src/tui/app/slash-dispatch.mjs +4 -2
- package/src/tui/app/text-entry-policy.mjs +25 -0
- package/src/tui/app/theme-effort-pickers.mjs +11 -16
- package/src/tui/app/usage-context-panels.mjs +21 -25
- package/src/tui/app/use-global-key-input.mjs +3 -2
- package/src/tui/app/use-prompt-draft-flow.mjs +6 -18
- package/src/tui/app/use-prompt-handlers.mjs +3 -2
- package/src/tui/app/use-welcome-prompt-hint.mjs +17 -3
- package/src/tui/components/Picker.jsx +1 -1
- package/src/tui/components/TextEntryPanel.jsx +7 -2
- package/src/tui/dist/index.mjs +652 -561
- package/src/tui/lib/voice-setup.mjs +1 -2
- package/src/tui/session/agent-job-feed.mjs +73 -11
- package/src/tui/session/session-api-ext.mjs +5 -6
- package/src/tui/session/session-api.mjs +1 -0
- package/src/tui/session/tool-approval.mjs +15 -5
- package/src/tui/session/tui-steering-persist.mjs +33 -19
- package/src/tui/session/tui-steering-persist.test.mjs +104 -0
- package/src/tui/session-local.mjs +0 -22
- package/scripts/.tmp-coverage-report.mjs +0 -96
- package/src/runtime/agent/orchestrator/tools/builtin/shell-monitor.mjs +0 -57
- package/src/runtime/agent/orchestrator/tools/builtin/shell-monitor.test.mjs +0 -57
|
@@ -26,6 +26,14 @@ export const SPAWN_PLATFORMS = {
|
|
|
26
26
|
'win32-x64': 'mixdog-spawn-win32-x64.exe',
|
|
27
27
|
};
|
|
28
28
|
|
|
29
|
+
export const TOKEN_PLATFORMS = {
|
|
30
|
+
'darwin-arm64': 'mixdog-token-darwin-arm64.node',
|
|
31
|
+
'darwin-x64': 'mixdog-token-darwin-x64.node',
|
|
32
|
+
'linux-arm64': 'mixdog-token-linux-arm64.node',
|
|
33
|
+
'linux-x64': 'mixdog-token-linux-x64.node',
|
|
34
|
+
'win32-x64': 'mixdog-token-win32-x64.node',
|
|
35
|
+
};
|
|
36
|
+
|
|
29
37
|
const RUNTIME_PLATFORMS = [
|
|
30
38
|
'linux-x64',
|
|
31
39
|
'linux-arm64',
|
|
@@ -43,6 +51,8 @@ const RUNTIME_MANIFEST_PATH = 'src/runtime/memory/data/runtime-manifest.json';
|
|
|
43
51
|
const GRAPH_MANIFEST_PATH = 'src/runtime/agent/orchestrator/tools/graph-manifest.json';
|
|
44
52
|
const SPAWN_MANIFEST_PATH = 'src/runtime/agent/orchestrator/tools/spawn-manifest.json';
|
|
45
53
|
const SPAWN_CARGO_PATH = 'native/mixdog-spawn/Cargo.toml';
|
|
54
|
+
const TOKEN_MANIFEST_PATH = 'src/runtime/agent/orchestrator/tools/token-manifest.json';
|
|
55
|
+
const TOKEN_CARGO_PATH = 'native/mixdog-token/Cargo.toml';
|
|
46
56
|
const PACKAGE_PATH = 'package.json';
|
|
47
57
|
|
|
48
58
|
function assertPlainObject(value, label) {
|
|
@@ -125,14 +135,43 @@ export function validatePatchManifest(manifest, cargoToml) {
|
|
|
125
135
|
|
|
126
136
|
export function validateRuntimeManifest(manifest) {
|
|
127
137
|
assertPlainObject(manifest, 'Runtime manifest');
|
|
128
|
-
|
|
129
|
-
|
|
138
|
+
assertExactKeys(
|
|
139
|
+
manifest,
|
|
140
|
+
['schema_version', 'generated_at', 'release_tag', 'pg', 'pgvector', 'assets'],
|
|
141
|
+
'Runtime manifest',
|
|
142
|
+
);
|
|
143
|
+
if (manifest.schema_version !== 1) {
|
|
144
|
+
throw new Error('Runtime manifest schema_version must be 1');
|
|
145
|
+
}
|
|
146
|
+
if (typeof manifest.generated_at !== 'string' || !manifest.generated_at) {
|
|
147
|
+
throw new Error('Runtime manifest generated_at must be a non-empty string');
|
|
148
|
+
}
|
|
149
|
+
if (typeof manifest.release_tag !== 'string' || !/^runtime-v\d+\.\d+\.\d+$/.test(manifest.release_tag)) {
|
|
150
|
+
throw new Error(`Runtime manifest release_tag is invalid: ${manifest.release_tag}`);
|
|
151
|
+
}
|
|
152
|
+
assertPlainObject(manifest.pg, 'Runtime manifest pg');
|
|
153
|
+
assertExactKeys(manifest.pg, ['major', 'minor'], 'Runtime manifest pg');
|
|
154
|
+
if (!Number.isSafeInteger(manifest.pg.major) || manifest.pg.major < 1) {
|
|
155
|
+
throw new Error('Runtime manifest pg.major is invalid');
|
|
156
|
+
}
|
|
157
|
+
if (!Number.isSafeInteger(manifest.pg.minor) || manifest.pg.minor < 0) {
|
|
158
|
+
throw new Error('Runtime manifest pg.minor is invalid');
|
|
159
|
+
}
|
|
160
|
+
assertPlainObject(manifest.pgvector, 'Runtime manifest pgvector');
|
|
161
|
+
assertExactKeys(manifest.pgvector, ['version'], 'Runtime manifest pgvector');
|
|
162
|
+
if (typeof manifest.pgvector.version !== 'string' || !STRICT_VERSION.test(manifest.pgvector.version)) {
|
|
163
|
+
throw new Error(`Runtime manifest pgvector.version is invalid: ${manifest.pgvector.version}`);
|
|
130
164
|
}
|
|
131
165
|
assertPlainObject(manifest.assets, 'Runtime manifest assets');
|
|
132
166
|
assertExactKeys(manifest.assets, RUNTIME_PLATFORMS, 'Runtime manifest assets');
|
|
133
|
-
for (const
|
|
134
|
-
|
|
135
|
-
|
|
167
|
+
for (const platform of RUNTIME_PLATFORMS) {
|
|
168
|
+
const asset = manifest.assets[platform];
|
|
169
|
+
assertPlainObject(asset, `Runtime asset ${platform}`);
|
|
170
|
+
assertExactKeys(asset, ['url', 'sha256', 'size'], `Runtime asset ${platform}`);
|
|
171
|
+
const filename = `mixdog-runtime-${platform}-pg${manifest.pg.major}.${manifest.pg.minor}-pgvector${manifest.pgvector.version}.tar.gz`;
|
|
172
|
+
const expected = `https://github.com/tribgames/mixdog/releases/download/${manifest.release_tag}/${filename}`;
|
|
173
|
+
if (asset.url !== expected) {
|
|
174
|
+
throw new Error(`${platform}: runtime asset URL must be ${expected}`);
|
|
136
175
|
}
|
|
137
176
|
if (typeof asset.sha256 !== 'string' || !SHA256.test(asset.sha256)) {
|
|
138
177
|
throw new Error(`${platform}: invalid sha256`);
|
|
@@ -219,6 +258,50 @@ export function validateSpawnManifest(manifest, cargoToml) {
|
|
|
219
258
|
return manifest;
|
|
220
259
|
}
|
|
221
260
|
|
|
261
|
+
export function validateTokenManifest(manifest, cargoToml) {
|
|
262
|
+
assertPlainObject(manifest, 'Token manifest');
|
|
263
|
+
assertExactKeys(manifest, ['version', '_comment', 'assets'], 'Token manifest');
|
|
264
|
+
if (typeof manifest.version !== 'string' || !STRICT_VERSION.test(manifest.version)) {
|
|
265
|
+
throw new Error(`Token manifest version is not strict MAJOR.MINOR.PATCH: ${manifest.version}`);
|
|
266
|
+
}
|
|
267
|
+
if (typeof manifest._comment !== 'string' || !manifest._comment) {
|
|
268
|
+
throw new Error('Token manifest _comment must be a non-empty string');
|
|
269
|
+
}
|
|
270
|
+
let inPackage = false;
|
|
271
|
+
let cargoVersion;
|
|
272
|
+
for (const line of String(cargoToml).split(/\r?\n/)) {
|
|
273
|
+
const section = line.match(/^\s*\[([^\]]+)\]\s*$/)?.[1];
|
|
274
|
+
if (section) {
|
|
275
|
+
inPackage = section === 'package';
|
|
276
|
+
continue;
|
|
277
|
+
}
|
|
278
|
+
if (inPackage) {
|
|
279
|
+
const version = line.match(/^\s*version\s*=\s*"([^"]+)"\s*$/)?.[1];
|
|
280
|
+
if (version) {
|
|
281
|
+
cargoVersion = version;
|
|
282
|
+
break;
|
|
283
|
+
}
|
|
284
|
+
}
|
|
285
|
+
}
|
|
286
|
+
if (!cargoVersion) throw new Error('Could not read [package] version from native/mixdog-token/Cargo.toml');
|
|
287
|
+
if (cargoVersion !== manifest.version) {
|
|
288
|
+
throw new Error(`Token Cargo version ${cargoVersion} does not match manifest version ${manifest.version}`);
|
|
289
|
+
}
|
|
290
|
+
assertPlainObject(manifest.assets, 'Token manifest assets');
|
|
291
|
+
assertExactKeys(manifest.assets, Object.keys(TOKEN_PLATFORMS), 'Token manifest assets');
|
|
292
|
+
for (const [platform, filename] of Object.entries(TOKEN_PLATFORMS)) {
|
|
293
|
+
const asset = manifest.assets[platform];
|
|
294
|
+
assertPlainObject(asset, `Token asset ${platform}`);
|
|
295
|
+
assertExactKeys(asset, ['url', 'sha256'], `Token asset ${platform}`);
|
|
296
|
+
const expected = `https://github.com/tribgames/mixdog/releases/download/token-v${manifest.version}/${filename}`;
|
|
297
|
+
if (asset.url !== expected) throw new Error(`${platform}: token asset URL must be ${expected}`);
|
|
298
|
+
if (typeof asset.sha256 !== 'string' || !SHA256.test(asset.sha256)) {
|
|
299
|
+
throw new Error(`${platform}: invalid token asset sha256`);
|
|
300
|
+
}
|
|
301
|
+
}
|
|
302
|
+
return manifest;
|
|
303
|
+
}
|
|
304
|
+
|
|
222
305
|
async function downloadAndVerify(label, asset, fetchImpl, timeoutMs, maxAssetBytes) {
|
|
223
306
|
console.log(`Downloading and verifying ${label}`);
|
|
224
307
|
const controller = new AbortController();
|
|
@@ -307,22 +390,37 @@ export async function verifyReleaseAssets({
|
|
|
307
390
|
graphManifestPath = GRAPH_MANIFEST_PATH,
|
|
308
391
|
spawnManifestPath = SPAWN_MANIFEST_PATH,
|
|
309
392
|
spawnCargoPath = SPAWN_CARGO_PATH,
|
|
393
|
+
tokenManifestPath = TOKEN_MANIFEST_PATH,
|
|
394
|
+
tokenCargoPath = TOKEN_CARGO_PATH,
|
|
310
395
|
packagePath = PACKAGE_PATH,
|
|
311
396
|
downloadOptions,
|
|
312
397
|
} = {}) {
|
|
313
|
-
const [
|
|
398
|
+
const [
|
|
399
|
+
patchSource,
|
|
400
|
+
cargoToml,
|
|
401
|
+
runtimeSource,
|
|
402
|
+
graphSource,
|
|
403
|
+
spawnSource,
|
|
404
|
+
spawnCargo,
|
|
405
|
+
tokenSource,
|
|
406
|
+
tokenCargo,
|
|
407
|
+
packageSource,
|
|
408
|
+
] = await Promise.all([
|
|
314
409
|
readFile(patchManifestPath, 'utf8'),
|
|
315
410
|
readFile(cargoPath, 'utf8'),
|
|
316
411
|
readFile(runtimeManifestPath, 'utf8'),
|
|
317
412
|
readFile(graphManifestPath, 'utf8'),
|
|
318
413
|
readFile(spawnManifestPath, 'utf8'),
|
|
319
414
|
readFile(spawnCargoPath, 'utf8'),
|
|
415
|
+
readFile(tokenManifestPath, 'utf8'),
|
|
416
|
+
readFile(tokenCargoPath, 'utf8'),
|
|
320
417
|
readFile(packagePath, 'utf8'),
|
|
321
418
|
]);
|
|
322
419
|
const patchManifest = validatePatchManifest(JSON.parse(patchSource), cargoToml);
|
|
323
420
|
const runtimeManifest = validateRuntimeManifest(JSON.parse(runtimeSource));
|
|
324
421
|
const graphManifest = validateGraphManifest(JSON.parse(graphSource), JSON.parse(packageSource));
|
|
325
422
|
const spawnManifest = validateSpawnManifest(JSON.parse(spawnSource), spawnCargo);
|
|
423
|
+
const tokenManifest = validateTokenManifest(JSON.parse(tokenSource), tokenCargo);
|
|
326
424
|
await verifyAssetDownloads(patchManifest.assets, downloadOptions);
|
|
327
425
|
console.log(`Verified all bundled patch assets for patch-v${patchManifest.version}.`);
|
|
328
426
|
await verifyAssetDownloads(runtimeManifest.assets, downloadOptions);
|
|
@@ -331,6 +429,8 @@ export async function verifyReleaseAssets({
|
|
|
331
429
|
console.log(`Verified all bundled graph assets for graph-v${graphManifest.version}.`);
|
|
332
430
|
await verifyAssetDownloads(spawnManifest.assets, downloadOptions);
|
|
333
431
|
console.log(`Verified all bundled spawn assets for spawn-v${spawnManifest.version}.`);
|
|
432
|
+
await verifyAssetDownloads(tokenManifest.assets, downloadOptions);
|
|
433
|
+
console.log(`Verified all bundled token assets for token-v${tokenManifest.version}.`);
|
|
334
434
|
}
|
|
335
435
|
|
|
336
436
|
if (process.argv[1] && import.meta.url === pathToFileURL(process.argv[1]).href) {
|
|
@@ -0,0 +1,89 @@
|
|
|
1
|
+
import { createHash } from 'node:crypto'
|
|
2
|
+
import { readFile, stat } from 'node:fs/promises'
|
|
3
|
+
import { resolve } from 'node:path'
|
|
4
|
+
|
|
5
|
+
import {
|
|
6
|
+
VOICE_RUNTIME_CONFIG,
|
|
7
|
+
VOICE_RUNTIME_TAG,
|
|
8
|
+
assertVoiceRuntimeManifestIdentity,
|
|
9
|
+
buildVoiceRuntimePlatforms,
|
|
10
|
+
downloadReleaseAsset,
|
|
11
|
+
fetchGithubRelease,
|
|
12
|
+
releaseAssetSha256,
|
|
13
|
+
} from './generate-voice-runtime-manifest.mjs'
|
|
14
|
+
|
|
15
|
+
const delay = (ms) => new Promise((resolveDelay) => setTimeout(resolveDelay, ms))
|
|
16
|
+
|
|
17
|
+
async function verifyOnce(manifestPath, repository, token) {
|
|
18
|
+
const release = await fetchGithubRelease(repository, VOICE_RUNTIME_TAG, token)
|
|
19
|
+
if (!release.draft) throw new Error(`${VOICE_RUNTIME_TAG} must remain draft until verification passes`)
|
|
20
|
+
|
|
21
|
+
const manifestBytes = await readFile(manifestPath)
|
|
22
|
+
const manifest = JSON.parse(manifestBytes.toString('utf8'))
|
|
23
|
+
await assertVoiceRuntimeManifestIdentity(manifest, { repository })
|
|
24
|
+
|
|
25
|
+
const expectedNames = new Set(['voice-runtime-manifest.json'])
|
|
26
|
+
for (const platform of VOICE_RUNTIME_CONFIG.platforms) {
|
|
27
|
+
expectedNames.add(platform.asset)
|
|
28
|
+
expectedNames.add(`${platform.asset}.sha256`)
|
|
29
|
+
}
|
|
30
|
+
const voiceAssets = (release.assets || []).filter((asset) =>
|
|
31
|
+
asset.name === 'voice-runtime-manifest.json' || asset.name.startsWith('whisper-server-'))
|
|
32
|
+
const unexpected = voiceAssets.filter((asset) => !expectedNames.has(asset.name))
|
|
33
|
+
if (unexpected.length > 0) {
|
|
34
|
+
throw new Error(`release has unexpected voice assets: ${unexpected.map((asset) => asset.name).join(', ')}`)
|
|
35
|
+
}
|
|
36
|
+
for (const name of expectedNames) {
|
|
37
|
+
if (!voiceAssets.some((asset) => asset.name === name)) {
|
|
38
|
+
throw new Error(`release is missing required asset ${name}`)
|
|
39
|
+
}
|
|
40
|
+
}
|
|
41
|
+
|
|
42
|
+
const generatedPlatforms = buildVoiceRuntimePlatforms(release.assets, {
|
|
43
|
+
repository,
|
|
44
|
+
tag: VOICE_RUNTIME_TAG,
|
|
45
|
+
})
|
|
46
|
+
if (JSON.stringify(generatedPlatforms) !== JSON.stringify(manifest.platforms)) {
|
|
47
|
+
throw new Error('released runtime assets do not match the generated manifest')
|
|
48
|
+
}
|
|
49
|
+
|
|
50
|
+
const manifestAsset = voiceAssets.find((asset) => asset.name === 'voice-runtime-manifest.json')
|
|
51
|
+
const localManifestHash = createHash('sha256').update(manifestBytes).digest('hex')
|
|
52
|
+
const localManifestSize = (await stat(manifestPath)).size
|
|
53
|
+
if (manifestAsset.size !== localManifestSize || releaseAssetSha256(manifestAsset) !== localManifestHash) {
|
|
54
|
+
throw new Error('uploaded voice-runtime-manifest.json does not match the verified local manifest')
|
|
55
|
+
}
|
|
56
|
+
|
|
57
|
+
for (const platform of VOICE_RUNTIME_CONFIG.platforms) {
|
|
58
|
+
const archive = voiceAssets.find((asset) => asset.name === platform.asset)
|
|
59
|
+
const sidecar = voiceAssets.find((asset) => asset.name === `${platform.asset}.sha256`)
|
|
60
|
+
const sidecarText = (await downloadReleaseAsset(sidecar, token)).toString('utf8').trim()
|
|
61
|
+
const declaredHash = /^([a-f0-9]{64})\s+/i.exec(sidecarText)?.[1]?.toLowerCase()
|
|
62
|
+
const archiveHash = releaseAssetSha256(archive)
|
|
63
|
+
if (declaredHash !== archiveHash) {
|
|
64
|
+
throw new Error(`${sidecar.name} does not match ${archive.name}`)
|
|
65
|
+
}
|
|
66
|
+
}
|
|
67
|
+
}
|
|
68
|
+
|
|
69
|
+
async function main() {
|
|
70
|
+
const manifestPath = resolve(process.argv[2] || 'src/runtime/channels/data/voice-runtime-manifest.json')
|
|
71
|
+
const repository = String(process.env.GITHUB_REPOSITORY || 'tribgames/mixdog')
|
|
72
|
+
let lastError
|
|
73
|
+
for (let attempt = 1; attempt <= 5; attempt++) {
|
|
74
|
+
try {
|
|
75
|
+
await verifyOnce(manifestPath, repository, process.env.GITHUB_TOKEN)
|
|
76
|
+
console.log(`verified complete draft release ${VOICE_RUNTIME_TAG}`)
|
|
77
|
+
return
|
|
78
|
+
} catch (error) {
|
|
79
|
+
lastError = error
|
|
80
|
+
if (attempt < 5) await delay(attempt * 2_000)
|
|
81
|
+
}
|
|
82
|
+
}
|
|
83
|
+
throw lastError
|
|
84
|
+
}
|
|
85
|
+
|
|
86
|
+
main().catch((error) => {
|
|
87
|
+
console.error(error?.stack || error)
|
|
88
|
+
process.exitCode = 1
|
|
89
|
+
})
|
|
@@ -0,0 +1,52 @@
|
|
|
1
|
+
{
|
|
2
|
+
"runtimeVersion": "1.9.2",
|
|
3
|
+
"whisperCommit": "306c88f4d1286aec1bf96e544632897886af5501",
|
|
4
|
+
"vulkanSdkVersion": "1.4.309.0",
|
|
5
|
+
"platforms": [
|
|
6
|
+
{
|
|
7
|
+
"key": "win32-x64",
|
|
8
|
+
"os": "win32",
|
|
9
|
+
"arch": "x64",
|
|
10
|
+
"backend": "vulkan",
|
|
11
|
+
"runner": "windows-2022",
|
|
12
|
+
"asset": "whisper-server-win32-x64-vulkan.zip",
|
|
13
|
+
"executable": "whisper-server.exe"
|
|
14
|
+
},
|
|
15
|
+
{
|
|
16
|
+
"key": "linux-x64",
|
|
17
|
+
"os": "linux",
|
|
18
|
+
"arch": "x64",
|
|
19
|
+
"backend": "vulkan",
|
|
20
|
+
"runner": "ubuntu-24.04",
|
|
21
|
+
"asset": "whisper-server-linux-x64-vulkan.zip",
|
|
22
|
+
"executable": "whisper-server"
|
|
23
|
+
},
|
|
24
|
+
{
|
|
25
|
+
"key": "linux-arm64",
|
|
26
|
+
"os": "linux",
|
|
27
|
+
"arch": "arm64",
|
|
28
|
+
"backend": "vulkan",
|
|
29
|
+
"runner": "ubuntu-24.04-arm",
|
|
30
|
+
"asset": "whisper-server-linux-arm64-vulkan.zip",
|
|
31
|
+
"executable": "whisper-server"
|
|
32
|
+
},
|
|
33
|
+
{
|
|
34
|
+
"key": "darwin-arm64",
|
|
35
|
+
"os": "darwin",
|
|
36
|
+
"arch": "arm64",
|
|
37
|
+
"backend": "metal",
|
|
38
|
+
"runner": "macos-14",
|
|
39
|
+
"asset": "whisper-server-darwin-arm64-metal.zip",
|
|
40
|
+
"executable": "whisper-server"
|
|
41
|
+
},
|
|
42
|
+
{
|
|
43
|
+
"key": "darwin-x64",
|
|
44
|
+
"os": "darwin",
|
|
45
|
+
"arch": "x64",
|
|
46
|
+
"backend": "cpu",
|
|
47
|
+
"runner": "macos-15-intel",
|
|
48
|
+
"asset": "whisper-server-darwin-x64-cpu.zip",
|
|
49
|
+
"executable": "whisper-server"
|
|
50
|
+
}
|
|
51
|
+
]
|
|
52
|
+
}
|
package/src/app.mjs
CHANGED
|
@@ -120,17 +120,54 @@ export async function run(argv = [], classifiedInvocation = null) {
|
|
|
120
120
|
// in src. Sample a few hot files — a newer source than the bundle means the
|
|
121
121
|
// running behavior will NOT match the tree, so warn loudly.
|
|
122
122
|
try {
|
|
123
|
-
const { statSync } = await import('node:fs');
|
|
123
|
+
const { readdirSync, statSync } = await import('node:fs');
|
|
124
124
|
const bundleMtime = statSync(bundle).mtimeMs;
|
|
125
125
|
const hotSources = [
|
|
126
126
|
join(__dirname, 'runtime', 'agent', 'orchestrator', 'session', 'agent-loop.mjs'),
|
|
127
127
|
join(__dirname, 'runtime', 'agent', 'orchestrator', 'session', 'loop', 'stored-tool-args.mjs'),
|
|
128
|
-
join(__dirname, 'tui', 'engine', 'session-api-ext.mjs'),
|
|
129
128
|
join(__dirname, 'standalone', 'agent-tool.mjs'),
|
|
130
129
|
];
|
|
131
|
-
|
|
132
|
-
|
|
133
|
-
|
|
130
|
+
// The TUI half of the guard used to sample ONE path that does not exist
|
|
131
|
+
// (tui/engine/session-api-ext.mjs), so every real src/tui edit ran silently
|
|
132
|
+
// against an old bundle. Walk the actual bundled tree instead — dist (the
|
|
133
|
+
// build output) and dev (excluded from the package) are not inputs.
|
|
134
|
+
const tuiSourceNewestMtime = (() => {
|
|
135
|
+
const skipDirs = new Set(['dist', 'dev', 'node_modules']);
|
|
136
|
+
// Ceilings, not expectations: the bundled tree is currently 2 levels
|
|
137
|
+
// deep with ~150 files, so depth 12 reaches every source that can end up
|
|
138
|
+
// in the bundle (the old depth-6 cut would have started missing files as
|
|
139
|
+
// soon as the tree grew), while the visit budget keeps this boot-time
|
|
140
|
+
// walk bounded on any pathological tree. The guard is advisory, so a
|
|
141
|
+
// budget-truncated walk still reports the newest mtime it did see.
|
|
142
|
+
const MAX_DEPTH = 12;
|
|
143
|
+
const MAX_VISITS = 5000;
|
|
144
|
+
let visits = 0;
|
|
145
|
+
let newest = 0;
|
|
146
|
+
const walk = (dir, depth) => {
|
|
147
|
+
if (depth > MAX_DEPTH || visits >= MAX_VISITS) return;
|
|
148
|
+
let entries;
|
|
149
|
+
try { entries = readdirSync(dir, { withFileTypes: true }); } catch { return; }
|
|
150
|
+
for (const entry of entries) {
|
|
151
|
+
if (visits >= MAX_VISITS) return;
|
|
152
|
+
visits += 1;
|
|
153
|
+
if (entry.isDirectory()) {
|
|
154
|
+
if (!skipDirs.has(entry.name)) walk(join(dir, entry.name), depth + 1);
|
|
155
|
+
continue;
|
|
156
|
+
}
|
|
157
|
+
if (!/\.(mjs|jsx|js)$/.test(entry.name)) continue;
|
|
158
|
+
try {
|
|
159
|
+
const mtime = statSync(join(dir, entry.name)).mtimeMs;
|
|
160
|
+
if (mtime > newest) newest = mtime;
|
|
161
|
+
} catch { /* unreadable file is not evidence of staleness */ }
|
|
162
|
+
}
|
|
163
|
+
};
|
|
164
|
+
walk(join(__dirname, 'tui'), 0);
|
|
165
|
+
return newest;
|
|
166
|
+
})();
|
|
167
|
+
const stale = tuiSourceNewestMtime > bundleMtime + 1_000
|
|
168
|
+
|| hotSources.some((file) => {
|
|
169
|
+
try { return statSync(file).mtimeMs > bundleMtime + 1_000; } catch { return false; }
|
|
170
|
+
});
|
|
134
171
|
if (stale) {
|
|
135
172
|
process.stderr.write(
|
|
136
173
|
'mixdog: TUI bundle is OLDER than runtime sources — behavior will not match the tree.\n'
|
package/src/headless-exec.mjs
CHANGED
|
@@ -13,7 +13,6 @@ import {
|
|
|
13
13
|
} from './runtime/shared/pristine-execution.mjs';
|
|
14
14
|
import { hasActiveBackgroundTasks } from './runtime/shared/background-tasks.mjs';
|
|
15
15
|
import { installProcessSignalCleanup } from './runtime/shared/process-shutdown.mjs';
|
|
16
|
-
import { sleep } from './runtime/shared/sleep.mjs';
|
|
17
16
|
import { stopStandaloneMemoryRuntimesForProcess } from './standalone/memory-runtime-proxy.mjs';
|
|
18
17
|
import { applyUsageDelta, createSessionStats } from './ui/session-stats.mjs';
|
|
19
18
|
|
|
@@ -21,6 +20,21 @@ function clean(value) {
|
|
|
21
20
|
return String(value ?? '').trim();
|
|
22
21
|
}
|
|
23
22
|
|
|
23
|
+
export async function prewarmHeadlessSearch(cwd, {
|
|
24
|
+
loadNativeSearch = () => import('./runtime/agent/orchestrator/tools/builtin/native-search-client.mjs'),
|
|
25
|
+
loadListTool = () => import('./runtime/agent/orchestrator/tools/builtin/list-tool.mjs'),
|
|
26
|
+
} = {}) {
|
|
27
|
+
const root = clean(cwd) || process.cwd();
|
|
28
|
+
const [nativeSearch, listTool] = await Promise.all([
|
|
29
|
+
loadNativeSearch(),
|
|
30
|
+
loadListTool(),
|
|
31
|
+
]);
|
|
32
|
+
await Promise.all([
|
|
33
|
+
nativeSearch.warmNativeSearchServer(),
|
|
34
|
+
listTool.prewarmFindEnumeration(root),
|
|
35
|
+
]);
|
|
36
|
+
}
|
|
37
|
+
|
|
24
38
|
function nonNegativeNumber(value) {
|
|
25
39
|
const number = Number(value);
|
|
26
40
|
return Number.isFinite(number) ? Math.max(0, number) : 0;
|
|
@@ -424,6 +438,8 @@ function createJsonLifecycle({
|
|
|
424
438
|
}
|
|
425
439
|
const durationMs = Math.max(0, completedAt - turnStartedAt);
|
|
426
440
|
const usage = usageSummary(stats, toolCallCount);
|
|
441
|
+
const terminationReason = clean(result?.terminationReason) || null;
|
|
442
|
+
const isApiError = terminationReason === 'refusal';
|
|
427
443
|
emit({
|
|
428
444
|
type: 'turn.completed',
|
|
429
445
|
thread_id: threadId,
|
|
@@ -441,7 +457,7 @@ function createJsonLifecycle({
|
|
|
441
457
|
turn_id: turnId,
|
|
442
458
|
session_id: threadId,
|
|
443
459
|
model: resolvedModel,
|
|
444
|
-
is_error:
|
|
460
|
+
is_error: isApiError,
|
|
445
461
|
duration_ms: durationMs,
|
|
446
462
|
duration_api_ms: providerDurationMs,
|
|
447
463
|
num_turns: 1,
|
|
@@ -449,6 +465,7 @@ function createJsonLifecycle({
|
|
|
449
465
|
tool_calls: toolCallCount,
|
|
450
466
|
result: finalText,
|
|
451
467
|
stop_reason: result?.stopReason ?? result?.stop_reason ?? null,
|
|
468
|
+
termination_reason: terminationReason,
|
|
452
469
|
usage,
|
|
453
470
|
}, completedAt);
|
|
454
471
|
},
|
|
@@ -491,28 +508,17 @@ function createJsonLifecycle({
|
|
|
491
508
|
};
|
|
492
509
|
}
|
|
493
510
|
|
|
494
|
-
|
|
495
|
-
sessionId,
|
|
496
|
-
clientHostPid = process.pid,
|
|
497
|
-
hasActiveTasks = hasActiveBackgroundTasks,
|
|
498
|
-
pollMs = 100,
|
|
499
|
-
} = {}) {
|
|
500
|
-
const scope = {
|
|
501
|
-
...(clean(sessionId) ? { callerSessionId: clean(sessionId) } : {}),
|
|
502
|
-
clientHostPid,
|
|
503
|
-
};
|
|
504
|
-
while (hasActiveTasks(scope)) {
|
|
505
|
-
await sleep(Math.max(10, Number(pollMs) || 100));
|
|
506
|
-
}
|
|
507
|
-
}
|
|
508
|
-
|
|
509
|
-
function writeUsageDocument(path, stats, runtime, toolCallCount = 0) {
|
|
511
|
+
function writeUsageDocument(path, stats, runtime, toolCallCount = 0, observedModels = []) {
|
|
510
512
|
const target = clean(path);
|
|
511
513
|
if (!target) return;
|
|
514
|
+
const models = [
|
|
515
|
+
clean(runtime?.model),
|
|
516
|
+
...Array.from(observedModels || [], clean),
|
|
517
|
+
].filter((value, index, values) => value && values.indexOf(value) === index);
|
|
512
518
|
const session = {
|
|
513
519
|
sessionId: clean(runtime?.id),
|
|
514
520
|
agentRole: 'primary',
|
|
515
|
-
models
|
|
521
|
+
models,
|
|
516
522
|
inputTokens: stats.inputTokens,
|
|
517
523
|
cacheTokens: stats.cachedTokens,
|
|
518
524
|
cacheWriteTokens: stats.cacheWriteTokens,
|
|
@@ -554,7 +560,6 @@ export async function runHeadlessExec({
|
|
|
554
560
|
memoryRuntimeCleanup = stopStandaloneMemoryRuntimesForProcess,
|
|
555
561
|
hasActiveTasks = hasActiveBackgroundTasks,
|
|
556
562
|
installSignalCleanupFn = installProcessSignalCleanup,
|
|
557
|
-
idlePollMs = 100,
|
|
558
563
|
} = {}) {
|
|
559
564
|
const prompt = clean(message);
|
|
560
565
|
if (!prompt) {
|
|
@@ -568,6 +573,7 @@ export async function runHeadlessExec({
|
|
|
568
573
|
}
|
|
569
574
|
|
|
570
575
|
const stats = createSessionStats();
|
|
576
|
+
const observedModels = new Set();
|
|
571
577
|
const lifecycle = json ? createJsonLifecycle({
|
|
572
578
|
write,
|
|
573
579
|
stats,
|
|
@@ -589,11 +595,24 @@ export async function runHeadlessExec({
|
|
|
589
595
|
let resultText = '';
|
|
590
596
|
let executionError = null;
|
|
591
597
|
let code = 1;
|
|
598
|
+
const taskScopeFor = (session) => ({
|
|
599
|
+
...(clean(session?.id) ? { callerSessionId: clean(session.id) } : {}),
|
|
600
|
+
clientHostPid: session?.clientHostPid,
|
|
601
|
+
});
|
|
592
602
|
const cleanup = (reason = 'exec-exit') => {
|
|
593
603
|
cleanupPromise ??= (async () => {
|
|
594
604
|
const errors = [];
|
|
595
605
|
try {
|
|
596
|
-
|
|
606
|
+
// Background jobs the model left running ON PURPOSE (a task's required
|
|
607
|
+
// server) must outlive this process: reaping them at exit destroys the
|
|
608
|
+
// very artifact the caller asked for. Detach instead of reap. cli.mjs
|
|
609
|
+
// exits explicitly, so surviving children cannot hold the process open.
|
|
610
|
+
if (runtime) {
|
|
611
|
+
await runtime.close(
|
|
612
|
+
reason,
|
|
613
|
+
hasActiveTasks(taskScopeFor(runtime)) ? { keepBackgroundWork: true } : {},
|
|
614
|
+
);
|
|
615
|
+
}
|
|
597
616
|
} catch (error) {
|
|
598
617
|
errors.push(error);
|
|
599
618
|
}
|
|
@@ -624,6 +643,13 @@ export async function runHeadlessExec({
|
|
|
624
643
|
|
|
625
644
|
try {
|
|
626
645
|
boundary = boundaryFactory({ provider, model, effort, fast });
|
|
646
|
+
// Fire-and-forget search prewarm, matching the long-lived host. Warm both
|
|
647
|
+
// the resident server and this Project's complete path inventory so the
|
|
648
|
+
// first normal find/glob does real indexed work instead of paying startup
|
|
649
|
+
// or a cold tree walk. Non-fatal by construction.
|
|
650
|
+
if (!/^(1|true|yes|on)$/i.test(String(process.env.MIXDOG_DISABLE_TOOL_PREWARM || '').trim())) {
|
|
651
|
+
void prewarmHeadlessSearch(cwd).catch(() => {});
|
|
652
|
+
}
|
|
627
653
|
signalCleanup = installSignalCleanupFn({
|
|
628
654
|
name: 'mixdog-exec',
|
|
629
655
|
timeoutMs: 20_000,
|
|
@@ -666,6 +692,8 @@ export async function runHeadlessExec({
|
|
|
666
692
|
const askOptions = {
|
|
667
693
|
onTextReset: () => true,
|
|
668
694
|
onUsageDelta: (delta) => {
|
|
695
|
+
const observedModel = clean(delta?.model);
|
|
696
|
+
if (observedModel) observedModels.add(observedModel);
|
|
669
697
|
applyUsageDelta(stats, delta);
|
|
670
698
|
lifecycle?.onUsageDelta(delta);
|
|
671
699
|
},
|
|
@@ -682,17 +710,14 @@ export async function runHeadlessExec({
|
|
|
682
710
|
} : {}),
|
|
683
711
|
};
|
|
684
712
|
({ result } = await runtime.ask(prompt, askOptions));
|
|
685
|
-
|
|
686
|
-
|
|
687
|
-
|
|
688
|
-
|
|
689
|
-
|
|
690
|
-
|
|
691
|
-
|
|
692
|
-
|
|
693
|
-
hasActiveTasks,
|
|
694
|
-
pollMs: idlePollMs,
|
|
695
|
-
});
|
|
713
|
+
// Exit NEVER waits on running background work. A job the model left
|
|
714
|
+
// running on purpose (a task's server) has no end, so waiting for it spent
|
|
715
|
+
// the whole remaining budget on an already-finished turn — 2026-08-23 full
|
|
716
|
+
// run: two trials, ~85s of real work each, 900s burned. Claude Code and
|
|
717
|
+
// Codex both leave the moment the turn ends. Completions that ALREADY
|
|
718
|
+
// arrived still get their follow-up turn below: that is a queued message,
|
|
719
|
+
// not a wait, so no result the model can act on is dropped.
|
|
720
|
+
while (completionPending) {
|
|
696
721
|
completionPending = false;
|
|
697
722
|
({ result } = await runtime.ask('', askOptions));
|
|
698
723
|
}
|
|
@@ -716,6 +741,7 @@ export async function runHeadlessExec({
|
|
|
716
741
|
stats,
|
|
717
742
|
runtime,
|
|
718
743
|
lifecycle?.toolCallCount || 0,
|
|
744
|
+
observedModels,
|
|
719
745
|
);
|
|
720
746
|
} catch (error) {
|
|
721
747
|
writeErr(`mixdog: usage log write failed: ${error?.message || error}\n`);
|