mixdog 0.9.152 → 0.9.154
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/LICENSES/browser-import-NOTICE.txt +11 -0
- package/NOTICE.md +11 -0
- package/README.md +8 -10
- package/package.json +19 -3
- package/scripts/computer-task-cost-report.mjs +144 -0
- package/scripts/run-office-live-tests.mjs +32 -0
- package/src/agents/front-worker/AGENT.md +13 -0
- package/src/agents/front-worker/agent.json +6 -0
- package/src/agents/heavy-worker/AGENT.md +10 -11
- package/src/agents/heavy-worker/agent.json +1 -1
- package/src/agents/maintainer/AGENT.md +0 -2
- package/src/agents/reviewer/AGENT.md +9 -13
- package/src/agents/reviewer/agent.json +1 -1
- package/src/agents/security/AGENT.md +14 -0
- package/src/agents/security/agent.json +6 -0
- package/src/agents/worker/AGENT.md +8 -8
- package/src/agents/worker/agent.json +1 -1
- package/src/agents/writer/AGENT.md +14 -0
- package/src/agents/writer/agent.json +6 -0
- package/src/rules/agent/00-common.md +4 -7
- package/src/rules/lead/01-general.md +0 -3
- package/src/rules/shared/10-tool-workflow.md +10 -2
- package/src/rules/shared/60-verification.md +2 -0
- package/src/runtime/agent/orchestrator/agent-runtime/agent-dispatch.mjs +183 -26
- package/src/runtime/agent/orchestrator/agent-runtime/session-builder.mjs +4 -0
- package/src/runtime/agent/orchestrator/cache-break-trace.mjs +109 -0
- package/src/runtime/agent/orchestrator/config.mjs +7 -2
- package/src/runtime/agent/orchestrator/providers/admission-scheduler.mjs +16 -3
- package/src/runtime/agent/orchestrator/providers/lib/anthropic-request-utils.mjs +9 -1
- package/src/runtime/agent/orchestrator/providers/media-normalization.mjs +30 -9
- package/src/runtime/agent/orchestrator/providers/openai-codex-metadata.mjs +18 -26
- package/src/runtime/agent/orchestrator/providers/openai-oauth-http-sse.mjs +25 -2
- package/src/runtime/agent/orchestrator/providers/openai-oauth-ws.mjs +103 -10
- package/src/runtime/agent/orchestrator/providers/openai-turn-state.mjs +68 -0
- package/src/runtime/agent/orchestrator/providers/openai-ws-delta.mjs +36 -11
- package/src/runtime/agent/orchestrator/providers/openai-ws-pool.mjs +140 -86
- package/src/runtime/agent/orchestrator/providers/openai-ws-stream.mjs +9 -13
- package/src/runtime/agent/orchestrator/session/agent-loop.mjs +72 -4
- package/src/runtime/agent/orchestrator/session/browser-snapshot-supersession.mjs +61 -0
- package/src/runtime/agent/orchestrator/session/compact/handoff.mjs +0 -22
- package/src/runtime/agent/orchestrator/session/compact/messages.mjs +2 -2
- package/src/runtime/agent/orchestrator/session/compact/runner.mjs +11 -23
- package/src/runtime/agent/orchestrator/session/compact/summary.mjs +28 -269
- package/src/runtime/agent/orchestrator/session/compact.mjs +0 -6
- package/src/runtime/agent/orchestrator/session/context-utils.mjs +86 -11
- package/src/runtime/agent/orchestrator/session/eager-dispatch.mjs +17 -1
- package/src/runtime/agent/orchestrator/session/image-strip-recovery.mjs +12 -8
- package/src/runtime/agent/orchestrator/session/loop/compact-policy.mjs +110 -2
- package/src/runtime/agent/orchestrator/session/loop/pre-dispatch-deny.mjs +3 -16
- package/src/runtime/agent/orchestrator/session/loop/recall-fasttrack.mjs +80 -18
- package/src/runtime/agent/orchestrator/session/loop/steering.mjs +6 -0
- package/src/runtime/agent/orchestrator/session/loop/tool-exec.mjs +3 -0
- package/src/runtime/agent/orchestrator/session/loop/tool-helpers.mjs +6 -0
- package/src/runtime/agent/orchestrator/session/manager/ask-session.mjs +80 -21
- package/src/runtime/agent/orchestrator/session/manager/compaction-runner.mjs +33 -72
- package/src/runtime/agent/orchestrator/session/manager/session-crud.mjs +21 -2
- package/src/runtime/agent/orchestrator/session/manager/session-lifecycle.mjs +25 -51
- package/src/runtime/agent/orchestrator/session/manager/status-telemetry.mjs +3 -0
- package/src/runtime/agent/orchestrator/session/manager/tool-resolution.mjs +4 -110
- package/src/runtime/agent/orchestrator/session/manager/turn-checkpoint-journal.mjs +39 -15
- package/src/runtime/agent/orchestrator/session/manager/turn-checkpoint.mjs +14 -4
- package/src/runtime/agent/orchestrator/session/pre-send-compact.mjs +41 -8
- package/src/runtime/agent/orchestrator/session/provider-prefix-guard.mjs +129 -12
- package/src/runtime/agent/orchestrator/session/send-with-recovery.mjs +2 -2
- package/src/runtime/agent/orchestrator/session/store/listing.mjs +8 -1
- package/src/runtime/agent/orchestrator/session/store/save-worker.mjs +15 -1
- package/src/runtime/agent/orchestrator/session/store-summary-index.mjs +36 -2
- package/src/runtime/agent/orchestrator/session/store-summary-reader.mjs +157 -30
- package/src/runtime/agent/orchestrator/session/store-summary-visibility.mjs +57 -0
- package/src/runtime/agent/orchestrator/session/store.mjs +3 -1
- package/src/runtime/agent/orchestrator/session/tool-batch.mjs +22 -0
- package/src/runtime/agent/orchestrator/tools/builtin/read-special-files.mjs +6 -6
- package/src/runtime/agent/orchestrator/tools/progress-message.mjs +4 -0
- package/src/runtime/attachments/pdf-extract.mjs +3 -0
- package/src/runtime/attachments/pdfjs-runtime.mjs +13 -0
- package/src/runtime/browser-bridge/action-schema.mjs +251 -0
- package/src/runtime/browser-bridge/client.mjs +75 -26
- package/src/runtime/browser-bridge/tool-defs.mjs +132 -15
- package/src/runtime/computer-bridge/action-schema.mjs +653 -0
- package/src/runtime/computer-bridge/client.mjs +211 -28
- package/src/runtime/computer-bridge/tool-defs.mjs +25 -45
- package/src/runtime/office/assurance-benchmark.mjs +358 -0
- package/src/runtime/office/assurance.mjs +833 -0
- package/src/runtime/office/benchmark.mjs +496 -0
- package/src/runtime/office/capabilities.mjs +551 -0
- package/src/runtime/office/com-adapter.mjs +283 -0
- package/src/runtime/office/content-model.mjs +183 -0
- package/src/runtime/office/contract-benchmark.mjs +105 -0
- package/src/runtime/office/design-library.mjs +1562 -0
- package/src/runtime/office/design-system.mjs +1966 -0
- package/src/runtime/office/index.mjs +2413 -0
- package/src/runtime/office/journal.mjs +82 -0
- package/src/runtime/office/office-com-host.ps1 +3781 -0
- package/src/runtime/office/office-com-session-host.ps1 +855 -0
- package/src/runtime/office/ooxml-validator.mjs +271 -0
- package/src/runtime/office/pagination.mjs +132 -0
- package/src/runtime/office/pdf-adapter.mjs +591 -0
- package/src/runtime/office/pdf-analysis.mjs +372 -0
- package/src/runtime/office/pdf-render.mjs +167 -0
- package/src/runtime/office/pdf-security.mjs +63 -0
- package/src/runtime/office/polish-benchmark.mjs +190 -0
- package/src/runtime/office/portable-ooxml.mjs +1867 -0
- package/src/runtime/office/quality-live-benchmark.mjs +486 -0
- package/src/runtime/office/quality-pipeline.mjs +136 -0
- package/src/runtime/office/tabular.mjs +302 -0
- package/src/runtime/office/templates/mixdog-executive.pptx +0 -0
- package/src/runtime/office/templates/mixdog-executive.pptx.mixdog.json +190 -0
- package/src/runtime/office/tool-defs.mjs +113 -0
- package/src/runtime/office/visual-diff.mjs +89 -0
- package/src/runtime/office/xlsx-assertions.mjs +172 -0
- package/src/runtime/office/xlsx-contract.mjs +147 -0
- package/src/runtime/shared/agent-route-config.mjs +9 -0
- package/src/runtime/shared/tool-execution-contract.mjs +16 -0
- package/src/runtime/shared/tool-surface.mjs +9 -0
- package/src/session-runtime/context-status.mjs +9 -3
- package/src/session-runtime/deferred-tool-delta.mjs +104 -0
- package/src/session-runtime/goal-runtime.mjs +617 -197
- package/src/session-runtime/lifecycle-api.mjs +50 -8
- package/src/session-runtime/provider-request-snapshot.mjs +33 -9
- package/src/session-runtime/resource-api.mjs +31 -94
- package/src/session-runtime/runtime-core.mjs +78 -26
- package/src/session-runtime/session-lifecycle.mjs +34 -12
- package/src/session-runtime/session-title.mjs +2 -2
- package/src/session-runtime/session-turn-api.mjs +15 -14
- package/src/session-runtime/tool-catalog-data.mjs +1 -1
- package/src/session-runtime/tool-catalog.mjs +38 -99
- package/src/session-runtime/tool-defs.mjs +6 -1
- package/src/session-runtime/tool-surface.mjs +18 -8
- package/src/session-runtime/workflow-agents-api.mjs +8 -25
- package/src/session-runtime/workflow.mjs +8 -6
- package/src/standalone/agent-tool/helpers.mjs +2 -2
- package/src/standalone/agent-tool/job-views.mjs +3 -1
- package/src/standalone/agent-tool/notify.mjs +5 -1
- package/src/standalone/agent-tool/render.mjs +3 -0
- package/src/standalone/agent-tool/spawn-flow.mjs +61 -16
- package/src/standalone/agent-tool/tool-def.mjs +1 -1
- package/src/standalone/agent-tool/worker-index.mjs +123 -0
- package/src/standalone/agent-tool/worker-rows.mjs +2 -1
- package/src/standalone/agent-tool.mjs +57 -10
- package/src/standalone/daemon.mjs +105 -6
- package/src/standalone/session-runtime-host.mjs +61 -48
- package/src/standalone/session-runtime-inline-host.mjs +34 -127
- package/src/standalone/session-runtime-worker.mjs +20 -198
- package/src/standalone/session-service.mjs +632 -7
- package/src/tui/dist/index.mjs +24 -1
- package/src/tui/lib/voice-setup.mjs +4 -1
- package/src/tui/session/context-state.mjs +2 -13
- package/src/tui/session/goal-continuation.mjs +38 -7
- package/src/tui/session/session-api-ext.mjs +9 -0
- package/src/tui/session/session-api.mjs +15 -1
- package/src/tui/session/session-flow.mjs +25 -1
- package/src/tui/session/turn.mjs +88 -16
- package/src/tui/session-local.mjs +25 -3
- package/src/vendor/statusline/src/gateway/route-meta.mjs +31 -0
- package/scripts/generate-voice-runtime-manifest.test.mjs +0 -135
- package/scripts/native-binary-arch.test.mjs +0 -111
- package/scripts/patch-replay.test.mjs +0 -94
- package/src/headless-exec.test.mjs +0 -539
- package/src/runtime/agent/orchestrator/agent-trace-timing.test.mjs +0 -57
- package/src/runtime/agent/orchestrator/context/collect-skills.test.mjs +0 -153
- package/src/runtime/agent/orchestrator/mcp/cancellation.test.mjs +0 -139
- package/src/runtime/agent/orchestrator/mcp/features.test.mjs +0 -66
- package/src/runtime/agent/orchestrator/mcp/security.test.mjs +0 -80
- package/src/runtime/agent/orchestrator/providers/anthropic-fast-mode.test.mjs +0 -90
- package/src/runtime/agent/orchestrator/providers/anthropic-server-fallback.test.mjs +0 -215
- package/src/runtime/agent/orchestrator/providers/antigravity-oauth.test.mjs +0 -165
- package/src/runtime/agent/orchestrator/providers/media-parity.test.mjs +0 -142
- package/src/runtime/agent/orchestrator/providers/openai-codex-identity.test.mjs +0 -153
- package/src/runtime/agent/orchestrator/providers/openai-codex-model.test.mjs +0 -24
- package/src/runtime/agent/orchestrator/providers/openai-ws-delta-parity.test.mjs +0 -190
- package/src/runtime/agent/orchestrator/providers/provider-replay.test.mjs +0 -235
- package/src/runtime/agent/orchestrator/providers/sse-framing.test.mjs +0 -719
- package/src/runtime/agent/orchestrator/session/cache/prefetch-cache.test.mjs +0 -40
- package/src/runtime/agent/orchestrator/session/compact-policy.test.mjs +0 -176
- package/src/runtime/agent/orchestrator/session/compaction-read-reset.test.mjs +0 -161
- package/src/runtime/agent/orchestrator/session/eager-dispatch.test.mjs +0 -115
- package/src/runtime/agent/orchestrator/session/evidence-union.test.mjs +0 -228
- package/src/runtime/agent/orchestrator/session/image-strip-recovery.test.mjs +0 -245
- package/src/runtime/agent/orchestrator/session/lossless-tool-output.test.mjs +0 -125
- package/src/runtime/agent/orchestrator/session/manager/pending-messages-persistence.test.mjs +0 -325
- package/src/runtime/agent/orchestrator/session/manager/session-lifecycle.test.mjs +0 -70
- package/src/runtime/agent/orchestrator/session/manager/turn-checkpoint-journal.test.mjs +0 -457
- package/src/runtime/agent/orchestrator/session/provider-prefix-guard.test.mjs +0 -117
- package/src/runtime/agent/orchestrator/session/reduction-metrics.test.mjs +0 -87
- package/src/runtime/agent/orchestrator/session/store/serialize.test.mjs +0 -60
- package/src/runtime/agent/orchestrator/session/store-summary-reader.test.mjs +0 -583
- package/src/runtime/agent/orchestrator/session/task-wait-control.test.mjs +0 -78
- package/src/runtime/agent/orchestrator/tools/builtin/absence-absorption.test.mjs +0 -43
- package/src/runtime/agent/orchestrator/tools/builtin/absolute-glob-expand.test.mjs +0 -63
- package/src/runtime/agent/orchestrator/tools/builtin/bash-tool-cwd-env.test.mjs +0 -14
- package/src/runtime/agent/orchestrator/tools/builtin/edit-encoding-eol.test.mjs +0 -224
- package/src/runtime/agent/orchestrator/tools/builtin/edit-sequential-occupation.test.mjs +0 -224
- package/src/runtime/agent/orchestrator/tools/builtin/enoent-outside-project.test.mjs +0 -56
- package/src/runtime/agent/orchestrator/tools/builtin/find-search-budget.test.mjs +0 -103
- package/src/runtime/agent/orchestrator/tools/builtin/git-command-policy.test.mjs +0 -30
- package/src/runtime/agent/orchestrator/tools/builtin/git-command-tool.test.mjs +0 -390
- package/src/runtime/agent/orchestrator/tools/builtin/git-repo-rw-lock.test.mjs +0 -83
- package/src/runtime/agent/orchestrator/tools/builtin/grep-output-budget.test.mjs +0 -31
- package/src/runtime/agent/orchestrator/tools/builtin/grep-single-file-rescue.test.mjs +0 -106
- package/src/runtime/agent/orchestrator/tools/builtin/lib/shell-job-records.test.mjs +0 -102
- package/src/runtime/agent/orchestrator/tools/builtin/list-tool-integrity.test.mjs +0 -152
- package/src/runtime/agent/orchestrator/tools/builtin/native-search-health.test.mjs +0 -24
- package/src/runtime/agent/orchestrator/tools/builtin/native-search-transport.test.mjs +0 -24
- package/src/runtime/agent/orchestrator/tools/builtin/noise-dir-visibility.test.mjs +0 -69
- package/src/runtime/agent/orchestrator/tools/builtin/read-glob-survey.test.mjs +0 -71
- package/src/runtime/agent/orchestrator/tools/builtin/runtime-capabilities.test.mjs +0 -56
- package/src/runtime/agent/orchestrator/tools/builtin/search-builders.test.mjs +0 -16
- package/src/runtime/agent/orchestrator/tools/builtin/search-glob-top-k.test.mjs +0 -39
- package/src/runtime/agent/orchestrator/tools/builtin/write-symlink.test.mjs +0 -153
- package/src/runtime/agent/orchestrator/tools/code-graph/aggregate-anchor-relocation.test.mjs +0 -55
- package/src/runtime/agent/orchestrator/tools/code-graph/dispatch.test.mjs +0 -88
- package/src/runtime/agent/orchestrator/tools/code-graph/loose-file-target.test.mjs +0 -72
- package/src/runtime/agent/orchestrator/tools/code-graph/memory-cache.test.mjs +0 -54
- package/src/runtime/agent/orchestrator/tools/code-graph/search-references.test.mjs +0 -20
- package/src/runtime/agent/orchestrator/tools/code-graph/symbol-search-scope.test.mjs +0 -55
- package/src/runtime/agent/orchestrator/tools/code-graph/text-span-mask.test.mjs +0 -268
- package/src/runtime/agent/orchestrator/tools/env-scrub.test.mjs +0 -57
- package/src/runtime/agent/orchestrator/tools/graph-binary-fetcher.test.mjs +0 -19
- package/src/runtime/agent/orchestrator/tools/lib/native-spawn-client.test.mjs +0 -104
- package/src/runtime/agent/orchestrator/tools/patch/engine-contract-gate.test.mjs +0 -661
- package/src/runtime/agent/orchestrator/tools/patch/patch-codec-eol.test.mjs +0 -552
- package/src/runtime/agent/orchestrator/tools/patch/patch-symlink.test.mjs +0 -141
- package/src/runtime/agent/orchestrator/tools/patch/v4a-pure-move.test.mjs +0 -57
- package/src/runtime/agent/orchestrator/tools/patch/v4a-section-coalesce.test.mjs +0 -81
- package/src/runtime/agent/orchestrator/tools/spawn-binary-fetcher.test.mjs +0 -52
- package/src/runtime/attachments/store.test.mjs +0 -209
- package/src/runtime/channels/lib/voice-runtime-fetcher.test.mjs +0 -17
- package/src/runtime/channels/lib/webhook/deliveries.test.mjs +0 -18
- package/src/runtime/channels/lib/webhook/relay-tunnel.test.mjs +0 -68
- package/src/runtime/media/adapters/codex-image.test.mjs +0 -22
- package/src/runtime/media/download.test.mjs +0 -52
- package/src/runtime/media/renditions.test.mjs +0 -99
- package/src/runtime/media/store.test.mjs +0 -159
- package/src/runtime/memory/lib/archive-security.test.mjs +0 -128
- package/src/runtime/memory/lib/embedding-model-config.test.mjs +0 -33
- package/src/runtime/memory/lib/http-wire.test.mjs +0 -15
- package/src/runtime/memory/lib/memory-cycle-packets.test.mjs +0 -49
- package/src/runtime/memory/lib/memory-cycle2-shared.test.mjs +0 -78
- package/src/runtime/memory/lib/memory-embedding-migration.test.mjs +0 -50
- package/src/runtime/memory/lib/pg/process.test.mjs +0 -36
- package/src/runtime/memory/lib/trace-store.test.mjs +0 -57
- package/src/runtime/memory/lib/transcript-ingest.test.mjs +0 -52
- package/src/runtime/shared/background-tasks.test.mjs +0 -78
- package/src/runtime/shared/bounded-download.test.mjs +0 -41
- package/src/runtime/shared/channel-notification-routing.test.mjs +0 -25
- package/src/runtime/shared/child-spawn-remote.test.mjs +0 -124
- package/src/runtime/shared/remote-intent.test.mjs +0 -40
- package/src/runtime/shared/resource-admission.test.mjs +0 -43
- package/src/runtime/shared/runtime-root.test.mjs +0 -34
- package/src/runtime/shared/schedule-time.test.mjs +0 -29
- package/src/runtime/shared/session-runtime-health.test.mjs +0 -83
- package/src/runtime/shared/skill-document.test.mjs +0 -81
- package/src/runtime/shared/tool-surface.test.mjs +0 -87
- package/src/runtime/shared/update-checker.test.mjs +0 -33
- package/src/runtime/shared/user-cwd.test.mjs +0 -43
- package/src/runtime/shared/webhook-session-run.test.mjs +0 -14
- package/src/session-runtime/agent-disable.test.mjs +0 -123
- package/src/session-runtime/cwd-plugins.test.mjs +0 -75
- package/src/session-runtime/cwd-tool-routing.test.mjs +0 -61
- package/src/session-runtime/global-extensions.test.mjs +0 -28
- package/src/session-runtime/goal-runtime.test.mjs +0 -108
- package/src/session-runtime/lifecycle-api.test.mjs +0 -21
- package/src/session-runtime/model-settings-persist.test.mjs +0 -105
- package/src/session-runtime/notification-bus.test.mjs +0 -148
- package/src/session-runtime/plugin-mcp-standard.test.mjs +0 -177
- package/src/session-runtime/prewarm.test.mjs +0 -52
- package/src/session-runtime/provider-request-snapshot.test.mjs +0 -37
- package/src/session-runtime/resource-api-global.test.mjs +0 -69
- package/src/session-runtime/session-lifecycle.test.mjs +0 -125
- package/src/session-runtime/session-title.test.mjs +0 -36
- package/src/session-runtime/skills-api.test.mjs +0 -99
- package/src/session-runtime/tool-policy-surface.test.mjs +0 -331
- package/src/standalone/agent-tool/helpers.test.mjs +0 -62
- package/src/standalone/agent-tool/notify.test.mjs +0 -37
- package/src/standalone/agent-tool/spawn-flow.test.mjs +0 -63
- package/src/standalone/channel-restart.test.mjs +0 -145
- package/src/standalone/channel-session-router.test.mjs +0 -37
- package/src/standalone/channel-worker-heartbeat.test.mjs +0 -30
- package/src/standalone/hook-bus/handlers.test.mjs +0 -88
- package/src/standalone/plugin-admin.test.mjs +0 -173
- package/src/standalone/session-runtime-agent-control-client.test.mjs +0 -71
- package/src/standalone/session-runtime-dispatch-cancel.test.mjs +0 -139
- package/src/standalone/session-runtime-host-factory.test.mjs +0 -31
- package/src/standalone/session-runtime-host-health.test.mjs +0 -160
- package/src/standalone/session-runtime-inline-host.test.mjs +0 -83
- package/src/standalone/session-runtime-provider-cooldown.test.mjs +0 -80
- package/src/standalone/session-runtime-shard-host.test.mjs +0 -569
- package/src/standalone/session-runtime-shard-router.test.mjs +0 -101
- package/src/tui/app/live-spinner-visibility.test.mjs +0 -44
- package/src/tui/app/panel-epoch.test.mjs +0 -1009
- package/src/tui/app/panel-handoff.test.mjs +0 -311
- package/src/tui/app/prompt-submit.test.mjs +0 -206
- package/src/tui/session/completion-card-restore.test.mjs +0 -111
- package/src/tui/session/goal-continuation.test.mjs +0 -84
- package/src/tui/session/oauth-flows.test.mjs +0 -134
- package/src/tui/session/queue-helpers.test.mjs +0 -46
- package/src/tui/session/session-action-surface.test.mjs +0 -108
- package/src/tui/session/task-wait-submit.test.mjs +0 -80
- package/src/tui/session/tool-result-status.test.mjs +0 -131
- package/src/tui/session/tui-steering-persist.test.mjs +0 -104
|
@@ -1,539 +0,0 @@
|
|
|
1
|
-
import assert from 'node:assert/strict';
|
|
2
|
-
import { mkdirSync, mkdtempSync, readFileSync, rmSync, writeFileSync } from 'node:fs';
|
|
3
|
-
import { tmpdir } from 'node:os';
|
|
4
|
-
import { join } from 'node:path';
|
|
5
|
-
import test from 'node:test';
|
|
6
|
-
|
|
7
|
-
import { classifyCliInvocation } from './headless-command.mjs';
|
|
8
|
-
import { prewarmHeadlessSearch, runHeadlessExec } from './headless-exec.mjs';
|
|
9
|
-
import { resolveCursorOAuthAccessToken } from './runtime/agent/orchestrator/providers/cursor-auth.mjs';
|
|
10
|
-
import { createPristineExecutionBoundary } from './runtime/shared/pristine-execution.mjs';
|
|
11
|
-
|
|
12
|
-
test('headless search prewarm starts only the canonical native server', async () => {
|
|
13
|
-
const calls = [];
|
|
14
|
-
await prewarmHeadlessSearch('/app/project', {
|
|
15
|
-
loadNativeSearch: async () => ({
|
|
16
|
-
async warmNativeSearchServer() {
|
|
17
|
-
calls.push('server');
|
|
18
|
-
return true;
|
|
19
|
-
},
|
|
20
|
-
}),
|
|
21
|
-
});
|
|
22
|
-
assert.deepEqual(calls, ['server']);
|
|
23
|
-
});
|
|
24
|
-
|
|
25
|
-
test('pristine headless execution binds Cursor OAuth credentials in process', async () => {
|
|
26
|
-
const root = mkdtempSync(join(tmpdir(), 'mixdog-headless-cursor-auth-test-'));
|
|
27
|
-
const dataDir = join(root, 'data');
|
|
28
|
-
mkdirSync(dataDir, { recursive: true });
|
|
29
|
-
writeFileSync(join(dataDir, 'cursor-oauth.json'), JSON.stringify({
|
|
30
|
-
access_token: 'cursor-test-token',
|
|
31
|
-
}));
|
|
32
|
-
const env = { MIXDOG_HOME: root };
|
|
33
|
-
const boundary = createPristineExecutionBoundary({
|
|
34
|
-
provider: 'cursor-oauth',
|
|
35
|
-
model: 'gemini-3.7-flash',
|
|
36
|
-
env,
|
|
37
|
-
});
|
|
38
|
-
try {
|
|
39
|
-
assert.equal(boundary.audit.authMode, 'in-process-host-oauth-binding');
|
|
40
|
-
assert.equal(await resolveCursorOAuthAccessToken(), 'cursor-test-token');
|
|
41
|
-
assert.deepEqual(boundary.loadConfig().providers, {
|
|
42
|
-
'cursor-oauth': { enabled: true },
|
|
43
|
-
});
|
|
44
|
-
} finally {
|
|
45
|
-
boundary.cleanup();
|
|
46
|
-
rmSync(root, { recursive: true, force: true });
|
|
47
|
-
}
|
|
48
|
-
});
|
|
49
|
-
|
|
50
|
-
test('headless exec runs one implicit-approval session and waits for tracked tasks', async () => {
|
|
51
|
-
const root = mkdtempSync(join(tmpdir(), 'mixdog-headless-exec-test-'));
|
|
52
|
-
const usageLogPath = join(root, 'usage.json');
|
|
53
|
-
const output = [];
|
|
54
|
-
const errors = [];
|
|
55
|
-
const runtimeOptions = [];
|
|
56
|
-
const activeScopes = [];
|
|
57
|
-
let activeChecks = 0;
|
|
58
|
-
let boundaryCleaned = false;
|
|
59
|
-
let runtimeClosed = false;
|
|
60
|
-
const daemonCleanupCalls = [];
|
|
61
|
-
const cleanupOrder = [];
|
|
62
|
-
try {
|
|
63
|
-
const code = await runHeadlessExec({
|
|
64
|
-
message: 'fix it',
|
|
65
|
-
provider: 'openai-oauth',
|
|
66
|
-
model: 'gpt-test',
|
|
67
|
-
effort: 'high',
|
|
68
|
-
fast: true,
|
|
69
|
-
usageLogPath,
|
|
70
|
-
idlePollMs: 1,
|
|
71
|
-
write: (text) => output.push(text),
|
|
72
|
-
writeErr: (text) => errors.push(text),
|
|
73
|
-
boundaryFactory: () => ({
|
|
74
|
-
runtimeRoot: join(root, 'runtime-root'),
|
|
75
|
-
loadConfig: () => ({ providers: { 'openai-oauth': { enabled: true } } }),
|
|
76
|
-
cleanup: () => {
|
|
77
|
-
boundaryCleaned = true;
|
|
78
|
-
cleanupOrder.push('boundary');
|
|
79
|
-
},
|
|
80
|
-
}),
|
|
81
|
-
runtimeFactory: async (options) => {
|
|
82
|
-
runtimeOptions.push(options);
|
|
83
|
-
return {
|
|
84
|
-
id: 'sess_exec_test',
|
|
85
|
-
model: 'gpt-test',
|
|
86
|
-
clientHostPid: 123,
|
|
87
|
-
ask: async (_prompt, optionsForAsk) => {
|
|
88
|
-
optionsForAsk.onUsageDelta({
|
|
89
|
-
model: 'gpt-fallback',
|
|
90
|
-
deltaInput: 11,
|
|
91
|
-
deltaCachedRead: 7,
|
|
92
|
-
deltaCacheWrite: 3,
|
|
93
|
-
deltaOutput: 5,
|
|
94
|
-
});
|
|
95
|
-
return { result: { content: 'done' } };
|
|
96
|
-
},
|
|
97
|
-
close: async () => {
|
|
98
|
-
runtimeClosed = true;
|
|
99
|
-
cleanupOrder.push('runtime');
|
|
100
|
-
},
|
|
101
|
-
};
|
|
102
|
-
},
|
|
103
|
-
memoryRuntimeCleanup: async () => {
|
|
104
|
-
cleanupOrder.push('memory');
|
|
105
|
-
},
|
|
106
|
-
daemonRuntimeCleanup: async (runtimeRoot, options) => {
|
|
107
|
-
daemonCleanupCalls.push({ runtimeRoot, options });
|
|
108
|
-
cleanupOrder.push('daemon');
|
|
109
|
-
},
|
|
110
|
-
hasActiveTasks: (scope) => {
|
|
111
|
-
activeScopes.push(scope);
|
|
112
|
-
activeChecks += 1;
|
|
113
|
-
return false;
|
|
114
|
-
},
|
|
115
|
-
installSignalCleanupFn: () => ({ uninstall() {} }),
|
|
116
|
-
});
|
|
117
|
-
|
|
118
|
-
assert.equal(code, 0);
|
|
119
|
-
assert.deepEqual(output, ['done\n']);
|
|
120
|
-
assert.deepEqual(errors, []);
|
|
121
|
-
assert.equal(runtimeOptions[0].approvalMode, 'implicit');
|
|
122
|
-
assert.equal(runtimeOptions[0].disallowDelegation, true);
|
|
123
|
-
assert.deepEqual(runtimeOptions[0].initialConfig.workflow, { active: 'headless' });
|
|
124
|
-
assert.equal(runtimeOptions[0].autoWakeCompletions, false);
|
|
125
|
-
assert.equal(runtimeOptions[0].toolMode, 'full');
|
|
126
|
-
assert.deepEqual(activeScopes[0], {
|
|
127
|
-
callerSessionId: 'sess_exec_test',
|
|
128
|
-
clientHostPid: 123,
|
|
129
|
-
});
|
|
130
|
-
assert.equal(boundaryCleaned, true);
|
|
131
|
-
assert.equal(runtimeClosed, true);
|
|
132
|
-
assert.deepEqual(cleanupOrder, ['runtime', 'daemon', 'memory', 'boundary']);
|
|
133
|
-
assert.deepEqual(daemonCleanupCalls, [{
|
|
134
|
-
runtimeRoot: join(root, 'runtime-root'),
|
|
135
|
-
options: { waitForExit: true, timeoutMs: 8_000 },
|
|
136
|
-
}]);
|
|
137
|
-
const usage = JSON.parse(readFileSync(usageLogPath, 'utf8'));
|
|
138
|
-
assert.deepEqual(usage.sessions[0].models, ['gpt-test', 'gpt-fallback']);
|
|
139
|
-
assert.deepEqual(usage.totals, {
|
|
140
|
-
inputTokens: 11,
|
|
141
|
-
cacheTokens: 7,
|
|
142
|
-
cacheWriteTokens: 3,
|
|
143
|
-
outputTokens: 5,
|
|
144
|
-
toolCallCountApprox: 0,
|
|
145
|
-
});
|
|
146
|
-
} finally {
|
|
147
|
-
rmSync(root, { recursive: true, force: true });
|
|
148
|
-
}
|
|
149
|
-
});
|
|
150
|
-
|
|
151
|
-
test('headless exec preserves the pristine root when isolated daemon shutdown fails', async () => {
|
|
152
|
-
const errors = [];
|
|
153
|
-
let cleanupOptions = null;
|
|
154
|
-
const code = await runHeadlessExec({
|
|
155
|
-
message: 'done',
|
|
156
|
-
provider: 'openai-oauth',
|
|
157
|
-
model: 'gpt-test',
|
|
158
|
-
usageLogPath: '',
|
|
159
|
-
write() {},
|
|
160
|
-
writeErr: (text) => errors.push(text),
|
|
161
|
-
boundaryFactory: () => ({
|
|
162
|
-
runtimeRoot: '/isolated/runtime',
|
|
163
|
-
loadConfig: () => ({ providers: { 'openai-oauth': { enabled: true } } }),
|
|
164
|
-
cleanup: (options) => { cleanupOptions = options; },
|
|
165
|
-
}),
|
|
166
|
-
runtimeFactory: async () => ({
|
|
167
|
-
id: 'sess_cleanup_failure',
|
|
168
|
-
model: 'gpt-test',
|
|
169
|
-
clientHostPid: 123,
|
|
170
|
-
async ask() { return { result: { content: 'done' } }; },
|
|
171
|
-
async close() {},
|
|
172
|
-
}),
|
|
173
|
-
daemonRuntimeCleanup: async () => { throw new Error('daemon stuck'); },
|
|
174
|
-
memoryRuntimeCleanup: async () => {},
|
|
175
|
-
hasActiveTasks: () => false,
|
|
176
|
-
installSignalCleanupFn: () => ({ uninstall() {} }),
|
|
177
|
-
});
|
|
178
|
-
|
|
179
|
-
assert.equal(code, 1);
|
|
180
|
-
assert.deepEqual(cleanupOptions, { preserveRoot: true });
|
|
181
|
-
assert.ok(errors.some((line) => line.includes('shutdown failed: daemon stuck')));
|
|
182
|
-
});
|
|
183
|
-
|
|
184
|
-
test('headless exec flushes the usage snapshot mid-session, before any exit path', async () => {
|
|
185
|
-
const root = mkdtempSync(join(tmpdir(), 'mixdog-headless-usage-flush-test-'));
|
|
186
|
-
const usageLogPath = join(root, 'usage.json');
|
|
187
|
-
// What a killed run would have left behind: read the file while `ask` is
|
|
188
|
-
// still running. Before the per-response flush this was always absent, so
|
|
189
|
-
// an agent-timeout trial reported no token spend at all.
|
|
190
|
-
let midSession = null;
|
|
191
|
-
try {
|
|
192
|
-
const code = await runHeadlessExec({
|
|
193
|
-
message: 'fix it',
|
|
194
|
-
provider: 'openai-oauth',
|
|
195
|
-
model: 'gpt-test',
|
|
196
|
-
effort: 'high',
|
|
197
|
-
fast: true,
|
|
198
|
-
usageLogPath,
|
|
199
|
-
idlePollMs: 1,
|
|
200
|
-
write: () => {},
|
|
201
|
-
writeErr: () => {},
|
|
202
|
-
boundaryFactory: () => ({
|
|
203
|
-
loadConfig: () => ({ providers: { 'openai-oauth': { enabled: true } } }),
|
|
204
|
-
cleanup: () => {},
|
|
205
|
-
}),
|
|
206
|
-
runtimeFactory: async () => ({
|
|
207
|
-
id: 'sess_flush_test',
|
|
208
|
-
model: 'gpt-test',
|
|
209
|
-
clientHostPid: 123,
|
|
210
|
-
ask: async (_prompt, options) => {
|
|
211
|
-
options.onUsageDelta({
|
|
212
|
-
deltaInput: 9,
|
|
213
|
-
deltaCachedRead: 4,
|
|
214
|
-
deltaCacheWrite: 2,
|
|
215
|
-
deltaOutput: 6,
|
|
216
|
-
});
|
|
217
|
-
try {
|
|
218
|
-
midSession = JSON.parse(readFileSync(usageLogPath, 'utf8'));
|
|
219
|
-
} catch (error) {
|
|
220
|
-
midSession = { error: error?.message ?? String(error) };
|
|
221
|
-
}
|
|
222
|
-
return { result: { content: 'done' } };
|
|
223
|
-
},
|
|
224
|
-
close: async () => {},
|
|
225
|
-
}),
|
|
226
|
-
hasActiveTasks: () => false,
|
|
227
|
-
installSignalCleanupFn: () => ({ uninstall() {} }),
|
|
228
|
-
});
|
|
229
|
-
|
|
230
|
-
assert.equal(code, 0);
|
|
231
|
-
assert.deepEqual(midSession?.totals, {
|
|
232
|
-
inputTokens: 9,
|
|
233
|
-
cacheTokens: 4,
|
|
234
|
-
cacheWriteTokens: 2,
|
|
235
|
-
outputTokens: 6,
|
|
236
|
-
toolCallCountApprox: 0,
|
|
237
|
-
});
|
|
238
|
-
} finally {
|
|
239
|
-
rmSync(root, { recursive: true, force: true });
|
|
240
|
-
}
|
|
241
|
-
});
|
|
242
|
-
|
|
243
|
-
test('headless exec answers an arrived completion and exits without waiting on live work', async () => {
|
|
244
|
-
const output = [];
|
|
245
|
-
const errors = [];
|
|
246
|
-
const prompts = [];
|
|
247
|
-
let closeOptions = null;
|
|
248
|
-
let notificationListener = null;
|
|
249
|
-
const code = await runHeadlessExec({
|
|
250
|
-
message: 'start the long task',
|
|
251
|
-
provider: 'openai-oauth',
|
|
252
|
-
model: 'gpt-test',
|
|
253
|
-
usageLogPath: '',
|
|
254
|
-
write: (text) => output.push(text),
|
|
255
|
-
writeErr: (text) => errors.push(text),
|
|
256
|
-
boundaryFactory: () => ({
|
|
257
|
-
loadConfig: () => ({ providers: { 'openai-oauth': { enabled: true } } }),
|
|
258
|
-
cleanup() {},
|
|
259
|
-
}),
|
|
260
|
-
runtimeFactory: async () => ({
|
|
261
|
-
id: 'sess_headless_drain',
|
|
262
|
-
model: 'gpt-test',
|
|
263
|
-
clientHostPid: 123,
|
|
264
|
-
onNotification(listener) {
|
|
265
|
-
notificationListener = listener;
|
|
266
|
-
return () => { notificationListener = null; };
|
|
267
|
-
},
|
|
268
|
-
async ask(prompt) {
|
|
269
|
-
prompts.push(prompt);
|
|
270
|
-
if (prompts.length === 1) {
|
|
271
|
-
notificationListener?.({
|
|
272
|
-
content: 'background task completed',
|
|
273
|
-
meta: { status: 'completed', execution_id: 'job_drain' },
|
|
274
|
-
});
|
|
275
|
-
return { result: { content: 'task started' } };
|
|
276
|
-
}
|
|
277
|
-
return { result: { content: 'final result' } };
|
|
278
|
-
},
|
|
279
|
-
async close(reason, options) { closeOptions = options ?? null; },
|
|
280
|
-
}),
|
|
281
|
-
// The job the model left running never ends — a server it was asked to
|
|
282
|
-
// keep up. Exit must not wait for it, and must not reap it either: this
|
|
283
|
-
// test hangs on a regression instead of failing slowly.
|
|
284
|
-
hasActiveTasks: () => true,
|
|
285
|
-
installSignalCleanupFn: () => ({ uninstall() {} }),
|
|
286
|
-
});
|
|
287
|
-
|
|
288
|
-
assert.equal(code, 0);
|
|
289
|
-
assert.deepEqual(errors, []);
|
|
290
|
-
assert.deepEqual(prompts, ['start the long task', '']);
|
|
291
|
-
assert.deepEqual(output, ['final result\n']);
|
|
292
|
-
assert.deepEqual(closeOptions, { keepBackgroundWork: true });
|
|
293
|
-
});
|
|
294
|
-
|
|
295
|
-
test('headless exec emits a timestamped JSONL lifecycle and exact tool count', async () => {
|
|
296
|
-
const root = mkdtempSync(join(tmpdir(), 'mixdog-headless-json-test-'));
|
|
297
|
-
const usageLogPath = join(root, 'usage.json');
|
|
298
|
-
const output = [];
|
|
299
|
-
const errors = [];
|
|
300
|
-
let notificationListener = null;
|
|
301
|
-
try {
|
|
302
|
-
const code = await runHeadlessExec({
|
|
303
|
-
message: 'fix it',
|
|
304
|
-
provider: 'openai-oauth',
|
|
305
|
-
model: 'gpt-test',
|
|
306
|
-
effort: 'high',
|
|
307
|
-
fast: true,
|
|
308
|
-
json: true,
|
|
309
|
-
usageLogPath,
|
|
310
|
-
write: (text) => output.push(text),
|
|
311
|
-
writeErr: (text) => errors.push(text),
|
|
312
|
-
boundaryFactory: () => ({
|
|
313
|
-
loadConfig: () => ({ providers: { 'openai-oauth': { enabled: true } } }),
|
|
314
|
-
cleanup() {},
|
|
315
|
-
}),
|
|
316
|
-
runtimeFactory: async () => ({
|
|
317
|
-
id: 'sess_json_test',
|
|
318
|
-
provider: 'openai-oauth',
|
|
319
|
-
model: 'gpt-test',
|
|
320
|
-
effort: 'high',
|
|
321
|
-
fast: true,
|
|
322
|
-
cwd: '/app',
|
|
323
|
-
clientHostPid: 123,
|
|
324
|
-
onNotification(listener) {
|
|
325
|
-
notificationListener = listener;
|
|
326
|
-
return () => { notificationListener = null; };
|
|
327
|
-
},
|
|
328
|
-
async ask(_prompt, options) {
|
|
329
|
-
options.onProviderSendStarted();
|
|
330
|
-
options.onReasoningDelta('inspect first');
|
|
331
|
-
options.onUsageDelta({
|
|
332
|
-
deltaInput: 11,
|
|
333
|
-
deltaCachedRead: 7,
|
|
334
|
-
deltaCacheWrite: 3,
|
|
335
|
-
deltaOutput: 5,
|
|
336
|
-
});
|
|
337
|
-
const call = {
|
|
338
|
-
id: 'call_1',
|
|
339
|
-
name: 'shell',
|
|
340
|
-
arguments: { command: 'echo ok' },
|
|
341
|
-
};
|
|
342
|
-
options.onAssistantToolCallObserved(call);
|
|
343
|
-
await options.onToolCall(1, [call]);
|
|
344
|
-
options.onToolPhaseStarted();
|
|
345
|
-
const toolClock = Date.now();
|
|
346
|
-
options.onToolResult({
|
|
347
|
-
role: 'tool',
|
|
348
|
-
toolCallId: 'call_1',
|
|
349
|
-
content: 'ok\n',
|
|
350
|
-
__earlyNotify: true,
|
|
351
|
-
toolTiming: {
|
|
352
|
-
dispatchStartedAt: toolClock,
|
|
353
|
-
executionStartedAt: toolClock + 2,
|
|
354
|
-
executionCompletedAt: toolClock + 5,
|
|
355
|
-
},
|
|
356
|
-
});
|
|
357
|
-
options.onToolResult({
|
|
358
|
-
role: 'tool',
|
|
359
|
-
toolCallId: 'call_1',
|
|
360
|
-
content: 'ok\n',
|
|
361
|
-
toolKind: 'normal',
|
|
362
|
-
toolTiming: {
|
|
363
|
-
dispatchStartedAt: toolClock,
|
|
364
|
-
executionStartedAt: toolClock + 2,
|
|
365
|
-
executionCompletedAt: toolClock + 5,
|
|
366
|
-
postprocessStartedAt: toolClock + 6,
|
|
367
|
-
resultCompletedAt: toolClock + 8,
|
|
368
|
-
},
|
|
369
|
-
});
|
|
370
|
-
options.onToolPhaseCompleted({ iteration: 1, calls: 1, elapsedMs: 2 });
|
|
371
|
-
notificationListener?.({
|
|
372
|
-
content: 'background task completed',
|
|
373
|
-
meta: { status: 'progress' },
|
|
374
|
-
});
|
|
375
|
-
options.onProviderSendStarted();
|
|
376
|
-
options.onAssistantText('done');
|
|
377
|
-
options.onUsageDelta({
|
|
378
|
-
deltaInput: 5,
|
|
379
|
-
deltaCachedRead: 0,
|
|
380
|
-
deltaCacheWrite: 0,
|
|
381
|
-
deltaOutput: 2,
|
|
382
|
-
});
|
|
383
|
-
return { result: { content: 'done', stopReason: 'end_turn' } };
|
|
384
|
-
},
|
|
385
|
-
async close() {},
|
|
386
|
-
}),
|
|
387
|
-
hasActiveTasks: () => false,
|
|
388
|
-
installSignalCleanupFn: () => ({ uninstall() {} }),
|
|
389
|
-
});
|
|
390
|
-
|
|
391
|
-
assert.equal(code, 0);
|
|
392
|
-
assert.deepEqual(errors, []);
|
|
393
|
-
const events = output.join('').trim().split('\n').map((line) => JSON.parse(line));
|
|
394
|
-
assert.equal(events[0].type, 'thread.started');
|
|
395
|
-
assert.equal(events[1].type, 'turn.started');
|
|
396
|
-
assert.ok(events.every((event) => event.schema_version === 1 && event.timestamp));
|
|
397
|
-
const toolStarted = events.find(
|
|
398
|
-
(event) => event.type === 'item.started' && event.item?.type === 'tool_call',
|
|
399
|
-
);
|
|
400
|
-
const toolCompleted = events.find(
|
|
401
|
-
(event) => event.type === 'item.completed' && event.item?.id === 'call_1',
|
|
402
|
-
);
|
|
403
|
-
assert.equal(toolStarted.item.name, 'shell');
|
|
404
|
-
assert.equal(toolCompleted.item.output, 'ok\n');
|
|
405
|
-
assert.equal(toolCompleted.item.status, 'completed');
|
|
406
|
-
assert.ok(toolCompleted.item.duration_ms >= 0);
|
|
407
|
-
assert.equal(toolCompleted.item.timing.dispatch_ms, 2);
|
|
408
|
-
assert.equal(toolCompleted.item.timing.execution_ms, 3);
|
|
409
|
-
assert.equal(toolCompleted.item.timing.batch_wait_ms, 1);
|
|
410
|
-
assert.equal(toolCompleted.item.timing.postprocess_ms, 2);
|
|
411
|
-
assert.equal(events.filter(
|
|
412
|
-
(event) => event.type === 'item.completed' && event.item?.id === 'call_1',
|
|
413
|
-
).length, 1);
|
|
414
|
-
assert.ok(events.some((event) => event.type === 'notification'));
|
|
415
|
-
assert.equal(events.filter((event) => event.type === 'model.request.completed').length, 2);
|
|
416
|
-
const terminal = events.at(-1);
|
|
417
|
-
assert.equal(terminal.type, 'result');
|
|
418
|
-
assert.equal(terminal.subtype, 'success');
|
|
419
|
-
assert.equal(terminal.session_id, 'sess_json_test');
|
|
420
|
-
assert.equal(terminal.provider_requests, 2);
|
|
421
|
-
assert.equal(terminal.tool_calls, 1);
|
|
422
|
-
assert.deepEqual(terminal.usage, {
|
|
423
|
-
input_tokens: 16,
|
|
424
|
-
cached_input_tokens: 7,
|
|
425
|
-
cache_write_input_tokens: 3,
|
|
426
|
-
output_tokens: 7,
|
|
427
|
-
tool_calls: 1,
|
|
428
|
-
});
|
|
429
|
-
const usage = JSON.parse(readFileSync(usageLogPath, 'utf8'));
|
|
430
|
-
assert.equal(usage.totals.toolCallCountApprox, 1);
|
|
431
|
-
} finally {
|
|
432
|
-
rmSync(root, { recursive: true, force: true });
|
|
433
|
-
}
|
|
434
|
-
});
|
|
435
|
-
|
|
436
|
-
test('headless exec emits structured JSONL failure before returning exit 1', async () => {
|
|
437
|
-
const output = [];
|
|
438
|
-
const errors = [];
|
|
439
|
-
const code = await runHeadlessExec({
|
|
440
|
-
message: 'fail',
|
|
441
|
-
provider: 'openai-oauth',
|
|
442
|
-
model: 'gpt-test',
|
|
443
|
-
json: true,
|
|
444
|
-
usageLogPath: '',
|
|
445
|
-
write: (text) => output.push(text),
|
|
446
|
-
writeErr: (text) => errors.push(text),
|
|
447
|
-
boundaryFactory: () => ({
|
|
448
|
-
loadConfig: () => ({ providers: { 'openai-oauth': { enabled: true } } }),
|
|
449
|
-
cleanup() {},
|
|
450
|
-
}),
|
|
451
|
-
runtimeFactory: async () => ({
|
|
452
|
-
id: 'sess_json_failure',
|
|
453
|
-
model: 'gpt-test',
|
|
454
|
-
clientHostPid: 123,
|
|
455
|
-
async ask() {
|
|
456
|
-
throw new Error('boom');
|
|
457
|
-
},
|
|
458
|
-
async close() {},
|
|
459
|
-
}),
|
|
460
|
-
installSignalCleanupFn: () => ({ uninstall() {} }),
|
|
461
|
-
});
|
|
462
|
-
|
|
463
|
-
const events = output.join('').trim().split('\n').map((line) => JSON.parse(line));
|
|
464
|
-
assert.equal(code, 1);
|
|
465
|
-
assert.ok(events.some((event) => event.type === 'turn.failed'));
|
|
466
|
-
assert.equal(events.at(-1).type, 'result');
|
|
467
|
-
assert.equal(events.at(-1).subtype, 'error_during_execution');
|
|
468
|
-
assert.deepEqual(events.at(-1).errors, ['boom']);
|
|
469
|
-
assert.deepEqual(errors, ['mixdog: boom\n']);
|
|
470
|
-
});
|
|
471
|
-
|
|
472
|
-
test('headless exec exposes typed refusal as an API error result', async () => {
|
|
473
|
-
const output = [];
|
|
474
|
-
const code = await runHeadlessExec({
|
|
475
|
-
message: 'request',
|
|
476
|
-
provider: 'anthropic-oauth',
|
|
477
|
-
model: 'claude-test',
|
|
478
|
-
json: true,
|
|
479
|
-
usageLogPath: '',
|
|
480
|
-
write: (text) => output.push(text),
|
|
481
|
-
writeErr() {},
|
|
482
|
-
boundaryFactory: () => ({
|
|
483
|
-
loadConfig: () => ({ providers: { 'anthropic-oauth': { enabled: true } } }),
|
|
484
|
-
cleanup() {},
|
|
485
|
-
}),
|
|
486
|
-
runtimeFactory: async () => ({
|
|
487
|
-
id: 'sess_json_refusal',
|
|
488
|
-
model: 'claude-test',
|
|
489
|
-
clientHostPid: 123,
|
|
490
|
-
async ask() {
|
|
491
|
-
return {
|
|
492
|
-
result: {
|
|
493
|
-
content: 'Unable to respond.',
|
|
494
|
-
stopReason: 'refusal',
|
|
495
|
-
terminationReason: 'refusal',
|
|
496
|
-
},
|
|
497
|
-
};
|
|
498
|
-
},
|
|
499
|
-
async close() {},
|
|
500
|
-
}),
|
|
501
|
-
hasActiveTasks: () => false,
|
|
502
|
-
installSignalCleanupFn: () => ({ uninstall() {} }),
|
|
503
|
-
});
|
|
504
|
-
|
|
505
|
-
const terminal = output.join('').trim().split('\n').map((line) => JSON.parse(line)).at(-1);
|
|
506
|
-
assert.equal(code, 0);
|
|
507
|
-
assert.equal(terminal.subtype, 'success');
|
|
508
|
-
assert.equal(terminal.is_error, true);
|
|
509
|
-
assert.equal(terminal.stop_reason, 'refusal');
|
|
510
|
-
assert.equal(terminal.termination_reason, 'refusal');
|
|
511
|
-
});
|
|
512
|
-
|
|
513
|
-
test('--json is accepted for exec and rejected for the interactive command', () => {
|
|
514
|
-
const exec = classifyCliInvocation([
|
|
515
|
-
'exec',
|
|
516
|
-
'--provider', 'openai-oauth',
|
|
517
|
-
'--model', 'gpt-test',
|
|
518
|
-
'--json',
|
|
519
|
-
'fix it',
|
|
520
|
-
]);
|
|
521
|
-
assert.equal(exec.kind, 'exec');
|
|
522
|
-
assert.equal(exec.options.json, true);
|
|
523
|
-
|
|
524
|
-
const interactive = classifyCliInvocation(['--json']);
|
|
525
|
-
assert.equal(interactive.kind, 'error');
|
|
526
|
-
assert.equal(interactive.error, 'option --json is only supported for mixdog exec');
|
|
527
|
-
});
|
|
528
|
-
|
|
529
|
-
test('headless exec rejects workflow selection', () => {
|
|
530
|
-
const invocation = classifyCliInvocation([
|
|
531
|
-
'exec',
|
|
532
|
-
'--provider', 'openai-oauth',
|
|
533
|
-
'--model', 'gpt-test',
|
|
534
|
-
'--workflow', 'solo',
|
|
535
|
-
'fix it',
|
|
536
|
-
]);
|
|
537
|
-
assert.equal(invocation.kind, 'error');
|
|
538
|
-
assert.equal(invocation.error, 'option --workflow is not supported for mixdog exec');
|
|
539
|
-
});
|
|
@@ -1,57 +0,0 @@
|
|
|
1
|
-
import test from 'node:test';
|
|
2
|
-
import assert from 'node:assert/strict';
|
|
3
|
-
import { mkdtempSync, readFileSync, rmSync } from 'node:fs';
|
|
4
|
-
import { tmpdir } from 'node:os';
|
|
5
|
-
import { join } from 'node:path';
|
|
6
|
-
import { spawnSync } from 'node:child_process';
|
|
7
|
-
|
|
8
|
-
test('tool traces expose dispatch, execution, collection, and postprocess timing', () => {
|
|
9
|
-
const dir = mkdtempSync(join(tmpdir(), 'mixdog-tool-timing-'));
|
|
10
|
-
try {
|
|
11
|
-
const tracePath = join(dir, 'agent-trace.jsonl');
|
|
12
|
-
const child = spawnSync(process.execPath, ['--input-type=module', '-e', `
|
|
13
|
-
import { readFileSync } from 'node:fs';
|
|
14
|
-
import { traceAgentTool } from './src/runtime/agent/orchestrator/agent-trace-format.mjs';
|
|
15
|
-
import { drainAgentTrace } from './src/runtime/agent/orchestrator/agent-trace-io.mjs';
|
|
16
|
-
traceAgentTool({
|
|
17
|
-
sessionId: 'timing-test',
|
|
18
|
-
iteration: 1,
|
|
19
|
-
toolName: 'grep',
|
|
20
|
-
toolKind: 'builtin',
|
|
21
|
-
toolMs: 50,
|
|
22
|
-
resultKind: 'normal',
|
|
23
|
-
resultText: 'ok',
|
|
24
|
-
toolTiming: {
|
|
25
|
-
dispatchStartedAt: 100,
|
|
26
|
-
executionStartedAt: 110,
|
|
27
|
-
executionCompletedAt: 160,
|
|
28
|
-
postprocessStartedAt: 175,
|
|
29
|
-
resultCompletedAt: 190,
|
|
30
|
-
},
|
|
31
|
-
});
|
|
32
|
-
await drainAgentTrace();
|
|
33
|
-
const row = JSON.parse(readFileSync(process.env.MIXDOG_AGENT_TRACE_PATH, 'utf8').trim());
|
|
34
|
-
process.stdout.write(JSON.stringify(row.payload.timing));
|
|
35
|
-
`], {
|
|
36
|
-
cwd: process.cwd(),
|
|
37
|
-
encoding: 'utf8',
|
|
38
|
-
env: {
|
|
39
|
-
...process.env,
|
|
40
|
-
MIXDOG_AGENT_TRACE_PATH: tracePath,
|
|
41
|
-
MIXDOG_AGENT_TRACE_DISABLE: '',
|
|
42
|
-
MIXDOG_AGENT_TRACE_LOCAL_DISABLE: '',
|
|
43
|
-
MIXDOG_RUNTIME_ROOT: join(dir, 'no-service'),
|
|
44
|
-
},
|
|
45
|
-
});
|
|
46
|
-
assert.equal(child.status, 0, child.stderr);
|
|
47
|
-
assert.deepEqual(JSON.parse(child.stdout), {
|
|
48
|
-
dispatch_wait_ms: 10,
|
|
49
|
-
execution_ms: 50,
|
|
50
|
-
result_collection_wait_ms: 15,
|
|
51
|
-
postprocess_ms: 15,
|
|
52
|
-
total_ms: 90,
|
|
53
|
-
});
|
|
54
|
-
} finally {
|
|
55
|
-
rmSync(dir, { recursive: true, force: true });
|
|
56
|
-
}
|
|
57
|
-
});
|