muonroi-cli 1.7.2 → 1.8.1
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/dist/packages/agent-harness-core/src/event-filter.js +1 -0
- package/dist/packages/agent-harness-core/src/event-redact.js +8 -0
- package/dist/packages/agent-harness-core/src/mcp-server.d.ts +16 -0
- package/dist/packages/agent-harness-core/src/mcp-server.js +88 -10
- package/dist/packages/agent-harness-core/src/protocol.d.ts +15 -0
- package/dist/packages/agent-harness-core/src/selector.js +2 -2
- package/dist/packages/agent-harness-core/src/transports/sidechannel.js +1 -1
- package/dist/packages/agent-harness-opentui/src/agent-mode.js +1 -1
- package/dist/packages/agent-harness-opentui/src/install.js +1 -1
- package/dist/src/__test-stubs__/vitest-setup.js +4 -0
- package/dist/src/agent-harness/__tests__/mock-model.spec.js +54 -0
- package/dist/src/agent-harness/mock-model.d.ts +1 -1
- package/dist/src/agent-harness/mock-model.js +143 -6
- package/dist/src/agent-harness/test-spawn.js +3 -1
- package/dist/src/chat/chat-keychain.js +9 -1
- package/dist/src/cli/cost-forensics.js +7 -4
- package/dist/src/cli/keys.d.ts +2 -2
- package/dist/src/cli/keys.js +107 -28
- package/dist/src/cli/usage-report.js +2 -2
- package/dist/src/council/__tests__/abort-threading.test.d.ts +1 -0
- package/dist/src/council/__tests__/abort-threading.test.js +193 -0
- package/dist/src/council/__tests__/clarification-prompt.test.js +21 -1
- package/dist/src/council/__tests__/council-turn-length-emit.test.d.ts +1 -0
- package/dist/src/council/__tests__/council-turn-length-emit.test.js +130 -0
- package/dist/src/council/__tests__/debate-round-budget.test.d.ts +1 -0
- package/dist/src/council/__tests__/debate-round-budget.test.js +45 -0
- package/dist/src/council/__tests__/evaluator-metrics.test.js +3 -0
- package/dist/src/council/__tests__/post-debate-recommendation.test.d.ts +1 -0
- package/dist/src/council/__tests__/post-debate-recommendation.test.js +58 -0
- package/dist/src/council/__tests__/research-tools.test.js +4 -0
- package/dist/src/council/__tests__/round-tools.test.js +6 -0
- package/dist/src/council/clarifier-question-cap.test.d.ts +12 -0
- package/dist/src/council/clarifier-question-cap.test.js +73 -0
- package/dist/src/council/clarifier.d.ts +29 -1
- package/dist/src/council/clarifier.js +64 -1
- package/dist/src/council/debate-planner.d.ts +2 -1
- package/dist/src/council/debate-planner.js +5 -3
- package/dist/src/council/debate.d.ts +9 -0
- package/dist/src/council/debate.js +88 -8
- package/dist/src/council/index.d.ts +46 -1
- package/dist/src/council/index.js +125 -19
- package/dist/src/council/llm.js +41 -13
- package/dist/src/council/prompts.js +36 -1
- package/dist/src/council/types.d.ts +14 -1
- package/dist/src/ee/__tests__/export-transcripts.test.js +1 -1
- package/dist/src/ee/bridge.d.ts +3 -1
- package/dist/src/ee/bridge.js +9 -3
- package/dist/src/ee/bridge.test.js +1 -2
- package/dist/src/ee/client.js +15 -2
- package/dist/src/ee/recall-ledger.d.ts +20 -0
- package/dist/src/ee/recall-ledger.js +40 -1
- package/dist/src/ee/search.d.ts +25 -0
- package/dist/src/ee/search.js +73 -0
- package/dist/src/ee/types.d.ts +15 -1
- package/dist/src/ee/who-am-i.d.ts +41 -0
- package/dist/src/ee/who-am-i.js +143 -0
- package/dist/src/ee/who-am-i.test.d.ts +1 -0
- package/dist/src/ee/who-am-i.test.js +101 -0
- package/dist/src/flow/compaction/index.d.ts +1 -0
- package/dist/src/flow/compaction/index.js +4 -0
- package/dist/src/generated/version.d.ts +1 -1
- package/dist/src/generated/version.js +1 -1
- package/dist/src/headless/council-answers.js +1 -0
- package/dist/src/headless/output.d.ts +30 -1
- package/dist/src/headless/output.js +180 -0
- package/dist/src/headless/output.test.js +61 -1
- package/dist/src/hooks/index.js +48 -2
- package/dist/src/index.d.ts +2 -0
- package/dist/src/index.js +59 -62
- package/dist/src/lsp/client-normalize.test.d.ts +12 -0
- package/dist/src/lsp/client-normalize.test.js +34 -0
- package/dist/src/lsp/client.d.ts +1 -0
- package/dist/src/lsp/client.js +10 -2
- package/dist/src/lsp/manager.d.ts +1 -1
- package/dist/src/lsp/manager.js +13 -5
- package/dist/src/lsp/manager.test.js +41 -2
- package/dist/src/lsp/runtime.d.ts +1 -1
- package/dist/src/lsp/runtime.js +2 -2
- package/dist/src/maintain/__tests__/gh-create-pr.test.js +7 -3
- package/dist/src/maintain/__tests__/pr-builder.test.js +7 -3
- package/dist/src/mcp/__tests__/client-pool.spec.js +3 -3
- package/dist/src/mcp/__tests__/ee-tools.test.js +42 -14
- package/dist/src/mcp/__tests__/forensics-tools.test.js +3 -3
- package/dist/src/mcp/__tests__/lsp-tools.test.js +4 -4
- package/dist/src/mcp/auto-setup.js +0 -8
- package/dist/src/mcp/client-pool.d.ts +1 -1
- package/dist/src/mcp/client-pool.js +25 -9
- package/dist/src/mcp/ee-tools.d.ts +10 -0
- package/dist/src/mcp/ee-tools.js +34 -0
- package/dist/src/mcp/self-verify-jobs.d.ts +1 -0
- package/dist/src/mcp/self-verify-jobs.js +1 -3
- package/dist/src/mcp/self-verify-runner.js +1 -1
- package/dist/src/models/catalog-gemini.test.js +12 -10
- package/dist/src/models/catalog.json +106 -38
- package/dist/src/orchestrator/__tests__/auto-commit.test.d.ts +1 -0
- package/dist/src/orchestrator/__tests__/auto-commit.test.js +142 -0
- package/dist/src/orchestrator/__tests__/batch-turn-runner.test.js +3 -0
- package/dist/src/orchestrator/__tests__/delegations.test.d.ts +1 -0
- package/dist/src/orchestrator/__tests__/delegations.test.js +96 -0
- package/dist/src/orchestrator/__tests__/message-processor.test.js +2 -0
- package/dist/src/orchestrator/__tests__/prompts.test.d.ts +1 -0
- package/dist/src/orchestrator/__tests__/prompts.test.js +88 -0
- package/dist/src/orchestrator/__tests__/stream-runner.test.js +29 -0
- package/dist/src/orchestrator/__tests__/sub-session-delegation.test.d.ts +1 -0
- package/dist/src/orchestrator/__tests__/sub-session-delegation.test.js +507 -0
- package/dist/src/orchestrator/agent.test.js +5 -2
- package/dist/src/orchestrator/auto-commit.d.ts +110 -0
- package/dist/src/orchestrator/auto-commit.js +366 -0
- package/dist/src/orchestrator/batch-turn-runner.d.ts +3 -2
- package/dist/src/orchestrator/batch-turn-runner.js +2 -1
- package/dist/src/orchestrator/compaction-proposer-prompt.d.ts +8 -0
- package/dist/src/orchestrator/compaction-proposer-prompt.js +43 -0
- package/dist/src/orchestrator/compaction.d.ts +22 -0
- package/dist/src/orchestrator/compaction.js +42 -0
- package/dist/src/orchestrator/delegations.d.ts +1 -0
- package/dist/src/orchestrator/delegations.js +41 -0
- package/dist/src/orchestrator/error-utils.js +27 -2
- package/dist/src/orchestrator/message-processor.d.ts +23 -5
- package/dist/src/orchestrator/message-processor.js +226 -2573
- package/dist/src/orchestrator/orchestrator.d.ts +11 -2
- package/dist/src/orchestrator/orchestrator.js +530 -43
- package/dist/src/orchestrator/pending-calls.js +2 -1
- package/dist/src/orchestrator/preprocessor.d.ts +13 -0
- package/dist/src/orchestrator/preprocessor.js +146 -0
- package/dist/src/orchestrator/prompts.d.ts +16 -0
- package/dist/src/orchestrator/prompts.js +130 -67
- package/dist/src/orchestrator/safety-askcard.d.ts +66 -0
- package/dist/src/orchestrator/safety-askcard.js +74 -0
- package/dist/src/orchestrator/sandbox.test.js +8 -5
- package/dist/src/orchestrator/scope-ceiling.js +1 -1
- package/dist/src/orchestrator/stall-watchdog.d.ts +47 -0
- package/dist/src/orchestrator/stall-watchdog.js +23 -0
- package/dist/src/orchestrator/stall-watchdog.test.js +36 -1
- package/dist/src/orchestrator/stream-runner.js +52 -30
- package/dist/src/orchestrator/sub-agent-cap.d.ts +2 -0
- package/dist/src/orchestrator/sub-agent-cap.js +12 -4
- package/dist/src/orchestrator/sub-agent-model-tier.js +1 -1
- package/dist/src/orchestrator/subagent-compactor.d.ts +21 -0
- package/dist/src/orchestrator/subagent-compactor.js +148 -24
- package/dist/src/orchestrator/subagent-compactor.spec.js +249 -1
- package/dist/src/orchestrator/text-tool-call-detector.d.ts +9 -5
- package/dist/src/orchestrator/text-tool-call-detector.js +16 -12
- package/dist/src/orchestrator/text-tool-call-detector.test.js +66 -8
- package/dist/src/orchestrator/tool-args-repair.js +1 -1
- package/dist/src/orchestrator/tool-engine.d.ts +153 -0
- package/dist/src/orchestrator/tool-engine.js +3138 -0
- package/dist/src/orchestrator/turn-runner-deps.d.ts +3 -0
- package/dist/src/pil/__tests__/discovery-types.test.js +15 -4
- package/dist/src/pil/__tests__/discovery.test.js +89 -145
- package/dist/src/pil/__tests__/dual-run.test.js +2 -0
- package/dist/src/pil/__tests__/layer1-intent-trace.test.js +3 -0
- package/dist/src/pil/__tests__/layer1-intent.test.js +3 -0
- package/dist/src/pil/__tests__/layer16-clarity.test.js +35 -32
- package/dist/src/pil/__tests__/layer18-acceptance.test.js +1 -78
- package/dist/src/pil/__tests__/layer2_5-ponytail.test.d.ts +1 -0
- package/dist/src/pil/__tests__/layer2_5-ponytail.test.js +53 -0
- package/dist/src/pil/__tests__/layer3-ee-injection.test.js +67 -4
- package/dist/src/pil/__tests__/layer3-injected-chunk.test.js +14 -2
- package/dist/src/pil/__tests__/layer4-gsd.test.js +30 -0
- package/dist/src/pil/__tests__/layer5-context.test.js +39 -1
- package/dist/src/pil/__tests__/layer6-output.test.js +30 -2
- package/dist/src/pil/__tests__/llm-classify.test.js +79 -1
- package/dist/src/pil/__tests__/orchestrator-integration.test.js +3 -1
- package/dist/src/pil/__tests__/pipeline.test.js +11 -8
- package/dist/src/pil/__tests__/response-tools.test.js +52 -0
- package/dist/src/pil/agent-operating-contract.d.ts +1 -1
- package/dist/src/pil/agent-operating-contract.js +5 -3
- package/dist/src/pil/agent-operating-contract.test.js +2 -2
- package/dist/src/pil/cheap-model-playbook.d.ts +1 -1
- package/dist/src/pil/cheap-model-playbook.js +1 -1
- package/dist/src/pil/cheap-model-workbooks.d.ts +1 -1
- package/dist/src/pil/cheap-model-workbooks.js +1 -1
- package/dist/src/pil/config.d.ts +20 -0
- package/dist/src/pil/config.js +35 -0
- package/dist/src/pil/discovery-types.d.ts +32 -9
- package/dist/src/pil/discovery.d.ts +3 -3
- package/dist/src/pil/discovery.js +138 -244
- package/dist/src/pil/index.d.ts +1 -1
- package/dist/src/pil/index.js +1 -1
- package/dist/src/pil/layer1-intent.d.ts +9 -1
- package/dist/src/pil/layer1-intent.js +62 -7
- package/dist/src/pil/layer1-intent.test.js +112 -2
- package/dist/src/pil/layer15-context-scan.js +1 -1
- package/dist/src/pil/layer16-clarity.d.ts +21 -25
- package/dist/src/pil/layer16-clarity.js +52 -88
- package/dist/src/pil/layer18-acceptance.d.ts +13 -3
- package/dist/src/pil/layer18-acceptance.js +1 -75
- package/dist/src/pil/layer2-personality.js +7 -8
- package/dist/src/pil/layer2_5-ponytail.d.ts +2 -0
- package/dist/src/pil/layer2_5-ponytail.js +41 -0
- package/dist/src/pil/layer3-ee-injection.js +180 -14
- package/dist/src/pil/layer4-gsd.js +22 -8
- package/dist/src/pil/layer5-context.d.ts +1 -0
- package/dist/src/pil/layer5-context.js +26 -11
- package/dist/src/pil/layer6-output.js +23 -5
- package/dist/src/pil/llm-classify.d.ts +11 -0
- package/dist/src/pil/llm-classify.js +196 -3
- package/dist/src/pil/native-capabilities-workbook.d.ts +1 -1
- package/dist/src/pil/native-capabilities-workbook.js +4 -3
- package/dist/src/pil/pipeline.js +15 -1
- package/dist/src/pil/response-tools.d.ts +16 -0
- package/dist/src/pil/response-tools.js +41 -0
- package/dist/src/pil/schema.d.ts +8 -0
- package/dist/src/pil/schema.js +24 -3
- package/dist/src/pil/task-tier-map.js +7 -7
- package/dist/src/pil/types.d.ts +9 -3
- package/dist/src/product-loop/__tests__/discovery-interview.test.js +103 -1
- package/dist/src/product-loop/__tests__/gather-selectable-alts.test.js +34 -0
- package/dist/src/product-loop/__tests__/sprint-self-verify.test.js +12 -12
- package/dist/src/product-loop/discovery-interview.d.ts +20 -0
- package/dist/src/product-loop/discovery-interview.js +121 -7
- package/dist/src/product-loop/gather.js +19 -5
- package/dist/src/product-loop/loop-driver.js +2 -2
- package/dist/src/product-loop/sprint-self-verify.js +2 -2
- package/dist/src/providers/__tests__/reasoning-roundtrip.test.js +12 -0
- package/dist/src/providers/auth/__tests__/gemini-oauth.test.d.ts +1 -5
- package/dist/src/providers/auth/__tests__/gemini-oauth.test.js +1 -5
- package/dist/src/providers/auth/gcloud.d.ts +28 -0
- package/dist/src/providers/auth/gcloud.js +102 -0
- package/dist/src/providers/auth/gemini-oauth.d.ts +46 -19
- package/dist/src/providers/auth/gemini-oauth.js +292 -105
- package/dist/src/providers/auth/grok-oauth.d.ts +1 -1
- package/dist/src/providers/auth/grok-oauth.js +1 -1
- package/dist/src/providers/auth/registry.js +28 -6
- package/dist/src/providers/gemini.d.ts +1 -1
- package/dist/src/providers/gemini.js +10 -3
- package/dist/src/providers/keychain.d.ts +2 -2
- package/dist/src/providers/keychain.js +20 -2
- package/dist/src/providers/openai.js +5 -1
- package/dist/src/providers/runtime.d.ts +1 -1
- package/dist/src/providers/runtime.js +11 -2
- package/dist/src/providers/siliconflow-sse-repair.js +1 -1
- package/dist/src/providers/strategies/google.strategy.d.ts +9 -2
- package/dist/src/providers/strategies/google.strategy.js +159 -9
- package/dist/src/providers/strategies/thinking-mode.js +19 -6
- package/dist/src/providers/types.d.ts +1 -1
- package/dist/src/router/decide.test.js +40 -8
- package/dist/src/router/step-router.d.ts +20 -0
- package/dist/src/router/step-router.js +73 -0
- package/dist/src/self-qa/agentic-loop.js +30 -21
- package/dist/src/self-qa/orchestrator.js +5 -5
- package/dist/src/self-qa/scenario-planner.js +1 -1
- package/dist/src/self-qa/spec-emitter.js +4 -4
- package/dist/src/storage/__tests__/hierarchical-session.test.d.ts +1 -0
- package/dist/src/storage/__tests__/hierarchical-session.test.js +161 -0
- package/dist/src/storage/__tests__/sweep-stale-pending.test.js +2 -2
- package/dist/src/storage/index.d.ts +1 -1
- package/dist/src/storage/index.js +1 -1
- package/dist/src/storage/migrations.js +13 -1
- package/dist/src/storage/transcript-response-entry.test.js +18 -5
- package/dist/src/storage/transcript-view.js +7 -1
- package/dist/src/storage/transcript.d.ts +3 -0
- package/dist/src/storage/transcript.js +254 -73
- package/dist/src/storage/ui-interaction-log.d.ts +17 -0
- package/dist/src/storage/usage.js +22 -5
- package/dist/src/tools/__tests__/native-tools.test.js +6 -0
- package/dist/src/tools/bash.d.ts +36 -23
- package/dist/src/tools/bash.js +59 -301
- package/dist/src/tools/bash.test.js +2 -257
- package/dist/src/tools/file-tracker.d.ts +5 -0
- package/dist/src/tools/file-tracker.js +14 -0
- package/dist/src/tools/file.js +2 -0
- package/dist/src/tools/git-safety.d.ts +38 -1
- package/dist/src/tools/git-safety.js +94 -4
- package/dist/src/tools/git-safety.test.js +45 -1
- package/dist/src/tools/native-tools.d.ts +1 -1
- package/dist/src/tools/native-tools.js +66 -1
- package/dist/src/tools/registry-bash-empty-command.test.js +4 -3
- package/dist/src/tools/registry-git-safety.test.js +17 -6
- package/dist/src/tools/registry.d.ts +6 -0
- package/dist/src/tools/registry.js +215 -13
- package/dist/src/types/index.d.ts +8 -2
- package/dist/src/ui/app.d.ts +1 -0
- package/dist/src/ui/app.js +0 -0
- package/dist/src/ui/components/council-question-card.js +1 -0
- package/dist/src/ui/components/message-view.js +37 -3
- package/dist/src/ui/components/structured-response-view.js +7 -0
- package/dist/src/ui/components/task-list-panel.js +3 -12
- package/dist/src/ui/containers/chat-feed.d.ts +40 -0
- package/dist/src/ui/containers/chat-feed.js +66 -0
- package/dist/src/ui/containers/modals-layer.d.ts +87 -0
- package/dist/src/ui/containers/modals-layer.js +18 -0
- package/dist/src/ui/slash/__tests__/menu-parity.test.d.ts +1 -0
- package/dist/src/ui/slash/__tests__/menu-parity.test.js +1 -0
- package/dist/src/ui/slash/__tests__/ponytail.test.d.ts +1 -0
- package/dist/src/ui/slash/__tests__/ponytail.test.js +37 -0
- package/dist/src/ui/slash/expand.js +14 -1
- package/dist/src/ui/slash/export.js +16 -1
- package/dist/src/ui/slash/menu-items.js +1 -0
- package/dist/src/ui/slash/ponytail.d.ts +2 -0
- package/dist/src/ui/slash/ponytail.js +19 -0
- package/dist/src/ui/status-bar/store.js +0 -1
- package/dist/src/ui/status-bar/store.test.js +2 -2
- package/dist/src/ui/use-app-logic.d.ts +26 -0
- package/dist/src/ui/use-app-logic.js +0 -0
- package/dist/src/ui/utils/relaunch.js +1 -1
- package/dist/src/ui/utils/text.d.ts +11 -0
- package/dist/src/ui/utils/text.js +21 -1
- package/dist/src/ui/utils/text.test.js +25 -1
- package/dist/src/ui/utils/tools.js +3 -1
- package/dist/src/usage/cost-log.js +2 -2
- package/dist/src/usage/estimator.d.ts +14 -0
- package/dist/src/usage/estimator.js +22 -0
- package/dist/src/utils/__tests__/compaction-caps.test.d.ts +21 -0
- package/dist/src/utils/__tests__/compaction-caps.test.js +187 -0
- package/dist/src/utils/__tests__/llm-deadline-abort.test.d.ts +1 -0
- package/dist/src/utils/__tests__/llm-deadline-abort.test.js +33 -0
- package/dist/src/utils/__tests__/llm-deadline.test.js +16 -4
- package/dist/src/utils/__tests__/logger.test.d.ts +1 -0
- package/dist/src/utils/__tests__/logger.test.js +115 -0
- package/dist/src/utils/__tests__/settings-agent-first.test.d.ts +1 -0
- package/dist/src/utils/__tests__/settings-agent-first.test.js +48 -0
- package/dist/src/utils/install-manager.d.ts +1 -0
- package/dist/src/utils/install-manager.js +76 -7
- package/dist/src/utils/install-manager.test.js +43 -1
- package/dist/src/utils/llm-deadline.d.ts +9 -1
- package/dist/src/utils/llm-deadline.js +31 -2
- package/dist/src/utils/logger.d.ts +28 -0
- package/dist/src/utils/logger.js +171 -0
- package/dist/src/utils/permission-mode.d.ts +33 -0
- package/dist/src/utils/permission-mode.js +164 -3
- package/dist/src/utils/permission-mode.test.js +92 -1
- package/dist/src/utils/settings.d.ts +36 -32
- package/dist/src/utils/settings.js +151 -123
- package/dist/src/utils/update-checker.test.js +5 -1
- package/package.json +1 -1
|
@@ -104,11 +104,12 @@ export function isHighValueToolResult(toolName, preview, explicitKeepIds, toolCa
|
|
|
104
104
|
const p = preview.toLowerCase();
|
|
105
105
|
if (/error|fail|todo|plan|done|✔|blocked|critical/.test(p))
|
|
106
106
|
return true;
|
|
107
|
-
if (/\.(ts|tsx|js|md|json|test|spec)\b/.test(p) || p.includes("src/") || p.includes("PLAN"))
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
return
|
|
111
|
-
|
|
107
|
+
if (/\.(ts|tsx|js|md|json|test|spec)\b/.test(p) || p.includes("src/") || p.includes("PLAN")) {
|
|
108
|
+
// For source code/plans, only keep verbatim if it is relatively small (under 12000 chars)
|
|
109
|
+
// to prevent large file reads from leaking memory/tokens indefinitely in long sessions.
|
|
110
|
+
return preview.length <= 12000;
|
|
111
|
+
}
|
|
112
|
+
return false;
|
|
112
113
|
}
|
|
113
114
|
if (toolCallId && explicitKeepIds?.has(toolCallId))
|
|
114
115
|
return true;
|
|
@@ -126,6 +127,7 @@ function resolveOpts(o) {
|
|
|
126
127
|
contextFillRatio: Math.min(0.95, Math.max(0.1, o?.contextFillRatio ?? 0.5)),
|
|
127
128
|
keepToolIds: keepIds,
|
|
128
129
|
persistArtifact: o?.persistArtifact,
|
|
130
|
+
stripOldReasoning: o?.stripOldReasoning ?? false,
|
|
129
131
|
};
|
|
130
132
|
}
|
|
131
133
|
/**
|
|
@@ -324,22 +326,25 @@ function rewriteOlderToolMessage(msg, previewChars, label, keepToolIds, persistA
|
|
|
324
326
|
export function compactSubAgentMessages(messages, opts = {}) {
|
|
325
327
|
const resolved = resolveOpts(opts);
|
|
326
328
|
const { outputPreviewChars, label, envelopeChars } = resolved;
|
|
327
|
-
//
|
|
328
|
-
//
|
|
329
|
-
//
|
|
330
|
-
|
|
329
|
+
// Step 4: Hard-limit message history sent to the model to prevent token bloating
|
|
330
|
+
// When input (messages + envelope) exceeds 50K characters and messages array is > 30,
|
|
331
|
+
// we slice the history to keep at most 30 messages (preserving system and user start).
|
|
332
|
+
let processedMessages = messages;
|
|
331
333
|
const messagesTotal = cumulativeMessageChars(messages);
|
|
332
334
|
const total = messagesTotal + envelopeChars;
|
|
333
|
-
|
|
334
|
-
|
|
335
|
-
|
|
336
|
-
|
|
335
|
+
if (total > 50_000 && messages.length > 30) {
|
|
336
|
+
processedMessages = sliceMessageHistory(messages, 30);
|
|
337
|
+
}
|
|
338
|
+
// Calculate effective thresholds and keep last turns using the processed messages
|
|
339
|
+
const processedMessagesTotal = cumulativeMessageChars(processedMessages);
|
|
340
|
+
const processedTotal = processedMessagesTotal + envelopeChars;
|
|
341
|
+
const { effectiveThresholdChars, effectiveKeepLastTurns } = computeDynamicParams(processedTotal, resolved);
|
|
337
342
|
// No-op: return the input BY REFERENCE (contract above) so `compacted === input`.
|
|
338
|
-
if (
|
|
339
|
-
return
|
|
340
|
-
const keepFrom = findKeepFromIndex(
|
|
343
|
+
if (processedTotal < effectiveThresholdChars)
|
|
344
|
+
return processedMessages;
|
|
345
|
+
const keepFrom = findKeepFromIndex(processedMessages, effectiveKeepLastTurns);
|
|
341
346
|
if (keepFrom <= 0)
|
|
342
|
-
return
|
|
347
|
+
return processedMessages;
|
|
343
348
|
// Walk older messages; rewrite fresh tool results into stubs, super-shrink
|
|
344
349
|
// already-stubbed results (F1), and strip args off older assistant
|
|
345
350
|
// tool-call shells (F1). The 1:1 assistant↔tool pairing required by the AI
|
|
@@ -347,8 +352,8 @@ export function compactSubAgentMessages(messages, opts = {}) {
|
|
|
347
352
|
// structure or count.
|
|
348
353
|
let firstUserSeen = false;
|
|
349
354
|
const out = [];
|
|
350
|
-
for (let i = 0; i <
|
|
351
|
-
const msg =
|
|
355
|
+
for (let i = 0; i < processedMessages.length; i++) {
|
|
356
|
+
const msg = processedMessages[i];
|
|
352
357
|
if (i >= keepFrom) {
|
|
353
358
|
out.push(msg);
|
|
354
359
|
continue;
|
|
@@ -378,17 +383,43 @@ export function compactSubAgentMessages(messages, opts = {}) {
|
|
|
378
383
|
continue;
|
|
379
384
|
}
|
|
380
385
|
if (msg.role === "assistant" && Array.isArray(msg.content)) {
|
|
381
|
-
//
|
|
382
|
-
//
|
|
383
|
-
// the
|
|
384
|
-
//
|
|
385
|
-
|
|
386
|
+
// T1.1 — strip reasoning parts from older assistant turns. DeepSeek V4
|
|
387
|
+
// Flash / R1 emit 2K-5K reasoning tokens per turn that accumulate
|
|
388
|
+
// across the multi-step loop. These are never re-read by the model
|
|
389
|
+
// but cost full input billing on every subsequent round. Strip them
|
|
390
|
+
// from turns older than keepLastTurns to cut ~30-50% of input tokens.
|
|
391
|
+
// Then F1 — strip args off older assistant tool-call shells.
|
|
392
|
+
let processed = resolved.stripOldReasoning ? stripAssistantReasoning(msg) : msg;
|
|
393
|
+
processed = stripAssistantToolCallArgs(processed);
|
|
394
|
+
out.push(processed);
|
|
386
395
|
continue;
|
|
387
396
|
}
|
|
388
397
|
out.push(msg);
|
|
389
398
|
}
|
|
390
399
|
return out;
|
|
391
400
|
}
|
|
401
|
+
/**
|
|
402
|
+
* T1.1 — strip reasoning parts from an assistant message. Reasoning tokens
|
|
403
|
+
* (CoT / `<think>`) from older turns have zero re-read value for the model
|
|
404
|
+
* on subsequent steps but they accumulate O(N) chars per turn and are billed
|
|
405
|
+
* as full input tokens. Removing them from turns older than keepLastTurns
|
|
406
|
+
* cuts ~30-50% of cumulative input in multi-step loops with reasoning models
|
|
407
|
+
* (DeepSeek V4 Flash, R1, etc.).
|
|
408
|
+
*
|
|
409
|
+
* Preserves text + tool-call parts (the structural skeleton the model needs
|
|
410
|
+
* to maintain coherent tool-call↔tool-result pairing).
|
|
411
|
+
*/
|
|
412
|
+
function stripAssistantReasoning(msg) {
|
|
413
|
+
if (!Array.isArray(msg.content))
|
|
414
|
+
return msg;
|
|
415
|
+
const parts = msg.content;
|
|
416
|
+
const filtered = parts.filter((part) => part.type !== "reasoning");
|
|
417
|
+
if (filtered.length === parts.length)
|
|
418
|
+
return msg; // nothing stripped
|
|
419
|
+
// Edge case: if ALL parts were reasoning (no text/tool-call), keep the
|
|
420
|
+
// message with an empty content array to preserve message-count pairing.
|
|
421
|
+
return { ...msg, content: filtered };
|
|
422
|
+
}
|
|
392
423
|
function stripAssistantToolCallArgs(msg) {
|
|
393
424
|
if (!Array.isArray(msg.content))
|
|
394
425
|
return msg;
|
|
@@ -424,4 +455,97 @@ function stripAssistantToolCallArgs(msg) {
|
|
|
424
455
|
return msg;
|
|
425
456
|
return { ...msg, content: next };
|
|
426
457
|
}
|
|
458
|
+
/**
|
|
459
|
+
* Injects Anthropic prompt caching (cacheControl) into the last message's content
|
|
460
|
+
* block(s) if the model is Claude (starts with 'claude').
|
|
461
|
+
* Creates a copy of the messages array and the last message to avoid mutating in-place.
|
|
462
|
+
*/
|
|
463
|
+
export function applyAnthropicPromptCaching(messages, modelId) {
|
|
464
|
+
if (!modelId.startsWith("claude")) {
|
|
465
|
+
return messages;
|
|
466
|
+
}
|
|
467
|
+
if (messages.length === 0) {
|
|
468
|
+
return messages;
|
|
469
|
+
}
|
|
470
|
+
const newMessages = [...messages];
|
|
471
|
+
const lastIndex = newMessages.length - 1;
|
|
472
|
+
const originalLastMsg = newMessages[lastIndex];
|
|
473
|
+
if (!originalLastMsg)
|
|
474
|
+
return messages;
|
|
475
|
+
const lastMsg = { ...originalLastMsg };
|
|
476
|
+
if (typeof lastMsg.content === "string") {
|
|
477
|
+
lastMsg.content = [
|
|
478
|
+
{
|
|
479
|
+
type: "text",
|
|
480
|
+
text: lastMsg.content,
|
|
481
|
+
providerOptions: {
|
|
482
|
+
anthropic: { cacheControl: { type: "ephemeral" } },
|
|
483
|
+
},
|
|
484
|
+
},
|
|
485
|
+
];
|
|
486
|
+
}
|
|
487
|
+
else if (Array.isArray(lastMsg.content)) {
|
|
488
|
+
const content = [...lastMsg.content];
|
|
489
|
+
const lastBlockIndex = content.length - 1;
|
|
490
|
+
if (lastBlockIndex >= 0) {
|
|
491
|
+
const lastBlock = { ...content[lastBlockIndex] };
|
|
492
|
+
lastBlock.providerOptions = {
|
|
493
|
+
...lastBlock.providerOptions,
|
|
494
|
+
anthropic: { cacheControl: { type: "ephemeral" } },
|
|
495
|
+
};
|
|
496
|
+
content[lastBlockIndex] = lastBlock;
|
|
497
|
+
lastMsg.content = content;
|
|
498
|
+
}
|
|
499
|
+
}
|
|
500
|
+
newMessages[lastIndex] = lastMsg;
|
|
501
|
+
return newMessages;
|
|
502
|
+
}
|
|
503
|
+
/**
|
|
504
|
+
* Safely slice message history to keep at most `maxMessages` messages.
|
|
505
|
+
* Preserves the system message(s) at the front and ensures that the slice
|
|
506
|
+
* starts with a "user" message and does not split assistant tool calls and
|
|
507
|
+
* corresponding tool results.
|
|
508
|
+
*/
|
|
509
|
+
export function sliceMessageHistory(messages, maxMessages = 30) {
|
|
510
|
+
if (messages.length <= maxMessages)
|
|
511
|
+
return messages;
|
|
512
|
+
// Find all user message indices (excluding system messages)
|
|
513
|
+
const userIndices = [];
|
|
514
|
+
for (let idx = 0; idx < messages.length; idx++) {
|
|
515
|
+
if (messages[idx]?.role === "user") {
|
|
516
|
+
userIndices.push(idx);
|
|
517
|
+
}
|
|
518
|
+
}
|
|
519
|
+
if (userIndices.length === 0) {
|
|
520
|
+
return messages;
|
|
521
|
+
}
|
|
522
|
+
// Group messages into turns.
|
|
523
|
+
// Each turn is a range [start, end] inclusive.
|
|
524
|
+
const turns = [];
|
|
525
|
+
for (let idx = 0; idx < userIndices.length; idx++) {
|
|
526
|
+
const start = userIndices[idx];
|
|
527
|
+
const end = idx + 1 < userIndices.length ? userIndices[idx + 1] - 1 : messages.length - 1;
|
|
528
|
+
turns.push({ start, end });
|
|
529
|
+
}
|
|
530
|
+
// Accumulate turns from the end, up to maxMessages
|
|
531
|
+
let keptMessagesCount = 0;
|
|
532
|
+
let keepFromIndex = -1;
|
|
533
|
+
for (let idx = turns.length - 1; idx >= 0; idx--) {
|
|
534
|
+
const turn = turns[idx];
|
|
535
|
+
const turnLength = turn.end - turn.start + 1;
|
|
536
|
+
if (keptMessagesCount === 0 || keptMessagesCount + turnLength <= maxMessages) {
|
|
537
|
+
keptMessagesCount += turnLength;
|
|
538
|
+
keepFromIndex = turn.start;
|
|
539
|
+
}
|
|
540
|
+
else {
|
|
541
|
+
break;
|
|
542
|
+
}
|
|
543
|
+
}
|
|
544
|
+
if (keepFromIndex === -1) {
|
|
545
|
+
return messages;
|
|
546
|
+
}
|
|
547
|
+
const kept = messages.slice(keepFromIndex);
|
|
548
|
+
const systemMessages = messages.filter((m) => m.role === "system");
|
|
549
|
+
return [...systemMessages, ...kept];
|
|
550
|
+
}
|
|
427
551
|
//# sourceMappingURL=subagent-compactor.js.map
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import { afterEach, describe, expect, it } from "vitest";
|
|
2
|
-
import { compactSubAgentMessages, cumulativeMessageChars } from "./subagent-compactor.js";
|
|
2
|
+
import { compactSubAgentMessages, cumulativeMessageChars, sliceMessageHistory } from "./subagent-compactor.js";
|
|
3
3
|
function bigText(label, kb) {
|
|
4
4
|
const block = `${label}:${"x".repeat(kb * 1000)}`;
|
|
5
5
|
return block;
|
|
@@ -384,5 +384,253 @@ describe("subagent-compactor: compactSubAgentMessages", () => {
|
|
|
384
384
|
}
|
|
385
385
|
expect(keptExplicit).toBe(true);
|
|
386
386
|
});
|
|
387
|
+
it("elides low-value large read_file results while keeping high-value ones", () => {
|
|
388
|
+
// Turn 1: large low-value read_file output (15000 chars of 'x', no ts/src/PLAN/error keywords)
|
|
389
|
+
const lowValueOutput = "x".repeat(15000);
|
|
390
|
+
// Turn 2: large high-value read_file output containing 'error'
|
|
391
|
+
const highValueOutputWithError = "error: failed to load module\n" + "x".repeat(15000);
|
|
392
|
+
// Turn 3: small source file read (5000 chars of 'x' containing 'src/index.ts')
|
|
393
|
+
const highValueSourceOutput = "src/index.ts\n" + "x".repeat(5000);
|
|
394
|
+
const msgs = [
|
|
395
|
+
{ role: "system", content: "You are the agent." },
|
|
396
|
+
{ role: "user", content: "perform task" },
|
|
397
|
+
{
|
|
398
|
+
role: "assistant",
|
|
399
|
+
content: [{ type: "tool-call", toolCallId: "call_low", toolName: "read_file", input: {} }],
|
|
400
|
+
},
|
|
401
|
+
{
|
|
402
|
+
role: "tool",
|
|
403
|
+
content: [
|
|
404
|
+
{
|
|
405
|
+
type: "tool-result",
|
|
406
|
+
toolCallId: "call_low",
|
|
407
|
+
toolName: "read_file",
|
|
408
|
+
output: { type: "text", value: lowValueOutput },
|
|
409
|
+
},
|
|
410
|
+
],
|
|
411
|
+
},
|
|
412
|
+
{
|
|
413
|
+
role: "assistant",
|
|
414
|
+
content: [{ type: "tool-call", toolCallId: "call_err", toolName: "read_file", input: {} }],
|
|
415
|
+
},
|
|
416
|
+
{
|
|
417
|
+
role: "tool",
|
|
418
|
+
content: [
|
|
419
|
+
{
|
|
420
|
+
type: "tool-result",
|
|
421
|
+
toolCallId: "call_err",
|
|
422
|
+
toolName: "read_file",
|
|
423
|
+
output: { type: "text", value: highValueOutputWithError },
|
|
424
|
+
},
|
|
425
|
+
],
|
|
426
|
+
},
|
|
427
|
+
{
|
|
428
|
+
role: "assistant",
|
|
429
|
+
content: [{ type: "tool-call", toolCallId: "call_src", toolName: "read_file", input: {} }],
|
|
430
|
+
},
|
|
431
|
+
{
|
|
432
|
+
role: "tool",
|
|
433
|
+
content: [
|
|
434
|
+
{
|
|
435
|
+
type: "tool-result",
|
|
436
|
+
toolCallId: "call_src",
|
|
437
|
+
toolName: "read_file",
|
|
438
|
+
output: { type: "text", value: highValueSourceOutput },
|
|
439
|
+
},
|
|
440
|
+
],
|
|
441
|
+
},
|
|
442
|
+
// Add a couple of low-value turns to push past threshold and keepLast turns (keepLast=1)
|
|
443
|
+
...toolTurn(9, 10),
|
|
444
|
+
];
|
|
445
|
+
// Force low value on the final kept turn
|
|
446
|
+
msgs[msgs.length - 1].content[0].toolName = "other_tool";
|
|
447
|
+
const out = compactSubAgentMessages(msgs, { thresholdChars: 10_000, keepLastTurns: 1 });
|
|
448
|
+
// low-value large read_file MUST be elided
|
|
449
|
+
const lowValMsg = out.find((m) => m.role === "tool" && Array.isArray(m.content) && m.content[0]?.toolCallId === "call_low");
|
|
450
|
+
expect((lowValMsg?.content)[0].output.value).toMatch(/elided by sub-agent compactor/);
|
|
451
|
+
// high-value large error read_file MUST NOT be elided
|
|
452
|
+
const errValMsg = out.find((m) => m.role === "tool" && Array.isArray(m.content) && m.content[0]?.toolCallId === "call_err");
|
|
453
|
+
expect((errValMsg?.content)[0].output.value).toBe(highValueOutputWithError);
|
|
454
|
+
// high-value small source read_file MUST NOT be elided
|
|
455
|
+
const srcValMsg = out.find((m) => m.role === "tool" && Array.isArray(m.content) && m.content[0]?.toolCallId === "call_src");
|
|
456
|
+
expect((srcValMsg?.content)[0].output.value).toBe(highValueSourceOutput);
|
|
457
|
+
});
|
|
458
|
+
it("T1.1 — strips reasoning parts from old assistant turns when stripOldReasoning is enabled", () => {
|
|
459
|
+
// Build history with reasoning parts in assistant messages
|
|
460
|
+
const msgs = [
|
|
461
|
+
{ role: "system", content: "You are an agent." },
|
|
462
|
+
{ role: "user", content: "fix the bug" },
|
|
463
|
+
];
|
|
464
|
+
// Add 6 tool turns with reasoning in assistant messages
|
|
465
|
+
for (let i = 1; i <= 6; i++) {
|
|
466
|
+
msgs.push({
|
|
467
|
+
role: "assistant",
|
|
468
|
+
content: [
|
|
469
|
+
{ type: "reasoning", text: `Thinking about step ${i}... ${"x".repeat(3000)}` },
|
|
470
|
+
{ type: "text", text: `Step ${i} analysis` },
|
|
471
|
+
{ type: "tool-call", toolCallId: `call_${i}`, toolName: "bash", input: `echo ${i}` },
|
|
472
|
+
],
|
|
473
|
+
});
|
|
474
|
+
msgs.push({
|
|
475
|
+
role: "tool",
|
|
476
|
+
content: [
|
|
477
|
+
{
|
|
478
|
+
type: "tool-result",
|
|
479
|
+
toolCallId: `call_${i}`,
|
|
480
|
+
toolName: "bash",
|
|
481
|
+
output: { type: "text", value: bigText(`R${i}`, 2) },
|
|
482
|
+
},
|
|
483
|
+
],
|
|
484
|
+
});
|
|
485
|
+
}
|
|
486
|
+
const out = compactSubAgentMessages(msgs, {
|
|
487
|
+
thresholdChars: 1000, // force compaction
|
|
488
|
+
keepLastTurns: 2,
|
|
489
|
+
stripOldReasoning: true,
|
|
490
|
+
});
|
|
491
|
+
// Old turns (1-4) should have reasoning stripped
|
|
492
|
+
const oldAssistants = out.filter((m, idx) => m.role === "assistant" && idx < out.length - 4);
|
|
493
|
+
for (const m of oldAssistants) {
|
|
494
|
+
const content = m.content;
|
|
495
|
+
if (!Array.isArray(content))
|
|
496
|
+
continue;
|
|
497
|
+
const hasReasoning = content.some((p) => p.type === "reasoning");
|
|
498
|
+
expect(hasReasoning).toBe(false);
|
|
499
|
+
}
|
|
500
|
+
// Recent turns (last 2) should still have reasoning
|
|
501
|
+
const recentAssistants = out.filter((m, idx) => m.role === "assistant" && idx >= out.length - 4);
|
|
502
|
+
for (const m of recentAssistants) {
|
|
503
|
+
const content = m.content;
|
|
504
|
+
if (!Array.isArray(content))
|
|
505
|
+
continue;
|
|
506
|
+
const hasReasoning = content.some((p) => p.type === "reasoning");
|
|
507
|
+
expect(hasReasoning).toBe(true);
|
|
508
|
+
}
|
|
509
|
+
});
|
|
510
|
+
it("T1.1 — does NOT strip reasoning when stripOldReasoning is false (default)", () => {
|
|
511
|
+
const msgs = [
|
|
512
|
+
{ role: "system", content: "You are an agent." },
|
|
513
|
+
{ role: "user", content: "fix the bug" },
|
|
514
|
+
];
|
|
515
|
+
for (let i = 1; i <= 4; i++) {
|
|
516
|
+
msgs.push({
|
|
517
|
+
role: "assistant",
|
|
518
|
+
content: [
|
|
519
|
+
{ type: "reasoning", text: `Thinking... ${"x".repeat(3000)}` },
|
|
520
|
+
{ type: "tool-call", toolCallId: `call_${i}`, toolName: "bash", input: `echo ${i}` },
|
|
521
|
+
],
|
|
522
|
+
});
|
|
523
|
+
msgs.push({
|
|
524
|
+
role: "tool",
|
|
525
|
+
content: [
|
|
526
|
+
{
|
|
527
|
+
type: "tool-result",
|
|
528
|
+
toolCallId: `call_${i}`,
|
|
529
|
+
toolName: "bash",
|
|
530
|
+
output: { type: "text", value: bigText(`R${i}`, 2) },
|
|
531
|
+
},
|
|
532
|
+
],
|
|
533
|
+
});
|
|
534
|
+
}
|
|
535
|
+
const out = compactSubAgentMessages(msgs, {
|
|
536
|
+
thresholdChars: 1000,
|
|
537
|
+
keepLastTurns: 1,
|
|
538
|
+
stripOldReasoning: false, // explicitly off
|
|
539
|
+
});
|
|
540
|
+
// All assistant messages should still have reasoning parts
|
|
541
|
+
const assistants = out.filter((m) => m.role === "assistant");
|
|
542
|
+
for (const m of assistants) {
|
|
543
|
+
const content = m.content;
|
|
544
|
+
if (!Array.isArray(content))
|
|
545
|
+
continue;
|
|
546
|
+
const hasReasoning = content.some((p) => p.type === "reasoning");
|
|
547
|
+
expect(hasReasoning).toBe(true);
|
|
548
|
+
}
|
|
549
|
+
});
|
|
550
|
+
describe("sliceMessageHistory & Step 4 truncation", () => {
|
|
551
|
+
it("preserves system prompt and user start while slicing excess messages", () => {
|
|
552
|
+
const msgs = [
|
|
553
|
+
{ role: "system", content: "system prompt" },
|
|
554
|
+
{ role: "user", content: "first prompt" },
|
|
555
|
+
{ role: "assistant", content: "first answer" },
|
|
556
|
+
{ role: "user", content: "second prompt" },
|
|
557
|
+
{
|
|
558
|
+
role: "assistant",
|
|
559
|
+
content: [{ type: "tool-call", toolCallId: "call-1", toolName: "bash", input: "ls" }],
|
|
560
|
+
},
|
|
561
|
+
{
|
|
562
|
+
role: "tool",
|
|
563
|
+
content: [{ type: "tool-result", toolCallId: "call-1", toolName: "bash", output: "file1" }],
|
|
564
|
+
},
|
|
565
|
+
{ role: "assistant", content: "second answer" },
|
|
566
|
+
{ role: "user", content: "third prompt" },
|
|
567
|
+
{
|
|
568
|
+
role: "assistant",
|
|
569
|
+
content: [{ type: "tool-call", toolCallId: "call-2", toolName: "bash", input: "pwd" }],
|
|
570
|
+
},
|
|
571
|
+
{
|
|
572
|
+
role: "tool",
|
|
573
|
+
content: [{ type: "tool-result", toolCallId: "call-2", toolName: "bash", output: "/dir" }],
|
|
574
|
+
},
|
|
575
|
+
{ role: "assistant", content: "third answer" },
|
|
576
|
+
];
|
|
577
|
+
// Slicing to max 5 messages should start cleanly at a user message
|
|
578
|
+
const sliced = sliceMessageHistory(msgs, 5);
|
|
579
|
+
expect(sliced[0].role).toBe("system");
|
|
580
|
+
expect(sliced[1].role).toBe("user");
|
|
581
|
+
// Slicing walked backward to find a user message start that satisfies max 5.
|
|
582
|
+
// Last messages:
|
|
583
|
+
// assistant: third answer (idx 10) -> tool result 2 (idx 9) -> assistant call 2 (idx 8) -> user third prompt (idx 7).
|
|
584
|
+
// That is 4 messages. Since kept must start with user, it stops at idx 7.
|
|
585
|
+
// So kept messages are from idx 7 onwards: [user third prompt, assistant call 2, tool result 2, assistant third answer]
|
|
586
|
+
expect(sliced).toHaveLength(5); // 1 system + 4 kept messages
|
|
587
|
+
expect(sliced[1].content).toBe("third prompt");
|
|
588
|
+
});
|
|
589
|
+
it("does not orphaned tool-calls and tool-results", () => {
|
|
590
|
+
const msgs = [
|
|
591
|
+
{ role: "system", content: "system prompt" },
|
|
592
|
+
{ role: "user", content: "prompt" },
|
|
593
|
+
{
|
|
594
|
+
role: "assistant",
|
|
595
|
+
content: [{ type: "tool-call", toolCallId: "call-99", toolName: "bash", input: "whoami" }],
|
|
596
|
+
},
|
|
597
|
+
{
|
|
598
|
+
role: "tool",
|
|
599
|
+
content: [{ type: "tool-result", toolCallId: "call-99", toolName: "bash", output: "root" }],
|
|
600
|
+
},
|
|
601
|
+
{ role: "assistant", content: "hello" },
|
|
602
|
+
];
|
|
603
|
+
// Even if maxMessages is 2, it shouldn't split assistant call-99 and tool result-99.
|
|
604
|
+
// Walking backward:
|
|
605
|
+
// - assistant: hello (count 1)
|
|
606
|
+
// - tool: result-99 (count 2, pendingToolCalls = {call-99})
|
|
607
|
+
// - assistant: call-99 (count 3, pendingToolCalls = {})
|
|
608
|
+
// - user: prompt (count 4, clean user start)
|
|
609
|
+
// So it must keep 4 messages + 1 system = 5 messages.
|
|
610
|
+
const sliced = sliceMessageHistory(msgs, 2);
|
|
611
|
+
expect(sliced).toHaveLength(5);
|
|
612
|
+
expect(sliced[1].role).toBe("user");
|
|
613
|
+
});
|
|
614
|
+
it("triggers slicing in compactSubAgentMessages when total exceeds 50K and messages count is > 30", () => {
|
|
615
|
+
const msgs = [
|
|
616
|
+
{ role: "system", content: "system" },
|
|
617
|
+
{ role: "user", content: "start" },
|
|
618
|
+
];
|
|
619
|
+
// Push 40 messages to exceed 30 messages limit
|
|
620
|
+
for (let i = 0; i < 20; i++) {
|
|
621
|
+
msgs.push({ role: "assistant", content: `helper ${i}` });
|
|
622
|
+
msgs.push({ role: "user", content: `next question ${i}` });
|
|
623
|
+
}
|
|
624
|
+
// Generate a massive envelope size or message content to exceed 50K total chars
|
|
625
|
+
const out = compactSubAgentMessages(msgs, {
|
|
626
|
+
envelopeChars: 60_000, // force exceeds 50K
|
|
627
|
+
thresholdChars: 200_000, // high threshold to prevent compaction from running on top
|
|
628
|
+
});
|
|
629
|
+
// The message history should have been sliced (will be much fewer than 42 messages)
|
|
630
|
+
expect(out.length).toBeLessThan(msgs.length);
|
|
631
|
+
expect(out[0].role).toBe("system");
|
|
632
|
+
expect(out[1].role).toBe("user");
|
|
633
|
+
});
|
|
634
|
+
});
|
|
387
635
|
});
|
|
388
636
|
//# sourceMappingURL=subagent-compactor.spec.js.map
|
|
@@ -42,12 +42,16 @@ export declare function detectTextEmittedToolCall(text: string): TextToolCallDet
|
|
|
42
42
|
* Parse the DeepSeek-native DSML tool-call markup into a structured list so the
|
|
43
43
|
* re-steer can restate the model's EXACT intent (much more effective than a
|
|
44
44
|
* generic "use the tool interface" nudge). Pure — no execution. Recognizes:
|
|
45
|
-
*
|
|
46
|
-
*
|
|
47
|
-
*
|
|
45
|
+
* Old format: <│invoke name="read_file">
|
|
46
|
+
* <│parameter name="file_path" string="true">src/app/foo.ts</│parameter>
|
|
47
|
+
* </│invoke>
|
|
48
|
+
* New format: <│ DSML │invoke name="read_file">
|
|
49
|
+
* <│ DSML │parameter name="file_path" string="true">src/app/foo.ts</│ DSML │parameter>
|
|
50
|
+
* </│ DSML │invoke>
|
|
51
|
+
* Both U+2502 (box-drawing │) and U+FF5C (fullwidth |) bars are recognized.
|
|
48
52
|
* Returns one entry per invoke block; args preserve insertion order. Tolerant of
|
|
49
|
-
* missing close tags (cheap models truncate)
|
|
50
|
-
*
|
|
53
|
+
* missing close tags (cheap models truncate). Returns [] when no parseable invoke
|
|
54
|
+
* block exists.
|
|
51
55
|
*/
|
|
52
56
|
export interface ParsedDsmlCall {
|
|
53
57
|
name: string;
|
|
@@ -90,16 +90,18 @@ const PARAM_ALTERNATION = PARAM_TAGS.join("|");
|
|
|
90
90
|
// is the Anthropic XML style; `<tool_call>` / `<function_calls>` are Qwen/other.
|
|
91
91
|
// These are matched directly (the wrapper itself is the signal).
|
|
92
92
|
const GENERIC_WRAPPER_RE = /<\/?(?:tool_call|function_calls|tool_use)\b|<invoke\b[^>]*\bname\s*=|<function\b[^>]*\bname\s*=/i;
|
|
93
|
-
// DeepSeek native tool-call markup leaking into text content. Signature is
|
|
94
|
-
//
|
|
95
|
-
// invoke/tool_calls/parameter tokens, e.g
|
|
93
|
+
// DeepSeek native tool-call markup leaking into text content. Signature is a
|
|
94
|
+
// vertical-bar sentinel — either U+FF5C fullwidth `|` or U+2502 box-drawing `│`
|
|
95
|
+
// — wrapping invoke/tool_calls/parameter tokens, e.g.:
|
|
96
|
+
// Old format: <|invoke name="read_file"> (U+FF5C, single bar)
|
|
97
|
+
// New format: <│ DSML │invoke name="…"> (U+2502, with optional DSML label)
|
|
96
98
|
// Live: storyflow_ui explore-A/B, deepseek T3 (session 799f0508e830) emitted a
|
|
97
|
-
// full
|
|
98
|
-
//
|
|
99
|
-
//
|
|
100
|
-
|
|
101
|
-
const DSML_WRAPPER_RE =
|
|
102
|
-
const DSML_INVOKE_NAME_RE =
|
|
99
|
+
// full DSML invoke block as text and made no real tool call → empty, silent turn.
|
|
100
|
+
// The generic `<invoke` matcher misses it because `<` is followed by the sentinel.
|
|
101
|
+
// Updated 2026-06-24 to cover both U+2502 and U+FF5C (tests use U+2502).
|
|
102
|
+
const DSML_BAR = "[\\s]*[│||]+[│||\\s]*"; // matches U+2502 (box-drawing), U+FF5C (fullwidth), ASCII pipe |, and spaces
|
|
103
|
+
const DSML_WRAPPER_RE = new RegExp(`${DSML_BAR}\\s*(?:DSML\\s*${DSML_BAR}\\s*)?(?:invoke|tool_calls?|parameter)\\b`, "i");
|
|
104
|
+
const DSML_INVOKE_NAME_RE = new RegExp(`${DSML_BAR}\\s*(?:DSML\\s*${DSML_BAR}\\s*)?invoke\\s+name\\s*=\\s*"([^"]+)"`, "i");
|
|
103
105
|
/** Build a per-tool detector: `<tool>` then (within a small gap) a `<param>` or `</tool>`. */
|
|
104
106
|
function buildToolRegexes() {
|
|
105
107
|
return TOOL_TAGS.map((tag) =>
|
|
@@ -136,10 +138,12 @@ function normalizeWrapperName(raw) {
|
|
|
136
138
|
const m = raw.match(/tool_call|function_calls|tool_use|invoke|function/i);
|
|
137
139
|
return m ? m[0].toLowerCase() : "tool_call";
|
|
138
140
|
}
|
|
139
|
-
|
|
140
|
-
const
|
|
141
|
+
// Guard regex: at least one DSML-bar sentinel must exist before we bother scanning
|
|
142
|
+
const DSML_GUARD_RE = /[│||]/;
|
|
143
|
+
const DSML_INVOKE_BLOCK_RE = new RegExp(`${DSML_BAR}\\s*(?:DSML\\s*${DSML_BAR}\\s*)?invoke\\s+name\\s*=\\s*"([^"]+)"([\\s\\S]*?)(?=${DSML_BAR}\\s*(?:DSML\\s*${DSML_BAR}\\s*)?invoke\\s|$)`, "gi");
|
|
144
|
+
const DSML_PARAM_RE = new RegExp(`${DSML_BAR}\\s*(?:DSML\\s*${DSML_BAR}\\s*)?parameter\\s+name\\s*=\\s*"([^"]+)"[^>]*>([\\s\\S]*?)<\\/?${DSML_BAR}\\s*(?:DSML\\s*${DSML_BAR}\\s*)?parameter`, "gi");
|
|
141
145
|
export function parseDsmlToolCalls(text) {
|
|
142
|
-
if (!text || !
|
|
146
|
+
if (!text || !DSML_GUARD_RE.test(text))
|
|
143
147
|
return [];
|
|
144
148
|
const calls = [];
|
|
145
149
|
DSML_INVOKE_BLOCK_RE.lastIndex = 0;
|