@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
@@ -1,22 +1,49 @@
1
+ /** Host-owned sqlite records for IM delivery. Opens one file and reads by key. */
2
+ import { type SqliteConnectionConfig } from './sqlite-connection.ts';
3
+ import type { NasHostLease, SqliteJournalMode } from './nas-host-lease.ts';
4
+ import type { ImUploadControlResult } from './upload-types.ts';
1
5
  import { SessionId } from '@deepseek-ai/dsh-session';
2
6
  import { type ImDeliveryAggregate, type ImExecutionAggregate, type ImProviderCursorAggregate } from './delivery-schema.ts';
3
- import type { ImAgentTaskId, ImCustomerListPage, ImCustomerListRequest, ImCustomerReadAckRequest, ImDeliveryScope, ImHandlingControl, ImOutboundRequestId, ImOutboundView, ImProviderCursorId, ImScopeId, ImSetHandlingControlRequest } from './delivery-types.ts';
4
- import type { ImAccountId } from './types.ts';
7
+ import type { ImAgentTaskId, ImCustomerSessionListRequest, ImCustomerSessionPage, ImCustomerListPage, ImCustomerListRequest, ImCustomerReadAckRequest, ImDeliveryScope, ImHandlingControl, ImOutboundRequestId, ImOutboundView, ImProviderCursorId, ImScopeId, ImSetHandlingControlRequest } from './delivery-types.ts';
8
+ import type { ImAccountId, ImPlatform } from './types.ts';
9
+ import type { ImInboundContentQuery } from './transport.ts';
10
+ import type { ImDeliveryAccountSlice } from './placement-slice.ts';
5
11
  export declare const IM_DELIVERY_UNIT = "gestaltrun_im_delivery";
6
12
  export declare const IM_DELIVERY_UNIT_VERSION = 1;
7
13
  /** Monotonic physical layout version stored in `PRAGMA user_version`. */
8
- export declare const IM_DELIVERY_SCHEMA_VERSION = 2;
14
+ export declare const IM_DELIVERY_SCHEMA_VERSION = 4;
15
+ /** 语句遇锁等待时长;node:sqlite 缺省为 0,任何持锁者都会让语句立即失败。NAS 交接窗口内旧实例的短暂事务通常在 2 秒内提交。 */
16
+ export declare const DEFAULT_DELIVERY_BUSY_TIMEOUT_MS = 2000;
17
+ /** 迁移单步遇锁重试的时间预算;超限抛 `IM_DELIVERY_SQLITE_BUSY`,由 supervisor 重启后再试,而不是无限期卡住启动。 */
18
+ export declare const DEFAULT_DELIVERY_MIGRATION_RETRY_BUDGET_MS = 30000;
19
+ /** Values one consumer derives from scope documents; created by `ImDeliverySqlite.projectScopes`. */
20
+ export interface ImScopeProjection<T> {
21
+ /**
22
+ * @param id - complete conversation identity.
23
+ * @returns the value derived from the stored document, or absence when none is stored; parses the document only when this projection has not derived it yet.
24
+ */
25
+ get(id: ImScopeId): T | undefined;
26
+ /** Stop deriving and release the values. Idempotent; `get` throws afterwards. */
27
+ dispose(): void;
28
+ }
9
29
  /**
10
30
  * Durable delivery records with key lookups and small parsed-document caches.
31
+ * Scope descriptors and correlation identifiers derived from parsed or written documents stay in memory without a
32
+ * size bound, so account-wide listings and SDK matching do not re-read documents. Like the document caches, they
33
+ * require this connection to be the only writer.
11
34
  * Physical unit tables stay compatible with the sqlite KV layout already on disk.
12
35
  */
13
36
  export declare class ImDeliverySqlite {
37
+ private readonly lease?;
38
+ private readonly releaseReservation?;
14
39
  /** Test-only: throw after this many scopes have been committed during 1→2 backfill. */
15
40
  static injectBackfillCrashAfter: number | undefined;
16
41
  /** Test-only: override the committed backfill batch size. */
17
42
  static backfillBatchSize: number | undefined;
18
43
  /** Test-only: fail the next customer-index write so putScope can prove rollback. */
19
44
  static failNextCustomerIndexWrite: boolean;
45
+ /** Test-only: fail the next cursor-account index write so putCursor can prove rollback. */
46
+ static failNextCursorIndexWrite: boolean;
20
47
  private readonly db;
21
48
  private readonly scopeSelect;
22
49
  private readonly scopeUpsert;
@@ -24,16 +51,43 @@ export declare class ImDeliverySqlite {
24
51
  private readonly cursorUpsert;
25
52
  private readonly executionSelect;
26
53
  private readonly executionUpsert;
54
+ private customerUnreadRevision;
55
+ private readonly customerUnreads;
27
56
  private readonly scopeCache;
57
+ /**
58
+ * Descriptor of each stored scope seen since open or the last bulk removal. A scope id hashes its
59
+ * descriptor (`encodeImScopeId`), so an entry stays exact while that scope is stored.
60
+ */
61
+ private readonly scopeDescriptors;
62
+ /** `correlationIdentifiers` of each parsed or written scope document, grouped by account; every write refreshes its entry. */
63
+ private readonly correlationByAccount;
64
+ /** Accounts whose every stored scope has a `correlationByAccount` entry; writes keep them complete until a bulk removal. */
65
+ private readonly correlationComplete;
66
+ private readonly projections;
28
67
  private readonly executionCache;
29
68
  private closed;
69
+ private readonly releaseFence;
30
70
  private constructor();
31
71
  /**
32
72
  * Open or create the delivery database and ensure unit tables plus lookup indexes.
33
73
  * @param path - sqlite file path, or `:memory:`.
74
+ * @param options - 日志、连接锁和页缓存设置、锁等待与重试预算及可选云端租约;所有访问检查本地期限。
34
75
  * @returns the opened record store.
35
76
  */
36
- static open(path: string): Promise<ImDeliverySqlite>;
77
+ static open(path: string, options?: SqliteConnectionConfig & {
78
+ readonly journalMode?: SqliteJournalMode;
79
+ readonly lease?: NasHostLease;
80
+ readonly busyTimeoutMs?: number;
81
+ readonly migrationRetryBudgetMs?: number;
82
+ }): Promise<ImDeliverySqlite>;
83
+ /** @param requestId - outbox intent owning private staged bytes and SDK metadata. @returns stored JSON; the media owner validates before use. */
84
+ mediaPreparation(requestId: ImOutboundRequestId): unknown;
85
+ /** @param requestId - owning intent. @param value - media-owner validated private JSON, never included in history or Remote snapshots. */
86
+ saveMediaPreparation(requestId: ImOutboundRequestId, value: object): void;
87
+ /** @returns file intents whose external preparation started without committed metadata. */
88
+ unfinishedFilePreparationIds(): readonly ImOutboundRequestId[];
89
+ /** @param requestId - predecessor intent under retry consideration. @returns whether its external file preparation remains unconfirmed. */
90
+ hasUnfinishedFilePreparation(requestId: ImOutboundRequestId): boolean;
37
91
  /**
38
92
  * Read one conversation aggregate by scope id.
39
93
  * @param id - complete conversation identity.
@@ -52,6 +106,25 @@ export declare class ImDeliverySqlite {
52
106
  * @returns the stored cursor, or absence.
53
107
  */
54
108
  getCursor(id: ImProviderCursorId): ImProviderCursorAggregate | undefined;
109
+ /**
110
+ * Opaque provider cursors for one account.
111
+ * Stored operations stay in sqlite and are not returned.
112
+ * Cursor ids come from the derived account index and are ordered by id.
113
+ * @param accountId - cursor owner.
114
+ */
115
+ checkpointCursors(accountId: ImAccountId): readonly {
116
+ readonly streamId: string;
117
+ readonly cursor: string;
118
+ }[];
119
+ /**
120
+ * Durable unsubmitted input, unsettled-send and unknown-send counts for one real account.
121
+ * Message rows are not copied out. Reads the derived per-scope counts maintained with each scope write.
122
+ * @param accountId - conversation owner.
123
+ */
124
+ checkpointCounts(accountId: ImAccountId): {
125
+ readonly pendingCount: number;
126
+ readonly unknownSendCount: number;
127
+ };
55
128
  /**
56
129
  * Persist one provider cursor.
57
130
  * @param id - provider feed identity.
@@ -89,10 +162,47 @@ export declare class ImDeliverySqlite {
89
162
  dispatchingScopeIds(): readonly ImScopeId[];
90
163
  /**
91
164
  * List conversations owned by one configured account.
165
+ * Membership comes from the account index; descriptors come from memory or the conversation summaries, not documents.
92
166
  * @param accountId - durable IM account.
93
- * @returns stored scopes for that account.
167
+ * @returns stored scopes for that account, in scope-id order.
94
168
  */
95
169
  scopesForAccount(accountId: ImAccountId): ImDeliveryScope[];
170
+ /**
171
+ * Read one conversation's descriptor without parsing its document.
172
+ * @param id - complete conversation identity.
173
+ * @returns the stored scope, or absence when no document is stored under `id`.
174
+ */
175
+ scopeDescriptor(id: ImScopeId): ImDeliveryScope | undefined;
176
+ /**
177
+ * Report whether one conversation holds received live inbound above its suppression watermark.
178
+ * Reads the pending index written with each document, which holds a scope exactly when its `cursor.pendingCount` is positive.
179
+ * @param id - complete conversation identity.
180
+ * @returns false for a scope without pending input or without a document.
181
+ */
182
+ hasPendingInbound(id: ImScopeId): boolean;
183
+ /** @param accountId - account owning the conversations. @param platform - admitted platform. @param uid - unqualified buyer UID. @returns matching real direct scopes using summary rows; older empty conversations use their saved direct recipient. The lookup does not populate the aggregate cache. */
184
+ directScopesForPeer(accountId: ImAccountId, platform: ImPlatform, uid: string): ImDeliveryScope[];
185
+ /** @param accountId - listener account. @param platform - admitted platform. @param request - platform message identity and optional conversation restriction. @returns matching real scopes from message rows and summaries, without parsing aggregates. */
186
+ inboundScopes(accountId: ImAccountId, platform: ImPlatform, request: ImInboundContentQuery): ImDeliveryScope[];
187
+ /**
188
+ * Conversations of one account whose document holds one of `identifiers` as the external message id of a live
189
+ * external inbound, or as the request or external message id of a sent outbound.
190
+ * The first call for an account parses the documents not yet parsed since open or the last bulk removal; later calls read no rows.
191
+ * @param accountId - durable IM account.
192
+ * @param identifiers - exact provider message ids or outbound request ids.
193
+ * @returns matching stored scopes, in scope-id order.
194
+ */
195
+ scopesWithCorrelationIdentifier(accountId: ImAccountId, identifiers: ReadonlySet<string>): ImDeliveryScope[];
196
+ /**
197
+ * Derive one value from every scope document parsed or written from now on. Each committed write refreshes the
198
+ * value and bulk removals clear it. A derivation failure during a write leaves that scope underived, and the next
199
+ * `get` derives it again and throws the failure to its caller; the committed write is unaffected.
200
+ * @param derive - pure function of one document; it must not keep the document.
201
+ * @returns accessor of the derived values.
202
+ */
203
+ projectScopes<T extends object>(derive: (value: ImDeliveryAggregate) => T): ImScopeProjection<T>;
204
+ /** @returns scopes with durable observations not yet transferred to the external queue. Reads the derived upload-pending index. */
205
+ uploadScopeIds(): readonly ImScopeId[];
96
206
  /**
97
207
  * Resolve the newest execution bound to one Agent Session.
98
208
  * @param sessionId - IM Agent Session.
@@ -111,6 +221,68 @@ export declare class ImDeliverySqlite {
111
221
  * @returns stored generations for that scope.
112
222
  */
113
223
  executionsForScope(scopeId: ImScopeId): ImExecutionAggregate[];
224
+ /**
225
+ * Newest execution per conversation, resolved with one indexed join per chunk of scope ids.
226
+ * Ties on generation resolve to the highest task id, the same row the per-scope
227
+ * `ORDER BY generation DESC LIMIT 1` query returns.
228
+ * @param scopeIds - complete conversation identities.
229
+ * @returns the highest-generation task of each scope that has one.
230
+ */
231
+ latestExecutionsForScopes(scopeIds: readonly ImScopeId[]): Map<ImScopeId, ImExecutionAggregate>;
232
+ /**
233
+ * @param accountId - durable IM account.
234
+ * @returns ids the account index lists and the scope table stores, in scope-id order.
235
+ */
236
+ private accountScopeIds;
237
+ /**
238
+ * Fill `scopeDescriptors` for stored ids from the conversation summaries written in the same transaction as each
239
+ * document, one statement per chunk. Ids whose summary is absent or invalid fall back to their documents.
240
+ * @param ids - ids whose documents are stored.
241
+ */
242
+ private loadScopeDescriptors;
243
+ /** @param ids - scope ids. @returns known descriptors in the order of `ids`; unknown ids are absent. */
244
+ private descriptorsOf;
245
+ /**
246
+ * Stored scope documents for explicit ids, fetched with one join per chunk.
247
+ * Ids without a stored document are absent from the result.
248
+ * @param ids - complete conversation identities.
249
+ * @returns parsed aggregates keyed by scope id.
250
+ */
251
+ private scopeDocuments;
252
+ /**
253
+ * Generations of every listed conversation in generation order, resolved with one
254
+ * indexed join per chunk. Scopes never overlap chunk boundaries, so per-scope order matches `executionsForScope`.
255
+ * @param scopeIds - complete conversation identities.
256
+ * @returns per-scope stored generations keyed by scope id.
257
+ */
258
+ private executionsForScopes;
259
+ /**
260
+ * Export all durable real-conversation facts for one account, excluding simulations and derived indexes.
261
+ * Documents, generations, read positions and handling controls resolve in a fixed number of
262
+ * batched queries, not per scope.
263
+ * @param accountId - source account; the caller drains writers before taking a final snapshot.
264
+ * @returns independent account slice; it contains buyer data and must not be logged.
265
+ */
266
+ exportAccountSlice(accountId: ImAccountId): ImDeliveryAccountSlice;
267
+ /**
268
+ * Read positions of one reader batch across scopes, grouped per scope.
269
+ * Reader order inside each group matches the per-scope point query (reader-id order).
270
+ * @param scopeIds - complete conversation identities.
271
+ * @returns acknowledged live-inbound positions keyed by scope id.
272
+ */
273
+ private readPositionsForScopes;
274
+ /**
275
+ * Handling controls batch across scopes.
276
+ * @param scopeIds - complete conversation identities.
277
+ * @returns stored controls keyed by scope id; scopes without a control are absent.
278
+ */
279
+ private handlingControlsForScopes;
280
+ /**
281
+ * Replace one account's real facts in one transaction, preserving other accounts, simulations and maximum reader positions.
282
+ * @param slice - wire-validated slice authorized for the target epoch; caller owns stopped listeners, drained queues and exclusive activation. Epoch receipts belong to the handoff journal.
283
+ * @throws ImPlacementSliceError for identities already owned by another account; transaction failures leave the prior slice intact unless the NAS fence closes the database, in which case the caller must remain stopped and reconcile.
284
+ */
285
+ replaceAccountSlice(slice: ImDeliveryAccountSlice): void;
114
286
  /**
115
287
  * Record a provider actor id that belongs to one configured account.
116
288
  * @param accountId - durable IM account.
@@ -154,12 +326,21 @@ export declare class ImDeliverySqlite {
154
326
  private insertExecutionIndexes;
155
327
  private clearExecutionIndexes;
156
328
  private rememberScope;
329
+ /** Drop parsed scope documents and every projection derived from them after documents were removed or replaced in bulk. */
330
+ private forgetScopes;
157
331
  private rememberExecution;
158
332
  /**
159
333
  * List customer rows for one account and source, grouped by customer key.
160
334
  * @param request - account, real/simulation filter, optional substring, and reader.
161
335
  */
162
336
  listCustomers(request: ImCustomerListRequest): ImCustomerListPage;
337
+ /**
338
+ * Read customer Session references without loading unread or handling state.
339
+ * @param request - account, source, search and keyset cursor.
340
+ * @returns Session references in the same order as the full customer list.
341
+ */
342
+ listCustomerSessions(request: ImCustomerSessionListRequest): ImCustomerSessionPage;
343
+ private customerGroups;
163
344
  /**
164
345
  * Advance one reader's inbound watermark with max semantics.
165
346
  * @param request - reader, scope, and observed arrival sequence.
@@ -168,8 +349,10 @@ export declare class ImDeliverySqlite {
168
349
  /**
169
350
  * Persist one scope's automatic-handling pause fact.
170
351
  * @param request - scope and pause fields.
352
+ * @param upload - successful product operation to journal atomically; absent for safety-only pauses.
353
+ * @returns committed control revision. Storage failure rolls back both control and observation.
171
354
  */
172
- setHandlingControl(request: ImSetHandlingControlRequest): ImHandlingControl;
355
+ setHandlingControl(request: ImSetHandlingControlRequest, upload?: ImUploadControlResult): ImHandlingControl;
173
356
  /**
174
357
  * Read one scope's automatic-handling pause fact.
175
358
  * @param scopeId - complete conversation identity.
@@ -178,6 +361,23 @@ export declare class ImDeliverySqlite {
178
361
  private requireHandlingControl;
179
362
  private customerMatches;
180
363
  private likeMatch;
364
+ /**
365
+ * One customer's acknowledged live-inbound watermarks, batched across scopes.
366
+ * @param readerId - reading customer-service reader.
367
+ * @param scopeIds - complete conversation identities of one page.
368
+ * @returns acknowledged sequence per scope; scopes without an ack read as 0.
369
+ */
370
+ private readWatermarksForScopes;
371
+ /** Reuse per-reader counts for this delivery revision; acknowledgements invalidate their reader. */
372
+ private customerUnreadCounts;
373
+ /**
374
+ * Unread live-inbound counts for scopes with their per-scope watermark, one range count per
375
+ * scope through a values join over the message index. Scopes without unread messages are absent.
376
+ * @param scopeIds - every scope the counts must cover; scopes missing from `watermarks` read from 0.
377
+ * @param watermarks - acknowledged sequence per scope id.
378
+ * @returns unread count per scope id.
379
+ */
380
+ private unreadCountsForScopes;
181
381
  private customerRow;
182
382
  private latestExecutionRow;
183
383
  private updateSummaryExecution;
@@ -19,6 +19,10 @@ export type ImSimulationInstanceId = Branded<'ImSimulationInstanceId'>;
19
19
  export type ImProviderCursorId = Branded<'ImProviderCursorId'>;
20
20
  /** Host-minted identity of one Agent admission batch. */
21
21
  export type ImAdmissionId = Branded<'ImAdmissionId'>;
22
+ /** Identity of one product's admission annotation provider. */
23
+ export type ImAdmissionAnnotatorId = Branded<'ImAdmissionAnnotatorId'>;
24
+ /** Stable identity of one analysis of one frozen admission batch. */
25
+ export type ImAdmissionAnnotationId = Branded<'ImAdmissionAnnotationId'>;
22
26
  /** Stable identity of one route-bound Agent task generation. */
23
27
  export type ImAgentTaskId = Branded<'ImAgentTaskId'>;
24
28
  /** Complete identity of a real platform conversation. */
@@ -102,8 +106,43 @@ export interface ImMessageQuote {
102
106
  readonly senderDisplayName?: string;
103
107
  readonly externalMessageId?: string;
104
108
  }
109
+ /** Private bytes supplied by a manual picker or an authorized Host tool; never recorded in Session data. */
110
+ export interface ImMediaInput {
111
+ readonly kind: 'image' | 'file';
112
+ readonly name: string;
113
+ readonly mime: string;
114
+ readonly base64: string;
115
+ }
116
+ /** Safe durable media description; SHA256 identifies the original selected bytes. */
117
+ export interface ImMediaDescription {
118
+ readonly kind: 'image' | 'file';
119
+ readonly name: string;
120
+ readonly mime: string;
121
+ readonly bytes: number;
122
+ readonly sha256: string;
123
+ /** Original immutable Session file, when an Agent resolved the source. Absent for manual uploads. */
124
+ readonly sourceFile?: import('@deepseek-ai/dsh-attachment').FileAttachmentRef;
125
+ }
126
+ /** Upload preparation facts, independent from the outbox message submission status. */
127
+ export interface ImMediaPreparationView {
128
+ readonly state: 'uploading' | 'prepared';
129
+ readonly imageUrl?: string;
130
+ }
131
+ /** Reusable Host media send; automatic callers supply their frozen task route. */
132
+ export interface ImSendMediaMessageRequest extends Omit<ImRegisterOutboundRequest, 'scope' | 'content'> {
133
+ readonly scope: ImRealDeliveryScope;
134
+ readonly file: ImMediaInput;
135
+ /** Provider-produced immutable file reference; omitted by manual uploads. */
136
+ readonly sourceFile?: import('@deepseek-ai/dsh-attachment').FileAttachmentRef;
137
+ readonly routeBinding?: ImOutboundRouteBinding;
138
+ }
105
139
  /** Text, image placeholder, or honest unsupported-message presentation. */
106
140
  export type ImMessageContent = {
141
+ readonly text: string;
142
+ readonly format: 'media';
143
+ readonly media: ImMediaDescription;
144
+ readonly quote?: ImMessageQuote;
145
+ } | {
107
146
  readonly text: string;
108
147
  readonly format: 'text' | 'markdown';
109
148
  readonly quote?: ImMessageQuote;
@@ -120,6 +159,8 @@ export type ImMessageContent = {
120
159
  };
121
160
  /** One normalized inbound provider message before persistence. */
122
161
  export interface ImInboundMessageInput {
162
+ /** Per-message platform facts; absence must not be filled with a later assignment. */
163
+ readonly uploadIdentity?: import('./upload-types.ts').ImUploadIdentity;
123
164
  readonly externalMessageId: string;
124
165
  readonly sender: ImSenderAttribution;
125
166
  readonly content: ImMessageContent;
@@ -146,8 +187,24 @@ export interface ImAdmissionMessageSource {
146
187
  readonly sender: ImSenderAttribution;
147
188
  readonly occurredAt: string;
148
189
  }
190
+ /** Optional audit metadata; absent legacy data means no annotation was recorded. */
191
+ export interface ImAdmissionAnnotationSource {
192
+ readonly id: ImAdmissionAnnotationId;
193
+ readonly annotatorId: ImAdmissionAnnotatorId;
194
+ readonly version: string;
195
+ readonly status: 'success' | 'timeout' | 'failed';
196
+ }
197
+ /** Analysis frozen before Session append; only successful results contain model-visible text. */
198
+ export type ImAdmissionAnnotation = ImAdmissionAnnotationSource & ({
199
+ readonly status: 'success';
200
+ readonly text: string;
201
+ } | {
202
+ readonly status: 'timeout' | 'failed';
203
+ });
149
204
  /** Frozen route and message evidence recorded with one Session user/message. */
150
205
  export interface ImAgentAdmissionSource {
206
+ /** Analysis identity and disposition; legacy sources omit this field. */
207
+ readonly annotations?: readonly ImAdmissionAnnotationSource[];
151
208
  readonly admissionId: ImAdmissionId;
152
209
  /** Customer pause generation at automatic input admission; absent legacy values mean zero. */
153
210
  readonly handlingEpoch?: number;
@@ -180,6 +237,8 @@ export interface ImAgentTaskView {
180
237
  }
181
238
  /** Durable inbound row safe for history UI and model tools. */
182
239
  export interface ImInboundMessageView {
240
+ /** Captured platform facts, absent in legacy stored messages. */
241
+ readonly uploadIdentity?: import('./upload-types.ts').ImUploadIdentity;
183
242
  readonly messageId: ImMessageId;
184
243
  readonly scopeId: ImScopeId;
185
244
  readonly externalMessageId: string;
@@ -226,6 +285,8 @@ export interface ImIngestInboundPageRequest {
226
285
  readonly observedCursor: string | null;
227
286
  readonly nextCursor: string | null;
228
287
  readonly presentation?: ImConversationPresentation;
288
+ /** Latest platform-provided direct peer identity; absence retains the stored identity. */
289
+ readonly directRecipient?: ImDirectRecipient;
229
290
  readonly messages: readonly ImInboundMessageInput[];
230
291
  }
231
292
  /** Receipt stored with the conversation aggregate before a page call resolves. */
@@ -368,6 +429,7 @@ export interface ImOutboundView {
368
429
  readonly scopeId: ImScopeId;
369
430
  readonly intent: ImOutboundIntent;
370
431
  readonly content: ImMessageContent;
432
+ readonly media?: ImMediaPreparationView;
371
433
  readonly status: ImOutboundStatus;
372
434
  /** Scope-local admission order, independent of timestamps. */
373
435
  readonly sequenceNumber: number;
@@ -479,7 +541,12 @@ export interface ImSendManualMessageRequest {
479
541
  */
480
542
  readonly accountId?: ImAccountId;
481
543
  readonly requestId: ImOutboundRequestId;
544
+ /** When supplied, dispatch requires the same epoch and a durable customer pause; changes reject before provider submission. */
545
+ readonly expectedHandlingEpoch?: number;
546
+ /** Text-only message; empty when media is present. The composer sends accompanying text as another intent. */
482
547
  readonly text: string;
548
+ /** One selected private file, staged only after Send. */
549
+ readonly media?: ImMediaInput;
483
550
  }
484
551
  /** Query or confirmation input for a manual message bound through its Session or scope. */
485
552
  export interface ImManualMessageQueryRequest {
@@ -490,7 +557,7 @@ export interface ImManualMessageQueryRequest {
490
557
  readonly accountId?: ImAccountId;
491
558
  readonly requestId: ImOutboundRequestId;
492
559
  }
493
- /** Explicit retry creates a distinct durable intent linked to an uncertain predecessor. */
560
+ /** Explicit retry links a distinct intent to an uncertain text message or a known failed media message; unknown media is not retryable. */
494
561
  export interface ImRetryManualMessageRequest {
495
562
  readonly sessionId: SessionId;
496
563
  /** Wangwang conversation identity; used when {@link sessionId} has no Agent task. */
@@ -559,6 +626,25 @@ export interface ImOutboundPage {
559
626
  readonly hasMore: boolean;
560
627
  readonly nextBeforeSequenceNumber?: number;
561
628
  }
629
+ /** Read-only records of one conversation document, valid only during the projection call that receives them. */
630
+ export interface ImConversationRecords {
631
+ readonly scope: ImDeliveryScope;
632
+ readonly cursor: ImConversationCursor;
633
+ /** @param request - bounded history cursor and origin filter. @returns the page `queryHistory` returns for this document. */
634
+ queryHistory(request: Omit<ImHistoryQueryRequest, 'scope'>): ImHistoryPage;
635
+ /** @param request - bounded outbox history cursor. @returns the page `queryOutbound` returns for this document. */
636
+ queryOutbound(request: Omit<ImOutboundQueryRequest, 'scope'>): ImOutboundPage;
637
+ }
638
+ /** Consumer values derived from conversation documents and refreshed by every committed document write. */
639
+ export interface ImConversationProjection<T> {
640
+ /**
641
+ * @param scope - complete conversation identity.
642
+ * @returns the value for the stored conversation, or for an empty one when none is stored; parses the document only when this projection has not derived it yet.
643
+ */
644
+ get(scope: ImDeliveryScope): T;
645
+ /** Stop deriving and release the values. Idempotent; `get` throws afterwards. */
646
+ dispose(): void;
647
+ }
562
648
  /** Result of comparing one durable Session log with pending IM submissions. */
563
649
  export interface ImSessionReconciliationResult {
564
650
  readonly sessionId: SessionId;
@@ -593,6 +679,14 @@ export interface ImCustomerListRequest {
593
679
  readonly customerKey: string;
594
680
  };
595
681
  }
682
+ /** Customer Session lookup with the same source, search, sorting and cursor rules as the full customer list. */
683
+ export type ImCustomerSessionListRequest = Omit<ImCustomerListRequest, 'readerId'>;
684
+ /** Customer identity and latest Session without unread or handling projections. */
685
+ export type ImCustomerSessionRow = Pick<ImCustomerRow, 'customerKey' | 'kind' | 'lastActivity' | 'latestSessionId'>;
686
+ /** Keyset page containing only customer Session references. */
687
+ export interface ImCustomerSessionPage extends Omit<ImCustomerListPage, 'items'> {
688
+ readonly items: readonly ImCustomerSessionRow[];
689
+ }
596
690
  /** One customer row projected from the rebuildable conversation index. */
597
691
  export interface ImCustomerRow {
598
692
  readonly customerKey: string;
@@ -641,6 +735,8 @@ export interface ImHandlingControl {
641
735
  }
642
736
  /** Persist or clear one scope's automatic-handling pause. */
643
737
  export interface ImSetHandlingControlRequest {
738
+ /** Successful product control result; omitted for automatic safety pauses. */
739
+ readonly uploadEvent?: Omit<import('./upload-types.ts').ImUploadControlResult, 'target' | 'accountId'>;
644
740
  readonly scopeId: ImScopeId;
645
741
  readonly automaticHandlingPaused: boolean;
646
742
  readonly reason?: string;
@@ -1,9 +1,14 @@
1
+ import type { ImUploadBinding } from './upload-types.ts';
2
+ import type { ImMessageUploadQueue } from './message-upload.ts';
1
3
  import type { ImDeliverySqlite } from './delivery-sqlite.ts';
2
- import type { ImBeginOutboundAttemptRequest, ImBeginOutboundAttemptResult, ImCancelPendingAiRequest, ImCommitProviderCursorRequest, ImConversationCursor, ImConversationPresentation, ImDeliveryOperationId, ImDeliveryScope, ImGetOutboundRequest, ImHistoryPage, ImHistoryQueryRequest, ImImportJsonlHistoryRequest, ImImportJsonlHistoryResult, ImInboundMessageView, ImInboundOperationQuery, ImInboundPageResult, ImIngestInboundPageRequest, ImMarkSubmittedRequest, ImMarkSubmittedResult, ImMessageId, ImMessageSource, ImOutboundAttemptId, ImOutboundPage, ImOutboundQueryRequest, ImOutboundRequestId, ImOutboundRouteBinding, ImOutboundView, ImPendingInboundRequest, ImProviderCursorCommitResult, ImProviderCursorId, ImProviderCursorOperationQuery, ImProviderCursorOwner, ImProviderCursorView, ImRealDeliveryScope, ImRegisterOutboundRequest, ImScopeId, ImSettleOutboundAttemptRequest, ImSettleSimulationOutboundRequest } from './delivery-types.ts';
4
+ import type { ImBeginOutboundAttemptRequest, ImBeginOutboundAttemptResult, ImCancelPendingAiRequest, ImCommitProviderCursorRequest, ImConversationCursor, ImConversationPresentation, ImConversationProjection, ImConversationRecords, ImDeliveryOperationId, ImDeliveryScope, ImGetOutboundRequest, ImHistoryPage, ImHistoryQueryRequest, ImImportJsonlHistoryRequest, ImImportJsonlHistoryResult, ImInboundMessageView, ImInboundOperationQuery, ImInboundPageResult, ImIngestInboundPageRequest, ImMarkSubmittedRequest, ImMarkSubmittedResult, ImMessageId, ImMessageSource, ImOutboundAttemptId, ImOutboundPage, ImOutboundQueryRequest, ImOutboundRequestId, ImOutboundRouteBinding, ImOutboundView, ImPendingInboundRequest, ImProviderCursorCommitResult, ImProviderCursorId, ImProviderCursorOperationQuery, ImProviderCursorOwner, ImProviderCursorView, ImRealDeliveryScope, ImRegisterOutboundRequest, ImScopeId, ImSettleOutboundAttemptRequest, ImSettleSimulationOutboundRequest } from './delivery-types.ts';
3
5
  import type { ImAccountId, ImPlatform, ImRouteResolution } from './types.ts';
4
6
  interface ImDeliveryHost {
7
+ uploadTarget?: ImUploadBinding['target'];
8
+ uploadIdentity?: ImUploadBinding['identity'];
9
+ onUploadReady?(): void;
5
10
  automaticHandlingBlocked(scopeId: ImScopeId): boolean;
6
- inspectAccount(id: ImAccountId): {
11
+ inspectAccount(id: ImAccountId, scope?: ImDeliveryScope): {
7
12
  readonly platform: ImPlatform;
8
13
  readonly paused: boolean;
9
14
  readonly revision: import('./types.ts').ImRevision;
@@ -16,6 +21,8 @@ interface ImDeliveryHost {
16
21
  readonly messageIds?: readonly ImMessageId[];
17
22
  readonly requestId?: ImOutboundRequestId;
18
23
  }): void;
24
+ /** A provider cursor advanced after its sqlite write. */
25
+ onProviderCursorApplied?(owner: ImProviderCursorOwner): void;
19
26
  }
20
27
  /**
21
28
  * Encode every scope component in an ordered JSON tuple before hashing.
@@ -35,13 +42,20 @@ export declare class ImDeliveryStore {
35
42
  private readonly host;
36
43
  private readonly tails;
37
44
  private readonly cursorTails;
45
+ private readonly cursors;
38
46
  /** @param records - sqlite delivery records. @param host - account policy and post-commit publisher. */
39
47
  constructor(records: ImDeliverySqlite, host: ImDeliveryHost);
40
- /** Mark interrupted platform calls uncertain before new work can claim them. */
48
+ /** Recover interrupted sends and unfinished file preparations before publishing the runtime, independently of provider availability. */
41
49
  recoverInterruptedAttempts(): Promise<void>;
42
- ingestInboundPage(request: ImIngestInboundPageRequest): Promise<ImInboundPageResult>;
50
+ /** @param request - Validated page and scope. @param receiptTime - Instance script time; absent uses the ingestion clock. @returns The durable page receipt. */
51
+ ingestInboundPage(request: ImIngestInboundPageRequest, receiptTime?: string): Promise<ImInboundPageResult>;
43
52
  queryInboundOperation(scope: ImDeliveryScope, operationId: ImDeliveryOperationId): ImInboundOperationQuery;
44
53
  getConversationCursor(scope: ImDeliveryScope): ImConversationCursor;
54
+ /**
55
+ * @param project - pure function of one conversation's records.
56
+ * @returns values derived at each document parse or write; a conversation without a document projects its initial records on every read.
57
+ */
58
+ projectConversations<T extends object>(project: (records: ImConversationRecords) => T): ImConversationProjection<T>;
45
59
  /** @param scope - durable conversation. @returns last admitted presentation, including the assigned service account when known. */
46
60
  getConversationPresentation(scope: ImDeliveryScope): ImConversationPresentation | undefined;
47
61
  getProviderCursor(owner: ImProviderCursorOwner): ImProviderCursorView;
@@ -65,14 +79,18 @@ export declare class ImDeliveryStore {
65
79
  getInbound(scope: ImDeliveryScope, messageId: ImMessageId): ImInboundMessageView;
66
80
  markSubmitted(request: ImMarkSubmittedRequest): Promise<ImMarkSubmittedResult>;
67
81
  importJsonlHistory(request: ImImportJsonlHistoryRequest): Promise<ImImportJsonlHistoryResult>;
68
- registerOutbound(request: ImRegisterOutboundRequest, frozenBinding?: ImOutboundRouteBinding): Promise<ImOutboundView>;
82
+ /** @param request - Scoped send intent. @param frozenBinding - Route observed by the Agent task. @param clock - Time sampled when the intent is stored. @returns Durable outbox intent. */
83
+ registerOutbound(request: ImRegisterOutboundRequest, frozenBinding?: ImOutboundRouteBinding, clock?: () => number): Promise<ImOutboundView>;
84
+ /** @param request - owning real outbox intent. @param media - durable upload display facts. @returns the updated row after its preparation commit. */
85
+ recordMediaPreparation(request: ImGetOutboundRequest, media: NonNullable<ImOutboundView['media']>): Promise<ImOutboundView>;
69
86
  beginOutboundAttempt(request: ImBeginOutboundAttemptRequest): Promise<ImBeginOutboundAttemptResult>;
70
87
  /** @param request - Owning attempt. @returns Whether its frozen route and handling epoch still authorize an external submission. */
71
88
  authorizeOutboundSubmission(request: ImBeginOutboundAttemptRequest & {
72
89
  readonly attemptId: ImOutboundAttemptId;
73
90
  }): boolean;
74
91
  settleOutboundAttempt(request: ImSettleOutboundAttemptRequest): Promise<ImOutboundView>;
75
- settleSimulationOutbound(request: ImSettleSimulationOutboundRequest): Promise<ImOutboundView>;
92
+ /** @param request - Local simulation intent. @param clock - Time sampled when the send settles. @returns Durable local receipt. */
93
+ settleSimulationOutbound(request: ImSettleSimulationOutboundRequest, clock?: () => number): Promise<ImOutboundView>;
76
94
  getOutbound(request: ImGetOutboundRequest): ImOutboundView | undefined;
77
95
  queryOutbound(request: ImOutboundQueryRequest): ImOutboundPage;
78
96
  cancelPendingAi(request: ImCancelPendingAiRequest): Promise<readonly ImOutboundView[]>;
@@ -85,6 +103,11 @@ export declare class ImDeliveryStore {
85
103
  private lookup;
86
104
  private require;
87
105
  private mutate;
106
+ /** Apply one transform to the stored or initial aggregate; the caller already holds the scope's queue. */
107
+ private applyMutation;
108
+ /** @param queue - external durable queue. @returns completion after every journal transfer is synced and removed from SQLite. */
109
+ transferUploads(queue: ImMessageUploadQueue): Promise<void>;
110
+ private persistMutation;
88
111
  private enqueue;
89
112
  private enqueueCursor;
90
113
  }
@@ -1,5 +1,5 @@
1
1
  /** Stable IM runtime configuration failure codes. */
2
- export type ImRuntimeErrorCode = 'IM_ACCOUNT_NOT_FOUND' | 'IM_ACCOUNT_EXISTS' | 'IM_ACCOUNT_SETUP_CONFIRMED' | 'IM_ACCOUNT_SETUP_NOT_FOUND' | 'IM_IDENTITY_MISMATCH' | 'IM_DELIVERY_OPERATION_REUSED' | 'IM_DELIVERY_SCOPE_INVALID' | 'IM_JSONL_INVALID' | 'IM_LOGIN_ATTEMPT_NOT_FOUND' | 'IM_LOGIN_UNAVAILABLE' | 'IM_MANUAL_ACCOUNT_UNAVAILABLE' | 'IM_MANUAL_MESSAGE_INVALID' | 'IM_MANUAL_RETRY_INVALID' | 'IM_MESSAGE_ALREADY_SUBMITTED' | 'IM_MESSAGE_NOT_FOUND' | 'IM_OPERATION_REUSED' | 'IM_OUTBOUND_ATTEMPT_MISMATCH' | 'IM_OUTBOUND_NOT_FOUND' | 'IM_OUTBOUND_REQUEST_REUSED' | 'IM_OUTBOUND_STATE_INVALID' | 'IM_ROUTE_INVALID' | 'IM_ROUTE_POLICY_DENIED' | 'IM_REAL_SESSION_NOT_FOUND' | 'IM_SESSION_PERSISTENCE_UNAVAILABLE' | 'IM_SIMULATION_INSTANCE_ACTIVE' | 'IM_SIMULATION_INSTANCE_NOT_FOUND' | 'IM_SIMULATION_INSTANCE_NOT_RUNNING' | 'IM_SIMULATION_SESSION_INVALID' | 'IM_SIMULATION_TARGET_INVALID';
2
+ export type ImRuntimeErrorCode = 'IM_ACCOUNT_REPLACED' | 'IM_ACCOUNT_CHANGED' | 'IM_MEDIA_PREPARATION_UNKNOWN' | 'IM_MEDIA_UPLOAD_FAILED' | 'IM_MEDIA_DOWNLOAD_FAILED' | 'IM_MEDIA_CONVERSATION_FAILED' | 'IM_MEDIA_FILE_UPLOAD_NOT_READY' | 'IM_MEDIA_INVALID' | 'IM_MEDIA_UNAVAILABLE' | 'IM_AUTHORIZATION_REQUIRED' | 'IM_EVALUATION_UNAVAILABLE' | 'IM_ACCOUNT_NOT_FOUND' | 'IM_ACCOUNT_EXISTS' | 'IM_ACCOUNT_SETUP_CONFIRMED' | 'IM_ACCOUNT_SETUP_NOT_FOUND' | 'IM_IDENTITY_MISMATCH' | 'IM_DELIVERY_OPERATION_REUSED' | 'IM_DELIVERY_SQLITE_BUSY' | 'IM_DELIVERY_SCOPE_INVALID' | 'IM_JSONL_INVALID' | 'IM_LOGIN_ATTEMPT_NOT_FOUND' | 'IM_LOGIN_UNAVAILABLE' | 'IM_MANUAL_ACCOUNT_UNAVAILABLE' | 'IM_MANUAL_MESSAGE_INVALID' | 'IM_MANUAL_HANDLING_CHANGED' | 'IM_MANUAL_RETRY_INVALID' | 'IM_MESSAGE_ALREADY_SUBMITTED' | 'IM_MESSAGE_NOT_FOUND' | 'IM_OPERATION_REUSED' | 'IM_OUTBOUND_ATTEMPT_MISMATCH' | 'IM_OUTBOUND_NOT_FOUND' | 'IM_OUTBOUND_REQUEST_REUSED' | 'IM_OUTBOUND_STATE_INVALID' | 'IM_ROUTE_INVALID' | 'IM_ROUTE_POLICY_DENIED' | 'IM_REAL_SESSION_NOT_FOUND' | 'IM_SESSION_PERSISTENCE_UNAVAILABLE' | 'IM_SIMULATION_CLOCK_REGISTERED' | 'IM_SIMULATION_INSTANCE_ACTIVE' | 'IM_SIMULATION_INSTANCE_NOT_FOUND' | 'IM_SIMULATION_INSTANCE_NOT_RUNNING' | 'IM_SIMULATION_SESSION_INVALID' | 'IM_SIMULATION_TARGET_INVALID';
3
3
  /** Structured IM runtime failure for requests that cannot produce a durable receipt. */
4
4
  export declare class ImRuntimeError extends Error {
5
5
  readonly code: ImRuntimeErrorCode;