mixdog 0.9.3 → 0.9.4
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/package.json +7 -3
- package/scripts/bench/lead-review-tasks-r3.json +20 -0
- package/scripts/bench/lead-review-tasks.json +20 -0
- package/scripts/bench/r4-mixed-tasks.json +20 -0
- package/scripts/bench/review-tasks.json +20 -0
- package/scripts/bench/round-codex.json +114 -0
- package/scripts/bench/round-mixdog-lead-r3.json +269 -0
- package/scripts/bench/round-mixdog-lead.json +269 -0
- package/scripts/bench/round-mixdog.json +126 -0
- package/scripts/bench/round-r10-bigsample.json +679 -0
- package/scripts/bench/round-r11-codexalign.json +257 -0
- package/scripts/bench/round-r4-codex.json +114 -0
- package/scripts/bench/round-r4-mixed.json +225 -0
- package/scripts/bench/round-r5-gpt-lead.json +259 -0
- package/scripts/bench/round-r6-codex.json +114 -0
- package/scripts/bench/round-r6-solo.json +257 -0
- package/scripts/bench/round-r7-full.json +254 -0
- package/scripts/bench/round-r8-fulldefault.json +255 -0
- package/scripts/bench-run.mjs +215 -29
- package/scripts/freevar-smoke.mjs +95 -0
- package/scripts/internal-comms-bench.mjs +1 -0
- package/scripts/internal-comms-smoke.mjs +10 -9
- package/scripts/mouse-probe.mjs +45 -0
- package/scripts/output-style-bench.mjs +13 -6
- package/scripts/output-style-smoke.mjs +4 -4
- package/scripts/provider-toolcall-test.mjs +7 -3
- package/scripts/recall-usecase-cases.json +18 -0
- package/scripts/recall-usecase-probe.json +6 -0
- package/scripts/session-bench.mjs +152 -6
- package/scripts/tool-smoke.mjs +23 -63
- package/scripts/tui-render-smoke.mjs +90 -0
- package/scripts/webhook-smoke.mjs +208 -0
- package/src/agents/debugger/AGENT.md +4 -1
- package/src/agents/heavy-worker/AGENT.md +6 -5
- package/src/agents/maintainer/AGENT.md +4 -0
- package/src/agents/reviewer/AGENT.md +2 -1
- package/src/agents/worker/AGENT.md +8 -4
- package/src/lib/rules-builder.cjs +4 -0
- package/src/mixdog-session-runtime.mjs +632 -2042
- package/src/output-styles/default.md +34 -9
- package/src/output-styles/{oneline.md → extreme-minimal.md} +5 -4
- package/src/output-styles/minimal.md +4 -1
- package/src/output-styles/simple.md +22 -7
- package/src/rules/agent/00-common.md +2 -0
- package/src/rules/lead/lead-brief.md +12 -0
- package/src/rules/lead/lead-tool.md +0 -11
- package/src/runtime/agent/orchestrator/agent-runtime/agent-loop-policy.mjs +25 -0
- package/src/runtime/agent/orchestrator/agent-runtime/cache-strategy.mjs +100 -23
- package/src/runtime/agent/orchestrator/agent-runtime/session-builder.mjs +6 -15
- package/src/runtime/agent/orchestrator/agent-trace-format.mjs +362 -0
- package/src/runtime/agent/orchestrator/agent-trace-io.mjs +410 -0
- package/src/runtime/agent/orchestrator/agent-trace.mjs +16 -735
- package/src/runtime/agent/orchestrator/config.mjs +69 -2
- package/src/runtime/agent/orchestrator/providers/anthropic-effort.mjs +62 -20
- package/src/runtime/agent/orchestrator/providers/anthropic-model-resolve.mjs +209 -0
- package/src/runtime/agent/orchestrator/providers/anthropic-oauth-credentials.mjs +489 -0
- package/src/runtime/agent/orchestrator/providers/anthropic-oauth.mjs +81 -1281
- package/src/runtime/agent/orchestrator/providers/anthropic-sse.mjs +607 -0
- package/src/runtime/agent/orchestrator/providers/anthropic.mjs +32 -3
- package/src/runtime/agent/orchestrator/providers/codex-client-meta.mjs +81 -0
- package/src/runtime/agent/orchestrator/providers/gemini-cache.mjs +248 -0
- package/src/runtime/agent/orchestrator/providers/gemini-schema.mjs +303 -0
- package/src/runtime/agent/orchestrator/providers/gemini-stream.mjs +505 -0
- package/src/runtime/agent/orchestrator/providers/gemini.mjs +43 -1013
- package/src/runtime/agent/orchestrator/providers/grok-oauth.mjs +17 -3
- package/src/runtime/agent/orchestrator/providers/model-catalog.mjs +86 -11
- package/src/runtime/agent/orchestrator/providers/model-list-sanitize.mjs +348 -0
- package/src/runtime/agent/orchestrator/providers/openai-codex-model.mjs +108 -0
- package/src/runtime/agent/orchestrator/providers/openai-compat-trace.mjs +58 -0
- package/src/runtime/agent/orchestrator/providers/openai-compat-wire.mjs +368 -0
- package/src/runtime/agent/orchestrator/providers/openai-compat-xai.mjs +760 -0
- package/src/runtime/agent/orchestrator/providers/openai-compat.mjs +40 -1143
- package/src/runtime/agent/orchestrator/providers/openai-oauth-http-sse.mjs +732 -0
- package/src/runtime/agent/orchestrator/providers/openai-oauth-login.mjs +193 -0
- package/src/runtime/agent/orchestrator/providers/openai-oauth-ws.mjs +297 -2123
- package/src/runtime/agent/orchestrator/providers/openai-oauth.mjs +130 -1002
- package/src/runtime/agent/orchestrator/providers/openai-ws-delta.mjs +227 -0
- package/src/runtime/agent/orchestrator/providers/openai-ws-events.mjs +67 -0
- package/src/runtime/agent/orchestrator/providers/openai-ws-pool.mjs +436 -0
- package/src/runtime/agent/orchestrator/providers/openai-ws-stream.mjs +1105 -0
- package/src/runtime/agent/orchestrator/providers/openai-ws.mjs +2 -1
- package/src/runtime/agent/orchestrator/session/compact/budget.mjs +288 -0
- package/src/runtime/agent/orchestrator/session/compact/constants.mjs +85 -0
- package/src/runtime/agent/orchestrator/session/compact/engine.mjs +749 -0
- package/src/runtime/agent/orchestrator/session/compact/messages.mjs +82 -0
- package/src/runtime/agent/orchestrator/session/compact/summary-schema.mjs +315 -0
- package/src/runtime/agent/orchestrator/session/compact/summary.mjs +643 -0
- package/src/runtime/agent/orchestrator/session/compact/text-utils.mjs +326 -0
- package/src/runtime/agent/orchestrator/session/compact.mjs +40 -2282
- package/src/runtime/agent/orchestrator/session/loop/compact-policy.mjs +14 -2
- package/src/runtime/agent/orchestrator/session/loop/completion-guards.mjs +61 -0
- package/src/runtime/agent/orchestrator/session/loop/pre-dispatch-deny.mjs +1 -3
- package/src/runtime/agent/orchestrator/session/loop/recall-fasttrack.mjs +182 -0
- package/src/runtime/agent/orchestrator/session/loop/steering-ladder.mjs +173 -0
- package/src/runtime/agent/orchestrator/session/loop/termination.mjs +58 -0
- package/src/runtime/agent/orchestrator/session/loop/tool-exec.mjs +239 -0
- package/src/runtime/agent/orchestrator/session/loop.mjs +251 -397
- package/src/runtime/agent/orchestrator/session/manager/compaction-runner.mjs +471 -0
- package/src/runtime/agent/orchestrator/session/manager/context-meta.mjs +7 -4
- package/src/runtime/agent/orchestrator/session/manager/prompt-utils.mjs +12 -0
- package/src/runtime/agent/orchestrator/session/manager/runtime-liveness.mjs +406 -0
- package/src/runtime/agent/orchestrator/session/manager/status-telemetry.mjs +80 -0
- package/src/runtime/agent/orchestrator/session/manager/usage-metrics.mjs +210 -0
- package/src/runtime/agent/orchestrator/session/manager.mjs +166 -1087
- package/src/runtime/agent/orchestrator/session/store-summary-index.mjs +189 -0
- package/src/runtime/agent/orchestrator/session/store.mjs +74 -179
- package/src/runtime/agent/orchestrator/tools/builtin/arg-guard.mjs +70 -20
- package/src/runtime/agent/orchestrator/tools/builtin/bash-tool.mjs +22 -2
- package/src/runtime/agent/orchestrator/tools/builtin/builtin-tools.mjs +32 -41
- package/src/runtime/agent/orchestrator/tools/builtin/list-tool.mjs +40 -0
- package/src/runtime/agent/orchestrator/tools/builtin/rg-runner.mjs +29 -0
- package/src/runtime/agent/orchestrator/tools/builtin/search-builders.mjs +8 -0
- package/src/runtime/agent/orchestrator/tools/builtin/search-path-diagnostics.mjs +126 -0
- package/src/runtime/agent/orchestrator/tools/builtin/search-tool.mjs +81 -92
- package/src/runtime/agent/orchestrator/tools/builtin/shell-job-paths.mjs +161 -0
- package/src/runtime/agent/orchestrator/tools/builtin/shell-job-process.mjs +108 -0
- package/src/runtime/agent/orchestrator/tools/builtin/shell-jobs.mjs +28 -265
- package/src/runtime/agent/orchestrator/tools/builtin.mjs +0 -6
- package/src/runtime/agent/orchestrator/tools/code-graph/dispatch.mjs +57 -3
- package/src/runtime/agent/orchestrator/tools/code-graph/keyword-match.mjs +82 -0
- package/src/runtime/agent/orchestrator/tools/code-graph/search.mjs +10 -122
- package/src/runtime/agent/orchestrator/tools/code-graph/text-columns.mjs +45 -0
- package/src/runtime/agent/orchestrator/tools/code-graph-tool-defs.mjs +6 -6
- package/src/runtime/agent/orchestrator/tools/graph-binary-fetcher.mjs +6 -3
- package/src/runtime/agent/orchestrator/tools/patch/constants.mjs +9 -0
- package/src/runtime/agent/orchestrator/tools/patch/dispatch.mjs +171 -0
- package/src/runtime/agent/orchestrator/tools/patch/matcher.mjs +471 -0
- package/src/runtime/agent/orchestrator/tools/patch/native-server.mjs +436 -0
- package/src/runtime/agent/orchestrator/tools/patch/orchestrator.mjs +342 -0
- package/src/runtime/agent/orchestrator/tools/patch/parsing.mjs +359 -0
- package/src/runtime/agent/orchestrator/tools/patch/paths.mjs +340 -0
- package/src/runtime/agent/orchestrator/tools/patch/v4a-convert.mjs +643 -0
- package/src/runtime/agent/orchestrator/tools/patch.mjs +36 -2959
- package/src/runtime/agent/orchestrator/tools/progress-message.mjs +0 -21
- package/src/runtime/agent/orchestrator/tools/shell-command.mjs +9 -72
- package/src/runtime/agent/orchestrator/tools/shell-powershell.mjs +77 -0
- package/src/runtime/agent/orchestrator/tools/shell-state.mjs +154 -0
- package/src/runtime/channels/backends/discord-access.mjs +32 -0
- package/src/runtime/channels/backends/discord-attachments.mjs +65 -0
- package/src/runtime/channels/backends/discord-gateway.mjs +233 -0
- package/src/runtime/channels/backends/discord.mjs +12 -292
- package/src/runtime/channels/index.mjs +229 -663
- package/src/runtime/channels/lib/backend-dispatch.mjs +44 -0
- package/src/runtime/channels/lib/event-pipeline.mjs +18 -1
- package/src/runtime/channels/lib/event-queue.mjs +63 -4
- package/src/runtime/channels/lib/inbound-routing.mjs +111 -0
- package/src/runtime/channels/lib/output-forwarder.mjs +1 -1
- package/src/runtime/channels/lib/owner-heartbeat.mjs +75 -0
- package/src/runtime/channels/lib/parent-bridge.mjs +88 -0
- package/src/runtime/channels/lib/runtime-paths.mjs +14 -4
- package/src/runtime/channels/lib/session-discovery.mjs +56 -4
- package/src/runtime/channels/lib/tool-dispatch.mjs +158 -0
- package/src/runtime/channels/lib/tool-format.mjs +1 -1
- package/src/runtime/channels/lib/transcript-discovery.mjs +4 -4
- package/src/runtime/channels/lib/voice-runtime-fetcher.mjs +6 -3
- package/src/runtime/channels/lib/voice-transcription.mjs +179 -0
- package/src/runtime/channels/lib/webhook/deliveries.mjs +312 -0
- package/src/runtime/channels/lib/webhook/log.mjs +42 -0
- package/src/runtime/channels/lib/webhook/ngrok.mjs +181 -0
- package/src/runtime/channels/lib/webhook/signature.mjs +60 -0
- package/src/runtime/channels/lib/webhook.mjs +36 -570
- package/src/runtime/channels/tool-defs.mjs +11 -130
- package/src/runtime/memory/index.mjs +201 -1948
- package/src/runtime/memory/lib/cycle-llm-adapters.mjs +58 -0
- package/src/runtime/memory/lib/cycle-scheduler.mjs +497 -0
- package/src/runtime/memory/lib/embedding-warmup.mjs +58 -0
- package/src/runtime/memory/lib/memory-config-flags.mjs +91 -0
- package/src/runtime/memory/lib/memory-cycle.mjs +1 -1
- package/src/runtime/memory/lib/memory-cycle2-gate.mjs +515 -0
- package/src/runtime/memory/lib/memory-cycle2-mutations.mjs +324 -0
- package/src/runtime/memory/lib/memory-cycle2-shared.mjs +18 -0
- package/src/runtime/memory/lib/memory-cycle2.mjs +24 -842
- package/src/runtime/memory/lib/memory-embed.mjs +149 -0
- package/src/runtime/memory/lib/memory-process-lock.mjs +162 -0
- package/src/runtime/memory/lib/memory-recall-store.mjs +22 -2
- package/src/runtime/memory/lib/pg/supervisor.mjs +1 -1
- package/src/runtime/memory/lib/query-handlers.mjs +780 -0
- package/src/runtime/memory/lib/recall-format.mjs +55 -0
- package/src/runtime/memory/lib/runtime-fetcher.mjs +8 -3
- package/src/runtime/memory/lib/transcript-ingest.mjs +425 -0
- package/src/runtime/memory/tool-defs.mjs +5 -13
- package/src/runtime/search/lib/http-fetch.mjs +274 -0
- package/src/runtime/search/lib/ssrf-guard.mjs +333 -0
- package/src/runtime/search/lib/web-tools.mjs +24 -602
- package/src/runtime/shared/atomic-file.mjs +26 -1
- package/src/runtime/shared/launcher-control.mjs +2 -2
- package/src/runtime/shared/tool-primitives.mjs +308 -0
- package/src/runtime/shared/tool-result-summary.mjs +515 -0
- package/src/runtime/shared/tool-surface.mjs +80 -898
- package/src/runtime/shared/transcript-writer.mjs +23 -0
- package/src/runtime/shared/update-checker.mjs +7 -4
- package/src/session-runtime/config-helpers.mjs +84 -2
- package/src/session-runtime/config-lifecycle.mjs +232 -0
- package/src/session-runtime/cwd-plugins.mjs +226 -0
- package/src/session-runtime/mcp-glue.mjs +177 -0
- package/src/session-runtime/model-recency.mjs +111 -0
- package/src/session-runtime/native-search.mjs +247 -0
- package/src/session-runtime/output-styles.mjs +11 -9
- package/src/session-runtime/prewarm.mjs +142 -0
- package/src/session-runtime/provider-models.mjs +278 -0
- package/src/session-runtime/provider-usage.mjs +120 -0
- package/src/session-runtime/quick-model-rows.mjs +170 -0
- package/src/session-runtime/quick-search-models.mjs +46 -0
- package/src/session-runtime/session-hooks.mjs +93 -0
- package/src/session-runtime/settings-api.mjs +319 -0
- package/src/session-runtime/tool-catalog.mjs +29 -29
- package/src/session-runtime/tool-defs.mjs +84 -0
- package/src/session-runtime/warmup-schedulers.mjs +201 -0
- package/src/standalone/agent-tool/helpers.mjs +237 -0
- package/src/standalone/agent-tool/notify.mjs +107 -0
- package/src/standalone/agent-tool/provider-init.mjs +143 -0
- package/src/standalone/agent-tool/render.mjs +152 -0
- package/src/standalone/agent-tool/tool-def.mjs +55 -0
- package/src/standalone/agent-tool.mjs +110 -671
- package/src/standalone/channel-worker.mjs +4 -7
- package/src/standalone/explore-tool.mjs +30 -9
- package/src/standalone/hook-bus/config.mjs +207 -0
- package/src/standalone/hook-bus/constants.mjs +90 -0
- package/src/standalone/hook-bus/handlers.mjs +481 -0
- package/src/standalone/hook-bus/payload.mjs +31 -0
- package/src/standalone/hook-bus/rules.mjs +77 -0
- package/src/standalone/hook-bus.mjs +77 -870
- package/src/standalone/memory-runtime-proxy.mjs +7 -0
- package/src/standalone/opencode-go-login.mjs +5 -1
- package/src/standalone/provider-admin.mjs +1 -16
- package/src/standalone/usage-dashboard.mjs +3 -1
- package/src/tui/App.jsx +945 -8094
- package/src/tui/app/app-format.mjs +206 -0
- package/src/tui/app/channel-pickers.mjs +510 -0
- package/src/tui/app/clipboard.mjs +67 -0
- package/src/tui/app/core-memory-picker.mjs +210 -0
- package/src/tui/app/extension-pickers.mjs +506 -0
- package/src/tui/app/input-parsers.mjs +193 -0
- package/src/tui/app/maintenance-pickers.mjs +324 -0
- package/src/tui/app/model-options.mjs +330 -0
- package/src/tui/app/model-picker.mjs +365 -0
- package/src/tui/app/onboarding-steps.mjs +400 -0
- package/src/tui/app/project-picker.mjs +247 -0
- package/src/tui/app/provider-setup-picker.mjs +580 -0
- package/src/tui/app/resume-picker.mjs +55 -0
- package/src/tui/app/route-pickers.mjs +419 -0
- package/src/tui/app/settings-picker.mjs +490 -0
- package/src/tui/app/slash-commands.mjs +101 -0
- package/src/tui/app/slash-dispatch.mjs +427 -0
- package/src/tui/app/text-layout.mjs +46 -0
- package/src/tui/app/theme-effort-pickers.mjs +154 -0
- package/src/tui/app/transcript-window.mjs +671 -0
- package/src/tui/app/use-mouse-input.mjs +460 -0
- package/src/tui/app/use-prompt-handlers.mjs +310 -0
- package/src/tui/app/use-transcript-scroll.mjs +510 -0
- package/src/tui/app/use-transcript-window.mjs +589 -0
- package/src/tui/components/ConfirmBar.jsx +1 -1
- package/src/tui/components/Picker.jsx +32 -4
- package/src/tui/components/PromptInput.jsx +23 -101
- package/src/tui/components/SlashCommandPalette.jsx +8 -1
- package/src/tui/components/StatusLine.jsx +63 -12
- package/src/tui/components/TextEntryPanel.jsx +11 -0
- package/src/tui/components/ToolExecution.jsx +52 -594
- package/src/tui/components/TranscriptItem.jsx +105 -0
- package/src/tui/components/UsagePanel.jsx +18 -4
- package/src/tui/components/prompt-input/edit-helpers.mjs +72 -0
- package/src/tui/components/prompt-input/voice-indicator.mjs +39 -0
- package/src/tui/components/tool-execution/ResultBody.jsx +56 -0
- package/src/tui/components/tool-execution/surface-detail.mjs +405 -0
- package/src/tui/components/tool-execution/text-format.mjs +161 -0
- package/src/tui/display-width.mjs +20 -3
- package/src/tui/dist/index.mjs +19652 -18630
- package/src/tui/engine/agent-job-feed.mjs +133 -0
- package/src/tui/engine/notification-plan.mjs +76 -0
- package/src/tui/engine/render-timing.mjs +17 -0
- package/src/tui/engine/tool-approval.mjs +94 -0
- package/src/tui/engine/tool-card-results.mjs +234 -0
- package/src/tui/engine/tool-result-status.mjs +135 -0
- package/src/tui/engine.mjs +122 -562
- package/src/tui/figures.mjs +5 -0
- package/src/tui/index.jsx +105 -0
- package/src/tui/input-editing.mjs +2 -2
- package/src/tui/markdown/format-token.mjs +4 -1
- package/src/tui/statusline-ansi-bridge.mjs +11 -3
- package/src/tui/theme.mjs +6 -0
- package/src/ui/statusline-agents.mjs +213 -0
- package/src/ui/statusline-format.mjs +146 -0
- package/src/ui/statusline-segments.mjs +148 -0
- package/src/ui/statusline.mjs +67 -501
- package/src/ui/tool-card.mjs +0 -1
- package/src/vendor/statusline/bin/statusline-route.mjs +15 -2
- package/src/workflows/default/WORKFLOW.md +1 -1
- package/src/workflows/sequential/WORKFLOW.md +1 -1
- package/vendor/ink/build/display-width.js +19 -3
- package/vendor/ink/build/ink.js +103 -6
- package/vendor/ink/build/log-update.js +17 -3
- package/vendor/ink/build/wrap-text.js +125 -0
- package/scripts/_test-folder-dialog.mjs +0 -30
- package/scripts/fix-brief-fn.mjs +0 -35
- package/scripts/fix-format-tool-surface.mjs +0 -24
- package/scripts/fix-tool-exec-visible.mjs +0 -42
- package/scripts/patch-agent-brief.mjs +0 -48
- package/scripts/patch-app.mjs +0 -21
- package/scripts/patch-app2.mjs +0 -18
- package/scripts/patch-dist-brief.mjs +0 -96
- package/scripts/patch-tool-exec.mjs +0 -70
- package/src/examples/schedules/SCHEDULE.example.md +0 -32
- package/src/examples/webhooks/WEBHOOK.example.md +0 -40
- package/src/runtime/agent/orchestrator/session/manager.reactive-persist.test.mjs +0 -107
- package/src/runtime/agent/orchestrator/tools/builtin/arg-guard.test.mjs +0 -143
- package/src/runtime/agent/orchestrator/tools/builtin/diagnostics-tool.mjs +0 -285
- package/src/runtime/agent/orchestrator/tools/builtin/external-tool-adapters.test.mjs +0 -162
- package/src/runtime/agent/orchestrator/tools/builtin/open-config-tool.mjs +0 -26
- package/src/runtime/shared/channel-notification-routing.test.mjs +0 -45
- package/src/runtime/shared/task-notification-envelope.test.mjs +0 -107
- package/src/runtime/shared/tool-execution-contract.test.mjs +0 -183
- package/src/standalone/agent-task-status.test.mjs +0 -76
- package/src/tui/components/tool-output-format.test.mjs +0 -399
- package/src/tui/display-width.test.mjs +0 -35
- package/src/tui/engine-runtime-notification.test.mjs +0 -115
- package/src/tui/engine-tool-result-text.test.mjs +0 -75
- package/src/tui/input-editing.selection.test.mjs +0 -75
- package/src/tui/markdown/format-token.test.mjs +0 -354
- package/src/tui/markdown/render-ansi.test.mjs +0 -108
- package/src/tui/markdown/stream-fence.test.mjs +0 -26
- package/src/tui/markdown/streaming-markdown.test.mjs +0 -70
- package/src/tui/paste-fix.test.mjs +0 -119
- package/src/tui/prompt-history-store.test.mjs +0 -52
- package/src/tui/statusline-ansi-bridge.test.mjs +0 -159
- package/src/tui/transcript-tool-failures.test.mjs +0 -111
- package/src/ui/markdown.test.mjs +0 -70
- package/src/ui/statusline-context-label.test.mjs +0 -15
- package/src/vendor/statusline/bin/statusline-lib.mjs +0 -186
- package/src/vendor/statusline/bin/statusline-route.test.mjs +0 -80
|
@@ -20,12 +20,9 @@ export const MEASURED_TOOL_USAGE = Object.freeze({
|
|
|
20
20
|
agent: 330,
|
|
21
21
|
shell: 81,
|
|
22
22
|
cwd: 2,
|
|
23
|
-
diagnostics: 2,
|
|
24
23
|
recall: 2,
|
|
25
24
|
search: 2,
|
|
26
25
|
web_fetch: 2,
|
|
27
|
-
provider_status: 2,
|
|
28
|
-
channel_status: 2,
|
|
29
26
|
});
|
|
30
27
|
const MEASURED_TOOL_ORDER = Object.freeze(Object.keys(MEASURED_TOOL_USAGE));
|
|
31
28
|
export const DEFERRED_DEFAULT_FULL_TOOLS = Object.freeze([
|
|
@@ -81,9 +78,6 @@ const READONLY_TOOL_NAMES = new Set([
|
|
|
81
78
|
'web_fetch',
|
|
82
79
|
'recall',
|
|
83
80
|
'memory',
|
|
84
|
-
'provider_status',
|
|
85
|
-
'channel_status',
|
|
86
|
-
'schedule_status',
|
|
87
81
|
'fetch',
|
|
88
82
|
'Skill',
|
|
89
83
|
]);
|
|
@@ -92,13 +86,8 @@ const DEFERRED_SELECT_ALIASES = {
|
|
|
92
86
|
search: ['search', 'web_fetch'],
|
|
93
87
|
web: ['web_fetch', 'search'],
|
|
94
88
|
memory: ['memory', 'recall'],
|
|
95
|
-
channels: ['reply', 'fetch'
|
|
96
|
-
discord: ['reply', 'fetch'
|
|
97
|
-
providers: ['provider_status'],
|
|
98
|
-
provider: ['provider_status'],
|
|
99
|
-
status: ['provider_status', 'channel_status', 'schedule_status'],
|
|
100
|
-
schedule: ['schedule_status', 'trigger_schedule', 'schedule_control'],
|
|
101
|
-
channel: ['channel_status'],
|
|
89
|
+
channels: ['reply', 'fetch'],
|
|
90
|
+
discord: ['reply', 'fetch'],
|
|
102
91
|
explore: ['explore'],
|
|
103
92
|
discovery: ['explore'],
|
|
104
93
|
agent: ['agent'],
|
|
@@ -111,10 +100,6 @@ const TOOL_SEARCH_SAFE_AUTO_ALIASES = new Set([
|
|
|
111
100
|
'web',
|
|
112
101
|
'search',
|
|
113
102
|
'agent',
|
|
114
|
-
'provider',
|
|
115
|
-
'providers',
|
|
116
|
-
'channel',
|
|
117
|
-
'schedule',
|
|
118
103
|
'memory',
|
|
119
104
|
]);
|
|
120
105
|
const TOOL_SEARCH_AMBIGUOUS_AUTO_QUERIES = new Set([
|
|
@@ -152,12 +137,9 @@ const TOOL_SEARCH_STOP_WORDS = new Set([
|
|
|
152
137
|
]);
|
|
153
138
|
const TOOL_SEARCH_ROW_ALIASES = Object.freeze({
|
|
154
139
|
agent: ['delegate', 'subagent', 'worker', 'parallel agent', 'background agent', 'reviewer', 'explorer'],
|
|
155
|
-
channel_status: ['channel status', 'discord status', 'channel config'],
|
|
156
140
|
cwd: ['cwd', 'working directory', 'current directory', 'project root', 'folder'],
|
|
157
141
|
memory: ['save memory', 'store memory', 'delete memory', 'forget memory', 'memory status'],
|
|
158
|
-
provider_status: ['provider status', 'auth status', 'model status', 'oauth status', 'provider config'],
|
|
159
142
|
recall: ['recall', 'previous work', 'past work', 'prior context', 'history', 'resume context'],
|
|
160
|
-
schedule_status: ['schedule status', 'cron status'],
|
|
161
143
|
search: ['web search', 'internet search', 'current info', 'latest info', 'online search', 'docs search'],
|
|
162
144
|
shell: ['run command', 'execute command', 'terminal', 'powershell', 'bash', 'run tests', 'test command', 'build command', 'npm', 'node', 'git'],
|
|
163
145
|
task: ['background task', 'async task', 'wait task', 'cancel task', 'task status'],
|
|
@@ -690,15 +672,33 @@ export function renderToolSearch(args = {}, session, mode = 'full') {
|
|
|
690
672
|
? autoToolSelectionNames(query, initialRows)
|
|
691
673
|
: [];
|
|
692
674
|
const selectedNames = forcedSelectedNames.length ? forcedSelectedNames : autoSelectedNames;
|
|
693
|
-
|
|
694
|
-
|
|
695
|
-
|
|
696
|
-
|
|
697
|
-
|
|
698
|
-
].filter((row) => row.name);
|
|
699
|
-
const matches = query
|
|
675
|
+
let toolSelection = selectedNames.length ? selectDeferredTools(session, selectedNames, mode) : null;
|
|
676
|
+
let selectionMode = forcedSelectedNames.length ? 'select' : (autoSelectedNames.length ? 'auto' : null);
|
|
677
|
+
let nextActiveNames = new Set((session?.tools || []).map((tool) => clean(tool?.name)).filter(Boolean));
|
|
678
|
+
let rows = catalog.map((tool) => toolRow(tool, nextActiveNames)).filter((row) => row.name);
|
|
679
|
+
let matches = query
|
|
700
680
|
? rankedToolSearchRows(rows, query)
|
|
701
681
|
: rows;
|
|
682
|
+
// Query hits must become loadable without a second explicit select turn:
|
|
683
|
+
// if the query produced ranked matches but the forced/auto path above
|
|
684
|
+
// loaded nothing (no exact-name/alias/category hit), fall back to
|
|
685
|
+
// auto-loading the ranked match names themselves (deferred/non-active
|
|
686
|
+
// only, capped at `limit`). Ambiguous queries stay excluded so a guess
|
|
687
|
+
// never silently loads a wrong category.
|
|
688
|
+
const STRONG_MATCH_REASONS = new Set(['exact-name', 'exact-alias', 'alias-phrase', 'phrase', 'all-tokens']);
|
|
689
|
+
const isStrongMatch = (row) => row.score >= 24 || (row.reasons || []).some((reason) => STRONG_MATCH_REASONS.has(reason));
|
|
690
|
+
const queryLoadedNothing = !toolSelection
|
|
691
|
+
|| (!toolSelection.added.length && !toolSelection.already.length);
|
|
692
|
+
if (query && !TOOL_SEARCH_AMBIGUOUS_AUTO_QUERIES.has(query) && matches.length && queryLoadedNothing) {
|
|
693
|
+
const fallbackNames = matches.filter((row) => !row.active && isStrongMatch(row)).map((row) => row.name).slice(0, limit);
|
|
694
|
+
if (fallbackNames.length) {
|
|
695
|
+
toolSelection = selectDeferredTools(session, fallbackNames, mode);
|
|
696
|
+
selectionMode = 'query';
|
|
697
|
+
nextActiveNames = new Set((session?.tools || []).map((tool) => clean(tool?.name)).filter(Boolean));
|
|
698
|
+
rows = catalog.map((tool) => toolRow(tool, nextActiveNames)).filter((row) => row.name);
|
|
699
|
+
matches = query ? rankedToolSearchRows(rows, query) : rows;
|
|
700
|
+
}
|
|
701
|
+
}
|
|
702
702
|
const selected = toolSelection
|
|
703
703
|
? {
|
|
704
704
|
mode: selectionMode,
|
|
@@ -706,7 +706,7 @@ export function renderToolSearch(args = {}, session, mode = 'full') {
|
|
|
706
706
|
}
|
|
707
707
|
: null;
|
|
708
708
|
const nativeToolSearch = toolSelection?.native
|
|
709
|
-
? toolSearchNativePayload(catalog, toolSelection.added, session?.provider)
|
|
709
|
+
? toolSearchNativePayload(catalog, [...new Set([...toolSelection.added, ...(toolSelection.already || [])])], session?.provider)
|
|
710
710
|
: null;
|
|
711
711
|
return JSON.stringify({
|
|
712
712
|
selected,
|
|
@@ -715,6 +715,6 @@ export function renderToolSearch(args = {}, session, mode = 'full') {
|
|
|
715
715
|
matches: matches.slice(0, limit),
|
|
716
716
|
activeTools: sortedNamesByMeasuredUsage(nextActiveNames),
|
|
717
717
|
discoveredTools: sortedNamesByMeasuredUsage(session?.deferredDiscoveredTools || []),
|
|
718
|
-
note: 'query ranks matches and auto-loads high-confidence deferred tools; select exact tool names, or query select:a,b, to force load.',
|
|
718
|
+
note: 'query ranks matches and auto-loads high-confidence deferred tools, including ranked search hits (deferred, non-active) up to limit; already-loaded/discovered tools are re-surfaced on re-selection; select exact tool names, or query select:a,b, to force load.',
|
|
719
719
|
}, null, 2);
|
|
720
720
|
}
|
|
@@ -0,0 +1,84 @@
|
|
|
1
|
+
// Standalone session tool definitions (wrapper tools surfaced by the runtime).
|
|
2
|
+
// Pure, self-contained schemas + the agent-hidden default helper. Extracted
|
|
3
|
+
// from mixdog-session-runtime.mjs; no runtime closure dependencies.
|
|
4
|
+
|
|
5
|
+
export const TOOL_SEARCH_TOOL = {
|
|
6
|
+
name: 'tool_search',
|
|
7
|
+
title: 'Tool Search',
|
|
8
|
+
annotations: {
|
|
9
|
+
title: 'Tool Search',
|
|
10
|
+
readOnlyHint: true,
|
|
11
|
+
destructiveHint: false,
|
|
12
|
+
idempotentHint: true,
|
|
13
|
+
openWorldHint: false,
|
|
14
|
+
agentHidden: true,
|
|
15
|
+
},
|
|
16
|
+
description: 'Search deferred tools; confident queries load matches.',
|
|
17
|
+
inputSchema: {
|
|
18
|
+
type: 'object',
|
|
19
|
+
properties: {
|
|
20
|
+
query: { type: 'string', description: 'Search text; confident hits load. select:a,b forces exact names.' },
|
|
21
|
+
select: { anyOf: [{ type: 'string' }, { type: 'array', items: { type: 'string' } }], description: 'Force exact tool names.' },
|
|
22
|
+
limit: { type: 'number', description: 'Max matches.' },
|
|
23
|
+
},
|
|
24
|
+
additionalProperties: false,
|
|
25
|
+
},
|
|
26
|
+
};
|
|
27
|
+
|
|
28
|
+
export const CWD_TOOL = {
|
|
29
|
+
name: 'cwd',
|
|
30
|
+
title: 'Work Project',
|
|
31
|
+
annotations: {
|
|
32
|
+
title: 'Work Project',
|
|
33
|
+
readOnlyHint: false,
|
|
34
|
+
destructiveHint: false,
|
|
35
|
+
idempotentHint: false,
|
|
36
|
+
openWorldHint: false,
|
|
37
|
+
agentHidden: true,
|
|
38
|
+
},
|
|
39
|
+
description: 'Show or set the session work project for tool execution.',
|
|
40
|
+
inputSchema: {
|
|
41
|
+
type: 'object',
|
|
42
|
+
properties: {
|
|
43
|
+
action: { type: 'string', enum: ['get', 'set'], description: 'Default get.' },
|
|
44
|
+
path: { type: 'string', description: 'Project directory for set.' },
|
|
45
|
+
},
|
|
46
|
+
additionalProperties: false,
|
|
47
|
+
},
|
|
48
|
+
};
|
|
49
|
+
|
|
50
|
+
export const SKILL_TOOL = {
|
|
51
|
+
name: 'Skill',
|
|
52
|
+
title: 'Skill',
|
|
53
|
+
annotations: {
|
|
54
|
+
title: 'Skill',
|
|
55
|
+
readOnlyHint: true,
|
|
56
|
+
destructiveHint: false,
|
|
57
|
+
idempotentHint: true,
|
|
58
|
+
openWorldHint: false,
|
|
59
|
+
agentHidden: false,
|
|
60
|
+
},
|
|
61
|
+
description: 'Load a named SKILL.md into context.',
|
|
62
|
+
inputSchema: {
|
|
63
|
+
type: 'object',
|
|
64
|
+
properties: {
|
|
65
|
+
name: { type: 'string', description: 'Skill name.' },
|
|
66
|
+
},
|
|
67
|
+
required: ['name'],
|
|
68
|
+
additionalProperties: false,
|
|
69
|
+
},
|
|
70
|
+
};
|
|
71
|
+
|
|
72
|
+
export const LEAD_DISALLOWED_TOOLS = Object.freeze([]);
|
|
73
|
+
export const AGENT_HIDDEN_WRAPPER_TOOLS = new Set([]);
|
|
74
|
+
|
|
75
|
+
export function applyStandaloneToolDefaults(tool) {
|
|
76
|
+
if (!tool || !AGENT_HIDDEN_WRAPPER_TOOLS.has(tool.name)) return tool;
|
|
77
|
+
return {
|
|
78
|
+
...tool,
|
|
79
|
+
annotations: {
|
|
80
|
+
...(tool.annotations || {}),
|
|
81
|
+
agentHidden: true,
|
|
82
|
+
},
|
|
83
|
+
};
|
|
84
|
+
}
|
|
@@ -0,0 +1,201 @@
|
|
|
1
|
+
// Background warmup/refresh schedulers, extracted from
|
|
2
|
+
// mixdog-session-runtime.mjs. Dependency-injected factory: the timer handles
|
|
3
|
+
// live in a caller-owned `timers` object (so the facade's clearTimeout teardown
|
|
4
|
+
// still sees them) and all route/config/state reads go through supplied
|
|
5
|
+
// accessors. Returns the schedule* functions plus a clearAll() teardown helper.
|
|
6
|
+
import { performance } from 'node:perf_hooks';
|
|
7
|
+
import { clean } from './session-text.mjs';
|
|
8
|
+
|
|
9
|
+
export function createWarmupSchedulers({
|
|
10
|
+
timers,
|
|
11
|
+
bootProfile,
|
|
12
|
+
getRoute,
|
|
13
|
+
getConfig,
|
|
14
|
+
isCloseRequested,
|
|
15
|
+
getActiveTurnCount,
|
|
16
|
+
getSessionCreatePromise,
|
|
17
|
+
getProviderModelsCache,
|
|
18
|
+
getProviderModelsPromise,
|
|
19
|
+
reloadFullConfig,
|
|
20
|
+
ensureConfigForRouteProvider,
|
|
21
|
+
ensureProvidersReady,
|
|
22
|
+
ensureProviderEnabled,
|
|
23
|
+
refreshStatuslineUsageSnapshot,
|
|
24
|
+
warmProviderModelCache,
|
|
25
|
+
cachedProviderSetup,
|
|
26
|
+
warmCatalogsInBackground,
|
|
27
|
+
isFirstTurnCompleted,
|
|
28
|
+
envFlag,
|
|
29
|
+
delays,
|
|
30
|
+
flags,
|
|
31
|
+
}) {
|
|
32
|
+
const {
|
|
33
|
+
providerWarmupDelayMs,
|
|
34
|
+
providerSetupWarmupDelayMs,
|
|
35
|
+
providerModelWarmupDelayMs,
|
|
36
|
+
modelCatalogWarmupDelayMs,
|
|
37
|
+
statuslineUsageWarmupDelayMs,
|
|
38
|
+
statuslineUsageRefreshDelayMs,
|
|
39
|
+
backgroundBusyRetryMs,
|
|
40
|
+
} = delays;
|
|
41
|
+
const {
|
|
42
|
+
providerWarmupEnabled,
|
|
43
|
+
modelPrefetchEnabled,
|
|
44
|
+
modelCatalogWarmupEnabled,
|
|
45
|
+
} = flags;
|
|
46
|
+
|
|
47
|
+
function scheduleProviderWarmup(delayMs = providerWarmupDelayMs) {
|
|
48
|
+
if (!providerWarmupEnabled) {
|
|
49
|
+
bootProfile('providers:warm-skipped');
|
|
50
|
+
return;
|
|
51
|
+
}
|
|
52
|
+
if (timers.providerWarmupTimer || isCloseRequested()) return;
|
|
53
|
+
timers.providerWarmupTimer = setTimeout(() => {
|
|
54
|
+
timers.providerWarmupTimer = null;
|
|
55
|
+
if (isCloseRequested()) return;
|
|
56
|
+
if (!isFirstTurnCompleted() && !envFlag('MIXDOG_PROVIDER_WARMUP_BEFORE_FIRST_TURN')) {
|
|
57
|
+
bootProfile('providers:warm-deferred', { reason: 'first-turn-pending' });
|
|
58
|
+
return;
|
|
59
|
+
}
|
|
60
|
+
if (getActiveTurnCount() > 0 || getSessionCreatePromise()) {
|
|
61
|
+
bootProfile('providers:warm-deferred', { reason: getActiveTurnCount() > 0 ? 'turn-active' : 'session-create' });
|
|
62
|
+
scheduleProviderWarmup(backgroundBusyRetryMs);
|
|
63
|
+
return;
|
|
64
|
+
}
|
|
65
|
+
const providersStartedAt = performance.now();
|
|
66
|
+
try {
|
|
67
|
+
reloadFullConfig();
|
|
68
|
+
} catch (error) {
|
|
69
|
+
bootProfile('config:full-failed', { error: error?.message || String(error) });
|
|
70
|
+
}
|
|
71
|
+
void ensureProvidersReady(getConfig().providers || {})
|
|
72
|
+
.then(() => {
|
|
73
|
+
bootProfile('providers:init:ready', { ms: (performance.now() - providersStartedAt).toFixed(1) });
|
|
74
|
+
if (isCloseRequested()) return null;
|
|
75
|
+
return true;
|
|
76
|
+
})
|
|
77
|
+
.catch((error) => bootProfile('providers:warm-failed', { error: error?.message || String(error) }));
|
|
78
|
+
}, delayMs);
|
|
79
|
+
timers.providerWarmupTimer.unref?.();
|
|
80
|
+
}
|
|
81
|
+
|
|
82
|
+
function scheduleProviderSetupWarmup(delayMs = providerSetupWarmupDelayMs) {
|
|
83
|
+
if (timers.providerSetupWarmupTimer || isCloseRequested()) return;
|
|
84
|
+
timers.providerSetupWarmupTimer = setTimeout(() => {
|
|
85
|
+
timers.providerSetupWarmupTimer = null;
|
|
86
|
+
if (isCloseRequested()) return;
|
|
87
|
+
void cachedProviderSetup()
|
|
88
|
+
.then(() => bootProfile('provider-setup:warm-ready'))
|
|
89
|
+
.catch((error) => bootProfile('provider-setup:warm-failed', { error: error?.message || String(error) }));
|
|
90
|
+
}, delayMs);
|
|
91
|
+
timers.providerSetupWarmupTimer.unref?.();
|
|
92
|
+
}
|
|
93
|
+
|
|
94
|
+
function scheduleProviderModelWarmup(delayMs = providerModelWarmupDelayMs) {
|
|
95
|
+
if (!modelPrefetchEnabled) return;
|
|
96
|
+
if (timers.providerModelWarmupTimer || isCloseRequested()) return;
|
|
97
|
+
timers.providerModelWarmupTimer = setTimeout(() => {
|
|
98
|
+
timers.providerModelWarmupTimer = null;
|
|
99
|
+
if (isCloseRequested() || Array.isArray(getProviderModelsCache().models) || getProviderModelsPromise()) return;
|
|
100
|
+
if (getActiveTurnCount() > 0 || getSessionCreatePromise()) {
|
|
101
|
+
bootProfile('provider-models:warm-deferred', { reason: getActiveTurnCount() > 0 ? 'turn-active' : 'session-create' });
|
|
102
|
+
scheduleProviderModelWarmup(backgroundBusyRetryMs);
|
|
103
|
+
return;
|
|
104
|
+
}
|
|
105
|
+
warmProviderModelCache();
|
|
106
|
+
}, delayMs);
|
|
107
|
+
timers.providerModelWarmupTimer.unref?.();
|
|
108
|
+
}
|
|
109
|
+
|
|
110
|
+
function scheduleModelCatalogWarmup(delayMs = modelCatalogWarmupDelayMs) {
|
|
111
|
+
if (!modelCatalogWarmupEnabled) {
|
|
112
|
+
bootProfile('model-catalog:warm-skipped', { reason: 'disabled' });
|
|
113
|
+
return;
|
|
114
|
+
}
|
|
115
|
+
if (timers.modelCatalogWarmupTimer || isCloseRequested()) return;
|
|
116
|
+
timers.modelCatalogWarmupTimer = setTimeout(() => {
|
|
117
|
+
timers.modelCatalogWarmupTimer = null;
|
|
118
|
+
if (isCloseRequested()) return;
|
|
119
|
+
if (getActiveTurnCount() > 0 || getSessionCreatePromise()) {
|
|
120
|
+
bootProfile('model-catalog:warm-deferred', { reason: getActiveTurnCount() > 0 ? 'turn-active' : 'session-create' });
|
|
121
|
+
scheduleModelCatalogWarmup(backgroundBusyRetryMs);
|
|
122
|
+
return;
|
|
123
|
+
}
|
|
124
|
+
void warmCatalogsInBackground()
|
|
125
|
+
.then(() => bootProfile('model-catalog:warm-ready'))
|
|
126
|
+
.catch((error) => bootProfile('model-catalog:warm-failed', { error: error?.message || String(error) }));
|
|
127
|
+
}, delayMs);
|
|
128
|
+
timers.modelCatalogWarmupTimer.unref?.();
|
|
129
|
+
}
|
|
130
|
+
|
|
131
|
+
function scheduleStatuslineUsageWarmup(delayMs = statuslineUsageWarmupDelayMs) {
|
|
132
|
+
const route = getRoute();
|
|
133
|
+
const providerId = clean(route?.provider);
|
|
134
|
+
if (!providerId || !providerId.includes('oauth')) {
|
|
135
|
+
bootProfile('statusline-usage:warm-skipped', { provider: providerId || null });
|
|
136
|
+
return;
|
|
137
|
+
}
|
|
138
|
+
if (timers.statuslineUsageWarmupTimer || isCloseRequested()) return;
|
|
139
|
+
timers.statuslineUsageWarmupTimer = setTimeout(async () => {
|
|
140
|
+
timers.statuslineUsageWarmupTimer = null;
|
|
141
|
+
if (isCloseRequested()) return;
|
|
142
|
+
if (getActiveTurnCount() > 0 || getSessionCreatePromise()) {
|
|
143
|
+
bootProfile('statusline-usage:warm-deferred', { reason: getActiveTurnCount() > 0 ? 'turn-active' : 'session-create' });
|
|
144
|
+
scheduleStatuslineUsageWarmup(backgroundBusyRetryMs);
|
|
145
|
+
return;
|
|
146
|
+
}
|
|
147
|
+
try {
|
|
148
|
+
ensureConfigForRouteProvider();
|
|
149
|
+
await ensureProvidersReady(ensureProviderEnabled(getConfig(), getRoute().provider));
|
|
150
|
+
if (isCloseRequested()) return;
|
|
151
|
+
refreshStatuslineUsageSnapshot(getRoute());
|
|
152
|
+
bootProfile('statusline-usage:warm-ready', { provider: clean(getRoute()?.provider) });
|
|
153
|
+
} catch (error) {
|
|
154
|
+
bootProfile('statusline-usage:warm-failed', { error: error?.message || String(error) });
|
|
155
|
+
} finally {
|
|
156
|
+
scheduleStatuslineUsageRefresh();
|
|
157
|
+
}
|
|
158
|
+
}, delayMs);
|
|
159
|
+
timers.statuslineUsageWarmupTimer.unref?.();
|
|
160
|
+
}
|
|
161
|
+
|
|
162
|
+
// Idle keep-alive loop: periodically re-fetch the OAuth usage snapshot so its
|
|
163
|
+
// cachedAt stays "live-fresh" and the statusline usage segment does not vanish
|
|
164
|
+
// after LIVE_USAGE_SNAPSHOT_MAX_AGE_MS while the session is idle. Turn-driven
|
|
165
|
+
// refreshes (recordStandaloneStatusTelemetry) already cover active sessions.
|
|
166
|
+
function scheduleStatuslineUsageRefresh(delayMs = statuslineUsageRefreshDelayMs) {
|
|
167
|
+
const route = getRoute();
|
|
168
|
+
const providerId = clean(route?.provider);
|
|
169
|
+
if (!providerId || !providerId.includes('oauth')) return;
|
|
170
|
+
if (timers.statuslineUsageRefreshTimer || isCloseRequested()) return;
|
|
171
|
+
timers.statuslineUsageRefreshTimer = setTimeout(async () => {
|
|
172
|
+
timers.statuslineUsageRefreshTimer = null;
|
|
173
|
+
if (isCloseRequested()) return;
|
|
174
|
+
if (getActiveTurnCount() > 0 || getSessionCreatePromise()) {
|
|
175
|
+
// Active turns refresh usage on their own; just re-arm the idle loop.
|
|
176
|
+
scheduleStatuslineUsageRefresh();
|
|
177
|
+
return;
|
|
178
|
+
}
|
|
179
|
+
try {
|
|
180
|
+
ensureConfigForRouteProvider();
|
|
181
|
+
await ensureProvidersReady(ensureProviderEnabled(getConfig(), getRoute().provider));
|
|
182
|
+
if (isCloseRequested()) return;
|
|
183
|
+
refreshStatuslineUsageSnapshot(getRoute());
|
|
184
|
+
} catch {
|
|
185
|
+
// Usage display must never affect the session runtime.
|
|
186
|
+
} finally {
|
|
187
|
+
scheduleStatuslineUsageRefresh();
|
|
188
|
+
}
|
|
189
|
+
}, delayMs);
|
|
190
|
+
timers.statuslineUsageRefreshTimer.unref?.();
|
|
191
|
+
}
|
|
192
|
+
|
|
193
|
+
return {
|
|
194
|
+
scheduleProviderWarmup,
|
|
195
|
+
scheduleProviderSetupWarmup,
|
|
196
|
+
scheduleProviderModelWarmup,
|
|
197
|
+
scheduleModelCatalogWarmup,
|
|
198
|
+
scheduleStatuslineUsageWarmup,
|
|
199
|
+
scheduleStatuslineUsageRefresh,
|
|
200
|
+
};
|
|
201
|
+
}
|
|
@@ -0,0 +1,237 @@
|
|
|
1
|
+
// Pure, dependency-light helpers extracted from the agent-tool facade. No
|
|
2
|
+
// module-level mutable state lives here (the frontmatter perm cache and all
|
|
3
|
+
// timers stay in the facade / their owner modules). Behavior-preserving:
|
|
4
|
+
// function bodies are identical to the originals.
|
|
5
|
+
import { existsSync, readFileSync } from 'node:fs';
|
|
6
|
+
import { isAbsolute, join, resolve } from 'node:path';
|
|
7
|
+
import {
|
|
8
|
+
normalizeAgentPermissionOrNone,
|
|
9
|
+
parseMarkdownFrontmatter,
|
|
10
|
+
} from '../../runtime/shared/markdown-frontmatter.mjs';
|
|
11
|
+
import { clearGatewaySessionRoute, writeGatewaySessionRoute } from '../../vendor/statusline/src/gateway/session-routes.mjs';
|
|
12
|
+
import { PRESET_ALIASES } from './tool-def.mjs';
|
|
13
|
+
|
|
14
|
+
export function envTimeoutMs(name, fallback) {
|
|
15
|
+
const raw = process.env[name];
|
|
16
|
+
if (raw === undefined || raw === '') return fallback;
|
|
17
|
+
const n = Number(raw);
|
|
18
|
+
return Number.isFinite(n) && n >= 0 ? Math.floor(n) : fallback;
|
|
19
|
+
}
|
|
20
|
+
|
|
21
|
+
export function clean(value) {
|
|
22
|
+
return String(value ?? '').trim();
|
|
23
|
+
}
|
|
24
|
+
|
|
25
|
+
export function agentTagOf(session) {
|
|
26
|
+
return clean(session?.agentTag);
|
|
27
|
+
}
|
|
28
|
+
|
|
29
|
+
export function normalizeAgentName(value) {
|
|
30
|
+
const id = clean(value).toLowerCase().replace(/[\s_]+/g, '-');
|
|
31
|
+
if (id === 'explorer') return 'explore';
|
|
32
|
+
if (id === 'maint' || id === 'maintenance' || id === 'memory') return 'maintainer';
|
|
33
|
+
if (id === 'heavy' || id === 'heavyworker') return 'heavy-worker';
|
|
34
|
+
if (id === 'review') return 'reviewer';
|
|
35
|
+
if (id === 'debug') return 'debugger';
|
|
36
|
+
return id;
|
|
37
|
+
}
|
|
38
|
+
|
|
39
|
+
export function positiveInt(value) {
|
|
40
|
+
const n = Number(value);
|
|
41
|
+
return Number.isFinite(n) && n > 0 ? Math.floor(n) : null;
|
|
42
|
+
}
|
|
43
|
+
|
|
44
|
+
export function terminalPidForContext(context = {}) {
|
|
45
|
+
return positiveInt(context?.clientHostPid);
|
|
46
|
+
}
|
|
47
|
+
|
|
48
|
+
export function agentScope(args = {}, context = {}) {
|
|
49
|
+
const scope = clean(args.scope || args.terminal || args.term).toLowerCase();
|
|
50
|
+
if (args.allTerminals === true || scope === 'all' || scope === 'global') return {};
|
|
51
|
+
return context || {};
|
|
52
|
+
}
|
|
53
|
+
|
|
54
|
+
export function sessionMatchesContext(session, context = {}) {
|
|
55
|
+
const wantedPid = terminalPidForContext(context);
|
|
56
|
+
if (!wantedPid) return true;
|
|
57
|
+
const sessionPid = positiveInt(session?.clientHostPid);
|
|
58
|
+
return !!sessionPid && sessionPid === wantedPid;
|
|
59
|
+
}
|
|
60
|
+
|
|
61
|
+
export function rowMatchesContext(row, context = {}) {
|
|
62
|
+
const wantedPid = terminalPidForContext(context);
|
|
63
|
+
if (!wantedPid) return true;
|
|
64
|
+
const rowPid = positiveInt(row?.clientHostPid);
|
|
65
|
+
return !!rowPid && rowPid === wantedPid;
|
|
66
|
+
}
|
|
67
|
+
|
|
68
|
+
export function nonNegativeInt(value) {
|
|
69
|
+
if (value === undefined || value === null || value === '') return null;
|
|
70
|
+
const n = Number(value);
|
|
71
|
+
return Number.isFinite(n) && n >= 0 ? Math.floor(n) : null;
|
|
72
|
+
}
|
|
73
|
+
|
|
74
|
+
export function presetKey(preset) {
|
|
75
|
+
return clean(preset?.id || preset?.name);
|
|
76
|
+
}
|
|
77
|
+
|
|
78
|
+
export function bridgeRouteForStatusline(preset = {}) {
|
|
79
|
+
const provider = clean(preset.provider);
|
|
80
|
+
const model = clean(preset.model);
|
|
81
|
+
if (!provider || !model) return null;
|
|
82
|
+
const out = {
|
|
83
|
+
mode: 'fixed',
|
|
84
|
+
defaultProvider: provider,
|
|
85
|
+
defaultModel: model,
|
|
86
|
+
};
|
|
87
|
+
const id = clean(preset.id);
|
|
88
|
+
const name = clean(preset.name);
|
|
89
|
+
const modelDisplay = clean(preset.modelDisplay || preset.display || preset.displayName);
|
|
90
|
+
const effort = clean(preset.effort);
|
|
91
|
+
if (id) out.presetId = id;
|
|
92
|
+
if (name) out.presetName = name;
|
|
93
|
+
if (modelDisplay) out.modelDisplay = modelDisplay;
|
|
94
|
+
if (effort) {
|
|
95
|
+
out.effort = effort;
|
|
96
|
+
out.displayEffort = effort;
|
|
97
|
+
}
|
|
98
|
+
if (preset.fast === true || preset.fast === false) out.fast = preset.fast;
|
|
99
|
+
return out;
|
|
100
|
+
}
|
|
101
|
+
|
|
102
|
+
export function writeAgentStatuslineRoute(sessionId, preset) {
|
|
103
|
+
const route = bridgeRouteForStatusline(preset);
|
|
104
|
+
if (!sessionId || !route) return false;
|
|
105
|
+
try { return writeGatewaySessionRoute(sessionId, route); } catch { return false; }
|
|
106
|
+
}
|
|
107
|
+
|
|
108
|
+
export function clearAgentStatuslineRoute(sessionId) {
|
|
109
|
+
if (!sessionId) return false;
|
|
110
|
+
try { return clearGatewaySessionRoute(sessionId); } catch { return false; }
|
|
111
|
+
}
|
|
112
|
+
|
|
113
|
+
export function findPreset(config, key) {
|
|
114
|
+
const wanted = clean(key).toLowerCase();
|
|
115
|
+
if (!wanted) return null;
|
|
116
|
+
const presets = Array.isArray(config?.presets) ? config.presets : [];
|
|
117
|
+
return presets.find((p) => {
|
|
118
|
+
return clean(p?.id).toLowerCase() === wanted || clean(p?.name).toLowerCase() === wanted;
|
|
119
|
+
}) || null;
|
|
120
|
+
}
|
|
121
|
+
|
|
122
|
+
export function synthesizePreset(config, key) {
|
|
123
|
+
const alias = PRESET_ALIASES.get(clean(key).toLowerCase());
|
|
124
|
+
if (!alias) return null;
|
|
125
|
+
const base = findPreset(config, alias.base);
|
|
126
|
+
if (!base) return null;
|
|
127
|
+
return {
|
|
128
|
+
...base,
|
|
129
|
+
id: alias.id,
|
|
130
|
+
name: alias.name,
|
|
131
|
+
effort: alias.effort,
|
|
132
|
+
};
|
|
133
|
+
}
|
|
134
|
+
|
|
135
|
+
export function normalizeAgentRoute(routeLike, fallbackProvider = '') {
|
|
136
|
+
const provider = clean(routeLike?.provider) || clean(fallbackProvider);
|
|
137
|
+
const model = clean(routeLike?.model);
|
|
138
|
+
if (!provider || !model) return null;
|
|
139
|
+
return {
|
|
140
|
+
provider,
|
|
141
|
+
model,
|
|
142
|
+
effort: clean(routeLike?.effort) || undefined,
|
|
143
|
+
fast: routeLike?.fast === true,
|
|
144
|
+
};
|
|
145
|
+
}
|
|
146
|
+
|
|
147
|
+
export function agentPresetName(agent) {
|
|
148
|
+
return `AGENT ${String(agent || '').toUpperCase()}`;
|
|
149
|
+
}
|
|
150
|
+
|
|
151
|
+
export async function resolvePrompt(args, cwd) {
|
|
152
|
+
const prompt = clean(args.prompt || args.message);
|
|
153
|
+
const file = clean(args.file);
|
|
154
|
+
if (prompt && file) throw new Error('agent: provide only one of prompt/message or file');
|
|
155
|
+
if (prompt) return prompt;
|
|
156
|
+
if (file) {
|
|
157
|
+
const target = isAbsolute(file) ? file : resolve(cwd || process.cwd(), file);
|
|
158
|
+
return readFileSync(target, 'utf8');
|
|
159
|
+
}
|
|
160
|
+
throw new Error('agent: prompt/message/file is required');
|
|
161
|
+
}
|
|
162
|
+
|
|
163
|
+
export function withCwdHeader(prompt, cwd) {
|
|
164
|
+
if (!cwd) return prompt;
|
|
165
|
+
if (String(prompt).startsWith('[effective-cwd]')) return prompt;
|
|
166
|
+
return `[effective-cwd] ${cwd}\n\n${prompt}`;
|
|
167
|
+
}
|
|
168
|
+
|
|
169
|
+
export function compactIso(value) {
|
|
170
|
+
const text = clean(value);
|
|
171
|
+
if (!text) return '';
|
|
172
|
+
return text.replace('T', ' ').replace(/\.\d+Z$/, 'Z');
|
|
173
|
+
}
|
|
174
|
+
|
|
175
|
+
export function formatElapsedMs(ms) {
|
|
176
|
+
const n = Number(ms);
|
|
177
|
+
if (!Number.isFinite(n) || n < 0) return '';
|
|
178
|
+
const totalSec = Math.floor(n / 1000);
|
|
179
|
+
if (totalSec < 60) return `${totalSec}s`;
|
|
180
|
+
const min = Math.floor(totalSec / 60);
|
|
181
|
+
const sec = totalSec % 60;
|
|
182
|
+
if (min < 60) return sec ? `${min}m${sec}s` : `${min}m`;
|
|
183
|
+
const hr = Math.floor(min / 60);
|
|
184
|
+
const remMin = min % 60;
|
|
185
|
+
return remMin ? `${hr}h${remMin}m` : `${hr}h`;
|
|
186
|
+
}
|
|
187
|
+
|
|
188
|
+
export function elapsedFromStamps(startedAt, finishedAt, status) {
|
|
189
|
+
const start = Date.parse(clean(startedAt));
|
|
190
|
+
if (!Number.isFinite(start)) return null;
|
|
191
|
+
const finish = Date.parse(clean(finishedAt));
|
|
192
|
+
const end = Number.isFinite(finish) ? finish : Date.now();
|
|
193
|
+
const label = formatElapsedMs(end - start);
|
|
194
|
+
if (!label) return null;
|
|
195
|
+
return Number.isFinite(finish) ? label : `${label} (running)`;
|
|
196
|
+
}
|
|
197
|
+
|
|
198
|
+
export function stripFinalAnswerWrapper(value) {
|
|
199
|
+
const text = String(value ?? '').trim();
|
|
200
|
+
const match = /^<final-answer\b[^>]*>([\s\S]*?)<\/final-answer>\s*$/i.exec(text);
|
|
201
|
+
return match ? match[1].trim() : text;
|
|
202
|
+
}
|
|
203
|
+
|
|
204
|
+
// Process-wide TTL cache for agent AGENT.md frontmatter permission. The file
|
|
205
|
+
// rarely changes, but this otherwise pays several existsSync()+readFileSync()
|
|
206
|
+
// calls on EVERY spawn — multiplied across a parallel fanout that is pure
|
|
207
|
+
// redundant synchronous I/O on the event loop, which blocks every other
|
|
208
|
+
// concurrent spawn. Short TTL keeps edits picked up quickly while collapsing
|
|
209
|
+
// burst reads. Cache + TTL are owned here alongside the sole reader.
|
|
210
|
+
const _frontmatterPermCache = new Map(); // key -> { value, atMs }
|
|
211
|
+
const FRONTMATTER_PERM_CACHE_TTL_MS = envTimeoutMs('MIXDOG_AGENT_FRONTMATTER_TTL_MS', 5_000);
|
|
212
|
+
|
|
213
|
+
export function readAgentFrontmatterPermission(agent, dataDir, standaloneSourceRoot) {
|
|
214
|
+
const cleanAgent = clean(agent);
|
|
215
|
+
if (!cleanAgent) return null;
|
|
216
|
+
const cacheKey = `${dataDir || ''}\u0000${cleanAgent}`;
|
|
217
|
+
const cached = _frontmatterPermCache.get(cacheKey);
|
|
218
|
+
if (cached && Date.now() - cached.atMs < FRONTMATTER_PERM_CACHE_TTL_MS) {
|
|
219
|
+
return cached.value;
|
|
220
|
+
}
|
|
221
|
+
const candidates = [];
|
|
222
|
+
if (dataDir) {
|
|
223
|
+
candidates.push(join(dataDir, 'agents', cleanAgent, 'AGENT.md'));
|
|
224
|
+
candidates.push(join(dataDir, 'agents', `${cleanAgent}.md`));
|
|
225
|
+
}
|
|
226
|
+
candidates.push(join(standaloneSourceRoot, 'agents', cleanAgent, 'AGENT.md'));
|
|
227
|
+
candidates.push(join(standaloneSourceRoot, 'agents', `${cleanAgent}.md`));
|
|
228
|
+
let resolved = null;
|
|
229
|
+
for (const file of candidates) {
|
|
230
|
+
if (!existsSync(file)) continue;
|
|
231
|
+
const fm = parseMarkdownFrontmatter(readFileSync(file, 'utf8'));
|
|
232
|
+
const permission = normalizeAgentPermissionOrNone(fm.permission);
|
|
233
|
+
if (permission) { resolved = permission; break; }
|
|
234
|
+
}
|
|
235
|
+
_frontmatterPermCache.set(cacheKey, { value: resolved, atMs: Date.now() });
|
|
236
|
+
return resolved;
|
|
237
|
+
}
|