narraeon 0.7.10 → 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/modelConnections.js +15 -0
- package/dist/node/protocol/modelUsage.js +4 -0
- package/dist/node/protocol/sillyTavernContent.js +1 -0
- package/dist/node/protocol/v1.js +97 -0
- package/dist/node/runtime/V1Runtime.js +105 -3
- package/dist/node/runtime/authoring/AuthoringEditPlan.js +79 -0
- package/dist/node/runtime/authoring/AuthoringRequestSnapshot.js +15 -3
- package/dist/node/runtime/authoring/AuthoringRetry.js +59 -0
- package/dist/node/runtime/authoring/DamagedGoogleContinuation.js +68 -0
- package/dist/node/runtime/authoring/ModelToolConversation.js +66 -25
- package/dist/node/runtime/content/ContentWorkspace.js +10 -2
- package/dist/node/runtime/content/CurrentTreeContentLibrary.js +14 -8
- package/dist/node/runtime/content/SillyTavernContent.js +796 -0
- package/dist/node/runtime/model/AiFailureLog.js +8 -1
- package/dist/node/runtime/model/DispatchFailure.js +8 -0
- package/dist/node/runtime/model/FileNativeModelAdapters.js +627 -18
- package/dist/node/runtime/model/GoogleHopJournal.js +90 -0
- package/dist/node/runtime/model/GoogleInteractionsStream.js +242 -0
- package/dist/node/runtime/model/GoogleRequestPreview.js +184 -0
- package/dist/node/runtime/model/ModelConnectionStore.js +4 -1
- package/dist/node/runtime/model/ProviderExchangeState.js +44 -1
- package/dist/node/runtime/model/ProviderStream.js +4 -2
- package/dist/node/runtime/play/FileNativePlayAdvanceStore.js +4 -4
- package/dist/node/runtime/play/FileNativePlayPresetStore.js +150 -78
- package/dist/node/runtime/play/FileNativePlayTimelineStore.js +199 -10
- package/dist/node/runtime/play/OrderedPlayPrompts.js +139 -178
- package/dist/node/runtime/play/PlayAdvanceLifecycle.js +51 -23
- package/dist/node/runtime/play/PlayCallChain.js +92 -8
- package/dist/node/runtime/play/PlayFollowupRequests.js +23 -3
- package/dist/node/runtime/prompt/PromptCompilationCodec.js +2 -1
- package/dist/node/runtime/setting/FileNativeSettingImprovementStore.js +321 -15
- package/dist/node/runtime/setting/SettingImprovementSession.js +285 -21
- package/dist/node/runtime/world-revision/FileNativeWorldRevisionStore.js +236 -15
- package/dist/node/runtime/world-revision/WorldRevisionSession.js +324 -50
- package/dist/node/runtime/world-revision/WorldRevisionWorkspace.js +47 -1
- package/dist/node/shared/application-preset-files.js +29 -0
- package/dist/node/shared/default-preset-host.js +50 -38
- package/dist/node/shared/first-party-action-choices.js +7 -11
- package/dist/node/shared/first-party-generic-panels.js +7 -11
- package/dist/node/shared/first-party-player-view.js +7 -11
- package/dist/node/shared/ordered-author-prompts.js +6 -4
- package/dist/node/shared/ordered-play-prompts.js +4 -2
- package/dist/node/shared/prompt-arrangement.js +205 -0
- package/dist/node/shared/sillytavern-preset.js +454 -0
- package/dist/web/assets/index-BthiDIQu.js +1432 -0
- package/dist/web/assets/{index-DH2BVsYd.css → index-wO9Y4HT1.css} +1 -1
- package/dist/web/index.html +2 -2
- package/package.json +1 -1
- package/dist/web/assets/index-B2kbaOGe.js +0 -1408
|
@@ -1,11 +1,14 @@
|
|
|
1
1
|
import { validPromptCompilation, validLogicalMessages, validPromptTools, validPlayFollowup, validProviderKind, validCacheStrategy, validToolStrategy, } from "../prompt/PromptCompilationCodec.js";
|
|
2
2
|
import { validAuthoringRequests, } from "../authoring/AuthoringRequestSnapshot.js";
|
|
3
3
|
import { randomUUID } from "node:crypto";
|
|
4
|
+
import { readGoogleHops, writeGoogleHop } from "../model/GoogleHopJournal.js";
|
|
4
5
|
import { mkdir, open, readFile, readdir, rename, rm, stat, } from "node:fs/promises";
|
|
5
6
|
import { linkExclusive } from "../FileNativeExclusiveLink.js";
|
|
6
7
|
import { dirname, join } from "node:path";
|
|
7
8
|
import { isDeepStrictEqual } from "node:util";
|
|
8
9
|
import { fictionGameplay } from "../../protocol/gameplay.js";
|
|
10
|
+
import { validGoogleInteractionsSteps } from "../model/ProviderExchangeState.js";
|
|
11
|
+
import { hasDamagedGoogleSteps, withoutDamagedGoogleSteps, } from "../authoring/DamagedGoogleContinuation.js";
|
|
9
12
|
import { ContentWorkspace } from "../content/ContentWorkspace.js";
|
|
10
13
|
import { contentTreeFingerprint } from "../content/ContentTreeFingerprint.js";
|
|
11
14
|
import { isPlayPresetBinding, } from "../play/FileNativePlayPresetStore.js";
|
|
@@ -28,19 +31,38 @@ export class FileNativeSettingImprovementStore {
|
|
|
28
31
|
createId() {
|
|
29
32
|
return `setting-${randomUUID()}`;
|
|
30
33
|
}
|
|
34
|
+
get sessionScope() {
|
|
35
|
+
return this.#sessionsRoot;
|
|
36
|
+
}
|
|
37
|
+
recordGoogleHop(sessionId, requestId, hop) {
|
|
38
|
+
assertSessionId(sessionId);
|
|
39
|
+
return writeGoogleHop(join(this.#sessionsRoot, ".google-hops", sessionId), requestId, hop, () => stat(this.#path(sessionId)).then(() => true, () => false));
|
|
40
|
+
}
|
|
31
41
|
async read(sessionId) {
|
|
32
42
|
await this.#mutationTail;
|
|
33
|
-
|
|
43
|
+
try {
|
|
44
|
+
const session = await this.#read(sessionId);
|
|
45
|
+
if (hasDamagedGoogleSteps(session) &&
|
|
46
|
+
(await this.#damagedSummary(sessionId)))
|
|
47
|
+
throw new Error("The historical conversation has damaged Google continuation; start a fresh model context");
|
|
48
|
+
return session;
|
|
49
|
+
}
|
|
50
|
+
catch (error) {
|
|
51
|
+
if (await this.#damagedSummary(sessionId))
|
|
52
|
+
throw new Error("The historical conversation has damaged Google continuation; start a fresh model context", { cause: error });
|
|
53
|
+
throw error;
|
|
54
|
+
}
|
|
34
55
|
}
|
|
35
56
|
async findLatestByPackage(packageId) {
|
|
36
57
|
const summaries = await this.listSummariesByPackage(packageId);
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
: this.read(summaries[0].sessionId);
|
|
58
|
+
const latest = summaries.find((summary) => !summary.damagedContinuation);
|
|
59
|
+
return latest === undefined ? null : this.read(latest.sessionId);
|
|
40
60
|
}
|
|
41
61
|
async findByCreationRequest(packageId, requestId) {
|
|
42
62
|
const summaries = await this.listSummariesByPackage(packageId);
|
|
43
63
|
const match = summaries.find(({ creationRequestId }) => creationRequestId === requestId);
|
|
64
|
+
if (match?.damagedContinuation)
|
|
65
|
+
throw new Error("The historical conversation has damaged Google continuation; start a fresh model context with a new request ID");
|
|
44
66
|
return match === undefined ? null : this.read(match.sessionId);
|
|
45
67
|
}
|
|
46
68
|
async listSummariesByPackage(packageId) {
|
|
@@ -65,10 +87,65 @@ export class FileNativeSettingImprovementStore {
|
|
|
65
87
|
save(session) {
|
|
66
88
|
return this.#mutate(async () => {
|
|
67
89
|
validateStoredSession(session);
|
|
90
|
+
await this.#preservePreviousSchema(session);
|
|
68
91
|
await publishJson(this.#path(session.sessionId), session);
|
|
69
92
|
await this.#recordSavedSession(session);
|
|
70
93
|
});
|
|
71
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
|
+
}
|
|
72
149
|
saveIfUnchanged(expected, replacement) {
|
|
73
150
|
return this.#mutate(async () => {
|
|
74
151
|
if (expected.sessionId !== replacement.sessionId)
|
|
@@ -77,6 +154,7 @@ export class FileNativeSettingImprovementStore {
|
|
|
77
154
|
const current = await this.#read(expected.sessionId);
|
|
78
155
|
if (!isDeepStrictEqual(current, expected))
|
|
79
156
|
return false;
|
|
157
|
+
await this.#preservePreviousSchema(replacement);
|
|
80
158
|
await publishJson(this.#path(replacement.sessionId), replacement);
|
|
81
159
|
await this.#recordSavedSession(replacement);
|
|
82
160
|
return true;
|
|
@@ -88,9 +166,15 @@ export class FileNativeSettingImprovementStore {
|
|
|
88
166
|
if (session.packageId !== packageId)
|
|
89
167
|
throw new Error("The setting-improvement conversation does not belong to this content package");
|
|
90
168
|
await rm(this.#path(sessionId));
|
|
169
|
+
await rm(join(this.#sessionsRoot, ".google-hops", sessionId), {
|
|
170
|
+
recursive: true,
|
|
171
|
+
force: true,
|
|
172
|
+
});
|
|
91
173
|
await rm(join(this.#sessionsRoot, `${sessionId}.schema-v1.json`), {
|
|
92
174
|
force: true,
|
|
93
175
|
});
|
|
176
|
+
for (const version of [2, 3, 4])
|
|
177
|
+
await rm(join(this.#sessionsRoot, `${sessionId}.schema-v${version}.json`), { force: true });
|
|
94
178
|
await this.#recordDeletedSession(session);
|
|
95
179
|
});
|
|
96
180
|
}
|
|
@@ -117,8 +201,20 @@ export class FileNativeSettingImprovementStore {
|
|
|
117
201
|
for (const entry of entries) {
|
|
118
202
|
if (!entry.isFile() || !sessionFilePattern.test(entry.name))
|
|
119
203
|
continue;
|
|
120
|
-
const
|
|
121
|
-
|
|
204
|
+
const sessionId = entry.name.slice(0, -5);
|
|
205
|
+
try {
|
|
206
|
+
const session = await this.#read(sessionId);
|
|
207
|
+
summaries.set(session.sessionId, (!hasDamagedGoogleSteps(session)
|
|
208
|
+
? null
|
|
209
|
+
: await this.#damagedSummary(sessionId)) ??
|
|
210
|
+
summarizeStoredSettingImprovementSession(session));
|
|
211
|
+
}
|
|
212
|
+
catch (error) {
|
|
213
|
+
const summary = await this.#damagedSummary(sessionId);
|
|
214
|
+
if (summary === null)
|
|
215
|
+
throw error;
|
|
216
|
+
summaries.set(sessionId, summary);
|
|
217
|
+
}
|
|
122
218
|
}
|
|
123
219
|
const completedRevision = await directoryRevision(this.#sessionsRoot);
|
|
124
220
|
if (completedRevision !== revision)
|
|
@@ -127,6 +223,41 @@ export class FileNativeSettingImprovementStore {
|
|
|
127
223
|
return;
|
|
128
224
|
}
|
|
129
225
|
}
|
|
226
|
+
async #damagedSummary(sessionId) {
|
|
227
|
+
let session;
|
|
228
|
+
try {
|
|
229
|
+
const raw = JSON.parse(await readFile(this.#path(sessionId), "utf8"));
|
|
230
|
+
const copy = withoutDamagedGoogleSteps(raw);
|
|
231
|
+
if (copy === null) {
|
|
232
|
+
if (hasDamagedGoogleSteps(raw))
|
|
233
|
+
throw new Error("Cannot safely identify the damaged historical conversation; settlement or identity is unverified");
|
|
234
|
+
return null;
|
|
235
|
+
}
|
|
236
|
+
session = validateStoredSession(copy);
|
|
237
|
+
}
|
|
238
|
+
catch (error) {
|
|
239
|
+
if (error instanceof Error &&
|
|
240
|
+
error.message.startsWith("Cannot safely identify"))
|
|
241
|
+
throw error;
|
|
242
|
+
return null;
|
|
243
|
+
}
|
|
244
|
+
if (session.sessionId !== sessionId ||
|
|
245
|
+
session.googleIdentity?.sessionId !== sessionId ||
|
|
246
|
+
session.googleIdentity.packageId !== session.packageId ||
|
|
247
|
+
session.googleIdentity.creationRequestId !== session.creationRequestId ||
|
|
248
|
+
(session.requests?.[0]?.requestId !== session.creationRequestId &&
|
|
249
|
+
!(session.schemaVersion === 5 &&
|
|
250
|
+
session.editReceipts?.some((receipt) => receipt.requestId === session.requests?.[0]?.requestId))) ||
|
|
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) ||
|
|
252
|
+
!/^[A-Za-z0-9][A-Za-z0-9._:-]{0,159}$/u.test(session.creationRequestId) ||
|
|
253
|
+
hasPendingToolCalls(session))
|
|
254
|
+
throw new Error("Cannot safely identify the damaged historical conversation; its owner or creation identity is unverified");
|
|
255
|
+
return {
|
|
256
|
+
...summarizeStoredSettingImprovementSession(session),
|
|
257
|
+
runStatus: "interrupted",
|
|
258
|
+
damagedContinuation: true,
|
|
259
|
+
};
|
|
260
|
+
}
|
|
130
261
|
async #recordSavedSession(session) {
|
|
131
262
|
if (this.#summaryIndex !== null)
|
|
132
263
|
this.#cacheSummary(summarizeStoredSettingImprovementSession(session));
|
|
@@ -219,8 +350,32 @@ export class FileNativeSettingImprovementStore {
|
|
|
219
350
|
const session = validateStoredSession(value);
|
|
220
351
|
if (session.sessionId !== sessionId)
|
|
221
352
|
throw new Error("Setting-improvement conversation identity does not match its file");
|
|
353
|
+
for (const request of session.requests ?? [])
|
|
354
|
+
if (request.wireRequest !== undefined)
|
|
355
|
+
request.wireRequest = await readGoogleHops(join(this.#sessionsRoot, ".google-hops", sessionId), request.requestId, request.wireRequest);
|
|
222
356
|
return session;
|
|
223
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
|
+
}
|
|
224
379
|
async #migrateLegacy(sessionId, source, value) {
|
|
225
380
|
if (!validLegacySession(value))
|
|
226
381
|
throw new Error("Legacy setting-improvement conversation is damaged");
|
|
@@ -267,6 +422,11 @@ export class FileNativeSettingImprovementStore {
|
|
|
267
422
|
assertSessionId(sessionId);
|
|
268
423
|
return join(this.#sessionsRoot, `${sessionId}.json`);
|
|
269
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
|
+
}
|
|
270
430
|
}
|
|
271
431
|
function compareSummaries(left, right) {
|
|
272
432
|
return (right.updatedAt - left.updatedAt ||
|
|
@@ -309,7 +469,8 @@ export function summarizeStoredSettingImprovementSession(session) {
|
|
|
309
469
|
changedFileCount: changedPaths.size,
|
|
310
470
|
exchange: session.exchange,
|
|
311
471
|
toolCalls: session.toolCalls,
|
|
312
|
-
needsRecovery: session.
|
|
472
|
+
needsRecovery: session.pendingEdit !== undefined ||
|
|
473
|
+
session.pendingSettlement !== null ||
|
|
313
474
|
session.runStatus === "running" ||
|
|
314
475
|
hasPendingToolCalls(session),
|
|
315
476
|
};
|
|
@@ -362,15 +523,49 @@ function validateStoredSession(value) {
|
|
|
362
523
|
"authorization",
|
|
363
524
|
"pendingSettlement",
|
|
364
525
|
"legacyDraft",
|
|
365
|
-
], [
|
|
366
|
-
|
|
526
|
+
], [
|
|
527
|
+
"contentPackageTitle",
|
|
528
|
+
"playPreset",
|
|
529
|
+
"requests",
|
|
530
|
+
"googleOutcomeUnknown",
|
|
531
|
+
"googleIdentity",
|
|
532
|
+
"retryDispatch",
|
|
533
|
+
"pendingEdit",
|
|
534
|
+
"editReceipts",
|
|
535
|
+
]) ||
|
|
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)) ||
|
|
367
549
|
(value.schemaVersion === 2
|
|
368
550
|
? value.requests !== undefined
|
|
369
|
-
:
|
|
370
|
-
|
|
371
|
-
|
|
372
|
-
|
|
373
|
-
|
|
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
|
+
})) ||
|
|
374
569
|
typeof value.sessionId !== "string" ||
|
|
375
570
|
typeof value.packageId !== "string" ||
|
|
376
571
|
!validOptionalTitle(value.contentPackageTitle) ||
|
|
@@ -381,6 +576,27 @@ function validateStoredSession(value) {
|
|
|
381
576
|
typeof value.creationRequestId !== "string" ||
|
|
382
577
|
!validPromptCompilation(value.bootstrap) ||
|
|
383
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)) ||
|
|
384
600
|
(value.playPreset !== undefined &&
|
|
385
601
|
!isPlayPresetBinding(value.playPreset)) ||
|
|
386
602
|
!Array.isArray(value.modelItems) ||
|
|
@@ -392,18 +608,102 @@ function validateStoredSession(value) {
|
|
|
392
608
|
typeof value.activeRequestId !== "string") ||
|
|
393
609
|
!Array.isArray(value.completedRequestIds) ||
|
|
394
610
|
(value.lastFailure !== null && typeof value.lastFailure !== "string") ||
|
|
611
|
+
(value.googleOutcomeUnknown !== undefined &&
|
|
612
|
+
(typeof value.googleOutcomeUnknown !== "boolean" ||
|
|
613
|
+
!isRecord(value.modelBinding) ||
|
|
614
|
+
value.modelBinding.provider !== "google_interactions")) ||
|
|
615
|
+
(value.googleIdentity !== undefined &&
|
|
616
|
+
(!isRecord(value.googleIdentity) ||
|
|
617
|
+
!hasExactKeys(value.googleIdentity, [
|
|
618
|
+
"sessionId",
|
|
619
|
+
"packageId",
|
|
620
|
+
"creationRequestId",
|
|
621
|
+
]) ||
|
|
622
|
+
typeof value.googleIdentity.sessionId !== "string" ||
|
|
623
|
+
typeof value.googleIdentity.packageId !== "string" ||
|
|
624
|
+
typeof value.googleIdentity.creationRequestId !== "string" ||
|
|
625
|
+
!isRecord(value.modelBinding) ||
|
|
626
|
+
value.modelBinding.provider !== "google_interactions")) ||
|
|
395
627
|
!validAuthorization(value.authorization) ||
|
|
396
628
|
!validPendingSettlement(value.pendingSettlement) ||
|
|
397
629
|
!validPendingSettlementForTranscript(value.pendingSettlement, value.modelItems, value.runStatus) ||
|
|
398
630
|
!validLegacyDraft(value.legacyDraft))
|
|
399
631
|
throw new Error("Setting-improvement conversation does not match its durable schema");
|
|
400
632
|
assertSessionId(value.sessionId);
|
|
633
|
+
if (isRecord(value.googleIdentity) &&
|
|
634
|
+
(value.googleIdentity.sessionId !== value.sessionId ||
|
|
635
|
+
value.googleIdentity.packageId !== value.packageId ||
|
|
636
|
+
value.googleIdentity.creationRequestId !== value.creationRequestId))
|
|
637
|
+
throw new Error("Cannot safely identify the historical conversation; its owner or creation identity is inconsistent");
|
|
401
638
|
if (!value.completedRequestIds.every((id) => typeof id === "string") ||
|
|
402
639
|
!value.messages.every(validMessage) ||
|
|
403
640
|
!value.modelItems.every(validModelItem))
|
|
404
641
|
throw new Error("Setting-improvement transcript is damaged");
|
|
405
642
|
return structuredClone(value);
|
|
406
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
|
+
}
|
|
407
707
|
function migrateLegacySession(value, applyOutcome) {
|
|
408
708
|
if (!validLegacySession(value))
|
|
409
709
|
throw new Error("Legacy setting-improvement conversation is damaged");
|
|
@@ -823,6 +1123,9 @@ function validModelToolCall(value) {
|
|
|
823
1123
|
function validProviderState(value) {
|
|
824
1124
|
if (!isRecord(value))
|
|
825
1125
|
return false;
|
|
1126
|
+
if (value.protocol === "google_interactions")
|
|
1127
|
+
return (hasExactKeys(value, ["protocol", "steps"]) &&
|
|
1128
|
+
validGoogleInteractionsSteps(value.steps));
|
|
826
1129
|
if (value.protocol === "chat_completions")
|
|
827
1130
|
return (hasExactKeys(value, ["protocol", "assistantMessage"]) &&
|
|
828
1131
|
Object.hasOwn(value, "assistantMessage"));
|
|
@@ -850,7 +1153,10 @@ function validModelUsage(value) {
|
|
|
850
1153
|
"totalTokens",
|
|
851
1154
|
];
|
|
852
1155
|
if (!isRecord(value) ||
|
|
853
|
-
!hasExactKeys(value, [...fields, "provenance"]) ||
|
|
1156
|
+
!hasExactKeys(value, [...fields, "provenance"], ["providerRaw", "accounting"]) ||
|
|
1157
|
+
(value.providerRaw !== undefined && !isRecord(value.providerRaw)) ||
|
|
1158
|
+
(value.accounting !== undefined &&
|
|
1159
|
+
value.accounting !== "google_interactions") ||
|
|
854
1160
|
!fields.every((field) => validNullableCount(value[field])) ||
|
|
855
1161
|
!isRecord(value.provenance) ||
|
|
856
1162
|
!hasExactKeys(value.provenance, fields))
|