@pikku/core 0.12.63 → 0.12.64

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 (198) hide show
  1. package/CHANGELOG.md +147 -0
  2. package/dist/dev/hot-reload.js +13 -55
  3. package/dist/dev/module-runner.d.ts +14 -0
  4. package/dist/dev/module-runner.js +59 -0
  5. package/dist/dev/reload-meta.d.ts +13 -0
  6. package/dist/dev/reload-meta.js +22 -0
  7. package/dist/errors/errors.d.ts +12 -0
  8. package/dist/errors/errors.js +19 -0
  9. package/dist/function/function-runner.d.ts +5 -5
  10. package/dist/function/function-runner.js +39 -111
  11. package/dist/function/functions.types.d.ts +17 -7
  12. package/dist/index.d.ts +5 -1
  13. package/dist/index.js +3 -1
  14. package/dist/middleware/auth-apikey.d.ts +1 -0
  15. package/dist/middleware/auth-bearer.d.ts +1 -0
  16. package/dist/middleware/auth-cookie.d.ts +1 -0
  17. package/dist/middleware/cors.d.ts +1 -0
  18. package/dist/middleware/index.d.ts +1 -1
  19. package/dist/middleware/index.js +1 -1
  20. package/dist/middleware/remote-auth.d.ts +1 -0
  21. package/dist/middleware/telemetry.d.ts +2 -0
  22. package/dist/middleware/timeout.d.ts +1 -0
  23. package/dist/permissions.d.ts +32 -25
  24. package/dist/permissions.js +104 -185
  25. package/dist/pikku-state.js +0 -2
  26. package/dist/scopes.d.ts +17 -0
  27. package/dist/scopes.js +59 -0
  28. package/dist/services/ai-embedding-service.d.ts +31 -0
  29. package/dist/services/ai-embedding-service.js +1 -0
  30. package/dist/services/credential-wire-service.d.ts +3 -1
  31. package/dist/services/credential-wire-service.js +10 -4
  32. package/dist/services/index.d.ts +4 -0
  33. package/dist/services/index.js +2 -0
  34. package/dist/services/meta-service.d.ts +15 -2
  35. package/dist/services/meta-service.js +51 -1
  36. package/dist/services/queue-webhook-service.d.ts +55 -0
  37. package/dist/services/queue-webhook-service.js +136 -0
  38. package/dist/services/scope-service.d.ts +67 -0
  39. package/dist/services/scope-service.js +1 -0
  40. package/dist/services/typed-secret-service.d.ts +8 -0
  41. package/dist/services/typed-secret-service.js +39 -4
  42. package/dist/services/webhook-service.d.ts +140 -0
  43. package/dist/services/webhook-service.js +44 -0
  44. package/dist/services/workflow-service.d.ts +7 -0
  45. package/dist/types/core.types.d.ts +34 -6
  46. package/dist/types/state.types.d.ts +20 -2
  47. package/dist/utils/hmac.d.ts +16 -0
  48. package/dist/utils/hmac.js +26 -0
  49. package/dist/utils/safe-fetch.d.ts +51 -0
  50. package/dist/utils/safe-fetch.js +192 -0
  51. package/dist/wirings/ai-agent/ai-agent-agui.d.ts +84 -0
  52. package/dist/wirings/ai-agent/ai-agent-agui.js +305 -0
  53. package/dist/wirings/ai-agent/ai-agent-prepare.d.ts +41 -1
  54. package/dist/wirings/ai-agent/ai-agent-prepare.js +146 -6
  55. package/dist/wirings/ai-agent/ai-agent-runner.js +6 -1
  56. package/dist/wirings/ai-agent/ai-agent-stream.js +61 -10
  57. package/dist/wirings/ai-agent/ai-agent.types.d.ts +27 -1
  58. package/dist/wirings/ai-agent/index.d.ts +1 -0
  59. package/dist/wirings/ai-agent/index.js +1 -0
  60. package/dist/wirings/ai-agent/voice-input.d.ts +1 -0
  61. package/dist/wirings/ai-agent/voice-input.js +2 -38
  62. package/dist/wirings/ai-agent/voice-output.d.ts +1 -0
  63. package/dist/wirings/channel/channel-common.js +0 -1
  64. package/dist/wirings/channel/channel-handler.js +1 -4
  65. package/dist/wirings/channel/channel-middleware-runner.d.ts +12 -0
  66. package/dist/wirings/channel/channel-middleware-runner.js +34 -19
  67. package/dist/wirings/channel/channel.types.d.ts +2 -6
  68. package/dist/wirings/cli/cli-runner.js +0 -2
  69. package/dist/wirings/cli/cli.types.d.ts +1 -2
  70. package/dist/wirings/http/http-routes.js +0 -3
  71. package/dist/wirings/http/http-runner.d.ts +9 -29
  72. package/dist/wirings/http/http-runner.js +9 -34
  73. package/dist/wirings/http/http.types.d.ts +1 -9
  74. package/dist/wirings/mcp/mcp-runner.js +0 -2
  75. package/dist/wirings/mcp/mcp.types.d.ts +5 -11
  76. package/dist/wirings/oauth2/index.d.ts +0 -3
  77. package/dist/wirings/oauth2/index.js +1 -2
  78. package/dist/wirings/rpc/addon-runner.d.ts +28 -0
  79. package/dist/wirings/rpc/addon-runner.js +173 -0
  80. package/dist/wirings/rpc/index.d.ts +5 -1
  81. package/dist/wirings/rpc/index.js +3 -1
  82. package/dist/wirings/rpc/remote-addon-auth.d.ts +26 -0
  83. package/dist/wirings/rpc/remote-addon-auth.js +43 -0
  84. package/dist/wirings/rpc/rpc-runner.d.ts +19 -0
  85. package/dist/wirings/rpc/rpc-runner.js +109 -3
  86. package/dist/wirings/rpc/rpc-types.d.ts +15 -1
  87. package/dist/wirings/rpc/wire-addon.js +9 -0
  88. package/dist/wirings/rpc/wire-remote-addon.d.ts +47 -0
  89. package/dist/wirings/rpc/wire-remote-addon.js +19 -0
  90. package/dist/wirings/scope/index.d.ts +3 -0
  91. package/dist/wirings/scope/index.js +2 -0
  92. package/dist/wirings/scope/scope.types.d.ts +39 -0
  93. package/dist/wirings/scope/scope.types.js +1 -0
  94. package/dist/wirings/scope/validate-scope-definitions.d.ts +16 -0
  95. package/dist/wirings/scope/validate-scope-definitions.js +76 -0
  96. package/dist/wirings/scope/wire-scope.d.ts +33 -0
  97. package/dist/wirings/scope/wire-scope.js +32 -0
  98. package/dist/wirings/workflow/dsl/index.d.ts +1 -1
  99. package/dist/wirings/workflow/dsl/workflow-dsl.types.d.ts +53 -1
  100. package/dist/wirings/workflow/graph/graph-node.d.ts +2 -0
  101. package/dist/wirings/workflow/graph/graph-runner.js +3 -0
  102. package/dist/wirings/workflow/graph/wire-workflow-graph.d.ts +6 -0
  103. package/dist/wirings/workflow/graph/wire-workflow-graph.js +1 -0
  104. package/dist/wirings/workflow/graph/workflow-graph.types.d.ts +5 -0
  105. package/dist/wirings/workflow/index.d.ts +2 -2
  106. package/dist/wirings/workflow/index.js +1 -1
  107. package/dist/wirings/workflow/pikku-workflow-service.d.ts +51 -1
  108. package/dist/wirings/workflow/pikku-workflow-service.js +171 -0
  109. package/dist/wirings/workflow/workflow.types.d.ts +1 -1
  110. package/package.json +5 -1
  111. package/run-tests.sh +1 -0
  112. package/src/dev/hot-reload.ts +13 -68
  113. package/src/dev/module-runner.test.ts +169 -0
  114. package/src/dev/module-runner.ts +103 -0
  115. package/src/dev/reload-meta.test.ts +31 -2
  116. package/src/dev/reload-meta.ts +26 -0
  117. package/src/errors/errors.ts +24 -0
  118. package/src/function/function-runner.test.ts +276 -142
  119. package/src/function/function-runner.ts +52 -145
  120. package/src/function/functions.types.ts +28 -38
  121. package/src/index.ts +8 -6
  122. package/src/middleware/index.ts +1 -5
  123. package/src/permissions.test.ts +153 -372
  124. package/src/permissions.ts +139 -259
  125. package/src/pikku-state.ts +0 -2
  126. package/src/scopes.test.ts +167 -0
  127. package/src/scopes.ts +69 -0
  128. package/src/services/ai-embedding-service.ts +31 -0
  129. package/src/services/credential-wire-service.ts +10 -4
  130. package/src/services/index.ts +20 -0
  131. package/src/services/meta-service.ts +68 -3
  132. package/src/services/queue-webhook-service.test.ts +408 -0
  133. package/src/services/queue-webhook-service.ts +182 -0
  134. package/src/services/scope-service.ts +75 -0
  135. package/src/services/typed-secret-service.test.ts +35 -0
  136. package/src/services/typed-secret-service.ts +39 -4
  137. package/src/services/webhook-service.ts +180 -0
  138. package/src/services/workflow-service.ts +7 -0
  139. package/src/types/core.types.ts +42 -14
  140. package/src/types/state.types.ts +23 -3
  141. package/src/utils/hmac.ts +27 -0
  142. package/src/utils/safe-fetch.test.ts +373 -0
  143. package/src/utils/safe-fetch.ts +213 -0
  144. package/src/wirings/ai-agent/ai-agent-agui.test.ts +1127 -0
  145. package/src/wirings/ai-agent/ai-agent-agui.ts +386 -0
  146. package/src/wirings/ai-agent/ai-agent-prepare.test.ts +308 -0
  147. package/src/wirings/ai-agent/ai-agent-prepare.ts +192 -8
  148. package/src/wirings/ai-agent/ai-agent-runner.test.ts +48 -1
  149. package/src/wirings/ai-agent/ai-agent-runner.ts +21 -0
  150. package/src/wirings/ai-agent/ai-agent-stream.test.ts +198 -10
  151. package/src/wirings/ai-agent/ai-agent-stream.ts +84 -10
  152. package/src/wirings/ai-agent/ai-agent.types.ts +28 -0
  153. package/src/wirings/ai-agent/index.ts +1 -0
  154. package/src/wirings/ai-agent/voice-input.ts +6 -35
  155. package/src/wirings/channel/channel-common.ts +0 -1
  156. package/src/wirings/channel/channel-handler.ts +1 -9
  157. package/src/wirings/channel/channel-middleware-runner.test.ts +28 -2
  158. package/src/wirings/channel/channel-middleware-runner.ts +39 -27
  159. package/src/wirings/channel/channel.types.ts +0 -6
  160. package/src/wirings/cli/cli-runner.ts +0 -2
  161. package/src/wirings/cli/cli.types.ts +0 -2
  162. package/src/wirings/http/http-routes.ts +0 -3
  163. package/src/wirings/http/http-runner.test.ts +1 -11
  164. package/src/wirings/http/http-runner.ts +16 -39
  165. package/src/wirings/http/http.types.ts +1 -10
  166. package/src/wirings/mcp/mcp-runner.ts +0 -2
  167. package/src/wirings/mcp/mcp.types.ts +3 -11
  168. package/src/wirings/oauth2/index.ts +0 -3
  169. package/src/wirings/rpc/addon-runner.ts +233 -0
  170. package/src/wirings/rpc/index.ts +17 -1
  171. package/src/wirings/rpc/remote-addon-auth.ts +69 -0
  172. package/src/wirings/rpc/rpc-runner.test.ts +319 -16
  173. package/src/wirings/rpc/rpc-runner.ts +155 -3
  174. package/src/wirings/rpc/rpc-types.ts +16 -5
  175. package/src/wirings/rpc/wire-addon.test.ts +6 -3
  176. package/src/wirings/rpc/wire-addon.ts +9 -0
  177. package/src/wirings/rpc/wire-remote-addon.ts +57 -0
  178. package/src/wirings/scope/index.ts +14 -0
  179. package/src/wirings/scope/scope.test.ts +135 -0
  180. package/src/wirings/scope/scope.types.ts +44 -0
  181. package/src/wirings/scope/validate-scope-definitions.ts +110 -0
  182. package/src/wirings/scope/wire-scope.ts +34 -0
  183. package/src/wirings/workflow/dsl/index.ts +4 -0
  184. package/src/wirings/workflow/dsl/workflow-dsl.types.ts +61 -0
  185. package/src/wirings/workflow/graph/graph-node.ts +2 -0
  186. package/src/wirings/workflow/graph/graph-runner.test.ts +58 -0
  187. package/src/wirings/workflow/graph/graph-runner.ts +3 -0
  188. package/src/wirings/workflow/graph/wire-workflow-graph.ts +7 -0
  189. package/src/wirings/workflow/graph/workflow-graph.types.ts +5 -0
  190. package/src/wirings/workflow/index.ts +5 -0
  191. package/src/wirings/workflow/pikku-workflow-service.test.ts +341 -0
  192. package/src/wirings/workflow/pikku-workflow-service.ts +236 -0
  193. package/src/wirings/workflow/workflow-step-session.test.ts +21 -5
  194. package/src/wirings/workflow/workflow.types.ts +4 -0
  195. package/tsconfig.tsbuildinfo +1 -1
  196. package/src/wirings/oauth2/oauth2-client.test.ts +0 -930
  197. package/src/wirings/oauth2/oauth2-client.ts +0 -331
  198. package/src/wirings/oauth2/oauth2-routes.ts +0 -234
@@ -48,9 +48,11 @@ import type { PikkuWire, SerializedError } from '../../types/core.types.js'
48
48
  import type { QueueService } from '../queue/queue.types.js'
49
49
  import { runScheduledTask } from '../scheduler/scheduler-runner.js'
50
50
  import type {
51
+ ApprovalOutcome,
51
52
  PikkuScenarioWire,
52
53
  StepState,
53
54
  StepStatus,
55
+ WorkflowApprovalOptions,
54
56
  WorkflowPlannedStep,
55
57
  WorkflowRun,
56
58
  WorkflowRunMirror,
@@ -203,6 +205,27 @@ addError(WorkflowRunCancelledError, {
203
205
  message: 'Workflow was cancelled.',
204
206
  })
205
207
 
208
+ /**
209
+ * A decision arrived for an approval gate that has already resolved. The gate
210
+ * caches its outcome as the step result and never re-reads run state, so the
211
+ * decision could not take effect — it is rejected rather than accepted and
212
+ * dropped.
213
+ */
214
+ export class WorkflowApprovalResolvedError extends PikkuError {
215
+ public payload: {
216
+ reason: string
217
+ outcome: ApprovalOutcome<unknown>['status']
218
+ }
219
+ constructor(reason: string, outcome: ApprovalOutcome<unknown>['status']) {
220
+ super(`Approval already ${outcome}: ${reason}`)
221
+ this.payload = { reason, outcome }
222
+ }
223
+ }
224
+ addError(WorkflowApprovalResolvedError, {
225
+ status: 409,
226
+ message: 'Approval has already been resolved.',
227
+ })
228
+
206
229
  export class WorkflowServiceNotInitialized extends Error {}
207
230
  export class WorkflowStepNameNotString extends Error {
208
231
  constructor(stepName: any) {
@@ -2148,6 +2171,214 @@ export abstract class PikkuWorkflowService implements WorkflowService {
2148
2171
  })
2149
2172
  }
2150
2173
 
2174
+ /**
2175
+ * Wake a run later by enqueuing a delayed orchestrator pass. Deliberately NOT
2176
+ * {@link scheduleSleep}: that resolves the step it is given, which for an
2177
+ * approval would resolve the gate itself. This only nudges the run to replay
2178
+ * and re-evaluate — the gate stays the sole judge of its own outcome.
2179
+ *
2180
+ * Best-effort by design. Expiry is decided from the recorded deadline on
2181
+ * replay, so losing this wake costs liveness (the run sits until something
2182
+ * else resumes it), never correctness.
2183
+ */
2184
+ private async scheduleRunWake(runId: string, delay: number): Promise<void> {
2185
+ try {
2186
+ const queueService = this.verifyQueueService()
2187
+ const run = await this.getRun(runId)
2188
+ if (!run?.workflow) return
2189
+ await queueService.add(
2190
+ this.getOrchestratorQueueName(run.workflow),
2191
+ { runId },
2192
+ { ...this.resolveStepJobOptions(), delay }
2193
+ )
2194
+ } catch (error) {
2195
+ this.logger?.warn(
2196
+ `Failed to schedule approval expiry wake for run ${runId}; expiry will still resolve on the next replay`,
2197
+ error
2198
+ )
2199
+ }
2200
+ }
2201
+
2202
+ /**
2203
+ * Durable step name for an approval gate. Namespaced separately from suspend
2204
+ * so the two can't collide, and derived from `reason` for the same reason
2205
+ * {@link getSuspendStepName} is: it must be stable across replays.
2206
+ */
2207
+ private getApprovalStepName(reason: string): string {
2208
+ return `__workflow_approval:${reason}`
2209
+ }
2210
+
2211
+ /**
2212
+ * Run-state key holding an approval gate's record. Hex-encoded because the
2213
+ * Mongo backend restricts state keys to `/^[a-zA-Z0-9_]+$/` and a `reason` is
2214
+ * arbitrary human text. One key per gate, so two gates resolving concurrently
2215
+ * can't clobber each other through a read-modify-write.
2216
+ */
2217
+ private approvalStateKey(stepName: string): string {
2218
+ let hex = ''
2219
+ for (const byte of new TextEncoder().encode(stepName)) {
2220
+ hex += byte.toString(16).padStart(2, '0')
2221
+ }
2222
+ return `__approval_${hex}`
2223
+ }
2224
+
2225
+ /**
2226
+ * Record a decision against an approval gate and wake the run. Called from
2227
+ * outside the workflow (an HTTP route, an RPC), so the schema value is NOT in
2228
+ * scope here — the payload is stored raw and validated on replay inside the
2229
+ * workflow body, which is the only place the schema exists. An invalid payload
2230
+ * therefore leaves the gate closed rather than failing the run.
2231
+ *
2232
+ * `reason` addresses the first reach of that gate. An approval reached more
2233
+ * than once under the same reason (e.g. in a loop) gets `#N`-suffixed step
2234
+ * rows that this cannot currently target.
2235
+ */
2236
+ public async approveStep(
2237
+ runId: string,
2238
+ reason: string,
2239
+ decision: unknown
2240
+ ): Promise<void> {
2241
+ const stepName = this.getApprovalStepName(reason)
2242
+ const stateKey = this.approvalStateKey(stepName)
2243
+
2244
+ // A resolved gate returns its cached step result and never re-reads state,
2245
+ // so a decision recorded now would be silently discarded — most obviously
2246
+ // when it loses the race with expiry. Reject instead, so the approver
2247
+ // learns their decision did not land.
2248
+ let resolved: StepState | undefined
2249
+ try {
2250
+ resolved = await this.getStepState(runId, stepName)
2251
+ } catch {
2252
+ // No step row yet: the run has not reached the gate. Recording a decision
2253
+ // ahead of it is legitimate — the gate picks it up on arrival.
2254
+ }
2255
+ if (resolved?.stepId && resolved.status === 'succeeded') {
2256
+ const outcome = resolved.result as ApprovalOutcome<unknown> | undefined
2257
+ throw new WorkflowApprovalResolvedError(
2258
+ reason,
2259
+ outcome?.status ?? 'decided'
2260
+ )
2261
+ }
2262
+
2263
+ const state = await this.getRunState(runId)
2264
+ const record = (state[stateKey] ?? {}) as Record<string, unknown>
2265
+ await this.updateRunState(runId, stateKey, {
2266
+ ...record,
2267
+ decision,
2268
+ decidedAt: new Date().toISOString(),
2269
+ error: undefined,
2270
+ })
2271
+ await this.resumeWorkflow(runId)
2272
+ }
2273
+
2274
+ private async approvalStep(
2275
+ runId: string,
2276
+ reason: string,
2277
+ options: WorkflowApprovalOptions
2278
+ ): Promise<ApprovalOutcome<unknown>> {
2279
+ const fromStepName = this.lastStepName(runId)
2280
+ const approvalStepName = this.nextStepKey(
2281
+ runId,
2282
+ this.getApprovalStepName(reason)
2283
+ )
2284
+ return await this.withStepLock(runId, approvalStepName, async () => {
2285
+ let stepState: StepState
2286
+ try {
2287
+ stepState = await this.getStepState(runId, approvalStepName)
2288
+ } catch {
2289
+ stepState = await this.insertStepState(
2290
+ runId,
2291
+ approvalStepName,
2292
+ 'pikkuWorkflowApproval',
2293
+ { reason, expiry: options.expiry },
2294
+ undefined,
2295
+ fromStepName
2296
+ )
2297
+ }
2298
+ if (!stepState.stepId) {
2299
+ stepState = await this.insertStepState(
2300
+ runId,
2301
+ approvalStepName,
2302
+ 'pikkuWorkflowApproval',
2303
+ { reason, expiry: options.expiry },
2304
+ undefined,
2305
+ fromStepName
2306
+ )
2307
+ }
2308
+
2309
+ // Unlike suspend, `succeeded` here means a decision (or expiry) was
2310
+ // actually resolved, and the step result IS the return channel.
2311
+ if (stepState.status === 'succeeded') {
2312
+ return stepState.result as ApprovalOutcome<unknown>
2313
+ }
2314
+
2315
+ const stateKey = this.approvalStateKey(approvalStepName)
2316
+ let record = ((await this.getRunState(runId))[stateKey] ?? {}) as {
2317
+ decision?: unknown
2318
+ decidedAt?: string
2319
+ expiresAt?: string
2320
+ error?: unknown
2321
+ }
2322
+
2323
+ if (stepState.status === 'pending') {
2324
+ await this.setStepRunning(stepState.stepId)
2325
+ // First reach: stamp the deadline and nudge the run awake when it
2326
+ // passes. The deadline is what's authoritative — see below.
2327
+ if (options.expiry !== undefined && !record.expiresAt) {
2328
+ const expiresAt = new Date(
2329
+ Date.now() + getDurationInMilliseconds(options.expiry)
2330
+ ).toISOString()
2331
+ record = { ...record, expiresAt }
2332
+ await this.updateRunState(runId, stateKey, record)
2333
+ await this.scheduleRunWake(
2334
+ runId,
2335
+ getDurationInMilliseconds(options.expiry)
2336
+ )
2337
+ }
2338
+ }
2339
+
2340
+ if (record.decision !== undefined) {
2341
+ const validation = await options.schema['~standard'].validate(
2342
+ record.decision
2343
+ )
2344
+ if (validation.issues) {
2345
+ // Drop the bad decision and re-close the gate, leaving the failure
2346
+ // legible to whoever tries next. Failing the run instead would let any
2347
+ // caller kill a workflow with a malformed payload.
2348
+ await this.updateRunState(runId, stateKey, {
2349
+ ...record,
2350
+ decision: undefined,
2351
+ decidedAt: undefined,
2352
+ error: validation.issues.map((issue) => ({
2353
+ message: issue.message,
2354
+ path: issue.path?.map((segment) =>
2355
+ typeof segment === 'object' ? segment.key : segment
2356
+ ),
2357
+ })),
2358
+ })
2359
+ throw new WorkflowSuspendedException(runId, reason)
2360
+ }
2361
+ const outcome: ApprovalOutcome<unknown> = {
2362
+ status: 'decided',
2363
+ data: validation.value,
2364
+ }
2365
+ await this.setStepResult(stepState.stepId, outcome)
2366
+ return outcome
2367
+ }
2368
+
2369
+ // Expiry is decided by comparing against the recorded deadline rather than
2370
+ // by the timer having fired, so a duplicate, late, or dropped timer all
2371
+ // produce the same answer.
2372
+ if (record.expiresAt && Date.now() >= Date.parse(record.expiresAt)) {
2373
+ const outcome: ApprovalOutcome<unknown> = { status: 'expired' }
2374
+ await this.setStepResult(stepState.stepId, outcome)
2375
+ return outcome
2376
+ }
2377
+
2378
+ throw new WorkflowSuspendedException(runId, reason)
2379
+ })
2380
+ }
2381
+
2151
2382
  public createWorkflowWire(
2152
2383
  name: string,
2153
2384
  runId: string,
@@ -2345,6 +2576,11 @@ export abstract class PikkuWorkflowService implements WorkflowService {
2345
2576
  await this.suspendStep(runId, reason)
2346
2577
  },
2347
2578
 
2579
+ approval: (async (reason: string, options: WorkflowApprovalOptions) => {
2580
+ this.verifyStepName(reason)
2581
+ return await this.approvalStep(runId, reason, options)
2582
+ }) as PikkuScenarioWire['approval'],
2583
+
2348
2584
  runScheduledTask: async (taskName: string) => {
2349
2585
  await runScheduledTask({ name: taskName })
2350
2586
  },
@@ -31,7 +31,9 @@ function capturingRpcService(): {
31
31
 
32
32
  describe('queued workflow steps carry the run wire pikkuUserId', () => {
33
33
  test('invokeStepRpc merges run.wire.pikkuUserId into the step wire override', async () => {
34
- pikkuState(null, 'package', 'singletonServices', { logger: silentLogger } as any)
34
+ pikkuState(null, 'package', 'singletonServices', {
35
+ logger: silentLogger,
36
+ } as any)
35
37
 
36
38
  const ws = new InMemoryWorkflowService()
37
39
  const runId = await ws.createRun('flow', {}, false, 'hash', {
@@ -56,19 +58,33 @@ describe('queued workflow steps carry the run wire pikkuUserId', () => {
56
58
  'user-abc',
57
59
  'the step wire override carries the run wire pikkuUserId so authed steps rehydrate'
58
60
  )
59
- assert.ok(rpc.wire().workflowStep, 'workflowStep provenance is still present')
61
+ assert.ok(
62
+ rpc.wire().workflowStep,
63
+ 'workflowStep provenance is still present'
64
+ )
60
65
  })
61
66
 
62
67
  test('no pikkuUserId key is injected when the run wire has none', async () => {
63
- pikkuState(null, 'package', 'singletonServices', { logger: silentLogger } as any)
68
+ pikkuState(null, 'package', 'singletonServices', {
69
+ logger: silentLogger,
70
+ } as any)
64
71
 
65
72
  const ws = new InMemoryWorkflowService()
66
- const runId = await ws.createRun('flow', {}, false, 'hash', { type: 'test' })
73
+ const runId = await ws.createRun('flow', {}, false, 'hash', {
74
+ type: 'test',
75
+ })
67
76
  await ws.insertStepState(runId, 'noauth', 'someFn', {})
68
77
  const stepState = await ws.getStepState(runId, 'noauth')
69
78
 
70
79
  const rpc = capturingRpcService()
71
- await (ws as any).invokeStepRpc(runId, 'noauth', stepState, 'someFn', {}, rpc.service)
80
+ await (ws as any).invokeStepRpc(
81
+ runId,
82
+ 'noauth',
83
+ stepState,
84
+ 'someFn',
85
+ {},
86
+ rpc.service
87
+ )
72
88
 
73
89
  assert.ok(
74
90
  !('pikkuUserId' in rpc.wire()),
@@ -14,6 +14,9 @@ export type {
14
14
  WorkflowWireDoInline,
15
15
  WorkflowWireSleep,
16
16
  WorkflowWireSuspend,
17
+ WorkflowWireApproval,
18
+ WorkflowApprovalOptions,
19
+ ApprovalOutcome,
17
20
  InputSource,
18
21
  OutputBinding,
19
22
  RpcStepMeta,
@@ -28,6 +31,7 @@ export type {
28
31
  SleepStepMeta,
29
32
  CancelStepMeta,
30
33
  SuspendStepMeta,
34
+ ApprovalStepMeta,
31
35
  SetStepMeta,
32
36
  SwitchCaseMeta,
33
37
  SwitchStepMeta,