mixdog 1.3.5 → 1.4.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/NOTICE.md +2 -2
- package/README.md +2 -2
- package/package.json +2 -11
- package/scripts/check-voice-runtime-inputs.mjs +25 -0
- package/scripts/daemon-stub.mjs +1 -1
- package/scripts/gate-local.mjs +16 -5
- package/scripts/latest-guard.mjs +21 -9
- package/scripts/lib/isolated-test-env.mjs +10 -5
- package/scripts/lib/run-node-tests.mjs +37 -37
- package/scripts/lib/scoped-temp.d.mts +16 -0
- package/scripts/lib/scoped-temp.mjs +123 -0
- package/scripts/lib/stale-temp.mjs +44 -0
- package/scripts/native-release-status.mjs +2 -2
- package/scripts/post-release-check.mjs +1 -6
- package/scripts/pptx-kit-index.mjs +17 -4
- package/scripts/recall-history-cases.json +1 -1
- package/scripts/suite-health.mjs +20 -2
- package/scripts/test-data-dir.mjs +10 -7
- package/scripts/test.mjs +7 -11
- package/src/agents/reviewer/AGENT.md +7 -11
- package/src/agents/reviewer/agent.json +1 -1
- package/src/app.mjs +4 -0
- package/src/defaults/shipped-history.json +22 -1
- package/src/defaults/skills/browser-use/SKILL.md +62 -12
- package/src/defaults/skills/code-tidy/SKILL.md +142 -270
- package/src/defaults/skills/code-tidy/references/agent-cleanup.md +4 -4
- package/src/defaults/skills/code-tidy/references/cleanup-report.md +31 -76
- package/src/defaults/skills/code-tidy/references/dead-code.md +6 -12
- package/src/defaults/skills/computer-use/SKILL.md +35 -9
- package/src/defaults/skills/setup/SKILL.md +22 -0
- package/src/defaults/skills/setup/references/actions.md +39 -5
- package/src/defaults/skills/setup/references/surfaces.md +11 -2
- package/src/defaults/skills/visualize/SKILL.md +2 -1
- package/src/lib/rules-builder.cjs +39 -20
- package/src/output-styles/common.md +2 -2
- package/src/output-styles/simple.md +1 -1
- package/src/rules/agent/AGENT.md +11 -10
- package/src/rules/lead/LEAD.md +23 -20
- package/src/rules/shared/05-tool-calls.md +5 -6
- package/src/rules/shared/10-tool-workflow.md +4 -0
- package/src/rules/shared/45-destructive.md +0 -2
- package/src/rules/shared/50-execution.md +2 -0
- package/src/rules/shared/60-verification.md +7 -5
- package/src/rules/shared/70-delivery.md +7 -9
- package/src/runtime/agent/orchestrator/agent-runtime/agent-progress-watchdog.mjs +29 -6
- package/src/runtime/agent/orchestrator/agent-runtime/cache-strategy.mjs +2 -3
- package/src/runtime/agent/orchestrator/agent-runtime/title-completion.mjs +3 -1
- package/src/runtime/agent/orchestrator/agent-trace-format.mjs +3 -1
- package/src/runtime/agent/orchestrator/cache-break-trace.mjs +2 -0
- package/src/runtime/agent/orchestrator/config.mjs +17 -1
- package/src/runtime/agent/orchestrator/context/collect.mjs +3 -0
- package/src/runtime/agent/orchestrator/context/deferred-tools.mjs +73 -68
- package/src/runtime/agent/orchestrator/context/skill-catalog.mjs +57 -2
- package/src/runtime/agent/orchestrator/mcp/agent-connectors.mjs +3 -3
- package/src/runtime/agent/orchestrator/mcp/client-transport.mjs +176 -72
- package/src/runtime/agent/orchestrator/mcp/client.mjs +19 -0
- package/src/runtime/agent/orchestrator/mcp/oauth-login.mjs +5 -2
- package/src/runtime/agent/orchestrator/providers/account-pool.mjs +192 -133
- package/src/runtime/agent/orchestrator/providers/admission-scheduler.mjs +42 -6
- package/src/runtime/agent/orchestrator/providers/anthropic-api-recovery.mjs +1 -1
- package/src/runtime/agent/orchestrator/providers/anthropic-api-request.mjs +7 -3
- package/src/runtime/agent/orchestrator/providers/anthropic-api-transport.mjs +8 -2
- package/src/runtime/agent/orchestrator/providers/anthropic-cache-warm.mjs +165 -0
- package/src/runtime/agent/orchestrator/providers/anthropic-max-tokens-recovery.mjs +6 -2
- package/src/runtime/agent/orchestrator/providers/anthropic-messages.mjs +2 -2
- package/src/runtime/agent/orchestrator/providers/anthropic-oauth-client-version.mjs +1 -1
- package/src/runtime/agent/orchestrator/providers/anthropic-oauth-credentials.mjs +4 -1
- package/src/runtime/agent/orchestrator/providers/anthropic-oauth-request/gzip-post.mjs +14 -14
- package/src/runtime/agent/orchestrator/providers/anthropic-oauth-request/initial-status.mjs +3 -1
- package/src/runtime/agent/orchestrator/providers/anthropic-oauth.mjs +159 -69
- package/src/runtime/agent/orchestrator/providers/anthropic-overload-fallback.mjs +25 -3
- package/src/runtime/agent/orchestrator/providers/anthropic-sse.mjs +70 -33
- package/src/runtime/agent/orchestrator/providers/anthropic-turn-result.mjs +2 -2
- package/src/runtime/agent/orchestrator/providers/anthropic.mjs +135 -87
- package/src/runtime/agent/orchestrator/providers/antigravity-transport.mjs +123 -102
- package/src/runtime/agent/orchestrator/providers/cache-warm-admission.mjs +90 -0
- package/src/runtime/agent/orchestrator/providers/command-code-oauth-login.mjs +1 -10
- package/src/runtime/agent/orchestrator/providers/cursor-wire-transport.mjs +25 -9
- package/src/runtime/agent/orchestrator/providers/cursor.mjs +25 -13
- package/src/runtime/agent/orchestrator/providers/devin-wire-transport.mjs +1 -1
- package/src/runtime/agent/orchestrator/providers/devin.mjs +3 -0
- package/src/runtime/agent/orchestrator/providers/grok-oauth-login.mjs +210 -20
- package/src/runtime/agent/orchestrator/providers/lib/anthropic-prefix-trace.mjs +76 -11
- package/src/runtime/agent/orchestrator/providers/lib/anthropic-request-utils.mjs +45 -6
- package/src/runtime/agent/orchestrator/providers/lib/first-output-window.mjs +58 -0
- package/src/runtime/agent/orchestrator/providers/lib/listen-on.mjs +14 -0
- package/src/runtime/agent/orchestrator/providers/lib/oauth-token-utils.mjs +17 -3
- package/src/runtime/agent/orchestrator/providers/media-normalization.mjs +52 -10
- package/src/runtime/agent/orchestrator/providers/mimo-oauth-login.mjs +1 -10
- package/src/runtime/agent/orchestrator/providers/model-proxy-channel.d.mts +8 -1
- package/src/runtime/agent/orchestrator/providers/model-proxy-channel.mjs +1 -0
- package/src/runtime/agent/orchestrator/providers/npm-cli-version.mjs +11 -3
- package/src/runtime/agent/orchestrator/providers/oauth-credential-probes.mjs +2 -1
- package/src/runtime/agent/orchestrator/providers/openai-compat-chat-send.mjs +1 -0
- package/src/runtime/agent/orchestrator/providers/openai-compat-chat-stream/chunk-events.mjs +9 -2
- package/src/runtime/agent/orchestrator/providers/openai-compat-chat-stream/stream-state.mjs +7 -0
- package/src/runtime/agent/orchestrator/providers/openai-compat-chat-stream.mjs +14 -3
- package/src/runtime/agent/orchestrator/providers/openai-compat-responses-events.mjs +4 -2
- package/src/runtime/agent/orchestrator/providers/openai-compat-responses.mjs +1 -0
- package/src/runtime/agent/orchestrator/providers/openai-compat-stream-common.mjs +5 -3
- package/src/runtime/agent/orchestrator/providers/openai-compat-stream.mjs +16 -3
- package/src/runtime/agent/orchestrator/providers/openai-compat-xai-send.mjs +1 -0
- package/src/runtime/agent/orchestrator/providers/openai-http-sse-response-state/response-events/output-items.mjs +5 -2
- package/src/runtime/agent/orchestrator/providers/openai-http-sse-response-state/response-events.mjs +13 -3
- package/src/runtime/agent/orchestrator/providers/openai-http-sse-response-state.mjs +13 -2
- package/src/runtime/agent/orchestrator/providers/openai-http-sse-watchdogs.mjs +81 -42
- package/src/runtime/agent/orchestrator/providers/openai-oauth-http-sse.mjs +8 -0
- package/src/runtime/agent/orchestrator/providers/openai-oauth-login.mjs +136 -5
- package/src/runtime/agent/orchestrator/providers/openai-oauth-tokens.mjs +10 -9
- package/src/runtime/agent/orchestrator/providers/openai-ws-send/send-context.mjs +22 -3
- package/src/runtime/agent/orchestrator/providers/openai-ws-send-attempts/attempt-context.mjs +43 -5
- package/src/runtime/agent/orchestrator/providers/openai-ws-send-attempts/handshake-failure.mjs +3 -1
- package/src/runtime/agent/orchestrator/providers/openai-ws-send-attempts/stream-failure.mjs +6 -3
- package/src/runtime/agent/orchestrator/providers/openai-ws-stream/event-switch.mjs +20 -12
- package/src/runtime/agent/orchestrator/providers/openai-ws-stream.mjs +7 -1
- package/src/runtime/agent/orchestrator/providers/openai-ws-watchdogs.mjs +48 -22
- package/src/runtime/agent/orchestrator/providers/provider-error-kind.mjs +65 -1
- package/src/runtime/agent/orchestrator/providers/quota-reading.mjs +38 -12
- package/src/runtime/agent/orchestrator/providers/registry.mjs +38 -0
- package/src/runtime/agent/orchestrator/providers/retry-executor.mjs +39 -7
- package/src/runtime/agent/orchestrator/providers/statusline-route-meta.mjs +6 -0
- package/src/runtime/agent/orchestrator/runtime-core/ask-user-tool-defs.mjs +49 -16
- package/src/runtime/agent/orchestrator/runtime-core/compaction-config-keys.mjs +62 -0
- package/src/runtime/agent/orchestrator/runtime-core/config-helpers.mjs +37 -16
- package/src/runtime/agent/orchestrator/runtime-core/deferred-tool-delta.mjs +100 -23
- package/src/runtime/agent/orchestrator/runtime-core/orchestration.mjs +7 -6
- package/src/runtime/agent/orchestrator/runtime-core/session-text.mjs +9 -3
- package/src/runtime/agent/orchestrator/runtime-core/tool-bridge.mjs +19 -4
- package/src/runtime/agent/orchestrator/runtime-core/tool-catalog.mjs +125 -30
- package/src/runtime/agent/orchestrator/runtime-core/tool-defs.mjs +3 -1
- package/src/runtime/agent/orchestrator/runtime-core/workflow.mjs +5 -2
- package/src/runtime/agent/orchestrator/session/cache/scoped-cache.mjs +1 -3
- package/src/runtime/agent/orchestrator/session/cache-warm.mjs +798 -0
- package/src/runtime/agent/orchestrator/session/catalog-db/schema.mjs +4 -1
- package/src/runtime/agent/orchestrator/session/compact/execution-tail.mjs +1 -8
- package/src/runtime/agent/orchestrator/session/compact/messages.mjs +23 -0
- package/src/runtime/agent/orchestrator/session/compact/observation-supersession.mjs +103 -23
- package/src/runtime/agent/orchestrator/session/compact/runner.mjs +280 -45
- package/src/runtime/agent/orchestrator/session/compaction-archive-capture.mjs +475 -58
- package/src/runtime/agent/orchestrator/session/compaction-archive.mjs +42 -17
- package/src/runtime/agent/orchestrator/session/context-utils.mjs +13 -1
- package/src/runtime/agent/orchestrator/session/conversation-import/claude.mjs +163 -22
- package/src/runtime/agent/orchestrator/session/conversation-import/codex.mjs +132 -17
- package/src/runtime/agent/orchestrator/session/conversation-import/convert.mjs +38 -7
- package/src/runtime/agent/orchestrator/session/conversation-import/imported-session.mjs +3 -0
- package/src/runtime/agent/orchestrator/session/conversation-import/turns.mjs +90 -22
- package/src/runtime/agent/orchestrator/session/eager-dispatch.mjs +4 -1
- package/src/runtime/agent/orchestrator/session/human-gate/gate.mjs +11 -5
- package/src/runtime/agent/orchestrator/session/human-gate/outbound-policy.mjs +0 -2
- package/src/runtime/agent/orchestrator/session/human-gate/requests.mjs +36 -4
- package/src/runtime/agent/orchestrator/session/human-gate/store.mjs +1 -1
- package/src/runtime/agent/orchestrator/session/loop/compact-policy.mjs +51 -25
- package/src/runtime/agent/orchestrator/session/loop/fresh-context.mjs +37 -3
- package/src/runtime/agent/orchestrator/session/loop/loop-state.mjs +3 -1
- package/src/runtime/agent/orchestrator/session/loop/no-tool-turn.mjs +1 -1
- package/src/runtime/agent/orchestrator/session/loop/pre-dispatch-deny.mjs +1 -1
- package/src/runtime/agent/orchestrator/session/loop/provider-tool-exclusions.mjs +13 -5
- package/src/runtime/agent/orchestrator/session/loop/route-fallback.mjs +31 -3
- package/src/runtime/agent/orchestrator/session/loop/send-phase.mjs +20 -3
- package/src/runtime/agent/orchestrator/session/loop/tool-exec/after-hook.mjs +2 -1
- package/src/runtime/agent/orchestrator/session/loop/tool-exec/before-hook.mjs +5 -4
- package/src/runtime/agent/orchestrator/session/loop/tool-exec/dispatch.mjs +16 -2
- package/src/runtime/agent/orchestrator/session/loop/tool-exec.mjs +16 -1
- package/src/runtime/agent/orchestrator/session/loop/tool-exposure-migration.mjs +49 -21
- package/src/runtime/agent/orchestrator/session/loop/tool-helpers.mjs +3 -10
- package/src/runtime/agent/orchestrator/session/loop/tool-phase.mjs +12 -3
- package/src/runtime/agent/orchestrator/session/manager/ask-queue.mjs +12 -2
- package/src/runtime/agent/orchestrator/session/manager/ask-session.mjs +19 -1
- package/src/runtime/agent/orchestrator/session/manager/ask-turn-execute.mjs +133 -90
- package/src/runtime/agent/orchestrator/session/manager/ask-turn-loop-options.mjs +3 -3
- package/src/runtime/agent/orchestrator/session/manager/ask-turn-settle.mjs +3 -1
- package/src/runtime/agent/orchestrator/session/manager/ask-turn-state.mjs +31 -4
- package/src/runtime/agent/orchestrator/session/manager/compaction-runner.mjs +30 -18
- package/src/runtime/agent/orchestrator/session/manager/owner-route-fallbacks.mjs +24 -14
- package/src/runtime/agent/orchestrator/session/manager/pending-message-entry.mjs +11 -2
- package/src/runtime/agent/orchestrator/session/manager/provider-replay.mjs +13 -0
- package/src/runtime/agent/orchestrator/session/manager/route-fallback-chain.mjs +130 -0
- package/src/runtime/agent/orchestrator/session/manager/rules-cache.mjs +27 -9
- package/src/runtime/agent/orchestrator/session/manager/runtime-liveness.mjs +81 -8
- package/src/runtime/agent/orchestrator/session/manager/session-close.mjs +2 -0
- package/src/runtime/agent/orchestrator/session/manager/session-lifecycle.mjs +27 -8
- package/src/runtime/agent/orchestrator/session/manager/session-prompt-composition.mjs +12 -5
- package/src/runtime/agent/orchestrator/session/manager/session-record.mjs +1 -1
- package/src/runtime/agent/orchestrator/session/manager/tool-resolution.mjs +9 -4
- package/src/runtime/agent/orchestrator/session/manager/turn-checkpoint-context.mjs +4 -0
- package/src/runtime/agent/orchestrator/session/manager/turn-checkpoint-journal.mjs +102 -15
- package/src/runtime/agent/orchestrator/session/manager/turn-checkpoint.mjs +21 -5
- package/src/runtime/agent/orchestrator/session/manager/turn-interruption.mjs +9 -0
- package/src/runtime/agent/orchestrator/session/pre-send-compact.mjs +24 -9
- package/src/runtime/agent/orchestrator/session/prompt-cache-idle-gaps.mjs +113 -0
- package/src/runtime/agent/orchestrator/session/prompt-cache-touch-store.mjs +150 -0
- package/src/runtime/agent/orchestrator/session/restart-prefix-parity.fixture.mjs +154 -0
- package/src/runtime/agent/orchestrator/session/routine-result.mjs +36 -9
- package/src/runtime/agent/orchestrator/session/sensitive-projection.mjs +100 -0
- package/src/runtime/agent/orchestrator/session/sensitive-values.mjs +257 -0
- package/src/runtime/agent/orchestrator/session/side-question-store.mjs +20 -7
- package/src/runtime/agent/orchestrator/session/side-question.mjs +18 -0
- package/src/runtime/agent/orchestrator/session/skill-update-notice.mjs +60 -0
- package/src/runtime/agent/orchestrator/session/store/live-state.mjs +6 -3
- package/src/runtime/agent/orchestrator/session/store/save-delta.mjs +9 -5
- package/src/runtime/agent/orchestrator/session/store/serialize.mjs +9 -8
- package/src/runtime/agent/orchestrator/session/store/session-delete.mjs +7 -0
- package/src/runtime/agent/orchestrator/session/store/session-persistence-events.mjs +29 -0
- package/src/runtime/agent/orchestrator/session/store-agent-worker-rows.mjs +8 -6
- package/src/runtime/agent/orchestrator/session/store-summary-catalog.mjs +16 -2
- package/src/runtime/agent/orchestrator/session/store-summary-fields.mjs +15 -0
- package/src/runtime/agent/orchestrator/session/store-summary-index.mjs +81 -22
- package/src/runtime/agent/orchestrator/session/store-summary-reader.mjs +92 -3
- package/src/runtime/agent/orchestrator/session/stored-attachment-history.mjs +32 -0
- package/src/runtime/agent/orchestrator/session/thinking-binding.mjs +108 -0
- package/src/runtime/agent/orchestrator/session/tool-batch/pre-dispatch.mjs +2 -2
- package/src/runtime/agent/orchestrator/session/tool-batch/record-outcome.mjs +2 -1
- package/src/runtime/agent/orchestrator/session/transcript-restore/restore.mjs +79 -3
- package/src/runtime/agent/orchestrator/session/transcript-restore/tool-result-status.mjs +9 -7
- package/src/runtime/agent/orchestrator/session/transcript-restore/tool-result-text.mjs +2 -2
- package/src/runtime/agent/orchestrator/session/turn-failure.mjs +5 -1
- package/src/runtime/agent/orchestrator/stall-policy.mjs +23 -0
- package/src/runtime/agent/orchestrator/tools/builtin/arg-guard.mjs +5 -2
- package/src/runtime/agent/orchestrator/tools/builtin/bash-tool/backgrounded-result.mjs +12 -1
- package/src/runtime/agent/orchestrator/tools/builtin/bash-tool/invocation-plan.mjs +2 -2
- package/src/runtime/agent/orchestrator/tools/builtin/bash-tool/misuse-guard.mjs +240 -0
- package/src/runtime/agent/orchestrator/tools/builtin/bash-tool/result-format.mjs +1 -1
- package/src/runtime/agent/orchestrator/tools/builtin/bash-tool/shell-cwd.mjs +38 -0
- package/src/runtime/agent/orchestrator/tools/builtin/bash-tool/spawn-env.mjs +4 -2
- package/src/runtime/agent/orchestrator/tools/builtin/bash-tool.mjs +31 -6
- package/src/runtime/agent/orchestrator/tools/builtin/builtin-tools.mjs +110 -26
- package/src/runtime/agent/orchestrator/tools/builtin/cache-layers.mjs +7 -1
- package/src/runtime/agent/orchestrator/tools/builtin/git-command-tool.mjs +31 -5
- package/src/runtime/agent/orchestrator/tools/builtin/git-worktree-tool.mjs +22 -8
- package/src/runtime/agent/orchestrator/tools/builtin/lib/scoped-search-flight.mjs +9 -5
- package/src/runtime/agent/orchestrator/tools/builtin/read-single-stream.mjs +7 -1
- package/src/runtime/agent/orchestrator/tools/builtin/read-streaming.mjs +6 -3
- package/src/runtime/agent/orchestrator/tools/builtin/read-tool/batch-dispatch.mjs +1 -1
- package/src/runtime/agent/orchestrator/tools/builtin/read-tool/batch-publication.mjs +23 -12
- package/src/runtime/agent/orchestrator/tools/builtin/read-tool/batch-snapshot.mjs +6 -1
- package/src/runtime/agent/orchestrator/tools/builtin/read-tool.mjs +1 -8
- package/src/runtime/agent/orchestrator/tools/builtin/shell-runtime.mjs +7 -1
- package/src/runtime/agent/orchestrator/tools/code-graph/dispatch/federation.mjs +3 -1
- package/src/runtime/agent/orchestrator/tools/code-graph-tool-defs.mjs +1 -1
- package/src/runtime/agent/orchestrator/tools/graph-manifest.json +13 -13
- package/src/runtime/agent/orchestrator/tools/lib/isolated-test-env.mjs +4 -3
- package/src/runtime/agent/orchestrator/tools/lib/shell-warm-standby.mjs +26 -4
- package/src/runtime/agent/orchestrator/tools/shell-command.mjs +36 -13
- package/src/runtime/browser-bridge/action-schema.mjs +13 -2
- package/src/runtime/browser-bridge/client.mjs +73 -8
- package/src/runtime/browser-bridge/input-fields.mjs +8 -9
- package/src/runtime/browser-bridge/mail-links.mjs +107 -0
- package/src/runtime/browser-bridge/tool-defs.mjs +1 -1
- package/src/runtime/channels/lib/config.mjs +6 -61
- package/src/runtime/channels/lib/crash-log.mjs +1 -1
- package/src/runtime/channels/lib/event-pipeline.mjs +4 -19
- package/src/runtime/channels/lib/event-queue.mjs +3 -18
- package/src/runtime/channels/lib/owned-runtime/automation-services.mjs +2 -2
- package/src/runtime/channels/lib/owned-runtime/automation-start.mjs +3 -4
- package/src/runtime/channels/lib/owned-runtime/config-reload.mjs +4 -59
- package/src/runtime/channels/lib/owned-runtime/owned-start.mjs +33 -115
- package/src/runtime/channels/lib/owned-runtime/owned-stop.mjs +5 -22
- package/src/runtime/channels/lib/owned-runtime/state.mjs +2 -8
- package/src/runtime/channels/lib/owned-runtime.mjs +6 -23
- package/src/runtime/channels/lib/parent-bridge.mjs +2 -2
- package/src/runtime/channels/lib/runtime-paths.mjs +4 -43
- package/src/runtime/channels/lib/scheduler.mjs +33 -147
- package/src/runtime/channels/lib/schedules-db.mjs +34 -11
- package/src/runtime/channels/lib/status-snapshot.mjs +1 -1
- package/src/runtime/channels/lib/tool-dispatch.mjs +1 -2
- package/src/runtime/channels/lib/webhook/signature.mjs +4 -1
- package/src/runtime/channels/lib/webhooks-db.mjs +5 -9
- package/src/runtime/channels/lib/whisper-port.mjs +10 -1
- package/src/runtime/channels/lib/worker-bootstrap.mjs +0 -1
- package/src/runtime/channels/lib/worker-ipc.mjs +0 -1
- package/src/runtime/channels/lib/worker-main.mjs +10 -34
- package/src/runtime/computer-bridge/action-schema.mjs +42 -1
- package/src/runtime/computer-bridge/actions.mjs +9 -1
- package/src/runtime/computer-bridge/client.mjs +180 -8
- package/src/runtime/computer-bridge/core-actions.mjs +2 -2
- package/src/runtime/computer-bridge/error-recovery.mjs +28 -2
- package/src/runtime/computer-bridge/linux-action-capabilities.mjs +24 -5
- package/src/runtime/computer-bridge/linux-readiness.d.mts +7 -2
- package/src/runtime/computer-bridge/linux-readiness.mjs +65 -23
- package/src/runtime/computer-bridge/pending-continuation.mjs +1 -1
- package/src/runtime/connectors/connector-accounts-mcp.mjs +230 -0
- package/src/runtime/connectors/gmail-app-password.mjs +172 -39
- package/src/runtime/connectors/google-mcp.mjs +10 -12
- package/src/runtime/local-provider/server-process.mjs +2 -1
- package/src/runtime/media/adapters/gemini-video.mjs +4 -1
- package/src/runtime/media/adapters/xai-media.mjs +2 -1
- package/src/runtime/memory/lib/embedding-worker.mjs +6 -3
- package/src/runtime/memory/lib/memory-action-handlers/cycle-backfill-action.mjs +1 -7
- package/src/runtime/memory/lib/memory-chunk-quality.mjs +1 -6
- package/src/runtime/memory/lib/pg/adapter.mjs +77 -70
- package/src/runtime/memory/lib/pg/supervisor.mjs +24 -12
- package/src/runtime/memory/lib/runtime-fetcher.mjs +23 -5
- package/src/runtime/office/authoring/pdf-author-action.mjs +1 -6
- package/src/runtime/office/design/design-art-direction.mjs +29 -27
- package/src/runtime/office/pdf/pdf-writer.mjs +6 -1
- package/src/runtime/office/portable/portable-snapshot-docx.mjs +7 -1
- package/src/runtime/office/portable/portable-snapshot-shared.mjs +6 -1
- package/src/runtime/office/quality/assurance-rendered.mjs +9 -2
- package/src/runtime/office/quality/assurance-structure-docx.mjs +3 -1
- package/src/runtime/office/quality/design-review.mjs +1 -5
- package/src/runtime/office/quality/document-brief.mjs +1 -5
- package/src/runtime/office/quality/forced-break-pages.mjs +9 -6
- package/src/runtime/office/quality/quality-pipeline.mjs +1 -1
- package/src/runtime/session-search/session-search-db.mjs +73 -9
- package/src/runtime/session-search/session-search-ingest.mjs +110 -20
- package/src/runtime/shared/abort-race.mjs +89 -0
- package/src/runtime/shared/agent-avatar.d.mts +0 -1
- package/src/runtime/shared/agent-kind.mjs +4 -3
- package/src/runtime/shared/bot-owned-work.mjs +56 -0
- package/src/runtime/shared/bounded-download.mjs +8 -3
- package/src/runtime/shared/config.mjs +2 -13
- package/src/runtime/shared/connector-accounts.mjs +20 -10
- package/src/runtime/shared/connector-catalog.d.mts +29 -0
- package/src/runtime/shared/connector-catalog.mjs +150 -0
- package/src/runtime/shared/connector-triggers.mjs +22 -19
- package/src/runtime/shared/defaults-separation.mjs +2 -17
- package/src/runtime/shared/delegated-approval-host.mjs +103 -11
- package/src/runtime/shared/err-text.mjs +24 -15
- package/src/runtime/shared/fetch-safe-listen.d.mts +16 -0
- package/src/runtime/shared/fetch-safe-listen.mjs +50 -0
- package/src/runtime/shared/file-permissions.d.mts +0 -8
- package/src/runtime/shared/file-permissions.mjs +0 -11
- package/src/runtime/shared/git-direct.test-support.mjs +19 -0
- package/src/runtime/shared/handoff-artifacts.d.mts +2 -1
- package/src/runtime/shared/handoff-artifacts.mjs +18 -8
- package/src/runtime/shared/heartbeat-interval.d.mts +4 -0
- package/src/runtime/shared/heartbeat-interval.mjs +19 -0
- package/src/runtime/shared/idle-auto-clear-gate.mjs +126 -0
- package/src/runtime/shared/llm/cost.mjs +5 -1
- package/src/runtime/shared/llm/usage-rollup.mjs +1 -1
- package/src/runtime/shared/loopback-listener.mjs +25 -2
- package/src/runtime/shared/mixy-presence.mjs +22 -0
- package/src/runtime/shared/model-route-chain.d.mts +7 -2
- package/src/runtime/shared/model-route-chain.mjs +22 -4
- package/src/runtime/shared/open-url.mjs +10 -0
- package/src/runtime/shared/orchestration.d.mts +2 -0
- package/src/runtime/shared/pg-schema-db.mjs +27 -16
- package/src/runtime/shared/profile-config.cjs +1 -1
- package/src/runtime/shared/prompt-origin.d.mts +4 -0
- package/src/runtime/shared/prompt-origin.mjs +21 -0
- package/src/runtime/shared/provider-accounts.mjs +7 -1
- package/src/runtime/shared/provider-display-name.mjs +29 -0
- package/src/runtime/shared/provider-timeout-phase.mjs +2 -0
- package/src/runtime/shared/routine-trigger.d.mts +2 -0
- package/src/runtime/shared/routine-trigger.mjs +10 -0
- package/src/runtime/shared/sensitive-path-rules.d.mts +2 -10
- package/src/runtime/shared/sensitive-path-rules.mjs +19 -41
- package/src/runtime/shared/session-activity-signal.mjs +48 -1
- package/src/runtime/shared/session-scratch.mjs +37 -0
- package/src/runtime/shared/session-sidebar-host.d.mts +1 -1
- package/src/runtime/shared/session-sidebar-host.mjs +7 -3
- package/src/runtime/shared/session-turn-prelude.mjs +18 -0
- package/src/runtime/shared/tool-card-model/agent-surface.mjs +1 -3
- package/src/runtime/shared/tool-card-model/card-status.mjs +15 -2
- package/src/runtime/shared/tool-result-summary.mjs +2 -15
- package/src/runtime/shared/tool-surface.mjs +3 -0
- package/src/runtime/shared/tool-work-units.mjs +2 -0
- package/src/runtime/shared/turn-snapshot-store.mjs +13 -6
- package/src/runtime/shared/worktrees/contract.mjs +4 -1
- package/src/runtime/shared/worktrees/git.mjs +15 -0
- package/src/runtime/shared/worktrees/manager.mjs +87 -38
- package/src/runtime/shared/worktrees/test-support.mjs +14 -13
- package/src/runtime/terminal-bridge/client.mjs +10 -1
- package/src/runtime/visualize-bridge/client.mjs +29 -8
- package/src/runtime/visualize-bridge/tool-defs.mjs +22 -8
- package/src/runtime/visualize-bridge/tool-meta.d.mts +5 -0
- package/src/runtime/visualize-bridge/tool-meta.mjs +22 -0
- package/src/runtime/web-search/lib/fetch-pipeline.mjs +2 -2
- package/src/session-runtime/agent-home-delivery.mjs +489 -185
- package/src/session-runtime/agent-task-sessions.mjs +467 -229
- package/src/session-runtime/agent-tool-routing.mjs +8 -41
- package/src/session-runtime/boot/apis.mjs +9 -0
- package/src/session-runtime/boot/infrastructure.mjs +4 -0
- package/src/session-runtime/boot/providers.mjs +3 -0
- package/src/session-runtime/boot/tools.mjs +11 -2
- package/src/session-runtime/bot-status.mjs +12 -6
- package/src/session-runtime/bot-target.mjs +38 -0
- package/src/session-runtime/channel-config-api.mjs +6 -1
- package/src/session-runtime/config-lifecycle.mjs +6 -0
- package/src/session-runtime/connector-merge-migration.mjs +305 -0
- package/src/session-runtime/conversation-import/codex-titles.mjs +88 -0
- package/src/session-runtime/conversation-import/discovery.mjs +271 -25
- package/src/session-runtime/conversation-import/service.mjs +26 -5
- package/src/session-runtime/handoff-attachments.mjs +82 -31
- package/src/session-runtime/internal-tool-executor/ask-user-tool.mjs +144 -41
- package/src/session-runtime/internal-tool-executor/bridge-tools.mjs +3 -0
- package/src/session-runtime/internal-tool-executor/reaction-tool.mjs +4 -1
- package/src/session-runtime/internal-tool-executor/workspace-tools.mjs +8 -2
- package/src/session-runtime/lifecycle/cold-heir.mjs +11 -6
- package/src/session-runtime/lifecycle/inheritance.mjs +16 -3
- package/src/session-runtime/lifecycle/session-catalog.mjs +12 -2
- package/src/session-runtime/lifecycle/teardown/runtime-stops.mjs +50 -4
- package/src/session-runtime/lifecycle/teardown.mjs +23 -5
- package/src/session-runtime/mcp-glue/server-catalog.mjs +55 -3
- package/src/session-runtime/mcp-oauth-login.mjs +5 -3
- package/src/session-runtime/mixy-heartbeat.mjs +153 -33
- package/src/session-runtime/model-route/set-route.mjs +11 -2
- package/src/session-runtime/notification-bus.mjs +31 -117
- package/src/session-runtime/notification-completion-queue.mjs +54 -0
- package/src/session-runtime/notification-listeners.mjs +76 -0
- package/src/session-runtime/owned-automation-run.mjs +2 -1
- package/src/session-runtime/prewarm/channel-start.mjs +3 -7
- package/src/session-runtime/provider-catalog-cache.mjs +82 -36
- package/src/session-runtime/provider-models/catalog-load.mjs +23 -6
- package/src/session-runtime/provider-models.mjs +4 -2
- package/src/session-runtime/resource-mcp-api.mjs +143 -224
- package/src/session-runtime/resource-mcp-connector-accounts.mjs +163 -0
- package/src/session-runtime/resource-mcp-oauth-api.mjs +217 -0
- package/src/session-runtime/resource-mcp-secrets.mjs +82 -0
- package/src/session-runtime/resource-mcp-servers.mjs +78 -0
- package/src/session-runtime/resource-mcp-store.mjs +49 -0
- package/src/session-runtime/resource-recall-api.mjs +6 -1
- package/src/session-runtime/routine-owner.mjs +40 -9
- package/src/session-runtime/runtime-feature-gates.mjs +5 -1
- package/src/session-runtime/services/agent-tool/close-flow.mjs +3 -0
- package/src/session-runtime/services/agent-tool/execute.mjs +14 -9
- package/src/session-runtime/services/agent-tool/job-views/agent-list.mjs +20 -4
- package/src/session-runtime/services/agent-tool/model-select.mjs +48 -7
- package/src/session-runtime/services/agent-tool/render.mjs +71 -17
- package/src/session-runtime/services/agent-tool/spawn-preset.mjs +13 -4
- package/src/session-runtime/services/agent-tool/tag-registry.mjs +1 -0
- package/src/session-runtime/services/agent-tool/tool-def.mjs +9 -9
- package/src/session-runtime/services/agent-tool/worker-index/awaiting-user-sync.mjs +51 -0
- package/src/session-runtime/services/agent-tool/worker-index/disposal.mjs +55 -0
- package/src/session-runtime/services/agent-tool/worker-index/released-store.mjs +123 -0
- package/src/session-runtime/services/agent-tool/worker-index/restart-settlement.mjs +112 -0
- package/src/session-runtime/services/agent-tool/worker-index/row-reads.mjs +36 -0
- package/src/session-runtime/services/agent-tool/worker-index/row-shape.mjs +2 -0
- package/src/session-runtime/services/agent-tool/worker-index/row-store.mjs +17 -4
- package/src/session-runtime/services/agent-tool/worker-index/row-writes.mjs +58 -0
- package/src/session-runtime/services/agent-tool/worker-index.mjs +41 -241
- package/src/session-runtime/services/agent-tool/worker-rows.mjs +3 -0
- package/src/session-runtime/services/channel-admin.mjs +7 -13
- package/src/session-runtime/services/channel-session-router.mjs +4 -2
- package/src/session-runtime/services/provider-admin.mjs +2 -1
- package/src/session-runtime/services/seeds.mjs +0 -28
- package/src/session-runtime/session-compaction-sync.mjs +27 -0
- package/src/session-runtime/session-create/session-options.mjs +1 -0
- package/src/session-runtime/settings-builtin-tools-api.mjs +11 -0
- package/src/session-runtime/settings-compaction-api.mjs +2 -40
- package/src/session-runtime/settings-system-api.mjs +13 -1
- package/src/session-runtime/setup-tool/agent-settings-actions.mjs +65 -0
- package/src/session-runtime/setup-tool/executor.mjs +145 -22
- package/src/session-runtime/setup-tool/extended-actions.mjs +8 -3
- package/src/session-runtime/setup-tool/session-route-actions.mjs +152 -0
- package/src/session-runtime/setup-tool/settings-contract.mjs +4 -3
- package/src/session-runtime/setup-tool/tool-defs.mjs +64 -5
- package/src/session-runtime/tool-policy/agent-profile-fold.mjs +17 -13
- package/src/session-runtime/tool-policy/policy-stage.mjs +17 -2
- package/src/session-runtime/turn/turn-run/ask-callbacks.mjs +1 -0
- package/src/session-runtime/workflow-agents-api/agent-editor.mjs +5 -1
- package/src/session-runtime/workflow-agents-api/agent-route.mjs +15 -4
- package/src/session-runtime/workflow-agents-api/onboarding.mjs +3 -3
- package/src/session-runtime/workflow-agents-api/style-and-mode.mjs +4 -0
- package/src/session-runtime/workspace/transitions.mjs +25 -0
- package/src/session-runtime/workspace/worktree-tool.mjs +26 -9
- package/src/standalone/channel-transport/client-registry/client-records.mjs +65 -0
- package/src/standalone/channel-transport/client-registry/client-registration.mjs +118 -0
- package/src/standalone/channel-transport/client-registry.mjs +24 -163
- package/src/standalone/channel-transport.mjs +1 -1
- package/src/standalone/daemon-channels-loader.mjs +10 -8
- package/src/standalone/daemon-session-control/list-page.mjs +72 -0
- package/src/standalone/daemon-session-control/request.mjs +51 -0
- package/src/standalone/daemon-session-control/room-host.mjs +57 -0
- package/src/standalone/daemon-session-control/route-control.mjs +48 -0
- package/src/standalone/daemon-session-control/send-turn.mjs +60 -0
- package/src/standalone/daemon-session-control/worktree-control.mjs +40 -0
- package/src/standalone/daemon-session-control.mjs +142 -190
- package/src/standalone/daemon.mjs +26 -3
- package/src/standalone/session-client.mjs +7 -3
- package/src/standalone/session-protocol.mjs +5 -0
- package/src/standalone/session-service/agent-tree/agent-child.mjs +1 -0
- package/src/standalone/session-service/agent-tree/agent-turns.mjs +16 -12
- package/src/standalone/session-service/quota-resume.mjs +3 -0
- package/src/standalone/session-service/retention/busy.mjs +65 -1
- package/src/standalone/session-service/session-calls/session-action.mjs +30 -2
- package/src/standalone/session-service/session-calls/session-views.mjs +8 -0
- package/src/standalone/session-tool-output.mjs +15 -7
- package/src/standalone/session-transport/client-registry/client-grace.mjs +12 -9
- package/src/tui/app/app-view/floating-panel.jsx +7 -4
- package/src/tui/app/model-options.mjs +3 -28
- package/src/tui/app/model-picker/route-selection/fast-parameters.mjs +1 -3
- package/src/tui/app/model-picker/route-selection.mjs +5 -6
- package/src/tui/app/provider-setup/oauth-actions.mjs +38 -20
- package/src/tui/app/provider-setup/provider-items.mjs +8 -2
- package/src/tui/app/provider-setup-picker.mjs +9 -5
- package/src/tui/components/QuestionPanel.jsx +13 -4
- package/src/tui/dist/index.mjs +133 -92
- package/src/tui/session/agent-job-feed/model-notification.mjs +3 -0
- package/src/tui/session/initial-state.mjs +3 -0
- package/src/tui/session/notice-surface.mjs +7 -1
- package/src/tui/session/queue-helpers.mjs +3 -42
- package/src/tui/session/session-api/routes-agent-definitions.mjs +14 -2
- package/src/tui/session/session-api/routes-write.mjs +5 -1
- package/src/tui/session/session-api/routes.mjs +1 -0
- package/src/tui/session/session-api/settings.mjs +17 -1
- package/src/tui/session/session-flow/auto-clear.mjs +4 -33
- package/src/tui/session/session-flow/drain-closed-queue.mjs +38 -0
- package/src/tui/session/session-flow/drain-enqueue.mjs +86 -0
- package/src/tui/session/session-flow/drain-retry.mjs +35 -0
- package/src/tui/session/session-flow/drain.mjs +118 -149
- package/src/tui/session/session-flow/queue/entry-shape.mjs +5 -0
- package/src/tui/session/session-flow/queue/take-entries.mjs +16 -2
- package/src/tui/session/tool-approval-delegation.mjs +64 -0
- package/src/tui/session/tool-approval-request.mjs +39 -0
- package/src/tui/session/tool-approval-settle.mjs +62 -0
- package/src/tui/session/tool-approval.mjs +69 -115
- package/src/tui/session/tui-steering-persist.mjs +8 -1
- package/src/tui/session/turn-stream.mjs +19 -2
- package/src/tui/session/turn.mjs +1 -0
- package/src/ui/statusline-agents.mjs +1 -3
- package/src/vendor/statusline/bin/statusline-route.mjs +1 -219
- package/src/vendor/statusline/src/gateway/route-meta.mjs +35 -13
- package/scripts/code-graph-description-contract.mjs +0 -188
- package/src/runtime/channels/lib/owned-runtime/ownership-refresh.mjs +0 -36
|
@@ -1,30 +1,33 @@
|
|
|
1
1
|
// Curated selection triggers for catalog connectors (MCP servers). A catalog
|
|
2
2
|
// entry carries its line as `trigger`; the deferred-tool manifest lists each
|
|
3
3
|
// connected server once as `- mcp__<server id>: <trigger>`, so one line must
|
|
4
|
-
// say
|
|
5
|
-
//
|
|
4
|
+
// say WHEN to call the server (the user requests it covers, plus a close
|
|
5
|
+
// negative where useful), not list its capabilities; plain ASCII English, at
|
|
6
|
+
// most CONNECTOR_TRIGGER_MAX_CHARS characters.
|
|
6
7
|
export const CONNECTOR_TRIGGER_MAX_CHARS = 100;
|
|
7
8
|
|
|
8
9
|
export const CONNECTOR_TRIGGERS = Object.freeze({
|
|
9
10
|
// Not a catalog connector (Mixdog uses the signed-in GitHub CLI), but a
|
|
10
11
|
// user-added `github` MCP server still gets this line.
|
|
11
|
-
github: 'GitHub
|
|
12
|
-
notion: 'Notion pages, databases and
|
|
13
|
-
linear: '
|
|
14
|
-
sentry: '
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
12
|
+
github: 'GitHub issues, pull requests, Actions runs and repo code search; not local Git history.',
|
|
13
|
+
notion: 'Finding, reading or updating Notion pages, databases and team notes.',
|
|
14
|
+
linear: 'Looking up, creating or triaging Linear issues, projects and cycles.',
|
|
15
|
+
sentry: 'Investigating production errors, stack traces and releases in Sentry.',
|
|
16
|
+
// The line never names an account: that arrives with the server's
|
|
17
|
+
// instructions when its tools are loaded.
|
|
18
|
+
gmail: 'Gmail email: sending, reading, searching or organizing mail.',
|
|
19
|
+
'google-calendar': 'Google Calendar: checking, creating or editing events.',
|
|
20
|
+
'google-drive': 'Google Drive: finding, reading or creating files Mixdog made or was given; not local files.',
|
|
21
|
+
jira: 'Looking up, creating or transitioning Jira tickets, sprints and JQL searches.',
|
|
22
|
+
confluence: 'Searching, reading or editing Confluence wiki pages; not Jira tickets.',
|
|
23
|
+
dropbox: 'Finding, reading, uploading or sharing Dropbox files; not local files.',
|
|
24
|
+
trello: 'Viewing, creating or moving Trello boards, lists and cards.',
|
|
25
|
+
airtable: 'Querying or updating Airtable bases, tables and records.',
|
|
26
|
+
stripe: 'Checking or acting on Stripe payments, invoices, subscriptions and refunds.',
|
|
27
|
+
vercel: 'Checking Vercel deployments, domains, logs and build errors.',
|
|
28
|
+
supabase: 'Working with Supabase Postgres tables, SQL, auth, storage or edge functions.',
|
|
29
|
+
postgres: 'Running SQL or inspecting schemas on a connected PostgreSQL database; not local files.',
|
|
30
|
+
'hugging-face': 'Finding Hugging Face Hub models, datasets, Spaces or papers; not local GGUF setup.',
|
|
28
31
|
});
|
|
29
32
|
|
|
30
33
|
// Server ids differ only in separators and case (google_calendar, huggingface).
|
|
@@ -40,13 +40,12 @@ const AGENT_SECTION_DEFAULTS = Object.freeze({
|
|
|
40
40
|
const MODULE_DEFAULTS = Object.freeze({ enabled: true });
|
|
41
41
|
|
|
42
42
|
export const CHANNELS_DEFAULTS = Object.freeze({
|
|
43
|
-
access: Object.freeze({ dmPolicy: 'allowlist', allowFrom: [], channels: {} }),
|
|
44
43
|
webhook: Object.freeze({ enabled: true, port: 3333 }),
|
|
45
44
|
});
|
|
46
45
|
|
|
47
46
|
// Disabled-agent roster shipped before `advisor` joined the defaults
|
|
48
47
|
// (worker … writer plus the Maintainer). Stored full lists are read against it.
|
|
49
|
-
|
|
48
|
+
const PREVIOUS_DISABLED_AGENT_ROSTER = Object.freeze([
|
|
50
49
|
'front-worker',
|
|
51
50
|
'heavy-worker',
|
|
52
51
|
'maintainer',
|
|
@@ -168,17 +167,6 @@ function removeLegacyKeys(root, dataDir) {
|
|
|
168
167
|
if (isPlainObject(agent.agents)) agent.agents = withoutRetiredAgentRoutes(agent.agents, dataDir);
|
|
169
168
|
next.agent = agent;
|
|
170
169
|
}
|
|
171
|
-
const channelEntries = next.channels?.access?.channels;
|
|
172
|
-
if (isPlainObject(channelEntries)) {
|
|
173
|
-
const channels = Object.fromEntries(
|
|
174
|
-
Object.entries(channelEntries).map(([id, entry]) => {
|
|
175
|
-
if (!isPlainObject(entry)) return [id, entry];
|
|
176
|
-
const { requireMention: _retired, ...rest } = entry;
|
|
177
|
-
return [id, rest];
|
|
178
|
-
})
|
|
179
|
-
);
|
|
180
|
-
next.channels = { ...next.channels, access: { ...next.channels.access, channels } };
|
|
181
|
-
}
|
|
182
170
|
return next;
|
|
183
171
|
}
|
|
184
172
|
|
|
@@ -188,10 +176,7 @@ function removeLegacyKeys(root, dataDir) {
|
|
|
188
176
|
* not set the marker. `stripRouteDefaults` drops shipped presets and
|
|
189
177
|
* maintenance routes (their defaults live with the agent config).
|
|
190
178
|
*/
|
|
191
|
-
|
|
192
|
-
root,
|
|
193
|
-
{ stripRouteDefaults = (agent) => agent, dataDir = resolvePluginData() } = {}
|
|
194
|
-
) {
|
|
179
|
+
function separateDefaultsInConfig(root, { stripRouteDefaults = (agent) => agent, dataDir = resolvePluginData() } = {}) {
|
|
195
180
|
const from = Number(root?.[DEFAULTS_VERSION_KEY]) || 0;
|
|
196
181
|
let next = { ...(root || {}) };
|
|
197
182
|
if (from < 1) {
|
|
@@ -6,14 +6,86 @@
|
|
|
6
6
|
// owner, and the copy is withdrawn as soon as the owner settles (answered in
|
|
7
7
|
// either place, timed out, its turn ended). Like held-request-host.mjs this
|
|
8
8
|
// registry is process-wide and dependency-free.
|
|
9
|
+
// Hand-offs take the same path: a session chats handed work to (`session`
|
|
10
|
+
// send / create from a bot conversation, agent-task-sessions.mjs) shows its
|
|
11
|
+
// own approvals in EVERY chat whose hand-off to it still owes its report, one
|
|
12
|
+
// copy per chat, all settling the one request. The report watch store
|
|
13
|
+
// (session-runtime/agent-home-delivery.mjs) notes and ends hand-offs as its
|
|
14
|
+
// watches come and go, whatever removes them; a chat
|
|
15
|
+
// whose last hand-off to the session ends loses its copy, the others keep
|
|
16
|
+
// theirs.
|
|
9
17
|
|
|
10
18
|
const KEY = Symbol.for('mixdog.delegatedApprovalHost.v1');
|
|
11
19
|
|
|
12
20
|
function registry() {
|
|
13
|
-
globalThis[KEY] ??= { queues: new Set(), pending: new Map(), opener: null };
|
|
21
|
+
globalThis[KEY] ??= { queues: new Set(), pending: new Map(), opener: null, handoffs: new Map(), ended: new Set() };
|
|
14
22
|
return globalThis[KEY];
|
|
15
23
|
}
|
|
16
24
|
|
|
25
|
+
/** The chats with a live hand-off to `sessionId`, each once (in noting order)
|
|
26
|
+
* with the name its latest hand-off gave the session: Map chat → `{ title }`. */
|
|
27
|
+
export function handoffChatsOf(sessionId) {
|
|
28
|
+
const chats = new Map();
|
|
29
|
+
for (const handoff of registry().handoffs.values()) {
|
|
30
|
+
if (handoff.sessionId !== sessionId) continue;
|
|
31
|
+
chats.delete(handoff.surfaceSessionId);
|
|
32
|
+
chats.set(handoff.surfaceSessionId, { title: handoff.title });
|
|
33
|
+
}
|
|
34
|
+
return chats;
|
|
35
|
+
}
|
|
36
|
+
|
|
37
|
+
/** Bring each pending request of `sessionId` that hand-offs reach to exactly
|
|
38
|
+
* the chats now handing it work: a new chat gets its copy, a chat whose
|
|
39
|
+
* hand-offs all ended loses only its own. */
|
|
40
|
+
function syncHandoffCopies(sessionId) {
|
|
41
|
+
const { pending } = registry();
|
|
42
|
+
const chats = handoffChatsOf(sessionId);
|
|
43
|
+
for (const asked of pending.values()) {
|
|
44
|
+
if (!asked.copies || asked.sessionId !== sessionId) continue;
|
|
45
|
+
for (const [chat, copy] of asked.copies) {
|
|
46
|
+
if (chats.has(chat)) continue;
|
|
47
|
+
asked.copies.delete(chat);
|
|
48
|
+
for (const queue of liveQueuesOf(chat)) queue.withdraw(copy.id, null);
|
|
49
|
+
}
|
|
50
|
+
for (const [chat, handoff] of chats) {
|
|
51
|
+
if (asked.copies.has(chat)) continue;
|
|
52
|
+
const copy = { id: asked.id, sessionId: chat, request: asked.copyFor(chat, handoff), answer: asked.answer };
|
|
53
|
+
asked.copies.set(chat, copy);
|
|
54
|
+
deliver(copy, () => pending.get(asked.id) === asked && asked.copies.get(chat) === copy);
|
|
55
|
+
}
|
|
56
|
+
}
|
|
57
|
+
}
|
|
58
|
+
|
|
59
|
+
// Run ids of hand-offs that ended: never noted again (a watch read before it
|
|
60
|
+
// was removed must not bring it back, however many hand-offs ended since).
|
|
61
|
+
// Run ids are never reused, so an ended id is final for the life of the
|
|
62
|
+
// process and there is nothing to age out: no count decides what is
|
|
63
|
+
// forgotten. One id string is kept per ended hand-off.
|
|
64
|
+
|
|
65
|
+
/** Chat `homeSessionId` handed session `taskSessionId` work (`title`: the
|
|
66
|
+
* target's name in that chat) under report watch `runId`. */
|
|
67
|
+
export function noteHandoff({ runId, taskSessionId, homeSessionId, title }) {
|
|
68
|
+
if (!runId || !taskSessionId || !homeSessionId || taskSessionId === homeSessionId) return;
|
|
69
|
+
if (registry().ended.has(String(runId))) return;
|
|
70
|
+
registry().handoffs.set(String(runId), {
|
|
71
|
+
sessionId: String(taskSessionId),
|
|
72
|
+
surfaceSessionId: String(homeSessionId),
|
|
73
|
+
title: String(title || ''),
|
|
74
|
+
});
|
|
75
|
+
syncHandoffCopies(String(taskSessionId));
|
|
76
|
+
}
|
|
77
|
+
|
|
78
|
+
/** The report watch `runId` is gone (its work reported back, was never
|
|
79
|
+
* taken, or the watch was retired): its hand-off ends for good. */
|
|
80
|
+
export function endHandoff(runId) {
|
|
81
|
+
const { handoffs, ended } = registry();
|
|
82
|
+
ended.add(String(runId));
|
|
83
|
+
const handoff = handoffs.get(String(runId));
|
|
84
|
+
if (!handoff) return;
|
|
85
|
+
handoffs.delete(String(runId));
|
|
86
|
+
syncHandoffCopies(handoff.sessionId);
|
|
87
|
+
}
|
|
88
|
+
|
|
17
89
|
/** `queue`: `{ sessionId() → string, disposed() → boolean, offer(delegated),
|
|
18
90
|
* withdraw(id, result) }` — one session runtime's approval queue. */
|
|
19
91
|
export function registerApprovalQueue(queue) {
|
|
@@ -46,20 +118,36 @@ function offerTo(sessionId, delegated) {
|
|
|
46
118
|
return live.length > 0;
|
|
47
119
|
}
|
|
48
120
|
|
|
49
|
-
/**
|
|
50
|
-
*
|
|
51
|
-
|
|
52
|
-
const {
|
|
53
|
-
|
|
54
|
-
if (offerTo(delegated.sessionId, delegated) || typeof opener !== 'function') return;
|
|
121
|
+
/** Offer `copy` in its chat now, or once that chat loads while `current()`
|
|
122
|
+
* still holds. */
|
|
123
|
+
function deliver(copy, current) {
|
|
124
|
+
const { opener } = registry();
|
|
125
|
+
if (offerTo(copy.sessionId, copy) || typeof opener !== 'function') return;
|
|
55
126
|
void Promise.resolve()
|
|
56
|
-
.then(() => opener(
|
|
127
|
+
.then(() => opener(copy.sessionId))
|
|
57
128
|
.then(() => {
|
|
58
|
-
if (
|
|
129
|
+
if (current()) offerTo(copy.sessionId, copy);
|
|
59
130
|
})
|
|
60
131
|
.catch(() => {});
|
|
61
132
|
}
|
|
62
133
|
|
|
134
|
+
/** Show `delegated` (`{ id, sessionId, request, answer(...finishArgs) → boolean }`,
|
|
135
|
+
* `sessionId` being the top-level conversation) there, now or once it loads. */
|
|
136
|
+
export function delegateApproval(delegated) {
|
|
137
|
+
const { pending } = registry();
|
|
138
|
+
pending.set(delegated.id, delegated);
|
|
139
|
+
deliver(delegated, () => pending.get(delegated.id) === delegated);
|
|
140
|
+
}
|
|
141
|
+
|
|
142
|
+
/** A top-level session's request (`{ id, sessionId, request, answer }`,
|
|
143
|
+
* `sessionId` being the asking session) that every chat handing it work
|
|
144
|
+
* shows too, now and while it waits: `copyFor(chat, { title })` is the
|
|
145
|
+
* request as that chat shows it. */
|
|
146
|
+
export function delegateToHandoffs(asked) {
|
|
147
|
+
registry().pending.set(asked.id, { ...asked, copies: new Map() });
|
|
148
|
+
syncHandoffCopies(asked.sessionId);
|
|
149
|
+
}
|
|
150
|
+
|
|
63
151
|
/** The owner settled `id`: every copy goes away. `result` (`{ approved, device }`)
|
|
64
152
|
* lets the copy say who answered. */
|
|
65
153
|
export function withdrawDelegatedApproval(id, result = null) {
|
|
@@ -68,7 +156,11 @@ export function withdrawDelegatedApproval(id, result = null) {
|
|
|
68
156
|
for (const queue of queues) queue.withdraw(id, result);
|
|
69
157
|
}
|
|
70
158
|
|
|
71
|
-
/** The pending delegated approvals a top-level session
|
|
159
|
+
/** The pending delegated approvals (and hand-off copies) a top-level session
|
|
160
|
+
* should show. */
|
|
72
161
|
export function delegatedApprovalsFor(sessionId) {
|
|
73
|
-
return [...registry().pending.values()].
|
|
162
|
+
return [...registry().pending.values()].flatMap((delegated) => {
|
|
163
|
+
if (delegated.copies) return delegated.copies.has(sessionId) ? [delegated.copies.get(sessionId)] : [];
|
|
164
|
+
return delegated.sessionId === sessionId ? [delegated] : [];
|
|
165
|
+
});
|
|
74
166
|
}
|
|
@@ -206,24 +206,25 @@ function hasTypedEvidence(error) {
|
|
|
206
206
|
);
|
|
207
207
|
}
|
|
208
208
|
|
|
209
|
-
// The window a provider timeout names (providerTimeoutError's message
|
|
210
|
-
|
|
209
|
+
// The window a provider timeout names (providerTimeoutError's message, or a
|
|
210
|
+
// first-byte watchdog's "within Nms"), or 0.
|
|
211
|
+
const timedOutMs = (text) => Number(/(?:timed out after|within) (\d+)ms/i.exec(text)?.[1]) || 0;
|
|
211
212
|
|
|
212
213
|
// A provider timeout before the model produced anything (isFirstByteTimeout):
|
|
213
|
-
// the model is slow, not the connection.
|
|
214
|
-
|
|
214
|
+
// the model is slow, not the connection. The provider name comes from the
|
|
215
|
+
// caller's own provider config, never from the error.
|
|
216
|
+
function stillThinkingText(error, text, providerLabel) {
|
|
215
217
|
const ms = Number(error.waitedMs) || timedOutMs(text);
|
|
216
|
-
const provider =
|
|
217
|
-
typeof error.providerLabel === 'string' && error.providerLabel ? error.providerLabel : 'the provider';
|
|
218
|
+
const provider = typeof providerLabel === 'string' && providerLabel ? providerLabel : 'the provider';
|
|
218
219
|
const attempts = Number(error.attempts) > 0 ? `; attempts: ${Math.floor(Number(error.attempts))}` : '';
|
|
219
220
|
return `Model still thinking: no output in ${formatDurationMs(ms)}; ${provider} sent nothing yet${attempts}.`;
|
|
220
221
|
}
|
|
221
222
|
|
|
222
223
|
// Stamped provider timeout phases and stream cuts. Null when the error names
|
|
223
224
|
// neither, so the text rules below decide.
|
|
224
|
-
function typedStreamFailureText(error, text) {
|
|
225
|
+
function typedStreamFailureText(error, text, providerLabel) {
|
|
225
226
|
if (!error || typeof error !== 'object') return null;
|
|
226
|
-
if (isFirstByteTimeout(error)) return stillThinkingText(error, text);
|
|
227
|
+
if (isFirstByteTimeout(error)) return stillThinkingText(error, text, providerLabel);
|
|
227
228
|
if (error.timeoutPhase === TIMEOUT_PHASE.IDLE) {
|
|
228
229
|
const ms = timedOutMs(text);
|
|
229
230
|
return ms > 0 ? `No progress ${formatDurationMs(ms)}.` : 'No progress.';
|
|
@@ -267,7 +268,7 @@ export function presentErrorText(error, options = {}) {
|
|
|
267
268
|
if (embeddedError) text = embeddedError;
|
|
268
269
|
text = oneLine(stripErrorPrefix(text));
|
|
269
270
|
if (!text) return 'Unknown error';
|
|
270
|
-
const streamFailure = typedStreamFailureText(error, text);
|
|
271
|
+
const streamFailure = typedStreamFailureText(error, text, options.providerLabel);
|
|
271
272
|
if (streamFailure) return streamFailure;
|
|
272
273
|
|
|
273
274
|
if (code === 'PROVIDER_PREFIX_MUTATION' || name === 'ProviderPrefixMutationError') {
|
|
@@ -369,12 +370,20 @@ export function presentErrorText(error, options = {}) {
|
|
|
369
370
|
return capText(safeErrorDetails(text) || 'Unknown error', max);
|
|
370
371
|
}
|
|
371
372
|
|
|
372
|
-
/**
|
|
373
|
-
|
|
374
|
-
|
|
375
|
-
|
|
376
|
-
|
|
377
|
-
|
|
373
|
+
/**
|
|
374
|
+
* Our own wording for a typed timeout retry (`first_response_timeout` /
|
|
375
|
+
* `stalled`), built only from the error's stamps and the window in its
|
|
376
|
+
* message; null for any other class. No error text is ever echoed.
|
|
377
|
+
*/
|
|
378
|
+
export function retryTimeoutText(error, reasonClass, providerLabel = null) {
|
|
379
|
+
const text = String(error?.message ?? '');
|
|
380
|
+
const ms = timedOutMs(text);
|
|
381
|
+
if (reasonClass === 'first_response_timeout') {
|
|
382
|
+
if (isFirstByteTimeout(error)) return stillThinkingText(error, text, providerLabel).replace(/\.$/, '');
|
|
383
|
+
return ms > 0 ? `No first response ${formatDurationMs(ms)}` : 'No first response';
|
|
384
|
+
}
|
|
385
|
+
if (reasonClass === 'stalled') return ms > 0 ? `No progress ${formatDurationMs(ms)}` : 'No progress';
|
|
386
|
+
return null;
|
|
378
387
|
}
|
|
379
388
|
|
|
380
389
|
/** The one retry status line every surface shows: `reason · retry n/max in 15s`. */
|
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
import type { Server } from 'node:net';
|
|
2
|
+
|
|
3
|
+
export const FETCH_BLOCKED_PORTS: ReadonlySet<number>;
|
|
4
|
+
|
|
5
|
+
export const FETCH_SAFE_ATTEMPTS: number;
|
|
6
|
+
|
|
7
|
+
export function listenOnFetchSafePort(
|
|
8
|
+
server: Server,
|
|
9
|
+
host: string,
|
|
10
|
+
onListening: () => void,
|
|
11
|
+
options?: {
|
|
12
|
+
attempts?: number;
|
|
13
|
+
isCurrent?: () => boolean;
|
|
14
|
+
portOf?: (server: Server) => number;
|
|
15
|
+
}
|
|
16
|
+
): void;
|
|
@@ -0,0 +1,50 @@
|
|
|
1
|
+
// Ports the WHATWG Fetch spec blocks ("bad port"): fetch (undici) and Chromium
|
|
2
|
+
// refuse to connect to them, so a loopback server that a fetch client or a
|
|
3
|
+
// browser context must reach cannot live on one. https://fetch.spec.whatwg.org/#port-blocking
|
|
4
|
+
export const FETCH_BLOCKED_PORTS = new Set([
|
|
5
|
+
0, 1, 7, 9, 11, 13, 15, 17, 19, 20, 21, 22, 23, 25, 37, 42, 43, 53, 69, 77, 79, 87, 95, 101, 102, 103, 104, 109, 110,
|
|
6
|
+
111, 113, 115, 117, 119, 123, 135, 137, 139, 143, 161, 179, 389, 427, 465, 512, 513, 514, 515, 526, 530, 531, 532,
|
|
7
|
+
540, 548, 554, 556, 563, 587, 601, 636, 989, 990, 993, 995, 1719, 1720, 1723, 2049, 3659, 4045, 4190, 5060, 5061,
|
|
8
|
+
6000, 6566, 6665, 6666, 6667, 6668, 6669, 6679, 6697, 10080,
|
|
9
|
+
]);
|
|
10
|
+
|
|
11
|
+
/** How many more OS-assigned ports a listener takes after a blocked one. */
|
|
12
|
+
export const FETCH_SAFE_ATTEMPTS = 16;
|
|
13
|
+
|
|
14
|
+
const assignedPort = (server) => {
|
|
15
|
+
const address = server.address();
|
|
16
|
+
return address && typeof address === 'object' ? address.port : 0;
|
|
17
|
+
};
|
|
18
|
+
|
|
19
|
+
/**
|
|
20
|
+
* `server.listen(0, host)` that never settles on a fetch-blocked port: when the
|
|
21
|
+
* OS assigns one, the server is closed and listened again, at most `attempts`
|
|
22
|
+
* more times. `onListening` runs once, on a safe port. When the attempts run
|
|
23
|
+
* out the server is closed and an 'error' is emitted, like a failed listen.
|
|
24
|
+
* `isCurrent` lets a caller that stopped the server meanwhile cancel the retry;
|
|
25
|
+
* `portOf` reads the assigned port.
|
|
26
|
+
*/
|
|
27
|
+
export function listenOnFetchSafePort(
|
|
28
|
+
server,
|
|
29
|
+
host,
|
|
30
|
+
onListening,
|
|
31
|
+
{ attempts = FETCH_SAFE_ATTEMPTS, isCurrent = () => true, portOf = assignedPort } = {}
|
|
32
|
+
) {
|
|
33
|
+
let retries = attempts;
|
|
34
|
+
const attempt = () => {
|
|
35
|
+
server.listen(0, host, () => {
|
|
36
|
+
if (!FETCH_BLOCKED_PORTS.has(portOf(server))) {
|
|
37
|
+
onListening();
|
|
38
|
+
return;
|
|
39
|
+
}
|
|
40
|
+
const exhausted = retries <= 0;
|
|
41
|
+
retries -= 1;
|
|
42
|
+
server.close(() => {
|
|
43
|
+
if (!isCurrent()) return;
|
|
44
|
+
if (exhausted) server.emit('error', new Error('no fetch-safe loopback port was assigned'));
|
|
45
|
+
else attempt();
|
|
46
|
+
});
|
|
47
|
+
});
|
|
48
|
+
};
|
|
49
|
+
attempt();
|
|
50
|
+
}
|
|
@@ -3,14 +3,6 @@ export function enforceOwnerOnlyAclWin32(targetPath: string, options?: { fresh?:
|
|
|
3
3
|
/** Test hook: the live per-process ACL helper, or null. */
|
|
4
4
|
export function ownerOnlyAclHelperForTests(): import('node:child_process').ChildProcess | null;
|
|
5
5
|
|
|
6
|
-
/** Test/diagnostic hook: helper attachment, pending requests, starts in the restart window. */
|
|
7
|
-
export function ownerOnlyAclHelperStats(): {
|
|
8
|
-
alive: boolean;
|
|
9
|
-
pid: number | null;
|
|
10
|
-
pending: number;
|
|
11
|
-
recentStarts: number;
|
|
12
|
-
};
|
|
13
|
-
|
|
14
6
|
/** Non-fresh calls verify (and tighten) the real DACL on every call through a shared helper. */
|
|
15
7
|
export function enforceOwnerOnlyAclWin32Async(targetPath: string, options?: { fresh?: boolean }): Promise<void>;
|
|
16
8
|
|
|
@@ -267,17 +267,6 @@ export async function prewarmOwnerOnlyAclWin32({ bridge = true } = {}) {
|
|
|
267
267
|
}
|
|
268
268
|
}
|
|
269
269
|
|
|
270
|
-
/** Test/diagnostic hook: whether a helper is attached, its requests awaiting
|
|
271
|
-
* an answer, and the starts inside the current restart window. */
|
|
272
|
-
export function ownerOnlyAclHelperStats() {
|
|
273
|
-
return {
|
|
274
|
-
alive: aclHelper !== null,
|
|
275
|
-
pid: aclHelper?.child.pid ?? null,
|
|
276
|
-
pending: aclHelper?.pending.size ?? 0,
|
|
277
|
-
recentStarts: aclHelperStartTimes.length,
|
|
278
|
-
};
|
|
279
|
-
}
|
|
280
|
-
|
|
281
270
|
/** Test hook: the live helper process, or null. */
|
|
282
271
|
export function ownerOnlyAclHelperForTests() {
|
|
283
272
|
return aclHelper?.child ?? null;
|
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
// Side-effect import for the git-heavy suites. Import it before anything that
|
|
2
|
+
// spawns git or snapshots process.env.
|
|
3
|
+
//
|
|
4
|
+
// On Windows `git` on PATH is usually Git for Windows' cmd\git.exe launcher,
|
|
5
|
+
// which starts the real git.exe as a second process, so every spawn pays two
|
|
6
|
+
// process creations (~75 ms instead of ~40 ms). These suites spawn hundreds of
|
|
7
|
+
// git processes, test and code under test alike (the git runner worker shares
|
|
8
|
+
// process.env), so the real binary's directory (`git --exec-path`, where git
|
|
9
|
+
// itself resolves its subcommands) goes first on PATH. Same git, same config;
|
|
10
|
+
// only the launcher hop is skipped. Elsewhere `git` already is the binary.
|
|
11
|
+
import { spawnSync } from 'node:child_process';
|
|
12
|
+
import { existsSync } from 'node:fs';
|
|
13
|
+
import { delimiter, join, normalize } from 'node:path';
|
|
14
|
+
|
|
15
|
+
if (process.platform === 'win32') {
|
|
16
|
+
const probe = spawnSync('git', ['--exec-path'], { encoding: 'utf8', windowsHide: true });
|
|
17
|
+
const dir = probe.status === 0 ? normalize(probe.stdout.trim()) : '';
|
|
18
|
+
if (dir && existsSync(join(dir, 'git.exe'))) process.env.PATH = `${dir}${delimiter}${process.env.PATH}`;
|
|
19
|
+
}
|
|
@@ -8,7 +8,8 @@ export interface HandoffFile {
|
|
|
8
8
|
id?: string;
|
|
9
9
|
}
|
|
10
10
|
export function normalizeHandoffFiles(files: unknown): HandoffFile[];
|
|
11
|
-
export function artifactLines(files: unknown, omitted?: number
|
|
11
|
+
export function artifactLines(files: unknown, omitted?: number): string[];
|
|
12
|
+
export function producedFileLines(files: unknown, omitted?: number): string[];
|
|
12
13
|
export interface HandoffRecord {
|
|
13
14
|
id: string;
|
|
14
15
|
attachmentRef: string;
|
|
@@ -31,20 +31,30 @@ export function normalizeHandoffFiles(files) {
|
|
|
31
31
|
}));
|
|
32
32
|
}
|
|
33
33
|
|
|
34
|
-
/**
|
|
35
|
-
*
|
|
36
|
-
|
|
37
|
-
* lists paths only — an id named in text is never trusted. */
|
|
38
|
-
export function artifactLines(files, omitted = 0, { ids = true } = {}) {
|
|
34
|
+
/** An agent completion's compact list of files it produced (name and path
|
|
35
|
+
* only; the Lead forwards or links by path), empty when there are none. */
|
|
36
|
+
export function producedFileLines(files, omitted = 0) {
|
|
39
37
|
const list = normalizeHandoffFiles(files);
|
|
40
38
|
if (!list.length) return [];
|
|
41
|
-
const attachable = (file) => (ids ? ` [attachment:${file.id}]` : ' (attachable: forward it by path)');
|
|
42
39
|
return [
|
|
43
|
-
|
|
40
|
+
'Files produced:',
|
|
41
|
+
...list.map((file) => `- ${oneLine(file.name)} — ${oneLine(file.path)}`),
|
|
42
|
+
...(omitted > 0 ? [`- …and ${omitted} more`] : []),
|
|
43
|
+
];
|
|
44
|
+
}
|
|
45
|
+
|
|
46
|
+
/** The model-facing list of a bot task report's `files`, empty when there are
|
|
47
|
+
* none; the row also records the stored files as its attachments, so their
|
|
48
|
+
* ids can be forwarded. */
|
|
49
|
+
export function artifactLines(files, omitted = 0) {
|
|
50
|
+
const list = normalizeHandoffFiles(files);
|
|
51
|
+
if (!list.length) return [];
|
|
52
|
+
return [
|
|
53
|
+
'Files from this turn (forward one with attachments: [id or path]; link a path to show it):',
|
|
44
54
|
...list.map(
|
|
45
55
|
(file) =>
|
|
46
56
|
`- ${oneLine(file.name)} (${oneLine(file.mimeType)}, ${sizeLabel(file.size)}) — ${oneLine(file.path)}${
|
|
47
|
-
file.id ?
|
|
57
|
+
file.id ? ` [attachment:${file.id}]` : ' (too large or unsupported to attach; link the path)'
|
|
48
58
|
}`
|
|
49
59
|
),
|
|
50
60
|
...(omitted > 0 ? [`- …and ${omitted} more file(s)`] : []),
|
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
// Mixy's heartbeat interval setting (`heartbeatIntervalMinutes` in the config):
|
|
2
|
+
// one of a few fixed choices, shared by the runtime, the desktop's IPC check
|
|
3
|
+
// and the Settings picker. "Off" is the separate `heartbeat` switch.
|
|
4
|
+
export const HEARTBEAT_INTERVAL_CHOICES = Object.freeze([30, 60, 120, 240]);
|
|
5
|
+
export const DEFAULT_HEARTBEAT_INTERVAL_MINUTES = 60;
|
|
6
|
+
|
|
7
|
+
/** `value` when it is one of the choices, else the default (a stored value
|
|
8
|
+
* that is no longer, or never was, a choice reads as the default). */
|
|
9
|
+
export function normalizeHeartbeatIntervalMinutes(value) {
|
|
10
|
+
return HEARTBEAT_INTERVAL_CHOICES.includes(value) ? value : DEFAULT_HEARTBEAT_INTERVAL_MINUTES;
|
|
11
|
+
}
|
|
12
|
+
|
|
13
|
+
/** `value` when it is one of the choices; throws otherwise (a write). */
|
|
14
|
+
export function requireHeartbeatIntervalMinutes(value) {
|
|
15
|
+
if (!HEARTBEAT_INTERVAL_CHOICES.includes(value)) {
|
|
16
|
+
throw new Error(`heartbeat interval must be one of ${HEARTBEAT_INTERVAL_CHOICES.join(', ')} minutes`);
|
|
17
|
+
}
|
|
18
|
+
return value;
|
|
19
|
+
}
|
|
@@ -0,0 +1,126 @@
|
|
|
1
|
+
// idle-auto-clear-gate.mjs — the idle auto-clear predicate, shared by the
|
|
2
|
+
// submit gate (tui/session/session-flow/auto-clear.mjs); the cache warmer
|
|
3
|
+
// (agent/orchestrator/session/cache-warm.mjs) registers its successful warms,
|
|
4
|
+
// which count as cache use (sessionActivityTimestamp). A thread is cleared before
|
|
5
|
+
// a submit when auto-clear is on, it has been idle for at least the window
|
|
6
|
+
// since its activity timestamp, and its context is not below the minimum
|
|
7
|
+
// share of the compaction trigger.
|
|
8
|
+
import { attachmentTextForPart, isAttachmentReference } from '../attachments/store.mjs';
|
|
9
|
+
|
|
10
|
+
export function promptContentText(content) {
|
|
11
|
+
if (typeof content === 'string') return content;
|
|
12
|
+
if (Array.isArray(content)) {
|
|
13
|
+
return content
|
|
14
|
+
.map((part) => {
|
|
15
|
+
if (typeof part === 'string') return part;
|
|
16
|
+
if (part?.type === 'text') return isAttachmentReference(part) ? attachmentTextForPart(part) : part.text || '';
|
|
17
|
+
if (part?.type === 'image') return '[Image]';
|
|
18
|
+
if (part?.type === 'file') return '[File]';
|
|
19
|
+
return part?.text || '';
|
|
20
|
+
})
|
|
21
|
+
.filter(Boolean)
|
|
22
|
+
.join('\n');
|
|
23
|
+
}
|
|
24
|
+
return String(content ?? '');
|
|
25
|
+
}
|
|
26
|
+
|
|
27
|
+
function timestampMs(value) {
|
|
28
|
+
if (value == null || value === '') return 0;
|
|
29
|
+
const n = Number(value);
|
|
30
|
+
if (Number.isFinite(n) && n > 0) return n;
|
|
31
|
+
const parsed = Date.parse(String(value));
|
|
32
|
+
return Number.isFinite(parsed) && parsed > 0 ? parsed : 0;
|
|
33
|
+
}
|
|
34
|
+
|
|
35
|
+
function hasModelVisibleConversation(session) {
|
|
36
|
+
const messages = Array.isArray(session?.messages) ? session.messages : [];
|
|
37
|
+
return messages.some((message) => {
|
|
38
|
+
const role = message?.role;
|
|
39
|
+
if (role !== 'user' && role !== 'assistant' && role !== 'tool') return false;
|
|
40
|
+
const text = promptContentText(message.content).trim();
|
|
41
|
+
if (role === 'user' && text.startsWith('<system-reminder>')) return false;
|
|
42
|
+
if (role === 'assistant' && text === '.' && !Array.isArray(message.toolCalls)) return false;
|
|
43
|
+
return !!text || role === 'assistant' || role === 'tool';
|
|
44
|
+
});
|
|
45
|
+
}
|
|
46
|
+
|
|
47
|
+
// A session's last successful cache-warm use (a read with no write),
|
|
48
|
+
// registered by the warmer (cache-warm.mjs, from its durable touch store):
|
|
49
|
+
// `{ at, provider, model, accountId }` — when, and the route and account whose
|
|
50
|
+
// cached prefix it read — or a bare epoch-ms time; null/0 when none.
|
|
51
|
+
let lastCacheWarmUse = () => null;
|
|
52
|
+
|
|
53
|
+
/** Register the source of a session's last successful cache-warm use
|
|
54
|
+
* (`(sessionId) => { at, provider, model, accountId } | epoch ms | null`);
|
|
55
|
+
* null restores none. */
|
|
56
|
+
export function setCacheWarmUseSource(source) {
|
|
57
|
+
lastCacheWarmUse = typeof source === 'function' ? source : () => null;
|
|
58
|
+
}
|
|
59
|
+
|
|
60
|
+
/** When a successful cache warm last used the session's prompt cache (epoch
|
|
61
|
+
* ms), or 0: the warm read and refreshed the cached prefix without moving
|
|
62
|
+
* any of the session's own timestamps. */
|
|
63
|
+
export function lastCacheWarmUseAt(sessionId) {
|
|
64
|
+
const use = sessionId ? lastCacheWarmUse(sessionId) : null;
|
|
65
|
+
return timestampMs(use && typeof use === 'object' ? use.at : use);
|
|
66
|
+
}
|
|
67
|
+
|
|
68
|
+
/** When a successful cache warm last used the prefix a request on `provider`
|
|
69
|
+
* / `model` / `accountId` reads (epoch ms), or 0: a warm on another route or
|
|
70
|
+
* account refreshed an entry this request cannot read, and a use without a
|
|
71
|
+
* recorded identity matches nothing. */
|
|
72
|
+
export function cacheWarmUseFor(sessionId, { provider, model, accountId = '' } = {}) {
|
|
73
|
+
const use = sessionId ? lastCacheWarmUse(sessionId) : null;
|
|
74
|
+
if (!use || typeof use !== 'object' || !use.provider) return 0;
|
|
75
|
+
const matches =
|
|
76
|
+
use.provider === provider && use.model === model && String(use.accountId || '') === String(accountId || '');
|
|
77
|
+
return matches ? timestampMs(use.at) : 0;
|
|
78
|
+
}
|
|
79
|
+
|
|
80
|
+
/**
|
|
81
|
+
* When the session was last active (0: no model-visible conversation): the
|
|
82
|
+
* latest of its own activity and the last successful cache warm. Idleness is
|
|
83
|
+
* measured from the last time the cached prompt was actually used, and a warm
|
|
84
|
+
* that read the cache without writing used it; the session's own timestamps
|
|
85
|
+
* are never moved.
|
|
86
|
+
*/
|
|
87
|
+
export function sessionActivityTimestamp(session, fallback = 0) {
|
|
88
|
+
if (!hasModelVisibleConversation(session)) return 0;
|
|
89
|
+
const own = timestampMs(session?.lastUsedAt) || timestampMs(session?.updatedAt) || timestampMs(fallback);
|
|
90
|
+
return Math.max(own, lastCacheWarmUseAt(session?.id));
|
|
91
|
+
}
|
|
92
|
+
|
|
93
|
+
// A zero is often an unavailable/stale meter field, not an authoritative
|
|
94
|
+
// measurement. Do not let it mask the sibling live estimate.
|
|
95
|
+
function contextUsageBelowThreshold(runtime, minContextPercent) {
|
|
96
|
+
if (!(minContextPercent > 0)) return false;
|
|
97
|
+
const status = runtime.contextStatus?.() || null;
|
|
98
|
+
const usedTokens = Math.max(
|
|
99
|
+
0,
|
|
100
|
+
Number(status?.usedTokens) || 0,
|
|
101
|
+
Number(status?.currentEstimatedTokens) || 0,
|
|
102
|
+
Number(status?.compaction?.currentEstimatedTokens) || 0
|
|
103
|
+
);
|
|
104
|
+
const triggerTokens = Number(
|
|
105
|
+
status?.compaction?.triggerTokens ||
|
|
106
|
+
status?.compaction?.autoCompactTokenLimit ||
|
|
107
|
+
runtime.session?.autoCompactTokenLimit ||
|
|
108
|
+
0
|
|
109
|
+
);
|
|
110
|
+
if (!Number.isFinite(usedTokens) || !Number.isFinite(triggerTokens) || !(usedTokens > 0 && triggerTokens > 0)) {
|
|
111
|
+
return true;
|
|
112
|
+
}
|
|
113
|
+
return (usedTokens / triggerTokens) * 100 < minContextPercent;
|
|
114
|
+
}
|
|
115
|
+
|
|
116
|
+
/**
|
|
117
|
+
* Whether the idle gate clears the thread before a submit at `at`: `cfg` is
|
|
118
|
+
* the auto-clear state (`enabled`, `idleMs`, `minContextPercent`), the
|
|
119
|
+
* activity is `activityAt` (sessionActivityTimestamp) and `runtime` answers
|
|
120
|
+
* `contextStatus()` for the thread. The submit gate adds its own transient
|
|
121
|
+
* conditions (busy, queued input, a clear already running).
|
|
122
|
+
*/
|
|
123
|
+
export function idleAutoClearDue({ cfg, activityAt, at, runtime }) {
|
|
124
|
+
if (!cfg?.enabled || !activityAt || at - activityAt < cfg.idleMs) return false;
|
|
125
|
+
return !contextUsageBelowThreshold(runtime, Number(cfg.minContextPercent ?? 10));
|
|
126
|
+
}
|