narraeon 0.8.0 → 0.8.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/dist/node/protocol/v1.js +22 -0
- package/dist/node/runtime/V1Runtime.js +34 -2
- package/dist/node/runtime/authoring/AuthoringEditPlan.js +79 -0
- package/dist/node/runtime/authoring/AuthoringRetry.js +59 -0
- package/dist/node/runtime/authoring/ModelToolConversation.js +22 -6
- package/dist/node/runtime/content/ContentWorkspace.js +7 -2
- package/dist/node/runtime/content/CurrentTreeContentLibrary.js +14 -8
- package/dist/node/runtime/model/DispatchFailure.js +8 -0
- package/dist/node/runtime/model/FileNativeModelAdapters.js +17 -0
- package/dist/node/runtime/play/FileNativePlayTimelineStore.js +49 -0
- package/dist/node/runtime/play/PlayAdvanceLifecycle.js +18 -14
- package/dist/node/runtime/play/PlayCallChain.js +16 -4
- package/dist/node/runtime/setting/FileNativeSettingImprovementStore.js +210 -8
- package/dist/node/runtime/setting/SettingImprovementSession.js +247 -24
- package/dist/node/runtime/world-revision/FileNativeWorldRevisionStore.js +154 -9
- package/dist/node/runtime/world-revision/WorldRevisionSession.js +199 -33
- package/dist/node/runtime/world-revision/WorldRevisionWorkspace.js +47 -1
- package/dist/web/assets/index-BthiDIQu.js +1432 -0
- package/dist/web/assets/{index-BKA1D0Rz.css → index-wO9Y4HT1.css} +1 -1
- package/dist/web/index.html +2 -2
- package/package.json +1 -1
- package/dist/web/assets/index-C_Qta8UL.js +0 -1432
|
@@ -31,6 +31,9 @@ export class FileNativeSettingImprovementStore {
|
|
|
31
31
|
createId() {
|
|
32
32
|
return `setting-${randomUUID()}`;
|
|
33
33
|
}
|
|
34
|
+
get sessionScope() {
|
|
35
|
+
return this.#sessionsRoot;
|
|
36
|
+
}
|
|
34
37
|
recordGoogleHop(sessionId, requestId, hop) {
|
|
35
38
|
assertSessionId(sessionId);
|
|
36
39
|
return writeGoogleHop(join(this.#sessionsRoot, ".google-hops", sessionId), requestId, hop, () => stat(this.#path(sessionId)).then(() => true, () => false));
|
|
@@ -84,10 +87,65 @@ export class FileNativeSettingImprovementStore {
|
|
|
84
87
|
save(session) {
|
|
85
88
|
return this.#mutate(async () => {
|
|
86
89
|
validateStoredSession(session);
|
|
90
|
+
await this.#preservePreviousSchema(session);
|
|
87
91
|
await publishJson(this.#path(session.sessionId), session);
|
|
88
92
|
await this.#recordSavedSession(session);
|
|
89
93
|
});
|
|
90
94
|
}
|
|
95
|
+
async preparePendingEdit(session) {
|
|
96
|
+
if (session.pendingEdit === undefined)
|
|
97
|
+
throw new Error("No pending setting conversation edit");
|
|
98
|
+
await publishJson(this.#pendingEditPath(session.packageId), {
|
|
99
|
+
schemaVersion: 1,
|
|
100
|
+
packageId: session.packageId,
|
|
101
|
+
sessionId: session.sessionId,
|
|
102
|
+
});
|
|
103
|
+
await this.save(session);
|
|
104
|
+
}
|
|
105
|
+
async recoverPendingEdits(packageId, lease) {
|
|
106
|
+
const path = this.#pendingEditPath(packageId);
|
|
107
|
+
let marker;
|
|
108
|
+
try {
|
|
109
|
+
marker = JSON.parse(await readFile(path, "utf8"));
|
|
110
|
+
}
|
|
111
|
+
catch (error) {
|
|
112
|
+
if (isNodeError(error) && error.code === "ENOENT")
|
|
113
|
+
return;
|
|
114
|
+
throw error;
|
|
115
|
+
}
|
|
116
|
+
if (!isRecord(marker) ||
|
|
117
|
+
!hasExactKeys(marker, ["schemaVersion", "packageId", "sessionId"]) ||
|
|
118
|
+
marker.schemaVersion !== 1 ||
|
|
119
|
+
marker.packageId !== packageId ||
|
|
120
|
+
typeof marker.sessionId !== "string")
|
|
121
|
+
throw new Error("Pending authoring edit marker is damaged");
|
|
122
|
+
assertSessionId(marker.sessionId);
|
|
123
|
+
const raw = JSON.parse(await readFile(this.#path(marker.sessionId), "utf8"));
|
|
124
|
+
const session = validateStoredSession(raw);
|
|
125
|
+
if (session.sessionId !== marker.sessionId ||
|
|
126
|
+
session.packageId !== packageId)
|
|
127
|
+
throw new Error("Pending edit identity does not match its package");
|
|
128
|
+
if (session.pendingEdit === undefined) {
|
|
129
|
+
await rm(path, { force: true });
|
|
130
|
+
return;
|
|
131
|
+
}
|
|
132
|
+
await this.finishPendingEdit(session, lease);
|
|
133
|
+
}
|
|
134
|
+
async finishPendingEdit(session, lease) {
|
|
135
|
+
const pending = session.pendingEdit;
|
|
136
|
+
if (pending === undefined)
|
|
137
|
+
throw new Error("No pending setting conversation edit");
|
|
138
|
+
const fingerprint = contentTreeFingerprint(lease.package.files);
|
|
139
|
+
if (fingerprint === pending.beforeFingerprint &&
|
|
140
|
+
fingerprint !== pending.afterFingerprint)
|
|
141
|
+
await lease.replace(pending.afterFiles);
|
|
142
|
+
else if (fingerprint !== pending.afterFingerprint)
|
|
143
|
+
throw new Error("Cannot recover the conversation edit because the content package changed; no further changes were made");
|
|
144
|
+
await publishJson(this.#path(session.sessionId), pending.next);
|
|
145
|
+
await rm(this.#pendingEditPath(session.packageId), { force: true });
|
|
146
|
+
await this.#recordSavedSession(pending.next);
|
|
147
|
+
return structuredClone(pending.next);
|
|
148
|
+
}
|
|
91
149
|
saveIfUnchanged(expected, replacement) {
|
|
92
150
|
return this.#mutate(async () => {
|
|
93
151
|
if (expected.sessionId !== replacement.sessionId)
|
|
@@ -96,6 +154,7 @@ export class FileNativeSettingImprovementStore {
|
|
|
96
154
|
const current = await this.#read(expected.sessionId);
|
|
97
155
|
if (!isDeepStrictEqual(current, expected))
|
|
98
156
|
return false;
|
|
157
|
+
await this.#preservePreviousSchema(replacement);
|
|
99
158
|
await publishJson(this.#path(replacement.sessionId), replacement);
|
|
100
159
|
await this.#recordSavedSession(replacement);
|
|
101
160
|
return true;
|
|
@@ -114,6 +173,8 @@ export class FileNativeSettingImprovementStore {
|
|
|
114
173
|
await rm(join(this.#sessionsRoot, `${sessionId}.schema-v1.json`), {
|
|
115
174
|
force: true,
|
|
116
175
|
});
|
|
176
|
+
for (const version of [2, 3, 4])
|
|
177
|
+
await rm(join(this.#sessionsRoot, `${sessionId}.schema-v${version}.json`), { force: true });
|
|
117
178
|
await this.#recordDeletedSession(session);
|
|
118
179
|
});
|
|
119
180
|
}
|
|
@@ -184,7 +245,9 @@ export class FileNativeSettingImprovementStore {
|
|
|
184
245
|
session.googleIdentity?.sessionId !== sessionId ||
|
|
185
246
|
session.googleIdentity.packageId !== session.packageId ||
|
|
186
247
|
session.googleIdentity.creationRequestId !== session.creationRequestId ||
|
|
187
|
-
session.requests?.[0]?.requestId !== session.creationRequestId
|
|
248
|
+
(session.requests?.[0]?.requestId !== session.creationRequestId &&
|
|
249
|
+
!(session.schemaVersion === 5 &&
|
|
250
|
+
session.editReceipts?.some((receipt) => receipt.requestId === session.requests?.[0]?.requestId))) ||
|
|
188
251
|
!/^package-[0-9a-f]{8}-[0-9a-f]{4}-4[0-9a-f]{3}-[89ab][0-9a-f]{3}-[0-9a-f]{12}$/u.test(session.packageId) ||
|
|
189
252
|
!/^[A-Za-z0-9][A-Za-z0-9._:-]{0,159}$/u.test(session.creationRequestId) ||
|
|
190
253
|
hasPendingToolCalls(session))
|
|
@@ -292,6 +355,27 @@ export class FileNativeSettingImprovementStore {
|
|
|
292
355
|
request.wireRequest = await readGoogleHops(join(this.#sessionsRoot, ".google-hops", sessionId), request.requestId, request.wireRequest);
|
|
293
356
|
return session;
|
|
294
357
|
}
|
|
358
|
+
async #preservePreviousSchema(next) {
|
|
359
|
+
if (next.schemaVersion < 4)
|
|
360
|
+
return;
|
|
361
|
+
let source;
|
|
362
|
+
try {
|
|
363
|
+
source = await readFile(this.#path(next.sessionId), "utf8");
|
|
364
|
+
}
|
|
365
|
+
catch (error) {
|
|
366
|
+
if (isNodeError(error) && error.code === "ENOENT")
|
|
367
|
+
return;
|
|
368
|
+
throw error;
|
|
369
|
+
}
|
|
370
|
+
const previous = validateStoredSession(JSON.parse(source));
|
|
371
|
+
if (previous.sessionId !== next.sessionId)
|
|
372
|
+
throw new Error("Setting-improvement conversation identity does not match its file");
|
|
373
|
+
if (previous.schemaVersion === next.schemaVersion)
|
|
374
|
+
return;
|
|
375
|
+
if (previous.schemaVersion > next.schemaVersion)
|
|
376
|
+
throw new Error("Cannot downgrade a setting-improvement conversation");
|
|
377
|
+
await publishImmutableSource(join(this.#sessionsRoot, `${next.sessionId}.schema-v${previous.schemaVersion}.json`), source);
|
|
378
|
+
}
|
|
295
379
|
async #migrateLegacy(sessionId, source, value) {
|
|
296
380
|
if (!validLegacySession(value))
|
|
297
381
|
throw new Error("Legacy setting-improvement conversation is damaged");
|
|
@@ -338,6 +422,11 @@ export class FileNativeSettingImprovementStore {
|
|
|
338
422
|
assertSessionId(sessionId);
|
|
339
423
|
return join(this.#sessionsRoot, `${sessionId}.json`);
|
|
340
424
|
}
|
|
425
|
+
#pendingEditPath(packageId) {
|
|
426
|
+
if (!/^package-[0-9a-f]{8}-[0-9a-f]{4}-4[0-9a-f]{3}-[89ab][0-9a-f]{3}-[0-9a-f]{12}$/u.test(packageId))
|
|
427
|
+
throw new Error("Invalid content package ID");
|
|
428
|
+
return join(this.#sessionsRoot, ".pending-edits", `${packageId}.json`);
|
|
429
|
+
}
|
|
341
430
|
}
|
|
342
431
|
function compareSummaries(left, right) {
|
|
343
432
|
return (right.updatedAt - left.updatedAt ||
|
|
@@ -380,7 +469,8 @@ export function summarizeStoredSettingImprovementSession(session) {
|
|
|
380
469
|
changedFileCount: changedPaths.size,
|
|
381
470
|
exchange: session.exchange,
|
|
382
471
|
toolCalls: session.toolCalls,
|
|
383
|
-
needsRecovery: session.
|
|
472
|
+
needsRecovery: session.pendingEdit !== undefined ||
|
|
473
|
+
session.pendingSettlement !== null ||
|
|
384
474
|
session.runStatus === "running" ||
|
|
385
475
|
hasPendingToolCalls(session),
|
|
386
476
|
};
|
|
@@ -439,15 +529,43 @@ function validateStoredSession(value) {
|
|
|
439
529
|
"requests",
|
|
440
530
|
"googleOutcomeUnknown",
|
|
441
531
|
"googleIdentity",
|
|
532
|
+
"retryDispatch",
|
|
533
|
+
"pendingEdit",
|
|
534
|
+
"editReceipts",
|
|
442
535
|
]) ||
|
|
443
|
-
(value.schemaVersion !== 2 &&
|
|
536
|
+
(value.schemaVersion !== 2 &&
|
|
537
|
+
value.schemaVersion !== 3 &&
|
|
538
|
+
value.schemaVersion !== 4 &&
|
|
539
|
+
value.schemaVersion !== 5) ||
|
|
540
|
+
(value.schemaVersion !== 4 &&
|
|
541
|
+
value.schemaVersion !== 5 &&
|
|
542
|
+
value.retryDispatch !== undefined) ||
|
|
543
|
+
(value.schemaVersion !== 5 && value.pendingEdit !== undefined) ||
|
|
544
|
+
(value.schemaVersion !== 5 && value.editReceipts !== undefined) ||
|
|
545
|
+
(value.editReceipts !== undefined &&
|
|
546
|
+
!validEditReceipts(value.editReceipts)) ||
|
|
547
|
+
(value.pendingEdit !== undefined &&
|
|
548
|
+
!validPendingEdit(value.pendingEdit, value)) ||
|
|
444
549
|
(value.schemaVersion === 2
|
|
445
550
|
? value.requests !== undefined
|
|
446
|
-
:
|
|
447
|
-
|
|
448
|
-
|
|
449
|
-
|
|
450
|
-
|
|
551
|
+
: (value.schemaVersion === 4 || value.schemaVersion === 5) &&
|
|
552
|
+
Array.isArray(value.requests) &&
|
|
553
|
+
value.requests.length === 0 &&
|
|
554
|
+
((value.schemaVersion === 5 && value.pendingEdit !== undefined) ||
|
|
555
|
+
(value.runStatus === "ready" &&
|
|
556
|
+
Array.isArray(value.modelItems) &&
|
|
557
|
+
value.modelItems.length === 0 &&
|
|
558
|
+
Array.isArray(value.messages) &&
|
|
559
|
+
value.messages.length === 0 &&
|
|
560
|
+
value.activeRequestId === null &&
|
|
561
|
+
Array.isArray(value.completedRequestIds) &&
|
|
562
|
+
value.completedRequestIds.length === 0))
|
|
563
|
+
? false
|
|
564
|
+
: !validAuthoringRequests(value.requests, validPromptCompilation, {
|
|
565
|
+
modelItems: value.modelItems,
|
|
566
|
+
activeRequestId: value.activeRequestId,
|
|
567
|
+
completedRequestIds: value.completedRequestIds,
|
|
568
|
+
})) ||
|
|
451
569
|
typeof value.sessionId !== "string" ||
|
|
452
570
|
typeof value.packageId !== "string" ||
|
|
453
571
|
!validOptionalTitle(value.contentPackageTitle) ||
|
|
@@ -458,6 +576,27 @@ function validateStoredSession(value) {
|
|
|
458
576
|
typeof value.creationRequestId !== "string" ||
|
|
459
577
|
!validPromptCompilation(value.bootstrap) ||
|
|
460
578
|
!validModelBinding(value.modelBinding) ||
|
|
579
|
+
(value.retryDispatch !== undefined &&
|
|
580
|
+
(!isRecord(value.retryDispatch) ||
|
|
581
|
+
!hasExactKeys(value.retryDispatch, [
|
|
582
|
+
"requestId",
|
|
583
|
+
"exchange",
|
|
584
|
+
"modelItemCount",
|
|
585
|
+
"state",
|
|
586
|
+
]) ||
|
|
587
|
+
typeof value.retryDispatch.requestId !== "string" ||
|
|
588
|
+
!validCount(value.retryDispatch.exchange) ||
|
|
589
|
+
!validCount(value.retryDispatch.modelItemCount) ||
|
|
590
|
+
!["dispatching", "retryable", "blocked"].includes(String(value.retryDispatch.state)) ||
|
|
591
|
+
(value.schemaVersion !== 4 && value.schemaVersion !== 5) ||
|
|
592
|
+
lastRequestId(value.requests) !== value.retryDispatch.requestId ||
|
|
593
|
+
(value.runStatus !== "running" && value.runStatus !== "interrupted") ||
|
|
594
|
+
value.pendingSettlement !== null ||
|
|
595
|
+
!validCount(value.exchange) ||
|
|
596
|
+
(value.retryDispatch.exchange !== value.exchange &&
|
|
597
|
+
value.retryDispatch.exchange !== Number(value.exchange) + 1) ||
|
|
598
|
+
!Array.isArray(value.modelItems) ||
|
|
599
|
+
value.retryDispatch.modelItemCount !== value.modelItems.length)) ||
|
|
461
600
|
(value.playPreset !== undefined &&
|
|
462
601
|
!isPlayPresetBinding(value.playPreset)) ||
|
|
463
602
|
!Array.isArray(value.modelItems) ||
|
|
@@ -502,6 +641,69 @@ function validateStoredSession(value) {
|
|
|
502
641
|
throw new Error("Setting-improvement transcript is damaged");
|
|
503
642
|
return structuredClone(value);
|
|
504
643
|
}
|
|
644
|
+
function lastRequestId(requests) {
|
|
645
|
+
if (!Array.isArray(requests))
|
|
646
|
+
return undefined;
|
|
647
|
+
const last = requests.at(-1);
|
|
648
|
+
return isRecord(last) ? last.requestId : undefined;
|
|
649
|
+
}
|
|
650
|
+
function validEditReceipts(value) {
|
|
651
|
+
if (!Array.isArray(value))
|
|
652
|
+
return false;
|
|
653
|
+
const ids = new Set();
|
|
654
|
+
return value.every((entry) => {
|
|
655
|
+
if (!isRecord(entry) ||
|
|
656
|
+
!hasExactKeys(entry, [
|
|
657
|
+
"requestId",
|
|
658
|
+
"messageId",
|
|
659
|
+
"message",
|
|
660
|
+
"filePolicy",
|
|
661
|
+
]) ||
|
|
662
|
+
typeof entry.requestId !== "string" ||
|
|
663
|
+
!/^[A-Za-z0-9][A-Za-z0-9._:-]{0,159}$/u.test(entry.requestId) ||
|
|
664
|
+
ids.has(entry.requestId) ||
|
|
665
|
+
typeof entry.messageId !== "string" ||
|
|
666
|
+
typeof entry.message !== "string" ||
|
|
667
|
+
entry.message.trim().length === 0 ||
|
|
668
|
+
entry.message.length > 64 * 1024 ||
|
|
669
|
+
(entry.filePolicy !== "keep" && entry.filePolicy !== "revert"))
|
|
670
|
+
return false;
|
|
671
|
+
ids.add(entry.requestId);
|
|
672
|
+
return true;
|
|
673
|
+
});
|
|
674
|
+
}
|
|
675
|
+
function validPendingEdit(value, original) {
|
|
676
|
+
if (!isRecord(value) ||
|
|
677
|
+
!hasExactKeys(value, [
|
|
678
|
+
"beforeFingerprint",
|
|
679
|
+
"afterFingerprint",
|
|
680
|
+
"afterFiles",
|
|
681
|
+
"next",
|
|
682
|
+
]) ||
|
|
683
|
+
typeof value.beforeFingerprint !== "string" ||
|
|
684
|
+
!/^[0-9a-f]{64}$/u.test(value.beforeFingerprint) ||
|
|
685
|
+
typeof value.afterFingerprint !== "string" ||
|
|
686
|
+
!/^[0-9a-f]{64}$/u.test(value.afterFingerprint) ||
|
|
687
|
+
!validContentTreeFiles(value.afterFiles) ||
|
|
688
|
+
contentTreeFingerprint(value.afterFiles) !== value.afterFingerprint ||
|
|
689
|
+
!isRecord(value.next) ||
|
|
690
|
+
value.next.pendingEdit !== undefined ||
|
|
691
|
+
value.next.schemaVersion !== 5 ||
|
|
692
|
+
value.next.sessionId !== original.sessionId ||
|
|
693
|
+
value.next.packageId !== original.packageId ||
|
|
694
|
+
value.next.creationRequestId !== original.creationRequestId ||
|
|
695
|
+
value.next.runStatus !== "running" ||
|
|
696
|
+
value.next.activeRequestId === null ||
|
|
697
|
+
original.pendingSettlement !== null)
|
|
698
|
+
return false;
|
|
699
|
+
try {
|
|
700
|
+
validateStoredSession(value.next);
|
|
701
|
+
return true;
|
|
702
|
+
}
|
|
703
|
+
catch {
|
|
704
|
+
return false;
|
|
705
|
+
}
|
|
706
|
+
}
|
|
505
707
|
function migrateLegacySession(value, applyOutcome) {
|
|
506
708
|
if (!validLegacySession(value))
|
|
507
709
|
throw new Error("Legacy setting-improvement conversation is damaged");
|