mixdog 1.4.1 → 1.4.2
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/package.json +3 -2
- package/scripts/build-shipped-history.mjs +115 -33
- package/scripts/gate-local.mjs +172 -61
- package/scripts/lib/gate-scheduler.mjs +138 -32
- package/scripts/lib/gate-timing-reporter.mjs +16 -0
- package/scripts/lib/run-node-tests.mjs +54 -29
- package/scripts/lib/scoped-temp.mjs +1 -0
- package/scripts/lib/test-failure-records.mjs +14 -1
- package/scripts/lib/test-failure-summary.mjs +16 -4
- package/scripts/lib/test-spawn-ledger.mjs +12 -3
- package/scripts/pptx-kit-index.mjs +5 -6
- package/scripts/suite-health.mjs +110 -28
- package/scripts/test.mjs +9 -14
- package/src/agents/advisor/AGENT.md +4 -4
- package/src/agents/advisor/agent.json +1 -1
- package/src/agents/explore/AGENT.md +22 -0
- package/src/agents/explore/agent.json +6 -0
- package/src/cli.mjs +7 -0
- package/src/defaults/agents.json +7 -0
- package/src/defaults/shipped-history.json +18 -2
- package/src/defaults/skills/browser-use/SKILL.md +4 -1
- package/src/defaults/skills/code-tidy/SKILL.md +71 -102
- package/src/defaults/skills/code-tidy/references/agent-cleanup.md +66 -114
- package/src/defaults/skills/code-tidy/references/cleanup-report.md +19 -35
- package/src/defaults/skills/code-tidy/references/dead-code.md +3 -9
- package/src/defaults/skills/code-tidy/references/test-suite-slop.md +16 -27
- package/src/defaults/skills/computer-use/SKILL.md +12 -2
- package/src/defaults/skills/goal-management/SKILL.md +1 -1
- package/src/defaults/skills/setup/references/actions.md +12 -6
- package/src/defaults/skills/setup/references/surfaces.md +2 -2
- package/src/defaults/skills/setup-guide/SKILL.md +5 -2
- package/src/defaults/skills/setup-guide/model-recommendations.json +13 -0
- package/src/defaults/skills/skill-creator/SKILL.md +85 -190
- package/src/defaults/skills/skill-creator/references/design.md +70 -162
- package/src/defaults/skills/skill-creator/references/evaluation.md +33 -92
- package/src/defaults/skills/test-suite/SKILL.md +194 -0
- package/src/defaults/skills/test-suite/references/case-management.md +51 -0
- package/src/defaults/skills/test-suite/references/flaky-policy.md +39 -0
- package/src/defaults/skills/test-suite/references/reliability.md +49 -0
- package/src/defaults/skills/test-suite/references/suite-design.md +78 -0
- package/src/defaults/skills/test-suite/scripts/inventory.mjs +335 -0
- package/src/defaults/skills/visualize/SKILL.md +6 -2
- package/src/lib/rules-builder.cjs +6 -30
- package/src/rules/lead/LEAD.md +3 -3
- package/src/rules/shared/20-research.md +3 -6
- package/src/rules/shared/40-editing.md +1 -0
- package/src/rules/shared/60-verification.md +10 -9
- package/src/rules/shared/70-delivery.md +4 -3
- package/src/runtime/agent/orchestrator/agent-runtime/agent-dispatch/abort-link.mjs +5 -1
- package/src/runtime/agent/orchestrator/agent-runtime/agent-dispatch.mjs +10 -1
- package/src/runtime/agent/orchestrator/agent-runtime/agent-progress-watchdog.mjs +130 -21
- package/src/runtime/agent/orchestrator/agent-runtime/cache-strategy.mjs +13 -0
- package/src/runtime/agent/orchestrator/agent-runtime/maintenance-route.mjs +4 -1
- package/src/runtime/agent/orchestrator/config.mjs +12 -7
- package/src/runtime/agent/orchestrator/context/deferred-tools.mjs +7 -7
- package/src/runtime/agent/orchestrator/context/role-instructions.mjs +5 -26
- package/src/runtime/agent/orchestrator/internal-agents.mjs +3 -14
- package/src/runtime/agent/orchestrator/mcp/agent-connectors.mjs +122 -12
- package/src/runtime/agent/orchestrator/mcp/child-tree.mjs +5 -3
- package/src/runtime/agent/orchestrator/mcp/client-config.mjs +13 -2
- package/src/runtime/agent/orchestrator/mcp/client-transport.mjs +152 -58
- package/src/runtime/agent/orchestrator/mcp/client.mjs +51 -18
- package/src/runtime/agent/orchestrator/mcp/oauth-login.mjs +83 -52
- package/src/runtime/agent/orchestrator/mcp/oauth.mjs +42 -26
- package/src/runtime/agent/orchestrator/providers/account-pool.mjs +43 -8
- package/src/runtime/agent/orchestrator/providers/anthropic-api-request.mjs +30 -28
- package/src/runtime/agent/orchestrator/providers/anthropic-oauth-client-version.mjs +1 -1
- package/src/runtime/agent/orchestrator/providers/anthropic-oauth-credentials.mjs +31 -21
- package/src/runtime/agent/orchestrator/providers/antigravity-oauth-login.mjs +3 -1
- package/src/runtime/agent/orchestrator/providers/antigravity-oauth-tokens.mjs +2 -2
- package/src/runtime/agent/orchestrator/providers/codex-client-meta.mjs +1 -1
- package/src/runtime/agent/orchestrator/providers/command-code-oauth.mjs +1 -1
- package/src/runtime/agent/orchestrator/providers/custom-provider-config.mjs +40 -0
- package/src/runtime/agent/orchestrator/providers/custom-provider-keys.mjs +34 -0
- package/src/runtime/agent/orchestrator/providers/custom-provider.mjs +3 -0
- package/src/runtime/agent/orchestrator/providers/devin-auth.mjs +14 -7
- package/src/runtime/agent/orchestrator/providers/devin-wire-transport.mjs +3 -2
- package/src/runtime/agent/orchestrator/providers/gemini-response.mjs +2 -2
- package/src/runtime/agent/orchestrator/providers/gemini-stream.mjs +4 -1
- package/src/runtime/agent/orchestrator/providers/grok-oauth-login.mjs +48 -31
- package/src/runtime/agent/orchestrator/providers/lib/oauth-page.mjs +0 -18
- package/src/runtime/agent/orchestrator/providers/lib/oauth-token-utils.mjs +138 -6
- package/src/runtime/agent/orchestrator/providers/mimo-oauth.mjs +1 -1
- package/src/runtime/agent/orchestrator/providers/model-cache.mjs +100 -33
- package/src/runtime/agent/orchestrator/providers/muse-code-oauth.mjs +1 -1
- package/src/runtime/agent/orchestrator/providers/oauth-secret-fields.fixtures.mjs +45 -0
- package/src/runtime/agent/orchestrator/providers/openai-oauth-login.mjs +26 -28
- package/src/runtime/agent/orchestrator/providers/openai-ws-stream/event-switch.mjs +3 -1
- package/src/runtime/agent/orchestrator/providers/openai-ws-watchdogs.mjs +1 -0
- package/src/runtime/agent/orchestrator/providers/opencode-go-oauth.mjs +1 -1
- package/src/runtime/agent/orchestrator/providers/opencode-go.mjs +3 -1
- package/src/runtime/agent/orchestrator/providers/registry.mjs +33 -2
- package/src/runtime/agent/orchestrator/providers/vertex-ai-auth.mjs +622 -0
- package/src/runtime/agent/orchestrator/providers/vertex-ai-maas.mjs +185 -0
- package/src/runtime/agent/orchestrator/providers/vertex-ai-models.mjs +445 -0
- package/src/runtime/agent/orchestrator/providers/vertex-ai.mjs +567 -0
- package/src/runtime/agent/orchestrator/runtime-core/builtin-features.mjs +2 -0
- package/src/runtime/agent/orchestrator/runtime-core/orchestration.mjs +2 -1
- package/src/runtime/agent/orchestrator/runtime-core/tool-catalog-data.mjs +3 -0
- package/src/runtime/agent/orchestrator/runtime-core/tool-defs.mjs +3 -0
- package/src/runtime/agent/orchestrator/runtime-core/workflow/context.mjs +14 -5
- package/src/runtime/agent/orchestrator/runtime-core/workflow-ids.mjs +1 -0
- package/src/runtime/agent/orchestrator/session/agent-room.d.mts +30 -5
- package/src/runtime/agent/orchestrator/session/agent-room.mjs +171 -37
- package/src/runtime/agent/orchestrator/session/cache-warm.mjs +94 -10
- package/src/runtime/agent/orchestrator/session/conversation-import/claude.mjs +3 -3
- package/src/runtime/agent/orchestrator/session/conversation-import/codex.mjs +10 -5
- package/src/runtime/agent/orchestrator/session/conversation-import/convert.mjs +5 -5
- package/src/runtime/agent/orchestrator/session/conversation-import/turns.mjs +11 -1
- package/src/runtime/agent/orchestrator/session/eager-dispatch.mjs +4 -3
- package/src/runtime/agent/orchestrator/session/human-gate/requests.mjs +36 -6
- package/src/runtime/agent/orchestrator/session/loop/fresh-context.mjs +5 -3
- package/src/runtime/agent/orchestrator/session/loop/tool-exec/before-hook.mjs +21 -2
- package/src/runtime/agent/orchestrator/session/loop/tool-exec/dispatch.mjs +2 -0
- package/src/runtime/agent/orchestrator/session/loop/tool-exec.mjs +23 -1
- package/src/runtime/agent/orchestrator/session/loop/turn-limits.mjs +13 -8
- package/src/runtime/agent/orchestrator/session/manager/ask-session.mjs +50 -28
- package/src/runtime/agent/orchestrator/session/manager/prompt-utils.mjs +1 -0
- package/src/runtime/agent/orchestrator/session/manager/rules-cache.mjs +3 -4
- package/src/runtime/agent/orchestrator/session/manager/runtime-liveness.mjs +2 -0
- package/src/runtime/agent/orchestrator/session/manager/session-close.mjs +7 -0
- package/src/runtime/agent/orchestrator/session/manager/session-crud.mjs +15 -0
- package/src/runtime/agent/orchestrator/session/manager/session-lifecycle.mjs +25 -14
- package/src/runtime/agent/orchestrator/session/manager/session-prompt-composition.mjs +17 -14
- package/src/runtime/agent/orchestrator/session/manager/session-scratch-environment.mjs +70 -0
- package/src/runtime/agent/orchestrator/session/manager/session-tool-surface.mjs +0 -3
- package/src/runtime/agent/orchestrator/session/manager/tool-resolution.mjs +7 -3
- package/src/runtime/agent/orchestrator/session/manager/turn-interruption.mjs +47 -29
- package/src/runtime/agent/orchestrator/session/manager/turn-reap.mjs +125 -0
- package/src/runtime/agent/orchestrator/session/mixy-name.mjs +11 -5
- package/src/runtime/agent/orchestrator/session/restart-prefix-parity.fixture.mjs +1 -1
- package/src/runtime/agent/orchestrator/session/store-summary-reader.mjs +243 -38
- package/src/runtime/agent/orchestrator/session/store-transcript-cache.mjs +163 -64
- package/src/runtime/agent/orchestrator/session/store-transcript-projection.mjs +164 -4
- package/src/runtime/agent/orchestrator/session/store-transcript-worker.mjs +142 -18
- package/src/runtime/agent/orchestrator/tools/builtin/bash-tool/completed-result.mjs +6 -2
- package/src/runtime/agent/orchestrator/tools/builtin/git-command-tool.mjs +36 -26
- package/src/runtime/agent/orchestrator/tools/builtin/git-worktree-tool.mjs +3 -5
- package/src/runtime/agent/orchestrator/tools/builtin/lib/shell-job-records.mjs +58 -20
- package/src/runtime/agent/orchestrator/tools/builtin/runtime-capabilities.mjs +20 -15
- package/src/runtime/agent/orchestrator/tools/builtin/shell-jobs.mjs +96 -20
- package/src/runtime/agent/orchestrator/tools/builtin/task-tool.mjs +59 -10
- package/src/runtime/agent/orchestrator/tools/graph-manifest.json +13 -13
- package/src/runtime/agent/orchestrator/tools/lib/native-spawn-client.mjs +99 -2
- package/src/runtime/agent/orchestrator/tools/lib/owned-shell-trees.mjs +2 -0
- package/src/runtime/agent/orchestrator/tools/lib/shell-descendants.mjs +103 -13
- package/src/runtime/agent/orchestrator/tools/lib/shell-run-background/promote-job.mjs +3 -1
- package/src/runtime/agent/orchestrator/tools/lib/shell-run-settle.mjs +49 -15
- package/src/runtime/agent/orchestrator/tools/lib/shell-run-spawn.mjs +45 -27
- package/src/runtime/agent/orchestrator/tools/lib/shell-run-state.mjs +34 -10
- package/src/runtime/agent/orchestrator/tools/lib/shell-warm-standby.mjs +2 -0
- package/src/runtime/agent/orchestrator/tools/lib/wait-until-fixture.mjs +26 -0
- package/src/runtime/agent/orchestrator/tools/shell-command.mjs +7 -2
- package/src/runtime/agent/orchestrator/tools/spawn-manifest.json +13 -13
- package/src/runtime/channels/lib/whisper-port.mjs +19 -10
- package/src/runtime/channels/lib/whisper-server.mjs +10 -1
- package/src/runtime/connectors/connector-accounts-mcp.mjs +236 -103
- package/src/runtime/connectors/gmail-app-password.mjs +3 -3
- package/src/runtime/explore/explorer.mjs +93 -0
- package/src/runtime/explore/tool-defs.mjs +35 -0
- package/src/runtime/explore/tool.mjs +123 -0
- package/src/runtime/memory/index.mjs +4 -0
- package/src/runtime/memory/lib/embedding-provider.mjs +4 -1
- package/src/runtime/memory/lib/embedding-worker.mjs +4 -0
- package/src/runtime/office/quality/assurance-structure-docx.mjs +1 -1
- package/src/runtime/shared/agent-route-config.mjs +17 -2
- package/src/runtime/shared/atomic-file.mjs +103 -2
- package/src/runtime/shared/background-tasks.mjs +15 -6
- package/src/runtime/shared/connector-accounts.mjs +5 -5
- package/src/runtime/shared/connector-catalog.d.mts +11 -4
- package/src/runtime/shared/connector-catalog.mjs +60 -52
- package/src/runtime/shared/custom-provider-keys.mjs +20 -0
- package/src/runtime/shared/file-has-data.mjs +26 -2
- package/src/runtime/shared/file-lock.mjs +13 -15
- package/src/runtime/shared/git-launch.d.mts +9 -0
- package/src/runtime/shared/git-launch.mjs +213 -0
- package/src/runtime/shared/git-runner-worker.mjs +2 -1
- package/src/runtime/shared/llm/cost.mjs +6 -1
- package/src/runtime/shared/llm/http-agent.mjs +313 -29
- package/src/runtime/shared/llm/model-catalog-projection.mjs +3 -2
- package/src/runtime/shared/llm/model-catalog.mjs +46 -6
- package/src/runtime/shared/llm/provider-catalog-registry.mjs +3 -0
- package/src/runtime/shared/llm/system-proxy.mjs +452 -0
- package/src/runtime/shared/provider-accounts.mjs +52 -2
- package/src/runtime/shared/staged-install-worker.mjs +5 -0
- package/src/runtime/shared/staged-update.mjs +47 -2
- package/src/runtime/shared/tool-surface.mjs +110 -36
- package/src/runtime/shared/tool-work-units.mjs +4 -0
- package/src/runtime/shared/turn-worktree-snapshot.mjs +414 -54
- package/src/runtime/shared/worktrees/activity-gate.mjs +215 -318
- package/src/runtime/shared/worktrees/activity-ledger-worker.mjs +56 -0
- package/src/runtime/shared/worktrees/activity-ledger.mjs +391 -0
- package/src/runtime/shared/worktrees/branch-management.mjs +9 -3
- package/src/runtime/shared/worktrees/git.mjs +57 -0
- package/src/runtime/shared/worktrees/manager.mjs +205 -137
- package/src/runtime/tidy/install.mjs +95 -15
- package/src/runtime/tidy/languages.mjs +17 -15
- package/src/runtime/tidy/process.mjs +7 -4
- package/src/runtime/tidy/resolve.mjs +19 -16
- package/src/runtime/tidy/runners/biome.mjs +3 -3
- package/src/runtime/tidy/tool.mjs +1 -1
- package/src/runtime/tidy/worktree.mjs +10 -6
- package/src/runtime/visualize-bridge/client.mjs +4 -1
- package/src/runtime/visualize-bridge/tool-defs.mjs +7 -2
- package/src/runtime/web-search/index.mjs +1 -1
- package/src/session-runtime/agent-task-sessions.mjs +410 -353
- package/src/session-runtime/boot/tools.mjs +2 -0
- package/src/session-runtime/bot-status.mjs +103 -103
- package/src/session-runtime/connector-merge-migration.mjs +18 -15
- package/src/session-runtime/conversation-import/discovery.mjs +159 -40
- package/src/session-runtime/conversation-import/service.mjs +9 -3
- package/src/session-runtime/handoff-attachments.mjs +1 -1
- package/src/session-runtime/held-requests.mjs +3 -1
- package/src/session-runtime/internal-tool-executor/feature-tools.mjs +2 -0
- package/src/session-runtime/internal-tool-executor/knowledge-tools.mjs +8 -0
- package/src/session-runtime/internal-tool-executor/workspace-tools.mjs +143 -116
- package/src/session-runtime/lifecycle/teardown/session-close.mjs +6 -1
- package/src/session-runtime/lifecycle/teardown.mjs +1 -1
- package/src/session-runtime/mcp-glue/server-catalog.mjs +16 -10
- package/src/session-runtime/mcp-glue/server-input.mjs +4 -1
- package/src/session-runtime/mixy-heartbeat.mjs +1 -1
- package/src/session-runtime/provider-auth/login.mjs +2 -2
- package/src/session-runtime/resource-mcp-api.mjs +98 -35
- package/src/session-runtime/resource-mcp-connector-accounts.mjs +44 -31
- package/src/session-runtime/resource-mcp-secrets.mjs +28 -11
- package/src/session-runtime/resource-mcp-servers.mjs +5 -4
- package/src/session-runtime/services/agent-tool/send-flow.mjs +10 -2
- package/src/session-runtime/services/agent-tool/spawn-flow/spawn-prep.mjs +8 -3
- package/src/session-runtime/services/agent-tool/spawn-flow/spawn-run.mjs +4 -1
- package/src/session-runtime/services/agent-watchdog-registry.mjs +32 -4
- package/src/session-runtime/services/channel-worker/daemon-spawn.mjs +2 -0
- package/src/session-runtime/services/daemon-candidate.mjs +4 -1
- package/src/session-runtime/services/memory-runtime-proxy/daemon-spawn.mjs +4 -0
- package/src/session-runtime/services/plugin-admin.mjs +10 -6
- package/src/session-runtime/services/provider-admin.mjs +281 -10
- package/src/session-runtime/session-turn-api.mjs +2 -0
- package/src/session-runtime/settings-builtin-tools-api.mjs +4 -1
- package/src/session-runtime/settings-system-api.mjs +1 -1
- package/src/session-runtime/setup-tool/agent-settings-actions.mjs +13 -3
- package/src/session-runtime/setup-tool/connector-widen-approval.mjs +96 -0
- package/src/session-runtime/setup-tool/executor.mjs +6 -2
- package/src/session-runtime/setup-tool/tool-defs.mjs +2 -1
- package/src/session-runtime/turn/agent-ops.mjs +23 -0
- package/src/session-runtime/workflow-agents-api/agent-connectors.mjs +49 -11
- package/src/session-runtime/workflow-agents-api/agent-route.mjs +10 -1
- package/src/session-runtime/workspace/activity-guards.mjs +176 -0
- package/src/session-runtime/workspace/session-view.mjs +149 -0
- package/src/session-runtime/workspace/side-checkouts.mjs +203 -0
- package/src/session-runtime/workspace/transition-runner.mjs +188 -0
- package/src/session-runtime/workspace/transitions.mjs +108 -1066
- package/src/session-runtime/workspace/workspace-merge.mjs +132 -0
- package/src/session-runtime/workspace/worktree-intents.mjs +242 -0
- package/src/session-runtime/workspace/worktree-maintenance.mjs +80 -0
- package/src/session-runtime/workspace/worktree-setup.mjs +85 -0
- package/src/session-runtime/workspace/worktree-tool.mjs +54 -54
- package/src/standalone/daemon-desktop-runtime.mjs +2 -0
- package/src/standalone/daemon-session-control.mjs +151 -136
- package/src/standalone/daemon.mjs +7 -0
- package/src/standalone/desktop-service-registry.mjs +9 -0
- package/src/standalone/local-session-runtime.mjs +5 -1
- package/src/standalone/session-client/daemon-unavailable.mjs +3 -0
- package/src/standalone/session-client.mjs +5 -1
- package/src/standalone/session-frame-stream.mjs +83 -11
- package/src/standalone/session-protocol.mjs +4 -0
- package/src/standalone/session-service/agent-tree.mjs +45 -9
- package/src/standalone/session-service/projection/transcript-window.mjs +700 -64
- package/src/standalone/session-service/session-calls/session-views.mjs +160 -104
- package/src/standalone/session-service/stored-reader.mjs +127 -40
- package/src/tui/App.jsx +154 -164
- package/src/tui/app/app-view/floating-panel.jsx +3 -0
- package/src/tui/app/app-view/text-entry-labels.mjs +1 -0
- package/src/tui/app/clipboard.mjs +21 -10
- package/src/tui/app/prompt-handlers/use-prompt-history-nav.mjs +1 -1
- package/src/tui/app/prompt-handlers/use-prompt-interrupt.mjs +3 -0
- package/src/tui/app/prompt-submit/provider-prompts.mjs +3 -1
- package/src/tui/app/provider-setup/api-key-actions.mjs +31 -1
- package/src/tui/app/provider-setup/oauth-actions.mjs +25 -6
- package/src/tui/app/provider-setup-picker.mjs +20 -13
- package/src/tui/app/shell-layout/row-budgets.mjs +12 -10
- package/src/tui/app/slash-commands.mjs +1 -0
- package/src/tui/app/slash-dispatch/session-commands.mjs +46 -0
- package/src/tui/app/slash-dispatch.mjs +1 -1
- package/src/tui/app/use-btw-panel.mjs +29 -0
- package/src/tui/app/use-context-panel-refresh.mjs +31 -0
- package/src/tui/app/use-model-catalog-cache.mjs +13 -10
- package/src/tui/app/use-mouse-input.mjs +11 -0
- package/src/tui/app/use-onboarding-start.mjs +8 -3
- package/src/tui/app/use-panel-transition.mjs +10 -1
- package/src/tui/app/use-pasted-buffers.mjs +28 -20
- package/src/tui/app/use-prompt-draft-flow.mjs +7 -0
- package/src/tui/app/use-prompt-layout-rows.mjs +3 -3
- package/src/tui/app/use-prompt-queue-history.mjs +3 -2
- package/src/tui/app/use-selection-focus.mjs +11 -2
- package/src/tui/app/use-selection-geometry.mjs +18 -9
- package/src/tui/app/use-selection-paint.mjs +4 -4
- package/src/tui/app/use-selection-stitch.mjs +3 -3
- package/src/tui/app/use-slash-palette.mjs +62 -0
- package/src/tui/app/use-terminal-chrome.mjs +2 -2
- package/src/tui/app/use-transcript-activity.mjs +47 -32
- package/src/tui/app/use-transcript-follow.mjs +12 -4
- package/src/tui/app/use-transcript-scroll.mjs +46 -17
- package/src/tui/app/use-transcript-swap-reset.mjs +27 -0
- package/src/tui/app/use-transcript-window/geometry.mjs +19 -5
- package/src/tui/app/use-transcript-window/instance-state.mjs +4 -0
- package/src/tui/app/use-transcript-window/selection-shift.mjs +5 -1
- package/src/tui/app/use-transcript-window.mjs +21 -11
- package/src/tui/app/use-tui-ready.mjs +18 -0
- package/src/tui/app/use-welcome-prompt-hint.mjs +5 -0
- package/src/tui/app/use-workflow-tab-cycle.mjs +5 -3
- package/src/tui/components/Picker.jsx +27 -3
- package/src/tui/components/SignInUrl.jsx +68 -0
- package/src/tui/components/TextEntryPanel.jsx +76 -46
- package/src/tui/components/sign-in-url-layout.mjs +24 -0
- package/src/tui/dist/index.mjs +1896 -1341
- package/src/tui/session/goal-turn-state.mjs +6 -2
- package/src/tui/session/session-api/controls.mjs +4 -0
- package/src/tui/session/session-api/extensions.mjs +8 -0
- package/src/tui/session/session-api/intake/abort.mjs +2 -1
- package/src/tui/session/session-api/intake.mjs +2 -0
- package/src/tui/session/session-api/integrations.mjs +2 -2
- package/src/tui/session/session-api/routes-agent-definitions.mjs +1 -0
- package/src/tui/session/session-api/settings.mjs +2 -2
- package/src/tui/session/transcript-spill/spill-writer.mjs +15 -0
- package/src/tui/session/transcript-spill.mjs +92 -4
- package/src/tui/session/transcript-store.mjs +2 -0
- package/src/defaults/skills/skill-creator/references/source-review.md +0 -94
|
@@ -14,6 +14,7 @@ import {
|
|
|
14
14
|
IMAGE_OMITTED_TEXT,
|
|
15
15
|
IMPORT_WARNING_IMAGE_OMITTED,
|
|
16
16
|
isWholeElement,
|
|
17
|
+
stringOrNull,
|
|
17
18
|
textPart,
|
|
18
19
|
titleFromText,
|
|
19
20
|
toolNote,
|
|
@@ -92,7 +93,7 @@ export function isClaudeEnvelopeText(text) {
|
|
|
92
93
|
const tagValue = (text, tag) => new RegExp(`<${tag}>([\\s\\S]*?)</${tag}>`).exec(text)?.[1].trim() || '';
|
|
93
94
|
|
|
94
95
|
/** A slash-command envelope as Claude Code shows it (`/name args`), else null. */
|
|
95
|
-
|
|
96
|
+
function claudeCommandText(text) {
|
|
96
97
|
const trimmed = String(text || '').trim();
|
|
97
98
|
if (!SLASH_COMMAND_RE.test(trimmed)) return null;
|
|
98
99
|
const name = (tagValue(trimmed, 'command-name') || tagValue(trimmed, 'command-message')).replace(/^\//, '');
|
|
@@ -101,7 +102,6 @@ export function claudeCommandText(text) {
|
|
|
101
102
|
}
|
|
102
103
|
|
|
103
104
|
const isMessageEntry = (record) => record?.type === 'user' || record?.type === 'assistant';
|
|
104
|
-
const stringOrNull = (value) => (typeof value === 'string' && value.trim() ? value.trim() : null);
|
|
105
105
|
const uuidOrNull = (value) => (typeof value === 'string' ? value : null);
|
|
106
106
|
const timestampOrNull = (value) => (typeof value === 'string' || typeof value === 'number' ? value : null);
|
|
107
107
|
// Rough fixed cost of one retained entry object, so tiny entries still count.
|
|
@@ -193,7 +193,7 @@ function entryParts(role, content, warnings, marks) {
|
|
|
193
193
|
* (a cleared rename) ends the name tier without falling back to the AI title.
|
|
194
194
|
* Other record types, such as `task-summary` or `agent-name`, never name it.
|
|
195
195
|
*/
|
|
196
|
-
|
|
196
|
+
function createClaudeTitleScan() {
|
|
197
197
|
let custom;
|
|
198
198
|
let ai;
|
|
199
199
|
let summary;
|
|
@@ -5,7 +5,14 @@
|
|
|
5
5
|
* messages, Codex's own wrapper user messages, tool outputs, reasoning,
|
|
6
6
|
* event_msg, token counts and turn_context.
|
|
7
7
|
*/
|
|
8
|
-
import {
|
|
8
|
+
import {
|
|
9
|
+
IMAGE_OMITTED_TEXT,
|
|
10
|
+
IMPORT_WARNING_IMAGE_OMITTED,
|
|
11
|
+
isWholeElement,
|
|
12
|
+
stringOrNull,
|
|
13
|
+
textPart,
|
|
14
|
+
toolNote,
|
|
15
|
+
} from './turns.mjs';
|
|
9
16
|
|
|
10
17
|
// User-role envelopes Codex injects itself (environment, instructions, abort
|
|
11
18
|
// notices, plugin hints, shell-command echoes, image wrappers).
|
|
@@ -36,7 +43,7 @@ export function isCodexEnvelopeText(text) {
|
|
|
36
43
|
const USER_REQUEST_HEADING = '## My request for Codex:';
|
|
37
44
|
|
|
38
45
|
/** A user text part as Codex displays it. */
|
|
39
|
-
|
|
46
|
+
function codexUserDisplayText(text) {
|
|
40
47
|
const at = text.lastIndexOf(USER_REQUEST_HEADING);
|
|
41
48
|
return at < 0 ? text : text.slice(at + USER_REQUEST_HEADING.length).trim();
|
|
42
49
|
}
|
|
@@ -64,8 +71,6 @@ function toolName(payload) {
|
|
|
64
71
|
return payload.type === 'local_shell_call' ? 'local_shell' : '';
|
|
65
72
|
}
|
|
66
73
|
|
|
67
|
-
const stringOrNull = (value) => (typeof value === 'string' && value.trim() ? value.trim() : null);
|
|
68
|
-
|
|
69
74
|
// `session_meta.thread_source` values Codex gives threads it runs for itself.
|
|
70
75
|
const INTERNAL_THREAD_SOURCES = {
|
|
71
76
|
subagent: 'subagent',
|
|
@@ -81,7 +86,7 @@ const INTERNAL_THREAD_SOURCES = {
|
|
|
81
86
|
* is the approval reviewer) or `{ internal: … }`. Other sources (cli, vscode,
|
|
82
87
|
* exec, mcp, custom) and features (automation, voice chat) are the user's.
|
|
83
88
|
*/
|
|
84
|
-
|
|
89
|
+
function codexInternalKind(payload) {
|
|
85
90
|
const byThread = INTERNAL_THREAD_SOURCES[payload?.thread_source];
|
|
86
91
|
if (byThread) return byThread;
|
|
87
92
|
const source = payload?.source;
|
|
@@ -58,7 +58,7 @@ export function createConversationConverter(
|
|
|
58
58
|
let records = 0;
|
|
59
59
|
let malformed = 0;
|
|
60
60
|
let tooLarge = false;
|
|
61
|
-
let historyPartial =
|
|
61
|
+
let historyPartial = null;
|
|
62
62
|
return {
|
|
63
63
|
/** True while every message seen so far is a Claude sidechain message. */
|
|
64
64
|
get sidechainOnly() {
|
|
@@ -67,9 +67,9 @@ export function createConversationConverter(
|
|
|
67
67
|
markTooLarge() {
|
|
68
68
|
tooLarge = true;
|
|
69
69
|
},
|
|
70
|
-
/** A history base could not be followed in full. */
|
|
71
|
-
markHistoryPartial() {
|
|
72
|
-
historyPartial =
|
|
70
|
+
/** A history base could not be followed in full; `warning` names why. */
|
|
71
|
+
markHistoryPartial(warning = IMPORT_WARNING_HISTORY_PARTIAL) {
|
|
72
|
+
historyPartial = warning;
|
|
73
73
|
},
|
|
74
74
|
/** Feed a line for the stored title only (Claude Code's tail window); other sources ignore it. */
|
|
75
75
|
pushTitleLine(raw) {
|
|
@@ -117,7 +117,7 @@ export function createConversationConverter(
|
|
|
117
117
|
if (transcriptChars(turns) > maxTranscriptChars) tooLarge = true;
|
|
118
118
|
const warnings = new Set(parsed.warnings);
|
|
119
119
|
if (malformed) warnings.add(IMPORT_WARNING_MALFORMED);
|
|
120
|
-
if (historyPartial) warnings.add(
|
|
120
|
+
if (historyPartial) warnings.add(historyPartial);
|
|
121
121
|
let errorCode = null;
|
|
122
122
|
if (tooLarge) errorCode = IMPORT_ERROR_TOO_LARGE;
|
|
123
123
|
else if (parsed.internalKind) errorCode = IMPORT_ERROR_INTERNAL;
|
|
@@ -11,6 +11,13 @@ export const IMPORT_WARNING_MALFORMED = 'malformed';
|
|
|
11
11
|
export const IMPORT_WARNING_CWD_MISSING = 'cwdMissing';
|
|
12
12
|
/** The conversation continues a history stored in another file that was not followed. */
|
|
13
13
|
export const IMPORT_WARNING_HISTORY_PARTIAL = 'historyPartial';
|
|
14
|
+
/** Earlier history was left out because following it would pass the import size limit. */
|
|
15
|
+
export const IMPORT_WARNING_HISTORY_SIZE_LIMIT = 'historySizeLimit';
|
|
16
|
+
/** Earlier history was left out because its file is missing or could not be read. */
|
|
17
|
+
export const IMPORT_WARNING_HISTORY_UNAVAILABLE = 'historyUnavailable';
|
|
18
|
+
|
|
19
|
+
/** A compressed rollout needs a Node.js runtime with zstd; used as the failure code and as the warning of an unreadable base. */
|
|
20
|
+
export const IMPORT_ERROR_NEEDS_NEWER_NODE = 'needsNewerNode';
|
|
14
21
|
|
|
15
22
|
/** Failure code of a record the source app writes for its own use (not a conversation). */
|
|
16
23
|
export const IMPORT_ERROR_INTERNAL = 'internalRecord';
|
|
@@ -33,7 +40,7 @@ export function toolNote(name) {
|
|
|
33
40
|
}
|
|
34
41
|
|
|
35
42
|
/** Longest model-facing run line, and the longest tool name shown in it. */
|
|
36
|
-
|
|
43
|
+
const TOOL_RUN_LINE_MAX_CHARS = 400;
|
|
37
44
|
const TOOL_RUN_NAME_MAX_CHARS = 40;
|
|
38
45
|
|
|
39
46
|
/**
|
|
@@ -67,6 +74,9 @@ function toolRunCounts(notes) {
|
|
|
67
74
|
return [...counts].map(([name, count]) => ({ name, count }));
|
|
68
75
|
}
|
|
69
76
|
|
|
77
|
+
/** A trimmed non-blank string, else null. */
|
|
78
|
+
export const stringOrNull = (value) => (typeof value === 'string' && value.trim() ? value.trim() : null);
|
|
79
|
+
|
|
70
80
|
export function textPart(text) {
|
|
71
81
|
return { text: String(text), note: false };
|
|
72
82
|
}
|
|
@@ -39,9 +39,10 @@ export function createEagerDispatcher({
|
|
|
39
39
|
repeatFailLimit,
|
|
40
40
|
});
|
|
41
41
|
const barriers = createEagerBarriers({ cwd });
|
|
42
|
-
// A capped
|
|
43
|
-
// (loop/turn-limits.mjs).
|
|
44
|
-
const
|
|
42
|
+
// A turn capped by calls or time runs its calls in order, so none past its
|
|
43
|
+
// cap starts early (loop/turn-limits.mjs).
|
|
44
|
+
const limits = turnLimitsOf(opts);
|
|
45
|
+
const capped = limits !== null && (limits.maxToolCalls !== Infinity || limits.wrapUpAt !== Infinity);
|
|
45
46
|
const execute = (call, entry) =>
|
|
46
47
|
executeToolFn(call.name, call.arguments, cwd, sessionId, sessionRef, {
|
|
47
48
|
toolCallId: call.id,
|
|
@@ -7,12 +7,19 @@
|
|
|
7
7
|
// secret — the user types a value into a target field; the request names
|
|
8
8
|
// the target and the field label, never a value;
|
|
9
9
|
// takeover — the user does a step themselves and says "done";
|
|
10
|
-
// permission — the user picks one of the offered scopes
|
|
10
|
+
// permission — the user picks one of the offered scopes;
|
|
11
|
+
// connector — a bot asks for more authority on one connector tool of its
|
|
12
|
+
// own: the card names the tool, its current level and the one
|
|
13
|
+
// asked for, and the person picks the level (`scope`: one of
|
|
14
|
+
// `choices`) or denies. Answered once, never remembered.
|
|
11
15
|
// A response is one of HUMAN_RESPONSES plus a `remember` scope; a policy lists
|
|
12
16
|
// the scopes it allows and can leave out `always`. Nothing here ever carries a
|
|
13
17
|
// secret value: a decision that names one is stripped to its verdict.
|
|
14
18
|
|
|
15
|
-
const HUMAN_REQUEST_KINDS = Object.freeze(['approval', 'outbound', 'secret', 'takeover', 'permission']);
|
|
19
|
+
const HUMAN_REQUEST_KINDS = Object.freeze(['approval', 'outbound', 'secret', 'takeover', 'permission', 'connector']);
|
|
20
|
+
/** The levels a connector card may raise a tool to. */
|
|
21
|
+
export const CONNECTOR_WIDEN_LEVELS = Object.freeze(['needs_approval', 'auto']);
|
|
22
|
+
const CONNECTOR_LEVELS = ['disabled', ...CONNECTOR_WIDEN_LEVELS];
|
|
16
23
|
const HUMAN_RESPONSES = Object.freeze(['approve', 'deny', 'edit-and-approve', 'provide-secret', 'skip', 'done']);
|
|
17
24
|
const REMEMBER_SCOPES = Object.freeze(['once', 'agent', 'always']);
|
|
18
25
|
export const SECRET_REQUEST_TIMEOUT_MS = 120_000;
|
|
@@ -36,6 +43,7 @@ const RESPONSES_BY_KIND = Object.freeze({
|
|
|
36
43
|
secret: ['provide-secret', 'skip'],
|
|
37
44
|
takeover: ['done', 'deny'],
|
|
38
45
|
permission: ['approve', 'deny'],
|
|
46
|
+
connector: ['approve', 'deny'],
|
|
39
47
|
});
|
|
40
48
|
const GRANTING_RESPONSES = new Set(['approve', 'edit-and-approve', 'provide-secret', 'done']);
|
|
41
49
|
const TEXT_LIMIT = 200;
|
|
@@ -89,12 +97,15 @@ function rememberScopes(value) {
|
|
|
89
97
|
return ['once', ...new Set(listed.filter((scope) => scope !== 'once'))];
|
|
90
98
|
}
|
|
91
99
|
|
|
92
|
-
/** The kind-specific fields of a request, whitelisted
|
|
93
|
-
*
|
|
100
|
+
/** The kind-specific fields of a request, whitelisted, plus the connector
|
|
101
|
+
* `account` a tool call is bound to (any kind; the card names it). Spread
|
|
102
|
+
* onto the presented request; `args` is replaced for a secret so no value
|
|
103
|
+
* rides it. */
|
|
94
104
|
export function humanRequestFields(input = {}) {
|
|
105
|
+
const bound = text(input?.account) ? { account: text(input.account) } : {};
|
|
95
106
|
const kind = input?.kind;
|
|
96
|
-
if (!HUMAN_REQUEST_KINDS.includes(kind)) return
|
|
97
|
-
const fields = { remember: rememberScopes(input.remember) };
|
|
107
|
+
if (!HUMAN_REQUEST_KINDS.includes(kind)) return bound;
|
|
108
|
+
const fields = { ...bound, remember: rememberScopes(input.remember) };
|
|
98
109
|
// The agent the request acts for (its name on the card and the remembered grant).
|
|
99
110
|
if (text(input.agentId)) fields.agentId = text(input.agentId);
|
|
100
111
|
if (kind === 'outbound' && isRecord(input.outbound)) {
|
|
@@ -187,6 +198,20 @@ export function humanRequestFields(input = {}) {
|
|
|
187
198
|
})),
|
|
188
199
|
};
|
|
189
200
|
}
|
|
201
|
+
if (kind === 'connector') {
|
|
202
|
+
const connector = isRecord(input.connector) ? input.connector : {};
|
|
203
|
+
const level = (value) => (CONNECTOR_LEVELS.includes(value) ? value : '');
|
|
204
|
+
fields.connector = {
|
|
205
|
+
server: text(connector.server),
|
|
206
|
+
tool: text(connector.tool),
|
|
207
|
+
from: level(connector.from),
|
|
208
|
+
requested: level(connector.requested),
|
|
209
|
+
choices: (Array.isArray(connector.choices) ? connector.choices : []).filter((choice) =>
|
|
210
|
+
CONNECTOR_WIDEN_LEVELS.includes(choice)
|
|
211
|
+
),
|
|
212
|
+
};
|
|
213
|
+
fields.remember = ['once'];
|
|
214
|
+
}
|
|
190
215
|
return fields;
|
|
191
216
|
}
|
|
192
217
|
|
|
@@ -226,6 +251,11 @@ export function normalizeHumanResponse(request, approved, extra = {}) {
|
|
|
226
251
|
const ids = (request?.permission?.scopes || []).map((scope) => scope.id);
|
|
227
252
|
if (ids.includes(extra?.scope)) result.scope = extra.scope;
|
|
228
253
|
}
|
|
254
|
+
// The level the person picked; none of the offered ones picks nothing.
|
|
255
|
+
if (kind === 'connector' && polarity) {
|
|
256
|
+
const choices = Array.isArray(request?.connector?.choices) ? request.connector.choices : [];
|
|
257
|
+
if (choices.includes(extra?.scope)) result.scope = extra.scope;
|
|
258
|
+
}
|
|
229
259
|
return result;
|
|
230
260
|
}
|
|
231
261
|
|
|
@@ -281,9 +281,11 @@ function currentSkillResolver(sessionRef, { preview = false } = {}) {
|
|
|
281
281
|
const { resource } = delivery;
|
|
282
282
|
// The tools whose use counts as use of the skill: its tool dependencies,
|
|
283
283
|
// an MCP server as `mcp:<server>`.
|
|
284
|
-
const tools = (resource.toolDependencies || []).flatMap(({ type, value }) =>
|
|
285
|
-
type === 'tool'
|
|
286
|
-
|
|
284
|
+
const tools = (resource.toolDependencies || []).flatMap(({ type, value }) => {
|
|
285
|
+
if (type === 'tool') return [value];
|
|
286
|
+
if (type === 'mcp') return [`mcp:${value}`];
|
|
287
|
+
return [];
|
|
288
|
+
});
|
|
287
289
|
if (preview) return { body: delivery.body, native: undefined, tools };
|
|
288
290
|
const envelope = loadSkillToolDependencies(
|
|
289
291
|
buildSkillToolEnvelope(name, resource.content, resource.dir, resource),
|
|
@@ -21,9 +21,20 @@ const isArgsObject = (value) => value && typeof value === 'object' && !Array.isA
|
|
|
21
21
|
|
|
22
22
|
async function askApproval(
|
|
23
23
|
call,
|
|
24
|
-
{
|
|
24
|
+
{
|
|
25
|
+
callerSessionId,
|
|
26
|
+
executeOpts,
|
|
27
|
+
toolApprovalHook,
|
|
28
|
+
decision,
|
|
29
|
+
request = null,
|
|
30
|
+
gate = null,
|
|
31
|
+
onApproved = null,
|
|
32
|
+
account = '',
|
|
33
|
+
}
|
|
25
34
|
) {
|
|
26
35
|
const askReason = String(decision?.reason || 'approval requested by hook').trim();
|
|
36
|
+
// The connector account the call is bound to rides on the card.
|
|
37
|
+
if (account) request = { ...(request || {}), account };
|
|
27
38
|
// While the user decides, the session is waiting on them, not working.
|
|
28
39
|
const releaseApprovalWait = beginSessionApprovalWait(callerSessionId);
|
|
29
40
|
let askOutcome;
|
|
@@ -72,9 +83,13 @@ export const CONNECTOR_APPROVAL_REASON = 'This connector tool is set to ask befo
|
|
|
72
83
|
* remembered outbound grant answered it) is not asked a second time. With the
|
|
73
84
|
* "Allow all actions" developer option on, `needs_approval` is not asked
|
|
74
85
|
* either (a payment or permanent delete was asked by the outbound gate above).
|
|
86
|
+
* `account` is the connector account the call is bound to, for the card.
|
|
75
87
|
* @returns {Promise<{ denial: string } | null>} the denial, or null to run
|
|
76
88
|
*/
|
|
77
|
-
export function applyConnectorApproval(
|
|
89
|
+
export function applyConnectorApproval(
|
|
90
|
+
call,
|
|
91
|
+
{ agentId, callerSessionId, executeOpts, toolApprovalHook, account = '' }
|
|
92
|
+
) {
|
|
78
93
|
if (call.outboundApproved) return null;
|
|
79
94
|
if (!actingBotId(callerSessionId)) return null;
|
|
80
95
|
if (agentMcpToolPermission(agentId, call.name) !== 'needs_approval') return null;
|
|
@@ -89,6 +104,7 @@ export function applyConnectorApproval(call, { agentId, callerSessionId, execute
|
|
|
89
104
|
toolApprovalHook,
|
|
90
105
|
decision: { reason: CONNECTOR_APPROVAL_REASON },
|
|
91
106
|
request: rememberable ? { kind: 'approval', agentId: botId, remember: ['once', 'agent'] } : null,
|
|
107
|
+
account,
|
|
92
108
|
onApproved: async (approval) => {
|
|
93
109
|
if (!rememberable || approval?.remember !== 'agent') return;
|
|
94
110
|
try {
|
|
@@ -130,6 +146,7 @@ export async function applyBeforeToolHook({
|
|
|
130
146
|
beforeToolHook,
|
|
131
147
|
toolApprovalHook,
|
|
132
148
|
mcpScopeId = null,
|
|
149
|
+
account = '',
|
|
133
150
|
}) {
|
|
134
151
|
const call = { name, args, cwd };
|
|
135
152
|
// ask_user and handoff_user are themselves the user prompt; they never go through the ordinary approval gate.
|
|
@@ -183,6 +200,8 @@ export async function applyBeforeToolHook({
|
|
|
183
200
|
decision,
|
|
184
201
|
request,
|
|
185
202
|
gate,
|
|
203
|
+
// Only while the call is still the one bound to it.
|
|
204
|
+
account: call.name === name ? account : '',
|
|
186
205
|
});
|
|
187
206
|
if (denied) return denied;
|
|
188
207
|
}
|
|
@@ -67,6 +67,8 @@ function dispatchMcp(name, args, { cwd, callerSessionId, sessionRef, executeOpts
|
|
|
67
67
|
signal: executeOpts.signal || null,
|
|
68
68
|
ownerKey: callerSessionId,
|
|
69
69
|
scopeId: mcpScopeId,
|
|
70
|
+
// A connector account defaults to (and becomes) this conversation's last used.
|
|
71
|
+
conversation: callerSessionId,
|
|
70
72
|
});
|
|
71
73
|
if (!isBuiltinMailRead(name, mcpScopeId)) return call;
|
|
72
74
|
// The runtime's own record of the links in a mail it read for this session:
|
|
@@ -5,7 +5,7 @@
|
|
|
5
5
|
// tool-exec/dispatch.mjs — executor per tool family
|
|
6
6
|
// tool-exec/after-hook.mjs — PostToolUse result override
|
|
7
7
|
// tool-exec/read-only-deadline.mjs — bounded read-only I/O with partial-output grace
|
|
8
|
-
import { isMcpTool } from '../../mcp/client.mjs';
|
|
8
|
+
import { isMcpTool, resolveMcpToolAccount } from '../../mcp/client.mjs';
|
|
9
9
|
import { connectorPolicyAgentOf } from '../../mcp/agent-connectors.mjs';
|
|
10
10
|
import { prepareDeferredToolCallThrough } from './deferred-call-through.mjs';
|
|
11
11
|
import { refreshDeferredMcpToolCatalog } from '../../runtime-core/tool-catalog.mjs';
|
|
@@ -58,6 +58,24 @@ async function executeToolOwned(name, args, cwd, callerSessionId, sessionRef, ex
|
|
|
58
58
|
callArgs = guarded.args;
|
|
59
59
|
completionToolOpts.shellGuardNotes = guarded.notes;
|
|
60
60
|
}
|
|
61
|
+
// A connector call is bound to its account before any hook or card sees it,
|
|
62
|
+
// so an approval approves that account, whatever is used last meanwhile.
|
|
63
|
+
// It is bound by its unique name; `account` is its label, for display.
|
|
64
|
+
let bound = '';
|
|
65
|
+
let account = '';
|
|
66
|
+
if (isMcpTool(name)) {
|
|
67
|
+
try {
|
|
68
|
+
const chosen = resolveMcpToolAccount(name, callArgs, {
|
|
69
|
+
scopeId: sessionRef?.mcpScopeId || null,
|
|
70
|
+
conversation: callerSessionId,
|
|
71
|
+
});
|
|
72
|
+
bound = chosen?.name || '';
|
|
73
|
+
account = chosen?.label || '';
|
|
74
|
+
} catch (error) {
|
|
75
|
+
return `Error: ${error?.message || String(error)}`;
|
|
76
|
+
}
|
|
77
|
+
if (bound) callArgs = { ...callArgs, account: bound };
|
|
78
|
+
}
|
|
61
79
|
const hooked = await applyBeforeToolHook({
|
|
62
80
|
name,
|
|
63
81
|
args: callArgs,
|
|
@@ -67,6 +85,7 @@ async function executeToolOwned(name, args, cwd, callerSessionId, sessionRef, ex
|
|
|
67
85
|
beforeToolHook: hookFrom(executeOpts, sessionRef, 'beforeToolHook'),
|
|
68
86
|
toolApprovalHook,
|
|
69
87
|
mcpScopeId: sessionRef?.mcpScopeId || null,
|
|
88
|
+
account,
|
|
70
89
|
});
|
|
71
90
|
if (hooked.denial) return hooked.denial;
|
|
72
91
|
const connectorDenied = await applyConnectorApproval(hooked, {
|
|
@@ -74,8 +93,11 @@ async function executeToolOwned(name, args, cwd, callerSessionId, sessionRef, ex
|
|
|
74
93
|
callerSessionId,
|
|
75
94
|
executeOpts,
|
|
76
95
|
toolApprovalHook,
|
|
96
|
+
account: hooked.name === name ? account : '',
|
|
77
97
|
});
|
|
78
98
|
if (connectorDenied) return connectorDenied.denial;
|
|
99
|
+
// A hook's rewrite of the same tool keeps the bound account.
|
|
100
|
+
if (bound && hooked.name === name) hooked.args = { ...hooked.args, account: bound };
|
|
79
101
|
// A decision can settle after its turn was cancelled. It must never
|
|
80
102
|
// authorize a new mutation, even if the hook ignores the abort signal.
|
|
81
103
|
throwIfAborted(executeOpts.signal);
|
|
@@ -1,12 +1,14 @@
|
|
|
1
1
|
/**
|
|
2
2
|
* turn-limits.mjs — a turn's caps (sendOpts.turnLimits; a heartbeat turn's,
|
|
3
|
-
* manager/ask-turn-loop-options.mjs): `maxToolCalls` tool calls
|
|
4
|
-
* `wrapUpAt` (epoch ms)
|
|
5
|
-
*
|
|
6
|
-
*
|
|
7
|
-
*
|
|
8
|
-
*
|
|
9
|
-
*
|
|
3
|
+
* manager/ask-turn-loop-options.mjs): `maxToolCalls` tool calls,
|
|
4
|
+
* `wrapUpAt` (epoch ms) and `maxToolRounds` tool-calling responses (an
|
|
5
|
+
* explorer's, runtime/explore/explorer.mjs). A call past the cap, or one made
|
|
6
|
+
* after the wrap-up time, is answered without running. Once a cap is reached
|
|
7
|
+
* the model is told to reply, and every later request of the turn forbids
|
|
8
|
+
* tool use (toolChoice 'none'). A response that still calls tools after that
|
|
9
|
+
* ends the turn with no reply, its calls answered unrun. A turn capped by
|
|
10
|
+
* calls or time runs no call eagerly (eager-dispatch.mjs), so none past the
|
|
11
|
+
* cap starts while streaming; a round cap cuts only between rounds.
|
|
10
12
|
*/
|
|
11
13
|
|
|
12
14
|
const NOTICE =
|
|
@@ -21,6 +23,8 @@ export function turnLimitsOf(opts) {
|
|
|
21
23
|
return {
|
|
22
24
|
maxToolCalls: Number.isInteger(limits.maxToolCalls) && limits.maxToolCalls >= 0 ? limits.maxToolCalls : Infinity,
|
|
23
25
|
wrapUpAt: Number.isFinite(limits.wrapUpAt) ? limits.wrapUpAt : Infinity,
|
|
26
|
+
maxToolRounds:
|
|
27
|
+
Number.isInteger(limits.maxToolRounds) && limits.maxToolRounds >= 0 ? limits.maxToolRounds : Infinity,
|
|
24
28
|
};
|
|
25
29
|
}
|
|
26
30
|
|
|
@@ -49,7 +53,8 @@ export function settleTurnLimits(state, overrun, now = Date.now()) {
|
|
|
49
53
|
}
|
|
50
54
|
const limits = turnLimitsOf(state.opts);
|
|
51
55
|
if (!limits || state.turnLimitNoticed) return;
|
|
52
|
-
if (state.toolCallsTotal < limits.maxToolCalls && now < limits.wrapUpAt)
|
|
56
|
+
if (state.toolCallsTotal < limits.maxToolCalls && now < limits.wrapUpAt && state.iterations < limits.maxToolRounds)
|
|
57
|
+
return;
|
|
53
58
|
state.turnLimitNoticed = true;
|
|
54
59
|
state.messages.push({ role: 'user', content: NOTICE, meta: { source: 'turn-limit' } });
|
|
55
60
|
}
|
|
@@ -19,6 +19,7 @@ import { armTurnDurability, claimTurnRuntime } from './ask-turn-open.mjs';
|
|
|
19
19
|
import { prepareAskTurn, runAskAgentLoop } from './ask-turn-execute.mjs';
|
|
20
20
|
import { commitAskTurn, publishAskTurn } from './ask-turn-settle.mjs';
|
|
21
21
|
import { finalizeAskTurnFailure } from './ask-turn-failure.mjs';
|
|
22
|
+
import { armTurnTimeoutOnProviderError, reapTurnShellJobs } from './turn-reap.mjs';
|
|
22
23
|
import { cacheWarmTurnKind, endCacheWarmTurn, failedTurnProvenance, noteCacheWarmTurn } from '../cache-warm.mjs';
|
|
23
24
|
|
|
24
25
|
export { _api_call_with_interrupt } from './ask-call-interrupt.mjs';
|
|
@@ -125,6 +126,9 @@ async function runAskTurn({ sessionId, opened, input, askOpts, onToolCall, cwdOv
|
|
|
125
126
|
});
|
|
126
127
|
return { result: terminalResultPreview, drained };
|
|
127
128
|
} catch (err) {
|
|
129
|
+
// The turn's terminal error: a provider timeout/stall that ended it (a
|
|
130
|
+
// retry the turn recovered from never reaches here) stops its shell work.
|
|
131
|
+
armTurnTimeoutOnProviderError(sessionId, opened.runtime, opened.turnSignal, err);
|
|
128
132
|
await finalizeAskTurnFailure({
|
|
129
133
|
sessionId,
|
|
130
134
|
err,
|
|
@@ -138,12 +142,49 @@ async function runAskTurn({ sessionId, opened, input, askOpts, onToolCall, cwdOv
|
|
|
138
142
|
transcriptMeta: transcript.raw,
|
|
139
143
|
askStartedAt,
|
|
140
144
|
});
|
|
145
|
+
reapTurnShellJobs(sessionId, opened.runtime);
|
|
141
146
|
throw err;
|
|
142
147
|
} finally {
|
|
143
|
-
|
|
148
|
+
// The result is already published; this only holds the session lock until
|
|
149
|
+
// the reader lease is removed, so the next turn's admission follows it. A
|
|
150
|
+
// failed release (the admission thread stopped) never replaces the turn's
|
|
151
|
+
// own outcome.
|
|
152
|
+
try {
|
|
153
|
+
await releaseWorkspace?.();
|
|
154
|
+
} catch {
|
|
155
|
+
// Intentionally ignored: see the comment above.
|
|
156
|
+
}
|
|
144
157
|
}
|
|
145
158
|
}
|
|
146
159
|
|
|
160
|
+
/** The prompt-cache warmer learns idle gaps from real turns only; a retry is
|
|
161
|
+
* classified by the failed turn's stored provenance. The rebuilt retry
|
|
162
|
+
* message keeps that provenance (ask-turn-execute.mjs). Returns the warmer's
|
|
163
|
+
* turn handle. */
|
|
164
|
+
function noteTurnForCacheWarm(sessionId, claimed, input) {
|
|
165
|
+
const retryProvenance = failedTurnProvenance(claimed.preSession?.messages, input);
|
|
166
|
+
if (retryProvenance) input.retryProvenance = retryProvenance;
|
|
167
|
+
return noteCacheWarmTurn(sessionId, cacheWarmTurnKind(input, claimed.preSession?.messages));
|
|
168
|
+
}
|
|
169
|
+
|
|
170
|
+
/** Clear the controller only if it's still ours (closeSession may have
|
|
171
|
+
* swapped it). The rest of the runtime entry stays intact so agent type=list
|
|
172
|
+
* can still surface the final stage (done/error/cancelling). */
|
|
173
|
+
function detachAskRuntime(sessionId, askGeneration) {
|
|
174
|
+
const entry = _getRuntimeEntry(sessionId);
|
|
175
|
+
if (entry && entry.generation === askGeneration) {
|
|
176
|
+
_unlinkParentAbortListener(entry);
|
|
177
|
+
entry.controller = null;
|
|
178
|
+
// Detach the live session reference; ask is over.
|
|
179
|
+
entry.session = null;
|
|
180
|
+
}
|
|
181
|
+
// Final-stage runtime diagnostics are useful only while the turn is
|
|
182
|
+
// unwinding. Once its controller is detached, retaining the full entry
|
|
183
|
+
// (and any accidental references hanging from it) for the host lifetime
|
|
184
|
+
// turns one-shot agent traffic into an unbounded manager Map.
|
|
185
|
+
_evictTerminalSessionRuntime(sessionId);
|
|
186
|
+
}
|
|
187
|
+
|
|
147
188
|
export async function askSession(sessionId, prompt, context, onToolCall, cwdOverride, explicitPrefetch, askOpts = {}) {
|
|
148
189
|
const askStartedAt = Date.now();
|
|
149
190
|
const transcript = createAskTranscriptMeta(askOpts?.transcriptMeta);
|
|
@@ -180,8 +221,9 @@ export async function askSession(sessionId, prompt, context, onToolCall, cwdOver
|
|
|
180
221
|
// Hoisted so the outer finally (which runs once after the whole turn loop)
|
|
181
222
|
// can compare against the last turn's generation.
|
|
182
223
|
let askGeneration = 0;
|
|
183
|
-
|
|
184
|
-
|
|
224
|
+
// Crash-checkpoint recovery and the session-start event belong to the first
|
|
225
|
+
// turn of this ask only.
|
|
226
|
+
let firstTurn = true;
|
|
185
227
|
// A failed-turn retry rewinds only the turn it retries, never a drained
|
|
186
228
|
// follow-up turn.
|
|
187
229
|
let retryFailedTurn = askOpts.retryFailedTurn === true;
|
|
@@ -218,17 +260,11 @@ export async function askSession(sessionId, prompt, context, onToolCall, cwdOver
|
|
|
218
260
|
const claimed = claimTurnRuntime({
|
|
219
261
|
sessionId,
|
|
220
262
|
askOpts,
|
|
221
|
-
recoverCheckpoint:
|
|
222
|
-
emitStart:
|
|
263
|
+
recoverCheckpoint: firstTurn,
|
|
264
|
+
emitStart: firstTurn,
|
|
223
265
|
});
|
|
224
|
-
|
|
225
|
-
|
|
226
|
-
// The rebuilt retry message keeps that provenance (ask-turn-execute.mjs).
|
|
227
|
-
const retryProvenance = failedTurnProvenance(claimed.preSession?.messages, input);
|
|
228
|
-
if (retryProvenance) input.retryProvenance = retryProvenance;
|
|
229
|
-
warmTurn = noteCacheWarmTurn(sessionId, cacheWarmTurnKind(input, claimed.preSession?.messages));
|
|
230
|
-
crashRecoveryChecked = true;
|
|
231
|
-
sessionStartEmitted = true;
|
|
266
|
+
warmTurn = noteTurnForCacheWarm(sessionId, claimed, input);
|
|
267
|
+
firstTurn = false;
|
|
232
268
|
askGeneration = claimed.askGeneration;
|
|
233
269
|
const opened = {
|
|
234
270
|
...claimed,
|
|
@@ -287,21 +323,7 @@ export async function askSession(sessionId, prompt, context, onToolCall, cwdOver
|
|
|
287
323
|
queue.releaseUnstarted();
|
|
288
324
|
// A thrown setup/provider path must never leave a delayed checkpoint
|
|
289
325
|
// writer alive after the turn lock is released.
|
|
290
|
-
|
|
291
|
-
// swapped it). Leave the rest of the runtime entry intact so agent type=list
|
|
292
|
-
// can still surface the final stage (done/error/cancelling).
|
|
293
|
-
const entry = _getRuntimeEntry(sessionId);
|
|
294
|
-
if (entry && entry.generation === askGeneration) {
|
|
295
|
-
_unlinkParentAbortListener(entry);
|
|
296
|
-
entry.controller = null;
|
|
297
|
-
// Detach the live session reference; ask is over.
|
|
298
|
-
entry.session = null;
|
|
299
|
-
}
|
|
300
|
-
// Final-stage runtime diagnostics are useful only while the turn is
|
|
301
|
-
// unwinding. Once its controller is detached, retaining the full entry
|
|
302
|
-
// (and any accidental references hanging from it) for the host lifetime
|
|
303
|
-
// turns one-shot agent traffic into an unbounded manager Map.
|
|
304
|
-
_evictTerminalSessionRuntime(sessionId);
|
|
326
|
+
detachAskRuntime(sessionId, askGeneration);
|
|
305
327
|
unlock();
|
|
306
328
|
}
|
|
307
329
|
}
|
|
@@ -578,6 +578,7 @@ function isReferenceFilesMessage(message) {
|
|
|
578
578
|
}
|
|
579
579
|
|
|
580
580
|
export { isProtectedContextUserMessage };
|
|
581
|
+
export { BP3_PART_SEPARATOR, bp3SystemMessage, bpEnvSystemMessage, replaceSystemMessageContent };
|
|
581
582
|
|
|
582
583
|
// Compact summary messages (role:'user', content startsWith SUMMARY_PREFIX)
|
|
583
584
|
// are synthetic anchors, not a real human turn — they must not count as
|
|
@@ -117,8 +117,7 @@ export function _buildBaseRules({
|
|
|
117
117
|
.join('\n\n---\n\n');
|
|
118
118
|
}
|
|
119
119
|
|
|
120
|
-
export function _buildAgentRules(
|
|
121
|
-
const key = String(profile || 'full');
|
|
120
|
+
export function _buildAgentRules() {
|
|
122
121
|
const PLUGIN_ROOT = mixdogRoot();
|
|
123
122
|
const DATA_DIR = resolvePluginData();
|
|
124
123
|
const RULES_DIR = join(PLUGIN_ROOT, 'rules');
|
|
@@ -126,8 +125,8 @@ export function _buildAgentRules(profile = 'full') {
|
|
|
126
125
|
'buildAgentRoleContent',
|
|
127
126
|
'agent role rules',
|
|
128
127
|
[join(RULES_DIR, 'agent'), join(DATA_DIR, 'mixdog-config.json')],
|
|
129
|
-
{ PLUGIN_ROOT, DATA_DIR
|
|
130
|
-
|
|
128
|
+
{ PLUGIN_ROOT, DATA_DIR },
|
|
129
|
+
'full'
|
|
131
130
|
);
|
|
132
131
|
}
|
|
133
132
|
|
|
@@ -35,6 +35,7 @@ import { registerSessionPurgeHook } from '../store.mjs';
|
|
|
35
35
|
import { finishComputerTurn } from '../../../../computer-bridge/client.mjs';
|
|
36
36
|
import { configureUsageMetricsRuntime, dropMetricSeenState, bumpUsageMetricsTurnId } from './usage-metrics.mjs';
|
|
37
37
|
import { runtimeState as _runtimeState, isTurnInFlight } from './runtime-state.mjs';
|
|
38
|
+
import { resetTurnShellReap } from './turn-reap.mjs';
|
|
38
39
|
|
|
39
40
|
export { IN_FLIGHT_STAGES, inFlightSessionActivity } from './runtime-state.mjs';
|
|
40
41
|
|
|
@@ -216,6 +217,7 @@ export function markSessionAskStart(id) {
|
|
|
216
217
|
// after provider admission (the scheduler emits stage=requesting).
|
|
217
218
|
const now = Date.now();
|
|
218
219
|
entry.askStartedAt = now;
|
|
220
|
+
resetTurnShellReap(id, entry);
|
|
219
221
|
_setLogicalModelRequest(entry, null);
|
|
220
222
|
entry.lastProgressAt = now;
|
|
221
223
|
entry.updatedAt = now;
|
|
@@ -30,6 +30,7 @@ import { releaseReadSnapshotScope } from '../../tools/builtin/snapshot-store.mjs
|
|
|
30
30
|
import { cancelOwnedShellTrees } from '../../tools/lib/owned-shell-trees.mjs';
|
|
31
31
|
import { noteShellOwnerSessionClosed } from '../../tools/builtin/lib/shell-job-records.mjs';
|
|
32
32
|
import { forgetSideQuestion } from '../side-question-store.mjs';
|
|
33
|
+
import { cancelBackgroundTasks } from '../../../../shared/background-tasks.mjs';
|
|
33
34
|
|
|
34
35
|
/**
|
|
35
36
|
* Close a session. Plants a `closed=true` tombstone on disk with a bumped
|
|
@@ -214,6 +215,12 @@ function cancelOwnedShellProcesses({ id, reason, tombstone }) {
|
|
|
214
215
|
} catch {
|
|
215
216
|
unconfirmed();
|
|
216
217
|
}
|
|
218
|
+
// What a finished command left running is a tracked task, not an owned tree.
|
|
219
|
+
try {
|
|
220
|
+
cancelBackgroundTasks({ surface: 'shell', executionSessionId: id, reason, notify: false });
|
|
221
|
+
} catch {
|
|
222
|
+
unconfirmed();
|
|
223
|
+
}
|
|
217
224
|
noteShellOwnerSessionClosed(id);
|
|
218
225
|
}
|
|
219
226
|
|
|
@@ -14,6 +14,7 @@ import { runSessionCompaction, resolveSessionCompactionPolicy } from './compacti
|
|
|
14
14
|
import {
|
|
15
15
|
currentContextEstimateTokens,
|
|
16
16
|
invalidateContextUsageSnapshot,
|
|
17
|
+
invalidateProviderContextBaseline,
|
|
17
18
|
recordContextUsageSnapshot,
|
|
18
19
|
resolveGaugeContextTokens,
|
|
19
20
|
} from '../loop/compact-policy.mjs';
|
|
@@ -26,6 +27,8 @@ import {
|
|
|
26
27
|
import { getProvider } from '../../providers/registry.mjs';
|
|
27
28
|
import { isSessionCompactionBlocked, getSessionAbortSignal, _runtimeEntries } from './runtime-liveness.mjs';
|
|
28
29
|
import { mintSessionId } from './session-id.mjs';
|
|
30
|
+
import { useOwnScratchEnvironment } from './session-scratch-environment.mjs';
|
|
31
|
+
import { rebindToChangedPrefix } from '../thinking-binding.mjs';
|
|
29
32
|
import { hideCompactionArchive } from '../compaction-archive-capture.mjs';
|
|
30
33
|
import { writeStderrBestEffort } from '../../../../shared/best-effort-stderr.mjs';
|
|
31
34
|
import { forgetSideQuestion } from '../side-question-store.mjs';
|
|
@@ -234,6 +237,18 @@ function forkOutgoingTranscript(session, messages, now, archiveAs = '') {
|
|
|
234
237
|
...clearedTokenAccounting(now),
|
|
235
238
|
};
|
|
236
239
|
delete fork.liveTurnMessages;
|
|
240
|
+
// The spread copied the source's environment: the fork names its own
|
|
241
|
+
// scratch directory (its shell commands use it), not the source's.
|
|
242
|
+
if (useOwnScratchEnvironment(fork)) {
|
|
243
|
+
// The system prefix changed: thinking stored in the copy was bound to the
|
|
244
|
+
// source's prefix (and an open tool round ended), and every measurement
|
|
245
|
+
// taken against it is stale. Resume then finds a matching prefix and
|
|
246
|
+
// would repair nothing.
|
|
247
|
+
fork.messages = rebindToChangedPrefix(fork.messages);
|
|
248
|
+
delete fork._providerPrefixGuardState;
|
|
249
|
+
invalidateContextUsageSnapshot(fork);
|
|
250
|
+
invalidateProviderContextBaseline(fork);
|
|
251
|
+
}
|
|
237
252
|
// The archive sidecar belongs to the source id; the fork has none.
|
|
238
253
|
if (fork.compaction?.archive) {
|
|
239
254
|
fork.compaction = { ...fork.compaction };
|