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
|
@@ -14,8 +14,12 @@ import { base64ByteLength, inlineFileKind } from '../shared/inline-file-kind.mjs
|
|
|
14
14
|
import { MAX_IMAGE_BYTES, SUPPORTED_IMAGE_MIME_TYPES } from '../attachments/limits.mjs';
|
|
15
15
|
|
|
16
16
|
const DISCOVERY_FILE = 'browser-bridge.json';
|
|
17
|
-
/**
|
|
18
|
-
*
|
|
17
|
+
/** How long a request may go without a single byte from the bridge. Above the
|
|
18
|
+
* bridge's own per-action timeouts (navigation settle, surface auto-open), so
|
|
19
|
+
* the bridge's specific error wins over a bare abort. It is a silence limit,
|
|
20
|
+
* not a total: a command that waits on the person (an approval card, which has
|
|
21
|
+
* no limit) is kept alive by the bridge with a space every few seconds
|
|
22
|
+
* (apps/desktop/src/main/browser/bridge-server.ts), and each byte restarts it. */
|
|
19
23
|
const REQUEST_TIMEOUT_MS = 45_000;
|
|
20
24
|
const MAX_REQUEST_BYTES = 256 * 1024;
|
|
21
25
|
const MAX_RESPONSE_BYTES = 150 * 1024 * 1024;
|
|
@@ -33,7 +37,7 @@ export async function finishBrowserTurn(sessionId, turnId, { aborted = false } =
|
|
|
33
37
|
if (!discovery) return;
|
|
34
38
|
turns.delete(turnId);
|
|
35
39
|
if (!turns.size) browserTurns.delete(sessionId);
|
|
36
|
-
const result = await
|
|
40
|
+
const result = await requestBridgeWithin(
|
|
37
41
|
discovery,
|
|
38
42
|
JSON.stringify({
|
|
39
43
|
action: 'finish_turn',
|
|
@@ -43,7 +47,7 @@ export async function finishBrowserTurn(sessionId, turnId, { aborted = false } =
|
|
|
43
47
|
// so its pages stay; only an aborted run reclaims them.
|
|
44
48
|
aborted: aborted === true,
|
|
45
49
|
}),
|
|
46
|
-
|
|
50
|
+
undefined,
|
|
47
51
|
{ sessionId, turnId, action: 'finish_turn' }
|
|
48
52
|
);
|
|
49
53
|
if (result.status !== 200 || result.body?.ok === false || result.body?.error) {
|
|
@@ -61,12 +65,10 @@ export async function browserSecretInput(sessionId, requestId, { signal } = {})
|
|
|
61
65
|
const discovery = readBridgeDiscovery(DISCOVERY_FILE);
|
|
62
66
|
if (!discovery || !sessionId || !requestId) return 'skipped';
|
|
63
67
|
try {
|
|
64
|
-
const result = await
|
|
68
|
+
const result = await requestBridgeWithin(
|
|
65
69
|
discovery,
|
|
66
70
|
JSON.stringify({ action: 'secret_input', session_id: String(sessionId), request_id: String(requestId) }),
|
|
67
|
-
signal
|
|
68
|
-
? AbortSignal.any([AbortSignal.timeout(REQUEST_TIMEOUT_MS), signal])
|
|
69
|
-
: AbortSignal.timeout(REQUEST_TIMEOUT_MS),
|
|
71
|
+
signal,
|
|
70
72
|
{ sessionId, action: 'secret_input' }
|
|
71
73
|
);
|
|
72
74
|
return result.status === 200 && result.body?.ok !== false && result.body?.value?.text === 'provided'
|
|
@@ -155,8 +157,9 @@ function unavailableMessage() {
|
|
|
155
157
|
class BrowserBridgeResponseError extends Error {}
|
|
156
158
|
|
|
157
159
|
// Read the body incrementally so a chunked response (no content-length) still
|
|
158
|
-
// honours MAX_RESPONSE_BYTES.
|
|
159
|
-
|
|
160
|
+
// honours MAX_RESPONSE_BYTES. `onChunk` hears every chunk read (the bridge's
|
|
161
|
+
// keep-alive spaces included).
|
|
162
|
+
export async function readCappedJson(response, maximum = MAX_RESPONSE_BYTES, onChunk = undefined) {
|
|
160
163
|
if (!response.body) throw new Error('empty response');
|
|
161
164
|
const reader = response.body.getReader();
|
|
162
165
|
const chunks = [];
|
|
@@ -165,6 +168,7 @@ export async function readCappedJson(response, maximum = MAX_RESPONSE_BYTES) {
|
|
|
165
168
|
for (;;) {
|
|
166
169
|
const result = await reader.read();
|
|
167
170
|
if (result.done) break;
|
|
171
|
+
onChunk?.();
|
|
168
172
|
bytes += result.value.byteLength;
|
|
169
173
|
if (bytes > maximum) {
|
|
170
174
|
await reader.cancel().catch(() => {});
|
|
@@ -178,7 +182,50 @@ export async function readCappedJson(response, maximum = MAX_RESPONSE_BYTES) {
|
|
|
178
182
|
return JSON.parse(Buffer.concat(chunks, bytes).toString('utf8'));
|
|
179
183
|
}
|
|
180
184
|
|
|
181
|
-
|
|
185
|
+
/** One bridge request's clock: it times out after `timeoutMs` without a byte
|
|
186
|
+
* from the bridge (`touch` restarts it), with the same TimeoutError an
|
|
187
|
+
* AbortSignal.timeout gives, and ends with `callerSignal` (the caller's own
|
|
188
|
+
* reason). `stop` leaves no timer behind. */
|
|
189
|
+
export function bridgeDeadline(callerSignal, timeoutMs = REQUEST_TIMEOUT_MS) {
|
|
190
|
+
const controller = new AbortController();
|
|
191
|
+
let timer = null;
|
|
192
|
+
const stop = () => {
|
|
193
|
+
if (timer) clearTimeout(timer);
|
|
194
|
+
timer = null;
|
|
195
|
+
};
|
|
196
|
+
const touch = () => {
|
|
197
|
+
stop();
|
|
198
|
+
timer = setTimeout(
|
|
199
|
+
() => controller.abort(new DOMException('The operation was aborted due to timeout', 'TimeoutError')),
|
|
200
|
+
timeoutMs
|
|
201
|
+
);
|
|
202
|
+
timer.unref?.();
|
|
203
|
+
};
|
|
204
|
+
touch();
|
|
205
|
+
return {
|
|
206
|
+
signal: callerSignal ? AbortSignal.any([controller.signal, callerSignal]) : controller.signal,
|
|
207
|
+
touch,
|
|
208
|
+
stop,
|
|
209
|
+
};
|
|
210
|
+
}
|
|
211
|
+
|
|
212
|
+
/** `requestBridge` under the silence limit of `bridgeDeadline`. */
|
|
213
|
+
export async function requestBridgeWithin(
|
|
214
|
+
discovery,
|
|
215
|
+
encodedPayload,
|
|
216
|
+
callerSignal,
|
|
217
|
+
timingContext,
|
|
218
|
+
timeoutMs = REQUEST_TIMEOUT_MS
|
|
219
|
+
) {
|
|
220
|
+
const deadline = bridgeDeadline(callerSignal, timeoutMs);
|
|
221
|
+
try {
|
|
222
|
+
return await requestBridge(discovery, encodedPayload, deadline.signal, timingContext, deadline.touch);
|
|
223
|
+
} finally {
|
|
224
|
+
deadline.stop();
|
|
225
|
+
}
|
|
226
|
+
}
|
|
227
|
+
|
|
228
|
+
async function requestBridge(discovery, encodedPayload, signal, timingContext, onActivity = undefined) {
|
|
182
229
|
const started = performance.now();
|
|
183
230
|
let body;
|
|
184
231
|
let status;
|
|
@@ -192,6 +239,7 @@ async function requestBridge(discovery, encodedPayload, signal, timingContext) {
|
|
|
192
239
|
body: encodedPayload,
|
|
193
240
|
signal,
|
|
194
241
|
});
|
|
242
|
+
onActivity?.();
|
|
195
243
|
status = response.status;
|
|
196
244
|
const contentLength = Number(response.headers.get('content-length'));
|
|
197
245
|
if (Number.isFinite(contentLength) && contentLength > MAX_RESPONSE_BYTES) {
|
|
@@ -199,10 +247,12 @@ async function requestBridge(discovery, encodedPayload, signal, timingContext) {
|
|
|
199
247
|
throw new BrowserBridgeResponseError(`browser bridge response exceeds ${MAX_RESPONSE_BYTES} bytes`);
|
|
200
248
|
}
|
|
201
249
|
try {
|
|
202
|
-
body = await readCappedJson(response);
|
|
250
|
+
body = await readCappedJson(response, MAX_RESPONSE_BYTES, onActivity);
|
|
203
251
|
return { body, status: response.status };
|
|
204
252
|
} catch (error) {
|
|
205
253
|
if (error instanceof BrowserBridgeResponseError) throw error;
|
|
254
|
+
// Silence past the clock, or the caller stopping, is not a bad response.
|
|
255
|
+
if (signal?.aborted) throw signal.reason ?? error;
|
|
206
256
|
throw new BrowserBridgeResponseError(`browser bridge returned an invalid response (HTTP ${response.status})`);
|
|
207
257
|
}
|
|
208
258
|
} finally {
|
|
@@ -225,6 +275,12 @@ export async function executeBrowserTool(args, options = {}) {
|
|
|
225
275
|
action: validated.action,
|
|
226
276
|
...validated.input,
|
|
227
277
|
session_id: sessionId,
|
|
278
|
+
// Set by the runtime from the session, never from the model's arguments: a
|
|
279
|
+
// schedule/webhook run (or work it started) whose saved routine is the
|
|
280
|
+
// user's grant for submits, posts and uploads (human-gate/run-policy.mjs).
|
|
281
|
+
...(options.unattendedRun === true
|
|
282
|
+
? { unattended_run: true, unattended_agent: String(options.unattendedAgent || '') }
|
|
283
|
+
: {}),
|
|
228
284
|
...(Number.isFinite(Number(options.turnId)) && Number(options.turnId) > 0
|
|
229
285
|
? { turn_id: Math.trunc(Number(options.turnId)) }
|
|
230
286
|
: {}),
|
|
@@ -243,14 +299,11 @@ export async function executeBrowserTool(args, options = {}) {
|
|
|
243
299
|
let bridgeResult;
|
|
244
300
|
for (let attempt = 0; attempt < 2; attempt += 1) {
|
|
245
301
|
try {
|
|
246
|
-
bridgeResult = await
|
|
247
|
-
|
|
248
|
-
|
|
249
|
-
|
|
250
|
-
|
|
251
|
-
: AbortSignal.timeout(REQUEST_TIMEOUT_MS),
|
|
252
|
-
{ sessionId, turnId: payload.turn_id, action: validated.action }
|
|
253
|
-
);
|
|
302
|
+
bridgeResult = await requestBridgeWithin(discovery, encodedPayload, options.signal, {
|
|
303
|
+
sessionId,
|
|
304
|
+
turnId: payload.turn_id,
|
|
305
|
+
action: validated.action,
|
|
306
|
+
});
|
|
254
307
|
break;
|
|
255
308
|
} catch (error) {
|
|
256
309
|
if (error?.name === 'TimeoutError') {
|
|
@@ -329,7 +329,7 @@ function cleanupStaleRuntimeFiles(now = Date.now()) {
|
|
|
329
329
|
// tighter TTLs keep dead-process residue from accumulating.
|
|
330
330
|
let ttl = RUNTIME_STALE_TTL;
|
|
331
331
|
if (/^status-.*\.json$/.test(file)) ttl = STATUS_FILE_TTL;
|
|
332
|
-
else if (/\.tmp
|
|
332
|
+
else if (/\.tmp$|\.mixdog-tmp-[0-9a-f]+$/.test(file)) ttl = TMP_FILE_TTL;
|
|
333
333
|
if (age > ttl) removeFileIfExists(fullPath);
|
|
334
334
|
} catch {}
|
|
335
335
|
});
|
|
@@ -26,6 +26,19 @@ const SCHEDULE_LOG = join(DATA_DIR, 'schedule.log');
|
|
|
26
26
|
// here. The channels worker owns shutdown sequencing (drain queues, persist
|
|
27
27
|
// baselines, release the scheduler lock, etc.) and a library-level exit(0)
|
|
28
28
|
// preempts that drain. The buffered appender drains pending log lines on exit.
|
|
29
|
+
function skipReasonText(outcome) {
|
|
30
|
+
switch (outcome.reason) {
|
|
31
|
+
case 'backoff':
|
|
32
|
+
return 'backing off after failures';
|
|
33
|
+
case 'paused':
|
|
34
|
+
return 'switched off';
|
|
35
|
+
case 'owner':
|
|
36
|
+
return `paused: ${outcome.message}`;
|
|
37
|
+
default:
|
|
38
|
+
return 'previous run still in progress';
|
|
39
|
+
}
|
|
40
|
+
}
|
|
41
|
+
|
|
29
42
|
function logSchedule(msg) {
|
|
30
43
|
msg = msg.replace(/\n$/, '');
|
|
31
44
|
process.stderr.write(`mixdog scheduler: ${msg}\n`);
|
|
@@ -686,13 +699,7 @@ ${prompt}`;
|
|
|
686
699
|
// already persisted the skip.
|
|
687
700
|
if (outcome?.skipped) {
|
|
688
701
|
schedule.lastSkippedAt = new Date().toISOString();
|
|
689
|
-
|
|
690
|
-
outcome.reason === 'backoff'
|
|
691
|
-
? 'backing off after failures'
|
|
692
|
-
: outcome.reason === 'owner'
|
|
693
|
-
? `paused: ${outcome.message}`
|
|
694
|
-
: 'previous run still in progress';
|
|
695
|
-
logSchedule(`${schedule.name}: skipped — ${why}\n`);
|
|
702
|
+
logSchedule(`${schedule.name}: skipped — ${skipReasonText(outcome)}\n`);
|
|
696
703
|
return false;
|
|
697
704
|
}
|
|
698
705
|
logSchedule(`${schedule.name} done\n`);
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import { mkdirSync, readFileSync, unlinkSync } from 'node:fs';
|
|
2
|
-
import { writeJsonAtomicSync } from '../../shared/atomic-file.mjs';
|
|
2
|
+
import { readJsonStateSync, updateJsonAtomicSync, writeJsonAtomicSync } from '../../shared/atomic-file.mjs';
|
|
3
3
|
function ensureDir(dirPath) {
|
|
4
4
|
mkdirSync(dirPath, { recursive: true });
|
|
5
5
|
}
|
|
@@ -19,10 +19,6 @@ function readJsonFile(filePath, fallback) {
|
|
|
19
19
|
return fallback;
|
|
20
20
|
}
|
|
21
21
|
}
|
|
22
|
-
function readJsonFileStrict(filePath) {
|
|
23
|
-
const raw = readFileSync(filePath, 'utf8');
|
|
24
|
-
return JSON.parse(raw);
|
|
25
|
-
}
|
|
26
22
|
function writeJsonFile(filePath, value) {
|
|
27
23
|
writeJsonAtomicSync(filePath, value, { compact: true, lock: true, fsync: false, fsyncDir: false });
|
|
28
24
|
}
|
|
@@ -38,19 +34,23 @@ class JsonStateFile {
|
|
|
38
34
|
writeJsonFile(this.filePath, value);
|
|
39
35
|
return value;
|
|
40
36
|
}
|
|
37
|
+
// A damaged file (empty, NUL-filled after a crash) is kept aside as
|
|
38
|
+
// <file>.corrupt-<ts> and the file restarts from the fallback; it is never
|
|
39
|
+
// overwritten in place and never blocks an update. That recovery is why the
|
|
40
|
+
// writes below can skip fsync.
|
|
41
41
|
ensure() {
|
|
42
|
-
writeJsonFile(this.filePath, this.
|
|
42
|
+
writeJsonFile(this.filePath, readJsonStateSync(this.filePath, { fallback: this.fallback }));
|
|
43
43
|
}
|
|
44
44
|
update(mutator) {
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
45
|
+
return updateJsonAtomicSync(
|
|
46
|
+
this.filePath,
|
|
47
|
+
(current) => {
|
|
48
|
+
const draft = current ?? structuredClone(this.fallback);
|
|
49
|
+
mutator(draft);
|
|
50
|
+
return draft;
|
|
51
|
+
},
|
|
52
|
+
{ compact: true, fsync: false, fsyncDir: false }
|
|
53
|
+
);
|
|
54
54
|
}
|
|
55
55
|
}
|
|
56
56
|
export { JsonStateFile, ensureDir, readJsonFile, removeFileIfExists, writeJsonFile };
|
|
@@ -43,7 +43,8 @@ import { fileURLToPath } from 'node:url';
|
|
|
43
43
|
import { pipeline } from 'node:stream/promises';
|
|
44
44
|
import { spawnSync } from 'node:child_process';
|
|
45
45
|
import { createGunzip } from 'node:zlib';
|
|
46
|
-
import { renameWithRetrySync, writeFileAtomicSync } from '../../shared/atomic-file.mjs';
|
|
46
|
+
import { renameWithRetrySync, writeFileAtomicSync, writeJsonAtomicSync } from '../../shared/atomic-file.mjs';
|
|
47
|
+
import { fileHasData } from '../../shared/file-has-data.mjs';
|
|
47
48
|
import { streamResponseToFile } from '../../shared/bounded-download.mjs';
|
|
48
49
|
import { platformEntryKey, sha256File, verifySha256File } from '../../shared/native-asset.mjs';
|
|
49
50
|
import { windowsProgramRoots, windowsSystemRoot } from '../../agent/orchestrator/tools/builtin/windows-roots.mjs';
|
|
@@ -264,7 +265,7 @@ async function loadManifest(dataDir) {
|
|
|
264
265
|
if (!res.ok) throw new Error(`[voice-runtime] manifest fetch failed: ${res.status} ${res.statusText}`);
|
|
265
266
|
const manifest = await res.json();
|
|
266
267
|
mkdirSync(join(dataDir, 'voice-runtime'), { recursive: true });
|
|
267
|
-
|
|
268
|
+
writeJsonAtomicSync(cachedPath, manifest);
|
|
268
269
|
return manifest;
|
|
269
270
|
}
|
|
270
271
|
|
|
@@ -439,7 +440,7 @@ function gcPublishedRuntimeSiblings(rootDir, prefix) {
|
|
|
439
440
|
}
|
|
440
441
|
|
|
441
442
|
async function extractGz(gzPath, destPath) {
|
|
442
|
-
await pipeline(createReadStream(gzPath), createGunzip(), createWriteStream(destPath));
|
|
443
|
+
await pipeline(createReadStream(gzPath), createGunzip(), createWriteStream(destPath, { flush: true }));
|
|
443
444
|
}
|
|
444
445
|
|
|
445
446
|
// Sweep .staging-* partials left by killed/crashed install attempts.
|
|
@@ -541,13 +542,13 @@ export async function ensureWhisperRuntime(dataDir, onProgress = null) {
|
|
|
541
542
|
const activeDir = join(rootDir, activeName);
|
|
542
543
|
const whisperCmd = join(activeDir, variant.executable);
|
|
543
544
|
|
|
544
|
-
if (
|
|
545
|
+
if (fileHasData(whisperCmd)) {
|
|
545
546
|
gcStaleVersions(rootDir, activeName, 'whisper-');
|
|
546
547
|
return { whisperCmd, version: ver, variantId: variant.id };
|
|
547
548
|
}
|
|
548
549
|
|
|
549
550
|
return _withInstallLock(rootDir, 'install', async () => {
|
|
550
|
-
if (
|
|
551
|
+
if (fileHasData(whisperCmd)) {
|
|
551
552
|
gcStaleVersions(rootDir, activeName, 'whisper-');
|
|
552
553
|
return { whisperCmd, version: ver, variantId: variant.id };
|
|
553
554
|
}
|
|
@@ -641,14 +642,14 @@ function resolveManagedWhisperCmd(dataDir) {
|
|
|
641
642
|
const variant = variants.find((v) => v.id === variantId);
|
|
642
643
|
if (variant?.executable) {
|
|
643
644
|
const p = join(activeDir, variant.executable);
|
|
644
|
-
if (
|
|
645
|
+
if (fileHasData(p)) return p;
|
|
645
646
|
}
|
|
646
647
|
}
|
|
647
648
|
} catch {}
|
|
648
649
|
}
|
|
649
650
|
for (const c of ['Release/whisper-cli.exe', 'Release/whisper-cli']) {
|
|
650
651
|
const p = join(activeDir, c);
|
|
651
|
-
if (
|
|
652
|
+
if (fileHasData(p)) return p;
|
|
652
653
|
}
|
|
653
654
|
return null;
|
|
654
655
|
}
|
|
@@ -659,7 +660,7 @@ function resolveManagedWhisperModelById(dataDir, modelId = 'standard') {
|
|
|
659
660
|
if (!entry?.filename) return null;
|
|
660
661
|
const modelDir = join(dataDir, 'voice', 'models');
|
|
661
662
|
const p = join(modelDir, entry.filename);
|
|
662
|
-
return
|
|
663
|
+
return fileHasData(p) ? p : null;
|
|
663
664
|
}
|
|
664
665
|
|
|
665
666
|
// Clean-install policy: the models dir holds ONLY filenames declared by the
|
|
@@ -712,13 +713,13 @@ export async function ensureFfmpegRuntime(dataDir, onProgress = null) {
|
|
|
712
713
|
const activeDir = join(rootDir, activeName);
|
|
713
714
|
const ffmpegPath = join(activeDir, platformEntry.executable);
|
|
714
715
|
|
|
715
|
-
if (
|
|
716
|
+
if (fileHasData(ffmpegPath)) {
|
|
716
717
|
gcStaleVersions(rootDir, activeName, 'ffmpeg-');
|
|
717
718
|
return { ffmpegPath, version: ver };
|
|
718
719
|
}
|
|
719
720
|
|
|
720
721
|
return _withInstallLock(rootDir, 'install', async () => {
|
|
721
|
-
if (
|
|
722
|
+
if (fileHasData(ffmpegPath)) {
|
|
722
723
|
gcStaleVersions(rootDir, activeName, 'ffmpeg-');
|
|
723
724
|
return { ffmpegPath, version: ver };
|
|
724
725
|
}
|
|
@@ -783,13 +784,13 @@ function resolveManagedFfmpegPath(dataDir) {
|
|
|
783
784
|
const platformEntry = manifest.ffmpeg?.platforms?.[key];
|
|
784
785
|
if (platformEntry?.executable) {
|
|
785
786
|
const p = join(activeDir, platformEntry.executable);
|
|
786
|
-
if (
|
|
787
|
+
if (fileHasData(p)) return p;
|
|
787
788
|
}
|
|
788
789
|
} catch {}
|
|
789
790
|
}
|
|
790
791
|
for (const c of ['ffmpeg.exe', 'ffmpeg']) {
|
|
791
792
|
const p = join(activeDir, c);
|
|
792
|
-
if (
|
|
793
|
+
if (fileHasData(p)) return p;
|
|
793
794
|
}
|
|
794
795
|
return null;
|
|
795
796
|
}
|
|
@@ -835,7 +836,7 @@ export function resolveVoiceRuntime(dataDir, { modelId = 'standard', cleanup = t
|
|
|
835
836
|
const managedFfmpegPath = resolveManagedFfmpegPath(dataDir);
|
|
836
837
|
const ext = process.platform === 'win32' ? '.exe' : '';
|
|
837
838
|
const managedServerCmd = managedWhisperCmd ? join(dirname(managedWhisperCmd), `whisper-server${ext}`) : null;
|
|
838
|
-
const serverCmd = managedServerCmd &&
|
|
839
|
+
const serverCmd = managedServerCmd && fileHasData(managedServerCmd) ? managedServerCmd : null;
|
|
839
840
|
return {
|
|
840
841
|
kind: 'managed-whisper.cpp',
|
|
841
842
|
label: 'whisper.cpp',
|
|
@@ -8,8 +8,9 @@
|
|
|
8
8
|
// verification stays local — the relay never inspects payloads. Works out
|
|
9
9
|
// of the box: no binary, no authtoken, no reserved domain.
|
|
10
10
|
import { randomBytes, randomUUID } from 'node:crypto';
|
|
11
|
-
import { readFileSync,
|
|
11
|
+
import { readFileSync, chmodSync } from 'node:fs';
|
|
12
12
|
import { join } from 'node:path';
|
|
13
|
+
import { readJsonStateSync, writeJsonAtomicSync } from '../../../shared/atomic-file.mjs';
|
|
13
14
|
import { DATA_DIR } from '../config.mjs';
|
|
14
15
|
import { logWebhook } from './log.mjs';
|
|
15
16
|
import { MAX_HOOK_FRAME_BYTES, MAX_HOOK_HEADER_BYTES, MAX_TUNNEL_BODY_BYTES } from './relay-tunnel/limits.mjs';
|
|
@@ -55,19 +56,28 @@ function hookIdentityPath() {
|
|
|
55
56
|
return join(DATA_DIR, 'relay-hook-device.json');
|
|
56
57
|
}
|
|
57
58
|
|
|
59
|
+
function validHookIdentity(parsed) {
|
|
60
|
+
if (
|
|
61
|
+
typeof parsed?.deviceId !== 'string' ||
|
|
62
|
+
!/^[0-9a-f-]{8,64}$/.test(parsed.deviceId) ||
|
|
63
|
+
typeof parsed.deviceSecret !== 'string' ||
|
|
64
|
+
parsed.deviceSecret.length < 16
|
|
65
|
+
) {
|
|
66
|
+
throw new Error('invalid hook tunnel identity');
|
|
67
|
+
}
|
|
68
|
+
return { deviceId: parsed.deviceId, deviceSecret: parsed.deviceSecret };
|
|
69
|
+
}
|
|
70
|
+
|
|
58
71
|
// Stable per-install identity (trust-on-first-use at the relay, mirroring
|
|
59
72
|
// the desktop leg). The secret never leaves this machine except toward the
|
|
60
73
|
// relay; the deviceId doubles as the public URL path segment.
|
|
61
74
|
function loadOrCreateHookIdentity() {
|
|
62
75
|
const path = hookIdentityPath();
|
|
63
76
|
try {
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
typeof parsed.deviceSecret === 'string' &&
|
|
69
|
-
parsed.deviceSecret.length >= 16
|
|
70
|
-
) {
|
|
77
|
+
// A damaged identity (empty, NUL-filled, malformed) is kept aside and the
|
|
78
|
+
// last good copy restored; only when there is none is a new identity made.
|
|
79
|
+
const stored = readJsonStateSync(path, { validate: validHookIdentity, secret: true, fallback: null });
|
|
80
|
+
if (stored) {
|
|
71
81
|
// The secret authenticates the webhook tunnel leg (whoever holds it can
|
|
72
82
|
// take over inbound deliveries), so clamp legacy world-readable files.
|
|
73
83
|
try {
|
|
@@ -75,20 +85,14 @@ function loadOrCreateHookIdentity() {
|
|
|
75
85
|
} catch {
|
|
76
86
|
/* windows/fs quirk */
|
|
77
87
|
}
|
|
78
|
-
return
|
|
88
|
+
return stored;
|
|
79
89
|
}
|
|
80
|
-
} catch {
|
|
81
|
-
|
|
90
|
+
} catch (err) {
|
|
91
|
+
logWebhook(`hook tunnel: identity read failed — ${err?.message || err}`);
|
|
82
92
|
}
|
|
83
93
|
const identity = { deviceId: randomUUID(), deviceSecret: randomBytes(24).toString('hex') };
|
|
84
94
|
try {
|
|
85
|
-
|
|
86
|
-
writeFileSync(path, JSON.stringify(identity, null, 2), { encoding: 'utf8', mode: 0o600 });
|
|
87
|
-
try {
|
|
88
|
-
chmodSync(path, 0o600);
|
|
89
|
-
} catch {
|
|
90
|
-
/* windows/fs quirk */
|
|
91
|
-
}
|
|
95
|
+
writeJsonAtomicSync(path, identity, { secret: true, backup: true });
|
|
92
96
|
} catch (err) {
|
|
93
97
|
logWebhook(`hook tunnel: identity persist failed — ${err?.message || err}`);
|
|
94
98
|
}
|
|
@@ -2,8 +2,6 @@ import { logWebhook } from './log.mjs';
|
|
|
2
2
|
import { recordEndpointEvent, updateDeliveryStatus } from '../webhooks-db.mjs';
|
|
3
3
|
import { UNTRUSTED_MARKER, scrubFenceMarker } from './untrusted-fence.mjs';
|
|
4
4
|
|
|
5
|
-
const DISPATCH_TIMEOUT_MS = 10 * 60 * 1000;
|
|
6
|
-
|
|
7
5
|
/** The headers a run's prompt shows (and a stored event keeps). */
|
|
8
6
|
function promptHeaders(headers) {
|
|
9
7
|
return Object.fromEntries(Object.entries(headers || {}).filter(([k]) => k.startsWith('x-') || k === 'content-type'));
|
|
@@ -47,33 +45,16 @@ function webhookEventSummary(headers, body) {
|
|
|
47
45
|
return [type, field(body?.action)].filter(Boolean).join(' · ').slice(0, 120);
|
|
48
46
|
}
|
|
49
47
|
|
|
50
|
-
function trackDispatch({ name, deliveryId, dispatchP,
|
|
48
|
+
function trackDispatch({ name, deliveryId, dispatchP, updateDeliveryStatus }) {
|
|
51
49
|
let settled = false;
|
|
52
|
-
const timeoutHandle = setTimeout(
|
|
53
|
-
() => controller.abort(new Error(`bridge dispatch timed out after ${DISPATCH_TIMEOUT_MS}ms`)),
|
|
54
|
-
DISPATCH_TIMEOUT_MS
|
|
55
|
-
);
|
|
56
50
|
const finish = (status, fields, message) => {
|
|
57
51
|
if (settled) return;
|
|
58
52
|
settled = true;
|
|
59
|
-
clearTimeout(timeoutHandle);
|
|
60
53
|
void updateDeliveryStatus(name, deliveryId, status, fields).catch((e) =>
|
|
61
54
|
logWebhook(`${name}: delivery status update failed: ${e?.message || e}`)
|
|
62
55
|
);
|
|
63
56
|
logWebhook(message);
|
|
64
57
|
};
|
|
65
|
-
controller.signal.addEventListener(
|
|
66
|
-
'abort',
|
|
67
|
-
() => {
|
|
68
|
-
const reason = controller.signal.reason;
|
|
69
|
-
finish(
|
|
70
|
-
'failed',
|
|
71
|
-
{ error: String(reason?.message || reason) },
|
|
72
|
-
`${name}: webhook session run aborted: ${reason?.message || reason}`
|
|
73
|
-
);
|
|
74
|
-
},
|
|
75
|
-
{ once: true }
|
|
76
|
-
);
|
|
77
58
|
dispatchP.then(
|
|
78
59
|
(run) =>
|
|
79
60
|
run?.skipped
|
|
@@ -88,23 +69,20 @@ function trackDispatch({ name, deliveryId, dispatchP, controller }) {
|
|
|
88
69
|
);
|
|
89
70
|
}
|
|
90
71
|
|
|
91
|
-
|
|
72
|
+
// `store` is the delivery record (injectable for tests).
|
|
73
|
+
function createWebhookSessionDispatcher({ getBridgeDispatch, store = { recordEndpointEvent, updateDeliveryStatus } }) {
|
|
74
|
+
const { recordEndpointEvent: recordEvent, updateDeliveryStatus: setStatus } = store;
|
|
92
75
|
async function dispatchSessionRun(name, fullPrompt, deliveryId, res, extra = {}) {
|
|
93
|
-
await
|
|
94
|
-
//
|
|
95
|
-
//
|
|
96
|
-
//
|
|
97
|
-
//
|
|
98
|
-
//
|
|
99
|
-
// the run itself kept going: the delivery row was already `failed` when the
|
|
100
|
-
// late result arrived and flipped it again. The timeout now aborts the
|
|
101
|
-
// dispatch through a signal the dispatcher receives, and the first outcome
|
|
102
|
-
// to land is the only one that writes a terminal status.
|
|
103
|
-
const controller = new AbortController();
|
|
76
|
+
await setStatus(name, deliveryId, 'processing');
|
|
77
|
+
// The HTTP caller is answered 202 at once; the run is a turn in the bot's
|
|
78
|
+
// chat and has no deadline (a run may wait on the user's approval for as
|
|
79
|
+
// long as it takes), so nothing here cancels it. The delivery stays
|
|
80
|
+
// `processing` until the run ends, then `done` / `skipped` / `failed`; a
|
|
81
|
+
// second delivery meanwhile is skipped by the run's own overlap rule.
|
|
104
82
|
const dispatchP = Promise.resolve(
|
|
105
83
|
getBridgeDispatch()({
|
|
106
84
|
prompt: fullPrompt,
|
|
107
|
-
signal:
|
|
85
|
+
signal: null,
|
|
108
86
|
// The endpoint's project folder, named to the bot in the turn.
|
|
109
87
|
cwd: extra.cwd || null,
|
|
110
88
|
attachments: extra.attachments || null,
|
|
@@ -123,7 +101,7 @@ function createWebhookSessionDispatcher({ getBridgeDispatch }) {
|
|
|
123
101
|
},
|
|
124
102
|
})
|
|
125
103
|
);
|
|
126
|
-
trackDispatch({ name, deliveryId, dispatchP,
|
|
104
|
+
trackDispatch({ name, deliveryId, dispatchP, updateDeliveryStatus: setStatus });
|
|
127
105
|
res.writeHead(202, { 'Content-Type': 'application/json' });
|
|
128
106
|
res.end(JSON.stringify({ status: 'accepted', handler: 'session', id: deliveryId }));
|
|
129
107
|
}
|
|
@@ -135,7 +113,7 @@ function createWebhookSessionDispatcher({ getBridgeDispatch }) {
|
|
|
135
113
|
if (!getBridgeDispatch()) throw new Error(`[webhook] session dispatch requires bridgeDispatch`);
|
|
136
114
|
// Kept for the app's "Run now", which re-runs the latest event. Losing
|
|
137
115
|
// the record never blocks the delivery itself.
|
|
138
|
-
await
|
|
116
|
+
await recordEvent(name, { body, headers: promptHeaders(headers) }).catch((e) =>
|
|
139
117
|
logWebhook(`${name}: event record failed: ${e?.message || e}`)
|
|
140
118
|
);
|
|
141
119
|
await dispatchSessionRun(name, run.prompt, deliveryId, res, {
|
|
@@ -148,7 +126,7 @@ function createWebhookSessionDispatcher({ getBridgeDispatch }) {
|
|
|
148
126
|
});
|
|
149
127
|
return true;
|
|
150
128
|
} catch (err) {
|
|
151
|
-
await
|
|
129
|
+
await setStatus(name, deliveryId, 'failed', { error: String(err?.message || err) });
|
|
152
130
|
logWebhook(`${name}: folder handler error: ${err}`);
|
|
153
131
|
return false;
|
|
154
132
|
}
|
|
@@ -10,6 +10,7 @@ import { Scheduler } from './scheduler.mjs';
|
|
|
10
10
|
import { hasPending as dispatchHasPending } from '../../agent/orchestrator/dispatch-persist.mjs';
|
|
11
11
|
import { stripSoftWarns } from '../../agent/orchestrator/tool-loop-guard.mjs';
|
|
12
12
|
import { JsonStateFile } from './state-file.mjs';
|
|
13
|
+
import { writeJsonAtomicSync } from '../../shared/atomic-file.mjs';
|
|
13
14
|
import {
|
|
14
15
|
makeInstanceId,
|
|
15
16
|
getStatusPath,
|
|
@@ -115,7 +116,7 @@ function writeBridgeState(active) {
|
|
|
115
116
|
try {
|
|
116
117
|
const stateFile = path.join(ensurePrivateRuntimeRoot(resolveRuntimeRoot()), 'bridge-state.json');
|
|
117
118
|
fs.mkdirSync(path.dirname(stateFile), { recursive: true });
|
|
118
|
-
|
|
119
|
+
writeJsonAtomicSync(stateFile, { active, ts: Date.now() }, { compact: true });
|
|
119
120
|
} catch {}
|
|
120
121
|
}
|
|
121
122
|
function isChannelBridgeActive() {
|
|
@@ -225,7 +226,7 @@ function wireWebhookHandlers() {
|
|
|
225
226
|
cwd: cwd || null,
|
|
226
227
|
attachments: attachments || null,
|
|
227
228
|
event: context?.event || '',
|
|
228
|
-
//
|
|
229
|
+
// The dispatch has no deadline; a caller-supplied signal still reaches the run.
|
|
229
230
|
signal: signal || null,
|
|
230
231
|
prompt,
|
|
231
232
|
skillSelection,
|
|
@@ -7,8 +7,9 @@
|
|
|
7
7
|
// arrays (~30 MB). The compact form is built once from tokenizer.json and
|
|
8
8
|
// cached next to it as tokenizer.bin, keyed by the source file's size and
|
|
9
9
|
// modification time.
|
|
10
|
-
import { readFileSync, statSync
|
|
10
|
+
import { readFileSync, statSync } from 'node:fs';
|
|
11
11
|
import { join } from 'node:path';
|
|
12
|
+
import { writeFileAtomicSync } from '../shared/atomic-file.mjs';
|
|
12
13
|
|
|
13
14
|
const MAGIC = 0x4d585442; // "MXTB"
|
|
14
15
|
const VERSION = 3;
|
|
@@ -93,6 +94,9 @@ function readCached(dir) {
|
|
|
93
94
|
const h = new Int32Array(buf.buffer, buf.byteOffset, 7);
|
|
94
95
|
const [size, mtime] = sourceStamp(dir);
|
|
95
96
|
if (h[0] !== MAGIC || h[1] !== VERSION || h[2] !== size || h[3] !== mtime % 2 ** 31) return null;
|
|
97
|
+
// header (28) + byte table (1024) + char pairs + merges + meta; a shorter
|
|
98
|
+
// file is torn and is rebuilt.
|
|
99
|
+
if (buf.length < 1052 + h[4] * 8 + h[5] * 12 + h[6]) return null;
|
|
96
100
|
return buf;
|
|
97
101
|
} catch {
|
|
98
102
|
return null;
|
|
@@ -104,7 +108,9 @@ export function loadCompactTokenizer(dir) {
|
|
|
104
108
|
let buf = readCached(dir);
|
|
105
109
|
if (!buf) {
|
|
106
110
|
buf = build(dir);
|
|
107
|
-
|
|
111
|
+
// Atomic: a torn cache (valid header, short body) would pass readCached's
|
|
112
|
+
// header check and then fail on every load.
|
|
113
|
+
writeFileAtomicSync(join(dir, 'tokenizer.bin'), buf);
|
|
108
114
|
}
|
|
109
115
|
// Copy into an aligned buffer so typed-array views are valid.
|
|
110
116
|
const ab = new ArrayBuffer(buf.length);
|