mixdog 1.3.1 → 1.3.3
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +8 -4
- package/package.json +8 -8
- package/scripts/native-release-status.mjs +238 -0
- package/scripts/verify-runtime-dependency-versions.mjs +158 -0
- package/src/agents/reviewer/AGENT.md +5 -7
- package/src/agents/reviewer/agent.json +1 -1
- package/src/defaults/shipped-history.json +9 -1
- package/src/defaults/skills/browser-use/SKILL.md +6 -3
- package/src/defaults/skills/local-provider/SKILL.md +33 -6
- package/src/defaults/skills/pptx/scripts/qc-pages.mjs +3 -2
- package/src/defaults/skills/setup/SKILL.md +23 -1
- package/src/defaults/skills/setup/references/actions.md +50 -3
- package/src/defaults/skills/setup/references/surfaces.md +23 -3
- package/src/defaults/skills/setup-guide/SKILL.md +7 -3
- package/src/defaults/skills/visualize/SKILL.md +9 -6
- package/src/defaults/skills/visualize/references/craft.md +8 -1
- package/src/help.mjs +1 -1
- package/src/lib/keychain-cjs.cjs +4 -1
- package/src/lib/rules-builder.cjs +14 -7
- package/src/rules/agent-mode/AGENT_MODE.md +8 -5
- package/src/rules/agent-mode/MIXY.md +6 -8
- package/src/rules/lead/LEAD.md +7 -10
- package/src/rules/shared/10-tool-workflow.md +4 -0
- package/src/rules/shared/20-research.md +2 -0
- package/src/rules/shared/45-destructive.md +5 -1
- package/src/rules/shared/60-verification.md +6 -4
- package/src/runtime/agent/orchestrator/agent-runtime/agent-dispatch/preset.mjs +5 -0
- package/src/runtime/agent/orchestrator/agent-runtime/agent-dispatch/session-prep.mjs +8 -0
- package/src/runtime/agent/orchestrator/agent-runtime/agent-progress-watchdog.mjs +6 -1
- package/src/runtime/agent/orchestrator/agent-runtime/cache-strategy.mjs +114 -26
- package/src/runtime/agent/orchestrator/agent-runtime/maintenance-route.mjs +8 -0
- package/src/runtime/agent/orchestrator/agent-runtime/room-supervisor-completion.mjs +5 -4
- package/src/runtime/agent/orchestrator/agent-trace.mjs +3 -0
- package/src/runtime/agent/orchestrator/cache-break-trace.mjs +4 -0
- package/src/runtime/agent/orchestrator/config-presets.mjs +2 -0
- package/src/runtime/agent/orchestrator/config-storage.mjs +10 -3
- package/src/runtime/agent/orchestrator/config.mjs +11 -0
- package/src/runtime/agent/orchestrator/context/role-instructions.mjs +9 -2
- package/src/runtime/agent/orchestrator/mcp/agent-connectors.mjs +97 -5
- package/src/runtime/agent/orchestrator/mcp/child-tree.mjs +6 -10
- package/src/runtime/agent/orchestrator/mcp/client.mjs +7 -4
- package/src/runtime/agent/orchestrator/providers/anthropic-api-request.mjs +84 -32
- package/src/runtime/agent/orchestrator/providers/anthropic-max-tokens.mjs +38 -11
- package/src/runtime/agent/orchestrator/providers/anthropic-messages.mjs +15 -3
- package/src/runtime/agent/orchestrator/providers/anthropic-model-resolve.mjs +1 -1
- package/src/runtime/agent/orchestrator/providers/anthropic-oauth-client-version.mjs +1 -1
- package/src/runtime/agent/orchestrator/providers/anthropic-oauth.mjs +84 -42
- package/src/runtime/agent/orchestrator/providers/anthropic-turn-result.mjs +2 -0
- package/src/runtime/agent/orchestrator/providers/anthropic.mjs +14 -1
- package/src/runtime/agent/orchestrator/providers/antigravity-request.mjs +6 -2
- package/src/runtime/agent/orchestrator/providers/auto-effort.mjs +27 -8
- package/src/runtime/agent/orchestrator/providers/cursor-client-version.mjs +1 -1
- package/src/runtime/agent/orchestrator/providers/cursor-wire.mjs +31 -4
- package/src/runtime/agent/orchestrator/providers/cursor.mjs +4 -1
- package/src/runtime/agent/orchestrator/providers/custom-provider-config.mjs +14 -1
- package/src/runtime/agent/orchestrator/providers/custom-provider.mjs +11 -8
- package/src/runtime/agent/orchestrator/providers/grok-client-version.mjs +1 -1
- package/src/runtime/agent/orchestrator/providers/lib/anthropic-prefix-trace.mjs +76 -0
- package/src/runtime/agent/orchestrator/providers/lib/anthropic-request-utils.mjs +16 -0
- package/src/runtime/agent/orchestrator/providers/lib/tail-reminder-split.mjs +13 -0
- package/src/runtime/agent/orchestrator/providers/media-normalization.mjs +16 -1
- package/src/runtime/agent/orchestrator/providers/mixdog-local.mjs +3 -1
- package/src/runtime/agent/orchestrator/providers/model-proxy-provider.mjs +1 -0
- package/src/runtime/agent/orchestrator/providers/openai-codex-metadata.mjs +5 -5
- package/src/runtime/agent/orchestrator/providers/openai-compat-models.mjs +9 -5
- package/src/runtime/agent/orchestrator/providers/openai-compat-responses.mjs +9 -1
- package/src/runtime/agent/orchestrator/providers/openai-compat-wire.mjs +2 -1
- package/src/runtime/agent/orchestrator/providers/openai-direct-request.mjs +30 -3
- package/src/runtime/agent/orchestrator/providers/openai-model-capabilities.mjs +1 -1
- package/src/runtime/agent/orchestrator/providers/openai-ws.mjs +3 -1
- package/src/runtime/agent/orchestrator/providers/opencode-go-oauth.mjs +61 -8
- package/src/runtime/agent/orchestrator/providers/registry.mjs +19 -0
- package/src/runtime/agent/orchestrator/providers/wire-gateway.mjs +13 -1
- package/src/runtime/agent/orchestrator/runtime-core/config-helpers.mjs +90 -24
- package/src/runtime/agent/orchestrator/runtime-core/fs-utils.mjs +8 -1
- package/src/runtime/agent/orchestrator/runtime-core/goal-mutations.mjs +1 -1
- package/src/runtime/agent/orchestrator/runtime-core/goal-runtime.mjs +5 -0
- package/src/runtime/agent/orchestrator/runtime-core/goal-storage.mjs +68 -22
- package/src/runtime/agent/orchestrator/runtime-core/goal-tool-exec.mjs +1 -1
- package/src/runtime/agent/orchestrator/runtime-core/goal-turns.mjs +10 -1
- package/src/runtime/agent/orchestrator/runtime-core/orchestration.mjs +4 -6
- package/src/runtime/agent/orchestrator/runtime-core/route-context-state.mjs +81 -0
- package/src/runtime/agent/orchestrator/runtime-core/session-text.mjs +2 -1
- package/src/runtime/agent/orchestrator/runtime-core/tool-catalog.mjs +52 -6
- package/src/runtime/agent/orchestrator/runtime-core/tool-defs.mjs +35 -13
- package/src/runtime/agent/orchestrator/runtime-core/workflow/agents.mjs +7 -6
- package/src/runtime/agent/orchestrator/runtime-core/workflow/context.mjs +1 -1
- package/src/runtime/agent/orchestrator/runtime-core/workflow/packs.mjs +8 -4
- package/src/runtime/agent/orchestrator/runtime-core/workflow.mjs +8 -3
- package/src/runtime/agent/orchestrator/session/agent-loop.mjs +1 -1
- package/src/runtime/agent/orchestrator/session/agent-mode.mjs +0 -6
- package/src/runtime/agent/orchestrator/session/agent-room-supervisor.d.mts +1 -1
- package/src/runtime/agent/orchestrator/session/agent-room-supervisor.mjs +22 -4
- package/src/runtime/agent/orchestrator/session/agent-room.d.mts +30 -2
- package/src/runtime/agent/orchestrator/session/agent-room.mjs +196 -45
- package/src/runtime/agent/orchestrator/session/attachment-preflight.mjs +80 -0
- package/src/runtime/agent/orchestrator/session/catalog-db/schema.mjs +4 -1
- package/src/runtime/agent/orchestrator/session/compact/continuation.mjs +15 -6
- package/src/runtime/agent/orchestrator/session/compact/execution-tail.mjs +45 -19
- package/src/runtime/agent/orchestrator/session/compact/forked-summary.mjs +3 -0
- package/src/runtime/agent/orchestrator/session/compact/messages.mjs +44 -0
- package/src/runtime/agent/orchestrator/session/compact/runner.mjs +185 -37
- package/src/runtime/agent/orchestrator/session/compact/summary-mode.mjs +16 -0
- package/src/runtime/agent/orchestrator/session/compact/summary-route.mjs +87 -0
- package/src/runtime/agent/orchestrator/session/compact/summary.mjs +2 -0
- package/src/runtime/agent/orchestrator/session/compact/text-utils.mjs +6 -2
- package/src/runtime/agent/orchestrator/session/compact/user-message-kind.mjs +4 -1
- package/src/runtime/agent/orchestrator/session/compaction-archive-capture.mjs +1 -1
- package/src/runtime/agent/orchestrator/session/context-utils.mjs +41 -10
- package/src/runtime/agent/orchestrator/session/human-gate/delegation.mjs +240 -0
- package/src/runtime/agent/orchestrator/session/human-gate/gate.mjs +102 -35
- package/src/runtime/agent/orchestrator/session/human-gate/outbound-policy.mjs +20 -0
- package/src/runtime/agent/orchestrator/session/human-gate/requests.mjs +19 -9
- package/src/runtime/agent/orchestrator/session/human-gate/run-policy.mjs +73 -0
- package/src/runtime/agent/orchestrator/session/human-gate/store.mjs +55 -27
- package/src/runtime/agent/orchestrator/session/human-gate/unattended-port.mjs +15 -0
- package/src/runtime/agent/orchestrator/session/loop/compact-policy.mjs +4 -1
- package/src/runtime/agent/orchestrator/session/loop/fresh-context.mjs +199 -109
- package/src/runtime/agent/orchestrator/session/loop/loop-state.mjs +5 -0
- package/src/runtime/agent/orchestrator/session/loop/no-tool-turn/continuation.mjs +2 -0
- package/src/runtime/agent/orchestrator/session/loop/no-tool-turn/empty-nudge.mjs +1 -1
- package/src/runtime/agent/orchestrator/session/loop/no-tool-turn/output-limit.mjs +62 -13
- package/src/runtime/agent/orchestrator/session/loop/no-tool-turn.mjs +22 -6
- package/src/runtime/agent/orchestrator/session/loop/pre-dispatch-deny.mjs +6 -2
- package/src/runtime/agent/orchestrator/session/loop/route-fallback.mjs +5 -5
- package/src/runtime/agent/orchestrator/session/loop/send-phase.mjs +10 -0
- package/src/runtime/agent/orchestrator/session/loop/steering.mjs +3 -0
- package/src/runtime/agent/orchestrator/session/loop/tool-exec/before-hook.mjs +33 -8
- package/src/runtime/agent/orchestrator/session/loop/tool-exec/call-prep.mjs +5 -0
- package/src/runtime/agent/orchestrator/session/loop/tool-exec/dispatch.mjs +27 -0
- package/src/runtime/agent/orchestrator/session/loop/tool-exec.mjs +2 -1
- package/src/runtime/agent/orchestrator/session/loop/tool-exposure-migration.mjs +12 -0
- package/src/runtime/agent/orchestrator/session/loop/tool-helpers.mjs +11 -5
- package/src/runtime/agent/orchestrator/session/manager/agent-listing-notice.mjs +102 -0
- package/src/runtime/agent/orchestrator/session/manager/ask-queue.mjs +2 -0
- package/src/runtime/agent/orchestrator/session/manager/ask-session.mjs +13 -1
- package/src/runtime/agent/orchestrator/session/manager/ask-turn-auto-effort.mjs +13 -2
- package/src/runtime/agent/orchestrator/session/manager/ask-turn-execute.mjs +27 -14
- package/src/runtime/agent/orchestrator/session/manager/ask-turn-prepare.mjs +14 -0
- package/src/runtime/agent/orchestrator/session/manager/ask-turn-settle.mjs +4 -0
- package/src/runtime/agent/orchestrator/session/manager/ask-turn-steering-drain.mjs +1 -0
- package/src/runtime/agent/orchestrator/session/manager/compaction-runner.mjs +44 -18
- package/src/runtime/agent/orchestrator/session/manager/idle-cleanup.mjs +32 -1
- package/src/runtime/agent/orchestrator/session/manager/orchestration-mode-switch-notice.mjs +115 -0
- package/src/runtime/agent/orchestrator/session/manager/owner-route-fallbacks.mjs +42 -0
- package/src/runtime/agent/orchestrator/session/manager/pending-message-entry.mjs +6 -0
- package/src/runtime/agent/orchestrator/session/manager/pending-messages.mjs +3 -9
- package/src/runtime/agent/orchestrator/session/manager/pending-spool-file.mjs +1 -1
- package/src/runtime/agent/orchestrator/session/manager/pending-spool-shard.mjs +5 -3
- package/src/runtime/agent/orchestrator/session/manager/prompt-utils.mjs +36 -0
- package/src/runtime/agent/orchestrator/session/manager/rules-cache.mjs +40 -1
- package/src/runtime/agent/orchestrator/session/manager/runtime-liveness.mjs +12 -0
- package/src/runtime/agent/orchestrator/session/manager/runtime-state.mjs +20 -0
- package/src/runtime/agent/orchestrator/session/manager/session-crud.mjs +28 -9
- package/src/runtime/agent/orchestrator/session/manager/session-lifecycle.mjs +55 -27
- package/src/runtime/agent/orchestrator/session/manager/session-prompt-composition.mjs +4 -9
- package/src/runtime/agent/orchestrator/session/manager/session-record.mjs +14 -1
- package/src/runtime/agent/orchestrator/session/manager/session-tool-surface.mjs +7 -3
- package/src/runtime/agent/orchestrator/session/manager/turn-checkpoint-journal.mjs +6 -15
- package/src/runtime/agent/orchestrator/session/manager/turn-interruption.mjs +24 -8
- package/src/runtime/agent/orchestrator/session/manager/workflow-switch-notice.mjs +94 -13
- package/src/runtime/agent/orchestrator/session/pre-send-compact.mjs +52 -11
- package/src/runtime/agent/orchestrator/session/provider-prefix-guard.mjs +21 -3
- package/src/runtime/agent/orchestrator/session/routine-result.d.mts +18 -3
- package/src/runtime/agent/orchestrator/session/routine-result.mjs +126 -12
- package/src/runtime/agent/orchestrator/session/runtime-user-context.mjs +3 -1
- package/src/runtime/agent/orchestrator/session/send-with-recovery.mjs +16 -3
- package/src/runtime/agent/orchestrator/session/side-question-store.mjs +3 -1
- package/src/runtime/agent/orchestrator/session/store/canonical-reader.mjs +41 -7
- package/src/runtime/agent/orchestrator/session/store/lifecycle-barriers.mjs +10 -2
- package/src/runtime/agent/orchestrator/session/store/load-cache.mjs +14 -2
- package/src/runtime/agent/orchestrator/session/store/session-delete.mjs +15 -1
- package/src/runtime/agent/orchestrator/session/store/sweep/stale-sweep.mjs +5 -4
- package/src/runtime/agent/orchestrator/session/store/sweep/sub-agent-retention.mjs +140 -0
- package/src/runtime/agent/orchestrator/session/store/sweep/sweep-plan.mjs +5 -0
- package/src/runtime/agent/orchestrator/session/store/sweep/sweep-record.mjs +15 -5
- package/src/runtime/agent/orchestrator/session/store/sweep/sweep-row.mjs +49 -1
- package/src/runtime/agent/orchestrator/session/store-summary-catalog.mjs +80 -1
- package/src/runtime/agent/orchestrator/session/store-summary-index.mjs +40 -5
- package/src/runtime/agent/orchestrator/session/store-summary-last-message.mjs +47 -2
- package/src/runtime/agent/orchestrator/session/store-summary-reader.mjs +33 -1
- package/src/runtime/agent/orchestrator/session/store-summary-visibility.d.mts +5 -0
- package/src/runtime/agent/orchestrator/session/store-summary-visibility.mjs +37 -0
- package/src/runtime/agent/orchestrator/session/store.mjs +1 -0
- package/src/runtime/agent/orchestrator/session/tail-reminder-rules.mjs +125 -0
- package/src/runtime/agent/orchestrator/session/tool-batch/finalize.mjs +4 -0
- package/src/runtime/agent/orchestrator/session/tool-result-offload.mjs +78 -43
- package/src/runtime/agent/orchestrator/session/transcript-restore/restore.mjs +35 -5
- package/src/runtime/agent/orchestrator/session/transcript-restore/user-text.mjs +42 -2
- package/src/runtime/agent/orchestrator/tools/builtin/advisory-lock.mjs +47 -14
- package/src/runtime/agent/orchestrator/tools/builtin/atomic-write.mjs +25 -5
- package/src/runtime/agent/orchestrator/tools/builtin/bash-tool/backgrounded-result.mjs +6 -3
- package/src/runtime/agent/orchestrator/tools/builtin/bash-tool/completed-result.mjs +13 -14
- package/src/runtime/agent/orchestrator/tools/builtin/bash-tool/transport-artifacts.mjs +9 -0
- package/src/runtime/agent/orchestrator/tools/builtin/bash-tool.mjs +19 -14
- package/src/runtime/agent/orchestrator/tools/builtin/builtin-tools.mjs +7 -1
- package/src/runtime/agent/orchestrator/tools/builtin/git-command-tool.mjs +127 -20
- package/src/runtime/agent/orchestrator/tools/builtin/git-worktree-tool.mjs +557 -0
- package/src/runtime/agent/orchestrator/tools/builtin/lib/shell-job-records.mjs +10 -12
- package/src/runtime/agent/orchestrator/tools/builtin/pdf-test-fixtures.mjs +5 -3
- package/src/runtime/agent/orchestrator/tools/builtin/read-image.mjs +4 -2
- package/src/runtime/agent/orchestrator/tools/builtin/read-single-media.mjs +47 -2
- package/src/runtime/agent/orchestrator/tools/builtin/read-special-files.mjs +264 -87
- package/src/runtime/agent/orchestrator/tools/builtin/shell-analysis.mjs +10 -4
- package/src/runtime/agent/orchestrator/tools/builtin/shell-jobs.mjs +42 -23
- package/src/runtime/agent/orchestrator/tools/code-graph/constants.mjs +8 -4
- package/src/runtime/agent/orchestrator/tools/lib/shell-descendants.mjs +9 -30
- package/src/runtime/agent/orchestrator/tools/lib/shell-run-settle.mjs +24 -12
- package/src/runtime/agent/orchestrator/tools/lib/shell-run-spawn.mjs +3 -2
- package/src/runtime/agent/orchestrator/tools/lib/shell-run-state.mjs +6 -2
- package/src/runtime/agent/orchestrator/tools/patch/delete-quarantine.mjs +2 -1
- package/src/runtime/agent/orchestrator/tools/patch/replay-capture.mjs +4 -2
- package/src/runtime/agent/orchestrator/tools/patch/rollback-state.mjs +33 -2
- package/src/runtime/agent/orchestrator/tools/patch-manifest.json +13 -13
- package/src/runtime/agent/orchestrator/tools/shell-command.mjs +5 -3
- package/src/runtime/agent/orchestrator/tools/shell-exec-output.mjs +3 -1
- package/src/runtime/attachments/attachment-reference.mjs +155 -0
- package/src/runtime/attachments/legacy-office.mjs +110 -0
- package/src/runtime/attachments/limits.mjs +5 -1
- package/src/runtime/attachments/office-extract.mjs +3 -6
- package/src/runtime/attachments/pdf-extract.mjs +84 -10
- package/src/runtime/attachments/prompt-restore.mjs +70 -0
- package/src/runtime/attachments/store.mjs +109 -33
- package/src/runtime/browser-bridge/client.mjs +73 -20
- package/src/runtime/channels/lib/runtime-paths.mjs +1 -1
- package/src/runtime/channels/lib/scheduler.mjs +14 -7
- package/src/runtime/channels/lib/state-file.mjs +15 -15
- package/src/runtime/channels/lib/voice-runtime-fetcher.mjs +14 -13
- package/src/runtime/channels/lib/webhook/relay-tunnel.mjs +22 -18
- package/src/runtime/channels/lib/webhook/session-dispatch.mjs +14 -36
- package/src/runtime/channels/lib/worker-main.mjs +3 -2
- package/src/runtime/effort-judge/compact-tokenizer.mjs +8 -2
- package/src/runtime/effort-judge/judge-client.mjs +66 -16
- package/src/runtime/effort-judge/model-install.mjs +47 -7
- package/src/runtime/local-provider/asset-installer.mjs +10 -4
- package/src/runtime/local-provider/catalog.mjs +5 -2
- package/src/runtime/local-provider/registered-models.mjs +64 -27
- package/src/runtime/local-provider/server.mjs +2 -3
- package/src/runtime/media/adapters/image-api.mjs +89 -0
- package/src/runtime/media/auth.mjs +64 -1
- package/src/runtime/media/catalog.mjs +66 -4
- package/src/runtime/media/jobs.mjs +4 -0
- package/src/runtime/media/lanes.mjs +46 -5
- package/src/runtime/media/media-usage.mjs +17 -0
- package/src/runtime/media/renditions.mjs +15 -15
- package/src/runtime/media/store.mjs +36 -35
- package/src/runtime/media/tool.mjs +8 -1
- package/src/runtime/memory/lib/cycle-scheduler/health-ledger.mjs +8 -2
- package/src/runtime/memory/lib/embedding-model-files.mjs +30 -7
- package/src/runtime/memory/lib/ingest/transcript-tail.mjs +16 -7
- package/src/runtime/memory/lib/memory-chunk-quality.mjs +14 -2
- package/src/runtime/memory/lib/pg/process.mjs +8 -4
- package/src/runtime/memory/lib/project-id-resolver.mjs +5 -1
- package/src/runtime/memory/lib/runtime-fetcher.mjs +6 -1
- package/src/runtime/office/core/journal.mjs +7 -14
- package/src/runtime/office/design/library/design-library-core.mjs +4 -16
- package/src/runtime/office/pdf/ocr-engine-worker.mjs +37 -0
- package/src/runtime/office/pdf/ocr-engine.mjs +146 -0
- package/src/runtime/office/pdf/pdf-ocr.mjs +51 -21
- package/src/runtime/office/pdf/pdf-render.mjs +30 -18
- package/src/runtime/office/portable/ooxml-validator.mjs +2 -2
- package/src/runtime/office/portable/portable-soffice.mjs +135 -29
- package/src/runtime/shared/agent-avatar-sprites.mjs +23 -8
- package/src/runtime/shared/agent-avatar.mjs +3 -2
- package/src/runtime/shared/agent-kind-migration.mjs +200 -0
- package/src/runtime/shared/agent-message-envelope.d.mts +6 -0
- package/src/runtime/shared/agent-message-envelope.mjs +44 -0
- package/src/runtime/shared/agent-reap-state.d.mts +4 -0
- package/src/runtime/shared/agent-room-host.d.mts +31 -0
- package/src/runtime/shared/agent-room-host.mjs +58 -0
- package/src/runtime/shared/agent-store.mjs +24 -7
- package/src/runtime/shared/agent-task-starts.mjs +27 -0
- package/src/runtime/shared/atomic-file.d.mts +50 -0
- package/src/runtime/shared/atomic-file.mjs +394 -132
- package/src/runtime/shared/background-tasks.mjs +7 -4
- package/src/runtime/shared/bot-hops.mjs +35 -0
- package/src/runtime/shared/bot-split-migration.mjs +1 -1
- package/src/runtime/shared/bounded-download.mjs +4 -2
- package/src/runtime/shared/config.mjs +39 -9
- package/src/runtime/shared/defaults-separation.mjs +6 -2
- package/src/runtime/shared/delegated-approval-host.mjs +74 -0
- package/src/runtime/shared/err-text.mjs +4 -0
- package/src/runtime/shared/error-presentation.mjs +22 -4
- package/src/runtime/shared/file-has-data.mjs +28 -0
- package/src/runtime/shared/llm/usage-ledger-import.mjs +37 -18
- package/src/runtime/shared/pending-agent-work.mjs +54 -0
- package/src/runtime/shared/process-lifecycle.mjs +8 -12
- package/src/runtime/shared/projects.mjs +50 -41
- package/src/runtime/shared/provider-accounts.mjs +17 -12
- package/src/runtime/shared/redact-directories.mjs +18 -0
- package/src/runtime/shared/remove-directory.mjs +28 -0
- package/src/runtime/shared/routine-trigger.d.mts +6 -1
- package/src/runtime/shared/routine-trigger.mjs +14 -6
- package/src/runtime/shared/run-child.d.mts +3 -0
- package/src/runtime/shared/run-child.mjs +24 -3
- package/src/runtime/shared/session-baseline-store.mjs +8 -2
- package/src/runtime/shared/session-sidebar-host.d.mts +11 -2
- package/src/runtime/shared/session-sidebar-host.mjs +16 -3
- package/src/runtime/shared/singleton-owner.mjs +7 -6
- package/src/runtime/shared/skill-tool-dependencies.mjs +4 -10
- package/src/runtime/shared/staged-update.mjs +4 -2
- package/src/runtime/shared/statusline/statusline-segments.mjs +4 -15
- package/src/runtime/shared/tool-execution-contract.mjs +10 -0
- package/src/runtime/shared/tool-work-units.mjs +12 -9
- package/src/runtime/shared/transcript-metadata.mjs +28 -3
- package/src/runtime/shared/transcript-writer/session-record.mjs +7 -6
- package/src/runtime/shared/turn-snapshot-store.mjs +5 -4
- package/src/runtime/shared/turn-snapshot.mjs +277 -124
- package/src/runtime/shared/user-cwd.mjs +9 -4
- package/src/runtime/shared/user-data-guard.mjs +92 -61
- package/src/runtime/shared/utf8-cut.mjs +10 -0
- package/src/runtime/shared/worktrees/activity-gate.mjs +251 -61
- package/src/runtime/shared/worktrees/contract.mjs +11 -3
- package/src/runtime/shared/worktrees/maintenance.mjs +11 -2
- package/src/runtime/shared/worktrees/manager.mjs +419 -52
- package/src/runtime/shared/worktrees/registry.mjs +29 -27
- package/src/runtime/shared/worktrees/tool-host.mjs +39 -0
- package/src/runtime/tidy/core-install.mjs +26 -4
- package/src/runtime/tidy/install.mjs +91 -23
- package/src/runtime/tidy/process.mjs +41 -8
- package/src/runtime/tidy/resolve.mjs +10 -7
- package/src/runtime/web-search/lib/config.mjs +4 -16
- package/src/session-runtime/agent-home-delivery.mjs +542 -204
- package/src/session-runtime/agent-profile-changes.mjs +16 -0
- package/src/session-runtime/agent-room-delivery.mjs +81 -35
- package/src/session-runtime/agent-task-sessions.mjs +811 -179
- package/src/session-runtime/agent-tool-routing.mjs +1 -0
- package/src/session-runtime/boot/apis.mjs +48 -24
- package/src/session-runtime/boot/config.mjs +1 -0
- package/src/session-runtime/boot/facade.mjs +16 -7
- package/src/session-runtime/boot/infrastructure.mjs +14 -2
- package/src/session-runtime/boot/providers.mjs +2 -2
- package/src/session-runtime/boot/shared.mjs +12 -6
- package/src/session-runtime/boot/tools.mjs +23 -3
- package/src/session-runtime/bot-status.mjs +432 -0
- package/src/session-runtime/config-lifecycle/lead-route-sync.mjs +29 -0
- package/src/session-runtime/config-lifecycle.mjs +60 -55
- package/src/session-runtime/cwd-plugins/core-memory-context.mjs +2 -4
- package/src/session-runtime/held-requests.mjs +31 -33
- package/src/session-runtime/internal-tool-executor/bridge-tools.mjs +10 -1
- package/src/session-runtime/internal-tool-executor/feature-tools.mjs +2 -2
- package/src/session-runtime/internal-tool-executor/workspace-tools.mjs +24 -26
- package/src/session-runtime/lifecycle/session-catalog.mjs +12 -2
- package/src/session-runtime/lifecycle/teardown.mjs +11 -3
- package/src/session-runtime/mcp-glue/connect.mjs +100 -186
- package/src/session-runtime/mcp-glue/initial-connect-gate.mjs +57 -0
- package/src/session-runtime/mcp-glue/server-connection.mjs +71 -0
- package/src/session-runtime/media-api.mjs +5 -2
- package/src/session-runtime/model-recency.mjs +1 -0
- package/src/session-runtime/model-route/route-tuning.mjs +4 -1
- package/src/session-runtime/model-route/set-route.mjs +61 -1
- package/src/session-runtime/owned-automation-run.mjs +8 -1
- package/src/session-runtime/provider-models/row-hydration.mjs +8 -1
- package/src/session-runtime/quick-model-rows/model-meta.mjs +2 -0
- package/src/session-runtime/quick-model-rows/provider-rows.mjs +7 -1
- package/src/session-runtime/route-resolution.mjs +16 -8
- package/src/session-runtime/route-state.mjs +1 -59
- package/src/session-runtime/routine-home-turn.mjs +5 -3
- package/src/session-runtime/runtime-bootstrap.mjs +5 -3
- package/src/session-runtime/runtime-paths.mjs +11 -5
- package/src/session-runtime/runtime-review-api.mjs +4 -1
- package/src/session-runtime/runtime-tunables.mjs +6 -0
- package/src/session-runtime/services/agent-tool/close-flow/close-all.mjs +11 -2
- package/src/session-runtime/services/agent-tool/close-flow.mjs +4 -2
- package/src/session-runtime/services/agent-tool/execute-send.mjs +1 -0
- package/src/session-runtime/services/agent-tool/execute-spawn.mjs +1 -0
- package/src/session-runtime/services/agent-tool/job-views/agent-list.mjs +34 -19
- package/src/session-runtime/services/agent-tool/restart-notify.mjs +10 -0
- package/src/session-runtime/services/agent-tool/send-flow.mjs +25 -21
- package/src/session-runtime/services/agent-tool/spawn-flow/job-start.mjs +7 -0
- package/src/session-runtime/services/agent-tool/spawn-flow/spawn-prep.mjs +6 -3
- package/src/session-runtime/services/agent-tool/spawn-flow/spawn-run.mjs +8 -5
- package/src/session-runtime/services/agent-tool/worker-index/row-liveness.mjs +22 -2
- package/src/session-runtime/services/agent-tool/worker-index.mjs +4 -6
- package/src/session-runtime/services/agent-tool.mjs +6 -0
- package/src/session-runtime/services/daemon-crash-capture/daemon-exit-record.mjs +1 -1
- package/src/session-runtime/services/daemon-crash-capture/retention.mjs +4 -3
- package/src/session-runtime/services/hook-bus/config-loader.mjs +14 -5
- package/src/session-runtime/services/hook-bus/rule-file.mjs +49 -11
- package/src/session-runtime/services/plugin-admin.mjs +26 -26
- package/src/session-runtime/services/provider-admin.mjs +26 -6
- package/src/session-runtime/services/schedule-access.mjs +33 -4
- package/src/session-runtime/services/seeds.mjs +4 -3
- package/src/session-runtime/services/usage-dashboard.mjs +1 -1
- package/src/session-runtime/services/usage-stats-model.mjs +2 -1
- package/src/session-runtime/services/worktree-access.mjs +27 -0
- package/src/session-runtime/session-create/materialize.mjs +2 -3
- package/src/session-runtime/session-create/session-options.mjs +15 -2
- package/src/session-runtime/session-hooks.mjs +9 -17
- package/src/session-runtime/settings-builtin-tools-api.mjs +65 -1
- package/src/session-runtime/settings-compaction-api.mjs +86 -39
- package/src/session-runtime/setup-tool/executor.mjs +12 -2
- package/src/session-runtime/setup-tool/settings-contract.mjs +34 -1
- package/src/session-runtime/setup-tool/tool-defs.mjs +20 -3
- package/src/session-runtime/skills-api.mjs +12 -5
- package/src/session-runtime/tool-policy/agent-profile-fold.mjs +349 -0
- package/src/session-runtime/tool-policy/mode-switch.mjs +54 -0
- package/src/session-runtime/tool-policy/pending-switches.mjs +59 -0
- package/src/session-runtime/tool-policy/policy-stage.mjs +183 -0
- package/src/session-runtime/tool-policy/session-switches.mjs +169 -0
- package/src/session-runtime/tool-policy/surface-helpers.mjs +114 -0
- package/src/session-runtime/tool-policy/workflow-overlay.mjs +145 -0
- package/src/session-runtime/tool-policy-refresh.mjs +48 -369
- package/src/session-runtime/tool-surface.mjs +10 -4
- package/src/session-runtime/turn/session-ops.mjs +8 -0
- package/src/session-runtime/turn/turn-run/turn-prepare.mjs +95 -88
- package/src/session-runtime/turn/turn-run/turn-timing.mjs +4 -3
- package/src/session-runtime/turn/turn-run.mjs +7 -2
- package/src/session-runtime/workflow-agents-api/agent-connectors.mjs +54 -7
- package/src/session-runtime/workflow-agents-api/agent-editor.mjs +527 -333
- package/src/session-runtime/workflow-agents-api/agent-route.mjs +85 -63
- package/src/session-runtime/workflow-agents-api/shared.mjs +5 -1
- package/src/session-runtime/workflow-agents-api/style-and-mode.mjs +14 -1
- package/src/session-runtime/workflow-agents-api/workflow-packs.mjs +6 -3
- package/src/session-runtime/workspace/transition-steps.mjs +1 -18
- package/src/session-runtime/workspace/transitions.mjs +279 -67
- package/src/session-runtime/workspace/workspace-state.mjs +7 -6
- package/src/session-runtime/workspace/worktree-tool.mjs +194 -0
- package/src/standalone/daemon-agent-control.mjs +4 -1
- package/src/standalone/daemon-lag-profiler.mjs +3 -1
- package/src/standalone/daemon-session-control.mjs +173 -15
- package/src/standalone/daemon-telemetry.mjs +9 -0
- package/src/standalone/daemon.mjs +33 -10
- package/src/standalone/local-session-runtime.mjs +33 -16
- package/src/standalone/session-client.mjs +7 -2
- package/src/standalone/session-protocol.mjs +8 -0
- package/src/standalone/session-runtime-inline-host/boot-loaders.mjs +2 -0
- package/src/standalone/session-service/agent-route-follow.mjs +69 -0
- package/src/standalone/session-service/session-calls/session-turns.mjs +33 -4
- package/src/standalone/session-service.mjs +21 -0
- package/src/standalone/session-state-projection.mjs +19 -9
- package/src/standalone/session-tool-output.mjs +36 -9
- package/src/standalone/staged-import.mjs +47 -0
- package/src/tui/App.jsx +2 -0
- package/src/tui/app/maintenance-pickers/auto-clear-picker.mjs +19 -13
- package/src/tui/app/message-selector.mjs +15 -2
- package/src/tui/app/model-options.mjs +16 -2
- package/src/tui/app/model-picker.mjs +2 -1
- package/src/tui/app/route-pickers.mjs +3 -1
- package/src/tui/app/settings-picker/settings-rows.mjs +2 -2
- package/src/tui/app/slash-commands.mjs +1 -1
- package/src/tui/app/usage-context-panels/context-model.mjs +8 -2
- package/src/tui/dist/index.mjs +617 -369
- package/src/tui/paste-attachments.mjs +0 -1
- package/src/tui/paste-text-policy.mjs +4 -5
- package/src/tui/prompt-history-store.mjs +16 -21
- package/src/tui/session/agent-job-feed/execution-delivery.mjs +4 -1
- package/src/tui/session/context-state.mjs +4 -0
- package/src/tui/session/notice-surface.mjs +3 -0
- package/src/tui/session/session-api/intake/abort.mjs +52 -18
- package/src/tui/session/session-api/intake/rewind.mjs +39 -4
- package/src/tui/session/session-api/media.mjs +2 -1
- package/src/tui/session/session-api/routes-agent-definitions.mjs +76 -0
- package/src/tui/session/session-api/routes-write.mjs +203 -0
- package/src/tui/session/session-api/routes.mjs +23 -247
- package/src/tui/session/session-api/settings.mjs +14 -1
- package/src/tui/session/session-flow/drain.mjs +28 -3
- package/src/tui/session/session-flow/failed-turn-rewind.mjs +4 -2
- package/src/tui/session/session-flow/queue/take-entries.mjs +8 -0
- package/src/tui/session/session-flow/steering.mjs +8 -3
- package/src/tui/session/tool-approval.mjs +142 -14
- package/src/tui/session/transcript-intake.mjs +2 -0
- package/src/tui/session/turn.mjs +23 -14
- package/src/tui/transcript-tool-failures.mjs +8 -2
- package/src/vendor/statusline/src/gateway/route-meta.mjs +7 -8
- package/src/vendor/statusline/src/gateway/session-routes.mjs +6 -7
- package/src/rules/agent-mode/ROUTINE.md +0 -6
- package/src/session-runtime/room-member-gate.mjs +0 -48
|
@@ -8,12 +8,18 @@
|
|
|
8
8
|
// effort. Decisions are logged locally (lengths and probabilities only, never
|
|
9
9
|
// the request text).
|
|
10
10
|
import { Worker } from 'node:worker_threads';
|
|
11
|
-
import { appendFileSync,
|
|
11
|
+
import { appendFileSync, mkdirSync, readFileSync, statSync } from 'node:fs';
|
|
12
12
|
import { dirname, join } from 'node:path';
|
|
13
13
|
import { fileURLToPath } from 'node:url';
|
|
14
14
|
import { resolvePluginData } from '../shared/plugin-paths.mjs';
|
|
15
15
|
import { onnxRuntimeSupported } from '../shared/onnx-runtime-support.mjs';
|
|
16
|
-
import {
|
|
16
|
+
import { fileHasData } from '../shared/file-has-data.mjs';
|
|
17
|
+
import {
|
|
18
|
+
effortJudgeInstallCurrent,
|
|
19
|
+
effortJudgeInstallStamped,
|
|
20
|
+
installEffortJudgeModel,
|
|
21
|
+
invalidateDamagedEffortJudgeInstall,
|
|
22
|
+
} from './model-install.mjs';
|
|
17
23
|
|
|
18
24
|
export const EFFORT_JUDGE_UNSUPPORTED = 'Auto effort is unsupported on darwin-x64 (no onnxruntime-node binding)';
|
|
19
25
|
const WORKER_PATH = fileURLToPath(new URL('./judge-worker.mjs', import.meta.url));
|
|
@@ -86,7 +92,9 @@ export function effortJudgeModelDir() {
|
|
|
86
92
|
// it is. Unstamped leftovers (hand-copied packs) never load.
|
|
87
93
|
export function effortJudgeAvailable(dir = effortJudgeModelDir()) {
|
|
88
94
|
if (!onnxRuntimeSupported()) return false;
|
|
89
|
-
|
|
95
|
+
// A model file a crash left empty or NUL-filled is not installed: the refresh
|
|
96
|
+
// then re-verifies the folder against the manifest hashes and fetches it again.
|
|
97
|
+
if (!MODEL_FILES.every((file) => fileHasData(join(dir, file)))) return false;
|
|
90
98
|
return Boolean(process.env.MIXDOG_EFFORT_JUDGE_DIR) || effortJudgeInstallStamped(dir);
|
|
91
99
|
}
|
|
92
100
|
|
|
@@ -136,6 +144,7 @@ function ensureWorker(dir) {
|
|
|
136
144
|
lastLoadError = msg.message;
|
|
137
145
|
lastLoadErrorAt = Date.now();
|
|
138
146
|
notifyReady(false);
|
|
147
|
+
void repairAfterLoadError(dir);
|
|
139
148
|
} else if (msg.type === 'result') {
|
|
140
149
|
settle(msg.id, (entry) => entry.resolve(msg.probs));
|
|
141
150
|
} else if (msg.type === 'error') {
|
|
@@ -201,6 +210,49 @@ function request(target, { request: text, prev, prevRequest, step }) {
|
|
|
201
210
|
// One install at a time (the settings install and the boot refresh can overlap).
|
|
202
211
|
let installing = null;
|
|
203
212
|
let refreshChecked = false;
|
|
213
|
+
let verifying = false;
|
|
214
|
+
|
|
215
|
+
/** Why the judge last failed to load; '' when it loaded or never failed. */
|
|
216
|
+
export function effortJudgeLastError() {
|
|
217
|
+
return lastLoadError;
|
|
218
|
+
}
|
|
219
|
+
|
|
220
|
+
// Runs `task` as the one install: the slot stays taken until it settles, so
|
|
221
|
+
// installs, refreshes and repairs never overlap.
|
|
222
|
+
function occupyInstall(task) {
|
|
223
|
+
const run = task().finally(() => {
|
|
224
|
+
if (installing === run) installing = null;
|
|
225
|
+
});
|
|
226
|
+
installing = run;
|
|
227
|
+
return run;
|
|
228
|
+
}
|
|
229
|
+
|
|
230
|
+
// A model that fails to load may be damaged on disk (a crash can leave a
|
|
231
|
+
// stamped file with a zeroed tail). Re-verify the hashes once per load error,
|
|
232
|
+
// off the turn's path: a mismatch drops the stamp and re-downloads only the bad
|
|
233
|
+
// files; matching hashes keep the plain LOAD_RETRY_MS back-off. Verifying and
|
|
234
|
+
// invalidating hold the install slot, after any install in flight has settled,
|
|
235
|
+
// so the stamp they remove is never one an installer just wrote.
|
|
236
|
+
async function repairAfterLoadError(dir) {
|
|
237
|
+
if (verifying || process.env.MIXDOG_EFFORT_JUDGE_DIR || !onnxRuntimeSupported()) return;
|
|
238
|
+
verifying = true;
|
|
239
|
+
try {
|
|
240
|
+
while (installing) await installing.catch(() => {});
|
|
241
|
+
await occupyInstall(async () => {
|
|
242
|
+
const bad = await invalidateDamagedEffortJudgeInstall(dir);
|
|
243
|
+
if (!bad.length) return;
|
|
244
|
+
process.stderr.write(`[effort-judge] reinstalling damaged model files: ${bad.join(', ')}\n`);
|
|
245
|
+
await runInstall();
|
|
246
|
+
// The files are replaced: the next turn loads them without the back-off.
|
|
247
|
+
lastLoadErrorAt = 0;
|
|
248
|
+
lastLoadError = '';
|
|
249
|
+
});
|
|
250
|
+
} catch (error) {
|
|
251
|
+
process.stderr.write(`[effort-judge] model repair failed: ${error?.message || error}\n`);
|
|
252
|
+
} finally {
|
|
253
|
+
verifying = false;
|
|
254
|
+
}
|
|
255
|
+
}
|
|
204
256
|
|
|
205
257
|
/**
|
|
206
258
|
* Installs or updates the judge model from the release the bundled manifest
|
|
@@ -210,19 +262,17 @@ let refreshChecked = false;
|
|
|
210
262
|
*/
|
|
211
263
|
export function installEffortJudge() {
|
|
212
264
|
if (!onnxRuntimeSupported()) return Promise.reject(new Error(EFFORT_JUDGE_UNSUPPORTED));
|
|
213
|
-
installing
|
|
214
|
-
|
|
215
|
-
|
|
216
|
-
|
|
217
|
-
|
|
218
|
-
|
|
219
|
-
}
|
|
220
|
-
|
|
221
|
-
|
|
222
|
-
|
|
223
|
-
|
|
224
|
-
});
|
|
225
|
-
return installing;
|
|
265
|
+
return installing ?? occupyInstall(runInstall);
|
|
266
|
+
}
|
|
267
|
+
|
|
268
|
+
async function runInstall() {
|
|
269
|
+
const dir = effortJudgeModelDir();
|
|
270
|
+
if (process.env.MIXDOG_EFFORT_JUDGE_DIR || (effortJudgeAvailable(dir) && effortJudgeInstallCurrent(dir))) {
|
|
271
|
+
if (!effortJudgeAvailable(dir)) throw new Error(`The Auto reasoning model is not installed (expected in ${dir}).`);
|
|
272
|
+
return;
|
|
273
|
+
}
|
|
274
|
+
await installEffortJudgeModel(dir);
|
|
275
|
+
if (worker) await shutdownEffortJudge();
|
|
226
276
|
}
|
|
227
277
|
|
|
228
278
|
/**
|
|
@@ -4,9 +4,11 @@
|
|
|
4
4
|
// download never replaces a working file. Files that already match are kept.
|
|
5
5
|
// install.json records the installed release; a newer bundled manifest makes
|
|
6
6
|
// the install stale and the next refresh downloads only what changed.
|
|
7
|
-
import { existsSync, mkdirSync, readFileSync, renameSync, rmSync
|
|
7
|
+
import { existsSync, mkdirSync, readFileSync, renameSync, rmSync } from 'node:fs';
|
|
8
|
+
import { open as openAsync } from 'node:fs/promises';
|
|
8
9
|
import { join } from 'node:path';
|
|
9
10
|
import { fileURLToPath } from 'node:url';
|
|
11
|
+
import { isFileFsyncUnsupported, parseJsonText, writeJsonAtomicSync } from '../shared/atomic-file.mjs';
|
|
10
12
|
import { downloadToFileWithRetry } from '../shared/bounded-download.mjs';
|
|
11
13
|
import { sha256File, validSha256 } from '../shared/native-asset.mjs';
|
|
12
14
|
|
|
@@ -30,24 +32,61 @@ export function effortJudgeManifest() {
|
|
|
30
32
|
return manifest;
|
|
31
33
|
}
|
|
32
34
|
|
|
35
|
+
// Release named by the stamp; '' when it is missing or damaged (empty,
|
|
36
|
+
// NUL-filled), so a damaged stamp reads as not installed and the next refresh
|
|
37
|
+
// re-verifies every file against its sha256 and stamps again.
|
|
38
|
+
function stampedRelease(dir) {
|
|
39
|
+
try {
|
|
40
|
+
return String(parseJsonText(readFileSync(join(dir, STAMP), 'utf8')).release || '');
|
|
41
|
+
} catch {
|
|
42
|
+
return '';
|
|
43
|
+
}
|
|
44
|
+
}
|
|
45
|
+
|
|
33
46
|
/** True when `dir` holds a complete install of some release (stamped). */
|
|
34
47
|
export function effortJudgeInstallStamped(dir) {
|
|
35
|
-
return
|
|
48
|
+
return stampedRelease(dir) !== '';
|
|
36
49
|
}
|
|
37
50
|
|
|
38
51
|
/** True when `dir` holds the release the manifest names. */
|
|
39
52
|
export function effortJudgeInstallCurrent(dir, manifest = effortJudgeManifest()) {
|
|
53
|
+
return stampedRelease(dir) === manifest.release;
|
|
54
|
+
}
|
|
55
|
+
|
|
56
|
+
/**
|
|
57
|
+
* Files in `dir` that are missing or differ from the manifest sha256. When any
|
|
58
|
+
* does, the stamp is removed so the folder reads as not installed and the next
|
|
59
|
+
* install re-downloads only those files (a crash can leave a stamped model with
|
|
60
|
+
* its tail zeroed: same size, wrong hash).
|
|
61
|
+
*/
|
|
62
|
+
export async function invalidateDamagedEffortJudgeInstall(dir, manifest = effortJudgeManifest()) {
|
|
63
|
+
const bad = [];
|
|
64
|
+
for (const [name, file] of Object.entries(manifest.files)) {
|
|
65
|
+
const target = join(dir, name);
|
|
66
|
+
if (!existsSync(target) || (await sha256File(target)) !== file.sha256) bad.push(name);
|
|
67
|
+
}
|
|
68
|
+
if (bad.length) rmSync(join(dir, STAMP), { force: true });
|
|
69
|
+
return bad;
|
|
70
|
+
}
|
|
71
|
+
|
|
72
|
+
// Flushes the downloaded file through a write handle (Windows refuses fsync on
|
|
73
|
+
// a read-only one) so a crash right after the rename cannot publish a file
|
|
74
|
+
// whose size is committed but whose tail never reached the disk.
|
|
75
|
+
export async function fsyncFile(path, open = openAsync) {
|
|
76
|
+
const handle = await open(path, 'r+');
|
|
40
77
|
try {
|
|
41
|
-
|
|
42
|
-
} catch {
|
|
43
|
-
|
|
78
|
+
await handle.sync();
|
|
79
|
+
} catch (err) {
|
|
80
|
+
if (!isFileFsyncUnsupported(err)) throw err;
|
|
81
|
+
} finally {
|
|
82
|
+
await handle.close();
|
|
44
83
|
}
|
|
45
84
|
}
|
|
46
85
|
|
|
47
86
|
/** Download what is missing or different, then stamp the release. */
|
|
48
87
|
export async function installEffortJudgeModel(
|
|
49
88
|
dir,
|
|
50
|
-
{ fetchFn = globalThis.fetch, manifest = effortJudgeManifest() } = {}
|
|
89
|
+
{ fetchFn = globalThis.fetch, manifest = effortJudgeManifest(), syncFile = fsyncFile } = {}
|
|
51
90
|
) {
|
|
52
91
|
mkdirSync(dir, { recursive: true });
|
|
53
92
|
let replaced = false;
|
|
@@ -65,6 +104,7 @@ export async function installEffortJudgeModel(
|
|
|
65
104
|
const actual = await sha256File(partial);
|
|
66
105
|
if (actual !== file.sha256)
|
|
67
106
|
throw new Error(`effort judge ${name}: sha256 mismatch (expected ${file.sha256}, got ${actual})`);
|
|
107
|
+
await syncFile(partial);
|
|
68
108
|
// From the first replaced file until the new stamp, the folder is a mix of
|
|
69
109
|
// releases: without a stamp it reads as not installed, so an interrupted
|
|
70
110
|
// install is never loaded.
|
|
@@ -76,5 +116,5 @@ export async function installEffortJudgeModel(
|
|
|
76
116
|
}
|
|
77
117
|
}
|
|
78
118
|
if (replaced) for (const name of DERIVED) rmSync(join(dir, name), { force: true });
|
|
79
|
-
|
|
119
|
+
writeJsonAtomicSync(join(dir, STAMP), { release: manifest.release }, { compact: true });
|
|
80
120
|
}
|
|
@@ -4,6 +4,7 @@ import { dirname, join } from 'node:path';
|
|
|
4
4
|
import { Readable, Transform } from 'node:stream';
|
|
5
5
|
import { pipeline } from 'node:stream/promises';
|
|
6
6
|
|
|
7
|
+
import { fileHasData } from '../shared/file-has-data.mjs';
|
|
7
8
|
import { sha256File } from '../shared/native-asset.mjs';
|
|
8
9
|
import { resolvePluginData } from '../shared/plugin-paths.mjs';
|
|
9
10
|
import {
|
|
@@ -125,9 +126,14 @@ export async function downloadVerifiedLocalAsset(
|
|
|
125
126
|
callback(null, chunk);
|
|
126
127
|
},
|
|
127
128
|
});
|
|
128
|
-
await pipeline(
|
|
129
|
-
|
|
130
|
-
|
|
129
|
+
await pipeline(
|
|
130
|
+
Readable.fromWeb(response.body),
|
|
131
|
+
meter,
|
|
132
|
+
createWriteStream(partial, { flags: resumed ? 'a' : 'w', flush: true }),
|
|
133
|
+
{
|
|
134
|
+
signal: operationSignal,
|
|
135
|
+
}
|
|
136
|
+
);
|
|
131
137
|
if (total !== asset.size) {
|
|
132
138
|
throw new Error(`[local-provider] incomplete download for ${asset.name || destination}: ${total}/${asset.size}`);
|
|
133
139
|
}
|
|
@@ -198,7 +204,7 @@ async function installRuntimeInternal({ dataDir, onProgress, fetchFn, signal })
|
|
|
198
204
|
}
|
|
199
205
|
const target = localProviderRuntimeDirectory(dataDir);
|
|
200
206
|
const executable = join(target, entry.executable);
|
|
201
|
-
if (
|
|
207
|
+
if (fileHasData(executable)) return localProviderCatalogStatus({ dataDir, hardware });
|
|
202
208
|
ensureDiskSpace(localProviderRuntimeRoot(dataDir), entry.downloadBytes);
|
|
203
209
|
const staging = join(localProviderRuntimeRoot(dataDir), `.staging-${process.pid}`);
|
|
204
210
|
const downloads = join(localProviderRuntimeRoot(dataDir), '.downloads');
|
|
@@ -2,6 +2,7 @@ import { existsSync, readFileSync, statSync } from 'node:fs';
|
|
|
2
2
|
import { join } from 'node:path';
|
|
3
3
|
import { fileURLToPath } from 'node:url';
|
|
4
4
|
|
|
5
|
+
import { fileHasData } from '../shared/file-has-data.mjs';
|
|
5
6
|
import { resolvePluginData } from '../shared/plugin-paths.mjs';
|
|
6
7
|
import { localProviderDiskStatus, partialAssetBytes } from './asset-storage.mjs';
|
|
7
8
|
import { localProviderHardwareStatus, localProviderPlatformKey } from './hardware.mjs';
|
|
@@ -54,7 +55,9 @@ export function localProviderModelPath(entry, dataDir = resolvePluginData()) {
|
|
|
54
55
|
export function exactLocalProviderFile(path, size) {
|
|
55
56
|
try {
|
|
56
57
|
const stat = statSync(path);
|
|
57
|
-
|
|
58
|
+
// A crash can commit the size of a renamed download without its bytes: a
|
|
59
|
+
// file whose head is all NUL is not installed (it is fetched again).
|
|
60
|
+
return stat.isFile() && stat.size === size && fileHasData(path);
|
|
58
61
|
} catch {
|
|
59
62
|
return false;
|
|
60
63
|
}
|
|
@@ -108,7 +111,7 @@ export function localProviderCatalogStatus({
|
|
|
108
111
|
platformSupported: Boolean(platformEntry),
|
|
109
112
|
available: Boolean(platformEntry && hardware?.supported && models.some((entry) => entry.compatible)),
|
|
110
113
|
runtime: {
|
|
111
|
-
installed: Boolean(executable &&
|
|
114
|
+
installed: Boolean(executable && fileHasData(executable)),
|
|
112
115
|
version: LOCAL_PROVIDER_MANIFEST.runtime.version,
|
|
113
116
|
downloadBytes: platformEntry?.downloadBytes || 0,
|
|
114
117
|
source: LOCAL_PROVIDER_MANIFEST.runtime.source,
|
|
@@ -1,7 +1,7 @@
|
|
|
1
|
-
import { existsSync,
|
|
1
|
+
import { existsSync, statSync } from 'node:fs';
|
|
2
2
|
import { join } from 'node:path';
|
|
3
3
|
import { resolvePluginData } from '../shared/plugin-paths.mjs';
|
|
4
|
-
import {
|
|
4
|
+
import { readJsonStateSync, unsupportedState, updateJsonAtomicSync } from '../shared/atomic-file.mjs';
|
|
5
5
|
|
|
6
6
|
const MAX_REGISTRY_BYTES = 1024 * 1024;
|
|
7
7
|
const cache = new Map();
|
|
@@ -42,44 +42,81 @@ function validateModel(entry) {
|
|
|
42
42
|
return entry;
|
|
43
43
|
}
|
|
44
44
|
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
if (
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
45
|
+
// Each candidate (the index or its backup) is bounded by its own bytes.
|
|
46
|
+
function registryText(content) {
|
|
47
|
+
if (content.length > MAX_REGISTRY_BYTES) throw new Error('[local-provider] registered model index is too large');
|
|
48
|
+
return content.toString('utf8');
|
|
49
|
+
}
|
|
50
|
+
|
|
51
|
+
function parseRegistry(value) {
|
|
52
|
+
// A newer build's index is left in place (reads as no models), not discarded.
|
|
53
|
+
if (Number.isSafeInteger(value?.schema) && value.schema > 1) {
|
|
54
|
+
throw unsupportedState(`[local-provider] registered model index schema ${value.schema} is newer than this build`);
|
|
55
|
+
}
|
|
53
56
|
if (value?.schema !== 1 || !Array.isArray(value.models) || value.models.length > 256) {
|
|
54
57
|
throw new Error('[local-provider] invalid registered model index');
|
|
55
58
|
}
|
|
56
59
|
const models = value.models.map(validateModel);
|
|
57
60
|
if (new Set(models.map((entry) => entry.id)).size !== models.length)
|
|
58
61
|
throw new Error('[local-provider] duplicate registered model ids');
|
|
62
|
+
return models;
|
|
63
|
+
}
|
|
64
|
+
|
|
65
|
+
export function registeredLocalModels(dataDir = resolvePluginData()) {
|
|
66
|
+
const path = registryPath(dataDir);
|
|
67
|
+
if (!existsSync(path)) return [];
|
|
68
|
+
const stat = statSync(path);
|
|
69
|
+
const stamp = `${stat.mtimeMs}:${stat.size}`;
|
|
70
|
+
if (cache.get(path)?.stamp === stamp) return cache.get(path).models.map((entry) => ({ ...entry }));
|
|
71
|
+
// A damaged index (empty, NUL-filled, truncated, invalid) never blocks the
|
|
72
|
+
// provider: its bytes are kept as <file>.corrupt-<ts>, the last good copy
|
|
73
|
+
// (<file>.bak) is restored when there is one, else no model is registered.
|
|
74
|
+
// The downloaded weights are untouched.
|
|
75
|
+
const models = readJsonStateSync(path, { validate: parseRegistry, decode: registryText, fallback: [] });
|
|
59
76
|
cache.set(path, { stamp, models });
|
|
60
77
|
return models.map((entry) => ({ ...entry }));
|
|
61
78
|
}
|
|
62
79
|
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
if (existing.contextWindow !== entry.contextWindow)
|
|
69
|
-
throw new Error('[local-provider] this model is already registered with another context size');
|
|
70
|
-
return existing;
|
|
71
|
-
}
|
|
72
|
-
if (models.length >= 256) throw new Error('[local-provider] registered model limit reached');
|
|
80
|
+
// Read-modify-write under the index lock. A damaged index was recovered by the
|
|
81
|
+
// read first; a newer build's index makes the write refuse instead of
|
|
82
|
+
// replacing it (and its backup) with this build's format.
|
|
83
|
+
function mutateRegistry(dataDir, change) {
|
|
84
|
+
registeredLocalModels(dataDir);
|
|
73
85
|
const path = registryPath(dataDir);
|
|
74
|
-
|
|
86
|
+
let result;
|
|
87
|
+
updateJsonAtomicSync(
|
|
88
|
+
path,
|
|
89
|
+
(raw) => {
|
|
90
|
+
const next = change(raw == null ? [] : parseRegistry(raw));
|
|
91
|
+
if (!next) return undefined;
|
|
92
|
+
result = next.result;
|
|
93
|
+
return { schema: 1, models: next.models };
|
|
94
|
+
},
|
|
95
|
+
{ backup: true }
|
|
96
|
+
);
|
|
75
97
|
cache.delete(path);
|
|
76
|
-
return
|
|
98
|
+
return result;
|
|
99
|
+
}
|
|
100
|
+
|
|
101
|
+
export function registerLocalModel(entry, dataDir = resolvePluginData()) {
|
|
102
|
+
validateModel(entry);
|
|
103
|
+
let existing;
|
|
104
|
+
const registered = mutateRegistry(dataDir, (models) => {
|
|
105
|
+
existing = models.find((model) => model.id === entry.id);
|
|
106
|
+
if (existing) return null;
|
|
107
|
+
if (models.length >= 256) throw new Error('[local-provider] registered model limit reached');
|
|
108
|
+
return { models: [...models, entry], result: entry };
|
|
109
|
+
});
|
|
110
|
+
if (!existing) return registered;
|
|
111
|
+
if (existing.contextWindow !== entry.contextWindow)
|
|
112
|
+
throw new Error('[local-provider] this model is already registered with another context size');
|
|
113
|
+
return existing;
|
|
77
114
|
}
|
|
78
115
|
|
|
79
116
|
export function forgetRegisteredLocalModel(modelId, dataDir = resolvePluginData()) {
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
117
|
+
mutateRegistry(dataDir, (models) =>
|
|
118
|
+
models.some((entry) => entry.id === modelId)
|
|
119
|
+
? { models: models.filter((entry) => entry.id !== modelId), result: undefined }
|
|
120
|
+
: null
|
|
121
|
+
);
|
|
85
122
|
}
|
|
@@ -1,5 +1,4 @@
|
|
|
1
|
-
import {
|
|
2
|
-
|
|
1
|
+
import { fileHasData } from '../shared/file-has-data.mjs';
|
|
3
2
|
import { resolvePluginData } from '../shared/plugin-paths.mjs';
|
|
4
3
|
import {
|
|
5
4
|
exactLocalProviderFile,
|
|
@@ -65,7 +64,7 @@ export async function ensureLocalProviderServer(modelId, { dataDir = resolvePlug
|
|
|
65
64
|
const contextWindow = localContextSettings(entry, dataDir).contextWindow;
|
|
66
65
|
const executable = localProviderRuntimeExecutable(dataDir);
|
|
67
66
|
const weights = localProviderModelPath(entry, dataDir);
|
|
68
|
-
if (!executable || !
|
|
67
|
+
if (!executable || !fileHasData(executable)) {
|
|
69
68
|
throw new Error('[local-provider] runtime is not installed');
|
|
70
69
|
}
|
|
71
70
|
if (!exactLocalProviderFile(weights, entry.size)) {
|
|
@@ -0,0 +1,89 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Normalized Image API adapter for the `openrouter` and `nano-gpt` lanes.
|
|
3
|
+
*
|
|
4
|
+
* Both gateways document the same contract: POST {base}/images with `model`,
|
|
5
|
+
* `prompt`, the model's advertised enum parameters and `input_references`
|
|
6
|
+
* (image_url parts; data URLs accepted), answering `data[].b64_json` (NanoGPT
|
|
7
|
+
* may answer a signed `url` instead) plus `usage`.
|
|
8
|
+
* https://openrouter.ai/docs/guides/overview/multimodal/image-generation
|
|
9
|
+
* https://docs.nano-gpt.com/api-reference/image-generation
|
|
10
|
+
*/
|
|
11
|
+
import { resolveImageApiAuth } from '../auth.mjs';
|
|
12
|
+
import { timeoutSignal } from '../bounded-signal.mjs';
|
|
13
|
+
import { decodeBase64Media, downloadPublicMedia } from '../download.mjs';
|
|
14
|
+
import { mediaError } from '../lanes.mjs';
|
|
15
|
+
import { imageApiUsage, withReportedUsage } from '../media-usage.mjs';
|
|
16
|
+
import { upstreamError } from '../upstream-error.mjs';
|
|
17
|
+
|
|
18
|
+
const IMAGE_TIMEOUT_MS = 180_000;
|
|
19
|
+
const LABELS = Object.freeze({ openrouter: 'OpenRouter image', 'nano-gpt': 'NanoGPT image' });
|
|
20
|
+
// Request field per Studio option; values come from the model's own enums.
|
|
21
|
+
const PARAMS = Object.freeze({
|
|
22
|
+
aspectRatio: 'aspect_ratio',
|
|
23
|
+
resolution: 'resolution',
|
|
24
|
+
quality: 'quality',
|
|
25
|
+
background: 'background',
|
|
26
|
+
});
|
|
27
|
+
|
|
28
|
+
function referenceUrl(reference) {
|
|
29
|
+
const data = String(reference?.base64 || '');
|
|
30
|
+
return data.startsWith('data:') ? data : `data:${reference?.mime || 'image/png'};base64,${data}`;
|
|
31
|
+
}
|
|
32
|
+
|
|
33
|
+
/** The stored format comes from the bytes, not a label: only rasters the store keeps. */
|
|
34
|
+
function rasterMime(bytes) {
|
|
35
|
+
if (bytes.length >= 8 && bytes.readUInt32BE(0) === 0x89504e47) return 'image/png';
|
|
36
|
+
if (bytes.length >= 3 && bytes[0] === 0xff && bytes[1] === 0xd8 && bytes[2] === 0xff) return 'image/jpeg';
|
|
37
|
+
if (bytes.length >= 12 && bytes.toString('ascii', 0, 4) === 'RIFF' && bytes.toString('ascii', 8, 12) === 'WEBP')
|
|
38
|
+
return 'image/webp';
|
|
39
|
+
return '';
|
|
40
|
+
}
|
|
41
|
+
|
|
42
|
+
export async function generateImage({ lane, model, prompt, options = {}, references = [], signal }) {
|
|
43
|
+
const label = LABELS[lane] || 'Image API';
|
|
44
|
+
const { baseURL, token } = resolveImageApiAuth(lane);
|
|
45
|
+
const body = { model, prompt, n: 1 };
|
|
46
|
+
for (const [option, param] of Object.entries(PARAMS)) {
|
|
47
|
+
const value = String(options?.[option] || '').trim();
|
|
48
|
+
if (value) body[param] = value;
|
|
49
|
+
}
|
|
50
|
+
if (references.length) {
|
|
51
|
+
body.input_references = references.map((reference) => ({
|
|
52
|
+
type: 'image_url',
|
|
53
|
+
image_url: { url: referenceUrl(reference) },
|
|
54
|
+
}));
|
|
55
|
+
}
|
|
56
|
+
let res;
|
|
57
|
+
try {
|
|
58
|
+
res = await fetch(`${baseURL}/images`, {
|
|
59
|
+
method: 'POST',
|
|
60
|
+
headers: { 'Content-Type': 'application/json', Authorization: `Bearer ${token}` },
|
|
61
|
+
body: JSON.stringify(body),
|
|
62
|
+
redirect: 'error',
|
|
63
|
+
signal: timeoutSignal(signal, IMAGE_TIMEOUT_MS),
|
|
64
|
+
});
|
|
65
|
+
} catch (error) {
|
|
66
|
+
if (error?.name === 'TimeoutError') throw mediaError(`${label} timed out`, 'MEDIA_TIMEOUT', 504);
|
|
67
|
+
throw error;
|
|
68
|
+
}
|
|
69
|
+
if (!res.ok) throw upstreamError(label, res.status, (await res.text().catch(() => '')).slice(0, 400));
|
|
70
|
+
const data = await res.json().catch(() => null);
|
|
71
|
+
const usage = imageApiUsage(data);
|
|
72
|
+
const entry = Array.isArray(data?.data) ? data.data[0] : null;
|
|
73
|
+
let bytes;
|
|
74
|
+
try {
|
|
75
|
+
if (entry?.b64_json) bytes = decodeBase64Media(entry.b64_json, label);
|
|
76
|
+
else if (entry?.url) bytes = await downloadPublicMedia(entry.url, { signal, label });
|
|
77
|
+
else throw mediaError(`${label} returned no image data`, 'MEDIA_EMPTY_RESULT', 502);
|
|
78
|
+
} catch (error) {
|
|
79
|
+
throw withReportedUsage(error, usage);
|
|
80
|
+
}
|
|
81
|
+
const mime = rasterMime(bytes);
|
|
82
|
+
if (!mime) {
|
|
83
|
+
throw withReportedUsage(
|
|
84
|
+
mediaError(`${label} returned an unsupported image format`, 'MEDIA_UPSTREAM_FAILED', 502),
|
|
85
|
+
usage
|
|
86
|
+
);
|
|
87
|
+
}
|
|
88
|
+
return { bytes, mime, revisedPrompt: entry.revised_prompt || null, usage };
|
|
89
|
+
}
|
|
@@ -5,11 +5,74 @@
|
|
|
5
5
|
* paths) so the Studio never holds a second copy of a session. API-key lanes
|
|
6
6
|
* read the same keychain-backed store the agent providers use.
|
|
7
7
|
*/
|
|
8
|
-
import {
|
|
8
|
+
import { readSection } from '../shared/config.mjs';
|
|
9
|
+
import {
|
|
10
|
+
getAgentApiKey,
|
|
11
|
+
hasAgentApiKey,
|
|
12
|
+
hasProviderApiKey,
|
|
13
|
+
resolveProviderApiKey,
|
|
14
|
+
} from '../shared/provider-api-key.mjs';
|
|
9
15
|
|
|
10
16
|
export { hasGrokOAuthCredentials } from '../agent/orchestrator/providers/oauth-credential-probes.mjs';
|
|
11
17
|
|
|
12
18
|
const XAI_BASE_URL = 'https://api.x.ai/v1';
|
|
19
|
+
// Both gateways serve the same normalized Image API (GET /images/models,
|
|
20
|
+
// POST /images) under these documented bases.
|
|
21
|
+
const IMAGE_API_BASE_URL = Object.freeze({
|
|
22
|
+
openrouter: 'https://openrouter.ai/api/v1',
|
|
23
|
+
'nano-gpt': 'https://api.nano-gpt.com/api/v1',
|
|
24
|
+
});
|
|
25
|
+
|
|
26
|
+
/** Lanes served by the normalized Image API adapter. */
|
|
27
|
+
export function imageApiLane(laneId) {
|
|
28
|
+
return Object.hasOwn(IMAGE_API_BASE_URL, laneId);
|
|
29
|
+
}
|
|
30
|
+
|
|
31
|
+
function nanoGptHost(baseURL) {
|
|
32
|
+
try {
|
|
33
|
+
const host = new URL(String(baseURL || '')).hostname.toLowerCase();
|
|
34
|
+
return host === 'nano-gpt.com' || host.endsWith('.nano-gpt.com');
|
|
35
|
+
} catch {
|
|
36
|
+
return false;
|
|
37
|
+
}
|
|
38
|
+
}
|
|
39
|
+
|
|
40
|
+
// NanoGPT is not a built-in provider: it is connected as an OpenAI-compatible
|
|
41
|
+
// custom provider, whose key lives under that provider's own id (keychain or
|
|
42
|
+
// its configured environment variable). The first enabled, keyed endpoint on
|
|
43
|
+
// a NanoGPT host is the lane's credential.
|
|
44
|
+
function nanoGptProviders() {
|
|
45
|
+
const providers = readSection('agent')?.providers || {};
|
|
46
|
+
return Object.entries(providers).filter(
|
|
47
|
+
([id, entry]) =>
|
|
48
|
+
id.startsWith('custom-') &&
|
|
49
|
+
entry?.type === 'custom' &&
|
|
50
|
+
entry.enabled !== false &&
|
|
51
|
+
entry.auth !== 'none' &&
|
|
52
|
+
nanoGptHost(entry.baseURL)
|
|
53
|
+
);
|
|
54
|
+
}
|
|
55
|
+
|
|
56
|
+
/** Presence-only probe for an Image API lane's key; no secret is read. */
|
|
57
|
+
export function hasImageApiCredentials(laneId) {
|
|
58
|
+
if (laneId === 'openrouter') return hasAgentApiKey('openrouter');
|
|
59
|
+
if (laneId === 'nano-gpt') return nanoGptProviders().some(([id, entry]) => hasProviderApiKey(id, entry));
|
|
60
|
+
return false;
|
|
61
|
+
}
|
|
62
|
+
|
|
63
|
+
/** Base URL + bearer for an Image API lane (OpenRouter key, or the connected NanoGPT custom provider's key). */
|
|
64
|
+
export function resolveImageApiAuth(laneId) {
|
|
65
|
+
let token = '';
|
|
66
|
+
if (laneId === 'openrouter') token = getAgentApiKey('openrouter') || '';
|
|
67
|
+
else if (laneId === 'nano-gpt') {
|
|
68
|
+
for (const [id, entry] of nanoGptProviders()) {
|
|
69
|
+
token = resolveProviderApiKey(id, entry);
|
|
70
|
+
if (token) break;
|
|
71
|
+
}
|
|
72
|
+
}
|
|
73
|
+
if (!imageApiLane(laneId) || !token) throw unauthenticated(`${laneId} API key is not configured`);
|
|
74
|
+
return { baseURL: IMAGE_API_BASE_URL[laneId], token };
|
|
75
|
+
}
|
|
13
76
|
|
|
14
77
|
// Built here rather than with lanes.mjs's mediaError: importing it would close
|
|
15
78
|
// an auth -> lanes -> catalog -> auth module cycle.
|