@pikku/core 0.12.79 → 0.12.82

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 (479) hide show
  1. package/CHANGELOG.md +473 -0
  2. package/dist/dev/hot-reload.js +1 -6
  3. package/dist/ecosystem.d.ts +27 -0
  4. package/dist/ecosystem.js +26 -0
  5. package/dist/errors/error-handler.js +4 -2
  6. package/dist/errors/index.d.ts +1 -1
  7. package/dist/errors/index.js +1 -1
  8. package/dist/function/function-runner.js +22 -47
  9. package/dist/function/functions.types.d.ts +9 -5
  10. package/dist/function/index.d.ts +1 -1
  11. package/dist/index.d.ts +14 -19
  12. package/dist/index.js +6 -11
  13. package/dist/middleware/auth-cookie.d.ts +0 -4
  14. package/dist/middleware/auth-cookie.js +38 -2
  15. package/dist/middleware/cors.js +1 -0
  16. package/dist/middleware/index.d.ts +1 -1
  17. package/dist/middleware/index.js +1 -1
  18. package/dist/middleware/remote-auth.js +14 -3
  19. package/dist/permissions.d.ts +8 -10
  20. package/dist/permissions.js +0 -11
  21. package/dist/pikku-state.js +16 -5
  22. package/dist/schema.js +35 -1
  23. package/dist/services/ai-agent-runner-service.d.ts +7 -0
  24. package/dist/services/ai-run-state-service.d.ts +17 -1
  25. package/dist/services/in-memory-ai-run-state-service.d.ts +6 -2
  26. package/dist/services/in-memory-ai-run-state-service.js +11 -1
  27. package/dist/services/in-memory-workflow-service.js +2 -0
  28. package/dist/services/index.d.ts +15 -15
  29. package/dist/services/index.js +5 -5
  30. package/dist/services/local-content-request-handler.d.ts +21 -0
  31. package/dist/services/local-content-request-handler.js +72 -53
  32. package/dist/services/local-content.d.ts +6 -0
  33. package/dist/services/local-content.js +14 -1
  34. package/dist/services/scoped-credential-service.d.ts +21 -0
  35. package/dist/services/scoped-credential-service.js +53 -0
  36. package/dist/services/workflow-service.d.ts +4 -2
  37. package/dist/testing/service-tests/agent-run-service-tests.d.ts +10 -0
  38. package/dist/testing/service-tests/agent-run-service-tests.js +72 -0
  39. package/dist/testing/service-tests/ai-storage-service-tests.d.ts +3 -0
  40. package/dist/testing/service-tests/ai-storage-service-tests.js +302 -0
  41. package/dist/testing/service-tests/channel-store-tests.d.ts +3 -0
  42. package/dist/testing/service-tests/channel-store-tests.js +72 -0
  43. package/dist/testing/service-tests/credential-service-tests.d.ts +3 -0
  44. package/dist/testing/service-tests/credential-service-tests.js +109 -0
  45. package/dist/testing/service-tests/deployment-service-tests.d.ts +3 -0
  46. package/dist/testing/service-tests/deployment-service-tests.js +21 -0
  47. package/dist/testing/service-tests/event-hub-store-tests.d.ts +3 -0
  48. package/dist/testing/service-tests/event-hub-store-tests.js +34 -0
  49. package/dist/testing/service-tests/secret-service-tests.d.ts +3 -0
  50. package/dist/testing/service-tests/secret-service-tests.js +80 -0
  51. package/dist/testing/service-tests/session-store-tests.d.ts +3 -0
  52. package/dist/testing/service-tests/session-store-tests.js +43 -0
  53. package/dist/testing/service-tests/workflow-run-service-tests.d.ts +3 -0
  54. package/dist/testing/service-tests/workflow-run-service-tests.js +42 -0
  55. package/dist/testing/service-tests/workflow-service-tests.d.ts +3 -0
  56. package/dist/testing/service-tests/workflow-service-tests.js +150 -0
  57. package/dist/testing/service-tests.d.ts +6 -0
  58. package/dist/testing/service-tests.js +26 -791
  59. package/dist/types/core.types.d.ts +11 -3
  60. package/dist/types/state.types.d.ts +14 -1
  61. package/dist/wirings/actor-flow/index.d.ts +1 -1
  62. package/dist/wirings/actor-flow/run-conversation.js +1 -6
  63. package/dist/wirings/ai-agent/agent-rpc.d.ts +15 -0
  64. package/dist/wirings/ai-agent/agent-rpc.js +53 -0
  65. package/dist/wirings/ai-agent/ai-agent-agui.js +1 -5
  66. package/dist/wirings/ai-agent/ai-agent-finalize.d.ts +58 -0
  67. package/dist/wirings/ai-agent/ai-agent-finalize.js +138 -0
  68. package/dist/wirings/ai-agent/ai-agent-interrupt.js +1 -0
  69. package/dist/wirings/ai-agent/ai-agent-memory.d.ts +2 -8
  70. package/dist/wirings/ai-agent/ai-agent-memory.js +37 -18
  71. package/dist/wirings/ai-agent/ai-agent-model-config.d.ts +7 -0
  72. package/dist/wirings/ai-agent/ai-agent-model-config.js +44 -1
  73. package/dist/wirings/ai-agent/ai-agent-prepare.js +5 -9
  74. package/dist/wirings/ai-agent/ai-agent-runner.js +89 -147
  75. package/dist/wirings/ai-agent/ai-agent-stream.js +109 -97
  76. package/dist/wirings/ai-agent/ai-agent-turn.d.ts +57 -0
  77. package/dist/wirings/ai-agent/ai-agent-turn.js +82 -0
  78. package/dist/wirings/ai-agent/ai-agent.types.d.ts +47 -2
  79. package/dist/wirings/ai-agent/index.d.ts +8 -7
  80. package/dist/wirings/ai-agent/index.js +5 -4
  81. package/dist/wirings/ai-agent/voice-input.js +1 -6
  82. package/dist/wirings/ai-agent/voice-output.js +2 -12
  83. package/dist/wirings/ai-scorer/ai-scorer-grade.d.ts +26 -0
  84. package/dist/wirings/ai-scorer/ai-scorer-grade.js +33 -0
  85. package/dist/wirings/ai-scorer/ai-scorer-judge.d.ts +17 -0
  86. package/dist/wirings/ai-scorer/ai-scorer-judge.js +92 -0
  87. package/dist/wirings/ai-scorer/ai-scorer-live.d.ts +15 -0
  88. package/dist/wirings/ai-scorer/ai-scorer-live.js +38 -0
  89. package/dist/wirings/ai-scorer/ai-scorer-registry.d.ts +18 -0
  90. package/dist/wirings/ai-scorer/ai-scorer-registry.js +46 -0
  91. package/dist/wirings/ai-scorer/ai-scorer-sampling.d.ts +8 -0
  92. package/dist/wirings/ai-scorer/ai-scorer-sampling.js +31 -0
  93. package/dist/wirings/ai-scorer/ai-scorer-snapshots.d.ts +10 -0
  94. package/dist/wirings/ai-scorer/ai-scorer-snapshots.js +40 -0
  95. package/dist/wirings/ai-scorer/ai-scorer-worker.d.ts +15 -0
  96. package/dist/wirings/ai-scorer/ai-scorer-worker.js +58 -0
  97. package/dist/wirings/ai-scorer/ai-scorer.d.ts +39 -0
  98. package/dist/wirings/ai-scorer/ai-scorer.js +40 -0
  99. package/dist/wirings/ai-scorer/ai-scorer.types.d.ts +90 -0
  100. package/dist/wirings/ai-scorer/ai-scorer.types.js +4 -0
  101. package/dist/wirings/ai-scorer/index.d.ts +6 -0
  102. package/dist/wirings/ai-scorer/index.js +5 -0
  103. package/dist/wirings/channel/channel-common.js +1 -0
  104. package/dist/wirings/channel/channel-handler.js +3 -5
  105. package/dist/wirings/channel/channel-rpc-service.d.ts +0 -6
  106. package/dist/wirings/channel/channel-rpc-service.js +0 -8
  107. package/dist/wirings/channel/channel-rpc.types.d.ts +6 -0
  108. package/dist/wirings/channel/channel-rpc.types.js +8 -0
  109. package/dist/wirings/channel/channel-runner.d.ts +1 -3
  110. package/dist/wirings/channel/channel-runner.js +16 -8
  111. package/dist/wirings/channel/channel.types.d.ts +2 -0
  112. package/dist/wirings/channel/index.d.ts +5 -6
  113. package/dist/wirings/channel/index.js +3 -4
  114. package/dist/wirings/channel/local/local-channel-runner.js +8 -1
  115. package/dist/wirings/channel/pikku-abstract-channel-handler.js +1 -0
  116. package/dist/wirings/channel/serverless/serverless-channel-runner.js +3 -0
  117. package/dist/wirings/cli/channel/cli-channel-runner.js +2 -0
  118. package/dist/wirings/cli/channel/cli-raw-channel-runner.js +9 -1
  119. package/dist/wirings/cli/channel/index.d.ts +1 -2
  120. package/dist/wirings/cli/channel/index.js +0 -1
  121. package/dist/wirings/cli/cli-runner.js +17 -3
  122. package/dist/wirings/cli/cli.types.d.ts +0 -8
  123. package/dist/wirings/cli/command-parser.js +13 -0
  124. package/dist/wirings/credential/index.d.ts +1 -1
  125. package/dist/wirings/gateway/gateway-runner.js +9 -2
  126. package/dist/wirings/gateway/index.d.ts +1 -1
  127. package/dist/wirings/http/http-routes.js +2 -0
  128. package/dist/wirings/http/http-runner.d.ts +0 -10
  129. package/dist/wirings/http/http-runner.js +14 -15
  130. package/dist/wirings/http/http.types.d.ts +0 -10
  131. package/dist/wirings/http/index.d.ts +2 -3
  132. package/dist/wirings/http/index.js +1 -1
  133. package/dist/wirings/mcp/index.d.ts +1 -1
  134. package/dist/wirings/mcp/mcp-runner.d.ts +15 -7
  135. package/dist/wirings/mcp/mcp-runner.js +18 -11
  136. package/dist/wirings/persona/index.d.ts +3 -4
  137. package/dist/wirings/persona/index.js +2 -3
  138. package/dist/wirings/queue/index.d.ts +1 -3
  139. package/dist/wirings/queue/index.js +1 -3
  140. package/dist/wirings/rpc/addon-runner.d.ts +4 -0
  141. package/dist/wirings/rpc/addon-runner.js +19 -3
  142. package/dist/wirings/rpc/rpc-runner.d.ts +8 -51
  143. package/dist/wirings/rpc/rpc-runner.js +55 -86
  144. package/dist/wirings/rpc/rpc-types.d.ts +7 -0
  145. package/dist/wirings/rpc/wire-addon.d.ts +13 -0
  146. package/dist/wirings/rpc/wire-addon.js +4 -0
  147. package/dist/wirings/scheduler/index.d.ts +1 -1
  148. package/dist/wirings/secret/validate-secret-definitions.js +2 -2
  149. package/dist/wirings/trigger/index.d.ts +1 -1
  150. package/dist/wirings/trigger/pikku-trigger-service.d.ts +0 -4
  151. package/dist/wirings/trigger/trigger-runner.js +1 -0
  152. package/dist/wirings/virtual-user/index.d.ts +5 -6
  153. package/dist/wirings/virtual-user/index.js +2 -4
  154. package/dist/wirings/virtual-user/run-virtual-user.js +11 -11
  155. package/dist/wirings/virtual-user/virtual-user-derive.js +4 -25
  156. package/dist/wirings/virtual-user/virtual-user-dispositions.js +1 -4
  157. package/dist/wirings/workflow/dsl/workflow-dsl.types.d.ts +100 -3
  158. package/dist/wirings/workflow/feature.js +7 -4
  159. package/dist/wirings/workflow/graph/graph-runner.d.ts +1 -2
  160. package/dist/wirings/workflow/graph/graph-runner.js +8 -7
  161. package/dist/wirings/workflow/graph/workflow-graph.types.d.ts +0 -4
  162. package/dist/wirings/workflow/index.d.ts +12 -7
  163. package/dist/wirings/workflow/index.js +7 -3
  164. package/dist/wirings/workflow/pikku-scenario-service.d.ts +17 -3
  165. package/dist/wirings/workflow/pikku-scenario-service.js +64 -10
  166. package/dist/wirings/workflow/pikku-workflow-service.d.ts +28 -147
  167. package/dist/wirings/workflow/pikku-workflow-service.js +85 -493
  168. package/dist/wirings/workflow/scenario-step.types.d.ts +8 -0
  169. package/dist/wirings/workflow/workflow-approval-audit.d.ts +16 -0
  170. package/dist/wirings/workflow/workflow-approval-audit.js +40 -0
  171. package/dist/wirings/workflow/workflow-approval-policy.d.ts +20 -0
  172. package/dist/wirings/workflow/workflow-approval-policy.js +48 -0
  173. package/dist/wirings/workflow/workflow-approval.d.ts +67 -0
  174. package/dist/wirings/workflow/workflow-approval.js +177 -0
  175. package/dist/wirings/workflow/workflow-constants.d.ts +26 -0
  176. package/dist/wirings/workflow/workflow-constants.js +35 -0
  177. package/dist/wirings/workflow/workflow-errors.d.ts +58 -0
  178. package/dist/wirings/workflow/workflow-errors.js +112 -0
  179. package/dist/wirings/workflow/workflow-meta-resolver.d.ts +11 -0
  180. package/dist/wirings/workflow/workflow-meta-resolver.js +31 -0
  181. package/dist/wirings/workflow/workflow-queue-routing.d.ts +8 -0
  182. package/dist/wirings/workflow/workflow-queue-routing.js +38 -0
  183. package/dist/wirings/workflow/workflow-queue-wiring.d.ts +20 -0
  184. package/dist/wirings/workflow/workflow-queue-wiring.js +79 -0
  185. package/dist/wirings/workflow/workflow-recovery.d.ts +68 -0
  186. package/dist/wirings/workflow/workflow-recovery.js +101 -0
  187. package/dist/wirings/workflow/workflow-run-engine.types.d.ts +54 -0
  188. package/dist/wirings/workflow/workflow-run-ownership.d.ts +17 -0
  189. package/dist/wirings/workflow/workflow-run-ownership.js +30 -0
  190. package/dist/wirings/workflow/workflow-suspend.d.ts +12 -0
  191. package/dist/wirings/workflow/workflow-suspend.js +33 -0
  192. package/dist/wirings/workflow/workflow.types.d.ts +8 -1
  193. package/knowledge/decisions/internals/a-non-streaming-agent-run-registers-with-airunstate-too.md +22 -0
  194. package/knowledge/decisions/internals/a-resumed-agent-turn-is-as-interruptible-as-the-first.md +20 -0
  195. package/knowledge/decisions/internals/a-scenario-step-template-is-offered-unfilled.md +21 -0
  196. package/knowledge/decisions/internals/a-virtual-user-decides-whether-to-trust-memory-once-per-turn.md +21 -0
  197. package/knowledge/decisions/internals/a-wall-clock-threshold-is-a-load-test-in-disguise.md +46 -0
  198. package/knowledge/decisions/internals/a-workflow-wire-is-built-from-the-run-not-from-the-rpc-service.md +45 -0
  199. package/knowledge/decisions/internals/addon-pikku-meta-ships-at-the-package-root-or-under-dist.md +32 -0
  200. package/knowledge/decisions/internals/agent-context-waits-for-a-tool-result-still-being-written.md +21 -0
  201. package/knowledge/decisions/internals/agent-speech-travels-as-a-custom-agui-event.md +22 -0
  202. package/knowledge/decisions/internals/an-addon-scope-root-loses-to-a-root-the-host-already-declares.md +39 -0
  203. package/knowledge/decisions/internals/an-agent-interrupt-is-not-a-failure.md +23 -0
  204. package/knowledge/decisions/internals/an-agent-run-owned-by-another-instance-says-so.md +25 -0
  205. package/knowledge/decisions/internals/an-agent-stream-send-must-return-the-inner-sends-promise.md +22 -0
  206. package/knowledge/decisions/internals/an-empty-text-part-is-omitted-from-an-agent-message.md +20 -0
  207. package/knowledge/decisions/internals/an-empty-transcript-is-not-recorded.md +22 -0
  208. package/knowledge/decisions/internals/an-unref-d-timer-cannot-be-awaited-under-node-test.md +66 -0
  209. package/knowledge/decisions/internals/channel-state-accessors-are-unsound-generics-that-every-implementation-asserts.md +33 -0
  210. package/knowledge/decisions/internals/gateway-listener-middleware-runs-without-an-rpc-on-the-wire.md +43 -0
  211. package/knowledge/decisions/internals/hot-reload-writes-into-the-function-map-captured-at-startup.md +26 -0
  212. package/knowledge/decisions/internals/index.md +42 -3
  213. package/knowledge/decisions/internals/only-exposed-functions-enter-a-virtual-user-catalogue.md +21 -0
  214. package/knowledge/decisions/internals/scenario-given-and-when-are-sugar-but-then-is-not.md +24 -0
  215. package/knowledge/decisions/internals/side-effects-are-an-allowlist-not-a-boolean.md +34 -0
  216. package/knowledge/decisions/internals/speech-synthesis-picks-a-voice-per-sentence-and-warns-once.md +24 -0
  217. package/knowledge/decisions/internals/the-actor-prompt-says-json-because-of-json-object-mode.md +22 -0
  218. package/knowledge/decisions/internals/the-agent-done-event-goes-through-the-middleware-and-is-awaited.md +26 -0
  219. package/knowledge/decisions/internals/the-api-report-pins-members-not-just-names.md +43 -0
  220. package/knowledge/decisions/internals/the-ecosystem-entry-point-carries-the-adapter-surface.md +58 -0
  221. package/knowledge/decisions/internals/the-middleware-resolution-cache-is-deliberately-unbounded.md +40 -0
  222. package/knowledge/decisions/internals/the-per-invocation-rpc-view-is-a-class.md +40 -0
  223. package/knowledge/decisions/internals/the-persona-runtime-is-exported-from-the-persona-entry-point.md +28 -0
  224. package/knowledge/decisions/internals/the-transcript-event-is-sent-ahead-of-the-run.md +25 -0
  225. package/knowledge/decisions/internals/the-virtual-user-catalogue-is-the-only-gate-on-what-may-be-called.md +21 -0
  226. package/knowledge/decisions/internals/the-worker-disposition-is-the-one-that-is-not-testing.md +22 -0
  227. package/knowledge/decisions/internals/thread-history-records-the-transcript-not-the-audio.md +25 -0
  228. package/knowledge/decisions/internals/validate-runs-checks-by-precondition.md +115 -0
  229. package/knowledge/decisions/internals/virtual-user-step-order-comes-from-insertion-order.md +21 -0
  230. package/knowledge/decisions/internals/voice-output-speaks-unless-voice-input-explicitly-says-otherwise.md +24 -0
  231. package/knowledge/decisions/internals/wiring-registries-erase-the-generics-their-wire-functions-capture.md +35 -0
  232. package/knowledge/decisions/security/a-dropped-audit-write-is-always-logged.md +4 -2
  233. package/knowledge/decisions/security/a-function-never-receives-the-secret-service.md +37 -0
  234. package/knowledge/decisions/security/a-graph-run-starts-at-an-entry-node-the-graph-declared.md +33 -0
  235. package/knowledge/decisions/security/a-permission-gets-a-wire-it-cannot-reply-on.md +33 -0
  236. package/knowledge/decisions/security/a-step-runs-the-function-the-workflow-dispatched-it-with.md +37 -0
  237. package/knowledge/decisions/security/a-virtual-user-is-never-offered-a-step-that-would-forge-its-own-oracle.md +28 -0
  238. package/knowledge/decisions/security/a-workflow-run-is-read-and-approved-by-its-owner.md +50 -0
  239. package/knowledge/decisions/security/an-agent-approval-is-claimed-before-the-tool-runs.md +33 -0
  240. package/knowledge/decisions/security/an-approval-answer-outlives-the-run-it-answered.md +59 -0
  241. package/knowledge/decisions/security/an-upload-is-counted-as-it-arrives-not-buffered-then-measured.md +24 -0
  242. package/knowledge/decisions/security/index.md +9 -0
  243. package/knowledge/questions/channel-middleware-accepts-bare-factories-that-nothing-resolves.md +35 -0
  244. package/knowledge/questions/index.md +2 -1
  245. package/knowledge/questions/unauthorized-channel-replies-escape-the-declared-out-type.md +44 -0
  246. package/package.json +15 -3
  247. package/scripts/generate-api-report.d.mts +1 -0
  248. package/scripts/generate-api-report.mjs +89 -0
  249. package/scripts/generate-api-report.mts +343 -0
  250. package/src/api-report.test.ts +69 -0
  251. package/src/column-form.ts +1 -4
  252. package/src/crypto-utils.test.ts +15 -2
  253. package/src/dev/hot-reload.ts +2 -7
  254. package/src/ecosystem.ts +40 -0
  255. package/src/errors/error-handler.ts +5 -3
  256. package/src/errors/error.test.ts +4 -1
  257. package/src/errors/index.ts +1 -1
  258. package/src/function/function-runner.test.ts +53 -1
  259. package/src/function/function-runner.ts +33 -48
  260. package/src/function/functions.types.ts +15 -9
  261. package/src/function/index.ts +0 -2
  262. package/src/handle-error.ts +1 -1
  263. package/src/index.ts +3 -57
  264. package/src/middleware/auth-cookie.test.ts +50 -0
  265. package/src/middleware/auth-cookie.ts +38 -2
  266. package/src/middleware/cors.ts +2 -1
  267. package/src/middleware/index.ts +1 -1
  268. package/src/middleware/remote-auth.test.ts +43 -0
  269. package/src/middleware/remote-auth.ts +17 -3
  270. package/src/middleware-runner.ts +4 -2
  271. package/src/no-any-casts.test.ts +57 -0
  272. package/src/permissions.test.ts +3 -1
  273. package/src/permissions.ts +22 -24
  274. package/src/pikku-state.ts +22 -6
  275. package/src/public-surface.json +554 -0
  276. package/src/public-surface.json.README +25 -0
  277. package/src/public-surface.test.ts +105 -0
  278. package/src/removed-legacy-exports.test.ts +62 -0
  279. package/src/schema.test.ts +78 -0
  280. package/src/schema.ts +36 -1
  281. package/src/services/ai-agent-runner-service.ts +12 -1
  282. package/src/services/ai-run-state-service.ts +18 -1
  283. package/src/services/audit-service.ts +2 -2
  284. package/src/services/in-memory-ai-run-state-service.ts +16 -2
  285. package/src/services/in-memory-workflow-service.ts +2 -0
  286. package/src/services/index.ts +4 -47
  287. package/src/services/local-content-request-handler.test.ts +43 -5
  288. package/src/services/local-content-request-handler.ts +103 -74
  289. package/src/services/local-content.ts +15 -1
  290. package/src/services/local-email-service.ts +5 -1
  291. package/src/services/scoped-credential-service.test.ts +86 -0
  292. package/src/services/scoped-credential-service.ts +63 -0
  293. package/src/services/system-role-guard.test.ts +4 -1
  294. package/src/services/workflow-service.ts +9 -2
  295. package/src/side-effects-are-declared.test.ts +84 -0
  296. package/src/source-files-stay-composable.test.ts +41 -0
  297. package/src/testing/service-tests/agent-run-service-tests.ts +98 -0
  298. package/src/testing/service-tests/ai-storage-service-tests.ts +379 -0
  299. package/src/testing/service-tests/channel-store-tests.ts +98 -0
  300. package/src/testing/service-tests/credential-service-tests.ts +143 -0
  301. package/src/testing/service-tests/deployment-service-tests.ts +33 -0
  302. package/src/testing/service-tests/event-hub-store-tests.ts +48 -0
  303. package/src/testing/service-tests/secret-service-tests.ts +105 -0
  304. package/src/testing/service-tests/session-store-tests.ts +62 -0
  305. package/src/testing/service-tests/workflow-run-service-tests.ts +59 -0
  306. package/src/testing/service-tests/workflow-service-tests.ts +308 -0
  307. package/src/testing/service-tests.ts +31 -1111
  308. package/src/types/core.types.ts +14 -3
  309. package/src/types/state.types.ts +17 -1
  310. package/src/wirings/actor-flow/index.ts +0 -3
  311. package/src/wirings/actor-flow/run-conversation.ts +1 -6
  312. package/src/wirings/ai-agent/agent-rpc.ts +120 -0
  313. package/src/wirings/ai-agent/ai-agent-agui.ts +1 -5
  314. package/src/wirings/ai-agent/ai-agent-finalize.test.ts +186 -0
  315. package/src/wirings/ai-agent/ai-agent-finalize.ts +197 -0
  316. package/src/wirings/ai-agent/ai-agent-interrupt.test.ts +2 -1
  317. package/src/wirings/ai-agent/ai-agent-interrupt.ts +1 -0
  318. package/src/wirings/ai-agent/ai-agent-memory.ts +62 -39
  319. package/src/wirings/ai-agent/ai-agent-model-config.test.ts +72 -3
  320. package/src/wirings/ai-agent/ai-agent-model-config.ts +49 -1
  321. package/src/wirings/ai-agent/ai-agent-prepare.ts +12 -12
  322. package/src/wirings/ai-agent/ai-agent-resume-authorization.test.ts +1 -0
  323. package/src/wirings/ai-agent/ai-agent-runner.test.ts +82 -2
  324. package/src/wirings/ai-agent/ai-agent-runner.ts +120 -160
  325. package/src/wirings/ai-agent/ai-agent-stream-output-hooks.test.ts +353 -0
  326. package/src/wirings/ai-agent/ai-agent-stream.test.ts +2 -0
  327. package/src/wirings/ai-agent/ai-agent-stream.ts +152 -117
  328. package/src/wirings/ai-agent/ai-agent-thread-ownership.test.ts +1 -1
  329. package/src/wirings/ai-agent/ai-agent-turn.test.ts +67 -0
  330. package/src/wirings/ai-agent/ai-agent-turn.ts +122 -0
  331. package/src/wirings/ai-agent/ai-agent.types.ts +65 -5
  332. package/src/wirings/ai-agent/index.ts +2 -16
  333. package/src/wirings/ai-agent/voice-input.ts +1 -6
  334. package/src/wirings/ai-agent/voice-output.ts +2 -12
  335. package/src/wirings/ai-scorer/ai-scorer-grade.test.ts +106 -0
  336. package/src/wirings/ai-scorer/ai-scorer-grade.ts +55 -0
  337. package/src/wirings/ai-scorer/ai-scorer-judge.test.ts +143 -0
  338. package/src/wirings/ai-scorer/ai-scorer-judge.ts +120 -0
  339. package/src/wirings/ai-scorer/ai-scorer-live.test.ts +174 -0
  340. package/src/wirings/ai-scorer/ai-scorer-live.ts +56 -0
  341. package/src/wirings/ai-scorer/ai-scorer-registry.ts +63 -0
  342. package/src/wirings/ai-scorer/ai-scorer-sampling.test.ts +34 -0
  343. package/src/wirings/ai-scorer/ai-scorer-sampling.ts +36 -0
  344. package/src/wirings/ai-scorer/ai-scorer-snapshots.test.ts +49 -0
  345. package/src/wirings/ai-scorer/ai-scorer-snapshots.ts +46 -0
  346. package/src/wirings/ai-scorer/ai-scorer-worker.test.ts +122 -0
  347. package/src/wirings/ai-scorer/ai-scorer-worker.ts +69 -0
  348. package/src/wirings/ai-scorer/ai-scorer.ts +76 -0
  349. package/src/wirings/ai-scorer/ai-scorer.types.ts +107 -0
  350. package/src/wirings/ai-scorer/index.ts +24 -0
  351. package/src/wirings/channel/channel-common.ts +5 -2
  352. package/src/wirings/channel/channel-handler-shapes.test.ts +72 -0
  353. package/src/wirings/channel/channel-handler.ts +7 -7
  354. package/src/wirings/channel/channel-rpc-service.ts +0 -14
  355. package/src/wirings/channel/channel-rpc.test.ts +25 -4
  356. package/src/wirings/channel/channel-rpc.types.ts +14 -0
  357. package/src/wirings/channel/channel-runner.ts +33 -20
  358. package/src/wirings/channel/channel.types.ts +2 -0
  359. package/src/wirings/channel/index.ts +1 -20
  360. package/src/wirings/channel/local/local-channel-runner.test.ts +68 -0
  361. package/src/wirings/channel/local/local-channel-runner.ts +9 -2
  362. package/src/wirings/channel/pikku-abstract-channel-handler.ts +2 -1
  363. package/src/wirings/channel/serverless/serverless-channel-runner.ts +6 -3
  364. package/src/wirings/cli/channel/cli-channel-runner.ts +3 -1
  365. package/src/wirings/cli/channel/cli-raw-channel-runner.test.ts +23 -0
  366. package/src/wirings/cli/channel/cli-raw-channel-runner.ts +12 -1
  367. package/src/wirings/cli/channel/cli-raw-client-runner.ts +4 -1
  368. package/src/wirings/cli/channel/index.ts +0 -7
  369. package/src/wirings/cli/cli-runner.test.ts +68 -0
  370. package/src/wirings/cli/cli-runner.ts +25 -5
  371. package/src/wirings/cli/cli.types.ts +0 -39
  372. package/src/wirings/cli/command-parser.test.ts +19 -0
  373. package/src/wirings/cli/command-parser.ts +17 -1
  374. package/src/wirings/credential/index.ts +0 -1
  375. package/src/wirings/gateway/gateway-channel-meta.test.ts +44 -0
  376. package/src/wirings/gateway/gateway-runner.ts +27 -19
  377. package/src/wirings/gateway/index.ts +0 -3
  378. package/src/wirings/http/http-routes.ts +4 -1
  379. package/src/wirings/http/http-runner.test.ts +66 -0
  380. package/src/wirings/http/http-runner.ts +21 -28
  381. package/src/wirings/http/http.types.ts +0 -15
  382. package/src/wirings/http/index.ts +2 -8
  383. package/src/wirings/http/pikku-fetch-http-request.ts +2 -2
  384. package/src/wirings/http/web-request.ts +1 -1
  385. package/src/wirings/mcp/index.ts +0 -1
  386. package/src/wirings/mcp/mcp-runner.test.ts +181 -0
  387. package/src/wirings/mcp/mcp-runner.ts +37 -21
  388. package/src/wirings/persona/index.ts +0 -8
  389. package/src/wirings/persona/persona-environments.test.ts +14 -3
  390. package/src/wirings/persona/persona.test.ts +13 -3
  391. package/src/wirings/persona/validate-personas.ts +5 -1
  392. package/src/wirings/queue/index.ts +0 -14
  393. package/src/wirings/queue/queue-runner.ts +1 -1
  394. package/src/wirings/rpc/addon-auth-tags.test.ts +1 -5
  395. package/src/wirings/rpc/addon-runner.ts +34 -3
  396. package/src/wirings/rpc/addon-secrets.test.ts +261 -0
  397. package/src/wirings/rpc/rpc-runner.test.ts +2 -0
  398. package/src/wirings/rpc/rpc-runner.ts +60 -136
  399. package/src/wirings/rpc/rpc-types.ts +11 -0
  400. package/src/wirings/rpc/wire-addon.ts +20 -1
  401. package/src/wirings/scheduler/index.ts +0 -1
  402. package/src/wirings/secret/validate-secret-definitions.test.ts +22 -0
  403. package/src/wirings/secret/validate-secret-definitions.ts +2 -2
  404. package/src/wirings/trigger/index.ts +0 -1
  405. package/src/wirings/trigger/pikku-trigger-service.ts +0 -5
  406. package/src/wirings/trigger/trigger-runner.ts +7 -5
  407. package/src/wirings/virtual-user/index.ts +0 -16
  408. package/src/wirings/virtual-user/run-virtual-user.test.ts +18 -9
  409. package/src/wirings/virtual-user/run-virtual-user.ts +28 -15
  410. package/src/wirings/virtual-user/virtual-user-agents.test.ts +4 -1
  411. package/src/wirings/virtual-user/virtual-user-derive.ts +4 -25
  412. package/src/wirings/virtual-user/virtual-user-dispositions.test.ts +7 -2
  413. package/src/wirings/virtual-user/virtual-user-dispositions.ts +1 -4
  414. package/src/wirings/virtual-user/virtual-user-intents.test.ts +13 -3
  415. package/src/wirings/workflow/dsl/workflow-dsl.types.ts +117 -4
  416. package/src/wirings/workflow/feature.ts +7 -4
  417. package/src/wirings/workflow/graph/graph-node.ts +1 -1
  418. package/src/wirings/workflow/graph/graph-runner.test.ts +76 -2
  419. package/src/wirings/workflow/graph/graph-runner.ts +13 -16
  420. package/src/wirings/workflow/graph/workflow-graph.types.ts +0 -5
  421. package/src/wirings/workflow/index.ts +14 -24
  422. package/src/wirings/workflow/pikku-scenario-service.ts +101 -27
  423. package/src/wirings/workflow/pikku-workflow-service.test.ts +15 -14
  424. package/src/wirings/workflow/pikku-workflow-service.ts +214 -743
  425. package/src/wirings/workflow/scenario-expectations.test.ts +75 -0
  426. package/src/wirings/workflow/scenario-hooks.test.ts +52 -0
  427. package/src/wirings/workflow/scenario-step.types.ts +8 -0
  428. package/src/wirings/workflow/workflow-approval-audit.ts +47 -0
  429. package/src/wirings/workflow/workflow-approval-policy.test.ts +524 -0
  430. package/src/wirings/workflow/workflow-approval-policy.ts +68 -0
  431. package/src/wirings/workflow/workflow-approval.ts +289 -0
  432. package/src/wirings/workflow/workflow-child-run-session.test.ts +89 -0
  433. package/src/wirings/workflow/workflow-constants.ts +47 -0
  434. package/src/wirings/workflow/workflow-dispatch-durability.test.ts +1 -1
  435. package/src/wirings/workflow/workflow-errors.ts +128 -0
  436. package/src/wirings/workflow/workflow-inline-authority.test.ts +12 -6
  437. package/src/wirings/workflow/workflow-meta-resolver.ts +45 -0
  438. package/src/wirings/workflow/workflow-missing-meta.test.ts +92 -0
  439. package/src/wirings/workflow/workflow-queue-routing.ts +85 -0
  440. package/src/wirings/workflow/workflow-queue-wiring.ts +149 -0
  441. package/src/wirings/workflow/workflow-recovery.ts +162 -0
  442. package/src/wirings/workflow/workflow-retry-policy.test.ts +1 -1
  443. package/src/wirings/workflow/workflow-run-authority.test.ts +212 -0
  444. package/src/wirings/workflow/workflow-run-engine.types.ts +91 -0
  445. package/src/wirings/workflow/workflow-run-ownership.ts +37 -0
  446. package/src/wirings/workflow/workflow-suspend.ts +61 -0
  447. package/src/wirings/workflow/workflow.types.ts +7 -9
  448. package/src/wirings-stay-decoupled.test.ts +127 -0
  449. package/tsconfig.json +1 -1
  450. package/tsconfig.tsbuildinfo +1 -1
  451. package/dist/internal.d.ts +0 -3
  452. package/dist/internal.js +0 -2
  453. package/dist/middleware/timeout.d.ts +0 -9
  454. package/dist/middleware/timeout.js +0 -15
  455. package/dist/pikku-response.d.ts +0 -6
  456. package/dist/pikku-response.js +0 -6
  457. package/dist/services/gopass-secrets.d.ts +0 -15
  458. package/dist/services/gopass-secrets.js +0 -76
  459. package/dist/services/http-scenario-actors.d.ts +0 -75
  460. package/dist/services/http-scenario-actors.js +0 -195
  461. package/dist/services/http-user-flow-actors.d.ts +0 -67
  462. package/dist/services/http-user-flow-actors.js +0 -193
  463. package/dist/services/scenario-actors-service.d.ts +0 -127
  464. package/dist/services/scenario-actors-service.js +0 -40
  465. package/dist/services/user-flow-actors-service.d.ts +0 -39
  466. package/dist/wirings/credential/wire-credential.d.ts +0 -48
  467. package/dist/wirings/credential/wire-credential.js +0 -47
  468. package/dist/wirings/oauth2/oauth2-client.d.ts +0 -47
  469. package/dist/wirings/oauth2/oauth2-client.js +0 -263
  470. package/dist/wirings/oauth2/oauth2-routes.d.ts +0 -35
  471. package/dist/wirings/oauth2/oauth2-routes.js +0 -146
  472. package/dist/wirings/scope/wire-scope.d.ts +0 -33
  473. package/dist/wirings/scope/wire-scope.js +0 -32
  474. package/dist/wirings/workflow/dsl/index.d.ts +0 -5
  475. package/dist/wirings/workflow/dsl/index.js +0 -4
  476. package/dist/wirings/workflow/graph/index.d.ts +0 -5
  477. package/dist/wirings/workflow/graph/index.js +0 -4
  478. package/src/internal.ts +0 -10
  479. /package/dist/{services/user-flow-actors-service.js → wirings/workflow/workflow-run-engine.types.js} +0 -0
@@ -13,8 +13,10 @@ Its `write` discards the event — but before it does, it warns once, and it loo
13
13
  for a logger in two places: the wire's own, then the singleton passed to the
14
14
  constructor.
15
15
 
16
- The fallback is the point. Wires frequently do not carry a logger, and with only
17
- `this.wire.logger` the warning would itself be dropped whenever the wire is bare.
16
+ The fallback is the point. `wire.logger` is an optional hook for a host that
17
+ wants invocation-scoped logging core never sets it, so on every path core
18
+ itself drives, the singleton is the only source there is. With only
19
+ `this.wire.logger` the warning would be dropped every time.
18
20
  An audit call that silently does nothing is the worst available outcome: the
19
21
  function believes it is producing an audit trail, the trail does not exist, and
20
22
  nothing anywhere says so. The warning names the function and the fix, and fires
@@ -0,0 +1,37 @@
1
+ ---
2
+ type: decision
3
+ title: A function never receives the secret service
4
+ description: Every function-, permission- and auth-facing services type is bounded by SecretlessServices, so reaching for `secrets` in a function body is a type error rather than a lint
5
+ tags: services
6
+ ---
7
+
8
+ # A function never receives the secret service
9
+
10
+ `SecretlessServices<Services>` is `Omit<Services, 'secrets'>`
11
+ (`packages/core/src/types/core.types.ts`), and
12
+ `CoreSecretlessSingletonServices` built from it is the constraint every
13
+ function-, permission- and auth-facing type is bounded by
14
+ (`packages/core/src/function/functions.types.ts`). Destructuring `secrets`
15
+ inside a `pikkuFunc` body does not lint — it does not compile.
16
+
17
+ The rule itself is older than the type: a function holding a `SecretService` can
18
+ read every secret in the vault, which makes its blast radius the whole vault
19
+ rather than the one credential it needs, and makes "which secrets does this
20
+ function depend on?" unanswerable. `[PKU950]` enforces the same confinement for
21
+ a `SecretService` reaching a function under an alias, because a rename does not
22
+ change what it is. Encoding it in the type is what makes the honest mistake
23
+ impossible rather than merely reported: secrets are resolved where things are
24
+ constructed — `pikkuServices`, `pikkuWireServices`, addon service factories,
25
+ middleware — and the function is handed the configured client.
26
+
27
+ The cost is that a function which needs to *ask about* a secret rather than read
28
+ one — "is this key set?", for a readiness or provisioning check — cannot do it
29
+ directly either. It goes through a service that holds `secrets` and exposes only
30
+ that question, which is how `@pikku/addon-console` checks whether an installed
31
+ addon's declared secrets are present.
32
+
33
+ **What this rules out:** widening a function's services type back to
34
+ `CoreServices` for a function that "only needs one secret", and passing the
35
+ secret service through under another name — the type follows the shape and
36
+ `[PKU950]` follows the type. It also rules out treating the absence as an
37
+ oversight to be patched with a cast.
@@ -0,0 +1,33 @@
1
+ ---
2
+ type: decision
3
+ title: A graph run starts at an entry node the graph declared
4
+ description: startNode may only name a node in meta.entryNodeIds, and no generated route offers it, because otherwise a caller picks which half of the graph to skip
5
+ tags: workflow
6
+ ---
7
+
8
+ # A graph run starts at an entry node the graph declared
9
+
10
+ `runWorkflowGraph` in
11
+ `packages/core/src/wirings/workflow/graph/graph-runner.ts` takes an optional
12
+ `startNode`. It used to accept any node id the graph contained, validated only
13
+ by `validateGraphReferences` (does the node exist) and `areDependenciesSatisfied`
14
+ (does its input reference another node). A node whose input is a literal or comes
15
+ from the trigger passes both — including a node that is only ever reached after a
16
+ validation, payment or approval node. Choosing it as the start does not skip the
17
+ gate so much as never reach it.
18
+
19
+ `startNode` is now checked against `meta.entryNodeIds`, the set the graph itself
20
+ declared, and the generated `POST /workflow/:workflowName/graph/:nodeId` route
21
+ that offered it to HTTP callers is gone.
22
+
23
+ The parameter stays, because it has a real internal user: `PikkuTriggerService`
24
+ (`packages/core/src/wirings/trigger/pikku-trigger-service.ts`) passes a target's
25
+ `startNode` when a trigger fires. That caller names a node the graph declared, so
26
+ the restriction costs it nothing.
27
+
28
+ **What this rules out:** re-exposing entry-node choice on a generated route, and
29
+ "validating" a `startNode` by existence or by dependency satisfaction — neither
30
+ says the graph meant that node to be an entry point. It does not rule out a graph
31
+ declaring several entry nodes; that is exactly how a graph says which starts are
32
+ legitimate. If a graph needs a start that is not an entry node, the answer is to
33
+ declare it as one, not to widen the check.
@@ -0,0 +1,33 @@
1
+ ---
2
+ type: decision
3
+ title: A permission gets a wire it cannot reply on
4
+ description: The permission wire is typed with Out = never so a permission cannot send on the channel; that narrowing is not a subtype, so the call site asserts
5
+ tags: core, permissions
6
+ ---
7
+
8
+ # A permission gets a wire it cannot reply on
9
+
10
+ `CorePikkuPermission` in `packages/core/src/function/functions.types.ts` types
11
+ its wire as `PikkuWire<In, never, false, any, PikkuRPC, never, never>`. The
12
+ second parameter is `Out`, and `never` there is deliberate: it makes
13
+ `wire.channel` a `PikkuChannel<unknown, never, …>`, whose `send` accepts
14
+ nothing. A permission is a gate — it answers `true` or `false` — and must not
15
+ be able to write a reply to the caller it is deciding about. A permission that
16
+ could `send` would be able to leak the very data the gate exists to withhold,
17
+ and it would do so before the function it guards has run.
18
+
19
+ `runPikkuFunc` holds an ordinary `PikkuWire`, whose default `Out` is `unknown`.
20
+ `unknown` is not assignable to `never`, and `send` is contravariant in its
21
+ argument, so the narrowing cannot be expressed as a subtype relation — the call
22
+ into `runPermissions` asserts to the exact permission-wire type rather than
23
+ relying on assignability.
24
+
25
+ **What this rules out:** widening `Out` on the permission wire to `unknown` to
26
+ delete the assertion. That silently hands every permission function a working
27
+ `channel.send`. It equally rules out replacing the assertion with `as any`,
28
+ which erases the target type and hides the fact that a specific, intentional
29
+ narrowing is happening.
30
+
31
+ The same reasoning explains the `never` in the fifth and sixth positions
32
+ (`MCPTools`, `IsChannel`): a permission is not an MCP tool host and does not own
33
+ the channel lifecycle.
@@ -0,0 +1,37 @@
1
+ ---
2
+ type: decision
3
+ title: A step runs the function the workflow dispatched it with
4
+ description: StepState records the step's function name so the worker can reject a queue message naming a different one, because the step executes under the run owner's identity
5
+ tags: workflow
6
+ ---
7
+
8
+ # A step runs the function the workflow dispatched it with
9
+
10
+ `pikkuWorkflowStepWorker` (`workflow-queue-workers.ts`) takes `rpcName` straight
11
+ off the queue message and hands it to `executeWorkflowStep`. That call runs as
12
+ the run's owner — `invokeStepRpc` copies `run.wire.pikkuUserId` onto the wire —
13
+ and `rpcWithWire` does not apply the `expose` gate that the public `/rpc` route
14
+ applies. So the message decided both *what* ran and *as whom*.
15
+
16
+ The claim in `executeWorkflowStepInner` read the step's status and nothing else;
17
+ there was no stored function name to compare against. `StepState` now carries
18
+ `rpcName`, written by `insertStepState` (which already received it) and returned
19
+ by every backend, and the claim rejects a message naming anything else with
20
+ `WorkflowStepFunctionMismatchError` — before any status is mutated, so a forged
21
+ message leaves the run untouched. The graph path takes the same value from
22
+ `nodes[nodeId].rpcName` rather than from the message.
23
+
24
+ `rpcName: undefined` means a store that never recorded one and cannot be
25
+ compared; `null` is a step with no function of its own. Only a recorded value is
26
+ checked.
27
+
28
+ **Reachability, stated plainly:** the step-worker queue is not reachable from the
29
+ public `/rpc/:rpcName` route — the worker is registered without `expose`. The
30
+ exposure this closes is write access to the queue backend, and any in-process
31
+ caller reaching `executeWorkflowStep` directly.
32
+
33
+ **What this rules out:** trusting a queue payload to name the function it
34
+ executes, here or in any future worker, and dropping `rpcName` from `StepState`
35
+ as redundant with the step data — the comparison is the only thing standing
36
+ between queue-write access and running any registered function as the run's
37
+ owner.
@@ -0,0 +1,28 @@
1
+ ---
2
+ type: decision
3
+ title: A virtual user is never offered a scenario, platform or addon step
4
+ description: Being able to invoke "the webhook arrives" lets the user manufacture the outcome it exists to discover, which invalidates every finding downstream
5
+ tags: core, virtual-user
6
+ ---
7
+
8
+ # A virtual user is never offered a scenario, platform or addon step
9
+
10
+ `deriveVirtualUserCatalogue` excludes scenario bodies and their steps, and
11
+ platform and addon steps, from what a virtual user may call.
12
+
13
+ For scenario bodies the argument is only efficiency: they are held out of every
14
+ deployed unit and are not network-callable, so offering one wastes a turn on a
15
+ 404.
16
+
17
+ For a platform or addon step the argument is the oracle itself. A virtual user's
18
+ findings are worth something *because* it cannot manufacture the outcomes it is
19
+ meant to discover. A user that could invoke "Stripe's webhook arrives" forges its
20
+ own payment success, and every finding downstream of that forgery is worthless —
21
+ not merely unreliable, but actively misleading, because it looks like evidence.
22
+
23
+ This is the same class of argument as `allowApprovalRequired` defaulting to
24
+ false, and it is enforced here at derivation rather than left to convention.
25
+
26
+ **What this rules out:** exposing platform steps behind a flag "for
27
+ convenience", or filtering them later in the run loop where a caller could skip
28
+ the filter.
@@ -0,0 +1,50 @@
1
+ ---
2
+ type: decision
3
+ title: A workflow run is read by its owner, and answered by whoever its gate declares
4
+ description: A run started through a session records that user and only that user may read it; who may answer an approval gate is the gate's own declaration, and a run with no recorded owner has no ownership to enforce
5
+ tags: workflow
6
+ ---
7
+
8
+ # A workflow run is read by its owner, and answered by whoever its gate declares
9
+
10
+ `WorkflowRunWire.pikkuUserId` has always been recorded on every run started
11
+ through a session — `RPCService.startWorkflow` copies it off the wire — and
12
+ nothing read it back. Run ids were the only secret protecting both the status
13
+ routes (which stream `output` and `error`) and `approveStep`, which took no
14
+ session at all and rejected only an already-resolved gate.
15
+
16
+ `assertWorkflowRunOwner`
17
+ (`packages/core/src/wirings/workflow/workflow-run-ownership.ts`) is the check the
18
+ **read** paths share: the generated status routes assert it against the run they
19
+ were already reading.
20
+
21
+ **A run with no recorded owner is not gated.** Triggers, schedulers and routes
22
+ wired without auth start runs with no `pikkuUserId`; there is nobody to compare a
23
+ caller against, and inventing one would reject the framework's own callers rather
24
+ than secure anything. Gate those at the entrypoint with `auth` or `permissions`.
25
+
26
+ This is ownership, not an approver model. It answers "is this your run", not "are
27
+ you entitled to approve this particular gate".
28
+
29
+ ## Approving is a separate question, and the gate answers it
30
+
31
+ `approveStep` originally shared `assertWorkflowRunOwner`, which made "only the
32
+ initiator may answer" the one available rule. That is right for "confirm your own
33
+ action" and exactly wrong for four-eyes sign-off, where the initiator is the one
34
+ person who must not sign. Which applies is a property of the decision, so it is
35
+ declared on the gate — `approvers` (`'any' | 'owner' | 'not-initiator'`) and
36
+ `approverScope` on `WorkflowApprovalOptions`.
37
+
38
+ It is enforced wherever the policy is known. Reaching the gate publishes the
39
+ policy into the run state, so a decision submitted after that is judged by the
40
+ approve entrypoint and refused with a 403. A decision can legitimately be
41
+ recorded before the run has reached the gate, and that one has no policy to be
42
+ judged against yet — it is judged on replay instead, alongside payload
43
+ validation, and discarded if it fails.
44
+
45
+ The default is therefore `any`: a gate is a pause for a decision, not an
46
+ authorization boundary, and a route that needs one has `auth`/`permissions`.
47
+
48
+ **What this rules out:** treating a run id as a capability, adding a new run read
49
+ path that does not take a session, and re-deriving who may approve from who
50
+ started the run.
@@ -0,0 +1,33 @@
1
+ ---
2
+ type: decision
3
+ title: An agent approval is claimed before the tool runs
4
+ description: resolveApproval is a compare-and-swap returning whether this caller won, because the read that precedes it is not a claim and ten concurrent approvals would otherwise mean ten refunds
5
+ tags: ai-agent
6
+ ---
7
+
8
+ # An agent approval is claimed before the tool runs
9
+
10
+ Resuming a suspended agent run read the run, checked `status === 'suspended'`,
11
+ snapshotted `pendingApprovals`, called `resolveApproval`, executed the tool, and
12
+ only then wrote `status: 'running'`. Nothing spanned that sequence — no
13
+ transaction, no row lock, no conditional update — so concurrent approvals of the
14
+ same tool call all observed `suspended`, all snapshotted the same list, and all
15
+ reached `execute`. `resolveApproval` returned `void`, so a loser could not even
16
+ tell.
17
+
18
+ `resolveApproval` is now the claim, and returns whether *this* caller made it.
19
+ The stores implement it as a compare-and-swap: Kysely updates the run row only
20
+ while `status = 'suspended'` and `pendingApprovals` still equals the list it
21
+ read; the tool-call stores move the row off `approvalStatus = 'pending'` and
22
+ count the rows they changed. Both resume paths run the tool only for the ids they
23
+ claimed, and a caller that claimed nothing gets an error rather than a silent
24
+ re-run.
25
+
26
+ The claim is per tool call, not per run, so concurrent approvals of *different*
27
+ tool calls on one run all proceed — which is the case that made a run-level
28
+ `claimSuspendedRun` the worse fit.
29
+
30
+ **What this rules out:** treating `getRun` as a claim, adding a resume path that
31
+ executes a tool without a `true` from `resolveApproval`, and implementing
32
+ `resolveApproval` as a read-modify-write in any new store — the return value is
33
+ a promise about atomicity, not a convenience.
@@ -0,0 +1,59 @@
1
+ ---
2
+ type: decision
3
+ title: An approval answer outlives the run it answered
4
+ description: Run state holds a decision only while the gate is open, so the settled answer carries decidedBy/decidedAt into the step result and every attempt is written to the audit sink, which has no foreign key to the run
5
+ tags: workflow, audit
6
+ ---
7
+
8
+ # An approval answer outlives the run it answered
9
+
10
+ An approval gate is asked for so it can be answered for afterwards. "Who
11
+ released the funds" is the entire point of four-eyes sign-off, and until this
12
+ change none of it was kept.
13
+
14
+ Run state is where a decision *waits*, not where it is kept. The record under
15
+ `workflowRuns.state.__approval_<hex>` is overwritten by the next write to that
16
+ key, and cleared outright — `decidedBy: undefined` included — whenever a
17
+ decision is refused on replay. A trail that erases exactly the events worth
18
+ keeping is not a trail.
19
+
20
+ So the answer is recorded twice, in two places with different lifetimes.
21
+
22
+ ## The settled decision keeps its provenance
23
+
24
+ `ApprovalOutcome<T>` carries `decidedBy` and `decidedAt` alongside `data`, so
25
+ the answer reaches `workflowStep.result` and, through it,
26
+ `workflowStepHistory` — append-only, and already the per-step event log. Both
27
+ are spread in only when present, so a gate answered without a session keeps the
28
+ shape it had before there was anything to record.
29
+
30
+ ## The audit sink is what survives deletion
31
+
32
+ `deleteRun` cascades: `workflowStep` is `onDelete('cascade')` from
33
+ `workflowRuns`, and `workflowStepHistory` from `workflowStep`. Deleting a run
34
+ therefore deletes the sign-off with it — and a *refused* attempt never reaches a
35
+ step at all, so it was never in that trail to begin with.
36
+
37
+ Every answer — accepted, refused at submission, or cleared on replay — is
38
+ written to the `AuditService` as `workflow.approval.decided`, with
39
+ `outcome: 'success' | 'denied'`, the decider under `userIdentity.pikkuUserId`,
40
+ and the run, reason, scopes and refusal in `metadata`. The `audit` table holds
41
+ no foreign key to any workflow table, which is precisely why it is the right
42
+ home.
43
+
44
+ `auditApprovalDecision`
45
+ (`packages/core/src/wirings/workflow/workflow-approval-audit.ts`) is a module of
46
+ its own rather than a method on `PikkuWorkflowService`: the trail is a separate
47
+ concern from running the workflow, and the service is already at the 2000-line
48
+ limit `source-files-stay-composable.test.ts` enforces.
49
+
50
+ **A sink that fails is logged, never thrown.** The trail must not be the reason a
51
+ decision is lost. A project with no `audit` service wired records nothing and is
52
+ otherwise unaffected — the sink is opt-in, and `auditSchema` is deliberately not
53
+ in `pikkuSchemas`.
54
+
55
+ **What this rules out:** a dedicated `workflowApprovals` table. It would have to
56
+ be implemented in all seven backends and would still be deleted with the run
57
+ unless it deliberately broke the cascade — at which point it is the audit table
58
+ with extra steps. If approvals later need to be *queried* as a first-class
59
+ entity rather than read back from the trail, that is when to revisit.
@@ -0,0 +1,24 @@
1
+ ---
2
+ type: decision
3
+ title: An upload is counted as it arrives, not buffered and then measured
4
+ description: Reading the whole body before checking its size hands an unauthenticated caller a way to spend the server's memory
5
+ tags: core, content
6
+ ---
7
+
8
+ # An upload is counted as it arrives, not buffered and then measured
9
+
10
+ The local content request handler accumulates the request body chunk by chunk,
11
+ tracking the running total, and abandons the read the moment it crosses the
12
+ limit. An oversized upload therefore costs the limit, not its own size.
13
+
14
+ The obvious alternative — `await request.arrayBuffer()` and then check
15
+ `byteLength` — has to hold the entire body in memory before it can decide the
16
+ body is too large. For an endpoint reachable before authentication that is a
17
+ way to spend the server's memory for the price of one request, and no size limit
18
+ configured anywhere prevents it.
19
+
20
+ `readRequestBody` in the node HTTP server aborts on the same terms, for the same
21
+ reason.
22
+
23
+ **What this rules out:** replacing the streaming accumulation with a single
24
+ buffered read because the limit check "still happens".
@@ -10,6 +10,12 @@ A rule about who may do what, and which way it fails when it is unsure.
10
10
 
11
11
  <!-- pikku:knowledge-index -->
12
12
  - [A dropped audit write is always logged](a-dropped-audit-write-is-always-logged.md) — The no-op audit service falls back to the singleton logger when the wire carries none, so an unconfigured audit call is never silent
13
+ - [A function never receives the secret service](a-function-never-receives-the-secret-service.md) — Every function-, permission- and auth-facing services type is bounded by SecretlessServices, so reaching for `secrets` in a function body is a type error rather than a lint
14
+ - [A graph run starts at an entry node the graph declared](a-graph-run-starts-at-an-entry-node-the-graph-declared.md) — startNode may only name a node in meta.entryNodeIds, and no generated route offers it, because otherwise a caller picks which half of the graph to skip
15
+ - [A permission gets a wire it cannot reply on](a-permission-gets-a-wire-it-cannot-reply-on.md) — The permission wire is typed with Out = never so a permission cannot send on the channel; that narrowing is not a subtype, so the call site asserts
16
+ - [A step runs the function the workflow dispatched it with](a-step-runs-the-function-the-workflow-dispatched-it-with.md) — StepState records the step's function name so the worker can reject a queue message naming a different one, because the step executes under the run owner's identity
17
+ - [A virtual user is never offered a scenario, platform or addon step](a-virtual-user-is-never-offered-a-step-that-would-forge-its-own-oracle.md) — Being able to invoke "the webhook arrives" lets the user manufacture the outcome it exists to discover, which invalidates every finding downstream
18
+ - [A workflow run is read by its owner, and answered by whoever its gate declares](a-workflow-run-is-read-and-approved-by-its-owner.md) — A run started through a session records that user and only that user may read it; who may answer an approval gate is the gate's own declaration, and a run with no recorded owner has no ownership to enforce
13
19
  - [An actor's missing approval decision defaults to denied](actor-flow-missing-approval-decisions-default-to-denied.md) — Every pending tool call gets an explicit decision; an id the persona LLM omitted is denied, so a dropped field can never read as consent
14
20
  - [Actor sign-in is proven by Set-Cookie, not a non-empty jar](actor-sign-in-is-proven-by-set-cookie-not-a-non-empty-jar.md) — HttpScenarioActor tracks its own signedIn flag and requires the sign-in response itself to set a cookie, because a populated jar proves nothing
15
21
  - [Actor sign-in only works for actor-flagged users](actor-sign-in-only-works-for-actor-flagged-users.md) — The scenario actor secret mints sessions for user rows flagged actor and nothing else, so holding it never impersonates a real user
@@ -24,8 +30,11 @@ A rule about who may do what, and which way it fails when it is unsure.
24
30
  - [Agent thread ownership fails closed when there is no principal](ai-agent-sessionless-deployments-have-no-thread-ownership.md) — A sessionless caller gets an ephemeral owner and reaches no stored thread, rather than reaching all of them
25
31
  - [An agent thread key is always prefixed with the trusted principal](ai-agent-thread-ownership-composes-the-session-principal.md) — Ownership keys are composed as principal:resourceId, so a client id can sub-divide its own boundary but never widen it
26
32
  - [Agent tool permission filtering reads the live function config, not the metadata](ai-agent-tool-filtering-reads-the-live-function-config.md) — The pikkuAuth brand survives only on live permission objects, so a metadata-driven check would silently admit every gated tool
33
+ - [An agent approval is claimed before the tool runs](an-agent-approval-is-claimed-before-the-tool-runs.md) — resolveApproval is a compare-and-swap returning whether this caller won, because the read that precedes it is not a claim and ten concurrent approvals would otherwise mean ten refunds
34
+ - [An approval answer outlives the run it answered](an-approval-answer-outlives-the-run-it-answered.md) — Run state holds a decision only while the gate is open, so the settled answer carries decidedBy/decidedAt into the step result and every attempt is written to the audit sink, which has no foreign key to the run
27
35
  - [An empty owners constraint matches nothing](an-empty-owners-constraint-matches-nothing.md) — owners is an authorization boundary, so every storage backend must treat [] as no rows rather than no filter
28
36
  - [An exposed function with no gate is reported at codegen, not at boot](an-exposed-ungated-function-is-a-codegen-warning.md) — The check runs in the inspector where function meta and every wireAddon declaration are both in hand, because neither source alone can tell a gated function from an ungated one
37
+ - [An upload is counted as it arrives, not buffered and then measured](an-upload-is-counted-as-it-arrives-not-buffered-then-measured.md) — Reading the whole body before checking its size hands an unauthenticated caller a way to spend the server's memory
29
38
  - [The console addon's privileged functions gate themselves](console-addon-privileged-functions-gate-themselves.md) — Thread listing is owner-scoped unless the caller holds admin, and addon installation requires an admin session, rather than trusting the host to register a global permission
30
39
  - [Core's SSRF guard matches host literals because edge runtimes have no DNS](core-safe-fetch-blocks-ssrf-by-host-literal-not-dns.md) — safeFetch rejects internal address literals and re-validates every redirect hop; it cannot stop DNS rebinding
31
40
  - [Core secrets are encrypted with a per-secret DEK wrapped by a KEK](core-secrets-use-a-per-secret-dek-wrapped-by-a-kek.md) — Envelope encryption keeps ciphertext untouched during key rotation, at the cost of storing two blobs per secret
@@ -0,0 +1,35 @@
1
+ ---
2
+ type: question
3
+ title: A channel's middleware list accepts bare factories that nothing ever resolves
4
+ description: CoreChannel.channelMiddleware admits CorePikkuChannelMiddlewareFactory, but no runner calls one, so a bare factory would stall the chain
5
+ tags: core, channel
6
+ ---
7
+
8
+ # A channel's middleware list accepts bare factories that nothing resolves
9
+
10
+ `CoreChannel.channelMiddleware` is typed as
11
+ `(CorePikkuChannelMiddleware | CorePikkuChannelMiddlewareFactory)[]`, but
12
+ `runPikkuFunc`'s `wireChannelMiddleware` parameter admits only
13
+ `CorePikkuChannelMiddleware[]`. The two call sites that bridge them —
14
+ `channel-common.ts` and `channel-handler.ts` — assert across the gap.
15
+
16
+ A factory is `(input: In) => CorePikkuChannelMiddleware`: the author is meant to
17
+ *call* it at wiring time and put the result in the array.
18
+ `combineChannelMiddleware` contains no factory-resolution branch, and neither
19
+ does `combineMiddleware` for ordinary middleware — that is consistent and
20
+ deliberate. So a bare, uncalled factory in `channelMiddleware` would be pushed
21
+ into the chain and then invoked as if it were middleware: it would receive
22
+ `(services, channel, next)` as its single `input` argument, return a middleware
23
+ function that nothing runs, and never call `next` — stalling the chain silently.
24
+
25
+ Nothing in this repository puts a bare factory there, so the failure is
26
+ hypothetical today. The type is what invites it.
27
+
28
+ **What would settle it:** deciding whether `channelMiddleware` should accept
29
+ only resolved middleware — in which case the union is simply wrong and should
30
+ lose its factory arm, and both assertions disappear — or whether the runner
31
+ should resolve factories, in which case `combineChannelMiddleware` needs a
32
+ branch and the ordinary middleware path probably needs the matching one.
33
+
34
+ Until then the assertions name the exact target type rather than `as any`, so
35
+ the gap is visible at both call sites.
@@ -11,5 +11,6 @@ currently makes a choice that nobody has defended — the choice stays, the doub
11
11
  gets recorded.
12
12
 
13
13
  <!-- pikku:knowledge-index -->
14
- _Nothing here yet._
14
+ - [A channel's middleware list accepts bare factories that nothing ever resolves](channel-middleware-accepts-bare-factories-that-nothing-resolves.md) — CoreChannel.channelMiddleware admits CorePikkuChannelMiddlewareFactory, but no runner calls one, so a bare factory would stall the chain
15
+ - [An unauthorized channel reply is sent outside the channel's declared Out type](unauthorized-channel-replies-escape-the-declared-out-type.md) — processMessageHandlers sends a bare string on auth failure, which no generated client type admits — nobody has decided what the typed shape should be
15
16
  <!-- /pikku:knowledge-index -->
@@ -0,0 +1,44 @@
1
+ ---
2
+ type: question
3
+ title: An unauthorized channel reply is sent outside the channel's declared Out type
4
+ description: processMessageHandlers sends a bare string on auth failure, which no generated client type admits — nobody has decided what the typed shape should be
5
+ tags: core, channel
6
+ ---
7
+
8
+ # An unauthorized channel reply is sent outside the channel's declared Out type
9
+
10
+ When a message arrives on a channel route that requires a session and none is
11
+ attached, `processMessageHandlers` in
12
+ `packages/core/src/wirings/channel/channel-handler.ts` logs the failure and then
13
+ sends the client a bare string:
14
+
15
+ ```ts
16
+ channelHandler.getChannel().send(`Unauthorized for ${routeMessage}`)
17
+ ```
18
+
19
+ That compiles only because `processMessageHandlers` takes
20
+ `PikkuChannelHandler`, whose generics default to `<unknown, unknown>`, so `send`
21
+ accepts anything at this call site. The channel the application actually
22
+ declared has a concrete `Out`, and the generated client is typed from it. A
23
+ client can therefore receive a value its own types say is impossible, and no
24
+ `@pikku/client-websocket` consumer has a branch for it.
25
+
26
+ The alternatives were never worked through:
27
+
28
+ - **Widen every channel's `Out`** to `Out | ChannelError`, so the error is part
29
+ of the contract. Honest, but it forces a discriminated union on every consumer
30
+ including channels that can never fail auth.
31
+ - **Send on a reserved envelope** the way `channel-rpc` does, and let the client
32
+ library surface it out-of-band rather than as a message. Keeps `Out` clean,
33
+ but adds a second framing that every runtime adapter has to honour.
34
+ - **Send nothing** and let the close code carry it. Simplest, and loses the
35
+ route name that makes the failure debuggable.
36
+
37
+ Until one is chosen the string stays, because dropping it silently is worse: a
38
+ client that is quietly ignored has no way to tell "unauthorized" from "the
39
+ server is slow".
40
+
41
+ **What would settle it:** deciding whether channel-level errors belong in `Out`
42
+ at all, which is the same question `channel-rpc` already answered for RPC frames
43
+ by giving them their own envelope. If the answer is "same as RPC", this becomes
44
+ a decision note and the string becomes an envelope.
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@pikku/core",
3
- "version": "0.12.79",
3
+ "version": "0.12.82",
4
4
  "description": "The Pikku runtime — functions, wirings, services, middleware and types",
5
5
  "author": "yasser.fadl@gmail.com",
6
6
  "license": "MIT",
@@ -15,8 +15,18 @@
15
15
  "test": "bash run-tests.sh",
16
16
  "test:watch": "bash run-tests.sh --watch",
17
17
  "test:coverage": "bash run-tests.sh --coverage",
18
- "prepublishOnly": "yarn build"
18
+ "prepublishOnly": "yarn build",
19
+ "api-report": "tsx scripts/generate-api-report.mts"
19
20
  },
21
+ "sideEffects": [
22
+ "./dist/errors/errors.js",
23
+ "./dist/wirings/rpc/rpc-runner.js",
24
+ "./dist/wirings/rpc/remote-addon-auth.js",
25
+ "./dist/wirings/workflow/workflow-approval-policy.js",
26
+ "./dist/wirings/workflow/workflow-errors.js",
27
+ "./dist/wirings/workflow/workflow-run-ownership.js",
28
+ "./dist/wirings/workflow/pikku-scenario-service.js"
29
+ ],
20
30
  "exports": {
21
31
  ".": "./dist/index.js",
22
32
  "./middleware": "./dist/middleware/index.js",
@@ -40,6 +50,7 @@
40
50
  "./node-host-resolver": "./dist/utils/node-host-resolver.js",
41
51
  "./mcp": "./dist/wirings/mcp/index.js",
42
52
  "./ai-agent": "./dist/wirings/ai-agent/index.js",
53
+ "./ai-scorer": "./dist/wirings/ai-scorer/index.js",
43
54
  "./gateway": "./dist/wirings/gateway/index.js",
44
55
  "./cli": "./dist/wirings/cli/index.js",
45
56
  "./cli/command-parser": "./dist/wirings/cli/command-parser.js",
@@ -62,7 +73,8 @@
62
73
  "./services/temporary-file-service": "./dist/services/temporary-file-service.js",
63
74
  "./crypto-utils": "./dist/crypto-utils.js",
64
75
  "./hmac": "./dist/utils/hmac.js",
65
- "./internal": "./dist/internal.js",
76
+ "./ecosystem": "./dist/ecosystem.js",
77
+ "./internal": "./dist/ecosystem.js",
66
78
  "./schema": "./dist/schema.js",
67
79
  "./testing": "./dist/testing/index.js",
68
80
  "./dev": "./dist/dev/hot-reload.js"
@@ -0,0 +1 @@
1
+ export {};
@@ -0,0 +1,89 @@
1
+ /**
2
+ * Emits `api-report.md`: every entry point's exported symbols with their full
3
+ * type signatures.
4
+ *
5
+ * `public-surface.json` pins the *names* a consumer can reach. That catches an
6
+ * export appearing or disappearing and nothing else — adding a method to
7
+ * `MetaService`, or making a field on `ChannelMeta` required, sails straight
8
+ * past it. Those are the changes that break a consumer's build, and there are
9
+ * far more of them: 130 exported classes carry 543 members between them, and
10
+ * 268 exported interfaces carry 1,385.
11
+ *
12
+ * So this walks the type checker instead and writes what each symbol actually
13
+ * *is*. The report is committed, and `api-report.test.ts` fails when the two
14
+ * disagree — so a member-level change is a reviewable diff rather than a
15
+ * surprise in someone else's CI.
16
+ *
17
+ * Run: yarn api-report
18
+ */
19
+ import { readFileSync, writeFileSync } from 'node:fs';
20
+ import { resolve, dirname } from 'node:path';
21
+ import { fileURLToPath } from 'node:url';
22
+ import ts from 'typescript';
23
+ const packageRoot = resolve(dirname(fileURLToPath(import.meta.url)), '..');
24
+ const pkg = JSON.parse(readFileSync(resolve(packageRoot, 'package.json'), 'utf-8'));
25
+ const entryPoints = Object.entries(pkg.exports).map(([subpath, dist]) => ({
26
+ subpath,
27
+ file: resolve(packageRoot, dist.replace('./dist/', './src/').replace(/\.js$/, '.ts')),
28
+ }));
29
+ const program = ts.createProgram(entryPoints.map((e) => e.file), {
30
+ target: ts.ScriptTarget.ESNext,
31
+ module: ts.ModuleKind.Node16,
32
+ moduleResolution: ts.ModuleResolutionKind.Node16,
33
+ strict: true,
34
+ skipLibCheck: true,
35
+ });
36
+ const checker = program.getTypeChecker();
37
+ /** One line per symbol: how it is declared, flattened and stripped of noise. */
38
+ const signature = (exported) => {
39
+ // A re-export is an alias; the declaration that matters is the target's, so
40
+ // an interface re-exported through a barrel still reports its members.
41
+ const symbol = exported.flags & ts.SymbolFlags.Alias
42
+ ? checker.getAliasedSymbol(exported)
43
+ : exported;
44
+ const declaration = symbol.declarations?.[0];
45
+ if (!declaration)
46
+ return exported.getName();
47
+ if (ts.isInterfaceDeclaration(declaration) ||
48
+ ts.isClassDeclaration(declaration) ||
49
+ ts.isTypeAliasDeclaration(declaration) ||
50
+ ts.isEnumDeclaration(declaration)) {
51
+ // The declaration itself, so member changes show up in the diff.
52
+ return declaration
53
+ .getText()
54
+ .replace(/\/\*\*[\s\S]*?\*\//g, '')
55
+ .replace(/\/\/[^\n]*/g, '')
56
+ .split('\n')
57
+ .map((l) => l.trim())
58
+ .filter(Boolean)
59
+ .join(' ');
60
+ }
61
+ const type = checker.getTypeOfSymbolAtLocation(symbol, declaration);
62
+ return `${exported.getName()}: ${checker.typeToString(type, undefined, ts.TypeFormatFlags.NoTruncation)}`;
63
+ };
64
+ const sections = [
65
+ '# @pikku/core API report',
66
+ '',
67
+ 'Generated by `yarn api-report`. Every exported symbol with its full',
68
+ 'signature, so a member-level change is a reviewable diff. Do not edit.',
69
+ '',
70
+ ];
71
+ for (const { subpath, file } of entryPoints) {
72
+ const source = program.getSourceFile(file);
73
+ if (!source)
74
+ continue;
75
+ const moduleSymbol = checker.getSymbolAtLocation(source);
76
+ if (!moduleSymbol)
77
+ continue;
78
+ const exports = checker
79
+ .getExportsOfModule(moduleSymbol)
80
+ .map((s) => [s.getName(), signature(s)])
81
+ .sort(([a], [b]) => a.localeCompare(b));
82
+ sections.push(`## ${subpath}`, '');
83
+ sections.push('```ts');
84
+ for (const [, sig] of exports)
85
+ sections.push(sig);
86
+ sections.push('```', '');
87
+ }
88
+ writeFileSync(resolve(packageRoot, 'api-report.md'), sections.join('\n'));
89
+ console.log(`api-report.md written for ${entryPoints.length} entry points`);