mixdog 1.3.0 → 1.3.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/NOTICE.md +52 -9
- package/README.md +8 -4
- package/package.json +2 -2
- package/scripts/native-release-status.mjs +238 -0
- package/src/defaults/shipped-history.json +8 -1
- 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 +54 -4
- package/src/defaults/skills/setup/references/surfaces.md +23 -3
- package/src/defaults/skills/setup-guide/SKILL.md +17 -10
- 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/output-styles/common.md +13 -8
- package/src/rules/agent-mode/AGENT_MODE.md +9 -6
- package/src/rules/agent-mode/MIXY.md +11 -6
- package/src/rules/lead/LEAD.md +2 -2
- package/src/rules/shared/10-tool-workflow.md +4 -0
- package/src/rules/shared/20-research.md +2 -0
- 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 +81 -16
- package/src/runtime/agent/orchestrator/mcp/agent-connectors.mjs +97 -4
- 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 +80 -31
- 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 +67 -38
- package/src/runtime/agent/orchestrator/providers/anthropic-turn-result.mjs +2 -0
- package/src/runtime/agent/orchestrator/providers/antigravity-oauth-catalog.mjs +7 -2
- package/src/runtime/agent/orchestrator/providers/antigravity-request.mjs +6 -2
- package/src/runtime/agent/orchestrator/providers/auto-effort.mjs +34 -16
- 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.mjs +11 -8
- package/src/runtime/agent/orchestrator/providers/lib/anthropic-prefix-trace.mjs +76 -0
- package/src/runtime/agent/orchestrator/providers/lib/anthropic-request-utils.mjs +19 -1
- package/src/runtime/agent/orchestrator/providers/lib/tail-reminder-split.mjs +13 -0
- package/src/runtime/agent/orchestrator/providers/lib/tool-call-wire-ids.mjs +65 -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-retry-profile.mjs +8 -2
- package/src/runtime/agent/orchestrator/providers/openai-compat-wire.mjs +10 -2
- 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-responses-input.mjs +3 -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/provider-error-kind.mjs +14 -2
- package/src/runtime/agent/orchestrator/providers/registry.mjs +9 -0
- package/src/runtime/agent/orchestrator/providers/retry-executor.mjs +17 -0
- 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-storage.mjs +68 -22
- package/src/runtime/agent/orchestrator/runtime-core/goal-tool-exec.mjs +1 -1
- package/src/runtime/agent/orchestrator/runtime-core/orchestration.mjs +5 -5
- 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 +12 -1
- package/src/runtime/agent/orchestrator/runtime-core/tool-defs.mjs +17 -6
- package/src/runtime/agent/orchestrator/runtime-core/workflow/agents.mjs +41 -10
- package/src/runtime/agent/orchestrator/runtime-core/workflow/context.mjs +6 -4
- package/src/runtime/agent/orchestrator/runtime-core/workflow/packs.mjs +8 -4
- package/src/runtime/agent/orchestrator/runtime-core/workflow.mjs +2 -0
- 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 +21 -2
- package/src/runtime/agent/orchestrator/session/agent-room.mjs +149 -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/execution-tail.mjs +14 -11
- package/src/runtime/agent/orchestrator/session/compact/forked-summary.mjs +3 -0
- package/src/runtime/agent/orchestrator/session/compact/runner.mjs +68 -18
- 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 +5 -1
- package/src/runtime/agent/orchestrator/session/compact/user-message-kind.mjs +1 -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 +230 -0
- package/src/runtime/agent/orchestrator/session/human-gate/gate.mjs +156 -25
- package/src/runtime/agent/orchestrator/session/human-gate/outbound-policy.mjs +20 -0
- package/src/runtime/agent/orchestrator/session/human-gate/requests.mjs +21 -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 +189 -105
- package/src/runtime/agent/orchestrator/session/loop/loop-state.mjs +9 -0
- package/src/runtime/agent/orchestrator/session/loop/pre-dispatch-deny.mjs +6 -2
- package/src/runtime/agent/orchestrator/session/loop/route-fallback.mjs +111 -9
- package/src/runtime/agent/orchestrator/session/loop/send-phase.mjs +11 -14
- package/src/runtime/agent/orchestrator/session/loop/steering.mjs +3 -0
- package/src/runtime/agent/orchestrator/session/loop/tool-exec/before-hook.mjs +38 -5
- package/src/runtime/agent/orchestrator/session/loop/tool-exec/dispatch.mjs +21 -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/loop/usage.mjs +46 -4
- package/src/runtime/agent/orchestrator/session/manager/ask-turn-auto-effort.mjs +13 -2
- package/src/runtime/agent/orchestrator/session/manager/ask-turn-commit.mjs +18 -8
- package/src/runtime/agent/orchestrator/session/manager/ask-turn-execute.mjs +4 -0
- package/src/runtime/agent/orchestrator/session/manager/ask-turn-prepare.mjs +6 -0
- package/src/runtime/agent/orchestrator/session/manager/ask-turn-settle.mjs +4 -0
- package/src/runtime/agent/orchestrator/session/manager/compaction-runner.mjs +44 -18
- package/src/runtime/agent/orchestrator/session/manager/orchestration-mode-switch-notice.mjs +99 -0
- package/src/runtime/agent/orchestrator/session/manager/owner-route-fallbacks.mjs +42 -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/provider-replay.mjs +50 -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-lifecycle.mjs +62 -47
- package/src/runtime/agent/orchestrator/session/manager/session-prompt-composition.mjs +6 -8
- package/src/runtime/agent/orchestrator/session/manager/session-record.mjs +12 -1
- package/src/runtime/agent/orchestrator/session/manager/turn-checkpoint-journal.mjs +6 -15
- package/src/runtime/agent/orchestrator/session/manager/turn-checkpoint.mjs +7 -1
- package/src/runtime/agent/orchestrator/session/manager/turn-interruption.mjs +24 -8
- package/src/runtime/agent/orchestrator/session/manager/usage-metrics.mjs +1 -1
- package/src/runtime/agent/orchestrator/session/manager/workflow-switch-notice.mjs +94 -13
- package/src/runtime/agent/orchestrator/session/mixy-name.mjs +5 -2
- package/src/runtime/agent/orchestrator/session/pre-send-compact.mjs +42 -10
- package/src/runtime/agent/orchestrator/session/provider-prefix-guard.mjs +21 -3
- package/src/runtime/agent/orchestrator/session/routine-result.d.mts +35 -4
- package/src/runtime/agent/orchestrator/session/routine-result.mjs +179 -16
- package/src/runtime/agent/orchestrator/session/send-with-recovery.mjs +5 -1
- 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/sweep-record.mjs +8 -3
- package/src/runtime/agent/orchestrator/session/store/sweep/sweep-row.mjs +36 -1
- package/src/runtime/agent/orchestrator/session/store-summary-catalog.mjs +4 -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/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 +49 -14
- package/src/runtime/agent/orchestrator/session/transcript-restore/user-text.mjs +134 -17
- package/src/runtime/agent/orchestrator/tools/builtin/advisory-lock.mjs +35 -13
- package/src/runtime/agent/orchestrator/tools/builtin/atomic-write.mjs +25 -5
- package/src/runtime/agent/orchestrator/tools/builtin/bash-tool/backgrounded-result.mjs +4 -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/lib/shell-job-records.mjs +6 -14
- 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 +34 -21
- 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/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/store.mjs +55 -30
- 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 +6 -3
- package/src/runtime/effort-judge/model-install.mjs +16 -8
- package/src/runtime/effort-judge/model-manifest.json +7 -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/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 +4 -0
- package/src/runtime/shared/agent-kind-migration.mjs +200 -0
- package/src/runtime/shared/agent-kind.mjs +152 -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-route-config.d.mts +1 -0
- package/src/runtime/shared/agent-route-config.mjs +22 -8
- package/src/runtime/shared/agent-store.mjs +149 -0
- 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 +30 -0
- package/src/runtime/shared/bot-split-migration.mjs +457 -0
- package/src/runtime/shared/bounded-download.mjs +4 -2
- package/src/runtime/shared/config-patch.mjs +20 -1
- package/src/runtime/shared/config.mjs +39 -9
- package/src/runtime/shared/connector-catalog.d.mts +1 -2
- package/src/runtime/shared/connector-catalog.mjs +2 -4
- package/src/runtime/shared/connector-triggers.mjs +2 -0
- 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 +7 -1
- package/src/runtime/shared/error-presentation.mjs +113 -10
- package/src/runtime/shared/file-has-data.mjs +28 -0
- package/src/runtime/shared/handoff-artifacts.d.mts +20 -0
- package/src/runtime/shared/handoff-artifacts.mjs +61 -0
- package/src/runtime/shared/llm/provider-model-identities.mjs +6 -1
- package/src/runtime/shared/llm/usage-accounting.mjs +7 -1
- package/src/runtime/shared/llm/usage-ledger-import.mjs +37 -18
- package/src/runtime/shared/model-route-fallback.mjs +79 -7
- package/src/runtime/shared/pending-agent-work.mjs +54 -0
- package/src/runtime/shared/plain-preview.mjs +22 -17
- 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/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/sensitive-path-rules.d.mts +15 -0
- package/src/runtime/shared/sensitive-path-rules.mjs +256 -0
- package/src/runtime/shared/session-baseline-store.mjs +8 -2
- 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 +6 -0
- 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 +44 -2
- package/src/runtime/shared/usage-readout-totals.mjs +15 -13
- package/src/runtime/shared/user-cwd.mjs +9 -4
- package/src/runtime/shared/user-data-guard.mjs +46 -44
- package/src/runtime/shared/utf8-cut.mjs +10 -0
- package/src/runtime/shared/worktrees/activity-gate.mjs +269 -61
- package/src/runtime/shared/worktrees/registry.mjs +26 -26
- package/src/runtime/tidy/core-install.mjs +26 -4
- package/src/runtime/tidy/install.mjs +91 -23
- package/src/runtime/web-search/lib/config.mjs +4 -16
- package/src/session-runtime/agent-home-delivery.mjs +859 -136
- package/src/session-runtime/agent-room-delivery.mjs +167 -19
- package/src/session-runtime/agent-task-sessions.mjs +831 -69
- package/src/session-runtime/agent-tool-routing.mjs +1 -0
- package/src/session-runtime/boot/apis.mjs +58 -24
- package/src/session-runtime/boot/config.mjs +1 -0
- package/src/session-runtime/boot/facade.mjs +11 -1
- package/src/session-runtime/boot/infrastructure.mjs +14 -2
- package/src/session-runtime/boot/providers.mjs +16 -6
- package/src/session-runtime/boot/shared.mjs +12 -6
- package/src/session-runtime/boot/tools.mjs +2 -2
- 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 +78 -32
- package/src/session-runtime/cwd-plugins/core-memory-context.mjs +2 -4
- package/src/session-runtime/handoff-attachments.mjs +593 -0
- package/src/session-runtime/held-requests.mjs +36 -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 +45 -6
- 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-route/route-persist.mjs +2 -2
- package/src/session-runtime/model-route/route-tuning.mjs +11 -2
- package/src/session-runtime/model-route/set-route.mjs +82 -2
- package/src/session-runtime/native-web-search.mjs +2 -2
- package/src/session-runtime/new-session-config.mjs +6 -1
- package/src/session-runtime/owned-automation-run.mjs +8 -1
- package/src/session-runtime/room-member-gate.mjs +16 -3
- package/src/session-runtime/route-resolution.mjs +21 -19
- 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-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/helpers.mjs +24 -5
- package/src/session-runtime/services/agent-tool/restart-notify.mjs +10 -0
- package/src/session-runtime/services/agent-tool/send-flow.mjs +36 -24
- package/src/session-runtime/services/agent-tool/spawn-flow/job-start.mjs +4 -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 +12 -6
- package/src/session-runtime/services/agent-tool/spawn-preset.mjs +5 -8
- package/src/session-runtime/services/agent-tool/tool-def.mjs +7 -0
- 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 +14 -4
- 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/session-create/materialize.mjs +2 -3
- package/src/session-runtime/session-create/session-options.mjs +15 -2
- package/src/session-runtime/settings-builtin-tools-api.mjs +63 -1
- package/src/session-runtime/settings-compaction-api.mjs +86 -39
- package/src/session-runtime/setup-tool/executor.mjs +20 -3
- package/src/session-runtime/setup-tool/extended-actions.mjs +19 -7
- package/src/session-runtime/setup-tool/settings-contract.mjs +37 -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 +265 -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 +165 -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 +47 -358
- package/src/session-runtime/tool-surface.mjs +5 -3
- package/src/session-runtime/turn/turn-run/turn-prepare.mjs +99 -81
- package/src/session-runtime/turn/turn-run/turn-timing.mjs +4 -3
- package/src/session-runtime/turn/turn-run.mjs +2 -1
- package/src/session-runtime/workflow-agents-api/agent-connectors.mjs +54 -7
- package/src/session-runtime/workflow-agents-api/agent-editor.mjs +578 -301
- package/src/session-runtime/workflow-agents-api/agent-route.mjs +113 -53
- package/src/session-runtime/workflow-agents-api/onboarding.mjs +11 -4
- 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/workspace-state.mjs +7 -6
- package/src/standalone/daemon-agent-control.mjs +4 -1
- package/src/standalone/daemon-session-control.mjs +60 -11
- package/src/standalone/daemon.mjs +34 -4
- package/src/standalone/local-session-runtime.mjs +22 -14
- package/src/standalone/session-client.mjs +7 -2
- package/src/standalone/session-protocol.mjs +7 -0
- package/src/standalone/session-service/agent-tree/agent-turns.mjs +46 -3
- package/src/standalone/session-service/agent-tree.mjs +4 -3
- package/src/standalone/session-service/session-calls/session-turns.mjs +19 -0
- package/src/standalone/session-service.mjs +11 -0
- package/src/standalone/session-state-projection.mjs +19 -9
- package/src/standalone/session-tool-output.mjs +35 -9
- package/src/tui/app/maintenance-pickers/auto-clear-picker.mjs +19 -13
- 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 +599 -342
- package/src/tui/prompt-history-store.mjs +16 -21
- package/src/tui/session/context-state.mjs +4 -0
- package/src/tui/session/notice-surface.mjs +3 -4
- package/src/tui/session/session-api/intake/abort.mjs +19 -1
- package/src/tui/session/session-api/media.mjs +2 -1
- package/src/tui/session/session-api/routes-agent-definitions.mjs +75 -0
- package/src/tui/session/session-api/routes-write.mjs +203 -0
- package/src/tui/session/session-api/routes.mjs +23 -236
- 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 +185 -20
- package/src/tui/session/transcript-intake.mjs +4 -0
- package/src/tui/session/turn-live-call.mjs +11 -3
- package/src/tui/session/turn.mjs +36 -17
- 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
|
@@ -11,6 +11,7 @@
|
|
|
11
11
|
* moves a long block into the attachment store; it is read back from there.
|
|
12
12
|
*/
|
|
13
13
|
import { isAttachmentReference, readAttachmentTextWithin } from '../../../../attachments/store.mjs';
|
|
14
|
+
import { isMediaReferencePart } from '../../../../attachments/attachment-reference.mjs';
|
|
14
15
|
import { toolResultText } from './tool-result-text.mjs';
|
|
15
16
|
|
|
16
17
|
const IMAGE_METADATA_LINE = /\[Image(?::| source:) [^\]\r\n]+\]\s*$/;
|
|
@@ -116,14 +117,85 @@ function referencedFeedbackText(part) {
|
|
|
116
117
|
}
|
|
117
118
|
}
|
|
118
119
|
|
|
120
|
+
const PASTED_LINES = /· (\d+) lines?$/;
|
|
121
|
+
const PASTED_TEXT_MAX_BYTES = 1024 * 1024;
|
|
122
|
+
/** Blob bytes one transcript load may read to count the lines of pastes
|
|
123
|
+
* whose stored name does not carry the count (pastes stored before it did). */
|
|
124
|
+
export const PASTED_LINE_READ_BUDGET_BYTES = 4 * 1024 * 1024;
|
|
125
|
+
|
|
126
|
+
/** A stored plain-text prompt part's text within the load's byte budget, else ''. */
|
|
127
|
+
function offloadedPromptText(part, budget) {
|
|
128
|
+
const allowance = Math.min(PASTED_TEXT_MAX_BYTES, budget.remaining);
|
|
129
|
+
if (allowance <= 0) return '';
|
|
130
|
+
try {
|
|
131
|
+
const text = readAttachmentTextWithin(part, allowance);
|
|
132
|
+
if (text == null) return '';
|
|
133
|
+
budget.remaining -= Buffer.byteLength(text, 'utf8');
|
|
134
|
+
return text;
|
|
135
|
+
} catch (error) {
|
|
136
|
+
if (error?.code !== 'ENOENT') budget.remaining -= allowance;
|
|
137
|
+
return '';
|
|
138
|
+
}
|
|
139
|
+
}
|
|
140
|
+
|
|
141
|
+
/** Visible lines of a stored paste (as `pastedTextLineCount` counts them), or
|
|
142
|
+
* null when the count would take a read past the load's budget. */
|
|
143
|
+
function storedPasteLines(part, budget) {
|
|
144
|
+
const named = PASTED_LINES.exec(String(part.filename || ''));
|
|
145
|
+
if (named) return Number(named[1]);
|
|
146
|
+
const allowance = Math.min(PASTED_TEXT_MAX_BYTES, budget.remaining);
|
|
147
|
+
if (allowance <= 0) return null;
|
|
148
|
+
try {
|
|
149
|
+
const read = readAttachmentTextWithin(part, allowance);
|
|
150
|
+
if (read == null) return null;
|
|
151
|
+
budget.remaining -= Buffer.byteLength(read, 'utf8');
|
|
152
|
+
const value = read.replace(/\r\n?/g, '\n').replace(/\n+$/, '');
|
|
153
|
+
return value ? value.split('\n').length : 0;
|
|
154
|
+
} catch (error) {
|
|
155
|
+
// A blob that failed its integrity check was read before it failed: the
|
|
156
|
+
// read counts against the cap (at most the allowance). A missing blob
|
|
157
|
+
// read nothing.
|
|
158
|
+
if (error?.code !== 'ENOENT') budget.remaining -= allowance;
|
|
159
|
+
return null;
|
|
160
|
+
}
|
|
161
|
+
}
|
|
162
|
+
|
|
163
|
+
/** The chip token a stored pasted / attached text stood for in the live
|
|
164
|
+
* bubble (intake moved its body into the store and dropped the token). A
|
|
165
|
+
* paste whose line count is unknown shows as the plain `[Pasted text #n]`. */
|
|
166
|
+
function storedTextToken(part, number, budget) {
|
|
167
|
+
if (part?.type !== 'text') return '';
|
|
168
|
+
if (part.textKind === 'paste') {
|
|
169
|
+
const lines = storedPasteLines(part, budget);
|
|
170
|
+
return lines == null ? `[Pasted text #${number}]` : `[Pasted text #${number} +${lines} lines]`;
|
|
171
|
+
}
|
|
172
|
+
if (part.textKind === 'file') return `[File #${number}: ${part.filename || 'attachment.txt'}]`;
|
|
173
|
+
return '';
|
|
174
|
+
}
|
|
175
|
+
|
|
119
176
|
/** The display lines of a stored prompt's parts, and for each image part the
|
|
120
177
|
* line that draws it: the composer's metadata line right before it, else a
|
|
121
|
-
* bare line of its own (a bare line never stands for the next image).
|
|
122
|
-
|
|
178
|
+
* bare line of its own (a bare line never stands for the next image). A
|
|
179
|
+
* stored pasted or attached text shows as its chip token. */
|
|
180
|
+
function restoredUserParts(content, budget) {
|
|
123
181
|
const texts = [];
|
|
124
182
|
const images = [];
|
|
125
183
|
let pendingMetadata = '';
|
|
184
|
+
let storedTexts = 0;
|
|
185
|
+
// Plain text the intake moved into the store (a long prompt, a bot's message).
|
|
186
|
+
const offloaded = [];
|
|
187
|
+
let companionText = false;
|
|
126
188
|
for (const part of content) {
|
|
189
|
+
// An attachment sent by reference is model-facing text; the bubble shows
|
|
190
|
+
// the attachment as the chip its message records.
|
|
191
|
+
if (isMediaReferencePart(part)) continue;
|
|
192
|
+
const token = isAttachmentReference(part) ? storedTextToken(part, storedTexts + 1, budget) : '';
|
|
193
|
+
if (token) {
|
|
194
|
+
storedTexts += 1;
|
|
195
|
+
texts.push(token);
|
|
196
|
+
pendingMetadata = '';
|
|
197
|
+
continue;
|
|
198
|
+
}
|
|
127
199
|
if (part?.type === 'image' || part?.type === 'input_image') {
|
|
128
200
|
let line = pendingMetadata;
|
|
129
201
|
if (!line) {
|
|
@@ -135,27 +207,72 @@ function restoredUserParts(content) {
|
|
|
135
207
|
continue;
|
|
136
208
|
}
|
|
137
209
|
const text = isAttachmentReference(part) ? referencedFeedbackText(part) : toolResultText([part]);
|
|
138
|
-
if (text)
|
|
210
|
+
if (text) {
|
|
211
|
+
texts.push(text);
|
|
212
|
+
if (!IMAGE_METADATA_LINE.test(text)) companionText = true;
|
|
213
|
+
} else if (isAttachmentReference(part) && part?.type === 'text' && !part.textKind) {
|
|
214
|
+
// Keeps its place among the parts until it is known whether to read it.
|
|
215
|
+
texts.push(part);
|
|
216
|
+
offloaded.push(part);
|
|
217
|
+
}
|
|
139
218
|
pendingMetadata = IMAGE_METADATA_LINE.test(text) ? text.trimEnd().split(/\r?\n/).pop().trim() : '';
|
|
140
219
|
}
|
|
141
|
-
|
|
220
|
+
// A prompt whose only real text was moved out restores it in place (bounded,
|
|
221
|
+
// within the load's read budget); image labels and chips are not companion
|
|
222
|
+
// text. Beside real text such parts stay out, as before.
|
|
223
|
+
const resolved = [];
|
|
224
|
+
for (const entry of texts) {
|
|
225
|
+
if (typeof entry === 'string') resolved.push(entry);
|
|
226
|
+
else if (!companionText) {
|
|
227
|
+
const text = offloadedPromptText(entry, budget);
|
|
228
|
+
if (text) resolved.push(text);
|
|
229
|
+
}
|
|
230
|
+
}
|
|
231
|
+
return { texts: resolved, images };
|
|
232
|
+
}
|
|
233
|
+
|
|
234
|
+
/**
|
|
235
|
+
* The user-bubble projection of one transcript load: each stored prompt's
|
|
236
|
+
* parts are worked out once (its text and its image refs share them, and a
|
|
237
|
+
* load that widens its range sees the same messages again), and the blob
|
|
238
|
+
* reads that count paste lines share one byte budget.
|
|
239
|
+
*/
|
|
240
|
+
export function createUserTextRestore({ readBudgetBytes = PASTED_LINE_READ_BUDGET_BYTES } = {}) {
|
|
241
|
+
const budget = { remaining: readBudgetBytes };
|
|
242
|
+
const parts = new WeakMap();
|
|
243
|
+
const partsOf = (content) => {
|
|
244
|
+
let found = parts.get(content);
|
|
245
|
+
if (!found) {
|
|
246
|
+
found = restoredUserParts(content, budget);
|
|
247
|
+
parts.set(content, found);
|
|
248
|
+
}
|
|
249
|
+
return found;
|
|
250
|
+
};
|
|
251
|
+
return {
|
|
252
|
+
text(content) {
|
|
253
|
+
if (!Array.isArray(content)) {
|
|
254
|
+
return withVisualizeFeedbackDisplay(typeof content === 'string' ? content : toolResultText(content));
|
|
255
|
+
}
|
|
256
|
+
return withVisualizeFeedbackDisplay(partsOf(content).texts.join('\n'));
|
|
257
|
+
},
|
|
258
|
+
/** The stored original of each image the restored bubble draws, by the
|
|
259
|
+
* `[Image: …]` line that draws it. Every image keeps its slot (one
|
|
260
|
+
* without a stored original has no `ref`) so later images do not shift
|
|
261
|
+
* onto it; empty when no image has a stored original. */
|
|
262
|
+
imageRefs(content) {
|
|
263
|
+
if (!Array.isArray(content)) return [];
|
|
264
|
+
const slots = partsOf(content).images.map(({ part, line }) =>
|
|
265
|
+
isAttachmentReference(part) ? { line, ref: part.attachmentRef } : { line }
|
|
266
|
+
);
|
|
267
|
+
return slots.some((slot) => slot.ref) ? slots : [];
|
|
268
|
+
},
|
|
269
|
+
};
|
|
142
270
|
}
|
|
143
271
|
|
|
144
272
|
export function restoredUserText(content) {
|
|
145
|
-
|
|
146
|
-
return withVisualizeFeedbackDisplay(typeof content === 'string' ? content : toolResultText(content));
|
|
147
|
-
}
|
|
148
|
-
return withVisualizeFeedbackDisplay(restoredUserParts(content).texts.join('\n'));
|
|
273
|
+
return createUserTextRestore().text(content);
|
|
149
274
|
}
|
|
150
275
|
|
|
151
|
-
/** The stored original of each image the restored bubble draws, by the
|
|
152
|
-
* `[Image: …]` line that draws it. Every image keeps its slot (one without a
|
|
153
|
-
* stored original has no `ref`) so later images do not shift onto it; empty
|
|
154
|
-
* when no image has a stored original. */
|
|
155
276
|
export function restoredUserImageRefs(content) {
|
|
156
|
-
|
|
157
|
-
const slots = restoredUserParts(content).images.map(({ part, line }) =>
|
|
158
|
-
isAttachmentReference(part) ? { line, ref: part.attachmentRef } : { line }
|
|
159
|
-
);
|
|
160
|
-
return slots.some((slot) => slot.ref) ? slots : [];
|
|
277
|
+
return createUserTextRestore().imageRefs(content);
|
|
161
278
|
}
|
|
@@ -1,16 +1,41 @@
|
|
|
1
|
-
// Cross-process advisory lock for patch/write operations. Uses a
|
|
2
|
-
//
|
|
3
|
-
//
|
|
4
|
-
//
|
|
5
|
-
//
|
|
6
|
-
import { openSync, closeSync, writeSync, readFileSync, unlinkSync, mkdirSync } from 'node:fs';
|
|
1
|
+
// Cross-process advisory lock for patch/write operations. Uses a lockfile
|
|
2
|
+
// written exclusively (`wx`) and a process.kill(pid, 0) liveness check to
|
|
3
|
+
// clean up after crashed holders. Pair with the in-process withPathLock for
|
|
4
|
+
// the same target: in-process serialises async callers in this Node, advisory
|
|
5
|
+
// lock serialises across Node processes.
|
|
6
|
+
import { openSync, closeSync, writeSync, readFileSync, unlinkSync, mkdirSync, realpathSync } from 'node:fs';
|
|
7
7
|
import { compareCodePoints } from '../../../../shared/code-point-order.mjs';
|
|
8
|
+
import { resolvePluginData } from '../../../../shared/plugin-paths.mjs';
|
|
8
9
|
import { sleep } from '../../../../shared/sleep.mjs';
|
|
9
|
-
import { dirname, basename, join } from 'node:path';
|
|
10
|
-
import { randomBytes } from 'node:crypto';
|
|
10
|
+
import { dirname, basename, join, resolve } from 'node:path';
|
|
11
|
+
import { createHash, randomBytes } from 'node:crypto';
|
|
11
12
|
|
|
13
|
+
// The deepest existing component (the target itself when present, so a leaf
|
|
14
|
+
// symlink shares its referent's lock like atomicWrite writes through it) is
|
|
15
|
+
// resolved; components not created yet are appended as given.
|
|
16
|
+
function canonicalLockTarget(targetPath) {
|
|
17
|
+
const resolved = resolve(targetPath);
|
|
18
|
+
const tail = [];
|
|
19
|
+
let dir = resolved;
|
|
20
|
+
for (;;) {
|
|
21
|
+
try {
|
|
22
|
+
return join(realpathSync.native(dir), ...tail.reverse());
|
|
23
|
+
} catch {
|
|
24
|
+
if (dirname(dir) === dir) return resolved;
|
|
25
|
+
tail.push(basename(dir));
|
|
26
|
+
dir = dirname(dir);
|
|
27
|
+
}
|
|
28
|
+
}
|
|
29
|
+
}
|
|
30
|
+
|
|
31
|
+
// Lockfiles live in the local data dir, keyed by the target's canonical path,
|
|
32
|
+
// never beside the target: sync-drive folders (e.g. Naver MYBOX) refuse
|
|
33
|
+
// dot-prefixed sibling files, and the pid liveness check only spans this host.
|
|
12
34
|
function lockFileFor(targetPath) {
|
|
13
|
-
|
|
35
|
+
const canonical = canonicalLockTarget(targetPath);
|
|
36
|
+
const key = process.platform === 'win32' ? canonical.toLowerCase() : canonical;
|
|
37
|
+
const hash = createHash('sha256').update(key).digest('hex');
|
|
38
|
+
return join(resolvePluginData(), 'locks', `${hash}.mixdog-lock`);
|
|
14
39
|
}
|
|
15
40
|
|
|
16
41
|
function isPidAlive(pid) {
|
|
@@ -87,15 +112,12 @@ function tryCreateLock(lockFile, token) {
|
|
|
87
112
|
|
|
88
113
|
async function acquireAdvisoryLock(targetPath, { timeoutMs = 5000, pollMs = 25 } = {}) {
|
|
89
114
|
const lockFile = lockFileFor(targetPath);
|
|
90
|
-
// Ensure parent directory exists before openSync('wx'); the lockfile lives
|
|
91
|
-
// next to the target, so writes to new files in missing parent dirs would
|
|
92
|
-
// otherwise fail with ENOENT here.
|
|
93
115
|
mkdirSync(dirname(lockFile), { recursive: true });
|
|
94
116
|
const token = `${process.pid}.${randomBytes(8).toString('hex')}`;
|
|
95
117
|
const deadline = Date.now() + timeoutMs;
|
|
96
118
|
let stalePostDeadlineRetryUsed = false;
|
|
97
119
|
const timeoutError = (holderPid) => {
|
|
98
|
-
const err = new Error(`advisory lock timeout: ${lockFile} held by pid ${holderPid}`);
|
|
120
|
+
const err = new Error(`advisory lock timeout: ${targetPath} (${lockFile}) held by pid ${holderPid}`);
|
|
99
121
|
err.code = 'EAGAIN';
|
|
100
122
|
return err;
|
|
101
123
|
};
|
|
@@ -24,9 +24,24 @@ function expectedTargetSnapshotChanged(currentStat, expected) {
|
|
|
24
24
|
return !sameFileVersion(currentStat, expected);
|
|
25
25
|
}
|
|
26
26
|
|
|
27
|
+
// Files the agent writes into a user's project are durable by default: without
|
|
28
|
+
// the flush a crash can publish the renamed file with every byte NUL.
|
|
29
|
+
// MIXDOG_ATOMIC_FSYNC=0|false|no|off opts out.
|
|
27
30
|
function atomicWriteShouldFsync(value) {
|
|
28
31
|
if (value === true || value === false) return value;
|
|
29
|
-
return
|
|
32
|
+
return !/^(0|false|no|off)$/i.test(String(process.env.MIXDOG_ATOMIC_FSYNC || ''));
|
|
33
|
+
}
|
|
34
|
+
|
|
35
|
+
// Filesystems that refuse fsync answer with one of these; durability is
|
|
36
|
+
// best-effort there and must not fail the write.
|
|
37
|
+
const FSYNC_TOLERATED = new Set(['EPERM', 'ENOTSUP', 'EINVAL']);
|
|
38
|
+
|
|
39
|
+
async function syncHandle(fh) {
|
|
40
|
+
try {
|
|
41
|
+
await fh.sync();
|
|
42
|
+
} catch (error) {
|
|
43
|
+
if (!FSYNC_TOLERATED.has(error?.code)) throw error;
|
|
44
|
+
}
|
|
30
45
|
}
|
|
31
46
|
|
|
32
47
|
// 'wx' preflight creates an empty placeholder at targetPath; if the write is
|
|
@@ -104,7 +119,10 @@ async function writeTempFile(tmp, content, { mode, fsync }) {
|
|
|
104
119
|
if (payloadByteLength(content) > STREAMING_THRESHOLD_BYTES) {
|
|
105
120
|
// Streaming path: avoid buffering the entire payload through
|
|
106
121
|
// fh.writeFile (which copies into a single Buffer).
|
|
107
|
-
|
|
122
|
+
// Owner-writable while we flush: the fsync reopens the file for writing,
|
|
123
|
+
// which a read-only mode would refuse; the final mode is applied after.
|
|
124
|
+
const streamMode = fsync ? mode | 0o200 : mode;
|
|
125
|
+
const ws = createWriteStream(tmp, { flags: 'wx', mode: streamMode });
|
|
108
126
|
// Stream construction precedes open; only the open event proves ownership.
|
|
109
127
|
ws.once('open', () => {
|
|
110
128
|
ownsTemp = true;
|
|
@@ -115,15 +133,16 @@ async function writeTempFile(tmp, content, { mode, fsync }) {
|
|
|
115
133
|
await pipeline(source, ws);
|
|
116
134
|
if (fsync) {
|
|
117
135
|
fh = await fsPromises.open(tmp, 'r+');
|
|
118
|
-
await fh
|
|
136
|
+
await syncHandle(fh);
|
|
119
137
|
await fh.close();
|
|
120
138
|
fh = null;
|
|
139
|
+
if (streamMode !== mode) await fsPromises.chmod(tmp, mode);
|
|
121
140
|
}
|
|
122
141
|
} else {
|
|
123
142
|
fh = await fsPromises.open(tmp, 'wx', mode);
|
|
124
143
|
ownsTemp = true;
|
|
125
144
|
await fh.writeFile(content);
|
|
126
|
-
if (fsync) await fh
|
|
145
|
+
if (fsync) await syncHandle(fh);
|
|
127
146
|
await fh.close();
|
|
128
147
|
fh = null;
|
|
129
148
|
}
|
|
@@ -260,7 +279,8 @@ export async function atomicWrite(
|
|
|
260
279
|
// beside the real target, so the rename stays on one filesystem.
|
|
261
280
|
const writeTarget = symlinkWriteTarget(targetPath) ?? targetPath;
|
|
262
281
|
const dir = dirname(writeTarget);
|
|
263
|
-
|
|
282
|
+
// No leading dot: sync-drive folders (e.g. Naver MYBOX) refuse dot-prefixed names.
|
|
283
|
+
const tmp = join(dir, `${basename(writeTarget)}.mixdog-tmp-${randomBytes(4).toString('hex')}`);
|
|
264
284
|
const existingStat = statOrNull(writeTarget);
|
|
265
285
|
let effectiveMode = mode;
|
|
266
286
|
if (effectiveMode === undefined && existingStat) effectiveMode = existingStat.mode & 0o777;
|
|
@@ -3,7 +3,7 @@ import { killShellJob, watchBackgroundShellJob } from '../shell-jobs.mjs';
|
|
|
3
3
|
import { renderBackgroundPartialOutput } from '../shell-output.mjs';
|
|
4
4
|
import { readShellTaskOutput } from '../lib/shell-task-output.mjs';
|
|
5
5
|
import { normalizeOutputPath } from '../path-utils.mjs';
|
|
6
|
-
import { cleanupArtifactOnTaskSettled, consumeTeeArtifact } from './transport-artifacts.mjs';
|
|
6
|
+
import { cleanupArtifactAfter, cleanupArtifactOnTaskSettled, consumeTeeArtifact } from './transport-artifacts.mjs';
|
|
7
7
|
import { _backgroundResultLines, _prependDestructiveWarning } from './result-format.mjs';
|
|
8
8
|
|
|
9
9
|
const DEFAULT_BACKGROUND_MESSAGE =
|
|
@@ -68,8 +68,9 @@ export function renderBackgroundedResult({ result, command, cwd, options, starte
|
|
|
68
68
|
}
|
|
69
69
|
// The promoted producer is still writing into the tee file, so it cannot be
|
|
70
70
|
// consumed here — but it must not survive the command either. Consume (and
|
|
71
|
-
// delete) it when the task settles.
|
|
72
|
-
if (teePlan) cleanupArtifactOnTaskSettled(teePlan.teePath, result.jobId, consumeTeeArtifact);
|
|
71
|
+
// delete) it when the task settles — for a queued command, its run.
|
|
72
|
+
if (teePlan && result.jobId) cleanupArtifactOnTaskSettled(teePlan.teePath, result.jobId, consumeTeeArtifact);
|
|
73
|
+
else if (teePlan) cleanupArtifactAfter(teePlan.teePath, result.settled, consumeTeeArtifact);
|
|
73
74
|
let taskBlock = null;
|
|
74
75
|
if (task) taskBlock = renderBackgroundTask(task, { ownerTool: true });
|
|
75
76
|
else if (result.jobId) taskBlock = `[task_id: ${result.jobId}]`;
|
|
@@ -1,9 +1,9 @@
|
|
|
1
1
|
import { renderBackgroundTask, startBackgroundTask } from '../../../../../shared/background-tasks.mjs';
|
|
2
2
|
import { killShellDescendants, waitForShellDescendants } from '../../lib/shell-descendants.mjs';
|
|
3
3
|
import { releaseShellJobWorkspace } from '../lib/shell-job-records.mjs';
|
|
4
|
-
import {
|
|
4
|
+
import { readFilterTeeCapture } from '../shell-analysis.mjs';
|
|
5
5
|
import { compactShellOutputLosslessly, renderShellOutputBody } from '../shell-lossless-compact.mjs';
|
|
6
|
-
import { removeTransportFileNow } from './transport-artifacts.mjs';
|
|
6
|
+
import { cleanupArtifactAfter, removeTransportFileNow } from './transport-artifacts.mjs';
|
|
7
7
|
import { DELIVERY_FULL_CHARS, EMPTY_PIECE, fitPieces, shellTextPiece } from '../lib/shell-delivery.mjs';
|
|
8
8
|
import { shellLogPiece } from '../lib/shell-task-output.mjs';
|
|
9
9
|
import { shellTaskIdentity } from './backgrounded-result.mjs';
|
|
@@ -74,17 +74,16 @@ function survivingDescendantsWarning(result, taskContext) {
|
|
|
74
74
|
return [heading, renderBackgroundTask(task, { ownerTool: true })].join('\n');
|
|
75
75
|
}
|
|
76
76
|
|
|
77
|
-
// Filter-swallow rescue: the tee file is ALWAYS consumed (deleted)
|
|
78
|
-
//
|
|
79
|
-
// the
|
|
80
|
-
// diagnostic turns.
|
|
81
|
-
function filterRescueNote(teePlan, { failed, stdout, stderr }) {
|
|
82
|
-
const rescueTail =
|
|
83
|
-
//
|
|
84
|
-
//
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
removeTransportFileNow(teePlan.teePath);
|
|
77
|
+
// Filter-swallow rescue: the tee file is ALWAYS consumed (deleted) — once the
|
|
78
|
+
// run's tree has settled when its result says it may still run; its tail is
|
|
79
|
+
// attached only when the run failed with an empty visible capture — the exact
|
|
80
|
+
// `(no output)` shape that previously cost the model extra diagnostic turns.
|
|
81
|
+
function filterRescueNote(teePlan, { failed, stdout, stderr }, settled) {
|
|
82
|
+
const rescueTail = readFilterTeeCapture(teePlan.teePath);
|
|
83
|
+
// Removal goes through the retry + exit-hook path: on Windows a process
|
|
84
|
+
// that survived an unconfirmed kill still holds the file.
|
|
85
|
+
if (settled) cleanupArtifactAfter(teePlan.teePath, settled);
|
|
86
|
+
else removeTransportFileNow(teePlan.teePath);
|
|
88
87
|
if (!failed || !rescueTail || stdout.trim() || stderr.trim()) return '';
|
|
89
88
|
return `\n\n[filter-swallowed output rescue] the command failed but its trailing filter(s) matched nothing, so the visible output was empty. Unfiltered pipeline output (tail):\n${rescueTail}`;
|
|
90
89
|
}
|
|
@@ -148,7 +147,7 @@ export function renderCompletedResult({
|
|
|
148
147
|
const { signal, exitCode, completedExit, benignExit, isReallyErrored } = outcome;
|
|
149
148
|
const commandExitedNonzero = completedExit && exitCode !== 0 && !benignExit;
|
|
150
149
|
const rescueNote = teePlan
|
|
151
|
-
? filterRescueNote(teePlan, { failed: isReallyErrored || commandExitedNonzero, stdout, stderr })
|
|
150
|
+
? filterRescueNote(teePlan, { failed: isReallyErrored || commandExitedNonzero, stdout, stderr }, result.settled)
|
|
152
151
|
: '';
|
|
153
152
|
const descendantWarning = survivingDescendantsWarning(result, { command, cwd, options, startedAtMs });
|
|
154
153
|
// rewriteNote states that the text which RAN is not the text the caller
|
|
@@ -79,6 +79,15 @@ export function removeTransportFileNow(file) {
|
|
|
79
79
|
finishTransportArtifact(file, removeTransportFile);
|
|
80
80
|
}
|
|
81
81
|
|
|
82
|
+
/** Remove `file` once a run's own lifetime promise settles: a queued command
|
|
83
|
+
* has no job to subscribe to until it runs. */
|
|
84
|
+
export function cleanupArtifactAfter(file, settled, remove = removeTransportFile) {
|
|
85
|
+
if (!file) return;
|
|
86
|
+
registerTransportArtifact(file);
|
|
87
|
+
const done = () => finishTransportArtifact(file, remove);
|
|
88
|
+
Promise.resolve(settled).then(done, done);
|
|
89
|
+
}
|
|
90
|
+
|
|
82
91
|
export function cleanupArtifactOnTaskSettled(file, jobId, remove = removeTransportFile) {
|
|
83
92
|
if (!file) return;
|
|
84
93
|
registerTransportArtifact(file);
|
|
@@ -12,7 +12,11 @@ import { recordShellCaptureTelemetry } from './shell-output.mjs';
|
|
|
12
12
|
import { normalizeErrorMessage } from './path-diagnostics.mjs';
|
|
13
13
|
import { invalidateBuiltinResultCache } from './cache-layers.mjs';
|
|
14
14
|
import { formatShellToolFailure } from './bash-tool/result-format.mjs';
|
|
15
|
-
import {
|
|
15
|
+
import {
|
|
16
|
+
cleanupArtifactAfter,
|
|
17
|
+
cleanupArtifactOnTaskSettled,
|
|
18
|
+
removeTransportFileNow,
|
|
19
|
+
} from './bash-tool/transport-artifacts.mjs';
|
|
16
20
|
import { combineAbortSignals, sessionAbortSignal } from './bash-tool/abort-signals.mjs';
|
|
17
21
|
import { beginInterruptibleTaskWait } from '../../session/task-wait-control.mjs';
|
|
18
22
|
import { prepareShellCommand } from './bash-tool/command-preflight.mjs';
|
|
@@ -39,9 +43,10 @@ export { buildShellSpawnEnv } from './bash-tool/spawn-env.mjs';
|
|
|
39
43
|
export { executeTaskTool } from './task-tool.mjs';
|
|
40
44
|
|
|
41
45
|
// Reads cached after a mutating command must not serve pre-command state. A
|
|
42
|
-
// promoted command has barely started when the tool returns
|
|
43
|
-
//
|
|
44
|
-
|
|
46
|
+
// promoted command has barely started when the tool returns, a queued one has
|
|
47
|
+
// not started at all and a killed tree may still run, so the invalidation
|
|
48
|
+
// repeats when it settles.
|
|
49
|
+
function settleMutationCaches(shellEffects, backgroundJobId, runSettled = null) {
|
|
45
50
|
const invalidate = () => {
|
|
46
51
|
if (shellEffects.mutationMode === 'paths') {
|
|
47
52
|
invalidateBuiltinResultCache(shellEffects.paths);
|
|
@@ -55,7 +60,7 @@ function settleMutationCaches(shellEffects, backgroundJobId, queuedSettled = nul
|
|
|
55
60
|
if (backgroundJobId && shellEffects.mutationMode !== 'none') {
|
|
56
61
|
subscribeShellJobSettled(backgroundJobId, invalidate);
|
|
57
62
|
}
|
|
58
|
-
if (
|
|
63
|
+
if (runSettled && shellEffects.mutationMode !== 'none') runSettled.then(invalidate).catch(() => {});
|
|
59
64
|
}
|
|
60
65
|
|
|
61
66
|
export async function executeBashTool(args, workDir, options = {}) {
|
|
@@ -89,7 +94,8 @@ export async function executeBashTool(args, workDir, options = {}) {
|
|
|
89
94
|
const inputWake = backgroundOnTimeout ? beginInterruptibleTaskWait(options?.sessionId) : null;
|
|
90
95
|
let backgrounded = false;
|
|
91
96
|
let backgroundJobId = null;
|
|
92
|
-
|
|
97
|
+
// A queued run, or a foreground tree that may outlive its result, settles later.
|
|
98
|
+
let runSettled = null;
|
|
93
99
|
try {
|
|
94
100
|
const invocation = await planShellInvocation({ command, resolvedSpec, cwd: bashWorkDir });
|
|
95
101
|
if (invocation.failure) return invocation.failure;
|
|
@@ -127,7 +133,7 @@ export async function executeBashTool(args, workDir, options = {}) {
|
|
|
127
133
|
});
|
|
128
134
|
backgrounded = result.backgrounded === true;
|
|
129
135
|
backgroundJobId = backgrounded ? result.jobId || null : null;
|
|
130
|
-
|
|
136
|
+
runSettled = result.settled || null;
|
|
131
137
|
const stdout = stripAnsi(result.stdout || '');
|
|
132
138
|
const stderr = stripAnsi(result.stderr || '');
|
|
133
139
|
recordShellCaptureTelemetry(options?.resultTelemetry, result, stdout, stderr);
|
|
@@ -152,14 +158,13 @@ export async function executeBashTool(args, workDir, options = {}) {
|
|
|
152
158
|
inputWake?.dispose();
|
|
153
159
|
combinedAbort?.cleanup?.();
|
|
154
160
|
if (hoisted.hoistPath) {
|
|
155
|
-
// A promoted or queued command still runs from the hoisted file
|
|
156
|
-
//
|
|
157
|
-
// locked — retried).
|
|
158
|
-
if (
|
|
159
|
-
|
|
160
|
-
} else if (backgrounded) cleanupArtifactOnTaskSettled(hoisted.hoistPath, backgroundJobId);
|
|
161
|
+
// A promoted or queued command still runs from the hoisted file, and so
|
|
162
|
+
// may a killed tree until it settles; a finished foreground one is done
|
|
163
|
+
// with it now (an unconfirmed kill may keep it locked — retried).
|
|
164
|
+
if (runSettled) cleanupArtifactAfter(hoisted.hoistPath, runSettled);
|
|
165
|
+
else if (backgrounded) cleanupArtifactOnTaskSettled(hoisted.hoistPath, backgroundJobId);
|
|
161
166
|
else removeTransportFileNow(hoisted.hoistPath);
|
|
162
167
|
}
|
|
163
|
-
settleMutationCaches(shellEffects, backgroundJobId,
|
|
168
|
+
settleMutationCaches(shellEffects, backgroundJobId, runSettled);
|
|
164
169
|
}
|
|
165
170
|
}
|
|
@@ -87,7 +87,13 @@ export const BUILTIN_TOOLS = [
|
|
|
87
87
|
type: 'integer',
|
|
88
88
|
minimum: 1,
|
|
89
89
|
maximum: PUBLIC_READ_WINDOW_MAX,
|
|
90
|
-
description:
|
|
90
|
+
description:
|
|
91
|
+
'Maximum line count; default 500 per exact file, 25 per glob-expanded file; batch default. Lines only: PDFs use pages.',
|
|
92
|
+
},
|
|
93
|
+
pages: {
|
|
94
|
+
type: 'string',
|
|
95
|
+
description:
|
|
96
|
+
'PDF page range "N" or "N-M", 1-based, ≤20 pages per call. Text pages return text; scanned pages (no text layer) return page images, or OCR text on a text-only model. Without pages, a PDF over 10 pages returns its first 5 pages and page count.',
|
|
91
97
|
},
|
|
92
98
|
},
|
|
93
99
|
required: ['file_path'],
|
|
@@ -24,7 +24,7 @@ import { access, mkdir, readFile, readdir, rm, stat, writeFile } from 'node:fs/p
|
|
|
24
24
|
import { unlinkSync } from 'node:fs';
|
|
25
25
|
import { homedir } from 'node:os';
|
|
26
26
|
import { join } from 'node:path';
|
|
27
|
-
import { writeJsonAtomicAsync } from '../../../../../shared/atomic-file.mjs';
|
|
27
|
+
import { holdsNoData, writeJsonAtomicAsync } from '../../../../../shared/atomic-file.mjs';
|
|
28
28
|
import { isPidAlive } from '../../../../../shared/pid-liveness.mjs';
|
|
29
29
|
import { displayShellCommand } from '../../../../../shared/shell-display.mjs';
|
|
30
30
|
import { tryAdmitWorkspaceActivity } from '../../../../../shared/worktrees/activity-gate.mjs';
|
|
@@ -339,23 +339,11 @@ export function releaseShellJobWorkspace(jobId) {
|
|
|
339
339
|
if (workspaceLeases.get(key) === leave) workspaceLeases.delete(key);
|
|
340
340
|
}
|
|
341
341
|
|
|
342
|
-
/** True once `sessionId` closed in this runtime. */
|
|
343
|
-
export function isShellOwnerSessionClosed(sessionId) {
|
|
344
|
-
return closedOwnerSessions.has(String(sessionId || '').trim());
|
|
345
|
-
}
|
|
346
|
-
|
|
347
342
|
/** Flag a still-open job record as "termination unconfirmed" for the readouts. */
|
|
348
343
|
export function markShellJobTerminationUnconfirmed(jobId) {
|
|
349
344
|
return patchRunningShellJobRecord(jobId, { terminationUnconfirmed: true });
|
|
350
345
|
}
|
|
351
346
|
|
|
352
|
-
/** Record the pids a held job's termination watch last found alive, so a
|
|
353
|
-
* reader in another process can tell a live hold from a stale one. */
|
|
354
|
-
export function noteShellJobSurvivors(jobId, pids) {
|
|
355
|
-
const survivorPids = (pids || []).map(Number).filter((pid) => Number.isInteger(pid) && pid > 0);
|
|
356
|
-
return patchRunningShellJobRecord(jobId, { survivorPids });
|
|
357
|
-
}
|
|
358
|
-
|
|
359
347
|
function patchRunningShellJobRecord(jobId, patch) {
|
|
360
348
|
const key = validJobId(jobId);
|
|
361
349
|
if (!key) return Promise.resolve(false);
|
|
@@ -431,7 +419,11 @@ export async function readShellJobRecord(jobId, { strict = false } = {}) {
|
|
|
431
419
|
const dir = jobsDir();
|
|
432
420
|
const jsonPath = join(dir, `${key}.json`);
|
|
433
421
|
try {
|
|
434
|
-
const
|
|
422
|
+
const text = await readFile(jsonPath, 'utf8');
|
|
423
|
+
// A crash left no data (fsync is off for these records): nothing to inspect,
|
|
424
|
+
// the same as no record, even for a strict reader.
|
|
425
|
+
if (holdsNoData(text)) return null;
|
|
426
|
+
const record = JSON.parse(text);
|
|
435
427
|
if (
|
|
436
428
|
strict &&
|
|
437
429
|
(!record ||
|
|
@@ -1,11 +1,14 @@
|
|
|
1
1
|
// Deterministic PDF fixtures shared by the read and MCP media tests.
|
|
2
|
-
|
|
2
|
+
// `pageText(index)` is the text of page index+1; null draws a filled box and
|
|
3
|
+
// no text, like a scanned page without a text layer.
|
|
4
|
+
export function makePdf(pageCount, { pageText = (i) => `Page ${i + 1}` } = {}) {
|
|
3
5
|
const objs = ['<< /Type /Catalog /Pages 2 0 R >>'];
|
|
4
6
|
const kids = Array.from({ length: pageCount }, (_, i) => `${4 + i * 2} 0 R`);
|
|
5
7
|
objs.push(`<< /Type /Pages /Kids [${kids.join(' ')}] /Count ${pageCount} >>`);
|
|
6
8
|
objs.push('<< /Type /Font /Subtype /Type1 /BaseFont /Helvetica >>');
|
|
7
9
|
for (let i = 0; i < pageCount; i += 1) {
|
|
8
|
-
const
|
|
10
|
+
const text = pageText(i);
|
|
11
|
+
const stream = text === null ? '0.2 g 20 20 160 160 re f' : `BT /F1 12 Tf 10 10 Td (${text}) Tj ET`;
|
|
9
12
|
objs.push(
|
|
10
13
|
`<< /Type /Page /Parent 2 0 R /MediaBox [0 0 200 200] /Contents ${5 + i * 2} 0 R /Resources << /Font << /F1 3 0 R >> >> >>`
|
|
11
14
|
);
|
|
@@ -44,4 +47,3 @@ export function makeEncryptedPdf() {
|
|
|
44
47
|
out += `trailer\n<< /Size 5 /Root 1 0 R /Encrypt 4 0 R /ID [<${'cd'.repeat(16)}> <${'cd'.repeat(16)}>] >>\nstartxref\n${xref}\n%%EOF\n`;
|
|
45
48
|
return Buffer.from(out, 'latin1');
|
|
46
49
|
}
|
|
47
|
-
|
|
@@ -37,8 +37,10 @@ export function imageMimeForPath(p) {
|
|
|
37
37
|
// behaviour (refuse over-cap originals with an isError text result, else emit
|
|
38
38
|
// the raw base64 image block). Decoder rejection is an explicit tool error;
|
|
39
39
|
// corrupt bytes must never enter model history.
|
|
40
|
-
|
|
41
|
-
|
|
40
|
+
// `knownMimeType` names the image type of a path whose extension does not
|
|
41
|
+
// (a stored attachment, recognized by its signature).
|
|
42
|
+
export async function readImageAsContent(fullPath, displayPath, preflightStat = null, knownMimeType = null) {
|
|
43
|
+
const mimeType = knownMimeType || imageMimeForPath(fullPath);
|
|
42
44
|
if (!mimeType) return null;
|
|
43
45
|
let st;
|
|
44
46
|
try {
|