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
|
@@ -1,11 +1,288 @@
|
|
|
1
1
|
import { existsSync } from 'node:fs';
|
|
2
|
-
import { dirname, join } from 'node:path';
|
|
3
|
-
import { execFile, execFileSync } from 'node:child_process';
|
|
4
|
-
import {
|
|
2
|
+
import { dirname, join, resolve } from 'node:path';
|
|
3
|
+
import { execFile, execFileSync, spawn } from 'node:child_process';
|
|
4
|
+
import { fileURLToPath } from 'node:url';
|
|
5
|
+
import { MessageChannel, receiveMessageOnPort, SHARE_ENV, Worker } from 'node:worker_threads';
|
|
5
6
|
|
|
6
|
-
const execFileAsync = promisify(execFile);
|
|
7
7
|
let cachedUserSid = null;
|
|
8
8
|
|
|
9
|
+
// Resolved per call (not promisified once at load) so the live binding of
|
|
10
|
+
// child_process.execFile is used.
|
|
11
|
+
function execFileAsync(file, args, options) {
|
|
12
|
+
return new Promise((done, fail) => {
|
|
13
|
+
execFile(file, args, options, (error, stdout, stderr) => {
|
|
14
|
+
if (error) fail(Object.assign(error, { stdout, stderr }));
|
|
15
|
+
else done({ stdout, stderr });
|
|
16
|
+
});
|
|
17
|
+
});
|
|
18
|
+
}
|
|
19
|
+
|
|
20
|
+
// Securing a published secret by spawning PowerShell costs ~150-200 ms per
|
|
21
|
+
// call, and secrets are read far more often than they change. Nothing is
|
|
22
|
+
// cached: every non-fresh async call still reads the file's real DACL and
|
|
23
|
+
// tightens it when it is not owner-only. File metadata (mtime/ctime/size/id)
|
|
24
|
+
// is never treated as evidence of permissions. To make that per-call check
|
|
25
|
+
// cheap, one long-lived PowerShell helper per process answers requests over
|
|
26
|
+
// stdin/stdout ("<id> <base64 path>" in, "<id> ok|fixed|err <base64 detail>"
|
|
27
|
+
// out); concurrent callers share it and are matched by request id. If the
|
|
28
|
+
// helper cannot start, crashes, closes its pipes or misses the request
|
|
29
|
+
// deadline, every request it held (and later ones, once the restart budget is
|
|
30
|
+
// spent) falls back to the spawn-per-call update, so a lost helper never
|
|
31
|
+
// skips a check. A definite ACL failure reported by the helper is an error,
|
|
32
|
+
// exactly like a failed per-call update.
|
|
33
|
+
const ACL_HELPER_REQUEST_TIMEOUT_MS = 15_000;
|
|
34
|
+
// At most this many helper starts per window. A lifetime budget let three
|
|
35
|
+
// unrelated losses (a request deadline missed across a system sleep, an
|
|
36
|
+
// antivirus stall) pin a days-old process to a PowerShell spawn per read.
|
|
37
|
+
const ACL_HELPER_MAX_STARTS = 3;
|
|
38
|
+
const ACL_HELPER_START_WINDOW_MS = 10 * 60_000;
|
|
39
|
+
let aclHelper = null;
|
|
40
|
+
let aclHelperStartTimes = [];
|
|
41
|
+
let aclHelperSeq = 0;
|
|
42
|
+
|
|
43
|
+
function aclHelperScript(sid) {
|
|
44
|
+
return [
|
|
45
|
+
"$ErrorActionPreference = 'Stop';",
|
|
46
|
+
`$sid = [System.Security.Principal.SecurityIdentifier]::new(${quoteForPowerShell(sid)});`,
|
|
47
|
+
'$full = [System.Security.AccessControl.FileSystemRights]::FullControl;',
|
|
48
|
+
'$utf8 = [System.Text.Encoding]::UTF8;',
|
|
49
|
+
'$out = [Console]::Out;',
|
|
50
|
+
'while ($null -ne ($line = [Console]::In.ReadLine())) {',
|
|
51
|
+
'$parts = $line.Split(@([char]32), 2);',
|
|
52
|
+
'if ($parts.Count -ne 2) { continue };',
|
|
53
|
+
'try {',
|
|
54
|
+
'$path = $utf8.GetString([Convert]::FromBase64String($parts[1]));',
|
|
55
|
+
'$directory = [System.IO.Directory]::Exists($path);',
|
|
56
|
+
'$current = if ($directory) { [System.IO.Directory]::GetAccessControl($path) }',
|
|
57
|
+
'else { [System.IO.File]::GetAccessControl($path) };',
|
|
58
|
+
'$rules = @($current.GetAccessRules($true, $true, [System.Security.Principal.SecurityIdentifier]));',
|
|
59
|
+
'$ownerOnly = $current.AreAccessRulesProtected -and $rules.Count -eq 1 -and',
|
|
60
|
+
"$rules[0].AccessControlType -eq 'Allow' -and $rules[0].IdentityReference -eq $sid -and",
|
|
61
|
+
'$rules[0].FileSystemRights -eq $full;',
|
|
62
|
+
"if ($ownerOnly) { $out.WriteLine($parts[0] + ' ok') } else {",
|
|
63
|
+
'$acl = if ($directory) { [System.Security.AccessControl.DirectorySecurity]::new() }',
|
|
64
|
+
'else { [System.Security.AccessControl.FileSecurity]::new() };',
|
|
65
|
+
'$acl.SetAccessRuleProtection($true, $false);',
|
|
66
|
+
'$acl.AddAccessRule([System.Security.AccessControl.FileSystemAccessRule]::new(',
|
|
67
|
+
'$sid, $full, [System.Security.AccessControl.AccessControlType]::Allow));',
|
|
68
|
+
'if ($directory) { [System.IO.Directory]::SetAccessControl($path, $acl) }',
|
|
69
|
+
'else { [System.IO.File]::SetAccessControl($path, $acl) };',
|
|
70
|
+
"$out.WriteLine($parts[0] + ' fixed') }",
|
|
71
|
+
'} catch {',
|
|
72
|
+
"$out.WriteLine($parts[0] + ' err ' + [Convert]::ToBase64String($utf8.GetBytes([string]$_.Exception.Message)))",
|
|
73
|
+
'};',
|
|
74
|
+
'$out.Flush() }',
|
|
75
|
+
].join(' ');
|
|
76
|
+
}
|
|
77
|
+
|
|
78
|
+
function startAclHelper(icacls, sid) {
|
|
79
|
+
const now = Date.now();
|
|
80
|
+
aclHelperStartTimes = aclHelperStartTimes.filter((at) => now - at < ACL_HELPER_START_WINDOW_MS);
|
|
81
|
+
if (aclHelperStartTimes.length >= ACL_HELPER_MAX_STARTS) return null;
|
|
82
|
+
aclHelperStartTimes.push(now);
|
|
83
|
+
const command = powerShellCommand(icacls, aclHelperScript(sid));
|
|
84
|
+
let child;
|
|
85
|
+
try {
|
|
86
|
+
// Long-lived: run from System32 so it never pins the caller's working
|
|
87
|
+
// directory (a folder a process's cwd sits in cannot be removed).
|
|
88
|
+
child = spawn(command.executable, command.args, {
|
|
89
|
+
cwd: dirname(icacls),
|
|
90
|
+
stdio: ['pipe', 'pipe', 'ignore'],
|
|
91
|
+
windowsHide: true,
|
|
92
|
+
});
|
|
93
|
+
} catch {
|
|
94
|
+
return null;
|
|
95
|
+
}
|
|
96
|
+
const helper = { child, pending: new Map(), buffer: '', dead: false };
|
|
97
|
+
helper.fail = (cause) => {
|
|
98
|
+
if (aclHelper === helper) aclHelper = null;
|
|
99
|
+
if (helper.dead) return;
|
|
100
|
+
helper.dead = true;
|
|
101
|
+
const lost = [...helper.pending.values()];
|
|
102
|
+
helper.pending.clear();
|
|
103
|
+
for (const request of lost) request.lost(cause);
|
|
104
|
+
try {
|
|
105
|
+
child.kill();
|
|
106
|
+
} catch {}
|
|
107
|
+
};
|
|
108
|
+
child.on('error', helper.fail);
|
|
109
|
+
child.on('exit', () => helper.fail(new Error('Windows ACL helper exited')));
|
|
110
|
+
child.stdin.on('error', helper.fail);
|
|
111
|
+
child.stdout.on('error', helper.fail);
|
|
112
|
+
child.stdout.on('close', () => helper.fail(new Error('Windows ACL helper closed its output')));
|
|
113
|
+
child.stdout.setEncoding('utf8');
|
|
114
|
+
child.stdout.on('data', (chunk) => {
|
|
115
|
+
helper.buffer += chunk;
|
|
116
|
+
let newline = helper.buffer.indexOf('\n');
|
|
117
|
+
while (newline >= 0) {
|
|
118
|
+
const [id, status, detail = ''] = helper.buffer.slice(0, newline).trim().split(' ');
|
|
119
|
+
helper.buffer = helper.buffer.slice(newline + 1);
|
|
120
|
+
newline = helper.buffer.indexOf('\n');
|
|
121
|
+
const request = helper.pending.get(id);
|
|
122
|
+
if (!request) continue;
|
|
123
|
+
helper.pending.delete(id);
|
|
124
|
+
syncAclHelperRef(helper);
|
|
125
|
+
request.answer(status, Buffer.from(detail, 'base64').toString('utf8'));
|
|
126
|
+
}
|
|
127
|
+
});
|
|
128
|
+
syncAclHelperRef(helper);
|
|
129
|
+
return helper;
|
|
130
|
+
}
|
|
131
|
+
|
|
132
|
+
// An idle helper never keeps the process alive; while a request awaits its
|
|
133
|
+
// answer the helper's handles do, so the answer is delivered even when the
|
|
134
|
+
// request's deadline timer is mocked or otherwise not a live handle.
|
|
135
|
+
function syncAclHelperRef(helper) {
|
|
136
|
+
const method = helper.pending.size > 0 ? 'ref' : 'unref';
|
|
137
|
+
helper.child[method]();
|
|
138
|
+
helper.child.stdin[method]?.();
|
|
139
|
+
helper.child.stdout[method]?.();
|
|
140
|
+
}
|
|
141
|
+
|
|
142
|
+
/** Resolves `{ status, detail }` from the helper, or `{ lost }` when it could not answer. */
|
|
143
|
+
function askAclHelper(targetPath, icacls, sid) {
|
|
144
|
+
if (!aclHelper) aclHelper = startAclHelper(icacls, sid);
|
|
145
|
+
const helper = aclHelper;
|
|
146
|
+
if (!helper) return Promise.resolve({ lost: new Error('Windows ACL helper unavailable') });
|
|
147
|
+
return new Promise((done) => {
|
|
148
|
+
aclHelperSeq += 1;
|
|
149
|
+
const id = String(aclHelperSeq);
|
|
150
|
+
const timer = setTimeout(() => {
|
|
151
|
+
helper.pending.delete(id);
|
|
152
|
+
syncAclHelperRef(helper);
|
|
153
|
+
done({ lost: new Error('Windows ACL helper timed out') });
|
|
154
|
+
helper.fail(new Error('Windows ACL helper timed out'));
|
|
155
|
+
}, ACL_HELPER_REQUEST_TIMEOUT_MS);
|
|
156
|
+
helper.pending.set(id, {
|
|
157
|
+
answer(status, detail) {
|
|
158
|
+
clearTimeout(timer);
|
|
159
|
+
done({ status, detail });
|
|
160
|
+
},
|
|
161
|
+
lost(cause) {
|
|
162
|
+
clearTimeout(timer);
|
|
163
|
+
done({ lost: cause });
|
|
164
|
+
},
|
|
165
|
+
});
|
|
166
|
+
syncAclHelperRef(helper);
|
|
167
|
+
helper.child.stdin.write(`${id} ${Buffer.from(targetPath, 'utf8').toString('base64')}\n`);
|
|
168
|
+
});
|
|
169
|
+
}
|
|
170
|
+
|
|
171
|
+
// ── Sync bridge ─────────────────────────────────────────────────────────
|
|
172
|
+
// Synchronous secret writes (token stores, config) cannot await the helper,
|
|
173
|
+
// and a blocking icacls spawn per write held the event loop (and the write's
|
|
174
|
+
// file lock) for seconds under load. A worker thread owns a second helper;
|
|
175
|
+
// a synchronous caller posts its path and blocks in Atomics.wait only for
|
|
176
|
+
// the answer — milliseconds once the helper runs (prewarmOwnerOnlyAclWin32
|
|
177
|
+
// starts it off the write path). Shared slots: [0] answer counter, [1] the
|
|
178
|
+
// worker exited, [2] the worker is ready. A worker not ready within
|
|
179
|
+
// ACL_SYNC_BRIDGE_READY_MS, or gone, sends the caller to the one-shot
|
|
180
|
+
// update; an answer later than ACL_SYNC_BRIDGE_TIMEOUT_MS fails the write.
|
|
181
|
+
const ACL_SYNC_BRIDGE_READY_MS = 10_000;
|
|
182
|
+
// The helper's own deadline plus its one-shot fallback's, with margin.
|
|
183
|
+
const ACL_SYNC_BRIDGE_TIMEOUT_MS = 2 * ACL_HELPER_REQUEST_TIMEOUT_MS + 5_000;
|
|
184
|
+
let aclSyncBridge = null;
|
|
185
|
+
let aclSyncBridgeBroken = false;
|
|
186
|
+
|
|
187
|
+
function syncBridge() {
|
|
188
|
+
if (aclSyncBridge || aclSyncBridgeBroken) return aclSyncBridge;
|
|
189
|
+
try {
|
|
190
|
+
const url = new URL('./file-permissions-worker.mjs', import.meta.url);
|
|
191
|
+
// Bundled copies (the desktop service) ship no worker beside them.
|
|
192
|
+
if (!existsSync(fileURLToPath(url))) throw new Error('Windows ACL bridge worker is not shipped here');
|
|
193
|
+
const { port1, port2 } = new MessageChannel();
|
|
194
|
+
const signal = new Int32Array(new SharedArrayBuffer(12));
|
|
195
|
+
const worker = new Worker(url, {
|
|
196
|
+
env: SHARE_ENV,
|
|
197
|
+
execArgv: [],
|
|
198
|
+
workerData: { port: port2, signal },
|
|
199
|
+
transferList: [port2],
|
|
200
|
+
});
|
|
201
|
+
const bridge = { worker, port: port1, signal, seq: 0, startedAt: Date.now() };
|
|
202
|
+
const drop = () => {
|
|
203
|
+
if (aclSyncBridge === bridge) aclSyncBridge = null;
|
|
204
|
+
};
|
|
205
|
+
worker.on('error', drop);
|
|
206
|
+
worker.on('exit', drop);
|
|
207
|
+
worker.unref();
|
|
208
|
+
port1.unref?.();
|
|
209
|
+
aclSyncBridge = bridge;
|
|
210
|
+
} catch {
|
|
211
|
+
aclSyncBridgeBroken = true;
|
|
212
|
+
}
|
|
213
|
+
return aclSyncBridge;
|
|
214
|
+
}
|
|
215
|
+
|
|
216
|
+
function dropSyncBridge(bridge) {
|
|
217
|
+
if (aclSyncBridge !== bridge) return;
|
|
218
|
+
aclSyncBridge = null;
|
|
219
|
+
void bridge.worker.terminate();
|
|
220
|
+
}
|
|
221
|
+
|
|
222
|
+
/** `{ ok: true }`, `{ ok: false, message, code }` (a definite ACL failure),
|
|
223
|
+
* `{ timedOut: true }`, or `{ lost }` when no bridge could answer. */
|
|
224
|
+
function askSyncBridge(targetPath) {
|
|
225
|
+
const bridge = syncBridge();
|
|
226
|
+
if (!bridge) return { lost: new Error('Windows ACL bridge unavailable') };
|
|
227
|
+
bridge.seq += 1;
|
|
228
|
+
const id = bridge.seq;
|
|
229
|
+
let seen = Atomics.load(bridge.signal, 0);
|
|
230
|
+
bridge.port.postMessage({ id, path: targetPath });
|
|
231
|
+
const deadline = Date.now() + ACL_SYNC_BRIDGE_TIMEOUT_MS;
|
|
232
|
+
for (;;) {
|
|
233
|
+
for (let received = receiveMessageOnPort(bridge.port); received; received = receiveMessageOnPort(bridge.port)) {
|
|
234
|
+
if (received.message?.id === id) return received.message;
|
|
235
|
+
}
|
|
236
|
+
const now = Date.now();
|
|
237
|
+
const ready = Atomics.load(bridge.signal, 2) === 1;
|
|
238
|
+
if (Atomics.load(bridge.signal, 1) === 1 || (!ready && now - bridge.startedAt >= ACL_SYNC_BRIDGE_READY_MS)) {
|
|
239
|
+
dropSyncBridge(bridge);
|
|
240
|
+
return { lost: new Error('Windows ACL bridge exited') };
|
|
241
|
+
}
|
|
242
|
+
if (now >= deadline) return { timedOut: true };
|
|
243
|
+
const wait = ready ? deadline - now : Math.min(deadline - now, bridge.startedAt + ACL_SYNC_BRIDGE_READY_MS - now);
|
|
244
|
+
Atomics.wait(bridge.signal, 0, seen, Math.max(1, wait));
|
|
245
|
+
seen = Atomics.load(bridge.signal, 0);
|
|
246
|
+
}
|
|
247
|
+
}
|
|
248
|
+
|
|
249
|
+
/** Test hook: synchronous calls skip the bridge (its one-shot fallback runs). */
|
|
250
|
+
export function disableOwnerOnlyAclSyncBridgeForTests() {
|
|
251
|
+
aclSyncBridgeBroken = true;
|
|
252
|
+
if (aclSyncBridge) dropSyncBridge(aclSyncBridge);
|
|
253
|
+
}
|
|
254
|
+
|
|
255
|
+
/** Starts, off any write path, what owner-only enforcement needs on Windows:
|
|
256
|
+
* this thread's SID and helper, and the sync bridge with its own helper.
|
|
257
|
+
* Best-effort; never rejects. */
|
|
258
|
+
export async function prewarmOwnerOnlyAclWin32({ bridge = true } = {}) {
|
|
259
|
+
if (process.platform !== 'win32') return;
|
|
260
|
+
try {
|
|
261
|
+
const icacls = icaclsPath();
|
|
262
|
+
if (cachedUserSid === null) cachedUserSid = await resolveCurrentUserPrincipalAsync();
|
|
263
|
+
if (!aclHelper) aclHelper = startAclHelper(icacls, cachedUserSid);
|
|
264
|
+
if (bridge) syncBridge()?.port.postMessage({ warm: true });
|
|
265
|
+
} catch {
|
|
266
|
+
/* enforcement itself reports any failure */
|
|
267
|
+
}
|
|
268
|
+
}
|
|
269
|
+
|
|
270
|
+
/** Test/diagnostic hook: whether a helper is attached, its requests awaiting
|
|
271
|
+
* an answer, and the starts inside the current restart window. */
|
|
272
|
+
export function ownerOnlyAclHelperStats() {
|
|
273
|
+
return {
|
|
274
|
+
alive: aclHelper !== null,
|
|
275
|
+
pid: aclHelper?.child.pid ?? null,
|
|
276
|
+
pending: aclHelper?.pending.size ?? 0,
|
|
277
|
+
recentStarts: aclHelperStartTimes.length,
|
|
278
|
+
};
|
|
279
|
+
}
|
|
280
|
+
|
|
281
|
+
/** Test hook: the live helper process, or null. */
|
|
282
|
+
export function ownerOnlyAclHelperForTests() {
|
|
283
|
+
return aclHelper?.child ?? null;
|
|
284
|
+
}
|
|
285
|
+
|
|
9
286
|
function systemToolPath(name) {
|
|
10
287
|
const systemRoot = process.env.SystemRoot || process.env.windir;
|
|
11
288
|
return systemRoot ? join(systemRoot, 'System32', name) : null;
|
|
@@ -155,6 +432,18 @@ function aclFailure(targetPath, cause, label = 'icacls') {
|
|
|
155
432
|
export function enforceOwnerOnlyAclWin32(targetPath, { fresh = false } = {}) {
|
|
156
433
|
if (process.platform !== 'win32') return;
|
|
157
434
|
const icacls = icaclsPath();
|
|
435
|
+
// Fresh files (the temp and lock files of every synchronous secret write,
|
|
436
|
+
// made while its file lock is held) go to the helper through the sync
|
|
437
|
+
// bridge: one round trip, no process spawned. Only a bridge that is gone
|
|
438
|
+
// falls back to the blocking one-shot update below.
|
|
439
|
+
if (fresh) {
|
|
440
|
+
const reply = askSyncBridge(resolve(targetPath));
|
|
441
|
+
if (reply.ok === true) return;
|
|
442
|
+
if (reply.ok === false) throw Object.assign(new Error(reply.message), { code: reply.code || 'EACLFAIL' });
|
|
443
|
+
if (reply.timedOut) {
|
|
444
|
+
throw aclFailure(targetPath, new Error('the Windows ACL bridge did not answer'), 'Windows ACL helper');
|
|
445
|
+
}
|
|
446
|
+
}
|
|
158
447
|
if (cachedUserSid === null) cachedUserSid = resolveCurrentUserPrincipal();
|
|
159
448
|
const command = ownerOnlyAclCommand(targetPath, cachedUserSid, fresh, icacls);
|
|
160
449
|
try {
|
|
@@ -168,8 +457,25 @@ export function enforceOwnerOnlyAclWin32(targetPath, { fresh = false } = {}) {
|
|
|
168
457
|
}
|
|
169
458
|
}
|
|
170
459
|
|
|
460
|
+
// Fresh files (unpublished temp/lock files, one or two per secret write) go
|
|
461
|
+
// through the helper too: never owner-only yet, each is secured explicitly by
|
|
462
|
+
// its DACL replacement. An icacls spawn per write, made inside the write's
|
|
463
|
+
// file lock, kept provider-accounts.json locked for 114 s under heavy load
|
|
464
|
+
// (each spawn blocked the event loop for seconds as well).
|
|
171
465
|
export async function enforceOwnerOnlyAclWin32Async(targetPath, { fresh = false } = {}) {
|
|
172
466
|
if (process.platform !== 'win32') return;
|
|
467
|
+
const icacls = icaclsPath();
|
|
468
|
+
if (cachedUserSid === null) cachedUserSid = await resolveCurrentUserPrincipalAsync();
|
|
469
|
+
const reply = await askAclHelper(resolve(targetPath), icacls, cachedUserSid);
|
|
470
|
+
if (reply.status === 'ok' || reply.status === 'fixed') return;
|
|
471
|
+
if (reply.status === 'err') {
|
|
472
|
+
throw aclFailure(targetPath, new Error(reply.detail || 'unknown error'), 'Windows ACL helper');
|
|
473
|
+
}
|
|
474
|
+
// The helper could not answer: verify with a one-shot update instead.
|
|
475
|
+
return applyOwnerOnlyAclAsync(targetPath, fresh);
|
|
476
|
+
}
|
|
477
|
+
|
|
478
|
+
async function applyOwnerOnlyAclAsync(targetPath, fresh) {
|
|
173
479
|
const icacls = icaclsPath();
|
|
174
480
|
if (cachedUserSid === null) cachedUserSid = await resolveCurrentUserPrincipalAsync();
|
|
175
481
|
const command = ownerOnlyAclCommand(targetPath, cachedUserSid, fresh, icacls);
|
|
@@ -1,80 +1,8 @@
|
|
|
1
|
-
// Worker-thread body for git-runner.mjs
|
|
2
|
-
//
|
|
3
|
-
//
|
|
4
|
-
//
|
|
5
|
-
|
|
6
|
-
import { spawn } from 'node:child_process';
|
|
1
|
+
// Worker-thread body for git-runner.mjs: git children are spawned on this
|
|
2
|
+
// thread, never on the host event loop (run-child.mjs). Each request resolves
|
|
3
|
+
// to { code, stdout, stderr } or an error with the same codes the in-process
|
|
4
|
+
// runner used (ETIMEDOUT, EMAXBUFFER, spawn errors).
|
|
5
|
+
import { runChild } from './run-child.mjs';
|
|
7
6
|
import { serveWorkerRequests } from './worker-requests.mjs';
|
|
8
7
|
|
|
9
|
-
|
|
10
|
-
return new Promise((resolvePromise, rejectPromise) => {
|
|
11
|
-
const hasInput = (typeof input === 'string' || ArrayBuffer.isView(input)) && input.length > 0;
|
|
12
|
-
const child = spawn('git', args, {
|
|
13
|
-
cwd,
|
|
14
|
-
...(env ? { env } : {}),
|
|
15
|
-
windowsHide: true,
|
|
16
|
-
stdio: [hasInput ? 'pipe' : 'ignore', 'pipe', 'pipe'],
|
|
17
|
-
});
|
|
18
|
-
const stdout = [];
|
|
19
|
-
const stderr = [];
|
|
20
|
-
let outputBytes = 0;
|
|
21
|
-
let stdinError = null;
|
|
22
|
-
let settled = false;
|
|
23
|
-
const finish = (error, result) => {
|
|
24
|
-
if (settled) return;
|
|
25
|
-
settled = true;
|
|
26
|
-
clearTimeout(timer);
|
|
27
|
-
if (error) rejectPromise(error);
|
|
28
|
-
else resolvePromise(result);
|
|
29
|
-
};
|
|
30
|
-
const timer = setTimeout(() => {
|
|
31
|
-
const error = new Error(`git ${args.join(' ')} timed out after ${timeoutMs}ms`);
|
|
32
|
-
error.code = 'ETIMEDOUT';
|
|
33
|
-
try {
|
|
34
|
-
child.kill();
|
|
35
|
-
} catch {}
|
|
36
|
-
finish(error);
|
|
37
|
-
}, timeoutMs);
|
|
38
|
-
const collect = (chunks, chunk) => {
|
|
39
|
-
const next = Buffer.from(chunk);
|
|
40
|
-
outputBytes += next.length;
|
|
41
|
-
if (outputBytes > maxBytes) {
|
|
42
|
-
const error = new Error(`git ${args.join(' ')} exceeded ${maxBytes} output bytes`);
|
|
43
|
-
error.code = 'EMAXBUFFER';
|
|
44
|
-
try {
|
|
45
|
-
child.kill();
|
|
46
|
-
} catch {}
|
|
47
|
-
finish(error);
|
|
48
|
-
return;
|
|
49
|
-
}
|
|
50
|
-
chunks.push(next);
|
|
51
|
-
};
|
|
52
|
-
child.stdout.on('data', (chunk) => collect(stdout, chunk));
|
|
53
|
-
child.stderr.on('data', (chunk) => collect(stderr, chunk));
|
|
54
|
-
child.once('error', (error) => finish(error));
|
|
55
|
-
child.once('close', (code) => {
|
|
56
|
-
const result = {
|
|
57
|
-
code: Number(code ?? 1),
|
|
58
|
-
// 'base64' keeps non-UTF-8 bytes (a replayable patch) intact across the thread hop.
|
|
59
|
-
stdout: Buffer.concat(stdout).toString(stdoutEncoding === 'base64' ? 'base64' : 'utf8'),
|
|
60
|
-
stderr: Buffer.concat(stderr).toString('utf8'),
|
|
61
|
-
};
|
|
62
|
-
// A failed stdin write only matters when git otherwise reported success.
|
|
63
|
-
if (stdinError && result.code === 0) {
|
|
64
|
-
finish(stdinError);
|
|
65
|
-
return;
|
|
66
|
-
}
|
|
67
|
-
finish(null, result);
|
|
68
|
-
});
|
|
69
|
-
if (child.stdin) {
|
|
70
|
-
// A short-lived git command can close before stdin is flushed. Keep
|
|
71
|
-
// that transport race inside this promise instead of an unhandled EPIPE.
|
|
72
|
-
child.stdin.on('error', (error) => {
|
|
73
|
-
stdinError = error;
|
|
74
|
-
});
|
|
75
|
-
child.stdin.end(input);
|
|
76
|
-
}
|
|
77
|
-
});
|
|
78
|
-
}
|
|
79
|
-
|
|
80
|
-
serveWorkerRequests(runGit);
|
|
8
|
+
serveWorkerRequests((request) => runChild({ ...request, command: 'git' }));
|
|
@@ -0,0 +1,24 @@
|
|
|
1
|
+
// Mixdog's own Google OAuth client (a "Desktop app" client, marked "Use this
|
|
2
|
+
// client for an AI-powered agent"), used by the Gmail, Google Calendar and
|
|
3
|
+
// Google Drive catalog connectors instead of dynamic client registration,
|
|
4
|
+
// which Google does not offer. Google treats desktop clients as public: the
|
|
5
|
+
// id and its non-confidential secret ship with the app, and every sign-in
|
|
6
|
+
// uses PKCE (S256), state and a loopback redirect
|
|
7
|
+
// (developers.google.com/identity/protocols/oauth2/native-app).
|
|
8
|
+
//
|
|
9
|
+
// The values come only from the environment; the source ships none, so a
|
|
10
|
+
// build without them offers no built-in Google sign-in (the user's own
|
|
11
|
+
// Google client still works).
|
|
12
|
+
|
|
13
|
+
const value = (name) => String(process.env[name] || '').trim();
|
|
14
|
+
|
|
15
|
+
/** `{ clientId, clientSecret }` of Mixdog's Google client, or null when this
|
|
16
|
+
* build has none. */
|
|
17
|
+
export function googleOAuthClient() {
|
|
18
|
+
const clientId = value('MIXDOG_GOOGLE_OAUTH_CLIENT_ID');
|
|
19
|
+
if (!clientId) return null;
|
|
20
|
+
return { clientId, clientSecret: value('MIXDOG_GOOGLE_OAUTH_CLIENT_SECRET') };
|
|
21
|
+
}
|
|
22
|
+
|
|
23
|
+
/** Google issues a refresh token only for `access_type=offline`. */
|
|
24
|
+
export const GOOGLE_AUTH_PARAMS = Object.freeze({ access_type: 'offline' });
|
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
export interface HeldRequestPushEvent {
|
|
2
|
+
sessionId: string;
|
|
3
|
+
agentId: string;
|
|
4
|
+
agentName: string;
|
|
5
|
+
requestId: string;
|
|
6
|
+
}
|
|
7
|
+
export function registerHeldRequestResolver(resolver: (id: string, decision: unknown) => boolean): () => void;
|
|
8
|
+
export function resolveHeldRequest(id: string, decision: unknown): boolean;
|
|
9
|
+
export function registerHeldRequestNotifier(notifier: (event: HeldRequestPushEvent) => void): () => void;
|
|
10
|
+
export function notifyHeldRequest(event: Partial<HeldRequestPushEvent>): void;
|
|
11
|
+
export function recordRunOwner(sessionId: string, agentId: string): void;
|
|
12
|
+
export function runOwnerOf(sessionId: string): string;
|
|
@@ -0,0 +1,71 @@
|
|
|
1
|
+
// Runtime ↔ desktop-service channel for held requests of unattended runs
|
|
2
|
+
// (session-runtime/held-requests.mjs). The daemon's held-request registry
|
|
3
|
+
// registers the resolver; the desktop service hosted in this daemon registers
|
|
4
|
+
// a notifier that sends the sanitized phone push. The session API answers a
|
|
5
|
+
// held request through resolveHeldRequest, whatever session it was sent from.
|
|
6
|
+
//
|
|
7
|
+
// Deliberately dependency-free: the desktop service bundle imports this file
|
|
8
|
+
// too, and both copies share one registry through a global symbol.
|
|
9
|
+
|
|
10
|
+
const KEY = Symbol.for('mixdog.heldRequestHost.v1');
|
|
11
|
+
|
|
12
|
+
function registry() {
|
|
13
|
+
globalThis[KEY] ??= { resolver: null, notifiers: [], runOwners: new Map() };
|
|
14
|
+
return globalThis[KEY];
|
|
15
|
+
}
|
|
16
|
+
|
|
17
|
+
const RUN_OWNER_LIMIT = 500;
|
|
18
|
+
/** Remember which agent an unattended run session belongs to, so its pushes
|
|
19
|
+
* follow that agent's notification mute after the run ends (bounded, newest kept). */
|
|
20
|
+
export function recordRunOwner(sessionId, agentId) {
|
|
21
|
+
const owners = registry().runOwners;
|
|
22
|
+
owners.delete(sessionId);
|
|
23
|
+
owners.set(String(sessionId), String(agentId));
|
|
24
|
+
while (owners.size > RUN_OWNER_LIMIT) owners.delete(owners.keys().next().value);
|
|
25
|
+
}
|
|
26
|
+
|
|
27
|
+
export function runOwnerOf(sessionId) {
|
|
28
|
+
return registry().runOwners.get(String(sessionId || '')) || '';
|
|
29
|
+
}
|
|
30
|
+
|
|
31
|
+
/** `resolver(id, decision) → boolean` (true when `id` was a pending held request). */
|
|
32
|
+
export function registerHeldRequestResolver(resolver) {
|
|
33
|
+
registry().resolver = resolver;
|
|
34
|
+
return () => {
|
|
35
|
+
if (registry().resolver === resolver) registry().resolver = null;
|
|
36
|
+
};
|
|
37
|
+
}
|
|
38
|
+
|
|
39
|
+
export function resolveHeldRequest(id, decision) {
|
|
40
|
+
const { resolver } = registry();
|
|
41
|
+
return typeof resolver === 'function' && String(id || '').startsWith('held_')
|
|
42
|
+
? resolver(id, decision) === true
|
|
43
|
+
: false;
|
|
44
|
+
}
|
|
45
|
+
|
|
46
|
+
/** `notifier({ sessionId, agentId, agentName, requestId })`: one per desktop service. */
|
|
47
|
+
export function registerHeldRequestNotifier(notifier) {
|
|
48
|
+
const entry = { notifier };
|
|
49
|
+
registry().notifiers.push(entry);
|
|
50
|
+
return () => {
|
|
51
|
+
const list = registry().notifiers;
|
|
52
|
+
const index = list.indexOf(entry);
|
|
53
|
+
if (index >= 0) list.splice(index, 1);
|
|
54
|
+
};
|
|
55
|
+
}
|
|
56
|
+
|
|
57
|
+
/** Hand one held request to every registered notifier. Carries ids and the
|
|
58
|
+
* agent's name only: never the tool, its arguments or any text. */
|
|
59
|
+
export function notifyHeldRequest({ sessionId, agentId, agentName, requestId }) {
|
|
60
|
+
const event = {
|
|
61
|
+
sessionId: String(sessionId || ''),
|
|
62
|
+
agentId: String(agentId || ''),
|
|
63
|
+
agentName: String(agentName || ''),
|
|
64
|
+
requestId: String(requestId || ''),
|
|
65
|
+
};
|
|
66
|
+
for (const { notifier } of registry().notifiers) {
|
|
67
|
+
try {
|
|
68
|
+
notifier(event);
|
|
69
|
+
} catch {}
|
|
70
|
+
}
|
|
71
|
+
}
|
|
@@ -75,8 +75,8 @@ function proxyConfigured() {
|
|
|
75
75
|
* The shared singleton dispatcher for LLM API requests. Created on first use.
|
|
76
76
|
*
|
|
77
77
|
* keepAliveTimeout (~60s) keeps a socket warm across the typical idle gap
|
|
78
|
-
* between agent turns;
|
|
79
|
-
*
|
|
78
|
+
* between agent turns; sockets per origin are unbounded unless
|
|
79
|
+
* MIXDOG_LLM_CONNECTIONS sets a cap.
|
|
80
80
|
*
|
|
81
81
|
* Returns `undefined` when a proxy / any custom (non-default) global dispatcher
|
|
82
82
|
* is configured, so `fetch({ dispatcher: undefined })` falls back to that
|
|
@@ -93,7 +93,11 @@ export function getLlmDispatcher() {
|
|
|
93
93
|
_agent = new (undici().Agent)({
|
|
94
94
|
keepAliveTimeout: envInt('MIXDOG_LLM_KEEPALIVE_MS', 60_000),
|
|
95
95
|
keepAliveMaxTimeout: envInt('MIXDOG_LLM_KEEPALIVE_MAX_MS', 90_000),
|
|
96
|
-
|
|
96
|
+
// No per-origin socket cap by default: each streaming (SSE) response
|
|
97
|
+
// holds its HTTP/1.1 socket for the whole generation, so a cap of N
|
|
98
|
+
// queued the (N+1)th concurrent agent until another finished. Rate
|
|
99
|
+
// limiting is the server's call (429 → admission cooldown).
|
|
100
|
+
connections: envInt('MIXDOG_LLM_CONNECTIONS', null),
|
|
97
101
|
// undici defaults both of these to 300s, which beheaded the Anthropic
|
|
98
102
|
// NON-STREAMING stall fallback (2026-08-03 regex-chess probe:
|
|
99
103
|
// UND_ERR_HEADERS_TIMEOUT at exactly 300s) — a non-streaming completion
|
|
@@ -93,7 +93,17 @@ const PROVIDER_CATALOGS = Object.freeze({
|
|
|
93
93
|
},
|
|
94
94
|
'cursor-oauth': { relay: true },
|
|
95
95
|
'cursor-api': { relay: true },
|
|
96
|
+
// The models.dev listing id of the Go gateway: a relay catalog, not a
|
|
97
|
+
// selectable provider (the model lookup needs it flagged as a relay).
|
|
96
98
|
'opencode-go': { relay: true },
|
|
99
|
+
// The Go gateway through an OpenCode Console login; the organization
|
|
100
|
+
// config's catalog is the live roster.
|
|
101
|
+
'opencode-go-oauth': {
|
|
102
|
+
modelsDev: 'opencode-go',
|
|
103
|
+
relay: true,
|
|
104
|
+
rosterFiles: ['opencode-go-oauth-models.json'],
|
|
105
|
+
rosterLimitsAuthoritative: true,
|
|
106
|
+
},
|
|
97
107
|
});
|
|
98
108
|
|
|
99
109
|
// Confirmed upstream catalog gaps, keyed provider → model. Each entry patches
|
|
@@ -71,6 +71,8 @@ export async function accountProviderSend(provider, instance, send, model, opts
|
|
|
71
71
|
requestedModel: model,
|
|
72
72
|
pricingModel: result.pricingModel,
|
|
73
73
|
sessionId,
|
|
74
|
+
// The session this one runs for (an agent's Lead), kept with its spend.
|
|
75
|
+
ownerSessionId: opts.session?.ownerSessionId || opts.session?.parentSessionId,
|
|
74
76
|
sourceType,
|
|
75
77
|
inputTokens: usage.inputTokens,
|
|
76
78
|
inputTokensKnown: usage.inputTokensKnown,
|
|
@@ -10,6 +10,18 @@ import { createHash } from 'node:crypto';
|
|
|
10
10
|
import { makeUsageRecord, usageRouteKind } from './usage-ledger.mjs';
|
|
11
11
|
import { priceUsage } from './cost.mjs';
|
|
12
12
|
|
|
13
|
+
function legacyRollupDays(parsed) {
|
|
14
|
+
if (parsed === null || typeof parsed !== 'object' || Array.isArray(parsed)) {
|
|
15
|
+
throw new SyntaxError('legacy rollup root is not an object');
|
|
16
|
+
}
|
|
17
|
+
if (!Object.hasOwn(parsed, 'days')) throw new SyntaxError('legacy rollup days is missing');
|
|
18
|
+
const { days } = parsed;
|
|
19
|
+
// A day map is an object. An array is the other accepted collection; null,
|
|
20
|
+
// strings, and other scalars are not.
|
|
21
|
+
if (days === null || typeof days !== 'object') throw new SyntaxError('legacy rollup days is not an object or array');
|
|
22
|
+
return days;
|
|
23
|
+
}
|
|
24
|
+
|
|
13
25
|
async function optionalFile(path) {
|
|
14
26
|
try {
|
|
15
27
|
return await readFile(path, 'utf8');
|
|
@@ -165,9 +177,26 @@ export async function importUsageHistory(ledger, dataDir, { now = Date.now(), re
|
|
|
165
177
|
session_id: event.sessionId,
|
|
166
178
|
}))
|
|
167
179
|
);
|
|
168
|
-
|
|
169
|
-
|
|
170
|
-
|
|
180
|
+
// A missing file is absence. Empty, unparsable, or wrong-shaped JSON is not:
|
|
181
|
+
// skip only this legacy import and leave importedThrough unset so a repair
|
|
182
|
+
// is retried instead of short-circuiting forever.
|
|
183
|
+
const legacyPath = join(dataDir, 'usage-rollup.local.json');
|
|
184
|
+
const legacy = await optionalFile(legacyPath);
|
|
185
|
+
let legacyReady = true;
|
|
186
|
+
if (legacy !== null) {
|
|
187
|
+
let days;
|
|
188
|
+
try {
|
|
189
|
+
days = legacyRollupDays(JSON.parse(legacy));
|
|
190
|
+
} catch (error) {
|
|
191
|
+
if (!(error instanceof SyntaxError)) throw error;
|
|
192
|
+
legacyReady = false;
|
|
193
|
+
process.stderr.write(
|
|
194
|
+
`[usage-ledger] warning: legacy rollup ${legacyPath} is empty, corrupt, or the wrong shape (${error.message}); skipping its import\n`
|
|
195
|
+
);
|
|
196
|
+
}
|
|
197
|
+
if (legacyReady) ledger.preserveLegacyDays(repriceRestoredDays(days));
|
|
198
|
+
}
|
|
199
|
+
if (legacyReady) ledger.set('importedThrough', until);
|
|
171
200
|
ledger.set('importReceipt', JSON.stringify({ ...result, until }));
|
|
172
201
|
return result;
|
|
173
202
|
}
|