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
|
@@ -8,9 +8,9 @@
|
|
|
8
8
|
* Data shape: { projects: [{ name, path, addedAt, lastSelectedAt? }] }
|
|
9
9
|
*/
|
|
10
10
|
import { homedir } from 'node:os';
|
|
11
|
-
import { existsSync, mkdirSync, readFileSync,
|
|
11
|
+
import { existsSync, mkdirSync, readFileSync, statSync } from 'node:fs';
|
|
12
12
|
import { basename, isAbsolute, join, resolve } from 'node:path';
|
|
13
|
-
import {
|
|
13
|
+
import { holdsNoData, readJsonStateSync, updateJsonAtomicSync, writeFileAtomicSync } from './atomic-file.mjs';
|
|
14
14
|
|
|
15
15
|
const MIXDOG_HOME = process.env.MIXDOG_HOME || join(homedir(), '.mixdog');
|
|
16
16
|
const PROJECTS_FILE = process.env.MIXDOG_PROJECTS_FILE
|
|
@@ -30,47 +30,57 @@ function normalizeKey(absPath) {
|
|
|
30
30
|
return process.platform === 'win32' ? text.replace(/[\\/]+$/, '').toLowerCase() : text.replace(/\/+$/, '');
|
|
31
31
|
}
|
|
32
32
|
|
|
33
|
+
// The registry document as a store. A strict read (permission checks and
|
|
34
|
+
// mutations) drops entries without an absolute path: they can never grant
|
|
35
|
+
// anything, and the replaced file survives as projects.json.bak.
|
|
36
|
+
function normalizeStore(parsed, strict) {
|
|
37
|
+
const projects = Array.isArray(parsed?.projects) ? parsed.projects : [];
|
|
38
|
+
return {
|
|
39
|
+
projects: projects
|
|
40
|
+
.filter(
|
|
41
|
+
(entry) => entry && typeof entry.path === 'string' && entry.path.trim() && (!strict || isAbsolute(entry.path))
|
|
42
|
+
)
|
|
43
|
+
.map((entry) => ({
|
|
44
|
+
name: String(entry.name || basename(entry.path) || entry.path),
|
|
45
|
+
path: String(entry.path),
|
|
46
|
+
addedAt: Number(entry.addedAt) || 0,
|
|
47
|
+
...(Number(entry.lastSelectedAt) > 0 ? { lastSelectedAt: Number(entry.lastSelectedAt) } : {}),
|
|
48
|
+
})),
|
|
49
|
+
};
|
|
50
|
+
}
|
|
51
|
+
|
|
52
|
+
// A damaged registry file (NUL, truncated, malformed) is kept aside as
|
|
53
|
+
// projects.json.corrupt-<ts> and the last good projects.json.bak is restored
|
|
54
|
+
// when there is one, else the registry starts empty: a damaged file never
|
|
55
|
+
// blocks adding, removing or selecting a project. Strict callers still see a
|
|
56
|
+
// real I/O failure (EACCES, EIO) as an error — an unreadable registry is not
|
|
57
|
+
// an empty one; the tolerant listing reads it as empty.
|
|
33
58
|
function readStore(strict = false) {
|
|
34
59
|
try {
|
|
35
|
-
|
|
36
|
-
const raw = readFileSync(PROJECTS_FILE, 'utf8');
|
|
37
|
-
const parsed = JSON.parse(raw);
|
|
38
|
-
if (
|
|
39
|
-
strict &&
|
|
40
|
-
(!Array.isArray(parsed?.projects) ||
|
|
41
|
-
parsed.projects.some(
|
|
42
|
-
(entry) => !entry || typeof entry.path !== 'string' || !entry.path.trim() || !isAbsolute(entry.path)
|
|
43
|
-
))
|
|
44
|
-
)
|
|
45
|
-
throw new TypeError('Project registry is invalid.');
|
|
46
|
-
const projects = Array.isArray(parsed?.projects) ? parsed.projects : [];
|
|
47
|
-
return {
|
|
48
|
-
projects: projects
|
|
49
|
-
.filter((entry) => entry && typeof entry.path === 'string' && entry.path.trim())
|
|
50
|
-
.map((entry) => ({
|
|
51
|
-
name: String(entry.name || basename(entry.path) || entry.path),
|
|
52
|
-
path: String(entry.path),
|
|
53
|
-
addedAt: Number(entry.addedAt) || 0,
|
|
54
|
-
...(Number(entry.lastSelectedAt) > 0 ? { lastSelectedAt: Number(entry.lastSelectedAt) } : {}),
|
|
55
|
-
})),
|
|
56
|
-
};
|
|
60
|
+
return normalizeStore(readJsonStateSync(PROJECTS_FILE), strict);
|
|
57
61
|
} catch (error) {
|
|
58
|
-
if (strict
|
|
62
|
+
if (strict) throw error;
|
|
59
63
|
return { projects: [] };
|
|
60
64
|
}
|
|
61
65
|
}
|
|
62
66
|
|
|
63
67
|
// Serialize the entire read/modify/write across cooperating processes, not
|
|
64
|
-
// just publication
|
|
65
|
-
//
|
|
66
|
-
//
|
|
68
|
+
// just publication: the file is read under the registry lock. Preserve
|
|
69
|
+
// synchronous return values and no-op behavior (an unchanged store is not
|
|
70
|
+
// written); failed reads, lock acquisition and writes propagate. Each write
|
|
71
|
+
// keeps the replaced version as projects.json.bak.
|
|
67
72
|
function mutateStore(mutator) {
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
73
|
+
let outcome;
|
|
74
|
+
updateJsonAtomicSync(
|
|
75
|
+
PROJECTS_FILE,
|
|
76
|
+
(current) => {
|
|
77
|
+
const store = normalizeStore(current, true);
|
|
78
|
+
outcome = mutator(store);
|
|
79
|
+
return outcome.changed ? store : undefined;
|
|
80
|
+
},
|
|
81
|
+
{ backup: true }
|
|
82
|
+
);
|
|
83
|
+
return outcome.value;
|
|
74
84
|
}
|
|
75
85
|
|
|
76
86
|
/**
|
|
@@ -78,7 +88,8 @@ function mutateStore(mutator) {
|
|
|
78
88
|
* selection store and memory project_id classification (resolveProjectId)
|
|
79
89
|
* agree. Rules:
|
|
80
90
|
* - Never overwrite an existing marker (user may have set 'common' or a
|
|
81
|
-
* custom id — respect it).
|
|
91
|
+
* custom id — respect it). A marker holding no data (empty or NUL, what a
|
|
92
|
+
* crash leaves) is no id: it is rewritten.
|
|
82
93
|
* - Only act when absPath is a real directory on disk; never create the
|
|
83
94
|
* project folder itself (addProject does NOT create the directory).
|
|
84
95
|
* - Marker value = provided name, else basename(absPath). Skip when the
|
|
@@ -97,14 +108,12 @@ function ensureProjectIdMarker(absPath, name) {
|
|
|
97
108
|
return; // missing / unstattable → silently skip
|
|
98
109
|
}
|
|
99
110
|
const markerPath = join(absPath, '.mixdog', 'project.id');
|
|
100
|
-
|
|
111
|
+
const present = existsSync(markerPath);
|
|
112
|
+
if (present && !holdsNoData(readFileSync(markerPath))) return; // preserve existing value
|
|
101
113
|
const value = String(name || basename(absPath) || '').trim();
|
|
102
114
|
if (!value || value.toLowerCase() === 'common') return;
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
const tmp = `${markerPath}.${process.pid}.tmp`;
|
|
106
|
-
writeFileSync(tmp, `${value}\n`, 'utf8');
|
|
107
|
-
renameSync(tmp, markerPath);
|
|
115
|
+
// A missing marker is created only if still missing (a concurrent creator's wins).
|
|
116
|
+
writeFileAtomicSync(markerPath, `${value}\n`, { createOnly: !present });
|
|
108
117
|
} catch {
|
|
109
118
|
/* best-effort marker; never throw */
|
|
110
119
|
}
|
|
@@ -1,7 +1,6 @@
|
|
|
1
1
|
import { createHash, randomUUID } from 'node:crypto';
|
|
2
|
-
import { readFileSync } from 'node:fs';
|
|
3
2
|
import { join } from 'node:path';
|
|
4
|
-
import {
|
|
3
|
+
import { readJsonStateSync, unsupportedState, updateJsonAtomic } from './atomic-file.mjs';
|
|
5
4
|
import { resolvePluginData } from './plugin-paths.mjs';
|
|
6
5
|
|
|
7
6
|
export const ACCOUNT_PROVIDERS = Object.freeze([
|
|
@@ -34,12 +33,19 @@ function requireAccountId(id) {
|
|
|
34
33
|
|
|
35
34
|
function validate(raw) {
|
|
36
35
|
if (raw == null) return { version: 1, providers: {} };
|
|
36
|
+
// A newer build's roster is left in place: reads see no accounts and writes
|
|
37
|
+
// refuse, so a downgrade never discards it.
|
|
38
|
+
if (Number.isSafeInteger(raw.version) && raw.version > 1) {
|
|
39
|
+
throw unsupportedState(`Provider account settings version ${raw.version} is newer than this build.`);
|
|
40
|
+
}
|
|
37
41
|
if (raw.version !== 1 || !raw.providers || typeof raw.providers !== 'object') {
|
|
38
42
|
throw new Error('Provider account settings are unreadable.');
|
|
39
43
|
}
|
|
40
44
|
for (const [provider, pool] of Object.entries(raw.providers)) {
|
|
41
|
-
|
|
42
|
-
|
|
45
|
+
// A provider a newer build added is carried as is: this build never reads
|
|
46
|
+
// or writes its pool, and treating it as damage would discard it.
|
|
47
|
+
if (!ACCOUNT_PROVIDERS.includes(provider)) continue;
|
|
48
|
+
if (!Array.isArray(pool?.accounts) || pool.accounts.length > MAX_ACCOUNTS) {
|
|
43
49
|
throw new Error('Invalid provider account list.');
|
|
44
50
|
}
|
|
45
51
|
const ids = pool.accounts.map((row) => requireAccountId(row.id));
|
|
@@ -48,13 +54,11 @@ function validate(raw) {
|
|
|
48
54
|
return raw;
|
|
49
55
|
}
|
|
50
56
|
|
|
57
|
+
// Every OAuth provider's init reads the roster, so a damaged one (crash
|
|
58
|
+
// leftovers, truncation, a shape this build rejects) must never throw: it is
|
|
59
|
+
// kept aside and the last good copy, which every write keeps, is restored.
|
|
51
60
|
function read(dataDir) {
|
|
52
|
-
|
|
53
|
-
return validate(parseJsonText(readFileSync(file(dataDir), 'utf8')));
|
|
54
|
-
} catch (error) {
|
|
55
|
-
if (error.code === 'ENOENT') return validate(null);
|
|
56
|
-
throw error;
|
|
57
|
-
}
|
|
61
|
+
return readJsonStateSync(file(dataDir), { validate, fallback: validate(null), secret: true });
|
|
58
62
|
}
|
|
59
63
|
|
|
60
64
|
// Labels an earlier build assigned automatically. They read as a different
|
|
@@ -106,7 +110,8 @@ export function readProviderAccountPool(provider, dataDir = resolvePluginData())
|
|
|
106
110
|
|
|
107
111
|
async function update(provider, mutate) {
|
|
108
112
|
requireAccountProvider(provider);
|
|
109
|
-
//
|
|
113
|
+
// A roster that parses yet fails validation is recovered here first; the
|
|
114
|
+
// mutator's validate would otherwise refuse every write. I/O failures throw.
|
|
110
115
|
read();
|
|
111
116
|
const result = await updateJsonAtomic(
|
|
112
117
|
file(),
|
|
@@ -117,7 +122,7 @@ async function update(provider, mutate) {
|
|
|
117
122
|
state.providers[provider] = pool;
|
|
118
123
|
return validate(state);
|
|
119
124
|
},
|
|
120
|
-
{ mode: 0o600, secret: true, fsyncDir: true }
|
|
125
|
+
{ mode: 0o600, secret: true, fsyncDir: true, backup: true }
|
|
121
126
|
);
|
|
122
127
|
return normalizePool(result.providers[provider]);
|
|
123
128
|
}
|
|
@@ -0,0 +1,18 @@
|
|
|
1
|
+
import { pathToFileURL } from 'node:url';
|
|
2
|
+
|
|
3
|
+
/**
|
|
4
|
+
* `text` with every mention of the given directories taken out (and the
|
|
5
|
+
* separator after it), whether written as a native path, with forward slashes
|
|
6
|
+
* or as a file URL, so an error never discloses a private scratch location.
|
|
7
|
+
*/
|
|
8
|
+
export function redactDirectories(text, directories) {
|
|
9
|
+
let out = String(text ?? '');
|
|
10
|
+
for (const directory of directories) {
|
|
11
|
+
if (!directory) continue;
|
|
12
|
+
const forms = new Set([directory, directory.replaceAll('\\', '/'), pathToFileURL(directory).href]);
|
|
13
|
+
for (const form of [...forms].sort((a, b) => b.length - a.length)) {
|
|
14
|
+
out = out.split(`${form}\\`).join('').split(`${form}/`).join('').split(form).join('');
|
|
15
|
+
}
|
|
16
|
+
}
|
|
17
|
+
return out;
|
|
18
|
+
}
|
|
@@ -0,0 +1,28 @@
|
|
|
1
|
+
import { rm } from 'node:fs/promises';
|
|
2
|
+
|
|
3
|
+
export const REMOVE_RETRY_MS = 100;
|
|
4
|
+
const REMOVE_ATTEMPTS = 6;
|
|
5
|
+
const RETRYABLE_REMOVE_CODES = new Set(['EBUSY', 'EPERM', 'ENOTEMPTY', 'EACCES']);
|
|
6
|
+
|
|
7
|
+
/**
|
|
8
|
+
* Removes a private scratch folder. A process that just exited can still hold
|
|
9
|
+
* its files for a moment on Windows, so a busy folder is retried briefly; a
|
|
10
|
+
* failure that persists is logged (never swallowed) and does not throw.
|
|
11
|
+
*/
|
|
12
|
+
export async function removeDirectoryWithRetry(
|
|
13
|
+
dir,
|
|
14
|
+
{ remove = rm, log = (line) => process.stderr.write(line), retryMs = REMOVE_RETRY_MS } = {}
|
|
15
|
+
) {
|
|
16
|
+
for (let attempt = 1; ; attempt += 1) {
|
|
17
|
+
try {
|
|
18
|
+
await remove(dir, { recursive: true, force: true });
|
|
19
|
+
return;
|
|
20
|
+
} catch (error) {
|
|
21
|
+
if (attempt >= REMOVE_ATTEMPTS || !RETRYABLE_REMOVE_CODES.has(error?.code)) {
|
|
22
|
+
log(`scratch folder ${dir} could not be removed: ${error?.message || error}\n`);
|
|
23
|
+
return;
|
|
24
|
+
}
|
|
25
|
+
await new Promise((resolve) => setTimeout(resolve, retryMs * attempt));
|
|
26
|
+
}
|
|
27
|
+
}
|
|
28
|
+
}
|
|
@@ -4,7 +4,12 @@ export interface RoutineTrigger {
|
|
|
4
4
|
event?: string;
|
|
5
5
|
}
|
|
6
6
|
|
|
7
|
-
export declare function routineTriggerHeader(input: {
|
|
7
|
+
export declare function routineTriggerHeader(input: {
|
|
8
|
+
kind: string;
|
|
9
|
+
name: string;
|
|
10
|
+
project?: string;
|
|
11
|
+
now?: Date | number;
|
|
12
|
+
}): string;
|
|
8
13
|
export declare function stripRoutineTriggerHeader(text: unknown): string;
|
|
9
14
|
export declare function normalizeRoutineTrigger(value: unknown): RoutineTrigger | null;
|
|
10
15
|
export declare function routineTriggerField(value: unknown): { routineTrigger?: RoutineTrigger };
|
|
@@ -4,18 +4,26 @@
|
|
|
4
4
|
// carries (live, persisted in meta.transcript, restored) so a chat paints it
|
|
5
5
|
// as a routine marker instead of a user bubble.
|
|
6
6
|
|
|
7
|
+
import { localTimestampParts } from './time-format.mjs';
|
|
8
|
+
|
|
7
9
|
const HEADER_PREFIX = '[Routine trigger]';
|
|
8
10
|
const KINDS = new Set(['schedule', 'webhook']);
|
|
9
11
|
|
|
10
|
-
/** The first line of a routine turn's prompt: which routine fired
|
|
11
|
-
*
|
|
12
|
-
*
|
|
13
|
-
|
|
12
|
+
/** The first line of a routine turn's prompt: which routine fired, when (local
|
|
13
|
+
* time and zone: the turn gets no other date), and how to treat the turn.
|
|
14
|
+
* It is the routine's only conduct text. Only the turn's content carries it,
|
|
15
|
+
* never the system prompt, so the thread's prompt cache is untouched. */
|
|
16
|
+
export function routineTriggerHeader({ kind, name, project = '', now = new Date() }) {
|
|
14
17
|
const what = kind === 'webhook' ? 'webhook' : 'schedule';
|
|
18
|
+
const at = localTimestampParts(now);
|
|
15
19
|
return [
|
|
16
20
|
`${HEADER_PREFIX} Your ${what} "${String(name)}" fired.`,
|
|
17
|
-
|
|
18
|
-
'
|
|
21
|
+
...(at ? [`Local time: ${at.date} ${at.time.slice(0, 5)} ${at.timeZone} (${at.offset}).`] : []),
|
|
22
|
+
'Carry out its instructions below now, yourself or by delegating.',
|
|
23
|
+
'Nobody is watching this turn and nothing asks for approval: act within your permissions.',
|
|
24
|
+
'Payments and permanent deletes are refused here: report that and suggest doing it from the chat.',
|
|
25
|
+
'If a sign-in, code or CAPTCHA blocks you, stop and say which site needs the user.',
|
|
26
|
+
'Reply in this chat in plain words, without tool names; if you handed work off, say it started — its result arrives here later.',
|
|
19
27
|
...(project ? [`Project folder: ${project}`] : []),
|
|
20
28
|
].join(' ');
|
|
21
29
|
}
|
|
@@ -8,6 +8,29 @@
|
|
|
8
8
|
// maxBytes, or the spawn error itself.
|
|
9
9
|
import { spawn } from 'node:child_process';
|
|
10
10
|
|
|
11
|
+
/** Kills `child` with everything it started. On Windows a plain kill ends only
|
|
12
|
+
* the child itself: `git` there is a launcher whose git.exe (and the alias,
|
|
13
|
+
* hook or filter it runs) would live on, still holding the output pipes. */
|
|
14
|
+
export function killChildTree(child) {
|
|
15
|
+
if (process.platform === 'win32' && child.pid && child.exitCode === null) {
|
|
16
|
+
const taskkill = `${process.env.SystemRoot || 'C:\\Windows'}\\System32\\taskkill.exe`;
|
|
17
|
+
const fallback = () => {
|
|
18
|
+
try {
|
|
19
|
+
child.kill();
|
|
20
|
+
} catch {}
|
|
21
|
+
};
|
|
22
|
+
spawn(taskkill, ['/PID', String(child.pid), '/T', '/F'], { stdio: 'ignore', windowsHide: true })
|
|
23
|
+
.once('error', fallback)
|
|
24
|
+
.once('exit', (code) => {
|
|
25
|
+
if (code !== 0) fallback();
|
|
26
|
+
});
|
|
27
|
+
return;
|
|
28
|
+
}
|
|
29
|
+
try {
|
|
30
|
+
child.kill();
|
|
31
|
+
} catch {}
|
|
32
|
+
}
|
|
33
|
+
|
|
11
34
|
export function runChild({ command, args, cwd, input, env, timeoutMs, maxBytes, stdoutEncoding = 'utf8' }) {
|
|
12
35
|
return new Promise((resolvePromise, rejectPromise) => {
|
|
13
36
|
const hasInput = (typeof input === 'string' || ArrayBuffer.isView(input)) && input.length > 0;
|
|
@@ -35,9 +58,7 @@ export function runChild({ command, args, cwd, input, env, timeoutMs, maxBytes,
|
|
|
35
58
|
? setTimeout(() => {
|
|
36
59
|
const error = new Error(`${command} ${args.join(' ')} timed out after ${timeoutMs}ms`);
|
|
37
60
|
error.code = 'ETIMEDOUT';
|
|
38
|
-
|
|
39
|
-
child.kill();
|
|
40
|
-
} catch {}
|
|
61
|
+
killChildTree(child);
|
|
41
62
|
finish(error);
|
|
42
63
|
}, timeoutMs)
|
|
43
64
|
: null;
|
|
@@ -809,8 +809,14 @@ export function _unsavedBytesForTest(sessionId) {
|
|
|
809
809
|
async function readBlob(sessionId, entry) {
|
|
810
810
|
if (!entry?.blob) return undefined;
|
|
811
811
|
try {
|
|
812
|
-
const
|
|
813
|
-
|
|
812
|
+
const path = join(blobDir(sessionId), entry.blob);
|
|
813
|
+
const bytes = await readFile(path);
|
|
814
|
+
if (digest(bytes) === entry.blob) return bytes;
|
|
815
|
+
// A torn blob (unflushed write cut by a crash). Blobs are published whole
|
|
816
|
+
// and never replaced, so a mismatch is damage: drop it, or the create-only
|
|
817
|
+
// write of a later capture of the same content would skip over it.
|
|
818
|
+
await rm(path, { force: true });
|
|
819
|
+
return undefined;
|
|
814
820
|
} catch {
|
|
815
821
|
return undefined;
|
|
816
822
|
}
|
|
@@ -1,4 +1,13 @@
|
|
|
1
|
-
export type SessionSidebarChange =
|
|
1
|
+
export type SessionSidebarChange =
|
|
2
|
+
| { favorite: boolean }
|
|
3
|
+
| { archived: boolean }
|
|
4
|
+
| { archived: true; cascade: false }
|
|
5
|
+
| { forgotten: true };
|
|
2
6
|
export type SessionSidebarHost = (sessionId: unknown, change: unknown) => unknown;
|
|
3
|
-
export
|
|
7
|
+
export type SessionSidebarState = { archived: Record<string, number> };
|
|
8
|
+
export function registerSessionSidebarHost(
|
|
9
|
+
host: SessionSidebarHost,
|
|
10
|
+
options?: { state?: (() => SessionSidebarState | Promise<SessionSidebarState>) | null }
|
|
11
|
+
): () => void;
|
|
4
12
|
export function applySessionSidebarChange(sessionId: string, change: SessionSidebarChange): Promise<void>;
|
|
13
|
+
export function readSessionSidebarState(): Promise<SessionSidebarState | null>;
|
|
@@ -15,11 +15,15 @@ function hosts() {
|
|
|
15
15
|
|
|
16
16
|
/**
|
|
17
17
|
* Register a desktop host. `host(sessionId, change)` applies one change:
|
|
18
|
-
* `{ favorite: boolean }
|
|
18
|
+
* `{ favorite: boolean }`, `{ archived: boolean }` (an archive takes the
|
|
19
|
+
* session's sub-agent sessions with it, a restore brings back the ones it took),
|
|
20
|
+
* `{ archived: true, cascade: false }` (that session alone)
|
|
21
|
+
* or `{ forgotten: true }` (a session the store deleted: its records go).
|
|
22
|
+
* `state()` answers the archive flags: `{ archived }`, session id → stamp.
|
|
19
23
|
* @returns {() => void} Unregister.
|
|
20
24
|
*/
|
|
21
|
-
export function registerSessionSidebarHost(host) {
|
|
22
|
-
const entry = { host };
|
|
25
|
+
export function registerSessionSidebarHost(host, { state = null } = {}) {
|
|
26
|
+
const entry = { host, state: typeof state === 'function' ? state : null };
|
|
23
27
|
hosts().push(entry);
|
|
24
28
|
return () => {
|
|
25
29
|
const list = hosts();
|
|
@@ -36,3 +40,12 @@ export async function applySessionSidebarChange(sessionId, change) {
|
|
|
36
40
|
if (!active) throw new Error('no desktop app is attached to this runtime');
|
|
37
41
|
await active.host(sessionId, change);
|
|
38
42
|
}
|
|
43
|
+
|
|
44
|
+
/** The active desktop's flags, `{ archived }` (session id → epoch-ms stamp);
|
|
45
|
+
* null when no desktop (or one without a state reader) is attached. */
|
|
46
|
+
export async function readSessionSidebarState() {
|
|
47
|
+
const active = hosts().at(-1);
|
|
48
|
+
if (!active?.state) return null;
|
|
49
|
+
const state = await active.state();
|
|
50
|
+
return { archived: { ...state?.archived } };
|
|
51
|
+
}
|
|
@@ -1,6 +1,6 @@
|
|
|
1
|
-
import {
|
|
1
|
+
import { rmdirSync, rmSync, statSync } from 'node:fs';
|
|
2
2
|
import { readFile, rename, rm, rmdir, stat, writeFile } from 'node:fs/promises';
|
|
3
|
-
import {
|
|
3
|
+
import { writeJsonAtomicSync } from './atomic-file.mjs';
|
|
4
4
|
import { withFileLock, withFileLockSync } from './file-lock.mjs';
|
|
5
5
|
import { readJsonSafe } from './json-file.mjs';
|
|
6
6
|
import { isPidAlive } from './pid-liveness.mjs';
|
|
@@ -19,11 +19,12 @@ const CLAIM_LOCK_STALE_MS = 5000;
|
|
|
19
19
|
const CLAIM_LOCK_OPTS = Object.freeze({ timeoutMs: CLAIM_LOCK_TIMEOUT_MS, staleMs: CLAIM_LOCK_STALE_MS });
|
|
20
20
|
const BUSY = Symbol('claim lock busy');
|
|
21
21
|
|
|
22
|
+
// A claim or handoff is flushed before it is published. The heartbeat's async
|
|
23
|
+
// refresh below is not: it rewrites the record every few seconds, and a record
|
|
24
|
+
// a crash leaves NUL reads as "no owner" (readJsonSafe), so the next claim
|
|
25
|
+
// simply takes over.
|
|
22
26
|
function writeJsonAtomic(path, value) {
|
|
23
|
-
|
|
24
|
-
const tmp = `${path}.${process.pid}.${Date.now()}.tmp`;
|
|
25
|
-
writeFileSync(tmp, `${JSON.stringify(value, null, 2)}\n`);
|
|
26
|
-
renameSync(tmp, path);
|
|
27
|
+
writeJsonAtomicSync(path, value);
|
|
27
28
|
}
|
|
28
29
|
|
|
29
30
|
// Held by another live process or this one's async holder ('busy'). Any other
|
|
@@ -1,8 +1,9 @@
|
|
|
1
1
|
// Explicit skill dependencies. Imported manifests are read, never rewritten.
|
|
2
|
-
import { createHash
|
|
3
|
-
import { existsSync,
|
|
2
|
+
import { createHash } from 'node:crypto';
|
|
3
|
+
import { existsSync, readFileSync, rmSync, statSync } from 'node:fs';
|
|
4
4
|
import { dirname, join, resolve } from 'node:path';
|
|
5
5
|
import { parseDocument } from 'yaml';
|
|
6
|
+
import { writeFileAtomicSync } from './atomic-file.mjs';
|
|
6
7
|
import { resolvePluginData } from './plugin-paths.mjs';
|
|
7
8
|
|
|
8
9
|
const MAX_METADATA_BYTES = 64 * 1024;
|
|
@@ -119,12 +120,5 @@ export function saveSkillToolDependencies(filePath, dependencies) {
|
|
|
119
120
|
const tools = normalizeSkillToolDependencies(dependencies, { strict: true });
|
|
120
121
|
const content = `${JSON.stringify({ dependencies: { tools } }, null, 2)}\n`;
|
|
121
122
|
if (Buffer.byteLength(content) > MAX_METADATA_BYTES) throw new Error('Skill dependency metadata is too large.');
|
|
122
|
-
|
|
123
|
-
const temporary = `${path}.${randomUUID()}.tmp`;
|
|
124
|
-
try {
|
|
125
|
-
writeFileSync(temporary, content, { encoding: 'utf8', flag: 'wx' });
|
|
126
|
-
renameSync(temporary, path);
|
|
127
|
-
} finally {
|
|
128
|
-
rmSync(temporary, { force: true });
|
|
129
|
-
}
|
|
123
|
+
writeFileAtomicSync(path, content);
|
|
130
124
|
}
|
|
@@ -50,7 +50,7 @@ import { resolvePluginData } from './plugin-paths.mjs';
|
|
|
50
50
|
import { isPidAlive as pidAlive } from './pid-liveness.mjs';
|
|
51
51
|
import { sleepSync } from './sleep.mjs';
|
|
52
52
|
import { detachedSpawnOpts, hiddenSpawnOpts } from './spawn-flags.mjs';
|
|
53
|
-
import { renameWithRetrySync } from './atomic-file.mjs';
|
|
53
|
+
import { renameWithRetrySync, writeJsonAtomicSync } from './atomic-file.mjs';
|
|
54
54
|
import {
|
|
55
55
|
isDevInstall,
|
|
56
56
|
localPackageVersion,
|
|
@@ -295,7 +295,9 @@ export async function runStagedInstall(version) {
|
|
|
295
295
|
|
|
296
296
|
if (!existsSync(join(pkgDir, 'src', 'cli.mjs'))) return { ok: false, error: 'staged cli.mjs missing' };
|
|
297
297
|
// Marker written LAST — its presence is the completion signal for the swap.
|
|
298
|
-
|
|
298
|
+
// Published whole and flushed: a NUL marker would read as "not staged" and
|
|
299
|
+
// force a full re-install.
|
|
300
|
+
writeJsonAtomicSync(markerPath(verDir), { version: v, pkgDir, stagedAt: Date.now() }, { compact: true });
|
|
299
301
|
return { ok: true, version: v, dir: pkgDir };
|
|
300
302
|
} catch (err) {
|
|
301
303
|
return { ok: false, error: err?.message || String(err) };
|
|
@@ -197,11 +197,11 @@ async function refreshShellJobsStatus(ownerPid) {
|
|
|
197
197
|
}
|
|
198
198
|
// A killed command whose processes are not confirmed gone has already
|
|
199
199
|
// reported its result, so its tool card is closed: it is listed until
|
|
200
|
-
// its owner retires the record.
|
|
201
|
-
//
|
|
200
|
+
// its owner retires the record. An incomplete observation can leave no
|
|
201
|
+
// known survivors even after the owner session closes; only the owning
|
|
202
|
+
// runtime can confirm termination, so its liveness decides, not the root's.
|
|
202
203
|
const unconfirmed = detail?.terminationUnconfirmed === true;
|
|
203
|
-
if (unconfirmed ? !
|
|
204
|
-
continue;
|
|
204
|
+
if (unconfirmed ? !isPidAlive(ownerByJob.get(id) || 0) : !(await isShellJobAlive(detail, p, dir, id))) continue;
|
|
205
205
|
// A foreground command is awaited in its own tool card; listing it here
|
|
206
206
|
// made every short command flash in and out of the shell list. Only
|
|
207
207
|
// backgrounded jobs (including promoted ones) are background work.
|
|
@@ -327,17 +327,6 @@ function summarizeShellJobs({ count, oldestMs, jobs, bySession }) {
|
|
|
327
327
|
return { count, elapsedLabel, jobs, sessions };
|
|
328
328
|
}
|
|
329
329
|
|
|
330
|
-
// A held record lives while its owner process does. Once its owner session
|
|
331
|
-
// has closed, it also needs a process left to hold: the root or a survivor
|
|
332
|
-
// its termination watch last reported. Without one it is an orphan whose
|
|
333
|
-
// processes are gone, and it resolves instead of staying unconfirmed.
|
|
334
|
-
function isHeldShellJobAlive(detail, ownerPid) {
|
|
335
|
-
if (!isPidAlive(ownerPid || 0)) return false;
|
|
336
|
-
if (detail?.ownerSessionClosed !== true) return true;
|
|
337
|
-
const survivors = Array.isArray(detail?.survivorPids) ? detail.survivorPids : [];
|
|
338
|
-
return [detail?.pid, ...survivors].some((pid) => isPidAlive(positiveInt(pid)));
|
|
339
|
-
}
|
|
340
|
-
|
|
341
330
|
async function isShellJobAlive(detail, detailPath, dir, id) {
|
|
342
331
|
const pid = positiveInt(detail?.pid);
|
|
343
332
|
if (!pid) return false;
|
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
import { clean } from './clean.mjs';
|
|
2
|
+
import { hopOf } from './bot-hops.mjs';
|
|
2
3
|
import {
|
|
3
4
|
parseTaskNotification,
|
|
4
5
|
renderTaskCompletionEnvelope,
|
|
@@ -113,6 +114,8 @@ export function normalizeToolNotifyContext(context = {}) {
|
|
|
113
114
|
callerSessionId: callerSessionId || null,
|
|
114
115
|
routingSessionId: routingSessionId || null,
|
|
115
116
|
clientHostPid,
|
|
117
|
+
// The bot-chain hop of the turn that launched the work (bot-hops.mjs).
|
|
118
|
+
...(hopOf(context.hop) ? { hop: context.hop } : {}),
|
|
116
119
|
};
|
|
117
120
|
}
|
|
118
121
|
|
|
@@ -133,6 +136,7 @@ function toolCompletionMeta({ surface = 'tool', id, status, resultType, instruct
|
|
|
133
136
|
? { routing_session_id: ctx.routingSessionId }
|
|
134
137
|
: {}),
|
|
135
138
|
...(ctx.clientHostPid ? { client_host_pid: String(ctx.clientHostPid) } : {}),
|
|
139
|
+
...(ctx.hop ? { hop: ctx.hop } : {}),
|
|
136
140
|
};
|
|
137
141
|
}
|
|
138
142
|
|
|
@@ -236,6 +240,12 @@ export function isTranscriptHiddenToolItem(item) {
|
|
|
236
240
|
// stopped, and the restored transcript must keep that row
|
|
237
241
|
// (scripts/turn-checkpoint-crash-test.mjs).
|
|
238
242
|
const INTERNAL_TRANSCRIPT_INTERRUPT_RE = /^\[request interrupted by user(?: for tool use)?\]$/i;
|
|
243
|
+
// Every runtime control row written as a user turn when a turn is cut short
|
|
244
|
+
// (including the bare "[Request interrupted]" a kept Stop prompt gets). One
|
|
245
|
+
// pattern for "not real conversation": realTurnCount and the preview/title
|
|
246
|
+
// noise filter both use it.
|
|
247
|
+
export const CONTROL_MARKER_RE =
|
|
248
|
+
/^\[(?:truncated|request interrupted(?: by user(?: for tool use)?| by process restart)?)\]$/i;
|
|
239
249
|
const TRANSCRIPT_CANCELLED_STATUS_RE = /^\[request interrupted(?: by process restart)?\]$/i;
|
|
240
250
|
|
|
241
251
|
// Crash/implicit interruption markers remain in model-visible history for
|
|
@@ -573,6 +573,15 @@ function gitStageUnit(a) {
|
|
|
573
573
|
});
|
|
574
574
|
}
|
|
575
575
|
|
|
576
|
+
function gitWorktreeUnit(a) {
|
|
577
|
+
return unitDescriptor('Git', {
|
|
578
|
+
active: 'Managing',
|
|
579
|
+
done: 'Managed',
|
|
580
|
+
noun: 'worktree',
|
|
581
|
+
effect: String(a.worktree_action || '').toLowerCase() !== 'list',
|
|
582
|
+
});
|
|
583
|
+
}
|
|
584
|
+
|
|
576
585
|
function cwdUnit(a) {
|
|
577
586
|
const action = String(a.action || a.type || '').toLowerCase();
|
|
578
587
|
// A bare `path` selects that Project even when action is omitted.
|
|
@@ -604,14 +613,6 @@ function cwdUnit(a) {
|
|
|
604
613
|
const [active, done] = words;
|
|
605
614
|
return unitDescriptor('Setup', { active, done, noun: 'session', effect: true });
|
|
606
615
|
}
|
|
607
|
-
if (action.endsWith('-worktree') || action.endsWith('-worktrees')) {
|
|
608
|
-
return unitDescriptor('Setup', {
|
|
609
|
-
active: 'Managing',
|
|
610
|
-
done: 'Managed',
|
|
611
|
-
noun: 'worktree',
|
|
612
|
-
effect: !action.startsWith('list'),
|
|
613
|
-
});
|
|
614
|
-
}
|
|
615
616
|
if (action === 'status') {
|
|
616
617
|
return unitDescriptor('Setup', { active: 'Checking', done: 'Checked', noun: 'session' });
|
|
617
618
|
}
|
|
@@ -715,7 +716,9 @@ const TOOL_UNITS = new Map([
|
|
|
715
716
|
(a) =>
|
|
716
717
|
a.action === 'stage'
|
|
717
718
|
? gitStageUnit(a)
|
|
718
|
-
:
|
|
719
|
+
: a.action === 'worktree'
|
|
720
|
+
? gitWorktreeUnit(a)
|
|
721
|
+
: unitDescriptor('Git', { count: queryCount(a, 'command', 'commands') || 1, noun: 'Git command' }),
|
|
719
722
|
],
|
|
720
723
|
['github', githubUnit],
|
|
721
724
|
// Preserve the staging work unit when rendering historical transcripts.
|