mixdog 0.9.2 → 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 +8 -3
- package/scripts/anthropic-maxtokens-test.mjs +119 -0
- 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/build-tui.mjs +13 -1
- package/scripts/explore-bench.mjs +124 -0
- package/scripts/freevar-smoke.mjs +95 -0
- package/scripts/hook-bus-test.mjs +191 -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/path-suffix-test.mjs +57 -0
- package/scripts/provider-toolcall-test.mjs +7 -3
- package/scripts/recall-bench.mjs +207 -0
- 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 +30 -67
- package/scripts/tui-render-smoke.mjs +90 -0
- package/scripts/webhook-smoke.mjs +208 -0
- package/src/agents/debugger/AGENT.md +5 -2
- package/src/agents/heavy-worker/AGENT.md +21 -11
- package/src/agents/maintainer/AGENT.md +4 -0
- package/src/agents/reviewer/AGENT.md +3 -2
- package/src/agents/worker/AGENT.md +21 -11
- package/src/lib/rules-builder.cjs +4 -0
- package/src/mixdog-session-runtime.mjs +933 -3731
- 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/repl.mjs +5 -5
- package/src/rules/agent/00-common.md +2 -0
- package/src/rules/agent/30-explorer.md +8 -11
- package/src/rules/lead/lead-brief.md +12 -0
- package/src/rules/lead/lead-tool.md +2 -9
- package/src/rules/shared/01-tool.md +11 -5
- 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/context/collect.mjs +51 -0
- package/src/runtime/agent/orchestrator/mcp/client.mjs +6 -2
- package/src/runtime/agent/orchestrator/providers/anthropic-effort.mjs +63 -21
- package/src/runtime/agent/orchestrator/providers/anthropic-max-tokens.mjs +93 -0
- 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 +97 -1343
- package/src/runtime/agent/orchestrator/providers/anthropic-sse.mjs +607 -0
- package/src/runtime/agent/orchestrator/providers/anthropic.mjs +78 -10
- package/src/runtime/agent/orchestrator/providers/api-usage.mjs +1 -13
- 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 +44 -1014
- package/src/runtime/agent/orchestrator/providers/grok-oauth.mjs +18 -4
- package/src/runtime/agent/orchestrator/providers/lib/usage-primitives.mjs +32 -0
- 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/oauth-usage.mjs +54 -20
- package/src/runtime/agent/orchestrator/providers/openai-codex-model.mjs +108 -0
- package/src/runtime/agent/orchestrator/providers/openai-compat-stream.mjs +19 -12
- 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 +41 -1142
- 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 +303 -2119
- package/src/runtime/agent/orchestrator/providers/openai-oauth.mjs +140 -995
- 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/providers/opencode-go-usage.mjs +38 -12
- package/src/runtime/agent/orchestrator/providers/retry-classifier.mjs +7 -8
- 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-debug.mjs +28 -0
- package/src/runtime/agent/orchestrator/session/loop/compact-policy.mjs +274 -0
- package/src/runtime/agent/orchestrator/session/loop/completion-guards.mjs +61 -0
- package/src/runtime/agent/orchestrator/session/loop/context-overflow.mjs +38 -0
- package/src/runtime/agent/orchestrator/session/loop/env.mjs +14 -0
- package/src/runtime/agent/orchestrator/session/loop/hidden-agents.mjs +21 -0
- package/src/runtime/agent/orchestrator/session/loop/pre-dispatch-deny.mjs +47 -0
- 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/steering.mjs +63 -0
- package/src/runtime/agent/orchestrator/session/loop/stored-tool-args.mjs +100 -0
- package/src/runtime/agent/orchestrator/session/loop/termination.mjs +58 -0
- package/src/runtime/agent/orchestrator/session/loop/tool-classify.mjs +52 -0
- package/src/runtime/agent/orchestrator/session/loop/tool-exec.mjs +239 -0
- package/src/runtime/agent/orchestrator/session/loop/tool-helpers.mjs +218 -0
- package/src/runtime/agent/orchestrator/session/loop/transcript-repair.mjs +101 -0
- package/src/runtime/agent/orchestrator/session/loop/usage.mjs +35 -0
- package/src/runtime/agent/orchestrator/session/loop.mjs +409 -1304
- package/src/runtime/agent/orchestrator/session/manager/compaction-runner.mjs +471 -0
- package/src/runtime/agent/orchestrator/session/manager/context-meta.mjs +230 -0
- package/src/runtime/agent/orchestrator/session/manager/pending-messages.mjs +235 -0
- package/src/runtime/agent/orchestrator/session/manager/prompt-utils.mjs +149 -0
- package/src/runtime/agent/orchestrator/session/manager/rules-cache.mjs +155 -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/tool-resolution.mjs +303 -0
- package/src/runtime/agent/orchestrator/session/manager/usage-metrics.mjs +210 -0
- package/src/runtime/agent/orchestrator/session/manager.mjs +226 -2114
- 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/stall-policy.mjs +3 -3
- 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 +33 -42
- package/src/runtime/agent/orchestrator/tools/builtin/external-tool-adapters.mjs +241 -0
- package/src/runtime/agent/orchestrator/tools/builtin/fuzzy-match.mjs +1 -1
- package/src/runtime/agent/orchestrator/tools/builtin/list-tool.mjs +40 -0
- package/src/runtime/agent/orchestrator/tools/builtin/path-diagnostics.mjs +42 -2
- package/src/runtime/agent/orchestrator/tools/builtin/read-formatting.mjs +1 -1
- package/src/runtime/agent/orchestrator/tools/builtin/read-single-tool.mjs +11 -4
- 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 -87
- 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 +78 -304
- package/src/runtime/agent/orchestrator/tools/builtin.mjs +11 -6
- package/src/runtime/agent/orchestrator/tools/code-graph/build.mjs +303 -0
- package/src/runtime/agent/orchestrator/tools/code-graph/constants.mjs +43 -0
- package/src/runtime/agent/orchestrator/tools/code-graph/disk-cache.mjs +382 -0
- package/src/runtime/agent/orchestrator/tools/code-graph/dispatch.mjs +551 -0
- package/src/runtime/agent/orchestrator/tools/code-graph/graph-binary.mjs +295 -0
- package/src/runtime/agent/orchestrator/tools/code-graph/graph-model.mjs +158 -0
- package/src/runtime/agent/orchestrator/tools/code-graph/keyword-match.mjs +82 -0
- package/src/runtime/agent/orchestrator/tools/code-graph/lang-predicates.mjs +128 -0
- package/src/runtime/agent/orchestrator/tools/code-graph/memory-cache.mjs +66 -0
- package/src/runtime/agent/orchestrator/tools/code-graph/project-root.mjs +44 -0
- package/src/runtime/agent/orchestrator/tools/code-graph/search.mjs +1080 -0
- package/src/runtime/agent/orchestrator/tools/code-graph/source-access.mjs +81 -0
- package/src/runtime/agent/orchestrator/tools/code-graph/span.mjs +19 -0
- package/src/runtime/agent/orchestrator/tools/code-graph/symbol-index.mjs +280 -0
- package/src/runtime/agent/orchestrator/tools/code-graph/text-columns.mjs +45 -0
- package/src/runtime/agent/orchestrator/tools/code-graph/text-mask.mjs +347 -0
- package/src/runtime/agent/orchestrator/tools/code-graph-tool-defs.mjs +6 -6
- package/src/runtime/agent/orchestrator/tools/code-graph.mjs +36 -4277
- 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 +1 -23
- package/src/runtime/agent/orchestrator/tools/shell-command.mjs +10 -74
- package/src/runtime/agent/orchestrator/tools/shell-powershell.mjs +77 -0
- package/src/runtime/agent/orchestrator/tools/shell-snapshot.mjs +2 -4
- 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 +241 -894
- package/src/runtime/channels/lib/backend-dispatch.mjs +44 -0
- package/src/runtime/channels/lib/boot-profile.mjs +23 -0
- package/src/runtime/channels/lib/crash-log.mjs +106 -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/index-drop-trace.mjs +72 -0
- package/src/runtime/channels/lib/output-forwarder.mjs +9 -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/telegram-format.mjs +19 -22
- 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/lib/whisper-language.mjs +42 -0
- package/src/runtime/channels/tool-defs.mjs +11 -130
- package/src/runtime/memory/index.mjs +258 -2050
- package/src/runtime/memory/lib/core-memory-store.mjs +351 -1
- 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/cycle-signatures.mjs +34 -0
- package/src/runtime/memory/lib/embedding-warmup.mjs +58 -0
- package/src/runtime/memory/lib/http-wire.mjs +57 -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 +72 -837
- 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-scope-filter.mjs +24 -0
- package/src/runtime/memory/lib/memory-recall-store.mjs +22 -2
- package/src/runtime/memory/lib/memory-retrievers.mjs +8 -0
- package/src/runtime/memory/lib/memory.mjs +20 -0
- package/src/runtime/memory/lib/pg/supervisor.mjs +1 -1
- package/src/runtime/memory/lib/promotion-fingerprint.mjs +50 -0
- package/src/runtime/memory/lib/query-handlers.mjs +780 -0
- package/src/runtime/memory/lib/recall-format.mjs +238 -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 +6 -14
- 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/abort-controller.mjs +1 -1
- package/src/runtime/shared/atomic-file.mjs +26 -1
- package/src/runtime/shared/background-tasks.mjs +2 -3
- package/src/runtime/shared/buffered-appender.mjs +149 -0
- package/src/runtime/shared/launcher-control.mjs +2 -2
- package/src/runtime/shared/task-notification-envelope.mjs +98 -0
- package/src/runtime/shared/tool-execution-contract.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 +52 -2
- package/src/runtime/shared/update-checker.mjs +7 -4
- package/src/session-runtime/config-helpers.mjs +291 -0
- package/src/session-runtime/config-lifecycle.mjs +232 -0
- package/src/session-runtime/cwd-plugins.mjs +226 -0
- package/src/session-runtime/effort.mjs +128 -0
- package/src/session-runtime/fs-utils.mjs +10 -0
- package/src/session-runtime/mcp-glue.mjs +177 -0
- package/src/session-runtime/model-capabilities.mjs +130 -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 +126 -0
- package/src/session-runtime/plugin-mcp.mjs +114 -0
- 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/session-text.mjs +100 -0
- package/src/session-runtime/settings-api.mjs +319 -0
- package/src/session-runtime/statusline-route.mjs +35 -0
- package/src/session-runtime/tool-catalog.mjs +720 -0
- package/src/session-runtime/tool-defs.mjs +84 -0
- package/src/session-runtime/warmup-schedulers.mjs +201 -0
- package/src/session-runtime/workflow.mjs +358 -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 +155 -677
- package/src/standalone/channel-worker.mjs +7 -9
- package/src/standalone/explore-tool.mjs +40 -12
- 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 +110 -746
- package/src/standalone/memory-runtime-proxy.mjs +7 -0
- package/src/standalone/opencode-go-login.mjs +125 -0
- package/src/standalone/provider-admin.mjs +15 -19
- package/src/standalone/usage-dashboard.mjs +3 -1
- package/src/tui/App.jsx +1163 -7571
- 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 +259 -80
- 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 +56 -588
- 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/components/tool-output-format.mjs +2 -2
- package/src/tui/display-width.mjs +20 -3
- package/src/tui/dist/index.mjs +18034 -17188
- package/src/tui/engine/agent-envelope.mjs +296 -0
- package/src/tui/engine/agent-job-feed.mjs +133 -0
- package/src/tui/engine/boot-profile.mjs +21 -0
- package/src/tui/engine/labels.mjs +67 -0
- package/src/tui/engine/notice-text.mjs +112 -0
- package/src/tui/engine/notification-plan.mjs +76 -0
- package/src/tui/engine/queue-helpers.mjs +161 -0
- package/src/tui/engine/render-timing.mjs +17 -0
- package/src/tui/engine/session-stats.mjs +46 -0
- package/src/tui/engine/tool-approval.mjs +94 -0
- package/src/tui/engine/tool-call-fields.mjs +23 -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/tool-result-text.mjs +126 -0
- package/src/tui/engine.mjs +405 -1385
- package/src/tui/figures.mjs +5 -0
- package/src/tui/index.jsx +105 -0
- package/src/tui/input-editing.mjs +60 -10
- package/src/tui/keyboard-protocol.mjs +2 -2
- package/src/tui/lib/voice-recorder.mjs +35 -19
- package/src/tui/markdown/format-token.mjs +11 -9
- package/src/tui/paste-attachments.mjs +38 -0
- package/src/tui/statusline-ansi-bridge.mjs +11 -3
- package/src/tui/theme.mjs +6 -0
- package/src/tui/themes/base.mjs +2 -2
- package/src/tui/themes/kanagawa.mjs +4 -4
- package/src/tui/themes/teal.mjs +4 -5
- package/src/tui/themes/utils.mjs +1 -1
- 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 +77 -462
- package/src/ui/tool-card.mjs +0 -1
- package/src/vendor/statusline/bin/statusline-route.mjs +15 -2
- package/src/workflows/default/WORKFLOW.md +16 -9
- package/src/workflows/sequential/WORKFLOW.md +16 -11
- package/src/workflows/solo/WORKFLOW.md +5 -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/open-config-tool.mjs +0 -26
- package/src/runtime/shared/channel-notification-routing.test.mjs +0 -45
- 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/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/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
|
@@ -0,0 +1,230 @@
|
|
|
1
|
+
// Context-window sizing, compaction target math, and session context-meta
|
|
2
|
+
// resolution. Extracted verbatim from manager.mjs (behavior-preserving).
|
|
3
|
+
import { getModelMetadataSync } from '../../providers/model-catalog.mjs';
|
|
4
|
+
import { resolveCompactTriggerTokens } from '../context-utils.mjs';
|
|
5
|
+
import { normalizeCompactType, DEFAULT_COMPACT_TYPE } from '../compact.mjs';
|
|
6
|
+
|
|
7
|
+
// Known context windows for the current-generation models this plugin
|
|
8
|
+
// routes to. Anything not listed falls through to guessContextWindow() —
|
|
9
|
+
// local llama/mistral/phi default to 8192, everything else 128000. Keep
|
|
10
|
+
// this map trimmed to live models; older generations slow down reads
|
|
11
|
+
// without buying anything.
|
|
12
|
+
const CONTEXT_WINDOWS = {
|
|
13
|
+
// OpenAI GPT-5.x family (openai / openai-oauth)
|
|
14
|
+
'gpt-5.5': 272000,
|
|
15
|
+
'gpt-5.4': 272000,
|
|
16
|
+
'gpt-5.4-mini': 272000,
|
|
17
|
+
'gpt-5.4-nano': 272000,
|
|
18
|
+
// Anthropic Claude 4.x
|
|
19
|
+
'claude-opus-4-8': 1000000,
|
|
20
|
+
'claude-opus-4-7': 1000000,
|
|
21
|
+
'claude-sonnet-4-6': 1000000,
|
|
22
|
+
'claude-haiku-4-5-20251001': 200000,
|
|
23
|
+
// Google Gemini 3.x
|
|
24
|
+
'gemini-3.1-pro': 1000000,
|
|
25
|
+
'gemini-3-pro': 1000000,
|
|
26
|
+
'gemini-3.5-flash': 1000000,
|
|
27
|
+
'gemini-3-flash': 1000000,
|
|
28
|
+
// xAI Grok (catalog polyfill mirror — model-catalog PRICING_OVERRIDES)
|
|
29
|
+
'grok-build-0.1': 256000,
|
|
30
|
+
'grok-4.20': 1000000,
|
|
31
|
+
};
|
|
32
|
+
// Family-pattern fallback used only when both the provider catalog and the
|
|
33
|
+
// exact-id table miss (cold metadata, before the LiteLLM/models.dev catalog
|
|
34
|
+
// warms). Keep these aligned with the catalog so /context, gateway, and the
|
|
35
|
+
// runtime agree on the boundary the first time a model is routed. Local models
|
|
36
|
+
// (llama/mistral/phi/qwen/gemma) stay small so an unknown local id never claims
|
|
37
|
+
// a giant window.
|
|
38
|
+
const LOCAL_PROVIDERS = new Set(['ollama', 'lmstudio', 'llamacpp', 'llama.cpp', 'local', '']);
|
|
39
|
+
export function guessContextWindow(model, provider = null) {
|
|
40
|
+
if (CONTEXT_WINDOWS[model])
|
|
41
|
+
return CONTEXT_WINDOWS[model];
|
|
42
|
+
const m = String(model || '').toLowerCase();
|
|
43
|
+
const p = String(provider || '').toLowerCase();
|
|
44
|
+
const isLocalProvider = LOCAL_PROVIDERS.has(p);
|
|
45
|
+
// Local/self-hosted families — never inflate an unknown local id.
|
|
46
|
+
if (isLocalProvider && (m.includes('llama') || m.includes('mistral') || m.includes('mixtral')
|
|
47
|
+
|| m.includes('phi') || m.includes('qwen') || m.includes('gemma')
|
|
48
|
+
|| m.includes('deepseek-r1') || m.includes('codellama')))
|
|
49
|
+
return 8192;
|
|
50
|
+
// Current hosted families by name pattern.
|
|
51
|
+
if (m.startsWith('claude-opus') || m.startsWith('claude-sonnet')) return 1000000;
|
|
52
|
+
if (m.startsWith('claude-haiku') || m.startsWith('claude-')) return 200000;
|
|
53
|
+
if (m.startsWith('gemini-3') || m.startsWith('gemini-2')) return 1000000;
|
|
54
|
+
if (m.startsWith('gpt-5')) return 272000;
|
|
55
|
+
if (m.startsWith('grok-build')) return 256000;
|
|
56
|
+
if (m.startsWith('grok-')) return 1000000;
|
|
57
|
+
if (m.startsWith('deepseek-v')) return 1000000;
|
|
58
|
+
return 128000;
|
|
59
|
+
}
|
|
60
|
+
export function positiveContextWindow(value) {
|
|
61
|
+
const n = Number(value);
|
|
62
|
+
return Number.isFinite(n) && n > 0 ? Math.floor(n) : null;
|
|
63
|
+
}
|
|
64
|
+
export function envFlag(name, fallback = false) {
|
|
65
|
+
const v = process.env[name];
|
|
66
|
+
if (v === undefined) return fallback;
|
|
67
|
+
return !['0', 'false', 'off', 'no'].includes(String(v).trim().toLowerCase());
|
|
68
|
+
}
|
|
69
|
+
function boundedPercent(value, fallback = null) {
|
|
70
|
+
const n = Number(value);
|
|
71
|
+
if (Number.isFinite(n) && n > 0 && n <= 100) return n;
|
|
72
|
+
return fallback;
|
|
73
|
+
}
|
|
74
|
+
function providerNameOf(provider) {
|
|
75
|
+
if (typeof provider === 'string') return provider.toLowerCase();
|
|
76
|
+
return String(provider?.name || provider?.id || '').toLowerCase();
|
|
77
|
+
}
|
|
78
|
+
// Carry the percent/ratio-named buffer config from a compaction config object
|
|
79
|
+
// onto session.compaction so the shared compact-policy parser honors configured
|
|
80
|
+
// buffer
|
|
81
|
+
// percent/ratio. Only finite positive values are copied; absent fields stay
|
|
82
|
+
// undefined so the default-ratio fallback still applies.
|
|
83
|
+
export function preserveBufferConfigFields(cfg = {}) {
|
|
84
|
+
const out = {};
|
|
85
|
+
for (const key of ['bufferPercent', 'bufferPct', 'bufferRatio', 'bufferFraction']) {
|
|
86
|
+
const n = Number(cfg?.[key]);
|
|
87
|
+
if (Number.isFinite(n) && n > 0) out[key] = n;
|
|
88
|
+
}
|
|
89
|
+
return out;
|
|
90
|
+
}
|
|
91
|
+
const COMPACT_TARGET_RATIO = 0.02;
|
|
92
|
+
const COMPACT_TARGET_MIN_TOKENS = 4_000;
|
|
93
|
+
const COMPACT_TARGET_MAX_TOKENS = 16_000;
|
|
94
|
+
function compactTargetRatio() {
|
|
95
|
+
const raw = process.env.MIXDOG_AGENT_COMPACT_TARGET_PERCENT
|
|
96
|
+
?? process.env.MIXDOG_COMPACT_TARGET_PERCENT
|
|
97
|
+
?? COMPACT_TARGET_RATIO;
|
|
98
|
+
const n = Number(raw);
|
|
99
|
+
if (!Number.isFinite(n) || n <= 0) return COMPACT_TARGET_RATIO;
|
|
100
|
+
return n > 1 ? n / 100 : n;
|
|
101
|
+
}
|
|
102
|
+
function compactTargetTokensForBoundary(boundaryTokens) {
|
|
103
|
+
const boundary = positiveContextWindow(boundaryTokens);
|
|
104
|
+
if (!boundary) return null;
|
|
105
|
+
const explicit = positiveContextWindow(
|
|
106
|
+
process.env.MIXDOG_AGENT_COMPACT_TARGET_TOKENS
|
|
107
|
+
?? process.env.MIXDOG_COMPACT_TARGET_TOKENS,
|
|
108
|
+
);
|
|
109
|
+
if (explicit) return Math.max(1, Math.min(boundary, explicit));
|
|
110
|
+
const minTarget = Math.min(boundary, positiveContextWindow(process.env.MIXDOG_COMPACT_TARGET_MIN_TOKENS) || COMPACT_TARGET_MIN_TOKENS);
|
|
111
|
+
const maxTarget = Math.min(boundary, positiveContextWindow(process.env.MIXDOG_COMPACT_TARGET_MAX_TOKENS) || COMPACT_TARGET_MAX_TOKENS);
|
|
112
|
+
const byRatio = Math.max(1, Math.floor(boundary * compactTargetRatio()));
|
|
113
|
+
return Math.max(1, Math.min(boundary, maxTarget, Math.max(minTarget, byRatio)));
|
|
114
|
+
}
|
|
115
|
+
function defaultEffectiveContextWindowPercent(provider) {
|
|
116
|
+
// Gateway/statusline route metadata reserves a universal 10% headroom from
|
|
117
|
+
// the raw catalog window. Keep session compaction on the same effective
|
|
118
|
+
// capacity so /context, the TUI statusline, and gateway telemetry agree.
|
|
119
|
+
return 90;
|
|
120
|
+
}
|
|
121
|
+
const PROVIDER_SYNTHETIC_CONTEXT_DEFAULT = 1_000_000;
|
|
122
|
+
function providerRawContextWindow(info, catalogInfo) {
|
|
123
|
+
if (!info || typeof info !== 'object') return null;
|
|
124
|
+
const fromApiFields = positiveContextWindow(info.context_window)
|
|
125
|
+
|| positiveContextWindow(info.max_context_window);
|
|
126
|
+
if (fromApiFields) return fromApiFields;
|
|
127
|
+
const fromCache = positiveContextWindow(info.contextWindow)
|
|
128
|
+
|| positiveContextWindow(info.maxContextWindow);
|
|
129
|
+
if (!fromCache) return null;
|
|
130
|
+
const catalogWindow = positiveContextWindow(catalogInfo?.contextWindow)
|
|
131
|
+
|| positiveContextWindow(catalogInfo?.maxContextWindow)
|
|
132
|
+
|| positiveContextWindow(catalogInfo?.context_window)
|
|
133
|
+
|| positiveContextWindow(catalogInfo?.max_context_window);
|
|
134
|
+
if (fromCache === PROVIDER_SYNTHETIC_CONTEXT_DEFAULT
|
|
135
|
+
&& catalogWindow
|
|
136
|
+
&& fromCache !== catalogWindow) {
|
|
137
|
+
return null;
|
|
138
|
+
}
|
|
139
|
+
return fromCache;
|
|
140
|
+
}
|
|
141
|
+
export function resolveSessionContextMeta(provider, model, seed = {}) {
|
|
142
|
+
const info = typeof provider?.getCachedModelInfo === 'function'
|
|
143
|
+
? provider.getCachedModelInfo(model)
|
|
144
|
+
: null;
|
|
145
|
+
const catalogInfo = getModelMetadataSync(model, providerNameOf(provider));
|
|
146
|
+
const rawContextWindow = providerRawContextWindow(info, catalogInfo)
|
|
147
|
+
|| positiveContextWindow(catalogInfo?.contextWindow)
|
|
148
|
+
|| positiveContextWindow(catalogInfo?.maxContextWindow)
|
|
149
|
+
|| positiveContextWindow(catalogInfo?.context_window)
|
|
150
|
+
|| positiveContextWindow(catalogInfo?.max_context_window)
|
|
151
|
+
|| positiveContextWindow(seed.rawContextWindow)
|
|
152
|
+
|| positiveContextWindow(seed.raw_context_window)
|
|
153
|
+
|| positiveContextWindow(seed.contextWindow)
|
|
154
|
+
|| guessContextWindow(model, providerNameOf(provider));
|
|
155
|
+
const effectiveContextWindowPercent = boundedPercent(
|
|
156
|
+
seed.effectiveContextWindowPercent
|
|
157
|
+
?? seed.effective_context_window_percent
|
|
158
|
+
?? info?.effectiveContextWindowPercent
|
|
159
|
+
?? info?.effective_context_window_percent
|
|
160
|
+
?? catalogInfo?.effectiveContextWindowPercent
|
|
161
|
+
?? catalogInfo?.effective_context_window_percent,
|
|
162
|
+
defaultEffectiveContextWindowPercent(provider),
|
|
163
|
+
);
|
|
164
|
+
const pct = boundedPercent(effectiveContextWindowPercent, 100);
|
|
165
|
+
const contextWindow = Math.max(1, Math.floor(rawContextWindow * pct / 100));
|
|
166
|
+
const compactBoundaryTokens = contextWindow;
|
|
167
|
+
const rawCompactLimit = positiveContextWindow(
|
|
168
|
+
seed.autoCompactTokenLimit
|
|
169
|
+
?? seed.auto_compact_token_limit
|
|
170
|
+
?? info?.autoCompactTokenLimit
|
|
171
|
+
?? info?.auto_compact_token_limit
|
|
172
|
+
?? catalogInfo?.autoCompactTokenLimit
|
|
173
|
+
?? catalogInfo?.auto_compact_token_limit,
|
|
174
|
+
);
|
|
175
|
+
// Legacy-data migration: old implementations derived autoCompactTokenLimit
|
|
176
|
+
// from the full effective/raw window and persisted it onto the session.
|
|
177
|
+
// A resumed session therefore re-seeds autoCompactTokenLimit == boundary
|
|
178
|
+
// (or the raw window), which compactTriggerForSession / loop policy used to
|
|
179
|
+
// honor as an explicit trigger, collapsing the compaction buffer to 0. Only
|
|
180
|
+
// accept an explicit limit that is STRICTLY BELOW the boundary; a value at
|
|
181
|
+
// or above the boundary is a derived full-window artifact and is dropped to
|
|
182
|
+
// null so the trigger falls back to the default boundary trigger.
|
|
183
|
+
const explicitCompactLimit = rawCompactLimit && rawCompactLimit < compactBoundaryTokens
|
|
184
|
+
? rawCompactLimit
|
|
185
|
+
: null;
|
|
186
|
+
// Do NOT derive the auto-compact limit from the full effective window.
|
|
187
|
+
// Setting it to contextWindow makes autoTriggerTokens == boundary and the
|
|
188
|
+
// compaction buffer collapse to 0 (loop.mjs:708-713 / compactTriggerForSession),
|
|
189
|
+
// so auto-compact only fires when the context is already at the limit —
|
|
190
|
+
// at which point semantic compact fails ("result exceeds budget" /
|
|
191
|
+
// "summary cannot fit") and the turn can no longer be resumed.
|
|
192
|
+
// Leave it null unless the provider/catalog/seed supplies an explicit
|
|
193
|
+
// limit; the downstream buffer logic (default 10%, capped 25%) then
|
|
194
|
+
// triggers compaction with headroom, matching the reference auto-compact threshold.
|
|
195
|
+
const autoCompactTokenLimit = explicitCompactLimit || null;
|
|
196
|
+
return {
|
|
197
|
+
contextWindow,
|
|
198
|
+
rawContextWindow,
|
|
199
|
+
effectiveContextWindowPercent,
|
|
200
|
+
autoCompactTokenLimit: autoCompactTokenLimit || null,
|
|
201
|
+
compactBoundaryTokens,
|
|
202
|
+
};
|
|
203
|
+
}
|
|
204
|
+
export function compactTriggerForSession(session, boundaryTokens) {
|
|
205
|
+
return resolveCompactTriggerTokens(session, boundaryTokens);
|
|
206
|
+
}
|
|
207
|
+
export function compactTargetBudget(boundaryTokens, reserveTokens, _sourceTokens = null, _ratio = null) {
|
|
208
|
+
const boundary = positiveContextWindow(boundaryTokens);
|
|
209
|
+
if (!boundary) return null;
|
|
210
|
+
const reserve = Math.max(0, Number(reserveTokens) || 0);
|
|
211
|
+
const targetEffective = compactTargetTokensForBoundary(boundary) || boundary;
|
|
212
|
+
return Math.max(1, Math.min(boundary, targetEffective + reserve));
|
|
213
|
+
}
|
|
214
|
+
export function semanticCompactionEnabledForSession(session) {
|
|
215
|
+
const cfg = session?.compaction || {};
|
|
216
|
+
if (process.env.MIXDOG_AGENT_COMPACT_SEMANTIC !== undefined) return envFlag('MIXDOG_AGENT_COMPACT_SEMANTIC', true);
|
|
217
|
+
if (process.env.MIXDOG_COMPACT_SEMANTIC !== undefined) return envFlag('MIXDOG_COMPACT_SEMANTIC', true);
|
|
218
|
+
if (cfg.semantic === false || cfg.semantic === 'false' || cfg.semantic === 'off') return false;
|
|
219
|
+
if (cfg.semantic === true || cfg.semantic === 'true' || cfg.semantic === 'on' || cfg.semantic === 'auto') return true;
|
|
220
|
+
return true;
|
|
221
|
+
}
|
|
222
|
+
export function compactTypeForSession(session) {
|
|
223
|
+
const cfg = session?.compaction || {};
|
|
224
|
+
const configured = process.env.MIXDOG_AGENT_COMPACT_TYPE
|
|
225
|
+
?? process.env.MIXDOG_COMPACT_TYPE
|
|
226
|
+
?? cfg.type
|
|
227
|
+
?? cfg.compactType
|
|
228
|
+
?? cfg.compact_type;
|
|
229
|
+
return normalizeCompactType(configured, DEFAULT_COMPACT_TYPE);
|
|
230
|
+
}
|
|
@@ -0,0 +1,235 @@
|
|
|
1
|
+
// Steering / pending-message queue with sync buffered + atomic-file persistence.
|
|
2
|
+
// Extracted verbatim from manager.mjs (behavior-preserving).
|
|
3
|
+
import { join } from 'path';
|
|
4
|
+
import { resolvePluginData } from '../../../../shared/plugin-paths.mjs';
|
|
5
|
+
import { updateJsonAtomicSync } from '../../../../shared/atomic-file.mjs';
|
|
6
|
+
import { promptContentText, isInternalRuntimeNotificationText } from './prompt-utils.mjs';
|
|
7
|
+
|
|
8
|
+
const _sessionPendingMessages = new Map();
|
|
9
|
+
const PENDING_MESSAGES_FILE = 'session-pending-messages.json';
|
|
10
|
+
const PENDING_MESSAGES_MODE = 0o600;
|
|
11
|
+
const _pendingPersistBuffers = new Map();
|
|
12
|
+
let _pendingPersistImmediate = null;
|
|
13
|
+
|
|
14
|
+
function pendingMessagesPath() {
|
|
15
|
+
return join(resolvePluginData(), PENDING_MESSAGES_FILE);
|
|
16
|
+
}
|
|
17
|
+
|
|
18
|
+
function isValidPendingSessionId(sessionId) {
|
|
19
|
+
return typeof sessionId === 'string' && /^[A-Za-z0-9_-]+$/.test(sessionId);
|
|
20
|
+
}
|
|
21
|
+
|
|
22
|
+
function normalizePendingStore(raw) {
|
|
23
|
+
const sessions = raw && typeof raw === 'object' && raw.sessions && typeof raw.sessions === 'object'
|
|
24
|
+
? raw.sessions
|
|
25
|
+
: {};
|
|
26
|
+
const out = { version: 1, updatedAt: Date.now(), sessions: {} };
|
|
27
|
+
for (const [sid, value] of Object.entries(sessions)) {
|
|
28
|
+
if (!isValidPendingSessionId(sid) || !Array.isArray(value)) continue;
|
|
29
|
+
const q = value
|
|
30
|
+
.map((entry) => {
|
|
31
|
+
if (typeof entry === 'string') return entry;
|
|
32
|
+
if (entry && typeof entry === 'object' && typeof entry.message === 'string') return entry.message;
|
|
33
|
+
return '';
|
|
34
|
+
})
|
|
35
|
+
.filter(Boolean);
|
|
36
|
+
if (q.length > 0) out.sessions[sid] = q;
|
|
37
|
+
}
|
|
38
|
+
return out;
|
|
39
|
+
}
|
|
40
|
+
|
|
41
|
+
function normalizePendingMessageEntry(entry) {
|
|
42
|
+
if (typeof entry === 'string') {
|
|
43
|
+
const text = entry.trim();
|
|
44
|
+
return text ? { content: text, text } : null;
|
|
45
|
+
}
|
|
46
|
+
if (Array.isArray(entry)) {
|
|
47
|
+
if (entry.length === 0) return null;
|
|
48
|
+
const text = promptContentText(entry).trim();
|
|
49
|
+
return { content: entry, text };
|
|
50
|
+
}
|
|
51
|
+
if (!entry || typeof entry !== 'object') return null;
|
|
52
|
+
const content = Object.prototype.hasOwnProperty.call(entry, 'content') ? entry.content : null;
|
|
53
|
+
if (content == null) return null;
|
|
54
|
+
const text = typeof entry.text === 'string' ? entry.text.trim() : promptContentText(content).trim();
|
|
55
|
+
if (Array.isArray(content)) return content.length > 0 ? { content, text } : null;
|
|
56
|
+
if (typeof content === 'string') {
|
|
57
|
+
const value = content.trim();
|
|
58
|
+
return value ? { content: value, text: text || value } : null;
|
|
59
|
+
}
|
|
60
|
+
const fallback = promptContentText(content).trim();
|
|
61
|
+
return fallback ? { content: fallback, text: text || fallback } : null;
|
|
62
|
+
}
|
|
63
|
+
|
|
64
|
+
function pendingMessageText(entry) {
|
|
65
|
+
const normalized = normalizePendingMessageEntry(entry);
|
|
66
|
+
return normalized ? String(normalized.text || promptContentText(normalized.content) || '').trim() : '';
|
|
67
|
+
}
|
|
68
|
+
|
|
69
|
+
function pendingMessageQueueEntry(entry) {
|
|
70
|
+
const normalized = normalizePendingMessageEntry(entry);
|
|
71
|
+
if (!normalized) return null;
|
|
72
|
+
if (typeof normalized.content === 'string' && normalized.content === normalized.text) return normalized.content;
|
|
73
|
+
return { content: normalized.content, text: normalized.text || promptContentText(normalized.content).trim() };
|
|
74
|
+
}
|
|
75
|
+
|
|
76
|
+
function persistPendingMessages(sessionId, messages) {
|
|
77
|
+
if (!isValidPendingSessionId(sessionId)) return 0;
|
|
78
|
+
const persistedMessages = (Array.isArray(messages) ? messages : [messages])
|
|
79
|
+
.map(pendingMessageText)
|
|
80
|
+
.filter(Boolean);
|
|
81
|
+
if (persistedMessages.length === 0) return 0;
|
|
82
|
+
let depth = 0;
|
|
83
|
+
try {
|
|
84
|
+
updateJsonAtomicSync(pendingMessagesPath(), (raw) => {
|
|
85
|
+
const next = normalizePendingStore(raw);
|
|
86
|
+
const q = Array.isArray(next.sessions[sessionId]) ? next.sessions[sessionId] : [];
|
|
87
|
+
q.push(...persistedMessages);
|
|
88
|
+
next.sessions[sessionId] = q;
|
|
89
|
+
next.updatedAt = Date.now();
|
|
90
|
+
depth = q.length;
|
|
91
|
+
return next;
|
|
92
|
+
}, { compact: true, lock: true, mode: PENDING_MESSAGES_MODE, fsync: false });
|
|
93
|
+
} catch (err) {
|
|
94
|
+
try { process.stderr.write(`[session] pending-message persist failed sessionId=${sessionId}: ${err?.message || err}\n`); } catch {}
|
|
95
|
+
}
|
|
96
|
+
return depth;
|
|
97
|
+
}
|
|
98
|
+
|
|
99
|
+
function flushPendingMessagePersistsSync() {
|
|
100
|
+
if (_pendingPersistImmediate) {
|
|
101
|
+
try { clearImmediate(_pendingPersistImmediate); } catch {}
|
|
102
|
+
_pendingPersistImmediate = null;
|
|
103
|
+
}
|
|
104
|
+
if (_pendingPersistBuffers.size === 0) return;
|
|
105
|
+
const batches = [..._pendingPersistBuffers.entries()];
|
|
106
|
+
_pendingPersistBuffers.clear();
|
|
107
|
+
for (const [sid, messages] of batches) {
|
|
108
|
+
persistPendingMessages(sid, messages);
|
|
109
|
+
}
|
|
110
|
+
}
|
|
111
|
+
|
|
112
|
+
function schedulePendingMessagePersist(sessionId, message) {
|
|
113
|
+
if (!isValidPendingSessionId(sessionId)) return 0;
|
|
114
|
+
const persistedMessage = pendingMessageText(message);
|
|
115
|
+
if (!persistedMessage) return 0;
|
|
116
|
+
const q = _pendingPersistBuffers.get(sessionId) || [];
|
|
117
|
+
q.push(persistedMessage);
|
|
118
|
+
_pendingPersistBuffers.set(sessionId, q);
|
|
119
|
+
if (!_pendingPersistImmediate) {
|
|
120
|
+
_pendingPersistImmediate = setImmediate(() => {
|
|
121
|
+
_pendingPersistImmediate = null;
|
|
122
|
+
flushPendingMessagePersistsSync();
|
|
123
|
+
});
|
|
124
|
+
}
|
|
125
|
+
return q.length;
|
|
126
|
+
}
|
|
127
|
+
|
|
128
|
+
function takeBufferedPendingMessages(sessionId) {
|
|
129
|
+
if (!isValidPendingSessionId(sessionId)) return [];
|
|
130
|
+
const buffered = _pendingPersistBuffers.get(sessionId);
|
|
131
|
+
if (!buffered || buffered.length === 0) return [];
|
|
132
|
+
_pendingPersistBuffers.delete(sessionId);
|
|
133
|
+
return buffered.slice();
|
|
134
|
+
}
|
|
135
|
+
|
|
136
|
+
function drainPersistedPendingMessages(sessionId) {
|
|
137
|
+
if (!isValidPendingSessionId(sessionId)) return [];
|
|
138
|
+
let drained = [];
|
|
139
|
+
try {
|
|
140
|
+
updateJsonAtomicSync(pendingMessagesPath(), (raw) => {
|
|
141
|
+
const next = normalizePendingStore(raw);
|
|
142
|
+
const q = Array.isArray(next.sessions[sessionId]) ? next.sessions[sessionId] : [];
|
|
143
|
+
drained = q.filter((m) => typeof m === 'string' && m.length > 0);
|
|
144
|
+
if (drained.length === 0) return undefined;
|
|
145
|
+
delete next.sessions[sessionId];
|
|
146
|
+
next.updatedAt = Date.now();
|
|
147
|
+
return next;
|
|
148
|
+
}, { compact: true, lock: true, mode: PENDING_MESSAGES_MODE, fsync: false });
|
|
149
|
+
} catch (err) {
|
|
150
|
+
try { process.stderr.write(`[session] pending-message drain failed sessionId=${sessionId}: ${err?.message || err}\n`); } catch {}
|
|
151
|
+
}
|
|
152
|
+
return drained;
|
|
153
|
+
}
|
|
154
|
+
|
|
155
|
+
function modelVisiblePendingMessages(messages) {
|
|
156
|
+
return (Array.isArray(messages) ? messages : [])
|
|
157
|
+
.map(pendingMessageQueueEntry)
|
|
158
|
+
.filter(Boolean)
|
|
159
|
+
.filter((message) => !isInternalRuntimeNotificationText(
|
|
160
|
+
message && typeof message === 'object' && Object.prototype.hasOwnProperty.call(message, 'content')
|
|
161
|
+
? message.content
|
|
162
|
+
: message,
|
|
163
|
+
));
|
|
164
|
+
}
|
|
165
|
+
|
|
166
|
+
export function _mergePendingMessageEntries(entries) {
|
|
167
|
+
const normalized = (Array.isArray(entries) ? entries : [])
|
|
168
|
+
.map(normalizePendingMessageEntry)
|
|
169
|
+
.filter(Boolean);
|
|
170
|
+
if (normalized.length === 0) return null;
|
|
171
|
+
const displayText = normalized.map((entry) => entry.text || promptContentText(entry.content))
|
|
172
|
+
.filter((text) => String(text || '').trim())
|
|
173
|
+
.join('\n');
|
|
174
|
+
if (normalized.every((entry) => typeof entry.content === 'string')) {
|
|
175
|
+
return {
|
|
176
|
+
content: normalized.map((entry) => entry.content).filter(Boolean).join('\n'),
|
|
177
|
+
text: displayText,
|
|
178
|
+
count: normalized.length,
|
|
179
|
+
};
|
|
180
|
+
}
|
|
181
|
+
const parts = [];
|
|
182
|
+
for (const entry of normalized) {
|
|
183
|
+
if (typeof entry.content === 'string') {
|
|
184
|
+
if (entry.content.trim()) parts.push({ type: 'text', text: entry.content });
|
|
185
|
+
} else if (Array.isArray(entry.content)) {
|
|
186
|
+
parts.push(...entry.content);
|
|
187
|
+
} else {
|
|
188
|
+
const text = promptContentText(entry.content);
|
|
189
|
+
if (text.trim()) parts.push({ type: 'text', text });
|
|
190
|
+
}
|
|
191
|
+
parts.push({ type: 'text', text: '\n' });
|
|
192
|
+
}
|
|
193
|
+
while (parts.length && parts[parts.length - 1]?.type === 'text' && parts[parts.length - 1]?.text === '\n') parts.pop();
|
|
194
|
+
return { content: parts, text: displayText || promptContentText(parts), count: normalized.length };
|
|
195
|
+
}
|
|
196
|
+
|
|
197
|
+
export function enqueuePendingMessage(sessionId, message) {
|
|
198
|
+
const entry = pendingMessageQueueEntry(message);
|
|
199
|
+
if (!sessionId || !entry) return 0;
|
|
200
|
+
let q = _sessionPendingMessages.get(sessionId);
|
|
201
|
+
if (!q) { q = []; _sessionPendingMessages.set(sessionId, q); }
|
|
202
|
+
q.push(entry);
|
|
203
|
+
const bufferedDepth = schedulePendingMessagePersist(sessionId, entry);
|
|
204
|
+
return Math.max(q.length, bufferedDepth || 0);
|
|
205
|
+
}
|
|
206
|
+
|
|
207
|
+
export function drainPendingMessages(sessionId) {
|
|
208
|
+
const q = _sessionPendingMessages.get(sessionId);
|
|
209
|
+
const memory = q && q.length > 0 ? q.slice() : [];
|
|
210
|
+
_sessionPendingMessages.delete(sessionId);
|
|
211
|
+
const persisted = [...takeBufferedPendingMessages(sessionId), ...drainPersistedPendingMessages(sessionId)];
|
|
212
|
+
const memoryVisible = modelVisiblePendingMessages(memory);
|
|
213
|
+
const persistedVisible = modelVisiblePendingMessages(persisted);
|
|
214
|
+
if (memoryVisible.length === 0) return persistedVisible;
|
|
215
|
+
if (persistedVisible.length === 0) return memoryVisible;
|
|
216
|
+
const persistedTexts = persistedVisible.map(pendingMessageText);
|
|
217
|
+
const prefixMatches = memoryVisible.every((m, i) => persistedTexts[i] === pendingMessageText(m));
|
|
218
|
+
if (prefixMatches) return [...memoryVisible, ...persistedVisible.slice(memoryVisible.length)];
|
|
219
|
+
const out = persistedVisible.slice();
|
|
220
|
+
const seen = new Set(persistedTexts);
|
|
221
|
+
for (const m of memoryVisible) {
|
|
222
|
+
const text = pendingMessageText(m);
|
|
223
|
+
if (!text || seen.has(text)) continue;
|
|
224
|
+
out.push(m);
|
|
225
|
+
seen.add(text);
|
|
226
|
+
}
|
|
227
|
+
return out;
|
|
228
|
+
}
|
|
229
|
+
|
|
230
|
+
// Cleanup hook for closeSession — drop the in-memory queue and buffered-persist
|
|
231
|
+
// entry so both Maps do not accumulate one entry per closed session.
|
|
232
|
+
export function _dropPendingMessageState(id) {
|
|
233
|
+
try { _sessionPendingMessages.delete(id); } catch { /* ignore */ }
|
|
234
|
+
try { _pendingPersistBuffers.delete(id); } catch { /* ignore */ }
|
|
235
|
+
}
|
|
@@ -0,0 +1,149 @@
|
|
|
1
|
+
// Prompt content + temporal helpers, extracted verbatim from manager.mjs
|
|
2
|
+
// (behavior-preserving). Pure string/date utilities with no session state.
|
|
3
|
+
import { isInternalRuntimeNotificationText as contractIsInternalRuntimeNotificationText } from '../../../../shared/tool-execution-contract.mjs';
|
|
4
|
+
import { SUMMARY_PREFIX } from '../compact.mjs';
|
|
5
|
+
|
|
6
|
+
export function promptContentText(content) {
|
|
7
|
+
if (typeof content === 'string') return content;
|
|
8
|
+
if (Array.isArray(content)) {
|
|
9
|
+
return content.map((part) => {
|
|
10
|
+
if (typeof part === 'string') return part;
|
|
11
|
+
if (part?.type === 'text') return part.text || '';
|
|
12
|
+
if (part?.type === 'image') return '[Image]';
|
|
13
|
+
return part?.text || '';
|
|
14
|
+
}).filter(Boolean).join('\n');
|
|
15
|
+
}
|
|
16
|
+
return String(content ?? '');
|
|
17
|
+
}
|
|
18
|
+
|
|
19
|
+
export function hasModelVisiblePromptContent(prompt) {
|
|
20
|
+
return !!promptContentText(prompt).trim();
|
|
21
|
+
}
|
|
22
|
+
|
|
23
|
+
export function promptContentBytes(content) {
|
|
24
|
+
try {
|
|
25
|
+
if (typeof content === 'string') return Buffer.byteLength(content, 'utf8');
|
|
26
|
+
return Buffer.byteLength(JSON.stringify(content), 'utf8');
|
|
27
|
+
} catch {
|
|
28
|
+
return Buffer.byteLength(promptContentText(content), 'utf8');
|
|
29
|
+
}
|
|
30
|
+
}
|
|
31
|
+
|
|
32
|
+
export function prefixUserTurnContent(content, contextBlock) {
|
|
33
|
+
if (!contextBlock) return content;
|
|
34
|
+
if (Array.isArray(content)) {
|
|
35
|
+
return [{ type: 'text', text: `${contextBlock}# Task\n` }, ...content];
|
|
36
|
+
}
|
|
37
|
+
return `${contextBlock}# Task\n${content}`;
|
|
38
|
+
}
|
|
39
|
+
|
|
40
|
+
export function prefixSessionStartContent(content, sessionBlock) {
|
|
41
|
+
if (!sessionBlock) return content;
|
|
42
|
+
if (Array.isArray(content)) {
|
|
43
|
+
return [{ type: 'text', text: `${sessionBlock}\n\n` }, ...content];
|
|
44
|
+
}
|
|
45
|
+
return `${sessionBlock}\n\n${content}`;
|
|
46
|
+
}
|
|
47
|
+
|
|
48
|
+
function localIsoDate(date = new Date()) {
|
|
49
|
+
const year = date.getFullYear();
|
|
50
|
+
const month = String(date.getMonth() + 1).padStart(2, '0');
|
|
51
|
+
const day = String(date.getDate()).padStart(2, '0');
|
|
52
|
+
return `${year}-${month}-${day}`;
|
|
53
|
+
}
|
|
54
|
+
|
|
55
|
+
function localDateTimeWithZone(date = new Date()) {
|
|
56
|
+
const datePart = localIsoDate(date);
|
|
57
|
+
const hh = String(date.getHours()).padStart(2, '0');
|
|
58
|
+
const mm = String(date.getMinutes()).padStart(2, '0');
|
|
59
|
+
const ss = String(date.getSeconds()).padStart(2, '0');
|
|
60
|
+
let zone = '';
|
|
61
|
+
try { zone = Intl.DateTimeFormat().resolvedOptions().timeZone || ''; } catch {}
|
|
62
|
+
return zone ? `${datePart} ${hh}:${mm}:${ss} ${zone}` : `${datePart} ${hh}:${mm}:${ss}`;
|
|
63
|
+
}
|
|
64
|
+
|
|
65
|
+
function temporalPromptText(content) {
|
|
66
|
+
const text = promptContentText(content)
|
|
67
|
+
.replace(/\s+/g, ' ')
|
|
68
|
+
.trim()
|
|
69
|
+
.toLowerCase();
|
|
70
|
+
return text;
|
|
71
|
+
}
|
|
72
|
+
|
|
73
|
+
function promptNeedsDateReminder(content) {
|
|
74
|
+
const text = temporalPromptText(content);
|
|
75
|
+
if (!text) return false;
|
|
76
|
+
return /(?:오늘|내일|어제|모레|그저께|요즘|최근|방금|아까|현재\s*(?:날짜|시간|시각)|지금\s*(?:몇\s*시|시간|날짜|요일)|몇\s*월\s*몇\s*일|몇\s*시|무슨\s*요일|요일|날짜|이번\s*(?:주|달|월|년)|지난\s*(?:주|달|월|년)|다음\s*(?:주|달|월|년)|올해|작년|내년|today|tomorrow|yesterday|recently|current\s+(?:date|time)|what\s+(?:date|time)|which\s+day|weekday|this\s+(?:week|month|year)|last\s+(?:week|month|year)|next\s+(?:week|month|year))/i.test(text);
|
|
77
|
+
}
|
|
78
|
+
|
|
79
|
+
function promptNeedsTimeReminder(content) {
|
|
80
|
+
const text = temporalPromptText(content);
|
|
81
|
+
if (!text) return false;
|
|
82
|
+
return /(?:현재\s*(?:시간|시각)|지금\s*(?:몇\s*시|시간)|몇\s*시|시각|시간|current\s+time|what\s+time|time\s+is\s+it)/i.test(text);
|
|
83
|
+
}
|
|
84
|
+
|
|
85
|
+
export function buildCurrentTimeBlock(content) {
|
|
86
|
+
const needsTime = promptNeedsTimeReminder(content);
|
|
87
|
+
if (!needsTime && !promptNeedsDateReminder(content)) return '';
|
|
88
|
+
return localDateTimeWithZone(new Date());
|
|
89
|
+
}
|
|
90
|
+
|
|
91
|
+
function sessionModelDisplay(model) {
|
|
92
|
+
const text = String(model || '').trim();
|
|
93
|
+
if (!text) return '';
|
|
94
|
+
return text
|
|
95
|
+
.replace(/-\d{4}-\d{2}-\d{2}$/, '')
|
|
96
|
+
.replace(/^gpt-/i, 'GPT-')
|
|
97
|
+
.replace(/(?:^|-)([a-z])/g, (m) => m.toUpperCase());
|
|
98
|
+
}
|
|
99
|
+
|
|
100
|
+
export function buildSessionStartBlock(session, cwd) {
|
|
101
|
+
if (!session || session.owner === 'agent') return '';
|
|
102
|
+
const lines = ['# Session'];
|
|
103
|
+
const effectiveCwd = String(cwd || session.cwd || '').trim();
|
|
104
|
+
if (effectiveCwd) lines.push(`Cwd: ${effectiveCwd}`);
|
|
105
|
+
const modelBits = [
|
|
106
|
+
sessionModelDisplay(session.model),
|
|
107
|
+
session.effort ? String(session.effort).trim().toUpperCase() : '',
|
|
108
|
+
session.fast === true ? 'FAST' : '',
|
|
109
|
+
].filter(Boolean);
|
|
110
|
+
if (modelBits.length) lines.push(`Model: ${modelBits.join(' · ')}`);
|
|
111
|
+
const workflowName = String(session.workflow?.name || session.workflow?.id || '').trim();
|
|
112
|
+
if (workflowName) lines.push(`Workflow: ${workflowName}`);
|
|
113
|
+
return lines.length > 1 ? lines.join('\n') : '';
|
|
114
|
+
}
|
|
115
|
+
|
|
116
|
+
export function isReferenceFilesMessage(message) {
|
|
117
|
+
return message?.role === 'user'
|
|
118
|
+
&& typeof message.content === 'string'
|
|
119
|
+
&& /^Reference files:\s*/i.test(message.content.trimStart());
|
|
120
|
+
}
|
|
121
|
+
|
|
122
|
+
export function isProtectedContextUserMessage(message) {
|
|
123
|
+
return message?.role === 'user'
|
|
124
|
+
&& typeof message.content === 'string'
|
|
125
|
+
&& message.content.trimStart().startsWith('<system-reminder>');
|
|
126
|
+
}
|
|
127
|
+
|
|
128
|
+
// Compact summary messages (role:'user', content startsWith SUMMARY_PREFIX)
|
|
129
|
+
// are synthetic anchors, not a real human turn — they must not count as
|
|
130
|
+
// "user conversation" or the post-clear/post-compact session-start block
|
|
131
|
+
// would be wrongly suppressed on the next real user turn.
|
|
132
|
+
function isSummaryAnchorMessage(message) {
|
|
133
|
+
return message?.role === 'user'
|
|
134
|
+
&& typeof message.content === 'string'
|
|
135
|
+
&& message.content.startsWith(SUMMARY_PREFIX);
|
|
136
|
+
}
|
|
137
|
+
|
|
138
|
+
export function hasUserConversationMessage(messages) {
|
|
139
|
+
return (Array.isArray(messages) ? messages : []).some((message) => (
|
|
140
|
+
message?.role === 'user'
|
|
141
|
+
&& !isProtectedContextUserMessage(message)
|
|
142
|
+
&& !isReferenceFilesMessage(message)
|
|
143
|
+
&& !isSummaryAnchorMessage(message)
|
|
144
|
+
));
|
|
145
|
+
}
|
|
146
|
+
|
|
147
|
+
export function isInternalRuntimeNotificationText(content) {
|
|
148
|
+
return contractIsInternalRuntimeNotificationText(promptContentText(content));
|
|
149
|
+
}
|