@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,3505 @@
1
+ /**
2
+ * Plugin-side action-metadata overlay (spec §5, §7.5). A map keyed by action
3
+ * `type` that enriches the editor registry with agent-facing descriptions,
4
+ * tags, typed input ports, and prerequisites — merged with `getAllActions()` at
5
+ * runtime (see actions.ts / linkage.ts / references.ts), never editing
6
+ * `ActionDefinition`.
7
+ *
8
+ * What lives here vs. what lives in the editor registry:
9
+ * - OUTPUTS come from the registry `outputSchema` — never duplicated here.
10
+ * - INPUT requirements: most actions now ship a machine-readable `inputSchema`
11
+ * (which actions.ts prefers). The `inputPorts` below add agent-facing prose
12
+ * and semantic port types (port-types.ts) that drive linkage compatibility,
13
+ * and remain the authoritative input list for any action shipping no schema.
14
+ * - `summary` / `whenToUse` / `whenNotToUse` / `tags` / `requires` exist ONLY
15
+ * here — they are the agent-discovery surface (`list_actions` /
16
+ * `describe_action`) and are not derivable from the registry.
17
+ *
18
+ * `requires` captures human/environment prerequisites the user must supply (a
19
+ * claim collection, a template id, a connected account, a PIN) — the things the
20
+ * agent must STOP and ASK the user for rather than invent. The multi-operation
21
+ * actions (`qi/collection.lifecycle`, `qi/collection.users`) dispatch on an
22
+ * `operation` input; their `inputPorts` note which fields apply to which op.
23
+ *
24
+ * Keep entries in sync with each action's `run()` requirements in the editor.
25
+ */
26
+ export interface ActionPort {
27
+ /** The input field name, exactly as the action's `run()` reads it. */
28
+ path: string;
29
+ /** Semantic port type (port-types.ts) — drives linkage compatibility. */
30
+ portType: string;
31
+ /** True when the action's `run()` throws if this field is missing. */
32
+ required?: boolean;
33
+ /** One-line, agent-facing description of what to put here. */
34
+ description?: string;
35
+ }
36
+
37
+ export interface ActionRequirement {
38
+ kind: string;
39
+ description: string;
40
+ }
41
+
42
+ export interface OverlayEntry {
43
+ summary?: string;
44
+ whenToUse?: string[];
45
+ whenNotToUse?: string[];
46
+ tags?: string[];
47
+ inputPorts?: ActionPort[];
48
+ outputPorts?: ActionPort[];
49
+ requires?: ActionRequirement[];
50
+ }
51
+
52
+ /**
53
+ * Keyed by action `type` (e.g. "qi/email.send"). Input field names + `required`
54
+ * flags are transcribed from the editor action `run()` checks. Output ports are
55
+ * NOT listed here — they come from the registry's `outputSchema` (actions.ts
56
+ * outputFields). Covers every registered action type.
57
+ */
58
+ export const ACTION_METADATA: Record<string, OverlayEntry> = {
59
+ // ── Claims & bids ─────────────────────────────────────────────────────────
60
+ 'qi/claim.submit': {
61
+ summary:
62
+ 'Submit a claim (an application/report/form submission) into a claim collection on a deed/entity, broadcasting an on-chain submission transaction. May prompt the submitter for a verification PIN at runtime.',
63
+ whenToUse: [
64
+ 'An automation needs to record a claim — an application, a report, a form submission — into an existing claim collection.',
65
+ ],
66
+ tags: ['claims'],
67
+ inputPorts: [
68
+ {
69
+ path: 'deedDid',
70
+ portType: 'entityDid',
71
+ required: true,
72
+ description: 'The entity/deed DID that owns the claim collection.',
73
+ },
74
+ {
75
+ path: 'collectionId',
76
+ portType: 'claimCollectionId',
77
+ required: true,
78
+ description: 'The claim collection to submit into.',
79
+ },
80
+ {
81
+ path: 'adminAddress',
82
+ portType: 'chainAddress',
83
+ required: true,
84
+ description: "The claim collection admin's chain address.",
85
+ },
86
+ {
87
+ path: 'surveyAnswers',
88
+ portType: 'object',
89
+ required: true,
90
+ description:
91
+ "The claim's answer data (the submitted form, as an object or a JSON string). Wire from an upstream survey/form block's output.",
92
+ },
93
+ {
94
+ path: 'pin',
95
+ portType: 'string',
96
+ required: false,
97
+ description:
98
+ 'Verification PIN; if omitted the action requests it from the user at runtime.',
99
+ },
100
+ ],
101
+ requires: [
102
+ {
103
+ kind: 'claimCollection',
104
+ description:
105
+ 'A claim collection (its deed DID, collection id, and admin address) to submit into — ask the user; never invent these.',
106
+ },
107
+ ],
108
+ },
109
+ 'qi/claim.evaluate': {
110
+ summary:
111
+ 'Approve or reject a submitted claim, broadcasting an on-chain evaluation transaction (optionally paying out a coin on approval and signing a verification UDID proof). Guards that the actor holds the evaluator (EA) role for the collection before proceeding.',
112
+ whenToUse: [
113
+ 'An automation needs to approve or reject a previously submitted claim and record the outcome on-chain.',
114
+ 'A claim-approval flow needs to pay out a coin amount when approving.',
115
+ ],
116
+ whenNotToUse: [
117
+ 'Do not use to record a new claim — use qi/claim.submit for that. This action only evaluates an existing claim by its claimId.',
118
+ ],
119
+ tags: ['claims'],
120
+ inputPorts: [
121
+ {
122
+ path: 'decision',
123
+ portType: 'string',
124
+ required: true,
125
+ description: 'Either "approve" or "reject"; any other value throws.',
126
+ },
127
+ {
128
+ path: 'claimId',
129
+ portType: 'claimId',
130
+ required: true,
131
+ description:
132
+ 'The claim being evaluated. Wire from the claim.submit block output (output.claimId).',
133
+ },
134
+ {
135
+ path: 'collectionId',
136
+ portType: 'claimCollectionId',
137
+ required: true,
138
+ description: 'The claim collection the claim belongs to.',
139
+ },
140
+ {
141
+ path: 'deedDid',
142
+ portType: 'entityDid',
143
+ required: true,
144
+ description: 'The deed/entity DID owning the collection.',
145
+ },
146
+ {
147
+ path: 'adminAddress',
148
+ portType: 'chainAddress',
149
+ required: true,
150
+ description:
151
+ 'Entity admin account address used for the on-chain evaluation.',
152
+ },
153
+ {
154
+ path: 'amount',
155
+ portType: 'object',
156
+ required: false,
157
+ description:
158
+ 'Payout coin ({ denom, amount }) credited on approval; accepts a JSON string, object, or array (first coin used).',
159
+ },
160
+ {
161
+ path: 'createUdid',
162
+ portType: 'boolean',
163
+ required: false,
164
+ description:
165
+ 'When true (and no verificationProof set), generates and signs a verification UDID proof; prompts for a PIN to sign.',
166
+ },
167
+ {
168
+ path: 'verificationProof',
169
+ portType: 'string',
170
+ required: false,
171
+ description:
172
+ 'Pre-existing verification proof URL; supplying it skips UDID creation.',
173
+ },
174
+ {
175
+ path: 'granteeAddress',
176
+ portType: 'chainAddress',
177
+ required: false,
178
+ description:
179
+ 'Evaluator address; resolves to the current user when omitted.',
180
+ },
181
+ {
182
+ path: 'capabilityCid',
183
+ portType: 'string',
184
+ required: false,
185
+ description:
186
+ 'Capability CID used when creating the UDID; defaults to deedDid.',
187
+ },
188
+ {
189
+ path: 'rubricAuthority',
190
+ portType: 'string',
191
+ required: false,
192
+ description:
193
+ 'Rubric authority used when creating the UDID; defaults to deedDid.',
194
+ },
195
+ {
196
+ path: 'rubricId',
197
+ portType: 'string',
198
+ required: false,
199
+ description:
200
+ 'Rubric identifier used when creating the UDID; defaults to deedDid.',
201
+ },
202
+ {
203
+ path: 'traceCid',
204
+ portType: 'string',
205
+ required: false,
206
+ description: 'Trace CID passed through to the UDID.',
207
+ },
208
+ {
209
+ path: 'items',
210
+ portType: 'array',
211
+ required: false,
212
+ description: 'Evaluation items passed through to the UDID.',
213
+ },
214
+ {
215
+ path: 'patch',
216
+ portType: 'object',
217
+ required: false,
218
+ description: 'Patch object passed through to the UDID.',
219
+ },
220
+ {
221
+ path: 'xeroInvoiceBlockId',
222
+ portType: 'string',
223
+ required: false,
224
+ description:
225
+ 'Bound Xero invoice block id; on approval, queues invoice work for that block.',
226
+ },
227
+ {
228
+ path: 'xeroPaymentBlockId',
229
+ portType: 'string',
230
+ required: false,
231
+ description:
232
+ 'Bound Xero payment block id used when resolving Xero bindings.',
233
+ },
234
+ {
235
+ path: 'claimSnapshot',
236
+ portType: 'object',
237
+ required: false,
238
+ description: 'Claim snapshot used to build the Xero invoice scope.',
239
+ },
240
+ ],
241
+ requires: [
242
+ {
243
+ kind: 'claimCollection',
244
+ description:
245
+ 'The deed DID, collection id, and admin address of the claim collection being evaluated — ask the user; never invent these.',
246
+ },
247
+ {
248
+ kind: 'evaluatorRole',
249
+ description:
250
+ 'The acting user must hold the evaluator (EA) role for this collection; the action refuses otherwise. Confirm the actor is an authorized evaluator.',
251
+ },
252
+ ],
253
+ },
254
+ 'qi/bid.submit': {
255
+ summary:
256
+ 'Submit a bid (an agent application) against a claim collection for a service_agent or evaluation_agent role, broadcasting an on-chain bid submission.',
257
+ whenToUse: [
258
+ 'An automation needs an agent to apply/bid for a service-agent or evaluation-agent role on a collection.',
259
+ ],
260
+ whenNotToUse: [
261
+ 'Do not use to approve or reject a bid — use qi/bid.evaluate for that.',
262
+ ],
263
+ tags: ['bids'],
264
+ inputPorts: [
265
+ {
266
+ path: 'collectionId',
267
+ portType: 'claimCollectionId',
268
+ required: true,
269
+ description: 'The claim collection the bid is submitted against.',
270
+ },
271
+ {
272
+ path: 'role',
273
+ portType: 'string',
274
+ required: true,
275
+ description:
276
+ 'Bid role: "service_agent" (sa) or "evaluation_agent" (ea); any other value throws.',
277
+ },
278
+ {
279
+ path: 'surveyAnswers',
280
+ portType: 'object',
281
+ required: true,
282
+ description:
283
+ 'Survey answers (object or JSON string) attached to the bid. Wire from an upstream survey/form block.',
284
+ },
285
+ {
286
+ path: 'deedDid',
287
+ portType: 'entityDid',
288
+ required: false,
289
+ description: 'Optional deed/entity DID associated with the bid.',
290
+ },
291
+ ],
292
+ requires: [
293
+ {
294
+ kind: 'claimCollection',
295
+ description:
296
+ 'The collection id (and optionally deed DID) to bid against — ask the user; never invent these.',
297
+ },
298
+ ],
299
+ },
300
+ 'qi/bid.evaluate': {
301
+ summary:
302
+ 'Approve or reject a submitted bid, broadcasting on-chain transactions. On approval it grants the applicant the service_agent or evaluation_agent role (with quota) and approves the bid; on rejection it records a reason.',
303
+ whenToUse: [
304
+ 'An automation needs to approve a bidding agent (granting them a service- or evaluation-agent role) or reject their bid.',
305
+ ],
306
+ whenNotToUse: [
307
+ 'Do not use to submit a new bid — use qi/bid.submit for that. This action only evaluates an existing bid by its bidId.',
308
+ ],
309
+ tags: ['bids'],
310
+ inputPorts: [
311
+ {
312
+ path: 'decision',
313
+ portType: 'string',
314
+ required: true,
315
+ description: 'Either "approve" or "reject"; any other value throws.',
316
+ },
317
+ {
318
+ path: 'bidId',
319
+ portType: 'bidId',
320
+ required: true,
321
+ description:
322
+ 'The bid being evaluated. Wire from the bid.submit block output (output.bidId).',
323
+ },
324
+ {
325
+ path: 'collectionId',
326
+ portType: 'claimCollectionId',
327
+ required: true,
328
+ description: 'The claim collection the bid belongs to.',
329
+ },
330
+ {
331
+ path: 'deedDid',
332
+ portType: 'entityDid',
333
+ required: true,
334
+ description: 'The deed/entity DID owning the collection.',
335
+ },
336
+ {
337
+ path: 'role',
338
+ portType: 'string',
339
+ required: true,
340
+ description:
341
+ 'Applicant role: "service_agent" (sa) or "evaluation_agent" (ea); determines which grant is issued on approval.',
342
+ },
343
+ {
344
+ path: 'applicantDid',
345
+ portType: 'did',
346
+ required: true,
347
+ description: "The bidding applicant's DID.",
348
+ },
349
+ {
350
+ path: 'applicantAddress',
351
+ portType: 'chainAddress',
352
+ required: true,
353
+ description: "The bidding applicant's wallet address.",
354
+ },
355
+ {
356
+ path: 'adminAddress',
357
+ portType: 'chainAddress',
358
+ required: false,
359
+ description:
360
+ 'Entity admin account address; required (throws if missing) when decision is "approve".',
361
+ },
362
+ {
363
+ path: 'maxAmounts',
364
+ portType: 'string',
365
+ required: false,
366
+ description:
367
+ 'JSON-encoded per-claim max amounts used when approving an evaluation_agent.',
368
+ },
369
+ {
370
+ path: 'reason',
371
+ portType: 'string',
372
+ required: false,
373
+ description:
374
+ 'Rejection reason; required (throws if missing) when decision is "reject".',
375
+ },
376
+ ],
377
+ requires: [
378
+ {
379
+ kind: 'claimCollection',
380
+ description:
381
+ 'The deed DID, collection id, and (for approvals) admin address of the collection — ask the user; never invent these.',
382
+ },
383
+ {
384
+ kind: 'evaluatorAuthority',
385
+ description:
386
+ 'The actor must be authorized to evaluate bids and grant roles for this collection. Confirm the actor has admin/approver authority.',
387
+ },
388
+ ],
389
+ },
390
+
391
+ // ── Claim collections (multi-operation dispatchers) ───────────────────────
392
+ 'qi/collection.lifecycle': {
393
+ summary:
394
+ 'Manage a claim collection lifecycle on a deed/entity. A single action that dispatches on `operation`: `create` mints a new collection (MsgCreateCollection) and emits a `created` event; `updateState`/`updateDates`/`updateQuota`/`updatePayments`/`updateIntents` mutate an existing one (the matching MsgUpdateCollection* tx); `refresh` is a read-only re-fetch. Every write returns the full latest on-chain CollectionState (incl. the chain-assigned collectionId); the admin address is resolved from chain, not passed in.',
395
+ whenToUse: [
396
+ 'Stand up a brand-new claim collection on an entity/deed (operation: create) before any claims or grants exist.',
397
+ 'Pause/reopen/close, reschedule, recap quota, or reconfigure payments/intents of an existing collection (operation: update*).',
398
+ 'Read back the current on-chain state of a collection without mutating it (operation: refresh).',
399
+ ],
400
+ whenNotToUse: [
401
+ 'Granting/revoking who can submit or evaluate on the collection — use qi/collection.users.',
402
+ 'Submitting or evaluating an actual claim — use qi/claim.submit / qi/claim.evaluate.',
403
+ ],
404
+ tags: ['collections'],
405
+ inputPorts: [
406
+ {
407
+ path: 'operation',
408
+ portType: 'string',
409
+ required: true,
410
+ description:
411
+ 'Which op to run: create | updateState | updateDates | updateQuota | updatePayments | updateIntents | refresh.',
412
+ },
413
+ {
414
+ path: 'collectionId',
415
+ portType: 'claimCollectionId',
416
+ required: true,
417
+ description:
418
+ 'Target collection — required for every op EXCEPT create (which mints a new one).',
419
+ },
420
+ {
421
+ path: 'entity',
422
+ portType: 'entityDid',
423
+ description:
424
+ 'create only (required): entity/deed DID the new collection belongs to.',
425
+ },
426
+ {
427
+ path: 'protocol',
428
+ portType: 'did',
429
+ description:
430
+ "create only (required): claim protocol DID the collection follows — a bare did: (no '#' fragment) resolved from the entity's linked claims.",
431
+ },
432
+ {
433
+ path: 'state',
434
+ portType: 'number',
435
+ description:
436
+ 'create / updateState (required for updateState): lifecycle state 0=OPEN, 1=PAUSED, 2=CLOSED.',
437
+ },
438
+ {
439
+ path: 'startDate',
440
+ portType: 'string',
441
+ description:
442
+ 'create / updateDates: ISO-8601 start date (updateDates needs at least one of startDate/endDate).',
443
+ },
444
+ {
445
+ path: 'endDate',
446
+ portType: 'string',
447
+ description: 'create / updateDates: ISO-8601 end date.',
448
+ },
449
+ {
450
+ path: 'quota',
451
+ portType: 'string',
452
+ description:
453
+ "create / updateQuota (required for updateQuota): max claims, '0' = unlimited; must be 0 or >= the current submitted count.",
454
+ },
455
+ {
456
+ path: 'payments',
457
+ portType: 'object',
458
+ description:
459
+ 'create / updatePayments (required for updatePayments): submission/evaluation/approval/rejection payment legs.',
460
+ },
461
+ {
462
+ path: 'intents',
463
+ portType: 'object',
464
+ description:
465
+ 'create / updateIntents (required for updateIntents): intent configuration.',
466
+ },
467
+ ],
468
+ requires: [
469
+ {
470
+ kind: 'entity',
471
+ description:
472
+ 'For create: the owning entity/deed DID and a ledgered claim protocol DID picked from the entity — ask the user; never invent these.',
473
+ },
474
+ {
475
+ kind: 'claimCollection',
476
+ description:
477
+ 'For every non-create op: an existing collection id whose admin authorises the change — ask the user; never invent it.',
478
+ },
479
+ ],
480
+ },
481
+ 'qi/collection.users': {
482
+ summary:
483
+ "Manage who can submit or evaluate claims on one collection. A single action that dispatches on `operation`: `add` grants submit/evaluate authz to a grantee (fanning out one grant per member when granteeKind is group-members); `revoke` does a read-modify-write that drops just the target collection's constraint while preserving the grantee's other grants; `list` is a read-only query of current grantees. Writes broadcast the claims-module authz tx and return its transactionHash.",
484
+ whenToUse: [
485
+ 'Authorize an agent/user (or every member of a group) to submit or evaluate claims for a collection (operation: add).',
486
+ "Remove one grantee's submit/evaluate authorization for a collection (operation: revoke).",
487
+ 'Enumerate who currently holds submit/evaluate authz on a collection (operation: list).',
488
+ ],
489
+ whenNotToUse: [
490
+ 'Creating or mutating the collection itself — use qi/collection.lifecycle.',
491
+ ],
492
+ tags: ['collections', 'grants'],
493
+ inputPorts: [
494
+ {
495
+ path: 'operation',
496
+ portType: 'string',
497
+ required: true,
498
+ description: 'Which op to run: add | revoke | list.',
499
+ },
500
+ {
501
+ path: 'collectionId',
502
+ portType: 'claimCollectionId',
503
+ required: true,
504
+ description: 'Target claim collection.',
505
+ },
506
+ {
507
+ path: 'adminAddress',
508
+ portType: 'chainAddress',
509
+ required: true,
510
+ description:
511
+ 'Granter entity admin account address (the authz granter) whose grants are written/queried.',
512
+ },
513
+ {
514
+ path: 'role',
515
+ portType: 'string',
516
+ description:
517
+ "add/revoke (required): authorization to grant or revoke — 'submit' or 'evaluate'.",
518
+ },
519
+ {
520
+ path: 'granteeAddress',
521
+ portType: 'chainAddress',
522
+ description:
523
+ "add/revoke: grantee bech32 address (required for revoke, and for add unless granteeKind is 'group-members').",
524
+ },
525
+ {
526
+ path: 'granteeKind',
527
+ portType: 'string',
528
+ description:
529
+ "add: how to treat the grantee — 'user', 'group-account', or 'group-members' (fan out per member). Defaults to 'user'.",
530
+ },
531
+ {
532
+ path: 'members',
533
+ portType: 'array',
534
+ description:
535
+ "add: resolved members to fan the grant out to — required and non-empty when granteeKind is 'group-members'.",
536
+ },
537
+ {
538
+ path: 'agentQuota',
539
+ portType: 'string',
540
+ description: "add: remaining agent quota; '0' = unlimited.",
541
+ },
542
+ {
543
+ path: 'maxAmount',
544
+ portType: 'array',
545
+ description: 'add: per-claim max amount cap (evaluate role).',
546
+ },
547
+ {
548
+ path: 'intentDurationNs',
549
+ portType: 'string',
550
+ description: 'add: intent duration in nanoseconds, as a string.',
551
+ },
552
+ {
553
+ path: 'deedDid',
554
+ portType: 'entityDid',
555
+ description: 'Entity (deed) DID forwarded to the handler.',
556
+ },
557
+ ],
558
+ requires: [
559
+ {
560
+ kind: 'claimCollection',
561
+ description:
562
+ 'The target collection id and its granter entity admin account address — ask the user; never invent them.',
563
+ },
564
+ {
565
+ kind: 'grantee',
566
+ description:
567
+ "For add/revoke: the grantee's bech32 address (or the group/members to fan out to) and the role (submit/evaluate) — ask the user.",
568
+ },
569
+ ],
570
+ },
571
+
572
+ // ── Notifications & integrations ──────────────────────────────────────────
573
+ 'qi/email.send': {
574
+ summary:
575
+ 'Sends a templated email via the configured email service; side-effect, delivers a real message to the recipient.',
576
+ whenToUse: [
577
+ 'An automation needs to email someone — a confirmation, notice, or alert.',
578
+ 'You have a pre-registered email template and a recipient address to send it to.',
579
+ ],
580
+ tags: ['notify', 'email'],
581
+ inputPorts: [
582
+ {
583
+ path: 'to',
584
+ portType: 'emailAddress',
585
+ required: true,
586
+ description:
587
+ 'Recipient email address. run() throws "Recipient (to) is required" if missing.',
588
+ },
589
+ {
590
+ path: 'templateName',
591
+ portType: 'string',
592
+ required: true,
593
+ description:
594
+ 'The pre-registered email template id to send. Either templateName or its alias `template` satisfies the requirement; run() throws "No template selected" if neither is provided. Ask the user which template.',
595
+ },
596
+ {
597
+ path: 'template',
598
+ portType: 'string',
599
+ description:
600
+ 'Alias for templateName — the template id. Provide this OR templateName.',
601
+ },
602
+ {
603
+ path: 'subject',
604
+ portType: 'string',
605
+ description: 'Email subject line.',
606
+ },
607
+ {
608
+ path: 'templateVersion',
609
+ portType: 'string',
610
+ description: 'Optional specific version of the template to render.',
611
+ },
612
+ {
613
+ path: 'variables',
614
+ portType: 'object',
615
+ description:
616
+ 'Template variables to interpolate; accepts an object or a JSON string (invalid JSON falls back to {}).',
617
+ },
618
+ {
619
+ path: 'cc',
620
+ portType: 'emailAddress',
621
+ description: 'Carbon-copy recipient address.',
622
+ },
623
+ {
624
+ path: 'bcc',
625
+ portType: 'emailAddress',
626
+ description: 'Blind-carbon-copy recipient address.',
627
+ },
628
+ {
629
+ path: 'replyTo',
630
+ portType: 'emailAddress',
631
+ description: 'Reply-to address for the email.',
632
+ },
633
+ ],
634
+ requires: [
635
+ {
636
+ kind: 'emailTemplate',
637
+ description:
638
+ 'A pre-registered email template id — ask the user; never invent one.',
639
+ },
640
+ ],
641
+ },
642
+ 'qi/matrix.dm': {
643
+ summary:
644
+ 'Direct-messages a user by their DID over Matrix — converts the target DID to a Matrix user id, finds or creates a DM room (tracked via m.direct account data), and sends the text. Side-effect: delivers a real Matrix message.',
645
+ whenToUse: [
646
+ 'You need to ping a specific person/agent identified by their DID with a chat message.',
647
+ 'An in-flow notification should go to a Matrix user rather than email.',
648
+ ],
649
+ whenNotToUse: [
650
+ 'Do NOT target a DID that resolves to the sender\'s own Matrix user id — a self-DM makes createRoom fail with 403 "already in the room". The recipient must be someone other than the signed-in sender.',
651
+ ],
652
+ tags: ['notify', 'matrix'],
653
+ inputPorts: [
654
+ {
655
+ path: 'targetDid',
656
+ portType: 'did',
657
+ required: true,
658
+ description:
659
+ 'Recipient DID; converted to @did-...:<homeserver>. Wire from an upstream block that produces the recipient DID. run() throws "Recipient DID is required" if blank.',
660
+ },
661
+ {
662
+ path: 'message',
663
+ portType: 'string',
664
+ required: true,
665
+ description:
666
+ 'The message text to send. run() throws "Message is required" if blank.',
667
+ },
668
+ ],
669
+ requires: [
670
+ {
671
+ kind: 'recipientDid',
672
+ description:
673
+ "A recipient DID distinct from the sender — ask the user; never use the sender's own DID.",
674
+ },
675
+ ],
676
+ },
677
+ 'qi/notification.push': {
678
+ summary:
679
+ 'Sends a notification through the configured notify service across a chosen delivery channel; side-effect, dispatches to one or more recipients.',
680
+ whenToUse: [
681
+ 'An automation needs to alert one or more recipients over a configurable channel (e.g. push/email/sms).',
682
+ 'You want CC/BCC and an HTML or text body in a single notification.',
683
+ ],
684
+ whenNotToUse: [
685
+ 'For a Matrix direct message to a DID use qi/matrix.dm; for a strictly templated email use qi/email.send — this block is the generic multi-channel notifier.',
686
+ ],
687
+ tags: ['notify'],
688
+ inputPorts: [
689
+ {
690
+ path: 'to',
691
+ portType: 'array',
692
+ required: true,
693
+ description:
694
+ 'Recipient(s); a single value or array — at least one required. run() throws "At least one recipient is required" if empty.',
695
+ },
696
+ {
697
+ path: 'channel',
698
+ portType: 'string',
699
+ description: 'Delivery channel for the notification (service-defined).',
700
+ },
701
+ { path: 'cc', portType: 'array', description: 'CC recipients.' },
702
+ { path: 'bcc', portType: 'array', description: 'BCC recipients.' },
703
+ {
704
+ path: 'subject',
705
+ portType: 'string',
706
+ description: 'Notification subject.',
707
+ },
708
+ {
709
+ path: 'body',
710
+ portType: 'string',
711
+ description: 'Notification body content.',
712
+ },
713
+ {
714
+ path: 'bodyType',
715
+ portType: 'string',
716
+ description: "Body format: 'text' or 'html'.",
717
+ },
718
+ { path: 'from', portType: 'string', description: 'Sender address.' },
719
+ { path: 'replyTo', portType: 'string', description: 'Reply-to address.' },
720
+ ],
721
+ requires: [
722
+ {
723
+ kind: 'recipient',
724
+ description:
725
+ 'At least one recipient — ask the user who should be notified.',
726
+ },
727
+ ],
728
+ },
729
+ 'qi/http.request': {
730
+ summary:
731
+ 'Performs an HTTP request to an external endpoint (via the configured http service, falling back to native fetch) and returns status plus parsed JSON data. No on-chain side-effect, but it does call out to a third-party URL.',
732
+ whenToUse: [
733
+ 'A flow needs to call an external API or webhook.',
734
+ 'You want to fetch or POST data to an off-chain service and use its response downstream (read {{block.output.data}} / {{block.response.*}}).',
735
+ ],
736
+ tags: ['integration', 'http'],
737
+ inputPorts: [
738
+ {
739
+ path: 'endpoint',
740
+ portType: 'string',
741
+ required: true,
742
+ description:
743
+ 'The request URL. Either endpoint or its alias `url` must be provided; run() throws "HTTP request action requires an endpoint or url input" if neither resolves to a non-empty string.',
744
+ },
745
+ {
746
+ path: 'url',
747
+ portType: 'string',
748
+ description:
749
+ 'Alias for endpoint — the request URL. Provide this OR endpoint.',
750
+ },
751
+ {
752
+ path: 'method',
753
+ portType: 'string',
754
+ description: 'HTTP method (GET, POST, etc.). Defaults to GET.',
755
+ },
756
+ {
757
+ path: 'headers',
758
+ portType: 'object',
759
+ description: 'Request headers as a key/value map.',
760
+ },
761
+ {
762
+ path: 'body',
763
+ portType: 'string',
764
+ description:
765
+ 'Request body; only sent for non-GET methods. Accepts a string or an object (serialized to JSON).',
766
+ },
767
+ ],
768
+ requires: [
769
+ {
770
+ kind: 'endpoint',
771
+ description:
772
+ 'The target endpoint URL (and any auth headers/credentials it needs) — ask the user; never invent an endpoint.',
773
+ },
774
+ ],
775
+ },
776
+ 'qi/credential.store': {
777
+ summary:
778
+ 'Stores a verifiable credential in Matrix via the matrix credential-storage service, keyed by credentialKey and deduplicated by a computed IPFS-compatible CID. Side-effect: persists the credential to a Matrix room.',
779
+ whenToUse: [
780
+ 'A flow has produced or collected a credential (e.g. a KYC/AML attestation) that must be persisted.',
781
+ 'You need an idempotent store keyed by content (re-storing the same credential returns duplicate:true).',
782
+ ],
783
+ tags: ['credentials'],
784
+ inputPorts: [
785
+ {
786
+ path: 'credentialKey',
787
+ portType: 'string',
788
+ required: true,
789
+ description:
790
+ 'Key under which to store the credential (e.g. "kycamllevel1"). run() throws "credentialKey is required" if missing.',
791
+ },
792
+ {
793
+ path: 'credential',
794
+ portType: 'object',
795
+ required: true,
796
+ description:
797
+ 'The credential to store — a JSON object (also accepts a JSON string, unwrapped up to 3 layers). Must resolve to a plain object, not an array. Wire from the upstream block that issues/collects the credential.',
798
+ },
799
+ {
800
+ path: 'roomId',
801
+ portType: 'roomId',
802
+ description:
803
+ 'Matrix room ID to store the credential in; defaults to empty if omitted.',
804
+ },
805
+ ],
806
+ requires: [
807
+ {
808
+ kind: 'credentialKey',
809
+ description:
810
+ 'The key (credential type id, e.g. kycamllevel1) under which to store — ask the user; never invent one.',
811
+ },
812
+ ],
813
+ },
814
+
815
+ // ── Wallet & identity ─────────────────────────────────────────────────────
816
+ 'qi/wallet.generate': {
817
+ summary:
818
+ 'Generate a fresh IXO secp256k1 key pair off-chain — no blockchain transaction. Returns the wallet address, derived DID, public key, and BIP39 mnemonic.',
819
+ whenToUse: [
820
+ 'Bootstrap a new oracle/agent identity at the start of a flow so downstream steps can fund it, create its IID document, or register a Matrix account.',
821
+ 'You need a DID + mnemonic + pubKey to feed into iid.create, matrix.register, or identity.create.',
822
+ ],
823
+ whenNotToUse: [
824
+ 'You also need the new wallet funded on-chain in one step — use wallet.generateAndFund instead (this action performs no funding transaction).',
825
+ ],
826
+ tags: ['wallet'],
827
+ inputPorts: [],
828
+ },
829
+ 'qi/wallet.fund': {
830
+ summary:
831
+ 'Fund an existing IXO wallet address with an on-chain funding transaction (defaults to 250000 base units). Side-effecting and confirmation-gated by default; returns the transaction hash.',
832
+ whenToUse: [
833
+ 'Top up a wallet address that already exists so it can pay for subsequent on-chain operations.',
834
+ 'Fund a wallet produced by an upstream wallet.generate step before it creates an IID document.',
835
+ ],
836
+ whenNotToUse: [
837
+ 'The wallet does not exist yet — use wallet.generate first, or wallet.generateAndFund to do both in one step.',
838
+ ],
839
+ tags: ['wallet'],
840
+ inputPorts: [
841
+ {
842
+ path: 'address',
843
+ portType: 'chainAddress',
844
+ required: true,
845
+ description:
846
+ 'IXO wallet address to fund. Typically wired from an upstream wallet.generate step output (address).',
847
+ },
848
+ {
849
+ path: 'amount',
850
+ portType: 'number',
851
+ description:
852
+ 'Funding amount in base units. Defaults to 250000 if omitted.',
853
+ },
854
+ ],
855
+ requires: [
856
+ {
857
+ kind: 'fundingAmount',
858
+ description:
859
+ 'Confirm the funding amount in base units (defaults to 250000 if not specified).',
860
+ },
861
+ ],
862
+ },
863
+ 'qi/wallet.generateAndFund': {
864
+ summary:
865
+ 'One-shot: generate a fresh IXO key pair off-chain, then fund the new wallet address on-chain (defaults to 250000 base units). Returns address, DID, public key, mnemonic, and the funding transaction hash.',
866
+ whenToUse: [
867
+ 'Bootstrap a ready-to-use, funded oracle/agent wallet in a single step at the start of a flow.',
868
+ 'You need both a new DID/mnemonic AND on-chain funds before creating IID documents or registering Matrix accounts.',
869
+ ],
870
+ whenNotToUse: [
871
+ 'You only need keys with no on-chain funding — use wallet.generate (this performs a funding transaction).',
872
+ 'The wallet already exists and only needs topping up — use wallet.fund.',
873
+ ],
874
+ tags: ['wallet'],
875
+ inputPorts: [
876
+ {
877
+ path: 'amount',
878
+ portType: 'number',
879
+ description:
880
+ 'Funding amount in base units for the generated wallet. Defaults to 250000 if omitted.',
881
+ },
882
+ ],
883
+ requires: [
884
+ {
885
+ kind: 'fundingAmount',
886
+ description:
887
+ 'Confirm the funding amount in base units (defaults to 250000 if not specified).',
888
+ },
889
+ ],
890
+ },
891
+ 'qi/iid.create': {
892
+ summary:
893
+ 'Create an on-chain IID (decentralized identity) document for a wallet, broadcasting a blockchain transaction. Returns the IID document DID and the creation transaction hash.',
894
+ whenToUse: [
895
+ 'Register an on-chain IID document for a wallet produced by an upstream wallet step.',
896
+ 'You need a DID anchored on-chain before it can be used as an oracle/agent identity.',
897
+ ],
898
+ whenNotToUse: [
899
+ 'You also need a Matrix account registered for the identity — use identity.create, which creates the IID document AND registers Matrix in one step.',
900
+ ],
901
+ tags: ['identity', 'iid'],
902
+ inputPorts: [
903
+ {
904
+ path: 'mnemonic',
905
+ portType: 'mnemonic',
906
+ required: true,
907
+ description:
908
+ 'Wallet mnemonic used to sign the IID creation. Typically wired from an upstream wallet step output (mnemonic).',
909
+ },
910
+ {
911
+ path: 'did',
912
+ portType: 'did',
913
+ required: true,
914
+ description:
915
+ 'DID for the IID document. Typically wired from an upstream wallet step output (did).',
916
+ },
917
+ {
918
+ path: 'address',
919
+ portType: 'chainAddress',
920
+ required: true,
921
+ description:
922
+ 'Blockchain address for the IID document. Typically wired from an upstream wallet step output (address).',
923
+ },
924
+ {
925
+ path: 'pubKey',
926
+ portType: 'string',
927
+ required: true,
928
+ description:
929
+ 'secp256k1 public key for the IID document. Typically wired from an upstream wallet step output (pubKey).',
930
+ },
931
+ ],
932
+ },
933
+ 'qi/identity.create': {
934
+ summary:
935
+ 'Full identity bootstrap: create an on-chain IID document AND register a Matrix account in two phases. Returns the DID, IID transaction hash, alreadyExisted flag, and the full Matrix account credentials (user ID, access token, room ID, device ID, mnemonic, password, recovery phrase, homeserver URL).',
936
+ whenToUse: [
937
+ 'Stand up a complete oracle/agent identity (on-chain IID + Matrix messaging account) from a single funded wallet.',
938
+ 'You need both an on-chain DID and Matrix credentials downstream.',
939
+ ],
940
+ whenNotToUse: [
941
+ 'You only need the on-chain IID document with no Matrix account — use iid.create.',
942
+ 'You only need a Matrix account for an identity that already has its IID document — use matrix.register.',
943
+ ],
944
+ tags: ['identity', 'matrix', 'iid'],
945
+ inputPorts: [
946
+ {
947
+ path: 'mnemonic',
948
+ portType: 'mnemonic',
949
+ required: true,
950
+ description:
951
+ 'Wallet mnemonic used to create the IID document and Matrix account. Typically wired from an upstream wallet step output (mnemonic).',
952
+ },
953
+ {
954
+ path: 'did',
955
+ portType: 'did',
956
+ required: true,
957
+ description:
958
+ 'DID for the IID document. Typically wired from an upstream wallet step output (did).',
959
+ },
960
+ {
961
+ path: 'address',
962
+ portType: 'chainAddress',
963
+ required: true,
964
+ description:
965
+ 'Blockchain address. Typically wired from an upstream wallet step output (address).',
966
+ },
967
+ {
968
+ path: 'pubKey',
969
+ portType: 'string',
970
+ required: true,
971
+ description:
972
+ 'secp256k1 public key. Typically wired from an upstream wallet step output (pubKey).',
973
+ },
974
+ {
975
+ path: 'pin',
976
+ portType: 'string',
977
+ required: true,
978
+ description: 'PIN used to register the Matrix account.',
979
+ },
980
+ {
981
+ path: 'oracleName',
982
+ portType: 'string',
983
+ required: true,
984
+ description: 'Oracle name for the Matrix account.',
985
+ },
986
+ {
987
+ path: 'avatarUrl',
988
+ portType: 'string',
989
+ description:
990
+ 'Avatar URL for the Matrix account (mapped from form logoUrl).',
991
+ },
992
+ {
993
+ path: 'formAnswers',
994
+ portType: 'string',
995
+ description:
996
+ 'JSON string of runtime form answers merged into inputs; explicit template inputs take precedence.',
997
+ },
998
+ ],
999
+ requires: [
1000
+ {
1001
+ kind: 'pin',
1002
+ description: 'A PIN is required to register the Matrix account.',
1003
+ },
1004
+ {
1005
+ kind: 'oracleName',
1006
+ description: 'An oracle name is required for the Matrix account.',
1007
+ },
1008
+ ],
1009
+ },
1010
+ 'qi/matrix.register': {
1011
+ summary:
1012
+ 'Register a Matrix messaging account for an existing wallet/DID. Returns the Matrix user ID, access token, room ID, device ID, mnemonic, password, recovery phrase, and homeserver URL.',
1013
+ whenToUse: [
1014
+ 'Give an existing identity a Matrix account for encrypted collaboration/messaging.',
1015
+ 'Add Matrix credentials to a wallet/DID that already has its on-chain IID document.',
1016
+ ],
1017
+ whenNotToUse: [
1018
+ 'The identity has no on-chain IID document yet and you want both at once — use identity.create, which creates the IID document and registers Matrix together.',
1019
+ ],
1020
+ tags: ['matrix'],
1021
+ inputPorts: [
1022
+ {
1023
+ path: 'mnemonic',
1024
+ portType: 'mnemonic',
1025
+ required: true,
1026
+ description:
1027
+ 'Wallet mnemonic used to register the Matrix account. Typically wired from an upstream wallet step output (mnemonic).',
1028
+ },
1029
+ {
1030
+ path: 'address',
1031
+ portType: 'chainAddress',
1032
+ required: true,
1033
+ description:
1034
+ 'Blockchain address. Typically wired from an upstream wallet step output (address).',
1035
+ },
1036
+ {
1037
+ path: 'did',
1038
+ portType: 'did',
1039
+ required: true,
1040
+ description:
1041
+ 'DID associated with the Matrix account. Typically wired from an upstream wallet step output (did).',
1042
+ },
1043
+ {
1044
+ path: 'pin',
1045
+ portType: 'string',
1046
+ required: true,
1047
+ description: 'PIN used to register the Matrix account.',
1048
+ },
1049
+ {
1050
+ path: 'oracleName',
1051
+ portType: 'string',
1052
+ required: true,
1053
+ description: 'Oracle name for the Matrix account.',
1054
+ },
1055
+ {
1056
+ path: 'avatarUrl',
1057
+ portType: 'string',
1058
+ description: 'Avatar URL for the Matrix account.',
1059
+ },
1060
+ ],
1061
+ requires: [
1062
+ {
1063
+ kind: 'pin',
1064
+ description: 'A PIN is required to register the Matrix account.',
1065
+ },
1066
+ {
1067
+ kind: 'oracleName',
1068
+ description: 'An oracle name is required for the Matrix account.',
1069
+ },
1070
+ ],
1071
+ },
1072
+
1073
+ // ── Oracle setup & deploy ─────────────────────────────────────────────────
1074
+ 'qi/entity.createOracle': {
1075
+ summary:
1076
+ 'Creates the oracle on-chain entity (broadcasts the entity-creation tx) and registers a P-256 keyAgreement encryption key on the oracle entity DID. On-chain side effect.',
1077
+ whenToUse: [
1078
+ 'As the on-chain entity step of the oracle-creation flow, after the oracle wallet, IID, and Matrix account exist.',
1079
+ ],
1080
+ tags: ['oracle', 'entity', 'onchain'],
1081
+ inputPorts: [
1082
+ {
1083
+ path: 'mnemonic',
1084
+ portType: 'mnemonic',
1085
+ required: true,
1086
+ description:
1087
+ 'Oracle wallet BIP39 mnemonic, typically from the wallet.generate output.',
1088
+ },
1089
+ {
1090
+ path: 'address',
1091
+ portType: 'chainAddress',
1092
+ required: true,
1093
+ description:
1094
+ 'Oracle wallet address, typically from the wallet.generate output.',
1095
+ },
1096
+ {
1097
+ path: 'did',
1098
+ portType: 'did',
1099
+ required: true,
1100
+ description: 'Oracle DID, typically from the wallet.generate output.',
1101
+ },
1102
+ {
1103
+ path: 'pubKey',
1104
+ portType: 'string',
1105
+ required: true,
1106
+ description:
1107
+ 'Oracle wallet public key, typically from the wallet.generate output.',
1108
+ },
1109
+ {
1110
+ path: 'pin',
1111
+ portType: 'string',
1112
+ required: true,
1113
+ description:
1114
+ 'PIN used to sign the entity creation; typically from the form output.',
1115
+ },
1116
+ {
1117
+ path: 'matrixAccessToken',
1118
+ portType: 'string',
1119
+ required: true,
1120
+ description:
1121
+ 'Oracle Matrix access token, typically from the matrix.register output.',
1122
+ },
1123
+ {
1124
+ path: 'matrixRoomId',
1125
+ portType: 'roomId',
1126
+ required: true,
1127
+ description:
1128
+ 'Oracle Matrix room id, typically from the matrix.register output.',
1129
+ },
1130
+ {
1131
+ path: 'oracleName',
1132
+ portType: 'string',
1133
+ required: true,
1134
+ description: 'Display name of the oracle.',
1135
+ },
1136
+ {
1137
+ path: 'apiUrl',
1138
+ portType: 'string',
1139
+ required: true,
1140
+ description: 'Oracle API URL.',
1141
+ },
1142
+ {
1143
+ path: 'price',
1144
+ portType: 'string',
1145
+ required: true,
1146
+ description: 'Oracle subscription price.',
1147
+ },
1148
+ {
1149
+ path: 'orgName',
1150
+ portType: 'string',
1151
+ description: 'Optional organization name.',
1152
+ },
1153
+ {
1154
+ path: 'description',
1155
+ portType: 'string',
1156
+ description: 'Optional oracle description.',
1157
+ },
1158
+ {
1159
+ path: 'location',
1160
+ portType: 'string',
1161
+ description: 'Optional oracle location.',
1162
+ },
1163
+ {
1164
+ path: 'logoUrl',
1165
+ portType: 'string',
1166
+ description: 'Optional oracle logo URL.',
1167
+ },
1168
+ {
1169
+ path: 'coverImageUrl',
1170
+ portType: 'string',
1171
+ description: 'Optional oracle cover image URL.',
1172
+ },
1173
+ {
1174
+ path: 'llmModel',
1175
+ portType: 'string',
1176
+ description: 'Optional LLM model identifier.',
1177
+ },
1178
+ {
1179
+ path: 'opening',
1180
+ portType: 'string',
1181
+ description: 'Optional oracle opening message.',
1182
+ },
1183
+ {
1184
+ path: 'communicationStyle',
1185
+ portType: 'string',
1186
+ description: 'Optional oracle communication style.',
1187
+ },
1188
+ {
1189
+ path: 'capabilities',
1190
+ portType: 'array',
1191
+ description: 'Optional oracle capabilities.',
1192
+ },
1193
+ {
1194
+ path: 'mcpConfig',
1195
+ portType: 'object',
1196
+ description: 'Optional MCP configuration.',
1197
+ },
1198
+ {
1199
+ path: 'parentProtocol',
1200
+ portType: 'string',
1201
+ description: 'Optional parent protocol DID.',
1202
+ },
1203
+ {
1204
+ path: 'formAnswers',
1205
+ portType: 'string',
1206
+ description:
1207
+ 'Optional JSON string of form answers; parsed and merged under the explicit inputs.',
1208
+ },
1209
+ ],
1210
+ requires: [
1211
+ {
1212
+ kind: 'oracleDetails',
1213
+ description:
1214
+ 'Oracle name, API URL, and price (plus optional org/description/branding/LLM settings) — ask the user.',
1215
+ },
1216
+ {
1217
+ kind: 'pin',
1218
+ description: 'A PIN used to sign the entity creation — ask the user.',
1219
+ },
1220
+ ],
1221
+ },
1222
+ oracle: {
1223
+ summary:
1224
+ 'Sends a prompt to the companion assistant via the askCompanion handler. Read-only — no on-chain, Matrix, or sandbox side effect.',
1225
+ whenToUse: [
1226
+ 'An automation needs to ask the in-app companion/assistant a question.',
1227
+ ],
1228
+ whenNotToUse: [
1229
+ 'Do not use for the oracle-creation pipeline — this is an unrelated companion-prompt action, not an oracle setup step.',
1230
+ ],
1231
+ tags: ['companion', 'prompt'],
1232
+ inputPorts: [
1233
+ {
1234
+ path: 'prompt',
1235
+ portType: 'string',
1236
+ required: true,
1237
+ description: 'The prompt text sent to the companion.',
1238
+ },
1239
+ ],
1240
+ },
1241
+ 'qi/oracle.contract': {
1242
+ summary:
1243
+ 'Contracts the oracle by ensuring the user-oracle Matrix DM room exists and the user has joined it, returning that room id. Matrix state side effect only — no chain calls.',
1244
+ whenToUse: [
1245
+ 'As the step that opens the user-oracle DM room before storing secrets/config, after the oracle entity exists.',
1246
+ ],
1247
+ tags: ['oracle', 'matrix', 'contract'],
1248
+ inputPorts: [
1249
+ {
1250
+ path: 'oracleEntityDid',
1251
+ portType: 'entityDid',
1252
+ required: true,
1253
+ description: 'Oracle entity DID, from the entity.createOracle output.',
1254
+ },
1255
+ ],
1256
+ },
1257
+ 'qi/oracle.configureOracle': {
1258
+ summary:
1259
+ 'Runs the full configure pipeline in one action: contracts the oracle (opens the user-oracle Matrix room), then JWE-encrypts and stores oracle secrets, then stores the oracle config — all into that room. Matrix state side effect.',
1260
+ whenToUse: [
1261
+ 'As the one-shot configure step that contracts the oracle and writes both secrets and config in sequence.',
1262
+ ],
1263
+ whenNotToUse: [
1264
+ 'Use this when you also need the contract phase; if the user-oracle room already exists and you only need to write secrets+config, use qi/oracle.storeSecretsAndConfig instead.',
1265
+ ],
1266
+ tags: ['oracle', 'configure', 'matrix', 'secrets'],
1267
+ inputPorts: [
1268
+ {
1269
+ path: 'oracleEntityDid',
1270
+ portType: 'entityDid',
1271
+ required: true,
1272
+ description: 'Oracle entity DID, from the entity.createOracle output.',
1273
+ },
1274
+ {
1275
+ path: 'publicKeyMultibase',
1276
+ portType: 'string',
1277
+ required: true,
1278
+ description:
1279
+ 'Oracle encryption public key (multibase), from the entity.createOracle output.',
1280
+ },
1281
+ {
1282
+ path: 'verificationMethodId',
1283
+ portType: 'string',
1284
+ required: true,
1285
+ description:
1286
+ 'Encryption verification method id, from the entity.createOracle output.',
1287
+ },
1288
+ {
1289
+ path: 'matrixHomeServerUrl',
1290
+ portType: 'string',
1291
+ required: true,
1292
+ description: 'Matrix homeserver URL, from the matrix.register output.',
1293
+ },
1294
+ {
1295
+ path: 'matrixUsername',
1296
+ portType: 'string',
1297
+ required: true,
1298
+ description:
1299
+ 'Matrix username, from the matrix.register output (matrixUserId).',
1300
+ },
1301
+ {
1302
+ path: 'matrixPassword',
1303
+ portType: 'string',
1304
+ required: true,
1305
+ description: 'Matrix password, from the matrix.register output.',
1306
+ },
1307
+ {
1308
+ path: 'mnemonic',
1309
+ portType: 'mnemonic',
1310
+ required: true,
1311
+ description:
1312
+ 'Oracle wallet mnemonic, from the wallet.generate output; stored as SECP_MNEMONIC secret.',
1313
+ },
1314
+ {
1315
+ path: 'matrixRecoveryPhrase',
1316
+ portType: 'string',
1317
+ required: true,
1318
+ description: 'Matrix recovery phrase, from the matrix.register output.',
1319
+ },
1320
+ {
1321
+ path: 'pin',
1322
+ portType: 'string',
1323
+ required: true,
1324
+ description:
1325
+ 'PIN, from the form output; stored as MATRIX_VALUE_PIN secret.',
1326
+ },
1327
+ {
1328
+ path: 'openRouterApiKeyJwe',
1329
+ portType: 'string',
1330
+ required: true,
1331
+ description:
1332
+ 'JWE-encrypted OpenRouter API key from the form (must be pre-encrypted before submit).',
1333
+ },
1334
+ {
1335
+ path: 'oracleName',
1336
+ portType: 'string',
1337
+ required: true,
1338
+ description: 'Oracle name.',
1339
+ },
1340
+ {
1341
+ path: 'entityDid',
1342
+ portType: 'entityDid',
1343
+ required: true,
1344
+ description: 'Oracle entity DID, written into the stored config.',
1345
+ },
1346
+ {
1347
+ path: 'openRouterApiKeyPlaintext',
1348
+ portType: 'string',
1349
+ description:
1350
+ 'Optional plaintext OpenRouter API key passed through to the deploy output.',
1351
+ },
1352
+ {
1353
+ path: 'mcpAuthSecrets',
1354
+ portType: 'object',
1355
+ description:
1356
+ 'Optional pre-encrypted MCP auth secrets (object or JSON string), merged into the stored secrets.',
1357
+ },
1358
+ {
1359
+ path: 'orgName',
1360
+ portType: 'string',
1361
+ description: 'Optional organisation name.',
1362
+ },
1363
+ {
1364
+ path: 'description',
1365
+ portType: 'string',
1366
+ description: 'Optional oracle description.',
1367
+ },
1368
+ {
1369
+ path: 'location',
1370
+ portType: 'string',
1371
+ description: 'Optional oracle location.',
1372
+ },
1373
+ {
1374
+ path: 'price',
1375
+ portType: 'number',
1376
+ description: 'Optional oracle price; defaults to 0.',
1377
+ },
1378
+ {
1379
+ path: 'apiUrl',
1380
+ portType: 'string',
1381
+ description: 'Optional oracle API URL.',
1382
+ },
1383
+ {
1384
+ path: 'logoUrl',
1385
+ portType: 'string',
1386
+ description: 'Optional oracle logo URL.',
1387
+ },
1388
+ {
1389
+ path: 'llmModel',
1390
+ portType: 'string',
1391
+ description: 'Optional LLM model identifier.',
1392
+ },
1393
+ {
1394
+ path: 'opening',
1395
+ portType: 'string',
1396
+ description: 'Optional oracle opening message.',
1397
+ },
1398
+ {
1399
+ path: 'communicationStyle',
1400
+ portType: 'string',
1401
+ description: 'Optional oracle communication style.',
1402
+ },
1403
+ {
1404
+ path: 'capabilities',
1405
+ portType: 'string',
1406
+ description: 'Optional oracle capabilities description.',
1407
+ },
1408
+ {
1409
+ path: 'skills',
1410
+ portType: 'array',
1411
+ description: 'Optional list of oracle skills.',
1412
+ },
1413
+ {
1414
+ path: 'mcpServers',
1415
+ portType: 'array',
1416
+ description: 'Optional list of configured MCP servers.',
1417
+ },
1418
+ {
1419
+ path: 'matrixUserId',
1420
+ portType: 'string',
1421
+ description: 'Optional oracle Matrix user id.',
1422
+ },
1423
+ {
1424
+ path: 'matrixAccountRoomId',
1425
+ portType: 'roomId',
1426
+ description: 'Optional oracle Matrix account room id.',
1427
+ },
1428
+ {
1429
+ path: 'oracleAddress',
1430
+ portType: 'chainAddress',
1431
+ description: 'Optional oracle wallet address.',
1432
+ },
1433
+ {
1434
+ path: 'oracleDid',
1435
+ portType: 'did',
1436
+ description: 'Optional oracle DID.',
1437
+ },
1438
+ {
1439
+ path: 'formAnswers',
1440
+ portType: 'string',
1441
+ description:
1442
+ 'Optional JSON string of form answers; parsed and merged under the explicit inputs.',
1443
+ },
1444
+ ],
1445
+ requires: [
1446
+ {
1447
+ kind: 'openRouterApiKey',
1448
+ description:
1449
+ 'An OpenRouter API key, JWE-encrypted before submit (openRouterApiKeyJwe) — ask the user.',
1450
+ },
1451
+ { kind: 'pin', description: 'A PIN stored as a secret — ask the user.' },
1452
+ ],
1453
+ },
1454
+ 'qi/oracle.storeSecrets': {
1455
+ summary:
1456
+ 'JWE-encrypts and writes the oracle secrets (SECP_MNEMONIC, Matrix admin password, recovery phrase, PIN, encrypted OpenRouter key, optional MCP auth secrets) into the user-oracle Matrix room via a fresh mxLogin. Matrix state side effect.',
1457
+ whenToUse: [
1458
+ 'As the secrets step after qi/oracle.contract, when you want to store secrets separately from config.',
1459
+ ],
1460
+ whenNotToUse: [
1461
+ 'If you also need to store the oracle config in the same step, use qi/oracle.storeSecretsAndConfig; this action writes secrets only.',
1462
+ ],
1463
+ tags: ['oracle', 'secrets', 'matrix'],
1464
+ inputPorts: [
1465
+ {
1466
+ path: 'matrixRoomId',
1467
+ portType: 'roomId',
1468
+ required: true,
1469
+ description:
1470
+ 'User-oracle Matrix room id, from the oracle.contract output.',
1471
+ },
1472
+ {
1473
+ path: 'publicKeyMultibase',
1474
+ portType: 'string',
1475
+ required: true,
1476
+ description:
1477
+ 'Oracle encryption public key (multibase), from the entity.createOracle output.',
1478
+ },
1479
+ {
1480
+ path: 'verificationMethodId',
1481
+ portType: 'string',
1482
+ required: true,
1483
+ description:
1484
+ 'Encryption verification method id, from the entity.createOracle output.',
1485
+ },
1486
+ {
1487
+ path: 'matrixHomeServerUrl',
1488
+ portType: 'string',
1489
+ required: true,
1490
+ description: 'Matrix homeserver URL, from the matrix.register output.',
1491
+ },
1492
+ {
1493
+ path: 'matrixUsername',
1494
+ portType: 'string',
1495
+ required: true,
1496
+ description:
1497
+ 'Matrix username, from the matrix.register output (matrixUserId).',
1498
+ },
1499
+ {
1500
+ path: 'matrixPassword',
1501
+ portType: 'string',
1502
+ required: true,
1503
+ description: 'Matrix password, from the matrix.register output.',
1504
+ },
1505
+ {
1506
+ path: 'mnemonic',
1507
+ portType: 'mnemonic',
1508
+ required: true,
1509
+ description:
1510
+ 'Oracle wallet mnemonic, from the wallet.generate output; stored as SECP_MNEMONIC.',
1511
+ },
1512
+ {
1513
+ path: 'matrixRecoveryPhrase',
1514
+ portType: 'string',
1515
+ required: true,
1516
+ description: 'Matrix recovery phrase, from the matrix.register output.',
1517
+ },
1518
+ {
1519
+ path: 'pin',
1520
+ portType: 'string',
1521
+ required: true,
1522
+ description: 'PIN, from the form output; stored as MATRIX_VALUE_PIN.',
1523
+ },
1524
+ {
1525
+ path: 'openRouterApiKeyJwe',
1526
+ portType: 'string',
1527
+ required: true,
1528
+ description:
1529
+ 'JWE-encrypted OpenRouter API key from the form (must be pre-encrypted before submit).',
1530
+ },
1531
+ {
1532
+ path: 'mcpAuthSecrets',
1533
+ portType: 'object',
1534
+ description:
1535
+ 'Optional pre-encrypted MCP auth secrets (object or JSON string), merged into the stored secrets.',
1536
+ },
1537
+ ],
1538
+ requires: [
1539
+ {
1540
+ kind: 'openRouterApiKey',
1541
+ description:
1542
+ 'An OpenRouter API key, JWE-encrypted before submit (openRouterApiKeyJwe) — ask the user.',
1543
+ },
1544
+ { kind: 'pin', description: 'A PIN stored as a secret — ask the user.' },
1545
+ ],
1546
+ },
1547
+ 'qi/oracle.storeConfig': {
1548
+ summary:
1549
+ 'Writes the oracle config (name, org, description, pricing, branding, LLM/communication settings, skills, MCP servers, identity refs) as a state event into the user-oracle Matrix room. Matrix state side effect.',
1550
+ whenToUse: [
1551
+ 'As the config step after secrets are stored, to persist the oracle public configuration into its Matrix room.',
1552
+ ],
1553
+ whenNotToUse: [
1554
+ 'If you also need to store secrets in the same step, use qi/oracle.storeSecretsAndConfig; this action writes config only.',
1555
+ ],
1556
+ tags: ['oracle', 'config', 'matrix'],
1557
+ inputPorts: [
1558
+ {
1559
+ path: 'matrixRoomId',
1560
+ portType: 'roomId',
1561
+ required: true,
1562
+ description:
1563
+ 'User-oracle Matrix room id, from the oracle.contract output.',
1564
+ },
1565
+ {
1566
+ path: 'oracleName',
1567
+ portType: 'string',
1568
+ required: true,
1569
+ description: 'Oracle name.',
1570
+ },
1571
+ {
1572
+ path: 'entityDid',
1573
+ portType: 'entityDid',
1574
+ required: true,
1575
+ description: 'Oracle entity DID, from the entity.createOracle output.',
1576
+ },
1577
+ {
1578
+ path: 'orgName',
1579
+ portType: 'string',
1580
+ description: 'Optional organisation name.',
1581
+ },
1582
+ {
1583
+ path: 'description',
1584
+ portType: 'string',
1585
+ description: 'Optional oracle description.',
1586
+ },
1587
+ {
1588
+ path: 'location',
1589
+ portType: 'string',
1590
+ description: 'Optional oracle location.',
1591
+ },
1592
+ {
1593
+ path: 'price',
1594
+ portType: 'number',
1595
+ description: 'Optional oracle price; defaults to 0.',
1596
+ },
1597
+ {
1598
+ path: 'apiUrl',
1599
+ portType: 'string',
1600
+ description: 'Optional oracle API URL.',
1601
+ },
1602
+ {
1603
+ path: 'logoUrl',
1604
+ portType: 'string',
1605
+ description: 'Optional oracle logo URL.',
1606
+ },
1607
+ {
1608
+ path: 'llmModel',
1609
+ portType: 'string',
1610
+ description: 'Optional LLM model identifier.',
1611
+ },
1612
+ {
1613
+ path: 'opening',
1614
+ portType: 'string',
1615
+ description: 'Optional oracle opening message.',
1616
+ },
1617
+ {
1618
+ path: 'communicationStyle',
1619
+ portType: 'string',
1620
+ description: 'Optional oracle communication style.',
1621
+ },
1622
+ {
1623
+ path: 'capabilities',
1624
+ portType: 'string',
1625
+ description: 'Optional oracle capabilities description.',
1626
+ },
1627
+ {
1628
+ path: 'skills',
1629
+ portType: 'array',
1630
+ description: 'Optional list of oracle skills.',
1631
+ },
1632
+ {
1633
+ path: 'mcpServers',
1634
+ portType: 'array',
1635
+ description: 'Optional list of configured MCP servers.',
1636
+ },
1637
+ {
1638
+ path: 'matrixUserId',
1639
+ portType: 'string',
1640
+ description: 'Optional oracle Matrix user id.',
1641
+ },
1642
+ {
1643
+ path: 'matrixAccountRoomId',
1644
+ portType: 'roomId',
1645
+ description: 'Optional oracle Matrix account room id.',
1646
+ },
1647
+ {
1648
+ path: 'oracleAddress',
1649
+ portType: 'chainAddress',
1650
+ description: 'Optional oracle wallet address.',
1651
+ },
1652
+ {
1653
+ path: 'oracleDid',
1654
+ portType: 'did',
1655
+ description: 'Optional oracle DID.',
1656
+ },
1657
+ ],
1658
+ },
1659
+ 'qi/oracle.storeSecretsAndConfig': {
1660
+ summary:
1661
+ 'Combined step: JWE-encrypts and stores the oracle secrets, then stores the oracle config, both into the existing user-oracle Matrix room. Matrix state side effect. Does NOT contract the room.',
1662
+ whenToUse: [
1663
+ 'As the combined secrets+config step after qi/oracle.contract, when the user-oracle room already exists.',
1664
+ ],
1665
+ whenNotToUse: [
1666
+ 'If the user-oracle room does not exist yet, use qi/oracle.configureOracle (which contracts first); use qi/oracle.storeSecrets or qi/oracle.storeConfig if you only need one of the two writes.',
1667
+ ],
1668
+ tags: ['oracle', 'secrets', 'config', 'matrix'],
1669
+ inputPorts: [
1670
+ {
1671
+ path: 'matrixRoomId',
1672
+ portType: 'roomId',
1673
+ required: true,
1674
+ description:
1675
+ 'User-oracle Matrix room id, from the oracle.contract output.',
1676
+ },
1677
+ {
1678
+ path: 'publicKeyMultibase',
1679
+ portType: 'string',
1680
+ required: true,
1681
+ description:
1682
+ 'Oracle encryption public key (multibase), from the entity.createOracle output.',
1683
+ },
1684
+ {
1685
+ path: 'verificationMethodId',
1686
+ portType: 'string',
1687
+ required: true,
1688
+ description:
1689
+ 'Encryption verification method id, from the entity.createOracle output.',
1690
+ },
1691
+ {
1692
+ path: 'matrixHomeServerUrl',
1693
+ portType: 'string',
1694
+ required: true,
1695
+ description: 'Matrix homeserver URL, from the matrix.register output.',
1696
+ },
1697
+ {
1698
+ path: 'matrixUsername',
1699
+ portType: 'string',
1700
+ required: true,
1701
+ description:
1702
+ 'Matrix username, from the matrix.register output (matrixUserId).',
1703
+ },
1704
+ {
1705
+ path: 'matrixPassword',
1706
+ portType: 'string',
1707
+ required: true,
1708
+ description: 'Matrix password, from the matrix.register output.',
1709
+ },
1710
+ {
1711
+ path: 'mnemonic',
1712
+ portType: 'mnemonic',
1713
+ required: true,
1714
+ description:
1715
+ 'Oracle wallet mnemonic, from the wallet.generate output; stored as SECP_MNEMONIC.',
1716
+ },
1717
+ {
1718
+ path: 'matrixRecoveryPhrase',
1719
+ portType: 'string',
1720
+ required: true,
1721
+ description: 'Matrix recovery phrase, from the matrix.register output.',
1722
+ },
1723
+ {
1724
+ path: 'pin',
1725
+ portType: 'string',
1726
+ required: true,
1727
+ description: 'PIN, from the form output; stored as MATRIX_VALUE_PIN.',
1728
+ },
1729
+ {
1730
+ path: 'openRouterApiKeyJwe',
1731
+ portType: 'string',
1732
+ required: true,
1733
+ description:
1734
+ 'JWE-encrypted OpenRouter API key from the form (must be pre-encrypted before submit).',
1735
+ },
1736
+ {
1737
+ path: 'oracleName',
1738
+ portType: 'string',
1739
+ required: true,
1740
+ description: 'Oracle name.',
1741
+ },
1742
+ {
1743
+ path: 'entityDid',
1744
+ portType: 'entityDid',
1745
+ required: true,
1746
+ description: 'Oracle entity DID, from the entity.createOracle output.',
1747
+ },
1748
+ {
1749
+ path: 'mcpAuthSecrets',
1750
+ portType: 'object',
1751
+ description:
1752
+ 'Optional pre-encrypted MCP auth secrets (object or JSON string), merged into the stored secrets.',
1753
+ },
1754
+ {
1755
+ path: 'orgName',
1756
+ portType: 'string',
1757
+ description: 'Optional organisation name.',
1758
+ },
1759
+ {
1760
+ path: 'description',
1761
+ portType: 'string',
1762
+ description: 'Optional oracle description.',
1763
+ },
1764
+ {
1765
+ path: 'location',
1766
+ portType: 'string',
1767
+ description: 'Optional oracle location.',
1768
+ },
1769
+ {
1770
+ path: 'price',
1771
+ portType: 'number',
1772
+ description: 'Optional oracle price; defaults to 0.',
1773
+ },
1774
+ {
1775
+ path: 'apiUrl',
1776
+ portType: 'string',
1777
+ description: 'Optional oracle API URL.',
1778
+ },
1779
+ {
1780
+ path: 'logoUrl',
1781
+ portType: 'string',
1782
+ description: 'Optional oracle logo URL.',
1783
+ },
1784
+ {
1785
+ path: 'llmModel',
1786
+ portType: 'string',
1787
+ description: 'Optional LLM model identifier.',
1788
+ },
1789
+ {
1790
+ path: 'opening',
1791
+ portType: 'string',
1792
+ description: 'Optional oracle opening message.',
1793
+ },
1794
+ {
1795
+ path: 'communicationStyle',
1796
+ portType: 'string',
1797
+ description: 'Optional oracle communication style.',
1798
+ },
1799
+ {
1800
+ path: 'capabilities',
1801
+ portType: 'string',
1802
+ description: 'Optional oracle capabilities description.',
1803
+ },
1804
+ {
1805
+ path: 'skills',
1806
+ portType: 'array',
1807
+ description: 'Optional list of oracle skills.',
1808
+ },
1809
+ {
1810
+ path: 'mcpServers',
1811
+ portType: 'array',
1812
+ description: 'Optional list of configured MCP servers.',
1813
+ },
1814
+ {
1815
+ path: 'matrixUserId',
1816
+ portType: 'string',
1817
+ description: 'Optional oracle Matrix user id.',
1818
+ },
1819
+ {
1820
+ path: 'matrixAccountRoomId',
1821
+ portType: 'roomId',
1822
+ description: 'Optional oracle Matrix account room id.',
1823
+ },
1824
+ {
1825
+ path: 'oracleAddress',
1826
+ portType: 'chainAddress',
1827
+ description: 'Optional oracle wallet address.',
1828
+ },
1829
+ {
1830
+ path: 'oracleDid',
1831
+ portType: 'did',
1832
+ description: 'Optional oracle DID.',
1833
+ },
1834
+ ],
1835
+ requires: [
1836
+ {
1837
+ kind: 'openRouterApiKey',
1838
+ description:
1839
+ 'An OpenRouter API key, JWE-encrypted before submit (openRouterApiKeyJwe) — ask the user.',
1840
+ },
1841
+ { kind: 'pin', description: 'A PIN stored as a secret — ask the user.' },
1842
+ ],
1843
+ },
1844
+ 'qi/oracle.deploy': {
1845
+ summary:
1846
+ 'Full deploy step: runs deploy setup (build), then deploy start (launches the oracle process), then if a URL is returned broadcasts an on-chain domain update to point the entity at the new deployment URL. Sandbox/process provisioning plus an on-chain tx side effect.',
1847
+ whenToUse: [
1848
+ 'As the final deploy step of the oracle-creation flow, after secrets and config are stored.',
1849
+ ],
1850
+ whenNotToUse: [
1851
+ 'If you need to run the build and launch phases as separate blocks, use qi/oracle.deploySetup and qi/oracle.deployStart instead of this combined action.',
1852
+ ],
1853
+ tags: ['oracle', 'deploy', 'onchain'],
1854
+ inputPorts: [
1855
+ {
1856
+ path: 'name',
1857
+ portType: 'string',
1858
+ required: true,
1859
+ description: 'Project name, from the form.',
1860
+ },
1861
+ {
1862
+ path: 'entityDid',
1863
+ portType: 'entityDid',
1864
+ required: true,
1865
+ description: 'Oracle entity DID, from the entity.createOracle output.',
1866
+ },
1867
+ {
1868
+ path: 'roomId',
1869
+ portType: 'roomId',
1870
+ required: true,
1871
+ description:
1872
+ 'User-oracle Matrix room id, from the oracle.configureOracle output.',
1873
+ },
1874
+ {
1875
+ path: 'config',
1876
+ portType: 'object',
1877
+ required: true,
1878
+ description: 'Oracle config object (or its JSON string).',
1879
+ },
1880
+ {
1881
+ path: 'secrets',
1882
+ portType: 'object',
1883
+ description: 'Optional map of secret name to value passed to deploy.',
1884
+ },
1885
+ ],
1886
+ },
1887
+ 'qi/oracle.deploySetup': {
1888
+ summary:
1889
+ 'Runs the oracle deploy setup (build) phase for the project, writing build output. Sandbox/process provisioning side effect; does not launch the running process.',
1890
+ whenToUse: [
1891
+ 'As the build phase of a split deploy, before qi/oracle.deployStart.',
1892
+ ],
1893
+ whenNotToUse: [
1894
+ 'This only builds — it does not start the oracle process or update the on-chain domain; pair it with qi/oracle.deployStart, or use qi/oracle.deploy to do everything in one block.',
1895
+ ],
1896
+ tags: ['oracle', 'deploy', 'setup'],
1897
+ inputPorts: [
1898
+ {
1899
+ path: 'name',
1900
+ portType: 'string',
1901
+ required: true,
1902
+ description: 'Project name, from the form.',
1903
+ },
1904
+ {
1905
+ path: 'config',
1906
+ portType: 'object',
1907
+ required: true,
1908
+ description: 'Oracle config object (or its JSON string).',
1909
+ },
1910
+ {
1911
+ path: 'roomId',
1912
+ portType: 'roomId',
1913
+ required: true,
1914
+ description:
1915
+ 'User-oracle Matrix room id, from the oracle contract/configure output.',
1916
+ },
1917
+ {
1918
+ path: 'secrets',
1919
+ portType: 'object',
1920
+ description:
1921
+ 'Optional map of secret name to value passed to deploy setup.',
1922
+ },
1923
+ ],
1924
+ },
1925
+ 'qi/oracle.deployStart': {
1926
+ summary:
1927
+ 'Launches the oracle running process for the project and returns its process id/status. Sandbox/process provisioning side effect; does not run the build or update the on-chain domain.',
1928
+ whenToUse: [
1929
+ 'As the launch phase of a split deploy, after qi/oracle.deploySetup.',
1930
+ ],
1931
+ whenNotToUse: [
1932
+ 'This starts the process only — run qi/oracle.deploySetup first for the build, and note it does not update the on-chain domain (qi/oracle.deploy does that).',
1933
+ ],
1934
+ tags: ['oracle', 'deploy', 'start'],
1935
+ inputPorts: [
1936
+ {
1937
+ path: 'name',
1938
+ portType: 'string',
1939
+ required: true,
1940
+ description: 'Project name, from the form.',
1941
+ },
1942
+ {
1943
+ path: 'entityDid',
1944
+ portType: 'entityDid',
1945
+ required: true,
1946
+ description: 'Oracle entity DID, from the entity.createOracle output.',
1947
+ },
1948
+ {
1949
+ path: 'roomId',
1950
+ portType: 'roomId',
1951
+ required: true,
1952
+ description:
1953
+ 'User-oracle Matrix room id, from the oracle contract/configure output.',
1954
+ },
1955
+ {
1956
+ path: 'secrets',
1957
+ portType: 'object',
1958
+ description:
1959
+ 'Optional map of secret name to value passed to deploy start.',
1960
+ },
1961
+ ],
1962
+ },
1963
+ 'qi/sandbox.provision': {
1964
+ summary:
1965
+ 'Provisions a sandbox for the oracle entity and returns the sandbox URL and status. Sandbox provisioning side effect.',
1966
+ whenToUse: [
1967
+ 'As the step that spins up a sandbox environment for an oracle entity.',
1968
+ ],
1969
+ tags: ['oracle', 'sandbox', 'provision'],
1970
+ inputPorts: [
1971
+ {
1972
+ path: 'entityDid',
1973
+ portType: 'entityDid',
1974
+ required: true,
1975
+ description:
1976
+ 'Oracle entity DID to provision a sandbox for, from the entity.createOracle output.',
1977
+ },
1978
+ {
1979
+ path: 'matrixRoomId',
1980
+ portType: 'roomId',
1981
+ required: true,
1982
+ description:
1983
+ 'Matrix room id associated with the oracle, from the oracle.contract output.',
1984
+ },
1985
+ ],
1986
+ },
1987
+
1988
+ // ── POD setup ─────────────────────────────────────────────────────────────
1989
+ 'qi/pod.domain-indexer-lookup': {
1990
+ summary:
1991
+ 'AI-assisted intent capture that takes a free-text purpose and queries the Domain Indexer for matching Blueprint candidates, returning a ranked list. Currently a stub that records the purpose and returns an empty candidate array; no real chain/indexer call yet.',
1992
+ whenToUse: [
1993
+ "Step 1 of POD setup — capture the user's purpose and surface candidate blueprints to choose from.",
1994
+ ],
1995
+ tags: ['pod', 'setup'],
1996
+ inputPorts: [
1997
+ {
1998
+ path: 'userMessage',
1999
+ portType: 'string',
2000
+ required: true,
2001
+ description:
2002
+ 'User-provided purpose text used to query the Domain Indexer (run() throws if both this and purposeDescription are empty).',
2003
+ },
2004
+ {
2005
+ path: 'purposeDescription',
2006
+ portType: 'string',
2007
+ description: 'Fallback purpose text used when userMessage is absent.',
2008
+ },
2009
+ ],
2010
+ },
2011
+ 'qi/pod.domain-single-selection': {
2012
+ summary:
2013
+ "Records the user's single Blueprint (protocol entity) selection and carries the blueprint DID forward to all later POD setup steps. Pure selection step — no chain/indexer read; it normalizes the DID and emits a derived entity context entry (class = blueprint DID).",
2014
+ whenToUse: [
2015
+ 'Step 2 of POD setup — lock in the Blueprint DID that the new POD will be created from.',
2016
+ ],
2017
+ whenNotToUse: [
2018
+ 'Selecting a parent organisation/DAO — use qi/pod.entity-single-selection.',
2019
+ 'Selecting POD members — use qi/pod.member-multi-select.',
2020
+ ],
2021
+ tags: ['pod', 'setup'],
2022
+ inputPorts: [
2023
+ {
2024
+ path: 'selectedBlueprintDid',
2025
+ portType: 'did',
2026
+ required: true,
2027
+ description:
2028
+ 'DID of the blueprint to select (run() throws if it cannot be extracted to a valid DID).',
2029
+ },
2030
+ {
2031
+ path: 'selectedBlueprintName',
2032
+ portType: 'string',
2033
+ description: 'Display name of the selected blueprint.',
2034
+ },
2035
+ {
2036
+ path: 'selectedBlueprintDescription',
2037
+ portType: 'string',
2038
+ description: 'Description of the selected blueprint.',
2039
+ },
2040
+ ],
2041
+ requires: [
2042
+ {
2043
+ kind: 'blueprintDid',
2044
+ description:
2045
+ 'A candidate Blueprint DID must be available to select (typically from the prior domain-indexer-lookup step).',
2046
+ },
2047
+ ],
2048
+ },
2049
+ 'qi/pod.entity-single-selection': {
2050
+ summary:
2051
+ 'Optionally records a single parent organisation (DAO/POD) for the new POD, setting parentDID. Pure selection step with no chain read; supports an explicit skip path that leaves the parent DID null.',
2052
+ whenToUse: [
2053
+ 'Step 3A of POD setup — optionally attach the new POD to a parent DAO/POD the user controls.',
2054
+ ],
2055
+ whenNotToUse: [
2056
+ 'Selecting the Blueprint the POD is built from — use qi/pod.domain-single-selection.',
2057
+ ],
2058
+ tags: ['pod', 'setup'],
2059
+ inputPorts: [
2060
+ {
2061
+ path: 'skipped',
2062
+ portType: 'boolean',
2063
+ description:
2064
+ 'Set true to skip selecting a parent entity, leaving parentDID null.',
2065
+ },
2066
+ {
2067
+ path: 'selectedEntityDid',
2068
+ portType: 'did',
2069
+ required: true,
2070
+ description:
2071
+ 'DID of the parent entity; required when not skipping (run() throws if absent and skipped is falsy).',
2072
+ },
2073
+ {
2074
+ path: 'selectedEntityName',
2075
+ portType: 'string',
2076
+ description: 'Display name of the selected parent entity.',
2077
+ },
2078
+ {
2079
+ path: 'selectedEntityType',
2080
+ portType: 'string',
2081
+ description: 'Entity type of the selected parent entity.',
2082
+ },
2083
+ ],
2084
+ },
2085
+ 'qi/pod.governance-config': {
2086
+ summary:
2087
+ 'Validates and records the governance group type and its Cosmos group DecisionPolicy. No chain write — it enforces type-specific rules (multisig requires a positive-integer threshold; weighted types require quorum/threshold in 0–1 and threshold+vetoThreshold <= 1) and carries the config forward to drive the membership step.',
2088
+ whenToUse: [
2089
+ 'Step 4 of POD setup — choose the governance model (categorical, multisig, nftStaking, tokenStaking) and set the decision policy.',
2090
+ ],
2091
+ tags: ['pod', 'governance'],
2092
+ inputPorts: [
2093
+ {
2094
+ path: 'groupName',
2095
+ portType: 'string',
2096
+ required: true,
2097
+ description:
2098
+ 'Human-readable governance group name (run() throws if empty).',
2099
+ },
2100
+ {
2101
+ path: 'groupType',
2102
+ portType: 'string',
2103
+ required: true,
2104
+ description:
2105
+ 'One of categorical | multisig | nftStaking | tokenStaking (run() throws if missing or invalid).',
2106
+ },
2107
+ {
2108
+ path: 'governance',
2109
+ portType: 'object',
2110
+ required: true,
2111
+ description:
2112
+ 'Cosmos group decision policy params; must include votingPeriod, plus threshold for multisig or quorum/threshold for weighted types (run() throws if missing or invalid).',
2113
+ },
2114
+ ],
2115
+ },
2116
+ 'qi/pod.list-domain-flows': {
2117
+ summary:
2118
+ 'Records a multi-select of protocol deeds and their Matrix flow templates to import after the POD is created. Pure selection step; normalizes the chosen templates (each needs protocolDid + sourceRoomId), and an empty selection is valid (POD created without imported flows).',
2119
+ whenToUse: [
2120
+ 'Step 3D of POD setup — choose which protocol flow templates the new POD should import.',
2121
+ ],
2122
+ tags: ['pod', 'setup'],
2123
+ inputPorts: [
2124
+ {
2125
+ path: 'selectedProtocolTemplates',
2126
+ portType: 'array',
2127
+ description:
2128
+ 'Selected protocol templates, each with protocolDid and sourceRoomId; empty selection is valid.',
2129
+ },
2130
+ {
2131
+ path: 'selectedFlowDids',
2132
+ portType: 'array',
2133
+ description:
2134
+ 'Source Matrix room IDs for selected templates; used as a fallback when no templates are selected.',
2135
+ },
2136
+ ],
2137
+ },
2138
+ 'qi/pod.member-multi-select': {
2139
+ summary:
2140
+ 'Validates and records POD membership, with required fields driven by the governance groupType from the previous step: categorical needs members with an Admin role and positive-integer voting power; multisig needs members plus an integer threshold not exceeding member count; nftStaking needs an NFT contract address; tokenStaking needs a token config. No chain write — selection/config only, with duplicate-DID rejection.',
2141
+ whenToUse: [
2142
+ "Step 5 of POD setup — define the POD's members or staking config according to the chosen governance type.",
2143
+ ],
2144
+ whenNotToUse: [
2145
+ 'Selecting the Blueprint or a parent entity — use qi/pod.domain-single-selection or qi/pod.entity-single-selection.',
2146
+ ],
2147
+ tags: ['pod', 'governance'],
2148
+ inputPorts: [
2149
+ {
2150
+ path: 'groupType',
2151
+ portType: 'string',
2152
+ description:
2153
+ 'Governance group type that determines which fields apply; defaults to categorical.',
2154
+ },
2155
+ {
2156
+ path: 'members',
2157
+ portType: 'array',
2158
+ description:
2159
+ 'Array of { did, role, votingPower }; required (non-empty) for categorical and multisig — categorical needs an Admin and positive-integer voting power per member.',
2160
+ },
2161
+ {
2162
+ path: 'multisigThreshold',
2163
+ portType: 'number',
2164
+ description:
2165
+ 'Minimum signers required; must be a positive integer not exceeding member count — multisig only.',
2166
+ },
2167
+ {
2168
+ path: 'nftContractAddress',
2169
+ portType: 'chainAddress',
2170
+ description:
2171
+ 'NFT staking contract address; required for nftStaking groups.',
2172
+ },
2173
+ {
2174
+ path: 'tokenConfig',
2175
+ portType: 'object',
2176
+ description:
2177
+ 'Token config; required for tokenStaking — needs tokenAddress when existing, else tokenName/tokenSymbol/positive tokenSupply.',
2178
+ },
2179
+ ],
2180
+ requires: [
2181
+ {
2182
+ kind: 'governanceGroupType',
2183
+ description:
2184
+ 'The governance groupType from the prior governance-config step, which decides which member fields are required.',
2185
+ },
2186
+ ],
2187
+ },
2188
+
2189
+ // ── Delegated integrations (author-bound, free-form) ──────────────────────
2190
+ 'qi/gmail.email.send': {
2191
+ summary:
2192
+ "Send a free-form email from the TEMPLATE AUTHOR's own connected Gmail account (Composio GMAIL_SEND_EMAIL). Delegated execution: the author connects and binds their Gmail ONCE at design time, and any runner of the flow then sends on the author's behalf via an opaque server-side binding — runners never connect Gmail and never hold the author's credential. Field values (recipient, subject, body) may be literals or reference upstream block outputs (e.g. an email submitted in an upstream form). Side-effecting: delivers a real message. Runs when the runner clicks the block, or automatically when a wired upstream block event fires (no slide-to-sign). Emits an email.sent event.",
2193
+ whenToUse: [
2194
+ "An automation needs to email someone as the template author, from the author's own Gmail inbox.",
2195
+ 'You want free-form subject/body (optionally wired from upstream block outputs), not a pre-registered template.',
2196
+ ],
2197
+ whenNotToUse: [
2198
+ "Do NOT confuse with qi/email.send, which sends a PRE-DEFINED TEMPLATED email from the PLATFORM owner's address; this block sends free-form content from the AUTHOR's own Gmail.",
2199
+ 'For Outlook use qi/outlook.email.send; to post to Slack use qi/slack.message.send.',
2200
+ ],
2201
+ tags: ['integration', 'email'],
2202
+ inputPorts: [
2203
+ {
2204
+ path: 'connection',
2205
+ portType: 'object',
2206
+ required: true,
2207
+ description:
2208
+ "The author's bound Gmail connection (set at design time). Runners never set this — it carries the author's opaque server-side binding, not their credential.",
2209
+ },
2210
+ {
2211
+ path: 'recipient_email',
2212
+ portType: 'emailAddress',
2213
+ required: true,
2214
+ description:
2215
+ "Recipient email address. May be a literal or wired from an upstream block output (e.g. a form's submitted email).",
2216
+ },
2217
+ {
2218
+ path: 'body',
2219
+ portType: 'string',
2220
+ required: true,
2221
+ description: 'Email body. Plain text unless is_html is set.',
2222
+ },
2223
+ {
2224
+ path: 'subject',
2225
+ portType: 'string',
2226
+ description: 'Optional email subject line.',
2227
+ },
2228
+ {
2229
+ path: 'cc',
2230
+ portType: 'emailAddress',
2231
+ description: 'Optional CC recipients; comma-separated email addresses.',
2232
+ },
2233
+ {
2234
+ path: 'bcc',
2235
+ portType: 'emailAddress',
2236
+ description:
2237
+ 'Optional BCC recipients; comma-separated email addresses.',
2238
+ },
2239
+ {
2240
+ path: 'is_html',
2241
+ portType: 'boolean',
2242
+ description: 'When true, render the body as HTML.',
2243
+ },
2244
+ ],
2245
+ requires: [
2246
+ {
2247
+ kind: 'connectedAccount',
2248
+ description:
2249
+ 'The template author must connect and bind their Gmail account at design time; ask the author to connect Gmail. Runners never connect anything.',
2250
+ },
2251
+ ],
2252
+ },
2253
+ 'qi/outlook.email.send': {
2254
+ summary:
2255
+ "Send a free-form email from the TEMPLATE AUTHOR's own connected Outlook account (Composio OUTLOOK_OUTLOOK_SEND_EMAIL). Delegated execution: the author connects and binds their Outlook ONCE at design time, and any runner of the flow then sends on the author's behalf via an opaque server-side binding — runners never connect Outlook and never hold the author's credential. Field values may be literals or reference upstream block outputs. Side-effecting: delivers a real message. Runs when the runner clicks the block, or automatically when a wired upstream block event fires (no slide-to-sign). Emits an email.sent event.",
2256
+ whenToUse: [
2257
+ "An automation needs to email someone as the template author, from the author's own Outlook inbox.",
2258
+ 'You want free-form subject/body (optionally wired from upstream block outputs), not a pre-registered template.',
2259
+ ],
2260
+ whenNotToUse: [
2261
+ "Do NOT confuse with qi/email.send, which sends a PRE-DEFINED TEMPLATED email from the PLATFORM owner's address; this block sends free-form content from the AUTHOR's own Outlook.",
2262
+ 'For Gmail use qi/gmail.email.send; to post to Slack use qi/slack.message.send.',
2263
+ ],
2264
+ tags: ['integration', 'email'],
2265
+ inputPorts: [
2266
+ {
2267
+ path: 'connection',
2268
+ portType: 'object',
2269
+ required: true,
2270
+ description:
2271
+ "The author's bound Outlook connection (set at design time). Runners never set this — it carries the author's opaque server-side binding, not their credential.",
2272
+ },
2273
+ {
2274
+ path: 'to_email',
2275
+ portType: 'emailAddress',
2276
+ required: true,
2277
+ description:
2278
+ "Recipient email address. May be a literal or wired from an upstream block output (e.g. a form's submitted email).",
2279
+ },
2280
+ {
2281
+ path: 'subject',
2282
+ portType: 'string',
2283
+ required: true,
2284
+ description: 'Email subject line.',
2285
+ },
2286
+ {
2287
+ path: 'body',
2288
+ portType: 'string',
2289
+ required: true,
2290
+ description: 'Email body. Plain text unless is_html is set.',
2291
+ },
2292
+ {
2293
+ path: 'to_name',
2294
+ portType: 'string',
2295
+ description: 'Optional display name for the recipient.',
2296
+ },
2297
+ {
2298
+ path: 'cc_emails',
2299
+ portType: 'emailAddress',
2300
+ description: 'Optional CC recipients; comma-separated email addresses.',
2301
+ },
2302
+ {
2303
+ path: 'bcc_emails',
2304
+ portType: 'emailAddress',
2305
+ description:
2306
+ 'Optional BCC recipients; comma-separated email addresses.',
2307
+ },
2308
+ {
2309
+ path: 'is_html',
2310
+ portType: 'boolean',
2311
+ description: 'When true, render the body as HTML.',
2312
+ },
2313
+ ],
2314
+ requires: [
2315
+ {
2316
+ kind: 'connectedAccount',
2317
+ description:
2318
+ 'The template author must connect and bind their Outlook account at design time; ask the author to connect Outlook. Runners never connect anything.',
2319
+ },
2320
+ ],
2321
+ },
2322
+ 'qi/slack.message.send': {
2323
+ summary:
2324
+ "Post a free-form message to a Slack channel from the TEMPLATE AUTHOR's own connected Slack workspace (Composio SLACK_CHAT_POST_MESSAGE). Delegated execution: the author connects and binds their Slack ONCE at design time, and any runner of the flow then posts on the author's behalf via an opaque server-side binding — runners never connect Slack and never hold the author's credential. Field values may be literals or reference upstream block outputs. Side-effecting: posts a real message. Runs when the runner clicks the block, or automatically when a wired upstream block event fires (no slide-to-sign). Emits a message.sent event.",
2325
+ whenToUse: [
2326
+ "An automation needs to post a Slack message as the template author, into the author's own workspace.",
2327
+ 'You want free-form message text (optionally wired from upstream block outputs).',
2328
+ ],
2329
+ whenNotToUse: [
2330
+ "Do NOT confuse with qi/email.send, which sends a PRE-DEFINED TEMPLATED email from the PLATFORM owner's address; this block posts free-form content to the AUTHOR's own Slack.",
2331
+ 'To email rather than post to Slack, use qi/gmail.email.send or qi/outlook.email.send.',
2332
+ ],
2333
+ tags: ['integration', 'messaging'],
2334
+ inputPorts: [
2335
+ {
2336
+ path: 'connection',
2337
+ portType: 'object',
2338
+ required: true,
2339
+ description:
2340
+ "The author's bound Slack connection (set at design time). Runners never set this — it carries the author's opaque server-side binding, not their credential.",
2341
+ },
2342
+ {
2343
+ path: 'channel',
2344
+ portType: 'string',
2345
+ required: true,
2346
+ description: 'Target channel ID or name, e.g. #general or C0123456.',
2347
+ },
2348
+ {
2349
+ path: 'markdown_text',
2350
+ portType: 'string',
2351
+ description:
2352
+ 'Optional message text in Slack markdown. May be a literal or wired from an upstream block output.',
2353
+ },
2354
+ {
2355
+ path: 'thread_ts',
2356
+ portType: 'string',
2357
+ description:
2358
+ 'Optional parent message timestamp to reply within a thread.',
2359
+ },
2360
+ ],
2361
+ requires: [
2362
+ {
2363
+ kind: 'connectedAccount',
2364
+ description:
2365
+ 'The template author must connect and bind their Slack account at design time; ask the author to connect Slack. Runners never connect anything.',
2366
+ },
2367
+ ],
2368
+ },
2369
+ 'qi/googlecalendar.event.create': {
2370
+ summary:
2371
+ "Create a free-form event on the TEMPLATE AUTHOR's own connected Google Calendar (Composio GOOGLECALENDAR_CREATE_EVENT). Delegated execution: the author connects and binds their calendar ONCE at design time, and any runner of the flow then creates events on the author's behalf via an opaque server-side binding — runners never connect a calendar and never hold the author's credential. Field values may be literals or reference upstream block outputs (e.g. a start time resolved upstream). Side-effecting: writes a real event. Runs when the runner clicks the block, or automatically when a wired upstream block event fires (no slide-to-sign). Emits an event.created event.",
2372
+ whenToUse: [
2373
+ "An automation needs to create a calendar event as the template author, on the author's own Google Calendar.",
2374
+ 'You want free-form event details (optionally wired from upstream block outputs).',
2375
+ ],
2376
+ whenNotToUse: [
2377
+ "Do NOT confuse with the older self-connected qi/calendar.event.create, where each runner connects their OWN calendar; this delegated block writes to the AUTHOR's own calendar on every runner's behalf.",
2378
+ "Do NOT confuse with qi/email.send (a PLATFORM-owner templated email); this block creates a calendar event from the AUTHOR's own account.",
2379
+ ],
2380
+ tags: ['integration', 'calendar', 'google'],
2381
+ inputPorts: [
2382
+ {
2383
+ path: 'connection',
2384
+ portType: 'object',
2385
+ required: true,
2386
+ description:
2387
+ "The author's bound Google Calendar connection (set at design time). Runners never set this — it carries the author's opaque server-side binding, not their credential.",
2388
+ },
2389
+ {
2390
+ path: 'start_datetime',
2391
+ portType: 'string',
2392
+ required: true,
2393
+ description:
2394
+ 'Event start time in ISO 8601, e.g. 2026-05-12T09:00:00. May be a literal or wired from an upstream block output.',
2395
+ },
2396
+ {
2397
+ path: 'summary',
2398
+ portType: 'string',
2399
+ description: 'Optional event title.',
2400
+ },
2401
+ {
2402
+ path: 'description',
2403
+ portType: 'string',
2404
+ description: 'Optional event description.',
2405
+ },
2406
+ {
2407
+ path: 'location',
2408
+ portType: 'string',
2409
+ description: 'Optional event location.',
2410
+ },
2411
+ {
2412
+ path: 'timezone',
2413
+ portType: 'string',
2414
+ description: 'Optional IANA timezone, e.g. Europe/London.',
2415
+ },
2416
+ {
2417
+ path: 'attendees',
2418
+ portType: 'string',
2419
+ description: 'Optional attendees as comma-separated email addresses.',
2420
+ },
2421
+ {
2422
+ path: 'calendar_id',
2423
+ portType: 'string',
2424
+ description:
2425
+ "Optional calendar to write to; use 'primary' for the author's main calendar.",
2426
+ },
2427
+ {
2428
+ path: 'event_duration_minutes',
2429
+ portType: 'number',
2430
+ description:
2431
+ 'Optional event duration in minutes; defaults to the calendar default if blank.',
2432
+ },
2433
+ ],
2434
+ requires: [
2435
+ {
2436
+ kind: 'connectedAccount',
2437
+ description:
2438
+ 'The template author must connect and bind their Google Calendar at design time; ask the author to connect Calendar. Runners never connect anything.',
2439
+ },
2440
+ ],
2441
+ },
2442
+
2443
+ // ── Calendar & Xero integrations ──────────────────────────────────────────
2444
+ 'qi/calendar.event.create': {
2445
+ summary:
2446
+ 'Create a new event on a connected Google Calendar via the GOOGLECALENDAR_CREATE_EVENT integration tool (Composio).',
2447
+ whenToUse: [
2448
+ 'An automation needs to schedule/book a new calendar event for someone.',
2449
+ 'A flow has resolved a start time (and optionally attendees) and should write a fresh event to a Google Calendar.',
2450
+ ],
2451
+ whenNotToUse: [
2452
+ 'To modify an event that already exists — use qi/calendar.event.update (which requires an event_id) instead of creating a duplicate.',
2453
+ ],
2454
+ tags: ['integration', 'calendar', 'google'],
2455
+ inputPorts: [
2456
+ {
2457
+ path: 'connection',
2458
+ portType: 'object',
2459
+ required: true,
2460
+ description:
2461
+ 'Pinned Google Calendar connection object carrying connectedAccountId and entityDid. Comes from the connected integration account, not an upstream block.',
2462
+ },
2463
+ {
2464
+ path: 'start_datetime',
2465
+ portType: 'string',
2466
+ required: true,
2467
+ description: 'Event start time in ISO 8601. run() throws if blank.',
2468
+ },
2469
+ {
2470
+ path: 'end_datetime',
2471
+ portType: 'string',
2472
+ description: 'Optional event end time in ISO 8601.',
2473
+ },
2474
+ {
2475
+ path: 'calendar_id',
2476
+ portType: 'string',
2477
+ description: 'Calendar to write to; defaults to "primary".',
2478
+ },
2479
+ {
2480
+ path: 'summary',
2481
+ portType: 'string',
2482
+ description: 'Optional event title.',
2483
+ },
2484
+ {
2485
+ path: 'description',
2486
+ portType: 'string',
2487
+ description: 'Optional event description.',
2488
+ },
2489
+ {
2490
+ path: 'location',
2491
+ portType: 'string',
2492
+ description: 'Optional event location.',
2493
+ },
2494
+ {
2495
+ path: 'timezone',
2496
+ portType: 'string',
2497
+ description: 'Optional timezone for the event.',
2498
+ },
2499
+ {
2500
+ path: 'send_updates',
2501
+ portType: 'string',
2502
+ description:
2503
+ 'Optional attendee-notification mode passed through to Calendar.',
2504
+ },
2505
+ {
2506
+ path: 'attendees',
2507
+ portType: 'string',
2508
+ description:
2509
+ 'Optional attendees as a JSON array, or newline-/comma-separated email list.',
2510
+ },
2511
+ ],
2512
+ requires: [
2513
+ {
2514
+ kind: 'connectedAccount',
2515
+ description:
2516
+ 'A connected Google Calendar account (connectedAccountId) pinned on the block — ask the user to connect Calendar before running.',
2517
+ },
2518
+ ],
2519
+ },
2520
+ 'qi/calendar.event.list': {
2521
+ summary:
2522
+ 'List/search events from a connected Google Calendar via the GOOGLECALENDAR_EVENTS_LIST integration tool (Composio). Read-only, no side effect.',
2523
+ whenToUse: [
2524
+ 'A flow needs to look up existing events (optionally filtered by time window or free-text query) before acting.',
2525
+ 'You need an event id from the calendar to feed a downstream qi/calendar.event.update block.',
2526
+ ],
2527
+ tags: ['integration', 'calendar', 'google', 'read'],
2528
+ inputPorts: [
2529
+ {
2530
+ path: 'connection',
2531
+ portType: 'object',
2532
+ required: true,
2533
+ description:
2534
+ 'Pinned Google Calendar connection object carrying connectedAccountId and entityDid.',
2535
+ },
2536
+ {
2537
+ path: 'calendarId',
2538
+ portType: 'string',
2539
+ description:
2540
+ 'Calendar to list from; defaults to "primary". Note camelCase here (list tool) vs snake_case calendar_id on create/update.',
2541
+ },
2542
+ {
2543
+ path: 'q',
2544
+ portType: 'string',
2545
+ description: 'Optional free-text search query for events.',
2546
+ },
2547
+ {
2548
+ path: 'timeMin',
2549
+ portType: 'string',
2550
+ description: 'Optional lower bound for event start time (ISO 8601).',
2551
+ },
2552
+ {
2553
+ path: 'timeMax',
2554
+ portType: 'string',
2555
+ description: 'Optional upper bound for event start time (ISO 8601).',
2556
+ },
2557
+ {
2558
+ path: 'timeZone',
2559
+ portType: 'string',
2560
+ description: 'Optional timezone for the returned times.',
2561
+ },
2562
+ {
2563
+ path: 'orderBy',
2564
+ portType: 'string',
2565
+ description: 'Optional ordering of results (e.g. startTime).',
2566
+ },
2567
+ {
2568
+ path: 'singleEvents',
2569
+ portType: 'boolean',
2570
+ description:
2571
+ 'Optional flag to expand recurring events into single instances.',
2572
+ },
2573
+ {
2574
+ path: 'maxResults',
2575
+ portType: 'string',
2576
+ description:
2577
+ 'Optional max number of events to return (parsed as a positive integer).',
2578
+ },
2579
+ ],
2580
+ requires: [
2581
+ {
2582
+ kind: 'connectedAccount',
2583
+ description:
2584
+ 'A connected Google Calendar account (connectedAccountId) pinned on the block — ask the user to connect Calendar before running.',
2585
+ },
2586
+ ],
2587
+ },
2588
+ 'qi/calendar.event.update': {
2589
+ summary:
2590
+ 'Update an existing Google Calendar event via the GOOGLECALENDAR_UPDATE_EVENT integration tool (Composio). This is a full PUT — omitted fields are cleared.',
2591
+ whenToUse: [
2592
+ 'A flow needs to reschedule or edit an existing calendar event identified by its event_id.',
2593
+ 'You have an event id (e.g. from qi/calendar.event.list) and want to change its time, title, attendees, etc.',
2594
+ ],
2595
+ whenNotToUse: [
2596
+ 'To create a brand-new event — use qi/calendar.event.create. Also note this is a full replace, so unspecified fields are cleared, not preserved.',
2597
+ ],
2598
+ tags: ['integration', 'calendar', 'google'],
2599
+ inputPorts: [
2600
+ {
2601
+ path: 'connection',
2602
+ portType: 'object',
2603
+ required: true,
2604
+ description:
2605
+ 'Pinned Google Calendar connection object carrying connectedAccountId and entityDid.',
2606
+ },
2607
+ {
2608
+ path: 'event_id',
2609
+ portType: 'eventId',
2610
+ required: true,
2611
+ description:
2612
+ 'Identifier of the existing event to update. run() throws if blank; wire from qi/calendar.event.list output or an upstream create.',
2613
+ },
2614
+ {
2615
+ path: 'start_datetime',
2616
+ portType: 'string',
2617
+ required: true,
2618
+ description: 'Event start time in ISO 8601. run() throws if blank.',
2619
+ },
2620
+ {
2621
+ path: 'end_datetime',
2622
+ portType: 'string',
2623
+ description: 'Optional event end time in ISO 8601.',
2624
+ },
2625
+ {
2626
+ path: 'calendar_id',
2627
+ portType: 'string',
2628
+ description: 'Calendar containing the event; defaults to "primary".',
2629
+ },
2630
+ {
2631
+ path: 'summary',
2632
+ portType: 'string',
2633
+ description: 'Optional event title (cleared if omitted — full PUT).',
2634
+ },
2635
+ {
2636
+ path: 'description',
2637
+ portType: 'string',
2638
+ description: 'Optional event description (cleared if omitted).',
2639
+ },
2640
+ {
2641
+ path: 'location',
2642
+ portType: 'string',
2643
+ description: 'Optional event location (cleared if omitted).',
2644
+ },
2645
+ {
2646
+ path: 'timezone',
2647
+ portType: 'string',
2648
+ description: 'Optional timezone for the event.',
2649
+ },
2650
+ {
2651
+ path: 'send_updates',
2652
+ portType: 'string',
2653
+ description:
2654
+ 'Optional attendee-notification mode passed through to Calendar.',
2655
+ },
2656
+ {
2657
+ path: 'attendees',
2658
+ portType: 'string',
2659
+ description:
2660
+ 'Optional attendees as a JSON array, or newline-/comma-separated email list.',
2661
+ },
2662
+ ],
2663
+ requires: [
2664
+ {
2665
+ kind: 'connectedAccount',
2666
+ description:
2667
+ 'A connected Google Calendar account (connectedAccountId) pinned on the block — ask the user to connect Calendar before running.',
2668
+ },
2669
+ {
2670
+ kind: 'eventId',
2671
+ description:
2672
+ 'The id of an existing calendar event to update — ask the user or source it from a qi/calendar.event.list / create step.',
2673
+ },
2674
+ ],
2675
+ },
2676
+ 'qi/xero.contact.create': {
2677
+ summary:
2678
+ 'Create a contact (customer/supplier) in a connected Xero organisation via the XERO_CREATE_CONTACT integration tool (Composio).',
2679
+ whenToUse: [
2680
+ 'A flow needs a Xero contact to exist before issuing an invoice to them.',
2681
+ 'Onboarding a new customer or supplier into the bookkeeping system.',
2682
+ ],
2683
+ tags: ['integration', 'xero', 'accounting'],
2684
+ inputPorts: [
2685
+ {
2686
+ path: 'connection',
2687
+ portType: 'object',
2688
+ required: true,
2689
+ description:
2690
+ 'Pinned Xero connection object carrying connectedAccountId and entityDid.',
2691
+ },
2692
+ {
2693
+ path: 'Name',
2694
+ portType: 'string',
2695
+ required: true,
2696
+ description: 'Contact display name. run() throws if blank.',
2697
+ },
2698
+ {
2699
+ path: 'tenant_id',
2700
+ portType: 'string',
2701
+ description: 'Xero org identifier; empty uses the connection default.',
2702
+ },
2703
+ {
2704
+ path: 'FirstName',
2705
+ portType: 'string',
2706
+ description: 'Contact first name.',
2707
+ },
2708
+ {
2709
+ path: 'LastName',
2710
+ portType: 'string',
2711
+ description: 'Contact last name.',
2712
+ },
2713
+ {
2714
+ path: 'EmailAddress',
2715
+ portType: 'string',
2716
+ description: 'Contact email address.',
2717
+ },
2718
+ {
2719
+ path: 'phone_number',
2720
+ portType: 'string',
2721
+ description: 'Contact phone number.',
2722
+ },
2723
+ {
2724
+ path: 'mobile_number',
2725
+ portType: 'string',
2726
+ description: 'Contact mobile number.',
2727
+ },
2728
+ {
2729
+ path: 'Website',
2730
+ portType: 'string',
2731
+ description: 'Contact website URL.',
2732
+ },
2733
+ {
2734
+ path: 'TaxNumber',
2735
+ portType: 'string',
2736
+ description: 'Contact tax number.',
2737
+ },
2738
+ {
2739
+ path: 'AccountNumber',
2740
+ portType: 'string',
2741
+ description: 'Contact account number.',
2742
+ },
2743
+ {
2744
+ path: 'DefaultCurrency',
2745
+ portType: 'string',
2746
+ description: 'Default currency code for the contact.',
2747
+ },
2748
+ {
2749
+ path: 'BankAccountDetails',
2750
+ portType: 'string',
2751
+ description: 'Contact bank account details.',
2752
+ },
2753
+ {
2754
+ path: 'IsCustomer',
2755
+ portType: 'boolean',
2756
+ description: 'Whether the contact is a customer.',
2757
+ },
2758
+ {
2759
+ path: 'IsSupplier',
2760
+ portType: 'boolean',
2761
+ description: 'Whether the contact is a supplier.',
2762
+ },
2763
+ ],
2764
+ requires: [
2765
+ {
2766
+ kind: 'connectedAccount',
2767
+ description:
2768
+ 'A connected Xero account (connectedAccountId) pinned on the block — ask the user to connect Xero before running.',
2769
+ },
2770
+ ],
2771
+ },
2772
+ 'qi/xero.invoice.create': {
2773
+ summary:
2774
+ 'Create an invoice (sales ACCREC or bill ACCPAY) with line items in a connected Xero organisation via the XERO_CREATE_INVOICE integration tool (Composio).',
2775
+ whenToUse: [
2776
+ 'A flow needs to bill a customer or record a supplier bill in Xero.',
2777
+ 'You have finalised line items and a contact and want to issue an invoice.',
2778
+ ],
2779
+ whenNotToUse: [
2780
+ 'To record money received against an invoice — use qi/xero.payment.create. To look up existing invoices — use qi/xero.invoice.list.',
2781
+ ],
2782
+ tags: ['integration', 'xero', 'accounting'],
2783
+ inputPorts: [
2784
+ {
2785
+ path: 'connection',
2786
+ portType: 'object',
2787
+ required: true,
2788
+ description:
2789
+ 'Pinned Xero connection object carrying connectedAccountId and entityDid.',
2790
+ },
2791
+ {
2792
+ path: 'LineItems',
2793
+ portType: 'array',
2794
+ required: true,
2795
+ description:
2796
+ 'Finalised array of invoice line-item objects (resolved from manual rows or an iterative upstream-array binding before run()). run() throws if empty.',
2797
+ },
2798
+ {
2799
+ path: 'tenant_id',
2800
+ portType: 'string',
2801
+ description: 'Xero org identifier; empty uses the connection default.',
2802
+ },
2803
+ {
2804
+ path: 'Type',
2805
+ portType: 'string',
2806
+ description:
2807
+ "Invoice type 'ACCREC' (sale) or 'ACCPAY' (bill); defaults to ACCREC. run() throws on any other value.",
2808
+ },
2809
+ {
2810
+ path: 'Status',
2811
+ portType: 'string',
2812
+ description: 'Invoice status (e.g. DRAFT, AUTHORISED).',
2813
+ },
2814
+ { path: 'Date', portType: 'string', description: 'Invoice date.' },
2815
+ { path: 'DueDate', portType: 'string', description: 'Invoice due date.' },
2816
+ {
2817
+ path: 'ContactID',
2818
+ portType: 'string',
2819
+ description:
2820
+ 'Xero contact UUID; preferred over ContactName. Wire from qi/xero.contact.create output.',
2821
+ },
2822
+ {
2823
+ path: 'ContactName',
2824
+ portType: 'string',
2825
+ description:
2826
+ 'Contact name fallback used only when ContactID is absent.',
2827
+ },
2828
+ {
2829
+ path: 'Reference',
2830
+ portType: 'string',
2831
+ description: 'Invoice reference text.',
2832
+ },
2833
+ {
2834
+ path: 'InvoiceNumber',
2835
+ portType: 'string',
2836
+ description: 'Invoice number.',
2837
+ },
2838
+ {
2839
+ path: 'CurrencyCode',
2840
+ portType: 'string',
2841
+ description: 'Invoice currency code.',
2842
+ },
2843
+ ],
2844
+ requires: [
2845
+ {
2846
+ kind: 'connectedAccount',
2847
+ description:
2848
+ 'A connected Xero account (connectedAccountId) pinned on the block — ask the user to connect Xero before running.',
2849
+ },
2850
+ ],
2851
+ },
2852
+ 'qi/xero.invoice.list': {
2853
+ summary:
2854
+ 'List/search invoices from a connected Xero organisation via the XERO_LIST_INVOICES integration tool (Composio). Read-only, no side effect.',
2855
+ whenToUse: [
2856
+ 'A flow needs to find existing invoices (filtered by status, contact, ids, or a where-clause) before acting.',
2857
+ 'You need an InvoiceID to feed a downstream qi/xero.payment.create block.',
2858
+ ],
2859
+ whenNotToUse: ['To create a new invoice — use qi/xero.invoice.create.'],
2860
+ tags: ['integration', 'xero', 'accounting', 'read'],
2861
+ inputPorts: [
2862
+ {
2863
+ path: 'connection',
2864
+ portType: 'object',
2865
+ required: true,
2866
+ description:
2867
+ 'Pinned Xero connection object carrying connectedAccountId and entityDid.',
2868
+ },
2869
+ {
2870
+ path: 'tenantId',
2871
+ portType: 'string',
2872
+ description:
2873
+ 'Xero org identifier (camelCase tenantId for the list tool, vs snake_case tenant_id on create).',
2874
+ },
2875
+ {
2876
+ path: 'page',
2877
+ portType: 'string',
2878
+ description: 'Page number; parsed to an integer >= 1.',
2879
+ },
2880
+ {
2881
+ path: 'order',
2882
+ portType: 'string',
2883
+ description: 'Sort order expression.',
2884
+ },
2885
+ {
2886
+ path: 'Statuses',
2887
+ portType: 'string',
2888
+ description: 'Filter by invoice statuses.',
2889
+ },
2890
+ {
2891
+ path: 'ContactIDs',
2892
+ portType: 'string',
2893
+ description: 'Filter by contact IDs.',
2894
+ },
2895
+ {
2896
+ path: 'InvoiceIDs',
2897
+ portType: 'string',
2898
+ description: 'Filter by invoice IDs.',
2899
+ },
2900
+ {
2901
+ path: 'where',
2902
+ portType: 'string',
2903
+ description: 'Xero where-clause filter expression.',
2904
+ },
2905
+ {
2906
+ path: 'createdByMyApp',
2907
+ portType: 'boolean',
2908
+ description: 'Limit to invoices created by this app.',
2909
+ },
2910
+ {
2911
+ path: 'includeArchived',
2912
+ portType: 'boolean',
2913
+ description: 'Include archived invoices.',
2914
+ },
2915
+ ],
2916
+ requires: [
2917
+ {
2918
+ kind: 'connectedAccount',
2919
+ description:
2920
+ 'A connected Xero account (connectedAccountId) pinned on the block — ask the user to connect Xero before running.',
2921
+ },
2922
+ ],
2923
+ },
2924
+ 'qi/xero.payment.create': {
2925
+ summary:
2926
+ 'Record a payment against an existing Xero invoice via the XERO_CREATE_PAYMENT integration tool (Composio) — the settlement step that marks an invoice/bill paid in the books.',
2927
+ whenToUse: [
2928
+ 'Settle/mark an existing (AUTHORISED) Xero invoice as paid after a payout.',
2929
+ 'Reconcile an on-chain transaction against an invoice by passing the tx hash as Reference.',
2930
+ ],
2931
+ whenNotToUse: [
2932
+ 'To create the invoice itself — use qi/xero.invoice.create first; payment.create requires an existing InvoiceID. To find invoices — use qi/xero.invoice.list.',
2933
+ ],
2934
+ tags: ['integration', 'xero', 'accounting', 'payment'],
2935
+ inputPorts: [
2936
+ {
2937
+ path: 'connection',
2938
+ portType: 'object',
2939
+ required: true,
2940
+ description:
2941
+ 'Pinned Xero connection object carrying connectedAccountId and entityDid.',
2942
+ },
2943
+ {
2944
+ path: 'InvoiceID',
2945
+ portType: 'invoiceId',
2946
+ required: true,
2947
+ description:
2948
+ 'UUID of the invoice being paid. run() throws if blank; usually wired from qi/xero.invoice.create or qi/xero.invoice.list output.',
2949
+ },
2950
+ {
2951
+ path: 'AccountID',
2952
+ portType: 'string',
2953
+ required: true,
2954
+ description:
2955
+ 'UUID of the Xero bank/treasury account the payment debits. run() throws if blank; set in template/org config.',
2956
+ },
2957
+ {
2958
+ path: 'Amount',
2959
+ portType: 'number',
2960
+ required: true,
2961
+ description:
2962
+ 'Payment amount; must be a positive number. run() throws if not finite or <= 0.',
2963
+ },
2964
+ {
2965
+ path: 'tenant_id',
2966
+ portType: 'string',
2967
+ description: 'Xero org identifier; empty uses the connection default.',
2968
+ },
2969
+ {
2970
+ path: 'Date',
2971
+ portType: 'string',
2972
+ description: 'Payment date (YYYY-MM-DD); defaults to today if blank.',
2973
+ },
2974
+ {
2975
+ path: 'Reference',
2976
+ portType: 'string',
2977
+ description:
2978
+ 'Free-text reference, typically the on-chain tx hash for crypto payouts.',
2979
+ },
2980
+ {
2981
+ path: 'CurrencyRate',
2982
+ portType: 'number',
2983
+ description: 'Optional currency rate for multicurrency payments.',
2984
+ },
2985
+ ],
2986
+ requires: [
2987
+ {
2988
+ kind: 'connectedAccount',
2989
+ description:
2990
+ 'A connected Xero account (connectedAccountId) pinned on the block — ask the user to connect Xero before running.',
2991
+ },
2992
+ {
2993
+ kind: 'invoiceId',
2994
+ description:
2995
+ 'The UUID of an existing AUTHORISED invoice to pay — source from qi/xero.invoice.create / list, or ask the user.',
2996
+ },
2997
+ {
2998
+ kind: 'xeroAccountId',
2999
+ description:
3000
+ 'The UUID of the Xero bank/treasury account to debit (AccountID) — org-level config; ask the user.',
3001
+ },
3002
+ ],
3003
+ },
3004
+
3005
+ // ── Carbon, entity transfer & payment ─────────────────────────────────────
3006
+ 'qi/carbon.loadBatches': {
3007
+ summary:
3008
+ "Read-only reconciliation of a wallet's owner-side and entity-admin-side CARBON batches into a unified harvestable/retireable view. No signing, no on-chain change; the output feeds downstream harvest/retire blocks.",
3009
+ whenToUse: [
3010
+ 'An automation needs to discover which CARBON batches a wallet can harvest or retire.',
3011
+ 'You need the harvestable/retireable batch lists to wire into a carbon.harvest or carbon.retire block.',
3012
+ ],
3013
+ whenNotToUse: [
3014
+ 'Do NOT use to harvest or retire — this is read-only. Use carbon.harvest to claim batches or carbon.retire to burn credits.',
3015
+ ],
3016
+ tags: ['carbon'],
3017
+ inputPorts: [
3018
+ {
3019
+ path: 'ownerAddress',
3020
+ portType: 'chainAddress',
3021
+ required: true,
3022
+ description:
3023
+ 'Wallet address whose owner-side and entity-admin-side CARBON batches are reconciled. Throws if missing.',
3024
+ },
3025
+ ],
3026
+ requires: [
3027
+ {
3028
+ kind: 'ownerAddress',
3029
+ description:
3030
+ 'The wallet address to reconcile — ask the user; never invent.',
3031
+ },
3032
+ ],
3033
+ },
3034
+ 'qi/carbon.harvest': {
3035
+ summary:
3036
+ "User-signed on-chain harvest: claims batches held on entity admin accounts the user owns into the user's wallet by building per-entity authz-grant + exec-transfer pairs. Side-effecting; success is proven by the returned transactionHash.",
3037
+ whenToUse: [
3038
+ "An automation needs to pull claimable CARBON batches from entity admin accounts into the owner's wallet.",
3039
+ 'You have harvestable batches (e.g. from carbon.loadBatches output) the user wants to claim.',
3040
+ ],
3041
+ whenNotToUse: [
3042
+ 'Do NOT use to discover batches — carbon.loadBatches is the read-only lister; harvest writes on-chain.',
3043
+ 'Do NOT use to retire/burn credits — use carbon.retire.',
3044
+ ],
3045
+ tags: ['carbon'],
3046
+ inputPorts: [
3047
+ {
3048
+ path: 'ownerAddress',
3049
+ portType: 'chainAddress',
3050
+ required: true,
3051
+ description:
3052
+ 'Wallet address owning the entity admin accounts to harvest from. Throws if missing.',
3053
+ },
3054
+ {
3055
+ path: 'tokens',
3056
+ portType: 'array',
3057
+ required: true,
3058
+ description:
3059
+ 'Batches to harvest (wire from carbon.loadBatches output.harvestableBatches); each item requires id, entityDid, adminAddress, and claimable > 0. Throws if empty or any item is missing a field.',
3060
+ },
3061
+ ],
3062
+ requires: [
3063
+ {
3064
+ kind: 'ownerAddress',
3065
+ description:
3066
+ 'The owner wallet address to harvest into — ask the user; never invent.',
3067
+ },
3068
+ ],
3069
+ },
3070
+ 'qi/carbon.retire': {
3071
+ summary:
3072
+ 'User-signed, IRREVERSIBLE retirement: permanently burns/offsets CARBON credits the user holds in their wallet via a single MsgRetireToken. Cannot be undone; success is proven by the returned transactionHash.',
3073
+ whenToUse: [
3074
+ 'An automation needs to permanently retire (offset/burn) credits the user holds in their wallet.',
3075
+ ],
3076
+ whenNotToUse: [
3077
+ 'IRREVERSIBLE — retiring burns credits permanently and cannot be undone; confirm intent before running.',
3078
+ 'Do NOT use to discover batches — carbon.loadBatches is the read-only lister.',
3079
+ 'Do NOT use to claim batches from entity admin accounts — use carbon.harvest.',
3080
+ ],
3081
+ tags: ['carbon'],
3082
+ inputPorts: [
3083
+ {
3084
+ path: 'owner',
3085
+ portType: 'chainAddress',
3086
+ required: true,
3087
+ description:
3088
+ 'Wallet address holding the credits to retire. Throws if missing.',
3089
+ },
3090
+ {
3091
+ path: 'tokens',
3092
+ portType: 'array',
3093
+ required: true,
3094
+ description:
3095
+ 'Batches to retire (wire from carbon.loadBatches output.retireableBatches); each item requires id and amount > 0. Throws if empty or any item lacks an amount.',
3096
+ },
3097
+ {
3098
+ path: 'reason',
3099
+ portType: 'string',
3100
+ description: 'Retirement reason; defaults to "offset" when omitted.',
3101
+ },
3102
+ {
3103
+ path: 'jurisdiction',
3104
+ portType: 'string',
3105
+ description:
3106
+ 'Retirement jurisdiction; defaults to "Global" when omitted.',
3107
+ },
3108
+ ],
3109
+ requires: [
3110
+ {
3111
+ kind: 'owner',
3112
+ description:
3113
+ 'The owner wallet address holding the credits to retire — ask the user; never invent.',
3114
+ },
3115
+ ],
3116
+ },
3117
+ 'qi/entity.transfer': {
3118
+ summary:
3119
+ 'User-signed, IRREVERSIBLE transfer of an entity (domain) ownership to a new owner DID via a single MsgTransferEntity; the host resolves group recipients to their DAO controller and ensures the recipient has an IID document. Cannot be undone; success is proven by the returned transactionHash.',
3120
+ whenToUse: [
3121
+ 'An automation needs to hand over ownership of a domain/entity to another user or DAO controller.',
3122
+ ],
3123
+ whenNotToUse: [
3124
+ 'IRREVERSIBLE — transferring permanently reassigns domain ownership and cannot be undone; confirm both the entity and the recipient before running.',
3125
+ ],
3126
+ tags: ['entity', 'transfer'],
3127
+ inputPorts: [
3128
+ {
3129
+ path: 'entityDid',
3130
+ portType: 'entityDid',
3131
+ required: true,
3132
+ description:
3133
+ 'Entity DID being transferred (did:ixo:entity:…). Throws if missing.',
3134
+ },
3135
+ {
3136
+ path: 'recipientDid',
3137
+ portType: 'did',
3138
+ required: true,
3139
+ description:
3140
+ 'New owner DID (a user DID or a did:ixo:wasm: controller). Throws if missing.',
3141
+ },
3142
+ {
3143
+ path: 'ownerDid',
3144
+ portType: 'did',
3145
+ description:
3146
+ 'Current owner DID; host defaults to the connected user when omitted.',
3147
+ },
3148
+ {
3149
+ path: 'ownerAddress',
3150
+ portType: 'chainAddress',
3151
+ description:
3152
+ 'Current owner address; host defaults to the connected user when omitted.',
3153
+ },
3154
+ ],
3155
+ requires: [
3156
+ {
3157
+ kind: 'entityDid',
3158
+ description:
3159
+ 'The entity/domain DID to transfer — ask the user; never invent.',
3160
+ },
3161
+ {
3162
+ kind: 'recipientDid',
3163
+ description:
3164
+ 'The new owner DID to transfer ownership to — ask the user; never invent.',
3165
+ },
3166
+ ],
3167
+ },
3168
+ 'qi/payment.execute': {
3169
+ summary:
3170
+ 'Drives the payment worklist by sending a structured prompt to the companion oracle, which proposes, executes, or checks payouts for selected rows via the worker batch routes and writes results back to the block. The "execute" verb triggers real payouts (side-effecting); "propose" and "check" do not move funds.',
3171
+ whenToUse: [
3172
+ 'An automation needs to propose, execute, or check status of payout rows in a payment block via the companion oracle.',
3173
+ ],
3174
+ whenNotToUse: [
3175
+ 'The "execute" verb disburses real funds — confirm the proposed rows before executing.',
3176
+ ],
3177
+ tags: ['payment'],
3178
+ inputPorts: [
3179
+ {
3180
+ path: 'rowIds',
3181
+ portType: 'array',
3182
+ required: true,
3183
+ description:
3184
+ 'IDs of the rows in the payment block to operate on (at least one). Throws if empty.',
3185
+ },
3186
+ {
3187
+ path: 'verb',
3188
+ portType: 'string',
3189
+ required: true,
3190
+ description:
3191
+ 'Which worker route to hit: "propose", "execute", or "check". Throws if missing or not one of these.',
3192
+ },
3193
+ {
3194
+ path: 'paymentBlockId',
3195
+ portType: 'string',
3196
+ required: true,
3197
+ description:
3198
+ 'The payment block ID so the oracle can edit_block results back into the right place. Throws if missing.',
3199
+ },
3200
+ {
3201
+ path: 'delegationCid',
3202
+ portType: 'string',
3203
+ description: "Optional user's UCAN delegation CID to the oracle.",
3204
+ },
3205
+ {
3206
+ path: 'skill',
3207
+ portType: 'object',
3208
+ description: 'Optional skill context with cid and name.',
3209
+ },
3210
+ ],
3211
+ requires: [
3212
+ {
3213
+ kind: 'paymentBlock',
3214
+ description:
3215
+ 'A payment block holding the payout rows (worker URL, sender, defaults live in its props) — ask the user; never invent.',
3216
+ },
3217
+ ],
3218
+ },
3219
+
3220
+ // ── Governance, protocol, domain & human-interaction ──────────────────────
3221
+ 'qi/proposal.create': {
3222
+ summary:
3223
+ 'Creates a new DAO governance proposal on-chain in the named DAO (resolves pre-proposal/group/proposal module addresses from the DAO core address, then submits the proposal with optional executable actions). On-chain side effect; requires confirmation.',
3224
+ whenToUse: [
3225
+ 'A flow needs to submit a new governance proposal to a DAO for members to vote on.',
3226
+ 'You want to put a set of executable on-chain actions up for DAO approval.',
3227
+ ],
3228
+ whenNotToUse: [
3229
+ 'Use qi/proposal.vote to cast a vote on an EXISTING proposal — this action creates a new proposal, it does not vote.',
3230
+ ],
3231
+ tags: ['governance', 'proposals', 'dao'],
3232
+ inputPorts: [
3233
+ {
3234
+ path: 'coreAddress',
3235
+ portType: 'chainAddress',
3236
+ required: true,
3237
+ description:
3238
+ 'The DAO core contract address that owns the proposal. Usually a known/configured DAO address.',
3239
+ },
3240
+ {
3241
+ path: 'title',
3242
+ portType: 'string',
3243
+ required: true,
3244
+ description: 'The proposal title.',
3245
+ },
3246
+ {
3247
+ path: 'description',
3248
+ portType: 'string',
3249
+ required: true,
3250
+ description: 'The proposal description / body.',
3251
+ },
3252
+ {
3253
+ path: 'actions',
3254
+ portType: 'array',
3255
+ description:
3256
+ 'Optional executable proposal actions; array of action objects or a JSON-string array.',
3257
+ },
3258
+ ],
3259
+ requires: [
3260
+ {
3261
+ kind: 'daoCoreAddress',
3262
+ description:
3263
+ 'The target DAO core contract address (coreAddress). Ask which DAO/group the proposal is for if not provided.',
3264
+ },
3265
+ ],
3266
+ },
3267
+ 'qi/proposal.vote': {
3268
+ summary:
3269
+ 'Casts a vote (yes, no, no_with_veto, or abstain) on an existing DAO proposal via its proposal module contract, with an optional rationale. On-chain side effect; requires confirmation.',
3270
+ whenToUse: [
3271
+ 'A flow needs to vote on a proposal that already exists on-chain.',
3272
+ 'You want to record yes/no/abstain/no_with_veto with an optional rationale.',
3273
+ ],
3274
+ whenNotToUse: [
3275
+ 'Use qi/proposal.create to submit a NEW proposal — this action only votes on one that already exists.',
3276
+ ],
3277
+ tags: ['governance', 'proposals', 'voting', 'dao'],
3278
+ inputPorts: [
3279
+ {
3280
+ path: 'proposalId',
3281
+ portType: 'proposalId',
3282
+ required: true,
3283
+ description:
3284
+ 'The proposal number to vote on. Typically wired from a proposal.create output.',
3285
+ },
3286
+ {
3287
+ path: 'vote',
3288
+ portType: 'string',
3289
+ required: true,
3290
+ description:
3291
+ 'The vote to cast; must be one of: yes, no, no_with_veto, abstain.',
3292
+ },
3293
+ {
3294
+ path: 'proposalContractAddress',
3295
+ portType: 'chainAddress',
3296
+ required: true,
3297
+ description:
3298
+ 'The proposal module contract address. Typically wired from a proposal.create output (proposalContractAddress).',
3299
+ },
3300
+ {
3301
+ path: 'rationale',
3302
+ portType: 'string',
3303
+ description: 'Optional rationale recorded with the vote.',
3304
+ },
3305
+ ],
3306
+ requires: [
3307
+ {
3308
+ kind: 'proposalId',
3309
+ description:
3310
+ 'An existing proposal id and its proposal module contract address. Ask which proposal to vote on if not wired from an upstream proposal.create.',
3311
+ },
3312
+ ],
3313
+ },
3314
+ 'qi/protocol.select': {
3315
+ summary:
3316
+ 'Records a user/agent selection of a protocol (by DID) and echoes the chosen DID, name, and type forward as outputs. No side effect — pure passthrough used to capture a protocol choice for downstream steps.',
3317
+ whenToUse: [
3318
+ 'A flow needs to capture which protocol/template was chosen and pass it downstream.',
3319
+ 'You want a selected protocol DID available as a typed output for later blocks.',
3320
+ ],
3321
+ tags: ['protocol', 'selection'],
3322
+ inputPorts: [
3323
+ {
3324
+ path: 'selectedProtocolDid',
3325
+ portType: 'did',
3326
+ required: true,
3327
+ description: 'DID of the protocol to select.',
3328
+ },
3329
+ {
3330
+ path: 'selectedProtocolName',
3331
+ portType: 'string',
3332
+ description: 'Optional display name of the selected protocol.',
3333
+ },
3334
+ {
3335
+ path: 'selectedProtocolType',
3336
+ portType: 'string',
3337
+ description: 'Optional type of the selected protocol.',
3338
+ },
3339
+ ],
3340
+ requires: [
3341
+ {
3342
+ kind: 'protocol',
3343
+ description:
3344
+ 'A protocol DID to select. Ask which protocol/template if not provided upstream.',
3345
+ },
3346
+ ],
3347
+ },
3348
+ 'qi/domain.sign': {
3349
+ summary:
3350
+ 'Creates a domain entity (e.g. DAO/pod) on-chain: optionally creates a governance group, creates the domain, then signs a Domain Card verifiable credential (PIN-gated), uploads it, attaches it as a linked resource, optionally links the new entity to a parent, sources its Matrix spaces, and imports selected protocol templates. Multi-step on-chain side effect; resumable via checkpoint; requires confirmation.',
3351
+ whenToUse: [
3352
+ 'A flow needs to mint a new domain/POD entity on-chain from an approved domain card.',
3353
+ 'You want to create a governed entity (with a governance group) and attach a signed Domain Card credential.',
3354
+ ],
3355
+ whenNotToUse: [
3356
+ 'Use qi/domain.card-preview to show/approve the card before signing — this action performs the irreversible on-chain creation and credential signing.',
3357
+ ],
3358
+ tags: ['domain', 'credentials', 'entity', 'on-chain', 'signing'],
3359
+ inputPorts: [
3360
+ {
3361
+ path: 'domainCardData',
3362
+ portType: 'object',
3363
+ required: true,
3364
+ description:
3365
+ 'Domain card envelope (object or JSON string) with credentialSubject.name; signed and used to create the domain. Typically wired from domain.card-preview output.',
3366
+ },
3367
+ {
3368
+ path: 'entityType',
3369
+ portType: 'string',
3370
+ description:
3371
+ 'Optional on-chain entity type; defaults from the credential type or dao/pod.',
3372
+ },
3373
+ {
3374
+ path: 'governanceConfig',
3375
+ portType: 'object',
3376
+ description:
3377
+ 'Optional governance group config (object or JSON string); when present a governance group is created and linked.',
3378
+ },
3379
+ {
3380
+ path: 'memberConfig',
3381
+ portType: 'object',
3382
+ description:
3383
+ 'Optional member config (object or JSON string) for the governance group.',
3384
+ },
3385
+ {
3386
+ path: 'flowTemplateConfig',
3387
+ portType: 'object',
3388
+ description:
3389
+ 'Optional flow template config with selected protocol templates to import into the new domain.',
3390
+ },
3391
+ {
3392
+ path: 'context',
3393
+ portType: 'array',
3394
+ description:
3395
+ 'Optional context entries (array of { key, val } or JSON string) attached to the domain.',
3396
+ },
3397
+ {
3398
+ path: 'selectedEntityDid',
3399
+ portType: 'entityDid',
3400
+ description:
3401
+ 'Optional parent entity DID to link the new POD to (alternates: parentDid, parentDID).',
3402
+ },
3403
+ ],
3404
+ },
3405
+ 'qi/domain.card-preview': {
3406
+ summary:
3407
+ 'Validates and approves an oracle-enriched Domain Card preview (checks credentialSubject.name), then echoes the approved domainCardData forward and emits an "approved" event. No on-chain side effect — a human-approval gate between card build and signing.',
3408
+ whenToUse: [
3409
+ 'A flow needs a human to review/approve a domain card before it is signed and created on-chain.',
3410
+ 'You want to pass approved domainCardData forward to qi/domain.sign.',
3411
+ ],
3412
+ whenNotToUse: [
3413
+ 'Use qi/domain.sign to actually create the domain and sign the credential — this action only previews and approves, it makes no on-chain changes.',
3414
+ ],
3415
+ tags: ['domain', 'credentials', 'preview', 'approval'],
3416
+ inputPorts: [
3417
+ {
3418
+ path: 'domainCardData',
3419
+ portType: 'object',
3420
+ required: true,
3421
+ description:
3422
+ 'Domain card envelope (object or JSON string) with credentialSubject.name; echoed forward for signing.',
3423
+ },
3424
+ {
3425
+ path: 'domainPreviewData',
3426
+ portType: 'object',
3427
+ description:
3428
+ 'Optional oracle-enriched human-facing preview of the domain card (object or JSON string).',
3429
+ },
3430
+ ],
3431
+ },
3432
+ 'qi/form.submit': {
3433
+ summary:
3434
+ "Human-interaction step that captures a person's form answers and emits them both as a JSON string (form.answers, matching the form block runtime) and as a parsed object. Gathers input from a user — not background automation.",
3435
+ whenToUse: [
3436
+ 'A flow needs to collect structured answers from a person via a form.',
3437
+ 'You want submitted form answers available downstream as JSON and as an object.',
3438
+ ],
3439
+ whenNotToUse: [
3440
+ 'Use qi/human.checkbox.set for a single yes/no acknowledgement — this action captures a full set of form answers.',
3441
+ ],
3442
+ tags: ['human', 'form', 'input'],
3443
+ inputPorts: [
3444
+ {
3445
+ path: 'answers',
3446
+ portType: 'object',
3447
+ description:
3448
+ 'Form answers as an object or JSON-string object (also read from form.answers). Normally supplied by the person filling the form.',
3449
+ },
3450
+ ],
3451
+ requires: [
3452
+ {
3453
+ kind: 'formSchema',
3454
+ description:
3455
+ 'A form definition/schema describing the fields the person fills in. Ask what the form should collect if not provided.',
3456
+ },
3457
+ ],
3458
+ },
3459
+ 'qi/human.form.submit': {
3460
+ summary:
3461
+ "Backward-compatible alias of qi/form.submit. Human-interaction step that captures a person's form answers and emits them as a JSON string (form.answers) and a parsed object. Gathers input from a user — not background automation.",
3462
+ whenToUse: [
3463
+ 'An existing/saved flow references the legacy human.form.submit type and needs a person to submit form answers.',
3464
+ ],
3465
+ whenNotToUse: [
3466
+ 'For new flows prefer the canonical qi/form.submit; use qi/human.checkbox.set for a single acknowledgement rather than a full form.',
3467
+ ],
3468
+ tags: ['human', 'form', 'input'],
3469
+ inputPorts: [
3470
+ {
3471
+ path: 'answers',
3472
+ portType: 'object',
3473
+ description:
3474
+ 'Form answers as an object or JSON-string object (also read from form.answers). Normally supplied by the person filling the form.',
3475
+ },
3476
+ ],
3477
+ requires: [
3478
+ {
3479
+ kind: 'formSchema',
3480
+ description:
3481
+ 'A form definition/schema describing the fields the person fills in. Ask what the form should collect if not provided.',
3482
+ },
3483
+ ],
3484
+ },
3485
+ 'qi/human.checkbox.set': {
3486
+ summary:
3487
+ 'Human-interaction step representing a checkbox a person ticks; emits checked (defaults to true when executed). Gathers a single boolean acknowledgement from a user — not background automation.',
3488
+ whenToUse: [
3489
+ 'A flow needs a person to acknowledge/confirm a single item before continuing.',
3490
+ 'You want a simple checked=true/false gate driven by a human.',
3491
+ ],
3492
+ whenNotToUse: [
3493
+ 'Use qi/form.submit when you need to collect multiple structured answers — this action only records a single checkbox state.',
3494
+ ],
3495
+ tags: ['human', 'checkbox', 'input'],
3496
+ inputPorts: [
3497
+ {
3498
+ path: 'checked',
3499
+ portType: 'boolean',
3500
+ description:
3501
+ 'Whether the checkbox should be checked; defaults to true when the step executes.',
3502
+ },
3503
+ ],
3504
+ },
3505
+ };