mixdog 0.9.96 → 0.9.99
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/README.md +0 -17
- package/package.json +16 -8
- package/scripts/build-tui.mjs +6 -6
- package/scripts/{channel-daemon-stub.mjs → daemon-stub.mjs} +18 -12
- package/src/defaults/skills/setup/SKILL.md +1 -1
- package/src/lib/keychain-cjs.cjs +1 -1
- package/src/lib/mixdog-debug.cjs +2 -3
- package/src/rules/agent/30-explorer.md +43 -32
- package/src/rules/lead/01-general.md +1 -2
- package/src/rules/lead/lead-tool.md +0 -3
- package/src/rules/shared/01-tool.md +11 -10
- package/src/runtime/agent/orchestrator/agent-trace-io.mjs +4 -13
- package/src/runtime/agent/orchestrator/agent-trace.mjs +19 -5
- package/src/runtime/agent/orchestrator/mcp/client.mjs +81 -33
- package/src/runtime/agent/orchestrator/providers/grok-oauth.mjs +7 -0
- package/src/runtime/agent/orchestrator/providers/media-normalization.mjs +30 -3
- package/src/runtime/agent/orchestrator/providers/model-catalog.mjs +5 -3
- package/src/runtime/agent/orchestrator/providers/openai-ws-stream.mjs +3 -6
- package/src/runtime/agent/orchestrator/providers/provider-catalog-cache.mjs +23 -1
- package/src/runtime/agent/orchestrator/providers/registry.mjs +2 -2
- package/src/runtime/agent/orchestrator/session/agent-loop.mjs +8 -6
- package/src/runtime/agent/orchestrator/session/compact.mjs +1 -1
- package/src/runtime/agent/orchestrator/session/eager-dispatch.mjs +22 -4
- package/src/runtime/agent/orchestrator/session/loop/tool-exec.mjs +20 -2
- package/src/runtime/agent/orchestrator/session/loop/tool-helpers.mjs +4 -3
- package/src/runtime/agent/orchestrator/session/manager/ask-session.mjs +42 -26
- package/src/runtime/agent/orchestrator/session/manager/compaction-runner.mjs +2 -3
- package/src/runtime/agent/orchestrator/session/manager/pending-messages.mjs +44 -6
- package/src/runtime/agent/orchestrator/session/manager/prefetch-bridge.mjs +12 -6
- package/src/runtime/agent/orchestrator/session/manager/prompt-utils.mjs +2 -1
- package/src/runtime/agent/orchestrator/session/manager/session-lock.mjs +38 -2
- package/src/runtime/agent/orchestrator/session/store/serialize.mjs +3 -6
- package/src/runtime/agent/orchestrator/session/store/summary-cache.mjs +10 -3
- package/src/runtime/agent/orchestrator/session/store-summary-index.mjs +4 -0
- package/src/runtime/agent/orchestrator/session/store-summary-reader.mjs +59 -42
- package/src/runtime/agent/orchestrator/session/tool-batch.mjs +3 -2
- package/src/runtime/agent/orchestrator/tools/builtin/bash-tool.mjs +12 -22
- package/src/runtime/agent/orchestrator/tools/builtin/binary-file.mjs +92 -19
- package/src/runtime/agent/orchestrator/tools/builtin/fs-reachability.mjs +2 -2
- package/src/runtime/agent/orchestrator/tools/builtin/list-tool.mjs +2 -0
- package/src/runtime/agent/orchestrator/tools/builtin/read-image-resize.mjs +103 -12
- package/src/runtime/agent/orchestrator/tools/builtin/read-image.mjs +4 -4
- package/src/runtime/agent/orchestrator/tools/builtin/read-single-tool.mjs +80 -30
- package/src/runtime/agent/orchestrator/tools/builtin/read-special-files.mjs +10 -30
- package/src/runtime/agent/orchestrator/tools/builtin/read-tool.mjs +41 -22
- package/src/runtime/agent/orchestrator/tools/builtin/rg-runner.mjs +6 -2
- package/src/runtime/agent/orchestrator/tools/code-graph/dispatch.mjs +55 -11
- package/src/runtime/agent/orchestrator/tools/code-graph/search-references.mjs +12 -1
- package/src/runtime/agent/orchestrator/tools/code-graph/search.mjs +55 -18
- package/src/runtime/agent/orchestrator/tools/code-graph-tool-defs.mjs +1 -0
- package/src/runtime/agent/orchestrator/tools/env-scrub.mjs +1 -2
- package/src/runtime/agent/orchestrator/tools/lib/pwsh-standby-pool.mjs +2 -2
- package/src/runtime/agent/orchestrator/tools/patch/native-server.mjs +1 -1
- package/src/runtime/agent/orchestrator/tools/patch/orchestrator.mjs +94 -105
- package/src/runtime/agent/orchestrator/tools/patch-tool-defs.mjs +2 -1
- package/src/runtime/agent/orchestrator/tools/patch.mjs +1 -1
- package/src/runtime/agent/orchestrator/tools/shell-command.mjs +7 -1
- package/src/runtime/attachments/pdf-extract.mjs +83 -0
- package/src/runtime/attachments/store.mjs +515 -0
- package/src/runtime/attachments/store.test.mjs +188 -0
- package/src/runtime/channels/lib/config.mjs +33 -33
- package/src/runtime/channels/lib/inbound-handler.mjs +7 -7
- package/src/runtime/channels/lib/interaction-handlers.mjs +4 -194
- package/src/runtime/channels/lib/memory-client.mjs +31 -157
- package/src/runtime/channels/lib/output-forwarder.mjs +16 -16
- package/src/runtime/channels/lib/owned-runtime.mjs +60 -60
- package/src/runtime/channels/lib/owner-heartbeat.mjs +1 -1
- package/src/runtime/channels/lib/{backend-dispatch.mjs → provider-dispatch.mjs} +6 -6
- package/src/runtime/channels/lib/runtime-paths.mjs +5 -34
- package/src/runtime/channels/lib/status-snapshot.mjs +3 -3
- package/src/runtime/channels/lib/tool-dispatch.mjs +4 -4
- package/src/runtime/channels/lib/worker-bootstrap.mjs +1 -1
- package/src/runtime/channels/lib/worker-ipc.mjs +4 -70
- package/src/runtime/channels/lib/worker-main.mjs +39 -58
- package/src/runtime/channels/{backends → providers}/discord-access.mjs +1 -1
- package/src/runtime/channels/{backends → providers}/discord-gateway.mjs +1 -1
- package/src/runtime/channels/{backends → providers}/discord.mjs +3 -3
- package/src/runtime/channels/{backends → providers}/telegram.mjs +12 -12
- package/src/runtime/channels/tool-defs.mjs +1 -1
- package/src/runtime/memory/index.mjs +72 -211
- package/src/runtime/memory/lib/agent-ipc.mjs +9 -8
- package/src/runtime/memory/lib/cycle-llm-adapters.mjs +2 -2
- package/src/runtime/memory/lib/http-router.mjs +1 -6
- package/src/runtime/memory/lib/http-wire.mjs +39 -4
- package/src/runtime/memory/lib/memory-config-flags.mjs +1 -1
- package/src/runtime/memory/lib/memory-cycle2-shared.mjs +4 -1
- package/src/runtime/memory/lib/memory-daemon-lifecycle.mjs +5 -0
- package/src/runtime/memory/lib/memory-log.mjs +1 -1
- package/src/runtime/memory/lib/memory-process-lock.mjs +5 -60
- package/src/runtime/memory/lib/memory.mjs +1 -1
- package/src/runtime/memory/lib/session-ingest.mjs +1 -1
- package/src/runtime/search/index.mjs +2 -2
- package/src/runtime/shared/abort-race.mjs +70 -0
- package/src/runtime/shared/child-spawn-gate.mjs +101 -63
- package/src/runtime/shared/config.mjs +11 -24
- package/src/runtime/shared/json-metrics.mjs +94 -0
- package/src/runtime/shared/llm/index.mjs +1 -1
- package/src/runtime/shared/owner-fair-gate.mjs +134 -0
- package/src/runtime/shared/resource-admission.mjs +6 -5
- package/src/runtime/shared/tool-execution-owner.mjs +12 -0
- package/src/runtime/shared/tool-result-summary.mjs +1 -1
- package/src/runtime/shared/tool-surface.mjs +91 -22
- package/src/runtime/shared/tool-workload-gates.mjs +29 -0
- package/src/runtime/shared/turn-snapshot.mjs +30 -2
- package/src/runtime/shared/turn-worktree-snapshot.mjs +19 -0
- package/src/session-runtime/channel-config-api.mjs +5 -5
- package/src/session-runtime/config-lifecycle.mjs +27 -27
- package/src/session-runtime/lifecycle-api.mjs +22 -3
- package/src/session-runtime/memory-runtime-prewarm.mjs +15 -0
- package/src/session-runtime/prewarm.mjs +1 -1
- package/src/session-runtime/provider-models.mjs +12 -2
- package/src/session-runtime/quick-model-rows.mjs +38 -7
- package/src/session-runtime/quick-search-models.mjs +15 -17
- package/src/session-runtime/remote-control.mjs +6 -7
- package/src/session-runtime/runtime-core.mjs +61 -32
- package/src/session-runtime/runtime-tunables.mjs +9 -6
- package/src/session-runtime/self-update.mjs +1 -1
- package/src/session-runtime/session-lifecycle.mjs +36 -15
- package/src/session-runtime/session-title.mjs +28 -4
- package/src/session-runtime/session-turn-api.mjs +55 -15
- package/src/session-runtime/settings-api.mjs +7 -7
- package/src/standalone/agent-dispatch-broker.mjs +1 -1
- package/src/standalone/channel-admin.mjs +42 -53
- package/src/standalone/{channel-daemon-client.mjs → channel-client.mjs} +16 -18
- package/src/standalone/{channel-daemon-transport.mjs → channel-transport.mjs} +73 -58
- package/src/standalone/channel-worker.mjs +124 -647
- package/src/standalone/{backend-daemon.mjs → daemon.mjs} +190 -132
- package/src/standalone/memory-runtime-proxy.mjs +58 -1
- package/src/standalone/{engine-daemon-client.mjs → session-client.mjs} +151 -86
- package/src/standalone/session-protocol.mjs +9 -4
- package/src/standalone/session-runtime-pool.mjs +393 -0
- package/src/standalone/session-runtime-record.mjs +20 -0
- package/src/standalone/session-runtime-worker.mjs +203 -0
- package/src/standalone/{engine-daemon-service.mjs → session-service.mjs} +192 -157
- package/src/standalone/{engine-daemon-transport.mjs → session-transport.mjs} +142 -63
- package/src/standalone/session-wire.mjs +55 -0
- package/src/tui/App.jsx +4 -4
- package/src/tui/app/app-format.mjs +1 -1
- package/src/tui/app/channel-pickers.mjs +41 -41
- package/src/tui/app/input-parsers.mjs +2 -2
- package/src/tui/app/onboarding-steps.mjs +2 -2
- package/src/tui/app/project-picker.mjs +8 -8
- package/src/tui/app/prompt-submit.mjs +15 -17
- package/src/tui/app/settings-picker.mjs +25 -25
- package/src/tui/app/use-prompt-handlers.mjs +7 -3
- package/src/tui/app/use-prompt-queue-history.mjs +1 -1
- package/src/tui/app/use-transcript-scroll.mjs +1 -1
- package/src/tui/components/ToolExecution.jsx +1 -1
- package/src/tui/components/prompt-input/restore-policy.mjs +1 -1
- package/src/tui/components/tool-execution/surface-detail.mjs +1 -1
- package/src/tui/dist/index.mjs +403 -170
- package/src/tui/hooks/useSession.mjs +16 -0
- package/src/tui/index.jsx +11 -11
- package/src/tui/paste-attachments.mjs +13 -7
- package/src/tui/prompt-history-store.mjs +52 -4
- package/src/tui/{engine → session}/agent-envelope.mjs +3 -3
- package/src/tui/{engine → session}/agent-job-feed.mjs +9 -5
- package/src/tui/{engine → session}/boot-profile.mjs +2 -2
- package/src/tui/{engine → session}/context-state.mjs +3 -3
- package/src/tui/{engine → session}/labels.mjs +2 -2
- package/src/tui/{engine → session}/live-share.mjs +28 -14
- package/src/tui/{engine → session}/notice-text.mjs +2 -2
- package/src/tui/{engine → session}/notification-plan.mjs +3 -3
- package/src/tui/session/oauth-flows.mjs +140 -0
- package/src/tui/{engine → session}/prompt-history.mjs +3 -3
- package/src/tui/{engine → session}/queue-helpers.mjs +8 -8
- package/src/tui/{engine → session}/render-timing.mjs +2 -2
- package/src/tui/{engine → session}/session-api-ext.mjs +42 -21
- package/src/tui/{engine → session}/session-api.mjs +17 -61
- package/src/tui/{engine → session}/session-flow.mjs +5 -3
- package/src/tui/{engine → session}/session-stats.mjs +3 -3
- package/src/tui/{engine → session}/tool-approval.mjs +3 -3
- package/src/tui/{engine → session}/tool-call-fields.mjs +2 -2
- package/src/tui/{engine → session}/tool-card-results.mjs +4 -4
- package/src/tui/{engine → session}/tool-result-status.mjs +5 -5
- package/src/tui/{engine → session}/tool-result-text.mjs +3 -3
- package/src/tui/{engine → session}/transcript-spill.mjs +34 -34
- package/src/tui/{engine → session}/turn.mjs +29 -107
- package/src/tui/{engine-local-session.mjs → session-local.mjs} +72 -78
- package/src/tui/session.mjs +22 -0
- package/src/ui/statusline-segments.mjs +1 -1
- package/src/ui/statusline.mjs +1 -1
- package/vendor/ink/build/components/App.js +10 -1
- package/src/session-runtime/memory-daemon-probe.mjs +0 -61
- package/src/standalone/engine-daemon-protocol.mjs +0 -32
- package/src/tui/engine/turn-watchdog.mjs +0 -92
- package/src/tui/engine.mjs +0 -22
- package/src/tui/hooks/useEngine.mjs +0 -16
- /package/src/runtime/agent/orchestrator/session/compact/{engine.mjs → runner.mjs} +0 -0
- /package/src/runtime/channels/{backends → providers}/discord-attachments.mjs +0 -0
- /package/src/tui/{engine → session}/agent-response-tail.mjs +0 -0
- /package/src/tui/{engine → session}/frame-batched-store.mjs +0 -0
- /package/src/tui/{engine → session}/tui-steering-persist.mjs +0 -0
package/README.md
CHANGED
|
@@ -65,23 +65,6 @@ measured cache writes; the archived OpenAI runs did not retain
|
|
|
65
65
|
artifacts, the exact run commands, and the metric scripts that recompute
|
|
66
66
|
every number above live under `benchmarks/terminal-bench-2.1/`.
|
|
67
67
|
|
|
68
|
-
## Boot overhead — measured, not estimated
|
|
69
|
-
|
|
70
|
-
Fixed cost of the default system prompt + tool schemas, measured with a
|
|
71
|
-
minimal one-turn ping ("hi"). Every number below is what the provider's own
|
|
72
|
-
usage accounting reported for the first API call — same tokenizer on both
|
|
73
|
-
sides of each row, no offline estimates (2026-08).
|
|
74
|
-
|
|
75
|
-
| Tokenizer | Baseline harness | First-call prompt | mixdog Lead | mixdog worker agent |
|
|
76
|
-
|-----------|------------------|------------------:|------------:|--------------------:|
|
|
77
|
-
| Anthropic — pristine containers, TB2.1 harness | Claude Code 2.1.220 | 19,149 tok (median, n=89) | **7,295 (−62%)** | 4,856 (−75%) |
|
|
78
|
-
| OpenAI — same host, same model (`gpt-5.6-sol`) | Codex CLI 0.145.0 | 17,706 tok | **7,488 (−58%)** | 4,616 (−74%) |
|
|
79
|
-
|
|
80
|
-
The Lead surface carries the full default rule set, skills manifest, and
|
|
81
|
-
per-user profile; spawning a sub-agent costs a flat ~4.6–4.9k tokens. Low
|
|
82
|
-
boot overhead compounds: it is re-read on every request, so it is the
|
|
83
|
-
baseline of every cache write, compaction, and long-session turn.
|
|
84
|
-
|
|
85
68
|
## Why mixdog
|
|
86
69
|
|
|
87
70
|
**Maximum performance at minimum cost**
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "mixdog",
|
|
3
|
-
"version": "0.9.
|
|
3
|
+
"version": "0.9.99",
|
|
4
4
|
"private": false,
|
|
5
5
|
"type": "module",
|
|
6
6
|
"description": "Standalone mixdog coding-agent CLI/TUI workspace.",
|
|
@@ -71,36 +71,42 @@
|
|
|
71
71
|
"test:agent-job-views": "node --test scripts/agent-job-terminal-view-test.mjs",
|
|
72
72
|
"test:agent-fanout": "node scripts/agent-parallel-smoke.mjs && node --test scripts/agent-route-batch-test.mjs scripts/execution-completion-dedup-test.mjs",
|
|
73
73
|
"test:toolcall": "node --test scripts/toolcall-args-test.mjs",
|
|
74
|
-
"test:tool-batching": "node --test scripts/eager-patch-shell-order-test.mjs scripts/session-bench-batching-test.mjs",
|
|
74
|
+
"test:tool-batching": "node --test scripts/eager-patch-shell-order-test.mjs scripts/session-bench-batching-test.mjs scripts/tool-cwd-rebind-test.mjs",
|
|
75
75
|
"test:shipmode": "node --test scripts/ship-mode-test.mjs",
|
|
76
76
|
"test:shellhardening": "node --test scripts/shell-hardening-test.mjs scripts/shell-failure-diagnostics-test.mjs scripts/windows-hide-spawn-options-test.mjs scripts/shell-job-session-scope-test.mjs",
|
|
77
77
|
"test:placeholder": "node --test scripts/compacted-placeholder-scrub-test.mjs",
|
|
78
78
|
"test:providers": "node --test scripts/provider-toolcall-test.mjs scripts/provider-contract-test.mjs scripts/provider-stream-stall-test.mjs scripts/provider-stream-outcome-test.mjs scripts/stream-frame-fault-matrix-test.mjs scripts/gemini-provider-test.mjs scripts/anthropic-transport-policy-test.mjs scripts/anthropic-native-block-replay-test.mjs scripts/openai-oauth-ws-1006-retry-test.mjs scripts/stream-close-retry-test.mjs scripts/openai-end-turn-signal-test.mjs",
|
|
79
79
|
"test:provider-admission": "node --test scripts/provider-admission-scheduler-test.mjs scripts/provider-stream-json-test.mjs",
|
|
80
|
-
"test:resource-admission": "node --test scripts/resource-admission-test.mjs scripts/child-spawn-isolation-test.mjs",
|
|
80
|
+
"test:resource-admission": "node --test scripts/resource-admission-test.mjs scripts/child-spawn-isolation-test.mjs scripts/owner-fair-gate-test.mjs",
|
|
81
81
|
"test:deferred-tools": "node --test scripts/deferred-tool-loading-test.mjs",
|
|
82
82
|
"test:anthropic-oauth-race": "node --test scripts/anthropic-oauth-refresh-race-test.mjs",
|
|
83
83
|
"test:grok-oauth-race": "node --test scripts/grok-oauth-refresh-race-test.mjs",
|
|
84
84
|
"test:atomiclock": "node --test scripts/atomic-lock-tryonce-test.mjs",
|
|
85
85
|
"test:keychain": "node --test scripts/keychain-prewarm-test.mjs",
|
|
86
|
+
"test:memory-leaks": "node --expose-gc --test scripts/memory-retention-test.mjs scripts/memory-worker-stability-test.mjs scripts/session-transport-test.mjs apps/desktop/src/renderer/memory-retention.test.mjs",
|
|
86
87
|
"test:memory-routing": "node --test scripts/memory-cycle-routing-test.mjs scripts/maintenance-default-routes-test.mjs scripts/embedding-worker-exit-test.mjs scripts/embedding-runtime-prune-test.mjs",
|
|
87
88
|
"test:embedding-runtime": "node --test scripts/embedding-runtime-prune-test.mjs scripts/memory-pg-recovery-test.mjs && node scripts/verify-embedding-runtime.mjs",
|
|
88
89
|
"test:embedding-runtime:core": "node --test scripts/embedding-runtime-prune-test.mjs scripts/memory-pg-recovery-test.mjs && node scripts/verify-embedding-runtime.mjs --core",
|
|
89
90
|
"test:embedding-runtime:warmup": "node scripts/verify-embedding-runtime.mjs --warmup",
|
|
91
|
+
"prepare:code-graph-test": "node scripts/prepare-code-graph-test.mjs",
|
|
90
92
|
"test:code-graph-dispatch": "node --test scripts/code-graph-dispatch-test.mjs",
|
|
91
93
|
"test:tui-queue": "node --test scripts/submit-commandbusy-race-test.mjs scripts/steering-drain-buckets-test.mjs scripts/abort-recovery-test.mjs scripts/message-rewind-test.mjs scripts/execution-pending-resume-kick-test.mjs scripts/execution-resume-esc-integration-test.mjs scripts/pending-stale-injection-test.mjs",
|
|
92
94
|
"test:tui-input-render": "node --test scripts/prompt-immediate-render-test.mjs scripts/prompt-key-parity-test.mjs",
|
|
93
95
|
"test:tui-streaming-window": "node --test scripts/streaming-tail-window-test.mjs scripts/tui-store-frame-batch-test.mjs && node scripts/tui-transcript-jitter-harness.mjs",
|
|
94
96
|
"test:tui-ambiguous-width": "node --test scripts/tui-ambiguous-width-test.mjs",
|
|
95
97
|
"test:release-assets": "node --check scripts/verify-release-assets.mjs && node --check scripts/verify-release-assets-test.mjs && node --check scripts/deploy-workflow-test.mjs && node --check scripts/release-version-discipline-test.mjs && node --test scripts/verify-release-assets-test.mjs scripts/deploy-workflow-test.mjs scripts/release-version-discipline-test.mjs",
|
|
96
|
-
"test:release-focused": "npm run test:release-
|
|
98
|
+
"test:release-focused": "npm run test:release-focused:contracts && npm run test:release-focused:providers && npm run test:release-focused:compact && npm run test:release-focused:session",
|
|
99
|
+
"test:release-focused:contracts": "npm run test:release-assets && npm run test:tool-contracts && npm run test:tool-batching && npm run test:placeholder && npm run smoke:patch && npm run test:patch-binary-cache",
|
|
100
|
+
"test:release-focused:providers": "npm run test:providers && npm run test:deferred-tools && npm run test:compact && npm run test:context && npm run prepare:code-graph-test && node --test scripts/code-graph-root-federation-test.mjs scripts/code-graph-aggregate-cwd-test.mjs && npm run test:code-graph-dispatch && node --test scripts/code-graph-disk-hit-test.mjs",
|
|
101
|
+
"test:release-focused:compact": "npm run smoke:compact && npm run test:shellhardening",
|
|
102
|
+
"test:release-focused:session": "npm run test:project-registry && npm run test:session && npm run test:workflow-editor && npm run test:embedding-runtime && node --test scripts/tui-transcript-perf-test.mjs",
|
|
97
103
|
"test:native-edit-wire": "node --test scripts/native-edit-wire-test.mjs",
|
|
98
104
|
"test:patch-binary-cache": "node --test scripts/patch-binary-cache-test.mjs",
|
|
99
105
|
"test:patch-parity": "node --test scripts/v4a-parity-test.mjs",
|
|
100
106
|
"test:project-registry": "node --test scripts/project-registry-isolation-test.mjs",
|
|
101
|
-
"test:
|
|
102
|
-
"smoke:
|
|
103
|
-
"test:session": "node --test scripts/session-orphan-sweep-test.mjs scripts/interrupted-turn-history-test.mjs scripts/turn-checkpoint-crash-test.mjs scripts/turn-outcome-fault-matrix-test.mjs scripts/session-save-fault-store-test.mjs scripts/session-disk-authority-test.mjs scripts/session-load-cache-race-test.mjs scripts/agent-loop-complete-turn-test.mjs scripts/session-heartbeat-lifecycle-test.mjs scripts/remote-transition-order-test.mjs scripts/session-new-reset-test.mjs scripts/turn-ttft-critical-path-test.mjs",
|
|
107
|
+
"test:session-transport": "node --test scripts/fair-call-scheduler-test.mjs scripts/session-transport-test.mjs scripts/session-tui-parity-test.mjs scripts/session-recovery-test.mjs scripts/session-submit-delivery-test.mjs scripts/daemon-process-lifetime-test.mjs scripts/session-multipane-stress-test.mjs && node --test scripts/runtime-isolation-stress-test.mjs",
|
|
108
|
+
"smoke:session": "node scripts/session-smoke.mjs",
|
|
109
|
+
"test:session": "node --test scripts/runtime-turn-contract-test.mjs scripts/session-orphan-sweep-test.mjs scripts/interrupted-turn-history-test.mjs scripts/turn-checkpoint-crash-test.mjs scripts/turn-outcome-fault-matrix-test.mjs scripts/session-save-fault-store-test.mjs scripts/session-disk-authority-test.mjs scripts/session-load-cache-race-test.mjs scripts/agent-loop-complete-turn-test.mjs scripts/session-heartbeat-lifecycle-test.mjs scripts/remote-transition-order-test.mjs scripts/session-new-reset-test.mjs scripts/turn-ttft-critical-path-test.mjs",
|
|
104
110
|
"test:live-canary": "node --test scripts/live-canary-test.mjs",
|
|
105
111
|
"test:rebindtail": "node --test scripts/forwarder-rebind-tail-test.mjs scripts/channel-remote-format-test.mjs",
|
|
106
112
|
"test:workflow-editor": "node --test scripts/workflow-id-test.mjs scripts/workflow-pack-editor-test.mjs",
|
|
@@ -122,7 +128,7 @@
|
|
|
122
128
|
"bench:explore": "node scripts/explore-bench.mjs",
|
|
123
129
|
"bench:output-style": "node scripts/output-style-bench.mjs",
|
|
124
130
|
"bench:session-context": "node scripts/session-context-bench.mjs",
|
|
125
|
-
"bench:
|
|
131
|
+
"bench:session-transport": "node scripts/session-transport-bench.mjs",
|
|
126
132
|
"bench:tool-overhead": "node scripts/tool-overhead-microbench.mjs",
|
|
127
133
|
"bench:internal-comms": "node scripts/internal-comms-bench.mjs",
|
|
128
134
|
"audit:models": "node scripts/model-catalog-audit.mjs",
|
|
@@ -154,9 +160,11 @@
|
|
|
154
160
|
"pg": "^8.22.0",
|
|
155
161
|
"puppeteer-core": "^25.2.0",
|
|
156
162
|
"react": "^19.2.7",
|
|
163
|
+
"sharp": "^0.35.3",
|
|
157
164
|
"string-width": "^8.2.1",
|
|
158
165
|
"strip-ansi": "^7.2.0",
|
|
159
166
|
"tiktoken": "^1.0.22",
|
|
167
|
+
"unpdf": "^1.8.0",
|
|
160
168
|
"undici": "^8.5.0",
|
|
161
169
|
"wrap-ansi": "^10.0.0",
|
|
162
170
|
"ws": "^8.21.0",
|
package/scripts/build-tui.mjs
CHANGED
|
@@ -41,13 +41,13 @@ const sharedRuntimeExternalPlugin = {
|
|
|
41
41
|
},
|
|
42
42
|
};
|
|
43
43
|
|
|
44
|
-
const
|
|
45
|
-
name: 'mixdog-
|
|
44
|
+
const sessionClientExternalPlugin = {
|
|
45
|
+
name: 'mixdog-session-client-external',
|
|
46
46
|
setup(build) {
|
|
47
|
-
build.onResolve({ filter: /^\.\.\/standalone\/
|
|
47
|
+
build.onResolve({ filter: /^\.\.\/standalone\/session-client\.mjs$/ }, () => ({
|
|
48
48
|
// Keep this module outside the TUI bundle so its import.meta.url stays
|
|
49
|
-
// anchored in src/standalone, where
|
|
50
|
-
path: '../../standalone/
|
|
49
|
+
// anchored in src/standalone, where daemon.mjs actually lives.
|
|
50
|
+
path: '../../standalone/session-client.mjs',
|
|
51
51
|
external: true,
|
|
52
52
|
}));
|
|
53
53
|
},
|
|
@@ -90,7 +90,7 @@ await build({
|
|
|
90
90
|
plugins: [
|
|
91
91
|
mixdogInkAliasPlugin,
|
|
92
92
|
sharedRuntimeExternalPlugin,
|
|
93
|
-
|
|
93
|
+
sessionClientExternalPlugin,
|
|
94
94
|
],
|
|
95
95
|
logLevel: 'info',
|
|
96
96
|
});
|
|
@@ -1,17 +1,18 @@
|
|
|
1
|
-
// Stub
|
|
2
|
-
// src/standalone/
|
|
1
|
+
// Stub channel service for the flip smoke: same lifecycle contract as
|
|
2
|
+
// src/standalone/daemon.mjs (pid-verified singleton claim, HTTP+SSE
|
|
3
3
|
// transport, discovery file, ready handshake, client-grace self-shutdown) but
|
|
4
4
|
// with a STUB runtime instead of worker-main — no Discord token needed. The
|
|
5
5
|
// real channel-worker.mjs spawn-or-attach path forks THIS via
|
|
6
|
-
//
|
|
6
|
+
// MIXDOG_DAEMON_ENTRY, so the flip is exercised end to end.
|
|
7
7
|
process.env.MIXDOG_WORKER_MODE = process.env.MIXDOG_WORKER_MODE || '1';
|
|
8
8
|
|
|
9
9
|
import os from 'node:os';
|
|
10
10
|
import path from 'node:path';
|
|
11
|
-
import { mkdirSync, writeFileSync } from 'node:fs';
|
|
11
|
+
import { mkdirSync, rmSync, writeFileSync } from 'node:fs';
|
|
12
|
+
import { writeJsonAtomicSync } from '../src/runtime/shared/atomic-file.mjs';
|
|
12
13
|
import { claimSingletonOwner, releaseSingletonOwner } from '../src/runtime/shared/singleton-owner.mjs';
|
|
13
14
|
import { safeIpcSend } from '../src/runtime/shared/safe-ipc-send.mjs';
|
|
14
|
-
import {
|
|
15
|
+
import { createChannelTransport } from '../src/standalone/channel-transport.mjs';
|
|
15
16
|
|
|
16
17
|
function runtimeRoot() {
|
|
17
18
|
return process.env.MIXDOG_RUNTIME_ROOT
|
|
@@ -20,8 +21,8 @@ function runtimeRoot() {
|
|
|
20
21
|
}
|
|
21
22
|
const RUNTIME_ROOT = runtimeRoot();
|
|
22
23
|
const DATA_DIR = process.env.MIXDOG_DATA_DIR ? path.resolve(process.env.MIXDOG_DATA_DIR) : RUNTIME_ROOT;
|
|
23
|
-
const DISCOVERY_PATH = path.join(RUNTIME_ROOT, '
|
|
24
|
-
const OWNER_PATH = path.join(DATA_DIR, '
|
|
24
|
+
const DISCOVERY_PATH = path.join(RUNTIME_ROOT, 'daemon.json');
|
|
25
|
+
const OWNER_PATH = path.join(DATA_DIR, 'daemon-owner.json');
|
|
25
26
|
|
|
26
27
|
function log(line) { if (process.env.DAEMON_SMOKE_VERBOSE) process.stderr.write(`[stub-daemon] ${line}\n`); }
|
|
27
28
|
|
|
@@ -31,23 +32,24 @@ async function shutdown(reason, code = 0) {
|
|
|
31
32
|
if (shuttingDown) return;
|
|
32
33
|
shuttingDown = true;
|
|
33
34
|
try { await transport?.stop?.(); } catch {}
|
|
35
|
+
try { rmSync(DISCOVERY_PATH, { force: true }); } catch {}
|
|
34
36
|
try { releaseSingletonOwner(OWNER_PATH, process.pid); } catch {}
|
|
35
37
|
process.exit(code);
|
|
36
38
|
}
|
|
37
39
|
|
|
38
40
|
async function main() {
|
|
39
41
|
try { mkdirSync(RUNTIME_ROOT, { recursive: true }); } catch {}
|
|
40
|
-
const claim = claimSingletonOwner(OWNER_PATH, { kind: '
|
|
42
|
+
const claim = claimSingletonOwner(OWNER_PATH, { kind: 'mixdog-daemon', pid: process.pid, meta: { cwd: process.cwd() } });
|
|
41
43
|
if (!claim.owned) { process.exit(0); } // race loser → spawner attaches to winner
|
|
42
44
|
process.on('exit', () => { try { releaseSingletonOwner(OWNER_PATH, process.pid); } catch {} });
|
|
43
45
|
|
|
44
46
|
// Optional env dump for the tool smoke: proves daemonEnv() spawned us with
|
|
45
|
-
// the
|
|
47
|
+
// the host-mode flags (MIXDOG_DAEMON_HOST=1, MIXDOG_CLI_OWNED=0).
|
|
46
48
|
if (process.env.SMOKE_CHANNEL_ENV_OUT) {
|
|
47
49
|
try {
|
|
48
50
|
writeFileSync(process.env.SMOKE_CHANNEL_ENV_OUT, JSON.stringify({
|
|
49
51
|
cliOwned: process.env.MIXDOG_CLI_OWNED,
|
|
50
|
-
|
|
52
|
+
host: process.env.MIXDOG_DAEMON_HOST,
|
|
51
53
|
}));
|
|
52
54
|
} catch { /* smoke-only, best-effort */ }
|
|
53
55
|
}
|
|
@@ -61,15 +63,19 @@ async function main() {
|
|
|
61
63
|
return { ok: true, name, args, leadPid: ctx.leadPid };
|
|
62
64
|
};
|
|
63
65
|
|
|
64
|
-
transport =
|
|
66
|
+
transport = createChannelTransport({
|
|
65
67
|
handleCall,
|
|
66
|
-
discoveryPath: DISCOVERY_PATH,
|
|
67
68
|
clientGraceMs: 250,
|
|
68
69
|
sweepMs: 1000,
|
|
69
70
|
log,
|
|
70
71
|
onClientsEmpty: () => { void shutdown('no live clients'); },
|
|
71
72
|
});
|
|
72
73
|
const { port, token } = await transport.start();
|
|
74
|
+
writeJsonAtomicSync(DISCOVERY_PATH, {
|
|
75
|
+
pid: process.pid,
|
|
76
|
+
startedAt: Date.now(),
|
|
77
|
+
endpoints: { channel: { port, token } },
|
|
78
|
+
}, { compact: true });
|
|
73
79
|
safeIpcSend(process, { type: 'ready', port, token });
|
|
74
80
|
log(`ready port=${port} pid=${process.pid}`);
|
|
75
81
|
}
|
|
@@ -132,7 +132,7 @@ apply it, and verify the result.
|
|
|
132
132
|
### Discord / Telegram / voice channels
|
|
133
133
|
|
|
134
134
|
1. Check `/channels` (or `/setting` → Channel) and runtime channel status.
|
|
135
|
-
2. Update the requested
|
|
135
|
+
2. Update the requested provider, credential, or main channel/chat setting.
|
|
136
136
|
3. Restart/reconnect only when the channel implementation requires it.
|
|
137
137
|
4. Verify channel presence and a non-secret status signal.
|
|
138
138
|
|
package/src/lib/keychain-cjs.cjs
CHANGED
|
@@ -6,7 +6,7 @@ const fs = require('fs');
|
|
|
6
6
|
const { resolvePluginData } = require('./plugin-paths.cjs');
|
|
7
7
|
|
|
8
8
|
const SERVICE = 'mixdog';
|
|
9
|
-
// Shared bound for every synchronous keychain
|
|
9
|
+
// Shared bound for every synchronous keychain provider (DPAPI/PowerShell on
|
|
10
10
|
// Windows, security(1) on macOS). A single env override keeps them consistent.
|
|
11
11
|
const KEYCHAIN_TIMEOUT_MS = Number(process.env.MIXDOG_KEYCHAIN_TIMEOUT_MS || 15000);
|
|
12
12
|
const POWERSHELL_TIMEOUT_MS = KEYCHAIN_TIMEOUT_MS;
|
package/src/lib/mixdog-debug.cjs
CHANGED
|
@@ -102,8 +102,7 @@ const CANONICAL_PLUGIN_LOG_NAMES = new Set([
|
|
|
102
102
|
'webhook.log',
|
|
103
103
|
'perf.log',
|
|
104
104
|
'tool-events.log',
|
|
105
|
-
'
|
|
106
|
-
'channels-worker-standalone.log',
|
|
105
|
+
'daemon.log',
|
|
107
106
|
'mixdog-tui.stderr.log',
|
|
108
107
|
]);
|
|
109
108
|
|
|
@@ -183,7 +182,7 @@ function rotateBoundedLog(filePath, maxBytes, keepBytes) {
|
|
|
183
182
|
}
|
|
184
183
|
|
|
185
184
|
// Shared bound for unbounded per-writer plugin logs (tool-events,
|
|
186
|
-
// memory
|
|
185
|
+
// memory runtime and daemon logs). Keep a tail so recent
|
|
187
186
|
// context survives rotation while the file stays under the 10 MB cap.
|
|
188
187
|
const PLUGIN_LOG_MAX_BYTES = 10 * 1024 * 1024;
|
|
189
188
|
const PLUGIN_LOG_KEEP_BYTES = 2 * 1024 * 1024;
|
|
@@ -9,19 +9,19 @@ kind: retrieval
|
|
|
9
9
|
Return only WHERE (`path:line`), never WHY. You ARE `explore`; never call it.
|
|
10
10
|
Use only grep/find/glob/code_graph; `read` and `list` are forbidden.
|
|
11
11
|
|
|
12
|
-
Turn 1 (`turn 1/3`) is the whole search and should already mint anchors.
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
a file/dir name or
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
12
|
+
Turn 1 (`turn 1/3`) is the whole search and should already mint anchors.
|
|
13
|
+
Extract every concrete locator facet implied by the query, deduplicate
|
|
14
|
+
overlap, and send the maximum independent fan-out in one batch under the
|
|
15
|
+
shared one-route contract; never cap query or facet count. Route each facet to
|
|
16
|
+
exactly one cheapest anchor source: `code_graph` `symbol_search` when it names
|
|
17
|
+
a plausible symbol/identifier; grep `content_with_context` with `pattern[]` of
|
|
18
|
+
4–8 code-token variants for a concept or quoted error; `find` `query[]` only
|
|
19
|
+
when the target itself is a file/dir name or unverified path fragment. For a
|
|
20
|
+
symptom/behavior query, include the reported surface and its immediate
|
|
21
|
+
producer as sibling facets in the same batch when both are needed to locate
|
|
22
|
+
the behavior. Never add generic repository metadata, tests, docs, or adjacent
|
|
23
|
+
subsystems unless the query asks for them, and never send the same facet to
|
|
24
|
+
multiple tools merely for confidence.
|
|
25
25
|
|
|
26
26
|
Grep defaults to `output_mode:"content_with_context"` with `context:0`
|
|
27
27
|
(matches only — the match line already carries its citable `path:line`) and a
|
|
@@ -35,33 +35,44 @@ queries to English identifiers.
|
|
|
35
35
|
Scope is every `<roots><root>…</root></roots>` entry when supplied, otherwise
|
|
36
36
|
session cwd. Search every supplied root in the turn-1 batch: grep/glob batch
|
|
37
37
|
`path[]`, while find uses one sibling call per root. Never silently fall back to
|
|
38
|
-
cwd or omit a supplied root. A
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
38
|
+
cwd or omit a supplied root. A tool-returned path is immutable evidence: copy
|
|
39
|
+
it exactly and never join, prefix, normalize, repair, or reconstruct it from a
|
|
40
|
+
root. A relative find result is a pre-anchor for code-location queries and may
|
|
41
|
+
only scope a later grep; for file/dir-location queries it may be returned
|
|
42
|
+
verbatim. For unverified `src` paths, use `find` first; never guess or invent
|
|
43
|
+
directories or pair `path:"."` with guessed `src/**`. Scoped grep/glob may use
|
|
44
|
+
only a supplied root or an exact find-returned path. After zero hits, change
|
|
45
|
+
tokens or scope, never wording or guessed paths.
|
|
43
46
|
|
|
44
47
|
An anchor is a `path:line` containing a query token or synonym, including a
|
|
45
48
|
code_graph hit. Generic terms without query specificity are zero. Never
|
|
46
|
-
re-locate, reconfirm, or
|
|
47
|
-
pre-anchor and counts as zero. After every result, stop and
|
|
48
|
-
|
|
49
|
+
re-locate, reconfirm, upgrade, or weaken an anchor. A path without `:line` is a
|
|
50
|
+
pre-anchor and counts as zero for code locations. After every result, stop and
|
|
51
|
+
answer as soon as the query's locator objective has specific-token anchors;
|
|
52
|
+
never return a weak or merely plausible anchor.
|
|
49
53
|
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
+
Turn 2 is allowed only when turn 1 has zero valid anchors for the locator
|
|
55
|
+
objective. Batch every unresolved facet at once with changed concrete tokens
|
|
56
|
+
or scope; never repeat the same tokens and scope. Exact pre-anchors may use
|
|
57
|
+
one batched scoped `content_with_context` grep with `head_limit` to mint
|
|
58
|
+
coordinates.
|
|
54
59
|
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
+
Turn 3 is allowed only when turn 2 produced new exact pre-anchors, or an
|
|
61
|
+
explicit flow/default-resolution query has entry anchors but still needs one
|
|
62
|
+
dependent hop. Batch every final scoped grep or resolving hop at once. Never
|
|
63
|
+
start a broad search, add a facet, or merely reword the query on turn 3; if
|
|
64
|
+
these conditions are absent, return `EXPLORATION_FAILED` after turn 2.
|
|
65
|
+
|
|
66
|
+
Use at most 3 tool turns and label every tool message `turn N/3`; normally use
|
|
67
|
+
one batch and one answer. An allowed turn is not a target: fail as soon as the
|
|
68
|
+
next turn lacks a concrete evidence-producing move. The first matching
|
|
69
|
+
entry/definition anchors a concept, value, or default; never trace its chain
|
|
70
|
+
unless the query explicitly asks for flow/default resolution.
|
|
60
71
|
|
|
61
72
|
Answer in at most 3 lines, most specific first:
|
|
62
73
|
`path:line — symbol — short reason`. Copy every cited `path:line` verbatim from
|
|
63
74
|
a tool result in this session; never estimate, adjust, or recall it. Every
|
|
64
75
|
code-location line requires `:line`; never return a bare filename or vague
|
|
65
76
|
prose. A file/dir-location query may return an exact verified path without
|
|
66
|
-
`:line`. Return `EXPLORATION_FAILED`
|
|
67
|
-
|
|
77
|
+
`:line`. Return `EXPLORATION_FAILED` when the bounded recovery rules above
|
|
78
|
+
cannot produce a verified anchor; never fabricate, estimate, or soften one.
|
|
@@ -7,12 +7,11 @@
|
|
|
7
7
|
- Destructive/hard-to-reverse action needs explicit confirmation and explicit
|
|
8
8
|
validated target paths — never `~`, a root, or unresolved variables/globs;
|
|
9
9
|
report material deletions with recoverability.
|
|
10
|
-
-
|
|
10
|
+
- Ask only for decisions.
|
|
11
11
|
- Build only what the task requires; trust internal and framework guarantees.
|
|
12
12
|
- Mid-task input: a replacement supersedes current work, an addition folds
|
|
13
13
|
into it, a status question gets a brief answer while work continues; after
|
|
14
14
|
context compaction continue from the summary — never restart or redo
|
|
15
15
|
finished work.
|
|
16
|
-
- When blocked, exhaust safe in-scope checks once and report the blocker.
|
|
17
16
|
- Your final message ends the turn: answer only when the work is done. After a
|
|
18
17
|
failed tool call, fix and re-run it, or state plainly that it is unresolved.
|
|
@@ -1,6 +1,3 @@
|
|
|
1
1
|
# Lead Tools
|
|
2
2
|
|
|
3
|
-
- Write-role agents self-verify with `shell`. Lead uses `shell` only for git,
|
|
4
|
-
benches, and cross-scope verification no retrieval tool can produce;
|
|
5
|
-
inspection stays on `read`/`grep`/`glob`/`list`/`find`/`code_graph`.
|
|
6
3
|
- Use the current project/workspace unless the request or tool requires another.
|
|
@@ -7,16 +7,17 @@
|
|
|
7
7
|
`read`; symbol/relation→`code_graph`; web/current→`search`; returned URL body→
|
|
8
8
|
`web_fetch`; prior work→`recall`; durable compact English memory→`memory`;
|
|
9
9
|
explicit project change→`cwd`; explicit user-requested conversation reset→
|
|
10
|
-
`session_manage`; process/env, git, build/run/test→`shell`.
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
10
|
+
`session_manage`; process/env, git, build/run/test→`shell`. Call `shell` only
|
|
11
|
+
when the task actually requires one of those operations; do not treat it as
|
|
12
|
+
a routine investigation or completion step. Never use shell equivalents for
|
|
13
|
+
file discovery or content retrieval.
|
|
14
|
+
- Use verified paths (cwd/project/user/tool); explicit paths may be outside cwd;
|
|
15
|
+
guessed fragments use `find`. Maximize fan-out across routed retrieval tools
|
|
16
|
+
and fetch all information needed in one batch; never re-fetch an unchanged
|
|
17
|
+
span.
|
|
18
|
+
- Once the edit is determined, finish in one assistant turn with one
|
|
19
|
+
`apply_patch` for all edits. If final verification actually requires `shell`,
|
|
20
|
+
use one verification chain after the edit; otherwise finish without it.
|
|
20
21
|
- After a call returns a background `task_id`, end the turn; its completion
|
|
21
22
|
notification resumes work. Never poll; use task control only for recovery or
|
|
22
23
|
a required blocking result.
|
|
@@ -9,7 +9,8 @@ import { isDiagnosticIOEnabled } from '../../../lib/mixdog-debug.cjs';
|
|
|
9
9
|
const WARNED_KEYS = new Set();
|
|
10
10
|
|
|
11
11
|
// ---------------------------------------------------------------------------
|
|
12
|
-
// In-memory buffer
|
|
12
|
+
// In-memory buffer. Product sessions flush directly into the daemon-hosted
|
|
13
|
+
// memory module; the standalone path can still use its local HTTP service.
|
|
13
14
|
// ---------------------------------------------------------------------------
|
|
14
15
|
let _buffer = [];
|
|
15
16
|
const _BUFFER_MAX = 2000;
|
|
@@ -225,21 +226,11 @@ try {
|
|
|
225
226
|
function _resolveServiceUrl() {
|
|
226
227
|
if (_serviceUrl) return _serviceUrl;
|
|
227
228
|
try {
|
|
228
|
-
//
|
|
229
|
-
// pid-validated; fall back to legacy active-instance.json memory_port.
|
|
229
|
+
// Standalone memory HTTP uses its pid-validated discovery advert.
|
|
230
230
|
const advertPort = readServicePort('memory', { requirePid: false });
|
|
231
231
|
if (advertPort) { _serviceAdvertPort = advertPort; _serviceUrl = `http://127.0.0.1:${advertPort}`; return _serviceUrl; }
|
|
232
232
|
_serviceAdvertPort = null;
|
|
233
|
-
|
|
234
|
-
? join(process.env.MIXDOG_RUNTIME_ROOT)
|
|
235
|
-
: join(os.tmpdir(), 'mixdog');
|
|
236
|
-
const activeFile = join(runtimeRoot, 'active-instance.json');
|
|
237
|
-
if (!existsSync(activeFile)) return null;
|
|
238
|
-
const active = JSON.parse(readFileSync(activeFile, 'utf-8'));
|
|
239
|
-
const port = Number(active && active.memory_port);
|
|
240
|
-
if (!Number.isFinite(port) || port <= 0) return null;
|
|
241
|
-
_serviceUrl = `http://127.0.0.1:${port}`;
|
|
242
|
-
return _serviceUrl;
|
|
233
|
+
return null;
|
|
243
234
|
} catch {
|
|
244
235
|
return null;
|
|
245
236
|
}
|
|
@@ -1,5 +1,5 @@
|
|
|
1
|
-
import { createHash } from 'crypto';
|
|
2
1
|
import { isInclusiveProvider } from '../../shared/llm/cost.mjs';
|
|
2
|
+
import { estimateJsonBytes, hashStructuredValue } from '../../shared/json-metrics.mjs';
|
|
3
3
|
import {
|
|
4
4
|
appendAgentTrace,
|
|
5
5
|
drainAgentTrace,
|
|
@@ -17,7 +17,23 @@ import {
|
|
|
17
17
|
|
|
18
18
|
function estimateProviderPayloadBytes(messages, model, tools) {
|
|
19
19
|
try {
|
|
20
|
-
|
|
20
|
+
let referencedBytes = 0;
|
|
21
|
+
const seen = new Set();
|
|
22
|
+
const walk = (value) => {
|
|
23
|
+
if (!value || typeof value !== 'object' || seen.has(value)) return;
|
|
24
|
+
seen.add(value);
|
|
25
|
+
if (typeof value.attachmentRef === 'string') {
|
|
26
|
+
const bytes = Number(value.sizeBytes) || 0;
|
|
27
|
+
referencedBytes += value.type === 'image' || value.type === 'file'
|
|
28
|
+
? Math.ceil(bytes / 3) * 4
|
|
29
|
+
: bytes;
|
|
30
|
+
return;
|
|
31
|
+
}
|
|
32
|
+
if (Array.isArray(value)) value.forEach(walk);
|
|
33
|
+
else Object.values(value).forEach(walk);
|
|
34
|
+
};
|
|
35
|
+
walk(messages);
|
|
36
|
+
return estimateJsonBytes({ model, messages, tools: tools || [] }) + referencedBytes;
|
|
21
37
|
}
|
|
22
38
|
catch {
|
|
23
39
|
return null;
|
|
@@ -60,9 +76,7 @@ function extractCacheWriteTokens(usage) {
|
|
|
60
76
|
// hashing megabytes per turn. Truncated SHA1 keeps the trace row compact.
|
|
61
77
|
function messagePrefixHash(messages) {
|
|
62
78
|
try {
|
|
63
|
-
|
|
64
|
-
const slice = json.length > 4096 ? json.slice(0, 4096) : json;
|
|
65
|
-
return createHash('sha1').update(slice).digest('hex').slice(0, 12);
|
|
79
|
+
return hashStructuredValue(messages || [], { maxStringChars: 4096 }).slice(0, 12);
|
|
66
80
|
} catch {
|
|
67
81
|
return null;
|
|
68
82
|
}
|