@pikku/core 0.12.79 → 0.12.80

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 (330) hide show
  1. package/CHANGELOG.md +161 -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/function/function-runner.js +20 -42
  7. package/dist/function/functions.types.d.ts +9 -5
  8. package/dist/index.d.ts +4 -9
  9. package/dist/index.js +3 -8
  10. package/dist/middleware/auth-cookie.d.ts +0 -4
  11. package/dist/middleware/auth-cookie.js +38 -2
  12. package/dist/middleware/cors.js +1 -0
  13. package/dist/middleware/index.d.ts +1 -1
  14. package/dist/middleware/index.js +1 -1
  15. package/dist/middleware/remote-auth.js +14 -3
  16. package/dist/permissions.d.ts +8 -10
  17. package/dist/permissions.js +0 -11
  18. package/dist/pikku-state.js +12 -5
  19. package/dist/schema.js +35 -1
  20. package/dist/services/ai-run-state-service.d.ts +7 -1
  21. package/dist/services/in-memory-ai-run-state-service.d.ts +1 -1
  22. package/dist/services/in-memory-ai-run-state-service.js +2 -1
  23. package/dist/services/in-memory-workflow-service.js +2 -0
  24. package/dist/services/local-content-request-handler.d.ts +21 -0
  25. package/dist/services/local-content-request-handler.js +72 -53
  26. package/dist/services/local-content.d.ts +6 -0
  27. package/dist/services/local-content.js +14 -1
  28. package/dist/services/workflow-service.d.ts +4 -2
  29. package/dist/testing/service-tests/agent-run-service-tests.d.ts +10 -0
  30. package/dist/testing/service-tests/agent-run-service-tests.js +72 -0
  31. package/dist/testing/service-tests/ai-storage-service-tests.d.ts +3 -0
  32. package/dist/testing/service-tests/ai-storage-service-tests.js +226 -0
  33. package/dist/testing/service-tests/channel-store-tests.d.ts +3 -0
  34. package/dist/testing/service-tests/channel-store-tests.js +72 -0
  35. package/dist/testing/service-tests/credential-service-tests.d.ts +3 -0
  36. package/dist/testing/service-tests/credential-service-tests.js +109 -0
  37. package/dist/testing/service-tests/deployment-service-tests.d.ts +3 -0
  38. package/dist/testing/service-tests/deployment-service-tests.js +21 -0
  39. package/dist/testing/service-tests/event-hub-store-tests.d.ts +3 -0
  40. package/dist/testing/service-tests/event-hub-store-tests.js +34 -0
  41. package/dist/testing/service-tests/secret-service-tests.d.ts +3 -0
  42. package/dist/testing/service-tests/secret-service-tests.js +80 -0
  43. package/dist/testing/service-tests/session-store-tests.d.ts +3 -0
  44. package/dist/testing/service-tests/session-store-tests.js +43 -0
  45. package/dist/testing/service-tests/workflow-run-service-tests.d.ts +3 -0
  46. package/dist/testing/service-tests/workflow-run-service-tests.js +42 -0
  47. package/dist/testing/service-tests/workflow-service-tests.d.ts +3 -0
  48. package/dist/testing/service-tests/workflow-service-tests.js +150 -0
  49. package/dist/testing/service-tests.d.ts +6 -0
  50. package/dist/testing/service-tests.js +26 -791
  51. package/dist/types/core.types.d.ts +11 -1
  52. package/dist/types/state.types.d.ts +1 -1
  53. package/dist/wirings/actor-flow/run-conversation.js +1 -6
  54. package/dist/wirings/ai-agent/agent-rpc.d.ts +15 -0
  55. package/dist/wirings/ai-agent/agent-rpc.js +53 -0
  56. package/dist/wirings/ai-agent/ai-agent-agui.js +1 -5
  57. package/dist/wirings/ai-agent/ai-agent-memory.js +3 -1
  58. package/dist/wirings/ai-agent/ai-agent-prepare.js +3 -9
  59. package/dist/wirings/ai-agent/ai-agent-runner.js +28 -107
  60. package/dist/wirings/ai-agent/ai-agent-stream.js +20 -61
  61. package/dist/wirings/ai-agent/ai-agent-turn.d.ts +56 -0
  62. package/dist/wirings/ai-agent/ai-agent-turn.js +81 -0
  63. package/dist/wirings/ai-agent/ai-agent.types.d.ts +1 -1
  64. package/dist/wirings/ai-agent/voice-input.js +1 -6
  65. package/dist/wirings/ai-agent/voice-output.js +2 -12
  66. package/dist/wirings/channel/channel-common.js +1 -0
  67. package/dist/wirings/channel/channel-handler.js +3 -5
  68. package/dist/wirings/channel/channel-rpc-service.d.ts +0 -6
  69. package/dist/wirings/channel/channel-rpc-service.js +0 -8
  70. package/dist/wirings/channel/channel-rpc.types.d.ts +6 -0
  71. package/dist/wirings/channel/channel-rpc.types.js +8 -0
  72. package/dist/wirings/channel/channel-runner.d.ts +1 -3
  73. package/dist/wirings/channel/channel-runner.js +16 -8
  74. package/dist/wirings/channel/channel.types.d.ts +2 -0
  75. package/dist/wirings/channel/pikku-abstract-channel-handler.js +1 -0
  76. package/dist/wirings/channel/serverless/serverless-channel-runner.js +3 -0
  77. package/dist/wirings/cli/channel/cli-channel-runner.js +2 -0
  78. package/dist/wirings/cli/cli-runner.js +4 -2
  79. package/dist/wirings/cli/cli.types.d.ts +0 -8
  80. package/dist/wirings/cli/command-parser.js +13 -0
  81. package/dist/wirings/gateway/gateway-runner.js +9 -2
  82. package/dist/wirings/http/http-routes.js +2 -0
  83. package/dist/wirings/http/http-runner.d.ts +0 -10
  84. package/dist/wirings/http/http-runner.js +6 -13
  85. package/dist/wirings/http/http.types.d.ts +0 -10
  86. package/dist/wirings/http/index.d.ts +1 -1
  87. package/dist/wirings/http/index.js +1 -1
  88. package/dist/wirings/mcp/mcp-runner.d.ts +0 -7
  89. package/dist/wirings/mcp/mcp-runner.js +0 -6
  90. package/dist/wirings/rpc/rpc-runner.d.ts +8 -51
  91. package/dist/wirings/rpc/rpc-runner.js +53 -86
  92. package/dist/wirings/rpc/rpc-types.d.ts +3 -0
  93. package/dist/wirings/secret/validate-secret-definitions.js +2 -2
  94. package/dist/wirings/trigger/pikku-trigger-service.d.ts +0 -4
  95. package/dist/wirings/trigger/trigger-runner.js +1 -0
  96. package/dist/wirings/virtual-user/run-virtual-user.js +11 -11
  97. package/dist/wirings/virtual-user/virtual-user-derive.js +4 -25
  98. package/dist/wirings/virtual-user/virtual-user-dispositions.js +1 -4
  99. package/dist/wirings/workflow/dsl/workflow-dsl.types.d.ts +29 -2
  100. package/dist/wirings/workflow/feature.js +7 -4
  101. package/dist/wirings/workflow/graph/graph-runner.d.ts +1 -2
  102. package/dist/wirings/workflow/graph/graph-runner.js +8 -7
  103. package/dist/wirings/workflow/graph/workflow-graph.types.d.ts +0 -4
  104. package/dist/wirings/workflow/index.d.ts +7 -2
  105. package/dist/wirings/workflow/index.js +5 -1
  106. package/dist/wirings/workflow/pikku-scenario-service.d.ts +17 -3
  107. package/dist/wirings/workflow/pikku-scenario-service.js +38 -10
  108. package/dist/wirings/workflow/pikku-workflow-service.d.ts +28 -147
  109. package/dist/wirings/workflow/pikku-workflow-service.js +71 -493
  110. package/dist/wirings/workflow/workflow-approval.d.ts +39 -0
  111. package/dist/wirings/workflow/workflow-approval.js +114 -0
  112. package/dist/wirings/workflow/workflow-constants.d.ts +26 -0
  113. package/dist/wirings/workflow/workflow-constants.js +35 -0
  114. package/dist/wirings/workflow/workflow-errors.d.ts +58 -0
  115. package/dist/wirings/workflow/workflow-errors.js +112 -0
  116. package/dist/wirings/workflow/workflow-meta-resolver.d.ts +11 -0
  117. package/dist/wirings/workflow/workflow-meta-resolver.js +31 -0
  118. package/dist/wirings/workflow/workflow-queue-routing.d.ts +8 -0
  119. package/dist/wirings/workflow/workflow-queue-routing.js +38 -0
  120. package/dist/wirings/workflow/workflow-queue-wiring.d.ts +20 -0
  121. package/dist/wirings/workflow/workflow-queue-wiring.js +79 -0
  122. package/dist/wirings/workflow/workflow-recovery.d.ts +68 -0
  123. package/dist/wirings/workflow/workflow-recovery.js +101 -0
  124. package/dist/wirings/workflow/workflow-run-engine.types.d.ts +54 -0
  125. package/dist/wirings/workflow/workflow-run-engine.types.js +1 -0
  126. package/dist/wirings/workflow/workflow-run-ownership.d.ts +16 -0
  127. package/dist/wirings/workflow/workflow-run-ownership.js +29 -0
  128. package/dist/wirings/workflow/workflow-suspend.d.ts +12 -0
  129. package/dist/wirings/workflow/workflow-suspend.js +33 -0
  130. package/dist/wirings/workflow/workflow.types.d.ts +7 -0
  131. package/knowledge/decisions/internals/a-non-streaming-agent-run-registers-with-airunstate-too.md +22 -0
  132. package/knowledge/decisions/internals/a-resumed-agent-turn-is-as-interruptible-as-the-first.md +20 -0
  133. package/knowledge/decisions/internals/a-scenario-step-template-is-offered-unfilled.md +21 -0
  134. package/knowledge/decisions/internals/a-virtual-user-decides-whether-to-trust-memory-once-per-turn.md +21 -0
  135. package/knowledge/decisions/internals/a-wall-clock-threshold-is-a-load-test-in-disguise.md +46 -0
  136. package/knowledge/decisions/internals/a-workflow-wire-is-built-from-the-run-not-from-the-rpc-service.md +45 -0
  137. package/knowledge/decisions/internals/agent-context-waits-for-a-tool-result-still-being-written.md +21 -0
  138. package/knowledge/decisions/internals/agent-speech-travels-as-a-custom-agui-event.md +22 -0
  139. package/knowledge/decisions/internals/an-agent-interrupt-is-not-a-failure.md +23 -0
  140. package/knowledge/decisions/internals/an-agent-run-owned-by-another-instance-says-so.md +25 -0
  141. package/knowledge/decisions/internals/an-agent-stream-send-must-return-the-inner-sends-promise.md +22 -0
  142. package/knowledge/decisions/internals/an-empty-text-part-is-omitted-from-an-agent-message.md +20 -0
  143. package/knowledge/decisions/internals/an-empty-transcript-is-not-recorded.md +22 -0
  144. package/knowledge/decisions/internals/an-unref-d-timer-cannot-be-awaited-under-node-test.md +66 -0
  145. package/knowledge/decisions/internals/channel-state-accessors-are-unsound-generics-that-every-implementation-asserts.md +33 -0
  146. package/knowledge/decisions/internals/gateway-listener-middleware-runs-without-an-rpc-on-the-wire.md +43 -0
  147. package/knowledge/decisions/internals/hot-reload-writes-into-the-function-map-captured-at-startup.md +26 -0
  148. package/knowledge/decisions/internals/index.md +38 -26
  149. package/knowledge/decisions/internals/only-exposed-functions-enter-a-virtual-user-catalogue.md +21 -0
  150. package/knowledge/decisions/internals/scenario-given-and-when-are-sugar-but-then-is-not.md +24 -0
  151. package/knowledge/decisions/internals/side-effects-are-an-allowlist-not-a-boolean.md +34 -0
  152. package/knowledge/decisions/internals/speech-synthesis-picks-a-voice-per-sentence-and-warns-once.md +24 -0
  153. package/knowledge/decisions/internals/the-actor-prompt-says-json-because-of-json-object-mode.md +22 -0
  154. package/knowledge/decisions/internals/the-agent-done-event-goes-through-the-middleware-and-is-awaited.md +26 -0
  155. package/knowledge/decisions/internals/the-api-report-pins-members-not-just-names.md +43 -0
  156. package/knowledge/decisions/internals/the-ecosystem-entry-point-carries-the-adapter-surface.md +58 -0
  157. package/knowledge/decisions/internals/the-middleware-resolution-cache-is-deliberately-unbounded.md +40 -0
  158. package/knowledge/decisions/internals/the-per-invocation-rpc-view-is-a-class.md +40 -0
  159. package/knowledge/decisions/internals/the-persona-runtime-is-exported-from-the-persona-entry-point.md +28 -0
  160. package/knowledge/decisions/internals/the-transcript-event-is-sent-ahead-of-the-run.md +25 -0
  161. package/knowledge/decisions/internals/the-virtual-user-catalogue-is-the-only-gate-on-what-may-be-called.md +21 -0
  162. package/knowledge/decisions/internals/the-worker-disposition-is-the-one-that-is-not-testing.md +22 -0
  163. package/knowledge/decisions/internals/thread-history-records-the-transcript-not-the-audio.md +25 -0
  164. package/knowledge/decisions/internals/virtual-user-step-order-comes-from-insertion-order.md +21 -0
  165. package/knowledge/decisions/internals/voice-output-speaks-unless-voice-input-explicitly-says-otherwise.md +24 -0
  166. package/knowledge/decisions/internals/wiring-registries-erase-the-generics-their-wire-functions-capture.md +35 -0
  167. package/knowledge/decisions/security/a-dropped-audit-write-is-always-logged.md +4 -2
  168. package/knowledge/decisions/security/a-graph-run-starts-at-an-entry-node-the-graph-declared.md +33 -0
  169. package/knowledge/decisions/security/a-permission-gets-a-wire-it-cannot-reply-on.md +33 -0
  170. package/knowledge/decisions/security/a-step-runs-the-function-the-workflow-dispatched-it-with.md +37 -0
  171. package/knowledge/decisions/security/a-virtual-user-is-never-offered-a-step-that-would-forge-its-own-oracle.md +28 -0
  172. package/knowledge/decisions/security/a-workflow-run-is-read-and-approved-by-its-owner.md +34 -0
  173. package/knowledge/decisions/security/an-agent-approval-is-claimed-before-the-tool-runs.md +33 -0
  174. package/knowledge/decisions/security/an-upload-is-counted-as-it-arrives-not-buffered-then-measured.md +24 -0
  175. package/knowledge/decisions/security/index.md +7 -0
  176. package/knowledge/questions/channel-middleware-accepts-bare-factories-that-nothing-resolves.md +35 -0
  177. package/knowledge/questions/index.md +2 -1
  178. package/knowledge/questions/unauthorized-channel-replies-escape-the-declared-out-type.md +44 -0
  179. package/package.json +13 -3
  180. package/scripts/generate-api-report.d.mts +1 -0
  181. package/scripts/generate-api-report.mjs +89 -0
  182. package/scripts/generate-api-report.mts +218 -0
  183. package/src/api-report.test.ts +69 -0
  184. package/src/column-form.ts +1 -4
  185. package/src/crypto-utils.test.ts +15 -2
  186. package/src/dev/hot-reload.ts +2 -7
  187. package/src/ecosystem.ts +40 -0
  188. package/src/errors/error-handler.ts +5 -3
  189. package/src/errors/error.test.ts +4 -1
  190. package/src/function/function-runner.test.ts +1 -1
  191. package/src/function/function-runner.ts +28 -39
  192. package/src/function/functions.types.ts +15 -9
  193. package/src/handle-error.ts +1 -1
  194. package/src/index.ts +3 -22
  195. package/src/middleware/auth-cookie.test.ts +50 -0
  196. package/src/middleware/auth-cookie.ts +38 -2
  197. package/src/middleware/cors.ts +2 -1
  198. package/src/middleware/index.ts +1 -1
  199. package/src/middleware/remote-auth.test.ts +43 -0
  200. package/src/middleware/remote-auth.ts +17 -3
  201. package/src/middleware-runner.ts +4 -2
  202. package/src/no-any-casts.test.ts +57 -0
  203. package/src/permissions.test.ts +3 -1
  204. package/src/permissions.ts +22 -24
  205. package/src/pikku-state.ts +17 -6
  206. package/src/public-surface.json +578 -0
  207. package/src/public-surface.json.README +25 -0
  208. package/src/public-surface.test.ts +105 -0
  209. package/src/removed-legacy-exports.test.ts +62 -0
  210. package/src/schema.test.ts +78 -0
  211. package/src/schema.ts +36 -1
  212. package/src/services/ai-run-state-service.ts +7 -1
  213. package/src/services/audit-service.ts +2 -2
  214. package/src/services/in-memory-ai-run-state-service.ts +3 -2
  215. package/src/services/in-memory-workflow-service.ts +2 -0
  216. package/src/services/index.ts +1 -4
  217. package/src/services/local-content-request-handler.test.ts +43 -5
  218. package/src/services/local-content-request-handler.ts +103 -74
  219. package/src/services/local-content.ts +15 -1
  220. package/src/services/local-email-service.ts +5 -1
  221. package/src/services/system-role-guard.test.ts +4 -1
  222. package/src/services/workflow-service.ts +9 -2
  223. package/src/side-effects-are-declared.test.ts +84 -0
  224. package/src/source-files-stay-composable.test.ts +41 -0
  225. package/src/testing/service-tests/agent-run-service-tests.ts +98 -0
  226. package/src/testing/service-tests/ai-storage-service-tests.ts +286 -0
  227. package/src/testing/service-tests/channel-store-tests.ts +98 -0
  228. package/src/testing/service-tests/credential-service-tests.ts +143 -0
  229. package/src/testing/service-tests/deployment-service-tests.ts +33 -0
  230. package/src/testing/service-tests/event-hub-store-tests.ts +48 -0
  231. package/src/testing/service-tests/secret-service-tests.ts +105 -0
  232. package/src/testing/service-tests/session-store-tests.ts +62 -0
  233. package/src/testing/service-tests/workflow-run-service-tests.ts +59 -0
  234. package/src/testing/service-tests/workflow-service-tests.ts +308 -0
  235. package/src/testing/service-tests.ts +31 -1111
  236. package/src/types/core.types.ts +15 -1
  237. package/src/types/state.types.ts +1 -1
  238. package/src/wirings/actor-flow/run-conversation.ts +1 -6
  239. package/src/wirings/ai-agent/agent-rpc.ts +120 -0
  240. package/src/wirings/ai-agent/ai-agent-agui.ts +1 -5
  241. package/src/wirings/ai-agent/ai-agent-interrupt.test.ts +2 -1
  242. package/src/wirings/ai-agent/ai-agent-memory.ts +8 -1
  243. package/src/wirings/ai-agent/ai-agent-prepare.ts +10 -12
  244. package/src/wirings/ai-agent/ai-agent-resume-authorization.test.ts +1 -0
  245. package/src/wirings/ai-agent/ai-agent-runner.test.ts +82 -2
  246. package/src/wirings/ai-agent/ai-agent-runner.ts +49 -120
  247. package/src/wirings/ai-agent/ai-agent-stream.test.ts +2 -0
  248. package/src/wirings/ai-agent/ai-agent-stream.ts +36 -63
  249. package/src/wirings/ai-agent/ai-agent-thread-ownership.test.ts +1 -1
  250. package/src/wirings/ai-agent/ai-agent-turn.ts +121 -0
  251. package/src/wirings/ai-agent/ai-agent.types.ts +1 -1
  252. package/src/wirings/ai-agent/voice-input.ts +1 -6
  253. package/src/wirings/ai-agent/voice-output.ts +2 -12
  254. package/src/wirings/channel/channel-common.ts +5 -2
  255. package/src/wirings/channel/channel-handler-shapes.test.ts +72 -0
  256. package/src/wirings/channel/channel-handler.ts +7 -7
  257. package/src/wirings/channel/channel-rpc-service.ts +0 -14
  258. package/src/wirings/channel/channel-rpc.test.ts +25 -4
  259. package/src/wirings/channel/channel-rpc.types.ts +14 -0
  260. package/src/wirings/channel/channel-runner.ts +33 -20
  261. package/src/wirings/channel/channel.types.ts +2 -0
  262. package/src/wirings/channel/local/local-channel-runner.ts +1 -1
  263. package/src/wirings/channel/pikku-abstract-channel-handler.ts +2 -1
  264. package/src/wirings/channel/serverless/serverless-channel-runner.ts +6 -3
  265. package/src/wirings/cli/channel/cli-channel-runner.ts +3 -1
  266. package/src/wirings/cli/channel/cli-raw-client-runner.ts +4 -1
  267. package/src/wirings/cli/cli-runner.ts +7 -4
  268. package/src/wirings/cli/cli.types.ts +0 -39
  269. package/src/wirings/cli/command-parser.test.ts +19 -0
  270. package/src/wirings/cli/command-parser.ts +17 -1
  271. package/src/wirings/gateway/gateway-channel-meta.test.ts +44 -0
  272. package/src/wirings/gateway/gateway-runner.ts +27 -19
  273. package/src/wirings/http/http-routes.ts +4 -1
  274. package/src/wirings/http/http-runner.ts +11 -26
  275. package/src/wirings/http/http.types.ts +0 -15
  276. package/src/wirings/http/index.ts +1 -7
  277. package/src/wirings/http/pikku-fetch-http-request.ts +2 -2
  278. package/src/wirings/http/web-request.ts +1 -1
  279. package/src/wirings/mcp/mcp-runner.ts +2 -16
  280. package/src/wirings/persona/persona-environments.test.ts +14 -3
  281. package/src/wirings/persona/persona.test.ts +13 -3
  282. package/src/wirings/persona/validate-personas.ts +5 -1
  283. package/src/wirings/queue/queue-runner.ts +1 -1
  284. package/src/wirings/rpc/addon-auth-tags.test.ts +1 -5
  285. package/src/wirings/rpc/rpc-runner.ts +58 -136
  286. package/src/wirings/rpc/rpc-types.ts +7 -0
  287. package/src/wirings/rpc/wire-addon.ts +3 -1
  288. package/src/wirings/secret/validate-secret-definitions.test.ts +22 -0
  289. package/src/wirings/secret/validate-secret-definitions.ts +2 -2
  290. package/src/wirings/trigger/pikku-trigger-service.ts +0 -5
  291. package/src/wirings/trigger/trigger-runner.ts +7 -5
  292. package/src/wirings/virtual-user/run-virtual-user.test.ts +18 -9
  293. package/src/wirings/virtual-user/run-virtual-user.ts +28 -15
  294. package/src/wirings/virtual-user/virtual-user-agents.test.ts +4 -1
  295. package/src/wirings/virtual-user/virtual-user-derive.ts +4 -25
  296. package/src/wirings/virtual-user/virtual-user-dispositions.test.ts +7 -2
  297. package/src/wirings/virtual-user/virtual-user-dispositions.ts +1 -4
  298. package/src/wirings/virtual-user/virtual-user-intents.test.ts +13 -3
  299. package/src/wirings/workflow/dsl/workflow-dsl.types.ts +35 -2
  300. package/src/wirings/workflow/feature.ts +7 -4
  301. package/src/wirings/workflow/graph/graph-node.ts +1 -1
  302. package/src/wirings/workflow/graph/graph-runner.test.ts +4 -2
  303. package/src/wirings/workflow/graph/graph-runner.ts +13 -16
  304. package/src/wirings/workflow/graph/workflow-graph.types.ts +0 -5
  305. package/src/wirings/workflow/index.ts +12 -4
  306. package/src/wirings/workflow/pikku-scenario-service.ts +54 -25
  307. package/src/wirings/workflow/pikku-workflow-service.test.ts +2 -2
  308. package/src/wirings/workflow/pikku-workflow-service.ts +190 -743
  309. package/src/wirings/workflow/scenario-hooks.test.ts +51 -0
  310. package/src/wirings/workflow/workflow-approval.ts +185 -0
  311. package/src/wirings/workflow/workflow-child-run-session.test.ts +89 -0
  312. package/src/wirings/workflow/workflow-constants.ts +47 -0
  313. package/src/wirings/workflow/workflow-dispatch-durability.test.ts +1 -1
  314. package/src/wirings/workflow/workflow-errors.ts +128 -0
  315. package/src/wirings/workflow/workflow-inline-authority.test.ts +12 -6
  316. package/src/wirings/workflow/workflow-meta-resolver.ts +45 -0
  317. package/src/wirings/workflow/workflow-missing-meta.test.ts +92 -0
  318. package/src/wirings/workflow/workflow-queue-routing.ts +85 -0
  319. package/src/wirings/workflow/workflow-queue-wiring.ts +149 -0
  320. package/src/wirings/workflow/workflow-recovery.ts +162 -0
  321. package/src/wirings/workflow/workflow-retry-policy.test.ts +1 -1
  322. package/src/wirings/workflow/workflow-run-authority.test.ts +215 -0
  323. package/src/wirings/workflow/workflow-run-engine.types.ts +91 -0
  324. package/src/wirings/workflow/workflow-run-ownership.ts +36 -0
  325. package/src/wirings/workflow/workflow-suspend.ts +61 -0
  326. package/src/wirings/workflow/workflow.types.ts +7 -0
  327. package/src/wirings-stay-decoupled.test.ts +123 -0
  328. package/tsconfig.json +1 -1
  329. package/tsconfig.tsbuildinfo +1 -1
  330. package/src/internal.ts +0 -10
@@ -1,10 +1,9 @@
1
1
  import { NotFoundError } from '../../errors/errors.js'
2
2
  import { addFunction } from '../../function/function-runner.js'
3
- import type { CorePikkuPermission } from '../../function/functions.types.js'
3
+ import type { CorePikkuFunctionConfig } from '../../function/functions.types.js'
4
4
  import { pikkuState, getSingletonServices } from '../../pikku-state.js'
5
5
  import { coerceTopLevelDataFromSchema, validateSchema } from '../../schema.js'
6
6
  import type { SessionService } from '../../services/user-session-service.js'
7
- import type { CorePikkuMiddleware } from '../../types/core.types.js'
8
7
  import { httpRouter } from '../http/routers/http-router.js'
9
8
  import type {
10
9
  ChannelMeta,
@@ -13,19 +12,34 @@ import type {
13
12
  RunChannelParams,
14
13
  } from './channel.types.js'
15
14
 
15
+ type HandlerConfig = CorePikkuFunctionConfig<any, any>
16
+
17
+ /**
18
+ * The config to register for a channel handler: the handler itself when it is a
19
+ * direct function config, or its inner `func` when it is a wrapper.
20
+ *
21
+ * knowledge: decisions/internals/channel-message-handlers-accept-three-config-shapes.md
22
+ */
23
+ const registeredHandler = (handler: unknown): HandlerConfig => {
24
+ const candidate = handler as { func?: unknown }
25
+ return typeof candidate.func === 'function'
26
+ ? (handler as HandlerConfig)
27
+ : (candidate.func as HandlerConfig)
28
+ }
29
+
16
30
  export const wireChannel = <
17
31
  In,
18
32
  Channel extends string,
19
- PikkuPermission extends CorePikkuPermission<In>,
20
- PikkuMiddleware extends CorePikkuMiddleware,
21
- ChannelFunction,
33
+ ChannelConnect,
34
+ ChannelDisconnect,
35
+ ChannelFunctionMessage,
22
36
  >(
23
37
  channel: CoreChannel<
24
38
  In,
25
39
  Channel,
26
- PikkuPermission,
27
- PikkuMiddleware,
28
- ChannelFunction
40
+ ChannelConnect,
41
+ ChannelDisconnect,
42
+ ChannelFunctionMessage
29
43
  >
30
44
  ) => {
31
45
  const channelsMeta = pikkuState(null, 'channel', 'meta')
@@ -38,19 +52,23 @@ export const wireChannel = <
38
52
  }
39
53
 
40
54
  if (channel.onConnect && channelMeta.connect) {
41
- addFunction(channelMeta.connect.pikkuFuncId, channel.onConnect as any)
55
+ addFunction(
56
+ channelMeta.connect.pikkuFuncId,
57
+ registeredHandler(channel.onConnect)
58
+ )
42
59
  }
43
60
 
44
61
  if (channel.onDisconnect && channelMeta.disconnect) {
45
- addFunction(channelMeta.disconnect.pikkuFuncId, channel.onDisconnect as any)
62
+ addFunction(
63
+ channelMeta.disconnect.pikkuFuncId,
64
+ registeredHandler(channel.onDisconnect)
65
+ )
46
66
  }
47
67
 
48
68
  if (channel.onMessage && channelMeta.message?.pikkuFuncId) {
49
69
  addFunction(
50
70
  channelMeta.message.pikkuFuncId,
51
- (channel.onMessage as any).func instanceof Function
52
- ? channel.onMessage
53
- : (channel.onMessage as any).func
71
+ registeredHandler(channel.onMessage)
54
72
  )
55
73
  }
56
74
 
@@ -63,17 +81,12 @@ export const wireChannel = <
63
81
  const wiringMeta = channelWirings[wiringKey]
64
82
  if (!wiringMeta) return
65
83
 
66
- addFunction(
67
- wiringMeta.pikkuFuncId,
68
- (handler as any).func instanceof Function
69
- ? handler
70
- : (handler as any).func
71
- )
84
+ addFunction(wiringMeta.pikkuFuncId, registeredHandler(handler))
72
85
  })
73
86
  })
74
87
  }
75
88
 
76
- pikkuState(null, 'channel', 'channels').set(channel.name, channel as any)
89
+ pikkuState(null, 'channel', 'channels').set(channel.name, channel)
77
90
  }
78
91
 
79
92
  const getMatchingChannelConfig = (path: string) => {
@@ -64,6 +64,8 @@ export interface ChannelMeta {
64
64
  message: ChannelMessageMeta | null
65
65
  messageWirings: Record<string, Record<string, ChannelMessageMeta>>
66
66
  binary?: boolean | null
67
+ /** Set when the channel was wired by a gateway rather than declared directly. */
68
+ gateway?: boolean
67
69
  summary?: string
68
70
  description?: string
69
71
  errors?: string[]
@@ -206,7 +206,7 @@ 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
  )
@@ -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)
@@ -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
 
@@ -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,
@@ -321,7 +322,8 @@ export async function runCLICommand({
321
322
  setState: (s) => {
322
323
  cliState = s
323
324
  },
324
- getState: () => cliState as any,
325
+ // knowledge: decisions/internals/channel-state-accessors-are-unsound-generics-that-every-implementation-asserts.md
326
+ getState: () => cliState as never,
325
327
  clearState: () => {
326
328
  cliState = undefined
327
329
  },
@@ -372,7 +374,8 @@ export async function runCLICommand({
372
374
  if (result !== undefined && !onOutput) {
373
375
  const commandRenderer = programData?.renderers[commandId]
374
376
  const jsonMode =
375
- (data as any).json === true || (data as any).output === 'json'
377
+ (data as { json?: unknown; output?: unknown }).json === true ||
378
+ (data as { json?: unknown; output?: unknown }).output === 'json'
376
379
  const finalRenderer: CorePikkuCLIRender<any> | undefined =
377
380
  jsonMode && commandRenderer !== undefined
378
381
  ? defaultJSONRenderer
@@ -491,7 +494,7 @@ export async function executeCLI({
491
494
 
492
495
  const config = createConfig
493
496
  ? await createConfig(new LocalVariablesService(), data)
494
- : ({} as any)
497
+ : ({} as CoreConfig)
495
498
 
496
499
  const singletonServices = await createSingletonServices(config)
497
500
  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
  }
@@ -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
  )
@@ -1,4 +1,5 @@
1
1
  import type {
2
+ CoreHTTPFunctionWiring,
2
3
  HTTPRouteConfig,
3
4
  HTTPRouteMap,
4
5
  HTTPRouteContract,
@@ -84,7 +85,9 @@ function registerRoute(
84
85
  timeout: route.timeout,
85
86
  headers: route.headers,
86
87
  sse: route.sse,
87
- } as any)
88
+ // `CoreHTTPFunctionWiring` is discriminated by `method`, and a group builds
89
+ // its routes from a method chosen at runtime — no arm can be narrowed to.
90
+ } as CoreHTTPFunctionWiring<unknown, unknown, string>)
88
91
  }
89
92
 
90
93
  function isRouteConfig(value: unknown): value is HTTPRouteConfig {
@@ -33,8 +33,10 @@ import { isProduction } from '../../env.js'
33
33
  import { pikkuState } from '../../pikku-state.js'
34
34
  import { PikkuFetchHTTPResponse } from './pikku-fetch-http-response.js'
35
35
  import { PikkuFetchHTTPRequest } from './pikku-fetch-http-request.js'
36
- import type { PikkuChannel } from '../channel/channel.types.js'
37
- import { unsupportedChannelRemote } from '../channel/channel-rpc.js'
36
+ import type { BinaryData, PikkuChannel } from '../channel/channel.types.js'
37
+ // The leaf module, not the channel-rpc barrel: http needs one refusal
38
+ // function, and the barrel drags in 4,932 lines of channel RPC runtime.
39
+ import { unsupportedChannelRemote } from '../channel/channel-rpc.types.js'
38
40
  import { addFunction, runPikkuFunc } from '../../function/function-runner.js'
39
41
  import { applyWebResponse } from './web-request.js'
40
42
  import { httpRouter } from './routers/http-router.js'
@@ -68,26 +70,6 @@ export const addHTTPMiddleware = <PikkuMiddleware extends CorePikkuMiddleware>(
68
70
  return middleware
69
71
  }
70
72
 
71
- /**
72
- * @deprecated HTTP-route-level permissions were removed in #972 — permissions
73
- * are now function-scoped only (declare them on the function via
74
- * `pikkuFunc({ permissions })`). This throwing stub exists solely so the pinned
75
- * bootstrap CLI (which still generates an `addHTTPPermission` wrapper) can
76
- * resolve the import at build time; it is never called. Delete once
77
- * `PIKKU_CLI_VERSION` in the CLI build is bumped past the release that removes
78
- * HTTP-route permissions.
79
- */
80
- export const addHTTPPermission = (
81
- _pattern: string,
82
- _permissions:
83
- | Record<string, CorePikkuPermission | CorePikkuPermission[]>
84
- | CorePikkuPermission[]
85
- ): never => {
86
- throw new Error(
87
- 'addHTTPPermission was removed in #972 — HTTP-route-level permissions no longer exist. Declare permissions on the function definition instead: pikkuFunc({ permissions }).'
88
- )
89
- }
90
-
91
73
  export const wireHTTP = <
92
74
  In,
93
75
  Out,
@@ -126,7 +108,8 @@ export const wireHTTP = <
126
108
  }
127
109
  pikkuState(null, 'http', 'routes')
128
110
  .get(httpWiring.method)
129
- ?.set(httpWiring.route, httpWiring as any)
111
+ // knowledge: decisions/internals/wiring-registries-erase-the-generics-their-wire-functions-capture.md
112
+ ?.set(httpWiring.route, httpWiring as CoreHTTPFunctionWiring<any, any, any>)
130
113
  }
131
114
 
132
115
  const getMatchingRoute = (requestType: string, requestPath: string) => {
@@ -261,7 +244,8 @@ const executeRoute = async (
261
244
  setState: (s) => {
262
245
  sseState = s
263
246
  },
264
- getState: () => sseState as any,
247
+ // knowledge: decisions/internals/channel-state-accessors-are-unsound-generics-that-every-implementation-asserts.md
248
+ getState: () => sseState as never,
265
249
  clearState: () => {
266
250
  sseState = undefined
267
251
  },
@@ -275,7 +259,7 @@ const executeRoute = async (
275
259
  const channelHandler = {
276
260
  getChannel: () => channelRef,
277
261
  send: (data: unknown, isBinary?: boolean) => {
278
- if (isBinary) channelRef.sendBinary(data as any)
262
+ if (isBinary) channelRef.sendBinary(data as BinaryData)
279
263
  else channelRef.send(data)
280
264
  },
281
265
  sendBinary: (data: any) => channelRef.sendBinary(data),
@@ -424,7 +408,8 @@ export const fetchData = async <In, Out>(
424
408
  globalMiddleware as CorePikkuMiddleware[]
425
409
  )
426
410
  }
427
- response.status(204).json(undefined as any)
411
+ // 204 carries no body, and `json` has no no-content overload.
412
+ response.status(204).json(undefined as never)
428
413
  return
429
414
  }
430
415
  scopedLogger.info({