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
|
@@ -6,11 +6,26 @@
|
|
|
6
6
|
*/
|
|
7
7
|
import { createHash } from 'node:crypto';
|
|
8
8
|
import { makeModelCache } from '../agent/orchestrator/providers/model-cache.mjs';
|
|
9
|
-
import {
|
|
9
|
+
import {
|
|
10
|
+
imageApiLane,
|
|
11
|
+
resolveAntigravityAuth,
|
|
12
|
+
resolveGeminiKey,
|
|
13
|
+
resolveImageApiAuth,
|
|
14
|
+
resolveXaiAuth,
|
|
15
|
+
} from './auth.mjs';
|
|
10
16
|
import { catalogHttpError, staleCatalog } from './catalog-errors.mjs';
|
|
11
17
|
|
|
12
18
|
const TTL_MS = 24 * 60 * 60_000;
|
|
13
19
|
const REQUEST_MS = 10_000;
|
|
20
|
+
// Gateway image catalogs can sit behind a slow edge: api.nano-gpt.com's
|
|
21
|
+
// /models answered only after ~44 s, so the 10 s default would read a slow
|
|
22
|
+
// catalog as an outage. Same budget as custom-provider discovery.
|
|
23
|
+
const GATEWAY_CATALOG_MS = 60_000;
|
|
24
|
+
|
|
25
|
+
/** Per-lane timeout of one catalog request. */
|
|
26
|
+
export function mediaCatalogTimeoutMs(lane) {
|
|
27
|
+
return imageApiLane(lane) ? GATEWAY_CATALOG_MS : REQUEST_MS;
|
|
28
|
+
}
|
|
14
29
|
const natural = new Intl.Collator('en', { numeric: true });
|
|
15
30
|
|
|
16
31
|
function methodsAllow(row, method) {
|
|
@@ -39,8 +54,51 @@ function grokLane(lane) {
|
|
|
39
54
|
return lane === 'xai' || lane === 'grok-oauth';
|
|
40
55
|
}
|
|
41
56
|
|
|
57
|
+
// Image API catalogs (OpenRouter, NanoGPT) describe each model with typed
|
|
58
|
+
// capability descriptors; only enum values become pickable controls.
|
|
59
|
+
const IMAGE_API_CONTROLS = Object.freeze({
|
|
60
|
+
aspect_ratio: 'aspectRatio',
|
|
61
|
+
resolution: 'resolution',
|
|
62
|
+
quality: 'quality',
|
|
63
|
+
background: 'background',
|
|
64
|
+
});
|
|
65
|
+
// The request schema's documented ceiling for `input_references`.
|
|
66
|
+
const IMAGE_API_MAX_REFERENCES = 16;
|
|
67
|
+
|
|
68
|
+
function imageApiOutputsImage(row) {
|
|
69
|
+
const outputs = row.architecture?.output_modalities;
|
|
70
|
+
// Image-to-image-only models (upscalers, editors) need an input image;
|
|
71
|
+
// Studio also runs prompt-only, so they stay out.
|
|
72
|
+
return Array.isArray(outputs) && outputs.includes('image') && row.capabilities?.image_generation !== false;
|
|
73
|
+
}
|
|
74
|
+
|
|
75
|
+
function imageApiReferenceCap(row) {
|
|
76
|
+
const descriptor = row.supported_parameters?.input_references;
|
|
77
|
+
if (descriptor?.type === 'range' && Number.isFinite(Number(descriptor.max))) {
|
|
78
|
+
return Math.max(0, Math.min(IMAGE_API_MAX_REFERENCES, Math.trunc(Number(descriptor.max))));
|
|
79
|
+
}
|
|
80
|
+
const inputs = row.architecture?.input_modalities;
|
|
81
|
+
const acceptsImages = Array.isArray(inputs) && inputs.includes('image') && row.capabilities?.image_to_image !== false;
|
|
82
|
+
return acceptsImages ? IMAGE_API_MAX_REFERENCES : 0;
|
|
83
|
+
}
|
|
84
|
+
|
|
85
|
+
function imageApiControls(row) {
|
|
86
|
+
const controls = {};
|
|
87
|
+
for (const [param, control] of Object.entries(IMAGE_API_CONTROLS)) {
|
|
88
|
+
const descriptor = row.supported_parameters?.[param];
|
|
89
|
+
const values =
|
|
90
|
+
descriptor?.type === 'enum' && Array.isArray(descriptor.values)
|
|
91
|
+
? [...new Set(descriptor.values.map((value) => String(value).trim()).filter(Boolean))]
|
|
92
|
+
: [];
|
|
93
|
+
if (values.length) controls[control] = values;
|
|
94
|
+
}
|
|
95
|
+
controls.maxReferences = imageApiReferenceCap(row);
|
|
96
|
+
return controls;
|
|
97
|
+
}
|
|
98
|
+
|
|
42
99
|
function kindFor(lane, row, id) {
|
|
43
100
|
if (row.deprecated === true || row.disabled === true) return null;
|
|
101
|
+
if (imageApiLane(lane)) return imageApiOutputsImage(row) ? 'image' : null;
|
|
44
102
|
if (lane === 'openai-oauth') return imageToolSupported(row, id) ? 'image' : null;
|
|
45
103
|
if (grokLane(lane)) {
|
|
46
104
|
if (/^grok-imagine-image(?:-|$)/.test(id)) return 'image';
|
|
@@ -118,7 +176,9 @@ export function projectMediaModels(lane, rows) {
|
|
|
118
176
|
const kind = kindFor(lane, row, id);
|
|
119
177
|
if (!kind) continue;
|
|
120
178
|
let controls = {};
|
|
121
|
-
if (
|
|
179
|
+
if (imageApiLane(lane)) {
|
|
180
|
+
controls = imageApiControls(row);
|
|
181
|
+
} else if (grokLane(lane) && kind === 'video') {
|
|
122
182
|
controls = { resolution: ['480p', '720p'] };
|
|
123
183
|
} else if (lane === 'gemini' && /^gemini-omni-/.test(id)) {
|
|
124
184
|
controls = { resolution: [], durations: [], maxReferences: 3 };
|
|
@@ -181,6 +241,7 @@ async function providerSource(lane) {
|
|
|
181
241
|
let auth;
|
|
182
242
|
if (lane === 'gemini') auth = { token: resolveGeminiKey() };
|
|
183
243
|
else if (lane === 'antigravity-oauth') auth = await resolveAntigravityAuth();
|
|
244
|
+
else if (imageApiLane(lane)) auth = resolveImageApiAuth(lane);
|
|
184
245
|
else auth = await resolveXaiAuth(lane);
|
|
185
246
|
// API-key and OAuth catalogs must not leak availability across credentials.
|
|
186
247
|
// Persist only a one-way scope hash, never a key or a bearer. OAuth bearers
|
|
@@ -200,7 +261,7 @@ async function providerSource(lane) {
|
|
|
200
261
|
}
|
|
201
262
|
|
|
202
263
|
export async function fetchMediaModelRows({ lane, auth, fetchFn = fetch }) {
|
|
203
|
-
const signal = AbortSignal.timeout(
|
|
264
|
+
const signal = AbortSignal.timeout(mediaCatalogTimeoutMs(lane));
|
|
204
265
|
if (lane === 'gemini') {
|
|
205
266
|
const { fetchGeminiModelPages } = await import('../agent/orchestrator/providers/lib/gemini-model-catalog.mjs');
|
|
206
267
|
return fetchGeminiModelPages(auth.token, async (url, init) => {
|
|
@@ -217,7 +278,8 @@ export async function fetchMediaModelRows({ lane, auth, fetchFn = fetch }) {
|
|
|
217
278
|
return Object.entries(models).map(([id, row]) => ({ ...(row && typeof row === 'object' ? row : {}), id }));
|
|
218
279
|
}
|
|
219
280
|
const { getLlmDispatcher } = await import('../shared/llm/http-agent.mjs');
|
|
220
|
-
|
|
281
|
+
// The Image API lists its generation models apart from the chat catalog.
|
|
282
|
+
const response = await fetchFn(`${auth.baseURL}/${imageApiLane(lane) ? 'images/models' : 'models'}`, {
|
|
221
283
|
headers: { Authorization: `Bearer ${auth.token}` },
|
|
222
284
|
redirect: 'error',
|
|
223
285
|
signal,
|
|
@@ -60,6 +60,10 @@ async function runAdapter({ lane, kind, model, requestModel, prompt, options, re
|
|
|
60
60
|
const adapter = await import('./adapters/codex-image.mjs');
|
|
61
61
|
return await adapter.generateImage({ model: requestModel || model, prompt, options, references, signal });
|
|
62
62
|
}
|
|
63
|
+
if (lane.id === 'openrouter' || lane.id === 'nano-gpt') {
|
|
64
|
+
const adapter = await import('./adapters/image-api.mjs');
|
|
65
|
+
return await adapter.generateImage({ lane: lane.id, model, prompt, options, references, signal });
|
|
66
|
+
}
|
|
63
67
|
if (lane.id === 'antigravity-oauth') {
|
|
64
68
|
const adapter = await import('./adapters/antigravity-image.mjs');
|
|
65
69
|
return await adapter.generateImage({ model, prompt, options, references, signal });
|
|
@@ -16,6 +16,7 @@ import {
|
|
|
16
16
|
hasGrokOAuthCredentials,
|
|
17
17
|
hasOpenAIOAuthCredentials,
|
|
18
18
|
} from '../agent/orchestrator/providers/oauth-credential-probes.mjs';
|
|
19
|
+
import { hasImageApiCredentials, imageApiLane } from './auth.mjs';
|
|
19
20
|
import { loadMediaModels, projectMediaModels } from './catalog.mjs';
|
|
20
21
|
import { catalogDiagnostic } from './catalog-errors.mjs';
|
|
21
22
|
import { MEDIA_KINDS } from './tool-defs.mjs';
|
|
@@ -98,6 +99,20 @@ const LANES = Object.freeze([
|
|
|
98
99
|
controls: GEMINI_IMAGE_CONTROLS,
|
|
99
100
|
}),
|
|
100
101
|
}),
|
|
102
|
+
// Gateway lanes on the normalized Image API: every model advertises its own
|
|
103
|
+
// parameters in the catalog, so the lane declares no controls of its own.
|
|
104
|
+
Object.freeze({
|
|
105
|
+
id: 'openrouter',
|
|
106
|
+
label: 'OpenRouter',
|
|
107
|
+
auth: Object.freeze({ type: 'api-key', provider: 'openrouter' }),
|
|
108
|
+
image: Object.freeze({ controls: Object.freeze({}) }),
|
|
109
|
+
}),
|
|
110
|
+
Object.freeze({
|
|
111
|
+
id: 'nano-gpt',
|
|
112
|
+
label: 'NanoGPT',
|
|
113
|
+
auth: Object.freeze({ type: 'api-key', provider: 'nano-gpt' }),
|
|
114
|
+
image: Object.freeze({ controls: Object.freeze({}) }),
|
|
115
|
+
}),
|
|
101
116
|
]);
|
|
102
117
|
|
|
103
118
|
const LANE_BY_ID = new Map(LANES.map((lane) => [lane.id, lane]));
|
|
@@ -117,6 +132,7 @@ const OAUTH_PROBES = Object.freeze({
|
|
|
117
132
|
|
|
118
133
|
function laneAuthenticated(lane) {
|
|
119
134
|
try {
|
|
135
|
+
if (imageApiLane(lane.id)) return hasImageApiCredentials(lane.id);
|
|
120
136
|
if (lane.auth.type === 'api-key') return hasAgentApiKey(lane.auth.provider);
|
|
121
137
|
return OAUTH_PROBES[lane.auth.provider]?.() === true;
|
|
122
138
|
} catch {
|
|
@@ -143,8 +159,23 @@ function laneKindView(lane, kind, models) {
|
|
|
143
159
|
};
|
|
144
160
|
}
|
|
145
161
|
|
|
162
|
+
const PENDING = Symbol('catalog-pending');
|
|
163
|
+
|
|
164
|
+
/** The catalog rows, or PENDING when they did not arrive within `budgetMs`
|
|
165
|
+
* (0 = wait). The request keeps running; the loader's singleflight lets the
|
|
166
|
+
* next listing join it instead of starting over. */
|
|
167
|
+
async function withinBudget(loading, budgetMs) {
|
|
168
|
+
if (!(budgetMs > 0)) return loading;
|
|
169
|
+
loading.catch(() => {});
|
|
170
|
+
let timer;
|
|
171
|
+
const expired = new Promise((resolve) => {
|
|
172
|
+
timer = setTimeout(resolve, budgetMs, PENDING);
|
|
173
|
+
});
|
|
174
|
+
return Promise.race([loading, expired]).finally(() => clearTimeout(timer));
|
|
175
|
+
}
|
|
176
|
+
|
|
146
177
|
export function createMediaLaneCatalog({ loadModels = loadMediaModels, authenticated = laneAuthenticated } = {}) {
|
|
147
|
-
async function laneView(lane) {
|
|
178
|
+
async function laneView(lane, budgetMs = 0) {
|
|
148
179
|
const signedIn = authenticated(lane);
|
|
149
180
|
const view = {
|
|
150
181
|
id: lane.id,
|
|
@@ -161,7 +192,15 @@ export function createMediaLaneCatalog({ loadModels = loadMediaModels, authentic
|
|
|
161
192
|
};
|
|
162
193
|
if (!signedIn) return view;
|
|
163
194
|
try {
|
|
164
|
-
const rows = await
|
|
195
|
+
const rows = await withinBudget(
|
|
196
|
+
Promise.resolve().then(() => loadModels(lane.id)),
|
|
197
|
+
budgetMs
|
|
198
|
+
);
|
|
199
|
+
if (rows === PENDING) {
|
|
200
|
+
// Still loading, not empty: the caller shows that and asks again.
|
|
201
|
+
view.catalogPending = true;
|
|
202
|
+
return view;
|
|
203
|
+
}
|
|
165
204
|
const models = projectMediaModels(lane.id, rows);
|
|
166
205
|
if (rows.catalogWarning) view.catalogWarning = rows.catalogWarning;
|
|
167
206
|
for (const kind of MEDIA_KINDS) {
|
|
@@ -187,9 +226,11 @@ export function createMediaLaneCatalog({ loadModels = loadMediaModels, authentic
|
|
|
187
226
|
}
|
|
188
227
|
|
|
189
228
|
return {
|
|
190
|
-
/** Live catalog shared by Studio and the model-facing media tool.
|
|
191
|
-
|
|
192
|
-
|
|
229
|
+
/** Live catalog shared by Studio and the model-facing media tool.
|
|
230
|
+
* `budgetMs` bounds the wait per lane: a lane whose catalog is slower
|
|
231
|
+
* answers `catalogPending` instead of holding every other lane. */
|
|
232
|
+
async listMediaLanes({ budgetMs = 0 } = {}) {
|
|
233
|
+
return (await Promise.all(LANES.map((lane) => laneView(lane, budgetMs)))).filter(Boolean);
|
|
193
234
|
},
|
|
194
235
|
async resolveMediaRequest({ lane: laneId, kind, model } = {}) {
|
|
195
236
|
const kindName = String(kind || '').trim();
|
|
@@ -73,6 +73,23 @@ export function xaiUsage(usage) {
|
|
|
73
73
|
return typeof ticks === 'number' && Number.isFinite(ticks) && ticks >= 0 ? { costUsd: ticks / TICKS_PER_USD } : null;
|
|
74
74
|
}
|
|
75
75
|
|
|
76
|
+
/**
|
|
77
|
+
* Image API response usage (OpenRouter / NanoGPT `POST /images`). `usage.cost`
|
|
78
|
+
* is USD; NanoGPT's top-level `cost` counts only when `paymentSource` says USD
|
|
79
|
+
* (it may be billed in XNO).
|
|
80
|
+
*/
|
|
81
|
+
export function imageApiUsage(body) {
|
|
82
|
+
const usage = body?.usage && typeof body.usage === 'object' ? body.usage : {};
|
|
83
|
+
const tokens = tokenUsage({
|
|
84
|
+
inputTokens: count(usage.prompt_tokens),
|
|
85
|
+
outputTokens: count(usage.completion_tokens),
|
|
86
|
+
outputImageTokens: count(usage.completion_tokens_details?.image_tokens),
|
|
87
|
+
});
|
|
88
|
+
const usd = String(body?.paymentSource || '').toUpperCase() === 'USD' ? body?.cost : undefined;
|
|
89
|
+
const cost = [usage.cost, usd].find((value) => typeof value === 'number' && Number.isFinite(value) && value >= 0);
|
|
90
|
+
return cost === undefined ? tokens : { ...tokens, costUsd: cost };
|
|
91
|
+
}
|
|
92
|
+
|
|
76
93
|
/** True when the provider reported something billable for the request. */
|
|
77
94
|
export function hasReportedUsage(usage) {
|
|
78
95
|
if (!usage) return false;
|
|
@@ -14,10 +14,11 @@
|
|
|
14
14
|
* what made the remote gallery slow.
|
|
15
15
|
*/
|
|
16
16
|
import { spawn } from 'node:child_process';
|
|
17
|
-
import {
|
|
18
|
-
import {
|
|
19
|
-
import { dirname, join } from 'node:path';
|
|
17
|
+
import { existsSync, readdirSync, statSync, unlinkSync } from 'node:fs';
|
|
18
|
+
import { join } from 'node:path';
|
|
20
19
|
|
|
20
|
+
import { writeFileAtomicSync } from '../shared/atomic-file.mjs';
|
|
21
|
+
import { fileHasData } from '../shared/file-has-data.mjs';
|
|
21
22
|
import { resolvePluginData } from '../shared/plugin-paths.mjs';
|
|
22
23
|
|
|
23
24
|
// maxEdge is the long edge in pixels; quality is the webp setting.
|
|
@@ -100,7 +101,14 @@ function cachedRendition(cacheDir, variant, id) {
|
|
|
100
101
|
// a plain cache miss into a thrown media request.
|
|
101
102
|
try {
|
|
102
103
|
const info = statSync(path);
|
|
103
|
-
if (info.isFile())
|
|
104
|
+
if (!info.isFile()) continue;
|
|
105
|
+
// A rendition a crash left empty or NUL-filled would be served forever;
|
|
106
|
+
// drop it so the miss below rebuilds it.
|
|
107
|
+
if (!fileHasData(path)) {
|
|
108
|
+
unlinkSync(path);
|
|
109
|
+
continue;
|
|
110
|
+
}
|
|
111
|
+
return { path, mime, bytes: info.size };
|
|
104
112
|
} catch {
|
|
105
113
|
/* absent or evicted mid-lookup */
|
|
106
114
|
}
|
|
@@ -166,21 +174,13 @@ function writeRendition(cacheDir, variant, id, extension, buffer) {
|
|
|
166
174
|
return null;
|
|
167
175
|
}
|
|
168
176
|
const path = renditionPath(cacheDir, variant, id, extension);
|
|
169
|
-
|
|
170
|
-
//
|
|
171
|
-
const staging = `${path}.${process.pid}.${randomBytes(6).toString('hex')}.tmp`;
|
|
172
|
-
writeFileSync(staging, buffer);
|
|
177
|
+
// Durable stage-then-rename: a reader never observes a half-written cache
|
|
178
|
+
// file, and a crash cannot publish one whose bytes are all NUL.
|
|
173
179
|
try {
|
|
174
|
-
|
|
180
|
+
writeFileAtomicSync(path, buffer);
|
|
175
181
|
} catch (error) {
|
|
176
182
|
// Another process may have won the same cache publication race.
|
|
177
183
|
if (!existsSync(path)) throw error;
|
|
178
|
-
} finally {
|
|
179
|
-
try {
|
|
180
|
-
unlinkSync(staging);
|
|
181
|
-
} catch {
|
|
182
|
-
/* renamed or already removed */
|
|
183
|
-
}
|
|
184
184
|
}
|
|
185
185
|
let bytes = buffer.length;
|
|
186
186
|
try {
|
|
@@ -6,12 +6,12 @@
|
|
|
6
6
|
* (newest first) and is written atomically under a file lock because the
|
|
7
7
|
* desktop and CLI can generate concurrently.
|
|
8
8
|
*/
|
|
9
|
-
import { existsSync, mkdirSync, readFileSync, renameSync, unlinkSync,
|
|
9
|
+
import { existsSync, mkdirSync, readFileSync, renameSync, unlinkSync, statSync } from 'node:fs';
|
|
10
10
|
import { dirname, join, resolve, sep } from 'node:path';
|
|
11
11
|
import { randomUUID } from 'node:crypto';
|
|
12
12
|
import { spawn } from 'node:child_process';
|
|
13
13
|
import { resolvePluginData } from '../shared/plugin-paths.mjs';
|
|
14
|
-
import {
|
|
14
|
+
import { readJsonStateSync, updateJsonAtomicSync, writeFileAtomicSync } from '../shared/atomic-file.mjs';
|
|
15
15
|
import { cacheRendition, ensureRendition, removeRenditions, renditionSpec } from './renditions.mjs';
|
|
16
16
|
|
|
17
17
|
// Renderer transport is base64 over IPC: refuse to inline anything larger.
|
|
@@ -50,10 +50,6 @@ function indexPath() {
|
|
|
50
50
|
return join(mediaDir(), 'index.json');
|
|
51
51
|
}
|
|
52
52
|
|
|
53
|
-
function indexLockPath() {
|
|
54
|
-
return `${indexPath()}.lock`;
|
|
55
|
-
}
|
|
56
|
-
|
|
57
53
|
function ensureDirs({ migrate = false } = {}) {
|
|
58
54
|
mkdirSync(assetsDir(), { recursive: true });
|
|
59
55
|
if (migrate && !layoutMigrationChecked) {
|
|
@@ -89,42 +85,45 @@ function readIndex() {
|
|
|
89
85
|
indexCacheHits += 1;
|
|
90
86
|
return indexCacheAssets;
|
|
91
87
|
}
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
}
|
|
102
|
-
const parsed = JSON.parse(raw);
|
|
103
|
-
if (!parsed || !Array.isArray(parsed.assets)) {
|
|
104
|
-
throw new Error(`media index is malformed: ${path}`);
|
|
105
|
-
}
|
|
106
|
-
indexDiskReads += 1;
|
|
107
|
-
return rememberIndex(path, signature, parsed.assets);
|
|
88
|
+
// A missing index is a genuinely empty catalog (first run). A DAMAGED one
|
|
89
|
+
// (empty, NUL-filled, truncated, wrong shape) is kept as index.json.corrupt-<ts>
|
|
90
|
+
// and the last good copy restored, so the gallery lists and saves keep
|
|
91
|
+
// working. Every other read failure (EACCES, EIO) still throws: answering
|
|
92
|
+
// with an empty list made the next save/delete write that emptiness back
|
|
93
|
+
// over every existing asset entry.
|
|
94
|
+
const assets = readJsonStateSync(path, { validate: catalogAssets, fallback: [] });
|
|
95
|
+
if (signature !== 'missing') indexDiskReads += 1;
|
|
96
|
+
return rememberIndex(path, signature, assets);
|
|
108
97
|
}
|
|
109
98
|
|
|
110
|
-
function
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
rememberIndex(path, indexSignature(path), assets);
|
|
99
|
+
function catalogAssets(parsed) {
|
|
100
|
+
if (!parsed || !Array.isArray(parsed.assets)) throw new Error(`media index is malformed: ${indexPath()}`);
|
|
101
|
+
return parsed.assets;
|
|
114
102
|
}
|
|
115
103
|
|
|
116
104
|
/**
|
|
117
105
|
* The one catalog read-modify-write. Every mutation (migrate, save, duration
|
|
118
|
-
* backfill, delete) holds index.lock across a fresh
|
|
119
|
-
*
|
|
120
|
-
*
|
|
121
|
-
*
|
|
106
|
+
* backfill, delete) holds index.lock across a fresh read and writes back only
|
|
107
|
+
* what the callback returns, so a concurrent CLI/desktop generation can never
|
|
108
|
+
* lose an entry — and a read that FAILS (rather than being absent) propagates
|
|
109
|
+
* out of the lock instead of writing an empty catalog over it. The previous
|
|
110
|
+
* catalog is kept as index.json.bak.
|
|
122
111
|
*/
|
|
123
112
|
function mutateIndex(mutate) {
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
|
|
113
|
+
// Sets a wrong-shaped catalog aside before the lock below takes the file.
|
|
114
|
+
readIndex();
|
|
115
|
+
let written = null;
|
|
116
|
+
updateJsonAtomicSync(
|
|
117
|
+
indexPath(),
|
|
118
|
+
(current) => {
|
|
119
|
+
const next = mutate(Array.isArray(current?.assets) ? current.assets : []);
|
|
120
|
+
if (!next) return undefined;
|
|
121
|
+
written = next;
|
|
122
|
+
return { version: 2, assets: next };
|
|
123
|
+
},
|
|
124
|
+
{ backup: true }
|
|
125
|
+
);
|
|
126
|
+
if (written) rememberIndex(indexPath(), indexSignature(indexPath()), written);
|
|
128
127
|
}
|
|
129
128
|
|
|
130
129
|
export function mediaStoreCacheStats() {
|
|
@@ -228,7 +227,9 @@ export function saveMediaAsset({ kind, lane, model, prompt, options = {}, mime,
|
|
|
228
227
|
const path = storedAssetPath(file);
|
|
229
228
|
if (!path) throw new Error('invalid media asset path');
|
|
230
229
|
mkdirSync(dirname(path), { recursive: true });
|
|
231
|
-
|
|
230
|
+
// Durable before the index names it: a crash must not leave a listed asset
|
|
231
|
+
// whose bytes are all NUL.
|
|
232
|
+
writeFileAtomicSync(path, bytes);
|
|
232
233
|
entry.file = file;
|
|
233
234
|
// The gallery is NOT capped (user). An entry-count trim used to unlink the
|
|
234
235
|
// oldest files here, which is data loss nobody asked for: a generated asset
|
|
@@ -28,6 +28,7 @@ const IMAGE_MIME = Object.freeze({
|
|
|
28
28
|
'.gif': 'image/gif',
|
|
29
29
|
});
|
|
30
30
|
const MAX_REFERENCE_BYTES = 12 * 1024 * 1024;
|
|
31
|
+
const GATEWAY_IMAGE_LANES = new Set(['openrouter', 'nano-gpt']);
|
|
31
32
|
|
|
32
33
|
let graph = null;
|
|
33
34
|
async function mediaGraph(deps) {
|
|
@@ -228,7 +229,13 @@ export function buildOptions(
|
|
|
228
229
|
if (resolution) {
|
|
229
230
|
validateChoice(controls, 'resolution', resolution, 'resolution');
|
|
230
231
|
options.resolution = resolution;
|
|
231
|
-
} else if (
|
|
232
|
+
} else if (
|
|
233
|
+
kind === 'image' &&
|
|
234
|
+
!GATEWAY_IMAGE_LANES.has(lane.id) &&
|
|
235
|
+
allowed(controls, 'resolution')?.includes('2k')
|
|
236
|
+
) {
|
|
237
|
+
// Gateway models bill per resolution tier: an omitted resolution keeps
|
|
238
|
+
// the provider's default there instead of silently upscaling.
|
|
232
239
|
options.resolution = '2k';
|
|
233
240
|
}
|
|
234
241
|
if (quality) {
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import
|
|
1
|
+
import { writeJsonAtomicSync } from '../../../shared/atomic-file.mjs';
|
|
2
2
|
|
|
3
3
|
const BACKLOG_WARN_COOLDOWN_MS = 10 * 60_000;
|
|
4
4
|
const BACKLOG_WARN_FAILURES = 5;
|
|
@@ -16,7 +16,13 @@ export function createCycleHealthLedger({ cycleStateFile, log }) {
|
|
|
16
16
|
|
|
17
17
|
function write() {
|
|
18
18
|
try {
|
|
19
|
-
|
|
19
|
+
// Rewritten on every cycle event and read by a statusline that treats an
|
|
20
|
+
// unparsable file as "nothing to show", so no fsync; rename keeps it untorn.
|
|
21
|
+
writeJsonAtomicSync(
|
|
22
|
+
cycleStateFile,
|
|
23
|
+
{ running, backlog, cycles: health, updatedAt: Date.now() },
|
|
24
|
+
{ compact: true, fsync: false }
|
|
25
|
+
);
|
|
20
26
|
} catch {
|
|
21
27
|
/* best-effort; statusline just shows nothing */
|
|
22
28
|
}
|
|
@@ -1,10 +1,12 @@
|
|
|
1
1
|
// Embedding model files in the transformers.js cache layout
|
|
2
2
|
// (<cache>/<model id>/<path>), so copies that library already downloaded are
|
|
3
3
|
// reused; a missing file is fetched from the Hugging Face Hub once.
|
|
4
|
-
import { createWriteStream, existsSync, mkdirSync, renameSync, rmSync } from 'node:fs';
|
|
4
|
+
import { createWriteStream, existsSync, mkdirSync, readFileSync, renameSync, rmSync } from 'node:fs';
|
|
5
5
|
import { dirname, join } from 'node:path';
|
|
6
6
|
import { Readable } from 'node:stream';
|
|
7
7
|
import { pipeline } from 'node:stream/promises';
|
|
8
|
+
import { parseJsonText } from '../../shared/atomic-file.mjs';
|
|
9
|
+
import { fileHasData } from '../../shared/file-has-data.mjs';
|
|
8
10
|
|
|
9
11
|
const HUB = 'https://huggingface.co';
|
|
10
12
|
|
|
@@ -17,7 +19,8 @@ async function download(modelId, file, target, { optional = false } = {}) {
|
|
|
17
19
|
mkdirSync(dirname(target), { recursive: true });
|
|
18
20
|
const partial = `${target}.${process.pid}.part`;
|
|
19
21
|
try {
|
|
20
|
-
|
|
22
|
+
// flush: fsync before close, so the renamed file cannot be all NUL after a crash.
|
|
23
|
+
await pipeline(Readable.fromWeb(res.body), createWriteStream(partial, { flush: true }));
|
|
21
24
|
renameSync(partial, target);
|
|
22
25
|
} catch (error) {
|
|
23
26
|
rmSync(partial, { force: true });
|
|
@@ -26,19 +29,39 @@ async function download(modelId, file, target, { optional = false } = {}) {
|
|
|
26
29
|
return true;
|
|
27
30
|
}
|
|
28
31
|
|
|
29
|
-
|
|
32
|
+
// A cached file that holds no data (empty, NUL-filled by a crash) or, for
|
|
33
|
+
// JSON, does not parse is not present: it is fetched again.
|
|
34
|
+
function cachedFileUsable(target) {
|
|
35
|
+
if (!fileHasData(target)) return false;
|
|
36
|
+
if (!target.endsWith('.json')) return true;
|
|
37
|
+
try {
|
|
38
|
+
parseJsonText(readFileSync(target, 'utf8'));
|
|
39
|
+
return true;
|
|
40
|
+
} catch {
|
|
41
|
+
return false;
|
|
42
|
+
}
|
|
43
|
+
}
|
|
44
|
+
|
|
45
|
+
/** Local path of `file`, downloading it first when absent or damaged. */
|
|
30
46
|
export async function ensureModelFile(cacheDir, modelId, file) {
|
|
31
47
|
const target = join(cacheDir, ...modelId.split('/'), ...file.split('/'));
|
|
32
|
-
if (!
|
|
48
|
+
if (!cachedFileUsable(target)) await download(modelId, file, target);
|
|
33
49
|
return target;
|
|
34
50
|
}
|
|
35
51
|
|
|
36
52
|
/** Local path of an ONNX graph; a graph published with external weights gets them alongside. */
|
|
37
53
|
export async function ensureModelGraph(cacheDir, modelId, file) {
|
|
38
54
|
const target = join(cacheDir, ...modelId.split('/'), ...file.split('/'));
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
55
|
+
const weights = `${target}_data`;
|
|
56
|
+
const graphPresent = fileHasData(target);
|
|
57
|
+
// The weights are checked on their own: an external-weights file a crash
|
|
58
|
+
// left empty or NUL-filled is fetched again even when the graph is intact.
|
|
59
|
+
// They are fetched before the graph, so a cached graph implies its weights
|
|
60
|
+
// finished downloading; a weights file that is absent stays absent (graphs
|
|
61
|
+
// without external data have none).
|
|
62
|
+
if (!graphPresent || (existsSync(weights) && !fileHasData(weights))) {
|
|
63
|
+
await download(modelId, `${file}_data`, weights, { optional: true });
|
|
42
64
|
}
|
|
65
|
+
if (!graphPresent) await download(modelId, file, target);
|
|
43
66
|
return target;
|
|
44
67
|
}
|
|
@@ -2,10 +2,10 @@
|
|
|
2
2
|
// One incremental read of a transcript file: the bytes past the persisted
|
|
3
3
|
// offset, split into complete JSONL lines, stored as conversation rows. The
|
|
4
4
|
// persisted boundary only advances past lines that were fully consumed (parsed
|
|
5
|
-
// + inserted or intentionally skipped); a
|
|
6
|
-
//
|
|
7
|
-
//
|
|
8
|
-
//
|
|
5
|
+
// + inserted or intentionally skipped); a transient insert error stops the walk
|
|
6
|
+
// and leaves the boundary untouched so the next sweep retries from the same
|
|
7
|
+
// position instead of silently consuming the line forever. A complete line
|
|
8
|
+
// that is not JSON (crash damage) is skipped, never a permanent stop.
|
|
9
9
|
import fs from 'node:fs';
|
|
10
10
|
import path from 'node:path';
|
|
11
11
|
import { cwdFromTranscriptPath, transcriptRowFromLine } from './transcript-rows.mjs';
|
|
@@ -73,8 +73,12 @@ export async function ingestTranscriptTail({ db, transcriptPath, cwd, offsets, r
|
|
|
73
73
|
const boundary = { bytes: tail.bytes, lineIndex: tail.lineIndex, generation: tail.generation };
|
|
74
74
|
let index = tail.lineIndex;
|
|
75
75
|
let count = 0;
|
|
76
|
-
for (const { line, consumedBytes } of completeLines(tail.text)) {
|
|
77
|
-
|
|
76
|
+
for (const { line: rawLine, consumedBytes } of completeLines(tail.text)) {
|
|
77
|
+
// A crash leaves a run of NUL bytes where data never reached disk, and the
|
|
78
|
+
// resumed writer appends after it. A raw NUL is never valid JSON, so drop
|
|
79
|
+
// them and keep the record that follows.
|
|
80
|
+
const line = rawLine.includes('\0') ? rawLine.replaceAll('\0', '') : rawLine;
|
|
81
|
+
if (!line.trim()) {
|
|
78
82
|
boundary.bytes += consumedBytes;
|
|
79
83
|
continue;
|
|
80
84
|
}
|
|
@@ -82,7 +86,12 @@ export async function ingestTranscriptTail({ db, transcriptPath, cwd, offsets, r
|
|
|
82
86
|
try {
|
|
83
87
|
parsed = JSON.parse(line);
|
|
84
88
|
} catch {
|
|
85
|
-
|
|
89
|
+
// The line is complete (a half-written one has no newline yet and is not
|
|
90
|
+
// yielded), so it can never become valid: skip it for good, or it would
|
|
91
|
+
// block every line after it.
|
|
92
|
+
log(`[transcript-watch] skipped a damaged line in ${path.basename(transcriptPath)}\n`);
|
|
93
|
+
boundary.bytes += consumedBytes;
|
|
94
|
+
continue;
|
|
86
95
|
}
|
|
87
96
|
index += 1;
|
|
88
97
|
const row = transcriptRowFromLine(parsed);
|
|
@@ -86,11 +86,23 @@ export function parseCycle1LineFormat(raw) {
|
|
|
86
86
|
const chunks = [];
|
|
87
87
|
for (const line of lines) {
|
|
88
88
|
const parts = line.split('|');
|
|
89
|
-
|
|
89
|
+
// Models often write a run as `1-8` (sessions 2026-10-22: 32 of 71 lines);
|
|
90
|
+
// an ascending range expands to its indexes, everything else stays invalid.
|
|
91
|
+
if (parts.length < 4 || !/^\d+(?:\s*-\s*\d+)?(?:\s*,\s*\d+(?:\s*-\s*\d+)?)*$/.test(parts[0].trim())) {
|
|
90
92
|
chunks.push({ _idxList: [], _parseError: 'invalid_line_format' });
|
|
91
93
|
continue;
|
|
92
94
|
}
|
|
93
|
-
const indexes =
|
|
95
|
+
const indexes = [];
|
|
96
|
+
for (const token of parts[0].split(',')) {
|
|
97
|
+
const [from, to = from] = token.split('-').map((value) => Number(value.trim()));
|
|
98
|
+
// Past the safe-integer range `n + 1 === n`, so the expansion would never end.
|
|
99
|
+
if (!Number.isSafeInteger(from) || !Number.isSafeInteger(to) || to < from || to - from >= 1000) {
|
|
100
|
+
indexes.length = 0;
|
|
101
|
+
indexes.push(0);
|
|
102
|
+
break;
|
|
103
|
+
}
|
|
104
|
+
for (let n = from; n <= to; n += 1) indexes.push(n);
|
|
105
|
+
}
|
|
94
106
|
chunks.push({
|
|
95
107
|
_idxList: indexes,
|
|
96
108
|
element: parts[1].trim(),
|
|
@@ -26,6 +26,7 @@ import { readdir, readFile, rm, stat } from 'node:fs/promises';
|
|
|
26
26
|
import { join } from 'node:path';
|
|
27
27
|
import { execFile, spawn, spawnSync } from 'node:child_process';
|
|
28
28
|
import { createConnection, createServer } from 'node:net';
|
|
29
|
+
import { holdsNoData, writeFileAtomicSync } from '../../../shared/atomic-file.mjs';
|
|
29
30
|
import { isPidAlive } from '../../../shared/pid-liveness.mjs';
|
|
30
31
|
import { runChild } from './run-child.mjs';
|
|
31
32
|
|
|
@@ -180,9 +181,12 @@ function stripPlatformInvalidLines(conf) {
|
|
|
180
181
|
function ensureConfV2(pgdataDir) {
|
|
181
182
|
const confPath = join(pgdataDir, 'postgresql.conf');
|
|
182
183
|
if (!existsSync(confPath)) return false;
|
|
183
|
-
|
|
184
|
+
// A conf a crash left empty or NUL-filled cannot be parsed by PostgreSQL; the
|
|
185
|
+
// defaults plus the overrides appended below are a working config.
|
|
186
|
+
const text = readFileSync(confPath, 'utf8');
|
|
187
|
+
const original = holdsNoData(text) ? '' : text;
|
|
184
188
|
let conf = stripPlatformInvalidLines(original);
|
|
185
|
-
let changed = conf !==
|
|
189
|
+
let changed = conf !== text;
|
|
186
190
|
if (!conf.includes(MIXDOG_CONF_V2_MARKER)) {
|
|
187
191
|
conf += buildV2Block();
|
|
188
192
|
changed = true;
|
|
@@ -196,7 +200,7 @@ function ensureConfV2(pgdataDir) {
|
|
|
196
200
|
changed = true;
|
|
197
201
|
}
|
|
198
202
|
if (!changed) return false;
|
|
199
|
-
|
|
203
|
+
writeFileAtomicSync(confPath, conf, { backup: true });
|
|
200
204
|
return true;
|
|
201
205
|
}
|
|
202
206
|
|
|
@@ -350,7 +354,7 @@ async function initClusterIfNeeded({ runtimeDir, pgdataDir, env }) {
|
|
|
350
354
|
const confPath = join(pgdataDir, 'postgresql.conf');
|
|
351
355
|
try {
|
|
352
356
|
const existing = readFileSync(confPath, 'utf8');
|
|
353
|
-
|
|
357
|
+
writeFileAtomicSync(confPath, existing + freshClusterConfAppend());
|
|
354
358
|
} catch (e) {
|
|
355
359
|
__mixdogMemoryLog(`[pg-process] postgresql.conf append failed: ${e?.message}\n`);
|
|
356
360
|
}
|