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.
Files changed (49) hide show
  1. package/dist/node/protocol/modelConnections.js +15 -0
  2. package/dist/node/protocol/modelUsage.js +4 -0
  3. package/dist/node/protocol/sillyTavernContent.js +1 -0
  4. package/dist/node/protocol/v1.js +97 -0
  5. package/dist/node/runtime/V1Runtime.js +105 -3
  6. package/dist/node/runtime/authoring/AuthoringEditPlan.js +79 -0
  7. package/dist/node/runtime/authoring/AuthoringRequestSnapshot.js +15 -3
  8. package/dist/node/runtime/authoring/AuthoringRetry.js +59 -0
  9. package/dist/node/runtime/authoring/DamagedGoogleContinuation.js +68 -0
  10. package/dist/node/runtime/authoring/ModelToolConversation.js +66 -25
  11. package/dist/node/runtime/content/ContentWorkspace.js +10 -2
  12. package/dist/node/runtime/content/CurrentTreeContentLibrary.js +14 -8
  13. package/dist/node/runtime/content/SillyTavernContent.js +796 -0
  14. package/dist/node/runtime/model/AiFailureLog.js +8 -1
  15. package/dist/node/runtime/model/DispatchFailure.js +8 -0
  16. package/dist/node/runtime/model/FileNativeModelAdapters.js +627 -18
  17. package/dist/node/runtime/model/GoogleHopJournal.js +90 -0
  18. package/dist/node/runtime/model/GoogleInteractionsStream.js +242 -0
  19. package/dist/node/runtime/model/GoogleRequestPreview.js +184 -0
  20. package/dist/node/runtime/model/ModelConnectionStore.js +4 -1
  21. package/dist/node/runtime/model/ProviderExchangeState.js +44 -1
  22. package/dist/node/runtime/model/ProviderStream.js +4 -2
  23. package/dist/node/runtime/play/FileNativePlayAdvanceStore.js +4 -4
  24. package/dist/node/runtime/play/FileNativePlayPresetStore.js +150 -78
  25. package/dist/node/runtime/play/FileNativePlayTimelineStore.js +199 -10
  26. package/dist/node/runtime/play/OrderedPlayPrompts.js +139 -178
  27. package/dist/node/runtime/play/PlayAdvanceLifecycle.js +51 -23
  28. package/dist/node/runtime/play/PlayCallChain.js +92 -8
  29. package/dist/node/runtime/play/PlayFollowupRequests.js +23 -3
  30. package/dist/node/runtime/prompt/PromptCompilationCodec.js +2 -1
  31. package/dist/node/runtime/setting/FileNativeSettingImprovementStore.js +321 -15
  32. package/dist/node/runtime/setting/SettingImprovementSession.js +285 -21
  33. package/dist/node/runtime/world-revision/FileNativeWorldRevisionStore.js +236 -15
  34. package/dist/node/runtime/world-revision/WorldRevisionSession.js +324 -50
  35. package/dist/node/runtime/world-revision/WorldRevisionWorkspace.js +47 -1
  36. package/dist/node/shared/application-preset-files.js +29 -0
  37. package/dist/node/shared/default-preset-host.js +50 -38
  38. package/dist/node/shared/first-party-action-choices.js +7 -11
  39. package/dist/node/shared/first-party-generic-panels.js +7 -11
  40. package/dist/node/shared/first-party-player-view.js +7 -11
  41. package/dist/node/shared/ordered-author-prompts.js +6 -4
  42. package/dist/node/shared/ordered-play-prompts.js +4 -2
  43. package/dist/node/shared/prompt-arrangement.js +205 -0
  44. package/dist/node/shared/sillytavern-preset.js +454 -0
  45. package/dist/web/assets/index-BthiDIQu.js +1432 -0
  46. package/dist/web/assets/{index-DH2BVsYd.css → index-wO9Y4HT1.css} +1 -1
  47. package/dist/web/index.html +2 -2
  48. package/package.json +1 -1
  49. package/dist/web/assets/index-B2kbaOGe.js +0 -1408
@@ -1,8 +1,29 @@
1
+ import { prepareAuthoringDispatch, failAuthoringDispatch, } from "./AuthoringRetry.js";
1
2
  import { randomUUID } from "node:crypto";
2
3
  import { aggregateModelUsage, } from "../../protocol/modelUsage.js";
3
4
  import { ModelHostCancelledError } from "../model/ModelHost.js";
5
+ import { withGoogleHop } from "../model/GoogleRequestPreview.js";
4
6
  const maximumExchangesPerMessage = 64;
5
7
  const maximumStreamTailCharacters = 240;
8
+ export function authoringExchange(session, requestId, boundaries) {
9
+ const bootstrap = session.requests?.at(-1)?.bootstrap ?? session.bootstrap;
10
+ return {
11
+ bootstrap,
12
+ toolUniverse: bootstrap.toolUniverse,
13
+ allowedTools: bootstrap.toolUniverse.map(({ name }) => name),
14
+ toolStrategy: bootstrap.toolStrategy,
15
+ tools: bootstrap.tools,
16
+ appended: session.modelItems,
17
+ ...(boundaries === undefined
18
+ ? {}
19
+ : { worldRevisionBoundaries: boundaries }),
20
+ requestId,
21
+ operationId: session.sessionId,
22
+ requestAttempt: 1,
23
+ exchange: session.exchange + 1,
24
+ maxOutputTokens: session.modelBinding.maxOutputTokens,
25
+ };
26
+ }
6
27
  /**
7
28
  * Deep Module for the shared authoring-conversation loop. Its Interface leaves
8
29
  * content-package and world-revision publication behind the settlement seam.
@@ -56,41 +77,51 @@ export class ModelToolConversation {
56
77
  }
57
78
  async #execute(input, live) {
58
79
  const { session } = input;
59
- const bootstrap = session.requests?.at(-1)?.bootstrap ?? session.bootstrap;
80
+ let phase = "settling";
60
81
  try {
61
82
  for (let round = 0; round < maximumExchangesPerMessage; round += 1) {
62
- session.exchange += 1;
83
+ let request = authoringExchange(session, input.exchangeRequestId ?? live.requestId, input.worldRevisionBoundaries);
84
+ if (input.retrySupported)
85
+ request = prepareAuthoringDispatch(session, request);
86
+ session.exchange = Math.max(session.exchange, request.exchange ?? session.exchange + 1);
87
+ if (session.modelBinding.provider === "google_interactions")
88
+ session.googleOutcomeUnknown = true;
63
89
  live.streaming = emptyStreaming();
64
90
  session.updatedAt = Date.now();
91
+ phase = "preparing";
65
92
  await input.save(session);
66
93
  input.changed?.(true);
67
- const response = await input.host.exchange({
68
- bootstrap,
69
- toolUniverse: bootstrap.toolUniverse,
70
- allowedTools: bootstrap.toolUniverse.map(({ name }) => name),
71
- toolStrategy: bootstrap.toolStrategy,
72
- tools: bootstrap.tools,
73
- appended: session.modelItems,
74
- requestId: live.requestId,
75
- operationId: session.sessionId,
76
- requestAttempt: 1,
77
- exchange: session.exchange,
78
- maxOutputTokens: session.modelBinding.maxOutputTokens,
79
- }, {
94
+ phase = "dispatched";
95
+ const response = await input.host.exchange(request, {
80
96
  signal: live.controller.signal,
97
+ onGoogleHop: (hop) => {
98
+ const requestId = input.exchangeRequestId ?? live.requestId;
99
+ const snapshot = session.requests?.findLast((entry) => entry.requestId === requestId);
100
+ if (snapshot?.wireRequest === undefined)
101
+ return;
102
+ snapshot.wireRequest = withGoogleHop(snapshot.wireRequest, hop);
103
+ return input.recordGoogleHop?.(requestId, hop);
104
+ },
81
105
  onDelta: (delta) => {
82
106
  recordDelta(live, delta);
83
107
  input.changed?.(false);
84
108
  },
85
109
  });
110
+ if (live.controller.signal.aborted)
111
+ throw new ModelHostCancelledError("The authoring request was cancelled");
112
+ phase = "validating";
86
113
  assertCompleteResponse(response);
87
114
  const toolCalls = response.toolCalls ?? [];
88
115
  assertUniqueToolCallIds(toolCalls);
89
116
  const visibleText = response.text ?? "";
90
- if (toolCalls.length === 0 && visibleText.trim().length === 0)
117
+ session.usage = aggregateModelUsage(session.usage, response.usage);
118
+ if (toolCalls.length === 0 && visibleText.trim().length === 0) {
119
+ phase = "dispatched";
91
120
  throw new Error(input.failure?.emptyResponse ??
92
121
  "The model returned an empty tool-free authoring response");
93
- session.usage = aggregateModelUsage(session.usage, response.usage);
122
+ }
123
+ delete session.retryDispatch;
124
+ phase = "settling";
94
125
  session.modelItems.push({
95
126
  kind: "assistant",
96
127
  text: visibleText,
@@ -100,6 +131,8 @@ export class ModelToolConversation {
100
131
  providerState: response.providerState,
101
132
  toolCalls: structuredClone(toolCalls),
102
133
  });
134
+ if (session.modelBinding.provider === "google_interactions")
135
+ session.googleOutcomeUnknown = false;
103
136
  session.updatedAt = Date.now();
104
137
  if (toolCalls.length === 0) {
105
138
  session.messages.push({
@@ -121,6 +154,7 @@ export class ModelToolConversation {
121
154
  }
122
155
  // The exchange budget only bounds a runaway loop. Every tool response is
123
156
  // already settled, so the conversation pauses and can resume unchanged.
157
+ delete session.retryDispatch;
124
158
  session.runStatus = "paused";
125
159
  session.activeRequestId = null;
126
160
  session.completedRequestIds = rememberRequest(session.completedRequestIds, live.requestId);
@@ -130,17 +164,20 @@ export class ModelToolConversation {
130
164
  return input.view(session);
131
165
  }
132
166
  catch (error) {
167
+ failAuthoringDispatch(session, error, phase);
133
168
  session.runStatus = "interrupted";
134
169
  session.activeRequestId = null;
135
170
  session.completedRequestIds = rememberRequest(session.completedRequestIds, live.requestId);
136
171
  session.lastFailure =
137
- error instanceof ModelHostCancelledError
138
- ? (input.failure?.cancelled ??
139
- "The authoring response was cancelled. Complete tool responses already settled were retained.")
140
- : error instanceof Error
141
- ? error.message
142
- : (input.failure?.interrupted ??
143
- "The authoring response was interrupted");
172
+ session.googleOutcomeUnknown === true
173
+ ? "The Google request outcome is unknown; start a fresh model context before sending again."
174
+ : error instanceof ModelHostCancelledError
175
+ ? (input.failure?.cancelled ??
176
+ "The authoring response was cancelled. Complete tool responses already settled were retained.")
177
+ : error instanceof Error
178
+ ? error.message
179
+ : (input.failure?.interrupted ??
180
+ "The authoring response was interrupted");
144
181
  session.updatedAt = Date.now();
145
182
  await input.save(session);
146
183
  return input.view(session);
@@ -170,7 +207,11 @@ export function appendAuthoringUserMessage(session, message, requestId) {
170
207
  export function resumePausedAuthoringConversation(session) {
171
208
  if (session.runStatus !== "paused")
172
209
  throw new Error("Only a paused authoring conversation can be continued");
173
- if (session.modelItems.at(-1)?.kind !== "tool")
210
+ const last = session.modelItems.at(-1);
211
+ if (last?.kind !== "tool" &&
212
+ !(session.modelBinding.provider === "google_interactions" &&
213
+ session.googleOutcomeUnknown === false &&
214
+ last?.kind === "user"))
174
215
  throw new Error("The paused authoring conversation does not end with settled tool results");
175
216
  session.runStatus = "running";
176
217
  session.activeRequestId = session.requests?.at(-1)?.requestId ?? null;
@@ -1,5 +1,6 @@
1
1
  import { isAbsolute, win32 } from "node:path";
2
2
  import { inflateRawSync } from "node:zlib";
3
+ import { FileNativeSettingImprovementStore } from "../setting/FileNativeSettingImprovementStore.js";
3
4
  import { portableContentTreePathKey } from "../../protocol/contentTree.js";
4
5
  import { fictionGameplay, gameplayFile, parseGameplay, } from "../../protocol/gameplay.js";
5
6
  import { defaultAppLocale, } from "../../protocol/appPreferences.js";
@@ -36,6 +37,10 @@ export class ContentWorkspace {
36
37
  limits: this.#limits,
37
38
  ...options.currentTree,
38
39
  });
40
+ const recovery = new FileNativeSettingImprovementStore(dataRoot, {
41
+ content: this,
42
+ });
43
+ this.#currentTreeLibrary.setBeforeLease((id, lease) => recovery.recoverPendingEdits(id, lease));
39
44
  }
40
45
  createCurrentTreeContentPackage(gameplay = fictionGameplay) {
41
46
  const locale = this.#locale();
@@ -64,8 +69,8 @@ export class ContentWorkspace {
64
69
  readCurrentTreeContentPackage(localId) {
65
70
  return this.#currentTreeLibrary.readPackage(localId);
66
71
  }
67
- beginCurrentTreeContentPackageOperation(localId) {
68
- return this.#currentTreeLibrary.acquireOperationLease(localId);
72
+ beginCurrentTreeContentPackageOperation(localId, options = {}) {
73
+ return this.#currentTreeLibrary.acquireOperationLease(localId, options);
69
74
  }
70
75
  inspectCurrentTreeContentPackage(files) {
71
76
  return inspectContentPackageCurrentTree(files);
@@ -96,6 +101,9 @@ export class ContentWorkspace {
96
101
  }, options.signal === undefined ? {} : { signal: options.signal });
97
102
  });
98
103
  }
104
+ importSillyTavernContent(title, files) {
105
+ return this.#mutate(() => this.#currentTreeLibrary.importPackage({ title, files }));
106
+ }
99
107
  copyCurrentTreeContentPackage(localId, options = {}) {
100
108
  return this.#mutate(() => this.#currentTreeLibrary.copyPackage(localId, options.signal === undefined ? {} : { signal: options.signal }));
101
109
  }
@@ -61,6 +61,10 @@ export class CurrentTreeContentLibrary {
61
61
  #beforeCurrentTreeReplace;
62
62
  #mutationTail = Promise.resolve();
63
63
  #legacyLocksRemoval;
64
+ #beforeLease;
65
+ setBeforeLease(hook) {
66
+ this.#beforeLease = hook;
67
+ }
64
68
  constructor(dataRoot, options = {}) {
65
69
  this.#legacyLocksRoot = join(dataRoot, "content", ".package-locks");
66
70
  this.#packagesRoot = join(dataRoot, "content", "packages");
@@ -162,9 +166,6 @@ export class CurrentTreeContentLibrary {
162
166
  }
163
167
  }
164
168
  async readPackage(localId) {
165
- const snapshot = await this.#readPackageSnapshot(localId);
166
- if (snapshot.metadata.schemaVersion === 2)
167
- return snapshot.package;
168
169
  const lease = await this.acquireOperationLease(localId, { wait: true });
169
170
  try {
170
171
  await ensureStoredPackageTitle(this.#packageRoot(localId), localId, lease.package.title);
@@ -221,7 +222,7 @@ export class CurrentTreeContentLibrary {
221
222
  localId,
222
223
  title: normalizedTitle,
223
224
  });
224
- return packageSummary(await this.readPackage(localId));
225
+ return packageSummary({ ...lease.package, title: normalizedTitle });
225
226
  }
226
227
  finally {
227
228
  lease.release();
@@ -283,19 +284,24 @@ export class CurrentTreeContentLibrary {
283
284
  releaseInProcessPackageLease(packageRoot);
284
285
  };
285
286
  try {
286
- const package_ = (await this.#readPackageSnapshot(localId)).package;
287
- return {
288
- package: structuredClone(package_),
287
+ let package_ = (await this.#readPackageSnapshot(localId)).package;
288
+ const lease = {
289
+ get package() {
290
+ return structuredClone(package_);
291
+ },
289
292
  replace: async (files, options = {}) => {
290
293
  if (released)
291
294
  throw new CurrentTreeContentPackageBusyError();
292
295
  options.signal?.throwIfAborted();
293
296
  const normalized = normalizeCurrentContentTreeFiles(files, this.#limits);
294
297
  assertGameplayModeUnchanged(package_.files, normalized);
295
- return this.#replaceNormalizedCurrentTree(localId, package_.title, normalized, options.signal);
298
+ package_ = await this.#replaceNormalizedCurrentTree(localId, package_.title, normalized, options.signal);
299
+ return structuredClone(package_);
296
300
  },
297
301
  release,
298
302
  };
303
+ await this.#beforeLease?.(localId, lease);
304
+ return lease;
299
305
  }
300
306
  catch (error) {
301
307
  release();