mixdog 0.8.0 → 0.9.0
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 +47 -23
- package/package.json +33 -27
- package/scripts/_test-folder-dialog.mjs +30 -0
- package/scripts/agent-parallel-smoke.mjs +388 -0
- package/scripts/agent-tag-reuse-smoke.mjs +183 -0
- package/scripts/background-task-meta-smoke.mjs +38 -0
- package/scripts/boot-smoke.mjs +52 -9
- package/scripts/build-runtime-linux.sh +348 -0
- package/scripts/build-runtime-macos.sh +217 -0
- package/scripts/build-runtime-windows.ps1 +242 -0
- package/scripts/compact-active-turn-test.mjs +68 -0
- package/scripts/compact-smoke.mjs +859 -129
- package/scripts/compact-trigger-migration-smoke.mjs +187 -0
- package/scripts/fix-brief-fn.mjs +35 -0
- package/scripts/fix-format-tool-surface.mjs +24 -0
- package/scripts/fix-tool-exec-visible.mjs +42 -0
- package/scripts/generate-runtime-manifest.mjs +166 -0
- package/scripts/hook-bus-test.mjs +330 -0
- package/scripts/lead-workflow-smoke.mjs +33 -39
- package/scripts/live-worker-smoke.mjs +43 -37
- package/scripts/llm-trace-summary.mjs +315 -0
- package/scripts/memory-meta-concurrency-test.mjs +20 -0
- package/scripts/output-style-smoke.mjs +56 -15
- package/scripts/parent-abort-link-test.mjs +44 -0
- package/scripts/patch-agent-brief.mjs +48 -0
- package/scripts/patch-app.mjs +21 -0
- package/scripts/patch-app2.mjs +18 -0
- package/scripts/patch-dist-brief.mjs +96 -0
- package/scripts/patch-tool-exec.mjs +70 -0
- package/scripts/pretool-ask-runtime-test.mjs +54 -0
- package/scripts/provider-toolcall-test.mjs +376 -0
- package/scripts/reactive-compact-persist-smoke.mjs +124 -0
- package/scripts/sanitize-tool-pairs-test.mjs +260 -0
- package/scripts/session-context-bench.mjs +344 -0
- package/scripts/session-ingest-smoke.mjs +177 -0
- package/scripts/set-effort-config-test.mjs +41 -0
- package/scripts/smoke-runtime-negative.ps1 +106 -0
- package/scripts/smoke-runtime-negative.sh +97 -0
- package/scripts/smoke.mjs +25 -0
- package/scripts/tool-result-hook-test.mjs +48 -0
- package/scripts/tool-smoke.mjs +1223 -95
- package/scripts/toolcall-args-test.mjs +150 -0
- package/scripts/tui-background-failure-smoke.mjs +73 -0
- package/scripts/usage-metrics-epoch-smoke.mjs +114 -0
- package/src/agents/debugger/AGENT.md +8 -0
- package/src/agents/explore/AGENT.md +4 -0
- package/src/agents/heavy-worker/AGENT.md +9 -3
- package/src/agents/maintainer/AGENT.md +4 -0
- package/src/agents/reviewer/AGENT.md +8 -0
- package/src/agents/scheduler-task/AGENT.md +12 -0
- package/src/agents/scheduler-task/agent.json +6 -0
- package/src/agents/webhook-handler/AGENT.md +12 -0
- package/src/agents/webhook-handler/agent.json +6 -0
- package/src/agents/worker/AGENT.md +9 -3
- package/src/app.mjs +77 -3
- package/src/defaults/hidden-roles.json +17 -12
- package/src/headless-role.mjs +117 -0
- package/src/help.mjs +30 -0
- package/src/hooks/lib/permission-evaluator.cjs +11 -475
- package/src/lib/keychain-cjs.cjs +9 -1
- package/src/lib/mixdog-debug.cjs +0 -7
- package/src/lib/rules-builder.cjs +240 -96
- package/src/lib/text-utils.cjs +1 -1
- package/src/mixdog-session-runtime.mjs +2325 -450
- package/src/output-styles/default.md +12 -28
- package/src/output-styles/extreme-simple.md +9 -6
- package/src/output-styles/simple.md +22 -9
- package/src/repl.mjs +118 -59
- package/src/rules/agent/00-common.md +15 -0
- package/src/rules/{bridge → agent}/20-skip-protocol.md +1 -2
- package/src/rules/agent/30-explorer.md +22 -0
- package/src/rules/{bridge → agent}/40-cycle1-agent.md +7 -0
- package/src/rules/{bridge → agent}/41-cycle2-agent.md +7 -0
- package/src/rules/{bridge → agent}/42-cycle3-agent.md +7 -0
- package/src/rules/lead/01-general.md +9 -5
- package/src/rules/lead/04-workflow.md +51 -12
- package/src/rules/lead/lead-tool.md +6 -0
- package/src/rules/shared/01-tool.md +12 -1
- package/src/runtime/agent/orchestrator/activity-bus.mjs +7 -18
- package/src/runtime/agent/orchestrator/agent-owner.mjs +11 -0
- package/src/runtime/agent/orchestrator/{smart-bridge/bridge-llm.mjs → agent-runtime/agent-dispatch.mjs} +138 -111
- package/src/runtime/agent/orchestrator/agent-runtime/agent-progress-watchdog.mjs +94 -0
- package/src/runtime/agent/orchestrator/{smart-bridge → agent-runtime}/cache-strategy.mjs +32 -23
- package/src/runtime/agent/orchestrator/{smart-bridge → agent-runtime}/session-builder.mjs +33 -27
- package/src/runtime/agent/orchestrator/{bridge-trace.mjs → agent-trace.mjs} +132 -81
- package/src/runtime/agent/orchestrator/cache-mtime.mjs +0 -21
- package/src/runtime/agent/orchestrator/config.mjs +174 -55
- package/src/runtime/agent/orchestrator/context/collect.mjs +195 -487
- package/src/runtime/agent/orchestrator/dispatch-persist.mjs +1 -1
- package/src/runtime/agent/orchestrator/internal-roles.mjs +77 -29
- package/src/runtime/agent/orchestrator/internal-tools.mjs +5 -6
- package/src/runtime/agent/orchestrator/mcp/client.mjs +15 -9
- package/src/runtime/agent/orchestrator/providers/anthropic-betas.mjs +7 -1
- package/src/runtime/agent/orchestrator/providers/anthropic-oauth.mjs +377 -243
- package/src/runtime/agent/orchestrator/providers/anthropic.mjs +146 -93
- package/src/runtime/agent/orchestrator/providers/api-usage.mjs +236 -4
- package/src/runtime/agent/orchestrator/providers/custom-tool-wire.mjs +49 -0
- package/src/runtime/agent/orchestrator/providers/gemini.mjs +58 -13
- package/src/runtime/agent/orchestrator/providers/grok-oauth.mjs +67 -149
- package/src/runtime/agent/orchestrator/providers/media-normalization.mjs +132 -2
- package/src/runtime/agent/orchestrator/providers/model-catalog.mjs +4 -1
- package/src/runtime/agent/orchestrator/providers/oauth-credential-probes.mjs +45 -0
- package/src/runtime/agent/orchestrator/providers/oauth-usage.mjs +61 -116
- package/src/runtime/agent/orchestrator/providers/openai-compat-presets.mjs +25 -0
- package/src/runtime/agent/orchestrator/providers/openai-compat-stream.mjs +79 -255
- package/src/runtime/agent/orchestrator/providers/openai-compat.mjs +221 -70
- package/src/runtime/agent/orchestrator/providers/openai-oauth-ws.mjs +477 -147
- package/src/runtime/agent/orchestrator/providers/openai-oauth.mjs +343 -496
- package/src/runtime/agent/orchestrator/providers/openai-ws.mjs +6 -6
- package/src/runtime/agent/orchestrator/providers/registry.mjs +88 -51
- package/src/runtime/agent/orchestrator/providers/retry-classifier.mjs +31 -11
- package/src/runtime/agent/orchestrator/providers/statusline-route-meta.mjs +41 -8
- package/src/runtime/agent/orchestrator/session/cache/post-edit-marks.mjs +4 -4
- package/src/runtime/agent/orchestrator/session/cache/read-cache.mjs +1 -1
- package/src/runtime/agent/orchestrator/session/compact.mjs +1173 -267
- package/src/runtime/agent/orchestrator/session/context-utils.mjs +199 -36
- package/src/runtime/agent/orchestrator/session/loop.mjs +851 -674
- package/src/runtime/agent/orchestrator/session/manager.mjs +1593 -466
- package/src/runtime/agent/orchestrator/session/manager.reactive-persist.test.mjs +107 -0
- package/src/runtime/agent/orchestrator/session/store.mjs +291 -46
- package/src/runtime/agent/orchestrator/session/tool-result-offload.mjs +2 -2
- package/src/runtime/agent/orchestrator/stall-policy.mjs +31 -16
- package/src/runtime/agent/orchestrator/tool-loop-guard.mjs +3 -219
- package/src/runtime/agent/orchestrator/tools/bash-session.mjs +34 -7
- package/src/runtime/agent/orchestrator/tools/builtin/advisory-lock.mjs +1 -1
- package/src/runtime/agent/orchestrator/tools/builtin/arg-guard.mjs +19 -0
- package/src/runtime/agent/orchestrator/tools/builtin/bash-tool.mjs +9 -9
- package/src/runtime/agent/orchestrator/tools/builtin/builtin-tools.mjs +60 -37
- package/src/runtime/agent/orchestrator/tools/builtin/cache-layers.mjs +21 -2
- package/src/runtime/agent/orchestrator/tools/builtin/device-paths.mjs +1 -1
- package/src/runtime/agent/orchestrator/tools/builtin/diagnostics-tool.mjs +0 -7
- package/src/runtime/agent/orchestrator/tools/builtin/glob-walk.mjs +1 -3
- package/src/runtime/agent/orchestrator/tools/builtin/list-tool.mjs +36 -12
- package/src/runtime/agent/orchestrator/tools/builtin/read-args.mjs +2 -0
- package/src/runtime/agent/orchestrator/tools/builtin/read-constants.mjs +2 -2
- package/src/runtime/agent/orchestrator/tools/builtin/read-formatting.mjs +5 -12
- package/src/runtime/agent/orchestrator/tools/builtin/read-image-resize.mjs +1 -1
- package/src/runtime/agent/orchestrator/tools/builtin/read-single-tool.mjs +4 -36
- package/src/runtime/agent/orchestrator/tools/builtin/read-tool.mjs +2 -40
- package/src/runtime/agent/orchestrator/tools/builtin/rg-runner.mjs +148 -27
- package/src/runtime/agent/orchestrator/tools/builtin/search-builders.mjs +2 -2
- package/src/runtime/agent/orchestrator/tools/builtin/search-tool.mjs +43 -75
- package/src/runtime/agent/orchestrator/tools/builtin/shell-jobs.mjs +90 -20
- package/src/runtime/agent/orchestrator/tools/builtin/snapshot-store.mjs +1 -1
- package/src/runtime/agent/orchestrator/tools/builtin.mjs +59 -5
- package/src/runtime/agent/orchestrator/tools/code-graph-state.mjs +86 -0
- package/src/runtime/agent/orchestrator/tools/code-graph-tool-defs.mjs +11 -11
- package/src/runtime/agent/orchestrator/tools/code-graph.mjs +4106 -4019
- package/src/runtime/agent/orchestrator/tools/patch-tool-defs.mjs +33 -4
- package/src/runtime/agent/orchestrator/tools/patch.mjs +90 -6
- package/src/runtime/agent/orchestrator/tools/progress-message.mjs +6 -4
- package/src/runtime/agent/orchestrator/tools/result-compression.mjs +4 -4
- package/src/runtime/agent/orchestrator/tools/shell-command.mjs +8 -1
- package/src/runtime/agent/orchestrator/tools/shell-snapshot.mjs +4 -4
- package/src/runtime/channels/index.mjs +152 -24
- package/src/runtime/channels/lib/scheduler.mjs +18 -14
- package/src/runtime/channels/lib/session-discovery.mjs +3 -2
- package/src/runtime/channels/lib/tool-format.mjs +0 -2
- package/src/runtime/channels/lib/transcript-discovery.mjs +3 -2
- package/src/runtime/channels/lib/webhook.mjs +1 -1
- package/src/runtime/channels/tool-defs.mjs +29 -29
- package/src/runtime/memory/index.mjs +635 -107
- package/src/runtime/memory/lib/agent-ipc.mjs +29 -12
- package/src/runtime/memory/lib/core-memory-store.mjs +2 -2
- package/src/runtime/memory/lib/embedding-model-config.mjs +55 -0
- package/src/runtime/memory/lib/embedding-provider.mjs +31 -4
- package/src/runtime/memory/lib/embedding-worker.mjs +19 -10
- package/src/runtime/memory/lib/memory-cycle1.mjs +38 -17
- package/src/runtime/memory/lib/memory-cycle2.mjs +6 -7
- package/src/runtime/memory/lib/memory-cycle3.mjs +4 -4
- package/src/runtime/memory/lib/memory-ops-policy.mjs +2 -1
- package/src/runtime/memory/lib/memory-session-merge.mjs +38 -0
- package/src/runtime/memory/lib/memory.mjs +88 -9
- package/src/runtime/memory/lib/model-profile.mjs +1 -1
- package/src/runtime/memory/lib/pg/adapter.mjs +15 -1
- package/src/runtime/memory/lib/pg/supervisor.mjs +12 -0
- package/src/runtime/memory/lib/runtime-fetcher.mjs +37 -3
- package/src/runtime/memory/lib/session-ingest.mjs +194 -0
- package/src/runtime/memory/lib/trace-store.mjs +96 -51
- package/src/runtime/memory/tool-defs.mjs +46 -37
- package/src/runtime/search/index.mjs +102 -466
- package/src/runtime/search/lib/web-tools.mjs +45 -25
- package/src/runtime/search/tool-defs.mjs +16 -23
- package/src/runtime/shared/abort-controller.mjs +1 -1
- package/src/runtime/shared/atomic-file.mjs +4 -3
- package/src/runtime/shared/background-tasks.mjs +122 -11
- package/src/runtime/shared/child-spawn-gate.mjs +145 -0
- package/src/runtime/shared/config.mjs +7 -4
- package/src/runtime/shared/err-text.mjs +131 -4
- package/src/runtime/shared/llm/cost.mjs +2 -2
- package/src/runtime/shared/llm/http-agent.mjs +23 -7
- package/src/runtime/shared/llm/index.mjs +34 -11
- package/src/runtime/shared/llm/usage-log.mjs +4 -4
- package/src/runtime/shared/markdown-frontmatter.mjs +56 -0
- package/src/runtime/shared/singleton-owner.mjs +104 -0
- package/src/runtime/shared/tool-execution-contract.mjs +199 -20
- package/src/runtime/shared/tool-execution-contract.test.mjs +183 -0
- package/src/runtime/shared/tool-surface.mjs +624 -98
- package/src/runtime/shared/user-data-guard.mjs +0 -2
- package/src/standalone/agent-task-status.mjs +203 -0
- package/src/standalone/agent-task-status.test.mjs +76 -0
- package/src/standalone/agent-tool.mjs +1913 -0
- package/src/standalone/channel-worker.mjs +370 -14
- package/src/standalone/explore-tool.mjs +165 -70
- package/src/standalone/folder-dialog.mjs +314 -0
- package/src/standalone/hook-bus.mjs +898 -22
- package/src/standalone/memory-runtime-proxy.mjs +320 -0
- package/src/standalone/projects.mjs +226 -0
- package/src/standalone/provider-admin.mjs +41 -24
- package/src/standalone/seeds.mjs +2 -69
- package/src/standalone/usage-dashboard.mjs +96 -8
- package/src/tui/App.jsx +4798 -2153
- package/src/tui/components/AnsiText.jsx +39 -28
- package/src/tui/components/ContextPanel.jsx +87 -29
- package/src/tui/components/Markdown.jsx +43 -77
- package/src/tui/components/MarkdownTable.jsx +9 -184
- package/src/tui/components/Message.jsx +28 -11
- package/src/tui/components/Picker.jsx +95 -56
- package/src/tui/components/PromptInput.jsx +367 -239
- package/src/tui/components/QueuedCommands.jsx +1 -1
- package/src/tui/components/SlashCommandPalette.jsx +27 -21
- package/src/tui/components/Spinner.jsx +67 -38
- package/src/tui/components/StatusLine.jsx +606 -38
- package/src/tui/components/TextEntryPanel.jsx +128 -9
- package/src/tui/components/ToolExecution.jsx +617 -368
- package/src/tui/components/TurnDone.jsx +3 -3
- package/src/tui/components/UsagePanel.jsx +3 -5
- package/src/tui/components/tool-output-format.mjs +365 -0
- package/src/tui/components/tool-output-format.test.mjs +220 -0
- package/src/tui/dist/index.mjs +8915 -2418
- package/src/tui/engine-runtime-notification.test.mjs +115 -0
- package/src/tui/engine-tool-result-text.test.mjs +75 -0
- package/src/tui/engine.mjs +1455 -279
- package/src/tui/figures.mjs +21 -40
- package/src/tui/index.jsx +75 -31
- package/src/tui/input-editing.mjs +25 -0
- package/src/tui/markdown/format-token.mjs +511 -68
- package/src/tui/markdown/format-token.test.mjs +216 -0
- package/src/tui/markdown/render-ansi.mjs +94 -0
- package/src/tui/markdown/render-ansi.test.mjs +108 -0
- package/src/tui/markdown/stream-fence.mjs +34 -0
- package/src/tui/markdown/stream-fence.test.mjs +26 -0
- package/src/tui/markdown/table-layout.mjs +250 -0
- package/src/tui/paste-attachments.mjs +0 -7
- package/src/tui/spinner-verbs.mjs +1 -2
- package/src/tui/statusline-ansi-bridge.mjs +172 -0
- package/src/tui/statusline-ansi-bridge.test.mjs +159 -0
- package/src/tui/theme.mjs +746 -24
- package/src/tui/time-format.mjs +1 -1
- package/src/tui/transcript-tool-failures.mjs +67 -0
- package/src/tui/transcript-tool-failures.test.mjs +111 -0
- package/src/ui/ansi.mjs +1 -2
- package/src/ui/markdown.mjs +85 -26
- package/src/ui/markdown.test.mjs +70 -0
- package/src/ui/model-display.mjs +121 -0
- package/src/ui/session-stats.mjs +44 -0
- package/src/ui/statusline-context-label.test.mjs +15 -0
- package/src/ui/statusline.mjs +386 -178
- package/src/ui/tool-card.mjs +3 -16
- package/src/vendor/statusline/bin/statusline-lib.mjs +8 -4
- package/src/vendor/statusline/bin/statusline-route.mjs +169 -37
- package/src/vendor/statusline/bin/statusline-route.test.mjs +80 -0
- package/src/vendor/statusline/scripts/lib/gateway-settings.mjs +3 -3
- package/src/vendor/statusline/src/gateway/claude-current.mjs +1 -1
- package/src/vendor/statusline/src/gateway/route-meta.mjs +44 -6
- package/src/vendor/statusline/src/gateway/session-routes.mjs +1 -1
- package/src/workflows/default/WORKFLOW.md +12 -5
- package/src/workflows/default/workflow.json +0 -1
- package/src/workflows/solo/WORKFLOW.md +15 -0
- package/src/workflows/solo/workflow.json +7 -0
- package/vendor/ink/build/output.js +6 -1
- package/src/agents/scheduler-task.md +0 -3
- package/src/agents/web-researcher/AGENT.md +0 -3
- package/src/agents/web-researcher/agent.json +0 -6
- package/src/agents/webhook-handler.md +0 -3
- package/src/rules/bridge/00-common.md +0 -5
- package/src/rules/bridge/30-explorer.md +0 -4
- package/src/rules/lead/00-tool-lead.md +0 -5
- package/src/rules/shared/00-language.md +0 -3
- package/src/runtime/agent/orchestrator/tools/builtin/native-edit-runner.mjs +0 -110
- package/src/runtime/agent/orchestrator/tools/mutation-content-cache.mjs +0 -67
- package/src/runtime/memory/lib/bridge-trace-queries.mjs +0 -120
- package/src/runtime/shared/llm/pid-cleanup.mjs +0 -27
- package/src/standalone/bridge-tool.mjs +0 -1414
- package/src/tui/runtime/shared/process-shutdown.mjs +0 -1
|
@@ -1,9 +1,78 @@
|
|
|
1
1
|
import { isOffloadedToolResultText } from './tool-result-offload.mjs';
|
|
2
2
|
import { createHash } from 'node:crypto';
|
|
3
3
|
|
|
4
|
-
//
|
|
4
|
+
// ---------------------------------------------------------------------------
|
|
5
|
+
// Conservative, Unicode-aware token estimator.
|
|
6
|
+
//
|
|
7
|
+
// This is a SAFETY estimator, NOT exact tokenizer parity. We cannot run the
|
|
8
|
+
// provider's real BPE tokenizer here (no network, no bundled vocab), so the
|
|
9
|
+
// goal is to never UNDERcount: a transcript the estimator says "fits" must
|
|
10
|
+
// genuinely fit the model context once compaction has run. The legacy chars/4
|
|
11
|
+
// heuristic badly undercounts Korean/CJK/kana/emoji and dense JSON/tool-call
|
|
12
|
+
// payloads (which BPE often splits into >=1 token per character or per byte),
|
|
13
|
+
// so a "fits" verdict was optimistic exactly where it mattered most.
|
|
14
|
+
//
|
|
15
|
+
// Strategy: weight each code point by how expensive it tends to be under a
|
|
16
|
+
// modern BPE tokenizer (cl100k/o200k-class), then take the MAX of that weighted
|
|
17
|
+
// sum and the chars/4 ASCII lower bound, then apply a small safety multiplier.
|
|
18
|
+
// Weights deliberately lean high (overcount) for CJK/Hangul/emoji.
|
|
19
|
+
//
|
|
20
|
+
// MIXDOG_TOKEN_ESTIMATE_SAFETY_MULTIPLIER (default 1.1, clamped 1.0..2.0) lets
|
|
21
|
+
// operators dial extra headroom without code changes.
|
|
22
|
+
function readSafetyMultiplier() {
|
|
23
|
+
const raw = Number(process.env.MIXDOG_TOKEN_ESTIMATE_SAFETY_MULTIPLIER);
|
|
24
|
+
if (Number.isFinite(raw)) return Math.min(2.0, Math.max(1.0, raw));
|
|
25
|
+
return 1.1;
|
|
26
|
+
}
|
|
27
|
+
const TOKEN_ESTIMATE_SAFETY_MULTIPLIER = readSafetyMultiplier();
|
|
28
|
+
|
|
29
|
+
// Per-code-point token-cost weight. Tuned to overcount, not match exactly.
|
|
30
|
+
function codePointTokenWeight(cp) {
|
|
31
|
+
// ASCII (latin letters, digits, punctuation, whitespace, control): the one
|
|
32
|
+
// region where chars/4 is roughly right — keep the cheap 0.25/char cost.
|
|
33
|
+
if (cp < 0x80) return 0.25;
|
|
34
|
+
// Hangul syllables + Jamo + compatibility Jamo. Korean is the worst case
|
|
35
|
+
// for chars/4: a single syllable frequently costs 1.5–3 BPE tokens, and
|
|
36
|
+
// rarer syllables fall back to multi-byte splits. Weight high for safety.
|
|
37
|
+
if (cp >= 0xAC00 && cp <= 0xD7A3) return 1.5;
|
|
38
|
+
if (cp >= 0x1100 && cp <= 0x11FF) return 1.5;
|
|
39
|
+
if (cp >= 0x3130 && cp <= 0x318F) return 1.5;
|
|
40
|
+
if (cp >= 0xA960 && cp <= 0xA97F) return 1.5;
|
|
41
|
+
if (cp >= 0xD7B0 && cp <= 0xD7FF) return 1.5;
|
|
42
|
+
// Hiragana / Katakana / Katakana phonetic extensions.
|
|
43
|
+
if (cp >= 0x3040 && cp <= 0x30FF) return 1.2;
|
|
44
|
+
if (cp >= 0x31F0 && cp <= 0x31FF) return 1.2;
|
|
45
|
+
// CJK unified ideographs (incl. Ext A) + compatibility ideographs.
|
|
46
|
+
if (cp >= 0x3400 && cp <= 0x4DBF) return 1.2;
|
|
47
|
+
if (cp >= 0x4E00 && cp <= 0x9FFF) return 1.2;
|
|
48
|
+
if (cp >= 0xF900 && cp <= 0xFAFF) return 1.2;
|
|
49
|
+
// CJK Extension B and beyond (supplementary ideographic plane).
|
|
50
|
+
if (cp >= 0x20000 && cp <= 0x2FA1F) return 1.2;
|
|
51
|
+
// Emoji / pictographs / dingbats / symbols — these explode under BPE
|
|
52
|
+
// (surrogate pairs, ZWJ sequences, variation selectors), so weight highest.
|
|
53
|
+
if (cp >= 0x2600 && cp <= 0x27BF) return 2.0;
|
|
54
|
+
if (cp >= 0x1F000 && cp <= 0x1FAFF) return 2.0;
|
|
55
|
+
if (cp >= 0x2190 && cp <= 0x21FF) return 1.5; // arrows
|
|
56
|
+
if (cp >= 0x2300 && cp <= 0x23FF) return 1.5; // technical symbols
|
|
57
|
+
// Latin-1 supplement / extended latin / IPA — pricier than ASCII (often a
|
|
58
|
+
// token per accented char) but cheaper than CJK.
|
|
59
|
+
if (cp < 0x0400) return 0.6;
|
|
60
|
+
// Everything else non-ASCII (Cyrillic, Greek, Arabic, Hebrew, Thai, …):
|
|
61
|
+
// multi-byte UTF-8, typically ~0.5–1 token/char. Stay conservative.
|
|
62
|
+
return 0.8;
|
|
63
|
+
}
|
|
64
|
+
|
|
65
|
+
// Conservative Unicode-aware token estimate. Iterates by code point (so
|
|
66
|
+
// surrogate-pair emoji are scored once, at the high emoji weight), takes the
|
|
67
|
+
// max of the weighted sum and the chars/4 ASCII floor, then applies the safety
|
|
68
|
+
// multiplier. Always overcounts relative to chars/4 for non-ASCII text.
|
|
5
69
|
function estimateTokens(text) {
|
|
6
|
-
|
|
70
|
+
const s = String(text ?? '');
|
|
71
|
+
if (s.length === 0) return 0;
|
|
72
|
+
let weighted = 0;
|
|
73
|
+
for (const ch of s) weighted += codePointTokenWeight(ch.codePointAt(0));
|
|
74
|
+
const asciiFloor = s.length / 4; // never below the legacy chars/4 lower bound
|
|
75
|
+
return Math.ceil(Math.max(weighted, asciiFloor) * TOKEN_ESTIMATE_SAFETY_MULTIPLIER);
|
|
7
76
|
}
|
|
8
77
|
function messageEstimateText(m) {
|
|
9
78
|
if (!m || typeof m !== 'object') return '';
|
|
@@ -46,56 +115,122 @@ export function estimateToolSchemaTokens(tools) {
|
|
|
46
115
|
|
|
47
116
|
/**
|
|
48
117
|
* Total headroom the caller should reserve out of the context window before
|
|
49
|
-
* compaction: tool-schema bytes + fixed request framing overhead. Pass
|
|
50
|
-
*
|
|
51
|
-
*
|
|
118
|
+
* compaction: tool-schema bytes + fixed request framing overhead. Pass this as
|
|
119
|
+
* `opts.reserveTokens` so semantic/recall compaction budgets account for
|
|
120
|
+
* request-side bytes the message estimate cannot see.
|
|
52
121
|
*/
|
|
53
122
|
export function estimateRequestReserveTokens(tools) {
|
|
54
123
|
return estimateToolSchemaTokens(tools) + REQUEST_OVERHEAD_TOKENS;
|
|
55
124
|
}
|
|
125
|
+
|
|
126
|
+
/**
|
|
127
|
+
* Live/current context numerator SSOT: transcript estimate + request reserve.
|
|
128
|
+
* Provider-reported usage is excluded (secondary metadata only).
|
|
129
|
+
* Empty / no-activity transcript returns 0 so fresh sessions do not show
|
|
130
|
+
* reserve-only phantom usage.
|
|
131
|
+
*
|
|
132
|
+
* @param {unknown[]} messages
|
|
133
|
+
* @param {unknown[]|number} toolsOrReserve tool list or precomputed reserve tokens
|
|
134
|
+
* @param {{ messageCount?: number }} [opts]
|
|
135
|
+
*/
|
|
136
|
+
export function estimateTranscriptContextUsage(messages, toolsOrReserve, opts = {}) {
|
|
137
|
+
const list = Array.isArray(messages) ? messages : [];
|
|
138
|
+
const count = Number.isFinite(Number(opts.messageCount)) ? Number(opts.messageCount) : list.length;
|
|
139
|
+
if (count <= 0 || list.length === 0) return 0;
|
|
140
|
+
const messageTokens = estimateMessagesTokens(list);
|
|
141
|
+
const reserve = typeof toolsOrReserve === 'number' && Number.isFinite(toolsOrReserve)
|
|
142
|
+
? Math.max(0, toolsOrReserve)
|
|
143
|
+
: estimateRequestReserveTokens(toolsOrReserve);
|
|
144
|
+
return messageTokens + reserve;
|
|
145
|
+
}
|
|
146
|
+
|
|
56
147
|
const TOOL_MISSING_STUB = '[Older tool result unavailable after context compaction]';
|
|
148
|
+
function collectAssistantToolCallIds(message) {
|
|
149
|
+
if (!message || message.role !== 'assistant') return [];
|
|
150
|
+
const ids = [];
|
|
151
|
+
const seen = new Set();
|
|
152
|
+
const add = (id) => {
|
|
153
|
+
if (!id || seen.has(id)) return;
|
|
154
|
+
seen.add(id);
|
|
155
|
+
ids.push(id);
|
|
156
|
+
};
|
|
157
|
+
if (Array.isArray(message.toolCalls)) {
|
|
158
|
+
for (const tc of message.toolCalls) add(tc?.id);
|
|
159
|
+
}
|
|
160
|
+
const blocksFrom = (blocks) => {
|
|
161
|
+
if (!Array.isArray(blocks)) return;
|
|
162
|
+
for (const b of blocks) {
|
|
163
|
+
if (b?.type === 'tool_use' && b.id) add(b.id);
|
|
164
|
+
}
|
|
165
|
+
};
|
|
166
|
+
blocksFrom(message.assistantBlocks);
|
|
167
|
+
blocksFrom(message.content);
|
|
168
|
+
return ids;
|
|
169
|
+
}
|
|
57
170
|
/**
|
|
58
|
-
* Tool-pair sanitization (
|
|
171
|
+
* Tool-pair sanitization (unmatched tool_use / tool_result repair):
|
|
172
|
+
* - Drop malformed `tool` messages without toolCallId.
|
|
59
173
|
* - Drop `tool` messages whose toolCallId has no surviving assistant tool_call.
|
|
60
|
-
* - For surviving assistant
|
|
61
|
-
*
|
|
62
|
-
*
|
|
63
|
-
*
|
|
64
|
-
*
|
|
174
|
+
* - For each surviving assistant tool_call, reattach the matching `tool`
|
|
175
|
+
* message (if any) immediately after that assistant; duplicate ids prefer
|
|
176
|
+
* the contiguous post-assistant block, then later matches, then earlier.
|
|
177
|
+
* - For tool_calls with no matching result, insert a stub tool message so
|
|
178
|
+
* the provider doesn't reject the request for unmatched tool_use_id.
|
|
179
|
+
* Non-tool message order is preserved; tool results are not duplicated.
|
|
65
180
|
*/
|
|
66
181
|
export function sanitizeToolPairs(messages) {
|
|
67
182
|
if (!Array.isArray(messages) || messages.length === 0) return messages;
|
|
68
183
|
const assistantCallIds = new Set();
|
|
69
184
|
for (const m of messages) {
|
|
70
|
-
|
|
71
|
-
for (const tc of m.toolCalls) {
|
|
72
|
-
if (tc && tc.id) assistantCallIds.add(tc.id);
|
|
73
|
-
}
|
|
74
|
-
}
|
|
75
|
-
}
|
|
76
|
-
const toolById = new Map();
|
|
77
|
-
for (const m of messages) {
|
|
78
|
-
if (m.role === 'tool' && m.toolCallId) toolById.set(m.toolCallId, m);
|
|
185
|
+
for (const id of collectAssistantToolCallIds(m)) assistantCallIds.add(id);
|
|
79
186
|
}
|
|
80
|
-
const
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
187
|
+
const pickToolResultForAssistant = (assistantIdx, toolCallId) => {
|
|
188
|
+
let i = assistantIdx + 1;
|
|
189
|
+
while (i < messages.length && messages[i]?.role === 'tool') {
|
|
190
|
+
const tm = messages[i];
|
|
191
|
+
if (tm.toolCallId === toolCallId) return tm;
|
|
192
|
+
i += 1;
|
|
193
|
+
}
|
|
194
|
+
let afterBlock = assistantIdx + 1;
|
|
195
|
+
while (afterBlock < messages.length && messages[afterBlock]?.role === 'tool') afterBlock += 1;
|
|
196
|
+
for (let j = afterBlock; j < messages.length; j += 1) {
|
|
197
|
+
const tm = messages[j];
|
|
198
|
+
if (tm?.role === 'tool' && tm.toolCallId === toolCallId) return tm;
|
|
199
|
+
}
|
|
200
|
+
for (let j = 0; j < assistantIdx; j += 1) {
|
|
201
|
+
const tm = messages[j];
|
|
202
|
+
if (tm?.role === 'tool' && tm.toolCallId === toolCallId) return tm;
|
|
203
|
+
}
|
|
204
|
+
return null;
|
|
205
|
+
};
|
|
206
|
+
const placedToolIds = new Set();
|
|
85
207
|
const result = [];
|
|
86
|
-
for (
|
|
208
|
+
for (let idx = 0; idx < messages.length; idx += 1) {
|
|
209
|
+
const m = messages[idx];
|
|
210
|
+
if (m.role === 'tool') {
|
|
211
|
+
if (!m.toolCallId) continue;
|
|
212
|
+
if (!assistantCallIds.has(m.toolCallId)) continue;
|
|
213
|
+
if (placedToolIds.has(m.toolCallId)) continue;
|
|
214
|
+
continue;
|
|
215
|
+
}
|
|
87
216
|
result.push(m);
|
|
88
|
-
if (m.role !== 'assistant'
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
if (
|
|
93
|
-
const
|
|
217
|
+
if (m.role !== 'assistant') continue;
|
|
218
|
+
const callIds = collectAssistantToolCallIds(m);
|
|
219
|
+
if (callIds.length === 0) continue;
|
|
220
|
+
for (const callId of callIds) {
|
|
221
|
+
if (placedToolIds.has(callId)) continue;
|
|
222
|
+
const existing = pickToolResultForAssistant(idx, callId);
|
|
223
|
+
if (existing) {
|
|
224
|
+
result.push(existing);
|
|
225
|
+
placedToolIds.add(callId);
|
|
226
|
+
continue;
|
|
227
|
+
}
|
|
94
228
|
result.push({
|
|
95
229
|
role: 'tool',
|
|
96
|
-
content:
|
|
97
|
-
toolCallId:
|
|
230
|
+
content: TOOL_MISSING_STUB,
|
|
231
|
+
toolCallId: callId,
|
|
98
232
|
});
|
|
233
|
+
placedToolIds.add(callId);
|
|
99
234
|
}
|
|
100
235
|
}
|
|
101
236
|
return result;
|
|
@@ -177,8 +312,27 @@ export function sanitizeAnthropicContentPairs(messages) {
|
|
|
177
312
|
if (!Array.isArray(messages)) return messages;
|
|
178
313
|
const work = messages.slice();
|
|
179
314
|
const out = [];
|
|
315
|
+
let pendingToolUseIds = new Set();
|
|
316
|
+
const stripOrphanToolResults = (userMsg, allowedIds) => {
|
|
317
|
+
if (userMsg?.role !== 'user' || !Array.isArray(userMsg.content)) return userMsg;
|
|
318
|
+
const hasToolResults = userMsg.content.some((b) => b?.type === 'tool_result');
|
|
319
|
+
if (!hasToolResults) return userMsg;
|
|
320
|
+
const filtered = userMsg.content.filter((b) => {
|
|
321
|
+
if (b?.type !== 'tool_result') return true;
|
|
322
|
+
if (!b.tool_use_id) return false;
|
|
323
|
+
return allowedIds.size > 0 && allowedIds.has(b.tool_use_id);
|
|
324
|
+
});
|
|
325
|
+
if (filtered.length === userMsg.content.length) return userMsg;
|
|
326
|
+
return { ...userMsg, content: filtered };
|
|
327
|
+
};
|
|
180
328
|
for (let i = 0; i < work.length; i++) {
|
|
181
329
|
let m = work[i];
|
|
330
|
+
if (m?.role === 'user' && Array.isArray(m.content)) {
|
|
331
|
+
const hadToolResults = m.content.some((b) => b?.type === 'tool_result');
|
|
332
|
+
m = stripOrphanToolResults(m, pendingToolUseIds);
|
|
333
|
+
work[i] = m;
|
|
334
|
+
if (hadToolResults) pendingToolUseIds = new Set();
|
|
335
|
+
}
|
|
182
336
|
// Drop tool_use blocks without an id from assistant messages — these
|
|
183
337
|
// come from partial streaming chunks that never finalised, and the
|
|
184
338
|
// provider rejects them as `tool_use ids were found without
|
|
@@ -192,13 +346,22 @@ export function sanitizeAnthropicContentPairs(messages) {
|
|
|
192
346
|
work[i] = m;
|
|
193
347
|
}
|
|
194
348
|
}
|
|
349
|
+
if (m?.role === 'user' && Array.isArray(m.content) && m.content.length === 0) continue;
|
|
195
350
|
out.push(m);
|
|
196
351
|
if (m?.role !== 'assistant' || !Array.isArray(m.content)) continue;
|
|
197
352
|
const toolUseIds = m.content
|
|
198
353
|
.filter((b) => b?.type === 'tool_use' && b.id)
|
|
199
354
|
.map((b) => b.id);
|
|
200
|
-
if (toolUseIds.length === 0)
|
|
201
|
-
|
|
355
|
+
if (toolUseIds.length === 0) {
|
|
356
|
+
pendingToolUseIds = new Set();
|
|
357
|
+
continue;
|
|
358
|
+
}
|
|
359
|
+
pendingToolUseIds = new Set(toolUseIds);
|
|
360
|
+
let next = work[i + 1];
|
|
361
|
+
if (next?.role === 'user' && Array.isArray(next.content)) {
|
|
362
|
+
next = stripOrphanToolResults(next, pendingToolUseIds);
|
|
363
|
+
work[i + 1] = next;
|
|
364
|
+
}
|
|
202
365
|
const nextResultIds = (next?.role === 'user' && Array.isArray(next.content))
|
|
203
366
|
? new Set(
|
|
204
367
|
next.content
|