mixdog 1.2.5 → 1.3.0
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/LICENSES/simple-icons-CC0-1.0.txt +30 -0
- package/NOTICE.md +19 -0
- package/package.json +4 -1
- package/scripts/check-model-recommendations.mjs +89 -0
- package/scripts/gate-local.mjs +7 -0
- package/scripts/lib/gate-scheduler.mjs +2 -2
- package/scripts/lib/isolated-test-env.mjs +16 -0
- package/scripts/lib/run-node-tests.mjs +5 -14
- package/scripts/provider-pingpong-live.mjs +9 -9
- package/src/agents/advisor/AGENT.md +4 -4
- package/src/agents/advisor/agent.json +1 -1
- package/src/agents/reviewer/AGENT.md +5 -4
- package/src/agents/reviewer/agent.json +1 -1
- package/src/defaults/shipped-history.json +17 -0
- package/src/defaults/skills/browser-use/SKILL.md +7 -1
- package/src/defaults/skills/computer-use/SKILL.md +3 -1
- package/src/defaults/skills/memory-management/SKILL.md +4 -1
- package/src/defaults/skills/setup/references/actions.md +11 -4
- package/src/defaults/skills/setup-guide/SKILL.md +175 -0
- package/src/defaults/skills/setup-guide/model-recommendations.json +333 -0
- package/src/defaults/skills/visualize/SKILL.md +3 -2
- package/src/lib/rules-builder.cjs +56 -7
- package/src/output-styles/common.md +22 -6
- package/src/output-styles/detailed.md +7 -5
- package/src/output-styles/minimal.md +7 -3
- package/src/output-styles/simple.md +2 -3
- package/src/rules/agent/AGENT.md +7 -1
- package/src/rules/agent-mode/AGENT_MODE.md +24 -0
- package/src/rules/agent-mode/MIXY.md +21 -0
- package/src/rules/agent-mode/ROUTINE.md +6 -0
- package/src/rules/lead/LEAD.md +9 -4
- package/src/rules/shared/10-tool-workflow.md +2 -1
- package/src/rules/shared/60-verification.md +4 -5
- package/src/rules/shared/70-delivery.md +3 -0
- package/src/runtime/agent/orchestrator/agent-runtime/cache-strategy.mjs +2 -1
- package/src/runtime/agent/orchestrator/agent-runtime/room-supervisor-completion.mjs +86 -0
- package/src/runtime/agent/orchestrator/config.mjs +6 -0
- package/src/runtime/agent/orchestrator/context/deferred-tools.mjs +77 -14
- package/src/runtime/agent/orchestrator/context/role-instructions.mjs +182 -13
- package/src/runtime/agent/orchestrator/context/skill-catalog.mjs +5 -3
- package/src/runtime/agent/orchestrator/internal-agents.mjs +10 -0
- package/src/runtime/agent/orchestrator/mcp/agent-connectors.mjs +215 -0
- package/src/runtime/agent/orchestrator/mcp/child-tree.mjs +58 -64
- package/src/runtime/agent/orchestrator/mcp/client-transport.mjs +10 -1
- package/src/runtime/agent/orchestrator/mcp/client.mjs +122 -15
- package/src/runtime/agent/orchestrator/mcp/oauth-account.mjs +97 -0
- package/src/runtime/agent/orchestrator/mcp/oauth-callback.mjs +2 -3
- package/src/runtime/agent/orchestrator/mcp/oauth-client.mjs +49 -0
- package/src/runtime/agent/orchestrator/mcp/oauth-credentials.mjs +59 -9
- package/src/runtime/agent/orchestrator/mcp/oauth-login.mjs +32 -7
- package/src/runtime/agent/orchestrator/mcp/oauth-provider.mjs +45 -6
- package/src/runtime/agent/orchestrator/mcp/oauth.mjs +195 -6
- package/src/runtime/agent/orchestrator/mcp/server-connectors.mjs +17 -0
- package/src/runtime/agent/orchestrator/providers/account-pool.mjs +9 -2
- package/src/runtime/agent/orchestrator/providers/anthropic-oauth-client-version.mjs +1 -1
- package/src/runtime/agent/orchestrator/providers/anthropic-oauth-credentials.mjs +41 -19
- package/src/runtime/agent/orchestrator/providers/anthropic-oauth.mjs +3 -2
- package/src/runtime/agent/orchestrator/providers/anthropic-sse-turn/turn-events/content-block-start.mjs +4 -1
- package/src/runtime/agent/orchestrator/providers/anthropic-sse-turn/turn-events.mjs +2 -0
- package/src/runtime/agent/orchestrator/providers/anthropic-sse-turn.mjs +3 -0
- package/src/runtime/agent/orchestrator/providers/anthropic-sse.mjs +2 -1
- package/src/runtime/agent/orchestrator/providers/anthropic.mjs +3 -1
- package/src/runtime/agent/orchestrator/providers/antigravity-oauth-login.mjs +11 -5
- package/src/runtime/agent/orchestrator/providers/antigravity-oauth-tokens.mjs +4 -0
- package/src/runtime/agent/orchestrator/providers/catalog-deadline.mjs +163 -0
- package/src/runtime/agent/orchestrator/providers/catalog-failures.mjs +34 -0
- package/src/runtime/agent/orchestrator/providers/cursor-client-version.mjs +1 -1
- package/src/runtime/agent/orchestrator/providers/grok-oauth-login.mjs +38 -14
- package/src/runtime/agent/orchestrator/providers/lib/oauth-page.mjs +100 -26
- package/src/runtime/agent/orchestrator/providers/lib/send-callbacks.mjs +24 -0
- package/src/runtime/agent/orchestrator/providers/oauth-credential-probes.mjs +13 -0
- package/src/runtime/agent/orchestrator/providers/oauth-usage.mjs +2 -1
- package/src/runtime/agent/orchestrator/providers/openai-compat-chat-send.mjs +1 -0
- package/src/runtime/agent/orchestrator/providers/openai-compat-chat-stream/chunk-events.mjs +4 -0
- package/src/runtime/agent/orchestrator/providers/openai-compat-chat-stream/stream-state.mjs +3 -1
- package/src/runtime/agent/orchestrator/providers/openai-compat-chat-stream.mjs +2 -0
- package/src/runtime/agent/orchestrator/providers/openai-compat-models.mjs +49 -4
- package/src/runtime/agent/orchestrator/providers/openai-compat-presets.mjs +0 -7
- package/src/runtime/agent/orchestrator/providers/openai-compat-responses-events.mjs +7 -1
- package/src/runtime/agent/orchestrator/providers/openai-compat-responses.mjs +1 -0
- package/src/runtime/agent/orchestrator/providers/openai-compat-stream.mjs +2 -0
- package/src/runtime/agent/orchestrator/providers/openai-compat-xai-send.mjs +1 -0
- package/src/runtime/agent/orchestrator/providers/openai-error-contract.mjs +17 -12
- package/src/runtime/agent/orchestrator/providers/openai-http-sse-response-state/response-events.mjs +5 -1
- package/src/runtime/agent/orchestrator/providers/openai-http-sse-response-state.mjs +11 -2
- package/src/runtime/agent/orchestrator/providers/openai-oauth-http-sse.mjs +1 -0
- package/src/runtime/agent/orchestrator/providers/openai-oauth-login.mjs +45 -16
- package/src/runtime/agent/orchestrator/providers/openai-oauth-tokens.mjs +10 -0
- package/src/runtime/agent/orchestrator/providers/openai-oauth-ws.mjs +1 -0
- package/src/runtime/agent/orchestrator/providers/openai-ws-connect.mjs +196 -0
- package/src/runtime/agent/orchestrator/providers/openai-ws-pool.mjs +74 -9
- package/src/runtime/agent/orchestrator/providers/openai-ws-stream/event-switch.mjs +14 -1
- package/src/runtime/agent/orchestrator/providers/openai-ws-stream.mjs +2 -0
- package/src/runtime/agent/orchestrator/providers/opencode-go-oauth-login.mjs +194 -0
- package/src/runtime/agent/orchestrator/providers/opencode-go-oauth-tokens.mjs +353 -0
- package/src/runtime/agent/orchestrator/providers/opencode-go-oauth.mjs +293 -0
- package/src/runtime/agent/orchestrator/providers/opencode-go-usage.mjs +33 -100
- package/src/runtime/agent/orchestrator/providers/opencode-go.mjs +23 -14
- package/src/runtime/agent/orchestrator/providers/registry.mjs +41 -27
- package/src/runtime/agent/orchestrator/runtime-core/ask-user-tool-defs.mjs +98 -4
- package/src/runtime/agent/orchestrator/runtime-core/context-status-shape.mjs +4 -0
- package/src/runtime/agent/orchestrator/runtime-core/extension-scopes.mjs +6 -4
- package/src/runtime/agent/orchestrator/runtime-core/orchestration.mjs +15 -8
- package/src/runtime/agent/orchestrator/runtime-core/plugin-mcp.mjs +1 -0
- package/src/runtime/agent/orchestrator/runtime-core/tool-bridge.mjs +12 -6
- package/src/runtime/agent/orchestrator/runtime-core/tool-catalog.mjs +32 -5
- package/src/runtime/agent/orchestrator/runtime-core/tool-defs.mjs +10 -4
- package/src/runtime/agent/orchestrator/runtime-core/workflow/agents.mjs +4 -1
- package/src/runtime/agent/orchestrator/runtime-core/workflow/context.mjs +50 -20
- package/src/runtime/agent/orchestrator/runtime-core/workflow-ids.mjs +2 -0
- package/src/runtime/agent/orchestrator/session/agent-home.d.mts +4 -0
- package/src/runtime/agent/orchestrator/session/agent-home.mjs +33 -0
- package/src/runtime/agent/orchestrator/session/agent-mode.mjs +62 -0
- package/src/runtime/agent/orchestrator/session/agent-pool-progress.mjs +15 -3
- package/src/runtime/agent/orchestrator/session/agent-room-supervisor.d.mts +31 -0
- package/src/runtime/agent/orchestrator/session/agent-room-supervisor.mjs +241 -0
- package/src/runtime/agent/orchestrator/session/agent-room.d.mts +162 -0
- package/src/runtime/agent/orchestrator/session/agent-room.mjs +641 -0
- package/src/runtime/agent/orchestrator/session/catalog-db/schema.mjs +7 -1
- package/src/runtime/agent/orchestrator/session/compact/execution-tail.mjs +74 -13
- package/src/runtime/agent/orchestrator/session/compact/forked-summary.mjs +102 -0
- package/src/runtime/agent/orchestrator/session/compact/runner.mjs +8 -2
- package/src/runtime/agent/orchestrator/session/compact/summary.mjs +16 -0
- package/src/runtime/agent/orchestrator/session/compact/user-message-kind.mjs +2 -0
- package/src/runtime/agent/orchestrator/session/compaction-archive-capture.mjs +6 -2
- package/src/runtime/agent/orchestrator/session/human-gate/gate.mjs +165 -0
- package/src/runtime/agent/orchestrator/session/human-gate/held-message.mjs +95 -0
- package/src/runtime/agent/orchestrator/session/human-gate/outbound-policy.mjs +221 -0
- package/src/runtime/agent/orchestrator/session/human-gate/requests.mjs +195 -0
- package/src/runtime/agent/orchestrator/session/human-gate/store.mjs +167 -0
- package/src/runtime/agent/orchestrator/session/lifecycle-scan.mjs +9 -0
- package/src/runtime/agent/orchestrator/session/loop/compact-policy.mjs +3 -1
- package/src/runtime/agent/orchestrator/session/loop/fresh-context.mjs +39 -2
- package/src/runtime/agent/orchestrator/session/loop/pre-dispatch-deny.mjs +10 -0
- package/src/runtime/agent/orchestrator/session/loop/send-phase.mjs +16 -1
- package/src/runtime/agent/orchestrator/session/loop/tool-exec/before-hook.mjs +82 -33
- package/src/runtime/agent/orchestrator/session/loop/tool-exec.mjs +11 -4
- package/src/runtime/agent/orchestrator/session/loop/tool-exposure-migration.mjs +5 -4
- package/src/runtime/agent/orchestrator/session/loop/tool-helpers.mjs +13 -3
- package/src/runtime/agent/orchestrator/session/loop/usage.mjs +7 -0
- package/src/runtime/agent/orchestrator/session/manager/ask-turn-execute.mjs +1 -0
- package/src/runtime/agent/orchestrator/session/manager/ask-turn-loop-options.mjs +6 -1
- package/src/runtime/agent/orchestrator/session/manager/ask-turn-prepare.mjs +6 -0
- package/src/runtime/agent/orchestrator/session/manager/ask-turn-settle.mjs +2 -0
- package/src/runtime/agent/orchestrator/session/manager/ask-turn-steering-drain.mjs +13 -5
- package/src/runtime/agent/orchestrator/session/manager/pending-lifecycle-epoch.mjs +0 -8
- package/src/runtime/agent/orchestrator/session/manager/pending-message-entry.mjs +10 -7
- package/src/runtime/agent/orchestrator/session/manager/pending-messages.mjs +62 -22
- package/src/runtime/agent/orchestrator/session/manager/prompt-utils.mjs +44 -16
- package/src/runtime/agent/orchestrator/session/manager/rules-cache.mjs +22 -5
- package/src/runtime/agent/orchestrator/session/manager/runtime-liveness.mjs +23 -1
- package/src/runtime/agent/orchestrator/session/manager/session-close.mjs +81 -34
- package/src/runtime/agent/orchestrator/session/manager/session-crud.mjs +39 -4
- package/src/runtime/agent/orchestrator/session/manager/session-lifecycle.mjs +27 -5
- package/src/runtime/agent/orchestrator/session/manager/session-prompt-composition.mjs +55 -8
- package/src/runtime/agent/orchestrator/session/manager/session-record.mjs +16 -0
- package/src/runtime/agent/orchestrator/session/manager/tool-resolution.mjs +3 -2
- package/src/runtime/agent/orchestrator/session/manager/usage-metrics.mjs +6 -0
- package/src/runtime/agent/orchestrator/session/manager/workflow-switch-notice.mjs +58 -0
- package/src/runtime/agent/orchestrator/session/manager.mjs +2 -2
- package/src/runtime/agent/orchestrator/session/mixy-name.mjs +24 -0
- package/src/runtime/agent/orchestrator/session/pre-send-compact.mjs +32 -5
- package/src/runtime/agent/orchestrator/session/request-fork.mjs +47 -0
- package/src/runtime/agent/orchestrator/session/routine-result.d.mts +70 -0
- package/src/runtime/agent/orchestrator/session/routine-result.mjs +277 -0
- package/src/runtime/agent/orchestrator/session/side-question-store.mjs +16 -1
- package/src/runtime/agent/orchestrator/session/side-question.mjs +5 -43
- package/src/runtime/agent/orchestrator/session/store/fs-probe.mjs +10 -0
- package/src/runtime/agent/orchestrator/session/store/lifecycle-barriers.mjs +73 -6
- package/src/runtime/agent/orchestrator/session/store/load-session.mjs +40 -0
- package/src/runtime/agent/orchestrator/session/store/save-fault.mjs +15 -0
- package/src/runtime/agent/orchestrator/session/store/session-paths.mjs +12 -0
- package/src/runtime/agent/orchestrator/session/store/sweep/sweep-row.mjs +8 -0
- package/src/runtime/agent/orchestrator/session/store-agent-worker-header.mjs +36 -0
- package/src/runtime/agent/orchestrator/session/store-agent-worker-rows.mjs +258 -43
- package/src/runtime/agent/orchestrator/session/store-record-strict-scan.mjs +325 -0
- package/src/runtime/agent/orchestrator/session/store-summary-catalog.mjs +84 -21
- package/src/runtime/agent/orchestrator/session/store-summary-index.mjs +44 -12
- package/src/runtime/agent/orchestrator/session/store-summary-last-message.mjs +46 -0
- package/src/runtime/agent/orchestrator/session/store-summary-reader.mjs +46 -30
- package/src/runtime/agent/orchestrator/session/store-summary-visibility.mjs +0 -4
- package/src/runtime/agent/orchestrator/session/store-transcript-cache.mjs +144 -45
- package/src/runtime/agent/orchestrator/session/store-transcript-projection.mjs +32 -13
- package/src/runtime/agent/orchestrator/session/store-transcript-worker.mjs +364 -38
- package/src/runtime/agent/orchestrator/session/store.mjs +9 -2
- package/src/runtime/agent/orchestrator/session/task-wait-control.mjs +43 -12
- package/src/runtime/agent/orchestrator/session/tool-result-offload.mjs +23 -6
- package/src/runtime/agent/orchestrator/session/transcript-restore/restore.mjs +44 -1
- package/src/runtime/agent/orchestrator/tools/builtin/arg-guard.mjs +12 -6
- package/src/runtime/agent/orchestrator/tools/builtin/bash-tool/backgrounded-result.mjs +13 -7
- package/src/runtime/agent/orchestrator/tools/builtin/bash-tool/bashism-rewrite.mjs +1 -1
- package/src/runtime/agent/orchestrator/tools/builtin/bash-tool/benign-exit.mjs +7 -1
- package/src/runtime/agent/orchestrator/tools/builtin/bash-tool/completed-result.mjs +40 -7
- package/src/runtime/agent/orchestrator/tools/builtin/bash-tool/deadline-plan.mjs +37 -34
- package/src/runtime/agent/orchestrator/tools/builtin/bash-tool/inline-hoist.mjs +1 -1
- package/src/runtime/agent/orchestrator/tools/builtin/bash-tool/spawn-env.mjs +0 -4
- package/src/runtime/agent/orchestrator/tools/builtin/bash-tool.mjs +6 -0
- package/src/runtime/agent/orchestrator/tools/builtin/builtin-tools.mjs +29 -27
- package/src/runtime/agent/orchestrator/tools/builtin/external-tool-adapters.mjs +1 -1
- package/src/runtime/agent/orchestrator/tools/builtin/find-files-tool.mjs +28 -11
- package/src/runtime/agent/orchestrator/tools/builtin/git-command-policy.mjs +12 -3
- package/src/runtime/agent/orchestrator/tools/builtin/git-command-tool.mjs +203 -101
- package/src/runtime/agent/orchestrator/tools/builtin/git-repo-rw-lock.mjs +90 -41
- package/src/runtime/agent/orchestrator/tools/builtin/lib/list-helpers.mjs +17 -1
- package/src/runtime/agent/orchestrator/tools/builtin/lib/shell-delivery.mjs +199 -0
- package/src/runtime/agent/orchestrator/tools/builtin/lib/shell-job-records.mjs +12 -0
- package/src/runtime/agent/orchestrator/tools/builtin/lib/shell-task-output.mjs +165 -39
- package/src/runtime/agent/orchestrator/tools/builtin/list-tool.mjs +25 -13
- package/src/runtime/agent/orchestrator/tools/builtin/native-search-client.mjs +113 -5
- package/src/runtime/agent/orchestrator/tools/builtin/read-batch.mjs +7 -6
- package/src/runtime/agent/orchestrator/tools/builtin/read-formatting.mjs +19 -4
- package/src/runtime/agent/orchestrator/tools/builtin/read-line-collector.mjs +5 -3
- package/src/runtime/agent/orchestrator/tools/builtin/read-single-render.mjs +25 -7
- package/src/runtime/agent/orchestrator/tools/builtin/read-special-files.mjs +2 -1
- package/src/runtime/agent/orchestrator/tools/builtin/read-tool/batch-render.mjs +3 -1
- package/src/runtime/agent/orchestrator/tools/builtin/search-glob-tool.mjs +12 -5
- package/src/runtime/agent/orchestrator/tools/builtin/search-grep-tool.mjs +61 -22
- package/src/runtime/agent/orchestrator/tools/builtin/search-path-diagnostics.mjs +32 -8
- package/src/runtime/agent/orchestrator/tools/builtin/shell-analysis.mjs +193 -19
- package/src/runtime/agent/orchestrator/tools/builtin/shell-direct-exe.mjs +57 -7
- package/src/runtime/agent/orchestrator/tools/builtin/shell-jobs.mjs +123 -32
- package/src/runtime/agent/orchestrator/tools/builtin/shell-lossless-compact.mjs +26 -1
- package/src/runtime/agent/orchestrator/tools/builtin/shell-output.mjs +13 -28
- package/src/runtime/agent/orchestrator/tools/builtin/task-tool.mjs +90 -11
- package/src/runtime/agent/orchestrator/tools/builtin/wait-budgets.mjs +23 -0
- package/src/runtime/agent/orchestrator/tools/builtin.mjs +54 -10
- package/src/runtime/agent/orchestrator/tools/code-graph/build.mjs +22 -28
- package/src/runtime/agent/orchestrator/tools/code-graph/dispatch.mjs +11 -7
- package/src/runtime/agent/orchestrator/tools/destructive-warning.mjs +126 -9
- package/src/runtime/agent/orchestrator/tools/graph-manifest.json +13 -13
- package/src/runtime/agent/orchestrator/tools/lib/owned-shell-trees.mjs +15 -6
- package/src/runtime/agent/orchestrator/tools/lib/shell-descendants.mjs +64 -2
- package/src/runtime/agent/orchestrator/tools/lib/shell-run-background/promote-job.mjs +10 -2
- package/src/runtime/agent/orchestrator/tools/lib/shell-run-background/promotion-results.mjs +13 -3
- package/src/runtime/agent/orchestrator/tools/lib/shell-run-settle.mjs +8 -0
- package/src/runtime/agent/orchestrator/tools/lib/shell-run-state.mjs +6 -1
- package/src/runtime/agent/orchestrator/tools/patch/v4a-convert.mjs +1 -1
- package/src/runtime/agent/orchestrator/tools/shell-command.mjs +104 -55
- package/src/runtime/agent/orchestrator/tools/shell-policy.mjs +7 -2
- package/src/runtime/agent/orchestrator/tools/shell-powershell.mjs +39 -23
- package/src/runtime/agent/orchestrator/tools/shell-snapshot.mjs +6 -2
- package/src/runtime/agent/orchestrator/tools/spawn-manifest.json +13 -13
- package/src/runtime/attachments/office-extract.mjs +11 -9
- package/src/runtime/browser-bridge/client.mjs +24 -0
- package/src/runtime/channels/lib/scheduler.mjs +40 -12
- package/src/runtime/channels/lib/schedules-db.mjs +71 -12
- package/src/runtime/channels/lib/webhook/session-dispatch.mjs +36 -16
- package/src/runtime/channels/lib/webhook.mjs +0 -1
- package/src/runtime/channels/lib/webhooks-db.mjs +100 -10
- package/src/runtime/channels/lib/worker-main.mjs +17 -20
- package/src/runtime/computer-bridge/client.mjs +28 -1
- package/src/runtime/connectors/gmail-app-password.mjs +690 -0
- package/src/runtime/connectors/google-mcp.mjs +446 -0
- package/src/runtime/memory/lib/core-memory-access.mjs +69 -0
- package/src/runtime/memory/lib/core-memory-file.mjs +16 -8
- package/src/runtime/memory/lib/core-memory-index.mjs +15 -9
- package/src/runtime/memory/lib/core-memory-management.mjs +10 -4
- package/src/runtime/memory/lib/core-memory-store.mjs +53 -30
- package/src/runtime/memory/lib/core-memory-uniqueness.mjs +10 -6
- package/src/runtime/memory/lib/embedding-tokenizer.mjs +32 -18
- package/src/runtime/memory/lib/http-router/session-routes.mjs +3 -2
- package/src/runtime/memory/lib/memory-action-handlers/core-actions.mjs +44 -14
- package/src/runtime/memory/lib/memory-schema/core-and-meta.mjs +11 -0
- package/src/runtime/memory/lib/memory.mjs +7 -1
- package/src/runtime/memory/lib/query-core-recall.mjs +9 -1
- package/src/runtime/memory/lib/query-hybrid-search.mjs +2 -0
- package/src/runtime/memory/lib/runtime-fetcher.mjs +1 -1
- package/src/runtime/memory/lib/tool-call-handler.mjs +9 -5
- package/src/runtime/memory/tool-defs.mjs +5 -0
- package/src/runtime/office/authoring/pptx-script-normalize.mjs +11 -8
- package/src/runtime/office/core/tabular.mjs +1 -1
- package/src/runtime/office/design/library/design-template-inspect.mjs +7 -4
- package/src/runtime/office/portable/docx-formatting.mjs +1 -1
- package/src/runtime/office/portable/docx-runs.mjs +1 -1
- package/src/runtime/office/portable/docx-tracked-edits.mjs +2 -2
- package/src/runtime/office/portable/portable-docx-edits.mjs +11 -10
- package/src/runtime/office/portable/portable-docx-operations.mjs +21 -17
- package/src/runtime/office/portable/portable-docx-parts.mjs +15 -9
- package/src/runtime/office/portable/portable-docx-styles.mjs +4 -1
- package/src/runtime/office/portable/portable-docx-xml.mjs +9 -9
- package/src/runtime/office/portable/portable-opc.mjs +27 -13
- package/src/runtime/office/portable/portable-pivot.mjs +3 -3
- package/src/runtime/office/portable/portable-pptx-chart.mjs +3 -3
- package/src/runtime/office/portable/portable-pptx-charts.mjs +24 -18
- package/src/runtime/office/portable/portable-pptx-core.mjs +5 -5
- package/src/runtime/office/portable/portable-pptx-deck.mjs +8 -4
- package/src/runtime/office/portable/portable-pptx-package.mjs +7 -4
- package/src/runtime/office/portable/portable-pptx-shapes.mjs +18 -9
- package/src/runtime/office/portable/portable-sheet-parts.mjs +9 -3
- package/src/runtime/office/portable/portable-sheet-xml.mjs +5 -5
- package/src/runtime/office/portable/portable-xlsx-charts.mjs +4 -1
- package/src/runtime/office/portable/portable-xlsx-drawings.mjs +10 -6
- package/src/runtime/office/portable/portable-xlsx-operations.mjs +8 -5
- package/src/runtime/office/portable/portable-xlsx-pivot-ops.mjs +1 -1
- package/src/runtime/office/portable/portable-xlsx-reference-shift.mjs +11 -7
- package/src/runtime/office/portable/portable-xlsx-sheet-edits.mjs +6 -3
- package/src/runtime/office/portable/portable-xml.mjs +1 -1
- package/src/runtime/shared/agent-avatar-sprites.mjs +445 -0
- package/src/runtime/shared/agent-avatar.d.mts +64 -0
- package/src/runtime/shared/agent-avatar.mjs +320 -0
- package/src/runtime/shared/agent-route-config.d.mts +3 -0
- package/src/runtime/shared/agent-route-config.mjs +16 -0
- package/src/runtime/shared/atomic-file.mjs +36 -14
- package/src/runtime/shared/background-tasks.mjs +60 -20
- package/src/runtime/shared/child-spawn-gate.mjs +26 -16
- package/src/runtime/shared/config.mjs +8 -11
- package/src/runtime/shared/connector-accounts.mjs +15 -0
- package/src/runtime/shared/connector-catalog.d.mts +52 -0
- package/src/runtime/shared/connector-catalog.mjs +230 -0
- package/src/runtime/shared/connector-triggers.d.mts +3 -0
- package/src/runtime/shared/connector-triggers.mjs +43 -0
- package/src/runtime/shared/file-lock.mjs +7 -1
- package/src/runtime/shared/file-permissions-worker.mjs +38 -0
- package/src/runtime/shared/file-permissions.d.mts +19 -4
- package/src/runtime/shared/file-permissions.mjs +310 -4
- package/src/runtime/shared/git-runner-worker.mjs +6 -78
- package/src/runtime/shared/google-oauth-client.mjs +24 -0
- package/src/runtime/shared/held-request-host.d.mts +12 -0
- package/src/runtime/shared/held-request-host.mjs +71 -0
- package/src/runtime/shared/llm/http-agent.mjs +7 -3
- package/src/runtime/shared/llm/provider-catalog-registry.mjs +10 -0
- package/src/runtime/shared/llm/usage-accounting.mjs +2 -0
- package/src/runtime/shared/llm/usage-ledger-import.mjs +32 -3
- package/src/runtime/shared/llm/usage-ledger-rollup.mjs +12 -5
- package/src/runtime/shared/llm/usage-ledger-worker.mjs +1 -1
- package/src/runtime/shared/llm/usage-ledger.mjs +20 -5
- package/src/runtime/shared/memory-snapshot.mjs +5 -1
- package/src/runtime/shared/notify-trace.mjs +8 -1
- package/src/runtime/shared/plain-preview.d.mts +1 -0
- package/src/runtime/shared/plain-preview.mjs +28 -0
- package/src/runtime/shared/pristine-execution-contract.json +5 -1
- package/src/runtime/shared/process-runner-worker.mjs +7 -0
- package/src/runtime/shared/process-runner.mjs +38 -0
- package/src/runtime/shared/provider-accounts.mjs +2 -1
- package/src/runtime/shared/provider-api-key.mjs +0 -1
- package/src/runtime/shared/read-row-numbers.mjs +1 -1
- package/src/runtime/shared/resource-admission.mjs +60 -18
- package/src/runtime/shared/routine-limits.d.mts +11 -0
- package/src/runtime/shared/routine-limits.mjs +21 -0
- package/src/runtime/shared/routine-trigger.d.mts +10 -0
- package/src/runtime/shared/routine-trigger.mjs +44 -0
- package/src/runtime/shared/run-child.mjs +84 -0
- package/src/runtime/shared/schedule-model-ref.mjs +9 -10
- package/src/runtime/shared/session-activity-signal.mjs +23 -0
- package/src/runtime/shared/shell-output-summary.mjs +23 -0
- package/src/runtime/shared/singleton-owner.mjs +126 -42
- package/src/runtime/shared/slow-span.mjs +4 -12
- package/src/runtime/shared/statusline/statusline-segments.mjs +13 -1
- package/src/runtime/shared/tool-output-limit.mjs +4 -2
- package/src/runtime/shared/tool-output-marker.mjs +58 -0
- package/src/runtime/shared/transcript-metadata.mjs +10 -0
- package/src/runtime/shared/turn-changes.mjs +0 -8
- package/src/runtime/shared/usage-readout-totals.mjs +56 -0
- package/src/runtime/shared/user-data-guard.mjs +131 -34
- package/src/runtime/shared/worktrees/activity-gate.mjs +33 -8
- package/src/runtime/shared/worktrees/git-head.mjs +20 -2
- package/src/runtime/shared/worktrees/lock.mjs +3 -2
- package/src/runtime/terminal-bridge/tool-defs.mjs +1 -1
- package/src/runtime/tidy/runners/biome.mjs +3 -1
- package/src/runtime/tidy/shadow.mjs +2 -1
- package/src/runtime/web-search/index.mjs +5 -7
- package/src/runtime/web-search/lib/formatter.mjs +42 -6
- package/src/runtime/web-search/tool-defs.mjs +6 -1
- package/src/session-runtime/agent-home-delivery.mjs +459 -0
- package/src/session-runtime/agent-profile-changes.mjs +21 -0
- package/src/session-runtime/agent-room-delivery.mjs +92 -0
- package/src/session-runtime/agent-task-sessions.mjs +200 -0
- package/src/session-runtime/boot/apis.mjs +50 -18
- package/src/session-runtime/boot/facade.mjs +5 -8
- package/src/session-runtime/boot/infrastructure.mjs +28 -17
- package/src/session-runtime/boot/tools.mjs +3 -0
- package/src/session-runtime/channel-config-api.mjs +53 -5
- package/src/session-runtime/cwd-plugins/core-memory-context.mjs +36 -20
- package/src/session-runtime/cwd-plugins/plugin-status.mjs +6 -1
- package/src/session-runtime/held-requests.mjs +228 -0
- package/src/session-runtime/internal-tool-executor/ask-user-tool.mjs +121 -0
- package/src/session-runtime/internal-tool-executor/feature-tools.mjs +5 -1
- package/src/session-runtime/internal-tool-executor/knowledge-tools.mjs +31 -2
- package/src/session-runtime/internal-tool-executor/workspace-tools.mjs +31 -2
- package/src/session-runtime/internal-tool-executor.mjs +2 -0
- package/src/session-runtime/lifecycle/session-catalog.mjs +9 -0
- package/src/session-runtime/lifecycle/session-switch/context-switch.mjs +4 -3
- package/src/session-runtime/lifecycle/session-switch/resume.mjs +11 -4
- package/src/session-runtime/lifecycle/session-switch/session-delete.mjs +3 -3
- package/src/session-runtime/lifecycle/shared.mjs +25 -2
- package/src/session-runtime/lifecycle/teardown/session-close.mjs +2 -0
- package/src/session-runtime/lifecycle/teardown.mjs +5 -2
- package/src/session-runtime/lifecycle-api.mjs +3 -1
- package/src/session-runtime/mcp-glue/server-catalog.mjs +8 -0
- package/src/session-runtime/mcp-glue/server-input.mjs +3 -0
- package/src/session-runtime/mcp-oauth-login.mjs +112 -15
- package/src/session-runtime/model-route/set-route.mjs +14 -3
- package/src/session-runtime/owned-automation-run.mjs +229 -0
- package/src/session-runtime/provider-auth/login.mjs +10 -6
- package/src/session-runtime/provider-auth/setup.mjs +19 -2
- package/src/session-runtime/provider-catalog-cache.mjs +101 -25
- package/src/session-runtime/provider-models/catalog-load.mjs +14 -3
- package/src/session-runtime/provider-models/row-hydration.mjs +4 -0
- package/src/session-runtime/provider-models.mjs +76 -10
- package/src/session-runtime/provider-readiness.mjs +4 -0
- package/src/session-runtime/resource-api.mjs +5 -0
- package/src/session-runtime/resource-mcp-api.mjs +107 -13
- package/src/session-runtime/resource-skills-api.mjs +12 -0
- package/src/session-runtime/room-member-gate.mjs +48 -0
- package/src/session-runtime/routine-home-turn.mjs +156 -0
- package/src/session-runtime/routine-owner.mjs +48 -0
- package/src/session-runtime/schedule-session-run.mjs +30 -64
- package/src/session-runtime/services/agent-tool/close-flow.mjs +2 -1
- package/src/session-runtime/services/agent-tool/helpers.mjs +10 -0
- package/src/session-runtime/services/agent-tool/job-views/agent-list.mjs +10 -2
- package/src/session-runtime/services/agent-tool/job-views/session-progress.mjs +13 -4
- package/src/session-runtime/services/agent-tool/notify.mjs +15 -4
- package/src/session-runtime/services/agent-tool/restart-notify.mjs +134 -0
- package/src/session-runtime/services/agent-tool/send-flow.mjs +24 -1
- package/src/session-runtime/services/agent-tool/spawn-flow/deferred-spawn.mjs +2 -1
- package/src/session-runtime/services/agent-tool/spawn-flow/job-start.mjs +4 -3
- package/src/session-runtime/services/agent-tool/spawn-flow/spawn-plan.mjs +19 -4
- package/src/session-runtime/services/agent-tool/spawn-flow/spawn-prep.mjs +2 -0
- package/src/session-runtime/services/agent-tool/spawn-flow/spawn-run.mjs +5 -1
- package/src/session-runtime/services/agent-tool/tag-registry/tag-lookup.mjs +8 -3
- package/src/session-runtime/services/agent-tool/tag-registry.mjs +3 -0
- package/src/session-runtime/services/agent-tool/worker-index/row-shape.mjs +4 -0
- package/src/session-runtime/services/agent-tool/worker-index.mjs +44 -10
- package/src/session-runtime/services/agent-tool.mjs +20 -0
- package/src/session-runtime/services/channel-admin.mjs +93 -24
- package/src/session-runtime/services/memory-access.mjs +37 -0
- package/src/session-runtime/services/provider-admin.mjs +166 -59
- package/src/session-runtime/services/schedule-access.mjs +91 -0
- package/src/session-runtime/services/usage-dashboard-model.mjs +2 -2
- package/src/session-runtime/services/usage-dashboard.mjs +27 -69
- package/src/session-runtime/session-create/session-options.mjs +34 -7
- package/src/session-runtime/session-hooks.mjs +16 -8
- package/src/session-runtime/setup-tool/executor.mjs +68 -6
- package/src/session-runtime/setup-tool/extended-actions.mjs +72 -14
- package/src/session-runtime/setup-tool/settings-contract.mjs +8 -6
- package/src/session-runtime/setup-tool/tool-defs.mjs +8 -0
- package/src/session-runtime/skills-api.mjs +16 -1
- package/src/session-runtime/tool-policy-refresh.mjs +238 -29
- package/src/session-runtime/turn/session-ops.mjs +10 -3
- package/src/session-runtime/turn/turn-run/ask-callbacks.mjs +11 -2
- package/src/session-runtime/turn/turn-run/turn-prepare.mjs +3 -0
- package/src/session-runtime/usage-stats-api.mjs +6 -1
- package/src/session-runtime/webhook-session-run.mjs +49 -67
- package/src/session-runtime/workflow-agents-api/agent-connectors.mjs +239 -0
- package/src/session-runtime/workflow-agents-api/agent-editor.mjs +556 -22
- package/src/session-runtime/workflow-agents-api/workflow-packs.mjs +11 -1
- package/src/session-runtime/workflow-agents-api.mjs +4 -1
- package/src/standalone/channel-transport/http-routes.mjs +10 -9
- package/src/standalone/daemon-agent-control.mjs +1 -8
- package/src/standalone/daemon-desktop-runtime.mjs +20 -7
- package/src/standalone/daemon-owner.mjs +117 -25
- package/src/standalone/daemon-session-control.mjs +11 -2
- package/src/standalone/daemon.mjs +61 -17
- package/src/standalone/session-call-cache.mjs +6 -0
- package/src/standalone/session-protocol.mjs +28 -1
- package/src/standalone/session-service/retention/busy.mjs +7 -2
- package/src/standalone/session-service/session-calls/session-turns.mjs +9 -0
- package/src/standalone/session-service.mjs +19 -0
- package/src/standalone/session-transport/routes.mjs +7 -6
- package/src/tui/app/model-options.mjs +39 -2
- package/src/tui/app/model-picker.mjs +16 -2
- package/src/tui/app/provider-setup-picker.mjs +1 -2
- package/src/tui/app/slash-commands.mjs +20 -0
- package/src/tui/dist/index.mjs +487 -128
- package/src/tui/session/context-state.mjs +6 -1
- package/src/tui/session/notice-surface.mjs +35 -4
- package/src/tui/session/notice-text.mjs +0 -1
- package/src/tui/session/queue-helpers.mjs +2 -1
- package/src/tui/session/session-api/extensions.mjs +4 -0
- package/src/tui/session/session-api/intake/submission.mjs +12 -13
- package/src/tui/session/session-api/integrations.mjs +9 -5
- package/src/tui/session/session-api/lifecycle/session-reset.mjs +13 -3
- package/src/tui/session/session-api/lifecycle.mjs +17 -1
- package/src/tui/session/session-api/routes.mjs +48 -3
- package/src/tui/session/session-flow/drain.mjs +20 -3
- package/src/tui/session/session-flow/queue/entry-shape.mjs +2 -1
- package/src/tui/session/session-flow/queue/take-entries.mjs +3 -2
- package/src/tui/session/session-flow/steering.mjs +104 -24
- package/src/tui/session/session-flow.mjs +2 -0
- package/src/tui/session/tool-approval.mjs +26 -5
- package/src/tui/session/transcript-intake.mjs +6 -2
- package/src/tui/session/transcript-spill.mjs +5 -1
- package/src/tui/session/tui-steering-persist.mjs +65 -42
- package/src/tui/session/turn-live-call.mjs +56 -0
- package/src/tui/session/turn-stream.mjs +33 -0
- package/src/tui/session/turn.mjs +50 -7
- package/src/ui/statusline.mjs +1 -6
- package/src/ui/streaming-markdown-heal.mjs +24 -7
- package/src/vendor/statusline/bin/statusline-route.mjs +0 -1
- package/src/vendor/statusline/src/gateway/route-meta.mjs +0 -1
- package/src/workflows/default/WORKFLOW.md +9 -13
- package/src/session-runtime/automation-agents.mjs +0 -94
- package/src/session-runtime/automation-workflow.mjs +0 -59
|
@@ -16,10 +16,17 @@
|
|
|
16
16
|
* field name and row key is a consumed contract and is used verbatim.
|
|
17
17
|
*/
|
|
18
18
|
import { readFileSync } from 'node:fs';
|
|
19
|
+
import { readFile, stat } from 'node:fs/promises';
|
|
19
20
|
import { join } from 'node:path';
|
|
20
|
-
import {
|
|
21
|
+
import {
|
|
22
|
+
isSettledSessionStamp,
|
|
23
|
+
sameSessionStamp,
|
|
24
|
+
sessionStampOf,
|
|
25
|
+
statSessionStamp,
|
|
26
|
+
} from './store/canonical-reader.mjs';
|
|
21
27
|
import { readJsonProjection } from '../../../shared/pending-json-projection.mjs';
|
|
22
28
|
import { listStoredAgentWorkers as assembleStoredAgentWorkers } from './store-agent-worker-pool.mjs';
|
|
29
|
+
import { readTopLevelFields } from './store-agent-worker-header.mjs';
|
|
23
30
|
import {
|
|
24
31
|
TAG_TOMBSTONE_TTL_MS,
|
|
25
32
|
agentPaneVisible,
|
|
@@ -166,16 +173,83 @@ function cancelStage(cancel, declaredStage) {
|
|
|
166
173
|
return /^cancel/i.test(declaredStage) || CANCELLED_AGENT_STATUS.test(declaredStage) ? declaredStage : cancel.status;
|
|
167
174
|
}
|
|
168
175
|
|
|
169
|
-
|
|
170
|
-
|
|
176
|
+
// The worker indexes were re-parsed in full on every pool listing and ancestry
|
|
177
|
+
// lookup, changed or not. Each keeps its last parse beside the bytes it came
|
|
178
|
+
// from: the file is still read every time (a few hundred KB), but an
|
|
179
|
+
// unchanged one — byte for byte, so no timestamp collision can serve a stale
|
|
180
|
+
// document — reuses that parse.
|
|
181
|
+
// Before the read, the full file stamp is checked under the same canonical
|
|
182
|
+
// policy the session headers use: a file that still has the exact settled
|
|
183
|
+
// { dev, ino, size, mtimeNs, ctimeNs } its bytes were proven to come from is
|
|
184
|
+
// not read again (each pool listing re-read the whole index synchronously).
|
|
185
|
+
const workerIndexCache = new Map(); // path -> { bytes, doc, stamp|null }
|
|
186
|
+
// The index documents an asynchronous listing read before its (synchronous)
|
|
187
|
+
// assembly; live only during that assembly.
|
|
188
|
+
let passIndexes = null; // path -> doc | null
|
|
189
|
+
|
|
190
|
+
function readWorkerIndexFile(path) {
|
|
191
|
+
if (passIndexes?.has(path)) return passIndexes.get(path);
|
|
171
192
|
// This process's own queued (not yet persisted) writes win over the file.
|
|
172
193
|
const projected = readJsonProjection(path);
|
|
173
194
|
if (projected) return projected;
|
|
195
|
+
const before = observeStamp(path);
|
|
196
|
+
const cached = workerIndexCache.get(path);
|
|
197
|
+
if (before && cached?.stamp && sameSessionStamp(cached.stamp, before)) return cached.doc;
|
|
198
|
+
let bytes;
|
|
174
199
|
try {
|
|
175
|
-
|
|
200
|
+
bytes = readFileSync(path);
|
|
176
201
|
} catch {
|
|
202
|
+
workerIndexCache.delete(path);
|
|
177
203
|
return null;
|
|
178
204
|
}
|
|
205
|
+
const stamp = before && isSettledSessionStamp(before) && sameSessionStamp(before, observeStamp(path)) ? before : null;
|
|
206
|
+
if (cached?.bytes.equals(bytes)) {
|
|
207
|
+
cached.stamp = stamp;
|
|
208
|
+
return cached.doc;
|
|
209
|
+
}
|
|
210
|
+
let doc = null;
|
|
211
|
+
try {
|
|
212
|
+
doc = JSON.parse(bytes.toString('utf8'));
|
|
213
|
+
} catch {
|
|
214
|
+
/* malformed: no rows */
|
|
215
|
+
}
|
|
216
|
+
workerIndexCache.set(path, { bytes, doc, stamp });
|
|
217
|
+
return doc;
|
|
218
|
+
}
|
|
219
|
+
|
|
220
|
+
/** readWorkerIndexFile with every stat and read awaited, under the same
|
|
221
|
+
* stamp and byte-equality cache. */
|
|
222
|
+
async function readWorkerIndexFileAsync(path) {
|
|
223
|
+
const projected = readJsonProjection(path);
|
|
224
|
+
if (projected) return projected;
|
|
225
|
+
const before = await observeStampAsync(path);
|
|
226
|
+
const cached = workerIndexCache.get(path);
|
|
227
|
+
if (before && cached?.stamp && sameSessionStamp(cached.stamp, before)) return cached.doc;
|
|
228
|
+
let bytes;
|
|
229
|
+
try {
|
|
230
|
+
bytes = await readFile(path);
|
|
231
|
+
} catch {
|
|
232
|
+
workerIndexCache.delete(path);
|
|
233
|
+
return null;
|
|
234
|
+
}
|
|
235
|
+
const settled = before && isSettledSessionStamp(before);
|
|
236
|
+
const stamp = settled && sameSessionStamp(before, await observeStampAsync(path)) ? before : null;
|
|
237
|
+
if (cached?.bytes.equals(bytes)) {
|
|
238
|
+
cached.stamp = stamp;
|
|
239
|
+
return cached.doc;
|
|
240
|
+
}
|
|
241
|
+
let doc = null;
|
|
242
|
+
try {
|
|
243
|
+
doc = JSON.parse(bytes.toString('utf8'));
|
|
244
|
+
} catch {
|
|
245
|
+
/* malformed: no rows */
|
|
246
|
+
}
|
|
247
|
+
workerIndexCache.set(path, { bytes, doc, stamp });
|
|
248
|
+
return doc;
|
|
249
|
+
}
|
|
250
|
+
|
|
251
|
+
function storedAgentWorkerIndex() {
|
|
252
|
+
return readWorkerIndexFile(storedAgentWorkerIndexPath());
|
|
179
253
|
}
|
|
180
254
|
|
|
181
255
|
function storedAgentWorkerIndexRows() {
|
|
@@ -211,7 +285,8 @@ export function listStoredAgentWorkerLinks() {
|
|
|
211
285
|
|
|
212
286
|
// The agent pool only reads top-level session fields, yet each refresh used to
|
|
213
287
|
// JSON.parse every active worker's whole transcript (megabytes per worker,
|
|
214
|
-
// every catalog publish). Cache the header per file
|
|
288
|
+
// every catalog publish). Cache the header per full file stamp instead (see
|
|
289
|
+
// probeWorkerSessionHeader).
|
|
215
290
|
// The cache holds exactly the headers the latest pool assembly read: a fixed
|
|
216
291
|
// LRU cap below the index size (263 headers against 256 slots) evicted every
|
|
217
292
|
// entry just before its reuse, so each refresh re-parsed every worker
|
|
@@ -250,30 +325,120 @@ const WORKER_HEADER_FIELDS = [
|
|
|
250
325
|
...SESSION_CANCEL_FIELDS,
|
|
251
326
|
];
|
|
252
327
|
|
|
328
|
+
// One assembly reads a session's header up to three times (reap check, row
|
|
329
|
+
// projection, orphan prune); each read used to stat the file again. Within an
|
|
330
|
+
// assembly the first answer stands, absent or unreadable included.
|
|
331
|
+
let passHeaders = null; // path -> header | null, live only during an assembly
|
|
332
|
+
|
|
333
|
+
function workerSessionHeaderPath(sessionId) {
|
|
334
|
+
return join(dataDir(), 'sessions', `${sessionId}.json`);
|
|
335
|
+
}
|
|
336
|
+
|
|
253
337
|
function readWorkerSessionHeader(sessionId) {
|
|
254
|
-
const path =
|
|
255
|
-
|
|
256
|
-
|
|
338
|
+
const path = workerSessionHeaderPath(sessionId);
|
|
339
|
+
if (passHeaders?.has(path)) return passHeaders.get(path);
|
|
340
|
+
const header = probeWorkerSessionHeader(path);
|
|
341
|
+
passHeaders?.set(path, header);
|
|
342
|
+
return header;
|
|
343
|
+
}
|
|
344
|
+
|
|
345
|
+
let workerHeaderReads = 0;
|
|
346
|
+
|
|
347
|
+
function observeStamp(path) {
|
|
348
|
+
try {
|
|
349
|
+
return statSessionStamp(path);
|
|
350
|
+
} catch {
|
|
351
|
+
return null;
|
|
352
|
+
}
|
|
353
|
+
}
|
|
354
|
+
|
|
355
|
+
// A header is reused only under the canonical full-stamp policy: the file
|
|
356
|
+
// still has the exact { dev, ino, size, mtimeNs, ctimeNs } its bytes were
|
|
357
|
+
// proven to come from (stat → read → stat), and that stamp was past the racy
|
|
358
|
+
// window when read. Size + mtime alone let an equal-length rewrite that kept
|
|
359
|
+
// its mtime serve stale ownership, status and cancel fields.
|
|
360
|
+
function probeWorkerSessionHeader(path) {
|
|
361
|
+
// Absent and unreadable alike: no header.
|
|
362
|
+
const before = observeStamp(path);
|
|
363
|
+
if (!before) return null;
|
|
257
364
|
const cached = workerSessionHeaderCache.get(path);
|
|
258
|
-
if (cached && cached.
|
|
365
|
+
if (cached && sameSessionStamp(cached.stamp, before)) {
|
|
259
366
|
cached.pass = headerPass;
|
|
260
367
|
return cached.header;
|
|
261
368
|
}
|
|
262
|
-
|
|
369
|
+
workerSessionHeaderCache.delete(path);
|
|
370
|
+
// A rewrite re-reads only this record, and only its top-level fields are
|
|
371
|
+
// decoded: the transcript bytes are validated, never materialized.
|
|
372
|
+
let header = null;
|
|
373
|
+
try {
|
|
374
|
+
header = readTopLevelFields(readFileSync(path), WORKER_HEADER_FIELDS);
|
|
375
|
+
} catch {
|
|
376
|
+
return null;
|
|
377
|
+
}
|
|
378
|
+
if (!header) return null;
|
|
379
|
+
workerHeaderReads += 1;
|
|
380
|
+
if (isSettledSessionStamp(before) && sameSessionStamp(before, observeStamp(path))) {
|
|
381
|
+
workerSessionHeaderCache.set(path, { stamp: before, header, pass: headerPass });
|
|
382
|
+
}
|
|
383
|
+
return header;
|
|
384
|
+
}
|
|
385
|
+
|
|
386
|
+
async function observeStampAsync(path) {
|
|
387
|
+
try {
|
|
388
|
+
return sessionStampOf(await stat(path, { bigint: true }));
|
|
389
|
+
} catch {
|
|
390
|
+
return null;
|
|
391
|
+
}
|
|
392
|
+
}
|
|
393
|
+
|
|
394
|
+
/** probeWorkerSessionHeader with every stat and read awaited, under the
|
|
395
|
+
* same full-stamp cache policy. */
|
|
396
|
+
async function probeWorkerSessionHeaderAsync(path) {
|
|
397
|
+
const before = await observeStampAsync(path);
|
|
398
|
+
if (!before) return null;
|
|
399
|
+
const cached = workerSessionHeaderCache.get(path);
|
|
400
|
+
if (cached && sameSessionStamp(cached.stamp, before)) return cached.header;
|
|
401
|
+
workerSessionHeaderCache.delete(path);
|
|
402
|
+
let header = null;
|
|
263
403
|
try {
|
|
264
|
-
|
|
404
|
+
header = readTopLevelFields(await readFile(path), WORKER_HEADER_FIELDS);
|
|
265
405
|
} catch {
|
|
266
406
|
return null;
|
|
267
407
|
}
|
|
268
|
-
if (!
|
|
269
|
-
|
|
270
|
-
|
|
271
|
-
|
|
408
|
+
if (!header) return null;
|
|
409
|
+
workerHeaderReads += 1;
|
|
410
|
+
if (isSettledSessionStamp(before) && sameSessionStamp(before, await observeStampAsync(path))) {
|
|
411
|
+
workerSessionHeaderCache.set(path, { stamp: before, header, pass: headerPass });
|
|
272
412
|
}
|
|
273
|
-
workerSessionHeaderCache.set(path, { mtimeMs: probe.mtimeMs, size: probe.size, header, pass: headerPass });
|
|
274
413
|
return header;
|
|
275
414
|
}
|
|
276
415
|
|
|
416
|
+
// Header reads in flight at once: each is a stat (and a read only when the
|
|
417
|
+
// file changed) on the shared libuv pool, which other file I/O also needs.
|
|
418
|
+
const HEADER_READ_CONCURRENCY = 4;
|
|
419
|
+
|
|
420
|
+
async function prefetchWorkerHeaders(sessionIds, headers) {
|
|
421
|
+
const paths = [...new Set([...sessionIds].map(workerSessionHeaderPath))].filter((path) => !headers.has(path));
|
|
422
|
+
let next = 0;
|
|
423
|
+
const drain = async () => {
|
|
424
|
+
while (next < paths.length) {
|
|
425
|
+
const path = paths[next++];
|
|
426
|
+
headers.set(path, await probeWorkerSessionHeaderAsync(path));
|
|
427
|
+
}
|
|
428
|
+
};
|
|
429
|
+
await Promise.all(Array.from({ length: Math.min(HEADER_READ_CONCURRENCY, paths.length) }, drain));
|
|
430
|
+
}
|
|
431
|
+
|
|
432
|
+
/** Test/diagnostic seam: cached headers and the records this process read. */
|
|
433
|
+
export function storedAgentWorkerHeaderStats() {
|
|
434
|
+
return {
|
|
435
|
+
entries: workerSessionHeaderCache.size,
|
|
436
|
+
reads: workerHeaderReads,
|
|
437
|
+
stickyIdle: stickyIdleSince.size,
|
|
438
|
+
indexes: workerIndexCache.size,
|
|
439
|
+
};
|
|
440
|
+
}
|
|
441
|
+
|
|
277
442
|
/** Owner record of a child row that is a Lead CONVERSATION. Only such an owner
|
|
278
443
|
* lets a missing pool row mean "the Lead's lease expired"; an Agent-owned or
|
|
279
444
|
* unreadable owner keeps its child rows visible exactly as before. */
|
|
@@ -377,16 +542,8 @@ function projectChildWorkerRow(row, context) {
|
|
|
377
542
|
}
|
|
378
543
|
|
|
379
544
|
function storedLeadWorkerRows() {
|
|
380
|
-
|
|
381
|
-
|
|
382
|
-
if (!parsed) {
|
|
383
|
-
try {
|
|
384
|
-
parsed = JSON.parse(readFileSync(path, 'utf8'));
|
|
385
|
-
} catch {
|
|
386
|
-
/* no resident Lead pool */
|
|
387
|
-
}
|
|
388
|
-
}
|
|
389
|
-
return workerRows(parsed);
|
|
545
|
+
// Absent or unreadable: no resident Lead pool.
|
|
546
|
+
return workerRows(readWorkerIndexFile(storedLeadWorkerIndexPath()));
|
|
390
547
|
}
|
|
391
548
|
|
|
392
549
|
/** A Lead-index row whose durable session turned out to be an agent's.
|
|
@@ -464,6 +621,72 @@ function pruneOrphanChildRows(bySessionId, { liveLeadSessionIds }) {
|
|
|
464
621
|
* never projected into either pool. `runningSessionIds` is the daemon's set of
|
|
465
622
|
* sessions whose turn is in flight; omitted, no row is shown as running. */
|
|
466
623
|
export function listStoredAgentWorkers({ runningSessionIds = null } = {}) {
|
|
624
|
+
headerPass += 1;
|
|
625
|
+
passHeaders = new Map();
|
|
626
|
+
try {
|
|
627
|
+
return assembleListedWorkers(runningSessionIds);
|
|
628
|
+
} finally {
|
|
629
|
+
passHeaders = null;
|
|
630
|
+
forgetUnreadHeaders();
|
|
631
|
+
}
|
|
632
|
+
}
|
|
633
|
+
|
|
634
|
+
// Keep what the latest assembly read, whatever the index size; forget the rest.
|
|
635
|
+
function forgetUnreadHeaders() {
|
|
636
|
+
for (const [path, entry] of workerSessionHeaderCache) {
|
|
637
|
+
if (entry.pass !== headerPass) workerSessionHeaderCache.delete(path);
|
|
638
|
+
}
|
|
639
|
+
}
|
|
640
|
+
|
|
641
|
+
/** listStoredAgentWorkers without blocking the event loop on the store: both
|
|
642
|
+
* indexes and every header the assembly can consult (each row's session,
|
|
643
|
+
* each Lead row's, and each child's owner) are read first with awaited
|
|
644
|
+
* stats and reads; the assembly then runs over what was read, with no file
|
|
645
|
+
* access left in it (any header it still asks for falls back to a read).
|
|
646
|
+
* `runningSessionIds` may be a function, asked once the reads are done. */
|
|
647
|
+
export async function listStoredAgentWorkersAsync({ runningSessionIds = null } = {}) {
|
|
648
|
+
const agentIndexPath = storedAgentWorkerIndexPath();
|
|
649
|
+
const leadIndexPath = storedLeadWorkerIndexPath();
|
|
650
|
+
const [agentIndex, leadIndex] = await Promise.all([
|
|
651
|
+
readWorkerIndexFileAsync(agentIndexPath),
|
|
652
|
+
readWorkerIndexFileAsync(leadIndexPath),
|
|
653
|
+
]);
|
|
654
|
+
const headers = new Map();
|
|
655
|
+
const childRows = workerRows(agentIndex);
|
|
656
|
+
const sessionIds = new Set(childRows.map((row) => cleanValue(row?.sessionId)));
|
|
657
|
+
for (const row of workerRows(leadIndex)) {
|
|
658
|
+
const sessionId = cleanValue(row?.sessionId);
|
|
659
|
+
if (sessionId && isStoredSessionId(sessionId)) sessionIds.add(sessionId);
|
|
660
|
+
}
|
|
661
|
+
await prefetchWorkerHeaders(sessionIds, headers);
|
|
662
|
+
const owners = new Set();
|
|
663
|
+
for (const row of childRows) {
|
|
664
|
+
const header = headers.get(workerSessionHeaderPath(cleanValue(row?.sessionId)));
|
|
665
|
+
const owner = row && typeof row === 'object' ? workerOwnerSessionId(row, header) : null;
|
|
666
|
+
if (owner) owners.add(owner);
|
|
667
|
+
}
|
|
668
|
+
await prefetchWorkerHeaders(owners, headers);
|
|
669
|
+
const running = typeof runningSessionIds === 'function' ? runningSessionIds() : runningSessionIds;
|
|
670
|
+
headerPass += 1;
|
|
671
|
+
for (const path of headers.keys()) {
|
|
672
|
+
const entry = workerSessionHeaderCache.get(path);
|
|
673
|
+
if (entry) entry.pass = headerPass;
|
|
674
|
+
}
|
|
675
|
+
passHeaders = headers;
|
|
676
|
+
passIndexes = new Map([
|
|
677
|
+
[agentIndexPath, agentIndex],
|
|
678
|
+
[leadIndexPath, leadIndex],
|
|
679
|
+
]);
|
|
680
|
+
try {
|
|
681
|
+
return assembleListedWorkers(running);
|
|
682
|
+
} finally {
|
|
683
|
+
passHeaders = null;
|
|
684
|
+
passIndexes = null;
|
|
685
|
+
forgetUnreadHeaders();
|
|
686
|
+
}
|
|
687
|
+
}
|
|
688
|
+
|
|
689
|
+
function assembleListedWorkers(runningSessionIds) {
|
|
467
690
|
const now = Date.now();
|
|
468
691
|
const index = storedAgentWorkerIndex();
|
|
469
692
|
const rows = workerRows(index);
|
|
@@ -487,21 +710,13 @@ export function listStoredAgentWorkers({ runningSessionIds = null } = {}) {
|
|
|
487
710
|
};
|
|
488
711
|
return tombstoneBlocksWork(work, findTagTombstone(work, tombstones));
|
|
489
712
|
};
|
|
490
|
-
|
|
491
|
-
|
|
492
|
-
|
|
493
|
-
|
|
494
|
-
|
|
495
|
-
|
|
496
|
-
|
|
497
|
-
|
|
498
|
-
|
|
499
|
-
pruneOrphanChildRows,
|
|
500
|
-
});
|
|
501
|
-
} finally {
|
|
502
|
-
// Keep what this assembly read, whatever the index size; forget the rest.
|
|
503
|
-
for (const [path, entry] of workerSessionHeaderCache) {
|
|
504
|
-
if (entry.pass !== headerPass) workerSessionHeaderCache.delete(path);
|
|
505
|
-
}
|
|
506
|
-
}
|
|
713
|
+
return assembleStoredAgentWorkers({
|
|
714
|
+
now,
|
|
715
|
+
runningSessionIds,
|
|
716
|
+
storedAgentWorkerIndexRows: () => rows.filter((row) => !isReaped(cleanValue(row.sessionId))),
|
|
717
|
+
projectChildWorkerRow,
|
|
718
|
+
storedLeadWorkerRows,
|
|
719
|
+
projectLeadWorkerRow,
|
|
720
|
+
pruneOrphanChildRows,
|
|
721
|
+
});
|
|
507
722
|
}
|
|
@@ -0,0 +1,325 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Strict session-record read that decodes only what its caller needs.
|
|
3
|
+
*
|
|
4
|
+
* `readTopLevelLifecycleRecord` (lifecycle-scan.mjs) is the authority: a full
|
|
5
|
+
* `JSON.parse`, then a top-level duplicate-key walk. On a transcript that
|
|
6
|
+
* materializes every message object — about 2.5 ms per MB, most of it for
|
|
7
|
+
* tool output nobody reads. This reader reaches the SAME verdict and the same
|
|
8
|
+
* values while decoding only:
|
|
9
|
+
* - top-level values whose key is not in `skip`;
|
|
10
|
+
* - of `messages` (when it is an array): each element whose `role` is in
|
|
11
|
+
* `fullMessageRoles` in full; every other object element as `{ role }`
|
|
12
|
+
* (or `{}` without one); non-object elements in full.
|
|
13
|
+
* Everything else is validated, never decoded.
|
|
14
|
+
*
|
|
15
|
+
* The fast path accepts a document only when it has PROVEN it valid:
|
|
16
|
+
* - no raw byte below 0x20 anywhere (one native regex test; JSON forbids
|
|
17
|
+
* them inside strings, so a document with whitespace other than spaces,
|
|
18
|
+
* i.e. a pretty-printed one, simply takes the authoritative path);
|
|
19
|
+
* - no invalid escape anywhere (one native regex test; a backslash outside
|
|
20
|
+
* a string fails the grammar walk below);
|
|
21
|
+
* - the complete grammar, walked over every byte outside strings: values,
|
|
22
|
+
* separators, exact number and literal spellings, nesting, and nothing
|
|
23
|
+
* but whitespace after the root object;
|
|
24
|
+
* - a root object without any repeated top-level key.
|
|
25
|
+
* Anything it cannot prove — malformed, ambiguous, or merely unusual — is
|
|
26
|
+
* handed to `readTopLevelLifecycleRecord` itself, so every rejection is the
|
|
27
|
+
* authority's own. Decoded values are `JSON.parse` of their exact UTF-8
|
|
28
|
+
* slices, which equal the full parse's sub-values (slices start and end at
|
|
29
|
+
* ASCII delimiters, so UTF-8 decoding agrees too).
|
|
30
|
+
*/
|
|
31
|
+
import { lifecycleRecordOf, readTopLevelLifecycleRecord } from './lifecycle-scan.mjs';
|
|
32
|
+
|
|
33
|
+
const QUOTE = 0x22;
|
|
34
|
+
const BACKSLASH = 0x5c;
|
|
35
|
+
const SPACE = 0x20;
|
|
36
|
+
const COMMA = 0x2c;
|
|
37
|
+
const COLON = 0x3a;
|
|
38
|
+
const OPEN_OBJECT = 0x7b;
|
|
39
|
+
const CLOSE_OBJECT = 0x7d;
|
|
40
|
+
const OPEN_ARRAY = 0x5b;
|
|
41
|
+
const CLOSE_ARRAY = 0x5d;
|
|
42
|
+
const MINUS = 0x2d;
|
|
43
|
+
const PLUS = 0x2b;
|
|
44
|
+
const DOT = 0x2e;
|
|
45
|
+
const ZERO = 0x30;
|
|
46
|
+
const NINE = 0x39;
|
|
47
|
+
const EMPTY = new Set();
|
|
48
|
+
|
|
49
|
+
// String-content faults, one native pass each (an alternation of the two is
|
|
50
|
+
// slower than both). Any raw control byte: inside a string JSON.parse rejects
|
|
51
|
+
// it; outside one only \t \n \r are whitespace, and those documents take the
|
|
52
|
+
// full path.
|
|
53
|
+
// biome-ignore lint/suspicious/noControlCharactersInRegex: control bytes are exactly what this detects.
|
|
54
|
+
const CONTROL_BYTE = /[\x00-\x1f]/;
|
|
55
|
+
// An escape that is not one of \" \\ \/ \b \f \n \r \t \uXXXX. The run before
|
|
56
|
+
// it starts after a non-backslash, so escapes pair from the run's start.
|
|
57
|
+
const INVALID_ESCAPE = /(?<!\\)(?:\\\\)*\\(?:[^"\\/bfnrtu]|u(?![0-9a-fA-F]{4}))/;
|
|
58
|
+
|
|
59
|
+
const isDigit = (code) => code >= ZERO && code <= NINE;
|
|
60
|
+
|
|
61
|
+
// Only spaces can remain: CONTROL_BYTE already sent every \t \n \r away.
|
|
62
|
+
function skipSpaces(text, at) {
|
|
63
|
+
let i = at;
|
|
64
|
+
while (text.charCodeAt(i) === SPACE) i++;
|
|
65
|
+
return i;
|
|
66
|
+
}
|
|
67
|
+
|
|
68
|
+
/** Index after the string opening at `at`, or -1. */
|
|
69
|
+
function stringEnd(text, at) {
|
|
70
|
+
let quote = at;
|
|
71
|
+
for (;;) {
|
|
72
|
+
quote = text.indexOf('"', quote + 1);
|
|
73
|
+
if (quote === -1) return -1;
|
|
74
|
+
let run = 0;
|
|
75
|
+
for (let k = quote - 1; text.charCodeAt(k) === BACKSLASH; k--) run++;
|
|
76
|
+
if ((run & 1) === 0) return quote + 1;
|
|
77
|
+
}
|
|
78
|
+
}
|
|
79
|
+
|
|
80
|
+
/** Index after the JSON number at `at`, or -1. */
|
|
81
|
+
function numberEnd(text, at) {
|
|
82
|
+
let i = at;
|
|
83
|
+
if (text.charCodeAt(i) === MINUS) i++;
|
|
84
|
+
if (text.charCodeAt(i) === ZERO) i++;
|
|
85
|
+
else if (isDigit(text.charCodeAt(i))) {
|
|
86
|
+
while (isDigit(text.charCodeAt(i))) i++;
|
|
87
|
+
} else return -1;
|
|
88
|
+
if (text.charCodeAt(i) === DOT) {
|
|
89
|
+
i++;
|
|
90
|
+
if (!isDigit(text.charCodeAt(i))) return -1;
|
|
91
|
+
while (isDigit(text.charCodeAt(i))) i++;
|
|
92
|
+
}
|
|
93
|
+
const exponent = text.charCodeAt(i);
|
|
94
|
+
if (exponent === 0x65 || exponent === 0x45) {
|
|
95
|
+
i++;
|
|
96
|
+
if (text.charCodeAt(i) === PLUS || text.charCodeAt(i) === MINUS) i++;
|
|
97
|
+
if (!isDigit(text.charCodeAt(i))) return -1;
|
|
98
|
+
while (isDigit(text.charCodeAt(i))) i++;
|
|
99
|
+
}
|
|
100
|
+
return i;
|
|
101
|
+
}
|
|
102
|
+
|
|
103
|
+
/** Index after `"key" :` starting at `at`, or -1. */
|
|
104
|
+
function memberValueStart(text, at) {
|
|
105
|
+
if (text.charCodeAt(at) !== QUOTE) return -1;
|
|
106
|
+
const end = stringEnd(text, at);
|
|
107
|
+
if (end === -1) return -1;
|
|
108
|
+
const colon = skipSpaces(text, end);
|
|
109
|
+
return text.charCodeAt(colon) === COLON ? colon + 1 : -1;
|
|
110
|
+
}
|
|
111
|
+
|
|
112
|
+
/** Index after one valid JSON value starting at or after `at`, or -1.
|
|
113
|
+
* Iterative, so nesting depth costs no stack. */
|
|
114
|
+
function valueEnd(text, at) {
|
|
115
|
+
const closers = [];
|
|
116
|
+
let i = at;
|
|
117
|
+
for (;;) {
|
|
118
|
+
i = skipSpaces(text, i);
|
|
119
|
+
const code = text.charCodeAt(i);
|
|
120
|
+
if (code === QUOTE) i = stringEnd(text, i);
|
|
121
|
+
else if (code === OPEN_OBJECT || code === OPEN_ARRAY) {
|
|
122
|
+
const closer = code === OPEN_OBJECT ? CLOSE_OBJECT : CLOSE_ARRAY;
|
|
123
|
+
i = skipSpaces(text, i + 1);
|
|
124
|
+
if (text.charCodeAt(i) === closer) i++;
|
|
125
|
+
else {
|
|
126
|
+
closers.push(closer);
|
|
127
|
+
if (closer === CLOSE_OBJECT) {
|
|
128
|
+
i = memberValueStart(text, i);
|
|
129
|
+
if (i === -1) return -1;
|
|
130
|
+
}
|
|
131
|
+
continue;
|
|
132
|
+
}
|
|
133
|
+
} else if (code === 0x74) i = text.startsWith('true', i) ? i + 4 : -1;
|
|
134
|
+
else if (code === 0x66) i = text.startsWith('false', i) ? i + 5 : -1;
|
|
135
|
+
else if (code === 0x6e) i = text.startsWith('null', i) ? i + 4 : -1;
|
|
136
|
+
else i = numberEnd(text, i);
|
|
137
|
+
if (i === -1) return -1;
|
|
138
|
+
// After a value: close containers or move to the next member/element.
|
|
139
|
+
for (;;) {
|
|
140
|
+
if (closers.length === 0) return i;
|
|
141
|
+
i = skipSpaces(text, i);
|
|
142
|
+
const next = text.charCodeAt(i);
|
|
143
|
+
const closer = closers[closers.length - 1];
|
|
144
|
+
if (next === closer) {
|
|
145
|
+
closers.pop();
|
|
146
|
+
i++;
|
|
147
|
+
continue;
|
|
148
|
+
}
|
|
149
|
+
if (next !== COMMA) return -1;
|
|
150
|
+
i = skipSpaces(text, i + 1);
|
|
151
|
+
if (closer === CLOSE_OBJECT) {
|
|
152
|
+
i = memberValueStart(text, i);
|
|
153
|
+
if (i === -1) return -1;
|
|
154
|
+
}
|
|
155
|
+
break;
|
|
156
|
+
}
|
|
157
|
+
}
|
|
158
|
+
}
|
|
159
|
+
|
|
160
|
+
const decodeSlice = (bytes, start, end) => JSON.parse(bytes.toString('utf8', start, end));
|
|
161
|
+
|
|
162
|
+
/** The key of the string token [start, end): its own bytes unless escaped. */
|
|
163
|
+
function decodeKey(bytes, text, start, end) {
|
|
164
|
+
for (let k = start + 1; k < end - 1; k++) {
|
|
165
|
+
if (text.charCodeAt(k) === BACKSLASH) return decodeSlice(bytes, start, end);
|
|
166
|
+
}
|
|
167
|
+
return bytes.toString('utf8', start + 1, end - 1);
|
|
168
|
+
}
|
|
169
|
+
|
|
170
|
+
function defineMember(target, key, value) {
|
|
171
|
+
// JSON.parse creates own data properties, `__proto__` included.
|
|
172
|
+
Object.defineProperty(target, key, { value, writable: true, enumerable: true, configurable: true });
|
|
173
|
+
}
|
|
174
|
+
|
|
175
|
+
// "role" spelled with escapes is at most 6 escapes of 6 chars plus quotes.
|
|
176
|
+
const MAX_ESCAPED_ROLE_KEY = 26;
|
|
177
|
+
|
|
178
|
+
/** One `messages` element [start, ...): `{ end, item }`, or null. */
|
|
179
|
+
function messageElement(bytes, text, start, fullRoles) {
|
|
180
|
+
if (text.charCodeAt(start) !== OPEN_OBJECT) {
|
|
181
|
+
const end = valueEnd(text, start);
|
|
182
|
+
return end === -1 ? null : { end, item: decodeSlice(bytes, start, end) };
|
|
183
|
+
}
|
|
184
|
+
let i = skipSpaces(text, start + 1);
|
|
185
|
+
let role;
|
|
186
|
+
let hasRole = false;
|
|
187
|
+
if (text.charCodeAt(i) === CLOSE_OBJECT) return { end: i + 1, item: {} };
|
|
188
|
+
for (;;) {
|
|
189
|
+
if (text.charCodeAt(i) !== QUOTE) return null;
|
|
190
|
+
const keyEnd = stringEnd(text, i);
|
|
191
|
+
if (keyEnd === -1) return null;
|
|
192
|
+
const isRole =
|
|
193
|
+
(keyEnd - i === 6 && text.startsWith('"role"', i)) ||
|
|
194
|
+
(keyEnd - i <= MAX_ESCAPED_ROLE_KEY && keyEnd - i > 6 && decodeKey(bytes, text, i, keyEnd) === 'role');
|
|
195
|
+
const colon = skipSpaces(text, keyEnd);
|
|
196
|
+
if (text.charCodeAt(colon) !== COLON) return null;
|
|
197
|
+
const valueStart = skipSpaces(text, colon + 1);
|
|
198
|
+
const end = valueEnd(text, valueStart);
|
|
199
|
+
if (end === -1) return null;
|
|
200
|
+
if (isRole) {
|
|
201
|
+
// Duplicate members resolve last-wins, as in the full parse.
|
|
202
|
+
role = decodeSlice(bytes, valueStart, end);
|
|
203
|
+
hasRole = true;
|
|
204
|
+
}
|
|
205
|
+
i = skipSpaces(text, end);
|
|
206
|
+
if (text.charCodeAt(i) === COMMA) {
|
|
207
|
+
i = skipSpaces(text, i + 1);
|
|
208
|
+
continue;
|
|
209
|
+
}
|
|
210
|
+
if (text.charCodeAt(i) !== CLOSE_OBJECT) return null;
|
|
211
|
+
const elementEnd = i + 1;
|
|
212
|
+
let item = {};
|
|
213
|
+
if (hasRole && fullRoles.has(role)) item = decodeSlice(bytes, start, elementEnd);
|
|
214
|
+
else if (hasRole) defineMember(item, 'role', role);
|
|
215
|
+
return { end: elementEnd, item };
|
|
216
|
+
}
|
|
217
|
+
}
|
|
218
|
+
|
|
219
|
+
/** Decode `messages` from the end in full, newest first, until `tailUntil`
|
|
220
|
+
* accepts one; `spans` holds each element's [start, end). */
|
|
221
|
+
function decodeTail(bytes, messages, spans, fullRoles, tailUntil) {
|
|
222
|
+
for (let k = messages.length - 1; k >= 0; k--) {
|
|
223
|
+
let item = messages[k];
|
|
224
|
+
if (item && typeof item === 'object' && !Array.isArray(item) && !fullRoles.has(item.role)) {
|
|
225
|
+
item = decodeSlice(bytes, spans[2 * k], spans[2 * k + 1]);
|
|
226
|
+
messages[k] = item;
|
|
227
|
+
}
|
|
228
|
+
if (tailUntil(item)) return;
|
|
229
|
+
}
|
|
230
|
+
}
|
|
231
|
+
|
|
232
|
+
/** The `messages` array [start, ...): `{ end, messages }`, or null. */
|
|
233
|
+
function messagesValue(bytes, text, start, fullRoles, tailUntil) {
|
|
234
|
+
const messages = [];
|
|
235
|
+
const spans = tailUntil ? [] : null;
|
|
236
|
+
let i = skipSpaces(text, start + 1);
|
|
237
|
+
if (text.charCodeAt(i) === CLOSE_ARRAY) return { end: i + 1, messages };
|
|
238
|
+
for (;;) {
|
|
239
|
+
const element = messageElement(bytes, text, i, fullRoles);
|
|
240
|
+
if (!element) return null;
|
|
241
|
+
messages.push(element.item);
|
|
242
|
+
spans?.push(i, element.end);
|
|
243
|
+
i = skipSpaces(text, element.end);
|
|
244
|
+
if (text.charCodeAt(i) === COMMA) {
|
|
245
|
+
i = skipSpaces(text, i + 1);
|
|
246
|
+
continue;
|
|
247
|
+
}
|
|
248
|
+
if (text.charCodeAt(i) !== CLOSE_ARRAY) return null;
|
|
249
|
+
if (tailUntil) decodeTail(bytes, messages, spans, fullRoles, tailUntil);
|
|
250
|
+
return { end: i + 1, messages };
|
|
251
|
+
}
|
|
252
|
+
}
|
|
253
|
+
|
|
254
|
+
/**
|
|
255
|
+
* The proven-valid, duplicate-free root object of `bytes`, or null when
|
|
256
|
+
* validity could not be proven (or the document is not such an object). Of
|
|
257
|
+
* it only the members `decode(key)` accepts are decoded — `messages`, when an
|
|
258
|
+
* array, as described in the module comment with `fullMessageRoles`; every
|
|
259
|
+
* other member is validated and left out. With `fullTailUntil`, `messages`
|
|
260
|
+
* is also decoded in full from its end, newest first, through the first
|
|
261
|
+
* element the predicate accepts.
|
|
262
|
+
*/
|
|
263
|
+
export function provenTopLevelObject(bytes, decode, fullMessageRoles = EMPTY, fullTailUntil = null) {
|
|
264
|
+
try {
|
|
265
|
+
return provenDocument(bytes, decode, fullMessageRoles, fullTailUntil);
|
|
266
|
+
} catch {
|
|
267
|
+
return null;
|
|
268
|
+
}
|
|
269
|
+
}
|
|
270
|
+
|
|
271
|
+
function provenDocument(bytes, decode, fullRoles, tailUntil) {
|
|
272
|
+
const text = bytes.toString('latin1');
|
|
273
|
+
if (CONTROL_BYTE.test(text) || INVALID_ESCAPE.test(text)) return null;
|
|
274
|
+
let i = skipSpaces(text, 0);
|
|
275
|
+
if (text.charCodeAt(i) !== OPEN_OBJECT) return null;
|
|
276
|
+
const doc = {};
|
|
277
|
+
const seen = new Set();
|
|
278
|
+
i = skipSpaces(text, i + 1);
|
|
279
|
+
if (text.charCodeAt(i) === CLOSE_OBJECT) i++;
|
|
280
|
+
else {
|
|
281
|
+
for (;;) {
|
|
282
|
+
if (text.charCodeAt(i) !== QUOTE) return null;
|
|
283
|
+
const keyEnd = stringEnd(text, i);
|
|
284
|
+
if (keyEnd === -1) return null;
|
|
285
|
+
const key = decodeKey(bytes, text, i, keyEnd);
|
|
286
|
+
if (seen.has(key)) return null;
|
|
287
|
+
seen.add(key);
|
|
288
|
+
const colon = skipSpaces(text, keyEnd);
|
|
289
|
+
if (text.charCodeAt(colon) !== COLON) return null;
|
|
290
|
+
const start = skipSpaces(text, colon + 1);
|
|
291
|
+
const wanted = decode(key);
|
|
292
|
+
let end;
|
|
293
|
+
if (wanted && key === 'messages' && text.charCodeAt(start) === OPEN_ARRAY) {
|
|
294
|
+
const value = messagesValue(bytes, text, start, fullRoles, tailUntil);
|
|
295
|
+
if (!value) return null;
|
|
296
|
+
end = value.end;
|
|
297
|
+
defineMember(doc, key, value.messages);
|
|
298
|
+
} else {
|
|
299
|
+
end = valueEnd(text, start);
|
|
300
|
+
if (end === -1) return null;
|
|
301
|
+
if (wanted) defineMember(doc, key, decodeSlice(bytes, start, end));
|
|
302
|
+
}
|
|
303
|
+
i = skipSpaces(text, end);
|
|
304
|
+
if (text.charCodeAt(i) === COMMA) {
|
|
305
|
+
i = skipSpaces(text, i + 1);
|
|
306
|
+
continue;
|
|
307
|
+
}
|
|
308
|
+
if (text.charCodeAt(i) !== CLOSE_OBJECT) return null;
|
|
309
|
+
i++;
|
|
310
|
+
break;
|
|
311
|
+
}
|
|
312
|
+
}
|
|
313
|
+
return skipSpaces(text, i) === text.length ? doc : null;
|
|
314
|
+
}
|
|
315
|
+
|
|
316
|
+
/**
|
|
317
|
+
* `readTopLevelLifecycleRecord` of the UTF-8 document in `bytes`, with `doc`
|
|
318
|
+
* decoded as described above — only for a proven document; a record from the
|
|
319
|
+
* authoritative fallback carries the complete document. Consumers must read
|
|
320
|
+
* nothing but non-skipped fields and, of non-full messages, `role`.
|
|
321
|
+
*/
|
|
322
|
+
export function readStrictRecordFields(bytes, { skip = EMPTY, fullMessageRoles = EMPTY, fullTailUntil = null } = {}) {
|
|
323
|
+
const doc = provenTopLevelObject(bytes, (key) => !skip.has(key), fullMessageRoles, fullTailUntil);
|
|
324
|
+
return doc ? lifecycleRecordOf(doc) : readTopLevelLifecycleRecord(bytes.toString('utf8'));
|
|
325
|
+
}
|