muonroi-cli 1.7.1 → 1.8.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/LICENSE +21 -21
- package/README.md +122 -122
- 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 +32 -3
- 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 +12 -12
- 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 +56 -21
- 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/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 +143 -104
- 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 +2 -2
- package/dist/src/lsp/manager.test.js +2 -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/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/oauth-callback.js +2 -2
- package/dist/src/mcp/parse-headers.test.js +14 -14
- package/dist/src/mcp/self-verify-jobs.js +1 -3
- package/dist/src/mcp/setup-guide-text.js +74 -74
- package/dist/src/mcp/smoke.test.js +43 -43
- package/dist/src/models/catalog-gemini.test.js +12 -10
- package/dist/src/models/catalog.json +106 -38
- package/dist/src/ops/doctor.js +7 -7
- 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 +2 -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 +1 -0
- package/dist/src/orchestrator/__tests__/stream-runner.test.js +29 -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 +364 -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 +21 -5
- package/dist/src/orchestrator/message-processor.js +219 -2573
- package/dist/src/orchestrator/orchestrator.d.ts +8 -2
- package/dist/src/orchestrator/orchestrator.js +187 -34
- package/dist/src/orchestrator/preprocessor.d.ts +13 -0
- package/dist/src/orchestrator/preprocessor.js +145 -0
- package/dist/src/orchestrator/prompts.d.ts +8 -0
- package/dist/src/orchestrator/prompts.js +76 -60
- 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-model-tier.js +1 -1
- package/dist/src/orchestrator/subagent-compactor.d.ts +14 -0
- package/dist/src/orchestrator/subagent-compactor.js +83 -10
- package/dist/src/orchestrator/subagent-compactor.spec.js +132 -0
- 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 +151 -0
- package/dist/src/orchestrator/tool-engine.js +3079 -0
- package/dist/src/orchestrator/turn-runner-deps.d.ts +1 -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__/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 +99 -44
- 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.js +3 -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/done-gate.js +3 -3
- package/dist/src/product-loop/gather.js +19 -5
- package/dist/src/product-loop/loop-driver.js +20 -20
- package/dist/src/product-loop/progress-snapshot.js +4 -4
- 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/mcp-vision-bridge.js +48 -48
- 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/reporter/index.js +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/scaffold/bb-ecosystem-apply.js +47 -47
- package/dist/src/scaffold/bb-quality-gate.js +5 -5
- package/dist/src/scaffold/continuation-prompt.js +60 -60
- package/dist/src/scaffold/init-new.js +453 -453
- package/dist/src/self-qa/__tests__/scenario-planner.test.js +3 -3
- package/dist/src/self-qa/agentic-loop.js +19 -19
- 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__/migrations.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/interaction-log.js +5 -5
- package/dist/src/storage/migrations.js +122 -122
- package/dist/src/storage/session-experience-store.js +4 -4
- package/dist/src/storage/sessions.d.ts +6 -0
- package/dist/src/storage/sessions.js +49 -42
- package/dist/src/storage/transcript-view.js +7 -1
- package/dist/src/storage/transcript.d.ts +1 -0
- package/dist/src/storage/transcript.js +55 -0
- package/dist/src/storage/ui-interaction-log.d.ts +17 -0
- package/dist/src/storage/usage.js +14 -14
- package/dist/src/storage/workspaces.js +12 -12
- 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 +56 -0
- 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 +5 -0
- package/dist/src/tools/registry.js +185 -11
- package/dist/src/types/index.d.ts +7 -1
- package/dist/src/ui/app.d.ts +2 -1
- 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/modals/session-picker-modal.js +14 -9
- package/dist/src/ui/modals/update-modal.js +2 -1
- 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/council-inspect.js +4 -4
- 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/types.d.ts +7 -0
- 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.d.ts +17 -0
- package/dist/src/ui/utils/relaunch.js +28 -4
- package/dist/src/ui/utils/relaunch.test.js +50 -0
- 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/clipboard-image.js +23 -23
- package/dist/src/utils/llm-deadline.d.ts +9 -1
- package/dist/src/utils/llm-deadline.js +31 -2
- 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 +27 -31
- package/dist/src/utils/settings.js +100 -109
- package/dist/src/utils/side-question.js +2 -2
- package/dist/src/utils/skills.js +3 -3
- package/dist/src/verify/__tests__/coverage-parsers.test.js +30 -30
- package/package.json +1 -1
|
@@ -34,8 +34,10 @@ import { normalizeModelId } from "../models/registry.js";
|
|
|
34
34
|
import { cheapModelShellLine, injectCheapModelPlaybook, injectCheapModelShellDirective, shouldInjectCheapModelPlaybook, } from "../pil/cheap-model-playbook.js";
|
|
35
35
|
import { injectCheapModelWorkbook, shouldInjectCheapModelWorkbook, subagentTaskType, } from "../pil/cheap-model-workbooks.js";
|
|
36
36
|
import { getProviderCapabilities } from "../providers/capabilities.js";
|
|
37
|
+
import { getVisionGuidanceForTextOnly } from "../providers/mcp-vision-bridge.js";
|
|
37
38
|
import { captureToolSchemas } from "../providers/patch-zod-schema.js";
|
|
38
39
|
import { buildTurnProviderOptions, requireRuntimeProvider, resolveModelRuntime, shouldDropParam, } from "../providers/runtime.js";
|
|
40
|
+
import { needsVisionProxy } from "../providers/vision-proxy.js";
|
|
39
41
|
import { wireDebug } from "../providers/wire-debug.js";
|
|
40
42
|
import { BashTool } from "../tools/bash.js";
|
|
41
43
|
import { createBuiltinTools } from "../tools/registry.js";
|
|
@@ -56,7 +58,7 @@ import { attachReminderToMessages, buildScopeReminder, cadenceForSize, shouldInj
|
|
|
56
58
|
import { recordCompaction, recordElision } from "./session-experience.js";
|
|
57
59
|
import { createStallWatchdog, STALL_ERROR_MESSAGE } from "./stall-watchdog.js";
|
|
58
60
|
import { wrapToolSetWithCap } from "./sub-agent-cap.js";
|
|
59
|
-
import { compactSubAgentMessages } from "./subagent-compactor.js";
|
|
61
|
+
import { compactSubAgentMessages, applyAnthropicPromptCaching } from "./subagent-compactor.js";
|
|
60
62
|
import { combineAbortSignals, firstLine, formatSubagentActivity } from "./tool-utils.js";
|
|
61
63
|
/**
|
|
62
64
|
* StreamRunner — extracted sub-agent stream lifecycle.
|
|
@@ -133,10 +135,19 @@ export class StreamRunner {
|
|
|
133
135
|
: topBash.getSandboxSettings(),
|
|
134
136
|
shellSettings: getCurrentShellSettings(),
|
|
135
137
|
});
|
|
138
|
+
// Resolve child model early so we can pass modelId to createBuiltinTools
|
|
139
|
+
// (needed for vision-proxy tools: analyze_image / ask_vision_proxy).
|
|
140
|
+
const childModelId = normalizeModelId(isVision
|
|
141
|
+
? VISION_MODEL
|
|
142
|
+
: isComputer
|
|
143
|
+
? COMPUTER_MODEL
|
|
144
|
+
: custom
|
|
145
|
+
? custom.model
|
|
146
|
+
: this.deps.resolveModelForTask(isExplore ? "explore" : isVerify || isVerifyDetect || isVerifyManifest ? "verify" : "general"));
|
|
136
147
|
// Mirror the file-local `createTools` wrapper from orchestrator.ts —
|
|
137
|
-
//
|
|
138
|
-
//
|
|
139
|
-
const childBaseToolsRaw = createBuiltinTools(childBash, childMode);
|
|
148
|
+
// pass modelId so registry can inject analyze_image/ask_vision_proxy for
|
|
149
|
+
// text-only child models (needsVisionProxy).
|
|
150
|
+
const childBaseToolsRaw = createBuiltinTools(childBash, childMode, { modelId: childModelId });
|
|
140
151
|
// Wrap with the cumulative cap so the sub-agent's tool loop cannot
|
|
141
152
|
// accumulate unbounded tool_result tokens. See sub-agent-cap.ts for the
|
|
142
153
|
// tiered compression schedule. The cap is per-invocation; each sub-agent
|
|
@@ -161,13 +172,6 @@ export class StreamRunner {
|
|
|
161
172
|
let lastActivity = initialDetail;
|
|
162
173
|
let childTools = childBaseTools;
|
|
163
174
|
let closeMcp;
|
|
164
|
-
const childModelId = normalizeModelId(isVision
|
|
165
|
-
? VISION_MODEL
|
|
166
|
-
: isComputer
|
|
167
|
-
? COMPUTER_MODEL
|
|
168
|
-
: custom
|
|
169
|
-
? custom.model
|
|
170
|
-
: this.deps.resolveModelForTask(isExplore ? "explore" : isVerify || isVerifyDetect || isVerifyManifest ? "verify" : "general"));
|
|
171
175
|
const topModelId = this.deps.getModelId();
|
|
172
176
|
if (childModelId !== topModelId) {
|
|
173
177
|
statusBarStore.setState({ routed_from: topModelId, model: childModelId });
|
|
@@ -212,6 +216,11 @@ export class StreamRunner {
|
|
|
212
216
|
const childSystem = shouldInjectCheapModelPlaybook(childRuntime.modelInfo)
|
|
213
217
|
? injectCheapModelShellDirective(childWithPlaybook, cheapModelShellLine(resolveShell({}).kind, process.platform))
|
|
214
218
|
: childWithPlaybook;
|
|
219
|
+
// Inject vision proxy guidance for text-only child models (DeepSeek etc.)
|
|
220
|
+
// so sub-agents know to use analyze_image / ask_vision_proxy when they
|
|
221
|
+
// receive image context or file paths. Mirrors top-level in message-processor.
|
|
222
|
+
const visionGuidance = needsVisionProxy(childModelId) ? getVisionGuidanceForTextOnly(childModelId) : "";
|
|
223
|
+
const childSystemWithVision = visionGuidance ? `${childSystem}\n\n${visionGuidance}` : childSystem;
|
|
215
224
|
onActivity?.(initialDetail);
|
|
216
225
|
if (childMode === "agent" && taskCaps.supportsClientTools(childRuntime.modelInfo)) {
|
|
217
226
|
const mcpBundle = await acquireMcpTools(loadMcpServers(), {
|
|
@@ -245,7 +254,8 @@ export class StreamRunner {
|
|
|
245
254
|
// signal is intentionally unused on this branch — vision was the only consumer.
|
|
246
255
|
void signal;
|
|
247
256
|
const childMessages = [{ role: "user", content: childPrompt }];
|
|
248
|
-
|
|
257
|
+
// The main agent manages its sub-agents, so don't apply an arbitrary hard limit.
|
|
258
|
+
const maxSteps = request.maxToolRounds ?? (this.deps.getMaxToolRounds() * 2);
|
|
249
259
|
// F1 parity — derive per-turn providerOptions so the sub-agent OpenAI calls
|
|
250
260
|
// carry a stable session-derived promptCacheKey (every tool round routes to
|
|
251
261
|
// the same cache node, keeping the unchanging prefix cached). The top-level
|
|
@@ -265,7 +275,7 @@ export class StreamRunner {
|
|
|
265
275
|
childMode,
|
|
266
276
|
childBash,
|
|
267
277
|
childRuntime,
|
|
268
|
-
childSystem,
|
|
278
|
+
childSystem: childSystemWithVision,
|
|
269
279
|
childMessages,
|
|
270
280
|
childTools,
|
|
271
281
|
childProviderOptions,
|
|
@@ -348,16 +358,16 @@ export class StreamRunner {
|
|
|
348
358
|
// Explore sub-agents are READ-ONLY research — a codebase investigation
|
|
349
359
|
// legitimately needs more grep/read steps than the tight general/medium=10
|
|
350
360
|
// cell allows. Cutting it early (esp. for reasoning models that front-load
|
|
351
|
-
//
|
|
352
|
-
//
|
|
353
|
-
//
|
|
361
|
+
// Sub-agent ceiling is no longer a hard halt. Per user request:
|
|
362
|
+
// "cũng áp dụng với sub agent nhé không nên hardcode maxtool mà nếu có
|
|
363
|
+
// vấn đề gì sẽ có main agent (khi spawn) kiểm soát đừng hard"
|
|
354
364
|
const _subCeiling = isExplore ? resolveCeiling("analyze", "large") : resolveCeiling("general", "medium");
|
|
355
365
|
const _subCounterKey = `subagent:${subCallId}`;
|
|
356
366
|
const _subStopWhen = (async (state) => {
|
|
367
|
+
incSessionStep(_subCounterKey); // Keep telemetry counter ticking
|
|
357
368
|
if (state.steps.length >= maxSteps)
|
|
358
369
|
return true;
|
|
359
|
-
|
|
360
|
-
return next >= _subCeiling;
|
|
370
|
+
return false;
|
|
361
371
|
});
|
|
362
372
|
// Silent-hang guard — mirror the top-level loop (message-processor.ts).
|
|
363
373
|
// A sub-agent provider connection can accept the request but never send a
|
|
@@ -436,12 +446,19 @@ export class StreamRunner {
|
|
|
436
446
|
/* fail-open */
|
|
437
447
|
}
|
|
438
448
|
};
|
|
449
|
+
// T1.1 + T1.2 — reasoning models (DeepSeek V4 Flash, R1) emit 2K-5K
|
|
450
|
+
// CoT tokens per turn that accumulate across the multi-step loop.
|
|
451
|
+
// Strip old reasoning and compact earlier (ratio 0.3 vs 0.5) to
|
|
452
|
+
// cut ~40-60% of cumulative input tokens.
|
|
453
|
+
const isReasoningModel = childRuntime.modelInfo?.reasoning === true;
|
|
439
454
|
const compacted = compactSubAgentMessages(stripped, {
|
|
440
455
|
thresholdChars: compactThreshold,
|
|
441
456
|
keepLastTurns: compactKeepLast,
|
|
442
457
|
contextWindowTokens: childCtxWindow,
|
|
458
|
+
contextFillRatio: isReasoningModel ? 0.3 : undefined,
|
|
443
459
|
keepToolIds: subKeepToolIds.length ? subKeepToolIds : undefined,
|
|
444
460
|
persistArtifact: persistSubArtifact,
|
|
461
|
+
stripOldReasoning: isReasoningModel,
|
|
445
462
|
});
|
|
446
463
|
if (compacted !== stripped)
|
|
447
464
|
recordCompaction(stepNumber);
|
|
@@ -456,21 +473,26 @@ export class StreamRunner {
|
|
|
456
473
|
const _subK = cadenceForSize(_subSize);
|
|
457
474
|
const _subShouldRemind = shouldInjectReminder(stepNumber, _subK);
|
|
458
475
|
const _subShouldWarn = shouldInjectSoftWarn(stepNumber, _subCeiling, _subCounterKey);
|
|
459
|
-
|
|
460
|
-
|
|
461
|
-
|
|
462
|
-
|
|
463
|
-
|
|
464
|
-
|
|
465
|
-
|
|
466
|
-
|
|
467
|
-
|
|
468
|
-
|
|
469
|
-
|
|
476
|
+
const finalMessages = (() => {
|
|
477
|
+
if (_subShouldRemind || _subShouldWarn) {
|
|
478
|
+
const _baseReminder = buildScopeReminder({
|
|
479
|
+
step: stepNumber,
|
|
480
|
+
ceiling: _subCeiling,
|
|
481
|
+
taskType: "general",
|
|
482
|
+
size: _subSize,
|
|
483
|
+
originalPrompt: prepared.request.prompt,
|
|
484
|
+
});
|
|
485
|
+
const _reminder = _subShouldWarn ? `[approaching ceiling] ${_baseReminder}` : _baseReminder;
|
|
486
|
+
return attachReminderToMessages(compacted, _reminder);
|
|
487
|
+
}
|
|
488
|
+
return compacted;
|
|
489
|
+
})();
|
|
490
|
+
if (childRuntime.modelId.startsWith("claude")) {
|
|
491
|
+
return { messages: applyAnthropicPromptCaching(finalMessages, childRuntime.modelId) };
|
|
470
492
|
}
|
|
471
493
|
if (compacted === stripped && stripped === messages)
|
|
472
494
|
return undefined;
|
|
473
|
-
return { messages:
|
|
495
|
+
return { messages: finalMessages };
|
|
474
496
|
},
|
|
475
497
|
...(childDropTemperature ? {} : { temperature: isExplore ? 0.2 : 0.5 }),
|
|
476
498
|
...(childDropMaxOutput ? {} : { maxOutputTokens: Math.min(this.deps.getMaxTokens(), 8_192) }),
|
|
@@ -24,7 +24,7 @@ export const TASK_TIER_PREFS = {
|
|
|
24
24
|
// general sub-agents downgrade to balanced (was premium-first) — the top
|
|
25
25
|
// model already planned the delegation; the focused execution doesn't need
|
|
26
26
|
// premium. Premium remains the fallback so premium-only providers are unaffected.
|
|
27
|
-
general: ["balanced", "premium"],
|
|
27
|
+
general: ["balanced", "fast", "premium"],
|
|
28
28
|
// verify keeps premium-first: a downgrade here would weaken the very check it
|
|
29
29
|
// exists to perform. Mirrors council `verify` role preference.
|
|
30
30
|
verify: ["premium", "balanced"],
|
|
@@ -96,6 +96,14 @@ export interface SubAgentCompactorOptions {
|
|
|
96
96
|
* source:"tool-artifact" so layer3/ee.query can later fetch "full tool result id=xxx".
|
|
97
97
|
*/
|
|
98
98
|
persistArtifact?: (toolCallId: string, toolName: string, fullContent: string, reason: string) => void;
|
|
99
|
+
/**
|
|
100
|
+
* T1.1 — strip reasoning parts from old assistant turns (older than
|
|
101
|
+
* keepLastTurns). Reasoning tokens (CoT / <think>) from prior turns are
|
|
102
|
+
* never re-read by the model but accumulate O(N) chars per turn, costing
|
|
103
|
+
* full input billing on every subsequent step. Default true for reasoning
|
|
104
|
+
* models, false otherwise.
|
|
105
|
+
*/
|
|
106
|
+
stripOldReasoning?: boolean;
|
|
99
107
|
}
|
|
100
108
|
/**
|
|
101
109
|
* G1 — coarse char→token conversion. The real ratio is provider/tokenizer
|
|
@@ -140,3 +148,9 @@ export declare function cumulativeMessageChars(messages: ReadonlyArray<ModelMess
|
|
|
140
148
|
* step.
|
|
141
149
|
*/
|
|
142
150
|
export declare function compactSubAgentMessages(messages: ReadonlyArray<ModelMessage>, opts?: SubAgentCompactorOptions): ModelMessage[];
|
|
151
|
+
/**
|
|
152
|
+
* Injects Anthropic prompt caching (cacheControl) into the last message's content
|
|
153
|
+
* block(s) if the model is Claude (starts with 'claude').
|
|
154
|
+
* Creates a copy of the messages array and the last message to avoid mutating in-place.
|
|
155
|
+
*/
|
|
156
|
+
export declare function applyAnthropicPromptCaching(messages: readonly ModelMessage[], modelId: string): ModelMessage[];
|
|
@@ -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
|
/**
|
|
@@ -378,17 +380,43 @@ export function compactSubAgentMessages(messages, opts = {}) {
|
|
|
378
380
|
continue;
|
|
379
381
|
}
|
|
380
382
|
if (msg.role === "assistant" && Array.isArray(msg.content)) {
|
|
381
|
-
//
|
|
382
|
-
//
|
|
383
|
-
// the
|
|
384
|
-
//
|
|
385
|
-
|
|
383
|
+
// T1.1 — strip reasoning parts from older assistant turns. DeepSeek V4
|
|
384
|
+
// Flash / R1 emit 2K-5K reasoning tokens per turn that accumulate
|
|
385
|
+
// across the multi-step loop. These are never re-read by the model
|
|
386
|
+
// but cost full input billing on every subsequent round. Strip them
|
|
387
|
+
// from turns older than keepLastTurns to cut ~30-50% of input tokens.
|
|
388
|
+
// Then F1 — strip args off older assistant tool-call shells.
|
|
389
|
+
let processed = resolved.stripOldReasoning ? stripAssistantReasoning(msg) : msg;
|
|
390
|
+
processed = stripAssistantToolCallArgs(processed);
|
|
391
|
+
out.push(processed);
|
|
386
392
|
continue;
|
|
387
393
|
}
|
|
388
394
|
out.push(msg);
|
|
389
395
|
}
|
|
390
396
|
return out;
|
|
391
397
|
}
|
|
398
|
+
/**
|
|
399
|
+
* T1.1 — strip reasoning parts from an assistant message. Reasoning tokens
|
|
400
|
+
* (CoT / `<think>`) from older turns have zero re-read value for the model
|
|
401
|
+
* on subsequent steps but they accumulate O(N) chars per turn and are billed
|
|
402
|
+
* as full input tokens. Removing them from turns older than keepLastTurns
|
|
403
|
+
* cuts ~30-50% of cumulative input in multi-step loops with reasoning models
|
|
404
|
+
* (DeepSeek V4 Flash, R1, etc.).
|
|
405
|
+
*
|
|
406
|
+
* Preserves text + tool-call parts (the structural skeleton the model needs
|
|
407
|
+
* to maintain coherent tool-call↔tool-result pairing).
|
|
408
|
+
*/
|
|
409
|
+
function stripAssistantReasoning(msg) {
|
|
410
|
+
if (!Array.isArray(msg.content))
|
|
411
|
+
return msg;
|
|
412
|
+
const parts = msg.content;
|
|
413
|
+
const filtered = parts.filter((part) => part.type !== "reasoning");
|
|
414
|
+
if (filtered.length === parts.length)
|
|
415
|
+
return msg; // nothing stripped
|
|
416
|
+
// Edge case: if ALL parts were reasoning (no text/tool-call), keep the
|
|
417
|
+
// message with an empty content array to preserve message-count pairing.
|
|
418
|
+
return { ...msg, content: filtered };
|
|
419
|
+
}
|
|
392
420
|
function stripAssistantToolCallArgs(msg) {
|
|
393
421
|
if (!Array.isArray(msg.content))
|
|
394
422
|
return msg;
|
|
@@ -424,4 +452,49 @@ function stripAssistantToolCallArgs(msg) {
|
|
|
424
452
|
return msg;
|
|
425
453
|
return { ...msg, content: next };
|
|
426
454
|
}
|
|
455
|
+
/**
|
|
456
|
+
* Injects Anthropic prompt caching (cacheControl) into the last message's content
|
|
457
|
+
* block(s) if the model is Claude (starts with 'claude').
|
|
458
|
+
* Creates a copy of the messages array and the last message to avoid mutating in-place.
|
|
459
|
+
*/
|
|
460
|
+
export function applyAnthropicPromptCaching(messages, modelId) {
|
|
461
|
+
if (!modelId.startsWith("claude")) {
|
|
462
|
+
return messages;
|
|
463
|
+
}
|
|
464
|
+
if (messages.length === 0) {
|
|
465
|
+
return messages;
|
|
466
|
+
}
|
|
467
|
+
const newMessages = [...messages];
|
|
468
|
+
const lastIndex = newMessages.length - 1;
|
|
469
|
+
const originalLastMsg = newMessages[lastIndex];
|
|
470
|
+
if (!originalLastMsg)
|
|
471
|
+
return messages;
|
|
472
|
+
const lastMsg = { ...originalLastMsg };
|
|
473
|
+
if (typeof lastMsg.content === "string") {
|
|
474
|
+
lastMsg.content = [
|
|
475
|
+
{
|
|
476
|
+
type: "text",
|
|
477
|
+
text: lastMsg.content,
|
|
478
|
+
providerOptions: {
|
|
479
|
+
anthropic: { cacheControl: { type: "ephemeral" } },
|
|
480
|
+
},
|
|
481
|
+
},
|
|
482
|
+
];
|
|
483
|
+
}
|
|
484
|
+
else if (Array.isArray(lastMsg.content)) {
|
|
485
|
+
const content = [...lastMsg.content];
|
|
486
|
+
const lastBlockIndex = content.length - 1;
|
|
487
|
+
if (lastBlockIndex >= 0) {
|
|
488
|
+
const lastBlock = { ...content[lastBlockIndex] };
|
|
489
|
+
lastBlock.providerOptions = {
|
|
490
|
+
...lastBlock.providerOptions,
|
|
491
|
+
anthropic: { cacheControl: { type: "ephemeral" } },
|
|
492
|
+
};
|
|
493
|
+
content[lastBlockIndex] = lastBlock;
|
|
494
|
+
lastMsg.content = content;
|
|
495
|
+
}
|
|
496
|
+
}
|
|
497
|
+
newMessages[lastIndex] = lastMsg;
|
|
498
|
+
return newMessages;
|
|
499
|
+
}
|
|
427
500
|
//# sourceMappingURL=subagent-compactor.js.map
|
|
@@ -384,5 +384,137 @@ 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: [{ type: "tool-result", toolCallId: "call_low", toolName: "read_file", output: { type: "text", value: lowValueOutput } }],
|
|
404
|
+
},
|
|
405
|
+
{
|
|
406
|
+
role: "assistant",
|
|
407
|
+
content: [{ type: "tool-call", toolCallId: "call_err", toolName: "read_file", input: {} }],
|
|
408
|
+
},
|
|
409
|
+
{
|
|
410
|
+
role: "tool",
|
|
411
|
+
content: [{ type: "tool-result", toolCallId: "call_err", toolName: "read_file", output: { type: "text", value: highValueOutputWithError } }],
|
|
412
|
+
},
|
|
413
|
+
{
|
|
414
|
+
role: "assistant",
|
|
415
|
+
content: [{ type: "tool-call", toolCallId: "call_src", toolName: "read_file", input: {} }],
|
|
416
|
+
},
|
|
417
|
+
{
|
|
418
|
+
role: "tool",
|
|
419
|
+
content: [{ type: "tool-result", toolCallId: "call_src", toolName: "read_file", output: { type: "text", value: highValueSourceOutput } }],
|
|
420
|
+
},
|
|
421
|
+
// Add a couple of low-value turns to push past threshold and keepLast turns (keepLast=1)
|
|
422
|
+
...toolTurn(9, 10),
|
|
423
|
+
];
|
|
424
|
+
// Force low value on the final kept turn
|
|
425
|
+
msgs[msgs.length - 1].content[0].toolName = "other_tool";
|
|
426
|
+
const out = compactSubAgentMessages(msgs, { thresholdChars: 10_000, keepLastTurns: 1 });
|
|
427
|
+
// low-value large read_file MUST be elided
|
|
428
|
+
const lowValMsg = out.find(m => m.role === "tool" && Array.isArray(m.content) && m.content[0]?.toolCallId === "call_low");
|
|
429
|
+
expect((lowValMsg?.content)[0].output.value).toMatch(/elided by sub-agent compactor/);
|
|
430
|
+
// high-value large error read_file MUST NOT be elided
|
|
431
|
+
const errValMsg = out.find(m => m.role === "tool" && Array.isArray(m.content) && m.content[0]?.toolCallId === "call_err");
|
|
432
|
+
expect((errValMsg?.content)[0].output.value).toBe(highValueOutputWithError);
|
|
433
|
+
// high-value small source read_file MUST NOT be elided
|
|
434
|
+
const srcValMsg = out.find(m => m.role === "tool" && Array.isArray(m.content) && m.content[0]?.toolCallId === "call_src");
|
|
435
|
+
expect((srcValMsg?.content)[0].output.value).toBe(highValueSourceOutput);
|
|
436
|
+
});
|
|
437
|
+
it("T1.1 — strips reasoning parts from old assistant turns when stripOldReasoning is enabled", () => {
|
|
438
|
+
// Build history with reasoning parts in assistant messages
|
|
439
|
+
const msgs = [
|
|
440
|
+
{ role: "system", content: "You are an agent." },
|
|
441
|
+
{ role: "user", content: "fix the bug" },
|
|
442
|
+
];
|
|
443
|
+
// Add 6 tool turns with reasoning in assistant messages
|
|
444
|
+
for (let i = 1; i <= 6; i++) {
|
|
445
|
+
msgs.push({
|
|
446
|
+
role: "assistant",
|
|
447
|
+
content: [
|
|
448
|
+
{ type: "reasoning", text: `Thinking about step ${i}... ${"x".repeat(3000)}` },
|
|
449
|
+
{ type: "text", text: `Step ${i} analysis` },
|
|
450
|
+
{ type: "tool-call", toolCallId: `call_${i}`, toolName: "bash", input: `echo ${i}` },
|
|
451
|
+
],
|
|
452
|
+
});
|
|
453
|
+
msgs.push({
|
|
454
|
+
role: "tool",
|
|
455
|
+
content: [
|
|
456
|
+
{ type: "tool-result", toolCallId: `call_${i}`, toolName: "bash", output: { type: "text", value: bigText(`R${i}`, 2) } },
|
|
457
|
+
],
|
|
458
|
+
});
|
|
459
|
+
}
|
|
460
|
+
const out = compactSubAgentMessages(msgs, {
|
|
461
|
+
thresholdChars: 1000, // force compaction
|
|
462
|
+
keepLastTurns: 2,
|
|
463
|
+
stripOldReasoning: true,
|
|
464
|
+
});
|
|
465
|
+
// Old turns (1-4) should have reasoning stripped
|
|
466
|
+
const oldAssistants = out.filter((m, idx) => m.role === "assistant" && idx < out.length - 4);
|
|
467
|
+
for (const m of oldAssistants) {
|
|
468
|
+
const content = m.content;
|
|
469
|
+
if (!Array.isArray(content))
|
|
470
|
+
continue;
|
|
471
|
+
const hasReasoning = content.some((p) => p.type === "reasoning");
|
|
472
|
+
expect(hasReasoning).toBe(false);
|
|
473
|
+
}
|
|
474
|
+
// Recent turns (last 2) should still have reasoning
|
|
475
|
+
const recentAssistants = out.filter((m, idx) => m.role === "assistant" && idx >= out.length - 4);
|
|
476
|
+
for (const m of recentAssistants) {
|
|
477
|
+
const content = m.content;
|
|
478
|
+
if (!Array.isArray(content))
|
|
479
|
+
continue;
|
|
480
|
+
const hasReasoning = content.some((p) => p.type === "reasoning");
|
|
481
|
+
expect(hasReasoning).toBe(true);
|
|
482
|
+
}
|
|
483
|
+
});
|
|
484
|
+
it("T1.1 — does NOT strip reasoning when stripOldReasoning is false (default)", () => {
|
|
485
|
+
const msgs = [
|
|
486
|
+
{ role: "system", content: "You are an agent." },
|
|
487
|
+
{ role: "user", content: "fix the bug" },
|
|
488
|
+
];
|
|
489
|
+
for (let i = 1; i <= 4; i++) {
|
|
490
|
+
msgs.push({
|
|
491
|
+
role: "assistant",
|
|
492
|
+
content: [
|
|
493
|
+
{ type: "reasoning", text: `Thinking... ${"x".repeat(3000)}` },
|
|
494
|
+
{ type: "tool-call", toolCallId: `call_${i}`, toolName: "bash", input: `echo ${i}` },
|
|
495
|
+
],
|
|
496
|
+
});
|
|
497
|
+
msgs.push({
|
|
498
|
+
role: "tool",
|
|
499
|
+
content: [
|
|
500
|
+
{ type: "tool-result", toolCallId: `call_${i}`, toolName: "bash", output: { type: "text", value: bigText(`R${i}`, 2) } },
|
|
501
|
+
],
|
|
502
|
+
});
|
|
503
|
+
}
|
|
504
|
+
const out = compactSubAgentMessages(msgs, {
|
|
505
|
+
thresholdChars: 1000,
|
|
506
|
+
keepLastTurns: 1,
|
|
507
|
+
stripOldReasoning: false, // explicitly off
|
|
508
|
+
});
|
|
509
|
+
// All assistant messages should still have reasoning parts
|
|
510
|
+
const assistants = out.filter((m) => m.role === "assistant");
|
|
511
|
+
for (const m of assistants) {
|
|
512
|
+
const content = m.content;
|
|
513
|
+
if (!Array.isArray(content))
|
|
514
|
+
continue;
|
|
515
|
+
const hasReasoning = content.some((p) => p.type === "reasoning");
|
|
516
|
+
expect(hasReasoning).toBe(true);
|
|
517
|
+
}
|
|
518
|
+
});
|
|
387
519
|
});
|
|
388
520
|
//# 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;
|