mixdog 1.2.6 → 1.3.1
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 +71 -9
- package/package.json +4 -1
- package/scripts/check-model-recommendations.mjs +89 -0
- package/scripts/gate-local.mjs +7 -0
- package/scripts/lib/isolated-test-env.mjs +16 -0
- package/src/agents/reviewer/AGENT.md +4 -2
- package/src/agents/reviewer/agent.json +1 -1
- package/src/defaults/shipped-history.json +10 -0
- package/src/defaults/skills/browser-use/SKILL.md +5 -0
- package/src/defaults/skills/memory-management/SKILL.md +4 -1
- package/src/defaults/skills/setup/references/actions.md +14 -4
- package/src/defaults/skills/setup-guide/SKILL.md +178 -0
- package/src/defaults/skills/setup-guide/model-recommendations.json +333 -0
- package/src/lib/rules-builder.cjs +55 -6
- package/src/output-styles/common.md +13 -8
- package/src/rules/agent/AGENT.md +4 -0
- package/src/rules/agent-mode/AGENT_MODE.md +24 -0
- package/src/rules/agent-mode/MIXY.md +28 -0
- package/src/rules/agent-mode/ROUTINE.md +6 -0
- package/src/rules/lead/LEAD.md +7 -3
- package/src/rules/shared/70-delivery.md +3 -0
- package/src/runtime/agent/orchestrator/agent-runtime/room-supervisor-completion.mjs +86 -0
- package/src/runtime/agent/orchestrator/context/deferred-tools.mjs +77 -14
- package/src/runtime/agent/orchestrator/context/role-instructions.mjs +241 -14
- package/src/runtime/agent/orchestrator/context/skill-catalog.mjs +4 -2
- package/src/runtime/agent/orchestrator/mcp/agent-connectors.mjs +216 -0
- package/src/runtime/agent/orchestrator/mcp/child-tree.mjs +5 -4
- package/src/runtime/agent/orchestrator/mcp/client-transport.mjs +10 -1
- package/src/runtime/agent/orchestrator/mcp/client.mjs +115 -11
- package/src/runtime/agent/orchestrator/mcp/oauth-account.mjs +97 -0
- 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/anthropic-oauth-client-version.mjs +1 -1
- package/src/runtime/agent/orchestrator/providers/anthropic-oauth-credentials.mjs +1 -0
- package/src/runtime/agent/orchestrator/providers/antigravity-oauth-catalog.mjs +7 -2
- package/src/runtime/agent/orchestrator/providers/antigravity-oauth-login.mjs +10 -3
- package/src/runtime/agent/orchestrator/providers/antigravity-oauth-tokens.mjs +4 -0
- package/src/runtime/agent/orchestrator/providers/auto-effort.mjs +7 -8
- package/src/runtime/agent/orchestrator/providers/cursor-client-version.mjs +1 -1
- package/src/runtime/agent/orchestrator/providers/lib/anthropic-request-utils.mjs +3 -1
- package/src/runtime/agent/orchestrator/providers/lib/tool-call-wire-ids.mjs +65 -0
- package/src/runtime/agent/orchestrator/providers/openai-compat-retry-profile.mjs +8 -2
- package/src/runtime/agent/orchestrator/providers/openai-compat-wire.mjs +9 -2
- package/src/runtime/agent/orchestrator/providers/openai-error-contract.mjs +17 -12
- package/src/runtime/agent/orchestrator/providers/openai-oauth-tokens.mjs +10 -0
- package/src/runtime/agent/orchestrator/providers/openai-responses-input.mjs +3 -1
- package/src/runtime/agent/orchestrator/providers/provider-error-kind.mjs +14 -2
- package/src/runtime/agent/orchestrator/providers/retry-executor.mjs +17 -0
- package/src/runtime/agent/orchestrator/runtime-core/ask-user-tool-defs.mjs +96 -1
- 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 +14 -0
- 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 +21 -5
- package/src/runtime/agent/orchestrator/runtime-core/workflow/agents.mjs +40 -7
- package/src/runtime/agent/orchestrator/runtime-core/workflow/context.mjs +52 -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-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 +166 -0
- package/src/runtime/agent/orchestrator/session/agent-room.mjs +650 -0
- package/src/runtime/agent/orchestrator/session/catalog-db/schema.mjs +7 -1
- 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 +192 -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/loop/compact-policy.mjs +3 -1
- package/src/runtime/agent/orchestrator/session/loop/loop-state.mjs +9 -0
- package/src/runtime/agent/orchestrator/session/loop/pre-dispatch-deny.mjs +10 -0
- package/src/runtime/agent/orchestrator/session/loop/route-fallback.mjs +108 -6
- package/src/runtime/agent/orchestrator/session/loop/send-phase.mjs +10 -1
- package/src/runtime/agent/orchestrator/session/loop/tool-exec/before-hook.mjs +74 -15
- package/src/runtime/agent/orchestrator/session/loop/tool-exec.mjs +9 -1
- 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 +49 -0
- package/src/runtime/agent/orchestrator/session/manager/ask-turn-commit.mjs +18 -8
- 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 +3 -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/pending-lifecycle-epoch.mjs +0 -8
- package/src/runtime/agent/orchestrator/session/manager/prompt-utils.mjs +44 -16
- package/src/runtime/agent/orchestrator/session/manager/provider-replay.mjs +50 -0
- package/src/runtime/agent/orchestrator/session/manager/rules-cache.mjs +22 -5
- package/src/runtime/agent/orchestrator/session/manager/session-crud.mjs +39 -4
- package/src/runtime/agent/orchestrator/session/manager/session-lifecycle.mjs +33 -24
- package/src/runtime/agent/orchestrator/session/manager/session-prompt-composition.mjs +58 -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/turn-checkpoint.mjs +7 -1
- 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/mixy-name.mjs +27 -0
- package/src/runtime/agent/orchestrator/session/pre-send-compact.mjs +32 -5
- package/src/runtime/agent/orchestrator/session/routine-result.d.mts +86 -0
- package/src/runtime/agent/orchestrator/session/routine-result.mjs +329 -0
- package/src/runtime/agent/orchestrator/session/store/sweep/sweep-row.mjs +8 -0
- package/src/runtime/agent/orchestrator/session/store-record-strict-scan.mjs +28 -9
- package/src/runtime/agent/orchestrator/session/store-summary-catalog.mjs +16 -2
- 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-visibility.mjs +0 -4
- package/src/runtime/agent/orchestrator/session/transcript-restore/restore.mjs +58 -10
- package/src/runtime/agent/orchestrator/session/transcript-restore/user-text.mjs +92 -15
- package/src/runtime/agent/orchestrator/tools/builtin/lib/shell-job-records.mjs +12 -0
- package/src/runtime/agent/orchestrator/tools/builtin/native-search-client.mjs +113 -5
- package/src/runtime/agent/orchestrator/tools/builtin/shell-jobs.mjs +4 -0
- package/src/runtime/agent/orchestrator/tools/lib/shell-descendants.mjs +25 -1
- package/src/runtime/agent/orchestrator/tools/lib/shell-run-background/promote-job.mjs +8 -0
- package/src/runtime/agent/orchestrator/tools/lib/shell-run-settle.mjs +6 -0
- package/src/runtime/agent/orchestrator/tools/lib/shell-run-state.mjs +3 -0
- package/src/runtime/agent/orchestrator/tools/shell-command.mjs +75 -57
- package/src/runtime/attachments/store.mjs +6 -0
- 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/effort-judge/judge-client.mjs +1 -1
- package/src/runtime/effort-judge/model-manifest.json +7 -7
- 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/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/tool-call-handler.mjs +2 -0
- package/src/runtime/memory/tool-defs.mjs +5 -0
- 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-kind.mjs +152 -0
- package/src/runtime/shared/agent-route-config.d.mts +4 -0
- package/src/runtime/shared/agent-route-config.mjs +30 -0
- package/src/runtime/shared/agent-store.mjs +132 -0
- package/src/runtime/shared/atomic-file.mjs +14 -1
- package/src/runtime/shared/bot-split-migration.mjs +457 -0
- package/src/runtime/shared/config-patch.mjs +20 -1
- package/src/runtime/shared/config.mjs +8 -1
- package/src/runtime/shared/connector-accounts.mjs +15 -0
- package/src/runtime/shared/connector-catalog.d.mts +51 -0
- package/src/runtime/shared/connector-catalog.mjs +228 -0
- package/src/runtime/shared/connector-triggers.d.mts +3 -0
- package/src/runtime/shared/connector-triggers.mjs +45 -0
- package/src/runtime/shared/err-text.mjs +3 -1
- package/src/runtime/shared/error-presentation.mjs +91 -6
- package/src/runtime/shared/google-oauth-client.mjs +24 -0
- package/src/runtime/shared/handoff-artifacts.d.mts +20 -0
- package/src/runtime/shared/handoff-artifacts.mjs +61 -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/provider-model-identities.mjs +6 -1
- package/src/runtime/shared/llm/usage-accounting.mjs +7 -1
- package/src/runtime/shared/model-route-fallback.mjs +79 -7
- package/src/runtime/shared/plain-preview.d.mts +1 -0
- package/src/runtime/shared/plain-preview.mjs +33 -0
- 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/schedule-model-ref.mjs +9 -10
- package/src/runtime/shared/sensitive-path-rules.d.mts +15 -0
- package/src/runtime/shared/sensitive-path-rules.mjs +256 -0
- package/src/runtime/shared/slow-span.mjs +4 -12
- package/src/runtime/shared/statusline/statusline-segments.mjs +13 -1
- 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 +58 -0
- package/src/runtime/shared/user-data-guard.mjs +119 -38
- package/src/session-runtime/agent-home-delivery.mjs +826 -0
- package/src/session-runtime/agent-profile-changes.mjs +21 -0
- package/src/session-runtime/agent-room-delivery.mjs +133 -0
- package/src/session-runtime/agent-task-sessions.mjs +460 -0
- package/src/session-runtime/boot/apis.mjs +63 -18
- package/src/session-runtime/boot/facade.mjs +5 -8
- package/src/session-runtime/boot/infrastructure.mjs +28 -17
- package/src/session-runtime/boot/providers.mjs +14 -4
- package/src/session-runtime/boot/tools.mjs +3 -0
- package/src/session-runtime/channel-config-api.mjs +53 -5
- package/src/session-runtime/config-lifecycle.mjs +43 -2
- 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/handoff-attachments.mjs +593 -0
- 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 +56 -3
- 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/resume.mjs +4 -0
- package/src/session-runtime/lifecycle/shared.mjs +6 -0
- 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/route-persist.mjs +2 -2
- package/src/session-runtime/model-route/route-tuning.mjs +7 -1
- package/src/session-runtime/model-route/set-route.mjs +35 -4
- package/src/session-runtime/native-web-search.mjs +2 -2
- package/src/session-runtime/new-session-config.mjs +6 -1
- package/src/session-runtime/owned-automation-run.mjs +229 -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/route-resolution.mjs +5 -11
- 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/helpers.mjs +30 -1
- package/src/session-runtime/services/agent-tool/send-flow.mjs +11 -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 +4 -1
- package/src/session-runtime/services/agent-tool/spawn-preset.mjs +5 -8
- package/src/session-runtime/services/agent-tool/tool-def.mjs +7 -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 +74 -39
- package/src/session-runtime/services/schedule-access.mjs +91 -0
- 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 +79 -6
- package/src/session-runtime/setup-tool/extended-actions.mjs +90 -20
- package/src/session-runtime/setup-tool/settings-contract.mjs +14 -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 +249 -29
- package/src/session-runtime/turn/session-ops.mjs +8 -1
- package/src/session-runtime/turn/turn-run/ask-callbacks.mjs +10 -2
- package/src/session-runtime/turn/turn-run/turn-prepare.mjs +14 -0
- 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 +650 -25
- package/src/session-runtime/workflow-agents-api/agent-route.mjs +67 -7
- package/src/session-runtime/workflow-agents-api/onboarding.mjs +11 -4
- 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/daemon-agent-control.mjs +1 -8
- package/src/standalone/daemon-session-control.mjs +21 -4
- package/src/standalone/daemon.mjs +31 -5
- package/src/standalone/session-protocol.mjs +27 -0
- package/src/standalone/session-service/agent-tree/agent-turns.mjs +46 -3
- package/src/standalone/session-service/agent-tree.mjs +4 -3
- package/src/standalone/session-service/session-calls/session-turns.mjs +28 -0
- package/src/standalone/session-service.mjs +19 -0
- package/src/tui/app/slash-commands.mjs +20 -0
- package/src/tui/dist/index.mjs +234 -95
- package/src/tui/session/context-state.mjs +6 -1
- package/src/tui/session/notice-surface.mjs +34 -4
- 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 +5 -0
- package/src/tui/session/session-api/integrations.mjs +8 -0
- 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 +61 -5
- package/src/tui/session/session-flow/drain.mjs +12 -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 +58 -24
- package/src/tui/session/tool-approval.mjs +26 -5
- package/src/tui/session/transcript-intake.mjs +8 -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 +64 -0
- package/src/tui/session/turn.mjs +34 -8
- package/src/session-runtime/automation-agents.mjs +0 -94
- package/src/session-runtime/automation-workflow.mjs +0 -59
|
@@ -0,0 +1,58 @@
|
|
|
1
|
+
// Session-cumulative figures behind the composer's usage readout, kept on the
|
|
2
|
+
// session record (`session.usageReadout`) so they survive restart, reload and
|
|
3
|
+
// compaction. One fold per model call of the session itself; calls of other
|
|
4
|
+
// sessions (delegated sub-agents have their own records) never enter.
|
|
5
|
+
//
|
|
6
|
+
// - cache hit = cacheRead / (input + cacheRead + cacheWrite) over every call
|
|
7
|
+
// whose prompt was measured. A call that could not measure its prompt
|
|
8
|
+
// (promptKnown === false) has unknown zeros, so it adds nothing.
|
|
9
|
+
// - speed = output tokens over request ms (provider request start → end of the
|
|
10
|
+
// response, the wait for the first token included: the same wall time as the
|
|
11
|
+
// usage ledger's `duration_ms`) of every call with positive output and
|
|
12
|
+
// positive duration: the same eligibility as the usage ledger and the usage
|
|
13
|
+
// dialog, so the readout and the dialog agree. Records saved before this
|
|
14
|
+
// definition carry `speedOutputTokens` / `speedMs` (first visible token →
|
|
15
|
+
// end); they are kept as they are and read only while no request-timed call
|
|
16
|
+
// has landed.
|
|
17
|
+
// - total tokens = input + cacheRead + cacheWrite + output, from the same sums.
|
|
18
|
+
|
|
19
|
+
function num(value) {
|
|
20
|
+
const n = Number(value);
|
|
21
|
+
return Number.isFinite(n) && n > 0 ? n : 0;
|
|
22
|
+
}
|
|
23
|
+
|
|
24
|
+
/** The zeroed totals record. */
|
|
25
|
+
export function emptyUsageReadout() {
|
|
26
|
+
return { input: 0, cacheRead: 0, cacheWrite: 0, output: 0, requestOutputTokens: 0, requestMs: 0 };
|
|
27
|
+
}
|
|
28
|
+
|
|
29
|
+
/**
|
|
30
|
+
* Fold one `onUsageDelta` event (and the call's request ms, 0 when it was
|
|
31
|
+
* not timed) into the totals; returns a new record, never mutates `totals`.
|
|
32
|
+
* Providers differ on whether input counts cache; the prompt total does not,
|
|
33
|
+
* so fresh input is the prompt less its cache read/write.
|
|
34
|
+
*/
|
|
35
|
+
export function foldUsageReadout(totals, delta = {}, requestMs = 0) {
|
|
36
|
+
const base = totals || emptyUsageReadout();
|
|
37
|
+
const next = { ...base };
|
|
38
|
+
if (delta.promptKnown !== false) {
|
|
39
|
+
const cacheRead = num(delta.deltaCachedRead);
|
|
40
|
+
const cacheWrite = num(delta.deltaCacheWrite);
|
|
41
|
+
const prompt = num(delta.deltaPrompt) || num(delta.deltaInput) + cacheRead + cacheWrite;
|
|
42
|
+
next.input = num(base.input) + Math.max(0, prompt - cacheRead - cacheWrite);
|
|
43
|
+
next.cacheRead = num(base.cacheRead) + cacheRead;
|
|
44
|
+
next.cacheWrite = num(base.cacheWrite) + cacheWrite;
|
|
45
|
+
}
|
|
46
|
+
const output = num(delta.deltaOutput);
|
|
47
|
+
// Every call's output enters the session's total tokens, timed or not. A
|
|
48
|
+
// record saved before the field existed has no output sum to extend, and a
|
|
49
|
+
// partial one would pass for the whole: it stays without until the session
|
|
50
|
+
// starts over.
|
|
51
|
+
if (Number.isFinite(base.output)) next.output = base.output + output;
|
|
52
|
+
const ms = num(requestMs);
|
|
53
|
+
if (ms > 0 && output > 0) {
|
|
54
|
+
next.requestOutputTokens = num(base.requestOutputTokens) + output;
|
|
55
|
+
next.requestMs = num(base.requestMs) + ms;
|
|
56
|
+
}
|
|
57
|
+
return next;
|
|
58
|
+
}
|
|
@@ -61,25 +61,92 @@ function scopeDirFor(dataDir) {
|
|
|
61
61
|
let lastBackupMs = 0;
|
|
62
62
|
let backupSeq = 0;
|
|
63
63
|
|
|
64
|
-
//
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
//
|
|
64
|
+
// `<iso-ms>-<10-digit seq>-<reason>`; an older build's name has no sequence.
|
|
65
|
+
const BACKUP_NAME_RE = /^(\d{4}-\d{2}-\d{2})T(\d{2})-(\d{2})-(\d{2})-(\d{3})Z-(?:(\d{10})-)?/;
|
|
66
|
+
|
|
67
|
+
// The newest backup order among `names` (time, then sequence). An older
|
|
68
|
+
// build's name sorts after every sequenced name of its millisecond, so its
|
|
69
|
+
// sequence counts as unbounded. Names that are no backup's are ignored.
|
|
70
|
+
function newestBackupOrder(names) {
|
|
71
|
+
let ms = 0;
|
|
72
|
+
let seq = -1;
|
|
73
|
+
for (const name of names) {
|
|
74
|
+
const match = BACKUP_NAME_RE.exec(name);
|
|
75
|
+
if (!match) continue;
|
|
76
|
+
const time = Date.parse(`${match[1]}T${match[2]}:${match[3]}:${match[4]}.${match[5]}Z`);
|
|
77
|
+
if (!Number.isFinite(time)) continue;
|
|
78
|
+
const order = match[6] === undefined ? Number.POSITIVE_INFINITY : Number(match[6]);
|
|
79
|
+
if (time > ms || (time === ms && order > seq)) {
|
|
80
|
+
ms = time;
|
|
81
|
+
seq = order;
|
|
82
|
+
}
|
|
83
|
+
}
|
|
84
|
+
return { ms, seq };
|
|
85
|
+
}
|
|
86
|
+
|
|
87
|
+
// A name that sorts after every backup in `names`, whichever process wrote
|
|
88
|
+
// them and whatever its clock said: the backup a restore picks as the newest
|
|
89
|
+
// is then the one written last, and pruning never takes it.
|
|
90
|
+
function nextBackupName(names, reason) {
|
|
91
|
+
const newest = newestBackupOrder(names);
|
|
92
|
+
lastBackupMs = Math.max(Date.now(), lastBackupMs, newest.ms);
|
|
93
|
+
if (lastBackupMs === newest.ms) {
|
|
94
|
+
if (newest.seq === Number.POSITIVE_INFINITY) lastBackupMs += 1;
|
|
95
|
+
else backupSeq = Math.max(backupSeq, newest.seq + 1);
|
|
96
|
+
}
|
|
97
|
+
const time = new Date(lastBackupMs).toISOString().replace(/[:.]/g, '-');
|
|
98
|
+
return `${time}-${String(backupSeq++).padStart(10, '0')}-${safeReason(reason)}`;
|
|
99
|
+
}
|
|
100
|
+
|
|
101
|
+
// The scope's backup directory names. A reservation lists the scope first and
|
|
102
|
+
// a listing failure throws: an order guessed without it could sort before the
|
|
103
|
+
// newest backup.
|
|
104
|
+
function scopeBackupNames(entries) {
|
|
105
|
+
return entries.filter((entry) => entry.isDirectory()).map((entry) => entry.name);
|
|
106
|
+
}
|
|
107
|
+
|
|
108
|
+
// Claims the next name with an exclusive mkdir (EEXIST retries after it).
|
|
109
|
+
// Returns the claimed dir and the scope's names including it, which pruning
|
|
110
|
+
// reuses instead of listing the scope again.
|
|
68
111
|
function reserveBackupDir(scopeDir, reason) {
|
|
69
112
|
mkdirSync(scopeDir, { recursive: true });
|
|
113
|
+
const names = scopeBackupNames(readdirSync(scopeDir, { withFileTypes: true }));
|
|
114
|
+
for (;;) {
|
|
115
|
+
const name = nextBackupName(names, reason);
|
|
116
|
+
names.push(name);
|
|
117
|
+
try {
|
|
118
|
+
mkdirSync(join(scopeDir, name));
|
|
119
|
+
return { backupDir: join(scopeDir, name), names };
|
|
120
|
+
} catch (error) {
|
|
121
|
+
if (error?.code !== 'EEXIST') throw error;
|
|
122
|
+
}
|
|
123
|
+
}
|
|
124
|
+
}
|
|
125
|
+
|
|
126
|
+
async function reserveBackupDirAsync(scopeDir, reason) {
|
|
127
|
+
await mkdirP(scopeDir, { recursive: true });
|
|
128
|
+
const names = scopeBackupNames(await readdirP(scopeDir, { withFileTypes: true }));
|
|
70
129
|
for (;;) {
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
const backupDir = join(scopeDir, `${time}-${String(backupSeq++).padStart(10, '0')}-${safeReason(reason)}`);
|
|
130
|
+
const name = nextBackupName(names, reason);
|
|
131
|
+
names.push(name);
|
|
74
132
|
try {
|
|
75
|
-
|
|
76
|
-
return backupDir;
|
|
133
|
+
await mkdirP(join(scopeDir, name));
|
|
134
|
+
return { backupDir: join(scopeDir, name), names };
|
|
77
135
|
} catch (error) {
|
|
78
136
|
if (error?.code !== 'EEXIST') throw error;
|
|
79
137
|
}
|
|
80
138
|
}
|
|
81
139
|
}
|
|
82
140
|
|
|
141
|
+
// The names past the newest `keep`, never the just-written backup.
|
|
142
|
+
function backupsToPrune(names, own, keep = 40) {
|
|
143
|
+
return [...names]
|
|
144
|
+
.sort()
|
|
145
|
+
.reverse()
|
|
146
|
+
.slice(keep)
|
|
147
|
+
.filter((name) => name !== own);
|
|
148
|
+
}
|
|
149
|
+
|
|
83
150
|
function safeReason(reason) {
|
|
84
151
|
return (
|
|
85
152
|
String(reason || 'snapshot')
|
|
@@ -197,14 +264,30 @@ function isStructurallyCompleteMixdogConfigBackup(parsed) {
|
|
|
197
264
|
return true;
|
|
198
265
|
}
|
|
199
266
|
|
|
200
|
-
|
|
201
|
-
|
|
267
|
+
// A backup that is absent, not a backup, or not JSON is no backup; any other
|
|
268
|
+
// failure (EIO, EACCES, EMFILE…) is transient and must not pass for one.
|
|
269
|
+
function isAbsentBackupError(error) {
|
|
270
|
+
return error instanceof SyntaxError || error?.code === 'ENOENT' || error?.code === 'ENOTDIR';
|
|
271
|
+
}
|
|
272
|
+
|
|
273
|
+
/** Backup directory names in `dir`, newest first; empty when absent, throws on other failures. */
|
|
274
|
+
function listBackupDirsNewestFirst(dir) {
|
|
202
275
|
try {
|
|
203
276
|
return readdirSync(dir, { withFileTypes: true })
|
|
204
277
|
.filter((entry) => entry.isDirectory())
|
|
205
278
|
.map((entry) => entry.name)
|
|
206
279
|
.sort()
|
|
207
280
|
.reverse();
|
|
281
|
+
} catch (error) {
|
|
282
|
+
if (isAbsentBackupError(error)) return [];
|
|
283
|
+
throw error;
|
|
284
|
+
}
|
|
285
|
+
}
|
|
286
|
+
|
|
287
|
+
/** Backup directory names in `dir`, newest first; empty when unreadable. */
|
|
288
|
+
function backupDirsNewestFirst(dir) {
|
|
289
|
+
try {
|
|
290
|
+
return listBackupDirsNewestFirst(dir);
|
|
208
291
|
} catch {
|
|
209
292
|
return [];
|
|
210
293
|
}
|
|
@@ -213,7 +296,8 @@ function backupDirsNewestFirst(dir) {
|
|
|
213
296
|
/**
|
|
214
297
|
* A backup is restorable only when its manifest names this data dir and this
|
|
215
298
|
* incarnation of it. Legacy unscoped backups carry no manifest, so they stay
|
|
216
|
-
* on disk but never match.
|
|
299
|
+
* on disk but never match. A transient read failure throws: skipping the
|
|
300
|
+
* backup would restore an older one instead.
|
|
217
301
|
*/
|
|
218
302
|
function readRestorableConfig(backupDir, dataDirKey, instanceId) {
|
|
219
303
|
try {
|
|
@@ -222,20 +306,21 @@ function readRestorableConfig(backupDir, dataDirKey, instanceId) {
|
|
|
222
306
|
if (manifest.instanceId !== instanceId) return null;
|
|
223
307
|
const parsed = JSON.parse(readFileSync(join(backupDir, 'mixdog-config.json'), 'utf8'));
|
|
224
308
|
return isStructurallyCompleteMixdogConfigBackup(parsed) ? parsed : null;
|
|
225
|
-
} catch {
|
|
226
|
-
return null;
|
|
309
|
+
} catch (error) {
|
|
310
|
+
if (isAbsentBackupError(error)) return null;
|
|
311
|
+
throw error;
|
|
227
312
|
}
|
|
228
313
|
}
|
|
229
314
|
|
|
230
315
|
function findRestorableConfig(dataDir, instanceId) {
|
|
231
316
|
const key = normalizeDataDir(dataDir);
|
|
232
317
|
const scopeDir = scopeDirFor(dataDir);
|
|
233
|
-
for (const name of
|
|
318
|
+
for (const name of listBackupDirsNewestFirst(scopeDir)) {
|
|
234
319
|
const parsed = readRestorableConfig(join(scopeDir, name), key, instanceId);
|
|
235
320
|
if (parsed) return parsed;
|
|
236
321
|
}
|
|
237
322
|
const root = getBackupRoot();
|
|
238
|
-
for (const name of
|
|
323
|
+
for (const name of listBackupDirsNewestFirst(root)) {
|
|
239
324
|
if (name.startsWith(SCOPE_PREFIX)) continue;
|
|
240
325
|
const parsed = readRestorableConfig(join(root, name), key, instanceId);
|
|
241
326
|
if (parsed) return parsed;
|
|
@@ -245,13 +330,15 @@ function findRestorableConfig(dataDir, instanceId) {
|
|
|
245
330
|
|
|
246
331
|
// Per-process restore lookup, keyed by data dir. A hit is reused while the
|
|
247
332
|
// backup root and the marker's instanceId are unchanged; this process's own
|
|
248
|
-
// backups invalidate it
|
|
333
|
+
// backups invalidate it, and a `fresh` lookup (an RMW write, which another
|
|
334
|
+
// process's newer backup may precede) always rescans.
|
|
249
335
|
const restoreLookupCache = new Map();
|
|
250
336
|
|
|
251
337
|
/**
|
|
252
338
|
* Newest structurally complete mixdog-config.json backed up from THIS data
|
|
253
339
|
* dir (skips degenerate single-section snapshots from a prior failed RMW).
|
|
254
340
|
* Never another data dir's config, and nothing for an uninitialized one.
|
|
341
|
+
* Throws on a transient read failure, which is never cached.
|
|
255
342
|
*/
|
|
256
343
|
export function loadLatestMixdogConfigFromBackup(dataDir, options) {
|
|
257
344
|
const instanceId = readInitInstanceId(dataDir);
|
|
@@ -259,7 +346,8 @@ export function loadLatestMixdogConfigFromBackup(dataDir, options) {
|
|
|
259
346
|
const key = normalizeDataDir(dataDir);
|
|
260
347
|
const root = getBackupRoot();
|
|
261
348
|
let hit = restoreLookupCache.get(key);
|
|
262
|
-
if (!hit || hit.root !== root || hit.instanceId !== instanceId) {
|
|
349
|
+
if (options?.fresh || !hit || hit.root !== root || hit.instanceId !== instanceId) {
|
|
350
|
+
restoreLookupCache.delete(key);
|
|
263
351
|
hit = { root, instanceId, config: findRestorableConfig(dataDir, instanceId) };
|
|
264
352
|
restoreLookupCache.set(key, hit);
|
|
265
353
|
}
|
|
@@ -297,8 +385,8 @@ function copyTree(src, dst, copied) {
|
|
|
297
385
|
}
|
|
298
386
|
|
|
299
387
|
/** Prune one data dir's scope only; other scopes and legacy entries stay. */
|
|
300
|
-
function pruneBackups(scopeDir,
|
|
301
|
-
for (const name of
|
|
388
|
+
function pruneBackups(scopeDir, names, own) {
|
|
389
|
+
for (const name of backupsToPrune(names, own)) {
|
|
302
390
|
try {
|
|
303
391
|
rmSync(join(scopeDir, name), { recursive: true, force: true });
|
|
304
392
|
} catch {}
|
|
@@ -364,23 +452,26 @@ function finishBackup(dataDir, backupDir, reason) {
|
|
|
364
452
|
legacyEvidenceCache.delete(normalizeDataDir(dataDir));
|
|
365
453
|
}
|
|
366
454
|
|
|
367
|
-
|
|
455
|
+
/** `dirs`: more data-dir folders this one snapshot also carries (a one-time
|
|
456
|
+
* migration's, e.g. agents/ and bots/); routine snapshots keep to
|
|
457
|
+
* USER_DATA_DIRS so the ring stays small. */
|
|
458
|
+
export function backupUserData(dataDir, reason = 'snapshot', { dirs = [] } = {}) {
|
|
368
459
|
if (skipBackups()) return { dir: null, copied: [] };
|
|
369
460
|
if (!dataDir || !existsSync(dataDir)) return { dir: null, copied: [] };
|
|
370
461
|
const scopeDir = scopeDirFor(dataDir);
|
|
371
|
-
const backupDir = reserveBackupDir(scopeDir, reason);
|
|
462
|
+
const { backupDir, names } = reserveBackupDir(scopeDir, reason);
|
|
372
463
|
const copied = [];
|
|
373
464
|
for (const rel of USER_DATA_FILES) {
|
|
374
465
|
const src = join(dataDir, rel);
|
|
375
466
|
if (existsSync(src)) copyTree(src, join(backupDir, rel), copied);
|
|
376
467
|
}
|
|
377
|
-
for (const rel of USER_DATA_DIRS) {
|
|
468
|
+
for (const rel of [...USER_DATA_DIRS, ...dirs]) {
|
|
378
469
|
const src = join(dataDir, rel);
|
|
379
470
|
if (existsSync(src)) copyTree(src, join(backupDir, rel), copied);
|
|
380
471
|
}
|
|
381
472
|
if (copied.length > 0) {
|
|
382
473
|
finishBackup(dataDir, backupDir, reason);
|
|
383
|
-
pruneBackups(scopeDir);
|
|
474
|
+
pruneBackups(scopeDir, names, basename(backupDir));
|
|
384
475
|
if (process.env.MIXDOG_SETUP_QUIET !== '1') {
|
|
385
476
|
process.stderr.write(`[user-data-backup] ${reason}: copied ${copied.length} file(s) to ${backupDir}\n`);
|
|
386
477
|
}
|
|
@@ -408,18 +499,8 @@ async function copyTreeAsync(src, dst, copied) {
|
|
|
408
499
|
copied.push(dst);
|
|
409
500
|
}
|
|
410
501
|
|
|
411
|
-
async function pruneBackupsAsync(scopeDir,
|
|
412
|
-
|
|
413
|
-
try {
|
|
414
|
-
entries = (await readdirP(scopeDir, { withFileTypes: true }))
|
|
415
|
-
.filter((entry) => entry.isDirectory())
|
|
416
|
-
.map((entry) => entry.name)
|
|
417
|
-
.sort()
|
|
418
|
-
.reverse();
|
|
419
|
-
} catch {
|
|
420
|
-
return;
|
|
421
|
-
}
|
|
422
|
-
for (const name of entries.slice(keep)) {
|
|
502
|
+
async function pruneBackupsAsync(scopeDir, names, own) {
|
|
503
|
+
for (const name of backupsToPrune(names, own)) {
|
|
423
504
|
try {
|
|
424
505
|
await rmP(join(scopeDir, name), { recursive: true, force: true });
|
|
425
506
|
} catch {}
|
|
@@ -430,7 +511,7 @@ export async function backupUserDataAsync(dataDir, reason = 'snapshot') {
|
|
|
430
511
|
if (skipBackups()) return { dir: null, copied: [] };
|
|
431
512
|
if (!dataDir || !existsSync(dataDir)) return { dir: null, copied: [] };
|
|
432
513
|
const scopeDir = scopeDirFor(dataDir);
|
|
433
|
-
const backupDir =
|
|
514
|
+
const { backupDir, names } = await reserveBackupDirAsync(scopeDir, reason);
|
|
434
515
|
const copied = [];
|
|
435
516
|
for (const rel of USER_DATA_FILES) {
|
|
436
517
|
const src = join(dataDir, rel);
|
|
@@ -442,7 +523,7 @@ export async function backupUserDataAsync(dataDir, reason = 'snapshot') {
|
|
|
442
523
|
}
|
|
443
524
|
if (copied.length > 0) {
|
|
444
525
|
finishBackup(dataDir, backupDir, reason);
|
|
445
|
-
await pruneBackupsAsync(scopeDir);
|
|
526
|
+
await pruneBackupsAsync(scopeDir, names, basename(backupDir));
|
|
446
527
|
if (process.env.MIXDOG_SETUP_QUIET !== '1') {
|
|
447
528
|
process.stderr.write(`[user-data-backup] ${reason}: copied ${copied.length} file(s) to ${backupDir}\n`);
|
|
448
529
|
}
|