mixdog 1.0.12 → 1.1.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/README.md +75 -181
- package/package.json +4 -2
- package/scripts/build-shipped-history.mjs +131 -0
- package/scripts/lib/run-node-tests.mjs +235 -13
- package/scripts/lib/test-failure-records.mjs +5 -0
- package/scripts/lib/test-failure-summary.mjs +9 -3
- package/scripts/lib/test-spawn-ledger.mjs +19 -0
- package/scripts/prune-embedding-runtime.mjs +9 -5
- package/scripts/test-direct.mjs +16 -1
- package/src/agents/advisor/AGENT.md +38 -0
- package/src/agents/advisor/agent.json +6 -0
- package/src/agents/reviewer/AGENT.md +5 -2
- package/src/agents/reviewer/agent.json +1 -1
- package/src/defaults/shipped-history.json +381 -0
- package/src/defaults/skills/goal-management/SKILL.md +11 -5
- package/src/defaults/skills/setup/SKILL.md +5 -8
- package/src/defaults/skills/setup/references/actions.md +61 -31
- package/src/defaults/skills/setup/references/surfaces.md +12 -10
- package/src/lib/rules-builder.cjs +1 -1
- package/src/runtime/agent/orchestrator/agent-runtime/cache-strategy.mjs +3 -1
- package/src/runtime/agent/orchestrator/agent-runtime/session-builder.mjs +4 -0
- package/src/runtime/agent/orchestrator/config-presets.d.mts +1 -0
- package/src/runtime/agent/orchestrator/config-presets.mjs +44 -0
- package/src/runtime/agent/orchestrator/config-providers-merge.mjs +9 -1
- package/src/runtime/agent/orchestrator/config.mjs +102 -21
- package/src/runtime/agent/orchestrator/providers/account-pool.mjs +77 -26
- package/src/runtime/agent/orchestrator/providers/anthropic-api-request.mjs +44 -14
- package/src/runtime/agent/orchestrator/providers/anthropic-oauth-credentials.mjs +2 -1
- package/src/runtime/agent/orchestrator/providers/anthropic.mjs +11 -2
- package/src/runtime/agent/orchestrator/providers/antigravity-oauth-login.mjs +2 -0
- package/src/runtime/agent/orchestrator/providers/cursor-auth.mjs +3 -3
- package/src/runtime/agent/orchestrator/providers/custom-provider-config.mjs +388 -0
- package/src/runtime/agent/orchestrator/providers/custom-provider.mjs +92 -109
- package/src/runtime/agent/orchestrator/providers/custom-reasoning.mjs +107 -0
- package/src/runtime/agent/orchestrator/providers/effort-configuration.mjs +51 -9
- package/src/runtime/agent/orchestrator/providers/grok-oauth-login.mjs +2 -1
- package/src/runtime/agent/orchestrator/providers/oauth-usage.mjs +14 -9
- package/src/runtime/agent/orchestrator/providers/openai-compat-chat-send.mjs +51 -11
- package/src/runtime/agent/orchestrator/providers/openai-compat-chat-stream.mjs +6 -1
- package/src/runtime/agent/orchestrator/providers/openai-compat-models.mjs +7 -5
- package/src/runtime/agent/orchestrator/providers/openai-compat-options.mjs +12 -0
- package/src/runtime/agent/orchestrator/providers/openai-compat-responses.mjs +27 -7
- package/src/runtime/agent/orchestrator/providers/openai-compat-wire.mjs +12 -2
- package/src/runtime/agent/orchestrator/providers/openai-compat.mjs +6 -1
- package/src/runtime/agent/orchestrator/providers/openai-oauth-login.mjs +2 -1
- package/src/runtime/agent/orchestrator/providers/quota-reading.mjs +70 -0
- package/src/runtime/agent/orchestrator/providers/registry.mjs +38 -3
- package/src/runtime/agent/orchestrator/runtime-core/builtin-features.mjs +19 -35
- package/src/runtime/agent/orchestrator/runtime-core/effort.mjs +5 -0
- package/src/runtime/agent/orchestrator/runtime-core/goal-mutations.mjs +0 -17
- package/src/runtime/agent/orchestrator/runtime-core/goal-state.mjs +0 -3
- package/src/runtime/agent/orchestrator/runtime-core/goal-store.mjs +0 -1
- package/src/runtime/agent/orchestrator/runtime-core/goal-text.mjs +1 -1
- package/src/runtime/agent/orchestrator/runtime-core/goal-tool-defs.mjs +1 -1
- package/src/runtime/agent/orchestrator/runtime-core/goal-tool-exec.mjs +1 -2
- package/src/runtime/agent/orchestrator/runtime-core/goal-turns.mjs +18 -4
- package/src/runtime/agent/orchestrator/runtime-core/orchestration.mjs +7 -1
- package/src/runtime/agent/orchestrator/runtime-core/session-route-policy.mjs +16 -0
- package/src/runtime/agent/orchestrator/runtime-core/tool-catalog.mjs +2 -0
- package/src/runtime/agent/orchestrator/runtime-core/tool-defs.mjs +2 -2
- package/src/runtime/agent/orchestrator/runtime-core/workflow/context.mjs +7 -7
- package/src/runtime/agent/orchestrator/runtime-core/workflow.mjs +6 -0
- package/src/runtime/agent/orchestrator/session/loop/step-auto-effort.mjs +28 -3
- package/src/runtime/agent/orchestrator/session/manager/ask-turn-auto-effort.mjs +24 -11
- package/src/runtime/agent/orchestrator/session/manager/ask-turn-execute.mjs +2 -6
- package/src/runtime/agent/orchestrator/session/manager/session-lifecycle.mjs +63 -3
- package/src/runtime/agent/orchestrator/session/manager/session-tool-surface.mjs +4 -4
- package/src/runtime/agent/orchestrator/session/transcript-restore/restore.mjs +1 -0
- package/src/runtime/agent/orchestrator/stall-policy.mjs +0 -1
- package/src/runtime/agent/orchestrator/tools/builtin/atomic-write.mjs +18 -8
- package/src/runtime/agent/orchestrator/tools/builtin/cache-layers.mjs +19 -20
- package/src/runtime/agent/orchestrator/tools/builtin/external-tool-adapters.mjs +6 -11
- package/src/runtime/agent/orchestrator/tools/builtin/file-version-test-helpers.mjs +11 -0
- package/src/runtime/agent/orchestrator/tools/builtin/file-version.mjs +13 -0
- package/src/runtime/agent/orchestrator/tools/builtin/lib/grep-single-file-rescue.mjs +8 -5
- package/src/runtime/agent/orchestrator/tools/builtin/lib/shell-task-output.mjs +25 -12
- package/src/runtime/agent/orchestrator/tools/builtin/raw-content-publication.mjs +17 -0
- package/src/runtime/agent/orchestrator/tools/builtin/read-range-index.mjs +136 -76
- package/src/runtime/agent/orchestrator/tools/builtin/read-single-render.mjs +15 -17
- package/src/runtime/agent/orchestrator/tools/builtin/read-single-tool.mjs +4 -2
- package/src/runtime/agent/orchestrator/tools/builtin/read-snapshot-runtime.mjs +11 -10
- package/src/runtime/agent/orchestrator/tools/builtin/read-source-windows.mjs +5 -3
- package/src/runtime/agent/orchestrator/tools/builtin/read-streaming.mjs +30 -16
- package/src/runtime/agent/orchestrator/tools/builtin/snapshot-helpers.mjs +4 -11
- package/src/runtime/agent/orchestrator/tools/builtin/snapshot-store.mjs +7 -3
- package/src/runtime/agent/orchestrator/tools/builtin/snapshot-validation.mjs +16 -19
- package/src/runtime/agent/orchestrator/tools/graph-manifest.json +13 -13
- package/src/runtime/agent/orchestrator/tools/lib/shell-run-background.mjs +4 -1
- package/src/runtime/agent/orchestrator/tools/lib/shell-run-settle.mjs +14 -4
- package/src/runtime/agent/orchestrator/tools/patch/apply-patch/codex-batch.mjs +58 -8
- package/src/runtime/agent/orchestrator/tools/patch/content-guard.mjs +47 -0
- package/src/runtime/agent/orchestrator/tools/patch/delete-quarantine.mjs +155 -0
- package/src/runtime/agent/orchestrator/tools/patch/dispatch/native-proofs.mjs +6 -2
- package/src/runtime/agent/orchestrator/tools/patch/dispatch/native-snapshots.mjs +6 -2
- package/src/runtime/agent/orchestrator/tools/patch/dispatch.mjs +19 -16
- package/src/runtime/agent/orchestrator/tools/patch/matcher.mjs +13 -44
- package/src/runtime/agent/orchestrator/tools/patch/orchestrator.mjs +2 -2
- package/src/runtime/agent/orchestrator/tools/patch/post-patch-excerpts.mjs +5 -2
- package/src/runtime/agent/orchestrator/tools/patch/replay-capture.mjs +47 -7
- package/src/runtime/agent/orchestrator/tools/patch/rollback-state.mjs +72 -8
- package/src/runtime/agent/orchestrator/tools/patch/section-coalesce.mjs +3 -2
- package/src/runtime/agent/orchestrator/tools/patch/sequence/run.mjs +9 -1
- package/src/runtime/agent/orchestrator/tools/patch/sequence/units.mjs +9 -1
- package/src/runtime/agent/orchestrator/tools/patch/sequence.mjs +6 -6
- package/src/runtime/agent/orchestrator/tools/patch/ui-diff.mjs +16 -4
- package/src/runtime/agent/orchestrator/tools/patch/v4a-convert.mjs +35 -14
- package/src/runtime/agent/orchestrator/tools/patch/wave.mjs +42 -4
- package/src/runtime/agent/orchestrator/tools/shell-codepage.mjs +296 -0
- package/src/runtime/agent/orchestrator/tools/shell-exec-output.mjs +75 -24
- package/src/runtime/channels/lib/config.mjs +3 -8
- package/src/runtime/channels/lib/voice-runtime-fetcher.mjs +7 -3
- package/src/{tui → runtime/channels}/lib/voice-setup.mjs +38 -22
- package/src/runtime/channels/lib/webhook/request-handler.mjs +42 -8
- package/src/runtime/computer-bridge/actions.mjs +2 -0
- package/src/runtime/effort-judge/judge-client.mjs +12 -3
- package/src/runtime/effort-judge/model-manifest.json +7 -7
- package/src/runtime/effort-judge/previous-human-request.mjs +162 -0
- package/src/runtime/effort-judge/verified-context-input.mjs +10 -0
- package/src/runtime/memory/lib/embedding-provider.mjs +12 -0
- package/src/runtime/memory/lib/http-router/session-routes.mjs +2 -1
- package/src/runtime/memory/lib/memory-config-flags.mjs +3 -2
- package/src/runtime/memory/lib/memory-embed.mjs +5 -0
- package/src/runtime/shared/agent-route-config.mjs +50 -2
- package/src/runtime/shared/bounded-file-read.mjs +166 -0
- package/src/runtime/shared/builtin-grandfathering.mjs +37 -0
- package/src/runtime/shared/config.mjs +24 -4
- package/src/runtime/shared/defaults-separation.mjs +263 -0
- package/src/runtime/shared/llm/anthropic-effort.mjs +4 -0
- package/src/runtime/shared/llm/model-catalog.mjs +23 -0
- package/src/runtime/shared/llm/usage-ledger-rollup.mjs +105 -1
- package/src/runtime/shared/llm/usage-ledger-worker.mjs +2 -1
- package/src/runtime/shared/llm/usage-ledger.mjs +72 -5
- package/src/runtime/shared/onnx-runtime-support.mjs +6 -0
- package/src/runtime/shared/orchestration.mjs +14 -6
- package/src/runtime/shared/projects.mjs +65 -48
- package/src/runtime/shared/provider-accounts.mjs +47 -6
- package/src/runtime/shared/quota-resume-store.mjs +255 -0
- package/src/runtime/shared/shipped-definitions.mjs +275 -0
- package/src/runtime/shared/tool-card-model.golden.json +3 -3
- package/src/runtime/shared/tool-result-summary.mjs +86 -15
- package/src/runtime/shared/transcript-metadata.mjs +2 -0
- package/src/runtime/shared/turn-review-reply.mjs +2 -0
- package/src/runtime/shared/turn-snapshot.mjs +65 -6
- package/src/runtime/shared/turn-worktree-snapshot.mjs +202 -22
- package/src/session-runtime/boot/apis.mjs +19 -0
- package/src/session-runtime/boot/config.mjs +4 -0
- package/src/session-runtime/boot/facade.mjs +4 -1
- package/src/session-runtime/boot/infrastructure.mjs +21 -2
- package/src/session-runtime/boot/session.mjs +3 -0
- package/src/session-runtime/channel-config-api.mjs +5 -0
- package/src/session-runtime/config-lifecycle/config-writers.mjs +3 -0
- package/src/session-runtime/config-lifecycle.mjs +31 -6
- package/src/session-runtime/goal-facade-api.mjs +4 -0
- package/src/session-runtime/lifecycle/inheritance.mjs +28 -6
- package/src/session-runtime/lifecycle/session-switch/context-switch.mjs +3 -0
- package/src/session-runtime/lifecycle/session-switch/resume.mjs +7 -1
- package/src/session-runtime/lifecycle/session-switch/session-delete.mjs +6 -0
- package/src/session-runtime/lifecycle/teardown/session-close.mjs +14 -2
- package/src/session-runtime/media-api.mjs +32 -22
- package/src/session-runtime/model-recency.mjs +14 -0
- package/src/session-runtime/model-route/route-tuning.mjs +3 -5
- package/src/session-runtime/model-route/set-route.mjs +49 -21
- package/src/session-runtime/model-route-api.mjs +2 -1
- package/src/session-runtime/notification-bus.mjs +31 -6
- package/src/session-runtime/provider-auth/login.mjs +6 -0
- package/src/session-runtime/provider-models/catalog-load.mjs +3 -1
- package/src/session-runtime/provider-models/row-hydration.mjs +15 -14
- package/src/session-runtime/quick-model-rows/model-meta.mjs +26 -0
- package/src/session-runtime/quick-model-rows/provider-rows.mjs +40 -14
- package/src/session-runtime/resource-api.mjs +8 -1
- package/src/session-runtime/runtime-core.mjs +4 -0
- package/src/session-runtime/runtime-facade.mjs +20 -1
- package/src/session-runtime/runtime-review-api.mjs +12 -1
- package/src/session-runtime/services/agent-tool/helpers.mjs +4 -0
- package/src/session-runtime/services/agent-tool/index-write-queue.mjs +10 -15
- package/src/session-runtime/services/agent-tool/spawn-flow/spawn-prep.mjs +11 -3
- package/src/session-runtime/services/agent-tool/spawn-preset.mjs +2 -0
- package/src/session-runtime/services/channel-admin.mjs +87 -13
- package/src/session-runtime/services/provider-admin.mjs +256 -63
- package/src/session-runtime/services/seeds.mjs +73 -142
- package/src/session-runtime/services/usage-dashboard-model.mjs +29 -0
- package/src/session-runtime/services/usage-dashboard.mjs +7 -0
- package/src/session-runtime/session-address.mjs +125 -0
- package/src/session-runtime/session-create.mjs +57 -2
- package/src/session-runtime/settings-builtin-tools-api.mjs +8 -0
- package/src/session-runtime/settings-compaction-api.mjs +13 -0
- package/src/session-runtime/setup-tool/executor.mjs +92 -40
- package/src/session-runtime/setup-tool/extended-actions.mjs +64 -5
- package/src/session-runtime/setup-tool/settings-contract.mjs +44 -1
- package/src/session-runtime/setup-tool/tool-defs.mjs +12 -7
- package/src/session-runtime/tool-surface.mjs +16 -9
- package/src/session-runtime/turn/session-ops.mjs +3 -0
- package/src/session-runtime/turn/share-ops.mjs +4 -1
- package/src/session-runtime/turn/turn-run/turn-prepare.mjs +6 -0
- package/src/session-runtime/usage-stats-api.mjs +15 -0
- package/src/session-runtime/workflow-agents-api/agent-editor.mjs +31 -5
- package/src/session-runtime/workflow-agents-api/agent-route.mjs +4 -0
- package/src/session-runtime/workflow-agents-api/workflow-packs.mjs +27 -7
- package/src/standalone/daemon-boot-coordinator.mjs +10 -0
- package/src/standalone/daemon-drain-policy.mjs +9 -0
- package/src/standalone/daemon-stored-session-views.mjs +7 -8
- package/src/standalone/daemon.mjs +21 -5
- package/src/standalone/local-session-runtime.mjs +11 -0
- package/src/standalone/session-client.mjs +52 -7
- package/src/standalone/session-protocol.mjs +8 -0
- package/src/standalone/session-service/agent-tree/agent-child.mjs +2 -0
- package/src/standalone/session-service/entries/lifecycle.mjs +2 -0
- package/src/standalone/session-service/entries/session-loads.mjs +9 -0
- package/src/standalone/session-service/projection/revisions.mjs +28 -4
- package/src/standalone/session-service/projection.mjs +1 -0
- package/src/standalone/session-service/quota-resume.mjs +140 -0
- package/src/standalone/session-service/session-calls/session-action.mjs +2 -2
- package/src/standalone/session-service/session-calls.mjs +10 -2
- package/src/standalone/session-service/stored-reader.mjs +26 -1
- package/src/standalone/session-service.mjs +21 -0
- package/src/standalone/session-transport/routes.mjs +2 -0
- package/src/tui/app/doctor.mjs +7 -11
- package/src/tui/app/maintenance-pickers/auto-clear-picker.mjs +3 -5
- package/src/tui/app/settings-picker/settings-rows.mjs +4 -6
- package/src/tui/app/settings-picker/settings-toggles/store-toggles.mjs +1 -1
- package/src/tui/app/slash-dispatch/route-commands.mjs +3 -2
- package/src/tui/components/ContextPanel.jsx +5 -1
- package/src/tui/components/Spinner.jsx +34 -319
- package/src/tui/components/SpinnerByline.jsx +256 -0
- package/src/tui/components/use-spinner-progress.mjs +113 -0
- package/src/tui/dist/index.mjs +15540 -14452
- package/src/tui/session/agent-job-feed/execution-delivery.mjs +13 -1
- package/src/tui/session/agent-job-feed/notification-router.mjs +3 -0
- package/src/tui/session/agent-job-feed/pending-resume.mjs +14 -1
- package/src/tui/session/agent-job-feed.mjs +4 -0
- package/src/tui/session/context-state.mjs +13 -1
- package/src/tui/session/initial-state.mjs +6 -0
- package/src/tui/session/quota-resume.mjs +263 -0
- package/src/tui/session/session-api/intake/abort.mjs +39 -35
- package/src/tui/session/session-api/intake/submission.mjs +13 -21
- package/src/tui/session/session-api/intake.mjs +3 -3
- package/src/tui/session/session-api/integrations.mjs +5 -1
- package/src/tui/session/session-api/lifecycle/dispose.mjs +1 -0
- package/src/tui/session/session-api/lifecycle/inheritance.mjs +2 -2
- package/src/tui/session/session-api/lifecycle/session-reset.mjs +4 -0
- package/src/tui/session/session-api/lifecycle.mjs +71 -2
- package/src/tui/session/session-api/media.mjs +2 -2
- package/src/tui/session/session-api/quota-resume.mjs +21 -0
- package/src/tui/session/session-api/routes.mjs +2 -0
- package/src/tui/session/session-api/settings.mjs +1 -0
- package/src/tui/session/session-api.mjs +2 -0
- package/src/tui/session/session-flow/auto-clear.mjs +14 -6
- package/src/tui/session/session-flow/drain.mjs +52 -5
- package/src/tui/session/session-flow/queue/take-entries.mjs +2 -3
- package/src/tui/session/session-flow/queue.mjs +3 -0
- package/src/tui/session/session-flow/steering.mjs +11 -0
- package/src/tui/session/session-flow.mjs +1 -0
- package/src/tui/session/tool-approval.mjs +7 -2
- package/src/tui/session/transcript-intake.mjs +1 -0
- package/src/tui/session/turn.mjs +40 -3
- package/src/vendor/statusline/bin/statusline-route.mjs +3 -2
|
@@ -1,7 +1,17 @@
|
|
|
1
|
-
import { existsSync, mkdirSync, readdirSync, readFileSync,
|
|
1
|
+
import { existsSync, mkdirSync, readdirSync, readFileSync, writeFileSync } from 'node:fs';
|
|
2
2
|
import { join } from 'node:path';
|
|
3
3
|
import { pluginSkillsRoots } from '../../runtime/shared/plugin-manifest.mjs';
|
|
4
|
-
import {
|
|
4
|
+
import {
|
|
5
|
+
EMPTY_HISTORY,
|
|
6
|
+
HISTORY_MARKER_FILE,
|
|
7
|
+
fileHash,
|
|
8
|
+
historyStamp,
|
|
9
|
+
holdsOnlyEditorFiles,
|
|
10
|
+
loadShippedHistory,
|
|
11
|
+
makeBackup,
|
|
12
|
+
packHashFromDir,
|
|
13
|
+
treeHashFromDir,
|
|
14
|
+
} from '../../runtime/shared/shipped-definitions.mjs';
|
|
5
15
|
|
|
6
16
|
export function ensureStandaloneEnvironment({ rootDir, dataDir }) {
|
|
7
17
|
if (!rootDir) throw new Error('standalone rootDir is required');
|
|
@@ -17,8 +27,27 @@ export function ensureStandaloneEnvironment({ rootDir, dataDir }) {
|
|
|
17
27
|
process.env.MIXDOG_PATCH_NATIVE_PREWARM ??= '0';
|
|
18
28
|
|
|
19
29
|
mkdirSync(dataDir, { recursive: true });
|
|
20
|
-
|
|
21
|
-
|
|
30
|
+
// Copies equal to the CURRENT shipped version are retired on every start; the
|
|
31
|
+
// (larger) historical comparison runs once per separation/history version.
|
|
32
|
+
const backup = makeBackup(dataDir);
|
|
33
|
+
const markerFile = join(dataDir, HISTORY_MARKER_FILE);
|
|
34
|
+
const stamp = historyStamp(rootDir);
|
|
35
|
+
let recorded = '';
|
|
36
|
+
try {
|
|
37
|
+
recorded = readFileSync(markerFile, 'utf8').trim();
|
|
38
|
+
} catch {
|
|
39
|
+
recorded = '';
|
|
40
|
+
}
|
|
41
|
+
const includeHistory = recorded !== stamp;
|
|
42
|
+
retireSeededSkillCopies({ rootDir, dataDir, backup, includeHistory });
|
|
43
|
+
retireSeededPackCopies({ rootDir, dataDir, backup, includeHistory });
|
|
44
|
+
if (includeHistory && !backup.failures) {
|
|
45
|
+
try {
|
|
46
|
+
writeFileSync(markerFile, `${stamp}\n`);
|
|
47
|
+
} catch {
|
|
48
|
+
/* marker only; the sweep simply repeats */
|
|
49
|
+
}
|
|
50
|
+
}
|
|
22
51
|
cleanupRetiredChannelSecrets(dataDir);
|
|
23
52
|
}
|
|
24
53
|
|
|
@@ -52,16 +81,21 @@ function cleanupRetiredChannelSecrets(dataDir) {
|
|
|
52
81
|
});
|
|
53
82
|
}
|
|
54
83
|
|
|
55
|
-
//
|
|
56
|
-
//
|
|
57
|
-
//
|
|
58
|
-
//
|
|
59
|
-
//
|
|
60
|
-
//
|
|
61
|
-
//
|
|
62
|
-
|
|
84
|
+
// Shipped definitions live in code (read in place from the package); the data
|
|
85
|
+
// dir should hold only copies the user changed. Older releases copied, or the
|
|
86
|
+
// editors saved, full copies that then pin the text of the day they were made.
|
|
87
|
+
// A copy is "unchanged" when it equals the current shipped version or ANY past
|
|
88
|
+
// shipped version (hashes in defaults/shipped-history.json, generated from git
|
|
89
|
+
// by scripts/build-shipped-history.mjs, normalised for CRLF/trailing space).
|
|
90
|
+
// Unchanged copies are moved — never deleted — into
|
|
91
|
+
// <dataDir>/backups/defaults-separation-<timestamp>/, so the shipped version
|
|
92
|
+
// applies again; anything else is the user's own and stays.
|
|
93
|
+
|
|
94
|
+
// Built-in skills (<root>/defaults/skills/<name>/): compared as a whole tree.
|
|
95
|
+
export function retireSeededSkillCopies({ rootDir, dataDir, backup = makeBackup(dataDir), includeHistory = true }) {
|
|
63
96
|
const targetRoot = join(dataDir, 'skills');
|
|
64
97
|
if (!existsSync(targetRoot)) return [];
|
|
98
|
+
const history = (includeHistory ? loadShippedHistory(rootDir) : EMPTY_HISTORY).skills;
|
|
65
99
|
const retired = [];
|
|
66
100
|
// The bundle is a plugin root; walk the same skill roots the collector reads.
|
|
67
101
|
for (const bundledDir of pluginSkillsRoots(join(rootDir, 'defaults'))) {
|
|
@@ -75,79 +109,53 @@ export function retireSeededSkillCopies({ rootDir, dataDir }) {
|
|
|
75
109
|
if (!entry.isDirectory()) continue;
|
|
76
110
|
const copy = join(targetRoot, entry.name);
|
|
77
111
|
if (!existsSync(copy)) continue;
|
|
78
|
-
|
|
79
|
-
if (
|
|
112
|
+
const copyHash = treeHashFromDir(copy);
|
|
113
|
+
if (!copyHash) continue;
|
|
114
|
+
const known = [treeHashFromDir(join(bundledDir, entry.name)), ...(history[entry.name] || [])];
|
|
115
|
+
if (!known.includes(copyHash)) continue;
|
|
116
|
+
if (backup.move(copy)) retired.push(entry.name);
|
|
80
117
|
}
|
|
81
118
|
}
|
|
82
119
|
return retired;
|
|
83
120
|
}
|
|
84
121
|
|
|
85
|
-
function sameTree(left, right) {
|
|
86
|
-
let leftEntries;
|
|
87
|
-
let rightEntries;
|
|
88
|
-
try {
|
|
89
|
-
leftEntries = readdirSync(left, { withFileTypes: true });
|
|
90
|
-
rightEntries = readdirSync(right, { withFileTypes: true });
|
|
91
|
-
} catch {
|
|
92
|
-
return false;
|
|
93
|
-
}
|
|
94
|
-
const names = (entries) => entries.map((entry) => entry.name).sort();
|
|
95
|
-
if (names(leftEntries).join('\0') !== names(rightEntries).join('\0')) return false;
|
|
96
|
-
for (const entry of leftEntries) {
|
|
97
|
-
const a = join(left, entry.name);
|
|
98
|
-
const b = join(right, entry.name);
|
|
99
|
-
if (entry.isDirectory()) {
|
|
100
|
-
if (!sameTree(a, b)) return false;
|
|
101
|
-
continue;
|
|
102
|
-
}
|
|
103
|
-
if (!sameFile(a, b)) return false;
|
|
104
|
-
}
|
|
105
|
-
return true;
|
|
106
|
-
}
|
|
107
|
-
|
|
108
|
-
// A seed copied from a CRLF checkout and a package shipped with LF are the
|
|
109
|
-
// same text; only carriage returns are ignored, nothing else.
|
|
110
|
-
function sameBytes(left, right) {
|
|
111
|
-
const strip = (buffer) => Buffer.from(buffer.toString('utf8').replace(/\r\n/g, '\n'), 'utf8');
|
|
112
|
-
return strip(left).equals(strip(right));
|
|
113
|
-
}
|
|
114
|
-
|
|
115
122
|
// Shipped workflows (<root>/workflows/<id>/WORKFLOW.md), agents
|
|
116
123
|
// (<root>/agents/<id>/AGENT.md + agent.json) and output styles
|
|
117
|
-
// (<root>/output-styles/<name>.md)
|
|
118
|
-
// the same id shadows them. The editors save a full copy, so saving a built-in
|
|
119
|
-
// pack without changing anything pins the text that shipped that day and no
|
|
120
|
-
// later app update ever reaches that user. Such a copy holds nothing of the
|
|
121
|
-
// user's: drop it so the current built-in applies again.
|
|
124
|
+
// (<root>/output-styles/<name>.md).
|
|
122
125
|
//
|
|
123
|
-
// "Unchanged"
|
|
124
|
-
//
|
|
125
|
-
//
|
|
126
|
-
//
|
|
127
|
-
//
|
|
126
|
+
// "Unchanged" for a pack means every loader-visible field matches: name,
|
|
127
|
+
// description, body and all other frontmatter/manifest fields (permission,
|
|
128
|
+
// hidden, model/tool fields, entry...). Only normalisation (CRLF, trailing
|
|
129
|
+
// space, quoting, an id equal to the folder name, the default entry) is
|
|
130
|
+
// ignored. A copy holding any file beyond the editor's own (added references,
|
|
131
|
+
// a `.deleted` tombstone) is the user's and is never touched; stray backups
|
|
132
|
+
// such as AGENT.md.bak do not count and travel to the backup with the copy.
|
|
128
133
|
const PACK_KINDS = [
|
|
129
|
-
{ dir: 'workflows', entry: 'WORKFLOW.md', files: ['WORKFLOW.md'] },
|
|
130
|
-
{ dir: 'agents', entry: 'AGENT.md', files: ['AGENT.md', 'agent.json'] },
|
|
134
|
+
{ dir: 'workflows', historyKey: 'workflows', entry: 'WORKFLOW.md', files: ['WORKFLOW.md'] },
|
|
135
|
+
{ dir: 'agents', historyKey: 'agents', entry: 'AGENT.md', files: ['AGENT.md', 'agent.json'] },
|
|
131
136
|
];
|
|
132
137
|
|
|
133
|
-
export function retireSeededPackCopies({ rootDir, dataDir }) {
|
|
138
|
+
export function retireSeededPackCopies({ rootDir, dataDir, backup = makeBackup(dataDir), includeHistory = true }) {
|
|
139
|
+
const history = includeHistory ? loadShippedHistory(rootDir) : EMPTY_HISTORY;
|
|
134
140
|
const retired = [];
|
|
135
141
|
for (const kind of PACK_KINDS) {
|
|
136
142
|
for (const name of childNames(join(dataDir, kind.dir), 'dir')) {
|
|
137
143
|
const bundled = join(rootDir, kind.dir, name);
|
|
138
144
|
const copy = join(dataDir, kind.dir, name);
|
|
139
|
-
|
|
140
|
-
if (!
|
|
141
|
-
if (!
|
|
142
|
-
|
|
145
|
+
const current = packHashFromDir(bundled, kind.entry, name);
|
|
146
|
+
if (!current) continue;
|
|
147
|
+
if (!holdsOnlyEditorFiles(copy, kind.files, kind.entry)) continue;
|
|
148
|
+
const copyHash = packHashFromDir(copy, kind.entry, name);
|
|
149
|
+
if (![current, ...(history[kind.historyKey][name] || [])].includes(copyHash)) continue;
|
|
150
|
+
if (backup.move(copy)) retired.push(`${kind.dir}/${name}`);
|
|
143
151
|
}
|
|
144
152
|
}
|
|
145
153
|
for (const name of childNames(join(dataDir, 'output-styles'), 'md')) {
|
|
146
|
-
const bundled = join(rootDir, 'output-styles', name);
|
|
154
|
+
const bundled = fileHash(join(rootDir, 'output-styles', name));
|
|
155
|
+
if (!bundled) continue;
|
|
147
156
|
const copy = join(dataDir, 'output-styles', name);
|
|
148
|
-
if (!
|
|
149
|
-
if (
|
|
150
|
-
if (removeEntry(copy)) retired.push(`output-styles/${name}`);
|
|
157
|
+
if (![bundled, ...(history.outputStyles[name] || [])].includes(fileHash(copy))) continue;
|
|
158
|
+
if (backup.move(copy)) retired.push(`output-styles/${name}`);
|
|
151
159
|
}
|
|
152
160
|
return retired;
|
|
153
161
|
}
|
|
@@ -163,80 +171,3 @@ function childNames(root, kind) {
|
|
|
163
171
|
kind === 'dir' ? entry.isDirectory() : entry.isFile() && entry.name.toLowerCase().endsWith('.md');
|
|
164
172
|
return entries.filter(wanted).map((entry) => entry.name);
|
|
165
173
|
}
|
|
166
|
-
|
|
167
|
-
// The copy may hold only the files an editor save writes; anything else in
|
|
168
|
-
// there was put there by the user and outranks the shipped pack.
|
|
169
|
-
function holdsOnly(dir, allowed) {
|
|
170
|
-
let entries;
|
|
171
|
-
try {
|
|
172
|
-
entries = readdirSync(dir, { withFileTypes: true });
|
|
173
|
-
} catch {
|
|
174
|
-
return false;
|
|
175
|
-
}
|
|
176
|
-
return entries.length > 0 && entries.every((entry) => entry.isFile() && allowed.includes(entry.name));
|
|
177
|
-
}
|
|
178
|
-
|
|
179
|
-
function samePackDefinition(bundledDir, copyDir, entry) {
|
|
180
|
-
const bundled = packDefinition(bundledDir, entry);
|
|
181
|
-
const copy = packDefinition(copyDir, entry);
|
|
182
|
-
if (!bundled || !copy) return false;
|
|
183
|
-
return bundled.name === copy.name && bundled.description === copy.description && bundled.body === copy.body;
|
|
184
|
-
}
|
|
185
|
-
|
|
186
|
-
// Same resolution the pack loaders use: agent.json wins over the markdown
|
|
187
|
-
// frontmatter for name/description, and the body is the markdown minus it.
|
|
188
|
-
function packDefinition(dir, entry) {
|
|
189
|
-
const manifest = readJsonFile(join(dir, 'agent.json'));
|
|
190
|
-
const raw = readTextFile(join(dir, oneLine(manifest.entry) || entry));
|
|
191
|
-
const doc = readMarkdownDocument(raw);
|
|
192
|
-
const body = String(doc.body || '')
|
|
193
|
-
.replace(/\r\n/g, '\n')
|
|
194
|
-
.trim();
|
|
195
|
-
if (!body) return null;
|
|
196
|
-
return {
|
|
197
|
-
name: oneLine(manifest.name || doc.frontmatter.name),
|
|
198
|
-
description: oneLine(manifest.description || doc.frontmatter.description),
|
|
199
|
-
body,
|
|
200
|
-
};
|
|
201
|
-
}
|
|
202
|
-
|
|
203
|
-
function readJsonFile(file) {
|
|
204
|
-
try {
|
|
205
|
-
const parsed = JSON.parse(readFileSync(file, 'utf8'));
|
|
206
|
-
return parsed && typeof parsed === 'object' ? parsed : {};
|
|
207
|
-
} catch {
|
|
208
|
-
return {};
|
|
209
|
-
}
|
|
210
|
-
}
|
|
211
|
-
|
|
212
|
-
function readTextFile(file) {
|
|
213
|
-
try {
|
|
214
|
-
return readFileSync(file, 'utf8');
|
|
215
|
-
} catch {
|
|
216
|
-
return '';
|
|
217
|
-
}
|
|
218
|
-
}
|
|
219
|
-
|
|
220
|
-
function oneLine(value) {
|
|
221
|
-
return String(value || '')
|
|
222
|
-
.replace(/\s+/g, ' ')
|
|
223
|
-
.trim();
|
|
224
|
-
}
|
|
225
|
-
|
|
226
|
-
function sameFile(left, right) {
|
|
227
|
-
try {
|
|
228
|
-
return sameBytes(readFileSync(left), readFileSync(right));
|
|
229
|
-
} catch {
|
|
230
|
-
return false;
|
|
231
|
-
}
|
|
232
|
-
}
|
|
233
|
-
|
|
234
|
-
function removeEntry(target) {
|
|
235
|
-
try {
|
|
236
|
-
rmSync(target, { recursive: true, force: true });
|
|
237
|
-
return true;
|
|
238
|
-
} catch {
|
|
239
|
-
// best-effort; a stuck copy simply keeps shadowing until removed by hand
|
|
240
|
-
return false;
|
|
241
|
-
}
|
|
242
|
-
}
|
|
@@ -445,9 +445,38 @@ function usageTotal(rows) {
|
|
|
445
445
|
return total;
|
|
446
446
|
}
|
|
447
447
|
|
|
448
|
+
// Credit feature of a subscription provider; null unless the account has it
|
|
449
|
+
// enabled. Codex counts credits, Claude's extra usage is USD.
|
|
450
|
+
export function creditsFromSnapshot(providerId, snapshot) {
|
|
451
|
+
const balance = snapshot?.balance;
|
|
452
|
+
if (providerId === 'openai-oauth') {
|
|
453
|
+
if (balance?.unit !== 'credits') return null;
|
|
454
|
+
const amount = num(balance.remainingCredits, null);
|
|
455
|
+
const unlimited = balance.unlimited === true;
|
|
456
|
+
if (!(balance.hasCredits === true || unlimited || (amount !== null && amount > 0))) return null;
|
|
457
|
+
return { unit: 'credits', balance: amount, ...(unlimited ? { unlimited: true } : {}) };
|
|
458
|
+
}
|
|
459
|
+
if (providerId === 'anthropic-oauth') {
|
|
460
|
+
const extra = (snapshot?.quotaWindows || []).find((w) => w?.label === 'EXTRA');
|
|
461
|
+
const spend = balance && num(balance.remainingUsd, null) !== null ? balance : null;
|
|
462
|
+
if (!extra && !spend) return null;
|
|
463
|
+
const remaining = num(spend?.remainingUsd ?? extra?.remainingUsd, null);
|
|
464
|
+
const monthlyLimit = num(spend?.budgetUsd ?? extra?.limitUsd, null);
|
|
465
|
+
const spent = num(spend?.spentUsd ?? extra?.usedUsd, null);
|
|
466
|
+
return {
|
|
467
|
+
unit: 'usd',
|
|
468
|
+
balance: remaining ?? (monthlyLimit !== null && spent !== null ? Math.max(0, monthlyLimit - spent) : null),
|
|
469
|
+
...(monthlyLimit !== null ? { monthlyLimit } : {}),
|
|
470
|
+
...(spent !== null ? { spent } : {}),
|
|
471
|
+
};
|
|
472
|
+
}
|
|
473
|
+
return null;
|
|
474
|
+
}
|
|
475
|
+
|
|
448
476
|
function snapshotRow(row) {
|
|
449
477
|
return {
|
|
450
478
|
...row,
|
|
479
|
+
...(row.credits ? { credits: { ...row.credits } } : {}),
|
|
451
480
|
windows: row.windows.map((window) => ({ ...window })),
|
|
452
481
|
...(row.resetCredits
|
|
453
482
|
? {
|
|
@@ -12,7 +12,9 @@ import {
|
|
|
12
12
|
readCachedApiUsageSnapshot,
|
|
13
13
|
} from '../../runtime/agent/orchestrator/providers/api-usage.mjs';
|
|
14
14
|
import { num } from '../../runtime/agent/orchestrator/providers/lib/usage-primitives.mjs';
|
|
15
|
+
import { readProviderAccountPool } from '../../runtime/shared/provider-accounts.mjs';
|
|
15
16
|
import {
|
|
17
|
+
creditsFromSnapshot,
|
|
16
18
|
applyApiUnavailable,
|
|
17
19
|
applyKnownRemaining,
|
|
18
20
|
applyKnownUsage,
|
|
@@ -31,6 +33,8 @@ import {
|
|
|
31
33
|
usageDashboardSnapshot,
|
|
32
34
|
} from './usage-dashboard-model.mjs';
|
|
33
35
|
|
|
36
|
+
const CREDIT_PROVIDERS = new Set(['openai-oauth', 'anthropic-oauth']);
|
|
37
|
+
|
|
34
38
|
async function oauthSnapshot(providerId, { refresh, getProvider, log }) {
|
|
35
39
|
const cached = readCachedOAuthUsageSnapshot({ provider: providerId, model: '' });
|
|
36
40
|
if (!refresh && cached) return cached;
|
|
@@ -206,6 +210,7 @@ async function oauthProviderRow(item, { providers, preview, snapshotOptions, row
|
|
|
206
210
|
row.primary = row.authenticated ? '' : 'not signed in';
|
|
207
211
|
row.detail = row.authenticated ? 'Checking provider usage' : item.detail || 'OAuth credentials missing';
|
|
208
212
|
row.tone = rowTone(row);
|
|
213
|
+
if (CREDIT_PROVIDERS.has(item.id)) row.useCredits = readProviderAccountPool(item.id).useCredits === true;
|
|
209
214
|
rows.push(row);
|
|
210
215
|
emit(true);
|
|
211
216
|
|
|
@@ -222,6 +227,8 @@ async function oauthProviderRow(item, { providers, preview, snapshotOptions, row
|
|
|
222
227
|
const resetCredits = normaliseResetCredits(snapshot?.resetCredits);
|
|
223
228
|
row.windows = windows;
|
|
224
229
|
if (resetCredits) row.resetCredits = resetCredits;
|
|
230
|
+
const credits = creditsFromSnapshot(item.id, snapshot);
|
|
231
|
+
if (credits) row.credits = credits;
|
|
225
232
|
row.updatedAt = num(snapshot?.cachedAt, null);
|
|
226
233
|
if (known) {
|
|
227
234
|
applyKnownRemaining(row, known, { estimated: false });
|
|
@@ -0,0 +1,125 @@
|
|
|
1
|
+
// session-address.mjs — the durable address of an addressed runtime.
|
|
2
|
+
//
|
|
3
|
+
// A daemon-addressed runtime (reserved or loaded by the session service) owns
|
|
4
|
+
// exactly ONE conversation id for its whole life. `reservedSessionId` is the
|
|
5
|
+
// one-shot "materialize me" token and is cleared on first adoption; the
|
|
6
|
+
// address outlives it. The address is bound at reservation and at
|
|
7
|
+
// resume/load-on-demand, and thereafter:
|
|
8
|
+
// - "no live session object" never permits creating another conversation;
|
|
9
|
+
// - an OPEN durable record under the address is resumed (same id);
|
|
10
|
+
// - a CLOSED record makes the runtime terminal (never recreated/overwritten);
|
|
11
|
+
// - a missing/unreadable record is an explicit failure (never minted).
|
|
12
|
+
// Admission (open → closing → terminal) is the canonical-close fence.
|
|
13
|
+
import { SessionClosedError } from '../runtime/agent/orchestrator/session/manager/session-errors.mjs';
|
|
14
|
+
|
|
15
|
+
export class SessionAddressError extends Error {
|
|
16
|
+
constructor(message, sessionId = null) {
|
|
17
|
+
super(message);
|
|
18
|
+
this.name = 'SessionAddressError';
|
|
19
|
+
this.sessionId = sessionId;
|
|
20
|
+
}
|
|
21
|
+
}
|
|
22
|
+
|
|
23
|
+
export function initSessionAddress(rt) {
|
|
24
|
+
rt.addressedSessionId = null;
|
|
25
|
+
rt.admission = 'open';
|
|
26
|
+
rt.lifecycleGeneration = 0;
|
|
27
|
+
}
|
|
28
|
+
|
|
29
|
+
/** Bind (or re-confirm) the durable address. A second, different id is a
|
|
30
|
+
* re-key attempt and is refused. */
|
|
31
|
+
export function bindSessionAddress(rt, id) {
|
|
32
|
+
const next = String(id || '').trim();
|
|
33
|
+
if (!next) throw new SessionAddressError('session address is required');
|
|
34
|
+
if (rt.addressedSessionId && rt.addressedSessionId !== next) {
|
|
35
|
+
throw new SessionAddressError(
|
|
36
|
+
`runtime is addressed as ${rt.addressedSessionId}; it cannot be re-keyed to ${next} (use a new session entry)`,
|
|
37
|
+
rt.addressedSessionId
|
|
38
|
+
);
|
|
39
|
+
}
|
|
40
|
+
rt.addressedSessionId = next;
|
|
41
|
+
return next;
|
|
42
|
+
}
|
|
43
|
+
|
|
44
|
+
export const isAddressedTo = (rt, id) => !!rt.addressedSessionId && rt.addressedSessionId === String(id || '');
|
|
45
|
+
|
|
46
|
+
/** Refuse an explicit transition that would move an addressed runtime to
|
|
47
|
+
* another conversation. */
|
|
48
|
+
export function assertNotAddressed(rt, operation) {
|
|
49
|
+
if (rt.addressedSessionId) {
|
|
50
|
+
throw new SessionAddressError(
|
|
51
|
+
`${operation} would re-key addressed session ${rt.addressedSessionId}; create a new session entry instead`,
|
|
52
|
+
rt.addressedSessionId
|
|
53
|
+
);
|
|
54
|
+
}
|
|
55
|
+
}
|
|
56
|
+
|
|
57
|
+
export const isAdmissionOpen = (rt) => !rt.admission || rt.admission === 'open';
|
|
58
|
+
|
|
59
|
+
export function assertAdmissionOpen(rt) {
|
|
60
|
+
if (isAdmissionOpen(rt)) return;
|
|
61
|
+
throw new SessionClosedError(rt.addressedSessionId || 'unknown', 'session is closed to new work', 'canonical-close');
|
|
62
|
+
}
|
|
63
|
+
|
|
64
|
+
/** Resume may only reopen the conversation this runtime is addressed as. */
|
|
65
|
+
export function assertResumeTarget(rt, id) {
|
|
66
|
+
assertAdmissionOpen(rt);
|
|
67
|
+
if (rt.addressedSessionId && rt.addressedSessionId !== String(id || '')) {
|
|
68
|
+
throw new SessionAddressError(
|
|
69
|
+
`runtime is addressed as ${rt.addressedSessionId}; it cannot resume ${id} (use a new session entry)`,
|
|
70
|
+
rt.addressedSessionId
|
|
71
|
+
);
|
|
72
|
+
}
|
|
73
|
+
}
|
|
74
|
+
|
|
75
|
+
/** Fence admission before the manager close can cancel owned shells. */
|
|
76
|
+
export function beginCanonicalClose(rt, sessionId) {
|
|
77
|
+
const previous = rt.admission || 'open';
|
|
78
|
+
if (!rt.addressedSessionId) rt.addressedSessionId = String(sessionId);
|
|
79
|
+
rt.admission = 'closing';
|
|
80
|
+
return previous;
|
|
81
|
+
}
|
|
82
|
+
|
|
83
|
+
/** Settle the fence: terminal after a durable close, restored otherwise. */
|
|
84
|
+
export function finishCanonicalClose(rt, previous, closed) {
|
|
85
|
+
if (closed) markTerminal(rt);
|
|
86
|
+
else rt.admission = previous;
|
|
87
|
+
}
|
|
88
|
+
|
|
89
|
+
export function markTerminal(rt) {
|
|
90
|
+
if (rt.admission !== 'terminal') {
|
|
91
|
+
rt.admission = 'terminal';
|
|
92
|
+
rt.lifecycleGeneration = (rt.lifecycleGeneration || 0) + 1;
|
|
93
|
+
}
|
|
94
|
+
}
|
|
95
|
+
|
|
96
|
+
/** Lifecycle view used by the notification bus; null = not an owner here. */
|
|
97
|
+
export function ownerLifecycle(rt, sessionId) {
|
|
98
|
+
if (!isAddressedTo(rt, sessionId)) return null;
|
|
99
|
+
return { state: rt.admission || 'open', generation: rt.lifecycleGeneration || 0 };
|
|
100
|
+
}
|
|
101
|
+
|
|
102
|
+
/** The durable record under the address: open | closed | missing. */
|
|
103
|
+
export function addressedRecord(rt, mgr) {
|
|
104
|
+
let record = null;
|
|
105
|
+
try {
|
|
106
|
+
record = mgr.getSession(rt.addressedSessionId);
|
|
107
|
+
} catch {
|
|
108
|
+
record = null;
|
|
109
|
+
}
|
|
110
|
+
if (!record) return { state: 'missing', record: null };
|
|
111
|
+
if (record.closed === true || record.status === 'closed') return { state: 'closed', record };
|
|
112
|
+
return { state: 'open', record };
|
|
113
|
+
}
|
|
114
|
+
|
|
115
|
+
export function closedAddressError(rt) {
|
|
116
|
+
markTerminal(rt);
|
|
117
|
+
return new SessionClosedError(rt.addressedSessionId, 'durable record is closed', 'canonical-close');
|
|
118
|
+
}
|
|
119
|
+
|
|
120
|
+
export function missingAddressError(rt) {
|
|
121
|
+
return new SessionAddressError(
|
|
122
|
+
`session ${rt.addressedSessionId} record is missing or unreadable; refusing to create another conversation`,
|
|
123
|
+
rt.addressedSessionId
|
|
124
|
+
);
|
|
125
|
+
}
|
|
@@ -8,11 +8,22 @@ import { runAbortable, throwIfAborted } from '../runtime/shared/abort-race.mjs';
|
|
|
8
8
|
import { elapsedMs, prepareRoute } from './session-create/route-prep.mjs';
|
|
9
9
|
import { materializeSession, prewarmWsTransport } from './session-create/materialize.mjs';
|
|
10
10
|
import { dispatchSessionStart } from './session-create/session-start-hook.mjs';
|
|
11
|
+
import { SessionClosedError } from '../runtime/agent/orchestrator/session/manager/session-errors.mjs';
|
|
12
|
+
import {
|
|
13
|
+
SessionAddressError,
|
|
14
|
+
addressedRecord,
|
|
15
|
+
assertAdmissionOpen,
|
|
16
|
+
closedAddressError,
|
|
17
|
+
isAddressedTo,
|
|
18
|
+
missingAddressError,
|
|
19
|
+
} from './session-address.mjs';
|
|
11
20
|
|
|
12
21
|
export function createSessionCreator(deps, routes) {
|
|
13
22
|
const { rt, mgr } = deps;
|
|
14
23
|
|
|
15
|
-
/** A still-open live session short-circuits creation.
|
|
24
|
+
/** A still-open live session short-circuits creation. For an addressed
|
|
25
|
+
* runtime a closed/missing record is terminal/explicit failure — never
|
|
26
|
+
* "no session, so create one". */
|
|
16
27
|
function liveSession() {
|
|
17
28
|
if (!rt.session?.id) return null;
|
|
18
29
|
const live = mgr.getSession(rt.session.id);
|
|
@@ -20,14 +31,56 @@ export function createSessionCreator(deps, routes) {
|
|
|
20
31
|
rt.session = live;
|
|
21
32
|
return live;
|
|
22
33
|
}
|
|
34
|
+
if (isAddressedTo(rt, rt.session.id)) {
|
|
35
|
+
if (live) throw closedAddressError(rt);
|
|
36
|
+
throw missingAddressError(rt);
|
|
37
|
+
}
|
|
23
38
|
rt.session = null;
|
|
24
39
|
return null;
|
|
25
40
|
}
|
|
26
41
|
|
|
42
|
+
/** Same-id recovery of an OPEN durable conversation (idle eviction, manager
|
|
43
|
+
* unload, load-on-demand, crash restore) through the lifecycle resume. */
|
|
44
|
+
async function resumeAddressed(signal) {
|
|
45
|
+
if (typeof deps.resumeAddressedSession !== 'function') {
|
|
46
|
+
throw new SessionAddressError('addressed session resume is unavailable', rt.addressedSessionId);
|
|
47
|
+
}
|
|
48
|
+
const id = rt.addressedSessionId;
|
|
49
|
+
await runAbortable(signal, () => deps.resumeAddressedSession(id), 'Session creation aborted');
|
|
50
|
+
assertAdmissionOpen(rt);
|
|
51
|
+
if (rt.session?.id === id) return rt.session;
|
|
52
|
+
const { state } = addressedRecord(rt, mgr);
|
|
53
|
+
if (state === 'closed') throw closedAddressError(rt);
|
|
54
|
+
throw new SessionAddressError(`session ${id} could not be resumed; refusing to create another`, id);
|
|
55
|
+
}
|
|
56
|
+
|
|
57
|
+
/** Decide, from the durable record under the address, whether this runtime
|
|
58
|
+
* may materialize (first time, reserved) or must resume. Synchronous so no
|
|
59
|
+
* await sits between the decision and the adoption. */
|
|
60
|
+
function addressDecision() {
|
|
61
|
+
if (!rt.addressedSessionId) return 'create';
|
|
62
|
+
const { state } = addressedRecord(rt, mgr);
|
|
63
|
+
if (state === 'closed') throw closedAddressError(rt);
|
|
64
|
+
if (state === 'open') return 'resume';
|
|
65
|
+
if (rt.reservedSessionId === rt.addressedSessionId) return 'create';
|
|
66
|
+
throw missingAddressError(rt);
|
|
67
|
+
}
|
|
68
|
+
|
|
27
69
|
async function buildSession(reason, signal, startedAt) {
|
|
28
70
|
const { coreMemoryContext, providerImpl } = await prepareRoute(deps, routes, signal, startedAt);
|
|
71
|
+
// The fence/record may have changed during the awaits above (close during
|
|
72
|
+
// reservation prewarm): re-validate synchronously right before adoption.
|
|
73
|
+
assertAdmissionOpen(rt);
|
|
74
|
+
if (addressDecision() === 'resume') return await resumeAddressed(signal);
|
|
29
75
|
materializeSession(deps, reason, coreMemoryContext);
|
|
30
76
|
await dispatchSessionStart(deps, reason, signal);
|
|
77
|
+
assertAdmissionOpen(rt);
|
|
78
|
+
if (!rt.session)
|
|
79
|
+
throw new SessionClosedError(
|
|
80
|
+
rt.addressedSessionId || 'unknown',
|
|
81
|
+
'session closed during creation',
|
|
82
|
+
'canonical-close'
|
|
83
|
+
);
|
|
31
84
|
prewarmWsTransport(rt, providerImpl);
|
|
32
85
|
throwIfAborted(signal);
|
|
33
86
|
bootProfile('session:create:ready', {
|
|
@@ -42,11 +95,13 @@ export function createSessionCreator(deps, routes) {
|
|
|
42
95
|
return async function createCurrentSession(reason = 'demand', options = {}) {
|
|
43
96
|
const signal = options?.signal || null;
|
|
44
97
|
throwIfAborted(signal);
|
|
98
|
+
assertAdmissionOpen(rt);
|
|
45
99
|
if (rt.sessionCreatePromise) {
|
|
46
100
|
return await runAbortable(signal, () => rt.sessionCreatePromise, 'Session creation aborted');
|
|
47
101
|
}
|
|
48
102
|
const live = liveSession();
|
|
49
103
|
if (live) return live;
|
|
104
|
+
const decision = addressDecision();
|
|
50
105
|
|
|
51
106
|
const startedAt = performance.now();
|
|
52
107
|
bootProfile('session:create:start', { mode: rt.mode, reason });
|
|
@@ -58,7 +113,7 @@ export function createSessionCreator(deps, routes) {
|
|
|
58
113
|
bootProfile('agent-loop:prewarm-failed', { error: error?.message || String(error) });
|
|
59
114
|
});
|
|
60
115
|
}
|
|
61
|
-
const promise = buildSession(reason, signal, startedAt);
|
|
116
|
+
const promise = decision === 'resume' ? resumeAddressed(signal) : buildSession(reason, signal, startedAt);
|
|
62
117
|
rt.sessionCreatePromise = promise;
|
|
63
118
|
try {
|
|
64
119
|
return await promise;
|
|
@@ -9,7 +9,9 @@ import {
|
|
|
9
9
|
} from '../runtime/agent/orchestrator/runtime-core/builtin-features.mjs';
|
|
10
10
|
import { LOCAL_PROVIDER_ID } from '../runtime/local-provider/managed-runtime.mjs';
|
|
11
11
|
import { getEmbeddingInfo } from '../runtime/memory/lib/embedding-provider.mjs';
|
|
12
|
+
import { onnxRuntimeSupported } from '../runtime/shared/onnx-runtime-support.mjs';
|
|
12
13
|
import {
|
|
14
|
+
EFFORT_JUDGE_UNSUPPORTED,
|
|
13
15
|
effortJudgeAvailable,
|
|
14
16
|
effortJudgeInfo,
|
|
15
17
|
effortJudgeInstalling,
|
|
@@ -82,6 +84,7 @@ export function createBuiltinToolSettings(
|
|
|
82
84
|
enabled: memoryToolsEnabledFn(),
|
|
83
85
|
installed: builtinInstalled(config, 'memory'),
|
|
84
86
|
info: getEmbeddingInfo(),
|
|
87
|
+
semanticSearchSupported: onnxRuntimeSupported(),
|
|
85
88
|
},
|
|
86
89
|
git: { enabled: gitToolsEnabledFn(), installed: builtinInstalled(config, 'git') },
|
|
87
90
|
office: { enabled: officeToolsEnabledFn(), installed: builtinInstalled(config, 'office') },
|
|
@@ -96,6 +99,7 @@ export function createBuiltinToolSettings(
|
|
|
96
99
|
installed: builtinInstalled(config, 'localProvider') && localProviderRuntimeInstalled,
|
|
97
100
|
},
|
|
98
101
|
autoEffort: {
|
|
102
|
+
supported: onnxRuntimeSupported(),
|
|
99
103
|
enabled: builtinFeatureActive(config, 'autoEffort'),
|
|
100
104
|
// On by default: "installed" means the judge model is on disk;
|
|
101
105
|
// "installing" covers the boot download the settings did not start.
|
|
@@ -135,6 +139,9 @@ export function createBuiltinToolSettings(
|
|
|
135
139
|
if (!TOGGLEABLE_BUILTINS.includes(name)) {
|
|
136
140
|
throw new TypeError('Built-in tool must be git, office, tidy, localProvider, or autoEffort.');
|
|
137
141
|
}
|
|
142
|
+
if (name === 'autoEffort' && enabled !== false && !onnxRuntimeSupported()) {
|
|
143
|
+
throw new Error(EFFORT_JUDGE_UNSUPPORTED);
|
|
144
|
+
}
|
|
138
145
|
if (name === 'localProvider' && enabled !== false && getLocalProviderStatus?.()?.runtime?.installed !== true) {
|
|
139
146
|
await prepareBuiltinFeature?.(name);
|
|
140
147
|
}
|
|
@@ -165,6 +172,7 @@ export function createBuiltinToolSettings(
|
|
|
165
172
|
if (!INSTALLABLE_BUILTIN_IDS.includes(name)) {
|
|
166
173
|
throw new TypeError('Built-in feature must be git, memory, office, tidy, localProvider, or autoEffort.');
|
|
167
174
|
}
|
|
175
|
+
if (name === 'autoEffort' && !onnxRuntimeSupported()) throw new Error(EFFORT_JUDGE_UNSUPPORTED);
|
|
168
176
|
await prepareBuiltinFeature?.(name);
|
|
169
177
|
const config = getConfig();
|
|
170
178
|
let nextConfig = setBuiltinInstalledInConfig({ ...config }, name, true);
|
|
@@ -1,4 +1,8 @@
|
|
|
1
1
|
// Context lifecycle settings: compaction budgets and the idle auto-clear.
|
|
2
|
+
import {
|
|
3
|
+
refreshLiveSessionsProviderCacheOpts,
|
|
4
|
+
refreshSessionProviderCacheOpts,
|
|
5
|
+
} from '../runtime/agent/orchestrator/session/manager/session-lifecycle.mjs';
|
|
2
6
|
|
|
3
7
|
// Every spelling of the main-context buffer budget; a new representation
|
|
4
8
|
// replaces the previous one.
|
|
@@ -46,6 +50,13 @@ export function createCompactionSettings({
|
|
|
46
50
|
formatDurationMs,
|
|
47
51
|
invalidateContextStatusCache,
|
|
48
52
|
}) {
|
|
53
|
+
// The Lead messages-tail TTL derives from the auto-clear idle window.
|
|
54
|
+
const refreshLeadCacheOpts = () => {
|
|
55
|
+
const config = getConfig();
|
|
56
|
+
refreshSessionProviderCacheOpts(getSession(), config);
|
|
57
|
+
refreshLiveSessionsProviderCacheOpts(config);
|
|
58
|
+
};
|
|
59
|
+
|
|
49
60
|
/** Apply a per-provider idle override (or its reset) and return the resolved view. */
|
|
50
61
|
const saveProviderIdle = (config, next, providerKey, input) => {
|
|
51
62
|
const providerIdleMs = { ...(next.providerIdleMs || {}) };
|
|
@@ -57,6 +68,7 @@ export function createCompactionSettings({
|
|
|
57
68
|
providerIdleMs[providerKey] = Math.max(60_000, Math.round(idleMs));
|
|
58
69
|
}
|
|
59
70
|
saveConfigAndAdopt({ ...config, autoClear: { ...next, providerIdleMs } });
|
|
71
|
+
refreshLeadCacheOpts();
|
|
60
72
|
};
|
|
61
73
|
|
|
62
74
|
return {
|
|
@@ -157,6 +169,7 @@ export function createCompactionSettings({
|
|
|
157
169
|
if (!hasOwn(input, 'enabled')) next.enabled = true;
|
|
158
170
|
}
|
|
159
171
|
saveConfigAndAdopt({ ...config, autoClear: next });
|
|
172
|
+
refreshLeadCacheOpts();
|
|
160
173
|
}
|
|
161
174
|
const resolved = this.getAutoClear();
|
|
162
175
|
return { ...resolved, label: formatDurationMs(resolved.idleMs) };
|