@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,816 @@
1
+ import type { z } from 'zod';
2
+ import type { BaseMessage } from '@langchain/core/messages';
3
+ import type { BaseChatModel } from '@langchain/core/language_models/chat_models';
4
+ import type { UserPreferences } from '../core/state';
5
+ import type { AttachmentViewSurface } from '../attachments/view';
6
+
7
+ /**
8
+ * Route shape returned by `OraclePlugin.getAuthExcludedRoutes()`. Paths are
9
+ * matched against the Hono router's request path (leading slash optional).
10
+ * This file is a verbatim port of `@ixo/oracle-runtime`'s plugin-api types —
11
+ * the plugin contract is identical across the Node and Workers runtimes; only
12
+ * the NestJS-specific `RequestMethod` type was replaced.
13
+ */
14
+ export interface AuthExcludedRoute {
15
+ /** Path under the plugin's controller, e.g. `weather/now`. Leading slash optional. */
16
+ path: string;
17
+ /** HTTP method (`GET`, `POST`, ...). Defaults to all methods when omitted. */
18
+ method?:
19
+ | 'GET'
20
+ | 'POST'
21
+ | 'PUT'
22
+ | 'PATCH'
23
+ | 'DELETE'
24
+ | 'OPTIONS'
25
+ | 'HEAD'
26
+ | 'ALL';
27
+ }
28
+
29
+ /** Merged Zod-validated env vars (core schema + every loaded plugin's `configSchema`). */
30
+ export type MergedConfig = Record<string, unknown>;
31
+
32
+ /** Plugin-scoped logger. */
33
+ export interface Logger {
34
+ log(message: unknown, ...optional: unknown[]): void;
35
+ error(message: unknown, ...optional: unknown[]): void;
36
+ warn(message: unknown, ...optional: unknown[]): void;
37
+ debug?(message: unknown, ...optional: unknown[]): void;
38
+ verbose?(message: unknown, ...optional: unknown[]): void;
39
+ /**
40
+ * Returns a new logger that auto-prefixes records with the given context.
41
+ * Optional — when absent, the same logger is returned unchanged.
42
+ */
43
+ child?(bindings: Record<string, unknown>): Logger;
44
+ }
45
+
46
+ /** LLM role tag used by `ctx.llm.get(role)`. Aligned with current `ModelRole`. */
47
+ export type ModelRole = 'main' | 'subagent' | 'utility' | (string & {});
48
+
49
+ /** Optional fields forwarded to ChatOpenAI when materialising an LLM. */
50
+ export type ChatOpenAIFields = Record<string, unknown>;
51
+
52
+ /** UCAN delegation envelope. */
53
+ export interface UcanDelegation {
54
+ readonly raw: string;
55
+ readonly issuer?: string;
56
+ readonly audience?: string;
57
+ readonly capabilities?: ReadonlyArray<{ resource: string; action: string }>;
58
+ }
59
+
60
+ /** Per-room secret index. */
61
+ export type SecretIndex = Record<
62
+ string,
63
+ { readonly key: string; readonly version?: number }
64
+ >;
65
+
66
+ /** Snapshot of a Matrix room's state events. */
67
+ export interface RoomStateSnapshot {
68
+ readonly roomId: string;
69
+ readonly state: ReadonlyArray<unknown>;
70
+ }
71
+
72
+ /** A single Matrix room event. */
73
+ export interface MatrixEvent {
74
+ readonly eventId: string;
75
+ readonly type: string;
76
+ readonly content: unknown;
77
+ readonly senderId?: string;
78
+ readonly originServerTs?: number;
79
+ }
80
+
81
+ /** Memory-enriched user context (mirrors `state.userContext`). */
82
+ export type UserContextData = Record<string, unknown>;
83
+
84
+ /** Read-only view over the LangGraph annotation state. */
85
+ export interface ReadonlyState {
86
+ readonly messages: readonly BaseMessage[];
87
+ readonly userContext?: UserContextData;
88
+ readonly loadedPlugins?: ReadonlySet<string>;
89
+ readonly [key: string]: unknown;
90
+ }
91
+
92
+ /**
93
+ * Read accessors registered by other plugins via `getSharedState()`.
94
+ * Forks can extend this via declaration merging (e.g. `userProfile`).
95
+ */
96
+ export interface SharedAccessors {
97
+ readonly [key: string]: unknown;
98
+ }
99
+
100
+ /** Which commerce persona a Matrix turn runs as. */
101
+ export type CommerceMode = 'support' | 'work';
102
+
103
+ /** Lifecycle of a thread-scoped work engagement. */
104
+ export type CommerceEngagementStatus = 'active' | 'delivered' | 'closed';
105
+
106
+ /**
107
+ * A thread-scoped work engagement: one per Matrix thread, keyed by the thread
108
+ * root event id (= session id). Stored in the room's state by the
109
+ * oracle-payments plugin; surfaced read-only on `ctx.commerce.engagement`.
110
+ */
111
+ export interface CommerceEngagement {
112
+ status: CommerceEngagementStatus;
113
+ serviceId: string;
114
+ serviceName: string;
115
+ priceUsd: number;
116
+ collectionId: string;
117
+ adminAddress: string;
118
+ /**
119
+ * DID of the user this job belongs to. The chain accepts one active claim
120
+ * intent per (agent, user claim collection), so "is there live work?" is a
121
+ * question about a USER, not a room — this is what lets the same engagement
122
+ * be found again from another room or another thread. Optional because
123
+ * engagements written before it existed are still readable.
124
+ */
125
+ userDid?: string;
126
+ /** ISO timestamp of engagement start. */
127
+ startedAt: string;
128
+ /**
129
+ * ISO timestamp of a user cancellation (`cancel_work`). Stamped when the
130
+ * cancellation is requested, before its release claim reaches the chain — an
131
+ * engagement that still reads `active` while carrying this timestamp is one
132
+ * whose release failed and needs another `cancel_work`.
133
+ */
134
+ cancelledAt?: string;
135
+ /** Free-text reason the user gave `cancel_work`, when any. */
136
+ cancelReason?: string;
137
+ /**
138
+ * Escrow intent locked on-chain at engagement start. `expiresAt` is the
139
+ * derived deadline (start + the grant's `intentDurationNs`) after which the
140
+ * reserved amount auto-releases. Expiry is the passive cleanup; the active
141
+ * one is a claim — submitting any claim against the intent (a delivery, or
142
+ * the release claim `cancel_work` files) settles it immediately.
143
+ */
144
+ intent?: { txHash: string; submittedAt: string; expiresAt?: string };
145
+ /** Work claim recorded by `deliver_work` (set by a later lane). */
146
+ claim?: { cid: string; txHash?: string; submittedAt: string };
147
+ /**
148
+ * The evaluation outcome already reported to the user in chat, stamped by
149
+ * the claim-status watcher. Its `status` is the raw on-chain
150
+ * `EvaluationStatus`, so a re-evaluation (a flagged claim later resolved)
151
+ * posts a second card while an unchanged status never re-posts.
152
+ */
153
+ paymentOutcome?: {
154
+ status: number;
155
+ outcome: CommercePaymentOutcome;
156
+ reportedAt: string;
157
+ };
158
+ }
159
+
160
+ /**
161
+ * How a submitted claim was settled, as rendered on the `payment_update` card.
162
+ * `under_review` is the chain's non-terminal FLAGGED status; `rejected` covers
163
+ * both a rejected and an invalidated claim — either way the escrow goes back.
164
+ */
165
+ export type CommercePaymentOutcome =
166
+ | 'approved'
167
+ | 'rejected'
168
+ | 'under_review'
169
+ | 'disputed';
170
+
171
+ /**
172
+ * Why a work-classified turn did not start an engagement. The first four are
173
+ * contract-gate failures (the user needs to contract or fix their contract);
174
+ * the last four are different in kind — the contract is fine, but the user
175
+ * already has a job running (`engagement_in_progress`), their account holds too
176
+ * few credits to reserve the payment (`insufficient_funds`, which they fix by
177
+ * topping up and nothing else), the on-chain payment reservation failed for
178
+ * some other reason (`intent_failed`), or the contract could not be checked at
179
+ * all (`contract_check_failed`, which is NOT evidence of an absent contract and
180
+ * must never be reported as one).
181
+ */
182
+ export type CommerceGateFailureReason =
183
+ | 'not_contracted'
184
+ | 'quota_exhausted'
185
+ | 'max_amount_too_low'
186
+ | 'service_not_contracted'
187
+ | 'engagement_in_progress'
188
+ | 'contract_check_failed'
189
+ | 'insufficient_funds'
190
+ | 'intent_failed';
191
+
192
+ /**
193
+ * The job already running when a new one is refused with
194
+ * `engagement_in_progress`. The chain accepts one active claim intent per
195
+ * (agent, user claim collection), so a user can have exactly one paid job with
196
+ * an oracle at a time — a second reservation could never be locked.
197
+ */
198
+ export interface CommerceInProgressEngagement {
199
+ serviceId: string;
200
+ serviceName: string;
201
+ /**
202
+ * Thread root event id the running job lives in — the only thread its
203
+ * `cancel_work` can be called from.
204
+ */
205
+ threadId: string;
206
+ /**
207
+ * The user already cancelled this job but its on-chain release did not land,
208
+ * so the reservation is still held. Retrying `cancel_work` in that thread
209
+ * completes the release; nothing else frees it before expiry.
210
+ */
211
+ releaseFailed?: boolean;
212
+ }
213
+
214
+ /** Gate-failure context attached when a work request lacked a usable contract. */
215
+ export interface CommerceGateFailure {
216
+ reason: CommerceGateFailureReason;
217
+ serviceId: string;
218
+ /** Display name of the requested service, when the agent card provides one. */
219
+ serviceName?: string;
220
+ /**
221
+ * What actually went wrong, in plain words — the chain's rejection text, the
222
+ * engine's status, the numbers that did not add up. The reason alone is a
223
+ * code; this is what lets the agent tell the user something true instead of
224
+ * "it failed". Absent only when the reason already is the whole story.
225
+ */
226
+ detail?: string;
227
+ /** The blocking job — present only for `engagement_in_progress`. */
228
+ inProgress?: CommerceInProgressEngagement;
229
+ }
230
+
231
+ /**
232
+ * Per-turn commerce routing outcome, produced by the Matrix message router
233
+ * and threaded into `RuntimeContext.commerce`. Absent entirely on HTTP turns
234
+ * and on Matrix turns where the commerce router is inert (no oracle-payments
235
+ * plugin registered).
236
+ */
237
+ export interface CommerceContext {
238
+ mode: CommerceMode;
239
+ /** The user's active engagement — present in work mode. */
240
+ engagement?: CommerceEngagement;
241
+ /**
242
+ * Room the active engagement's durable record lives in. Usually the room the
243
+ * turn arrived in, but NOT when the user continued live work from somewhere
244
+ * else — the engagement record stays where it started, so everything that
245
+ * settles it (`deliver_work`, `cancel_work`) must address it here rather
246
+ * than at `ctx.session.roomId`.
247
+ */
248
+ engagementRoomId?: string;
249
+ /** Thread root the active engagement's record is keyed by. Same rule. */
250
+ engagementThreadId?: string;
251
+ /** Present when the user asked for work but the contract gate failed. */
252
+ gate?: CommerceGateFailure;
253
+ }
254
+
255
+ export type ToolCallEventPayload = Record<string, unknown>;
256
+ export type ActionCallEventPayload = Record<string, unknown>;
257
+ export type RenderComponentEventPayload = Record<string, unknown>;
258
+ export type ReasoningEventPayload = Record<string, unknown>;
259
+ export type BrowserToolCallEventPayload = Record<string, unknown>;
260
+ export type RouterEventPayload = Record<string, unknown>;
261
+ export type MessageCacheInvalidationPayload = Record<string, unknown>;
262
+
263
+ import type { AgentMiddleware } from 'langchain';
264
+ export type { AgentMiddleware };
265
+
266
+ /**
267
+ * The agent's structured interface to a plugin. Drives Tier-1 prompt
268
+ * composition and powers `list_capabilities` discovery.
269
+ */
270
+ export interface PluginManifest {
271
+ /** Human-readable name. */
272
+ title: string;
273
+
274
+ /** One-line description shown in the Tier-1 prompt for `always` plugins. */
275
+ summary: string;
276
+
277
+ /** Triggers — when the agent should consider this plugin. */
278
+ whenToUse: string[];
279
+
280
+ /** Anti-patterns — when the agent should NOT use this plugin. */
281
+ whenNotToUse?: string[];
282
+
283
+ /** Few-shot examples teaching the agent how to invoke the plugin. */
284
+ examples?: ManifestExample[];
285
+
286
+ /** Categorization for grouping and filtering. */
287
+ tags?: string[];
288
+ category?:
289
+ | 'data'
290
+ | 'communication'
291
+ | 'automation'
292
+ | 'memory'
293
+ | 'integration'
294
+ | 'ui'
295
+ | 'auth'
296
+ | 'observability'
297
+ | 'core';
298
+
299
+ /**
300
+ * Discovery and loading mode:
301
+ * - `always` — tools bound to agent at boot; listed in Tier-1 prompt.
302
+ * - `on-demand` — tools NOT bound; manifest discoverable via
303
+ * `list_capabilities`; agent calls `load_capability(name)`
304
+ * to load.
305
+ * - `silent` — invisible to agent; runs as middleware-only.
306
+ *
307
+ * Default: `on-demand`.
308
+ */
309
+ visibility?: 'always' | 'on-demand' | 'silent';
310
+
311
+ /** Stability hint surfaced to the agent (`experimental` → warning footnote). */
312
+ stability?: 'stable' | 'beta' | 'experimental';
313
+ }
314
+
315
+ export interface ManifestExample {
316
+ /** Representative user message. */
317
+ user: string;
318
+ /** Optional reasoning. */
319
+ thought?: string;
320
+ /** Tool the agent should call. */
321
+ tool: string;
322
+ /** Tool args. */
323
+ args?: Record<string, unknown>;
324
+ }
325
+
326
+ /**
327
+ * Author-provided prompt customization. Every field is optional — when absent
328
+ * the runtime falls back to its built-in defaults. Authored once, baked into
329
+ * the system prompt at build time.
330
+ */
331
+ export interface OraclePromptConfig {
332
+ /**
333
+ * Full replacement of the identity preamble. When set, used verbatim as the
334
+ * opening paragraph. When unset, the composer generates one from
335
+ * `name`/`org`/`description`.
336
+ */
337
+ opening?: string;
338
+
339
+ /**
340
+ * Appended to the runtime's operating-principles section. Use for tone,
341
+ * voice, formality, or oracle-specific behavioral hints.
342
+ */
343
+ communicationStyle?: string;
344
+
345
+ /**
346
+ * Author-written elevator pitch for what this oracle does. Rendered above
347
+ * the plugin-derived capability list — additive, not a replacement.
348
+ */
349
+ capabilities?: string;
350
+
351
+ /**
352
+ * Free-form standing guidance injected verbatim into the system prompt under
353
+ * a dedicated `## Custom Instructions` section. Use for house style, domain
354
+ * rules, guardrails, or anything that doesn't fit `communicationStyle` (tone)
355
+ * or `capabilities` (the elevator pitch). The runtime renders the section
356
+ * only when there is content; it also routes operating guides contributed by
357
+ * loaded on-demand capabilities (e.g. the Flow Builder guide) through the
358
+ * same section, appended after the author's text.
359
+ */
360
+ customInstructions?: string;
361
+ }
362
+
363
+ /**
364
+ * Developer-facing oracle config. Passed inline to `createOracleApp`. The
365
+ * runtime fills in `entityDid` from the `ORACLE_ENTITY_DID` env var and
366
+ * combines this with bundled defaults to build the internal `OracleIdentity`.
367
+ */
368
+ export interface OracleConfig {
369
+ /** Oracle display name. Required. */
370
+ name: string;
371
+ /** Sponsoring organization. Optional — composer falls back gracefully. */
372
+ org?: string;
373
+ /** One-line description of what this oracle is for. */
374
+ description?: string;
375
+ /** Optional prompt customization (opening, communicationStyle, capabilities). */
376
+ prompt?: OraclePromptConfig;
377
+ }
378
+
379
+ /**
380
+ * Identity of the oracle running this plugin. Built by the runtime from
381
+ * `OracleConfig` + `ORACLE_ENTITY_DID` env var. Plugins read it via
382
+ * `PluginContext.identity` / `RuntimeContext` and should treat it as
383
+ * read-only.
384
+ */
385
+ export interface OracleIdentity {
386
+ name: string;
387
+ org: string;
388
+ description: string;
389
+ entityDid: string;
390
+ /** Author-provided prompt customization. Undefined when not configured. */
391
+ prompt?: OraclePromptConfig;
392
+ }
393
+
394
+ /**
395
+ * Passed to plugin methods called at request build time
396
+ * (`getTools`, `getSubAgents`, `getMiddlewares`). Lives once per request build.
397
+ *
398
+ * Holds no user, no session, no live socket, no request data.
399
+ */
400
+ export interface PluginContext<TConfig = MergedConfig> {
401
+ /** Merged Zod-validated env vars (core + all loaded plugins' configSchemas). */
402
+ config: TConfig;
403
+
404
+ /** Identity of this oracle. */
405
+ identity: OracleIdentity;
406
+
407
+ /** Set of plugin names currently loaded — drives soft-dep branching. */
408
+ availablePlugins: ReadonlySet<string>;
409
+
410
+ /** Plugin-scoped logger (auto-prefixed with the plugin's name). */
411
+ logger: Logger;
412
+ }
413
+
414
+ /**
415
+ * Passed to tool handlers, sub-agent handlers, and to plugin middlewares' hook
416
+ * functions. Built fresh per graph invocation.
417
+ */
418
+ export interface RuntimeContext<TConfig = MergedConfig> {
419
+ /** Authenticated user identity (validated by core auth middleware). */
420
+ user: {
421
+ did: string;
422
+ /** e.g. `@did-ixo-ixo1abc:ixo.world`. */
423
+ matrixUserId: string;
424
+ ucanDelegation: UcanDelegation;
425
+ timezone?: string;
426
+ currentTime?: string;
427
+ };
428
+
429
+ /** Session info from `SessionsService`. */
430
+ session: {
431
+ /** = `thread_id`; thread root `eventId`. */
432
+ id: string;
433
+ client: 'portal' | 'matrix' | 'slack';
434
+ wsId?: string;
435
+ requestId: string;
436
+ roomId?: string;
437
+ };
438
+
439
+ /** Read-only view over the graph state's history. */
440
+ history: {
441
+ messages: readonly BaseMessage[];
442
+ recent: (n: number) => BaseMessage[];
443
+ /** Memory enrichment from existing `state.userContext`. */
444
+ userContext: UserContextData;
445
+ /** Typed view over `Annotation.Root`. */
446
+ state: ReadonlyState;
447
+ };
448
+
449
+ /** Same merged Zod-validated env. */
450
+ config: TConfig;
451
+
452
+ /** Set of plugin names currently loaded (boot-fixed). */
453
+ availablePlugins: ReadonlySet<string>;
454
+
455
+ /** Plugins the agent has loaded for THIS thread via `load_capability`. */
456
+ loadedPlugins: ReadonlySet<string>;
457
+
458
+ /** Per-room secrets (JWE-encrypted, 24h cache; today's `SecretsService`). */
459
+ secrets: {
460
+ getIndex: () => Promise<SecretIndex>;
461
+ getValues: (keys: string[]) => Promise<Record<string, string>>;
462
+ };
463
+
464
+ /**
465
+ * Short-TTL keyed value store for content the LLM should never relay
466
+ * verbatim (UCAN invocation CARs, JWTs, signed envelopes, etc.). A
467
+ * producing tool stores the value and returns a short hex blobId; a
468
+ * consuming tool looks the value up server-side and forwards it on. Blobs
469
+ * are namespaced by issuing user DID — cross-user reads always miss.
470
+ */
471
+ blobStore: {
472
+ /** Store a value and return a fresh `blob_<16 hex>` id. TTL clamped to
473
+ * the service's `MAX_TTL_SECONDS` (24h); defaults to `DEFAULT_TTL_SECONDS`
474
+ * (1h) when omitted. Pass `userDid` from a trusted source — never from
475
+ * LLM-supplied arguments. */
476
+ put: (params: {
477
+ userDid: string;
478
+ name: string;
479
+ value: string;
480
+ ttlSeconds?: number;
481
+ }) => Promise<string>;
482
+ /** Retrieve a blob by id, scoped to the requesting user. Returns `null`
483
+ * if the blob doesn't exist, has expired, or belongs to a different user. */
484
+ get: (params: {
485
+ userDid: string;
486
+ blobId: string;
487
+ }) => Promise<{ name: string; value: string } | null>;
488
+ /** Lightweight format check (`blob_<16 hex>`). Use in tool input handlers
489
+ * to reject malformed IDs before paying for a cache lookup. */
490
+ isValidBlobId: (value: unknown) => value is string;
491
+ };
492
+
493
+ /** Matrix client, scoped operations only. */
494
+ matrix: {
495
+ postToRoom: (roomId: string, content: unknown) => Promise<string>;
496
+ /**
497
+ * Post a timeline event with a caller-chosen event type (e.g. the
498
+ * `ixo.oracle.*` protocol events). `postToRoom` is the `m.room.message`
499
+ * shorthand; this is the general form. Returns the new event id.
500
+ */
501
+ postEvent: (
502
+ roomId: string,
503
+ eventType: string,
504
+ content: object,
505
+ ) => Promise<string>;
506
+ getRoomState: (roomId: string) => Promise<RoomStateSnapshot>;
507
+ getEventById: (roomId: string, eventId: string) => Promise<MatrixEvent>;
508
+ /**
509
+ * Credentials of the oracle's own crypto-less device for plugins that run
510
+ * their own Matrix client (editor, flows). Minted and kept by the gateway.
511
+ */
512
+ botCredentials: () => Promise<{
513
+ baseUrl: string;
514
+ userId: string;
515
+ accessToken: string;
516
+ deviceId: string;
517
+ }>;
518
+ };
519
+
520
+ /** UCAN authorization helpers. */
521
+ ucan: {
522
+ requireCapability: (resource: string, action: string) => void;
523
+ hasCapability: (resource: string, action: string) => boolean;
524
+ mintInvocation: (
525
+ target: { did: string; capability: string },
526
+ opts?: { skipCache?: boolean; can?: string },
527
+ ) => Promise<string>;
528
+ /**
529
+ * Resolve a downstream service URL to its did:web identifier. Returns
530
+ * `null` when the document is missing or has no `id` — used by plugins
531
+ * minting service-targeted UCAN invocations.
532
+ */
533
+ resolveServiceDid: (serviceUrl: string) => Promise<string | null>;
534
+ /**
535
+ * `true` once the oracle has loaded its Ed25519 signing mnemonic. Plugins
536
+ * gate registration of mint-capable tools on this — without a key, minting
537
+ * is a no-op and the tool should surface an error rather than pretend.
538
+ */
539
+ hasSigningKey: () => boolean;
540
+ /**
541
+ * Mint a UCAN invocation from a directly-supplied delegation CAR (rather
542
+ * than a per-user cached one), targeted at a specific service route. For
543
+ * callers that already hold the delegation and need a fresh single-use
544
+ * invocation.
545
+ */
546
+ createInvocationFromDelegation: (
547
+ delegationCar: string,
548
+ serviceUrl: string,
549
+ capability: { can: string; with: string; nb?: Record<string, unknown> },
550
+ options?: { maxTtlSeconds?: number },
551
+ ) => Promise<{ invocation: string } | { error: string }>;
552
+ /**
553
+ * Mint a SELF-SIGNED invocation — issued by this oracle with NO proof
554
+ * chain — for calling a downstream service AS THE ORACLE ITSELF (not on a
555
+ * user's behalf). Returns `{ invocation }` on success or `{ error }` with a
556
+ * surfaced-verbatim reason (signing key missing, did:web unreachable, etc.).
557
+ */
558
+ mintSelfSignedInvocation: (
559
+ serviceUrl: string,
560
+ capability: { can: string; with: string },
561
+ options?: { maxTtlSeconds?: number },
562
+ ) => Promise<{ invocation: string } | { error: string }>;
563
+ /**
564
+ * Fetch, from the UCAN Store Worker, a delegation the user deposited for
565
+ * this oracle over `opts.resource`. Selects the newest active delegation
566
+ * whose capability covers `opts.requiredAbility`. Returns `{ token, with }`
567
+ * on success or `{ error }` — `'no-delegation'` when none satisfies the
568
+ * request, `'store-error'` (with `detail`) for auth / network / store
569
+ * failures.
570
+ */
571
+ getServiceDelegation: (
572
+ userDid: string,
573
+ opts: { storeUrl: string; resource: string; requiredAbility: string },
574
+ ) => Promise<
575
+ | { token: string; with: string }
576
+ | { error: 'no-delegation' | 'store-error'; detail?: string }
577
+ >;
578
+ };
579
+
580
+ /** LLM provider. */
581
+ llm: {
582
+ get: (role: ModelRole, params?: ChatOpenAIFields) => BaseChatModel;
583
+ };
584
+
585
+ /** Typed event emitter (today's `@ixo/oracles-events` 7 event types). */
586
+ emit: {
587
+ toolCall: (payload: ToolCallEventPayload) => void;
588
+ actionCall: (payload: ActionCallEventPayload) => void;
589
+ renderComponent: (payload: RenderComponentEventPayload) => void;
590
+ reasoning: (payload: ReasoningEventPayload) => void;
591
+ browserToolCall: (payload: BrowserToolCallEventPayload) => void;
592
+ router: (payload: RouterEventPayload) => void;
593
+ messageCacheInvalidation: (
594
+ payload: MessageCacheInvalidationPayload,
595
+ ) => void;
596
+ };
597
+
598
+ /** Plugin-scoped logger. */
599
+ logger: Logger;
600
+
601
+ /**
602
+ * Host task scheduler for this user, when the runtime provides one (the
603
+ * Workers runtime does; absent elsewhere). Used by the tasks plugin.
604
+ */
605
+ tasks?: OracleTasksSurface;
606
+
607
+ /**
608
+ * Host store for the user's per-room behavioural preferences (tone,
609
+ * language, names). Present when the host persists room state; the
610
+ * `set_user_preferences` tool writes through it and the turn preparer
611
+ * hydrates `state.userPreferences` from it before the agent is built.
612
+ */
613
+ preferences?: UserPreferencesSurface;
614
+
615
+ /**
616
+ * Host bridge to the user's browser over the realtime (socket.io) channel:
617
+ * browser tools declared by the client (`state.browserTools`) and AG-UI
618
+ * actions (`state.agActions`) are invoked through it and awaited until the
619
+ * browser answers. Present when the runtime terminates client sockets
620
+ * (the Workers runtime does); absent on hosts without a realtime channel.
621
+ */
622
+ frontend?: FrontendCallSurface;
623
+
624
+ /**
625
+ * Host access to the session's attachments for the `view_attachment`
626
+ * tool: present on hosts that offload old inline payloads (the Workers
627
+ * runtime), absent elsewhere.
628
+ */
629
+ attachments?: AttachmentViewSurface;
630
+ /**
631
+ * Register a cleanup that runs when THIS turn ends — completed, failed or
632
+ * aborted. For anything a tool keeps open across calls within a turn (an
633
+ * MCP client session, a pooled connection): close it here rather than on
634
+ * an idle timer. On Workers a pending timer keeps the user's Durable
635
+ * Object resident (and billed) and blocks WebSocket hibernation, whereas a
636
+ * turn-end close costs nothing.
637
+ */
638
+ onTurnEnd?: (dispose: () => void | Promise<void>) => void;
639
+
640
+ /** Propagates from the HTTP request / graph invocation. */
641
+ abortSignal: AbortSignal;
642
+
643
+ /**
644
+ * Commerce routing outcome for this Matrix turn (support vs work persona,
645
+ * active engagement, gate failure, cancellation). Absent on HTTP turns and
646
+ * whenever the commerce router is inert.
647
+ */
648
+ commerce?: CommerceContext;
649
+
650
+ /** Read accessors for state owned by other plugins. */
651
+ shared: SharedAccessors;
652
+
653
+ /**
654
+ * Identifier of the inbound tool call that triggered this handler, when
655
+ * available. Undefined for direct/test invocations. Used by tools that
656
+ * return a LangGraph `Command` and need to append a matching `ToolMessage`
657
+ * to the state update.
658
+ */
659
+ toolCallId?: string;
660
+ }
661
+
662
+ /** Lifecycle of a scheduled task. */
663
+ export type OracleTaskStatus =
664
+ | 'active'
665
+ | 'paused'
666
+ | 'completed'
667
+ | 'cancelled'
668
+ | 'failed';
669
+
670
+ /** How a task run is triggered. */
671
+ export type OracleTaskSchedule =
672
+ | { kind: 'cron'; cron: string; timezone?: string }
673
+ | { kind: 'once'; at: string }
674
+ | { kind: 'interval'; everySeconds: number };
675
+
676
+ /** Stored task record (persisted in the user's own database → owner file). */
677
+ export interface OracleTaskRecord {
678
+ id: string;
679
+ title: string;
680
+ /** Markdown intent body — what the agent should do each run. */
681
+ intent: string;
682
+ schedule: OracleTaskSchedule;
683
+ status: OracleTaskStatus;
684
+ /** `before-action` pauses each run for user approval; `never` runs freely. */
685
+ approval: 'never' | 'before-action';
686
+ createdAt: string;
687
+ updatedAt: string;
688
+ nextRunAt?: string;
689
+ lastRunAt?: string;
690
+ lastResult?: { ok: boolean; summary: string; at: string };
691
+ consecutiveFailures: number;
692
+ /**
693
+ * Dedicated `[Task] <title>` room this task delivers into. Absent → the
694
+ * user's main oracle room (the Node runtime's `delivery.roomId: 'main'`).
695
+ */
696
+ deliveryRoomId?: string;
697
+ }
698
+
699
+ export interface OracleTaskInput {
700
+ title: string;
701
+ intent: string;
702
+ schedule: OracleTaskSchedule;
703
+ approval?: 'never' | 'before-action';
704
+ /**
705
+ * Whether the task gets its own `[Task] <title>` room. `auto` (default)
706
+ * applies the Node heuristic: sub-daily cron, long intent, or a
707
+ * monitor/watch/track intent; `before-action` tasks always get one.
708
+ */
709
+ dedicatedRoom?: 'auto' | 'yes' | 'no';
710
+ }
711
+
712
+ /**
713
+ * Bridge to the user's browser over the realtime channel. Both calls emit the
714
+ * corresponding event (`browser_tool_call` / `action_call`) to the sockets
715
+ * subscribed to `sessionId` and resolve with the browser's answer, rejecting
716
+ * on the browser's `error`, an AG-UI `success: false`, or the timeout —
717
+ * exactly the Node runtime's `callBrowserTool` / `callAgAction` contract.
718
+ */
719
+ export interface FrontendCallSurface {
720
+ callBrowserTool(params: FrontendCallParams): Promise<unknown>;
721
+ callAgAction(params: FrontendCallParams): Promise<unknown>;
722
+ /** Whether at least one authenticated realtime client is attached to the session. */
723
+ hasClient(sessionId: string): boolean;
724
+ }
725
+
726
+ export interface FrontendCallParams {
727
+ sessionId: string;
728
+ toolCallId: string;
729
+ toolName: string;
730
+ args: Record<string, unknown>;
731
+ /** Default 15 s (browser tools) / 10 s (AG-UI actions), as on Node. */
732
+ timeoutMs?: number;
733
+ }
734
+
735
+ /**
736
+ * Per-user task scheduling surface, implemented by the host runtime (on
737
+ * Workers: a Durable-Object-alarm scheduler whose records live inside the
738
+ * user's own SQLite file). Optional — absent when the host has no scheduler.
739
+ */
740
+ export interface OracleTasksSurface {
741
+ preview(
742
+ input: OracleTaskInput,
743
+ ): Promise<{ ok: boolean; nextRuns: string[]; problems: string[] }>;
744
+ create(input: OracleTaskInput): Promise<OracleTaskRecord>;
745
+ list(): Promise<OracleTaskRecord[]>;
746
+ get(id: string): Promise<OracleTaskRecord | null>;
747
+ update(
748
+ id: string,
749
+ patch: Partial<OracleTaskInput>,
750
+ ): Promise<OracleTaskRecord>;
751
+ pause(id: string): Promise<OracleTaskRecord>;
752
+ resume(id: string): Promise<OracleTaskRecord>;
753
+ cancel(id: string): Promise<OracleTaskRecord>;
754
+ /** Resolve a pending before-action approval for the given run. */
755
+ resolveApproval(
756
+ taskId: string,
757
+ decision: 'approve' | 'reject',
758
+ note?: string,
759
+ ): Promise<{ resolved: boolean }>;
760
+ }
761
+
762
+ export interface PluginTool {
763
+ name: string;
764
+ description: string;
765
+ schema: z.ZodType;
766
+ handler: (args: unknown, ctx: RuntimeContext) => Promise<unknown>;
767
+ /** Override visibility — by default inherits from the plugin's `manifest.visibility`. */
768
+ visibility?: 'always' | 'on-demand' | 'silent';
769
+ /**
770
+ * Billing gate. `'contracted'` marks a tool that performs paid contracted
771
+ * work: the runtime binds it only while the turn runs inside an active work
772
+ * engagement (`ctx.commerce.mode === 'work'`) and hides it from the model
773
+ * otherwise — a hard code gate on top of the prompt overlay.
774
+ */
775
+ billing?: 'contracted';
776
+ }
777
+
778
+ export interface PluginSubAgent {
779
+ /** Tool name the agent will see (e.g. `call_memory_agent`). */
780
+ name: string;
781
+ description: string;
782
+ systemPrompt: string | ((ctx: PluginContext) => string);
783
+ tools: PluginTool[] | ((ctx: PluginContext) => PluginTool[]);
784
+ /** LLM role; default `subagent`. */
785
+ model?: ModelRole;
786
+ /** Sub-agent-scoped middleware (e.g. summarization for long conversations). */
787
+ middlewares?: AgentMiddleware[];
788
+ /**
789
+ * Forward this sub-agent's internal tool calls + results into the parent
790
+ * graph's message history so the UI renders them in the main chat.
791
+ *
792
+ * - `true` → forward ALL of this sub-agent's own tools
793
+ * - `false` / omitted → forward nothing
794
+ * - `string[]` → forward only the listed tool names
795
+ *
796
+ * Runtime-injected passthrough tools (e.g. memory CRUD) are never
797
+ * forwarded — they're already on the main agent.
798
+ */
799
+ forwardTools?: boolean | string[];
800
+ /** Called after sub-agent completes; can emit follow-up events. */
801
+ onComplete?: (result: string, ctx: RuntimeContext) => Promise<void>;
802
+ }
803
+
804
+ /**
805
+ * Per-room user preferences as the host stores them — the same record the
806
+ * Node runtime's `UserPreferencesService` keeps in the room's
807
+ * `ixo.room.state`/`user_prefs` event. `set` merges: only the provided
808
+ * fields change, and the host stamps `updatedAt`.
809
+ */
810
+ export interface UserPreferencesSurface {
811
+ get(roomId: string): Promise<UserPreferences | undefined>;
812
+ set(
813
+ roomId: string,
814
+ partial: Partial<UserPreferences>,
815
+ ): Promise<UserPreferences>;
816
+ }