@pikku/core 0.12.72 → 0.12.77

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 (848) hide show
  1. package/CHANGELOG.md +1171 -0
  2. package/dist/column-form.d.ts +32 -0
  3. package/dist/column-form.js +42 -0
  4. package/dist/crypto-utils.d.ts +43 -7
  5. package/dist/crypto-utils.js +163 -42
  6. package/dist/data-classification.d.ts +44 -0
  7. package/dist/dev/hot-reload.js +11 -30
  8. package/dist/dev/module-runner.d.ts +3 -7
  9. package/dist/dev/module-runner.js +4 -10
  10. package/dist/dev/reload-meta.d.ts +8 -20
  11. package/dist/dev/reload-meta.js +9 -29
  12. package/dist/errors/error-handler.d.ts +5 -30
  13. package/dist/errors/error-handler.js +16 -32
  14. package/dist/errors/errors.d.ts +32 -151
  15. package/dist/errors/errors.js +55 -157
  16. package/dist/function/abort-scope.d.ts +47 -0
  17. package/dist/function/abort-scope.js +63 -0
  18. package/dist/function/function-runner.js +37 -32
  19. package/dist/function/functions.types.d.ts +57 -136
  20. package/dist/function/functions.types.js +0 -58
  21. package/dist/function/index.d.ts +1 -1
  22. package/dist/function/list.types.d.ts +12 -62
  23. package/dist/function/list.types.js +4 -25
  24. package/dist/handle-error.d.ts +0 -13
  25. package/dist/handle-error.js +0 -18
  26. package/dist/index.d.ts +11 -5
  27. package/dist/index.js +7 -2
  28. package/dist/middleware/auth-apikey.d.ts +3 -18
  29. package/dist/middleware/auth-apikey.js +0 -17
  30. package/dist/middleware/auth-bearer.d.ts +6 -41
  31. package/dist/middleware/auth-bearer.js +5 -41
  32. package/dist/middleware/auth-cookie.d.ts +5 -27
  33. package/dist/middleware/auth-cookie.js +2 -26
  34. package/dist/middleware/cors.d.ts +7 -34
  35. package/dist/middleware/cors.js +7 -34
  36. package/dist/middleware/remote-auth.d.ts +3 -1
  37. package/dist/middleware/remote-auth.js +4 -3
  38. package/dist/middleware/telemetry.d.ts +8 -33
  39. package/dist/middleware/telemetry.js +2 -31
  40. package/dist/middleware-runner.d.ts +4 -55
  41. package/dist/middleware-runner.js +5 -74
  42. package/dist/permissions.d.ts +3 -44
  43. package/dist/permissions.js +19 -71
  44. package/dist/pikku-request.d.ts +0 -6
  45. package/dist/pikku-request.js +0 -6
  46. package/dist/pikku-state.d.ts +0 -26
  47. package/dist/pikku-state.js +2 -30
  48. package/dist/remote.d.ts +3 -5
  49. package/dist/remote.js +9 -8
  50. package/dist/schema.d.ts +5 -39
  51. package/dist/schema.js +5 -39
  52. package/dist/scopes.d.ts +4 -23
  53. package/dist/scopes.js +7 -48
  54. package/dist/secret-value.d.ts +56 -0
  55. package/dist/secret-value.js +46 -0
  56. package/dist/services/ai-agent-runner-service.d.ts +20 -0
  57. package/dist/services/ai-embedding-service.d.ts +2 -25
  58. package/dist/services/audit-service.d.ts +74 -4
  59. package/dist/services/audit-service.js +8 -7
  60. package/dist/services/content-service.d.ts +1 -46
  61. package/dist/services/credential-service.d.ts +3 -40
  62. package/dist/services/credential-wire-service.d.ts +5 -0
  63. package/dist/services/credential-wire-service.js +9 -1
  64. package/dist/services/deployment-service.d.ts +3 -9
  65. package/dist/services/email-service.d.ts +2 -1
  66. package/dist/services/gateway-service.d.ts +0 -15
  67. package/dist/services/http-personas.d.ts +80 -0
  68. package/dist/services/http-personas.js +233 -0
  69. package/dist/services/in-memory-queue-service.d.ts +0 -14
  70. package/dist/services/in-memory-queue-service.js +1 -15
  71. package/dist/services/in-memory-trigger-service.d.ts +0 -18
  72. package/dist/services/in-memory-trigger-service.js +1 -18
  73. package/dist/services/in-memory-workflow-service.d.ts +0 -16
  74. package/dist/services/in-memory-workflow-service.js +4 -33
  75. package/dist/services/index.d.ts +8 -9
  76. package/dist/services/index.js +3 -6
  77. package/dist/services/istanbul-coverage-service.d.ts +1 -5
  78. package/dist/services/istanbul-coverage-service.js +2 -8
  79. package/dist/services/jwt-service.d.ts +1 -16
  80. package/dist/services/local-content-request-handler.d.ts +29 -0
  81. package/dist/services/local-content-request-handler.js +176 -0
  82. package/dist/services/local-content.d.ts +13 -2
  83. package/dist/services/local-content.js +40 -13
  84. package/dist/services/local-gateway-service.d.ts +0 -16
  85. package/dist/services/local-gateway-service.js +2 -17
  86. package/dist/services/local-secrets.d.ts +4 -7
  87. package/dist/services/local-secrets.js +7 -7
  88. package/dist/services/logger-console.d.ts +3 -7
  89. package/dist/services/logger-console.js +3 -7
  90. package/dist/services/logger.d.ts +22 -40
  91. package/dist/services/meta-service.d.ts +23 -26
  92. package/dist/services/meta-service.js +22 -36
  93. package/dist/services/personas-service.d.ts +134 -0
  94. package/dist/services/personas-service.js +40 -0
  95. package/dist/services/pikku-user-id.js +0 -4
  96. package/dist/services/queue-webhook-service.d.ts +2 -36
  97. package/dist/services/queue-webhook-service.js +10 -42
  98. package/dist/services/scheduler-service.d.ts +1 -50
  99. package/dist/services/scheduler-service.js +0 -10
  100. package/dist/services/schema-service.d.ts +1 -24
  101. package/dist/services/scope-service.d.ts +49 -34
  102. package/dist/services/scoped-secret-service.d.ts +4 -7
  103. package/dist/services/scoped-secret-service.js +0 -4
  104. package/dist/services/secret-host-binding.d.ts +8 -0
  105. package/dist/services/secret-host-binding.js +36 -0
  106. package/dist/services/secret-service.d.ts +12 -35
  107. package/dist/services/secretless.d.ts +6 -0
  108. package/dist/services/secretless.js +21 -0
  109. package/dist/services/stub-tracker.d.ts +7 -18
  110. package/dist/services/stub-tracker.js +8 -18
  111. package/dist/services/system-role-guard.d.ts +33 -0
  112. package/dist/services/system-role-guard.js +38 -0
  113. package/dist/services/trigger-service.d.ts +0 -12
  114. package/dist/services/typed-secret-service.d.ts +5 -11
  115. package/dist/services/typed-secret-service.js +1 -7
  116. package/dist/services/v8-coverage-service.d.ts +2 -3
  117. package/dist/services/v8-coverage-service.js +1 -2
  118. package/dist/services/variables-service.d.ts +1 -8
  119. package/dist/services/webhook-service.d.ts +21 -64
  120. package/dist/services/webhook-service.js +6 -20
  121. package/dist/services/workflow-service.d.ts +3 -15
  122. package/dist/testing/service-tests.js +6 -23
  123. package/dist/time-utils.d.ts +0 -16
  124. package/dist/time-utils.js +1 -19
  125. package/dist/types/core.types.d.ts +120 -219
  126. package/dist/types/core.types.js +0 -42
  127. package/dist/types/state.types.d.ts +4 -9
  128. package/dist/utils/hmac.d.ts +4 -10
  129. package/dist/utils/hmac.js +4 -10
  130. package/dist/utils/safe-fetch.d.ts +7 -35
  131. package/dist/utils/safe-fetch.js +13 -53
  132. package/dist/utils.d.ts +1 -6
  133. package/dist/utils.js +6 -15
  134. package/dist/wirings/actor-flow/actor-flow.types.d.ts +1 -34
  135. package/dist/wirings/actor-flow/index.d.ts +0 -9
  136. package/dist/wirings/actor-flow/run-conversation.d.ts +5 -5
  137. package/dist/wirings/actor-flow/run-conversation.js +14 -7
  138. package/dist/wirings/ai-agent/ai-agent-agui.d.ts +0 -5
  139. package/dist/wirings/ai-agent/ai-agent-agui.js +46 -12
  140. package/dist/wirings/ai-agent/ai-agent-helpers.d.ts +7 -0
  141. package/dist/wirings/ai-agent/ai-agent-helpers.js +7 -0
  142. package/dist/wirings/ai-agent/ai-agent-interrupt.d.ts +153 -0
  143. package/dist/wirings/ai-agent/ai-agent-interrupt.js +256 -0
  144. package/dist/wirings/ai-agent/ai-agent-memory.js +0 -2
  145. package/dist/wirings/ai-agent/ai-agent-model-config.d.ts +0 -9
  146. package/dist/wirings/ai-agent/ai-agent-model-config.js +1 -9
  147. package/dist/wirings/ai-agent/ai-agent-prepare.d.ts +10 -99
  148. package/dist/wirings/ai-agent/ai-agent-prepare.js +65 -132
  149. package/dist/wirings/ai-agent/ai-agent-registry.d.ts +2 -1
  150. package/dist/wirings/ai-agent/ai-agent-registry.js +5 -1
  151. package/dist/wirings/ai-agent/ai-agent-runner.js +63 -22
  152. package/dist/wirings/ai-agent/ai-agent-stream.d.ts +25 -2
  153. package/dist/wirings/ai-agent/ai-agent-stream.js +180 -64
  154. package/dist/wirings/ai-agent/ai-agent.types.d.ts +124 -4
  155. package/dist/wirings/ai-agent/index.d.ts +6 -4
  156. package/dist/wirings/ai-agent/index.js +5 -4
  157. package/dist/wirings/ai-agent/voice-input.d.ts +59 -1
  158. package/dist/wirings/ai-agent/voice-input.js +90 -12
  159. package/dist/wirings/ai-agent/voice-output.d.ts +69 -1
  160. package/dist/wirings/ai-agent/voice-output.js +162 -50
  161. package/dist/wirings/channel/channel-common.d.ts +7 -20
  162. package/dist/wirings/channel/channel-common.js +7 -21
  163. package/dist/wirings/channel/channel-handler.js +25 -6
  164. package/dist/wirings/channel/channel-host-rpc.d.ts +25 -0
  165. package/dist/wirings/channel/channel-host-rpc.js +38 -0
  166. package/dist/wirings/channel/channel-middleware-runner.d.ts +0 -12
  167. package/dist/wirings/channel/channel-middleware-runner.js +0 -12
  168. package/dist/wirings/channel/channel-rpc-registry.d.ts +31 -0
  169. package/dist/wirings/channel/channel-rpc-registry.js +89 -0
  170. package/dist/wirings/channel/channel-rpc-responder.d.ts +15 -0
  171. package/dist/wirings/channel/channel-rpc-responder.js +71 -0
  172. package/dist/wirings/channel/channel-rpc-service.d.ts +40 -0
  173. package/dist/wirings/channel/channel-rpc-service.js +106 -0
  174. package/dist/wirings/channel/channel-rpc-validators.d.ts +14 -0
  175. package/dist/wirings/channel/channel-rpc-validators.js +30 -0
  176. package/dist/wirings/channel/channel-rpc.d.ts +5 -0
  177. package/dist/wirings/channel/channel-rpc.js +5 -0
  178. package/dist/wirings/channel/channel-rpc.types.d.ts +90 -0
  179. package/dist/wirings/channel/channel-rpc.types.js +50 -0
  180. package/dist/wirings/channel/channel-runner.d.ts +0 -4
  181. package/dist/wirings/channel/channel-runner.js +0 -14
  182. package/dist/wirings/channel/channel-store.d.ts +0 -10
  183. package/dist/wirings/channel/channel.types.d.ts +12 -1
  184. package/dist/wirings/channel/define-channel-routes.d.ts +0 -20
  185. package/dist/wirings/channel/define-channel-routes.js +0 -20
  186. package/dist/wirings/channel/eventhub-service.d.ts +0 -18
  187. package/dist/wirings/channel/index.d.ts +4 -1
  188. package/dist/wirings/channel/index.js +2 -0
  189. package/dist/wirings/channel/local/local-channel-runner.js +3 -1
  190. package/dist/wirings/channel/local/local-eventhub-service.d.ts +0 -33
  191. package/dist/wirings/channel/local/local-eventhub-service.js +2 -36
  192. package/dist/wirings/channel/log-channels.d.ts +0 -4
  193. package/dist/wirings/channel/log-channels.js +0 -4
  194. package/dist/wirings/channel/pikku-abstract-channel-handler.js +6 -0
  195. package/dist/wirings/channel/serverless/serverless-channel-runner.js +2 -5
  196. package/dist/wirings/cli/channel/cli-approval.d.ts +41 -0
  197. package/dist/wirings/cli/channel/cli-approval.js +81 -0
  198. package/dist/wirings/cli/channel/cli-channel-runner.d.ts +0 -4
  199. package/dist/wirings/cli/channel/cli-channel-runner.js +3 -25
  200. package/dist/wirings/cli/channel/cli-raw-channel-runner.d.ts +47 -9
  201. package/dist/wirings/cli/channel/cli-raw-channel-runner.js +24 -16
  202. package/dist/wirings/cli/channel/cli-raw-client-runner.d.ts +38 -0
  203. package/dist/wirings/cli/channel/cli-raw-client-runner.js +129 -0
  204. package/dist/wirings/cli/channel/index.d.ts +5 -0
  205. package/dist/wirings/cli/channel/index.js +2 -0
  206. package/dist/wirings/cli/cli-runner.d.ts +20 -20
  207. package/dist/wirings/cli/cli-runner.js +28 -89
  208. package/dist/wirings/cli/cli.types.d.ts +14 -3
  209. package/dist/wirings/cli/command-parser.d.ts +1 -10
  210. package/dist/wirings/cli/command-parser.js +10 -87
  211. package/dist/wirings/cli/define-cli-commands.d.ts +1 -17
  212. package/dist/wirings/cli/define-cli-commands.js +1 -17
  213. package/dist/wirings/credential/credential.types.d.ts +0 -12
  214. package/dist/wirings/credential/define-credential.d.ts +48 -0
  215. package/dist/wirings/credential/define-credential.js +47 -0
  216. package/dist/wirings/credential/index.d.ts +1 -1
  217. package/dist/wirings/credential/index.js +1 -1
  218. package/dist/wirings/credential/validate-credential-definitions.d.ts +2 -4
  219. package/dist/wirings/gateway/gateway-runner.d.ts +1 -20
  220. package/dist/wirings/gateway/gateway-runner.js +8 -105
  221. package/dist/wirings/gateway/gateway.types.d.ts +7 -80
  222. package/dist/wirings/http/http-routes.d.ts +0 -63
  223. package/dist/wirings/http/http-routes.js +0 -63
  224. package/dist/wirings/http/http-runner.d.ts +0 -99
  225. package/dist/wirings/http/http-runner.js +9 -165
  226. package/dist/wirings/http/http.types.d.ts +14 -55
  227. package/dist/wirings/http/log-http-routes.d.ts +0 -4
  228. package/dist/wirings/http/log-http-routes.js +0 -4
  229. package/dist/wirings/http/pikku-fetch-http-request.d.ts +0 -40
  230. package/dist/wirings/http/pikku-fetch-http-request.js +0 -58
  231. package/dist/wirings/http/pikku-fetch-http-response.js +0 -3
  232. package/dist/wirings/http/routers/path-to-regex.js +2 -13
  233. package/dist/wirings/http/web-request.d.ts +0 -8
  234. package/dist/wirings/http/web-request.js +25 -17
  235. package/dist/wirings/mcp/mcp-runner.d.ts +1 -4
  236. package/dist/wirings/mcp/mcp-runner.js +1 -14
  237. package/dist/wirings/mcp/mcp.types.d.ts +2 -35
  238. package/dist/wirings/oauth2/oauth2.types.d.ts +0 -28
  239. package/dist/wirings/oauth2/oauth2.types.js +0 -3
  240. package/dist/wirings/persona/define-personas.d.ts +32 -0
  241. package/dist/wirings/persona/define-personas.js +31 -0
  242. package/dist/wirings/persona/index.d.ts +21 -0
  243. package/dist/wirings/persona/index.js +17 -0
  244. package/dist/wirings/persona/persona-email.d.ts +37 -0
  245. package/dist/wirings/persona/persona-email.js +69 -0
  246. package/dist/wirings/persona/persona-environments.d.ts +45 -0
  247. package/dist/wirings/persona/persona-environments.js +81 -0
  248. package/dist/wirings/persona/persona-mailbox.d.ts +101 -0
  249. package/dist/wirings/persona/persona-mailbox.js +53 -0
  250. package/dist/wirings/persona/persona.types.d.ts +136 -0
  251. package/dist/wirings/persona/persona.types.js +1 -0
  252. package/dist/wirings/persona/validate-personas.d.ts +53 -0
  253. package/dist/wirings/persona/validate-personas.js +94 -0
  254. package/dist/wirings/queue/index.d.ts +3 -0
  255. package/dist/wirings/queue/index.js +2 -3
  256. package/dist/wirings/queue/queue-identity.d.ts +28 -0
  257. package/dist/wirings/queue/queue-identity.js +103 -0
  258. package/dist/wirings/queue/queue-runner.d.ts +0 -19
  259. package/dist/wirings/queue/queue-runner.js +9 -30
  260. package/dist/wirings/queue/queue.types.d.ts +20 -90
  261. package/dist/wirings/queue/register-queue-helper.d.ts +0 -12
  262. package/dist/wirings/queue/register-queue-helper.js +0 -11
  263. package/dist/wirings/queue/signed-queue-service.d.ts +17 -0
  264. package/dist/wirings/queue/signed-queue-service.js +42 -0
  265. package/dist/wirings/queue/validate-worker-config.d.ts +2 -23
  266. package/dist/wirings/queue/validate-worker-config.js +0 -14
  267. package/dist/wirings/role/define-system-role.d.ts +32 -0
  268. package/dist/wirings/role/define-system-role.js +31 -0
  269. package/dist/wirings/role/index.d.ts +3 -0
  270. package/dist/wirings/role/index.js +2 -0
  271. package/dist/wirings/role/role.types.d.ts +43 -0
  272. package/dist/wirings/role/role.types.js +1 -0
  273. package/dist/wirings/role/validate-role-definitions.d.ts +21 -0
  274. package/dist/wirings/role/validate-role-definitions.js +71 -0
  275. package/dist/wirings/rpc/addon-runner.d.ts +0 -19
  276. package/dist/wirings/rpc/addon-runner.js +0 -51
  277. package/dist/wirings/rpc/remote-addon-auth.d.ts +3 -13
  278. package/dist/wirings/rpc/remote-addon-auth.js +7 -11
  279. package/dist/wirings/rpc/rpc-runner.d.ts +11 -18
  280. package/dist/wirings/rpc/rpc-runner.js +88 -105
  281. package/dist/wirings/rpc/rpc-types.d.ts +7 -6
  282. package/dist/wirings/rpc/wire-addon.d.ts +25 -0
  283. package/dist/wirings/rpc/wire-addon.js +62 -0
  284. package/dist/wirings/rpc/wire-remote-addon.d.ts +3 -28
  285. package/dist/wirings/rpc/wire-remote-addon.js +0 -8
  286. package/dist/wirings/scheduler/log-schedulers.d.ts +0 -4
  287. package/dist/wirings/scheduler/log-schedulers.js +0 -4
  288. package/dist/wirings/scheduler/scheduler-runner.d.ts +0 -1
  289. package/dist/wirings/scheduler/scheduler-runner.js +0 -1
  290. package/dist/wirings/scheduler/scheduler.types.d.ts +1 -14
  291. package/dist/wirings/scope/define-scope.d.ts +32 -0
  292. package/dist/wirings/scope/define-scope.js +31 -0
  293. package/dist/wirings/scope/index.d.ts +1 -1
  294. package/dist/wirings/scope/index.js +1 -1
  295. package/dist/wirings/scope/scope.types.d.ts +7 -9
  296. package/dist/wirings/scope/validate-scope-definitions.d.ts +2 -21
  297. package/dist/wirings/scope/validate-scope-definitions.js +3 -21
  298. package/dist/wirings/secret/index.d.ts +1 -1
  299. package/dist/wirings/secret/index.js +1 -1
  300. package/dist/wirings/secret/secret.types.d.ts +19 -15
  301. package/dist/wirings/secret/secret.types.js +1 -1
  302. package/dist/wirings/secret/validate-secret-definitions.d.ts +2 -4
  303. package/dist/wirings/trigger/trigger-runner.d.ts +0 -27
  304. package/dist/wirings/trigger/trigger-runner.js +1 -24
  305. package/dist/wirings/trigger/trigger.types.d.ts +1 -82
  306. package/dist/wirings/trigger/trigger.types.js +0 -34
  307. package/dist/wirings/variable/index.d.ts +1 -1
  308. package/dist/wirings/variable/index.js +1 -1
  309. package/dist/wirings/variable/validate-variable-definitions.d.ts +2 -4
  310. package/dist/wirings/variable/variable.types.d.ts +1 -13
  311. package/dist/wirings/variable/variable.types.js +1 -1
  312. package/dist/wirings/virtual-user/index.d.ts +30 -0
  313. package/dist/wirings/virtual-user/index.js +10 -0
  314. package/dist/wirings/virtual-user/prepare-virtual-user-run.d.ts +54 -0
  315. package/dist/wirings/virtual-user/prepare-virtual-user-run.js +49 -0
  316. package/dist/wirings/virtual-user/run-virtual-user.d.ts +92 -0
  317. package/dist/wirings/virtual-user/run-virtual-user.js +478 -0
  318. package/dist/wirings/virtual-user/virtual-user-agents.d.ts +38 -0
  319. package/dist/wirings/virtual-user/virtual-user-agents.js +24 -0
  320. package/dist/wirings/virtual-user/virtual-user-catalogue.d.ts +92 -0
  321. package/dist/wirings/virtual-user/virtual-user-catalogue.js +134 -0
  322. package/dist/wirings/virtual-user/virtual-user-derive.d.ts +26 -0
  323. package/dist/wirings/virtual-user/virtual-user-derive.js +137 -0
  324. package/dist/wirings/virtual-user/virtual-user-dispositions.d.ts +79 -0
  325. package/dist/wirings/virtual-user/virtual-user-dispositions.js +128 -0
  326. package/dist/wirings/virtual-user/virtual-user-intents.d.ts +78 -0
  327. package/dist/wirings/virtual-user/virtual-user-intents.js +142 -0
  328. package/dist/wirings/virtual-user/virtual-user-rng.d.ts +24 -0
  329. package/dist/wirings/virtual-user/virtual-user-rng.js +44 -0
  330. package/dist/wirings/virtual-user/virtual-user-run-store.d.ts +90 -0
  331. package/dist/wirings/virtual-user/virtual-user-run-store.js +1 -0
  332. package/dist/wirings/virtual-user/virtual-user-target.d.ts +21 -0
  333. package/dist/wirings/virtual-user/virtual-user-target.js +34 -0
  334. package/dist/wirings/virtual-user/virtual-user.types.d.ts +199 -0
  335. package/dist/wirings/virtual-user/virtual-user.types.js +8 -0
  336. package/dist/wirings/workflow/dsl/workflow-dsl.types.d.ts +19 -15
  337. package/dist/wirings/workflow/dsl/workflow-runner.d.ts +0 -4
  338. package/dist/wirings/workflow/dsl/workflow-runner.js +0 -4
  339. package/dist/wirings/workflow/feature.d.ts +0 -19
  340. package/dist/wirings/workflow/feature.js +0 -19
  341. package/dist/wirings/workflow/graph/graph-node.d.ts +0 -98
  342. package/dist/wirings/workflow/graph/graph-node.js +0 -34
  343. package/dist/wirings/workflow/graph/graph-runner.js +6 -41
  344. package/dist/wirings/workflow/graph/wire-workflow-graph.d.ts +0 -4
  345. package/dist/wirings/workflow/graph/workflow-graph.types.d.ts +0 -58
  346. package/dist/wirings/workflow/graph/workflow-graph.types.js +0 -6
  347. package/dist/wirings/workflow/index.d.ts +5 -7
  348. package/dist/wirings/workflow/index.js +3 -17
  349. package/dist/wirings/workflow/pikku-scenario-service.d.ts +87 -5
  350. package/dist/wirings/workflow/pikku-scenario-service.js +204 -43
  351. package/dist/wirings/workflow/pikku-workflow-service.d.ts +7 -459
  352. package/dist/wirings/workflow/pikku-workflow-service.js +58 -551
  353. package/dist/wirings/workflow/run-timeline.d.ts +0 -47
  354. package/dist/wirings/workflow/run-timeline.js +0 -22
  355. package/dist/wirings/workflow/scenario-cookie-jar.d.ts +0 -23
  356. package/dist/wirings/workflow/scenario-cookie-jar.js +0 -16
  357. package/dist/wirings/workflow/scenario-poll.d.ts +0 -15
  358. package/dist/wirings/workflow/scenario-poll.js +0 -12
  359. package/dist/wirings/workflow/scenario-prose.d.ts +0 -28
  360. package/dist/wirings/workflow/scenario-prose.js +1 -19
  361. package/dist/wirings/workflow/scenario-step-guards.d.ts +0 -13
  362. package/dist/wirings/workflow/scenario-step-guards.js +1 -14
  363. package/dist/wirings/workflow/scenario-step.types.d.ts +84 -12
  364. package/dist/wirings/workflow/scenario-step.types.js +5 -1
  365. package/dist/wirings/workflow/scenario-surface.d.ts +16 -0
  366. package/dist/wirings/workflow/scenario-surface.js +56 -0
  367. package/dist/wirings/workflow/workflow-invocation-id.d.ts +0 -18
  368. package/dist/wirings/workflow/workflow-invocation-id.js +2 -22
  369. package/dist/wirings/workflow/workflow-queue-workers.d.ts +0 -20
  370. package/dist/wirings/workflow/workflow-queue-workers.js +0 -19
  371. package/dist/wirings/workflow/workflow.types.d.ts +5 -195
  372. package/knowledge/decisions/index.md +19 -0
  373. package/knowledge/decisions/internals/a-secret-that-fails-to-decrypt-fails-the-whole-read.md +49 -0
  374. package/knowledge/decisions/internals/a-virtual-user-run-is-not-a-workflow-and-not-a-queued-job.md +48 -0
  375. package/knowledge/decisions/internals/actor-flow-conversations-seed-a-hidden-kickoff-message.md +23 -0
  376. package/knowledge/decisions/internals/actor-flow-drives-the-target-through-a-transport-seam.md +24 -0
  377. package/knowledge/decisions/internals/actor-flow-verdicts-are-llm-self-evaluations.md +25 -0
  378. package/knowledge/decisions/internals/addon-package-roots-resolve-by-walking-node-module-search-paths.md +26 -0
  379. package/knowledge/decisions/internals/addon-singleton-services-are-cached-per-namespace-not-per-package.md +33 -0
  380. package/knowledge/decisions/internals/addon-workflow-names-are-prefixed-with-the-consumer-namespace.md +28 -0
  381. package/knowledge/decisions/internals/ai-agent-agui-bridge-obeys-the-client-ordering-contract.md +29 -0
  382. package/knowledge/decisions/internals/ai-agent-audio-chunks-carry-the-format-the-provider-returned.md +20 -0
  383. package/knowledge/decisions/internals/ai-agent-credential-suspensions-hide-the-tool-result.md +26 -0
  384. package/knowledge/decisions/internals/ai-agent-delegate-and-supervise-hide-different-text.md +26 -0
  385. package/knowledge/decisions/internals/ai-agent-llm-tool-arguments-have-nulls-stripped.md +23 -0
  386. package/knowledge/decisions/internals/ai-agent-model-config-stays-a-single-resolution-seam.md +25 -0
  387. package/knowledge/decisions/internals/ai-agent-onerror-hooks-cannot-change-the-failure.md +22 -0
  388. package/knowledge/decisions/internals/ai-agent-runner-methods-must-keep-their-receiver.md +22 -0
  389. package/knowledge/decisions/internals/ai-agent-stream-persistence-is-best-effort.md +27 -0
  390. package/knowledge/decisions/internals/ai-agent-sub-agents-inherit-the-parent-context-block.md +26 -0
  391. package/knowledge/decisions/internals/ai-agent-tool-execute-failures-are-logged-unconditionally.md +25 -0
  392. package/knowledge/decisions/internals/ai-agent-voice-input-transcribes-audio-parts-in-place.md +22 -0
  393. package/knowledge/decisions/internals/ai-agent-working-memory-is-persisted-only-when-valid.md +25 -0
  394. package/knowledge/decisions/internals/channel-message-handlers-accept-three-config-shapes.md +30 -0
  395. package/knowledge/decisions/internals/channel-middleware-caches-only-statically-resolved-middleware.md +31 -0
  396. package/knowledge/decisions/internals/channel-state-is-per-socket-session-state-is-per-user.md +29 -0
  397. package/knowledge/decisions/internals/channel-user-id-is-persisted-after-onconnect-middleware-runs.md +28 -0
  398. package/knowledge/decisions/internals/cli-option-names-are-camelcase-in-state-and-kebab-on-the-command-line.md +27 -0
  399. package/knowledge/decisions/internals/cli-parse-errors-are-routed-by-message-prefix.md +28 -0
  400. package/knowledge/decisions/internals/cli-stdout-is-reserved-for-machine-readable-output.md +34 -0
  401. package/knowledge/decisions/internals/cli-unknown-long-options-warn-instead-of-failing.md +29 -0
  402. package/knowledge/decisions/internals/core-column-form-is-an-axis-of-its-own.md +84 -0
  403. package/knowledge/decisions/internals/core-data-classification-brand-is-an-optional-property.md +41 -0
  404. package/knowledge/decisions/internals/core-function-runner-restores-the-wire-fields-it-overwrites.md +44 -0
  405. package/knowledge/decisions/internals/core-hot-reload-merges-generated-meta-never-replaces-it.md +39 -0
  406. package/knowledge/decisions/internals/core-hot-reload-owns-its-module-registry.md +42 -0
  407. package/knowledge/decisions/internals/core-middleware-order-is-scope-then-priority.md +39 -0
  408. package/knowledge/decisions/internals/core-schema-defaults-apply-on-every-transport.md +43 -0
  409. package/knowledge/decisions/internals/core-scopes-are-an-and-gate-separate-from-permissions.md +38 -0
  410. package/knowledge/decisions/internals/core-state-is-a-global-map-written-only-at-registration-time.md +44 -0
  411. package/knowledge/decisions/internals/email-meta-is-read-uncached-because-codegen-rewrites-it-mid-session.md +27 -0
  412. package/knowledge/decisions/internals/gateway-adapters-resolve-lazily-and-are-promise-cached.md +32 -0
  413. package/knowledge/decisions/internals/gateway-webhook-challenges-echo-bytes-not-json.md +28 -0
  414. package/knowledge/decisions/internals/gateway-wiring-is-a-meta-wiring-over-http-and-channels.md +31 -0
  415. package/knowledge/decisions/internals/generated-src-paths-in-pikku-meta-are-absolute.md +26 -0
  416. package/knowledge/decisions/internals/http-request-bodies-are-read-once-and-shared.md +32 -0
  417. package/knowledge/decisions/internals/http-route-groups-cascade-config-in-a-fixed-order.md +28 -0
  418. package/knowledge/decisions/internals/http-router-matches-normalized-paths-but-returns-registered-ones.md +30 -0
  419. package/knowledge/decisions/internals/http-runner-logs-through-a-trace-scoped-logger-functions-do-not.md +26 -0
  420. package/knowledge/decisions/internals/http-set-cookie-headers-are-appended-never-joined.md +28 -0
  421. package/knowledge/decisions/internals/http-sse-streams-flush-headers-only-after-middleware.md +32 -0
  422. package/knowledge/decisions/internals/http-wiring-without-metadata-is-skipped-not-fatal.md +26 -0
  423. package/knowledge/decisions/internals/in-a-scenario-a-4xx-is-data-not-an-exception.md +25 -0
  424. package/knowledge/decisions/internals/in-memory-workflow-history-aliases-the-live-step-object.md +26 -0
  425. package/knowledge/decisions/internals/index.md +117 -0
  426. package/knowledge/decisions/internals/istanbul-statement-counts-attach-to-the-start-line-only.md +25 -0
  427. package/knowledge/decisions/internals/local-trigger-and-gateway-services-assume-a-single-process.md +26 -0
  428. package/knowledge/decisions/internals/node-only-builtins-are-imported-dynamically.md +24 -0
  429. package/knowledge/decisions/internals/one-project-shape-check-two-validators.md +53 -0
  430. package/knowledge/decisions/internals/queue-group-concurrency-keeps-one-shared-queue-fair.md +28 -0
  431. package/knowledge/decisions/internals/queue-jobs-always-carry-an-explicit-attempts-count.md +27 -0
  432. package/knowledge/decisions/internals/remote-addons-dispatch-over-http-instead-of-local-meta.md +31 -0
  433. package/knowledge/decisions/internals/rpc-names-resolve-through-package-scope-before-root.md +32 -0
  434. package/knowledge/decisions/internals/scenario-agent-calls-sign-in-on-401-only.md +27 -0
  435. package/knowledge/decisions/internals/scenario-meta-lives-apart-from-app-meta-but-merges-when-read-off-disk.md +26 -0
  436. package/knowledge/decisions/internals/scenario-steps-return-drained-response-records.md +27 -0
  437. package/knowledge/decisions/internals/scenarios-live-in-files-named-for-them.md +48 -0
  438. package/knowledge/decisions/internals/scope-roots-may-be-co-declared-by-an-addon-and-its-host-app.md +30 -0
  439. package/knowledge/decisions/internals/serverless-channel-disconnect-must-tolerate-a-missing-channel.md +28 -0
  440. package/knowledge/decisions/internals/the-dev-queue-copies-prod-timing-and-serialization-semantics.md +30 -0
  441. package/knowledge/decisions/internals/the-embedding-model-is-pinned-per-service-and-doc-query-embedding-is-split.md +29 -0
  442. package/knowledge/decisions/internals/the-in-memory-workflow-service-is-inline-only-and-single-process.md +27 -0
  443. package/knowledge/decisions/internals/the-kek-salt-is-scoped-to-the-key-version.md +40 -0
  444. package/knowledge/decisions/internals/the-schema-service-is-never-stubbed.md +26 -0
  445. package/knowledge/decisions/internals/trigger-declaration-is-split-from-trigger-source.md +33 -0
  446. package/knowledge/decisions/internals/typed-secret-service-caches-for-the-process-lifetime.md +26 -0
  447. package/knowledge/decisions/internals/validate-checks-personas-through-a-shared-module.md +43 -0
  448. package/knowledge/decisions/internals/webhook-delivery-history-records-every-attempt-best-effort.md +26 -0
  449. package/knowledge/decisions/internals/webhook-service-collaborators-are-constructor-args-not-locator-lookups.md +25 -0
  450. package/knowledge/decisions/internals/whether-a-run-is-inline-is-read-from-the-run-record.md +58 -0
  451. package/knowledge/decisions/internals/workflow-approval-expiry-is-decided-from-a-recorded-deadline.md +34 -0
  452. package/knowledge/decisions/internals/workflow-core-never-imports-a-browser-driver.md +42 -0
  453. package/knowledge/decisions/internals/workflow-dsl-meta-separates-runtime-expressions-from-literals.md +38 -0
  454. package/knowledge/decisions/internals/workflow-features-resolve-scenarios-by-object-identity.md +29 -0
  455. package/knowledge/decisions/internals/workflow-graph-inline-and-queued-runs-share-one-planner.md +42 -0
  456. package/knowledge/decisions/internals/workflow-graph-node-notes-are-excluded-from-the-graph-hash.md +25 -0
  457. package/knowledge/decisions/internals/workflow-inline-runs-report-their-run-id-before-they-can-fail.md +29 -0
  458. package/knowledge/decisions/internals/workflow-invocation-id-is-the-dedupe-key-not-step-id.md +43 -0
  459. package/knowledge/decisions/internals/workflow-queued-step-dispatch-requires-an-explicit-opt-in.md +29 -0
  460. package/knowledge/decisions/internals/workflow-queues-are-per-workflow-by-default.md +42 -0
  461. package/knowledge/decisions/internals/workflow-repeated-step-names-get-an-ordinal-suffix.md +33 -0
  462. package/knowledge/decisions/internals/workflow-replay-reads-its-steps-once-and-caches-only-the-immutable-half.md +32 -0
  463. package/knowledge/decisions/internals/workflow-retries-are-owned-by-the-workflow-not-the-queue.md +31 -0
  464. package/knowledge/decisions/internals/workflow-run-capabilities-are-extensions-not-subclasses.md +39 -0
  465. package/knowledge/decisions/internals/workflow-run-mirror-is-never-a-source-of-truth.md +29 -0
  466. package/knowledge/decisions/internals/workflow-run-polling-backs-off-to-the-callers-ceiling.md +33 -0
  467. package/knowledge/decisions/internals/workflow-run-timeline-is-a-pure-fold-over-durable-history.md +37 -0
  468. package/knowledge/decisions/internals/workflow-scenario-assertions-never-retry-and-record-one-step.md +50 -0
  469. package/knowledge/decisions/internals/workflow-scenario-hooks-are-a-scenario-only-affordance.md +43 -0
  470. package/knowledge/decisions/internals/workflow-scenario-prose-is-rendered-from-typed-calls-not-parsed-from-english.md +32 -0
  471. package/knowledge/decisions/internals/workflow-scenario-quarantine-reason-lives-in-code.md +18 -0
  472. package/knowledge/decisions/internals/workflow-scenario-step-targets-are-string-literals-for-the-inspector.md +34 -0
  473. package/knowledge/decisions/internals/workflow-step-compensation-runs-as-its-own-durable-step.md +26 -0
  474. package/knowledge/decisions/internals/workflow-step-dispatch-failure-is-transient-not-a-run-failure.md +33 -0
  475. package/knowledge/decisions/internals/workflow-step-lock-is-held-only-to-claim-the-step.md +27 -0
  476. package/knowledge/decisions/internals/workflow-step-rpc-name-is-provenance-only.md +34 -0
  477. package/knowledge/decisions/internals/workflow-suspend-and-approval-reasons-are-durable-step-identities.md +38 -0
  478. package/knowledge/decisions/internals/workflow-suspended-runs-keep-their-in-process-context.md +30 -0
  479. package/knowledge/decisions/security/a-dropped-audit-write-is-always-logged.md +26 -0
  480. package/knowledge/decisions/security/actor-flow-missing-approval-decisions-default-to-denied.md +22 -0
  481. package/knowledge/decisions/security/actor-sign-in-is-proven-by-set-cookie-not-a-non-empty-jar.md +27 -0
  482. package/knowledge/decisions/security/actor-sign-in-only-works-for-actor-flagged-users.md +27 -0
  483. package/knowledge/decisions/security/addon-auth-and-tags-only-tighten.md +43 -0
  484. package/knowledge/decisions/security/addon-config-gates-apply-only-at-the-namespaced-rpc-boundary.md +52 -0
  485. package/knowledge/decisions/security/addon-scopes-are-resolved-where-the-function-runs.md +46 -0
  486. package/knowledge/decisions/security/ai-agent-approval-forwarding-requires-a-symbol-brand.md +27 -0
  487. package/knowledge/decisions/security/ai-agent-credential-requests-are-symbol-branded.md +37 -0
  488. package/knowledge/decisions/security/ai-agent-gate-requires-a-session-only-when-auth-is-true.md +31 -0
  489. package/knowledge/decisions/security/ai-agent-ownership-failures-never-echo-the-resource.md +23 -0
  490. package/knowledge/decisions/security/ai-agent-resume-re-runs-the-authorization-gate.md +22 -0
  491. package/knowledge/decisions/security/ai-agent-sessionless-deployments-have-no-thread-ownership.md +39 -0
  492. package/knowledge/decisions/security/ai-agent-thread-ownership-composes-the-session-principal.md +30 -0
  493. package/knowledge/decisions/security/ai-agent-tool-filtering-reads-the-live-function-config.md +24 -0
  494. package/knowledge/decisions/security/an-empty-owners-constraint-matches-nothing.md +30 -0
  495. package/knowledge/decisions/security/an-exposed-ungated-function-is-a-codegen-warning.md +49 -0
  496. package/knowledge/decisions/security/console-addon-privileged-functions-gate-themselves.md +76 -0
  497. package/knowledge/decisions/security/core-safe-fetch-blocks-ssrf-by-host-literal-not-dns.md +39 -0
  498. package/knowledge/decisions/security/core-secrets-use-a-per-secret-dek-wrapped-by-a-kek.md +37 -0
  499. package/knowledge/decisions/security/gateway-handlers-run-through-the-function-runner-gate.md +31 -0
  500. package/knowledge/decisions/security/gateway-middleware-sessions-must-be-bridged-onto-the-wire.md +30 -0
  501. package/knowledge/decisions/security/global-permissions-and-function-permissions-are-independent-gates.md +40 -0
  502. package/knowledge/decisions/security/http-error-detail-is-withheld-from-clients-in-production.md +33 -0
  503. package/knowledge/decisions/security/http-request-bodies-are-bounded-before-they-are-buffered.md +46 -0
  504. package/knowledge/decisions/security/index.md +55 -0
  505. package/knowledge/decisions/security/mcp-internal-error-details-are-double-gated-on-production.md +27 -0
  506. package/knowledge/decisions/security/passphrases-are-stretched-key-material-is-expanded.md +40 -0
  507. package/knowledge/decisions/security/permission-auth-filtering-requires-live-permission-functions.md +31 -0
  508. package/knowledge/decisions/security/pikku-carries-actor-scopes-as-data-and-the-app-grants-them.md +26 -0
  509. package/knowledge/decisions/security/queue-job-identities-are-signed-at-enqueue.md +69 -0
  510. package/knowledge/decisions/security/queue-jobs-carry-the-producers-pikku-user-id.md +39 -0
  511. package/knowledge/decisions/security/remote-addon-tokens-are-client-credentials-not-mesh-trust.md +34 -0
  512. package/knowledge/decisions/security/scaffold-features-are-authenticated-unless-opted-out.md +49 -0
  513. package/knowledge/decisions/security/scenario-step-functions-are-never-externally-invocable.md +30 -0
  514. package/knowledge/decisions/security/scope-resolution-happens-at-the-session-boundary-and-sync-never-deletes.md +28 -0
  515. package/knowledge/decisions/security/self-authentication-is-declared-not-detected.md +34 -0
  516. package/knowledge/decisions/security/signed-content-urls-bind-the-request-path.md +37 -0
  517. package/knowledge/decisions/security/webhook-bodies-are-signed-before-they-are-enqueued.md +25 -0
  518. package/knowledge/decisions/security/workflow-actor-steps-always-use-the-real-transport.md +34 -0
  519. package/knowledge/decisions/security/workflow-approval-payloads-are-validated-on-replay-inside-the-workflow.md +40 -0
  520. package/knowledge/decisions/security/workflow-queued-steps-rehydrate-their-session-from-the-run-wire.md +32 -0
  521. package/knowledge/decisions/security/workflow-scenario-sessions-are-isolated-per-actor-and-per-scenario.md +32 -0
  522. package/knowledge/decisions/security/workflow-scenario-steps-are-never-network-invocable.md +31 -0
  523. package/knowledge/index.md +24 -0
  524. package/knowledge/questions/index.md +15 -0
  525. package/package.json +6 -2
  526. package/run-tests.sh +0 -0
  527. package/src/column-form.test.ts +97 -0
  528. package/src/column-form.ts +58 -0
  529. package/src/crypto-utils.test.ts +460 -19
  530. package/src/crypto-utils.ts +306 -59
  531. package/src/data-classification.ts +45 -7
  532. package/src/dev/hot-reload.test.ts +0 -4
  533. package/src/dev/hot-reload.ts +11 -30
  534. package/src/dev/module-runner.ts +7 -32
  535. package/src/dev/reload-meta.ts +9 -29
  536. package/src/errors/error-handler.ts +20 -35
  537. package/src/errors/error.test.ts +30 -1
  538. package/src/errors/errors.ts +73 -157
  539. package/src/function/abort-scope.test.ts +97 -0
  540. package/src/function/abort-scope.ts +80 -0
  541. package/src/function/function-runner.test.ts +0 -7
  542. package/src/function/function-runner.ts +62 -32
  543. package/src/function/functions.types.ts +95 -138
  544. package/src/function/index.ts +1 -0
  545. package/src/function/list.types.test.ts +3 -25
  546. package/src/function/list.types.ts +12 -62
  547. package/src/handle-error.ts +0 -18
  548. package/src/index.ts +84 -3
  549. package/src/middleware/auth-apikey.test.ts +0 -1
  550. package/src/middleware/auth-apikey.ts +0 -17
  551. package/src/middleware/auth-bearer.test.ts +3 -5
  552. package/src/middleware/auth-bearer.ts +5 -41
  553. package/src/middleware/auth-cookie.test.ts +0 -6
  554. package/src/middleware/auth-cookie.ts +2 -26
  555. package/src/middleware/cors.test.ts +34 -0
  556. package/src/middleware/cors.ts +12 -33
  557. package/src/middleware/remote-auth.test.ts +26 -10
  558. package/src/middleware/remote-auth.ts +11 -3
  559. package/src/middleware/telemetry.ts +2 -31
  560. package/src/middleware-runner.test.ts +0 -2
  561. package/src/middleware-runner.ts +5 -74
  562. package/src/permissions.test.ts +30 -0
  563. package/src/permissions.ts +24 -74
  564. package/src/pikku-request.ts +0 -6
  565. package/src/pikku-state.ts +2 -30
  566. package/src/production-barrels-stay-lean.test.ts +110 -0
  567. package/src/remote.test.ts +173 -0
  568. package/src/remote.ts +18 -8
  569. package/src/schema.ts +5 -39
  570. package/src/scopes.ts +7 -48
  571. package/src/secret-value.test.ts +204 -0
  572. package/src/secret-value.ts +111 -0
  573. package/src/services/ai-agent-runner-service.ts +20 -0
  574. package/src/services/ai-embedding-service.ts +3 -25
  575. package/src/services/audit-service.ts +88 -11
  576. package/src/services/content-service.ts +1 -46
  577. package/src/services/credential-service.ts +3 -40
  578. package/src/services/credential-wire-service.test.ts +0 -2
  579. package/src/services/credential-wire-service.ts +9 -1
  580. package/src/services/deployment-service.ts +3 -9
  581. package/src/services/email-service.ts +3 -1
  582. package/src/services/gateway-service.ts +0 -15
  583. package/src/services/{http-scenario-actors-converse.test.ts → http-personas-converse.test.ts} +38 -9
  584. package/src/services/{http-scenario-actors.test.ts → http-personas.test.ts} +39 -22
  585. package/src/services/{http-scenario-actors.ts → http-personas.ts} +85 -45
  586. package/src/services/in-memory-queue-service.ts +1 -15
  587. package/src/services/in-memory-trigger-service.ts +1 -18
  588. package/src/services/in-memory-workflow-service.test.ts +0 -13
  589. package/src/services/in-memory-workflow-service.ts +4 -38
  590. package/src/services/index.ts +23 -18
  591. package/src/services/istanbul-coverage-service.ts +2 -8
  592. package/src/services/jwt-service.ts +1 -16
  593. package/src/services/local-content-request-handler.test.ts +202 -0
  594. package/src/services/local-content-request-handler.ts +267 -0
  595. package/src/services/local-content.test.ts +159 -27
  596. package/src/services/local-content.ts +55 -23
  597. package/src/services/local-gateway-service.ts +2 -17
  598. package/src/services/local-secrets.test.ts +20 -5
  599. package/src/services/local-secrets.ts +15 -11
  600. package/src/services/logger-console.test.ts +0 -1
  601. package/src/services/logger-console.ts +3 -7
  602. package/src/services/logger.ts +31 -46
  603. package/src/services/meta-service.test.ts +1 -5
  604. package/src/services/meta-service.ts +41 -61
  605. package/src/services/{scenario-actors-service.ts → personas-service.ts} +48 -43
  606. package/src/services/pikku-user-id.ts +0 -4
  607. package/src/services/queue-webhook-service.test.ts +2 -1
  608. package/src/services/queue-webhook-service.ts +10 -42
  609. package/src/services/scheduler-service.ts +1 -50
  610. package/src/services/schema-service.ts +1 -24
  611. package/src/services/scope-service.ts +50 -34
  612. package/src/services/scoped-secret-service.ts +4 -7
  613. package/src/services/secret-host-binding.test.ts +138 -0
  614. package/src/services/secret-host-binding.ts +51 -0
  615. package/src/services/secret-service.ts +12 -35
  616. package/src/services/secretless.test.ts +54 -0
  617. package/src/services/secretless.ts +29 -0
  618. package/src/services/stub-tracker.ts +8 -18
  619. package/src/services/system-role-guard.test.ts +93 -0
  620. package/src/services/system-role-guard.ts +71 -0
  621. package/src/services/trigger-service.ts +0 -12
  622. package/src/services/typed-secret-service.ts +12 -14
  623. package/src/services/v8-coverage-service.ts +3 -6
  624. package/src/services/variables-service.ts +1 -8
  625. package/src/services/webhook-service.ts +23 -64
  626. package/src/services/workflow-service.ts +3 -20
  627. package/src/testing/service-tests.ts +6 -32
  628. package/src/time-utils.ts +1 -19
  629. package/src/types/core.types.ts +137 -229
  630. package/src/types/state.types.ts +7 -9
  631. package/src/utils/hmac.ts +4 -10
  632. package/src/utils/safe-fetch.ts +13 -54
  633. package/src/utils.test.ts +11 -2
  634. package/src/utils.ts +6 -15
  635. package/src/wirings/actor-flow/actor-flow.types.ts +1 -34
  636. package/src/wirings/actor-flow/index.ts +0 -9
  637. package/src/wirings/actor-flow/run-conversation.test.ts +11 -6
  638. package/src/wirings/actor-flow/run-conversation.ts +19 -12
  639. package/src/wirings/ai-agent/ai-agent-agui.test.ts +91 -10
  640. package/src/wirings/ai-agent/ai-agent-agui.ts +49 -17
  641. package/src/wirings/ai-agent/ai-agent-helpers.ts +20 -0
  642. package/src/wirings/ai-agent/ai-agent-interrupt.test.ts +842 -0
  643. package/src/wirings/ai-agent/ai-agent-interrupt.ts +399 -0
  644. package/src/wirings/ai-agent/ai-agent-memory.ts +0 -2
  645. package/src/wirings/ai-agent/ai-agent-model-config.ts +1 -9
  646. package/src/wirings/ai-agent/ai-agent-prepare.test.ts +202 -31
  647. package/src/wirings/ai-agent/ai-agent-prepare.ts +89 -139
  648. package/src/wirings/ai-agent/ai-agent-registry.test.ts +191 -6
  649. package/src/wirings/ai-agent/ai-agent-registry.ts +18 -1
  650. package/src/wirings/ai-agent/ai-agent-resume-authorization.test.ts +0 -2
  651. package/src/wirings/ai-agent/ai-agent-runner.test.ts +11 -9
  652. package/src/wirings/ai-agent/ai-agent-runner.ts +85 -35
  653. package/src/wirings/ai-agent/ai-agent-stream.test.ts +205 -103
  654. package/src/wirings/ai-agent/ai-agent-stream.ts +224 -76
  655. package/src/wirings/ai-agent/ai-agent-thread-ownership.test.ts +301 -0
  656. package/src/wirings/ai-agent/ai-agent.types.ts +130 -5
  657. package/src/wirings/ai-agent/index.ts +37 -3
  658. package/src/wirings/ai-agent/voice-input.test.ts +137 -7
  659. package/src/wirings/ai-agent/voice-input.ts +96 -12
  660. package/src/wirings/ai-agent/voice-output.test.ts +512 -0
  661. package/src/wirings/ai-agent/voice-output.ts +243 -56
  662. package/src/wirings/channel/channel-common.ts +15 -20
  663. package/src/wirings/channel/channel-handler.test.ts +50 -0
  664. package/src/wirings/channel/channel-handler.ts +32 -11
  665. package/src/wirings/channel/channel-host-rpc.test.ts +150 -0
  666. package/src/wirings/channel/channel-host-rpc.ts +69 -0
  667. package/src/wirings/channel/channel-middleware-runner.test.ts +0 -1
  668. package/src/wirings/channel/channel-middleware-runner.ts +0 -12
  669. package/src/wirings/channel/channel-rpc-registry.ts +116 -0
  670. package/src/wirings/channel/channel-rpc-responder.ts +117 -0
  671. package/src/wirings/channel/channel-rpc-service.ts +146 -0
  672. package/src/wirings/channel/channel-rpc-validators.ts +65 -0
  673. package/src/wirings/channel/channel-rpc.test.ts +820 -0
  674. package/src/wirings/channel/channel-rpc.ts +5 -0
  675. package/src/wirings/channel/channel-rpc.types.ts +150 -0
  676. package/src/wirings/channel/channel-runner.ts +0 -14
  677. package/src/wirings/channel/channel-store.ts +0 -10
  678. package/src/wirings/channel/channel.types.ts +19 -8
  679. package/src/wirings/channel/define-channel-routes.ts +0 -20
  680. package/src/wirings/channel/eventhub-service.ts +0 -18
  681. package/src/wirings/channel/index.ts +35 -0
  682. package/src/wirings/channel/local/local-channel-handler.ts +3 -1
  683. package/src/wirings/channel/local/local-channel-runner.test.ts +0 -10
  684. package/src/wirings/channel/local/local-channel-runner.ts +3 -1
  685. package/src/wirings/channel/local/local-eventhub-service.test.ts +0 -13
  686. package/src/wirings/channel/local/local-eventhub-service.ts +2 -37
  687. package/src/wirings/channel/log-channels.ts +0 -4
  688. package/src/wirings/channel/pikku-abstract-channel-handler.test.ts +83 -2
  689. package/src/wirings/channel/pikku-abstract-channel-handler.ts +7 -0
  690. package/src/wirings/channel/serverless/serverless-channel-runner.ts +2 -5
  691. package/src/wirings/cli/channel/cli-approval.test.ts +177 -0
  692. package/src/wirings/cli/channel/cli-approval.ts +135 -0
  693. package/src/wirings/cli/channel/cli-channel-runner.ts +4 -26
  694. package/src/wirings/cli/channel/cli-raw-channel-runner.test.ts +169 -0
  695. package/src/wirings/cli/channel/cli-raw-channel-runner.ts +59 -16
  696. package/src/wirings/cli/channel/cli-raw-client-runner.test.ts +480 -0
  697. package/src/wirings/cli/channel/cli-raw-client-runner.ts +185 -0
  698. package/src/wirings/cli/channel/index.ts +13 -0
  699. package/src/wirings/cli/cli-runner.test.ts +0 -1
  700. package/src/wirings/cli/cli-runner.ts +46 -88
  701. package/src/wirings/cli/cli.types.ts +14 -3
  702. package/src/wirings/cli/command-parser.test.ts +0 -4
  703. package/src/wirings/cli/command-parser.ts +11 -91
  704. package/src/wirings/cli/define-cli-commands.ts +1 -17
  705. package/src/wirings/credential/credential.types.ts +0 -12
  706. package/src/wirings/credential/{wire-credential.ts → define-credential.ts} +7 -7
  707. package/src/wirings/credential/index.ts +1 -1
  708. package/src/wirings/credential/validate-credential-definitions.ts +2 -4
  709. package/src/wirings/gateway/gateway-runner.test.ts +1 -21
  710. package/src/wirings/gateway/gateway-runner.ts +8 -110
  711. package/src/wirings/gateway/gateway.types.ts +7 -80
  712. package/src/wirings/http/http-routes.test.ts +0 -3
  713. package/src/wirings/http/http-routes.ts +0 -86
  714. package/src/wirings/http/http-runner.test.ts +0 -1
  715. package/src/wirings/http/http-runner.ts +8 -167
  716. package/src/wirings/http/http.types.ts +15 -62
  717. package/src/wirings/http/log-http-routes.ts +0 -4
  718. package/src/wirings/http/pikku-fetch-http-request.test.ts +2 -10
  719. package/src/wirings/http/pikku-fetch-http-request.ts +0 -58
  720. package/src/wirings/http/pikku-fetch-http-response.test.ts +1 -1
  721. package/src/wirings/http/pikku-fetch-http-response.ts +0 -3
  722. package/src/wirings/http/routers/path-to-regex.test.ts +4 -17
  723. package/src/wirings/http/routers/path-to-regex.ts +2 -13
  724. package/src/wirings/http/web-request.test.ts +33 -2
  725. package/src/wirings/http/web-request.ts +30 -17
  726. package/src/wirings/mcp/mcp-endpoint-registry.test.ts +0 -1
  727. package/src/wirings/mcp/mcp-runner.ts +2 -17
  728. package/src/wirings/mcp/mcp.types.ts +7 -42
  729. package/src/wirings/oauth2/oauth2.types.ts +0 -30
  730. package/src/wirings/persona/define-personas.ts +33 -0
  731. package/src/wirings/persona/index.ts +62 -0
  732. package/src/wirings/persona/persona-email.ts +87 -0
  733. package/src/wirings/persona/persona-environments.test.ts +183 -0
  734. package/src/wirings/persona/persona-environments.ts +138 -0
  735. package/src/wirings/persona/persona-mailbox.ts +156 -0
  736. package/src/wirings/persona/persona.test.ts +220 -0
  737. package/src/wirings/persona/persona.types.ts +142 -0
  738. package/src/wirings/persona/validate-personas.ts +133 -0
  739. package/src/wirings/queue/index.ts +13 -3
  740. package/src/wirings/queue/queue-identity.test.ts +454 -0
  741. package/src/wirings/queue/queue-identity.ts +176 -0
  742. package/src/wirings/queue/queue-runner.ts +12 -31
  743. package/src/wirings/queue/queue.types.ts +25 -90
  744. package/src/wirings/queue/register-queue-helper.ts +0 -14
  745. package/src/wirings/queue/signed-queue-service.ts +60 -0
  746. package/src/wirings/queue/validate-worker-config.ts +2 -28
  747. package/src/wirings/role/define-system-role.ts +33 -0
  748. package/src/wirings/role/index.ts +13 -0
  749. package/src/wirings/role/role.test.ts +104 -0
  750. package/src/wirings/role/role.types.ts +47 -0
  751. package/src/wirings/role/validate-role-definitions.ts +93 -0
  752. package/src/wirings/rpc/addon-auth-tags.test.ts +223 -0
  753. package/src/wirings/rpc/addon-runner.ts +0 -56
  754. package/src/wirings/rpc/addon-scopes.test.ts +225 -0
  755. package/src/wirings/rpc/remote-addon-auth.ts +9 -16
  756. package/src/wirings/rpc/rpc-runner.test.ts +192 -6
  757. package/src/wirings/rpc/rpc-runner.ts +145 -127
  758. package/src/wirings/rpc/rpc-types.ts +11 -6
  759. package/src/wirings/rpc/wire-addon.test.ts +43 -1
  760. package/src/wirings/rpc/wire-addon.ts +99 -0
  761. package/src/wirings/rpc/wire-remote-addon.ts +9 -29
  762. package/src/wirings/scheduler/log-schedulers.ts +0 -4
  763. package/src/wirings/scheduler/scheduler-runner.test.ts +1 -8
  764. package/src/wirings/scheduler/scheduler-runner.ts +0 -2
  765. package/src/wirings/scheduler/scheduler.types.ts +1 -14
  766. package/src/wirings/scope/{wire-scope.ts → define-scope.ts} +5 -6
  767. package/src/wirings/scope/index.ts +1 -1
  768. package/src/wirings/scope/scope.test.ts +1 -2
  769. package/src/wirings/scope/scope.types.ts +7 -9
  770. package/src/wirings/scope/validate-scope-definitions.ts +3 -21
  771. package/src/wirings/secret/index.ts +1 -1
  772. package/src/wirings/secret/secret.types.ts +19 -15
  773. package/src/wirings/secret/validate-secret-definitions.ts +2 -4
  774. package/src/wirings/trigger/trigger-runner.ts +1 -27
  775. package/src/wirings/trigger/trigger.types.ts +1 -82
  776. package/src/wirings/variable/index.ts +1 -1
  777. package/src/wirings/variable/validate-variable-definitions.ts +2 -4
  778. package/src/wirings/variable/variable.types.ts +1 -13
  779. package/src/wirings/virtual-user/index.ts +88 -0
  780. package/src/wirings/virtual-user/prepare-virtual-user-run.test.ts +115 -0
  781. package/src/wirings/virtual-user/prepare-virtual-user-run.ts +95 -0
  782. package/src/wirings/virtual-user/run-virtual-user.test.ts +765 -0
  783. package/src/wirings/virtual-user/run-virtual-user.ts +671 -0
  784. package/src/wirings/virtual-user/virtual-user-agents.test.ts +65 -0
  785. package/src/wirings/virtual-user/virtual-user-agents.ts +57 -0
  786. package/src/wirings/virtual-user/virtual-user-catalogue.test.ts +215 -0
  787. package/src/wirings/virtual-user/virtual-user-catalogue.ts +184 -0
  788. package/src/wirings/virtual-user/virtual-user-derive.test.ts +398 -0
  789. package/src/wirings/virtual-user/virtual-user-derive.ts +173 -0
  790. package/src/wirings/virtual-user/virtual-user-dispositions.test.ts +63 -0
  791. package/src/wirings/virtual-user/virtual-user-dispositions.ts +213 -0
  792. package/src/wirings/virtual-user/virtual-user-intents.test.ts +208 -0
  793. package/src/wirings/virtual-user/virtual-user-intents.ts +185 -0
  794. package/src/wirings/virtual-user/virtual-user-rng.test.ts +72 -0
  795. package/src/wirings/virtual-user/virtual-user-rng.ts +50 -0
  796. package/src/wirings/virtual-user/virtual-user-run-store.ts +98 -0
  797. package/src/wirings/virtual-user/virtual-user-target.ts +47 -0
  798. package/src/wirings/virtual-user/virtual-user.types.ts +219 -0
  799. package/src/wirings/workflow/dsl/workflow-dsl.types.ts +19 -20
  800. package/src/wirings/workflow/dsl/workflow-runner.ts +0 -4
  801. package/src/wirings/workflow/feature.ts +0 -19
  802. package/src/wirings/workflow/graph/graph-node.ts +0 -136
  803. package/src/wirings/workflow/graph/graph-runner.test.ts +20 -19
  804. package/src/wirings/workflow/graph/graph-runner.ts +6 -41
  805. package/src/wirings/workflow/graph/wire-workflow-graph.ts +0 -4
  806. package/src/wirings/workflow/graph/workflow-graph.types.ts +0 -58
  807. package/src/wirings/workflow/index.ts +10 -44
  808. package/src/wirings/workflow/pikku-scenario-service.ts +235 -61
  809. package/src/wirings/workflow/pikku-workflow-service.test.ts +0 -39
  810. package/src/wirings/workflow/pikku-workflow-service.ts +77 -674
  811. package/src/wirings/workflow/run-timeline.test.ts +7 -19
  812. package/src/wirings/workflow/run-timeline.ts +0 -56
  813. package/src/wirings/workflow/scenario-cookie-jar.test.ts +0 -1
  814. package/src/wirings/workflow/scenario-cookie-jar.ts +0 -25
  815. package/src/wirings/workflow/scenario-expectations.test.ts +2 -7
  816. package/src/wirings/workflow/scenario-hooks.test.ts +2 -7
  817. package/src/wirings/workflow/scenario-poll.test.ts +0 -2
  818. package/src/wirings/workflow/scenario-poll.ts +0 -15
  819. package/src/wirings/workflow/scenario-prose.test.ts +5 -7
  820. package/src/wirings/workflow/scenario-prose.ts +1 -29
  821. package/src/wirings/workflow/scenario-service.test.ts +2 -10
  822. package/src/wirings/workflow/scenario-step-guards.ts +1 -14
  823. package/src/wirings/workflow/scenario-step.test.ts +163 -19
  824. package/src/wirings/workflow/scenario-step.types.ts +94 -12
  825. package/src/wirings/workflow/scenario-surface.test.ts +146 -0
  826. package/src/wirings/workflow/scenario-surface.ts +71 -0
  827. package/src/wirings/workflow/workflow-dispatch-durability.test.ts +14 -15
  828. package/src/wirings/workflow/workflow-dispatch-payload.test.ts +0 -4
  829. package/src/wirings/workflow/workflow-inline-authority.test.ts +169 -0
  830. package/src/wirings/workflow/workflow-invocation-id.test.ts +0 -2
  831. package/src/wirings/workflow/workflow-invocation-id.ts +2 -22
  832. package/src/wirings/workflow/workflow-mirror.test.ts +0 -7
  833. package/src/wirings/workflow/workflow-on-error.test.ts +0 -9
  834. package/src/wirings/workflow/workflow-queue-workers.ts +0 -21
  835. package/src/wirings/workflow/workflow-replay-snapshot.test.ts +8 -7
  836. package/src/wirings/workflow/workflow-retry-policy.test.ts +0 -5
  837. package/src/wirings/workflow/workflow-run-context.test.ts +5 -10
  838. package/src/wirings/workflow/workflow-run-polling.test.ts +0 -5
  839. package/src/wirings/workflow/workflow-step-ordinal.test.ts +19 -4
  840. package/src/wirings/workflow/workflow-step-session.test.ts +0 -7
  841. package/src/wirings/workflow/workflow.types.ts +5 -201
  842. package/tsconfig.tsbuildinfo +1 -1
  843. package/tsconfig.type-tests.json +12 -0
  844. package/src/middleware/timeout.ts +0 -22
  845. package/src/pikku-response.ts +0 -5
  846. package/src/wirings/mcp/mcp-endpoint-registry.test.d.ts +0 -1
  847. package/src/wirings/workflow/dsl/index.ts +0 -36
  848. package/src/wirings/workflow/graph/index.ts +0 -15
package/CHANGELOG.md CHANGED
@@ -1,3 +1,1174 @@
1
+ ## 0.12.77
2
+
3
+ ### Patch Changes
4
+
5
+ - 3df4f95: Scaffold virtual user runs as RPCs, backed by a run store.
6
+
7
+ `pikku persona run` could already turn a declared persona loose on a running
8
+ stage, but only from a terminal, and the result only existed in that terminal's
9
+ output. There was no way for CI, a console, or a scheduled job to start a run —
10
+ and nothing kept what a run found, so this week's findings could not be compared
11
+ against last week's.
12
+
13
+ `scaffold.virtualUser` now generates two RPCs and the function behind them:
14
+ - `runVirtualUser({ persona, goals?, memory?, disposition?, budget?, seed? })
15
+ -> { runId }`
16
+ - `getVirtualUserRun({ runId }) -> { status, findings, tally, memory, … }`
17
+
18
+ They are gated on separate scopes — `virtualUser:run` and `virtualUser:read` —
19
+ because an adversarial run's findings are working exploits carrying live ids,
20
+ which makes reading them the more sensitive of the two. Production refuses every
21
+ disposition but `accountable`, checked against the effective one so the
22
+ per-run override cannot smuggle another in.
23
+
24
+ **A run is not a workflow and not a queued job.** It explores, so no two
25
+ attempts take the same steps and there is nothing to replay; and the record
26
+ already carries the progress a queue would only be holding on the way to the
27
+ same place. `runVirtualUser` writes the record, dispatches without awaiting, and
28
+ returns the id. The cost is stated on the type: a restart mid-run strands a
29
+ record at `running`, so a run older than its budget window and still `running`
30
+ is dead rather than working.
31
+
32
+ `@pikku/core` gains `VirtualUserRunStore` (with `virtualUserRunStore` on
33
+ `CoreSingletonServices`), and `@pikku/kysely` ships
34
+ `KyselyVirtualUserRunStore`, which creates its own table on first use like the
35
+ audit sink — the runtime never needs it, so it arrives with the feature that
36
+ fills it rather than in every database.
37
+
38
+ Also in core: `prepareVirtualUserRun`, which derives the catalogue, intents,
39
+ scopes and reachable agents in one place. `pikku persona run` reads the
40
+ inspector state and the generated RPC reads `metaService`, and the two have to
41
+ agree — otherwise the same persona and seed explore a different API depending on
42
+ how the run was started. `personaScopes` moved here from the CLI for the same
43
+ reason and is still re-exported from its old home. `PRODUCTION_DISPOSITION` is
44
+ now exported from `@pikku/core/virtual-user`, which it should always have been.
45
+
46
+ ## 0.12.76
47
+
48
+ ### Patch Changes
49
+
50
+ - 62ea4cc: The audit trail is now readable — in the generated meta, through an RPC, and as
51
+ a page in the console.
52
+
53
+ `audit: true` reaches `FunctionRuntimeMeta.audit` as its resolved form
54
+ (`{ durability }`), so which functions record anything is answerable without
55
+ running them. It is informational: the runner still resolves audit from the live
56
+ function config, so meta and runtime cannot disagree.
57
+
58
+ `AuditService` grows an optional read side — `query(AuditQuery)` and `facets()`.
59
+ Optional because a sink can legitimately be write-only: a queue producer that
60
+ hands events to another system has nothing to read back, and a reader that finds
61
+ these absent should say the trail is not readable here rather than that it is
62
+ empty. The two are very different answers to give someone auditing a system.
63
+
64
+ `KyselyAuditService` implements both, newest first with offset paging, filtered
65
+ by user, action and time window. Two things it now gets right that are easy to
66
+ get wrong: an empty filter array means "match nothing" rather than "no filter",
67
+ and results are read by physical _and_ camelCase key, because `CamelCasePlugin`
68
+ is on most pikku Kysely instances and renames result keys on the way out — the
69
+ mismatch does not throw, it returns a page of `undefined`. `init()` creates the
70
+ `audit` table for projects that do not migrate it themselves, from a new
71
+ exported `auditSchema` that stays out of `pikkuSchemas` because the runtime does
72
+ not need it.
73
+
74
+ The console addon exposes `console:getAudits` and `console:getAuditFilters`
75
+ behind a new `pikku:audit:read` scope, and forwards the application's `audit`
76
+ service into the addon's own services — without that last part every install
77
+ reported the trail as unreadable, whatever sink it had configured.
78
+
79
+ The console gets an Audit trail page: an infinite list filtered server-side by
80
+ user and action, and a row that opens the whole event, metadata rendered as a
81
+ JSON tree. Refused, unreadable and empty are three different screens, because
82
+ "you may not read this", "nobody can read this" and "nothing happened" are three
83
+ different facts.
84
+
85
+ Events name the person who caused them. The trail records a user id — the only
86
+ thing stable enough to record, since a name can change after the event — so
87
+ `getAudits` resolves those ids against better-auth's user directory at read
88
+ time, and the page shows the name while keeping the recorded id on the event.
89
+ The filter follows: pick a colleague by name, filter by the id. A scenario
90
+ actor is labelled as one, so synthetic traffic is not mistaken for real, and a
91
+ caller who was signed out shows the wire identity pikku resolved for them
92
+ rather than being credited to the system.
93
+
94
+ **Breaking, for anyone already reading `AuditEvent`:** `actor` is now
95
+ `userIdentity`, and its type `AuditActor` is `AuditUserIdentity`; `AuditQuery`
96
+ takes `userIds`/`orgId` in place of `actorUserIds`/`actorOrgId`, and
97
+ `AuditFacets` returns `userIds`. In pikku an _actor_ is a synthetic person a
98
+ scenario drives, flagged on the user row — so naming the causer of an event
99
+ `actor` made the synthetic case unsayable (`actor.actor === true`) and implied
100
+ every recorded action was a test. The overwhelming majority are ordinary
101
+ customers. The `audit` table follows: `actor_user_id` / `actor_org_id`
102
+ are now `user_id` / `org_id`, and a `pikku_user_id` column joins them so the
103
+ wire identity of a caller who never signed in survives the round trip — the
104
+ sink was dropping it, which left the console's Session field permanently
105
+ blank. A project that already migrated the table needs to rename the two
106
+ columns and add the third; `KyselyAuditService.init()` creates the new shape
107
+ for anyone who did not.
108
+
109
+ - 9dddff8: Split a column's at-rest form out of its classification.
110
+
111
+ `security: 'encrypted'` sat beside `'secret'` as though the two were
112
+ alternatives, which made the field unanswerable: a token hash and a live bearer
113
+ token are both secret, one must never be encrypted — the digest _is_ the lookup
114
+ key — and the other must always be. A column now carries a second, independent
115
+ `form: 'plain' | 'hashed' | 'wrapped' | 'sealed'` saying how the bytes are held.
116
+
117
+ Declaring a form other than `plain` makes the column's INSERT/UPDATE type
118
+ nominal — `WrappedValue`, `SealedValue`, `HashedValue` — so a plain string no
119
+ longer compiles there and the only way to write the column is with something an
120
+ encrypt, seal or hash call produced. `envelopeEncrypt`, `envelopeRewrap` and
121
+ `wrapDEK` now return the brand, and a new `hashToken` produces `HashedValue`, so
122
+ the round trip needs no casts; `column-form.ts` exports deliberately-named
123
+ `unsafeAs*` assertions for backfills, fixtures and values sealed elsewhere.
124
+ Reads are unaffected — the brands widen to `string` and compose with the
125
+ classification brand as `Secret<WrappedValue>`.
126
+
127
+ `wrapped` and `sealed` stay distinct because a sealed value is one the
128
+ application cannot read back; storing one where the other belongs is a row
129
+ nobody can open.
130
+
131
+ A `secret` column that has not declared a form now warns (PKU483), and a form on
132
+ a non-text column warns and is dropped (PKU484). Both are warnings, so existing
133
+ projects keep migrating — `pikku db --fail-on-warn` opts into the ratchet, and
134
+ an explicit `form: 'plain'` is the acknowledgement that silences it. The legacy
135
+ `security: 'encrypted'` keeps working and now expands to the pair it always
136
+ meant, `secret` + `wrapped`.
137
+
138
+ - 78b29f0: `SecretService` now returns a `SecretValue<T>` rather than the bare value, so a
139
+ vault secret cannot reach a sink by accident.
140
+
141
+ `SecretValue` is nominally typed, which means it is not assignable to `string`
142
+ (or to any other concretely-typed field). Every sink with a real type — a
143
+ database column, an email body, a session payload — rejects it with no lint
144
+ rule involved. The sinks typed `any`, `unknown`, or a free generic — the logger,
145
+ queue payloads, webhook and email inputs, and a function's own output — are
146
+ guarded with `Safe<T>`, which collapses a `SecretValue` found anywhere inside
147
+ `T`, however deeply nested, to `never`.
148
+
149
+ Unwrap deliberately at the point the secret reaches the wire:
150
+
151
+ ```ts
152
+ const secret = await secrets.getSecret('BETTER_AUTH_SECRET')
153
+ betterAuth({ secret: secret.reveal() })
154
+ ```
155
+
156
+ Two behaviours cover what types cannot see. Structured serialization redacts —
157
+ `JSON.stringify` and node's inspect both yield `[secret]`, so an audit or log
158
+ write stays honest without crashing the request. String coercion throws
159
+ `SecretCoercionError`, because a template literal is always a leak.
160
+
161
+ `AuditLog.write` is guarded the same way as the logger, since an audit event
162
+ carries `input` and `metadata` as `unknown` and nominality alone cannot stop a
163
+ secret landing in one.
164
+
165
+ `.reveal()` is the deliberate escape hatch, and what it hands back is an
166
+ ordinary string as far as every sink signature is concerned. **PKU953** closes
167
+ that gap: under `pikku all --security` the inspector reports a revealed secret
168
+ that flows into a logger, an audit, a queue, an email or a webhook — `console` included.
169
+
170
+ This also fixed a real one: `remote-addon-auth.ts` called `String(token)` on an
171
+ `unknown` and wrote the result straight into an `Authorization` header.
172
+
173
+ ## 0.12.75
174
+
175
+ ### Patch Changes
176
+
177
+ - 32277d5: Make a voice conversation with an agent something a chat surface can turn on, rather than
178
+ something each consumer reassembles.
179
+
180
+ The server half already worked — `voiceInput` transcribed, `voiceOutput` synthesized a
181
+ sentence at a time, and the AG-UI mapper forwarded the audio. What was missing was the
182
+ turn's own words. The client sends audio, so only the server ever knows what was said,
183
+ and nothing carried that back: a spoken turn rendered as an empty user bubble followed by
184
+ an answer to a question nobody could see, and thread history recorded the base64 audio
185
+ blob instead of the transcript — megabytes of unreadable data in place of the only
186
+ readable record of the turn.
187
+
188
+ `voiceInput` now records what it heard, the stream emits it as a `transcript` event ahead
189
+ of the run (the reply starts within a few hundred milliseconds, and a question that
190
+ appears after its answer reads as the wrong question), and it reaches the browser as
191
+ `pikku:transcript`. Both run paths persist the transcribed message rather than the one
192
+ that arrived on the wire. `audio-delta` also carries the sentence it says, which is what
193
+ a barge-in needs to report the part the user actually heard — a reply cut off after "I'll
194
+ delete the staging database and" is answered very differently depending on whether the
195
+ model knows the sentence never landed.
196
+
197
+ `@pikku/voice-agents` gains the two things a voice UI needs and could not get: a live
198
+ input level, attached to the source rather than to a detector so it keeps reading on the
199
+ Silero path, and the microphone list — re-readable on demand, because device labels are
200
+ empty until permission is granted and nothing fires when it is. `VoiceSession` also
201
+ learned manual turn boundaries, so push-to-talk is a mode rather than a detector fought
202
+ to a standstill: holding the key through a three-second pause is someone thinking, and
203
+ any endpointer worth having would cut them off.
204
+
205
+ `<PikkuAgentChat voice />` puts a microphone beside the send button, promotes it to
206
+ primary when nothing is typed, and opens an indicator with a live level bar, a device
207
+ picker and a hold-to-record toggle. It plays the agent's speech, and cancels the run on
208
+ barge-in — talking over the agent should stop the bill, not just the sound.
209
+
210
+ Opt-in, because the component cannot check the two things it depends on: the agent has to
211
+ be wired with `voiceInput` for the audio to be understood and `voiceOutput` for anything
212
+ to come back.
213
+
214
+ - ea8aabf: Serve `LocalContent` uploads and signed reads under Bun.
215
+
216
+ `LocalContent` hands the browser a `PUT <uploadUrlPrefix>/<key>` upload URL and a signed
217
+ `GET <assetUrlPrefix>/<key>` read URL, but it is a `ContentService` and cannot answer
218
+ either — something in the serving path has to. Only `@pikku/node-http-server` did. The
219
+ same project served under Bun handed out upload URLs that 404ed, with nothing naming the
220
+ cause: the config was accepted, the service was constructed, and the URLs looked right.
221
+
222
+ `@pikku/core` now exports `createLocalContentRequestHandler` from
223
+ `@pikku/core/services/local-content-request-handler` — the server half of `LocalContent`,
224
+ expressed in Web `Request`/`Response` so every runtime shares one implementation of the
225
+ signature check rather than each re-deriving it. It returns `null` for anything that is
226
+ not a content request, which is the caller's signal to carry on with its normal routing.
227
+
228
+ `PikkuBunServer` accepts `config.content` and a `contentSigningJWT` option, mirroring
229
+ `PikkuNodeHTTPServer`, and answers both prefixes ahead of static mounts and routing.
230
+ `BunServerRunner` was dropping `contentSigningJWT` on the floor, which silently disabled
231
+ signed asset reads for every Bun project even once the prefixes were served — the config
232
+ arrived, the service that verifies its signatures did not.
233
+
234
+ Signed reads are refused unless every claim matches, the path included: without that, a
235
+ signature minted for one asset would read any other.
236
+
237
+ - 33e96ab: Make a CLI served over a channel typecheck in a real project.
238
+
239
+ Both of these are unreachable for a hand-written `wireChannel`, whose routes are usually
240
+ bare identifiers, and unavoidable for a CLI one, whose routes are command ids.
241
+
242
+ `ChannelsMap` emitted route and message keys unquoted. A command id is a kebab or dotted
243
+ name far more often than not — `app-smoke`, `registry.search`, `package.upgrade-pikku` —
244
+ and each one ends the property early, so the generated map is not parseable TypeScript at
245
+ all. One project's map came out with 107 syntax errors from a single CLI channel. Keys are
246
+ now quoted when they are not bare identifiers, and left alone when they are, so existing
247
+ generated output is unchanged.
248
+
249
+ `executeRawCLIViaChannel` typed its renderers `Record<string, CorePikkuCLIRender<any>>`,
250
+ whose services parameter defaults to `CoreServices`. The renderers a generated client
251
+ passes are the app's own, typed against its `SingletonServices`, and a function taking
252
+ those is not assignable to one taking `CoreServices` — so the generated client failed to
253
+ compile for any app that adds a service, which is every app.
254
+
255
+ Rather than widen the type, it now says what is actually true on that side of the socket:
256
+ a renderer running on the client gets a logger and nothing else, because there is no
257
+ service container there to resolve anything from. `CorePikkuCLIClientRender` and
258
+ `ClientCLIRenderServices` are new exports of `@pikku/core/cli/channel`. They are not
259
+ expressible as `CorePikkuCLIRender`, whose `Services` parameter is constrained to
260
+ `CoreSingletonServices` and so demands a `config`, `variables` and `secrets` the client
261
+ cannot invent. The one cast from the app's renderer type to that shape is localised to the
262
+ generated client, where it is sound: generation refuses to emit the file at all if a
263
+ renderer reaches for a service other than `logger`.
264
+
265
+ - fd72e58: Drop `scenario.step` — a scenario step is now always a `given`, `when` or
266
+ `then`.
267
+
268
+ `step` rendered no keyword, which made it the phase to reach for whenever a
269
+ step did not obviously fit one of the three. That is exactly the step a reader
270
+ cannot check: a scenario is read by people deciding whether it describes the
271
+ behaviour they wanted, and a row that says what it does without saying whether
272
+ it is setup, action or claim tells them nothing to agree or disagree with. It
273
+ was also the escape hatch from the assertion lint — a scenario with no `then`
274
+ could be made to stop complaining by demoting its steps rather than by
275
+ asserting anything.
276
+
277
+ Replace `scenario.step(...)` with whichever of `given`, `when` or `then` the
278
+ step actually is. `then` is not a rename: it makes the step's bindings
279
+ witnesses rather than alternatives, so every declared surface runs and they
280
+ must agree.
281
+
282
+ - fd72e58: Make personas a first-class surface rather than a detail of the test runner.
283
+
284
+ A persona is now read in three places — the knowledge base resolves `persona:`
285
+ URIs against it, scenarios cast it as an actor, and a virtual user runs as it —
286
+ so it gets its own page at `/personas` under a new **People** section in the
287
+ rail, alongside Users. The card is a profile: avatar, name, job title, computed
288
+ address, the system roles they hold, and how many scenarios cast them. Opening
289
+ one expands each role to the scopes it confers, which is the half of the picture
290
+ that explains a 403.
291
+
292
+ `definePersonas` takes an optional `avatarUrl` — any URL a browser can load.
293
+ Nothing is derived from the address: a persona's address is synthetic, so a
294
+ derived identicon would be the same shrug for everyone. Omitted, the console
295
+ keeps drawing the deterministic colour-and-icon avatar from the persona's id.
296
+
297
+ - fd72e58: Read the actors that are not people on the personas page.
298
+
299
+ The platform — the app acting on itself, what `pikkuPlatformScenarioStep`
300
+ declares — now has a row of its own, alongside one per addon whose system a
301
+ step makes act. They sit behind a People / System / All filter that opens on
302
+ the people: a subject holds no roles and signs in as nobody, so leading with it
303
+ would put the rows nothing is authorized through above the ones that are.
304
+
305
+ The platform row is built in rather than derived. A project that has never
306
+ written a platform step still has a platform, and a card that appeared the
307
+ moment somebody declared their first step would read as a feature they had
308
+ switched on.
309
+
310
+ Also: PKU680 now counts `expectService`, `expectError` and `expectEventually`
311
+ as assertions. They are inline steps and carry no phase, so a scenario whose
312
+ only witness was a recorded service call was being told it never asserts.
313
+
314
+ - 894b2f8: `defineScope` and `defineSystemRole` accumulate across call sites again. Only `definePersonas` is one-per-codebase.
315
+
316
+ The previous release made all three single-declaration constructs, which no project scaffolding user-admin could satisfy: the CLI generates a `defineScope` of its own in `user-admin.gen.ts` carrying the whole `admin` tree, and `@pikku/addon-console` spells the same tree out again, so a second hand-written declaration failed the build with PKU583 — and the losing file's scopes were dropped from the metadata rather than merged.
317
+
318
+ Exempting generated files would have reinstated exactly the ambiguity the rule removes, only for the files nobody can read the rule from. The real fix is for `admin` to be a default scope nobody declares, at which point the rule can come back for scopes and roles.
319
+
320
+ `definePersonas` is unaffected: nothing generates one, so its single call site stands.
321
+
322
+ - dd19aa7: Drop `scopes` from sessionless functions, rename `selfAuthenticated`, and make both
323
+ escape hatches opt-in.
324
+
325
+ **`scopes` are gone from `pikkuSessionlessFunc`.** They are AND-ed and `verifyScopes`
326
+ fails closed on a session that does not exist, so every scope listed on a sessionless
327
+ function rejected the anonymous caller it exists to serve. `CorePikkuSessionlessFunctionConfig`
328
+ now states this once in core, and the generated `pikkuSessionlessFunc` / `pikkuVoidFunc`
329
+ configs derive from it — so the field is absent rather than subtracted.
330
+
331
+ `@pikku/addon-console`'s `installAddon` and `installOpenapiAddon` are now `pikkuFunc`.
332
+ Both set `auth: true` and `scopes: ['admin']`, and a test exercises that gate, so the
333
+ scopes were load-bearing — they only compiled as sessionless because the config accepted
334
+ a field it could not honour. No behaviour change: both already required a session.
335
+
336
+ **`selfAuthenticated` is now `permissionsInBody`.** It never described authentication:
337
+ what it records is that the permission check lives in the function body rather than in a
338
+ declared `permissions` entry.
339
+
340
+ **Both escape hatches must be opted into**, via a new `allow` block in
341
+ `pikku.config.json`:
342
+
343
+ ```json
344
+ "allow": { "permissionsInBody": true, "complexWorkflows": true }
345
+ ```
346
+
347
+ Unset means unavailable, and using the feature is a build error naming the flag that
348
+ would permit it — PKU576 for `permissionsInBody`, PKU643 for `pikkuWorkflowComplexFunc`.
349
+ Both trade something the tooling can inspect for something only a reader can verify: a
350
+ permission check buried in a body, or workflow steps that cannot be serialized into the
351
+ graph, replayed, or migrated. Both are occasionally right, and both are the path of least
352
+ resistance whenever the declarative form is merely inconvenient. Whoever owns the project
353
+ makes that call once, in writing, instead of every author making it silently at the call
354
+ site.
355
+
356
+ **PKU574's message no longer contradicts any of this.** Every function it reports is
357
+ sessionless — that is how the population is selected, not a finding — yet it opened by
358
+ reporting that they "require neither a session", then advised adding scopes. It now names
359
+ them as sessionless and recommends only gates an anonymous caller can meet:
360
+ `permissions`, `auth: true`, `wireAddon({ auth: true })`, or dropping `expose: true`.
361
+ `permissionsInBody` is deliberately absent from that list: a diagnostic should not
362
+ advertise its own escape hatch.
363
+
364
+ - 50ec500: Make `defineScope`, `defineSystemRole` and `definePersonas` single-declaration constructs
365
+ — exactly one call site per codebase, the rule `pikkuBetterAuth` has always had.
366
+
367
+ Each of the three already takes a keyed object, so one call declares as many entries as
368
+ you like. Spreading the calls across files bought nothing and cost the thing that matters:
369
+ there was no answer to "where do I add a persona?", so downstream tooling and agents had
370
+ nowhere unambiguous to read from or append to. The only duplicate handling that existed
371
+ caught a narrow case — the same id declared twice with different content — and said
372
+ nothing about the same id declared twice in two files.
373
+
374
+ A second call now fails the build with `PKU583` (`defineScope`), `PKU584`
375
+ (`defineSystemRole`) or `PKU585` (`definePersonas`), naming both source files and saying
376
+ to declare them all in one call. A second call in the _same_ file is refused too: "the
377
+ file" is not an answer either when the file holds two calls.
378
+
379
+ ## 0.12.74
380
+
381
+ ### Patch Changes
382
+
383
+ - 6a307f0: Fix four latent correctness bugs in the function, RPC and error runtimes, and
384
+ remove dead code from the workflow service surface.
385
+
386
+ `WorkflowService.getNodesWithoutSteps` is gone. It was declared on the abstract
387
+ service and implemented by all five storage backends, and nothing ever called
388
+ it — hence the non-core packages in this changeset, which only lose that method.
389
+
390
+ **An RPC could execute twice.** `RPCService.invoke`, its addon path, and
391
+ `rpcWithWire` each wrapped the _execution_ of a resolved function in a `try`
392
+ whose `catch` treated `RPCNotFoundError` as "not found locally" and re-dispatched
393
+ the call through `deploymentService`. A nested `rpc.invoke` to a missing name,
394
+ raised from inside an already-running function, therefore re-ran that function on
395
+ a remote instance after its local side effects had already committed. Resolution
396
+ is now separated from execution on all three paths, so only a genuinely
397
+ unresolvable name reaches the fallback. What escapes to callers is unchanged.
398
+
399
+ **`addonNamespace` leaked between sibling calls.** The function runner's
400
+ middleware path restored `rpc`, `functionId`, `audit` and `addonNamespace` after
401
+ an invocation; the non-middleware path restored the first three but not the
402
+ fourth. A call into an addon function with no middleware left the addon's
403
+ namespace on the wire, so subsequent sibling calls resolved the wrong
404
+ per-instance singletons and `credentialOverrides`.
405
+
406
+ **Errors registered on a subclass never resolved.** `misc.errors` was typed
407
+ `Map<PikkuError, ErrorDetails>` — instances — while `addError` stores
408
+ constructors, a mismatch hidden by its `error: any` parameter. The instance
409
+ lookup in `getErrorResponse` was consequently dead, and lookup fell through to a
410
+ scan comparing `constructor.name`, so a subclass of a registered error got no
411
+ status mapping at all. The map is now typed `Map<PikkuErrorConstructor, …>` and
412
+ lookup walks the prototype chain first. Name matching is retained, deliberately,
413
+ as the fallback that keeps error mapping working when two copies of
414
+ `@pikku/core` are installed.
415
+
416
+ **`createWeakUID` collided across instances.** The prefix was
417
+ `Date.now().toString(36)` evaluated at module load, so any two instances loading
418
+ the module in the same millisecond emitted identical `channelId` and `requestId`
419
+ sequences — reproducibly, not just in principle. It is now seeded lazily from
420
+ `crypto.randomUUID()`.
421
+
422
+ Also: `pikkuState` keys its global map with `Symbol.for` rather than `Symbol`, so
423
+ two copies of the package share registrations instead of silently getting
424
+ disjoint state; and the local channel upgrade path no longer keys its middleware
425
+ cache on the raw request path, which grew the cache without bound while the
426
+ cached value never varied by path.
427
+
428
+ - afef587: Close eleven security weaknesses found in a review of `@pikku/core`. Most are
429
+ breaking, and two invalidate data or credentials already in the wild — read the
430
+ migration notes before upgrading.
431
+
432
+ **Breaking: AI agent thread ownership now fails closed.** Reading, listing,
433
+ resuming or approving an existing thread or run requires a resolved session
434
+ principal (`userId`, or `orgId` for `sessionScope: 'org'`), regardless of the
435
+ agent's `auth` setting. Previously a request without a session had no ownership
436
+ model at all: the caller-supplied `resourceId` was accepted as the ownership
437
+ key, so any caller could read or resume another party's thread by naming its
438
+ `resourceId`. Worse, `threadOwnerConstraint` returned `undefined` for a
439
+ sessionless caller, and `undefined` means _no filter_ rather than _no rows_ —
440
+ so `getAgentThreads` returned every thread in the deployment. It now returns
441
+ `string[]`, empty for a sessionless caller, which every storage backend already
442
+ treats as matching nothing. Sessionless agents still run one-shot conversations,
443
+ each with a fresh unguessable owner; what they lose is cross-request continuity.
444
+ Wire a session to restore it.
445
+
446
+ **Breaking: stored secrets and credentials must be re-entered.** `deriveKey` ran
447
+ a single unsalted round of SHA-256 over the passphrase and used the digest
448
+ directly as the AES-GCM key — roughly one hash per brute-force guess, with one
449
+ rainbow table working against every deployment. It is now PBKDF2-HMAC-SHA256 at
450
+ 600,000 iterations over a random salt. There is no compatibility path: every
451
+ value held by the kysely, mongodb and redis secret services and the kysely
452
+ credential service becomes undecryptable. They fail loud, naming the key and
453
+ `key_version`, so the app hard-fails on first secret read until each is re-set.
454
+
455
+ The KEK salt is scoped to the key version and stored alongside it, rather than
456
+ per secret, so a bulk read costs one derivation instead of N — `getSecrets` over
457
+ 50 secrets went from ~2.3s to ~48ms, and rotation from ~4.6s to ~94ms. This adds
458
+ a salt table (kysely), hash field (redis) or collection (mongodb), created
459
+ automatically on first use.
460
+
461
+ **Breaking: `PIKKU_REMOTE_SECRET` must be at least 32 characters.** The
462
+ remote-RPC session envelope moved from PBKDF2 to HKDF, which expands
463
+ high-entropy key material rather than stretching a low-entropy passphrase. That
464
+ took a remote hop from ~269ms to ~0.4ms — PBKDF2 was running twice per request —
465
+ but HKDF supplies no brute-force resistance, so the secret must carry the
466
+ entropy itself. A shorter secret now throws `WeakKeyMaterialError` at both ends.
467
+ Generate one with `openssl rand -base64 32` and roll it out to every service in
468
+ the mesh together: existing bearer tokens are format-incompatible, so a partial
469
+ rollout produces 401s until every instance is updated. The Cloudflare, Lambda
470
+ and Azure deployment services each hand-rolled a copy of `buildRemoteHeaders`
471
+ and now call the shared one, which is what keeps the two sides in step.
472
+
473
+ **Breaking: previously signed content URLs stop verifying.** `LocalContent`
474
+ signed only `{signedAt, expiresAt, notBefore}`, so a signature proved when a URL
475
+ was issued but never what it was issued for — any valid token was a skeleton
476
+ key, and swapping the pathname from a public thumbnail to a private document
477
+ still verified. The signature now binds the request path. Separately, the
478
+ verifier returned "valid" when no JWT service was wired, which is how
479
+ `pikku serve` ran: a forged `?signedAt=0&expiresAt=99999999999999` was accepted.
480
+ It now rejects with 403, `LocalContent` requires a `JWTService`, and
481
+ `pikku serve`/`pikku dev` mint an ephemeral per-process signing key so local
482
+ development works without shipping a fail-open path. In-flight signed URLs must
483
+ be re-issued.
484
+
485
+ **Request body size limits now apply to every adapter.** The `maxBodySize` cap
486
+ existed only in `PikkuFetchHTTPRequest`. The real hole was uWebSockets, which
487
+ drove `res.onData` itself and concatenated every chunk with no bound and nothing
488
+ downstream able to intervene; it now drops chunks past the limit and replies 413
489
+ before routing. Fastify delegates to its native `bodyLimit` (set only when
490
+ `maxBodySize` is configured, so fastify's stricter 1 MB default is never
491
+ loosened), and `PikkuExpressServer` feeds the limit into its body parsers. Two
492
+ paths can only reject rather than prevent, and are documented as such:
493
+ `express-middleware` mounted on your own app receives an already-parsed body, so
494
+ that deployment must bound its own parser; Next server actions bottom out at
495
+ `experimental.serverActions.bodySizeLimit`.
496
+
497
+ **Breaking: the console addon's privileged functions are gated by default.**
498
+ `wireAddon` gains a `scopes` option that applies to every function in the
499
+ addon's namespace, and the console scaffold now generates
500
+ `wireAddon({ name: 'console', package: '@pikku/addon-console', scopes: ['admin'] })`.
501
+ Previously the console's entire privileged surface — around 54 functions
502
+ including `credentialGet`, which returns a resolved OAuth token for an arbitrary
503
+ `userId`, `updateFunctionBody`, and `installAddon`, which shells out to a
504
+ package install — was protected only by an optional host-registered
505
+ `addGlobalPermission`. `resolveGlobalPermissions` returns `[]` when none is
506
+ registered and permission checking then no-ops, so an app that never registered
507
+ one served those functions to anyone, and with the template's default
508
+ `scaffold.rpc: "no-auth"` that meant unauthenticated. All of them now return 403
509
+ `MissingScopeError` without an `admin`, `admin:*` or `*` scope. **Regenerating
510
+ is required** — an app holding an old `console.gen.ts` stays open.
511
+ `installAddon` and `installOpenapiAddon` additionally declare their own
512
+ `auth: true, scopes: ['admin']`, and `getAgentThreads` now scopes its listing to
513
+ the session's own threads unless the caller holds admin.
514
+
515
+ Addon scopes are enforced in `runPikkuFunc` rather than at the RPC boundary,
516
+ because a wiring can reference an addon function directly — the inspector
517
+ records the addon's `packageName` on HTTP, channel, schedule, queue, CLI,
518
+ trigger, gateway and MCP wirings — and those paths never call `resolveNamespace`.
519
+ Enforcing at the RPC seam would have covered only the `namespace:function` form
520
+ while reading as complete.
521
+
522
+ **Breaking: `wireAddon`'s `auth` and `tags` now apply on direct wirings too.**
523
+ Both were read only by `resolveAddonFunction`, so they had exactly the hole
524
+ scopes had: `wireAddon({ name: 'console', package: '@pikku/addon-console', auth:
525
+ true, tags: ['admin'] })` gated `rpc('console:credentialGet')` and gated nothing
526
+ at all on an HTTP route wired straight to `credentialGet`. A consumer who
527
+ reached for the documented way to lock an addon down got a control that was
528
+ silently inert on every wiring except one. Both now resolve in `runPikkuFunc`.
529
+ `auth` merges as an OR — `auth: false` from an addon is ignored on a direct
530
+ wiring, because an addon may require a session the wiring did not but must never
531
+ waive one it did. Addon tags resolve to concrete middleware against the **root**
532
+ tag groups before the call rather than being folded into the function's
533
+ inherited middleware: `addTagMiddleware('admin', …)` is written by the consuming
534
+ app and registers under the root package, whereas `combineMiddleware` would look
535
+ the tag up under the addon's own `packageName`, where it does not exist.
536
+
537
+ One consequence worth naming: an addon that wires `auth: true` and also runs its
538
+ own sessionless internal work — a scheduled task or queue worker inside the
539
+ addon calling a sibling function — is now gated where it previously was not,
540
+ because a bare `rpc('fn')` from inside the addon reaches `runPikkuFunc` with the
541
+ addon's `packageName` like any other call. This reverses a decision that
542
+ deliberately scoped the gate to the namespaced boundary; that reasoning held
543
+ only while the boundary was real, and a direct wiring can enter an addon without
544
+ crossing it, so "already inside" is not something the runtime can infer. Such an
545
+ addon should carry authorization on the function via
546
+ `pikkuFunc({ permissions })`, which has always been enforced on every path. A
547
+ follow-up will add execution provenance so an intra-addon call can be
548
+ distinguished from an external one and skip the addon-level check; that needs a
549
+ marker no caller outside the process can set, which is its own design problem
550
+ and does not belong in a security fix.
551
+
552
+ **Codegen now warns when an exposed function has no gate (PKU574).** The
553
+ generated `POST /rpc/:rpcName` dispatcher forwards to `rpc.exposed`, which
554
+ refuses anything without `expose: true` — but nothing checked whether the
555
+ target was gated, because a dispatcher cannot know what it dispatches to. The
556
+ console shipped ~54 privileged functions through that gap and the toolchain was
557
+ silent. The inspector now reports every function that is exposed, sessionless,
558
+ and carries no `auth`, `scopes` or `permissions` of its own and none from a
559
+ governing `wireAddon`. It is a `warn`, not a critical: `expose: true` on an
560
+ ungated sessionless function is correct for a genuinely public endpoint, so it
561
+ blocks a build only under `--fail-on-warn`.
562
+
563
+ Two pieces of metadata were missing for this to be answerable statically, and
564
+ both are now recorded. A `pikkuSessionlessFunc`'s own `auth: true` was read at
565
+ runtime but never written to function meta, so a self-gated function was
566
+ indistinguishable from an ungated one — `sessionless` carries the baseline, and
567
+ `auth` now carries the tightening. And `wireAddon`'s `scopes`, `auth` and `tags`
568
+ were not parsed at all: the inspector recorded the addon's `rpcEndpoint` and its
569
+ secret, variable and credential overrides, and dropped every one of its gates.
570
+ An addon whose gates are not statically knowable is treated as gated, because a
571
+ false positive on a correctly-secured addon costs more than the one case it
572
+ would catch.
573
+
574
+ **Breaking: an application's global permissions now apply inside addons.**
575
+ `resolveGlobalPermissions` read only the bucket matching the function's own
576
+ package, but the generated `addGlobalPermission` wrapper takes no package
577
+ argument and always registers under the root. An app-wide rule like "every
578
+ request needs a signed-in user" therefore stopped at the addon boundary, and the
579
+ bucket an addon's functions did read was one no host could write to — which is
580
+ why the console addon's recommended `addGlobalPermission([isAdmin],
581
+ '@pikku/addon-console')` was never a gate anybody could actually install. A
582
+ function now resolves the root bucket and its own package's, root first.
583
+ Unioning is safe in a way nothing else here would be: globals AND, so adding the
584
+ root ones can only tighten. Package buckets stay one-way — a package's globals
585
+ never reach root functions, or an installed addon could gate the whole
586
+ application. Apps with both a root global and addon-provided functions will see
587
+ those functions gated where they previously were not.
588
+
589
+ **Codegen now records whether each HTTP route requires a session, and warns
590
+ about inert addon tags (PKU575).** Four separate things can demand a session —
591
+ the function's `sessionless`, its own `auth`, the route's (or its group's)
592
+ `auth`, and the addon it belongs to — and answering "which routes are open?"
593
+ meant joining all four by hand and knowing which wins. Each route's meta now
594
+ carries the resolved `requiresSession` alongside the route's own `auth`. Scopes
595
+ count as requiring one, since they are matched against the session's and fail
596
+ closed. Anything not statically knowable resolves to `true`, matching PKU574:
597
+ a route that looks stricter than it is costs less than one that looks open and
598
+ isn't. Separately, `wireAddon({ tags: ['admin'] })` reads like a gate and is
599
+ applied like one right up until no `addTagMiddleware('admin', …)` exists, at
600
+ which point it resolves to an empty list and gates nothing; that now warns.
601
+ Only addon tags are reported — a tag on a function is as likely to be
602
+ organizational, and warning about those would bury the case that matters.
603
+
604
+ **Object-shorthand permissions were missing from meta.** The inspector visited
605
+ `ts.PropertyAssignment` but not `ts.ShorthandPropertyAssignment`, so
606
+ `permissions: { canAdminOrg }` — enforced identically to the longhand form at
607
+ runtime, since `verifyPermissions` has a non-array branch — was recorded as _no
608
+ permissions at all_. That is the most dangerous direction for meta to be wrong
609
+ in: an audit reading it sees an open door where one is shut. It cost this review
610
+ a false IDOR report across ~35 billing and org functions before the source
611
+ contradicted the metadata.
612
+
613
+ **Functions that authorize in their own body can say so.** A webhook receiver
614
+ verifying a signature, or a handler redeeming a signed invite, is genuinely
615
+ closed while carrying no session, scope or permission — indistinguishable in
616
+ meta from one nobody remembered to gate, and so warned about forever by PKU574.
617
+ `selfAuthenticated: true` on the function config records the claim and silences
618
+ the warning for that function. It is declarative only: nothing at runtime reads
619
+ it and it grants nothing. Detection was rejected deliberately — inferring it
620
+ from the body means a function that _looks_ like it checks something silences
621
+ the warning while checking nothing, and a warning that is usually wrong stops
622
+ being read.
623
+
624
+ **Breaking: `scaffold.<feature>` is now `boolean | { auth?, path? }`, and `true`
625
+ means authenticated.** The old `'auth' | 'no-auth' | false` read like a
626
+ starter-file preference while being a live authorization decision, set three
627
+ directories from the functions it governed — the shape the console incident
628
+ took. A surface now becomes public only by writing `{ auth: false }`, so
629
+ omitting a field can never open anything: the failure mode of a forgotten flag
630
+ is a locked door. `{ path }` additionally overrides where the file is
631
+ generated, which previously could only be set for all features at once via
632
+ `pikkuDir`.
633
+
634
+ The legacy strings are **refused, not coerced**. `resolveScaffoldFeature` throws
635
+ naming the key and its replacement (`"rpc": "no-auth"` → `"rpc": { "auth":
636
+ false }`), and it does so at config load, not downstream. An earlier design used
637
+ a bare `string` for the output path, under which `"no-auth"` would have parsed
638
+ as a file named `no-auth` and every unmigrated config would have silently
639
+ produced nonsense; the object form makes any string invalid, so the failure is
640
+ loud.
641
+
642
+ The collapse is deliberately not uniform in effect. For `rpc` the flag was a
643
+ blanket "no anonymous RPC in this app" set on a dispatcher that cannot know what
644
+ it dispatches to; for `userAdmin` it was redundant, since the generated
645
+ functions are already `pikkuFunc` with `scopes: ['admin:users:list']`. But for
646
+ `agent`, `workflow`, `events` and `scenarios` it is the only gate — those
647
+ generate real endpoints the app never authors — so `true` keeps them
648
+ authenticated rather than opening them as a side effect of a config cleanup.
649
+ `webhook` and `remoteRpc` have no auth dimension at all (`serialize-remote-rpc.ts`
650
+ hardcodes `auth: false`), so an `{ auth }` on them is ignored. The three
651
+ configs in this repo are migrated preserving their current behaviour exactly.
652
+
653
+ **Queue job identities are signed.** A job carried the producer's `pikkuUserId`
654
+ as a plain string and the worker resolved a session from it with no
655
+ verification, so write access to the queue backend was act-as-any-user. The
656
+ identity is now `pq1.<claim>.<hmac>`, HMAC-SHA256 over the claim and the
657
+ canonicalized job payload, keyed by HKDF expansion of a new
658
+ `PIKKU_QUEUE_IDENTITY_SECRET`. Producers opt in by wrapping their queue service
659
+ with `SignedQueueService`. This fails safe rather than closed: with no secret
660
+ configured the identity is dropped and jobs still process, warning once per
661
+ process, so no existing deployment breaks on upgrade — it simply loses queue
662
+ identity until the secret is set. The payload rather than the job id is bound
663
+ because SQS, Cloudflare Queues, Azure and the in-memory service all mint ids
664
+ after `add` returns.
665
+
666
+ **Workflow inline state is read from the run record.** `isInline` consulted a
667
+ process-local `Map`, while `WorkflowRun.inline` is durable. Any instance that
668
+ did not start a run disagreed with the record, so one instance could dispatch a
669
+ queued job for a workflow another was already executing in-process. It is now
670
+ async and resolves through the durable identity, cached only when a context
671
+ already exists so a passive reader allocates nothing. The same `Map` also leaked:
672
+ `nextStepKey` fabricated replay state on every step, and `releaseContext`
673
+ refused to free anything carrying it, so runs whose steps executed outside a
674
+ `beginReplay` bracket — the step-worker queue path — stranded their context and
675
+ step state for the process lifetime. Contexts are now released by an explicit
676
+ execution counter. Step ordinals reset per execution rather than accumulating
677
+ across step-worker invocations in one process, which makes step naming
678
+ independent of how work was distributed.
679
+
680
+ **Secret reads fail loud in every store.** `MongoDBSecretService.getSecrets`
681
+ skipped rows that failed to decrypt, and the redis equivalent dropped every
682
+ rejection via `Promise.allSettled`, including the "No KEK available for
683
+ key_version N" configuration error. Both now throw, naming the key and its key
684
+ version, matching the kysely behaviour. This matters most alongside the KEK
685
+ change above: without it, an upgrade surfaces as a partial secrets map and an
686
+ opaque downstream failure instead of an error naming the secret to re-enter.
687
+
688
+ **A second middleware registration for a pattern no longer erases the first.**
689
+ `addHTTPMiddleware`, `addTagMiddleware` and `addChannelMiddleware` groups are
690
+ keyed by pattern or tag and held one source file each, so a second file's call
691
+ overwrote the first's. Codegen emits its imports from what is stored, so the
692
+ losing file was never imported and its middleware never registered — the
693
+ runtime composes repeated registrations for a pattern happily, and only codegen
694
+ dropped one. Adding an unrelated `addHTTPMiddleware('*', …)` to an app was
695
+ enough to silently unregister the generated better-auth session bridge, which
696
+ fails open and gives no sign until a request arrives without a session. A group
697
+ now carries every registration made for it, and all of them are imported.
698
+
699
+ - 8075f6a: Confine `SecretService` to the places an app is wired.
700
+
701
+ `secrets` is now omitted from the services every function, AI agent, workflow,
702
+ permission and wire receives, and the function runner replaces it with a
703
+ throwing accessor so a cast cannot reach past the type. It stays available in
704
+ `pikkuServices`, `pikkuWireServices`, addon service factories and middleware —
705
+ read a secret there, give it to a service, and have the function ask that
706
+ service.
707
+
708
+ Alongside it:
709
+ - `wireSecret` gains `allowedHosts`, refusing a secret attached to a host it was
710
+ not declared for. Permissive by default; strict via
711
+ `config.secrets.requireAllowedHosts`.
712
+ - `pikku-graph`'s `httpRequest` resolves and attaches its credential inside a new
713
+ `httpRequester` service instead of holding the plaintext in the function.
714
+ - New inspector diagnostics: `PKU950` (a `SecretService` exposed under another
715
+ service name), `PKU951` (a secret read that no `wireSecret` declares) and
716
+ `PKU952` (a secret read with a non-literal key).
717
+
718
+ ## 0.12.73
719
+
720
+ ### Patch Changes
721
+
722
+ - c984df6: Give an agent's tools back the descriptions their authors wrote
723
+
724
+ A tool's description is what the model is told the tool does, and the main
725
+ thing it chooses between tools on. It was not reaching the model. `description`
726
+ is classed as a verbose field, so it is stripped from the metadata bundled into
727
+ the generated bootstrap — the copy `pikkuState('function', 'meta')` is built
728
+ from. `buildToolDefs` read the description from there, found it always
729
+ undefined, and fell through to the tool's own name. Every agent has been
730
+ choosing between bare identifiers. The same fallback was offering an addon's
731
+ MCP tools under their names, for the same reason.
732
+
733
+ Tool definitions now resolve descriptions through `metaService`, which reads
734
+ the verbose metadata and falls back to the minimal copy, so the authored text
735
+ is recovered wherever the generated `.pikku` directory is readable. Where it is
736
+ not — no `metaService`, or a deployment shipping only the stripped copy — a
737
+ tool falls back to its name, which is what it did before. Addon metadata is
738
+ likewise loaded verbose-first. `title` is no longer part of the chain: a title
739
+ labels a tool in a UI, it does not tell a model when to reach for it.
740
+
741
+ An addon has to ship the verbose file for any of this to reach it. `tsc` only
742
+ emits the JSON it sees imported and nothing imports the verbose meta, so the
743
+ bundled addons now copy it into `dist` explicitly.
744
+
745
+ `ref()` is resolved at build time. It used to be pushed through codegen as an
746
+ opaque string, so `ref('todos:doesNotExist')` generated cleanly and failed only
747
+ when the agent ran. The inspector now resolves each reference against the
748
+ project's functions, or — using the namespace-to-package mapping `wireAddon`
749
+ already provides — against the addon's own metadata, and reports an unwired
750
+ namespace (`PKU152`) or a missing function (`PKU153`) at codegen. An addon that
751
+ has not been built yet contributed no metadata and is skipped rather than
752
+ reported missing.
753
+
754
+ New `pikku --strict-meta` additionally fails the build on any agent tool with
755
+ no description (`PKU154`), including tools reached through an addon. It is off
756
+ by default, so nothing that builds today stops building; turn it on to hold a
757
+ project to the metadata its agents actually run on.
758
+
759
+ - 63ff32b: Run a CLI's commands on the server, over the connection the client opened
760
+
761
+ A CLI that talks to a service has to ship the service's command tree, so the
762
+ two versions drift: the binary someone installed months ago still believes in
763
+ flags and commands the server has since changed. This makes the command tree
764
+ the server's, and leaves the client holding only a socket.
765
+
766
+ `wireCLI` gains `auth`, and a program wired with a channel entrypoint now
767
+ generates a `__raw` route: the client forwards argv untouched, the server
768
+ parses it, runs the command, and streams the output back as it happens. The
769
+ terminating frame carries the exit code, so a failed remote command still exits
770
+ non-zero locally. Renderers stay on the client and are matched by the command
771
+ id the server reports; an unrecognised command falls back to JSON rather than
772
+ failing.
773
+
774
+ Every channel gains `channel.remote(...)`: calling a function on the peer at
775
+ the other end of the connection and waiting for its answer. A channel is
776
+ otherwise fire-and-forget in both directions, so this is what reaches a peer
777
+ that has no address of its own — a CLI on a laptop, a browser tab, a sandbox
778
+ behind NAT. It is on `channel` rather than `rpc` because it is bound to one
779
+ connection: which peer answers is the socket the call goes out on, not
780
+ something the RPC map could resolve. Any `wireChannel` gets it — a client
781
+ registers what it is willing to answer to, and a name outside that list is
782
+ refused.
783
+
784
+ Requests are correlated by id, time out, and fail fast when the socket closes
785
+ rather than waiting out the timeout. Replies are taken off the socket ahead of
786
+ routing, so a channel needs no route for them and an answer can never be
787
+ mistaken for a new message; the transport is created on first use and released
788
+ when the channel closes, which is also what fails anything the departing peer
789
+ still owed an answer to. Channels that only flow one way — SSE, an agent's
790
+ output stream, a locally-run CLI — refuse the call outright instead of waiting
791
+ for an answer that was never going to come.
792
+
793
+ What a peer answers with is its word, so it is checked before a caller sees it
794
+ — against the schema codegen already generated from the function's declared
795
+ return type, the same one an agent tool or an HTTP response is checked against.
796
+ A capability is declared with `pikkuRemoteChannelFunc`, which takes the usual
797
+ `title` / `description` / `input` / `output` but no `func` — this side owns the
798
+ contract, the peer owns the body. It registers under its name like any other
799
+ function, so `channel.remote` is typed off the same generated map as
800
+ `rpc.remote` and no caller has to cast, and a local call throws rather than
801
+ missing: reaching it locally means a command asked the server for something
802
+ only a client knows. A client on an older build fails the call it answered
803
+ rather than the caller failing later somewhere with no reason to expect a bad
804
+ shape; a name with no declared contract is left alone. Both frame guards
805
+ validate the whole envelope rather than the action tag alone, and a failure
806
+ payload with a non-string name or message falls back rather than being attached
807
+ to an `Error`.
808
+
809
+ The arguments going the other way are checked too, against the schema for the
810
+ capability's declared input, before anything is registered or sent. That is not
811
+ a boundary — the peer runs the code and has to check what it was handed, and a
812
+ caller that meant harm would send arguments that pass. It catches drift, where a
813
+ server built against a newer capability signature calls a client that predates
814
+ it, and fails it here rather than inside someone else's process.
815
+
816
+ A channel-driven CLI command uses this to ask its caller for machine-local
817
+ facts mid-run — a git sha, a working tree, a local file. The CLI wire's own
818
+ channel is synthetic (it exists so a command can stream progress without
819
+ knowing where that goes), so it delegates `remote` to the connection the
820
+ command actually arrived on.
821
+
822
+ Because that runs code on someone's machine at a remote caller's request, the
823
+ capability map says what _can_ run and approval says whether a particular call
824
+ _should_. A capability may be declared `{ execute, needsApproval }`, sharing
825
+ `ApprovalPolicy` — `needsApproval` and `approvalDescriptionFn` — with
826
+ `AIAgentToolDef`, which has carried both since before channels could call back:
827
+ both are an allowlist of named callables invoked by something other than the
828
+ code that wrote them. The runtime around them is deliberately not shared, since
829
+ an agent suspends its run and resumes it later while a reverse call is a live
830
+ await with a person at the other end.
831
+
832
+ A capability written as a bare function is unclassified, and unclassified means
833
+ approval is required — the annotation nobody got round to writing is the one
834
+ most likely to matter, so it fails closed. Declare
835
+ `{ execute, needsApproval: false }` for a capability that may run unattended.
836
+ Nothing infers this: core cannot tell a read-only capability from a destructive
837
+ one, so `needsApproval: false` is the author asserting it, and the assertion is
838
+ the only thing standing between a remote caller and the machine.
839
+
840
+ The default is the opposite of `AIAgentToolDef`'s, where absence means "do not
841
+ ask" — a tool is written by the same people who run the server it executes on,
842
+ and a capability is not.
843
+
844
+ `executeRawCLIViaChannel` reads `--auto-approve` and
845
+ `--dangerously-auto-approve` out of argv (or `PIKKU_AUTO_APPROVE` /
846
+ `PIKKU_DANGEROUSLY_AUTO_APPROVE`) and strips them before argv reaches the
847
+ server — what may run on this machine is this machine's decision, and a flag
848
+ the server can see is one the server could act on. `--auto-approve` permits the
849
+ classified-safe set and refuses the rest; `--dangerously-auto-approve` permits
850
+ everything and says so once on stderr. Interactively the user is asked per
851
+ call, with `y` / `n` / `a`, where `a` is remembered for that one capability for
852
+ the rest of the run and never written to disk — widening it to the session
853
+ would quietly turn an interactive run into `--dangerously-auto-approve`. A run
854
+ with no terminal and no flag refuses rather than assuming yes, because CI is
855
+ exactly where an unattended `git push` would otherwise happen. The tiers are
856
+ meaningful here in a way they would not be for an agent: the caller is a
857
+ deterministic program whose source can be read, so "these calls are always
858
+ fine" is a claim someone can actually justify.
859
+
860
+ A peer that is asking a human sends a pending frame first, which stops the
861
+ caller's timeout. Without it any approval slower than the timeout would fail
862
+ the call and then discard the decision when it finally arrived. The call is
863
+ still failed the moment the socket drops — what actually happens when a peer
864
+ dies mid-prompt — and a peer that sends the frame dishonestly can do nothing
865
+ but keep its own call waiting. A refusal is sent as an answer, so a denied call
866
+ fails its command immediately rather than hanging.
867
+
868
+ Fixes found on the way, each of which broke this path:
869
+ - A websocket upgrade wrote middleware headers (CORS, on every request)
870
+ straight onto the socket, so the first bytes a client saw were headers rather
871
+ than `ws`'s `101` status line and the handshake failed to parse. Header
872
+ writes are now buffered and flushed behind a status line only when the
873
+ upgrade is actually being rejected.
874
+ - An upgrade socket had no error listener while the channel opened, so a client
875
+ that gave up mid-handshake took the whole server process down with an
876
+ unhandled `ECONNRESET`.
877
+ - `onConnect` and `onDisconnect` never saw the session established during the
878
+ upgrade, so a channel could not tell who had just connected.
879
+ - Setting the routing key on a channel result mutated the value in place, which
880
+ throws for a primitive under ESM strict mode.
881
+
882
+ - ba6cc08: fix: stop leaking internal error detail and bound the request body size
883
+
884
+ HTTP error responses no longer forward an error's `payload` or its raw `message` for
885
+ registered 5xx errors — those responses carry the registered error message instead, so an
886
+ internal error that happens to hold a `payload` cannot leak it to the client. Errors
887
+ registered with a 4xx status keep their message and payload, and `exposeErrors` still
888
+ surfaces the full detail outside production.
889
+
890
+ `PikkuFetchHTTPRequest` now caps how much of a request body it buffers, rejecting the
891
+ declared `content-length` up front and measuring the stream as it arrives so a lying or
892
+ absent header cannot exhaust memory. Exceeding the limit throws `PayloadTooLargeError`
893
+ (413). The ceiling defaults to 10MB and is configurable via the new `maxBodySize` option on
894
+ the constructor and on `RunHTTPWiringOptions`.
895
+
896
+ - d007191: `cors()` takes an `exposeHeaders` option.
897
+
898
+ Without `Access-Control-Expose-Headers` a cross-origin caller can read only the
899
+ CORS-safelisted response headers, so any header the client is meant to act on was
900
+ invisible to it. The cross-site session relay in `@pikku/better-auth` is the case that
901
+ surfaced it: the client cannot read `x-pikku-cross-site-set-cookie` off a cross-origin
902
+ response without being told it may.
903
+
904
+ Defaults to none, so nothing new is exposed unless it is named.
905
+
906
+ - a7b26c5: rename the inspected declarations to `define*`: `wireScope` → `defineScope`, `wireSecret` → `defineSecret`, `wireVariable` → `defineVariable`, `wireCredential` → `defineCredential`
907
+
908
+ `wire*` meant two unrelated things. A transport wiring attaches a function to
909
+ something that can invoke it — `wireHTTP`, `wireChannel`, `wireScheduler`,
910
+ `wireQueueWorker` and the rest — and the thing it wires runs. These four wire
911
+ nothing: they are no-ops that exist only so the call typechecks, they are
912
+ tree-shaken out of the build, and their whole job is to be found by the
913
+ inspector's AST pass and turned into a type union. One word for both left the
914
+ declaration reading like a registration with a runtime.
915
+
916
+ So the vocabulary splits: **`wire*` is a transport, `define*` is an inspected
917
+ declaration.**
918
+
919
+ ```ts
920
+ import { defineScope } from '@pikku/core/scope'
921
+ import { defineSecret } from '@pikku/core/secret'
922
+ import { defineVariable } from '@pikku/core/variable'
923
+ import { defineCredential } from '@pikku/core/credential'
924
+
925
+ defineScope({ admin: { scopes: { invoices: { scopes: { create: {} } } } } })
926
+ ```
927
+
928
+ **Breaking:** no alias is kept. Rename the four call sites; the module subpaths
929
+ (`@pikku/core/scope`, `/secret`, `/variable`) are unchanged.
930
+
931
+ The inspector matches these by identifier text, so a stale `wire*` call is not a
932
+ type error — it is silently not extracted, and the generated union comes back
933
+ empty. That fails as "this scope isn't declared" on code that was fine a moment
934
+ ago, nowhere near the declaration. Grep for the old names rather than trusting a
935
+ clean build.
936
+
937
+ An addon published with `.pikku` output generated before this release re-exports
938
+ `wireSecret` from `@pikku/core/secret` and will not typecheck against this core
939
+ until it is rebuilt and republished.
940
+
941
+ - 457cb25: Add `definePersonas()`: the people a project's scenarios and virtual users run
942
+ as, declared in code.
943
+
944
+ There used to be three names for two-and-a-bit things — an _actor_ in
945
+ `scenarios.actors`, a _persona_ in `scenarios.personas`, and a _virtual user_
946
+ declared separately against an actor. In practice almost every actor was its own
947
+ kind, so the second set carried no information and the third was a third place
948
+ for a name to drift. There is now one declaration:
949
+
950
+ ```ts
951
+ definePersonas({
952
+ shopper: {
953
+ name: 'Sam Shopper',
954
+ jobTitle: 'Shopper',
955
+ personality: 'Buys in a hurry and leaves tabs open',
956
+ roles: ['customer'],
957
+ disposition: 'careless',
958
+ goals: ['Buy something without reading anything'],
959
+ account: {},
960
+ },
961
+ })
962
+ ```
963
+
964
+ A persona is a person: what they are like, what they want, the roles they hold,
965
+ and **one** account they sign in with — `account: {}` plus `linkedAccounts` for
966
+ the rare case of more, modelled on how better-auth does linking. A persona with a
967
+ `disposition` is a virtual user; `runnable: false` marks someone who only ever
968
+ exists to be acted upon — banned, shared with, reset — and is never handed a
969
+ session.
970
+
971
+ **A persona names roles, never scopes.** Scopes come from `defineSystemRole()`
972
+ expansion, so the build fails if a persona names a role nobody declared, and
973
+ fails again if a role confers a scope no `defineScope` declares. Running one only
974
+ ever has to check that its roles are still valid.
975
+
976
+ **Addresses are computed, never declared.** `personaEmail(id, domain, runId)`
977
+ derives `<id>[+runId]@<domain>` from `scenarios.emailDomain`, so a seed, a
978
+ scenario run and a virtual-user run cannot disagree about who they are signing in
979
+ as. `scenarios.actors` and `scenarios.personas` are gone from
980
+ `pikku.config.json` — only `emailDomain` remains.
981
+
982
+ `actor` survives in exactly one place: the name of a **slot in a scenario step**,
983
+ which is the role a persona is cast in for that step. `pikkuVirtualUser()`,
984
+ `kind`, `grants` and the `actor` field are removed; the `actors` service is now
985
+ `personas`, and the CLI's `virtual-user` commands are now `pikku persona list` /
986
+ `pikku persona run`. `budget` and `allowApprovalRequired` moved to run flags —
987
+ how much you will spend today is not a fact about a person.
988
+
989
+ `@pikku/cucumber` drops its `Actor` class and `ActorDispatchContext`: a
990
+ hand-rolled cookie jar that a persona's own typed session replaces outright.
991
+
992
+ - f7567ad: Add `defineSystemRole()`: roles that ship with the product, declared in code.
993
+
994
+ A system role is to a console-composed role what an AWS managed policy is to a
995
+ customer-managed one — the console may show and grant it, but not rename,
996
+ re-scope or delete it. The CLI extracts declarations by AST and generates a
997
+ `SystemRoleName` union, so naming a role that does not exist fails the build,
998
+ and a role granting a scope no `defineScope` declares fails it too.
999
+
1000
+ Removal is additive on the same terms as `defineScope`: deleting a declaration
1001
+ leaves an inert row rather than revoking everyone's grant mid-deploy.
1002
+
1003
+ `ScopeService` gains `syncSystemRoles`, `findStaleSystemRoles` and
1004
+ `pruneSystemRoles`; `Role` gains `system` and `declared`. Implementations
1005
+ enforce immutability through the shared `assertRoleIsMutable` /
1006
+ `assertRoleNameAvailable` guards rather than each inventing the rule.
1007
+
1008
+ - ba6cc08: Security hardening: removed the gopass secret service and stopped MCP internal errors leaking stack traces.
1009
+
1010
+ **Breaking:** `GopassSecretService` and the `@pikku/core/services/gopass-secrets` subpath export are gone. The service shelled out to the `gopass` binary and its key validation accepted `../`, so a caller-supplied key could traverse out of the configured prefix namespace and read secrets outside it. Rather than harden a shell-out that few projects used, the service is removed. Anyone importing it should implement `SecretService` against their own secret backend. Pre-0.13 breaking changes still ship as a patch.
1011
+
1012
+ MCP internal errors (JSON-RPC `-32603`) previously always attached `data: { message, stack }`, handing any MCP client an internal stack trace. That payload is now gated on `exposeErrors`, which defaults to `!isProduction()` — the same convention `handleHTTPError` already uses. In production a client receives a bare `Internal error` with no `message` and no `stack`; `RunMCPEndpointParams` accepts an explicit `exposeErrors` to override the default.
1013
+
1014
+ - a2e21e5: Keep the persona runtime off the production barrels
1015
+
1016
+ `@pikku/core/services` exported `HttpPersona`, `createHttpPersonas` and
1017
+ `readScenarioHttpResponse` as values, and `@pikku/core/workflow` exported
1018
+ `readScenarioHttpResponse` and `postScenarioJson`. Both are barrels a production
1019
+ server imports, and `http-personas` reaches the actor-flow conversation runner
1020
+ and through it the agent runner — so signing-in-as-a-persona machinery sat in the
1021
+ module graph of every app that imported services.
1022
+
1023
+ Tree-shaking only removes that if you bundle. An unbundled Node or Lambda deploy
1024
+ loads whatever the graph names, which is the case this matters in.
1025
+
1026
+ The values now come from `@pikku/core/persona`, which is where the rest of the
1027
+ persona API already lives. **Types stay exactly where they were** — `import type`
1028
+ erases, so it costs a bundle nothing, and moving them would put core in a cycle
1029
+ with the code that describes its own function types.
1030
+
1031
+ `serialize-personas` generates the new import, so a regenerated
1032
+ `pikku-personas.gen.ts` picks it up with no edit. Anything importing these four
1033
+ values from `@pikku/core/services` or `@pikku/core/workflow` changes the
1034
+ specifier to `@pikku/core/persona`; the names and signatures are unchanged.
1035
+
1036
+ A test walks each barrel's value-import graph and fails if scenario runtime
1037
+ reappears, so this cannot regress quietly.
1038
+
1039
+ - 457cb25: Let a persona do a real job in production, and say where it may act.
1040
+
1041
+ A persona was only ever a test subject: something you pointed at a stage to find
1042
+ out what the product does wrong. But the same declaration — a name, a job, the
1043
+ roles it holds and what it is trying to get done — describes a teammate doing
1044
+ the work for real, and nothing about the engine cared which one it was.
1045
+
1046
+ Four changes make that difference explicit and enforced.
1047
+
1048
+ **`environments` moves to the top level of `pikku.config.json`**, out from under
1049
+ `scenarios`. It was never a scenario's anything: `persona run` targets one, and
1050
+ now so does `persona sync`. An environment may be flagged `production: true` —
1051
+ a flag rather than a reserved name, because projects call it `prod`, `live` or
1052
+ `eu-prod`, and more than one environment can be production.
1053
+
1054
+ **A persona may name its `environments`.** Omitting them means every configured
1055
+ environment _except_ the production ones, so nothing reaches production by being
1056
+ forgotten. Naming a production environment requires `disposition: 'accountable'`.
1057
+ The rule is checked twice, on purpose: the inspector refuses to generate a
1058
+ declaration that breaks it, and sign-in re-checks against the environment
1059
+ actually resolved — the build check trusts the file, and the run check does not
1060
+ trust which artifact got deployed. An unresolved environment fails closed.
1061
+
1062
+ **`disposition: 'accountable'`** is that production disposition. It sits opposite
1063
+ `adversarial` on the intent axis rather than the care axis: what it changes stays
1064
+ changed, every call is recorded against its name, and it stops to ask rather than
1065
+ acting and reporting afterwards. Alongside it, **agents now appear in a persona's
1066
+ computed catalogue**, gated by the same scopes as the RPCs — an agent is reached
1067
+ rather than declared, so a persona finds the specialists its roles unlock and
1068
+ chooses between calling the API itself and handing the work over. That also fixes
1069
+ a latent gap: `talkTo` was wired at the target but never advertised in the
1070
+ instructions, so it was never used.
1071
+
1072
+ **`pikku persona sync <environment>`** provisions them: it creates each account
1073
+ and applies the roles it declares, additively, and never revokes. Seeding is test
1074
+ data and `db seed` does not run in production; a teammate doing a real job still
1075
+ needs an account and its grants. It needs both halves of an environment — its API
1076
+ to sign the person in, its database to write the grants — and `--dry-run` reports
1077
+ who would be provisioned, with what, and why anyone was skipped.
1078
+
1079
+ In the console, a virtual user now says where it may act — the environments it
1080
+ named, or the rule when it named none — and its dossier carries the `sync`
1081
+ command alongside the `run` one, because the account is not a by-product of a
1082
+ run. `accountable` reads as a disposition like the rest.
1083
+
1084
+ - 86a50b9: scenario: replace `browser: true` + `func` with per-surface bindings on `pikkuScenarioStep`
1085
+
1086
+ A step now declares one implementation per surface it can be driven through:
1087
+
1088
+ ```ts
1089
+ export const buysTheItem = pikkuScenarioStep<{ sku: string }, { orderId: string }>({
1090
+ name: 'buysTheItem',
1091
+ description: 'buys the item',
1092
+ browser: async (services, data, { browser }) => { ... },
1093
+ default: async (services, data, { rpc }) => { ... },
1094
+ })
1095
+ ```
1096
+
1097
+ `pikku scenario run --run browser|cli|default` picks which surface the run drives,
1098
+ and the two phases resolve bindings differently:
1099
+ - **Actions** (`given` / `when` / `step`) run exactly one binding — the run
1100
+ surface if it has one, otherwise `default`. A step with neither now fails with
1101
+ `ScenarioNoSurfaceBinding` instead of silently running server-side.
1102
+ - **Assertions** (`then`) are witnesses, not alternatives: every declared binding
1103
+ runs and they must agree. Two surfaces reporting different things fails the run
1104
+ with `ScenarioWitnessDisagreement` rather than reporting a pass. An assertion
1105
+ with no witness the run can execute at all fails with `ScenarioNoWitness` —
1106
+ without it the step returns `undefined` and renders as a tick, reporting a pass
1107
+ for something nobody checked.
1108
+
1109
+ A scenario written as a step ladder that never calls `then` is now a **PKU680**
1110
+ critical. It proves only that nothing threw, so an assertion-free ladder of
1111
+ browser-bound actions would score perfect coverage while checking nothing.
1112
+
1113
+ The report gains a surface-coverage line — `n/m steps ran on browser`, counted
1114
+ over every step, so an action that fell back to the server lowers the ratio
1115
+ rather than needing a footnote. That also makes surfaces comparable over one
1116
+ denominator: a scenario is `4/4` on a default run and `3/4` on a browser one.
1117
+ Assertions that fell back are named separately and gate `--strict`, since a
1118
+ sentence claiming the actor saw something nobody looked at is a different problem
1119
+ from an action taking a shortcut.
1120
+
1121
+ **Breaking:** `browser: true` and the third `B extends boolean` type argument are
1122
+ gone. Rename `func` to `default` (or to `browser` where the step drove a browser)
1123
+ and drop the type argument.
1124
+
1125
+ - 0e0f6eb: Add virtual users: LLM-driven synthetic users that work a real stage in
1126
+ character.
1127
+
1128
+ A scenario proves a path somebody thought of. A virtual user works the same
1129
+ ground without the script — it signs in as a declared persona over the app's own
1130
+ auth, is handed the scenarios' BDD prose and the schema of every endpoint it may
1131
+ reach, and decides for itself what to do. It asserts nothing; a run produces
1132
+ findings, and their absence only ever means "not this time, not with this seed".
1133
+
1134
+ There is nothing extra to declare. A persona with a `disposition` is a virtual
1135
+ user, and running it is what makes it one — see the `definePersonas` changeset
1136
+ for the declaration itself. Listing, describing or running one never loads the
1137
+ app: the inspector reads the literal declaration, the CLI writes
1138
+ `scopes/pikku-personas-meta.gen.json`, and `MetaService.getPersonasMeta()`
1139
+ serves it.
1140
+
1141
+ **Dispositions are engine dials, not prose.** Each carries its own intent weights
1142
+ (continue / suspend / resume / abandon), temperature, re-read and repeat rates,
1143
+ and switches: `careless` puts things down and picks them up in the wrong order,
1144
+ `newcomer` starts with no memory, `auditor` is never offered a mutation,
1145
+ `adversarial` is shown the catalogue its roles do not cover — being offered a
1146
+ call it should not be able to make is the test — while those roles stay live as
1147
+ the oracle, so a success outside them is authorization drift rather than a pass.
1148
+
1149
+ **Nothing is retrieved against.** The whole reachable catalogue goes into the
1150
+ instructions (~8k tokens on a 430-RPC project, cached for the run), because a
1151
+ ranking function would make the user only as adventurous as the ranking and lose
1152
+ exactly the endpoints worth stumbling into. Schema first: an endpoint must be
1153
+ described before it may be called.
1154
+
1155
+ **No money in core.** The engine counts steps, calls, mutations and tokens; what
1156
+ they cost is the app's to decide through `stop(tally)`.
1157
+
1158
+ CLI: `pikku persona list` and `pikku persona run <environment> [name]`, with
1159
+ flags overriding a declaration for reproduction (`--seed`, `--steps`,
1160
+ `--disposition`). Spending is a run flag too — `--steps`, `--mutations` and
1161
+ `--duration` bound a run, because how much you will spend today is not a fact
1162
+ about a person. Console: a Virtual Users screen beside Scenarios, built out of
1163
+ core's own derivation functions so it shows a run's actual inputs rather than a
1164
+ second implementation of them.
1165
+
1166
+ `dev-ai-runner` now ships its own `@pikku/ai-vercel` and
1167
+ `@ai-sdk/openai-compatible` instead of requiring them from the project. Behind a
1168
+ proxy one openai-compatible provider answers for every prefix, so there was never
1169
+ a per-vendor package worth making somebody install; the project's copies still
1170
+ win when it has them, and both load from the same place or neither does.
1171
+
1
1172
  ## 0.12.72
2
1173
 
3
1174
  ### Patch Changes