@pikku/core 0.12.84 → 0.12.86

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 (564) hide show
  1. package/CHANGELOG.md +424 -0
  2. package/dist/bootstrap-compat/root.d.ts +20 -0
  3. package/dist/classification/column-form.d.ts +32 -0
  4. package/dist/classification/column-form.js +42 -0
  5. package/dist/classification/data-classification.d.ts +64 -0
  6. package/dist/classification/index.d.ts +13 -0
  7. package/dist/classification/index.js +2 -0
  8. package/dist/classification/secret-value.d.ts +56 -0
  9. package/dist/classification/secret-value.js +46 -0
  10. package/dist/crypto-utils.d.ts +1 -1
  11. package/dist/ecosystem/agent-scorer.d.ts +1 -1
  12. package/dist/ecosystem/agent.d.ts +4 -1
  13. package/dist/ecosystem/channel/local.d.ts +2 -0
  14. package/dist/ecosystem/channel/local.js +2 -0
  15. package/dist/ecosystem/channel/serverless.d.ts +9 -0
  16. package/dist/ecosystem/channel/serverless.js +1 -0
  17. package/dist/ecosystem/channel.d.ts +3 -0
  18. package/dist/ecosystem/channel.js +1 -0
  19. package/dist/ecosystem/cli.d.ts +1 -1
  20. package/dist/ecosystem/cli.js +1 -1
  21. package/dist/ecosystem/credential.d.ts +1 -0
  22. package/dist/ecosystem/crypto-utils.d.ts +8 -0
  23. package/dist/ecosystem/crypto-utils.js +1 -0
  24. package/dist/ecosystem/dev.d.ts +1 -0
  25. package/dist/ecosystem/dev.js +1 -0
  26. package/{src/ecosystem/testing.ts → dist/ecosystem/errors.d.ts} +3 -3
  27. package/dist/ecosystem/errors.js +2 -0
  28. package/dist/ecosystem/function.d.ts +1 -1
  29. package/dist/ecosystem/function.js +1 -1
  30. package/dist/ecosystem/http.d.ts +3 -1
  31. package/dist/ecosystem/http.js +3 -1
  32. package/dist/ecosystem/mcp.d.ts +1 -1
  33. package/dist/ecosystem/mcp.js +1 -1
  34. package/dist/ecosystem/middleware.d.ts +2 -1
  35. package/dist/ecosystem/middleware.js +2 -1
  36. package/dist/ecosystem/persona.d.ts +4 -2
  37. package/dist/ecosystem/persona.js +2 -1
  38. package/dist/ecosystem/queue.d.ts +4 -2
  39. package/dist/ecosystem/queue.js +3 -1
  40. package/dist/ecosystem/role.d.ts +1 -0
  41. package/dist/ecosystem/rpc.d.ts +2 -0
  42. package/dist/ecosystem/rpc.js +1 -0
  43. package/dist/ecosystem/scenario.d.ts +8 -2
  44. package/dist/ecosystem/scenario.js +5 -1
  45. package/dist/ecosystem/scheduler.d.ts +2 -1
  46. package/dist/ecosystem/scheduler.js +2 -1
  47. package/dist/ecosystem/schema.d.ts +2 -1
  48. package/dist/ecosystem/schema.js +1 -1
  49. package/dist/ecosystem/scope.d.ts +2 -0
  50. package/dist/ecosystem/scope.js +1 -0
  51. package/dist/ecosystem/secret.d.ts +2 -0
  52. package/dist/ecosystem/secret.js +1 -0
  53. package/dist/ecosystem/services/local-content.d.ts +2 -1
  54. package/dist/ecosystem/services/local-content.js +1 -1
  55. package/dist/ecosystem/services/local-meta.d.ts +1 -0
  56. package/dist/ecosystem/services/local-meta.js +1 -0
  57. package/dist/ecosystem/services.d.ts +11 -3
  58. package/dist/ecosystem/services.js +7 -1
  59. package/dist/ecosystem/trigger.d.ts +1 -0
  60. package/dist/ecosystem/trigger.js +1 -1
  61. package/dist/ecosystem/types.d.ts +6 -3
  62. package/dist/ecosystem/types.js +3 -2
  63. package/dist/ecosystem/variable.d.ts +1 -0
  64. package/dist/ecosystem/virtual-user.d.ts +3 -0
  65. package/dist/ecosystem/virtual-user.js +3 -0
  66. package/dist/ecosystem/workflow.d.ts +2 -0
  67. package/dist/ecosystem/workflow.js +2 -0
  68. package/dist/errors/index.d.ts +8 -0
  69. package/dist/errors/index.js +1 -0
  70. package/dist/errors/serialized-error.d.ts +7 -0
  71. package/dist/function/function-meta.types.d.ts +139 -0
  72. package/dist/function/function-runner.d.ts +3 -2
  73. package/dist/function/function-runner.js +31 -11
  74. package/dist/function/functions.types.d.ts +10 -2
  75. package/dist/function/index.d.ts +8 -2
  76. package/dist/function/index.js +4 -1
  77. package/dist/function/pikku-request.d.ts +8 -0
  78. package/dist/function/pikku-request.js +15 -0
  79. package/dist/middleware/auth-apikey.d.ts +2 -10
  80. package/dist/middleware/auth-apikey.js +1 -1
  81. package/dist/middleware/auth-bearer.d.ts +2 -10
  82. package/dist/middleware/auth-bearer.js +1 -1
  83. package/dist/middleware/auth-cookie.d.ts +2 -10
  84. package/dist/middleware/auth-cookie.js +1 -1
  85. package/dist/middleware/cors.d.ts +5 -11
  86. package/dist/middleware/cors.js +13 -6
  87. package/dist/middleware/index.d.ts +3 -0
  88. package/dist/middleware/index.js +1 -0
  89. package/dist/middleware/middleware-factories.d.ts +9 -0
  90. package/dist/middleware/middleware-factories.js +19 -0
  91. package/dist/middleware/middleware.types.d.ts +27 -0
  92. package/dist/middleware/middleware.types.js +1 -0
  93. package/dist/middleware/remote-auth.d.ts +2 -2
  94. package/dist/middleware/remote-auth.js +1 -1
  95. package/dist/middleware/telemetry.d.ts +4 -20
  96. package/dist/middleware/telemetry.js +1 -1
  97. package/dist/middleware-runner.d.ts +2 -1
  98. package/dist/pikku-state.d.ts +2 -1
  99. package/dist/services/audit-service.d.ts +1 -1
  100. package/dist/services/credential-wire-service.js +1 -1
  101. package/dist/services/email-service.d.ts +1 -1
  102. package/dist/services/file-scenario-run-store.d.ts +58 -0
  103. package/dist/services/file-scenario-run-store.js +214 -0
  104. package/dist/services/in-memory-workflow-service.d.ts +1 -1
  105. package/dist/services/index.d.ts +5 -1
  106. package/dist/services/index.js +3 -0
  107. package/dist/services/local-secrets.d.ts +1 -1
  108. package/dist/services/local-secrets.js +1 -1
  109. package/dist/services/logger.d.ts +7 -7
  110. package/dist/services/meta-service.d.ts +2 -1
  111. package/dist/services/scoped-secret-service.d.ts +1 -1
  112. package/dist/services/secret-service.d.ts +1 -1
  113. package/dist/services/typed-secret-service.d.ts +16 -2
  114. package/dist/services/typed-secret-service.js +5 -0
  115. package/dist/services/webhook-service.d.ts +1 -1
  116. package/dist/services/workflow-service.d.ts +2 -1
  117. package/dist/testing/index.d.ts +1 -0
  118. package/dist/testing/index.js +1 -0
  119. package/dist/types/core.types.d.ts +30 -180
  120. package/dist/types/core.types.js +1 -19
  121. package/dist/types/index.d.ts +2 -0
  122. package/dist/types/index.js +1 -0
  123. package/dist/types/state.types.d.ts +3 -1
  124. package/dist/utils.d.ts +13 -0
  125. package/dist/utils.js +2 -0
  126. package/dist/wirings/addon/addon-runner.d.ts +17 -0
  127. package/dist/wirings/addon/addon-runner.js +150 -0
  128. package/dist/wirings/addon/index.d.ts +6 -0
  129. package/dist/wirings/addon/index.js +3 -0
  130. package/dist/wirings/addon/remote-addon-auth.d.ts +16 -0
  131. package/dist/wirings/addon/remote-addon-auth.js +39 -0
  132. package/dist/wirings/addon/wire-addon.d.ts +80 -0
  133. package/dist/wirings/addon/wire-addon.js +120 -0
  134. package/dist/wirings/addon/wire-remote-addon.d.ts +22 -0
  135. package/dist/wirings/addon/wire-remote-addon.js +11 -0
  136. package/dist/wirings/agent/agent-prepare.d.ts +3 -3
  137. package/dist/wirings/agent/agent-prepare.js +1 -1
  138. package/dist/wirings/agent/agent-runner.js +2 -3
  139. package/dist/wirings/agent/agent-stream.js +2 -2
  140. package/dist/wirings/agent/agent-utils.d.ts +13 -0
  141. package/dist/wirings/agent/agent-utils.js +16 -0
  142. package/dist/wirings/agent/agent.types.d.ts +3 -2
  143. package/dist/wirings/agent/index.d.ts +1 -1
  144. package/dist/wirings/agent/voice-input.js +1 -1
  145. package/dist/wirings/agent/voice-output.js +1 -1
  146. package/dist/wirings/agent-scorer/agent-scorer-judge.d.ts +13 -0
  147. package/dist/wirings/agent-scorer/agent-scorer-judge.js +52 -0
  148. package/dist/wirings/agent-scorer/agent-scorer.d.ts +7 -1
  149. package/dist/wirings/agent-scorer/agent-scorer.js +1 -0
  150. package/dist/wirings/agent-scorer/agent-scorer.types.d.ts +20 -0
  151. package/dist/wirings/agent-scorer/index.d.ts +1 -1
  152. package/dist/wirings/channel/channel-common.d.ts +2 -1
  153. package/dist/wirings/channel/channel-middleware-runner.d.ts +2 -1
  154. package/dist/wirings/channel/channel.types.d.ts +1 -1
  155. package/dist/wirings/channel/serverless/index.d.ts +1 -0
  156. package/dist/wirings/cli/channel/index.d.ts +1 -1
  157. package/dist/wirings/cli/cli-runner.d.ts +2 -1
  158. package/dist/wirings/cli/cli-runner.js +4 -1
  159. package/dist/wirings/cli/cli.types.d.ts +21 -2
  160. package/dist/wirings/cli/command-parser.js +157 -23
  161. package/dist/wirings/gateway/gateway.types.d.ts +2 -1
  162. package/dist/wirings/http/http-runner.d.ts +1 -1
  163. package/dist/wirings/http/http-runner.js +1 -1
  164. package/dist/wirings/http/http.types.d.ts +2 -2
  165. package/dist/wirings/http/index.d.ts +1 -0
  166. package/dist/wirings/http/index.js +1 -0
  167. package/dist/wirings/http/pikku-fetch-http-request.js +1 -1
  168. package/dist/wirings/http/pikku-fetch-http-response.js +2 -2
  169. package/dist/wirings/mcp/mcp.types.d.ts +1 -1
  170. package/dist/wirings/persona/index.d.ts +1 -1
  171. package/dist/wirings/persona/index.js +1 -1
  172. package/dist/wirings/persona/persona.types.d.ts +14 -0
  173. package/dist/wirings/queue/queue.types.d.ts +1 -1
  174. package/dist/wirings/queue/signed-queue-service.d.ts +1 -1
  175. package/dist/wirings/rpc/index.d.ts +0 -6
  176. package/dist/wirings/rpc/index.js +0 -3
  177. package/dist/wirings/rpc/remote-addon-auth.d.ts +1 -1
  178. package/dist/wirings/rpc/remote-addon-auth.js +1 -1
  179. package/dist/wirings/rpc/rpc-runner.js +2 -2
  180. package/dist/wirings/rpc/wire-addon.d.ts +1 -1
  181. package/dist/wirings/scheduler/scheduler.types.d.ts +2 -1
  182. package/dist/wirings/scope/index.d.ts +1 -0
  183. package/dist/wirings/scope/index.js +1 -0
  184. package/dist/wirings/secret/secret.types.d.ts +10 -0
  185. package/dist/wirings/secret/validate-secret-definitions.js +2 -0
  186. package/dist/wirings/variable/validate-variable-definitions.js +2 -0
  187. package/dist/wirings/variable/variable.types.d.ts +9 -0
  188. package/dist/wirings/virtual-user/index.d.ts +1 -1
  189. package/dist/wirings/virtual-user/index.js +1 -1
  190. package/dist/wirings/virtual-user/prepare-virtual-user-run.d.ts +1 -1
  191. package/dist/wirings/virtual-user/virtual-user-derive.d.ts +1 -1
  192. package/dist/wirings/workflow/graph/graph-node.d.ts +9 -2
  193. package/dist/wirings/workflow/graph/graph-node.js +2 -0
  194. package/dist/wirings/workflow/graph/graph-runner.d.ts +1 -0
  195. package/dist/wirings/workflow/graph/graph-runner.js +283 -31
  196. package/dist/wirings/workflow/graph/workflow-graph.types.d.ts +16 -1
  197. package/dist/wirings/workflow/index.d.ts +2 -0
  198. package/dist/wirings/workflow/index.js +1 -0
  199. package/dist/wirings/workflow/pikku-scenario-service.d.ts +14 -4
  200. package/dist/wirings/workflow/pikku-scenario-service.js +44 -16
  201. package/dist/wirings/workflow/pikku-workflow-service.d.ts +2 -1
  202. package/dist/wirings/workflow/pikku-workflow-service.js +3 -4
  203. package/dist/wirings/workflow/run-timeline.d.ts +1 -1
  204. package/dist/wirings/workflow/scenario-run.types.d.ts +171 -0
  205. package/dist/wirings/workflow/scenario-run.types.js +1 -0
  206. package/dist/wirings/workflow/scenario-step-guards.d.ts +1 -2
  207. package/dist/wirings/workflow/scenario-step-guards.js +0 -8
  208. package/dist/wirings/workflow/scenario-step.types.d.ts +27 -12
  209. package/dist/wirings/workflow/workflow-approval.js +2 -1
  210. package/dist/wirings/workflow/workflow-run-engine.types.d.ts +2 -1
  211. package/dist/wirings/workflow/workflow.types.d.ts +2 -1
  212. package/knowledge/decisions/index.md +2 -0
  213. package/knowledge/decisions/internals/a-scenario-step-template-is-offered-unfilled.md +1 -1
  214. package/knowledge/decisions/internals/a-secret-that-fails-to-decrypt-fails-the-whole-read.md +1 -1
  215. package/knowledge/decisions/internals/a-virtual-user-run-is-not-a-workflow-and-not-a-queued-job.md +2 -2
  216. package/knowledge/decisions/internals/a-wall-clock-threshold-is-a-load-test-in-disguise.md +6 -6
  217. package/knowledge/decisions/internals/a-workflow-wire-is-built-from-the-run-not-from-the-rpc-service.md +1 -1
  218. package/knowledge/decisions/internals/addon-singleton-services-are-cached-per-namespace-not-per-package.md +1 -1
  219. package/knowledge/decisions/internals/addon-workflow-names-are-prefixed-with-the-consumer-namespace.md +1 -1
  220. package/knowledge/decisions/internals/an-addon-scope-root-loses-to-a-root-the-host-already-declares.md +1 -1
  221. package/knowledge/decisions/internals/channel-message-handlers-accept-three-config-shapes.md +1 -1
  222. package/knowledge/decisions/internals/channel-middleware-caches-only-statically-resolved-middleware.md +1 -1
  223. package/knowledge/decisions/internals/channel-user-id-is-persisted-after-onconnect-middleware-runs.md +1 -1
  224. package/knowledge/decisions/internals/cli-option-names-are-camelcase-in-state-and-kebab-on-the-command-line.md +1 -1
  225. package/knowledge/decisions/internals/cli-parse-errors-are-routed-by-message-prefix.md +1 -1
  226. package/knowledge/decisions/internals/cli-unknown-long-options-warn-instead-of-failing.md +1 -1
  227. package/knowledge/decisions/internals/core-column-form-is-an-axis-of-its-own.md +4 -4
  228. package/knowledge/decisions/internals/core-data-classification-brand-is-an-optional-property.md +1 -1
  229. package/knowledge/decisions/internals/core-function-runner-restores-the-wire-fields-it-overwrites.md +3 -3
  230. package/knowledge/decisions/internals/core-hot-reload-merges-generated-meta-never-replaces-it.md +2 -2
  231. package/knowledge/decisions/internals/core-hot-reload-owns-its-module-registry.md +2 -2
  232. package/knowledge/decisions/internals/core-middleware-order-is-scope-then-priority.md +1 -1
  233. package/knowledge/decisions/internals/core-schema-defaults-apply-on-every-transport.md +1 -1
  234. package/knowledge/decisions/internals/core-scopes-are-an-and-gate-separate-from-permissions.md +3 -3
  235. package/knowledge/decisions/internals/gateway-adapters-resolve-lazily-and-are-promise-cached.md +1 -1
  236. package/knowledge/decisions/internals/gateway-listener-middleware-runs-without-an-rpc-on-the-wire.md +4 -4
  237. package/knowledge/decisions/internals/gateway-wiring-is-a-meta-wiring-over-http-and-channels.md +1 -1
  238. package/knowledge/decisions/internals/http-router-matches-normalized-paths-but-returns-registered-ones.md +1 -1
  239. package/knowledge/decisions/internals/in-a-scenario-a-4xx-is-data-not-an-exception.md +1 -1
  240. package/knowledge/decisions/internals/in-memory-workflow-history-aliases-the-live-step-object.md +1 -1
  241. package/knowledge/decisions/internals/index.md +4 -2
  242. package/knowledge/decisions/internals/istanbul-statement-counts-attach-to-the-start-line-only.md +1 -1
  243. package/knowledge/decisions/internals/one-project-shape-check-two-validators.md +1 -1
  244. package/knowledge/decisions/internals/queue-jobs-always-carry-an-explicit-attempts-count.md +1 -1
  245. package/knowledge/decisions/internals/remote-addons-dispatch-over-http-instead-of-local-meta.md +1 -1
  246. package/knowledge/decisions/internals/rpc-names-resolve-through-package-scope-before-root.md +1 -1
  247. package/knowledge/decisions/internals/scenario-given-and-when-are-sugar-but-then-is-not.md +2 -2
  248. package/knowledge/decisions/internals/scenarios-live-in-files-named-for-them.md +5 -5
  249. package/knowledge/decisions/internals/the-actor-prompt-says-json-because-of-json-object-mode.md +1 -1
  250. package/knowledge/decisions/internals/the-api-report-pins-members-not-just-names.md +1 -1
  251. package/knowledge/decisions/internals/the-ecosystem-entry-point-carries-the-adapter-surface.md +43 -19
  252. package/knowledge/decisions/internals/the-persona-runtime-is-exported-from-the-persona-entry-point.md +1 -1
  253. package/knowledge/decisions/internals/the-worker-disposition-is-the-one-that-is-not-testing.md +1 -1
  254. package/knowledge/decisions/internals/validate-runs-checks-by-precondition.md +3 -3
  255. package/knowledge/decisions/internals/virtual-user-step-order-comes-from-insertion-order.md +1 -1
  256. package/knowledge/decisions/internals/webhook-delivery-history-records-every-attempt-best-effort.md +1 -1
  257. package/knowledge/decisions/internals/wiring-registries-erase-the-generics-their-wire-functions-capture.md +1 -1
  258. package/knowledge/decisions/internals/workflow-approval-expiry-is-decided-from-a-recorded-deadline.md +1 -1
  259. package/knowledge/decisions/internals/workflow-core-never-imports-a-browser-driver.md +1 -1
  260. package/knowledge/decisions/internals/workflow-inline-runs-report-their-run-id-before-they-can-fail.md +1 -1
  261. package/knowledge/decisions/internals/workflow-invocation-id-is-the-dedupe-key-not-step-id.md +1 -1
  262. package/knowledge/decisions/internals/workflow-run-capabilities-are-extensions-not-subclasses.md +1 -1
  263. package/knowledge/decisions/internals/workflow-run-timeline-is-a-pure-fold-over-durable-history.md +1 -1
  264. package/knowledge/decisions/internals/workflow-scenario-assertions-never-retry-and-record-one-step.md +9 -5
  265. package/knowledge/decisions/internals/workflow-scenario-hooks-are-a-scenario-only-affordance.md +1 -1
  266. package/knowledge/decisions/internals/workflow-step-rpc-name-is-provenance-only.md +2 -2
  267. package/knowledge/decisions/internals/workflow-suspend-and-approval-reasons-are-durable-step-identities.md +1 -1
  268. package/knowledge/decisions/security/a-function-never-receives-the-secret-service.md +1 -1
  269. package/knowledge/decisions/security/a-step-runs-the-function-the-workflow-dispatched-it-with.md +1 -1
  270. package/knowledge/decisions/security/a-virtual-user-is-never-offered-a-step-that-would-forge-its-own-oracle.md +2 -3
  271. package/knowledge/decisions/security/actor-sign-in-is-proven-by-set-cookie-not-a-non-empty-jar.md +1 -1
  272. package/knowledge/decisions/security/addon-auth-and-tags-only-tighten.md +1 -1
  273. package/knowledge/decisions/security/agent-gate-requires-a-session-only-when-auth-is-true.md +1 -1
  274. package/knowledge/decisions/security/an-approval-answer-outlives-the-run-it-answered.md +3 -3
  275. package/knowledge/decisions/security/console-addon-privileged-functions-gate-themselves.md +4 -3
  276. package/knowledge/decisions/security/core-safe-fetch-blocks-ssrf-by-host-literal-not-dns.md +1 -1
  277. package/knowledge/decisions/security/gateway-middleware-sessions-must-be-bridged-onto-the-wire.md +2 -2
  278. package/knowledge/decisions/security/global-permissions-and-function-permissions-are-independent-gates.md +1 -1
  279. package/knowledge/decisions/security/http-request-bodies-are-bounded-before-they-are-buffered.md +2 -2
  280. package/knowledge/decisions/security/index.md +2 -0
  281. package/knowledge/decisions/security/mcp-internal-error-details-are-double-gated-on-production.md +1 -1
  282. package/knowledge/decisions/security/queue-jobs-carry-the-producers-pikku-user-id.md +3 -3
  283. package/knowledge/decisions/security/remote-addon-tokens-are-client-credentials-not-mesh-trust.md +1 -1
  284. package/knowledge/decisions/security/scaffold-features-are-authenticated-unless-opted-out.md +2 -2
  285. package/knowledge/decisions/security/self-authentication-is-declared-not-detected.md +1 -1
  286. package/knowledge/decisions/security/signed-content-urls-bind-the-request-path.md +2 -2
  287. package/knowledge/index.md +3 -1
  288. package/knowledge/questions/channel-middleware-accepts-bare-factories-that-nothing-resolves.md +1 -1
  289. package/knowledge/questions/index.md +2 -0
  290. package/package.json +14 -13
  291. package/run-tests.sh +0 -0
  292. package/scripts/generate-api-report.d.mts +1 -1
  293. package/scripts/generate-api-report.mjs +67 -60
  294. package/scripts/generate-api-report.mts +11 -45
  295. package/src/api-report.test.ts +70 -1
  296. package/src/app-alias-surface.test.ts +179 -0
  297. package/src/app-leaf-surface.test.ts +217 -0
  298. package/src/bootstrap-compat/root.ts +20 -0
  299. package/src/{column-form.test.ts → classification/column-form.test.ts} +2 -2
  300. package/src/{data-classification.ts → classification/data-classification.ts} +1 -5
  301. package/src/classification/index.ts +38 -0
  302. package/src/{secret-value.test.ts → classification/secret-value.test.ts} +2 -2
  303. package/src/{secret-value.ts → classification/secret-value.ts} +1 -6
  304. package/src/crypto-utils.ts +1 -1
  305. package/src/dev/reload-meta.test.ts +1 -1
  306. package/src/errors/index.ts +9 -0
  307. package/src/errors/serialized-error.ts +9 -0
  308. package/src/factory-functions.test.ts +4 -2
  309. package/src/function/function-meta.types.ts +151 -0
  310. package/src/function/function-runner.test.ts +4 -2
  311. package/src/function/function-runner.ts +47 -14
  312. package/src/function/functions.types.ts +11 -7
  313. package/src/function/index.ts +24 -5
  314. package/src/function/list.types.ts +1 -3
  315. package/src/middleware/auth-apikey.ts +4 -2
  316. package/src/middleware/auth-bearer.test.ts +1 -1
  317. package/src/middleware/auth-bearer.ts +4 -2
  318. package/src/middleware/auth-cookie.ts +4 -1
  319. package/src/middleware/cors.test.ts +126 -5
  320. package/src/middleware/cors.ts +19 -8
  321. package/src/middleware/index.ts +16 -0
  322. package/src/middleware/middleware-factories.ts +62 -0
  323. package/src/middleware/middleware.types.ts +61 -0
  324. package/src/middleware/remote-auth.test.ts +1 -1
  325. package/src/middleware/remote-auth.ts +1 -1
  326. package/src/middleware/telemetry.ts +4 -2
  327. package/src/middleware-runner.test.ts +2 -3
  328. package/src/middleware-runner.ts +2 -2
  329. package/src/no-root-barrel.test.ts +114 -0
  330. package/src/permissions.ts +2 -4
  331. package/src/pikku-state.ts +3 -1
  332. package/src/public-surface.json +56 -241
  333. package/src/public-surface.json.README +3 -3
  334. package/src/public-surface.test.ts +5 -5
  335. package/src/remote.test.ts +1 -1
  336. package/src/removed-legacy-exports.test.ts +5 -1
  337. package/src/services/audit-service.ts +1 -1
  338. package/src/services/credential-wire-service.ts +1 -1
  339. package/src/services/email-service.ts +2 -4
  340. package/src/services/file-scenario-run-store.test.ts +349 -0
  341. package/src/services/file-scenario-run-store.ts +282 -0
  342. package/src/services/in-memory-workflow-service.ts +1 -1
  343. package/src/services/index.ts +13 -1
  344. package/src/services/local-content-request-handler.ts +1 -2
  345. package/src/services/local-secrets.ts +1 -1
  346. package/src/services/logger.ts +7 -7
  347. package/src/services/meta-service.ts +2 -2
  348. package/src/services/queue-webhook-service.test.ts +1 -1
  349. package/src/services/scoped-secret-service.ts +1 -1
  350. package/src/services/secret-service.ts +1 -1
  351. package/src/services/typed-secret-service.test.ts +52 -0
  352. package/src/services/typed-secret-service.ts +26 -2
  353. package/src/services/webhook-service.ts +1 -1
  354. package/src/services/workflow-service.ts +2 -1
  355. package/src/template-alias-surface.test.ts +240 -0
  356. package/src/testing/index.ts +1 -0
  357. package/src/types/core.types.ts +33 -285
  358. package/src/types/index.ts +2 -0
  359. package/src/types/state.types.ts +6 -5
  360. package/src/types/typed-credentials.test.ts +72 -0
  361. package/src/utils.ts +34 -0
  362. package/src/wirings/{rpc → addon}/addon-scopes.test.ts +1 -1
  363. package/src/wirings/addon/index.ts +12 -0
  364. package/src/wirings/{rpc → addon}/remote-addon-auth.ts +4 -1
  365. package/src/wirings/{rpc → addon}/wire-addon.ts +37 -2
  366. package/src/wirings/agent/agent-prepare.ts +8 -5
  367. package/src/wirings/agent/agent-runner.ts +2 -3
  368. package/src/wirings/agent/agent-stream.ts +2 -3
  369. package/src/wirings/agent/agent-utils.ts +19 -0
  370. package/src/wirings/agent/agent.types.ts +3 -3
  371. package/src/wirings/agent/index.ts +1 -0
  372. package/src/wirings/agent/voice-input.ts +1 -1
  373. package/src/wirings/agent/voice-output.test.ts +2 -4
  374. package/src/wirings/agent/voice-output.ts +1 -1
  375. package/src/wirings/agent-scorer/agent-scorer-judge.test.ts +145 -0
  376. package/src/wirings/agent-scorer/agent-scorer-judge.ts +60 -0
  377. package/src/wirings/agent-scorer/agent-scorer.ts +8 -0
  378. package/src/wirings/agent-scorer/agent-scorer.types.ts +21 -0
  379. package/src/wirings/agent-scorer/index.ts +1 -0
  380. package/src/wirings/channel/channel-common.ts +4 -2
  381. package/src/wirings/channel/channel-handler.ts +2 -5
  382. package/src/wirings/channel/channel-middleware-runner.ts +1 -1
  383. package/src/wirings/channel/channel-rpc.types.ts +1 -5
  384. package/src/wirings/channel/channel.types.ts +1 -2
  385. package/src/wirings/channel/local/local-channel-runner.test.ts +1 -2
  386. package/src/wirings/channel/serverless/index.ts +1 -0
  387. package/src/wirings/cli/channel/cli-raw-channel-runner.ts +2 -2
  388. package/src/wirings/cli/channel/index.ts +1 -3
  389. package/src/wirings/cli/cli-runner.test.ts +1 -1
  390. package/src/wirings/cli/cli-runner.ts +11 -7
  391. package/src/wirings/cli/cli.types.ts +24 -3
  392. package/src/wirings/cli/command-parser.test.ts +388 -1
  393. package/src/wirings/cli/command-parser.ts +189 -29
  394. package/src/wirings/gateway/gateway-runner.test.ts +1 -2
  395. package/src/wirings/gateway/gateway-runner.ts +3 -6
  396. package/src/wirings/gateway/gateway.types.ts +5 -4
  397. package/src/wirings/http/http-routes.test.ts +1 -2
  398. package/src/wirings/http/http-runner-addon-ref.test.ts +238 -0
  399. package/src/wirings/http/http-runner.test.ts +3 -8
  400. package/src/wirings/http/http-runner.ts +9 -3
  401. package/src/wirings/http/http.types.ts +3 -12
  402. package/src/wirings/http/index.ts +1 -0
  403. package/src/wirings/http/pikku-fetch-http-request.ts +1 -1
  404. package/src/wirings/http/pikku-fetch-http-response.ts +3 -2
  405. package/src/wirings/mcp/mcp-runner.test.ts +1 -1
  406. package/src/wirings/mcp/mcp.types.ts +1 -2
  407. package/src/wirings/persona/index.ts +1 -4
  408. package/src/wirings/persona/persona.types.ts +14 -0
  409. package/src/wirings/queue/queue-identity.test.ts +1 -1
  410. package/src/wirings/queue/queue.types.ts +2 -6
  411. package/src/wirings/queue/signed-queue-service.ts +1 -1
  412. package/src/wirings/rpc/index.ts +0 -12
  413. package/src/wirings/rpc/rpc-runner.test.ts +4 -4
  414. package/src/wirings/rpc/rpc-runner.ts +3 -3
  415. package/src/wirings/scheduler/scheduler.types.ts +2 -5
  416. package/src/wirings/scope/index.ts +1 -0
  417. package/src/wirings/secret/secret.types.ts +10 -0
  418. package/src/wirings/secret/validate-secret-definitions.test.ts +23 -0
  419. package/src/wirings/secret/validate-secret-definitions.ts +2 -0
  420. package/src/wirings/variable/validate-variable-definitions.test.ts +23 -0
  421. package/src/wirings/variable/validate-variable-definitions.ts +2 -0
  422. package/src/wirings/variable/variable.types.ts +9 -0
  423. package/src/wirings/virtual-user/index.ts +1 -4
  424. package/src/wirings/virtual-user/prepare-virtual-user-run.ts +1 -1
  425. package/src/wirings/virtual-user/virtual-user-derive.test.ts +1 -1
  426. package/src/wirings/virtual-user/virtual-user-derive.ts +1 -1
  427. package/src/wirings/virtual-user/virtual-user.types.ts +1 -5
  428. package/src/wirings/workflow/graph/graph-foreach-queued.test.ts +569 -0
  429. package/src/wirings/workflow/graph/graph-foreach.test.ts +626 -0
  430. package/src/wirings/workflow/graph/graph-node.ts +16 -1
  431. package/src/wirings/workflow/graph/graph-runner.ts +387 -61
  432. package/src/wirings/workflow/graph/workflow-graph.types.ts +28 -3
  433. package/src/wirings/workflow/index.ts +8 -0
  434. package/src/wirings/workflow/pikku-scenario-service.ts +59 -19
  435. package/src/wirings/workflow/pikku-workflow-service.ts +6 -8
  436. package/src/wirings/workflow/run-timeline.ts +1 -1
  437. package/src/wirings/workflow/scenario-expectations.test.ts +28 -13
  438. package/src/wirings/workflow/scenario-run.types.ts +186 -0
  439. package/src/wirings/workflow/scenario-step-guards.ts +1 -14
  440. package/src/wirings/workflow/scenario-step.test.ts +32 -7
  441. package/src/wirings/workflow/scenario-step.types.ts +27 -12
  442. package/src/wirings/workflow/workflow-approval-policy.test.ts +117 -47
  443. package/src/wirings/workflow/workflow-approval.ts +2 -1
  444. package/src/wirings/workflow/workflow-run-engine.types.ts +2 -1
  445. package/src/wirings/workflow/workflow.types.ts +4 -12
  446. package/src/wirings-stay-decoupled.test.ts +6 -3
  447. package/tsconfig.tsbuildinfo +1 -1
  448. package/tsconfig.type-tests.json +3 -2
  449. package/dist/ecosystem/ai-agent.d.ts +0 -18
  450. package/dist/ecosystem/ai-agent.js +0 -6
  451. package/dist/ecosystem/ai-scorer.d.ts +0 -12
  452. package/dist/ecosystem/ai-scorer.js +0 -5
  453. package/dist/services/ai-agent-runner-service.d.ts +0 -220
  454. package/dist/services/ai-run-state-service.d.ts +0 -29
  455. package/dist/services/ai-storage-service.d.ts +0 -18
  456. package/dist/services/in-memory-ai-run-state-service.d.ts +0 -19
  457. package/dist/services/in-memory-ai-run-state-service.js +0 -54
  458. package/dist/testing/service-tests/ai-storage-service-tests.d.ts +0 -3
  459. package/dist/testing/service-tests/ai-storage-service-tests.js +0 -302
  460. package/dist/wirings/ai-agent/agent-rpc.d.ts +0 -15
  461. package/dist/wirings/ai-agent/agent-rpc.js +0 -53
  462. package/dist/wirings/ai-agent/ai-agent-agui.d.ts +0 -79
  463. package/dist/wirings/ai-agent/ai-agent-agui.js +0 -327
  464. package/dist/wirings/ai-agent/ai-agent-finalize.d.ts +0 -58
  465. package/dist/wirings/ai-agent/ai-agent-finalize.js +0 -138
  466. package/dist/wirings/ai-agent/ai-agent-helpers.d.ts +0 -35
  467. package/dist/wirings/ai-agent/ai-agent-helpers.js +0 -47
  468. package/dist/wirings/ai-agent/ai-agent-interrupt.d.ts +0 -153
  469. package/dist/wirings/ai-agent/ai-agent-interrupt.js +0 -257
  470. package/dist/wirings/ai-agent/ai-agent-memory.d.ts +0 -42
  471. package/dist/wirings/ai-agent/ai-agent-memory.js +0 -333
  472. package/dist/wirings/ai-agent/ai-agent-model-config.d.ts +0 -16
  473. package/dist/wirings/ai-agent/ai-agent-model-config.js +0 -51
  474. package/dist/wirings/ai-agent/ai-agent-prepare.d.ts +0 -117
  475. package/dist/wirings/ai-agent/ai-agent-prepare.js +0 -728
  476. package/dist/wirings/ai-agent/ai-agent-registry.d.ts +0 -16
  477. package/dist/wirings/ai-agent/ai-agent-registry.js +0 -51
  478. package/dist/wirings/ai-agent/ai-agent-runner.d.ts +0 -7
  479. package/dist/wirings/ai-agent/ai-agent-runner.js +0 -580
  480. package/dist/wirings/ai-agent/ai-agent-stream.d.ts +0 -41
  481. package/dist/wirings/ai-agent/ai-agent-stream.js +0 -1029
  482. package/dist/wirings/ai-agent/ai-agent-turn.d.ts +0 -57
  483. package/dist/wirings/ai-agent/ai-agent-turn.js +0 -82
  484. package/dist/wirings/ai-agent/ai-agent-utils.d.ts +0 -1
  485. package/dist/wirings/ai-agent/ai-agent-utils.js +0 -1
  486. package/dist/wirings/ai-agent/ai-agent.types.d.ts +0 -625
  487. package/dist/wirings/ai-agent/index.d.ts +0 -12
  488. package/dist/wirings/ai-agent/index.js +0 -10
  489. package/dist/wirings/ai-agent/voice-input.d.ts +0 -69
  490. package/dist/wirings/ai-agent/voice-input.js +0 -150
  491. package/dist/wirings/ai-agent/voice-output.d.ts +0 -85
  492. package/dist/wirings/ai-agent/voice-output.js +0 -196
  493. package/dist/wirings/ai-scorer/ai-scorer-grade.d.ts +0 -26
  494. package/dist/wirings/ai-scorer/ai-scorer-grade.js +0 -33
  495. package/dist/wirings/ai-scorer/ai-scorer-judge.d.ts +0 -17
  496. package/dist/wirings/ai-scorer/ai-scorer-judge.js +0 -92
  497. package/dist/wirings/ai-scorer/ai-scorer-live.d.ts +0 -15
  498. package/dist/wirings/ai-scorer/ai-scorer-live.js +0 -38
  499. package/dist/wirings/ai-scorer/ai-scorer-registry.d.ts +0 -18
  500. package/dist/wirings/ai-scorer/ai-scorer-registry.js +0 -46
  501. package/dist/wirings/ai-scorer/ai-scorer-sampling.d.ts +0 -8
  502. package/dist/wirings/ai-scorer/ai-scorer-sampling.js +0 -31
  503. package/dist/wirings/ai-scorer/ai-scorer-snapshots.d.ts +0 -10
  504. package/dist/wirings/ai-scorer/ai-scorer-snapshots.js +0 -40
  505. package/dist/wirings/ai-scorer/ai-scorer-worker.d.ts +0 -15
  506. package/dist/wirings/ai-scorer/ai-scorer-worker.js +0 -58
  507. package/dist/wirings/ai-scorer/ai-scorer.d.ts +0 -39
  508. package/dist/wirings/ai-scorer/ai-scorer.js +0 -40
  509. package/dist/wirings/ai-scorer/ai-scorer.types.d.ts +0 -90
  510. package/dist/wirings/ai-scorer/ai-scorer.types.js +0 -4
  511. package/dist/wirings/ai-scorer/index.d.ts +0 -6
  512. package/dist/wirings/ai-scorer/index.js +0 -5
  513. package/src/ecosystem/agent-scorer.ts +0 -24
  514. package/src/ecosystem/agent.ts +0 -42
  515. package/src/ecosystem/channel/local.ts +0 -1
  516. package/src/ecosystem/channel.ts +0 -22
  517. package/src/ecosystem/cli/channel.ts +0 -25
  518. package/src/ecosystem/cli.ts +0 -21
  519. package/src/ecosystem/credential.ts +0 -10
  520. package/src/ecosystem/dev.ts +0 -12
  521. package/src/ecosystem/function.ts +0 -28
  522. package/src/ecosystem/gateway.ts +0 -4
  523. package/src/ecosystem/hmac.ts +0 -1
  524. package/src/ecosystem/http.ts +0 -22
  525. package/src/ecosystem/mcp.ts +0 -17
  526. package/src/ecosystem/middleware.ts +0 -27
  527. package/src/ecosystem/node-host-resolver.ts +0 -1
  528. package/src/ecosystem/node.ts +0 -5
  529. package/src/ecosystem/oauth2.ts +0 -4
  530. package/src/ecosystem/persona.ts +0 -30
  531. package/src/ecosystem/queue.ts +0 -23
  532. package/src/ecosystem/remote.ts +0 -9
  533. package/src/ecosystem/role.ts +0 -13
  534. package/src/ecosystem/rpc.ts +0 -7
  535. package/src/ecosystem/safe-fetch.ts +0 -8
  536. package/src/ecosystem/scenario.ts +0 -32
  537. package/src/ecosystem/scheduler.ts +0 -5
  538. package/src/ecosystem/schema.ts +0 -1
  539. package/src/ecosystem/scope.ts +0 -14
  540. package/src/ecosystem/secret.ts +0 -15
  541. package/src/ecosystem/services/istanbul-coverage.ts +0 -1
  542. package/src/ecosystem/services/local-content-request-handler.ts +0 -16
  543. package/src/ecosystem/services/local-content.ts +0 -1
  544. package/src/ecosystem/services/v8-coverage.ts +0 -1
  545. package/src/ecosystem/services.ts +0 -82
  546. package/src/ecosystem/trigger.ts +0 -7
  547. package/src/ecosystem/types.ts +0 -34
  548. package/src/ecosystem/variable.ts +0 -14
  549. package/src/ecosystem/virtual-user.ts +0 -48
  550. package/src/ecosystem/workflow.ts +0 -68
  551. package/src/ecosystem.ts +0 -40
  552. package/src/index.ts +0 -244
  553. /package/dist/{services/ai-agent-runner-service.js → bootstrap-compat/root.js} +0 -0
  554. /package/dist/{services/ai-run-state-service.js → classification/data-classification.js} +0 -0
  555. /package/dist/{services/ai-storage-service.js → errors/serialized-error.js} +0 -0
  556. /package/dist/{wirings/ai-agent/ai-agent.types.js → function/function-meta.types.js} +0 -0
  557. /package/src/{column-form.ts → classification/column-form.ts} +0 -0
  558. /package/src/{pikku-request.test.ts → function/pikku-request.test.ts} +0 -0
  559. /package/src/{pikku-request.ts → function/pikku-request.ts} +0 -0
  560. /package/src/wirings/{rpc → addon}/addon-auth-tags.test.ts +0 -0
  561. /package/src/wirings/{rpc → addon}/addon-runner.ts +0 -0
  562. /package/src/wirings/{rpc → addon}/addon-secrets.test.ts +0 -0
  563. /package/src/wirings/{rpc → addon}/wire-addon.test.ts +0 -0
  564. /package/src/wirings/{rpc → addon}/wire-remote-addon.ts +0 -0
@@ -11,7 +11,7 @@ An approval gate is asked for so it can be answered for afterwards. "Who
11
11
  released the funds" is the entire point of four-eyes sign-off, and until this
12
12
  change none of it was kept.
13
13
 
14
- Run state is where a decision *waits*, not where it is kept. The record under
14
+ Run state is where a decision _waits_, not where it is kept. The record under
15
15
  `workflowRuns.state.__approval_<hex>` is overwritten by the next write to that
16
16
  key, and cleared outright — `decidedBy: undefined` included — whenever a
17
17
  decision is refused on replay. A trail that erases exactly the events worth
@@ -31,7 +31,7 @@ shape it had before there was anything to record.
31
31
 
32
32
  `deleteRun` cascades: `workflowStep` is `onDelete('cascade')` from
33
33
  `workflowRuns`, and `workflowStepHistory` from `workflowStep`. Deleting a run
34
- therefore deletes the sign-off with it — and a *refused* attempt never reaches a
34
+ therefore deletes the sign-off with it — and a _refused_ attempt never reaches a
35
35
  step at all, so it was never in that trail to begin with.
36
36
 
37
37
  Every answer — accepted, refused at submission, or cleared on replay — is
@@ -55,5 +55,5 @@ in `pikkuSchemas`.
55
55
  **What this rules out:** a dedicated `workflowApprovals` table. It would have to
56
56
  be implemented in all seven backends and would still be deleted with the run
57
57
  unless it deliberately broke the cascade — at which point it is the audit table
58
- with extra steps. If approvals later need to be *queried* as a first-class
58
+ with extra steps. If approvals later need to be _queried_ as a first-class
59
59
  entity rather than read back from the trail, that is when to revisit.
@@ -62,9 +62,10 @@ checked whether or not the host registered a global permission — an app that
62
62
  hand-wires `wireAddon({ name: 'console', package: '@pikku/addon-console' })`
63
63
  still cannot reach `execFileSync` unauthenticated.
64
64
 
65
- The scopes are spelled as literals here because they are scope ids in the
66
- addon's own `wireScope` tree the same tree the `pikku:console:scopes:*` gates
67
- on the scope-admin functions come from. The generated secret and variable
65
+ The scopes are spelled as literals here because they are scope ids in this
66
+ addon's own `wireScope` tree. The `admin:scopes:*` gates on the scope-admin
67
+ functions come from a different tree those functions now live in
68
+ `@pikku/addon-admin`, which declares its own. The generated secret and variable
68
69
  brokers live in the app's own scaffold rather than the addon, so the addon
69
70
  boundary never covered them; they carry `pikku:console:secrets:*` and
70
71
  `pikku:console:variables:*` themselves.
@@ -10,7 +10,7 @@ tags: core
10
10
  `packages/core/src/utils/safe-fetch.ts` guards every outbound fetch that core
11
11
  makes on a caller-supplied URL. `@pikku/core` runs in edge runtimes (Cloudflare
12
12
  Workers) that have no Node `dns` module, so the guard cannot resolve a hostname
13
- and inspect the resulting address. It instead rejects address *literals* that are
13
+ and inspect the resulting address. It instead rejects address _literals_ that are
14
14
  obviously internal — loopback, `10/8`, `172.16/12`, `192.168/16`, `0.0.0.0/8`,
15
15
  link-local `169.254/16` (the cloud metadata endpoint), IPv6 `::`, `::1`,
16
16
  `fe80::/10` and `fc00::/7` — plus the alias forms that reach the same targets:
@@ -13,8 +13,8 @@ every `runPikkuFunc` call in the gateway runner, copying `await wire.getSession(
13
13
  onto `wire.session` when the latter is unset. Gateway middleware is the only
14
14
  place a webhook can acquire a session at all — typically by mapping a verified
15
15
  platform sender id to a user. Middleware that assigns `wire.session` directly
16
- needs no help, but the idiomatic `wire.setSession()` writes into the *enclosing
17
- wiring's* session service, and the handler's own invocation does not read that
16
+ needs no help, but the idiomatic `wire.setSession()` writes into the _enclosing
17
+ wiring's_ session service, and the handler's own invocation does not read that
18
18
  service.
19
19
 
20
20
  Without the bridge, the failure mode is silent and it fails open in the way that
@@ -23,7 +23,7 @@ satisfy an admin-only function's requirements, because the two gates are
23
23
  evaluated independently against separate inputs. An empty or absent group passes,
24
24
  so declaring no permissions means "globals only", not "deny".
25
25
 
26
- Globals are bucketed per package, and a function reads *both* the root bucket and
26
+ Globals are bucketed per package, and a function reads _both_ the root bucket and
27
27
  its own package's — root first. The generated `addGlobalPermission` wrapper takes
28
28
  no package argument, so an application's rules only ever land in the root bucket;
29
29
  resolving the package bucket alone meant a host rule like "every request needs a
@@ -26,11 +26,11 @@ they divide into two kinds, and the difference is what can actually be
26
26
  prevented rather than merely reported:
27
27
 
28
28
  - **Prevention.** uWebSockets drives `res.onData` itself, so it counts bytes and
29
- *drops* chunks past the limit rather than concatenating, replying 413 before
29
+ _drops_ chunks past the limit rather than concatenating, replying 413 before
30
30
  routing — the only point at which the buffering can be stopped, which is why
31
31
  it deviates from the fetch adapter's 404-an-unmatched-route order. Fastify
32
32
  delegates to native `bodyLimit`, set only when `maxBodySize` is configured so
33
- the default never *loosens* fastify's stricter 1 MB. `PikkuExpressServer`
33
+ the default never _loosens_ fastify's stricter 1 MB. `PikkuExpressServer`
34
34
  feeds the limit into `express.json`/`text`/`urlencoded`.
35
35
  - **Rejection only.** `express-middleware` is handed an already-parsed body, so
36
36
  its guard makes the rejection uniform but cannot reclaim the memory; a
@@ -9,6 +9,7 @@ description: Who may do what in core — gates, defaults, and the failures they
9
9
  A rule about who may do what, and which way it fails when it is unsure.
10
10
 
11
11
  <!-- pikku:knowledge-index -->
12
+
12
13
  - [A dropped audit write is always logged](a-dropped-audit-write-is-always-logged.md) — The no-op audit service falls back to the singleton logger when the wire carries none, so an unconfigured audit call is never silent
13
14
  - [A function never receives the secret service](a-function-never-receives-the-secret-service.md) — Every function-, permission- and auth-facing services type is bounded by SecretlessServices, so reaching for `secrets` in a function body is a type error rather than a lint
14
15
  - [A graph run starts at an entry node the graph declared](a-graph-run-starts-at-an-entry-node-the-graph-declared.md) — startNode may only name a node in meta.entryNodeIds, and no generated route offers it, because otherwise a caller picks which half of the graph to skip
@@ -60,4 +61,5 @@ A rule about who may do what, and which way it fails when it is unsure.
60
61
  - [A queued workflow step rehydrates its session from the persisted run wire](workflow-queued-steps-rehydrate-their-session-from-the-run-wire.md) — The queue job payload is just `{ runId }`, so without threading `pikkuUserId` an authed step sees no session and throws
61
62
  - [Scenario sessions are isolated per actor and reset between scenarios](workflow-scenario-sessions-are-isolated-per-actor-and-per-scenario.md) — One jar per actor keeps two personas from sharing a session; a browser reset keeps one scenario from leaving the next signed in as somebody else
62
63
  - [A scenario step is never registered as a callable RPC and never dispatched on the queue](workflow-scenario-steps-are-never-network-invocable.md) — A step drives a browser and holds an actor's session, so exposing it as an RPC would put that reach on the network
64
+
63
65
  <!-- /pikku:knowledge-index -->
@@ -9,7 +9,7 @@ tags: mcp
9
9
 
10
10
  `RunMCPEndpointParams.exposeErrors` in
11
11
  `packages/core/src/wirings/mcp/mcp-runner.ts` defaults to `!isProduction()`, and
12
- `runMCPPikkuFunc` checks `exposeErrors && !isProduction()` *again* when building
12
+ `runMCPPikkuFunc` checks `exposeErrors && !isProduction()` _again_ when building
13
13
  the `-32603` internal-error response that carries `{ message, stack }`. The
14
14
  second check looks redundant against the default — it is not. The default only
15
15
  applies when the caller omits the option; a caller that passes
@@ -19,10 +19,10 @@ set by whoever enqueues the job. The adapter surfaces it as
19
19
  The claim is now HMAC-signed at enqueue and verified in `runQueueJob` — see
20
20
  [[queue-job-identities-are-signed-at-enqueue]], which supersedes the "any
21
21
  producer may name any user" reading below. Everything here still holds for the
22
- *authorization* question: signing proves the producer held the deployment
22
+ _authorization_ question: signing proves the producer held the deployment
23
23
  secret, not that it was entitled to name that user.
24
24
 
25
- There is no re-authentication at the worker boundary — the queue payload *is* the
25
+ There is no re-authentication at the worker boundary — the queue payload _is_ the
26
26
  credential. Workers run with `auth: false`, so nothing downstream re-checks that
27
27
  the producer was entitled to name that user. This is deliberate: the whole point
28
28
  is to let a background job resolve the same per-user credentials the original
@@ -35,5 +35,5 @@ client-supplied field flow into `JobOptions.pikkuUserId` without an authorizatio
35
35
  check first. It also rules out treating the queue as an authorization boundary —
36
36
  do not add a "the worker validated the producer's rights" assumption anywhere
37
37
  downstream, and do not drop the id from the wire on the theory that workers are
38
- unauthenticated anyway. Dropping an id that fails *signature* verification is a
38
+ unauthenticated anyway. Dropping an id that fails _signature_ verification is a
39
39
  different matter and is what the worker now does.
@@ -23,7 +23,7 @@ logged or traced. Binding a `credentialId` without a wire that supports
23
23
  credentials throws rather than falling back to a shared secret.
24
24
 
25
25
  Resolution fails closed. `null` is returned only when no auth is bound at all,
26
- which is the addon declaring its remote surface public. Once auth *is* bound, a
26
+ which is the addon declaring its remote surface public. Once auth _is_ bound, a
27
27
  resolved value of `null`, `undefined` or `''` raises `RemoteAddonAuthError` (401)
28
28
  instead of dispatching an unauthenticated request that the host might accept as
29
29
  anonymous.
@@ -16,7 +16,7 @@ dispatcher that cannot know what it dispatches to — wrong the moment one
16
16
  legitimately public sessionless function exists. For `userAdmin` it was
17
17
  redundant: the generated functions are `pikkuFunc` with `scopes:
18
18
  ['admin:users:list']`, gated twice over already. But for `agent`, `workflow`,
19
- `events` and `scenarios` it was the *only* gate, because those generate real
19
+ `events` and `scenarios` it was the _only_ gate, because those generate real
20
20
  endpoints the app never authors and has nowhere else to declare a gate for.
21
21
  A uniform collapse to boolean would have silently opened four surfaces.
22
22
 
@@ -33,7 +33,7 @@ it.
33
33
 
34
34
  The object form is also what makes the legacy strings safe to remove. An
35
35
  earlier design used `string` for the output path, under which `"no-auth"` would
36
- have parsed as *a file named `no-auth`* — every unmigrated config silently
36
+ have parsed as _a file named `no-auth`_ — every unmigrated config silently
37
37
  producing nonsense instead of failing. With `boolean | object`, a string is
38
38
  never valid, so `resolveScaffoldFeature` refuses `'auth'` and `'no-auth'` by
39
39
  name and prints the replacement. Coercing them was rejected outright: the value
@@ -17,7 +17,7 @@ them on every build.
17
17
  The fix is a declaration — `selfAuthenticated: true` on the function config —
18
18
  not analysis of the body. Detection would have to recognise an open-ended set of
19
19
  authorization idioms, and it fails in the expensive direction: a body that
20
- *looks* like it checks something silences the warning for a function that
20
+ _looks_ like it checks something silences the warning for a function that
21
21
  checks nothing. A declaration cannot be wrong by accident. Someone typed it,
22
22
  it is greppable, and it survives into meta where an audit reads it as what it
23
23
  is — a claim by a named author, not an inference.
@@ -13,8 +13,8 @@ requested. `LocalContent` requires a `JWTService` and throws without one; a
13
13
  verifier with no key available rejects with 403 rather than allowing the
14
14
  request, logging once per process so an attacker-triggerable path cannot flood.
15
15
 
16
- A signature over `{signedAt, expiresAt, notBefore}` alone proves only *when* a
17
- URL was issued, never *what* it was issued for. Any valid token was a skeleton
16
+ A signature over `{signedAt, expiresAt, notBefore}` alone proves only _when_ a
17
+ URL was issued, never _what_ it was issued for. Any valid token was a skeleton
18
18
  key: swap the pathname from a public thumbnail to a private document and the
19
19
  signature still verifies. Timestamps bound the window; only the path binds the
20
20
  asset.
@@ -6,7 +6,7 @@ description: Why @pikku/core is built the way it is — the reasoning that used
6
6
 
7
7
  # Knowledge
8
8
 
9
- Pikku's source says *what* it does. These notes say *why*, for the handful of
9
+ Pikku's source says _what_ it does. These notes say _why_, for the handful of
10
10
  choices where the code alone reads as arbitrary or, worse, reads as a mistake.
11
11
 
12
12
  The rule that produced this directory: **reasoning never lives in code.** A
@@ -19,6 +19,8 @@ meta files, the export surface. A note that repeats them is a copy that will
19
19
  drift.
20
20
 
21
21
  <!-- pikku:knowledge-index -->
22
+
22
23
  - [decisions](decisions/index.md) — a rule that was chosen, and what it rules out
23
24
  - [questions](questions/index.md) — something asked and not yet answered
25
+
24
26
  <!-- /pikku:knowledge-index -->
@@ -14,7 +14,7 @@ tags: core, channel
14
14
  `channel-common.ts` and `channel-handler.ts` — assert across the gap.
15
15
 
16
16
  A factory is `(input: In) => CorePikkuChannelMiddleware`: the author is meant to
17
- *call* it at wiring time and put the result in the array.
17
+ _call_ it at wiring time and put the result in the array.
18
18
  `combineChannelMiddleware` contains no factory-resolution branch, and neither
19
19
  does `combineMiddleware` for ordinary middleware — that is consistent and
20
20
  deliberate. So a bare, uncalled factory in `channelMiddleware` would be pushed
@@ -11,6 +11,8 @@ currently makes a choice that nobody has defended — the choice stays, the doub
11
11
  gets recorded.
12
12
 
13
13
  <!-- pikku:knowledge-index -->
14
+
14
15
  - [A channel's middleware list accepts bare factories that nothing ever resolves](channel-middleware-accepts-bare-factories-that-nothing-resolves.md) — CoreChannel.channelMiddleware admits CorePikkuChannelMiddlewareFactory, but no runner calls one, so a bare factory would stall the chain
15
16
  - [An unauthorized channel reply is sent outside the channel's declared Out type](unauthorized-channel-replies-escape-the-declared-out-type.md) — processMessageHandlers sends a bare string on auth failure, which no generated client type admits — nobody has decided what the typed shape should be
17
+
16
18
  <!-- /pikku:knowledge-index -->
package/package.json CHANGED
@@ -1,11 +1,9 @@
1
1
  {
2
2
  "name": "@pikku/core",
3
- "version": "0.12.84",
3
+ "version": "0.12.86",
4
4
  "description": "The Pikku runtime — functions, wirings, services, middleware and types",
5
5
  "author": "yasser.fadl@gmail.com",
6
6
  "license": "MIT",
7
- "module": "dist/index.js",
8
- "main": "dist/index.js",
9
7
  "type": "module",
10
8
  "scripts": {
11
9
  "tsc": "tsc && tsc -p tsconfig.type-tests.json",
@@ -21,14 +19,15 @@
21
19
  "sideEffects": [
22
20
  "./dist/errors/errors.js",
23
21
  "./dist/wirings/rpc/rpc-runner.js",
24
- "./dist/wirings/rpc/remote-addon-auth.js",
22
+ "./dist/wirings/addon/remote-addon-auth.js",
25
23
  "./dist/wirings/workflow/workflow-approval-policy.js",
26
24
  "./dist/wirings/workflow/workflow-errors.js",
27
25
  "./dist/wirings/workflow/workflow-run-ownership.js",
28
26
  "./dist/wirings/workflow/pikku-scenario-service.js"
29
27
  ],
30
28
  "exports": {
31
- ".": "./dist/index.js",
29
+ ".": "./dist/bootstrap-compat/root.js",
30
+ "./types": "./dist/types/index.js",
32
31
  "./middleware": "./dist/middleware/index.js",
33
32
  "./function": "./dist/function/index.js",
34
33
  "./channel": "./dist/wirings/channel/index.js",
@@ -46,6 +45,7 @@
46
45
  "./scheduler": "./dist/wirings/scheduler/index.js",
47
46
  "./trigger": "./dist/wirings/trigger/index.js",
48
47
  "./rpc": "./dist/wirings/rpc/index.js",
48
+ "./addon": "./dist/wirings/addon/index.js",
49
49
  "./safe-fetch": "./dist/utils/safe-fetch.js",
50
50
  "./node-host-resolver": "./dist/utils/node-host-resolver.js",
51
51
  "./mcp": "./dist/wirings/mcp/index.js",
@@ -73,8 +73,9 @@
73
73
  "./services/temporary-file-service": "./dist/services/temporary-file-service.js",
74
74
  "./crypto-utils": "./dist/crypto-utils.js",
75
75
  "./hmac": "./dist/utils/hmac.js",
76
- "./ecosystem": "./dist/ecosystem.js",
77
- "./ecosystem/*": "./dist/ecosystem/*.js",
76
+ "./state": "./dist/pikku-state.js",
77
+ "./classification": "./dist/classification/index.js",
78
+ "./utils": "./dist/utils.js",
78
79
  "./schema": "./dist/schema.js",
79
80
  "./testing": "./dist/testing/index.js",
80
81
  "./dev": "./dist/dev/hot-reload.js"
@@ -82,19 +83,19 @@
82
83
  "dependencies": {
83
84
  "@standard-schema/spec": "^1.1.0",
84
85
  "@types/json-schema": "^7.0.15",
85
- "cookie": "^1.1.1",
86
+ "cookie": "^2.0.1",
86
87
  "json-schema": "^0.4.0",
87
- "path-to-regexp": "^8.3.0",
88
+ "path-to-regexp": "^8.4.2",
88
89
  "picoquery": "^2.5.0"
89
90
  },
90
91
  "devDependencies": {
91
92
  "@ag-ui/core": "0.0.57",
92
- "@types/node": "^24.11.0",
93
- "esbuild": "~0.27.0",
94
- "tsx": "^4.21.0",
93
+ "@types/node": "^24.13.3",
94
+ "esbuild": "~0.28.2",
95
+ "tsx": "^4.23.12",
95
96
  "typescript": "^6.0.3"
96
97
  },
97
98
  "engines": {
98
99
  "node": ">=24"
99
100
  }
100
- }
101
+ }
package/run-tests.sh CHANGED
File without changes
@@ -1 +1 @@
1
- export {};
1
+ export {}
@@ -16,74 +16,81 @@
16
16
  *
17
17
  * Run: yarn api-report
18
18
  */
19
- import { readFileSync, writeFileSync } from 'node:fs';
20
- import { resolve, dirname } from 'node:path';
21
- import { fileURLToPath } from 'node:url';
22
- import ts from 'typescript';
23
- const packageRoot = resolve(dirname(fileURLToPath(import.meta.url)), '..');
24
- const pkg = JSON.parse(readFileSync(resolve(packageRoot, 'package.json'), 'utf-8'));
19
+ import { readFileSync, writeFileSync } from 'node:fs'
20
+ import { resolve, dirname } from 'node:path'
21
+ import { fileURLToPath } from 'node:url'
22
+ import ts from 'typescript'
23
+ const packageRoot = resolve(dirname(fileURLToPath(import.meta.url)), '..')
24
+ const pkg = JSON.parse(
25
+ readFileSync(resolve(packageRoot, 'package.json'), 'utf-8')
26
+ )
25
27
  const entryPoints = Object.entries(pkg.exports).map(([subpath, dist]) => ({
26
- subpath,
27
- file: resolve(packageRoot, dist.replace('./dist/', './src/').replace(/\.js$/, '.ts')),
28
- }));
29
- const program = ts.createProgram(entryPoints.map((e) => e.file), {
28
+ subpath,
29
+ file: resolve(
30
+ packageRoot,
31
+ dist.replace('./dist/', './src/').replace(/\.js$/, '.ts')
32
+ ),
33
+ }))
34
+ const program = ts.createProgram(
35
+ entryPoints.map((e) => e.file),
36
+ {
30
37
  target: ts.ScriptTarget.ESNext,
31
38
  module: ts.ModuleKind.Node16,
32
39
  moduleResolution: ts.ModuleResolutionKind.Node16,
33
40
  strict: true,
34
41
  skipLibCheck: true,
35
- });
36
- const checker = program.getTypeChecker();
42
+ }
43
+ )
44
+ const checker = program.getTypeChecker()
37
45
  /** One line per symbol: how it is declared, flattened and stripped of noise. */
38
46
  const signature = (exported) => {
39
- // A re-export is an alias; the declaration that matters is the target's, so
40
- // an interface re-exported through a barrel still reports its members.
41
- const symbol = exported.flags & ts.SymbolFlags.Alias
42
- ? checker.getAliasedSymbol(exported)
43
- : exported;
44
- const declaration = symbol.declarations?.[0];
45
- if (!declaration)
46
- return exported.getName();
47
- if (ts.isInterfaceDeclaration(declaration) ||
48
- ts.isClassDeclaration(declaration) ||
49
- ts.isTypeAliasDeclaration(declaration) ||
50
- ts.isEnumDeclaration(declaration)) {
51
- // The declaration itself, so member changes show up in the diff.
52
- return declaration
53
- .getText()
54
- .replace(/\/\*\*[\s\S]*?\*\//g, '')
55
- .replace(/\/\/[^\n]*/g, '')
56
- .split('\n')
57
- .map((l) => l.trim())
58
- .filter(Boolean)
59
- .join(' ');
60
- }
61
- const type = checker.getTypeOfSymbolAtLocation(symbol, declaration);
62
- return `${exported.getName()}: ${checker.typeToString(type, undefined, ts.TypeFormatFlags.NoTruncation)}`;
63
- };
47
+ // A re-export is an alias; the declaration that matters is the target's, so
48
+ // an interface re-exported through a barrel still reports its members.
49
+ const symbol =
50
+ exported.flags & ts.SymbolFlags.Alias
51
+ ? checker.getAliasedSymbol(exported)
52
+ : exported
53
+ const declaration = symbol.declarations?.[0]
54
+ if (!declaration) return exported.getName()
55
+ if (
56
+ ts.isInterfaceDeclaration(declaration) ||
57
+ ts.isClassDeclaration(declaration) ||
58
+ ts.isTypeAliasDeclaration(declaration) ||
59
+ ts.isEnumDeclaration(declaration)
60
+ ) {
61
+ // The declaration itself, so member changes show up in the diff.
62
+ return declaration
63
+ .getText()
64
+ .replace(/\/\*\*[\s\S]*?\*\//g, '')
65
+ .replace(/\/\/[^\n]*/g, '')
66
+ .split('\n')
67
+ .map((l) => l.trim())
68
+ .filter(Boolean)
69
+ .join(' ')
70
+ }
71
+ const type = checker.getTypeOfSymbolAtLocation(symbol, declaration)
72
+ return `${exported.getName()}: ${checker.typeToString(type, undefined, ts.TypeFormatFlags.NoTruncation)}`
73
+ }
64
74
  const sections = [
65
- '# @pikku/core API report',
66
- '',
67
- 'Generated by `yarn api-report`. Every exported symbol with its full',
68
- 'signature, so a member-level change is a reviewable diff. Do not edit.',
69
- '',
70
- ];
75
+ '# @pikku/core API report',
76
+ '',
77
+ 'Generated by `yarn api-report`. Every exported symbol with its full',
78
+ 'signature, so a member-level change is a reviewable diff. Do not edit.',
79
+ '',
80
+ ]
71
81
  for (const { subpath, file } of entryPoints) {
72
- const source = program.getSourceFile(file);
73
- if (!source)
74
- continue;
75
- const moduleSymbol = checker.getSymbolAtLocation(source);
76
- if (!moduleSymbol)
77
- continue;
78
- const exports = checker
79
- .getExportsOfModule(moduleSymbol)
80
- .map((s) => [s.getName(), signature(s)])
81
- .sort(([a], [b]) => a.localeCompare(b));
82
- sections.push(`## ${subpath}`, '');
83
- sections.push('```ts');
84
- for (const [, sig] of exports)
85
- sections.push(sig);
86
- sections.push('```', '');
82
+ const source = program.getSourceFile(file)
83
+ if (!source) continue
84
+ const moduleSymbol = checker.getSymbolAtLocation(source)
85
+ if (!moduleSymbol) continue
86
+ const exports = checker
87
+ .getExportsOfModule(moduleSymbol)
88
+ .map((s) => [s.getName(), signature(s)])
89
+ .sort(([a], [b]) => a.localeCompare(b))
90
+ sections.push(`## ${subpath}`, '')
91
+ sections.push('```ts')
92
+ for (const [, sig] of exports) sections.push(sig)
93
+ sections.push('```', '')
87
94
  }
88
- writeFileSync(resolve(packageRoot, 'api-report.md'), sections.join('\n'));
89
- console.log(`api-report.md written for ${entryPoints.length} entry points`);
95
+ writeFileSync(resolve(packageRoot, 'api-report.md'), sections.join('\n'))
96
+ console.log(`api-report.md written for ${entryPoints.length} entry points`)
@@ -180,9 +180,7 @@ const isPublic = (member: ts.ClassElement | ts.TypeElement): boolean =>
180
180
  */
181
181
  const membered = (
182
182
  declaration:
183
- | ts.ClassDeclaration
184
- | ts.InterfaceDeclaration
185
- | ts.EnumDeclaration,
183
+ ts.ClassDeclaration | ts.InterfaceDeclaration | ts.EnumDeclaration,
186
184
  members: ts.NodeArray<ts.ClassElement | ts.TypeElement | ts.EnumMember>
187
185
  ): string => {
188
186
  if (members.length === 0) return oneLine(declaration.getText())
@@ -233,17 +231,6 @@ const signature = (exported: ts.Symbol): string => {
233
231
  return `${exported.getName()}: ${checker.typeToString(type, undefined, ts.TypeFormatFlags.NoTruncation)}`
234
232
  }
235
233
 
236
- /**
237
- * Entry points whose signatures move with the CLI's codegen. Everything else
238
- * is what an application imports, and is what a compatibility promise covers.
239
- *
240
- * knowledge: decisions/internals/the-ecosystem-entry-point-carries-the-adapter-surface.md
241
- */
242
- const isEcosystem = (subpath: string) =>
243
- subpath === './ecosystem' ||
244
- subpath === './internal' ||
245
- subpath.startsWith('./ecosystem/')
246
-
247
234
  type Exported = { name: string; members: number; signature: string }
248
235
 
249
236
  /** The members the report lists, which is what the tally is counting. */
@@ -282,33 +269,16 @@ for (const { subpath, file } of entryPoints) {
282
269
  )
283
270
  }
284
271
 
285
- // A name reachable from more than one entry point is counted once, against the
286
- // weaker promise reaching it through a stable subpath is what a consumer will
287
- // have done, whatever else also exports it.
288
- const tierOf = new Map<string, string>()
272
+ // A name reachable from more than one entry point is counted once: what a
273
+ // compatibility promise owes is the name, not each door onto it.
289
274
  const membersOf = new Map<string, number>()
290
- for (const [subpath, exports] of modules) {
291
- const tier = isEcosystem(subpath) ? 'ecosystem' : 'stable'
292
- for (const { name, members } of exports) {
293
- if (tier === 'stable' || !tierOf.has(name)) tierOf.set(name, tier)
275
+ for (const exports of modules.values())
276
+ for (const { name, members } of exports)
294
277
  membersOf.set(name, Math.max(membersOf.get(name) ?? 0, members))
295
- }
296
- }
297
278
 
298
- const tally = (tier: string) => {
299
- const names = [...tierOf].filter(([, t]) => t === tier).map(([n]) => n)
300
- return {
301
- entryPoints: [...modules.keys()].filter(
302
- (s) => (isEcosystem(s) ? 'ecosystem' : 'stable') === tier
303
- ).length,
304
- names: names.length,
305
- members: names.reduce((total, n) => total + (membersOf.get(n) ?? 0), 0),
306
- }
307
- }
308
- const stable = tally('stable')
309
- const ecosystem = tally('ecosystem')
310
- const observable =
311
- stable.names + stable.members + ecosystem.names + ecosystem.members
279
+ const names = membersOf.size
280
+ const members = [...membersOf.values()].reduce((total, n) => total + n, 0)
281
+ const observable = names + members
312
282
 
313
283
  const reachableFrom = new Map<string, number>()
314
284
  for (const exports of modules.values())
@@ -337,13 +307,9 @@ const sections: string[] = [
337
307
  '',
338
308
  '## What a compatibility promise covers',
339
309
  '',
340
- `**${observable} observable things**: ${stable.names + ecosystem.names} exported names, plus`,
341
- `${stable.members + ecosystem.members} members on the classes and interfaces among them.`,
342
- '',
343
- '| tier | entry points | names | members |',
344
- '| --- | ---: | ---: | ---: |',
345
- `| stable | ${stable.entryPoints} | ${stable.names} | ${stable.members} |`,
346
- `| ecosystem | ${ecosystem.entryPoints} | ${ecosystem.names} | ${ecosystem.members} |`,
310
+ `**${observable} observable things**: ${names} exported names, plus`,
311
+ `${members} members on the classes and interfaces among them, reachable`,
312
+ `through ${modules.size} entry points.`,
347
313
  '',
348
314
  'An entry point whose exports are mostly *exclusive* is a self-contained',
349
315
  'subsystem rather than shared machinery — which tends to mean a newer one.',
@@ -1,7 +1,8 @@
1
1
  import { describe, test } from 'node:test'
2
2
  import assert from 'node:assert/strict'
3
3
  import { execFileSync } from 'node:child_process'
4
- import { readFileSync, writeFileSync } from 'node:fs'
4
+ import { mkdtempSync, readFileSync, rmSync, writeFileSync } from 'node:fs'
5
+ import { tmpdir } from 'node:os'
5
6
  import { fileURLToPath } from 'node:url'
6
7
  import { join, dirname } from 'node:path'
7
8
 
@@ -67,3 +68,71 @@ describe('the API report matches the code', () => {
67
68
  )
68
69
  })
69
70
  })
71
+
72
+ /**
73
+ * Every branch that touches the public surface rewrites the whole report, so
74
+ * two of them meeting is a ~9000-line conflict with no meaningful resolution
75
+ * in it. A merge driver takes the conflict away; the test above is what still
76
+ * insists on a report that matches the code.
77
+ *
78
+ * knowledge: decisions/internals/the-api-report-pins-members-not-just-names.md
79
+ */
80
+ describe('merging the API report is not hand work', () => {
81
+ const repoRoot = join(packageRoot, '..', '..')
82
+
83
+ test('.gitattributes routes the report through the driver', () => {
84
+ const attributes = readFileSync(join(repoRoot, '.gitattributes'), 'utf-8')
85
+
86
+ assert.match(
87
+ attributes,
88
+ /^packages\/core\/api-report\.md\s+merge=api-report\b/m,
89
+ 'the report must name the merge driver, or git conflicts on it as usual'
90
+ )
91
+ })
92
+
93
+ test('the setup script registers the driver as a plain take-one-side', () => {
94
+ // Against a throwaway repository rather than this one: a driver is written
95
+ // to `.git/config`, which every worktree of a checkout shares, so running
96
+ // this in place would reach into whatever else is checked out beside it.
97
+ const sandbox = mkdtempSync(join(tmpdir(), 'pikku-merge-driver-'))
98
+ try {
99
+ execFileSync('git', ['init', '--quiet', sandbox], { stdio: 'pipe' })
100
+ execFileSync(
101
+ 'node',
102
+ [join(repoRoot, 'scripts', 'setup-merge-drivers.mjs')],
103
+ { cwd: sandbox, stdio: 'pipe' }
104
+ )
105
+ const driver = execFileSync(
106
+ 'git',
107
+ ['config', '--local', 'merge.api-report.driver'],
108
+ { cwd: sandbox, stdio: 'pipe' }
109
+ )
110
+ .toString()
111
+ .trim()
112
+
113
+ // `true` writes nothing and succeeds, which is how a driver tells git to
114
+ // keep the side already in %A. Anything that regenerates the report is
115
+ // the trap this pins shut: git runs a driver while merging that one
116
+ // path, with the rest of the tree still unmerged, so the generator reads
117
+ // the old sources and resolves to a report missing exactly the API the
118
+ // incoming branch adds — conflict-free and wrong, which is worse than a
119
+ // conflict. The regeneration belongs to the guard above, which runs
120
+ // against a finished tree.
121
+ assert.equal(
122
+ driver,
123
+ 'true',
124
+ 'the driver must resolve to one side verbatim and run nothing'
125
+ )
126
+ } finally {
127
+ rmSync(sandbox, { recursive: true, force: true })
128
+ }
129
+ })
130
+
131
+ test('postinstall runs the setup, so a fresh clone is not left without it', () => {
132
+ const manifest = JSON.parse(
133
+ readFileSync(join(repoRoot, 'package.json'), 'utf-8')
134
+ )
135
+
136
+ assert.match(manifest.scripts.postinstall, /setup-merge-drivers\.mjs/)
137
+ })
138
+ })