@gestaltrun/dsh-im-runtime 0.1.0 → 0.1.6

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 (62) hide show
  1. package/README.i18n.yaml +35 -20
  2. package/README.md +142 -10
  3. package/README.zh.md +141 -9
  4. package/lib/index.js +140 -2
  5. package/lib/inline-image-DuX6vSEI.js +20 -0
  6. package/lib/read-receipts.js +413 -0
  7. package/lib/read-state-CnUZKCuY.js +1868 -0
  8. package/lib/runtime-Bm6iSEpv.js +10939 -0
  9. package/lib/tools.js +1 -1
  10. package/lib/types/account-placement.d.ts +22 -0
  11. package/lib/types/admission-annotations.d.ts +40 -0
  12. package/lib/types/agent-coordinator.d.ts +47 -2
  13. package/lib/types/agent-media.d.ts +46 -0
  14. package/lib/types/agent-prompt.d.ts +8 -2
  15. package/lib/types/checkpoint-recovery.d.ts +32 -0
  16. package/lib/types/checkpoint.d.ts +127 -0
  17. package/lib/types/consumer-chat-links.d.ts +59 -0
  18. package/lib/types/delivery-schema.d.ts +126 -2
  19. package/lib/types/delivery-sqlite.d.ts +206 -6
  20. package/lib/types/delivery-types.d.ts +97 -1
  21. package/lib/types/delivery.d.ts +29 -6
  22. package/lib/types/errors.d.ts +1 -1
  23. package/lib/types/evaluation-accounts.d.ts +86 -0
  24. package/lib/types/index.d.ts +34 -2
  25. package/lib/types/inline-image.d.ts +7 -0
  26. package/lib/types/input-drain.d.ts +73 -0
  27. package/lib/types/knowledge-image.d.ts +64 -0
  28. package/lib/types/media-lifetime.d.ts +22 -0
  29. package/lib/types/media.d.ts +32 -0
  30. package/lib/types/message-preview.d.ts +4 -0
  31. package/lib/types/message-upload.d.ts +55 -0
  32. package/lib/types/nas-host-lease.d.ts +33 -0
  33. package/lib/types/placement-slice.d.ts +30 -0
  34. package/lib/types/policies.d.ts +10 -0
  35. package/lib/types/preset-model.d.ts +50 -0
  36. package/lib/types/read-context.d.ts +27 -0
  37. package/lib/types/read-receipts.d.ts +10 -0
  38. package/lib/types/read-records.d.ts +30 -0
  39. package/lib/types/read-state.d.ts +18 -0
  40. package/lib/types/read-types.d.ts +28 -0
  41. package/lib/types/restart-recovery.d.ts +38 -0
  42. package/lib/types/runtime.d.ts +318 -10
  43. package/lib/types/schema.d.ts +13 -2
  44. package/lib/types/script-clock.d.ts +14 -0
  45. package/lib/types/sdk.d.ts +108 -0
  46. package/lib/types/service-types.d.ts +161 -4
  47. package/lib/types/session-events.d.ts +9 -9
  48. package/lib/types/simulation-batch.d.ts +40 -0
  49. package/lib/types/simulation-controller.d.ts +16 -3
  50. package/lib/types/simulation-operator.d.ts +19 -0
  51. package/lib/types/sqlite-connection.d.ts +27 -0
  52. package/lib/types/time.d.ts +13 -0
  53. package/lib/types/transport.d.ts +44 -4
  54. package/lib/types/transports.d.ts +7 -0
  55. package/lib/types/types.d.ts +22 -5
  56. package/lib/types/typing.d.ts +33 -0
  57. package/lib/types/upload-types.d.ts +121 -0
  58. package/lib/types.js +2 -1
  59. package/locale/en.json +1 -1
  60. package/locale/zh.json +1 -1
  61. package/package.json +67 -41
  62. package/lib/runtime-C0-S-_CC.js +0 -6758
@@ -0,0 +1,413 @@
1
+ import { a as senderUid, c as encodeImScopeId, i as sameBuyer, n as buyerReadState, r as buyerUid, t as buyerReadQuery, u as imDeliveryScopeSchema } from "./read-state-CnUZKCuY.js";
2
+ import { brandString } from "@deepseek-ai/dsh-brand";
3
+ import { SessionId } from "@deepseek-ai/dsh-session";
4
+ import { defineDomain, domainTable } from "@deepseek-ai/dsh-storage-domain";
5
+ import { z } from "zod";
6
+ import { createUserMessage } from "@deepseek-ai/dsh-llm";
7
+ //#region src/read-records.ts
8
+ /** Independent durable read evidence; no tokens, raw SDK payloads or message bodies. */
9
+ const association = z.object({
10
+ requestId: z.string().min(1).transform((value) => brandString(value)),
11
+ platformMessageId: z.string().min(1),
12
+ sdkConversationId: z.string().min(1),
13
+ buyerUid: z.string().min(1),
14
+ singleton: z.boolean(),
15
+ queried: z.boolean(),
16
+ state: z.enum([
17
+ "unknown",
18
+ "unread",
19
+ "read"
20
+ ]),
21
+ observedAt: z.iso.datetime(),
22
+ sessionId: z.string().transform(SessionId).optional()
23
+ });
24
+ /** Product-owned read records; opening older IM configuration requires no migration. */
25
+ const imReadsDomainSpec = defineDomain({
26
+ name: "gestaltrun_im_reads",
27
+ version: 1,
28
+ tables: { scopes: domainTable(z.object({
29
+ schema: z.literal("gestaltrun.im-reads.v1"),
30
+ scope: imDeliveryScopeSchema.refine((value) => value.kind === "real" && value.platform === "wangwang" && value.conversationKind === "direct"),
31
+ reported: z.record(z.string(), z.literal(true)),
32
+ outbounds: z.record(z.string(), association)
33
+ })) }
34
+ });
35
+ //#endregion
36
+ //#region src/read-context.ts
37
+ function recorded(session, scope, requestId) {
38
+ const scopeId = encodeImScopeId(scope);
39
+ return session.snapshotEvents().some((event) => event.type === "im/message-read" && event.data.requestId === requestId && encodeImScopeId(event.data.scope) === scopeId);
40
+ }
41
+ function append(session, scope, row) {
42
+ if (recorded(session, scope, row.requestId)) return false;
43
+ const data = {
44
+ scope,
45
+ requestId: row.requestId,
46
+ platformMessageId: brandString(row.platformMessageId),
47
+ observedAt: row.observedAt
48
+ };
49
+ session.append("im/message-read", data);
50
+ return true;
51
+ }
52
+ /** @param ctx - optional live Agent and persistence services. @param scope - verified outbound scope. @param row - confirmed buyer evidence. @returns after a live Session fact is durable; absent Agents receive it at their next pre-step. */
53
+ async function recordLiveRead(ctx, scope, row) {
54
+ if (row.sessionId === void 0) return;
55
+ const agent = ctx.get("agents")?.get(row.sessionId);
56
+ const sessions = ctx.get("sessions");
57
+ if (agent !== void 0 && sessions !== void 0) {
58
+ append(agent.session, scope, row);
59
+ await sessions.flush(agent.session);
60
+ }
61
+ }
62
+ /** @param session - recorded facts and previously admitted contexts. @param proposed - contexts already proposed for this step. @returns each previously unannounced read once; source facts reconstruct the notification after a restart. */
63
+ function readContextMessages(session, proposed = []) {
64
+ const sent = /* @__PURE__ */ new Set();
65
+ const key = (scopeId, requestId) => `${scopeId}:${requestId}`;
66
+ const remember = (message) => {
67
+ if (message.source.kind === "im-read") sent.add(key(message.source.scopeId, message.source.requestId));
68
+ };
69
+ for (const event of session.snapshotEvents()) if (event.type === "user/message") remember(event.data);
70
+ for (const message of proposed) remember(message);
71
+ const messages = [];
72
+ for (const event of session.snapshotEvents()) {
73
+ if (event.type !== "im/message-read") continue;
74
+ const scopeId = encodeImScopeId(event.data.scope);
75
+ const id = key(scopeId, event.data.requestId);
76
+ if (sent.has(id)) continue;
77
+ sent.add(id);
78
+ const text = `The buyer has read outbound message ${event.data.requestId}. This receipt confirms reading; it does not confirm agreement or satisfaction.`;
79
+ messages.push(createUserMessage({
80
+ content: [{
81
+ type: "text",
82
+ text
83
+ }],
84
+ source: {
85
+ kind: "im-read",
86
+ scopeId,
87
+ requestId: event.data.requestId,
88
+ form: "notice",
89
+ summary: "Buyer read the message"
90
+ }
91
+ }));
92
+ }
93
+ return messages;
94
+ }
95
+ /** @param ctx - registration owner. @param table - independent durable read evidence. @returns exact child disposer, joined before the table closes. Context reconstruction does not wake idle Agents. */
96
+ function installReadContext(ctx, table) {
97
+ return ctx.inject(["agents", "sessions"], (child) => {
98
+ child.effect(() => child.on("agent/pre-step", async ({ agent }, next) => {
99
+ const decision = await next();
100
+ if (decision.kind === "reject") return decision;
101
+ let changed = false;
102
+ for (const [, record] of table.entries()) for (const row of Object.values(record.outbounds)) if (row.state === "read" && row.sessionId === agent.id) changed = append(agent.session, record.scope, row) || changed;
103
+ const contexts = readContextMessages(agent.session, decision.messages);
104
+ if (changed || contexts.length > 0) await child.sessions.flush(agent.session);
105
+ return {
106
+ ...decision,
107
+ messages: [...decision.messages, ...contexts]
108
+ };
109
+ }));
110
+ }).dispose;
111
+ }
112
+ //#endregion
113
+ //#region src/read-receipts.ts
114
+ const configSchema = z.object({ pendingMessageLimit: z.number().int().positive().max(1e5).default(1e3) }).strict();
115
+ const messageSchema = z.object({ message: z.object({
116
+ messageId: z.string().min(1),
117
+ cid: z.string().min(1).optional(),
118
+ appCid: z.string().min(1).optional(),
119
+ uuid: z.string().optional(),
120
+ sender: z.union([z.string(), z.object({ uid: z.string() })]),
121
+ receivers: z.array(z.string()).optional()
122
+ }) });
123
+ var ReadOwner = class {
124
+ ctx;
125
+ sdk;
126
+ table;
127
+ limit;
128
+ pending = /* @__PURE__ */ new Map();
129
+ queried = /* @__PURE__ */ new Map();
130
+ observers = /* @__PURE__ */ new Set();
131
+ chain = Promise.resolve();
132
+ closed = false;
133
+ constructor(ctx, sdk, table, limit) {
134
+ this.ctx = ctx;
135
+ this.sdk = sdk;
136
+ this.table = table;
137
+ this.limit = limit;
138
+ }
139
+ snapshot(scope) {
140
+ const key = encodeImScopeId(scope);
141
+ const status = this.sdk.status(scope.accountId);
142
+ return Object.values(this.table.get(key)?.outbounds ?? {}).flatMap((row) => row.state === "unknown" || row.state === "unread" && (status.state !== "online" || this.queried.get(`${key}:${row.requestId}`) !== status.generation) ? [] : [{
143
+ requestId: row.requestId,
144
+ state: row.state
145
+ }]);
146
+ }
147
+ subscribe(listener) {
148
+ this.observers.add(listener);
149
+ return () => {
150
+ this.observers.delete(listener);
151
+ };
152
+ }
153
+ submit(event, availabilityChanged = false) {
154
+ if (this.closed) return Promise.resolve();
155
+ const task = this.chain.then(async () => {
156
+ if (this.closed) return;
157
+ if (event !== void 0) await this.accept(event);
158
+ else for (const pending of [...this.pending.values()]) await this.correlate(pending);
159
+ if (availabilityChanged) for (const [, stored] of this.table.entries()) this.publish(stored.scope);
160
+ });
161
+ this.chain = task.catch(() => {
162
+ this.ctx.logger.warn("IM_READ_SYNC_FAILED: read evidence remains unknown; no automatic retry");
163
+ });
164
+ return this.chain;
165
+ }
166
+ current(event) {
167
+ const status = this.sdk.status(event.accountId);
168
+ return status.state === "online" && status.generation === event.generation;
169
+ }
170
+ async accept(event) {
171
+ if (!this.current(event)) return;
172
+ if (event.kind === "message") {
173
+ const parsed = messageSchema.safeParse(event.data);
174
+ if (!parsed.success || parsed.data.message.cid === void 0 && parsed.data.message.appCid === void 0) return;
175
+ await this.observe({
176
+ event,
177
+ source: "message",
178
+ message: parsed.data.message
179
+ });
180
+ } else if (event.kind === "receipt") {
181
+ const parsed = z.object({
182
+ messageId: z.string().min(1),
183
+ requestId: z.string().min(1),
184
+ conversationId: z.string().min(1)
185
+ }).safeParse(event.data);
186
+ if (!parsed.success) return;
187
+ await this.observe({
188
+ event,
189
+ source: "receipt",
190
+ message: {
191
+ messageId: parsed.data.messageId,
192
+ uuid: parsed.data.requestId,
193
+ cid: parsed.data.conversationId
194
+ }
195
+ });
196
+ } else if (event.kind === "read-to-sender") {
197
+ const values = Array.isArray(event.data.events) ? event.data.events : [event.data];
198
+ for (const value of values) {
199
+ const parsed = z.object({
200
+ cid: z.string(),
201
+ conversationType: z.literal(1),
202
+ messageId: z.string(),
203
+ receiverCount: z.number().int().nonnegative().optional(),
204
+ unreadCount: z.number().int().nonnegative().optional()
205
+ }).safeParse(value);
206
+ if (!parsed.success) continue;
207
+ await this.observe({
208
+ event,
209
+ source: "read-to-sender",
210
+ message: {
211
+ messageId: parsed.data.messageId,
212
+ cid: parsed.data.cid
213
+ }
214
+ });
215
+ for (const [key, stored] of this.table.entries()) {
216
+ if (stored.scope.accountId !== event.accountId) continue;
217
+ for (const row of Object.values(stored.outbounds)) if (row.platformMessageId === parsed.data.messageId && row.sdkConversationId === parsed.data.cid && row.state !== "read") {
218
+ const state = buyerReadState(parsed.data, row.singleton);
219
+ if (state !== "unknown") {
220
+ this.queried.set(`${key}:${row.requestId}`, event.generation);
221
+ await this.save(key, stored.scope, {
222
+ ...row,
223
+ state,
224
+ observedAt: (/* @__PURE__ */ new Date()).toISOString()
225
+ });
226
+ } else if (parsed.data.receiverCount !== void 0 && parsed.data.unreadCount !== void 0) {
227
+ const next = await this.query(event, key, stored.scope, row, "push");
228
+ await this.save(key, stored.scope, next);
229
+ }
230
+ }
231
+ }
232
+ }
233
+ }
234
+ }
235
+ async observe(observed) {
236
+ const { event, message } = observed;
237
+ const key = `${event.accountId}:${event.generation}:${message.messageId}`;
238
+ if (!this.pending.has(key) && this.pending.size >= this.limit) {
239
+ const oldest = this.pending.keys().next().value;
240
+ if (oldest !== void 0) this.pending.delete(oldest);
241
+ this.ctx.logger.warn("IM_READ_PENDING_FULL: oldest unmatched observation discarded; its receipt remains unknown");
242
+ }
243
+ this.pending.set(key, observed);
244
+ await this.correlate(observed);
245
+ }
246
+ async correlate({ event, message, source }) {
247
+ if (!this.current(event)) {
248
+ this.remove(event, message.messageId);
249
+ return;
250
+ }
251
+ const matches = this.ctx.imRuntime.matchSdkMessage(event.accountId, message.messageId, message.uuid);
252
+ if (matches.length !== 1) return;
253
+ const match = matches[0];
254
+ const buyer = buyerUid(match.buyer);
255
+ const sender = senderUid(message.sender);
256
+ const cid = message.cid ?? message.appCid;
257
+ const key = encodeImScopeId(match.scope);
258
+ const stored = this.table.get(key) ?? {
259
+ schema: "gestaltrun.im-reads.v1",
260
+ scope: match.scope,
261
+ reported: {},
262
+ outbounds: {}
263
+ };
264
+ if (match.inbound !== void 0) {
265
+ if (source !== "message" || sender === void 0 || !sameBuyer(sender, buyer) || match.inbound.sender.kind !== "external" || match.inbound.sender.senderId !== match.buyer.providerActorId) return;
266
+ if (stored.reported[message.messageId] !== void 0) {
267
+ this.remove(event, message.messageId);
268
+ return;
269
+ }
270
+ await this.table.put(key, {
271
+ ...stored,
272
+ reported: {
273
+ ...stored.reported,
274
+ [message.messageId]: true
275
+ }
276
+ });
277
+ if (!this.current(event)) return;
278
+ this.remove(event, message.messageId);
279
+ try {
280
+ await this.sdk.withConnection(event.accountId, event.generation, (lease) => lease.markRead(brandString(cid), [brandString(message.messageId)]));
281
+ } catch (error) {
282
+ this.ctx.logger.warn("IM_READ_REPORT_FAILED: buyer read acknowledgement unconfirmed; no automatic retry");
283
+ }
284
+ return;
285
+ }
286
+ const outbound = match.outbound;
287
+ const account = this.ctx.imRuntime.snapshot().accounts.find((value) => value.id === event.accountId);
288
+ if (outbound === void 0 || account?.identity.platform !== "wangwang") return;
289
+ const previous = stored.outbounds[outbound.requestId];
290
+ if (previous !== void 0 && (previous.platformMessageId !== message.messageId || previous.buyerUid !== buyer || previous.sdkConversationId !== cid)) return;
291
+ if (source === "message") {
292
+ if (sender === void 0 || account.identity.mainServiceAccountId === void 0 || !sameBuyer(sender, account.identity.mainServiceAccountId)) return;
293
+ } else {
294
+ if (outbound.externalMessageId !== message.messageId) return;
295
+ try {
296
+ const resolved = previous?.sdkConversationId ?? await this.sdk.withConnection(event.accountId, event.generation, (lease) => lease.resolveConversation(brandString(buyer)));
297
+ if (!this.current(event) || resolved !== cid) {
298
+ this.remove(event, message.messageId);
299
+ return;
300
+ }
301
+ } catch (error) {
302
+ this.remove(event, message.messageId);
303
+ this.ctx.logger.warn("IM_READ_CONVERSATION_FAILED: read association unconfirmed; no automatic retry");
304
+ return;
305
+ }
306
+ }
307
+ const task = this.ctx.imRuntime.getAgentTask(match.scope);
308
+ const sessionId = outbound.manualBinding?.sessionId ?? (task !== void 0 && task.createdAt <= outbound.createdAt ? task.sessionId : void 0);
309
+ let row = previous ?? {
310
+ requestId: outbound.requestId,
311
+ platformMessageId: message.messageId,
312
+ sdkConversationId: cid,
313
+ buyerUid: buyer,
314
+ singleton: message.receivers?.length === 1 && sameBuyer(message.receivers[0], buyer),
315
+ queried: false,
316
+ state: "unknown",
317
+ observedAt: (/* @__PURE__ */ new Date()).toISOString(),
318
+ ...sessionId === void 0 ? {} : { sessionId }
319
+ };
320
+ this.remove(event, message.messageId);
321
+ row = await this.query(event, key, match.scope, row, "association");
322
+ await this.save(key, match.scope, row);
323
+ }
324
+ async query(event, key, scope, row, phase) {
325
+ const queryKey = phase === "association" ? `${key}:${row.requestId}` : `${key}:${row.requestId}:push`;
326
+ if (row.state === "read" || this.queried.get(queryKey) === event.generation) return row;
327
+ this.queried.set(queryKey, event.generation);
328
+ let next = {
329
+ ...row,
330
+ queried: true,
331
+ state: "unknown"
332
+ };
333
+ await this.save(key, scope, next);
334
+ if (!this.current(event)) return next;
335
+ try {
336
+ const result = await this.sdk.withConnection(event.accountId, event.generation, (lease) => lease.queryRead(brandString(row.sdkConversationId), brandString(row.platformMessageId)));
337
+ if (!this.current(event)) return next;
338
+ const parsed = buyerReadQuery(result, row.buyerUid);
339
+ next = {
340
+ ...next,
341
+ singleton: row.singleton || parsed.singleton,
342
+ state: parsed.state,
343
+ observedAt: (/* @__PURE__ */ new Date()).toISOString()
344
+ };
345
+ this.queried.set(`${key}:${row.requestId}`, event.generation);
346
+ } catch (error) {
347
+ this.ctx.logger.warn("IM_BUYER_READ_QUERY_FAILED: buyer state remains unknown; no automatic retry");
348
+ }
349
+ return next;
350
+ }
351
+ publish(scope) {
352
+ for (const observer of this.observers) try {
353
+ observer(scope);
354
+ } catch (error) {
355
+ this.observers.delete(observer);
356
+ this.ctx.logger.warn("IM_READ_OBSERVER_FAILED: subscription withdrawn");
357
+ }
358
+ }
359
+ remove(event, id) {
360
+ this.pending.delete(`${event.accountId}:${event.generation}:${id}`);
361
+ }
362
+ async save(key, scope, row) {
363
+ const current = this.table.get(key) ?? {
364
+ schema: "gestaltrun.im-reads.v1",
365
+ scope,
366
+ reported: {},
367
+ outbounds: {}
368
+ };
369
+ await this.table.put(key, {
370
+ ...current,
371
+ outbounds: {
372
+ ...current.outbounds,
373
+ [row.requestId]: row
374
+ }
375
+ });
376
+ this.publish(scope);
377
+ if (row.state === "read") await recordLiveRead(this.ctx, scope, row);
378
+ }
379
+ async dispose() {
380
+ this.closed = true;
381
+ await this.chain;
382
+ this.pending.clear();
383
+ this.queried.clear();
384
+ this.observers.clear();
385
+ }
386
+ };
387
+ /** Host services required before opening independent read storage. */
388
+ const inject = ["imRuntime", "storageDomain"];
389
+ /** @param ctx - durable runtime and storage owner. @param config - bounded unmatched-message admission. @returns after the read domain and optional SDK consumers are installed. */
390
+ async function apply(ctx, config = {}) {
391
+ const spec = configSchema.parse(config);
392
+ const domain = await ctx.storageDomain.open(imReadsDomainSpec);
393
+ ctx.effect(function* () {
394
+ yield ctx.effect(() => () => domain.close(), "imReads: domain");
395
+ yield installReadContext(ctx, domain.table("scopes"));
396
+ yield ctx.inject(["imSdk"], (child) => {
397
+ const owner = new ReadOwner(child, child.imSdk, domain.table("scopes"), spec.pendingMessageLimit);
398
+ child.effect(function* () {
399
+ yield child.effect(() => () => owner.dispose(), "imReads: joined work");
400
+ yield child.provide("imReads", owner);
401
+ yield child.imSdk.subscribeEvents((event) => owner.submit(event));
402
+ yield child.imRuntime.subscribeDelivery(() => {
403
+ owner.submit();
404
+ });
405
+ yield child.imSdk.subscribe(() => {
406
+ owner.submit(void 0, true);
407
+ });
408
+ });
409
+ }).dispose;
410
+ });
411
+ }
412
+ //#endregion
413
+ export { apply, inject };