@pikku/core 0.12.79 → 0.12.82

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (479) hide show
  1. package/CHANGELOG.md +473 -0
  2. package/dist/dev/hot-reload.js +1 -6
  3. package/dist/ecosystem.d.ts +27 -0
  4. package/dist/ecosystem.js +26 -0
  5. package/dist/errors/error-handler.js +4 -2
  6. package/dist/errors/index.d.ts +1 -1
  7. package/dist/errors/index.js +1 -1
  8. package/dist/function/function-runner.js +22 -47
  9. package/dist/function/functions.types.d.ts +9 -5
  10. package/dist/function/index.d.ts +1 -1
  11. package/dist/index.d.ts +14 -19
  12. package/dist/index.js +6 -11
  13. package/dist/middleware/auth-cookie.d.ts +0 -4
  14. package/dist/middleware/auth-cookie.js +38 -2
  15. package/dist/middleware/cors.js +1 -0
  16. package/dist/middleware/index.d.ts +1 -1
  17. package/dist/middleware/index.js +1 -1
  18. package/dist/middleware/remote-auth.js +14 -3
  19. package/dist/permissions.d.ts +8 -10
  20. package/dist/permissions.js +0 -11
  21. package/dist/pikku-state.js +16 -5
  22. package/dist/schema.js +35 -1
  23. package/dist/services/ai-agent-runner-service.d.ts +7 -0
  24. package/dist/services/ai-run-state-service.d.ts +17 -1
  25. package/dist/services/in-memory-ai-run-state-service.d.ts +6 -2
  26. package/dist/services/in-memory-ai-run-state-service.js +11 -1
  27. package/dist/services/in-memory-workflow-service.js +2 -0
  28. package/dist/services/index.d.ts +15 -15
  29. package/dist/services/index.js +5 -5
  30. package/dist/services/local-content-request-handler.d.ts +21 -0
  31. package/dist/services/local-content-request-handler.js +72 -53
  32. package/dist/services/local-content.d.ts +6 -0
  33. package/dist/services/local-content.js +14 -1
  34. package/dist/services/scoped-credential-service.d.ts +21 -0
  35. package/dist/services/scoped-credential-service.js +53 -0
  36. package/dist/services/workflow-service.d.ts +4 -2
  37. package/dist/testing/service-tests/agent-run-service-tests.d.ts +10 -0
  38. package/dist/testing/service-tests/agent-run-service-tests.js +72 -0
  39. package/dist/testing/service-tests/ai-storage-service-tests.d.ts +3 -0
  40. package/dist/testing/service-tests/ai-storage-service-tests.js +302 -0
  41. package/dist/testing/service-tests/channel-store-tests.d.ts +3 -0
  42. package/dist/testing/service-tests/channel-store-tests.js +72 -0
  43. package/dist/testing/service-tests/credential-service-tests.d.ts +3 -0
  44. package/dist/testing/service-tests/credential-service-tests.js +109 -0
  45. package/dist/testing/service-tests/deployment-service-tests.d.ts +3 -0
  46. package/dist/testing/service-tests/deployment-service-tests.js +21 -0
  47. package/dist/testing/service-tests/event-hub-store-tests.d.ts +3 -0
  48. package/dist/testing/service-tests/event-hub-store-tests.js +34 -0
  49. package/dist/testing/service-tests/secret-service-tests.d.ts +3 -0
  50. package/dist/testing/service-tests/secret-service-tests.js +80 -0
  51. package/dist/testing/service-tests/session-store-tests.d.ts +3 -0
  52. package/dist/testing/service-tests/session-store-tests.js +43 -0
  53. package/dist/testing/service-tests/workflow-run-service-tests.d.ts +3 -0
  54. package/dist/testing/service-tests/workflow-run-service-tests.js +42 -0
  55. package/dist/testing/service-tests/workflow-service-tests.d.ts +3 -0
  56. package/dist/testing/service-tests/workflow-service-tests.js +150 -0
  57. package/dist/testing/service-tests.d.ts +6 -0
  58. package/dist/testing/service-tests.js +26 -791
  59. package/dist/types/core.types.d.ts +11 -3
  60. package/dist/types/state.types.d.ts +14 -1
  61. package/dist/wirings/actor-flow/index.d.ts +1 -1
  62. package/dist/wirings/actor-flow/run-conversation.js +1 -6
  63. package/dist/wirings/ai-agent/agent-rpc.d.ts +15 -0
  64. package/dist/wirings/ai-agent/agent-rpc.js +53 -0
  65. package/dist/wirings/ai-agent/ai-agent-agui.js +1 -5
  66. package/dist/wirings/ai-agent/ai-agent-finalize.d.ts +58 -0
  67. package/dist/wirings/ai-agent/ai-agent-finalize.js +138 -0
  68. package/dist/wirings/ai-agent/ai-agent-interrupt.js +1 -0
  69. package/dist/wirings/ai-agent/ai-agent-memory.d.ts +2 -8
  70. package/dist/wirings/ai-agent/ai-agent-memory.js +37 -18
  71. package/dist/wirings/ai-agent/ai-agent-model-config.d.ts +7 -0
  72. package/dist/wirings/ai-agent/ai-agent-model-config.js +44 -1
  73. package/dist/wirings/ai-agent/ai-agent-prepare.js +5 -9
  74. package/dist/wirings/ai-agent/ai-agent-runner.js +89 -147
  75. package/dist/wirings/ai-agent/ai-agent-stream.js +109 -97
  76. package/dist/wirings/ai-agent/ai-agent-turn.d.ts +57 -0
  77. package/dist/wirings/ai-agent/ai-agent-turn.js +82 -0
  78. package/dist/wirings/ai-agent/ai-agent.types.d.ts +47 -2
  79. package/dist/wirings/ai-agent/index.d.ts +8 -7
  80. package/dist/wirings/ai-agent/index.js +5 -4
  81. package/dist/wirings/ai-agent/voice-input.js +1 -6
  82. package/dist/wirings/ai-agent/voice-output.js +2 -12
  83. package/dist/wirings/ai-scorer/ai-scorer-grade.d.ts +26 -0
  84. package/dist/wirings/ai-scorer/ai-scorer-grade.js +33 -0
  85. package/dist/wirings/ai-scorer/ai-scorer-judge.d.ts +17 -0
  86. package/dist/wirings/ai-scorer/ai-scorer-judge.js +92 -0
  87. package/dist/wirings/ai-scorer/ai-scorer-live.d.ts +15 -0
  88. package/dist/wirings/ai-scorer/ai-scorer-live.js +38 -0
  89. package/dist/wirings/ai-scorer/ai-scorer-registry.d.ts +18 -0
  90. package/dist/wirings/ai-scorer/ai-scorer-registry.js +46 -0
  91. package/dist/wirings/ai-scorer/ai-scorer-sampling.d.ts +8 -0
  92. package/dist/wirings/ai-scorer/ai-scorer-sampling.js +31 -0
  93. package/dist/wirings/ai-scorer/ai-scorer-snapshots.d.ts +10 -0
  94. package/dist/wirings/ai-scorer/ai-scorer-snapshots.js +40 -0
  95. package/dist/wirings/ai-scorer/ai-scorer-worker.d.ts +15 -0
  96. package/dist/wirings/ai-scorer/ai-scorer-worker.js +58 -0
  97. package/dist/wirings/ai-scorer/ai-scorer.d.ts +39 -0
  98. package/dist/wirings/ai-scorer/ai-scorer.js +40 -0
  99. package/dist/wirings/ai-scorer/ai-scorer.types.d.ts +90 -0
  100. package/dist/wirings/ai-scorer/ai-scorer.types.js +4 -0
  101. package/dist/wirings/ai-scorer/index.d.ts +6 -0
  102. package/dist/wirings/ai-scorer/index.js +5 -0
  103. package/dist/wirings/channel/channel-common.js +1 -0
  104. package/dist/wirings/channel/channel-handler.js +3 -5
  105. package/dist/wirings/channel/channel-rpc-service.d.ts +0 -6
  106. package/dist/wirings/channel/channel-rpc-service.js +0 -8
  107. package/dist/wirings/channel/channel-rpc.types.d.ts +6 -0
  108. package/dist/wirings/channel/channel-rpc.types.js +8 -0
  109. package/dist/wirings/channel/channel-runner.d.ts +1 -3
  110. package/dist/wirings/channel/channel-runner.js +16 -8
  111. package/dist/wirings/channel/channel.types.d.ts +2 -0
  112. package/dist/wirings/channel/index.d.ts +5 -6
  113. package/dist/wirings/channel/index.js +3 -4
  114. package/dist/wirings/channel/local/local-channel-runner.js +8 -1
  115. package/dist/wirings/channel/pikku-abstract-channel-handler.js +1 -0
  116. package/dist/wirings/channel/serverless/serverless-channel-runner.js +3 -0
  117. package/dist/wirings/cli/channel/cli-channel-runner.js +2 -0
  118. package/dist/wirings/cli/channel/cli-raw-channel-runner.js +9 -1
  119. package/dist/wirings/cli/channel/index.d.ts +1 -2
  120. package/dist/wirings/cli/channel/index.js +0 -1
  121. package/dist/wirings/cli/cli-runner.js +17 -3
  122. package/dist/wirings/cli/cli.types.d.ts +0 -8
  123. package/dist/wirings/cli/command-parser.js +13 -0
  124. package/dist/wirings/credential/index.d.ts +1 -1
  125. package/dist/wirings/gateway/gateway-runner.js +9 -2
  126. package/dist/wirings/gateway/index.d.ts +1 -1
  127. package/dist/wirings/http/http-routes.js +2 -0
  128. package/dist/wirings/http/http-runner.d.ts +0 -10
  129. package/dist/wirings/http/http-runner.js +14 -15
  130. package/dist/wirings/http/http.types.d.ts +0 -10
  131. package/dist/wirings/http/index.d.ts +2 -3
  132. package/dist/wirings/http/index.js +1 -1
  133. package/dist/wirings/mcp/index.d.ts +1 -1
  134. package/dist/wirings/mcp/mcp-runner.d.ts +15 -7
  135. package/dist/wirings/mcp/mcp-runner.js +18 -11
  136. package/dist/wirings/persona/index.d.ts +3 -4
  137. package/dist/wirings/persona/index.js +2 -3
  138. package/dist/wirings/queue/index.d.ts +1 -3
  139. package/dist/wirings/queue/index.js +1 -3
  140. package/dist/wirings/rpc/addon-runner.d.ts +4 -0
  141. package/dist/wirings/rpc/addon-runner.js +19 -3
  142. package/dist/wirings/rpc/rpc-runner.d.ts +8 -51
  143. package/dist/wirings/rpc/rpc-runner.js +55 -86
  144. package/dist/wirings/rpc/rpc-types.d.ts +7 -0
  145. package/dist/wirings/rpc/wire-addon.d.ts +13 -0
  146. package/dist/wirings/rpc/wire-addon.js +4 -0
  147. package/dist/wirings/scheduler/index.d.ts +1 -1
  148. package/dist/wirings/secret/validate-secret-definitions.js +2 -2
  149. package/dist/wirings/trigger/index.d.ts +1 -1
  150. package/dist/wirings/trigger/pikku-trigger-service.d.ts +0 -4
  151. package/dist/wirings/trigger/trigger-runner.js +1 -0
  152. package/dist/wirings/virtual-user/index.d.ts +5 -6
  153. package/dist/wirings/virtual-user/index.js +2 -4
  154. package/dist/wirings/virtual-user/run-virtual-user.js +11 -11
  155. package/dist/wirings/virtual-user/virtual-user-derive.js +4 -25
  156. package/dist/wirings/virtual-user/virtual-user-dispositions.js +1 -4
  157. package/dist/wirings/workflow/dsl/workflow-dsl.types.d.ts +100 -3
  158. package/dist/wirings/workflow/feature.js +7 -4
  159. package/dist/wirings/workflow/graph/graph-runner.d.ts +1 -2
  160. package/dist/wirings/workflow/graph/graph-runner.js +8 -7
  161. package/dist/wirings/workflow/graph/workflow-graph.types.d.ts +0 -4
  162. package/dist/wirings/workflow/index.d.ts +12 -7
  163. package/dist/wirings/workflow/index.js +7 -3
  164. package/dist/wirings/workflow/pikku-scenario-service.d.ts +17 -3
  165. package/dist/wirings/workflow/pikku-scenario-service.js +64 -10
  166. package/dist/wirings/workflow/pikku-workflow-service.d.ts +28 -147
  167. package/dist/wirings/workflow/pikku-workflow-service.js +85 -493
  168. package/dist/wirings/workflow/scenario-step.types.d.ts +8 -0
  169. package/dist/wirings/workflow/workflow-approval-audit.d.ts +16 -0
  170. package/dist/wirings/workflow/workflow-approval-audit.js +40 -0
  171. package/dist/wirings/workflow/workflow-approval-policy.d.ts +20 -0
  172. package/dist/wirings/workflow/workflow-approval-policy.js +48 -0
  173. package/dist/wirings/workflow/workflow-approval.d.ts +67 -0
  174. package/dist/wirings/workflow/workflow-approval.js +177 -0
  175. package/dist/wirings/workflow/workflow-constants.d.ts +26 -0
  176. package/dist/wirings/workflow/workflow-constants.js +35 -0
  177. package/dist/wirings/workflow/workflow-errors.d.ts +58 -0
  178. package/dist/wirings/workflow/workflow-errors.js +112 -0
  179. package/dist/wirings/workflow/workflow-meta-resolver.d.ts +11 -0
  180. package/dist/wirings/workflow/workflow-meta-resolver.js +31 -0
  181. package/dist/wirings/workflow/workflow-queue-routing.d.ts +8 -0
  182. package/dist/wirings/workflow/workflow-queue-routing.js +38 -0
  183. package/dist/wirings/workflow/workflow-queue-wiring.d.ts +20 -0
  184. package/dist/wirings/workflow/workflow-queue-wiring.js +79 -0
  185. package/dist/wirings/workflow/workflow-recovery.d.ts +68 -0
  186. package/dist/wirings/workflow/workflow-recovery.js +101 -0
  187. package/dist/wirings/workflow/workflow-run-engine.types.d.ts +54 -0
  188. package/dist/wirings/workflow/workflow-run-ownership.d.ts +17 -0
  189. package/dist/wirings/workflow/workflow-run-ownership.js +30 -0
  190. package/dist/wirings/workflow/workflow-suspend.d.ts +12 -0
  191. package/dist/wirings/workflow/workflow-suspend.js +33 -0
  192. package/dist/wirings/workflow/workflow.types.d.ts +8 -1
  193. package/knowledge/decisions/internals/a-non-streaming-agent-run-registers-with-airunstate-too.md +22 -0
  194. package/knowledge/decisions/internals/a-resumed-agent-turn-is-as-interruptible-as-the-first.md +20 -0
  195. package/knowledge/decisions/internals/a-scenario-step-template-is-offered-unfilled.md +21 -0
  196. package/knowledge/decisions/internals/a-virtual-user-decides-whether-to-trust-memory-once-per-turn.md +21 -0
  197. package/knowledge/decisions/internals/a-wall-clock-threshold-is-a-load-test-in-disguise.md +46 -0
  198. package/knowledge/decisions/internals/a-workflow-wire-is-built-from-the-run-not-from-the-rpc-service.md +45 -0
  199. package/knowledge/decisions/internals/addon-pikku-meta-ships-at-the-package-root-or-under-dist.md +32 -0
  200. package/knowledge/decisions/internals/agent-context-waits-for-a-tool-result-still-being-written.md +21 -0
  201. package/knowledge/decisions/internals/agent-speech-travels-as-a-custom-agui-event.md +22 -0
  202. package/knowledge/decisions/internals/an-addon-scope-root-loses-to-a-root-the-host-already-declares.md +39 -0
  203. package/knowledge/decisions/internals/an-agent-interrupt-is-not-a-failure.md +23 -0
  204. package/knowledge/decisions/internals/an-agent-run-owned-by-another-instance-says-so.md +25 -0
  205. package/knowledge/decisions/internals/an-agent-stream-send-must-return-the-inner-sends-promise.md +22 -0
  206. package/knowledge/decisions/internals/an-empty-text-part-is-omitted-from-an-agent-message.md +20 -0
  207. package/knowledge/decisions/internals/an-empty-transcript-is-not-recorded.md +22 -0
  208. package/knowledge/decisions/internals/an-unref-d-timer-cannot-be-awaited-under-node-test.md +66 -0
  209. package/knowledge/decisions/internals/channel-state-accessors-are-unsound-generics-that-every-implementation-asserts.md +33 -0
  210. package/knowledge/decisions/internals/gateway-listener-middleware-runs-without-an-rpc-on-the-wire.md +43 -0
  211. package/knowledge/decisions/internals/hot-reload-writes-into-the-function-map-captured-at-startup.md +26 -0
  212. package/knowledge/decisions/internals/index.md +42 -3
  213. package/knowledge/decisions/internals/only-exposed-functions-enter-a-virtual-user-catalogue.md +21 -0
  214. package/knowledge/decisions/internals/scenario-given-and-when-are-sugar-but-then-is-not.md +24 -0
  215. package/knowledge/decisions/internals/side-effects-are-an-allowlist-not-a-boolean.md +34 -0
  216. package/knowledge/decisions/internals/speech-synthesis-picks-a-voice-per-sentence-and-warns-once.md +24 -0
  217. package/knowledge/decisions/internals/the-actor-prompt-says-json-because-of-json-object-mode.md +22 -0
  218. package/knowledge/decisions/internals/the-agent-done-event-goes-through-the-middleware-and-is-awaited.md +26 -0
  219. package/knowledge/decisions/internals/the-api-report-pins-members-not-just-names.md +43 -0
  220. package/knowledge/decisions/internals/the-ecosystem-entry-point-carries-the-adapter-surface.md +58 -0
  221. package/knowledge/decisions/internals/the-middleware-resolution-cache-is-deliberately-unbounded.md +40 -0
  222. package/knowledge/decisions/internals/the-per-invocation-rpc-view-is-a-class.md +40 -0
  223. package/knowledge/decisions/internals/the-persona-runtime-is-exported-from-the-persona-entry-point.md +28 -0
  224. package/knowledge/decisions/internals/the-transcript-event-is-sent-ahead-of-the-run.md +25 -0
  225. package/knowledge/decisions/internals/the-virtual-user-catalogue-is-the-only-gate-on-what-may-be-called.md +21 -0
  226. package/knowledge/decisions/internals/the-worker-disposition-is-the-one-that-is-not-testing.md +22 -0
  227. package/knowledge/decisions/internals/thread-history-records-the-transcript-not-the-audio.md +25 -0
  228. package/knowledge/decisions/internals/validate-runs-checks-by-precondition.md +115 -0
  229. package/knowledge/decisions/internals/virtual-user-step-order-comes-from-insertion-order.md +21 -0
  230. package/knowledge/decisions/internals/voice-output-speaks-unless-voice-input-explicitly-says-otherwise.md +24 -0
  231. package/knowledge/decisions/internals/wiring-registries-erase-the-generics-their-wire-functions-capture.md +35 -0
  232. package/knowledge/decisions/security/a-dropped-audit-write-is-always-logged.md +4 -2
  233. package/knowledge/decisions/security/a-function-never-receives-the-secret-service.md +37 -0
  234. package/knowledge/decisions/security/a-graph-run-starts-at-an-entry-node-the-graph-declared.md +33 -0
  235. package/knowledge/decisions/security/a-permission-gets-a-wire-it-cannot-reply-on.md +33 -0
  236. package/knowledge/decisions/security/a-step-runs-the-function-the-workflow-dispatched-it-with.md +37 -0
  237. package/knowledge/decisions/security/a-virtual-user-is-never-offered-a-step-that-would-forge-its-own-oracle.md +28 -0
  238. package/knowledge/decisions/security/a-workflow-run-is-read-and-approved-by-its-owner.md +50 -0
  239. package/knowledge/decisions/security/an-agent-approval-is-claimed-before-the-tool-runs.md +33 -0
  240. package/knowledge/decisions/security/an-approval-answer-outlives-the-run-it-answered.md +59 -0
  241. package/knowledge/decisions/security/an-upload-is-counted-as-it-arrives-not-buffered-then-measured.md +24 -0
  242. package/knowledge/decisions/security/index.md +9 -0
  243. package/knowledge/questions/channel-middleware-accepts-bare-factories-that-nothing-resolves.md +35 -0
  244. package/knowledge/questions/index.md +2 -1
  245. package/knowledge/questions/unauthorized-channel-replies-escape-the-declared-out-type.md +44 -0
  246. package/package.json +15 -3
  247. package/scripts/generate-api-report.d.mts +1 -0
  248. package/scripts/generate-api-report.mjs +89 -0
  249. package/scripts/generate-api-report.mts +343 -0
  250. package/src/api-report.test.ts +69 -0
  251. package/src/column-form.ts +1 -4
  252. package/src/crypto-utils.test.ts +15 -2
  253. package/src/dev/hot-reload.ts +2 -7
  254. package/src/ecosystem.ts +40 -0
  255. package/src/errors/error-handler.ts +5 -3
  256. package/src/errors/error.test.ts +4 -1
  257. package/src/errors/index.ts +1 -1
  258. package/src/function/function-runner.test.ts +53 -1
  259. package/src/function/function-runner.ts +33 -48
  260. package/src/function/functions.types.ts +15 -9
  261. package/src/function/index.ts +0 -2
  262. package/src/handle-error.ts +1 -1
  263. package/src/index.ts +3 -57
  264. package/src/middleware/auth-cookie.test.ts +50 -0
  265. package/src/middleware/auth-cookie.ts +38 -2
  266. package/src/middleware/cors.ts +2 -1
  267. package/src/middleware/index.ts +1 -1
  268. package/src/middleware/remote-auth.test.ts +43 -0
  269. package/src/middleware/remote-auth.ts +17 -3
  270. package/src/middleware-runner.ts +4 -2
  271. package/src/no-any-casts.test.ts +57 -0
  272. package/src/permissions.test.ts +3 -1
  273. package/src/permissions.ts +22 -24
  274. package/src/pikku-state.ts +22 -6
  275. package/src/public-surface.json +554 -0
  276. package/src/public-surface.json.README +25 -0
  277. package/src/public-surface.test.ts +105 -0
  278. package/src/removed-legacy-exports.test.ts +62 -0
  279. package/src/schema.test.ts +78 -0
  280. package/src/schema.ts +36 -1
  281. package/src/services/ai-agent-runner-service.ts +12 -1
  282. package/src/services/ai-run-state-service.ts +18 -1
  283. package/src/services/audit-service.ts +2 -2
  284. package/src/services/in-memory-ai-run-state-service.ts +16 -2
  285. package/src/services/in-memory-workflow-service.ts +2 -0
  286. package/src/services/index.ts +4 -47
  287. package/src/services/local-content-request-handler.test.ts +43 -5
  288. package/src/services/local-content-request-handler.ts +103 -74
  289. package/src/services/local-content.ts +15 -1
  290. package/src/services/local-email-service.ts +5 -1
  291. package/src/services/scoped-credential-service.test.ts +86 -0
  292. package/src/services/scoped-credential-service.ts +63 -0
  293. package/src/services/system-role-guard.test.ts +4 -1
  294. package/src/services/workflow-service.ts +9 -2
  295. package/src/side-effects-are-declared.test.ts +84 -0
  296. package/src/source-files-stay-composable.test.ts +41 -0
  297. package/src/testing/service-tests/agent-run-service-tests.ts +98 -0
  298. package/src/testing/service-tests/ai-storage-service-tests.ts +379 -0
  299. package/src/testing/service-tests/channel-store-tests.ts +98 -0
  300. package/src/testing/service-tests/credential-service-tests.ts +143 -0
  301. package/src/testing/service-tests/deployment-service-tests.ts +33 -0
  302. package/src/testing/service-tests/event-hub-store-tests.ts +48 -0
  303. package/src/testing/service-tests/secret-service-tests.ts +105 -0
  304. package/src/testing/service-tests/session-store-tests.ts +62 -0
  305. package/src/testing/service-tests/workflow-run-service-tests.ts +59 -0
  306. package/src/testing/service-tests/workflow-service-tests.ts +308 -0
  307. package/src/testing/service-tests.ts +31 -1111
  308. package/src/types/core.types.ts +14 -3
  309. package/src/types/state.types.ts +17 -1
  310. package/src/wirings/actor-flow/index.ts +0 -3
  311. package/src/wirings/actor-flow/run-conversation.ts +1 -6
  312. package/src/wirings/ai-agent/agent-rpc.ts +120 -0
  313. package/src/wirings/ai-agent/ai-agent-agui.ts +1 -5
  314. package/src/wirings/ai-agent/ai-agent-finalize.test.ts +186 -0
  315. package/src/wirings/ai-agent/ai-agent-finalize.ts +197 -0
  316. package/src/wirings/ai-agent/ai-agent-interrupt.test.ts +2 -1
  317. package/src/wirings/ai-agent/ai-agent-interrupt.ts +1 -0
  318. package/src/wirings/ai-agent/ai-agent-memory.ts +62 -39
  319. package/src/wirings/ai-agent/ai-agent-model-config.test.ts +72 -3
  320. package/src/wirings/ai-agent/ai-agent-model-config.ts +49 -1
  321. package/src/wirings/ai-agent/ai-agent-prepare.ts +12 -12
  322. package/src/wirings/ai-agent/ai-agent-resume-authorization.test.ts +1 -0
  323. package/src/wirings/ai-agent/ai-agent-runner.test.ts +82 -2
  324. package/src/wirings/ai-agent/ai-agent-runner.ts +120 -160
  325. package/src/wirings/ai-agent/ai-agent-stream-output-hooks.test.ts +353 -0
  326. package/src/wirings/ai-agent/ai-agent-stream.test.ts +2 -0
  327. package/src/wirings/ai-agent/ai-agent-stream.ts +152 -117
  328. package/src/wirings/ai-agent/ai-agent-thread-ownership.test.ts +1 -1
  329. package/src/wirings/ai-agent/ai-agent-turn.test.ts +67 -0
  330. package/src/wirings/ai-agent/ai-agent-turn.ts +122 -0
  331. package/src/wirings/ai-agent/ai-agent.types.ts +65 -5
  332. package/src/wirings/ai-agent/index.ts +2 -16
  333. package/src/wirings/ai-agent/voice-input.ts +1 -6
  334. package/src/wirings/ai-agent/voice-output.ts +2 -12
  335. package/src/wirings/ai-scorer/ai-scorer-grade.test.ts +106 -0
  336. package/src/wirings/ai-scorer/ai-scorer-grade.ts +55 -0
  337. package/src/wirings/ai-scorer/ai-scorer-judge.test.ts +143 -0
  338. package/src/wirings/ai-scorer/ai-scorer-judge.ts +120 -0
  339. package/src/wirings/ai-scorer/ai-scorer-live.test.ts +174 -0
  340. package/src/wirings/ai-scorer/ai-scorer-live.ts +56 -0
  341. package/src/wirings/ai-scorer/ai-scorer-registry.ts +63 -0
  342. package/src/wirings/ai-scorer/ai-scorer-sampling.test.ts +34 -0
  343. package/src/wirings/ai-scorer/ai-scorer-sampling.ts +36 -0
  344. package/src/wirings/ai-scorer/ai-scorer-snapshots.test.ts +49 -0
  345. package/src/wirings/ai-scorer/ai-scorer-snapshots.ts +46 -0
  346. package/src/wirings/ai-scorer/ai-scorer-worker.test.ts +122 -0
  347. package/src/wirings/ai-scorer/ai-scorer-worker.ts +69 -0
  348. package/src/wirings/ai-scorer/ai-scorer.ts +76 -0
  349. package/src/wirings/ai-scorer/ai-scorer.types.ts +107 -0
  350. package/src/wirings/ai-scorer/index.ts +24 -0
  351. package/src/wirings/channel/channel-common.ts +5 -2
  352. package/src/wirings/channel/channel-handler-shapes.test.ts +72 -0
  353. package/src/wirings/channel/channel-handler.ts +7 -7
  354. package/src/wirings/channel/channel-rpc-service.ts +0 -14
  355. package/src/wirings/channel/channel-rpc.test.ts +25 -4
  356. package/src/wirings/channel/channel-rpc.types.ts +14 -0
  357. package/src/wirings/channel/channel-runner.ts +33 -20
  358. package/src/wirings/channel/channel.types.ts +2 -0
  359. package/src/wirings/channel/index.ts +1 -20
  360. package/src/wirings/channel/local/local-channel-runner.test.ts +68 -0
  361. package/src/wirings/channel/local/local-channel-runner.ts +9 -2
  362. package/src/wirings/channel/pikku-abstract-channel-handler.ts +2 -1
  363. package/src/wirings/channel/serverless/serverless-channel-runner.ts +6 -3
  364. package/src/wirings/cli/channel/cli-channel-runner.ts +3 -1
  365. package/src/wirings/cli/channel/cli-raw-channel-runner.test.ts +23 -0
  366. package/src/wirings/cli/channel/cli-raw-channel-runner.ts +12 -1
  367. package/src/wirings/cli/channel/cli-raw-client-runner.ts +4 -1
  368. package/src/wirings/cli/channel/index.ts +0 -7
  369. package/src/wirings/cli/cli-runner.test.ts +68 -0
  370. package/src/wirings/cli/cli-runner.ts +25 -5
  371. package/src/wirings/cli/cli.types.ts +0 -39
  372. package/src/wirings/cli/command-parser.test.ts +19 -0
  373. package/src/wirings/cli/command-parser.ts +17 -1
  374. package/src/wirings/credential/index.ts +0 -1
  375. package/src/wirings/gateway/gateway-channel-meta.test.ts +44 -0
  376. package/src/wirings/gateway/gateway-runner.ts +27 -19
  377. package/src/wirings/gateway/index.ts +0 -3
  378. package/src/wirings/http/http-routes.ts +4 -1
  379. package/src/wirings/http/http-runner.test.ts +66 -0
  380. package/src/wirings/http/http-runner.ts +21 -28
  381. package/src/wirings/http/http.types.ts +0 -15
  382. package/src/wirings/http/index.ts +2 -8
  383. package/src/wirings/http/pikku-fetch-http-request.ts +2 -2
  384. package/src/wirings/http/web-request.ts +1 -1
  385. package/src/wirings/mcp/index.ts +0 -1
  386. package/src/wirings/mcp/mcp-runner.test.ts +181 -0
  387. package/src/wirings/mcp/mcp-runner.ts +37 -21
  388. package/src/wirings/persona/index.ts +0 -8
  389. package/src/wirings/persona/persona-environments.test.ts +14 -3
  390. package/src/wirings/persona/persona.test.ts +13 -3
  391. package/src/wirings/persona/validate-personas.ts +5 -1
  392. package/src/wirings/queue/index.ts +0 -14
  393. package/src/wirings/queue/queue-runner.ts +1 -1
  394. package/src/wirings/rpc/addon-auth-tags.test.ts +1 -5
  395. package/src/wirings/rpc/addon-runner.ts +34 -3
  396. package/src/wirings/rpc/addon-secrets.test.ts +261 -0
  397. package/src/wirings/rpc/rpc-runner.test.ts +2 -0
  398. package/src/wirings/rpc/rpc-runner.ts +60 -136
  399. package/src/wirings/rpc/rpc-types.ts +11 -0
  400. package/src/wirings/rpc/wire-addon.ts +20 -1
  401. package/src/wirings/scheduler/index.ts +0 -1
  402. package/src/wirings/secret/validate-secret-definitions.test.ts +22 -0
  403. package/src/wirings/secret/validate-secret-definitions.ts +2 -2
  404. package/src/wirings/trigger/index.ts +0 -1
  405. package/src/wirings/trigger/pikku-trigger-service.ts +0 -5
  406. package/src/wirings/trigger/trigger-runner.ts +7 -5
  407. package/src/wirings/virtual-user/index.ts +0 -16
  408. package/src/wirings/virtual-user/run-virtual-user.test.ts +18 -9
  409. package/src/wirings/virtual-user/run-virtual-user.ts +28 -15
  410. package/src/wirings/virtual-user/virtual-user-agents.test.ts +4 -1
  411. package/src/wirings/virtual-user/virtual-user-derive.ts +4 -25
  412. package/src/wirings/virtual-user/virtual-user-dispositions.test.ts +7 -2
  413. package/src/wirings/virtual-user/virtual-user-dispositions.ts +1 -4
  414. package/src/wirings/virtual-user/virtual-user-intents.test.ts +13 -3
  415. package/src/wirings/workflow/dsl/workflow-dsl.types.ts +117 -4
  416. package/src/wirings/workflow/feature.ts +7 -4
  417. package/src/wirings/workflow/graph/graph-node.ts +1 -1
  418. package/src/wirings/workflow/graph/graph-runner.test.ts +76 -2
  419. package/src/wirings/workflow/graph/graph-runner.ts +13 -16
  420. package/src/wirings/workflow/graph/workflow-graph.types.ts +0 -5
  421. package/src/wirings/workflow/index.ts +14 -24
  422. package/src/wirings/workflow/pikku-scenario-service.ts +101 -27
  423. package/src/wirings/workflow/pikku-workflow-service.test.ts +15 -14
  424. package/src/wirings/workflow/pikku-workflow-service.ts +214 -743
  425. package/src/wirings/workflow/scenario-expectations.test.ts +75 -0
  426. package/src/wirings/workflow/scenario-hooks.test.ts +52 -0
  427. package/src/wirings/workflow/scenario-step.types.ts +8 -0
  428. package/src/wirings/workflow/workflow-approval-audit.ts +47 -0
  429. package/src/wirings/workflow/workflow-approval-policy.test.ts +524 -0
  430. package/src/wirings/workflow/workflow-approval-policy.ts +68 -0
  431. package/src/wirings/workflow/workflow-approval.ts +289 -0
  432. package/src/wirings/workflow/workflow-child-run-session.test.ts +89 -0
  433. package/src/wirings/workflow/workflow-constants.ts +47 -0
  434. package/src/wirings/workflow/workflow-dispatch-durability.test.ts +1 -1
  435. package/src/wirings/workflow/workflow-errors.ts +128 -0
  436. package/src/wirings/workflow/workflow-inline-authority.test.ts +12 -6
  437. package/src/wirings/workflow/workflow-meta-resolver.ts +45 -0
  438. package/src/wirings/workflow/workflow-missing-meta.test.ts +92 -0
  439. package/src/wirings/workflow/workflow-queue-routing.ts +85 -0
  440. package/src/wirings/workflow/workflow-queue-wiring.ts +149 -0
  441. package/src/wirings/workflow/workflow-recovery.ts +162 -0
  442. package/src/wirings/workflow/workflow-retry-policy.test.ts +1 -1
  443. package/src/wirings/workflow/workflow-run-authority.test.ts +212 -0
  444. package/src/wirings/workflow/workflow-run-engine.types.ts +91 -0
  445. package/src/wirings/workflow/workflow-run-ownership.ts +37 -0
  446. package/src/wirings/workflow/workflow-suspend.ts +61 -0
  447. package/src/wirings/workflow/workflow.types.ts +7 -9
  448. package/src/wirings-stay-decoupled.test.ts +127 -0
  449. package/tsconfig.json +1 -1
  450. package/tsconfig.tsbuildinfo +1 -1
  451. package/dist/internal.d.ts +0 -3
  452. package/dist/internal.js +0 -2
  453. package/dist/middleware/timeout.d.ts +0 -9
  454. package/dist/middleware/timeout.js +0 -15
  455. package/dist/pikku-response.d.ts +0 -6
  456. package/dist/pikku-response.js +0 -6
  457. package/dist/services/gopass-secrets.d.ts +0 -15
  458. package/dist/services/gopass-secrets.js +0 -76
  459. package/dist/services/http-scenario-actors.d.ts +0 -75
  460. package/dist/services/http-scenario-actors.js +0 -195
  461. package/dist/services/http-user-flow-actors.d.ts +0 -67
  462. package/dist/services/http-user-flow-actors.js +0 -193
  463. package/dist/services/scenario-actors-service.d.ts +0 -127
  464. package/dist/services/scenario-actors-service.js +0 -40
  465. package/dist/services/user-flow-actors-service.d.ts +0 -39
  466. package/dist/wirings/credential/wire-credential.d.ts +0 -48
  467. package/dist/wirings/credential/wire-credential.js +0 -47
  468. package/dist/wirings/oauth2/oauth2-client.d.ts +0 -47
  469. package/dist/wirings/oauth2/oauth2-client.js +0 -263
  470. package/dist/wirings/oauth2/oauth2-routes.d.ts +0 -35
  471. package/dist/wirings/oauth2/oauth2-routes.js +0 -146
  472. package/dist/wirings/scope/wire-scope.d.ts +0 -33
  473. package/dist/wirings/scope/wire-scope.js +0 -32
  474. package/dist/wirings/workflow/dsl/index.d.ts +0 -5
  475. package/dist/wirings/workflow/dsl/index.js +0 -4
  476. package/dist/wirings/workflow/graph/index.d.ts +0 -5
  477. package/dist/wirings/workflow/graph/index.js +0 -4
  478. package/src/internal.ts +0 -10
  479. /package/dist/{services/user-flow-actors-service.js → wirings/workflow/workflow-run-engine.types.js} +0 -0
@@ -206,14 +206,21 @@ export const runLocalChannel = async ({
206
206
 
207
207
  const { onMessage, onBinaryMessage } = processMessageHandlers(
208
208
  services,
209
- channelConfig as any,
209
+ channelConfig,
210
210
  channelHandler,
211
211
  userSession
212
212
  )
213
213
  channelHandler.registerOnMessage(async (data) => {
214
214
  try {
215
215
  const result = await onMessage(data)
216
- await channel.send(result)
216
+ // Guarded exactly as the connect path above is. A handler with nothing
217
+ // to say is normal — a gateway websocket's generated message handler
218
+ // always returns undefined — and sending that raised `send requires a
219
+ // non-empty message`, which surfaced as a channel that accepted a
220
+ // connection and then silently delivered nothing.
221
+ if (result !== undefined) {
222
+ await channel.send(result)
223
+ }
217
224
  } catch (e: any) {
218
225
  singletonServices.logger.error(e)
219
226
  const errorResponse = getErrorResponse(e)
@@ -34,7 +34,8 @@ export abstract class PikkuAbstractChannelHandler<
34
34
  setState: (s) => {
35
35
  channelState = s
36
36
  },
37
- getState: () => channelState as any,
37
+ // knowledge: decisions/internals/channel-state-accessors-are-unsound-generics-that-every-implementation-asserts.md
38
+ getState: () => channelState as never,
38
39
  clearState: () => {
39
40
  channelState = undefined
40
41
  },
@@ -117,7 +117,8 @@ export const runChannelConnect = async ({
117
117
  channelName: channelConfig.name,
118
118
  })
119
119
  channel.setState = (state) => channelStore.setState(channelId, state)
120
- channel.getState = () => channelStore.getState(channelId) as any
120
+ // knowledge: decisions/internals/channel-state-accessors-are-unsound-generics-that-every-implementation-asserts.md
121
+ channel.getState = () => channelStore.getState(channelId) as never
121
122
  channel.clearState = () => channelStore.clearState(channelId)
122
123
 
123
124
  const wire: PikkuRawWire = {
@@ -198,7 +199,8 @@ export const runChannelDisconnect = async ({
198
199
  channelName,
199
200
  })
200
201
  channel.setState = (state) => channelStore.setState(channelId, state)
201
- channel.getState = () => channelStore.getState(channelId) as any
202
+ // knowledge: decisions/internals/channel-state-accessors-are-unsound-generics-that-every-implementation-asserts.md
203
+ channel.getState = () => channelStore.getState(channelId) as never
202
204
  channel.clearState = () => channelStore.clearState(channelId)
203
205
 
204
206
  const userSession = new PikkuSessionService<CoreUserSession>(
@@ -271,7 +273,8 @@ export const runChannelMessage = async (
271
273
  channelName,
272
274
  })
273
275
  channel.setState = (state) => channelStore.setState(channelId, state)
274
- channel.getState = () => channelStore.getState(channelId) as any
276
+ // knowledge: decisions/internals/channel-state-accessors-are-unsound-generics-that-every-implementation-asserts.md
277
+ channel.getState = () => channelStore.getState(channelId) as never
275
278
  channel.clearState = () => channelStore.clearState(channelId)
276
279
 
277
280
  const userSession = new PikkuSessionService<CoreUserSession>(
@@ -90,7 +90,9 @@ export async function executeCLIViaChannel({
90
90
  return
91
91
  }
92
92
 
93
- renderer(null as any, response, undefined)
93
+ // A CLI channel response is rendered off-process: there are no services
94
+ // to hand the renderer, and every CLI renderer ignores the argument.
95
+ renderer(null as never, response, undefined)
94
96
  }
95
97
 
96
98
  commandRoute.subscribe(commandId, responseHandler)
@@ -103,6 +103,29 @@ describe('handleRawCLI', () => {
103
103
  assert.match(result.error!, /deploy plan is red/)
104
104
  })
105
105
 
106
+ test('masks the raw error message to the client in production', async () => {
107
+ const prior = process.env.NODE_ENV
108
+ process.env.NODE_ENV = 'production'
109
+ try {
110
+ wireTestCLI(() => {
111
+ throw new Error('connection to internal-db:5432 failed: bad password')
112
+ })
113
+
114
+ const result = await handleRawCLI({
115
+ programName: 'fabric',
116
+ args: ['deploy'],
117
+ singletonServices,
118
+ })
119
+
120
+ assert.equal(result.exitCode, 1)
121
+ assert.equal(result.error, 'Command failed')
122
+ assert.doesNotMatch(result.error!, /internal-db|password/)
123
+ } finally {
124
+ if (prior === undefined) delete process.env.NODE_ENV
125
+ else process.env.NODE_ENV = prior
126
+ }
127
+ })
128
+
106
129
  test('exits 0 on help and 1 on an unknown command', async () => {
107
130
  wireTestCLI(() => ({}))
108
131
 
@@ -1,4 +1,5 @@
1
1
  import { pikkuState } from '../../../pikku-state.js'
2
+ import { isProduction } from '../../../env.js'
2
3
  import type { CLIMeta } from '../cli.types.js'
3
4
  import { parseCLIArguments, generateCommandHelp } from '../command-parser.js'
4
5
  import { runCLICommand } from '../cli-runner.js'
@@ -132,6 +133,16 @@ export async function handleRawCLI({
132
133
  // A command can throw anything; `.message` off a string is undefined, and
133
134
  // the run would then exit 1 saying nothing at all.
134
135
  const message = e instanceof Error ? e.message : String(e)
135
- return { error: message || 'Command failed', exitCode: 1, commandId }
136
+ // The raw message can carry internals (a stack, a DB error, a path). This
137
+ // runs over a channel that may be remote, so return a generic error in
138
+ // production and keep the detail server-side. Dev keeps the message inline.
139
+ singletonServices.logger.error?.(
140
+ `CLI channel command failed: ${message}`,
141
+ e
142
+ )
143
+ const clientError = isProduction()
144
+ ? 'Command failed'
145
+ : message || 'Command failed'
146
+ return { error: clientError, exitCode: 1, commandId }
136
147
  }
137
148
  }
@@ -32,7 +32,10 @@ export type CorePikkuCLIClientRender<Data> = (
32
32
  ) => void | Promise<void>
33
33
 
34
34
  /** The normal case once the server owns the command tree. */
35
- const defaultJSONRenderer: CorePikkuCLIClientRender<any> = (_services, data) => {
35
+ const defaultJSONRenderer: CorePikkuCLIClientRender<any> = (
36
+ _services,
37
+ data
38
+ ) => {
36
39
  console.log(JSON.stringify(data))
37
40
  }
38
41
 
@@ -3,13 +3,6 @@ export { handleRawCLI } from './cli-raw-channel-runner.js'
3
3
  export type { RawCLIFrame, RawCLIResult } from './cli-raw-channel-runner.js'
4
4
  export { executeRawCLIViaChannel } from './cli-raw-client-runner.js'
5
5
  export type {
6
- ClientCLIRenderServices,
7
6
  CorePikkuCLIClientRender,
8
7
  } from './cli-raw-client-runner.js'
9
- export {
10
- APPROVAL_FLAGS,
11
- approverForMode,
12
- createTerminalApprover,
13
- takeApprovalFlags,
14
- } from './cli-approval.js'
15
8
  export type { ApprovalMode } from './cli-approval.js'
@@ -396,6 +396,74 @@ describe('CLI Runner', () => {
396
396
  const programs = pikkuState(null, 'cli', 'programs')
397
397
  assert.strictEqual(programs['nonexistent'], undefined)
398
398
  })
399
+
400
+ test('enforces command-level permissions declared on a CLI command', async () => {
401
+ let functionRan = false
402
+
403
+ pikkuState(null, 'cli', 'meta', {
404
+ programs: {
405
+ 'sec-cli': {
406
+ program: 'sec-cli',
407
+ commands: {
408
+ secret: {
409
+ command: 'secret',
410
+ pikkuFuncId: 'secretFunc',
411
+ positionals: [],
412
+ options: {},
413
+ },
414
+ },
415
+ options: {},
416
+ },
417
+ },
418
+ renderers: {},
419
+ })
420
+ pikkuState(null, 'function', 'meta', {
421
+ secretFunc: {
422
+ pikkuFuncId: 'secretFunc',
423
+ inputSchemaName: null,
424
+ outputSchemaName: null,
425
+ sessionless: true,
426
+ },
427
+ })
428
+
429
+ wireCLI({
430
+ program: 'sec-cli',
431
+ commands: {
432
+ // command.func is a function-config object, so unwrapFunc reads the
433
+ // inner func's auth/permissions and previously dropped the
434
+ // command-level ones — the branch this fix restores.
435
+ secret: {
436
+ func: {
437
+ func: async () => {
438
+ functionRan = true
439
+ return {}
440
+ },
441
+ },
442
+ // A permission that always denies.
443
+ permissions: { deny: async () => false },
444
+ } as any,
445
+ },
446
+ })
447
+
448
+ const result = await runCLICommand({
449
+ program: 'sec-cli',
450
+ commandPath: ['secret'],
451
+ data: {},
452
+ singletonServices,
453
+ createWireServices,
454
+ }).catch((error) => ({ success: false, error }))
455
+
456
+ assert.equal(
457
+ functionRan,
458
+ false,
459
+ 'the function must not run when a declared permission denies'
460
+ )
461
+ assert.notEqual(
462
+ (result as any).success,
463
+ true,
464
+ 'the command must not report success when permission is denied'
465
+ )
466
+ })
399
467
  })
400
468
 
401
469
  describe('pikkuCLIRender', () => {
@@ -16,6 +16,7 @@ import type {
16
16
  CLIMeta,
17
17
  } from './cli.types.js'
18
18
  import type {
19
+ CoreConfig,
19
20
  CoreSingletonServices,
20
21
  CoreServices,
21
22
  CreateWireServices,
@@ -24,7 +25,7 @@ import type {
24
25
  CreateSingletonServices,
25
26
  } from '../../types/core.types.js'
26
27
  import type { PikkuChannel } from '../channel/channel.types.js'
27
- import { unsupportedChannelRemote } from '../channel/channel-rpc.js'
28
+ import { unsupportedChannelRemote } from '../channel/channel-rpc.types.js'
28
29
  import {
29
30
  PikkuSessionService,
30
31
  createMiddlewareSessionWireProps,
@@ -168,7 +169,24 @@ function registerCLICommands(
168
169
  }
169
170
  }
170
171
 
171
- addFunction(funcName, unwrapFunc(command), currentMeta?.packageName)
172
+ // Merge the command-level auth/permissions into the registered config so
173
+ // they are actually enforced by the function runner. They were accepted by
174
+ // the types but dropped here, making a command's declared access control a
175
+ // silent no-op. Command-level wins over the handler's own, then falls back
176
+ // to it.
177
+ const unwrapped = unwrapFunc(command)
178
+ const commandAuth = typeof command === 'object' ? command.auth : undefined
179
+ const commandPermissions =
180
+ typeof command === 'object' ? command.permissions : undefined
181
+ addFunction(
182
+ funcName,
183
+ {
184
+ ...unwrapped,
185
+ auth: commandAuth ?? unwrapped.auth,
186
+ permissions: commandPermissions ?? unwrapped.permissions,
187
+ },
188
+ currentMeta?.packageName
189
+ )
172
190
 
173
191
  if (typeof command === 'object' && command.render) {
174
192
  if (programs[program]) {
@@ -321,7 +339,8 @@ export async function runCLICommand({
321
339
  setState: (s) => {
322
340
  cliState = s
323
341
  },
324
- getState: () => cliState as any,
342
+ // knowledge: decisions/internals/channel-state-accessors-are-unsound-generics-that-every-implementation-asserts.md
343
+ getState: () => cliState as never,
325
344
  clearState: () => {
326
345
  cliState = undefined
327
346
  },
@@ -372,7 +391,8 @@ export async function runCLICommand({
372
391
  if (result !== undefined && !onOutput) {
373
392
  const commandRenderer = programData?.renderers[commandId]
374
393
  const jsonMode =
375
- (data as any).json === true || (data as any).output === 'json'
394
+ (data as { json?: unknown; output?: unknown }).json === true ||
395
+ (data as { json?: unknown; output?: unknown }).output === 'json'
376
396
  const finalRenderer: CorePikkuCLIRender<any> | undefined =
377
397
  jsonMode && commandRenderer !== undefined
378
398
  ? defaultJSONRenderer
@@ -491,7 +511,7 @@ export async function executeCLI({
491
511
 
492
512
  const config = createConfig
493
513
  ? await createConfig(new LocalVariablesService(), data)
494
- : ({} as any)
514
+ : ({} as CoreConfig)
495
515
 
496
516
  const singletonServices = await createSingletonServices(config)
497
517
  pikkuState(null, 'package', 'singletonServices', singletonServices)
@@ -197,18 +197,6 @@ export type ValidateParameters<Params extends string, Input> =
197
197
  ? Params
198
198
  : never
199
199
 
200
- /**
201
- * Extract output type from a Pikku function config type
202
- */
203
- export type ExtractFunctionOutput<Func> =
204
- Func extends CorePikkuFunctionConfig<infer FuncType, any, any>
205
- ? FuncType extends
206
- | CorePikkuFunction<any, infer Output, any, any, any>
207
- | CorePikkuFunctionSessionless<any, infer Output, any, any, any>
208
- ? Output
209
- : never
210
- : never
211
-
212
200
  /**
213
201
  * CLI command configuration that infers options from function input type.
214
202
  * This is a helper type for creating type-safe CLI commands.
@@ -290,33 +278,6 @@ export type CLICommandShorthand<
290
278
  >,
291
279
  > = PikkuFunctionConfig
292
280
 
293
- /**
294
- * Command definition (either full or shorthand)
295
- */
296
- export type CLICommandDefinition<
297
- In,
298
- Out,
299
- PikkuFunctionConfig extends CorePikkuFunctionConfig<
300
- | CorePikkuFunction<In, Out, any, any, any>
301
- | CorePikkuFunctionSessionless<In, Out, any, any, any>
302
- >,
303
- PikkuPermission extends CorePikkuPermission<any, any, any>,
304
- PikkuMiddleware extends CorePikkuMiddleware,
305
- Options = any,
306
- Subcommands extends Record<string, CoreCLICommandConfig<any, any, any>> =
307
- Record<string, CoreCLICommandConfig<any, any, any>>,
308
- > =
309
- | CoreCLICommand<
310
- In,
311
- Out,
312
- PikkuFunctionConfig,
313
- PikkuPermission,
314
- PikkuMiddleware,
315
- Options,
316
- Subcommands
317
- >
318
- | CLICommandShorthand<In, Out, PikkuFunctionConfig>
319
-
320
281
  /**
321
282
  * CLI wiring configuration
322
283
  */
@@ -710,5 +710,24 @@ describe('Command Parser', () => {
710
710
  assert.ok(result.errors.some((e) => e.includes('Unknown option: -x')))
711
711
  assert.deepStrictEqual(result.warnings, [])
712
712
  })
713
+ describe('short-flag cluster is bounded (DoS)', () => {
714
+ test('a huge short-flag cluster yields a single error, not one per char', () => {
715
+ const huge = '-' + 'a'.repeat(200_000)
716
+ const result = parseCLIArguments(
717
+ ['greet', 'Alice', huge],
718
+ 'test-cli',
719
+ testMeta
720
+ )
721
+ const clusterErrors = result.errors.filter((e) =>
722
+ e.includes('Unknown option')
723
+ )
724
+ // Without the cap this would be ~200k errors (one per character).
725
+ assert.ok(
726
+ clusterErrors.length <= 2,
727
+ `expected the oversized cluster to collapse to a single error, got ${clusterErrors.length}`
728
+ )
729
+ assert.ok(result.errors.some((e) => e.includes('too long')))
730
+ })
731
+ })
713
732
  })
714
733
  })
@@ -86,6 +86,15 @@ export interface ParsedCommand {
86
86
  warnings: string[]
87
87
  }
88
88
 
89
+ /**
90
+ * A real short-flag cluster is a handful of characters (`-abc`). Anything past
91
+ * this is treated as a single malformed token rather than scanned per
92
+ * character — the parser runs over untrusted CLI-channel frames, and a
93
+ * multi-megabyte `-aaaa…` would otherwise be O(length) work that also pushed
94
+ * one error per character.
95
+ */
96
+ const MAX_SHORT_FLAG_CLUSTER = 64
97
+
89
98
  export function parseCLIArguments(
90
99
  args: string[],
91
100
  programName: string,
@@ -238,6 +247,13 @@ export function parseCLIArguments(
238
247
  }
239
248
  }
240
249
  } else if (arg.startsWith('-') && arg.length > 1) {
250
+ if (arg.length > MAX_SHORT_FLAG_CLUSTER) {
251
+ result.errors.push(
252
+ `Unknown option: ${arg.slice(0, MAX_SHORT_FLAG_CLUSTER)}… (too long)`
253
+ )
254
+ currentIndex++
255
+ continue
256
+ }
241
257
  for (let i = 1; i < arg.length; i++) {
242
258
  const shortFlag = arg[i]
243
259
 
@@ -354,7 +370,7 @@ function parseOptionValue(value: string, optionDef?: CLIOption): any {
354
370
  return parseFloat(value)
355
371
  }
356
372
 
357
- if (optionDef.choices && !optionDef.choices.includes(value as any)) {
373
+ if (optionDef.choices && !optionDef.choices.includes(value as never)) {
358
374
  // Deliberately not an error here — applyOptionDefaults reports bad choices.
359
375
  return value
360
376
  }
@@ -2,7 +2,6 @@ export { defineCredential } from './define-credential.js'
2
2
  export { validateAndBuildCredentialDefinitionsMeta } from './validate-credential-definitions.js'
3
3
  export type {
4
4
  CoreCredential,
5
- CredentialDefinitionMeta,
6
5
  CredentialDefinitionsMeta,
7
6
  CredentialDefinitions,
8
7
  } from './credential.types.js'
@@ -0,0 +1,44 @@
1
+ import { describe, test } from 'node:test'
2
+ import assert from 'node:assert/strict'
3
+
4
+ import { pikkuState, resetPikkuState } from '../../pikku-state.js'
5
+ import { wireGateway } from './gateway-runner.js'
6
+
7
+ const wireWebsocketGateway = (name: string) => {
8
+ resetPikkuState()
9
+ wireGateway({
10
+ name,
11
+ type: 'websocket',
12
+ route: `/${name}`,
13
+ adapter: { name: 'test', parse: () => null, send: async () => {} },
14
+ func: { func: async () => undefined },
15
+ } as any)
16
+ return pikkuState(null, 'channel', 'meta')[name]!
17
+ }
18
+
19
+ describe('a gateway-wired websocket channel writes complete channel meta', () => {
20
+ test('the fields every channel reader indexes are present', () => {
21
+ const meta = wireWebsocketGateway('chatGateway')
22
+
23
+ // channel-handler.ts indexes messageWirings without a guard, and the
24
+ // serverless and local runners read disconnect directly.
25
+ assert.deepEqual(
26
+ meta.messageWirings,
27
+ {},
28
+ 'messageWirings must be an object — channel-handler indexes it unguarded'
29
+ )
30
+ assert.equal(meta.disconnect, null)
31
+ assert.equal(meta.input, null)
32
+ })
33
+
34
+ test('it is marked as gateway-wired', () => {
35
+ assert.equal(wireWebsocketGateway('flagged').gateway, true)
36
+ })
37
+
38
+ test('connect and message point at the generated handlers', () => {
39
+ const meta = wireWebsocketGateway('handlers')
40
+
41
+ assert.equal(meta.connect?.pikkuFuncId, 'gateway__handlers__connect')
42
+ assert.equal(meta.message?.pikkuFuncId, 'gateway__handlers__message')
43
+ })
44
+ })
@@ -3,6 +3,7 @@ import { NotFoundError, UnauthorizedError } from '../../errors/errors.js'
3
3
  import { addFunction, runPikkuFunc } from '../../function/function-runner.js'
4
4
  import { runMiddleware } from '../../middleware-runner.js'
5
5
  import { httpRouter } from '../http/routers/http-router.js'
6
+ import type { CoreHTTPFunctionWiring } from '../http/http.types.js'
6
7
  import type {
7
8
  CoreGateway,
8
9
  GatewayAdapter,
@@ -50,7 +51,7 @@ const registerGatewayHandler = (config: CoreGateway): string => {
50
51
  outputSchemaName: declared?.outputSchemaName ?? null,
51
52
  sessionless: declared?.sessionless ?? true,
52
53
  }
53
- addFunction(funcId, config.func as any)
54
+ addFunction(funcId, config.func)
54
55
  return funcId
55
56
  }
56
57
 
@@ -99,7 +100,7 @@ export const wireGateway = (config: CoreGateway): void => {
99
100
  break
100
101
  default:
101
102
  throw new Error(
102
- `Unknown gateway type '${(config as any).type}' for gateway '${config.name}'`
103
+ `Unknown gateway type '${config.type}' for gateway '${config.name}'`
103
104
  )
104
105
  }
105
106
  }
@@ -118,7 +119,7 @@ const wireWebhookGateway = (config: CoreGateway): void => {
118
119
  func: createWebhookPostHandler(config),
119
120
  }
120
121
 
121
- addFunction(postFuncId, postHandler as any)
122
+ addFunction(postFuncId, postHandler)
122
123
 
123
124
  if (!routes.has('post')) {
124
125
  routes.set('post', new Map())
@@ -128,7 +129,10 @@ const wireWebhookGateway = (config: CoreGateway): void => {
128
129
  route,
129
130
  func: postHandler,
130
131
  auth: false,
131
- } as any)
132
+ // A gateway handler takes (services, data, wire) rather than a pikku
133
+ // function's shape, so it never matches CorePikkuFunctionConfig. It is only
134
+ // ever invoked through runPikkuFunc, which is what makes that safe.
135
+ } as unknown as CoreHTTPFunctionWiring<unknown, unknown, string>)
132
136
 
133
137
  // knowledge: decisions/internals/gateway-adapters-resolve-lazily-and-are-promise-cached.md
134
138
  if (typeof adapter === 'function' || adapter.verifyWebhook) {
@@ -139,7 +143,7 @@ const wireWebhookGateway = (config: CoreGateway): void => {
139
143
  func: createWebhookVerifyHandler(config),
140
144
  }
141
145
 
142
- addFunction(verifyFuncId, verifyHandler as any)
146
+ addFunction(verifyFuncId, verifyHandler)
143
147
 
144
148
  if (!routes.has('get')) {
145
149
  routes.set('get', new Map())
@@ -151,7 +155,7 @@ const wireWebhookGateway = (config: CoreGateway): void => {
151
155
  auth: false,
152
156
  // knowledge: decisions/internals/gateway-webhook-challenges-echo-bytes-not-json.md
153
157
  returnsJSON: false,
154
- } as any)
158
+ } as unknown as CoreHTTPFunctionWiring<unknown, unknown, string>)
155
159
  }
156
160
 
157
161
  httpRouter.reset()
@@ -187,17 +191,17 @@ const createWebhookPostHandler = (config: CoreGateway) => {
187
191
  platform: adapter.name,
188
192
  send: (msg: GatewayOutboundMessage) => adapter.send(parsed.senderId, msg),
189
193
  }
190
- ;(wire as any).gateway = gateway
194
+ wire.gateway = gateway
191
195
 
192
196
  // Gateway middleware runs outside the gate so it can establish the session the gate checks.
193
197
  const invoke = async () => {
194
- await bridgeMiddlewareSession(wire as any)
198
+ await bridgeMiddlewareSession(wire)
195
199
  return await runPikkuFunc('gateway', name, handlerFuncId, {
196
200
  singletonServices: services,
197
201
  data: () => parsed,
198
202
  auth: config.auth,
199
203
  inheritedMiddleware,
200
- wire: wire as any,
204
+ wire,
201
205
  })
202
206
  }
203
207
 
@@ -275,9 +279,12 @@ const wireWebsocketGateway = (config: CoreGateway): void => {
275
279
  name,
276
280
  route,
277
281
  gateway: true,
282
+ input: null,
278
283
  connect: { pikkuFuncId: connectFuncId },
284
+ disconnect: null,
279
285
  message: { pikkuFuncId: messageFuncId },
280
- } as any
286
+ messageWirings: {},
287
+ }
281
288
 
282
289
  const userMiddleware = config.middleware as CorePikkuMiddleware[] | undefined
283
290
  const handlerFuncId = registerGatewayHandler(config)
@@ -287,7 +294,7 @@ const wireWebsocketGateway = (config: CoreGateway): void => {
287
294
  auth: false,
288
295
  func: async (services: any, _data: unknown, wire: PikkuWire) => {
289
296
  const adapter = await resolveGatewayAdapter(config, services)
290
- ;(wire as any).gateway = {
297
+ wire.gateway = {
291
298
  gatewayName: name,
292
299
  senderId: '',
293
300
  platform: adapter.name,
@@ -296,7 +303,7 @@ const wireWebsocketGateway = (config: CoreGateway): void => {
296
303
  },
297
304
  } satisfies PikkuGateway
298
305
  },
299
- } as any)
306
+ })
300
307
 
301
308
  addFunction(messageFuncId, {
302
309
  auth: false,
@@ -313,16 +320,16 @@ const wireWebsocketGateway = (config: CoreGateway): void => {
313
320
  wire.channel?.send(msg)
314
321
  },
315
322
  }
316
- ;(wire as any).gateway = gateway
323
+ wire.gateway = gateway
317
324
 
318
325
  const invoke = async () => {
319
- await bridgeMiddlewareSession(wire as any)
326
+ await bridgeMiddlewareSession(wire)
320
327
  return await runPikkuFunc('gateway', name, handlerFuncId, {
321
328
  singletonServices: services,
322
329
  data: () => parsed,
323
330
  auth: config.auth,
324
331
  inheritedMiddleware,
325
- wire: wire as any,
332
+ wire,
326
333
  })
327
334
  }
328
335
 
@@ -337,7 +344,7 @@ const wireWebsocketGateway = (config: CoreGateway): void => {
337
344
  wire.channel?.send(result)
338
345
  }
339
346
  },
340
- } as any)
347
+ })
341
348
 
342
349
  // knowledge: decisions/internals/gateway-wiring-is-a-meta-wiring-over-http-and-channels.md
343
350
  channels.set(name, {
@@ -346,7 +353,7 @@ const wireWebsocketGateway = (config: CoreGateway): void => {
346
353
  auth: false,
347
354
  onConnect: { func: async () => {} },
348
355
  onMessage: { func: async () => {} },
349
- } as any)
356
+ })
350
357
 
351
358
  httpRouter.reset()
352
359
  }
@@ -377,7 +384,7 @@ export const createListenerMessageHandler = (
377
384
  platform: adapter.name,
378
385
  send: (msg: GatewayOutboundMessage) => adapter.send(parsed.senderId, msg),
379
386
  }
380
- ;(wire as any).gateway = gateway
387
+ wire.gateway = gateway
381
388
 
382
389
  const invoke = async () => {
383
390
  await bridgeMiddlewareSession(wire)
@@ -390,10 +397,11 @@ export const createListenerMessageHandler = (
390
397
  })
391
398
  }
392
399
 
400
+ // knowledge: decisions/internals/gateway-listener-middleware-runs-without-an-rpc-on-the-wire.md
393
401
  const result: any = userMiddleware?.length
394
402
  ? await runMiddleware(
395
403
  singletonServices,
396
- wire as any,
404
+ wire as PikkuWire,
397
405
  userMiddleware,
398
406
  invoke
399
407
  )
@@ -6,13 +6,10 @@ export {
6
6
  export type {
7
7
  GatewayAdapter,
8
8
  GatewayAdapterFactory,
9
- GatewayAttachment,
10
9
  GatewayInboundMessage,
11
10
  GatewayOutboundMessage,
12
- GatewayMeta,
13
11
  GatewaysMeta,
14
12
  GatewayTransportType,
15
13
  CoreGateway,
16
- PikkuGateway,
17
14
  WebhookVerificationResult,
18
15
  } from './gateway.types.js'