@nowcrew/daemon 0.6.87-beta.2 → 0.6.87-beta.4
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/dist/cloud-report-uploader.js +27 -25
- package/dist/execution-runner.js +4 -2
- package/dist/external-output.js +154 -68
- package/dist/local-executor.js +37 -5
- package/dist/local-memory/pending.js +136 -2
- package/dist/local-memory/runtime.js +8 -1
- package/dist/local-memory/write.js +126 -4
- package/dist/main.js +6 -2
- package/dist/project-skills/capability.js +109 -0
- package/dist/project-skills/controller-convergence.js +57 -0
- package/dist/project-skills/projection-set-switch.js +419 -0
- package/dist/project-skills/projection-state-domain.js +170 -0
- package/dist/project-skills/projection-state-store.js +842 -0
- package/dist/project-skills/projection-state-transaction.js +322 -0
- package/dist/project-skills/projection-state.js +3 -0
- package/dist/project-skills/runtime-root-bootstrap.js +47 -0
- package/dist/project-skills/runtime-root-domain.js +268 -0
- package/dist/project-skills/runtime-root-gc.js +293 -0
- package/dist/project-skills/runtime-root-lease-artifact.js +46 -0
- package/dist/project-skills/runtime-root-leases.js +487 -0
- package/dist/project-skills/runtime-root-source-identity.js +60 -0
- package/dist/project-skills/runtime-root-startup.js +49 -0
- package/dist/project-skills/runtime-root-state-artifact-domain.js +143 -0
- package/dist/project-skills/runtime-root-state-index.js +356 -0
- package/dist/project-skills/runtime-root-store.js +722 -0
- package/dist/project-skills/serve-capability.js +28 -0
- package/dist/project-skills/serve-startup.js +37 -0
- package/dist/prompt.js +4 -1
- package/dist/runner.js +3 -1
- package/dist/serve.js +17 -11
- package/dist/storage-identity.js +4 -1
- package/dist/storage-ownership.js +93 -5
- package/dist/storage-runtime-controller.js +40 -20
- package/dist/storage-runtime.js +38 -2
- package/dist/storage-serve-adapter.js +22 -5
- package/package.json +1 -1
|
@@ -4,7 +4,7 @@ import { dirname, join, relative, resolve, sep } from "node:path";
|
|
|
4
4
|
import { z } from "zod";
|
|
5
5
|
import { containsLikelySecret } from "../agent-memory/policy.js";
|
|
6
6
|
import { LOCAL_MEMORY_INDEX_CODE_POINT_CAP, LOCAL_MEMORY_INDEX_ENTRY_CAP, LOCAL_MEMORY_TOTAL_CODE_POINT_CAP, renderLocalMemoryIndexCard, selectLocalMemoryIndexEntries, } from "./index-budget.js";
|
|
7
|
-
import { persistPendingMemoryCandidate } from "./pending.js";
|
|
7
|
+
import { persistPendingMemoryCandidate, readPendingMemoryCandidates, removePendingMemoryCandidate, } from "./pending.js";
|
|
8
8
|
const CategorySchema = z.enum([
|
|
9
9
|
"preferences", "project-rules", "testing", "debugging", "git", "incidents", "lessons",
|
|
10
10
|
]);
|
|
@@ -228,6 +228,47 @@ function normalize(value) {
|
|
|
228
228
|
function activeOrPinnedIds(entries) {
|
|
229
229
|
return new Set(entries.filter((entry) => entry.status === "active" || entry.status === "pinned").map((entry) => entry.id));
|
|
230
230
|
}
|
|
231
|
+
function pinnedIds(entries) {
|
|
232
|
+
return new Set(entries.filter((entry) => entry.status === "pinned").map((entry) => entry.id));
|
|
233
|
+
}
|
|
234
|
+
const DETAIL_CATEGORIES = new Set([
|
|
235
|
+
"preferences", "project-rules", "testing", "debugging", "git", "incidents", "lessons",
|
|
236
|
+
]);
|
|
237
|
+
function detailCategoryFromFile(file) {
|
|
238
|
+
if (!file.startsWith("notes/") || !file.endsWith(".md"))
|
|
239
|
+
return null;
|
|
240
|
+
const category = file.slice("notes/".length, -".md".length);
|
|
241
|
+
return DETAIL_CATEGORIES.has(category) ? category : null;
|
|
242
|
+
}
|
|
243
|
+
function readNoteBody(content, id) {
|
|
244
|
+
const begin = content.indexOf(`<!-- NOWWORK:MEMORY_ENTRY:BEGIN id=${id}`);
|
|
245
|
+
if (begin < 0)
|
|
246
|
+
return null;
|
|
247
|
+
const bodyStart = content.indexOf("\n\n", begin);
|
|
248
|
+
const bodyEnd = content.indexOf(`\n<!-- NOWWORK:MEMORY_ENTRY:END id=${id}`, bodyStart + 2);
|
|
249
|
+
if (bodyStart < 0 || bodyEnd < 0)
|
|
250
|
+
return null;
|
|
251
|
+
return content.slice(bodyStart + 2, bodyEnd).trim();
|
|
252
|
+
}
|
|
253
|
+
function archivedChange(entry, detailBody) {
|
|
254
|
+
const detailCategory = detailCategoryFromFile(entry.file);
|
|
255
|
+
if (detailCategory === null)
|
|
256
|
+
return null;
|
|
257
|
+
return {
|
|
258
|
+
operation: "create",
|
|
259
|
+
memoryId: null,
|
|
260
|
+
baseRevision: null,
|
|
261
|
+
supersedesMemoryId: null,
|
|
262
|
+
title: entry.title,
|
|
263
|
+
kind: entry.type,
|
|
264
|
+
summary: entry.summary,
|
|
265
|
+
triggers: entry.triggers,
|
|
266
|
+
priority: entry.priority,
|
|
267
|
+
detailCategory,
|
|
268
|
+
detailBody,
|
|
269
|
+
reviewAfter: entry.reviewAfter,
|
|
270
|
+
};
|
|
271
|
+
}
|
|
231
272
|
function emptyResult() {
|
|
232
273
|
return { createdIds: [], updatedIds: [], statusChangedIds: [], deferredIds: [], pendingIds: [] };
|
|
233
274
|
}
|
|
@@ -341,7 +382,9 @@ export async function applyLocalMemoryResult(input) {
|
|
|
341
382
|
const statusChangedIds = input.result.statusChanges.map((change) => change.memoryId);
|
|
342
383
|
const deferredIds = [];
|
|
343
384
|
const pendingCandidates = [];
|
|
385
|
+
const resolvedPendingIds = new Set();
|
|
344
386
|
const noteUpdates = new Map();
|
|
387
|
+
let allowActiveEviction = false;
|
|
345
388
|
const randomId = input.randomId ?? randomUUID;
|
|
346
389
|
const executionId = input.executionId ?? "unknown";
|
|
347
390
|
const defer = (change, index) => {
|
|
@@ -399,7 +442,10 @@ export async function applyLocalMemoryResult(input) {
|
|
|
399
442
|
: item),
|
|
400
443
|
entry,
|
|
401
444
|
];
|
|
402
|
-
const
|
|
445
|
+
const allowEvictionForChange = change.operation === "create" && change.priority === "high";
|
|
446
|
+
const protectedIds = new Set(allowEvictionForChange ? pinnedIds(entries) : activeOrPinnedIds(entries));
|
|
447
|
+
if (allowEvictionForChange)
|
|
448
|
+
allowActiveEviction = true;
|
|
403
449
|
if (change.supersedesMemoryId !== null)
|
|
404
450
|
protectedIds.delete(change.supersedesMemoryId);
|
|
405
451
|
const projection = projectIndex(memory, candidateEntries, protectedIds);
|
|
@@ -428,15 +474,91 @@ export async function applyLocalMemoryResult(input) {
|
|
|
428
474
|
else if (existingCreate === undefined)
|
|
429
475
|
createdIds.push(id);
|
|
430
476
|
}
|
|
431
|
-
|
|
432
|
-
|
|
477
|
+
// Pending create candidates are durable overflow, not dead ends. Promote only
|
|
478
|
+
// when the current catalog has room; supersede/update candidates stay pending
|
|
479
|
+
// until a fresh revision-aware decision can safely replay them.
|
|
480
|
+
const pendingCandidatesOnDisk = await readPendingMemoryCandidates({ agentDir: input.agentDir });
|
|
481
|
+
for (const pending of pendingCandidatesOnDisk) {
|
|
482
|
+
const change = pending.candidate;
|
|
483
|
+
if (change.operation !== "create")
|
|
484
|
+
continue;
|
|
485
|
+
const id = stableId(`pending:${pending.pendingId}`, 0, change);
|
|
486
|
+
const existing = entries.find((entry) => entry.id === id);
|
|
487
|
+
if (existing !== undefined) {
|
|
488
|
+
resolvedPendingIds.add(pending.pendingId);
|
|
489
|
+
continue;
|
|
490
|
+
}
|
|
491
|
+
const duplicate = entries.find((entry) => (entry.status === "active" || entry.status === "pinned")
|
|
492
|
+
&& (normalize(entry.title) === normalize(change.title)
|
|
493
|
+
|| normalize(entry.summary) === normalize(change.summary)));
|
|
494
|
+
if (duplicate !== undefined) {
|
|
495
|
+
const duplicatePath = resolve(input.agentDir, duplicate.file);
|
|
496
|
+
const duplicateBody = readNoteBody(noteUpdates.get(duplicatePath) ?? await readNote(duplicatePath), duplicate.id);
|
|
497
|
+
if (duplicateBody === change.detailBody)
|
|
498
|
+
resolvedPendingIds.add(pending.pendingId);
|
|
499
|
+
continue;
|
|
500
|
+
}
|
|
501
|
+
const entry = entryFromChange(change, id, 1, "active");
|
|
502
|
+
const candidateEntries = [...entries, entry];
|
|
503
|
+
const allowEvictionForPending = change.priority === "high";
|
|
504
|
+
const projection = projectIndex(memory, candidateEntries, allowEvictionForPending ? pinnedIds(entries) : activeOrPinnedIds(entries));
|
|
505
|
+
if (allowEvictionForPending)
|
|
506
|
+
allowActiveEviction = true;
|
|
507
|
+
if (projection.omittedIds.includes(id))
|
|
508
|
+
continue;
|
|
509
|
+
const notePath = resolve(input.agentDir, entry.file);
|
|
510
|
+
if (!isWithin(resolve(input.agentDir, "notes"), notePath))
|
|
511
|
+
throw new Error("local_memory_note_path_invalid");
|
|
512
|
+
const previous = noteUpdates.get(notePath) ?? await readNote(notePath);
|
|
513
|
+
const replacement = noteSection(id, 1, change.title, change.detailBody);
|
|
514
|
+
const existingSection = replaceNote(previous, id, replacement);
|
|
515
|
+
const next = existingSection ?? `${previous.trimEnd()}${previous.trimEnd() ? "\n\n" : ""}${replacement}`;
|
|
516
|
+
noteUpdates.set(notePath, next);
|
|
517
|
+
entries = candidateEntries;
|
|
518
|
+
createdIds.push(id);
|
|
519
|
+
resolvedPendingIds.add(pending.pendingId);
|
|
520
|
+
}
|
|
521
|
+
let finalProjection = projectIndex(memory, entries, allowActiveEviction ? pinnedIds(entries) : activeOrPinnedIds(entries));
|
|
522
|
+
if (finalProjection.reason !== null
|
|
523
|
+
&& (!allowActiveEviction || finalProjection.reason === "index_over_budget_pinned")) {
|
|
433
524
|
throw new Error(finalProjection.reason);
|
|
525
|
+
}
|
|
526
|
+
if (allowActiveEviction) {
|
|
527
|
+
const omittedActiveEntries = finalProjection.omittedIds
|
|
528
|
+
.map((id) => entries.find((entry) => entry.id === id))
|
|
529
|
+
.filter((entry) => entry?.status === "active");
|
|
530
|
+
for (const entry of omittedActiveEntries) {
|
|
531
|
+
const notePath = resolve(input.agentDir, entry.file);
|
|
532
|
+
const content = noteUpdates.get(notePath) ?? await readNote(notePath);
|
|
533
|
+
const detailBody = readNoteBody(content, entry.id);
|
|
534
|
+
const change = detailBody === null ? null : archivedChange(entry, detailBody);
|
|
535
|
+
if (change === null)
|
|
536
|
+
throw new Error("local_memory_note_anchor_missing");
|
|
537
|
+
const pendingId = `pending-${stableId(`evicted:${executionId}`, 0, change).slice("mem_".length)}`;
|
|
538
|
+
pendingCandidates.push({
|
|
539
|
+
schemaVersion: 1,
|
|
540
|
+
pendingId,
|
|
541
|
+
createdAt: new Date().toISOString(),
|
|
542
|
+
reason: "index_over_budget",
|
|
543
|
+
source: { executionId, pruneTraceId: input.pruneTraceId ?? null },
|
|
544
|
+
candidate: change,
|
|
545
|
+
});
|
|
546
|
+
deferredIds.push(pendingId);
|
|
547
|
+
entries = entries.map((item) => item.id === entry.id ? { ...item, status: "archived" } : item);
|
|
548
|
+
}
|
|
549
|
+
finalProjection = projectIndex(memory, entries, pinnedIds(entries));
|
|
550
|
+
if (finalProjection.reason !== null)
|
|
551
|
+
throw new Error(finalProjection.reason);
|
|
552
|
+
}
|
|
434
553
|
for (const candidate of pendingCandidates)
|
|
435
554
|
await persistPendingMemoryCandidate({ agentDir: input.agentDir, candidate });
|
|
436
555
|
for (const [path, content] of noteUpdates)
|
|
437
556
|
await atomicWrite(path, content);
|
|
438
557
|
if (noteUpdates.size > 0 || statusChangedIds.length > 0)
|
|
439
558
|
await atomicWrite(memoryPath, finalProjection.memory);
|
|
559
|
+
for (const pendingId of resolvedPendingIds) {
|
|
560
|
+
await removePendingMemoryCandidate({ agentDir: input.agentDir, pendingId });
|
|
561
|
+
}
|
|
440
562
|
return {
|
|
441
563
|
createdIds,
|
|
442
564
|
updatedIds,
|
package/dist/main.js
CHANGED
|
@@ -24,7 +24,7 @@ import { formatDaemonStartupError } from "./daemon-startup-error.js";
|
|
|
24
24
|
import { assertManagedServiceStartup } from "./managed-service-startup.js";
|
|
25
25
|
import { daemonGlobalInstallation } from "./daemon-installation.js";
|
|
26
26
|
import { acquireDaemonInstallationLease, runWithDaemonInstallationLease, } from "./daemon-installation-lease.js";
|
|
27
|
-
import { loadStorageRuntime } from "./storage-runtime.js";
|
|
27
|
+
import { createStorageRuntimeOwnershipBinding, loadStorageRuntime } from "./storage-runtime.js";
|
|
28
28
|
import { readStorageMigrationReceipt } from "./storage-migration-controller.js";
|
|
29
29
|
import { storageMigrationStatusReplay } from "./storage-status-replay.js";
|
|
30
30
|
import { runStorageWorkerCommand } from "./storage-worker-command.js";
|
|
@@ -160,6 +160,9 @@ async function main() {
|
|
|
160
160
|
...(process.getuid === undefined ? {} : { uid: process.getuid() }),
|
|
161
161
|
environment: process.env,
|
|
162
162
|
});
|
|
163
|
+
const targetStorageBinding = storageRuntime === null
|
|
164
|
+
? null
|
|
165
|
+
: createStorageRuntimeOwnershipBinding(storageRuntime);
|
|
163
166
|
const targetConfirmation = storageRuntime?.identity.storageMigrationId === undefined
|
|
164
167
|
? null
|
|
165
168
|
: createStorageTargetConfirmationHandler({
|
|
@@ -172,7 +175,8 @@ async function main() {
|
|
|
172
175
|
? {
|
|
173
176
|
ready: false,
|
|
174
177
|
statusOutboxPath: join(storageRuntime.local.stateRoot, "storage-status-outbox.json"),
|
|
175
|
-
identity:
|
|
178
|
+
identity: targetStorageBinding.identity,
|
|
179
|
+
bindServerIdentity: targetStorageBinding.bind,
|
|
176
180
|
...(targetConfirmation === null ? {} : { confirm: targetConfirmation.handle }),
|
|
177
181
|
...(storageRuntime.identity.storageMigrationId === undefined ? {} : {
|
|
178
182
|
replayStatus: async (send) => {
|
|
@@ -0,0 +1,109 @@
|
|
|
1
|
+
import { PROJECT_SKILLS_CAPABILITY, PROJECT_SKILL_PROJECTION_V2_CAPABILITY, } from "./types.js";
|
|
2
|
+
export function projectSkillV2RolloutReady(platform, readiness) {
|
|
3
|
+
const platformReady = platform === "darwin"
|
|
4
|
+
|| platform === "linux"
|
|
5
|
+
|| (platform === "win32" && readiness.nativeWindowsSmokePassed);
|
|
6
|
+
return platformReady
|
|
7
|
+
&& readiness.runtimeRootStorePrimitiveReady
|
|
8
|
+
&& readiness.runtimeRootStoreRecoveryReady
|
|
9
|
+
&& readiness.leaseRecoveryReady
|
|
10
|
+
&& readiness.controllerReady
|
|
11
|
+
&& readiness.executionHandlerReady
|
|
12
|
+
&& readiness.codexAdapterReady
|
|
13
|
+
&& readiness.claudeAdapterReady;
|
|
14
|
+
}
|
|
15
|
+
export function effectiveProjectSkillCapabilities(capabilities, projectSkillsReady, projectSkillV2Ready) {
|
|
16
|
+
const projectSkillCapabilitiesReady = projectSkillsReady && projectSkillV2Ready;
|
|
17
|
+
if (!projectSkillCapabilitiesReady
|
|
18
|
+
&& !capabilities.includes(PROJECT_SKILLS_CAPABILITY)
|
|
19
|
+
&& !capabilities.includes(PROJECT_SKILL_PROJECTION_V2_CAPABILITY)
|
|
20
|
+
&& Object.isFrozen(capabilities))
|
|
21
|
+
return capabilities;
|
|
22
|
+
const filtered = capabilities.filter((capability) => capability !== PROJECT_SKILL_PROJECTION_V2_CAPABILITY
|
|
23
|
+
&& (projectSkillCapabilitiesReady || capability !== PROJECT_SKILLS_CAPABILITY));
|
|
24
|
+
if (!projectSkillCapabilitiesReady || !filtered.includes(PROJECT_SKILLS_CAPABILITY)) {
|
|
25
|
+
return Object.freeze(filtered);
|
|
26
|
+
}
|
|
27
|
+
const v1Index = filtered.indexOf(PROJECT_SKILLS_CAPABILITY);
|
|
28
|
+
return Object.freeze([
|
|
29
|
+
...filtered.slice(0, v1Index + 1),
|
|
30
|
+
PROJECT_SKILL_PROJECTION_V2_CAPABILITY,
|
|
31
|
+
...filtered.slice(v1Index + 1),
|
|
32
|
+
]);
|
|
33
|
+
}
|
|
34
|
+
export function withProjectSkillCapabilityStatus(value, status) {
|
|
35
|
+
return {
|
|
36
|
+
...value,
|
|
37
|
+
projectSkillsStatus: status,
|
|
38
|
+
capabilities: status === "ready"
|
|
39
|
+
? value.capabilities
|
|
40
|
+
: effectiveProjectSkillCapabilities(value.capabilities, false, false),
|
|
41
|
+
};
|
|
42
|
+
}
|
|
43
|
+
const settleReadinessGate = async (operation) => {
|
|
44
|
+
try {
|
|
45
|
+
return await operation() === true;
|
|
46
|
+
}
|
|
47
|
+
catch {
|
|
48
|
+
return false;
|
|
49
|
+
}
|
|
50
|
+
};
|
|
51
|
+
export async function createProjectSkillConnectionCapabilitySnapshot(input) {
|
|
52
|
+
const runtimeRootStorePrimitiveReady = await settleReadinessGate(input.probeRuntimeRootStorePrimitive);
|
|
53
|
+
const controllerReady = await settleReadinessGate(input.initializeController);
|
|
54
|
+
const recoveryReady = await settleReadinessGate(input.recoverRuntimeRootsAndLeases);
|
|
55
|
+
const readiness = Object.freeze({
|
|
56
|
+
runtimeRootStorePrimitiveReady,
|
|
57
|
+
runtimeRootStoreRecoveryReady: recoveryReady,
|
|
58
|
+
leaseRecoveryReady: recoveryReady,
|
|
59
|
+
controllerReady,
|
|
60
|
+
executionHandlerReady: true,
|
|
61
|
+
codexAdapterReady: true,
|
|
62
|
+
claudeAdapterReady: true,
|
|
63
|
+
nativeWindowsSmokePassed: false,
|
|
64
|
+
});
|
|
65
|
+
const projectSkillsReady = projectSkillV2RolloutReady(input.platform, readiness);
|
|
66
|
+
return Object.freeze({
|
|
67
|
+
capabilities: effectiveProjectSkillCapabilities(input.baseCapabilities, projectSkillsReady, projectSkillsReady),
|
|
68
|
+
projectSkillsReady,
|
|
69
|
+
readiness,
|
|
70
|
+
});
|
|
71
|
+
}
|
|
72
|
+
export async function prepareProjectSkillConnectionCapabilities(input) {
|
|
73
|
+
const snapshot = await createProjectSkillConnectionCapabilitySnapshot({
|
|
74
|
+
...input,
|
|
75
|
+
baseCapabilities: [...input.baseCapabilities, ...await input.additionalCapabilities()],
|
|
76
|
+
});
|
|
77
|
+
input.applyReadiness(snapshot.projectSkillsReady);
|
|
78
|
+
return snapshot.capabilities;
|
|
79
|
+
}
|
|
80
|
+
export function createProjectSkillControllerReadiness(input) {
|
|
81
|
+
let status = "initializing";
|
|
82
|
+
let initialization = null;
|
|
83
|
+
const ensure = () => {
|
|
84
|
+
if (status === "ready")
|
|
85
|
+
return Promise.resolve(true);
|
|
86
|
+
if (initialization !== null)
|
|
87
|
+
return initialization;
|
|
88
|
+
status = "initializing";
|
|
89
|
+
const attempt = input.initialize().then(() => {
|
|
90
|
+
status = "ready";
|
|
91
|
+
return true;
|
|
92
|
+
}, (error) => {
|
|
93
|
+
status = "unavailable";
|
|
94
|
+
input.onUnavailable(error);
|
|
95
|
+
return false;
|
|
96
|
+
}).finally(() => {
|
|
97
|
+
if (initialization === attempt)
|
|
98
|
+
initialization = null;
|
|
99
|
+
input.onSettled();
|
|
100
|
+
});
|
|
101
|
+
initialization = attempt;
|
|
102
|
+
return attempt;
|
|
103
|
+
};
|
|
104
|
+
return Object.freeze({
|
|
105
|
+
status: () => status,
|
|
106
|
+
ensure,
|
|
107
|
+
applyPreflight: (ready) => { status = ready ? "ready" : "unavailable"; },
|
|
108
|
+
});
|
|
109
|
+
}
|
|
@@ -0,0 +1,57 @@
|
|
|
1
|
+
import { ProjectProjectionError, } from "./reconciler.js";
|
|
2
|
+
export const MAX_REMEMBERED_PROJECT_SKILL_AGENTS = 64;
|
|
3
|
+
const compareBinary = (left, right) => {
|
|
4
|
+
if (left < right)
|
|
5
|
+
return -1;
|
|
6
|
+
if (left > right)
|
|
7
|
+
return 1;
|
|
8
|
+
return 0;
|
|
9
|
+
};
|
|
10
|
+
const copySnapshot = (snapshot) => Object.freeze({
|
|
11
|
+
bindings: Object.freeze(snapshot.bindings.map((binding) => Object.freeze({ ...binding }))),
|
|
12
|
+
generation: snapshot.generation,
|
|
13
|
+
});
|
|
14
|
+
export function createProjectSkillSnapshotEpoch(maxEntries = MAX_REMEMBERED_PROJECT_SKILL_AGENTS) {
|
|
15
|
+
if (!Number.isSafeInteger(maxEntries) || maxEntries < 1) {
|
|
16
|
+
throw new ProjectProjectionError("skill_projection_failed");
|
|
17
|
+
}
|
|
18
|
+
const snapshots = new Map();
|
|
19
|
+
return Object.freeze({
|
|
20
|
+
begin() {
|
|
21
|
+
snapshots.clear();
|
|
22
|
+
},
|
|
23
|
+
remember(handle, snapshot) {
|
|
24
|
+
const previous = snapshots.get(handle);
|
|
25
|
+
if (previous !== undefined && snapshot.generation < previous.generation)
|
|
26
|
+
return;
|
|
27
|
+
if (previous === undefined && snapshots.size >= maxEntries) {
|
|
28
|
+
throw new ProjectProjectionError("skill_projection_failed");
|
|
29
|
+
}
|
|
30
|
+
snapshots.set(handle, copySnapshot(snapshot));
|
|
31
|
+
},
|
|
32
|
+
forget(handle) {
|
|
33
|
+
snapshots.delete(handle);
|
|
34
|
+
},
|
|
35
|
+
entries() {
|
|
36
|
+
return Object.freeze([...snapshots.entries()]
|
|
37
|
+
.sort(([left], [right]) => compareBinary(left, right))
|
|
38
|
+
.map(([handle, snapshot]) => Object.freeze([handle, copySnapshot(snapshot)])));
|
|
39
|
+
},
|
|
40
|
+
});
|
|
41
|
+
}
|
|
42
|
+
const stableErrorCode = (reason) => reason?.code === "skill_projection_snapshot_corrupt"
|
|
43
|
+
? "skill_projection_snapshot_corrupt"
|
|
44
|
+
: "skill_projection_failed";
|
|
45
|
+
export function toPathPrivateConvergenceResults(entries, results) {
|
|
46
|
+
return Object.freeze(entries.map(([handle], index) => {
|
|
47
|
+
const result = results[index];
|
|
48
|
+
if (result?.status === "fulfilled") {
|
|
49
|
+
return Object.freeze({ handle, status: "fulfilled" });
|
|
50
|
+
}
|
|
51
|
+
return Object.freeze({
|
|
52
|
+
handle,
|
|
53
|
+
status: "rejected",
|
|
54
|
+
errorCode: stableErrorCode(result?.reason),
|
|
55
|
+
});
|
|
56
|
+
}));
|
|
57
|
+
}
|