mixdog 1.3.1 → 1.3.3
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/README.md +8 -4
- package/package.json +8 -8
- package/scripts/native-release-status.mjs +238 -0
- package/scripts/verify-runtime-dependency-versions.mjs +158 -0
- package/src/agents/reviewer/AGENT.md +5 -7
- package/src/agents/reviewer/agent.json +1 -1
- package/src/defaults/shipped-history.json +9 -1
- package/src/defaults/skills/browser-use/SKILL.md +6 -3
- package/src/defaults/skills/local-provider/SKILL.md +33 -6
- package/src/defaults/skills/pptx/scripts/qc-pages.mjs +3 -2
- package/src/defaults/skills/setup/SKILL.md +23 -1
- package/src/defaults/skills/setup/references/actions.md +50 -3
- package/src/defaults/skills/setup/references/surfaces.md +23 -3
- package/src/defaults/skills/setup-guide/SKILL.md +7 -3
- package/src/defaults/skills/visualize/SKILL.md +9 -6
- package/src/defaults/skills/visualize/references/craft.md +8 -1
- package/src/help.mjs +1 -1
- package/src/lib/keychain-cjs.cjs +4 -1
- package/src/lib/rules-builder.cjs +14 -7
- package/src/rules/agent-mode/AGENT_MODE.md +8 -5
- package/src/rules/agent-mode/MIXY.md +6 -8
- package/src/rules/lead/LEAD.md +7 -10
- package/src/rules/shared/10-tool-workflow.md +4 -0
- package/src/rules/shared/20-research.md +2 -0
- package/src/rules/shared/45-destructive.md +5 -1
- package/src/rules/shared/60-verification.md +6 -4
- package/src/runtime/agent/orchestrator/agent-runtime/agent-dispatch/preset.mjs +5 -0
- package/src/runtime/agent/orchestrator/agent-runtime/agent-dispatch/session-prep.mjs +8 -0
- package/src/runtime/agent/orchestrator/agent-runtime/agent-progress-watchdog.mjs +6 -1
- package/src/runtime/agent/orchestrator/agent-runtime/cache-strategy.mjs +114 -26
- package/src/runtime/agent/orchestrator/agent-runtime/maintenance-route.mjs +8 -0
- package/src/runtime/agent/orchestrator/agent-runtime/room-supervisor-completion.mjs +5 -4
- package/src/runtime/agent/orchestrator/agent-trace.mjs +3 -0
- package/src/runtime/agent/orchestrator/cache-break-trace.mjs +4 -0
- package/src/runtime/agent/orchestrator/config-presets.mjs +2 -0
- package/src/runtime/agent/orchestrator/config-storage.mjs +10 -3
- package/src/runtime/agent/orchestrator/config.mjs +11 -0
- package/src/runtime/agent/orchestrator/context/role-instructions.mjs +9 -2
- package/src/runtime/agent/orchestrator/mcp/agent-connectors.mjs +97 -5
- package/src/runtime/agent/orchestrator/mcp/child-tree.mjs +6 -10
- package/src/runtime/agent/orchestrator/mcp/client.mjs +7 -4
- package/src/runtime/agent/orchestrator/providers/anthropic-api-request.mjs +84 -32
- package/src/runtime/agent/orchestrator/providers/anthropic-max-tokens.mjs +38 -11
- package/src/runtime/agent/orchestrator/providers/anthropic-messages.mjs +15 -3
- package/src/runtime/agent/orchestrator/providers/anthropic-model-resolve.mjs +1 -1
- package/src/runtime/agent/orchestrator/providers/anthropic-oauth-client-version.mjs +1 -1
- package/src/runtime/agent/orchestrator/providers/anthropic-oauth.mjs +84 -42
- package/src/runtime/agent/orchestrator/providers/anthropic-turn-result.mjs +2 -0
- package/src/runtime/agent/orchestrator/providers/anthropic.mjs +14 -1
- package/src/runtime/agent/orchestrator/providers/antigravity-request.mjs +6 -2
- package/src/runtime/agent/orchestrator/providers/auto-effort.mjs +27 -8
- package/src/runtime/agent/orchestrator/providers/cursor-client-version.mjs +1 -1
- package/src/runtime/agent/orchestrator/providers/cursor-wire.mjs +31 -4
- package/src/runtime/agent/orchestrator/providers/cursor.mjs +4 -1
- package/src/runtime/agent/orchestrator/providers/custom-provider-config.mjs +14 -1
- package/src/runtime/agent/orchestrator/providers/custom-provider.mjs +11 -8
- package/src/runtime/agent/orchestrator/providers/grok-client-version.mjs +1 -1
- package/src/runtime/agent/orchestrator/providers/lib/anthropic-prefix-trace.mjs +76 -0
- package/src/runtime/agent/orchestrator/providers/lib/anthropic-request-utils.mjs +16 -0
- package/src/runtime/agent/orchestrator/providers/lib/tail-reminder-split.mjs +13 -0
- package/src/runtime/agent/orchestrator/providers/media-normalization.mjs +16 -1
- package/src/runtime/agent/orchestrator/providers/mixdog-local.mjs +3 -1
- package/src/runtime/agent/orchestrator/providers/model-proxy-provider.mjs +1 -0
- package/src/runtime/agent/orchestrator/providers/openai-codex-metadata.mjs +5 -5
- package/src/runtime/agent/orchestrator/providers/openai-compat-models.mjs +9 -5
- package/src/runtime/agent/orchestrator/providers/openai-compat-responses.mjs +9 -1
- package/src/runtime/agent/orchestrator/providers/openai-compat-wire.mjs +2 -1
- package/src/runtime/agent/orchestrator/providers/openai-direct-request.mjs +30 -3
- package/src/runtime/agent/orchestrator/providers/openai-model-capabilities.mjs +1 -1
- package/src/runtime/agent/orchestrator/providers/openai-ws.mjs +3 -1
- package/src/runtime/agent/orchestrator/providers/opencode-go-oauth.mjs +61 -8
- package/src/runtime/agent/orchestrator/providers/registry.mjs +19 -0
- package/src/runtime/agent/orchestrator/providers/wire-gateway.mjs +13 -1
- package/src/runtime/agent/orchestrator/runtime-core/config-helpers.mjs +90 -24
- package/src/runtime/agent/orchestrator/runtime-core/fs-utils.mjs +8 -1
- package/src/runtime/agent/orchestrator/runtime-core/goal-mutations.mjs +1 -1
- package/src/runtime/agent/orchestrator/runtime-core/goal-runtime.mjs +5 -0
- package/src/runtime/agent/orchestrator/runtime-core/goal-storage.mjs +68 -22
- package/src/runtime/agent/orchestrator/runtime-core/goal-tool-exec.mjs +1 -1
- package/src/runtime/agent/orchestrator/runtime-core/goal-turns.mjs +10 -1
- package/src/runtime/agent/orchestrator/runtime-core/orchestration.mjs +4 -6
- package/src/runtime/agent/orchestrator/runtime-core/route-context-state.mjs +81 -0
- package/src/runtime/agent/orchestrator/runtime-core/session-text.mjs +2 -1
- package/src/runtime/agent/orchestrator/runtime-core/tool-catalog.mjs +52 -6
- package/src/runtime/agent/orchestrator/runtime-core/tool-defs.mjs +35 -13
- package/src/runtime/agent/orchestrator/runtime-core/workflow/agents.mjs +7 -6
- package/src/runtime/agent/orchestrator/runtime-core/workflow/context.mjs +1 -1
- package/src/runtime/agent/orchestrator/runtime-core/workflow/packs.mjs +8 -4
- package/src/runtime/agent/orchestrator/runtime-core/workflow.mjs +8 -3
- package/src/runtime/agent/orchestrator/session/agent-loop.mjs +1 -1
- package/src/runtime/agent/orchestrator/session/agent-mode.mjs +0 -6
- package/src/runtime/agent/orchestrator/session/agent-room-supervisor.d.mts +1 -1
- package/src/runtime/agent/orchestrator/session/agent-room-supervisor.mjs +22 -4
- package/src/runtime/agent/orchestrator/session/agent-room.d.mts +30 -2
- package/src/runtime/agent/orchestrator/session/agent-room.mjs +196 -45
- package/src/runtime/agent/orchestrator/session/attachment-preflight.mjs +80 -0
- package/src/runtime/agent/orchestrator/session/catalog-db/schema.mjs +4 -1
- package/src/runtime/agent/orchestrator/session/compact/continuation.mjs +15 -6
- package/src/runtime/agent/orchestrator/session/compact/execution-tail.mjs +45 -19
- package/src/runtime/agent/orchestrator/session/compact/forked-summary.mjs +3 -0
- package/src/runtime/agent/orchestrator/session/compact/messages.mjs +44 -0
- package/src/runtime/agent/orchestrator/session/compact/runner.mjs +185 -37
- package/src/runtime/agent/orchestrator/session/compact/summary-mode.mjs +16 -0
- package/src/runtime/agent/orchestrator/session/compact/summary-route.mjs +87 -0
- package/src/runtime/agent/orchestrator/session/compact/summary.mjs +2 -0
- package/src/runtime/agent/orchestrator/session/compact/text-utils.mjs +6 -2
- package/src/runtime/agent/orchestrator/session/compact/user-message-kind.mjs +4 -1
- package/src/runtime/agent/orchestrator/session/compaction-archive-capture.mjs +1 -1
- package/src/runtime/agent/orchestrator/session/context-utils.mjs +41 -10
- package/src/runtime/agent/orchestrator/session/human-gate/delegation.mjs +240 -0
- package/src/runtime/agent/orchestrator/session/human-gate/gate.mjs +102 -35
- package/src/runtime/agent/orchestrator/session/human-gate/outbound-policy.mjs +20 -0
- package/src/runtime/agent/orchestrator/session/human-gate/requests.mjs +19 -9
- package/src/runtime/agent/orchestrator/session/human-gate/run-policy.mjs +73 -0
- package/src/runtime/agent/orchestrator/session/human-gate/store.mjs +55 -27
- package/src/runtime/agent/orchestrator/session/human-gate/unattended-port.mjs +15 -0
- package/src/runtime/agent/orchestrator/session/loop/compact-policy.mjs +4 -1
- package/src/runtime/agent/orchestrator/session/loop/fresh-context.mjs +199 -109
- package/src/runtime/agent/orchestrator/session/loop/loop-state.mjs +5 -0
- package/src/runtime/agent/orchestrator/session/loop/no-tool-turn/continuation.mjs +2 -0
- package/src/runtime/agent/orchestrator/session/loop/no-tool-turn/empty-nudge.mjs +1 -1
- package/src/runtime/agent/orchestrator/session/loop/no-tool-turn/output-limit.mjs +62 -13
- package/src/runtime/agent/orchestrator/session/loop/no-tool-turn.mjs +22 -6
- package/src/runtime/agent/orchestrator/session/loop/pre-dispatch-deny.mjs +6 -2
- package/src/runtime/agent/orchestrator/session/loop/route-fallback.mjs +5 -5
- package/src/runtime/agent/orchestrator/session/loop/send-phase.mjs +10 -0
- package/src/runtime/agent/orchestrator/session/loop/steering.mjs +3 -0
- package/src/runtime/agent/orchestrator/session/loop/tool-exec/before-hook.mjs +33 -8
- package/src/runtime/agent/orchestrator/session/loop/tool-exec/call-prep.mjs +5 -0
- package/src/runtime/agent/orchestrator/session/loop/tool-exec/dispatch.mjs +27 -0
- package/src/runtime/agent/orchestrator/session/loop/tool-exec.mjs +2 -1
- package/src/runtime/agent/orchestrator/session/loop/tool-exposure-migration.mjs +12 -0
- package/src/runtime/agent/orchestrator/session/loop/tool-helpers.mjs +11 -5
- package/src/runtime/agent/orchestrator/session/manager/agent-listing-notice.mjs +102 -0
- package/src/runtime/agent/orchestrator/session/manager/ask-queue.mjs +2 -0
- package/src/runtime/agent/orchestrator/session/manager/ask-session.mjs +13 -1
- package/src/runtime/agent/orchestrator/session/manager/ask-turn-auto-effort.mjs +13 -2
- package/src/runtime/agent/orchestrator/session/manager/ask-turn-execute.mjs +27 -14
- package/src/runtime/agent/orchestrator/session/manager/ask-turn-prepare.mjs +14 -0
- package/src/runtime/agent/orchestrator/session/manager/ask-turn-settle.mjs +4 -0
- package/src/runtime/agent/orchestrator/session/manager/ask-turn-steering-drain.mjs +1 -0
- package/src/runtime/agent/orchestrator/session/manager/compaction-runner.mjs +44 -18
- package/src/runtime/agent/orchestrator/session/manager/idle-cleanup.mjs +32 -1
- package/src/runtime/agent/orchestrator/session/manager/orchestration-mode-switch-notice.mjs +115 -0
- package/src/runtime/agent/orchestrator/session/manager/owner-route-fallbacks.mjs +42 -0
- package/src/runtime/agent/orchestrator/session/manager/pending-message-entry.mjs +6 -0
- package/src/runtime/agent/orchestrator/session/manager/pending-messages.mjs +3 -9
- package/src/runtime/agent/orchestrator/session/manager/pending-spool-file.mjs +1 -1
- package/src/runtime/agent/orchestrator/session/manager/pending-spool-shard.mjs +5 -3
- package/src/runtime/agent/orchestrator/session/manager/prompt-utils.mjs +36 -0
- package/src/runtime/agent/orchestrator/session/manager/rules-cache.mjs +40 -1
- package/src/runtime/agent/orchestrator/session/manager/runtime-liveness.mjs +12 -0
- package/src/runtime/agent/orchestrator/session/manager/runtime-state.mjs +20 -0
- package/src/runtime/agent/orchestrator/session/manager/session-crud.mjs +28 -9
- package/src/runtime/agent/orchestrator/session/manager/session-lifecycle.mjs +55 -27
- package/src/runtime/agent/orchestrator/session/manager/session-prompt-composition.mjs +4 -9
- package/src/runtime/agent/orchestrator/session/manager/session-record.mjs +14 -1
- package/src/runtime/agent/orchestrator/session/manager/session-tool-surface.mjs +7 -3
- package/src/runtime/agent/orchestrator/session/manager/turn-checkpoint-journal.mjs +6 -15
- package/src/runtime/agent/orchestrator/session/manager/turn-interruption.mjs +24 -8
- package/src/runtime/agent/orchestrator/session/manager/workflow-switch-notice.mjs +94 -13
- package/src/runtime/agent/orchestrator/session/pre-send-compact.mjs +52 -11
- package/src/runtime/agent/orchestrator/session/provider-prefix-guard.mjs +21 -3
- package/src/runtime/agent/orchestrator/session/routine-result.d.mts +18 -3
- package/src/runtime/agent/orchestrator/session/routine-result.mjs +126 -12
- package/src/runtime/agent/orchestrator/session/runtime-user-context.mjs +3 -1
- package/src/runtime/agent/orchestrator/session/send-with-recovery.mjs +16 -3
- package/src/runtime/agent/orchestrator/session/side-question-store.mjs +3 -1
- package/src/runtime/agent/orchestrator/session/store/canonical-reader.mjs +41 -7
- package/src/runtime/agent/orchestrator/session/store/lifecycle-barriers.mjs +10 -2
- package/src/runtime/agent/orchestrator/session/store/load-cache.mjs +14 -2
- package/src/runtime/agent/orchestrator/session/store/session-delete.mjs +15 -1
- package/src/runtime/agent/orchestrator/session/store/sweep/stale-sweep.mjs +5 -4
- package/src/runtime/agent/orchestrator/session/store/sweep/sub-agent-retention.mjs +140 -0
- package/src/runtime/agent/orchestrator/session/store/sweep/sweep-plan.mjs +5 -0
- package/src/runtime/agent/orchestrator/session/store/sweep/sweep-record.mjs +15 -5
- package/src/runtime/agent/orchestrator/session/store/sweep/sweep-row.mjs +49 -1
- package/src/runtime/agent/orchestrator/session/store-summary-catalog.mjs +80 -1
- package/src/runtime/agent/orchestrator/session/store-summary-index.mjs +40 -5
- package/src/runtime/agent/orchestrator/session/store-summary-last-message.mjs +47 -2
- package/src/runtime/agent/orchestrator/session/store-summary-reader.mjs +33 -1
- package/src/runtime/agent/orchestrator/session/store-summary-visibility.d.mts +5 -0
- package/src/runtime/agent/orchestrator/session/store-summary-visibility.mjs +37 -0
- package/src/runtime/agent/orchestrator/session/store.mjs +1 -0
- package/src/runtime/agent/orchestrator/session/tail-reminder-rules.mjs +125 -0
- package/src/runtime/agent/orchestrator/session/tool-batch/finalize.mjs +4 -0
- package/src/runtime/agent/orchestrator/session/tool-result-offload.mjs +78 -43
- package/src/runtime/agent/orchestrator/session/transcript-restore/restore.mjs +35 -5
- package/src/runtime/agent/orchestrator/session/transcript-restore/user-text.mjs +42 -2
- package/src/runtime/agent/orchestrator/tools/builtin/advisory-lock.mjs +47 -14
- package/src/runtime/agent/orchestrator/tools/builtin/atomic-write.mjs +25 -5
- package/src/runtime/agent/orchestrator/tools/builtin/bash-tool/backgrounded-result.mjs +6 -3
- package/src/runtime/agent/orchestrator/tools/builtin/bash-tool/completed-result.mjs +13 -14
- package/src/runtime/agent/orchestrator/tools/builtin/bash-tool/transport-artifacts.mjs +9 -0
- package/src/runtime/agent/orchestrator/tools/builtin/bash-tool.mjs +19 -14
- package/src/runtime/agent/orchestrator/tools/builtin/builtin-tools.mjs +7 -1
- package/src/runtime/agent/orchestrator/tools/builtin/git-command-tool.mjs +127 -20
- package/src/runtime/agent/orchestrator/tools/builtin/git-worktree-tool.mjs +557 -0
- package/src/runtime/agent/orchestrator/tools/builtin/lib/shell-job-records.mjs +10 -12
- package/src/runtime/agent/orchestrator/tools/builtin/pdf-test-fixtures.mjs +5 -3
- package/src/runtime/agent/orchestrator/tools/builtin/read-image.mjs +4 -2
- package/src/runtime/agent/orchestrator/tools/builtin/read-single-media.mjs +47 -2
- package/src/runtime/agent/orchestrator/tools/builtin/read-special-files.mjs +264 -87
- package/src/runtime/agent/orchestrator/tools/builtin/shell-analysis.mjs +10 -4
- package/src/runtime/agent/orchestrator/tools/builtin/shell-jobs.mjs +42 -23
- package/src/runtime/agent/orchestrator/tools/code-graph/constants.mjs +8 -4
- package/src/runtime/agent/orchestrator/tools/lib/shell-descendants.mjs +9 -30
- package/src/runtime/agent/orchestrator/tools/lib/shell-run-settle.mjs +24 -12
- package/src/runtime/agent/orchestrator/tools/lib/shell-run-spawn.mjs +3 -2
- package/src/runtime/agent/orchestrator/tools/lib/shell-run-state.mjs +6 -2
- package/src/runtime/agent/orchestrator/tools/patch/delete-quarantine.mjs +2 -1
- package/src/runtime/agent/orchestrator/tools/patch/replay-capture.mjs +4 -2
- package/src/runtime/agent/orchestrator/tools/patch/rollback-state.mjs +33 -2
- package/src/runtime/agent/orchestrator/tools/patch-manifest.json +13 -13
- package/src/runtime/agent/orchestrator/tools/shell-command.mjs +5 -3
- package/src/runtime/agent/orchestrator/tools/shell-exec-output.mjs +3 -1
- package/src/runtime/attachments/attachment-reference.mjs +155 -0
- package/src/runtime/attachments/legacy-office.mjs +110 -0
- package/src/runtime/attachments/limits.mjs +5 -1
- package/src/runtime/attachments/office-extract.mjs +3 -6
- package/src/runtime/attachments/pdf-extract.mjs +84 -10
- package/src/runtime/attachments/prompt-restore.mjs +70 -0
- package/src/runtime/attachments/store.mjs +109 -33
- package/src/runtime/browser-bridge/client.mjs +73 -20
- package/src/runtime/channels/lib/runtime-paths.mjs +1 -1
- package/src/runtime/channels/lib/scheduler.mjs +14 -7
- package/src/runtime/channels/lib/state-file.mjs +15 -15
- package/src/runtime/channels/lib/voice-runtime-fetcher.mjs +14 -13
- package/src/runtime/channels/lib/webhook/relay-tunnel.mjs +22 -18
- package/src/runtime/channels/lib/webhook/session-dispatch.mjs +14 -36
- package/src/runtime/channels/lib/worker-main.mjs +3 -2
- package/src/runtime/effort-judge/compact-tokenizer.mjs +8 -2
- package/src/runtime/effort-judge/judge-client.mjs +66 -16
- package/src/runtime/effort-judge/model-install.mjs +47 -7
- package/src/runtime/local-provider/asset-installer.mjs +10 -4
- package/src/runtime/local-provider/catalog.mjs +5 -2
- package/src/runtime/local-provider/registered-models.mjs +64 -27
- package/src/runtime/local-provider/server.mjs +2 -3
- package/src/runtime/media/adapters/image-api.mjs +89 -0
- package/src/runtime/media/auth.mjs +64 -1
- package/src/runtime/media/catalog.mjs +66 -4
- package/src/runtime/media/jobs.mjs +4 -0
- package/src/runtime/media/lanes.mjs +46 -5
- package/src/runtime/media/media-usage.mjs +17 -0
- package/src/runtime/media/renditions.mjs +15 -15
- package/src/runtime/media/store.mjs +36 -35
- package/src/runtime/media/tool.mjs +8 -1
- package/src/runtime/memory/lib/cycle-scheduler/health-ledger.mjs +8 -2
- package/src/runtime/memory/lib/embedding-model-files.mjs +30 -7
- package/src/runtime/memory/lib/ingest/transcript-tail.mjs +16 -7
- package/src/runtime/memory/lib/memory-chunk-quality.mjs +14 -2
- package/src/runtime/memory/lib/pg/process.mjs +8 -4
- package/src/runtime/memory/lib/project-id-resolver.mjs +5 -1
- package/src/runtime/memory/lib/runtime-fetcher.mjs +6 -1
- package/src/runtime/office/core/journal.mjs +7 -14
- package/src/runtime/office/design/library/design-library-core.mjs +4 -16
- package/src/runtime/office/pdf/ocr-engine-worker.mjs +37 -0
- package/src/runtime/office/pdf/ocr-engine.mjs +146 -0
- package/src/runtime/office/pdf/pdf-ocr.mjs +51 -21
- package/src/runtime/office/pdf/pdf-render.mjs +30 -18
- package/src/runtime/office/portable/ooxml-validator.mjs +2 -2
- package/src/runtime/office/portable/portable-soffice.mjs +135 -29
- package/src/runtime/shared/agent-avatar-sprites.mjs +23 -8
- package/src/runtime/shared/agent-avatar.mjs +3 -2
- package/src/runtime/shared/agent-kind-migration.mjs +200 -0
- package/src/runtime/shared/agent-message-envelope.d.mts +6 -0
- package/src/runtime/shared/agent-message-envelope.mjs +44 -0
- package/src/runtime/shared/agent-reap-state.d.mts +4 -0
- package/src/runtime/shared/agent-room-host.d.mts +31 -0
- package/src/runtime/shared/agent-room-host.mjs +58 -0
- package/src/runtime/shared/agent-store.mjs +24 -7
- package/src/runtime/shared/agent-task-starts.mjs +27 -0
- package/src/runtime/shared/atomic-file.d.mts +50 -0
- package/src/runtime/shared/atomic-file.mjs +394 -132
- package/src/runtime/shared/background-tasks.mjs +7 -4
- package/src/runtime/shared/bot-hops.mjs +35 -0
- package/src/runtime/shared/bot-split-migration.mjs +1 -1
- package/src/runtime/shared/bounded-download.mjs +4 -2
- package/src/runtime/shared/config.mjs +39 -9
- package/src/runtime/shared/defaults-separation.mjs +6 -2
- package/src/runtime/shared/delegated-approval-host.mjs +74 -0
- package/src/runtime/shared/err-text.mjs +4 -0
- package/src/runtime/shared/error-presentation.mjs +22 -4
- package/src/runtime/shared/file-has-data.mjs +28 -0
- package/src/runtime/shared/llm/usage-ledger-import.mjs +37 -18
- package/src/runtime/shared/pending-agent-work.mjs +54 -0
- package/src/runtime/shared/process-lifecycle.mjs +8 -12
- package/src/runtime/shared/projects.mjs +50 -41
- package/src/runtime/shared/provider-accounts.mjs +17 -12
- package/src/runtime/shared/redact-directories.mjs +18 -0
- package/src/runtime/shared/remove-directory.mjs +28 -0
- package/src/runtime/shared/routine-trigger.d.mts +6 -1
- package/src/runtime/shared/routine-trigger.mjs +14 -6
- package/src/runtime/shared/run-child.d.mts +3 -0
- package/src/runtime/shared/run-child.mjs +24 -3
- package/src/runtime/shared/session-baseline-store.mjs +8 -2
- package/src/runtime/shared/session-sidebar-host.d.mts +11 -2
- package/src/runtime/shared/session-sidebar-host.mjs +16 -3
- package/src/runtime/shared/singleton-owner.mjs +7 -6
- package/src/runtime/shared/skill-tool-dependencies.mjs +4 -10
- package/src/runtime/shared/staged-update.mjs +4 -2
- package/src/runtime/shared/statusline/statusline-segments.mjs +4 -15
- package/src/runtime/shared/tool-execution-contract.mjs +10 -0
- package/src/runtime/shared/tool-work-units.mjs +12 -9
- package/src/runtime/shared/transcript-metadata.mjs +28 -3
- package/src/runtime/shared/transcript-writer/session-record.mjs +7 -6
- package/src/runtime/shared/turn-snapshot-store.mjs +5 -4
- package/src/runtime/shared/turn-snapshot.mjs +277 -124
- package/src/runtime/shared/user-cwd.mjs +9 -4
- package/src/runtime/shared/user-data-guard.mjs +92 -61
- package/src/runtime/shared/utf8-cut.mjs +10 -0
- package/src/runtime/shared/worktrees/activity-gate.mjs +251 -61
- package/src/runtime/shared/worktrees/contract.mjs +11 -3
- package/src/runtime/shared/worktrees/maintenance.mjs +11 -2
- package/src/runtime/shared/worktrees/manager.mjs +419 -52
- package/src/runtime/shared/worktrees/registry.mjs +29 -27
- package/src/runtime/shared/worktrees/tool-host.mjs +39 -0
- package/src/runtime/tidy/core-install.mjs +26 -4
- package/src/runtime/tidy/install.mjs +91 -23
- package/src/runtime/tidy/process.mjs +41 -8
- package/src/runtime/tidy/resolve.mjs +10 -7
- package/src/runtime/web-search/lib/config.mjs +4 -16
- package/src/session-runtime/agent-home-delivery.mjs +542 -204
- package/src/session-runtime/agent-profile-changes.mjs +16 -0
- package/src/session-runtime/agent-room-delivery.mjs +81 -35
- package/src/session-runtime/agent-task-sessions.mjs +811 -179
- package/src/session-runtime/agent-tool-routing.mjs +1 -0
- package/src/session-runtime/boot/apis.mjs +48 -24
- package/src/session-runtime/boot/config.mjs +1 -0
- package/src/session-runtime/boot/facade.mjs +16 -7
- package/src/session-runtime/boot/infrastructure.mjs +14 -2
- package/src/session-runtime/boot/providers.mjs +2 -2
- package/src/session-runtime/boot/shared.mjs +12 -6
- package/src/session-runtime/boot/tools.mjs +23 -3
- package/src/session-runtime/bot-status.mjs +432 -0
- package/src/session-runtime/config-lifecycle/lead-route-sync.mjs +29 -0
- package/src/session-runtime/config-lifecycle.mjs +60 -55
- package/src/session-runtime/cwd-plugins/core-memory-context.mjs +2 -4
- package/src/session-runtime/held-requests.mjs +31 -33
- package/src/session-runtime/internal-tool-executor/bridge-tools.mjs +10 -1
- package/src/session-runtime/internal-tool-executor/feature-tools.mjs +2 -2
- package/src/session-runtime/internal-tool-executor/workspace-tools.mjs +24 -26
- package/src/session-runtime/lifecycle/session-catalog.mjs +12 -2
- package/src/session-runtime/lifecycle/teardown.mjs +11 -3
- package/src/session-runtime/mcp-glue/connect.mjs +100 -186
- package/src/session-runtime/mcp-glue/initial-connect-gate.mjs +57 -0
- package/src/session-runtime/mcp-glue/server-connection.mjs +71 -0
- package/src/session-runtime/media-api.mjs +5 -2
- package/src/session-runtime/model-recency.mjs +1 -0
- package/src/session-runtime/model-route/route-tuning.mjs +4 -1
- package/src/session-runtime/model-route/set-route.mjs +61 -1
- package/src/session-runtime/owned-automation-run.mjs +8 -1
- package/src/session-runtime/provider-models/row-hydration.mjs +8 -1
- package/src/session-runtime/quick-model-rows/model-meta.mjs +2 -0
- package/src/session-runtime/quick-model-rows/provider-rows.mjs +7 -1
- package/src/session-runtime/route-resolution.mjs +16 -8
- package/src/session-runtime/route-state.mjs +1 -59
- package/src/session-runtime/routine-home-turn.mjs +5 -3
- package/src/session-runtime/runtime-bootstrap.mjs +5 -3
- package/src/session-runtime/runtime-paths.mjs +11 -5
- package/src/session-runtime/runtime-review-api.mjs +4 -1
- package/src/session-runtime/runtime-tunables.mjs +6 -0
- package/src/session-runtime/services/agent-tool/close-flow/close-all.mjs +11 -2
- package/src/session-runtime/services/agent-tool/close-flow.mjs +4 -2
- package/src/session-runtime/services/agent-tool/execute-send.mjs +1 -0
- package/src/session-runtime/services/agent-tool/execute-spawn.mjs +1 -0
- package/src/session-runtime/services/agent-tool/job-views/agent-list.mjs +34 -19
- package/src/session-runtime/services/agent-tool/restart-notify.mjs +10 -0
- package/src/session-runtime/services/agent-tool/send-flow.mjs +25 -21
- package/src/session-runtime/services/agent-tool/spawn-flow/job-start.mjs +7 -0
- package/src/session-runtime/services/agent-tool/spawn-flow/spawn-prep.mjs +6 -3
- package/src/session-runtime/services/agent-tool/spawn-flow/spawn-run.mjs +8 -5
- package/src/session-runtime/services/agent-tool/worker-index/row-liveness.mjs +22 -2
- package/src/session-runtime/services/agent-tool/worker-index.mjs +4 -6
- package/src/session-runtime/services/agent-tool.mjs +6 -0
- package/src/session-runtime/services/daemon-crash-capture/daemon-exit-record.mjs +1 -1
- package/src/session-runtime/services/daemon-crash-capture/retention.mjs +4 -3
- package/src/session-runtime/services/hook-bus/config-loader.mjs +14 -5
- package/src/session-runtime/services/hook-bus/rule-file.mjs +49 -11
- package/src/session-runtime/services/plugin-admin.mjs +26 -26
- package/src/session-runtime/services/provider-admin.mjs +26 -6
- package/src/session-runtime/services/schedule-access.mjs +33 -4
- package/src/session-runtime/services/seeds.mjs +4 -3
- package/src/session-runtime/services/usage-dashboard.mjs +1 -1
- package/src/session-runtime/services/usage-stats-model.mjs +2 -1
- package/src/session-runtime/services/worktree-access.mjs +27 -0
- package/src/session-runtime/session-create/materialize.mjs +2 -3
- package/src/session-runtime/session-create/session-options.mjs +15 -2
- package/src/session-runtime/session-hooks.mjs +9 -17
- package/src/session-runtime/settings-builtin-tools-api.mjs +65 -1
- package/src/session-runtime/settings-compaction-api.mjs +86 -39
- package/src/session-runtime/setup-tool/executor.mjs +12 -2
- package/src/session-runtime/setup-tool/settings-contract.mjs +34 -1
- package/src/session-runtime/setup-tool/tool-defs.mjs +20 -3
- package/src/session-runtime/skills-api.mjs +12 -5
- package/src/session-runtime/tool-policy/agent-profile-fold.mjs +349 -0
- package/src/session-runtime/tool-policy/mode-switch.mjs +54 -0
- package/src/session-runtime/tool-policy/pending-switches.mjs +59 -0
- package/src/session-runtime/tool-policy/policy-stage.mjs +183 -0
- package/src/session-runtime/tool-policy/session-switches.mjs +169 -0
- package/src/session-runtime/tool-policy/surface-helpers.mjs +114 -0
- package/src/session-runtime/tool-policy/workflow-overlay.mjs +145 -0
- package/src/session-runtime/tool-policy-refresh.mjs +48 -369
- package/src/session-runtime/tool-surface.mjs +10 -4
- package/src/session-runtime/turn/session-ops.mjs +8 -0
- package/src/session-runtime/turn/turn-run/turn-prepare.mjs +95 -88
- package/src/session-runtime/turn/turn-run/turn-timing.mjs +4 -3
- package/src/session-runtime/turn/turn-run.mjs +7 -2
- package/src/session-runtime/workflow-agents-api/agent-connectors.mjs +54 -7
- package/src/session-runtime/workflow-agents-api/agent-editor.mjs +527 -333
- package/src/session-runtime/workflow-agents-api/agent-route.mjs +85 -63
- package/src/session-runtime/workflow-agents-api/shared.mjs +5 -1
- package/src/session-runtime/workflow-agents-api/style-and-mode.mjs +14 -1
- package/src/session-runtime/workflow-agents-api/workflow-packs.mjs +6 -3
- package/src/session-runtime/workspace/transition-steps.mjs +1 -18
- package/src/session-runtime/workspace/transitions.mjs +279 -67
- package/src/session-runtime/workspace/workspace-state.mjs +7 -6
- package/src/session-runtime/workspace/worktree-tool.mjs +194 -0
- package/src/standalone/daemon-agent-control.mjs +4 -1
- package/src/standalone/daemon-lag-profiler.mjs +3 -1
- package/src/standalone/daemon-session-control.mjs +173 -15
- package/src/standalone/daemon-telemetry.mjs +9 -0
- package/src/standalone/daemon.mjs +33 -10
- package/src/standalone/local-session-runtime.mjs +33 -16
- package/src/standalone/session-client.mjs +7 -2
- package/src/standalone/session-protocol.mjs +8 -0
- package/src/standalone/session-runtime-inline-host/boot-loaders.mjs +2 -0
- package/src/standalone/session-service/agent-route-follow.mjs +69 -0
- package/src/standalone/session-service/session-calls/session-turns.mjs +33 -4
- package/src/standalone/session-service.mjs +21 -0
- package/src/standalone/session-state-projection.mjs +19 -9
- package/src/standalone/session-tool-output.mjs +36 -9
- package/src/standalone/staged-import.mjs +47 -0
- package/src/tui/App.jsx +2 -0
- package/src/tui/app/maintenance-pickers/auto-clear-picker.mjs +19 -13
- package/src/tui/app/message-selector.mjs +15 -2
- package/src/tui/app/model-options.mjs +16 -2
- package/src/tui/app/model-picker.mjs +2 -1
- package/src/tui/app/route-pickers.mjs +3 -1
- package/src/tui/app/settings-picker/settings-rows.mjs +2 -2
- package/src/tui/app/slash-commands.mjs +1 -1
- package/src/tui/app/usage-context-panels/context-model.mjs +8 -2
- package/src/tui/dist/index.mjs +617 -369
- package/src/tui/paste-attachments.mjs +0 -1
- package/src/tui/paste-text-policy.mjs +4 -5
- package/src/tui/prompt-history-store.mjs +16 -21
- package/src/tui/session/agent-job-feed/execution-delivery.mjs +4 -1
- package/src/tui/session/context-state.mjs +4 -0
- package/src/tui/session/notice-surface.mjs +3 -0
- package/src/tui/session/session-api/intake/abort.mjs +52 -18
- package/src/tui/session/session-api/intake/rewind.mjs +39 -4
- package/src/tui/session/session-api/media.mjs +2 -1
- package/src/tui/session/session-api/routes-agent-definitions.mjs +76 -0
- package/src/tui/session/session-api/routes-write.mjs +203 -0
- package/src/tui/session/session-api/routes.mjs +23 -247
- package/src/tui/session/session-api/settings.mjs +14 -1
- package/src/tui/session/session-flow/drain.mjs +28 -3
- package/src/tui/session/session-flow/failed-turn-rewind.mjs +4 -2
- package/src/tui/session/session-flow/queue/take-entries.mjs +8 -0
- package/src/tui/session/session-flow/steering.mjs +8 -3
- package/src/tui/session/tool-approval.mjs +142 -14
- package/src/tui/session/transcript-intake.mjs +2 -0
- package/src/tui/session/turn.mjs +23 -14
- package/src/tui/transcript-tool-failures.mjs +8 -2
- package/src/vendor/statusline/src/gateway/route-meta.mjs +7 -8
- package/src/vendor/statusline/src/gateway/session-routes.mjs +6 -7
- package/src/rules/agent-mode/ROUTINE.md +0 -6
- package/src/session-runtime/room-member-gate.mjs +0 -48
|
@@ -1,6 +1,10 @@
|
|
|
1
1
|
// Text/token/message helpers and secret redaction for compaction.
|
|
2
2
|
import { createHash } from 'node:crypto';
|
|
3
|
-
import {
|
|
3
|
+
import {
|
|
4
|
+
attachmentTextForPart,
|
|
5
|
+
isAttachmentReference,
|
|
6
|
+
isPermanentAttachmentError,
|
|
7
|
+
} from '../../../../attachments/store.mjs';
|
|
4
8
|
import { estimateMessagesTokens } from '../context-utils.mjs';
|
|
5
9
|
import { inlineFileKind } from '../../../../shared/inline-file-kind.mjs';
|
|
6
10
|
|
|
@@ -44,7 +48,7 @@ const SENSITIVE_RAW_KEY_RES = [
|
|
|
44
48
|
// string. Consumes the WHOLE value after the key — spaces, `Bearer `/`Basic `
|
|
45
49
|
// scheme words, quoted values with internal spaces, and `;`-separated cookie
|
|
46
50
|
// pairs — so no secret fragment survives.
|
|
47
|
-
function redactRawSecretString(text) {
|
|
51
|
+
export function redactRawSecretString(text) {
|
|
48
52
|
let value = String(text ?? '');
|
|
49
53
|
if (!value) return value;
|
|
50
54
|
for (const keyRe of SENSITIVE_RAW_KEY_RES) {
|
|
@@ -5,6 +5,7 @@
|
|
|
5
5
|
import { SUMMARY_PREFIX_ANCHOR } from './summary-anchor.mjs';
|
|
6
6
|
import { isImportedMessage } from '../imported-message.mjs';
|
|
7
7
|
import {
|
|
8
|
+
CONTROL_MARKER_RE,
|
|
8
9
|
isInternalRuntimeNotificationText,
|
|
9
10
|
isModelVisibleToolCompletionWrapper,
|
|
10
11
|
} from '../../../../shared/tool-execution-contract.mjs';
|
|
@@ -86,6 +87,9 @@ export function isContextAckMessage(message, previous = null) {
|
|
|
86
87
|
const SYNTHETIC_USER_SOURCES = new Set([
|
|
87
88
|
// A schedule's output posted into its owner's home thread: untrusted data.
|
|
88
89
|
'schedule-result',
|
|
90
|
+
// A delivered task or channel notification: turn input, never the human's
|
|
91
|
+
// request, whatever its text looks like.
|
|
92
|
+
'task-notification',
|
|
89
93
|
'goal-continuation',
|
|
90
94
|
'goal-closeout',
|
|
91
95
|
'compact-context',
|
|
@@ -132,7 +136,6 @@ export function isActualUserInstructionMessage(message) {
|
|
|
132
136
|
// isActualUserInstructionMessage deliberately keeps treating "[truncated]" as
|
|
133
137
|
// an instruction (compaction tail selection depends on it), so the real-turn
|
|
134
138
|
// predicate below recognizes it on its own.
|
|
135
|
-
const CONTROL_MARKER_RE = /^\[(?:truncated|request interrupted(?: by user(?: for tool use)?| by process restart)?)\]$/i;
|
|
136
139
|
|
|
137
140
|
/** Whether a message stands for real human conversation: a compact summary
|
|
138
141
|
* (it replaced earlier human turns), imported prose, or the user's own
|
|
@@ -226,7 +226,7 @@ const recordExists = (dir, sessionId) => existsSync(join(dir, `${sessionId}.json
|
|
|
226
226
|
* since `epoch` was captured wins. Any outcome but a rename removes the temp
|
|
227
227
|
* file. */
|
|
228
228
|
async function publishArchive({ dir, path, sessionId, text, epoch }) {
|
|
229
|
-
const tmp = join(dir,
|
|
229
|
+
const tmp = join(dir, `${basename(path)}.mixdog-tmp-${randomBytes(4).toString('hex')}`);
|
|
230
230
|
const removeTmp = () => {
|
|
231
231
|
try {
|
|
232
232
|
unlinkSync(tmp);
|
|
@@ -260,24 +260,55 @@ function messageImageAllowance(m) {
|
|
|
260
260
|
if (!m || typeof m !== 'object') return 0;
|
|
261
261
|
return messageImageDescriptors(m).reduce((sum, descriptor) => sum + imageDescriptorAllowance(descriptor), 0);
|
|
262
262
|
}
|
|
263
|
-
// Inline document (PDF) allowance
|
|
264
|
-
//
|
|
265
|
-
//
|
|
263
|
+
// Inline document (PDF) allowance. A native PDF reader turns every page into
|
|
264
|
+
// one image AND sends the text it extracts from that page (the provider PDF
|
|
265
|
+
// docs: each page is billed as an image plus its text, typically
|
|
266
|
+
// 1,500-3,000 text tokens for a dense page). The page image is bounded like
|
|
267
|
+
// any image — the reader downscales it to the vision ceiling — so a page costs
|
|
268
|
+
// at most IMAGE_TOKEN_ALLOWANCE for its picture whatever its file size. A
|
|
269
|
+
// scanned page has no text layer and costs only its picture.
|
|
270
|
+
//
|
|
271
|
+
// The base64 payload itself is excluded from text estimates (see
|
|
266
272
|
// jsonFallbackFromPart), so this allowance is the document's entire cost.
|
|
267
273
|
const FILE_TOKEN_ALLOWANCE_FLOOR = 1_500;
|
|
268
274
|
const FILE_MAX_TOKEN_ALLOWANCE = 300_000;
|
|
269
|
-
//
|
|
270
|
-
//
|
|
271
|
-
//
|
|
272
|
-
const
|
|
275
|
+
// Extracted PDF text is priced at two characters per token: the low end of
|
|
276
|
+
// the documented per-page range for Latin text, and close to the measured
|
|
277
|
+
// density of Korean/CJK text, so neither is read low.
|
|
278
|
+
const PDF_TEXT_CHARS_PER_TOKEN = 2;
|
|
279
|
+
// A PDF that records its page count but not its text layer (older parts, an
|
|
280
|
+
// inline part from a path that did not measure it) keeps the documented
|
|
281
|
+
// mid-range per-page figure.
|
|
282
|
+
const FILE_TOKENS_PER_PAGE_UNMEASURED = 2_500;
|
|
273
283
|
// A PDF that lowers to text costs at most its extraction budget (~4 bytes/token).
|
|
274
284
|
const PDF_TEXT_MAX_TOKENS = Math.ceil(PDF_TEXT_MAX_BYTES / 4);
|
|
285
|
+
// Without a page count only the file size is known: ~bytes/16 (a typical page
|
|
286
|
+
// is 50-100KB) with a floor and a cap. Never used once pages are known — a
|
|
287
|
+
// scan's bytes are its image data, which the reader downscales, so bytes/16
|
|
288
|
+
// priced a 5 MB, 37-page scan at ~330k tokens instead of ~58k.
|
|
275
289
|
function fileDescriptorAllowance(descriptor, nativePdf = true) {
|
|
276
|
-
const
|
|
277
|
-
|
|
278
|
-
|
|
290
|
+
const pages = descriptor.pageCount || 0;
|
|
291
|
+
let native;
|
|
292
|
+
if (pages > 0 && Number.isFinite(descriptor.textChars)) {
|
|
293
|
+
native = pages * IMAGE_TOKEN_ALLOWANCE + Math.ceil(descriptor.textChars / PDF_TEXT_CHARS_PER_TOKEN);
|
|
294
|
+
} else if (pages > 0) {
|
|
295
|
+
native = pages * FILE_TOKENS_PER_PAGE_UNMEASURED;
|
|
296
|
+
} else {
|
|
297
|
+
native = Math.max(FILE_TOKEN_ALLOWANCE_FLOOR, Math.ceil((descriptor.sizeBytes || 0) / 16));
|
|
298
|
+
}
|
|
299
|
+
native = Math.min(FILE_MAX_TOKEN_ALLOWANCE, native);
|
|
279
300
|
return nativePdf ? native : Math.min(native, PDF_TEXT_MAX_TOKENS);
|
|
280
301
|
}
|
|
302
|
+
|
|
303
|
+
/** The tokens one PDF part costs inline (native reader unless `nativePdf` is false). */
|
|
304
|
+
export function pdfPartAllowance(part, { nativePdf = true } = {}) {
|
|
305
|
+
return contentFileDescriptors([part]).reduce((sum, d) => sum + fileDescriptorAllowance(d, nativePdf), 0);
|
|
306
|
+
}
|
|
307
|
+
|
|
308
|
+
/** The tokens one image part costs inline. */
|
|
309
|
+
export function imagePartAllowance(part) {
|
|
310
|
+
return contentImageDescriptors([part]).reduce((sum, d) => sum + imageDescriptorAllowance(d), 0);
|
|
311
|
+
}
|
|
281
312
|
/**
|
|
282
313
|
* How many tokens the native-PDF meter overstates `messages` for a provider
|
|
283
314
|
* that takes PDFs as text (nativePdf === false); subtract it from the
|
|
@@ -0,0 +1,240 @@
|
|
|
1
|
+
// session/human-gate/delegation.mjs
|
|
2
|
+
// Who a delegated session works for, recorded when it is created and re-bound
|
|
3
|
+
// only when another session sends it work (rebindDelegation): a sub-agent (any
|
|
4
|
+
// depth) carries `delegation` on its session record —
|
|
5
|
+
// { rootSessionId, via, path }
|
|
6
|
+
// `rootSessionId` is the top-level conversation the work started in; `via`
|
|
7
|
+
// says what it is: 'coding' (a coding Lead's session or a task session a bot
|
|
8
|
+
// created) or 'bot' (a bot's own home chat or room member session); either way
|
|
9
|
+
// the agents under it mirror their ordinary approval cards into that root, but
|
|
10
|
+
// a 'bot' root's sub-agents are no bot: they ask no bot approval and use no
|
|
11
|
+
// bot grant. `path` is the agent steps from the root down
|
|
12
|
+
// ({ kind: 'agent', agent }).
|
|
13
|
+
// A task session a bot created is an ordinary coding Lead session: top-level,
|
|
14
|
+
// never delegated from the bot, and its sub-agents are rooted at it.
|
|
15
|
+
// It is copied from the parent's record (or bound from the parent itself when
|
|
16
|
+
// the parent is top-level) by the host at creation — never from tool input —
|
|
17
|
+
// so no parent walk happens at gate time. When a session sends an existing
|
|
18
|
+
// worker a new request, the record is re-bound to that sender's root. A
|
|
19
|
+
// record from before this field existed, or one stored in an older shape (a
|
|
20
|
+
// task step in its path, a home/room/run/unknown `via`), is derived again from
|
|
21
|
+
// its parent by the same rules (backfillOf) and kept on the in-memory record,
|
|
22
|
+
// which its next save persists. A session that has a parent link but no valid
|
|
23
|
+
// or derivable root has no delegation (it is no bot's own session either).
|
|
24
|
+
// Work a schedule/webhook run started (a sub-agent, a task session) carries the
|
|
25
|
+
// `run` it was started under on its record, stamped at creation from its
|
|
26
|
+
// creator (never from creation options or tool input) and re-stamped, whether
|
|
27
|
+
// or not a root resolves, when another session sends it work (`run: null` =
|
|
28
|
+
// attended). A record from before the stamp keeps the run it recorded (an old
|
|
29
|
+
// `via: 'run'` delegation, an `agent:<bot>@run` task source, or its parent's
|
|
30
|
+
// run when it recorded no delegation): that is stamped before its delegation is
|
|
31
|
+
// derived again, which would drop it.
|
|
32
|
+
import { agentIdOfHomeSession } from '../agent-home.mjs';
|
|
33
|
+
import { roomMemberOfSession } from '../agent-room.mjs';
|
|
34
|
+
import { loadSession } from '../store/load-session.mjs';
|
|
35
|
+
import { _liveSessions } from '../store/live-state.mjs';
|
|
36
|
+
import { saveSessionAsync } from '../store/save-worker.mjs';
|
|
37
|
+
import { isTurnInFlight, runtimeState } from '../manager/runtime-state.mjs';
|
|
38
|
+
import { unattendedApprovalPort } from './unattended-port.mjs';
|
|
39
|
+
|
|
40
|
+
const VIA = new Set(['coding', 'bot']);
|
|
41
|
+
const MAX_PATH = 32;
|
|
42
|
+
const clean = (value) => (typeof value === 'string' ? value.trim() : '');
|
|
43
|
+
const isRecord = (value) => Boolean(value) && typeof value === 'object' && !Array.isArray(value);
|
|
44
|
+
|
|
45
|
+
const agentOwned = (record) =>
|
|
46
|
+
clean(record?.owner).toLowerCase() === 'agent' || clean(record?.visibility).toLowerCase() === 'agent-only';
|
|
47
|
+
|
|
48
|
+
/** The session that spawned `record`, or '' for a top-level session (a task
|
|
49
|
+
* session a bot created is top-level, like any coding Lead). */
|
|
50
|
+
function parentOf(record) {
|
|
51
|
+
return agentOwned(record) ? clean(record.parentSessionId) || clean(record.ownerSessionId) : '';
|
|
52
|
+
}
|
|
53
|
+
|
|
54
|
+
/** The step a new session adds to its parent's path. */
|
|
55
|
+
const stepOf = (record) => ({ kind: 'agent', agent: clean(record.agent) });
|
|
56
|
+
|
|
57
|
+
/** A recorded delegation, or null when it is malformed or carries a step that
|
|
58
|
+
* is not an agent (a stored task step: derived again, never trusted). */
|
|
59
|
+
function validDelegation(value) {
|
|
60
|
+
if (!isRecord(value) || !clean(value.rootSessionId) || !VIA.has(value.via)) return null;
|
|
61
|
+
if (!Array.isArray(value.path) || value.path.length === 0 || value.path.length > MAX_PATH) return null;
|
|
62
|
+
if (!value.path.every((step) => step?.kind === 'agent')) return null;
|
|
63
|
+
return value;
|
|
64
|
+
}
|
|
65
|
+
|
|
66
|
+
/** The recorded delegation of `record`, else its legacy backfill; null when
|
|
67
|
+
* it has none and none can be derived. */
|
|
68
|
+
function delegationOfRecord(id, record, depth) {
|
|
69
|
+
stampRun(id, record);
|
|
70
|
+
return validDelegation(record.delegation) || backfillOf(id, record, depth);
|
|
71
|
+
}
|
|
72
|
+
|
|
73
|
+
const RUN_TASK_SOURCE = /^agent:([A-Za-z0-9_-]+)@run$/;
|
|
74
|
+
const runStamp = (kind, agentId, name) =>
|
|
75
|
+
clean(agentId)
|
|
76
|
+
? { kind: kind === 'webhook' ? 'webhook' : 'schedule', agentId: clean(agentId), name: String(name || '') }
|
|
77
|
+
: null;
|
|
78
|
+
|
|
79
|
+
/** The run a record from before the `run` stamp was started by, from what it
|
|
80
|
+
* recorded itself: an old `via: 'run'` delegation, a host-set `@run` task
|
|
81
|
+
* source, else — a sub-agent that recorded no delegation — its parent's run. */
|
|
82
|
+
function legacyRunOf(id, record, depth) {
|
|
83
|
+
const old = record.delegation;
|
|
84
|
+
if (isRecord(old) && old.via === 'run' && isRecord(old.run)) {
|
|
85
|
+
return runStamp(old.run.kind, old.rootAgentId || agentIdOfHomeSession(old.rootSessionId), old.run.name);
|
|
86
|
+
}
|
|
87
|
+
const taskBot =
|
|
88
|
+
!agentOwned(record) && clean(record.sourceType) === 'lead'
|
|
89
|
+
? RUN_TASK_SOURCE.exec(clean(record.sourceName))?.[1]
|
|
90
|
+
: '';
|
|
91
|
+
if (taskBot) return runStamp('schedule', taskBot, '');
|
|
92
|
+
const parent = parentOf(record);
|
|
93
|
+
if (!parent || parent === id || isRecord(old) || depth >= MAX_PATH) return null;
|
|
94
|
+
const parentRecord = loadRecord(parent);
|
|
95
|
+
return parentRecord ? stampRun(parent, parentRecord, depth + 1) : null;
|
|
96
|
+
}
|
|
97
|
+
|
|
98
|
+
/** The `run` of `record`: its stamp (`null` = attended), else the run it
|
|
99
|
+
* recorded before stamps existed, kept on the live record. */
|
|
100
|
+
function stampRun(id, record, depth = 0) {
|
|
101
|
+
if (record.run !== undefined) return isRecord(record.run) ? record.run : null;
|
|
102
|
+
const run = legacyRunOf(id, record, depth);
|
|
103
|
+
if (run && _liveSessions.get(id) === record) record.run = run;
|
|
104
|
+
return run;
|
|
105
|
+
}
|
|
106
|
+
|
|
107
|
+
/** The run stamped on session `sessionId` (human-gate/delegation.mjs), or null. */
|
|
108
|
+
export function runStampOf(sessionId) {
|
|
109
|
+
const id = clean(sessionId);
|
|
110
|
+
const record = id ? loadRecord(id) : null;
|
|
111
|
+
return record ? stampRun(id, record) : null;
|
|
112
|
+
}
|
|
113
|
+
|
|
114
|
+
/** The schedule/webhook run `sessionId` is, or inherited from the session that
|
|
115
|
+
* started it, as `{ kind, agentId, name }`; null otherwise (a room member's
|
|
116
|
+
* own session is no run here). */
|
|
117
|
+
function runOfSession(sessionId) {
|
|
118
|
+
const run = unattendedApprovalPort()?.runOf?.(sessionId);
|
|
119
|
+
return run && run.kind !== 'room' ? { kind: run.kind, agentId: run.agentId, name: String(run.name || '') } : null;
|
|
120
|
+
}
|
|
121
|
+
|
|
122
|
+
/**
|
|
123
|
+
* The `run` to stamp on a session being created with `opts`: the run its
|
|
124
|
+
* creator (the spawning session, or the chat a task session was created from)
|
|
125
|
+
* is or inherited, else null. Work a run started stays the run's, however long
|
|
126
|
+
* ago the run ended; it is read from the creator, never from tool input.
|
|
127
|
+
*/
|
|
128
|
+
export function runForNewSession(opts = {}) {
|
|
129
|
+
const creator = parentOf(opts) || clean(opts.taskOrigin?.sessionId);
|
|
130
|
+
return creator && creator !== clean(opts.id) ? runOfSession(creator) : null;
|
|
131
|
+
}
|
|
132
|
+
|
|
133
|
+
function loadRecord(sessionId) {
|
|
134
|
+
try {
|
|
135
|
+
return loadSession(sessionId);
|
|
136
|
+
} catch {
|
|
137
|
+
return null;
|
|
138
|
+
}
|
|
139
|
+
}
|
|
140
|
+
|
|
141
|
+
/** What a top-level session `sessionId` is to a child: a bot's own home chat
|
|
142
|
+
* or room member session ('bot', no record needed, the id names its agent),
|
|
143
|
+
* a record-bearing coding session ('coding'), else nothing. */
|
|
144
|
+
function viaOf(sessionId, record) {
|
|
145
|
+
if (roomMemberOfSession(sessionId) || agentIdOfHomeSession(sessionId)) return 'bot';
|
|
146
|
+
return record && !agentOwned(record) ? 'coding' : null;
|
|
147
|
+
}
|
|
148
|
+
|
|
149
|
+
/** The root of a top-level `parent` for a child taking `step`. */
|
|
150
|
+
function rootFrom(parent, record, step) {
|
|
151
|
+
const via = viaOf(parent, record);
|
|
152
|
+
return via ? { rootSessionId: parent, via, path: [step] } : null;
|
|
153
|
+
}
|
|
154
|
+
|
|
155
|
+
/** The delegation a legacy `record` (id `id`, a parent link, none valid
|
|
156
|
+
* recorded) is given, derived now from its parent, and kept on the live
|
|
157
|
+
* record; null when it cannot be derived. */
|
|
158
|
+
function backfillOf(id, record, depth = 0) {
|
|
159
|
+
const parent = parentOf(record);
|
|
160
|
+
if (!parent || parent === id || depth >= MAX_PATH) return null;
|
|
161
|
+
const parentRecord = loadRecord(parent);
|
|
162
|
+
const step = stepOf(record);
|
|
163
|
+
let delegation = null;
|
|
164
|
+
if (parentOf(parentRecord)) {
|
|
165
|
+
const inherited = delegationOfRecord(parent, parentRecord, depth + 1);
|
|
166
|
+
if (!inherited || inherited.path.length >= MAX_PATH) return null;
|
|
167
|
+
delegation = { ...inherited, path: [...inherited.path, step] };
|
|
168
|
+
} else {
|
|
169
|
+
delegation = rootFrom(parent, parentRecord, step);
|
|
170
|
+
}
|
|
171
|
+
if (delegation && _liveSessions.get(id) === record) record.delegation = delegation;
|
|
172
|
+
return delegation;
|
|
173
|
+
}
|
|
174
|
+
|
|
175
|
+
/**
|
|
176
|
+
* The `delegation` to record on a session being created with `opts` (its
|
|
177
|
+
* owner/visibility, parentSessionId/ownerSessionId and agent), or
|
|
178
|
+
* null: none for a top-level session, none for one whose parent's root cannot
|
|
179
|
+
* be established (it is no bot's session: nothing is asked or mirrored for it).
|
|
180
|
+
*/
|
|
181
|
+
export function delegationForNewSession(opts = {}) {
|
|
182
|
+
const parent = parentOf(opts);
|
|
183
|
+
if (!parent || parent === clean(opts.id)) return null;
|
|
184
|
+
return delegationUnder(parent, stepOf(opts));
|
|
185
|
+
}
|
|
186
|
+
|
|
187
|
+
/** The delegation of a session taking `step` under the session `parent`. */
|
|
188
|
+
function delegationUnder(parent, step) {
|
|
189
|
+
const record = loadRecord(parent);
|
|
190
|
+
if (record && parentOf(record)) {
|
|
191
|
+
// The parent is delegated itself: its recorded root, one step deeper.
|
|
192
|
+
const inherited = delegationOfRecord(parent, record);
|
|
193
|
+
if (!inherited || inherited.path.length >= MAX_PATH) return null;
|
|
194
|
+
return { ...inherited, path: [...inherited.path, step] };
|
|
195
|
+
}
|
|
196
|
+
return rootFrom(parent, record, step);
|
|
197
|
+
}
|
|
198
|
+
|
|
199
|
+
/**
|
|
200
|
+
* Session `callerSessionId` sends the sub-agent or task session `sessionId`
|
|
201
|
+
* new work (`agent send`, a spawn of its tag, a task-session send): from now
|
|
202
|
+
* on it is run-stamped as it would have been created under the caller, and a
|
|
203
|
+
* sub-agent is rooted under the caller's root when one can be established; the
|
|
204
|
+
* record is saved, so a restart keeps it — work an unattended run started is
|
|
205
|
+
* attended when the chat later reuses it, and the reverse. The run is passed on
|
|
206
|
+
* whether or not a root resolves. A turn already running is never made a run
|
|
207
|
+
* mid-turn. Resolves true when the record changed; false when nothing applies
|
|
208
|
+
* (no record, neither a sub-agent nor a task session, no or the same caller,
|
|
209
|
+
* nothing different).
|
|
210
|
+
*/
|
|
211
|
+
export async function rebindDelegation(sessionId, callerSessionId) {
|
|
212
|
+
const id = clean(sessionId);
|
|
213
|
+
const caller = clean(callerSessionId);
|
|
214
|
+
const record = id ? loadRecord(id) : null;
|
|
215
|
+
if (!record || record.closed === true || !caller || caller === id) return false;
|
|
216
|
+
const delegated = Boolean(parentOf(record));
|
|
217
|
+
if (!delegated && !record.taskOrigin) return false;
|
|
218
|
+
const next = delegated ? delegationUnder(caller, stepOf(record)) : null;
|
|
219
|
+
const run = runOfSession(caller);
|
|
220
|
+
const current = stampRun(id, record);
|
|
221
|
+
const same = (a, b) => JSON.stringify(a ?? null) === JSON.stringify(b ?? null);
|
|
222
|
+
const reroot = Boolean(next) && !same(validDelegation(record.delegation), next);
|
|
223
|
+
if (!reroot && same(current, run)) return false;
|
|
224
|
+
if (run && !current && isTurnInFlight(runtimeState.get(id))) return false;
|
|
225
|
+
if (reroot) record.delegation = next;
|
|
226
|
+
if (run || current) record.run = run; // null = attended: legacy provenance no longer applies
|
|
227
|
+
await saveSessionAsync(record, { expectedGeneration: record.generation });
|
|
228
|
+
return true;
|
|
229
|
+
}
|
|
230
|
+
|
|
231
|
+
/** `{ state: 'resolved', ...delegation }` for a sub-agent whose root is known,
|
|
232
|
+
* else `{ state: 'top' }`: a top-level session, one with no record, or one
|
|
233
|
+
* with a parent link and no derivable root (no bot's session either). */
|
|
234
|
+
export function delegationOf(sessionId) {
|
|
235
|
+
const id = clean(sessionId);
|
|
236
|
+
const record = id ? loadRecord(id) : null;
|
|
237
|
+
const parent = record ? parentOf(record) : '';
|
|
238
|
+
const delegation = parent && parent !== id ? delegationOfRecord(id, record) : null;
|
|
239
|
+
return delegation ? { state: 'resolved', ...delegation } : { state: 'top' };
|
|
240
|
+
}
|
|
@@ -9,17 +9,29 @@
|
|
|
9
9
|
// actingBotId); an ordinary coding session's outbound call passes with the
|
|
10
10
|
// hook decision unchanged. Other surfaces (group rooms) call it with their own
|
|
11
11
|
// decision to get the same outbound rule. After the user answered,
|
|
12
|
-
// recordHumanDecision stores a "don't ask again" grant and the audit line.
|
|
13
|
-
//
|
|
14
|
-
//
|
|
12
|
+
// recordHumanDecision stores a "don't ask again" grant and the audit line.
|
|
13
|
+
// Schedule and webhook runs never ask (run-policy.mjs): ordinary actions are
|
|
14
|
+
// allowed, payments and permanent deletes denied. Group-room members register
|
|
15
|
+
// a port here (session-runtime/held-requests.mjs) that turns their asks into
|
|
16
|
+
// held requests when no approval card is attached.
|
|
15
17
|
import { MIXY_AGENT_ID, agentIdOfHomeSession } from '../agent-home.mjs';
|
|
16
18
|
import { serverConnector } from '../../mcp/server-connectors.mjs';
|
|
17
19
|
import { readAgentDefinitionKind } from '../../../../shared/agent-kind.mjs';
|
|
18
20
|
import { isContactAgentId } from '../../../../shared/agent-route-config.mjs';
|
|
19
21
|
import { mixdogRoot, resolvePluginData } from '../../../../shared/plugin-paths.mjs';
|
|
20
22
|
import { roomMemberOfSession } from '../agent-room.mjs';
|
|
21
|
-
import {
|
|
23
|
+
import { agentDisplayName } from '../../context/role-instructions.mjs';
|
|
24
|
+
import { delegationOf } from './delegation.mjs';
|
|
25
|
+
import { setUnattendedApprovalPort, unattendedApprovalPort } from './unattended-port.mjs';
|
|
26
|
+
import { classifyAsDelete, classifyOutboundCall, isRememberableAction } from './outbound-policy.mjs';
|
|
22
27
|
import { humanGateStore } from './store.mjs';
|
|
28
|
+
import {
|
|
29
|
+
AUTOMATION_GRANT_REASON,
|
|
30
|
+
RUN_FORBIDDEN_REASON,
|
|
31
|
+
isGrantedRun,
|
|
32
|
+
isRemoteDeleteInRun,
|
|
33
|
+
savesAutomation,
|
|
34
|
+
} from './run-policy.mjs';
|
|
23
35
|
|
|
24
36
|
export const OUTBOUND_APPROVAL_REASON = 'This sends or posts outside Mixdog; it needs your approval.';
|
|
25
37
|
export const BLOCKED_ACTION_REASON = "This agent's permission blocks this action.";
|
|
@@ -27,46 +39,70 @@ export const BLOCKED_ACTION_REASON = "This agent's permission blocks this action
|
|
|
27
39
|
* payments and deletes never. */
|
|
28
40
|
const outboundRemember = (category) => (isRememberableAction(category) ? ['once', 'agent'] : ['once']);
|
|
29
41
|
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
* run that would need a takeover (session-runtime/held-requests.mjs). */
|
|
34
|
-
export function setUnattendedApprovalPort(port) {
|
|
35
|
-
unattendedPort = port || null;
|
|
36
|
-
}
|
|
37
|
-
export function unattendedApprovalPort() {
|
|
38
|
-
return unattendedPort;
|
|
39
|
-
}
|
|
42
|
+
// The unattended runs' port lives in unattended-port.mjs; its setter and
|
|
43
|
+
// getter are re-exported here.
|
|
44
|
+
export { setUnattendedApprovalPort, unattendedApprovalPort };
|
|
40
45
|
|
|
41
|
-
/** The agent a session is bound to: its unattended run's owner, a
|
|
42
|
-
* member, a home thread's agent; '' for any other
|
|
43
|
-
function
|
|
44
|
-
const run =
|
|
46
|
+
/** The agent a session is bound to by itself: its unattended run's owner, a
|
|
47
|
+
* room member, a home thread's agent; '' for any other session. */
|
|
48
|
+
function ownBoundAgentId(sessionId) {
|
|
49
|
+
const run = unattendedApprovalPort()?.runOf?.(sessionId);
|
|
45
50
|
if (run?.agentId) return run.agentId;
|
|
46
51
|
return roomMemberOfSession(sessionId)?.agentId || agentIdOfHomeSession(sessionId) || '';
|
|
47
52
|
}
|
|
48
53
|
|
|
49
54
|
/** The agent a session acts for: its unattended run's owner, a room member,
|
|
50
|
-
* a home thread's agent, else Mixy.
|
|
55
|
+
* a home thread's agent, else Mixy. Work a schedule/webhook run started (a
|
|
56
|
+
* sub-agent, a task session) acts for the run's owner; a chat's sub-agent, a
|
|
57
|
+
* chat-created task session and a coding Lead are bound to nothing. */
|
|
51
58
|
export function agentIdForSession(sessionId) {
|
|
52
|
-
return
|
|
59
|
+
return ownBoundAgentId(sessionId) || MIXY_AGENT_ID;
|
|
53
60
|
}
|
|
54
61
|
|
|
55
62
|
/** The bot a session acts for, or '' when none: Mixy in her own home thread
|
|
56
63
|
* (or her unattended run / room member session), or a contact agent (`kind:
|
|
57
64
|
* bot`, isContactAgentId) in its home thread, room member session or
|
|
58
|
-
* unattended run.
|
|
59
|
-
*
|
|
60
|
-
*
|
|
61
|
-
*
|
|
62
|
-
|
|
63
|
-
|
|
65
|
+
* unattended run. Work a schedule/webhook run started (a sub-agent, a task
|
|
66
|
+
* session) resolves to the run's bot (ownBoundAgentId → runOf). A coding Lead
|
|
67
|
+
* (also id 'lead'), a task session a bot created in a chat, their workflow
|
|
68
|
+
* agents and every sub-agent of a chat (also a bot's) are bound to nothing
|
|
69
|
+
* here, so they are no bot. */
|
|
70
|
+
export function actingBotId(sessionId) {
|
|
71
|
+
const id = ownBoundAgentId(sessionId);
|
|
64
72
|
if (!id) return '';
|
|
65
73
|
if (id === MIXY_AGENT_ID) return id;
|
|
66
74
|
const kind = readAgentDefinitionKind({ dataDir: resolvePluginData(), shippedRoot: mixdogRoot(), id });
|
|
67
75
|
return isContactAgentId(id, kind) ? id : '';
|
|
68
76
|
}
|
|
69
77
|
|
|
78
|
+
/** The contact bot (`kind: bot`) a session acts for, or '' for Mixy, a coding
|
|
79
|
+
* Lead, a workflow agent and every other session. Mixy shares the `lead` id
|
|
80
|
+
* with the coding Lead (her connector policy is hers alone, never the Lead's:
|
|
81
|
+
* connectorPolicyAgentOf), and a standing allow is a contact bot's own policy
|
|
82
|
+
* entry: none is offered to Mixy, a Lead or a workflow agent. */
|
|
83
|
+
export function actingContactBotId(sessionId) {
|
|
84
|
+
const id = actingBotId(sessionId);
|
|
85
|
+
return id && id !== MIXY_AGENT_ID ? id : '';
|
|
86
|
+
}
|
|
87
|
+
|
|
88
|
+
// Chain token the desktop translates (ApprovalCard approvalChainLabel).
|
|
89
|
+
const LEAD_NAME = 'Lead';
|
|
90
|
+
|
|
91
|
+
/** Where a sub-agent's approval cards also surface, and who is asking:
|
|
92
|
+
* `{ sessionId, chain }` — the session it works under (a coding Lead, a
|
|
93
|
+
* bot-created task session, a bot's home chat or room member session) and
|
|
94
|
+
* the names from there down to the asker ("Lead › Reviewer", "사무관 ›
|
|
95
|
+
* Worker"). Only where the card shows: no bot identity, grant or remember
|
|
96
|
+
* scope follows from it. Null for a top-level session. */
|
|
97
|
+
export function approvalSurface(sessionId) {
|
|
98
|
+
const delegation = delegationOf(sessionId);
|
|
99
|
+
if (delegation.state !== 'resolved') return null;
|
|
100
|
+
const bot = delegation.via === 'bot' ? ownBoundAgentId(delegation.rootSessionId) : '';
|
|
101
|
+
const chain = [bot ? agentDisplayName(bot) || bot : LEAD_NAME];
|
|
102
|
+
for (const step of delegation.path) chain.push(agentDisplayName(step.agent) || step.agent);
|
|
103
|
+
return { sessionId: delegation.rootSessionId, chain };
|
|
104
|
+
}
|
|
105
|
+
|
|
70
106
|
const actionOf = (decision) => String(decision?.action || decision?.decision || '').toLowerCase();
|
|
71
107
|
|
|
72
108
|
async function mcpAnnotations(name, scopeId) {
|
|
@@ -114,21 +150,44 @@ async function describeEmails(call, outbound, ctx) {
|
|
|
114
150
|
* @param {{ name: string, args: any }} call
|
|
115
151
|
* @param {object|null} decision the PreToolUse decision so far
|
|
116
152
|
* @param {{ sessionId?: string, mcpScopeId?: string, annotations?: object, store?: object }} ctx
|
|
117
|
-
* @returns {Promise<{ decision: object|null, request: object|null, gate: object|null }>}
|
|
153
|
+
* @returns {Promise<{ decision: object|null, request: object|null, gate: object|null, remembered?: true }>}
|
|
118
154
|
* `request` holds the kind-specific fields an `ask` presents; `gate` the
|
|
119
|
-
* audit identity `{ agentId, kind, action, tool, target }
|
|
155
|
+
* audit identity `{ agentId, kind, action, tool, target }`; `remembered`
|
|
156
|
+
* marks an outbound call a stored grant already answered.
|
|
120
157
|
*/
|
|
121
158
|
export async function humanGateDecision(call, decision, ctx = {}) {
|
|
122
159
|
const action = actionOf(decision);
|
|
123
160
|
if (action === 'deny' || action === 'block') return { decision, request: null, gate: null };
|
|
124
161
|
const agentId = agentIdForSession(ctx.sessionId);
|
|
125
162
|
const annotations = ctx.annotations ?? (await mcpAnnotations(call?.name, ctx.mcpScopeId));
|
|
126
|
-
|
|
163
|
+
let classified = classifyOutboundCall({ name: call?.name, args: call?.args, annotations });
|
|
164
|
+
const remoteDelete = isRemoteDeleteInRun({ name: call?.name, annotations }, classified);
|
|
165
|
+
// In a schedule/webhook run only a recoverable trash may delete remote items.
|
|
166
|
+
if (isGrantedRun(unattendedApprovalPort()?.runOf?.(ctx.sessionId)) && remoteDelete) {
|
|
167
|
+
const store = ctx.store || (await humanGateStore());
|
|
168
|
+
await store.recordAudit({
|
|
169
|
+
agentId,
|
|
170
|
+
kind: 'outbound',
|
|
171
|
+
action: 'delete',
|
|
172
|
+
tool: String(call?.name || ''),
|
|
173
|
+
target: '',
|
|
174
|
+
decision: 'run-forbidden',
|
|
175
|
+
device: '',
|
|
176
|
+
});
|
|
177
|
+
const base = decision && typeof decision === 'object' ? decision : {};
|
|
178
|
+
return { decision: { ...base, action: 'deny', reason: RUN_FORBIDDEN_REASON }, request: null, gate: null };
|
|
179
|
+
}
|
|
180
|
+
// Where a bot is attended, the same connector delete the runs refuse
|
|
181
|
+
// (delete_message, remove_member…) is a permanent delete: it asks, never remembered.
|
|
182
|
+
if (remoteDelete && classified?.category !== 'delete' && classified?.category !== 'payment') {
|
|
183
|
+
classified = classifyAsDelete({ name: call?.name, args: call?.args });
|
|
184
|
+
}
|
|
127
185
|
const outbound = classified && actingBotId(ctx.sessionId) ? classified : null;
|
|
128
186
|
if (!outbound) {
|
|
129
187
|
if (action !== 'ask') return { decision, request: null, gate: null };
|
|
130
188
|
return {
|
|
131
|
-
|
|
189
|
+
// Saving a schedule or webhook says plainly what the approval grants.
|
|
190
|
+
decision: savesAutomation(call) ? { ...decision, reason: AUTOMATION_GRANT_REASON } : decision,
|
|
132
191
|
request: { kind: 'approval', agentId, remember: ['once'] },
|
|
133
192
|
gate: { agentId, kind: 'approval', tool: String(call?.name || ''), target: '' },
|
|
134
193
|
};
|
|
@@ -145,8 +204,10 @@ export async function humanGateDecision(call, decision, ctx = {}) {
|
|
|
145
204
|
editable: outbound.editable,
|
|
146
205
|
details: outbound.details,
|
|
147
206
|
},
|
|
148
|
-
remember:
|
|
207
|
+
remember: ['once'],
|
|
149
208
|
};
|
|
209
|
+
// "Don't ask again" is the bot's permission.
|
|
210
|
+
request.remember = outboundRemember(outbound.category);
|
|
150
211
|
const store = ctx.store || (await humanGateStore());
|
|
151
212
|
const mode = await store.modeFor({ agentId, action: outbound.category });
|
|
152
213
|
const base = decision && typeof decision === 'object' ? decision : {};
|
|
@@ -156,7 +217,8 @@ export async function humanGateDecision(call, decision, ctx = {}) {
|
|
|
156
217
|
}
|
|
157
218
|
if (action !== 'ask' && mode === 'allow') {
|
|
158
219
|
await store.recordAudit({ ...gate, decision: 'remembered', device: '' });
|
|
159
|
-
|
|
220
|
+
// The remembered grant answers this call: no later layer asks it again.
|
|
221
|
+
return { decision, request: null, gate: null, remembered: true };
|
|
160
222
|
}
|
|
161
223
|
const described = await describeEmails(call, outbound, ctx);
|
|
162
224
|
if (described) {
|
|
@@ -173,7 +235,8 @@ export async function humanGateDecision(call, decision, ctx = {}) {
|
|
|
173
235
|
return {
|
|
174
236
|
decision: { ...(rewriting ? base : unrewritten), action: 'ask', reason: base.reason || OUTBOUND_APPROVAL_REASON },
|
|
175
237
|
request,
|
|
176
|
-
|
|
238
|
+
// The scopes this request offered: a grant is stored only for one of them.
|
|
239
|
+
gate: { ...gate, remember: request.remember },
|
|
177
240
|
};
|
|
178
241
|
}
|
|
179
242
|
|
|
@@ -185,8 +248,12 @@ export async function recordHumanDecision(gate, approval, { store } = {}) {
|
|
|
185
248
|
let decision = approval?.response || (approval?.approved === true ? 'approve' : 'deny');
|
|
186
249
|
if (approval?.timedOut) decision = 'timeout';
|
|
187
250
|
const device = typeof approval?.device === 'string' ? approval.device : '';
|
|
188
|
-
|
|
251
|
+
// A scope the request did not offer (a payment or permanent delete is only
|
|
252
|
+
// ever answered once) stores nothing.
|
|
253
|
+
const offered = !Array.isArray(gate.remember) || gate.remember.includes(approval?.remember);
|
|
254
|
+
if (approval?.approved === true && gate.action && offered) {
|
|
189
255
|
await target.grant({ agentId: gate.agentId, action: gate.action, scope: approval.remember, device });
|
|
190
256
|
}
|
|
191
|
-
|
|
257
|
+
const { remember: _offered, ...audit } = gate;
|
|
258
|
+
await target.recordAudit({ ...audit, decision, device });
|
|
192
259
|
}
|
|
@@ -23,6 +23,14 @@ export const ACTION_KINDS = Object.freeze([
|
|
|
23
23
|
'payment',
|
|
24
24
|
'delete',
|
|
25
25
|
'trash',
|
|
26
|
+
// Browser Use gated actions (main/browser/action-approval.ts): a form
|
|
27
|
+
// submit / Enter, a click on a send or post control, and a file upload. They
|
|
28
|
+
// are not classified here (the desktop browser host asks); only their grants
|
|
29
|
+
// live in this store. Operator-confirmed actions
|
|
30
|
+
// (MIXDOG_BROWSER_CONFIRM_ACTIONS) have no kind: they always ask.
|
|
31
|
+
'browser_submit',
|
|
32
|
+
'browser_post',
|
|
33
|
+
'browser_upload',
|
|
26
34
|
]);
|
|
27
35
|
const UNREMEMBERED_ACTIONS = new Set(['payment', 'delete']);
|
|
28
36
|
|
|
@@ -204,6 +212,18 @@ export function classifyOutboundCall({ name, args, annotations } = {}) {
|
|
|
204
212
|
if (!category && annotations?.readOnlyHint === true) return null;
|
|
205
213
|
if (!category && annotations?.outboundHint === true) category = 'message';
|
|
206
214
|
if (!category) return null;
|
|
215
|
+
return mcpOutbound(toolName, server, input, category);
|
|
216
|
+
}
|
|
217
|
+
|
|
218
|
+
/** The classification of connector tool `name` as a permanent delete — for a
|
|
219
|
+
* delete the name-based list above does not know (run-policy.mjs decides which). */
|
|
220
|
+
export function classifyAsDelete({ name, args } = {}) {
|
|
221
|
+
const toolName = String(name || '');
|
|
222
|
+
const [, server = ''] = toolName.split('__');
|
|
223
|
+
return mcpOutbound(toolName, server, isRecord(args) ? args : {}, 'delete');
|
|
224
|
+
}
|
|
225
|
+
|
|
226
|
+
function mcpOutbound(toolName, server, input, category) {
|
|
207
227
|
const details = detailsOf(category, input);
|
|
208
228
|
const spends = category === 'payment' || category === 'delete';
|
|
209
229
|
return {
|