@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
@@ -0,0 +1,343 @@
1
+ /**
2
+ * Emits `api-report.md`: every entry point's exported symbols with their full
3
+ * type signatures.
4
+ *
5
+ * `public-surface.json` pins the *names* a consumer can reach, by reading
6
+ * `Object.keys` off each entry point. That catches an export appearing or
7
+ * disappearing and nothing else — adding a method to `MetaService`, or making
8
+ * a field on `ChannelMeta` required, sails straight past it, and so does every
9
+ * type-only export, which erases before there is an object to enumerate.
10
+ *
11
+ * Those are the changes that break a consumer's build, and the report's own
12
+ * summary shows how many more of them there are than there are names.
13
+ *
14
+ * So this walks the type checker instead and writes what each symbol actually
15
+ * *is*. The report is committed, and `api-report.test.ts` fails when the two
16
+ * disagree — so a member-level change is a reviewable diff rather than a
17
+ * surprise in someone else's CI.
18
+ *
19
+ * A member per line, and no member bodies. Both are load-bearing rather than
20
+ * cosmetic: the report exists to be diffed, and a class emitted as one long
21
+ * line of its own source is neither reviewable nor mergeable.
22
+ *
23
+ * Run: yarn api-report
24
+ */
25
+ import { readFileSync, writeFileSync } from 'node:fs'
26
+ import { resolve, dirname } from 'node:path'
27
+ import { fileURLToPath } from 'node:url'
28
+ import ts from 'typescript'
29
+
30
+ const packageRoot = resolve(dirname(fileURLToPath(import.meta.url)), '..')
31
+ const pkg = JSON.parse(
32
+ readFileSync(resolve(packageRoot, 'package.json'), 'utf-8')
33
+ )
34
+
35
+ const entryPoints = Object.entries(pkg.exports as Record<string, string>).map(
36
+ ([subpath, dist]) => ({
37
+ subpath,
38
+ file: resolve(
39
+ packageRoot,
40
+ dist.replace('./dist/', './src/').replace(/\.js$/, '.ts')
41
+ ),
42
+ })
43
+ )
44
+
45
+ const program = ts.createProgram(
46
+ entryPoints.map((e) => e.file),
47
+ {
48
+ target: ts.ScriptTarget.ESNext,
49
+ module: ts.ModuleKind.Node16,
50
+ moduleResolution: ts.ModuleResolutionKind.Node16,
51
+ strict: true,
52
+ skipLibCheck: true,
53
+ }
54
+ )
55
+ const checker = program.getTypeChecker()
56
+
57
+ /** Comments are noise in a report the diff is read for. */
58
+ const stripComments = (text: string): string =>
59
+ text.replace(/\/\*[\s\S]*?\*\//g, '').replace(/\/\/[^\n]*/g, '')
60
+
61
+ /**
62
+ * Punctuation that makes a line and its successor one thing.
63
+ *
64
+ * Read from both ends rather than one, because the closers are ambiguous from
65
+ * behind: `string[]`, `Promise<T>` and `= {}` all end in a closing bracket and
66
+ * none of them is a continuation. What the *next* line starts with is not.
67
+ */
68
+ const CONTINUES = /(=>|[,;{([<|&=])$/
69
+ const CLOSES = /^(=>|extends\b|[}\])>,;|&])/
70
+
71
+ /**
72
+ * A member's own text on one line — long, but never long enough to matter.
73
+ *
74
+ * Inside an object type the author's newline *was* the separator, so collapsing
75
+ * without restoring it produces `{ a: string b: number }`, which is not the
76
+ * type it describes and does not parse in the ```ts fence it is printed into.
77
+ */
78
+ const oneLine = (text: string): string => {
79
+ const lines = stripComments(text)
80
+ .split('\n')
81
+ .map((l) => l.trim())
82
+ .filter(Boolean)
83
+ let depth = 0
84
+ const joined = lines
85
+ .map((line, index) => {
86
+ const inObjectType = depth > 0
87
+ for (const character of line) {
88
+ if (character === '{') depth++
89
+ else if (character === '}') depth--
90
+ }
91
+ const next = lines[index + 1]
92
+ return inObjectType &&
93
+ next !== undefined &&
94
+ !CONTINUES.test(line) &&
95
+ !CLOSES.test(next)
96
+ ? `${line};`
97
+ : line
98
+ })
99
+ .join(' ')
100
+ return joined.replace(/\(\s+/g, '(').replace(/\s+\)/g, ')')
101
+ }
102
+
103
+ /**
104
+ * A class member as a consumer sees it: the signature, and nothing after it.
105
+ *
106
+ * The body is where every character of this report used to come from — a class
107
+ * arrived as its own source, so the report promised implementation details and
108
+ * a one-word change to a method rewrote the whole declaration. Truncating at
109
+ * the body leaves exactly the part a compatibility promise covers.
110
+ */
111
+ const memberSignature = (member: ts.ClassElement | ts.TypeElement): string => {
112
+ const source = member.getSourceFile().text
113
+ const start = member.getStart()
114
+
115
+ const body = (member as { body?: ts.Node }).body
116
+ if (body) {
117
+ const head = oneLine(source.slice(start, body.getStart())).replace(
118
+ /\s*$/,
119
+ ''
120
+ )
121
+ // An inferred return type is still a return type a consumer depends on, and
122
+ // truncating at the body is what threw the author's away.
123
+ if (ts.isMethodDeclaration(member) || ts.isGetAccessorDeclaration(member)) {
124
+ const signature = member.type
125
+ ? undefined
126
+ : checker.getSignatureFromDeclaration(member)
127
+ if (signature) {
128
+ const returns = checker.getReturnTypeOfSignature(signature)
129
+ return `${head}: ${checker.typeToString(returns, undefined, ts.TypeFormatFlags.NoTruncation)}`
130
+ }
131
+ }
132
+ return head
133
+ }
134
+
135
+ // A property's initializer is a body by another name. Its annotation stands in
136
+ // for it, and the checker supplies one when the author left it inferred.
137
+ if (ts.isPropertyDeclaration(member) && member.initializer) {
138
+ if (member.type) return oneLine(source.slice(start, member.type.end))
139
+ const type = checker.getTypeAtLocation(member)
140
+ return `${oneLine(source.slice(start, member.name.end))}: ${checker.typeToString(type, undefined, ts.TypeFormatFlags.NoTruncation)}`
141
+ }
142
+
143
+ return oneLine(member.getText())
144
+ }
145
+
146
+ /** Members a consumer can reach, so a private field is not a promise. */
147
+ const isPublic = (member: ts.ClassElement | ts.TypeElement): boolean =>
148
+ !(ts.getModifiers(member as ts.HasModifiers) ?? []).some(
149
+ (modifier) => modifier.kind === ts.SyntaxKind.PrivateKeyword
150
+ ) && !(member.name && ts.isPrivateIdentifier(member.name))
151
+
152
+ /**
153
+ * A declaration with members, one member per line.
154
+ *
155
+ * The line break is the point. Two branches that touch different methods of the
156
+ * same class are a textual conflict when the class is one line and an ordinary
157
+ * merge when it is not, which is a tax the whole repo was paying: every rebase
158
+ * over this file stopped to re-resolve a forty-thousand-character line by hand.
159
+ */
160
+ const membered = (
161
+ declaration:
162
+ | ts.ClassDeclaration
163
+ | ts.InterfaceDeclaration
164
+ | ts.EnumDeclaration,
165
+ members: ts.NodeArray<ts.ClassElement | ts.TypeElement | ts.EnumMember>
166
+ ): string => {
167
+ if (members.length === 0) return oneLine(declaration.getText())
168
+ const source = declaration.getSourceFile().text
169
+ // `members.pos` is the offset just past the `{`, so the header comes with its
170
+ // own brace and needs no reassembling.
171
+ const header = oneLine(source.slice(declaration.getStart(), members.pos))
172
+ const lines = ts.isEnumDeclaration(declaration)
173
+ ? (members as readonly ts.EnumMember[]).map(
174
+ (member) => `${oneLine(member.getText())},`
175
+ )
176
+ : (members as readonly (ts.ClassElement | ts.TypeElement)[])
177
+ .filter(isPublic)
178
+ .map(memberSignature)
179
+ return [header, ...lines.map((line) => ` ${line}`), '}'].join('\n')
180
+ }
181
+
182
+ /** How a symbol is declared, in as many lines as its members need. */
183
+ const signature = (exported: ts.Symbol): string => {
184
+ // A re-export is an alias; the declaration that matters is the target's, so
185
+ // an interface re-exported through a barrel still reports its members.
186
+ const symbol =
187
+ exported.flags & ts.SymbolFlags.Alias
188
+ ? checker.getAliasedSymbol(exported)
189
+ : exported
190
+ const declaration = symbol.declarations?.[0]
191
+ if (!declaration) return exported.getName()
192
+
193
+ if (
194
+ ts.isClassDeclaration(declaration) ||
195
+ ts.isInterfaceDeclaration(declaration) ||
196
+ ts.isEnumDeclaration(declaration)
197
+ ) {
198
+ return membered(declaration, declaration.members)
199
+ }
200
+
201
+ if (ts.isTypeAliasDeclaration(declaration)) {
202
+ // No members to walk, so the author's own line breaks are the ones that
203
+ // keep a long union reviewable.
204
+ return stripComments(declaration.getText())
205
+ .split('\n')
206
+ .map((l) => l.trimEnd())
207
+ .filter((l) => l.trim())
208
+ .join('\n')
209
+ }
210
+
211
+ const type = checker.getTypeOfSymbolAtLocation(symbol, declaration)
212
+ return `${exported.getName()}: ${checker.typeToString(type, undefined, ts.TypeFormatFlags.NoTruncation)}`
213
+ }
214
+
215
+ /**
216
+ * Entry points whose signatures move with the CLI's codegen. Everything else
217
+ * is what an application imports, and is what a compatibility promise covers.
218
+ *
219
+ * knowledge: decisions/internals/the-ecosystem-entry-point-carries-the-adapter-surface.md
220
+ */
221
+ const ECOSYSTEM_SUBPATHS = new Set(['./ecosystem', './internal'])
222
+
223
+ type Exported = { name: string; members: number; signature: string }
224
+
225
+ /** The members the report lists, which is what the tally is counting. */
226
+ const memberCount = (declaration: ts.Declaration): number => {
227
+ if (ts.isInterfaceDeclaration(declaration)) return declaration.members.length
228
+ if (ts.isEnumDeclaration(declaration)) return declaration.members.length
229
+ if (!ts.isClassDeclaration(declaration)) return 0
230
+ return declaration.members.filter(isPublic).length
231
+ }
232
+
233
+ const modules = new Map<string, Exported[]>()
234
+
235
+ for (const { subpath, file } of entryPoints) {
236
+ const source = program.getSourceFile(file)
237
+ if (!source) continue
238
+ const moduleSymbol = checker.getSymbolAtLocation(source)
239
+ if (!moduleSymbol) continue
240
+
241
+ modules.set(
242
+ subpath,
243
+ checker
244
+ .getExportsOfModule(moduleSymbol)
245
+ .map((exported) => {
246
+ const symbol =
247
+ exported.flags & ts.SymbolFlags.Alias
248
+ ? checker.getAliasedSymbol(exported)
249
+ : exported
250
+ const declaration = symbol.declarations?.[0]
251
+ return {
252
+ name: exported.getName(),
253
+ members: declaration ? memberCount(declaration) : 0,
254
+ signature: signature(exported),
255
+ }
256
+ })
257
+ .sort((a, b) => a.name.localeCompare(b.name))
258
+ )
259
+ }
260
+
261
+ // A name reachable from more than one entry point is counted once, against the
262
+ // weaker promise — reaching it through a stable subpath is what a consumer will
263
+ // have done, whatever else also exports it.
264
+ const tierOf = new Map<string, string>()
265
+ const membersOf = new Map<string, number>()
266
+ for (const [subpath, exports] of modules) {
267
+ const tier = ECOSYSTEM_SUBPATHS.has(subpath) ? 'ecosystem' : 'stable'
268
+ for (const { name, members } of exports) {
269
+ if (tier === 'stable' || !tierOf.has(name)) tierOf.set(name, tier)
270
+ membersOf.set(name, Math.max(membersOf.get(name) ?? 0, members))
271
+ }
272
+ }
273
+
274
+ const tally = (tier: string) => {
275
+ const names = [...tierOf].filter(([, t]) => t === tier).map(([n]) => n)
276
+ return {
277
+ entryPoints: [...modules.keys()].filter(
278
+ (s) => (ECOSYSTEM_SUBPATHS.has(s) ? 'ecosystem' : 'stable') === tier
279
+ ).length,
280
+ names: names.length,
281
+ members: names.reduce((total, n) => total + (membersOf.get(n) ?? 0), 0),
282
+ }
283
+ }
284
+ const stable = tally('stable')
285
+ const ecosystem = tally('ecosystem')
286
+ const observable =
287
+ stable.names + stable.members + ecosystem.names + ecosystem.members
288
+
289
+ const reachableFrom = new Map<string, number>()
290
+ for (const exports of modules.values())
291
+ for (const { name } of exports)
292
+ reachableFrom.set(name, (reachableFrom.get(name) ?? 0) + 1)
293
+
294
+ const perEntryPoint = [...modules]
295
+ .map(([subpath, exports]) => {
296
+ const exclusive = exports.filter(
297
+ ({ name }) => reachableFrom.get(name) === 1
298
+ )
299
+ return {
300
+ subpath,
301
+ exports: exports.length,
302
+ exclusive: exclusive.length,
303
+ members: exclusive.reduce((total, e) => total + e.members, 0),
304
+ }
305
+ })
306
+ .sort((a, b) => b.exclusive + b.members - (a.exclusive + a.members))
307
+
308
+ const sections: string[] = [
309
+ '# @pikku/core API report',
310
+ '',
311
+ 'Generated by `yarn api-report`. Every exported symbol with its full',
312
+ 'signature, so a member-level change is a reviewable diff. Do not edit.',
313
+ '',
314
+ '## What a compatibility promise covers',
315
+ '',
316
+ `**${observable} observable things**: ${stable.names + ecosystem.names} exported names, plus`,
317
+ `${stable.members + ecosystem.members} members on the classes and interfaces among them.`,
318
+ '',
319
+ '| tier | entry points | names | members |',
320
+ '| --- | ---: | ---: | ---: |',
321
+ `| stable | ${stable.entryPoints} | ${stable.names} | ${stable.members} |`,
322
+ `| ecosystem | ${ecosystem.entryPoints} | ${ecosystem.names} | ${ecosystem.members} |`,
323
+ '',
324
+ 'An entry point whose exports are mostly *exclusive* is a self-contained',
325
+ 'subsystem rather than shared machinery — which tends to mean a newer one.',
326
+ '',
327
+ '| entry point | exports | exclusive | members on those |',
328
+ '| --- | ---: | ---: | ---: |',
329
+ ...perEntryPoint.map(
330
+ (e) => `| \`${e.subpath}\` | ${e.exports} | ${e.exclusive} | ${e.members} |`
331
+ ),
332
+ '',
333
+ ]
334
+
335
+ for (const [subpath, exports] of modules) {
336
+ sections.push(`## ${subpath}`, '')
337
+ sections.push('```ts')
338
+ for (const { signature } of exports) sections.push(signature)
339
+ sections.push('```', '')
340
+ }
341
+
342
+ writeFileSync(resolve(packageRoot, 'api-report.md'), sections.join('\n'))
343
+ console.log(`api-report.md written for ${entryPoints.length} entry points`)
@@ -0,0 +1,69 @@
1
+ import { describe, test } from 'node:test'
2
+ import assert from 'node:assert/strict'
3
+ import { execFileSync } from 'node:child_process'
4
+ import { readFileSync, writeFileSync } from 'node:fs'
5
+ import { fileURLToPath } from 'node:url'
6
+ import { join, dirname } from 'node:path'
7
+
8
+ const packageRoot = join(dirname(fileURLToPath(import.meta.url)), '..')
9
+ const reportPath = join(packageRoot, 'api-report.md')
10
+
11
+ /**
12
+ * `public-surface.json` pins the *names* a consumer can reach. That catches an
13
+ * export appearing or vanishing and nothing else — adding a method to
14
+ * `MetaService`, or making a field on `ChannelMeta` required, sails past it,
15
+ * as does every type-only export. Those are the changes that break a
16
+ * consumer's build, and the report's summary counts how many more of them
17
+ * there are than there are names.
18
+ *
19
+ * So `api-report.md` records what each symbol *is*, and this fails when the
20
+ * committed report and the code disagree.
21
+ *
22
+ * knowledge: decisions/internals/the-api-report-pins-members-not-just-names.md
23
+ */
24
+ describe('the API report matches the code', () => {
25
+ test('regenerating produces no diff', () => {
26
+ const committed = readFileSync(reportPath, 'utf-8')
27
+
28
+ execFileSync(
29
+ 'npx',
30
+ ['tsx', join(packageRoot, 'scripts', 'generate-api-report.mts')],
31
+ { cwd: packageRoot, stdio: 'pipe' }
32
+ )
33
+ const regenerated = readFileSync(reportPath, 'utf-8')
34
+
35
+ if (committed !== regenerated) {
36
+ // Restore, so a failing run does not leave the tree dirty.
37
+ const committedLines = committed.split('\n')
38
+ const regeneratedLines = regenerated.split('\n')
39
+ const firstDiff = regeneratedLines.findIndex(
40
+ (line, i) => line !== committedLines[i]
41
+ )
42
+ writeFileSync(reportPath, committed)
43
+
44
+ assert.fail(
45
+ 'the public API changed but api-report.md was not regenerated.\n' +
46
+ `First difference at line ${firstDiff + 1}:\n` +
47
+ ` committed: ${committedLines[firstDiff] ?? '(end of file)'}\n` +
48
+ ` regenerated: ${regeneratedLines[firstDiff] ?? '(end of file)'}\n` +
49
+ 'Run `yarn api-report` and review the diff — a member added to an ' +
50
+ 'exported interface or class is a change consumers compile against.'
51
+ )
52
+ }
53
+ })
54
+
55
+ test('the report actually contains member-level detail', () => {
56
+ // Guards the test above: a report of bare names would match itself forever
57
+ // while pinning nothing that public-surface.json does not already pin.
58
+ const report = readFileSync(reportPath, 'utf-8')
59
+
60
+ assert.ok(
61
+ /export interface MetaService \{[^}]*getHttpMeta/.test(report),
62
+ 'expected MetaService to be reported with its members'
63
+ )
64
+ assert.ok(
65
+ !/^\w+: any$/m.test(report),
66
+ 'symbols reported as `any` mean re-export aliases are not being followed'
67
+ )
68
+ })
69
+ })
@@ -23,10 +23,7 @@ export const hashToken = async (raw: string): Promise<HashedValue> => {
23
23
  if (!subtle) {
24
24
  throw new Error('WebCrypto not available')
25
25
  }
26
- const digest = await subtle.digest(
27
- 'SHA-256',
28
- new TextEncoder().encode(raw)
29
- )
26
+ const digest = await subtle.digest('SHA-256', new TextEncoder().encode(raw))
30
27
  let hex = ''
31
28
  for (const byte of new Uint8Array(digest)) {
32
29
  hex += byte.toString(16).padStart(2, '0')
@@ -627,8 +627,21 @@ describe('envelope encryption', () => {
627
627
  )
628
628
  })
629
629
 
630
+ /**
631
+ * Timed against a derivation measured here rather than a fixed millisecond
632
+ * budget. A constant threshold is a load test in disguise: this runs
633
+ * alongside two thousand other tests, and 50ms left barely 4x headroom over
634
+ * a 10ms window, so the suite went red roughly one run in five. Timing the
635
+ * derivation the test already needs costs nothing and makes both sides scale
636
+ * together, so the margin survives whatever else the machine is doing.
637
+ *
638
+ * knowledge: decisions/internals/a-wall-clock-threshold-is-a-load-test-in-disguise.md
639
+ */
630
640
  test('N secrets cost one KEK derivation, not N', async () => {
641
+ const derivationStart = performance.now()
631
642
  const kek = await deriveKEK(passphrase, salt)
643
+ const oneDerivation = performance.now() - derivationStart
644
+
632
645
  const count = 50
633
646
  const stored = []
634
647
  for (let i = 0; i < count; i++) {
@@ -648,8 +661,8 @@ describe('envelope encryption', () => {
648
661
  const elapsed = performance.now() - start
649
662
 
650
663
  assert.ok(
651
- elapsed < 50,
652
- `unwrapping ${count} DEKs took ${elapsed.toFixed(1)}ms; a re-derivation per secret would cost seconds`
664
+ elapsed < oneDerivation,
665
+ `unwrapping ${count} DEKs took ${elapsed.toFixed(1)}ms, longer than the ${oneDerivation.toFixed(1)}ms a single KEK derivation costs — re-deriving per secret would cost about ${(oneDerivation * count).toFixed(0)}ms`
653
666
  )
654
667
  })
655
668
  })
@@ -26,7 +26,7 @@ const isFunctionConfig = (
26
26
  typeof value === 'object' &&
27
27
  value !== null &&
28
28
  'func' in value &&
29
- typeof (value as any).func === 'function'
29
+ typeof (value as { func?: unknown }).func === 'function'
30
30
  )
31
31
  }
32
32
 
@@ -104,12 +104,7 @@ export async function pikkuDevReloader(
104
104
  return
105
105
  }
106
106
 
107
- // Register every function-config export, replacing known functions and
108
- // adding new ones. Write into the map captured at startup, NOT pikkuState's
109
- // current one: a dev-server watcher may have temporarily swapped in a
110
- // codegen-scoped map, whose writes are discarded when it restores.
111
- // Schemas are NOT touched here: `input`/`output` hold raw zod schemas while
112
- // the schema map carries codegen JSON schemas; mixing them crashed reloads.
107
+ // knowledge: decisions/internals/hot-reload-writes-into-the-function-map-captured-at-startup.md
113
108
  for (const [exportName, exportValue] of Object.entries(mod)) {
114
109
  if (!isFunctionConfig(exportValue)) continue
115
110
  const isNew = !functionsMap.has(exportName)
@@ -0,0 +1,40 @@
1
+ /**
2
+ * The surface a package in the Pikku ecosystem reaches for — a runtime adapter,
3
+ * a service, the code generator's output, the CLI — and application code does
4
+ * not.
5
+ *
6
+ * Split out from the package root so 1.0's compatibility promise covers what
7
+ * applications actually import. These signatures move when the runtime needs
8
+ * them to — `runPikkuFunc` was reshaped over the last year while every field on
9
+ * `PikkuWire` survived — and promising stability on both would mean promising
10
+ * the weaker of the two.
11
+ *
12
+ * Not `/internal`: generated bootstrap files import from here, so the specifier
13
+ * lands in the user's own `.pikku` directory, and telling someone they are
14
+ * touching internals when the code generator put it there is both wrong and
15
+ * self-defeating — it could never be broken anyway. Not `/runtime` either: that
16
+ * reads as runtime-versus-compile-time, i.e. the real API, and `packages/runtimes/*`
17
+ * already claims the word while the CLI is the largest consumer here.
18
+ *
19
+ * `/internal` stays as an alias because the pinned bootstrap CLI still emits it.
20
+ *
21
+ * knowledge: decisions/internals/the-ecosystem-entry-point-carries-the-adapter-surface.md
22
+ */
23
+ export {
24
+ pikkuState,
25
+ resetPikkuState,
26
+ getAllPackageStates,
27
+ getSingletonServices,
28
+ getCreateWireServices,
29
+ setSingletonServices,
30
+ } from './pikku-state.js'
31
+ export { runPikkuFunc, addFunction } from './function/function-runner.js'
32
+ export {
33
+ addGlobalMiddleware,
34
+ addTagMiddleware as addMiddleware,
35
+ } from './middleware-runner.js'
36
+ export { httpRouter } from './wirings/http/routers/http-router.js'
37
+ export type {
38
+ CreateSingletonServices,
39
+ CreateWireServices,
40
+ } from './types/core.types.js'
@@ -15,7 +15,8 @@ export class PikkuError extends Error {
15
15
  * everything else.
16
16
  */
17
17
  export const isExpectedError = (error: unknown): boolean =>
18
- error instanceof PikkuError || (error as any)?.expected === true
18
+ error instanceof PikkuError ||
19
+ (error as { expected?: unknown } | null)?.expected === true
19
20
 
20
21
  export interface ErrorDetails {
21
22
  status: number
@@ -48,7 +49,7 @@ export const getErrorResponse = (
48
49
  ): { status: number; message: string; mcpCode?: number } | undefined => {
49
50
  const errors = pikkuState(null, 'misc', 'errors')
50
51
 
51
- let ctor: unknown = (error as any)?.constructor
52
+ let ctor: unknown = (error as { constructor?: unknown } | null)?.constructor
52
53
  while (typeof ctor === 'function' && ctor !== Object) {
53
54
  const details = errors.get(ctor as PikkuErrorConstructor)
54
55
  if (details) {
@@ -57,7 +58,8 @@ export const getErrorResponse = (
57
58
  ctor = Object.getPrototypeOf(ctor)
58
59
  }
59
60
 
60
- const name = (error as any)?.constructor?.name
61
+ const name = (error as { constructor?: { name?: string } } | null)
62
+ ?.constructor?.name
61
63
  return name
62
64
  ? Array.from(errors.entries()).find(([e]) => e.name === name)?.[1]
63
65
  : undefined
@@ -199,7 +199,10 @@ describe('getErrorResponse resolution order', () => {
199
199
  test('a subclass of a registered error inherits its registered status', () => {
200
200
  class TenantNotFoundError extends NotFoundError {}
201
201
  const expected = getErrorResponse(new NotFoundError())
202
- assert.deepStrictEqual(getErrorResponse(new TenantNotFoundError()), expected)
202
+ assert.deepStrictEqual(
203
+ getErrorResponse(new TenantNotFoundError()),
204
+ expected
205
+ )
203
206
  })
204
207
 
205
208
  test('an unregistered, non-colliding error class resolves to undefined', () => {
@@ -1,2 +1,2 @@
1
1
  export * from './errors.js'
2
- export { PikkuError, addError, addErrors } from './error-handler.js'
2
+ export { PikkuError, addError } from './error-handler.js'
@@ -14,6 +14,7 @@ import type { CorePermissionGroup } from './functions.types.js'
14
14
  import { PikkuSessionService } from '../services/user-session-service.js'
15
15
  import { MissingScopeError, ReadonlySessionError } from '../errors/errors.js'
16
16
  import { createInvocationAudit } from '../services/audit-service.js'
17
+ import { SecretAccessDeniedError } from '../services/secretless.js'
17
18
 
18
19
  beforeEach(() => {
19
20
  resetPikkuState()
@@ -637,7 +638,7 @@ describe('runPikkuFunc - Integration Tests', () => {
637
638
  singletonServices: mockSingletonServices,
638
639
  data: () => ({}),
639
640
  auth: false,
640
- wire: { session: { readonly: true } as any },
641
+ wire: { session: { readonly: true } },
641
642
  }),
642
643
  ReadonlySessionError
643
644
  )
@@ -1563,3 +1564,54 @@ describe('runPikkuFunc - scopes', () => {
1563
1564
  })
1564
1565
  })
1565
1566
  })
1567
+
1568
+ describe('runPikkuFunc - secrets', () => {
1569
+ const singletonServicesWithSecrets = {
1570
+ ...mockSingletonServices,
1571
+ secrets: { getSecret: async () => 'super-secret' },
1572
+ } as any
1573
+
1574
+ const addSecretReadingFunction = (
1575
+ funcName: string,
1576
+ seen: { error?: unknown }
1577
+ ) => {
1578
+ addTestFunction(funcName, {
1579
+ func: async (services: any) => {
1580
+ try {
1581
+ return services.secrets
1582
+ } catch (error) {
1583
+ seen.error = error
1584
+ return 'denied'
1585
+ }
1586
+ },
1587
+ })
1588
+ }
1589
+
1590
+ const run = (funcName: string) =>
1591
+ runPikkuFunc('rpc', Math.random().toString(), funcName, {
1592
+ singletonServices: singletonServicesWithSecrets,
1593
+ getAllServices: () => singletonServicesWithSecrets,
1594
+ data: () => ({}),
1595
+ auth: false,
1596
+ wire: {},
1597
+ })
1598
+
1599
+ test('a function never receives the secrets service', async () => {
1600
+ const seen: { error?: unknown } = {}
1601
+ addSecretReadingFunction('readsSecrets', seen)
1602
+
1603
+ assert.equal(await run('readsSecrets'), 'denied')
1604
+ assert.ok(seen.error instanceof SecretAccessDeniedError)
1605
+ })
1606
+
1607
+ test('no metadata flag can restore the secrets service', async () => {
1608
+ const seen: { error?: unknown } = {}
1609
+ addSecretReadingFunction('claimsAnExemption', seen)
1610
+ Object.assign(pikkuState(null, 'function', 'meta')['claimsAnExemption']!, {
1611
+ secretBroker: true,
1612
+ })
1613
+
1614
+ assert.equal(await run('claimsAnExemption'), 'denied')
1615
+ assert.ok(seen.error instanceof SecretAccessDeniedError)
1616
+ })
1617
+ })