mixdog 1.3.5 → 1.4.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/NOTICE.md +2 -2
- package/README.md +2 -2
- package/package.json +2 -11
- package/scripts/daemon-stub.mjs +1 -1
- package/scripts/gate-local.mjs +4 -2
- package/scripts/native-release-status.mjs +2 -2
- package/scripts/recall-history-cases.json +1 -1
- package/scripts/suite-health.mjs +10 -1
- package/scripts/test.mjs +7 -11
- package/src/agents/reviewer/AGENT.md +7 -11
- package/src/agents/reviewer/agent.json +1 -1
- package/src/defaults/shipped-history.json +17 -1
- package/src/defaults/skills/browser-use/SKILL.md +59 -11
- 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 +33 -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 +10 -8
- package/src/rules/agent/AGENT.md +6 -6
- package/src/rules/lead/LEAD.md +18 -20
- package/src/rules/shared/05-tool-calls.md +5 -6
- package/src/rules/shared/10-tool-workflow.md +2 -0
- package/src/rules/shared/60-verification.md +7 -5
- 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/config.mjs +17 -1
- package/src/runtime/agent/orchestrator/context/collect.mjs +3 -0
- package/src/runtime/agent/orchestrator/context/deferred-tools.mjs +72 -67
- package/src/runtime/agent/orchestrator/context/skill-catalog.mjs +54 -1
- 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/providers/account-pool.mjs +181 -133
- package/src/runtime/agent/orchestrator/providers/admission-scheduler.mjs +42 -6
- package/src/runtime/agent/orchestrator/providers/anthropic-cache-warm.mjs +165 -0
- 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.mjs +145 -66
- package/src/runtime/agent/orchestrator/providers/anthropic-overload-fallback.mjs +25 -3
- package/src/runtime/agent/orchestrator/providers/anthropic-sse.mjs +64 -33
- 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/cursor-wire-transport.mjs +25 -9
- package/src/runtime/agent/orchestrator/providers/cursor.mjs +22 -13
- 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/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-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/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 +26 -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 +12 -7
- 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/session-text.mjs +2 -0
- 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/session/cache-warm.mjs +792 -0
- package/src/runtime/agent/orchestrator/session/compact/runner.mjs +133 -22
- 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/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 +18 -22
- package/src/runtime/agent/orchestrator/session/loop/fresh-context.mjs +32 -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/route-fallback.mjs +17 -3
- package/src/runtime/agent/orchestrator/session/loop/send-phase.mjs +7 -2
- 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 +3 -2
- 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 +10 -0
- 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-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 +23 -13
- 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 +129 -0
- package/src/runtime/agent/orchestrator/session/manager/runtime-liveness.mjs +20 -2
- package/src/runtime/agent/orchestrator/session/manager/session-close.mjs +2 -0
- package/src/runtime/agent/orchestrator/session/manager/session-lifecycle.mjs +8 -0
- package/src/runtime/agent/orchestrator/session/manager/session-record.mjs +1 -1
- package/src/runtime/agent/orchestrator/session/manager/tool-resolution.mjs +7 -2
- package/src/runtime/agent/orchestrator/session/manager/turn-checkpoint-journal.mjs +102 -15
- package/src/runtime/agent/orchestrator/session/manager/turn-checkpoint.mjs +18 -5
- package/src/runtime/agent/orchestrator/session/pre-send-compact.mjs +19 -8
- package/src/runtime/agent/orchestrator/session/prompt-cache-idle-gaps.mjs +113 -0
- package/src/runtime/agent/orchestrator/session/prompt-cache-touch-store.mjs +127 -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-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 +12 -2
- package/src/runtime/agent/orchestrator/session/store-summary-index.mjs +44 -14
- package/src/runtime/agent/orchestrator/session/store-summary-reader.mjs +84 -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 +18 -1
- 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/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/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.mjs +25 -5
- package/src/runtime/agent/orchestrator/tools/builtin/builtin-tools.mjs +65 -7
- package/src/runtime/agent/orchestrator/tools/builtin/shell-runtime.mjs +7 -1
- 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/webhooks-db.mjs +5 -9
- 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 +27 -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/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/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/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 -14
- package/src/runtime/shared/delegated-approval-host.mjs +103 -11
- package/src/runtime/shared/err-text.mjs +24 -15
- 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/heartbeat-interval.d.mts +4 -0
- package/src/runtime/shared/heartbeat-interval.mjs +19 -0
- package/src/runtime/shared/idle-auto-clear-gate.mjs +104 -0
- package/src/runtime/shared/llm/usage-rollup.mjs +1 -1
- package/src/runtime/shared/mixy-presence.mjs +22 -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 +34 -1
- 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-result-summary.mjs +1 -14
- package/src/runtime/shared/worktrees/test-support.mjs +9 -4
- package/src/runtime/terminal-bridge/client.mjs +10 -1
- package/src/runtime/visualize-bridge/client.mjs +27 -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 +448 -185
- package/src/session-runtime/agent-task-sessions.mjs +329 -180
- 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 +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/internal-tool-executor/ask-user-tool.mjs +132 -28
- package/src/session-runtime/internal-tool-executor/bridge-tools.mjs +2 -0
- package/src/session-runtime/lifecycle/inheritance.mjs +16 -3
- package/src/session-runtime/lifecycle/session-catalog.mjs +3 -1
- 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/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/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/routine-owner.mjs +40 -9
- package/src/session-runtime/services/agent-tool/close-flow.mjs +3 -0
- package/src/session-runtime/services/agent-tool/spawn-preset.mjs +7 -2
- package/src/session-runtime/services/agent-tool/tag-registry.mjs +1 -0
- 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/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 +16 -2
- package/src/session-runtime/turn/turn-run/ask-callbacks.mjs +1 -0
- package/src/session-runtime/workflow-agents-api/style-and-mode.mjs +4 -0
- 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 +124 -188
- 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/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 +23 -2
- package/src/standalone/session-service/session-calls/session-views.mjs +8 -0
- package/src/standalone/session-tool-output.mjs +15 -7
- package/src/tui/app/model-options.mjs +3 -28
- package/src/tui/dist/index.mjs +58 -64
- 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 +5 -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/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
|
@@ -0,0 +1,104 @@
|
|
|
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
|
+
// When a session's prompt cache was last used by a successful cache warm (a
|
|
48
|
+
// read with no write), registered by the warmer (cache-warm.mjs, from its
|
|
49
|
+
// durable touch store); 0 when none.
|
|
50
|
+
let lastCacheWarmUse = () => 0;
|
|
51
|
+
|
|
52
|
+
/** Register the source of a session's last successful cache-warm use
|
|
53
|
+
* (`(sessionId) => epoch ms | 0`); null restores none. */
|
|
54
|
+
export function setCacheWarmUseSource(source) {
|
|
55
|
+
lastCacheWarmUse = typeof source === 'function' ? source : () => 0;
|
|
56
|
+
}
|
|
57
|
+
|
|
58
|
+
/**
|
|
59
|
+
* When the session was last active (0: no model-visible conversation): the
|
|
60
|
+
* latest of its own activity and the last successful cache warm. Idleness is
|
|
61
|
+
* measured from the last time the cached prompt was actually used, and a warm
|
|
62
|
+
* that read the cache without writing used it; the session's own timestamps
|
|
63
|
+
* are never moved.
|
|
64
|
+
*/
|
|
65
|
+
export function sessionActivityTimestamp(session, fallback = 0) {
|
|
66
|
+
if (!hasModelVisibleConversation(session)) return 0;
|
|
67
|
+
const own = timestampMs(session?.lastUsedAt) || timestampMs(session?.updatedAt) || timestampMs(fallback);
|
|
68
|
+
return Math.max(own, timestampMs(lastCacheWarmUse(session?.id)));
|
|
69
|
+
}
|
|
70
|
+
|
|
71
|
+
// A zero is often an unavailable/stale meter field, not an authoritative
|
|
72
|
+
// measurement. Do not let it mask the sibling live estimate.
|
|
73
|
+
function contextUsageBelowThreshold(runtime, minContextPercent) {
|
|
74
|
+
if (!(minContextPercent > 0)) return false;
|
|
75
|
+
const status = runtime.contextStatus?.() || null;
|
|
76
|
+
const usedTokens = Math.max(
|
|
77
|
+
0,
|
|
78
|
+
Number(status?.usedTokens) || 0,
|
|
79
|
+
Number(status?.currentEstimatedTokens) || 0,
|
|
80
|
+
Number(status?.compaction?.currentEstimatedTokens) || 0
|
|
81
|
+
);
|
|
82
|
+
const triggerTokens = Number(
|
|
83
|
+
status?.compaction?.triggerTokens ||
|
|
84
|
+
status?.compaction?.autoCompactTokenLimit ||
|
|
85
|
+
runtime.session?.autoCompactTokenLimit ||
|
|
86
|
+
0
|
|
87
|
+
);
|
|
88
|
+
if (!Number.isFinite(usedTokens) || !Number.isFinite(triggerTokens) || !(usedTokens > 0 && triggerTokens > 0)) {
|
|
89
|
+
return true;
|
|
90
|
+
}
|
|
91
|
+
return (usedTokens / triggerTokens) * 100 < minContextPercent;
|
|
92
|
+
}
|
|
93
|
+
|
|
94
|
+
/**
|
|
95
|
+
* Whether the idle gate clears the thread before a submit at `at`: `cfg` is
|
|
96
|
+
* the auto-clear state (`enabled`, `idleMs`, `minContextPercent`), the
|
|
97
|
+
* activity is `activityAt` (sessionActivityTimestamp) and `runtime` answers
|
|
98
|
+
* `contextStatus()` for the thread. The submit gate adds its own transient
|
|
99
|
+
* conditions (busy, queued input, a clear already running).
|
|
100
|
+
*/
|
|
101
|
+
export function idleAutoClearDue({ cfg, activityAt, at, runtime }) {
|
|
102
|
+
if (!cfg?.enabled || !activityAt || at - activityAt < cfg.idleMs) return false;
|
|
103
|
+
return !contextUsageBelowThreshold(runtime, Number(cfg.minContextPercent ?? 10));
|
|
104
|
+
}
|
|
@@ -22,7 +22,7 @@ export function usageRollupDayKey(ts) {
|
|
|
22
22
|
// background runners. An unknown source counts as conversation on purpose: a
|
|
23
23
|
// new runner showing up in the user's totals is visible and correctable, while
|
|
24
24
|
// silently dropping the user's own turns is neither.
|
|
25
|
-
const BACKGROUND_SOURCES = new Set(['memory-cycle', 'schedule', 'webhook', 'native-web-search']);
|
|
25
|
+
const BACKGROUND_SOURCES = new Set(['memory-cycle', 'schedule', 'webhook', 'native-web-search', 'cache-warm']);
|
|
26
26
|
|
|
27
27
|
export function isConversationUsageSource(sourceType) {
|
|
28
28
|
const id = typeof sourceType === 'string' ? sourceType.trim().toLowerCase() : '';
|
|
@@ -0,0 +1,22 @@
|
|
|
1
|
+
// Whether the representative bot (Mixy) exists: the one rule. The desktop
|
|
2
|
+
// answers from it (getLeadStatus `exists`, main/mixy-first.ts), and so do
|
|
3
|
+
// Mixy's routines (routine-owner.mjs `no-mixy`), her heartbeat and the
|
|
4
|
+
// heartbeat reports queued for her chat (mixy-heartbeat.mjs,
|
|
5
|
+
// agent-home-delivery.mjs), all through routine-owner.mjs readMixyExists.
|
|
6
|
+
//
|
|
7
|
+
// `created: false` (Mixy's agent.json) is no. Otherwise she exists on
|
|
8
|
+
// evidence that the user made or uses her:
|
|
9
|
+
// - a name (the 대표 에이전트 dialog always sets one; her profile can);
|
|
10
|
+
// - a message the user wrote in her thread (`homeChatted`,
|
|
11
|
+
// agent-kind.mjs homeThreadEvidence);
|
|
12
|
+
// - a schedule or webhook she owns (`ownsRoutines`): the user set it up,
|
|
13
|
+
// and since v1.3.0 it ran for her whether or not she was named.
|
|
14
|
+
// Without any of them she does not exist: v1.3.0's untouched automatic
|
|
15
|
+
// `{"created": true}`, and an undecided profile (`created` null). Her own
|
|
16
|
+
// posts in her thread, heartbeat reports and routine results, are no
|
|
17
|
+
// evidence. `ownsRoutines` is read only when nothing else decides.
|
|
18
|
+
export async function mixyExists(status, ownsRoutines) {
|
|
19
|
+
if (status?.created === false) return false;
|
|
20
|
+
if (String(status?.name ?? '').trim() || status?.homeChatted === true) return true;
|
|
21
|
+
return (await ownsRoutines()) === true;
|
|
22
|
+
}
|
|
@@ -1,21 +1,32 @@
|
|
|
1
|
-
|
|
1
|
+
import { createSharedAbortable } from './abort-race.mjs';
|
|
2
|
+
|
|
3
|
+
/** Lazy per-dataDir PG handle: one in-flight init, bootstrap lock, retry after
|
|
4
|
+
* failure. `signal` withdraws a caller; the init (a PG runtime install
|
|
5
|
+
* included) is cancelled once every caller has (createSharedAbortable). */
|
|
2
6
|
export function createPgSchemaDb({ schema, ddl, defaultDataDir, ensurePg, withLock }) {
|
|
3
7
|
const ready = new Map();
|
|
4
|
-
return
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
const
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
8
|
+
return function getDb(dataDir = defaultDataDir(), { signal = null } = {}) {
|
|
9
|
+
let shared = ready.get(dataDir);
|
|
10
|
+
if (!shared) {
|
|
11
|
+
const entry = createSharedAbortable(
|
|
12
|
+
async (initSignal) => {
|
|
13
|
+
const { db, pool } = await ensurePg(dataDir, { schema, signal: initSignal });
|
|
14
|
+
initSignal.throwIfAborted();
|
|
15
|
+
// Serialize CREATE TABLE across concurrent first-boot processes on the
|
|
16
|
+
// same cluster-global advisory lock the adapter uses for schema bootstrap.
|
|
17
|
+
await withLock(pool, () => db.exec(ddl));
|
|
18
|
+
return db;
|
|
19
|
+
},
|
|
20
|
+
{
|
|
21
|
+
// Let the next call retry after a transient failure or a cancellation.
|
|
22
|
+
onSettled: (failed) => {
|
|
23
|
+
if (failed && ready.get(dataDir) === entry) ready.delete(dataDir);
|
|
24
|
+
},
|
|
25
|
+
}
|
|
26
|
+
);
|
|
27
|
+
shared = entry;
|
|
28
|
+
ready.set(dataDir, entry);
|
|
19
29
|
}
|
|
30
|
+
return shared.join(signal);
|
|
20
31
|
};
|
|
21
32
|
}
|
|
@@ -5,7 +5,7 @@
|
|
|
5
5
|
const PROFILE_LANGUAGES = Object.freeze([
|
|
6
6
|
{ id: 'system', label: 'System (locale)', prompt: null },
|
|
7
7
|
{ id: 'en', label: 'English', prompt: 'English' },
|
|
8
|
-
{ id: 'ko', label: '
|
|
8
|
+
{ id: 'ko', label: '한국어', prompt: 'Korean' },
|
|
9
9
|
{ id: 'ja', label: '日本語', prompt: 'Japanese (日本語)' },
|
|
10
10
|
{ id: 'zh-Hans', label: '中文(简体)', prompt: 'Simplified Chinese (简体中文)' },
|
|
11
11
|
{ id: 'zh-Hant', label: '中文(繁體)', prompt: 'Traditional Chinese (繁體中文)' },
|
|
@@ -0,0 +1,4 @@
|
|
|
1
|
+
export declare const PROMPT_ORIGIN_CHANNEL: 'channel';
|
|
2
|
+
export declare const PROMPT_ORIGIN_AGENT_REPORT: 'agent-report';
|
|
3
|
+
export type PromptOrigin = typeof PROMPT_ORIGIN_CHANNEL | typeof PROMPT_ORIGIN_AGENT_REPORT;
|
|
4
|
+
export declare function promptOriginOf(value: unknown): PromptOrigin | null;
|
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
// A submitted prompt's provenance, carried apart from its presentation mode
|
|
2
|
+
// (`origin` on the submit options, the queue entry, the turn input and the
|
|
3
|
+
// stored user message's meta). Dependency-free: the desktop main process
|
|
4
|
+
// stamps it too (agent-room-turn.ts).
|
|
5
|
+
|
|
6
|
+
/** Real external activity presented as a task notification: an inbound
|
|
7
|
+
* channel message (Discord, Telegram, webhook event rules …). */
|
|
8
|
+
export const PROMPT_ORIGIN_CHANNEL = 'channel';
|
|
9
|
+
|
|
10
|
+
/** A turn the runtime starts because another bot or agent reported back: a
|
|
11
|
+
* bot-home wake (daemon.mjs `wake`) or a group-room member's relay of its
|
|
12
|
+
* reported work (agent-room-turn.ts). System-caused, though presented as a
|
|
13
|
+
* prompt. */
|
|
14
|
+
export const PROMPT_ORIGIN_AGENT_REPORT = 'agent-report';
|
|
15
|
+
|
|
16
|
+
const KNOWN_ORIGINS = new Set([PROMPT_ORIGIN_CHANNEL, PROMPT_ORIGIN_AGENT_REPORT]);
|
|
17
|
+
|
|
18
|
+
/** `value` when it is a known provenance, else null. */
|
|
19
|
+
export function promptOriginOf(value) {
|
|
20
|
+
return KNOWN_ORIGINS.has(value) ? value : null;
|
|
21
|
+
}
|
|
@@ -15,7 +15,7 @@ export const ACCOUNT_PROVIDERS = Object.freeze([
|
|
|
15
15
|
'antigravity-oauth',
|
|
16
16
|
'opencode-go-oauth',
|
|
17
17
|
]);
|
|
18
|
-
const MAX_ACCOUNTS = 20;
|
|
18
|
+
export const MAX_ACCOUNTS = 20;
|
|
19
19
|
// `dataDir` defaults to this process's data dir; an isolated runtime passes
|
|
20
20
|
// the host's dir explicitly while its own MIXDOG_DATA_DIR points elsewhere.
|
|
21
21
|
const file = (dataDir = resolvePluginData()) => join(dataDir, 'provider-accounts.json');
|
|
@@ -597,6 +597,12 @@ export function providerAccountExhaustedForModelOnly(row, now = Date.now(), mode
|
|
|
597
597
|
// enough that yesterday's number cannot send every fallback to one account.
|
|
598
598
|
const USAGE_EVIDENCE_MAX_AGE_MS = 60 * 60_000;
|
|
599
599
|
|
|
600
|
+
/** Whether a usage reading taken at `checkedAt` may still steer a decision. */
|
|
601
|
+
export function quotaReadingFresh(checkedAt, now = Date.now()) {
|
|
602
|
+
const at = Number(checkedAt);
|
|
603
|
+
return at > 0 && now - at <= USAGE_EVIDENCE_MAX_AGE_MS;
|
|
604
|
+
}
|
|
605
|
+
|
|
600
606
|
// Remaining headroom across every window that applies to this model, or null
|
|
601
607
|
// when nothing recent measured the account. The fullest window decides: an
|
|
602
608
|
// account with 4% of its burst window left cannot serve now, however untouched
|
|
@@ -0,0 +1,29 @@
|
|
|
1
|
+
// Display names for provider ids; an unknown id displays as itself.
|
|
2
|
+
const PROVIDER_DISPLAY_NAMES = new Map([
|
|
3
|
+
['openai-oauth', 'OpenAI OAuth'],
|
|
4
|
+
['anthropic-oauth', 'Anthropic OAuth'],
|
|
5
|
+
['grok-oauth', 'Grok OAuth'],
|
|
6
|
+
['muse-code-oauth', 'Muse Code'],
|
|
7
|
+
['command-code-oauth', 'Command Code'],
|
|
8
|
+
['mimo-oauth', 'MiMo'],
|
|
9
|
+
['cursor-oauth', 'Cursor OAuth'],
|
|
10
|
+
['devin-oauth', 'Devin'],
|
|
11
|
+
['antigravity-oauth', 'Antigravity OAuth'],
|
|
12
|
+
['openai', 'OpenAI API'],
|
|
13
|
+
['openai-api', 'OpenAI API'],
|
|
14
|
+
['anthropic', 'Anthropic API'],
|
|
15
|
+
['anthropic-api', 'Anthropic API'],
|
|
16
|
+
['gemini', 'Gemini API'],
|
|
17
|
+
['gemini-api', 'Gemini API'],
|
|
18
|
+
['xai', 'xAI API'],
|
|
19
|
+
['xai-api', 'xAI API'],
|
|
20
|
+
['deepseek', 'DeepSeek API'],
|
|
21
|
+
['deepseek-api', 'DeepSeek API'],
|
|
22
|
+
['opencode-go-oauth', 'OpenCode Go OAuth'],
|
|
23
|
+
['openrouter', 'OpenRouter'],
|
|
24
|
+
['mixdog-local', 'Local Provider'],
|
|
25
|
+
['default', 'Default'],
|
|
26
|
+
]);
|
|
27
|
+
|
|
28
|
+
export const providerDisplayName = (provider) =>
|
|
29
|
+
PROVIDER_DISPLAY_NAMES.get(String(provider || '').toLowerCase()) ?? (provider || 'Provider');
|
|
@@ -8,6 +8,8 @@ export const TIMEOUT_PHASE = Object.freeze({
|
|
|
8
8
|
FIRST_CHUNK: 'first-chunk',
|
|
9
9
|
// A stream that already produced output went silent.
|
|
10
10
|
IDLE: 'idle',
|
|
11
|
+
// A sign-in token refresh did not answer: an auth problem, not a slow model.
|
|
12
|
+
AUTH: 'auth',
|
|
11
13
|
});
|
|
12
14
|
|
|
13
15
|
/** A timeout before the model produced anything: headers or first chunk. */
|
|
@@ -4,6 +4,8 @@ export interface RoutineTrigger {
|
|
|
4
4
|
event?: string;
|
|
5
5
|
}
|
|
6
6
|
|
|
7
|
+
export declare const ROUTINE_QUIET: 'ROUTINE_QUIET';
|
|
8
|
+
export declare function isRoutineQuietReply(text: unknown): boolean;
|
|
7
9
|
export declare function routineTriggerHeader(input: {
|
|
8
10
|
kind: string;
|
|
9
11
|
name: string;
|
|
@@ -9,6 +9,15 @@ import { localTimestampParts } from './time-format.mjs';
|
|
|
9
9
|
const HEADER_PREFIX = '[Routine trigger]';
|
|
10
10
|
const KINDS = new Set(['schedule', 'webhook']);
|
|
11
11
|
|
|
12
|
+
/** The whole reply of a routine turn with nothing new to tell: kept in the
|
|
13
|
+
* record, never painted, previewed, counted unread or notified. */
|
|
14
|
+
export const ROUTINE_QUIET = 'ROUTINE_QUIET';
|
|
15
|
+
|
|
16
|
+
/** Whether a reply is exactly the routine quiet sentinel. */
|
|
17
|
+
export function isRoutineQuietReply(text) {
|
|
18
|
+
return String(text ?? '').trim() === ROUTINE_QUIET;
|
|
19
|
+
}
|
|
20
|
+
|
|
12
21
|
/** The first line of a routine turn's prompt: which routine fired, when (local
|
|
13
22
|
* time and zone: the turn gets no other date), and how to treat the turn.
|
|
14
23
|
* It is the routine's only conduct text. Only the turn's content carries it,
|
|
@@ -24,6 +33,7 @@ export function routineTriggerHeader({ kind, name, project = '', now = new Date(
|
|
|
24
33
|
'Payments and permanent deletes are refused here: report that and suggest doing it from the chat.',
|
|
25
34
|
'If a sign-in, code or CAPTCHA blocks you, stop and say which site needs the user.',
|
|
26
35
|
'Reply in this chat in plain words, without tool names; if you handed work off, say it started — its result arrives here later.',
|
|
36
|
+
`Tell what is new in a fitting tone; if nothing is worth telling, reply with exactly ${ROUTINE_QUIET}.`,
|
|
27
37
|
...(project ? [`Project folder: ${project}`] : []),
|
|
28
38
|
].join(' ');
|
|
29
39
|
}
|
|
@@ -1,15 +1,7 @@
|
|
|
1
1
|
export interface SensitiveScope {
|
|
2
|
+
/** A Mixdog home or data folder, as path segments; only its secret subset is denied. */
|
|
2
3
|
root: string[];
|
|
3
|
-
/** Subfolders of the root that stay reachable. */
|
|
4
|
-
except: readonly string[];
|
|
5
|
-
/** Whole tree (a registered private root) or Mixdog's secret subset. */
|
|
6
|
-
whole: boolean;
|
|
7
4
|
}
|
|
8
|
-
export function
|
|
9
|
-
export function matchesSensitivePath(
|
|
10
|
-
path: string,
|
|
11
|
-
within?: readonly SensitiveScope[],
|
|
12
|
-
options?: { envTemplates?: boolean }
|
|
13
|
-
): boolean;
|
|
5
|
+
export function matchesSensitivePath(path: string, within?: readonly SensitiveScope[]): boolean;
|
|
14
6
|
export function mixdogSensitiveScopes(extraData?: readonly string[]): SensitiveScope[];
|
|
15
7
|
export function isSensitiveLocalPath(path: string, options?: { extraData?: readonly string[] }): boolean;
|
|
@@ -1,8 +1,7 @@
|
|
|
1
|
-
// The credential / secret file deny list
|
|
2
|
-
//
|
|
3
|
-
//
|
|
4
|
-
//
|
|
5
|
-
// between bots and agents (session-runtime/handoff-attachments.mjs).
|
|
1
|
+
// The credential / secret file deny list of the runtime's attachment
|
|
2
|
+
// hand-offs between bots and agents (session-runtime/handoff-attachments.mjs):
|
|
3
|
+
// an automatic transfer never carries a secret-bearing file. A paired device
|
|
4
|
+
// is the user's own and gets no such list (it opens what the desktop opens).
|
|
6
5
|
//
|
|
7
6
|
// Matching is on SEGMENTS of a path, lower-cased on every platform, so a
|
|
8
7
|
// denied name cannot hide behind letter case, trailing dots/spaces or an NTFS
|
|
@@ -113,10 +112,6 @@ const CREDENTIAL_FILES = new Set([
|
|
|
113
112
|
* checked in often enough with real values that no name is exempt) and
|
|
114
113
|
* `<stage>.env`. */
|
|
115
114
|
const ENV_FILE = /^(?:\.env.*|[^.]{1,64}(?:\.[^.]{1,64}){0,3}\.env)$/;
|
|
116
|
-
/** The dotenv templates a person may still open on purpose from a paired
|
|
117
|
-
* device (the desktop's remote file lanes, `envTemplates: true`); automatic
|
|
118
|
-
* transfers between bots and agents never get this allowance. */
|
|
119
|
-
const ENV_TEMPLATES = new Set(['.env.example', '.env.sample', '.env.template', '.env.dist']);
|
|
120
115
|
/** Credential JSON a cloud or OAuth client writes or downloads:
|
|
121
116
|
* `credentials.json`, `*credentials*.json`, `service-account*.json`,
|
|
122
117
|
* `client_secret*.json`. */
|
|
@@ -143,10 +138,9 @@ function backupOriginal(name) {
|
|
|
143
138
|
|
|
144
139
|
/** A file whose own name says it holds a secret, wherever it lives: a
|
|
145
140
|
* credential file, a dotenv file, a key or keystore, or a backup of one. */
|
|
146
|
-
function secretFileName(name
|
|
141
|
+
function secretFileName(name) {
|
|
147
142
|
const original = backupOriginal(name);
|
|
148
|
-
if (CREDENTIAL_FILES.has(original) || CREDENTIAL_JSON.test(original)) return true;
|
|
149
|
-
if (ENV_FILE.test(original)) return !(envTemplates && ENV_TEMPLATES.has(original));
|
|
143
|
+
if (CREDENTIAL_FILES.has(original) || CREDENTIAL_JSON.test(original) || ENV_FILE.test(original)) return true;
|
|
150
144
|
const dot = original.lastIndexOf('.');
|
|
151
145
|
if (dot >= 0 && KEY_EXTENSIONS.has(original.slice(dot))) return true;
|
|
152
146
|
return SSH_PRIVATE_KEY.test(original);
|
|
@@ -156,14 +150,7 @@ function secretFileName(name, { envTemplates = false } = {}) {
|
|
|
156
150
|
// (tool results, shell output, media), which stay reachable; only the files
|
|
157
151
|
// and folders that carry provider tokens, API keys, pairing and bridge secrets
|
|
158
152
|
// are denied.
|
|
159
|
-
const MIXDOG_SECRET_DIRECTORIES = new Set([
|
|
160
|
-
'secrets',
|
|
161
|
-
'mcp-oauth',
|
|
162
|
-
'provider-accounts',
|
|
163
|
-
'telegram',
|
|
164
|
-
'relay-data',
|
|
165
|
-
'dev-profile',
|
|
166
|
-
]);
|
|
153
|
+
const MIXDOG_SECRET_DIRECTORIES = new Set(['secrets', 'mcp-oauth', 'provider-accounts', 'relay-data', 'dev-profile']);
|
|
167
154
|
const MIXDOG_SECRET_FILE =
|
|
168
155
|
/^\.?(mixdog-config|provider-accounts|relay-hook-device|auth\.json|[a-z0-9-]+-oauth(-credentials)?\.json|[a-z0-9-]+-bridge\.json)/;
|
|
169
156
|
const MIXDOG_SECRET_EXTENSIONS = new Set(['.dpapi', '.token']);
|
|
@@ -177,7 +164,7 @@ function mixdogSecret(rest) {
|
|
|
177
164
|
|
|
178
165
|
/** The segments of a path as the rules compare them: no extended-path prefix,
|
|
179
166
|
* no NTFS stream suffix, no trailing dots or spaces, lower case. */
|
|
180
|
-
|
|
167
|
+
function sensitivePathSegments(path) {
|
|
181
168
|
return String(path)
|
|
182
169
|
.replace(/^[\\/]{2}[?.][\\/]/, '')
|
|
183
170
|
.split(/[\\/]+/)
|
|
@@ -198,30 +185,21 @@ function hasRun(segments, run) {
|
|
|
198
185
|
}
|
|
199
186
|
|
|
200
187
|
/**
|
|
201
|
-
* Whether `path` is denied under `within`: scopes `{ root: segments
|
|
202
|
-
*
|
|
203
|
-
*
|
|
204
|
-
* true`, everything but `except`). Pass both the lexical and canonical form.
|
|
205
|
-
* `envTemplates`: a user-initiated remote open may read the dotenv
|
|
206
|
-
* templates; nothing else gets that allowance.
|
|
188
|
+
* Whether `path` is denied under `within`: scopes `{ root: segments }` —
|
|
189
|
+
* Mixdog's home and data folders, where only their secret subset is denied.
|
|
190
|
+
* Pass both the lexical and canonical form.
|
|
207
191
|
*/
|
|
208
|
-
export function matchesSensitivePath(path, within = []
|
|
192
|
+
export function matchesSensitivePath(path, within = []) {
|
|
209
193
|
const segments = sensitivePathSegments(resolve(path));
|
|
210
194
|
const name = segments[segments.length - 1] ?? '';
|
|
211
195
|
if (segments.some((segment) => SECRET_DIRECTORIES.has(segment))) return true;
|
|
212
|
-
if (secretFileName(name
|
|
213
|
-
|
|
214
|
-
({ root }) =>
|
|
196
|
+
if (secretFileName(name) || SECRET_SEQUENCES.some((run) => hasRun(segments, run))) return true;
|
|
197
|
+
return within.some(
|
|
198
|
+
({ root }) =>
|
|
199
|
+
root.length > 0 &&
|
|
200
|
+
root.every((segment, index) => segments[index] === segment) &&
|
|
201
|
+
mixdogSecret(segments.slice(root.length))
|
|
215
202
|
);
|
|
216
|
-
// Mixdog's home and data folders follow Mixdog's own rule even inside a
|
|
217
|
-
// registered private root: a dev or isolated profile keeps them in the app
|
|
218
|
-
// profile, and the tool output and media there stay reachable.
|
|
219
|
-
const own = containing.filter(({ whole }) => !whole);
|
|
220
|
-
if (own.length) return own.some(({ root }) => mixdogSecret(segments.slice(root.length)));
|
|
221
|
-
return containing.some(({ root, except }) => {
|
|
222
|
-
const rest = segments.slice(root.length);
|
|
223
|
-
return rest.length > 0 && !except.includes(rest[0]);
|
|
224
|
-
});
|
|
225
203
|
}
|
|
226
204
|
|
|
227
205
|
function realOrSelf(path) {
|
|
@@ -241,7 +219,7 @@ export function mixdogSensitiveScopes(extraData = []) {
|
|
|
241
219
|
const scopes = [];
|
|
242
220
|
for (const root of roots) {
|
|
243
221
|
for (const form of new Set([resolve(root), realOrSelf(resolve(root))])) {
|
|
244
|
-
scopes.push({ root: sensitivePathSegments(form)
|
|
222
|
+
scopes.push({ root: sensitivePathSegments(form) });
|
|
245
223
|
}
|
|
246
224
|
}
|
|
247
225
|
return scopes;
|
|
@@ -13,7 +13,40 @@ export function onSessionActivityChange(listener) {
|
|
|
13
13
|
}
|
|
14
14
|
|
|
15
15
|
export function emitSessionActivityChange(sessionId = '') {
|
|
16
|
-
|
|
16
|
+
emit(listeners, sessionId);
|
|
17
|
+
}
|
|
18
|
+
|
|
19
|
+
// A turn started on a session: the one point every turn passes through, whoever
|
|
20
|
+
// started it. Unlike the activity pulse it never fires on a settle or a wait.
|
|
21
|
+
const turnStartListeners = new Set();
|
|
22
|
+
|
|
23
|
+
export function onSessionTurnStart(listener) {
|
|
24
|
+
if (typeof listener !== 'function') return () => {};
|
|
25
|
+
turnStartListeners.add(listener);
|
|
26
|
+
return () => turnStartListeners.delete(listener);
|
|
27
|
+
}
|
|
28
|
+
|
|
29
|
+
export function emitSessionTurnStart(sessionId = '') {
|
|
30
|
+
emit(turnStartListeners, sessionId);
|
|
31
|
+
}
|
|
32
|
+
|
|
33
|
+
// A live session started or stopped showing an approval card waiting on the
|
|
34
|
+
// user (its runtime state's `toolApproval`): the session catalog, which
|
|
35
|
+
// stamps that on each row, listens and re-reads.
|
|
36
|
+
const approvalCardListeners = new Set();
|
|
37
|
+
|
|
38
|
+
export function onSessionApprovalCardChange(listener) {
|
|
39
|
+
if (typeof listener !== 'function') return () => {};
|
|
40
|
+
approvalCardListeners.add(listener);
|
|
41
|
+
return () => approvalCardListeners.delete(listener);
|
|
42
|
+
}
|
|
43
|
+
|
|
44
|
+
export function emitSessionApprovalCardChange(sessionId = '') {
|
|
45
|
+
emit(approvalCardListeners, sessionId);
|
|
46
|
+
}
|
|
47
|
+
|
|
48
|
+
function emit(targets, sessionId) {
|
|
49
|
+
for (const listener of [...targets]) {
|
|
17
50
|
try {
|
|
18
51
|
listener(String(sessionId || ''));
|
|
19
52
|
} catch {
|
|
@@ -4,7 +4,7 @@ export type SessionSidebarChange =
|
|
|
4
4
|
| { archived: true; cascade: false }
|
|
5
5
|
| { forgotten: true };
|
|
6
6
|
export type SessionSidebarHost = (sessionId: unknown, change: unknown) => unknown;
|
|
7
|
-
export type SessionSidebarState = { archived: Record<string, number> };
|
|
7
|
+
export type SessionSidebarState = { archived: Record<string, number>; favorites?: Record<string, number> };
|
|
8
8
|
export function registerSessionSidebarHost(
|
|
9
9
|
host: SessionSidebarHost,
|
|
10
10
|
options?: { state?: (() => SessionSidebarState | Promise<SessionSidebarState>) | null }
|
|
@@ -41,11 +41,15 @@ export async function applySessionSidebarChange(sessionId, change) {
|
|
|
41
41
|
await active.host(sessionId, change);
|
|
42
42
|
}
|
|
43
43
|
|
|
44
|
-
/** The active desktop's flags, `{ archived }` (session id →
|
|
45
|
-
*
|
|
44
|
+
/** The active desktop's flags, `{ archived, favorites? }` (session id →
|
|
45
|
+
* epoch-ms stamp; `favorites` only from a desktop that reports them); null
|
|
46
|
+
* when no desktop (or one without a state reader) is attached. */
|
|
46
47
|
export async function readSessionSidebarState() {
|
|
47
48
|
const active = hosts().at(-1);
|
|
48
49
|
if (!active?.state) return null;
|
|
49
50
|
const state = await active.state();
|
|
50
|
-
return {
|
|
51
|
+
return {
|
|
52
|
+
archived: { ...state?.archived },
|
|
53
|
+
...(state?.favorites && typeof state.favorites === 'object' ? { favorites: { ...state.favorites } } : {}),
|
|
54
|
+
};
|
|
51
55
|
}
|
|
@@ -0,0 +1,18 @@
|
|
|
1
|
+
// What must land in a session's transcript right before its next turn's
|
|
2
|
+
// prompt, whoever starts that turn: the daemon registers one prelude
|
|
3
|
+
// (agent-home-delivery.mjs postBeforeAgentTurn) and the prompt queue's drain
|
|
4
|
+
// (tui/session/session-flow/drain.mjs) awaits it before presenting a batch.
|
|
5
|
+
|
|
6
|
+
let prelude = null;
|
|
7
|
+
|
|
8
|
+
/** Register `fn(sessionId)` (null or a promise per turn); a non-function clears it. */
|
|
9
|
+
export function setSessionTurnPrelude(fn) {
|
|
10
|
+
prelude = typeof fn === 'function' ? fn : null;
|
|
11
|
+
}
|
|
12
|
+
|
|
13
|
+
/** The prelude's work before a turn of `sessionId`: a promise, or null when
|
|
14
|
+
* there is none (no await, so a turn with nothing owed starts as before). */
|
|
15
|
+
export function sessionTurnPrelude(sessionId) {
|
|
16
|
+
if (!prelude || !sessionId) return null;
|
|
17
|
+
return prelude(String(sessionId)) || null;
|
|
18
|
+
}
|
|
@@ -526,20 +526,7 @@ function summarizeCodeGraphResult({ text, translate }) {
|
|
|
526
526
|
return null;
|
|
527
527
|
}
|
|
528
528
|
|
|
529
|
-
function summarizeFetchResult({ text
|
|
530
|
-
// Channel `fetch` (Discord message fetch — args carry channel/messageId/limit,
|
|
531
|
-
// never url/uri) is not a WEB fetch: the status/size probes would miss and
|
|
532
|
-
// the result fall to raw JSON, so it takes the generic JSON/text summarizer.
|
|
533
|
-
if (normalized === 'fetch') {
|
|
534
|
-
const a = parseToolArgs(args);
|
|
535
|
-
const isChannelFetch =
|
|
536
|
-
!firstText(a.url, a.uri) && Boolean(firstText(a.channel, a.channelId, a.chatId, a.messageId) || a.limit != null);
|
|
537
|
-
if (isChannelFetch) {
|
|
538
|
-
const n = countNonEmptyLines(text);
|
|
539
|
-
if (trimmed && looksLineOriented(text) && n > 0) return countLabel(translate, n, 'message');
|
|
540
|
-
return summarizeGenericResult(text, translate);
|
|
541
|
-
}
|
|
542
|
-
}
|
|
529
|
+
function summarizeFetchResult({ text }) {
|
|
543
530
|
// Status: require a status-like context (HTTP NNN, "Status: NNN",
|
|
544
531
|
// or "NNN OK"/"NNN Not Found") rather than any bare 3-digit number.
|
|
545
532
|
const status =
|
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
// Temporary git repositories and data dirs for the worktree suites.
|
|
2
|
+
import '../git-direct.test-support.mjs';
|
|
2
3
|
import { execFileSync } from 'node:child_process';
|
|
3
|
-
import { chmodSync, cpSync, existsSync, mkdtempSync, rmSync, writeFileSync, mkdirSync } from 'node:fs';
|
|
4
|
+
import { appendFileSync, chmodSync, cpSync, existsSync, mkdtempSync, rmSync, writeFileSync, mkdirSync } from 'node:fs';
|
|
4
5
|
import { tmpdir } from 'node:os';
|
|
5
6
|
import { dirname, join } from 'node:path';
|
|
6
7
|
|
|
@@ -67,9 +68,13 @@ function repoTemplate(commit) {
|
|
|
67
68
|
if (existing && existsSync(existing)) return existing;
|
|
68
69
|
const dir = tempDir('mixdog-wt-template-');
|
|
69
70
|
git(dir, 'init', '-q', '-b', 'main');
|
|
70
|
-
git
|
|
71
|
-
|
|
72
|
-
git
|
|
71
|
+
// One config write instead of a `git config` process per key. Auto
|
|
72
|
+
// maintenance is off, so no commit (the test's or a merge's) starts a
|
|
73
|
+
// `git maintenance run` child.
|
|
74
|
+
appendFileSync(
|
|
75
|
+
join(dir, '.git', 'config'),
|
|
76
|
+
'[user]\n\tname = Mixdog Test\n\temail = mixdog@example.com\n[core]\n\tautocrlf = false\n[maintenance]\n\tauto = false\n'
|
|
77
|
+
);
|
|
73
78
|
if (commit) commitFile(dir, 'README.md', 'hello\n', 'initial');
|
|
74
79
|
templates.set(commit, dir);
|
|
75
80
|
return dir;
|
|
@@ -262,10 +262,19 @@ function oldestRowsOverCap(rows) {
|
|
|
262
262
|
function formatRun(value) {
|
|
263
263
|
const cursor = Number.isSafeInteger(value?.cursor) ? value.cursor : 0;
|
|
264
264
|
const tab = value?.tab !== undefined ? ` tab=${value.tab}` : '';
|
|
265
|
+
let where = '';
|
|
266
|
+
if (value?.shown === true) where = 'The terminal is shown in the desktop window.';
|
|
267
|
+
else if (value?.shown === false) {
|
|
268
|
+
where =
|
|
269
|
+
'The terminal is not shown in the desktop window right now (a paired phone may still show it); it keeps running.';
|
|
270
|
+
}
|
|
265
271
|
return [
|
|
266
272
|
`Terminal run: ${value?.id || `tab ${value?.tab}`} · ${tabLabel(value)} · cursor=${cursor}`,
|
|
267
273
|
`Command sent to the side terminal. Read its output with action=read${tab} since=${cursor}.`,
|
|
268
|
-
|
|
274
|
+
where,
|
|
275
|
+
]
|
|
276
|
+
.filter(Boolean)
|
|
277
|
+
.join('\n');
|
|
269
278
|
}
|
|
270
279
|
|
|
271
280
|
async function readJson(response) {
|