@vinaverse/client 0.1.0-alpha.10 → 0.1.0-alpha.12

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 (51) hide show
  1. package/README.md +22 -4
  2. package/dist/agent/index.cjs +43 -9
  3. package/dist/agent/index.cjs.map +1 -1
  4. package/dist/agent/index.d.cts +3 -3
  5. package/dist/agent/index.d.ts +3 -3
  6. package/dist/agent/index.js +1 -1
  7. package/dist/backend/index.cjs +37 -10
  8. package/dist/backend/index.cjs.map +1 -1
  9. package/dist/backend/index.d.cts +13 -3
  10. package/dist/backend/index.d.ts +13 -3
  11. package/dist/backend/index.js +38 -11
  12. package/dist/backend/index.js.map +1 -1
  13. package/dist/{chunk-O3NLAZGZ.js → chunk-5THD6XC7.js} +45 -11
  14. package/dist/chunk-5THD6XC7.js.map +1 -0
  15. package/dist/chunk-D56MNG3E.js +9 -0
  16. package/dist/{chunk-YNUHXJD6.js.map → chunk-D56MNG3E.js.map} +1 -1
  17. package/dist/contract-Be2oVHNr.d.cts +6 -0
  18. package/dist/contract-Be2oVHNr.d.ts +6 -0
  19. package/dist/conversation/index.cjs +2 -2
  20. package/dist/conversation/index.cjs.map +1 -1
  21. package/dist/conversation/index.d.cts +5 -5
  22. package/dist/conversation/index.d.ts +5 -5
  23. package/dist/conversation/index.js +1 -1
  24. package/dist/history/index.cjs +282 -0
  25. package/dist/history/index.cjs.map +1 -0
  26. package/dist/history/index.d.cts +91 -0
  27. package/dist/history/index.d.ts +91 -0
  28. package/dist/history/index.js +261 -0
  29. package/dist/history/index.js.map +1 -0
  30. package/dist/index.cjs +2 -2
  31. package/dist/index.cjs.map +1 -1
  32. package/dist/index.d.cts +2 -2
  33. package/dist/index.d.ts +2 -2
  34. package/dist/index.js +1 -1
  35. package/dist/{runtime-Z1RI10lU.d.ts → runtime-BR2lS_Vg.d.cts} +26 -3
  36. package/dist/{runtime-CISPp9nJ.d.cts → runtime-CZyVCueC.d.ts} +26 -3
  37. package/dist/{session-CPz4pcPu.d.cts → session-D0ijOQX3.d.cts} +98 -5
  38. package/dist/{session-CPz4pcPu.d.ts → session-D0ijOQX3.d.ts} +98 -5
  39. package/dist/{store-DZka9T2-.d.ts → store-CGRSxVcL.d.ts} +1 -1
  40. package/dist/{store-BxiicOKG.d.cts → store-CxoHwd64.d.cts} +1 -1
  41. package/dist/testing/index.cjs +158 -14
  42. package/dist/testing/index.cjs.map +1 -1
  43. package/dist/testing/index.d.cts +42 -5
  44. package/dist/testing/index.d.ts +42 -5
  45. package/dist/testing/index.js +159 -15
  46. package/dist/testing/index.js.map +1 -1
  47. package/package.json +24 -1
  48. package/dist/chunk-O3NLAZGZ.js.map +0 -1
  49. package/dist/chunk-YNUHXJD6.js +0 -9
  50. package/dist/contract-DmQs8amJ.d.cts +0 -6
  51. package/dist/contract-DmQs8amJ.d.ts +0 -6
@@ -0,0 +1,91 @@
1
+ import { h as AgentFailureReason, j as AgentKey, o as AgentRuntime, A as AgentBackend } from '../runtime-CZyVCueC.js';
2
+ import { T as TimelineItem } from '../store-CGRSxVcL.js';
3
+ import { S as Schemas } from '../session-D0ijOQX3.js';
4
+
5
+ type ConversationSummary = Schemas['ConversationSummary'];
6
+ type HistoryItem = Schemas['TimelineItem'];
7
+ /**
8
+ * The key a past conversation continues under for this person: its side and business as it was bound when it started
9
+ * (`experience_mode`, `business_scope_id`), the `thread` given (default `chat`). For `runtime.continue(key, id)` and
10
+ * `deleteConversation(key, id)`, so the device forgets it only where that key still continues it.
11
+ */
12
+ declare function continueKey(conversation: ConversationSummary, subject: string | null, thread?: string): AgentKey;
13
+ /**
14
+ * A past conversation's items as the kits draw them: messages as sent (an `assistant` reply is the agent's, cut-off
15
+ * replies and refusals marked), cards restored (drawn as they were, taking no answer or action), a proposal with the
16
+ * outcome it had. Keyed by the server's ids, so pages merge without doubles.
17
+ */
18
+ declare function historyItems(data: readonly HistoryItem[]): TimelineItem[];
19
+ interface AgentHistoryState {
20
+ readonly conversations: readonly ConversationSummary[];
21
+ /** A page is being read. */
22
+ readonly loading: boolean;
23
+ /** Why the last page could not be read; null when it was. `off` when the backend serves no history. */
24
+ readonly failure: AgentFailureReason | null;
25
+ /** Every page is read. */
26
+ readonly done: boolean;
27
+ /** The first page was asked for. */
28
+ readonly started: boolean;
29
+ }
30
+ interface AgentHistory {
31
+ getSnapshot(): AgentHistoryState;
32
+ subscribe(listener: () => void): () => void;
33
+ /** Reads the first page anew: the first time, after a failure, or to see what changed. */
34
+ refresh(): void;
35
+ /** The next older page, when there is one and none is being read. */
36
+ loadMore(): void;
37
+ /** Reads again the page that failed: the first (as `refresh`), or the next. */
38
+ retry(): void;
39
+ /**
40
+ * Deletes a past conversation (the runtime's `deleteConversation` for the key that holds it, so the device forgets
41
+ * it only where the key still continues it) and drops its row. One already gone (`not_found`) is dropped too.
42
+ */
43
+ remove(key: AgentKey, conversationId: string): Promise<void>;
44
+ }
45
+ interface AgentHistoryOptions {
46
+ readonly runtime: AgentRuntime;
47
+ /** Default: the runtime's. */
48
+ readonly backend?: AgentBackend;
49
+ /** The app's side: `customer` or `business`. */
50
+ readonly mode: string;
51
+ /** The business acted for, with `business`. */
52
+ readonly scope?: string | null;
53
+ /** The agent, for a backend that serves more than one. */
54
+ readonly agent?: string | null;
55
+ /** Conversations per page (1–50). Default 20. */
56
+ readonly pageSize?: number;
57
+ }
58
+ /** A person's past conversations, newest activity first. */
59
+ declare function createAgentHistory(options: AgentHistoryOptions): AgentHistory;
60
+ interface AgentTranscriptState {
61
+ /** The conversation's items, oldest first, as the kits draw them (`historyItems`). */
62
+ readonly items: readonly TimelineItem[];
63
+ readonly loading: boolean;
64
+ /** Why the last page could not be read: `not_found` (deleted, or never theirs), `expired`, … */
65
+ readonly failure: AgentFailureReason | null;
66
+ /** Nothing earlier to read. */
67
+ readonly done: boolean;
68
+ readonly started: boolean;
69
+ }
70
+ interface AgentTranscript {
71
+ getSnapshot(): AgentTranscriptState;
72
+ subscribe(listener: () => void): () => void;
73
+ /** Reads the newest page anew. */
74
+ load(): void;
75
+ /** The page before the earliest read, prepended. */
76
+ loadEarlier(): void;
77
+ /** Reads again the page that failed: the newest (as `load`), or the earlier one. */
78
+ retry(): void;
79
+ }
80
+ interface AgentTranscriptOptions {
81
+ readonly runtime: AgentRuntime;
82
+ /** Default: the runtime's. */
83
+ readonly backend?: AgentBackend;
84
+ readonly conversationId: string;
85
+ /** Items per page. Default 30. */
86
+ readonly pageSize?: number;
87
+ }
88
+ /** A past conversation's transcript, read-only: newest page first, earlier pages prepended. */
89
+ declare function createAgentTranscript(options: AgentTranscriptOptions): AgentTranscript;
90
+
91
+ export { type AgentHistory, type AgentHistoryOptions, type AgentHistoryState, type AgentTranscript, type AgentTranscriptOptions, type AgentTranscriptState, type ConversationSummary, continueKey, createAgentHistory, createAgentTranscript, historyItems };
@@ -0,0 +1,261 @@
1
+ import { isRefusal } from '../chunk-5THD6XC7.js';
2
+ import '../chunk-46D3FSLU.js';
3
+ import '../chunk-GWLLUOFY.js';
4
+
5
+ // src/history/index.ts
6
+ function continueKey(conversation, subject, thread) {
7
+ return {
8
+ subject,
9
+ mode: conversation.experience_mode,
10
+ scope: conversation.business_scope_id ?? null,
11
+ ...thread ? { thread } : {}
12
+ };
13
+ }
14
+ function historyItems(data) {
15
+ return data.map((item, seq) => {
16
+ if (item.type === "card") {
17
+ const outcome = item.proposal_outcome;
18
+ const card = {
19
+ type: "card",
20
+ key: item.card.id,
21
+ busy: false,
22
+ restored: true,
23
+ card: {
24
+ id: item.card.id,
25
+ kind: item.card.kind,
26
+ card: item.card,
27
+ locked: [],
28
+ turnId: item.turn_id,
29
+ outcome: outcome && outcome !== "pending" ? outcome : null,
30
+ seq
31
+ }
32
+ };
33
+ return card;
34
+ }
35
+ const message = {
36
+ type: "message",
37
+ key: `h:${item.id}`,
38
+ role: item.role === "assistant" ? "agent" : item.role,
39
+ text: item.text,
40
+ state: "sent",
41
+ ...item.interrupted ? { partial: "interrupted" } : {},
42
+ ...item.refusal ? { refusal: true } : {}
43
+ };
44
+ return message;
45
+ });
46
+ }
47
+ function reasonOf(error) {
48
+ if (isRefusal(error)) return error.reason;
49
+ const given = error?.reason;
50
+ return typeof given === "string" ? given : "unknown";
51
+ }
52
+ function staleCursor(error) {
53
+ const e = error;
54
+ return e?.outcome === "unavailable" && e.gatewayCode === "invalid_request" && e.retryable === false;
55
+ }
56
+ function paged(runtime, empty) {
57
+ let state = empty;
58
+ let epoch = runtime.epoch();
59
+ let asked = 0;
60
+ const listeners = /* @__PURE__ */ new Set();
61
+ const set = (next) => {
62
+ state = { ...state, ...next };
63
+ for (const l of [...listeners]) l();
64
+ };
65
+ const fresh = () => {
66
+ if (runtime.epoch() === epoch) return;
67
+ epoch = runtime.epoch();
68
+ asked++;
69
+ state = empty;
70
+ };
71
+ let off = null;
72
+ return {
73
+ get: () => {
74
+ fresh();
75
+ return state;
76
+ },
77
+ set,
78
+ subscribe(listener) {
79
+ listeners.add(listener);
80
+ off ??= runtime.subscribe(() => {
81
+ const before = state;
82
+ fresh();
83
+ if (state !== before) for (const l of [...listeners]) l();
84
+ });
85
+ return () => {
86
+ listeners.delete(listener);
87
+ if (listeners.size === 0) {
88
+ off?.();
89
+ off = null;
90
+ }
91
+ };
92
+ },
93
+ /** Reads a page; `apply` lands only for the newest read, under the same person. */
94
+ read(call, apply, failed) {
95
+ fresh();
96
+ const mine = ++asked;
97
+ const under = epoch;
98
+ set({ loading: true });
99
+ call().then(
100
+ (value) => {
101
+ if (mine === asked && under === runtime.epoch()) apply(value);
102
+ },
103
+ (error) => {
104
+ if (mine === asked && under === runtime.epoch()) failed(error);
105
+ }
106
+ );
107
+ }
108
+ };
109
+ }
110
+ var NO_HISTORY = {
111
+ conversations: [],
112
+ loading: false,
113
+ failure: null,
114
+ done: false,
115
+ started: false
116
+ };
117
+ function createAgentHistory(options) {
118
+ const { runtime } = options;
119
+ const backend = options.backend ?? runtime.backend;
120
+ const store = paged(runtime, NO_HISTORY);
121
+ let cursor = null;
122
+ let restarted = false;
123
+ const page = (from) => {
124
+ const list = backend.listConversations?.bind(backend);
125
+ if (!list) {
126
+ store.set({ started: true, failure: "off", done: true });
127
+ return;
128
+ }
129
+ store.read(
130
+ () => list({
131
+ mode: options.mode,
132
+ scope: options.scope ?? null,
133
+ agent: options.agent ?? null,
134
+ limit: options.pageSize ?? 20,
135
+ cursor: from
136
+ }),
137
+ (got) => {
138
+ const known = new Set(
139
+ from ? store.get().conversations.map((c) => c.id) : []
140
+ );
141
+ cursor = got.next_cursor;
142
+ store.set({
143
+ conversations: [
144
+ ...from ? store.get().conversations : [],
145
+ ...got.data.filter((c) => !known.has(c.id))
146
+ ],
147
+ loading: false,
148
+ failure: null,
149
+ done: cursor === null
150
+ });
151
+ },
152
+ (error) => {
153
+ if (from !== null && staleCursor(error) && !restarted) {
154
+ restarted = true;
155
+ cursor = null;
156
+ page(null);
157
+ return;
158
+ }
159
+ store.set({ loading: false, failure: reasonOf(error) });
160
+ }
161
+ );
162
+ };
163
+ const refresh = () => {
164
+ cursor = null;
165
+ restarted = false;
166
+ store.set({ started: true });
167
+ page(null);
168
+ };
169
+ const loadMore = () => {
170
+ const s = store.get();
171
+ if (!s.started || s.loading || s.done || cursor === null) return;
172
+ page(cursor);
173
+ };
174
+ return {
175
+ getSnapshot: store.get,
176
+ subscribe: store.subscribe,
177
+ refresh,
178
+ loadMore,
179
+ retry: () => cursor === null ? refresh() : loadMore(),
180
+ async remove(key, conversationId) {
181
+ try {
182
+ await runtime.deleteConversation(key, conversationId);
183
+ } catch (error) {
184
+ if (!(isRefusal(error) && error.reason === "not_found")) throw error;
185
+ }
186
+ store.set({
187
+ conversations: store.get().conversations.filter((c) => c.id !== conversationId)
188
+ });
189
+ }
190
+ };
191
+ }
192
+ var NO_TRANSCRIPT = {
193
+ items: [],
194
+ loading: false,
195
+ failure: null,
196
+ done: false,
197
+ started: false
198
+ };
199
+ function createAgentTranscript(options) {
200
+ const { runtime } = options;
201
+ const backend = options.backend ?? runtime.backend;
202
+ const store = paged(runtime, NO_TRANSCRIPT);
203
+ let cursor = null;
204
+ let restarted = false;
205
+ const page = (from) => {
206
+ const read = backend.readConversation?.bind(backend);
207
+ if (!read) {
208
+ store.set({ started: true, failure: "off", done: true });
209
+ return;
210
+ }
211
+ store.read(
212
+ () => read(options.conversationId, {
213
+ limit: options.pageSize ?? 30,
214
+ cursor: from
215
+ }),
216
+ (got) => {
217
+ const earlier = historyItems(got.data);
218
+ const held = from ? store.get().items : [];
219
+ const keys = new Set(held.map((i) => i.key));
220
+ cursor = got.next_cursor;
221
+ store.set({
222
+ items: [...earlier.filter((i) => !keys.has(i.key)), ...held],
223
+ loading: false,
224
+ failure: null,
225
+ done: cursor === null
226
+ });
227
+ },
228
+ (error) => {
229
+ if (from !== null && staleCursor(error) && !restarted) {
230
+ restarted = true;
231
+ cursor = null;
232
+ page(null);
233
+ return;
234
+ }
235
+ store.set({ loading: false, failure: reasonOf(error) });
236
+ }
237
+ );
238
+ };
239
+ const load = () => {
240
+ cursor = null;
241
+ restarted = false;
242
+ store.set({ started: true });
243
+ page(null);
244
+ };
245
+ const loadEarlier = () => {
246
+ const s = store.get();
247
+ if (!s.started || s.loading || s.done || cursor === null) return;
248
+ page(cursor);
249
+ };
250
+ return {
251
+ getSnapshot: store.get,
252
+ subscribe: store.subscribe,
253
+ load,
254
+ loadEarlier,
255
+ retry: () => cursor === null ? load() : loadEarlier()
256
+ };
257
+ }
258
+
259
+ export { continueKey, createAgentHistory, createAgentTranscript, historyItems };
260
+ //# sourceMappingURL=index.js.map
261
+ //# sourceMappingURL=index.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["../../src/history/index.ts"],"names":[],"mappings":"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","file":"index.js","sourcesContent":["// @vinaverse/client/history: a person's past conversations with the agent (C2), through the tenant's backend\n// (`AgentBackend.listConversations`, `readConversation`): the list, newest activity first, a page at a time; a past\n// conversation's transcript, newest page first, earlier pages prepended; delete through the runtime. No React: the\n// kits bind to it. Nothing begun before a sign-out lands after it (the runtime's `epoch`).\nimport {\n type AgentBackend,\n type AgentFailureReason,\n type AgentKey,\n type AgentRuntime,\n isRefusal,\n} from '../agent/runtime.js';\nimport type {\n CardItem,\n MessageItem,\n TimelineItem,\n} from '../conversation/view.js';\nimport type { Schemas } from '../events.js';\n\nexport type ConversationSummary = Schemas['ConversationSummary'];\ntype HistoryItem = Schemas['TimelineItem'];\n\n/**\n * The key a past conversation continues under for this person: its side and business as it was bound when it started\n * (`experience_mode`, `business_scope_id`), the `thread` given (default `chat`). For `runtime.continue(key, id)` and\n * `deleteConversation(key, id)`, so the device forgets it only where that key still continues it.\n */\nexport function continueKey(\n conversation: ConversationSummary,\n subject: string | null,\n thread?: string,\n): AgentKey {\n return {\n subject,\n mode: conversation.experience_mode,\n scope: conversation.business_scope_id ?? null,\n ...(thread ? { thread } : {}),\n };\n}\n\n/**\n * A past conversation's items as the kits draw them: messages as sent (an `assistant` reply is the agent's, cut-off\n * replies and refusals marked), cards restored (drawn as they were, taking no answer or action), a proposal with the\n * outcome it had. Keyed by the server's ids, so pages merge without doubles.\n */\nexport function historyItems(data: readonly HistoryItem[]): TimelineItem[] {\n return data.map((item, seq): TimelineItem => {\n if (item.type === 'card') {\n const outcome = item.proposal_outcome;\n const card: CardItem = {\n type: 'card',\n key: item.card.id,\n busy: false,\n restored: true,\n card: {\n id: item.card.id,\n kind: item.card.kind,\n card: item.card as unknown as Readonly<Record<string, unknown>>,\n locked: [],\n turnId: item.turn_id,\n outcome: outcome && outcome !== 'pending' ? outcome : null,\n seq,\n },\n };\n return card;\n }\n const message: MessageItem = {\n type: 'message',\n key: `h:${item.id}`,\n role: item.role === 'assistant' ? 'agent' : item.role,\n text: item.text,\n state: 'sent',\n ...(item.interrupted ? { partial: 'interrupted' as const } : {}),\n ...(item.refusal ? { refusal: true as const } : {}),\n };\n return message;\n });\n}\n\n/** Why a page could not be read, as the runtime says it. */\nfunction reasonOf(error: unknown): AgentFailureReason {\n if (isRefusal(error)) return error.reason;\n const given = (error as { reason?: unknown } | null)?.reason;\n return typeof given === 'string' ? (given as AgentFailureReason) : 'unknown';\n}\n\n/**\n * The cursor restart (`vinaverse.agent-backend/1`): a page asked with a cursor, refused `unavailable`, not retryable,\n * with the gateway's `invalid_request`, had a cursor the gateway no longer takes (stale, or of other filters). Asked\n * again from the first page, once. Any other refusal is said as it is.\n */\nfunction staleCursor(error: unknown): boolean {\n const e = error as {\n outcome?: unknown;\n gatewayCode?: unknown;\n retryable?: unknown;\n } | null;\n return (\n e?.outcome === 'unavailable' &&\n e.gatewayCode === 'invalid_request' &&\n e.retryable === false\n );\n}\n\n/** A paged store: one page read at a time, dropped when it lands after a newer read or a sign-out. */\nfunction paged<S extends { readonly loading: boolean }>(\n runtime: AgentRuntime,\n empty: S,\n) {\n let state = empty;\n let epoch = runtime.epoch();\n let asked = 0;\n const listeners = new Set<() => void>();\n const set = (next: Partial<S>) => {\n state = { ...state, ...next };\n for (const l of [...listeners]) l();\n };\n // A sign-out empties what the last person had, as soon as anyone listens again.\n const fresh = () => {\n if (runtime.epoch() === epoch) return;\n epoch = runtime.epoch();\n asked++;\n state = empty;\n };\n let off: (() => void) | null = null;\n return {\n get: () => {\n fresh();\n return state;\n },\n set,\n subscribe(listener: () => void) {\n listeners.add(listener);\n off ??= runtime.subscribe(() => {\n const before = state;\n fresh();\n if (state !== before) for (const l of [...listeners]) l();\n });\n return () => {\n listeners.delete(listener);\n if (listeners.size === 0) {\n off?.();\n off = null;\n }\n };\n },\n /** Reads a page; `apply` lands only for the newest read, under the same person. */\n read<T>(\n call: () => Promise<T>,\n apply: (value: T) => void,\n failed: (error: unknown) => void,\n ) {\n fresh();\n const mine = ++asked;\n const under = epoch;\n set({ loading: true } as Partial<S>);\n call().then(\n (value) => {\n if (mine === asked && under === runtime.epoch()) apply(value);\n },\n (error: unknown) => {\n if (mine === asked && under === runtime.epoch()) failed(error);\n },\n );\n },\n };\n}\n\nexport interface AgentHistoryState {\n readonly conversations: readonly ConversationSummary[];\n /** A page is being read. */\n readonly loading: boolean;\n /** Why the last page could not be read; null when it was. `off` when the backend serves no history. */\n readonly failure: AgentFailureReason | null;\n /** Every page is read. */\n readonly done: boolean;\n /** The first page was asked for. */\n readonly started: boolean;\n}\n\nexport interface AgentHistory {\n getSnapshot(): AgentHistoryState;\n subscribe(listener: () => void): () => void;\n /** Reads the first page anew: the first time, after a failure, or to see what changed. */\n refresh(): void;\n /** The next older page, when there is one and none is being read. */\n loadMore(): void;\n /** Reads again the page that failed: the first (as `refresh`), or the next. */\n retry(): void;\n /**\n * Deletes a past conversation (the runtime's `deleteConversation` for the key that holds it, so the device forgets\n * it only where the key still continues it) and drops its row. One already gone (`not_found`) is dropped too.\n */\n remove(key: AgentKey, conversationId: string): Promise<void>;\n}\n\nexport interface AgentHistoryOptions {\n readonly runtime: AgentRuntime;\n /** Default: the runtime's. */\n readonly backend?: AgentBackend;\n /** The app's side: `customer` or `business`. */\n readonly mode: string;\n /** The business acted for, with `business`. */\n readonly scope?: string | null;\n /** The agent, for a backend that serves more than one. */\n readonly agent?: string | null;\n /** Conversations per page (1–50). Default 20. */\n readonly pageSize?: number;\n}\n\nconst NO_HISTORY: AgentHistoryState = {\n conversations: [],\n loading: false,\n failure: null,\n done: false,\n started: false,\n};\n\n/** A person's past conversations, newest activity first. */\nexport function createAgentHistory(options: AgentHistoryOptions): AgentHistory {\n const { runtime } = options;\n const backend = options.backend ?? runtime.backend;\n const store = paged(runtime, NO_HISTORY);\n let cursor: string | null = null;\n let restarted = false;\n const page = (from: string | null) => {\n const list = backend.listConversations?.bind(backend);\n if (!list) {\n store.set({ started: true, failure: 'off', done: true });\n return;\n }\n store.read(\n () =>\n list({\n mode: options.mode,\n scope: options.scope ?? null,\n agent: options.agent ?? null,\n limit: options.pageSize ?? 20,\n cursor: from,\n }),\n (got) => {\n const known = new Set(\n from ? store.get().conversations.map((c) => c.id) : [],\n );\n cursor = got.next_cursor;\n store.set({\n conversations: [\n ...(from ? store.get().conversations : []),\n ...got.data.filter((c) => !known.has(c.id)),\n ],\n loading: false,\n failure: null,\n done: cursor === null,\n });\n },\n (error) => {\n if (from !== null && staleCursor(error) && !restarted) {\n restarted = true;\n cursor = null;\n page(null);\n return;\n }\n store.set({ loading: false, failure: reasonOf(error) });\n },\n );\n };\n const refresh = () => {\n cursor = null;\n restarted = false;\n store.set({ started: true });\n page(null);\n };\n const loadMore = () => {\n const s = store.get();\n if (!s.started || s.loading || s.done || cursor === null) return;\n page(cursor);\n };\n return {\n getSnapshot: store.get,\n subscribe: store.subscribe,\n refresh,\n loadMore,\n retry: () => (cursor === null ? refresh() : loadMore()),\n async remove(key, conversationId) {\n try {\n await runtime.deleteConversation(key, conversationId);\n } catch (error) {\n if (!(isRefusal(error) && error.reason === 'not_found')) throw error;\n }\n store.set({\n conversations: store\n .get()\n .conversations.filter((c) => c.id !== conversationId),\n });\n },\n };\n}\n\nexport interface AgentTranscriptState {\n /** The conversation's items, oldest first, as the kits draw them (`historyItems`). */\n readonly items: readonly TimelineItem[];\n readonly loading: boolean;\n /** Why the last page could not be read: `not_found` (deleted, or never theirs), `expired`, … */\n readonly failure: AgentFailureReason | null;\n /** Nothing earlier to read. */\n readonly done: boolean;\n readonly started: boolean;\n}\n\nexport interface AgentTranscript {\n getSnapshot(): AgentTranscriptState;\n subscribe(listener: () => void): () => void;\n /** Reads the newest page anew. */\n load(): void;\n /** The page before the earliest read, prepended. */\n loadEarlier(): void;\n /** Reads again the page that failed: the newest (as `load`), or the earlier one. */\n retry(): void;\n}\n\nexport interface AgentTranscriptOptions {\n readonly runtime: AgentRuntime;\n /** Default: the runtime's. */\n readonly backend?: AgentBackend;\n readonly conversationId: string;\n /** Items per page. Default 30. */\n readonly pageSize?: number;\n}\n\nconst NO_TRANSCRIPT: AgentTranscriptState = {\n items: [],\n loading: false,\n failure: null,\n done: false,\n started: false,\n};\n\n/** A past conversation's transcript, read-only: newest page first, earlier pages prepended. */\nexport function createAgentTranscript(\n options: AgentTranscriptOptions,\n): AgentTranscript {\n const { runtime } = options;\n const backend = options.backend ?? runtime.backend;\n const store = paged(runtime, NO_TRANSCRIPT);\n let cursor: string | null = null;\n let restarted = false;\n const page = (from: string | null) => {\n const read = backend.readConversation?.bind(backend);\n if (!read) {\n store.set({ started: true, failure: 'off', done: true });\n return;\n }\n store.read(\n () =>\n read(options.conversationId, {\n limit: options.pageSize ?? 30,\n cursor: from,\n }),\n (got) => {\n const earlier = historyItems(got.data);\n const held = from ? store.get().items : [];\n const keys = new Set(held.map((i) => i.key));\n cursor = got.next_cursor;\n store.set({\n items: [...earlier.filter((i) => !keys.has(i.key)), ...held],\n loading: false,\n failure: null,\n done: cursor === null,\n });\n },\n (error) => {\n // An earlier page's cursor went stale: read back from the newest page, once.\n if (from !== null && staleCursor(error) && !restarted) {\n restarted = true;\n cursor = null;\n page(null);\n return;\n }\n store.set({ loading: false, failure: reasonOf(error) });\n },\n );\n };\n const load = () => {\n cursor = null;\n restarted = false;\n store.set({ started: true });\n page(null);\n };\n const loadEarlier = () => {\n const s = store.get();\n if (!s.started || s.loading || s.done || cursor === null) return;\n page(cursor);\n };\n return {\n getSnapshot: store.get,\n subscribe: store.subscribe,\n load,\n loadEarlier,\n retry: () => (cursor === null ? load() : loadEarlier()),\n };\n}\n"]}
package/dist/index.cjs CHANGED
@@ -2,8 +2,8 @@
2
2
 
3
3
  // src/contract.ts
4
4
  var CONTRACT = {
5
- api: "v1-draft.22",
6
- commit: "4695e3dc042e6e0cf1936aae24fcb093eacd27c8"
5
+ api: "v1-draft.24",
6
+ commit: "a7fd1e0f13093b50c1ebdb5d5d0fe8b724daae39"
7
7
  };
8
8
 
9
9
  // src/engine.ts