@ixo/oracle-runtime-workers 0.2.0

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 (215) hide show
  1. package/README.md +110 -0
  2. package/package.json +77 -0
  3. package/src/attachments/classify.ts +114 -0
  4. package/src/attachments/content-blocks.ts +51 -0
  5. package/src/attachments/download.ts +235 -0
  6. package/src/attachments/extract.ts +241 -0
  7. package/src/attachments/index.ts +84 -0
  8. package/src/attachments/magic.ts +133 -0
  9. package/src/attachments/pipeline.ts +409 -0
  10. package/src/attachments/retention.ts +205 -0
  11. package/src/attachments/route.ts +41 -0
  12. package/src/attachments/sandbox-archive.ts +90 -0
  13. package/src/attachments/sanitize.ts +35 -0
  14. package/src/attachments/types.ts +86 -0
  15. package/src/attachments/view.ts +107 -0
  16. package/src/core/env.ts +200 -0
  17. package/src/core/index.ts +513 -0
  18. package/src/core/llm.ts +842 -0
  19. package/src/core/main-agent-types.ts +123 -0
  20. package/src/core/main-agent.ts +399 -0
  21. package/src/core/manifest.ts +365 -0
  22. package/src/core/meta-tools.ts +232 -0
  23. package/src/core/middlewares/byo-history-sanitizer.ts +165 -0
  24. package/src/core/middlewares/capability-gate.ts +76 -0
  25. package/src/core/middlewares/index.ts +18 -0
  26. package/src/core/middlewares/page-context.ts +98 -0
  27. package/src/core/middlewares/safety-guardrail.ts +117 -0
  28. package/src/core/middlewares/summarization.ts +99 -0
  29. package/src/core/middlewares/tool-repetition-guard.ts +125 -0
  30. package/src/core/middlewares/tool-validation.ts +83 -0
  31. package/src/core/middlewares/work-status.ts +57 -0
  32. package/src/core/openrouter-pricing.ts +124 -0
  33. package/src/core/plugin-loader.ts +192 -0
  34. package/src/core/plugins/index.ts +6 -0
  35. package/src/core/plugins/skills/index.ts +8 -0
  36. package/src/core/plugins/skills/skills-tools.ts +356 -0
  37. package/src/core/plugins/skills/skills.plugin.ts +113 -0
  38. package/src/core/plugins/weather/index.ts +8 -0
  39. package/src/core/plugins/weather/weather-client.ts +153 -0
  40. package/src/core/plugins/weather/weather-middleware.ts +27 -0
  41. package/src/core/plugins/weather/weather-sub-agent.ts +65 -0
  42. package/src/core/plugins/weather/weather-tools.ts +103 -0
  43. package/src/core/plugins/weather/weather.plugin.ts +227 -0
  44. package/src/core/prompt-composer.ts +469 -0
  45. package/src/core/registries.ts +642 -0
  46. package/src/core/runtime-context.ts +619 -0
  47. package/src/core/state.ts +108 -0
  48. package/src/core/sub-agent-fallback.ts +187 -0
  49. package/src/core/subagent-as-tool.ts +322 -0
  50. package/src/core/template.ts +245 -0
  51. package/src/core/utils.ts +246 -0
  52. package/src/core/wrap-plugin-tool.ts +100 -0
  53. package/src/do/ambient.ts +266 -0
  54. package/src/do/contracts.ts +562 -0
  55. package/src/do/debug-timers.ts +120 -0
  56. package/src/do/fresh-stub.ts +32 -0
  57. package/src/do/gateway-retry.ts +73 -0
  58. package/src/do/secrets-adapter.ts +32 -0
  59. package/src/do/size-alerts.ts +76 -0
  60. package/src/do/sse-stream.ts +472 -0
  61. package/src/do/transcript.ts +192 -0
  62. package/src/do/turn-body.ts +84 -0
  63. package/src/do/turn-metadata.ts +146 -0
  64. package/src/do/ucan-service.ts +339 -0
  65. package/src/do/user-oracle-do.ts +3035 -0
  66. package/src/gateway-worker.ts +82 -0
  67. package/src/index.ts +237 -0
  68. package/src/llm/byo-adapter.ts +59 -0
  69. package/src/llm/byo-catalog.ts +520 -0
  70. package/src/llm/byo-client.ts +280 -0
  71. package/src/llm/byo-routes.ts +255 -0
  72. package/src/llm/byo-service.ts +836 -0
  73. package/src/llm/chatgpt-oauth.ts +430 -0
  74. package/src/llm/provider-error.ts +283 -0
  75. package/src/matrix/gateway-do.ts +918 -0
  76. package/src/matrix/ingest.ts +209 -0
  77. package/src/matrix/replay-format.ts +34 -0
  78. package/src/matrix/reply-chain.ts +109 -0
  79. package/src/matrix/room-state-codec.ts +115 -0
  80. package/src/matrix/user-homeserver.ts +106 -0
  81. package/src/matrix/work-status.ts +294 -0
  82. package/src/memory/session-history-indexer.ts +328 -0
  83. package/src/owner-store/ixo-vfs-store.ts +570 -0
  84. package/src/owner-store/matrix-media-store.ts +136 -0
  85. package/src/owner-store/migrating-store.ts +185 -0
  86. package/src/owner-store/owner-copy-errors.ts +175 -0
  87. package/src/owner-store/retry.ts +45 -0
  88. package/src/owner-store/tus-upload.ts +333 -0
  89. package/src/owner-store/types.ts +0 -0
  90. package/src/plugin-api/define-plugin.ts +45 -0
  91. package/src/plugin-api/index.ts +56 -0
  92. package/src/plugin-api/oracle-plugin.ts +145 -0
  93. package/src/plugin-api/tool-helper.ts +82 -0
  94. package/src/plugin-api/types.ts +816 -0
  95. package/src/plugin-api/ucan-errors.ts +16 -0
  96. package/src/plugins/agui/agui-agent.ts +115 -0
  97. package/src/plugins/agui/agui.plugin.ts +148 -0
  98. package/src/plugins/agui/index.ts +2 -0
  99. package/src/plugins/attachments/attachments.plugin.ts +54 -0
  100. package/src/plugins/attachments/index.ts +6 -0
  101. package/src/plugins/attachments/view-attachment-tool.ts +91 -0
  102. package/src/plugins/base64.ts +26 -0
  103. package/src/plugins/composio/composio-tools.ts +365 -0
  104. package/src/plugins/composio/composio-ucan.ts +43 -0
  105. package/src/plugins/composio/composio.plugin.ts +214 -0
  106. package/src/plugins/composio/index.ts +15 -0
  107. package/src/plugins/domain-indexer/domain-indexer-agent.ts +57 -0
  108. package/src/plugins/domain-indexer/domain-indexer-tools.ts +253 -0
  109. package/src/plugins/domain-indexer/domain-indexer.plugin.ts +100 -0
  110. package/src/plugins/domain-indexer/index.ts +3 -0
  111. package/src/plugins/editor/blocknote-bridge.ts +120 -0
  112. package/src/plugins/editor/content-session.ts +331 -0
  113. package/src/plugins/editor/content-tools.ts +686 -0
  114. package/src/plugins/editor/document-model.ts +0 -0
  115. package/src/plugins/editor/editor-access-denied-tool.ts +58 -0
  116. package/src/plugins/editor/editor-agent.ts +34 -0
  117. package/src/plugins/editor/editor-config.ts +64 -0
  118. package/src/plugins/editor/editor-mx.ts +128 -0
  119. package/src/plugins/editor/editor.plugin.ts +143 -0
  120. package/src/plugins/editor/failures.ts +154 -0
  121. package/src/plugins/editor/index.ts +31 -0
  122. package/src/plugins/editor/jsdom-shim.ts +55 -0
  123. package/src/plugins/editor/prompts.ts +59 -0
  124. package/src/plugins/editor/prop-policy.ts +147 -0
  125. package/src/plugins/editor/provider.ts +386 -0
  126. package/src/plugins/editor/room-membership.ts +95 -0
  127. package/src/plugins/editor/standalone-editor-tool.ts +200 -0
  128. package/src/plugins/firecrawl/firecrawl-agent.ts +93 -0
  129. package/src/plugins/firecrawl/firecrawl-tools.ts +220 -0
  130. package/src/plugins/firecrawl/firecrawl.plugin.ts +105 -0
  131. package/src/plugins/firecrawl/index.ts +15 -0
  132. package/src/plugins/flows/action-metadata.ts +3505 -0
  133. package/src/plugins/flows/actions.ts +190 -0
  134. package/src/plugins/flows/edit.ts +314 -0
  135. package/src/plugins/flows/errors.ts +38 -0
  136. package/src/plugins/flows/explain.ts +43 -0
  137. package/src/plugins/flows/flow-doc.ts +111 -0
  138. package/src/plugins/flows/flows.plugin.ts +129 -0
  139. package/src/plugins/flows/forms.ts +330 -0
  140. package/src/plugins/flows/index.ts +2 -0
  141. package/src/plugins/flows/linkage.ts +147 -0
  142. package/src/plugins/flows/port-types.ts +53 -0
  143. package/src/plugins/flows/prompts.ts +65 -0
  144. package/src/plugins/flows/read.ts +392 -0
  145. package/src/plugins/flows/references.ts +66 -0
  146. package/src/plugins/flows/test-support.ts +165 -0
  147. package/src/plugins/flows/tools/authoring.ts +516 -0
  148. package/src/plugins/flows/tools/discovery.ts +119 -0
  149. package/src/plugins/flows/tools/forms.ts +142 -0
  150. package/src/plugins/flows/tools/inspect.ts +165 -0
  151. package/src/plugins/flows/tools/linkage.ts +117 -0
  152. package/src/plugins/flows/tools/settings.ts +184 -0
  153. package/src/plugins/flows/translator.ts +328 -0
  154. package/src/plugins/flows/types.ts +210 -0
  155. package/src/plugins/flows/ydoc-helpers.ts +543 -0
  156. package/src/plugins/index.ts +70 -0
  157. package/src/plugins/mcp-call-timeout.ts +38 -0
  158. package/src/plugins/mcp-tool-adapter.ts +87 -0
  159. package/src/plugins/memory/index.ts +17 -0
  160. package/src/plugins/memory/memory-tools.ts +463 -0
  161. package/src/plugins/memory/memory-ucan.ts +51 -0
  162. package/src/plugins/memory/memory.plugin.ts +122 -0
  163. package/src/plugins/memory/types.ts +147 -0
  164. package/src/plugins/portal/action-log.ts +34 -0
  165. package/src/plugins/portal/index.ts +2 -0
  166. package/src/plugins/portal/portal.plugin.ts +203 -0
  167. package/src/plugins/sandbox/index.ts +34 -0
  168. package/src/plugins/sandbox/sandbox-bridge.ts +295 -0
  169. package/src/plugins/sandbox/sandbox-mcp.ts +127 -0
  170. package/src/plugins/sandbox/sandbox-write-blob.ts +103 -0
  171. package/src/plugins/sandbox/sandbox.plugin.ts +599 -0
  172. package/src/plugins/tasks/index.ts +17 -0
  173. package/src/plugins/tasks/manifest.ts +81 -0
  174. package/src/plugins/tasks/middleware.ts +219 -0
  175. package/src/plugins/tasks/tasks-tools.ts +414 -0
  176. package/src/plugins/tasks/tasks.plugin.ts +74 -0
  177. package/src/plugins/ucan-failure.ts +133 -0
  178. package/src/plugins/user-preferences/index.ts +22 -0
  179. package/src/plugins/user-preferences/schema.ts +31 -0
  180. package/src/plugins/user-preferences/user-preferences-store.ts +172 -0
  181. package/src/plugins/user-preferences/user-preferences-tool.ts +131 -0
  182. package/src/plugins/user-preferences/user-preferences.plugin.ts +137 -0
  183. package/src/plugins/vfs/index.ts +40 -0
  184. package/src/plugins/vfs/vfs-auth.ts +56 -0
  185. package/src/plugins/vfs/vfs-client.ts +655 -0
  186. package/src/plugins/vfs/vfs-content.ts +181 -0
  187. package/src/plugins/vfs/vfs-errors.ts +163 -0
  188. package/src/plugins/vfs/vfs-sandbox-tools.ts +272 -0
  189. package/src/plugins/vfs/vfs-tools.ts +635 -0
  190. package/src/plugins/vfs/vfs.plugin.ts +201 -0
  191. package/src/realtime/frontend-call-registry.ts +163 -0
  192. package/src/realtime/realtime-endpoint.ts +619 -0
  193. package/src/realtime/session-socket-hub.ts +249 -0
  194. package/src/realtime/socket-io-codec.ts +187 -0
  195. package/src/secrets/jwe.ts +74 -0
  196. package/src/secrets/pin-cipher.ts +43 -0
  197. package/src/secrets/secrets-service.ts +319 -0
  198. package/src/shell/app.ts +564 -0
  199. package/src/shell/auth.ts +290 -0
  200. package/src/sqlite/blob-codec.ts +100 -0
  201. package/src/sqlite/blob-compactor.ts +146 -0
  202. package/src/sqlite/cache-config.ts +45 -0
  203. package/src/sqlite/database.ts +643 -0
  204. package/src/sqlite/do-vfs.ts +1485 -0
  205. package/src/sqlite/index.ts +61 -0
  206. package/src/sqlite/serialization.ts +370 -0
  207. package/src/sqlite/sessions-store.ts +311 -0
  208. package/src/sqlite/sqlite-saver.ts +919 -0
  209. package/src/sqlite/vacuum-policy.ts +77 -0
  210. package/src/sqlite/wa-sqlite-loader.ts +123 -0
  211. package/src/sqlite/wasm.d.ts +10 -0
  212. package/src/tasks/schedule.ts +174 -0
  213. package/src/tasks/scheduler.ts +753 -0
  214. package/src/tasks/spec.ts +125 -0
  215. package/src/tasks/store.ts +351 -0
@@ -0,0 +1,61 @@
1
+ export {
2
+ loadSqlite,
3
+ sqliteVersion,
4
+ type SqliteRuntime,
5
+ type WaSqliteModule,
6
+ } from './wa-sqlite-loader';
7
+ export {
8
+ DoVfs,
9
+ DoVfsError,
10
+ normalizeJournalModeHeader,
11
+ VFS_PAGE_SIZE,
12
+ DEFAULT_CACHE_PAGES,
13
+ type DoVfsOptions,
14
+ type DoVfsStats,
15
+ type VfsSnapshot,
16
+ } from './do-vfs';
17
+ export {
18
+ parseChunkCacheBytes,
19
+ cachePagesForBytes,
20
+ DEFAULT_CHUNK_CACHE_BYTES,
21
+ } from './cache-config';
22
+ export {
23
+ shouldVacuum,
24
+ vacuumWanted,
25
+ VACUUM_IDLE_MS,
26
+ VACUUM_MIN_INTERVAL_MS,
27
+ type VacuumPolicyInput,
28
+ type VacuumVerdict,
29
+ } from './vacuum-policy';
30
+ export {
31
+ DoSqliteDatabase,
32
+ vfsNameForObject,
33
+ type DoSqliteContext,
34
+ type DoSqliteOpenOptions,
35
+ type RunResult,
36
+ type SqlParam,
37
+ type SqlParams,
38
+ type SqlRow,
39
+ type SqlValue,
40
+ } from './database';
41
+ export {
42
+ SqliteSaver,
43
+ DEFAULT_MAX_CHECKPOINTS_PER_THREAD,
44
+ PRUNE_SLACK,
45
+ type SqliteSaverOptions,
46
+ } from './sqlite-saver';
47
+ export {
48
+ cleanAdditionalKwargs,
49
+ stringify,
50
+ type AttachmentMeta,
51
+ type CleanAdditionalKwargs,
52
+ type ReasoningDetail,
53
+ } from './serialization';
54
+ export {
55
+ SessionsStore,
56
+ UNTITLED_SESSION,
57
+ type CreateSessionInput,
58
+ type ListSessionsResult,
59
+ type SessionRecord,
60
+ type TouchSessionInput,
61
+ } from './sessions-store';
@@ -0,0 +1,370 @@
1
+ /**
2
+ * Message serialization helpers shared with the Node runtime's
3
+ * `@ixo/sqlite-saver` (ported from its `utils.ts`). The wire format of the
4
+ * `messages.message` column must stay byte-compatible so a database written
5
+ * by either runtime loads in the other:
6
+ *
7
+ * JSON.stringify(message, replacer = _default)
8
+ *
9
+ * where `message.toJSON()` is LangChain's `Serializable` envelope
10
+ * (`{ lc: 1, type: 'constructor', id: [...], kwargs: {...} }`) and `_default`
11
+ * mirrors `JsonPlusSerializer`'s handling of `undefined`, Set/Map/RegExp/Error
12
+ * and LangGraph `Send` objects. `stringify` is fast-safe-stringify's
13
+ * circular-safe variant, typed.
14
+ */
15
+
16
+ type Replacer = (this: unknown, key: string, value: unknown) => unknown;
17
+
18
+ const CIRCULAR_REPLACE_NODE = '[Circular]';
19
+
20
+ type Restore = {
21
+ parent: Record<string, unknown> | unknown[];
22
+ key: string | number;
23
+ value: unknown;
24
+ descriptor?: PropertyDescriptor;
25
+ };
26
+
27
+ interface GetterReplacement {
28
+ value: unknown;
29
+ key: string | number;
30
+ replacement: string;
31
+ }
32
+
33
+ function isObjectLike(
34
+ value: unknown,
35
+ ): value is Record<string, unknown> | unknown[] {
36
+ return typeof value === 'object' && value !== null;
37
+ }
38
+
39
+ /**
40
+ * `JSON.stringify` that never throws on circular references: every back
41
+ * reference is temporarily replaced by `'[Circular]'` while serializing and
42
+ * restored afterwards, so the input object is left untouched.
43
+ */
44
+ export function stringify(
45
+ obj: unknown,
46
+ replacer?: Replacer,
47
+ spacer?: string | number,
48
+ ): string {
49
+ const restores: Restore[] = [];
50
+ const getterReplacements: GetterReplacement[] = [];
51
+ decirc(obj, '', [], undefined, restores, getterReplacements);
52
+ try {
53
+ const effectiveReplacer =
54
+ getterReplacements.length === 0
55
+ ? replacer
56
+ : replaceGetterValues(replacer, getterReplacements);
57
+ return JSON.stringify(obj, effectiveReplacer, spacer);
58
+ } catch {
59
+ return JSON.stringify(
60
+ '[unable to serialize, circular reference is too complex to analyze]',
61
+ );
62
+ } finally {
63
+ while (restores.length !== 0) {
64
+ const part = restores.pop();
65
+ if (part === undefined) break;
66
+ if (part.descriptor !== undefined) {
67
+ Object.defineProperty(part.parent, part.key, part.descriptor);
68
+ } else if (Array.isArray(part.parent)) {
69
+ part.parent[Number(part.key)] = part.value;
70
+ } else {
71
+ part.parent[String(part.key)] = part.value;
72
+ }
73
+ }
74
+ }
75
+ }
76
+
77
+ function setReplace(
78
+ replacement: string,
79
+ value: unknown,
80
+ key: string | number,
81
+ parent: Record<string, unknown> | unknown[],
82
+ restores: Restore[],
83
+ getterReplacements: GetterReplacement[],
84
+ ): void {
85
+ const descriptor = Object.getOwnPropertyDescriptor(parent, key);
86
+ if (descriptor?.get !== undefined) {
87
+ if (descriptor.configurable) {
88
+ Object.defineProperty(parent, key, { value: replacement });
89
+ restores.push({ parent, key, value, descriptor });
90
+ } else {
91
+ getterReplacements.push({ value, key, replacement });
92
+ }
93
+ return;
94
+ }
95
+ if (Array.isArray(parent)) {
96
+ parent[Number(key)] = replacement;
97
+ } else {
98
+ parent[String(key)] = replacement;
99
+ }
100
+ restores.push({ parent, key, value });
101
+ }
102
+
103
+ function decirc(
104
+ value: unknown,
105
+ key: string | number,
106
+ stack: unknown[],
107
+ parent: Record<string, unknown> | unknown[] | undefined,
108
+ restores: Restore[],
109
+ getterReplacements: GetterReplacement[],
110
+ ): void {
111
+ if (!isObjectLike(value)) return;
112
+ if (stack.includes(value)) {
113
+ if (parent !== undefined)
114
+ setReplace(
115
+ CIRCULAR_REPLACE_NODE,
116
+ value,
117
+ key,
118
+ parent,
119
+ restores,
120
+ getterReplacements,
121
+ );
122
+ return;
123
+ }
124
+ stack.push(value);
125
+ if (Array.isArray(value)) {
126
+ for (let i = 0; i < value.length; i++) {
127
+ decirc(value[i], i, stack, value, restores, getterReplacements);
128
+ }
129
+ } else {
130
+ for (const childKey of Object.keys(value)) {
131
+ decirc(
132
+ value[childKey],
133
+ childKey,
134
+ stack,
135
+ value,
136
+ restores,
137
+ getterReplacements,
138
+ );
139
+ }
140
+ }
141
+ stack.pop();
142
+ }
143
+
144
+ function replaceGetterValues(
145
+ replacer: Replacer | undefined,
146
+ getterReplacements: GetterReplacement[],
147
+ ): Replacer {
148
+ const inner: Replacer = replacer ?? ((_key, value) => value);
149
+ return function (this: unknown, key, value) {
150
+ let effective = value;
151
+ if (getterReplacements.length > 0) {
152
+ for (let i = 0; i < getterReplacements.length; i++) {
153
+ const part = getterReplacements[i];
154
+ if (part !== undefined && part.key === key && part.value === value) {
155
+ effective = part.replacement;
156
+ getterReplacements.splice(i, 1);
157
+ break;
158
+ }
159
+ }
160
+ }
161
+ return inner.call(this, key, effective);
162
+ };
163
+ }
164
+
165
+ interface ConstructorEnvelope {
166
+ lc: 2;
167
+ type: 'constructor';
168
+ id: string[];
169
+ method: string | null;
170
+ args: unknown[];
171
+ kwargs: Record<string, unknown>;
172
+ }
173
+
174
+ function encodeConstructorArgs(
175
+ constructorName: string,
176
+ method?: string,
177
+ args?: unknown[],
178
+ kwargs?: Record<string, unknown>,
179
+ ): ConstructorEnvelope {
180
+ return {
181
+ lc: 2,
182
+ type: 'constructor',
183
+ id: [constructorName],
184
+ method: method ?? null,
185
+ args: args ?? [],
186
+ kwargs: kwargs ?? {},
187
+ };
188
+ }
189
+
190
+ function isSend(
191
+ value: unknown,
192
+ ): value is { lg_name: 'Send'; node: unknown; args: unknown } {
193
+ return (
194
+ isObjectLike(value) && !Array.isArray(value) && value.lg_name === 'Send'
195
+ );
196
+ }
197
+
198
+ /** Replacer applied to every value while serializing a message (mirrors `JsonPlusSerializer`). */
199
+ export function _default(obj: unknown): unknown {
200
+ if (obj === undefined) {
201
+ return { lc: 2, type: 'undefined' };
202
+ }
203
+ if (obj instanceof Set || obj instanceof Map) {
204
+ return encodeConstructorArgs(obj.constructor.name, undefined, [
205
+ Array.from(obj),
206
+ ]);
207
+ }
208
+ if (obj instanceof RegExp) {
209
+ return encodeConstructorArgs('RegExp', undefined, [obj.source, obj.flags]);
210
+ }
211
+ if (obj instanceof Error) {
212
+ return encodeConstructorArgs(obj.constructor.name, undefined, [
213
+ obj.message,
214
+ ]);
215
+ }
216
+ if (isSend(obj)) {
217
+ return { node: obj.node, args: obj.args };
218
+ }
219
+ return obj;
220
+ }
221
+
222
+ export interface AttachmentMeta {
223
+ filename: string;
224
+ mimetype: string;
225
+ size?: number;
226
+ mxcUri?: string;
227
+ eventId?: string;
228
+ category: string;
229
+ }
230
+
231
+ export interface ReasoningDetail {
232
+ type: string;
233
+ text: string;
234
+ }
235
+
236
+ /**
237
+ * The `additional_kwargs` shape persisted on every message row. Same
238
+ * allowlist as the Node runtime so both runtimes read each other's rows.
239
+ */
240
+ export interface CleanAdditionalKwargs {
241
+ msgFromMatrixRoom: boolean;
242
+ timestamp: string;
243
+ oracleName: string;
244
+ /** Provenance marker set by LangChain middlewares (e.g. `"summarization"`). */
245
+ lc_source?: string;
246
+ reasoning?: string;
247
+ reasoningDetails?: ReasoningDetail[];
248
+ attachment?: AttachmentMeta;
249
+ attachments?: AttachmentMeta[];
250
+ // Group-chat speaker + threading metadata (the gating middleware needs these across turns).
251
+ senderDid?: string;
252
+ senderMatrixUserId?: string;
253
+ senderDisplayName?: string;
254
+ threadId?: string;
255
+ eventId?: string;
256
+ isGroupChat?: boolean;
257
+ 'm.mentions'?: { user_ids?: string[] };
258
+ 'm.relates_to'?: { 'm.in_reply_to'?: { event_id: string } };
259
+ [key: string]: unknown;
260
+ }
261
+
262
+ function isRecord(value: unknown): value is Record<string, unknown> {
263
+ return typeof value === 'object' && value !== null && !Array.isArray(value);
264
+ }
265
+
266
+ function isAttachmentMeta(value: unknown): value is AttachmentMeta {
267
+ return (
268
+ isRecord(value) &&
269
+ typeof value.filename === 'string' &&
270
+ typeof value.mimetype === 'string' &&
271
+ typeof value.category === 'string'
272
+ );
273
+ }
274
+
275
+ function isReasoningDetail(value: unknown): value is ReasoningDetail {
276
+ return (
277
+ isRecord(value) &&
278
+ typeof value.type === 'string' &&
279
+ typeof value.text === 'string'
280
+ );
281
+ }
282
+
283
+ /** OpenRouter-style reasoning fields on the raw streamed chunk, when the model emitted them. */
284
+ function extractReasoning(additionalKwargs: Record<string, unknown>): {
285
+ reasoning?: string;
286
+ reasoningDetails?: ReasoningDetail[];
287
+ } {
288
+ const raw = additionalKwargs.__raw_response;
289
+ if (!isRecord(raw) || !Array.isArray(raw.choices)) return {};
290
+ const first: unknown = raw.choices[0];
291
+ if (!isRecord(first) || !isRecord(first.delta)) return {};
292
+ const { reasoning, reasoning_details: details } = first.delta;
293
+ if (typeof reasoning !== 'string' || reasoning.length === 0) return {};
294
+ const reasoningDetails = Array.isArray(details)
295
+ ? details
296
+ .filter(isReasoningDetail)
297
+ .filter((detail) => detail.text.trim().length > 0)
298
+ .map(({ type, text }) => ({ type, text }))
299
+ : [];
300
+ return reasoningDetails.length > 0
301
+ ? { reasoning, reasoningDetails }
302
+ : { reasoning };
303
+ }
304
+
305
+ /**
306
+ * Reduce a message's `additional_kwargs` to the persisted allowlist. An
307
+ * existing `timestamp` is preserved: every checkpoint `put` rewrites every
308
+ * message row, so stamping "now" would churn `created_at` on the whole thread
309
+ * each super-step and destroy the transcript's chronological order.
310
+ */
311
+ export function cleanAdditionalKwargs(
312
+ additionalKwargs: Record<string, unknown>,
313
+ msgFromMatrixRoom: boolean,
314
+ oracleName: string,
315
+ ): CleanAdditionalKwargs {
316
+ const cleaned: CleanAdditionalKwargs = {
317
+ msgFromMatrixRoom,
318
+ timestamp:
319
+ typeof additionalKwargs.timestamp === 'string' &&
320
+ additionalKwargs.timestamp.length > 0
321
+ ? additionalKwargs.timestamp
322
+ : new Date().toISOString(),
323
+ oracleName,
324
+ };
325
+ if (typeof additionalKwargs.lc_source === 'string')
326
+ cleaned.lc_source = additionalKwargs.lc_source;
327
+ if (isAttachmentMeta(additionalKwargs.attachment))
328
+ cleaned.attachment = additionalKwargs.attachment;
329
+ if (
330
+ Array.isArray(additionalKwargs.attachments) &&
331
+ additionalKwargs.attachments.length > 0
332
+ ) {
333
+ const attachments = additionalKwargs.attachments.filter(isAttachmentMeta);
334
+ if (attachments.length > 0) cleaned.attachments = attachments;
335
+ }
336
+ for (const key of [
337
+ 'senderDid',
338
+ 'senderMatrixUserId',
339
+ 'senderDisplayName',
340
+ 'threadId',
341
+ 'eventId',
342
+ ] as const) {
343
+ const value = additionalKwargs[key];
344
+ if (typeof value === 'string') cleaned[key] = value;
345
+ }
346
+ if (typeof additionalKwargs.isGroupChat === 'boolean')
347
+ cleaned.isGroupChat = additionalKwargs.isGroupChat;
348
+ const mentions = additionalKwargs['m.mentions'];
349
+ if (isRecord(mentions)) {
350
+ const userIds = mentions.user_ids;
351
+ cleaned['m.mentions'] =
352
+ Array.isArray(userIds) &&
353
+ userIds.every((id): id is string => typeof id === 'string')
354
+ ? { user_ids: userIds }
355
+ : {};
356
+ }
357
+ const relatesTo = additionalKwargs['m.relates_to'];
358
+ if (isRecord(relatesTo)) {
359
+ const inReplyTo = relatesTo['m.in_reply_to'];
360
+ cleaned['m.relates_to'] =
361
+ isRecord(inReplyTo) && typeof inReplyTo.event_id === 'string'
362
+ ? { 'm.in_reply_to': { event_id: inReplyTo.event_id } }
363
+ : {};
364
+ }
365
+ const { reasoning, reasoningDetails } = extractReasoning(additionalKwargs);
366
+ if (reasoning !== undefined) cleaned.reasoning = reasoning;
367
+ if (reasoningDetails !== undefined)
368
+ cleaned.reasoningDetails = reasoningDetails;
369
+ return cleaned;
370
+ }