@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
@@ -1,1029 +0,0 @@
1
- import { finalizeAgentRun, lastUserMessageText, } from './ai-agent-finalize.js';
2
- import { pikkuState, getSingletonServices } from '../../pikku-state.js';
3
- import { applyInputMiddleware } from './ai-agent-turn.js';
4
- import { AIProviderNotConfiguredError } from '../../errors/errors.js';
5
- import { randomUUID } from './ai-agent-utils.js';
6
- import { SPOKEN_TRANSCRIPT } from './voice-input.js';
7
- import { combineChannelMiddleware, wrapChannelWithMiddleware, } from '../channel/channel-middleware-runner.js';
8
- import { resolveMemoryServices, loadContextMessages, trimMessages, getWorkingMemoryMiddleware, } from './ai-agent-memory.js';
9
- import { prepareAgentRun, resolveAgent, buildInstructions, buildToolDefs, createScopedChannel, resolveOwnerResourceId, agentSessionScope, assertResourceOwner, assertResourcePrincipalOwner, assertAgentAuthorized, ToolApprovalRequired, ToolCredentialRequired, APPROVAL_REQUIRED, CREDENTIAL_REQUIRED, } from './ai-agent-prepare.js';
10
- import { resolveModelConfig } from './ai-agent-model-config.js';
11
- import { getInFlightTools, isAbortError, persistOrphanedToolResults, registerInterruptibleRun, signalRunInterrupt, trackInterruptNote, trackToolExecution, } from './ai-agent-interrupt.js';
12
- function createPersistingChannel(parent, storage, threadId, logger) {
13
- let fullText = '';
14
- let stepText = '';
15
- let stepGenerativeUI = null;
16
- let stepToolCalls = [];
17
- let stepToolResults = [];
18
- const totalUsage = {
19
- inputTokens: 0,
20
- outputTokens: 0,
21
- };
22
- // Survives the per-step flush below, which clears its own buffers: the run
23
- // record needs every call the run made, not just the last step's.
24
- const runToolCalls = [];
25
- const flushStep = async (opts) => {
26
- if (!storage)
27
- return;
28
- const text = stepText;
29
- const generativeUI = stepGenerativeUI;
30
- const calls = stepToolCalls;
31
- const results = stepToolResults;
32
- stepText = '';
33
- stepGenerativeUI = null;
34
- stepToolCalls = [];
35
- stepToolResults = [];
36
- if (text || generativeUI != null || calls.length > 0) {
37
- const assistantMsg = {
38
- id: randomUUID(),
39
- role: 'assistant',
40
- content: generativeUI != null
41
- ? [
42
- ...(text ? [{ type: 'text', text }] : []),
43
- { type: 'generative-ui', spec: generativeUI },
44
- ]
45
- : text || undefined,
46
- toolCalls: calls.length > 0 ? calls : undefined,
47
- ...(opts?.interrupted ? { interrupted: true } : {}),
48
- createdAt: new Date(),
49
- };
50
- const messages = [assistantMsg];
51
- if (results.length > 0) {
52
- messages.push({
53
- id: randomUUID(),
54
- role: 'tool',
55
- toolResults: results,
56
- createdAt: new Date(),
57
- });
58
- }
59
- await storage.saveMessages(threadId, messages);
60
- }
61
- };
62
- const flushDetached = () => {
63
- void flushStep().catch((error) => {
64
- logger?.error('Failed to persist agent messages', {
65
- threadId,
66
- error,
67
- });
68
- });
69
- };
70
- const runToolCallIndex = new Map();
71
- const channel = {
72
- channelId: parent.channelId,
73
- openingData: parent.openingData,
74
- get state() {
75
- return parent.state;
76
- },
77
- get fullText() {
78
- return fullText;
79
- },
80
- get totalUsage() {
81
- return totalUsage;
82
- },
83
- get runToolCalls() {
84
- return runToolCalls;
85
- },
86
- flush: flushStep,
87
- close: () => parent.close(),
88
- sendBinary: (data) => parent.sendBinary(data),
89
- send: (event) => {
90
- // Accumulated whether or not storage is configured: `fullText` is what
91
- // the client was streamed, and an interrupted run has to be able to
92
- // report the fragment it got through even with persistence turned off.
93
- if (event.type === 'text-delta')
94
- fullText += event.text;
95
- if (event.type === 'tool-call') {
96
- runToolCallIndex.set(event.toolCallId, runToolCalls.length);
97
- runToolCalls.push({
98
- name: event.toolName,
99
- args: event.args,
100
- result: '',
101
- });
102
- }
103
- if (event.type === 'tool-result') {
104
- const index = runToolCallIndex.get(event.toolCallId);
105
- const result = typeof event.result === 'string'
106
- ? event.result
107
- : JSON.stringify(event.result);
108
- const call = index === undefined ? undefined : runToolCalls[index];
109
- if (call) {
110
- call.result = result;
111
- if (event.error)
112
- call.error = event.error;
113
- }
114
- }
115
- if (storage) {
116
- switch (event.type) {
117
- case 'text-delta':
118
- stepText += event.text;
119
- break;
120
- case 'tool-call':
121
- stepToolCalls.push({
122
- id: event.toolCallId,
123
- name: event.toolName,
124
- args: event.args,
125
- });
126
- break;
127
- case 'tool-result':
128
- stepToolResults.push({
129
- id: event.toolCallId,
130
- name: event.toolName,
131
- result: typeof event.result === 'string'
132
- ? event.result
133
- : JSON.stringify(event.result),
134
- ...(event.error ? { error: event.error } : {}),
135
- });
136
- break;
137
- case 'generative-ui':
138
- stepGenerativeUI = event.spec;
139
- break;
140
- case 'usage':
141
- totalUsage.inputTokens += event.tokens.input;
142
- totalUsage.outputTokens += event.tokens.output;
143
- if (event.model)
144
- totalUsage.model = event.model;
145
- flushDetached();
146
- break;
147
- case 'done':
148
- flushDetached();
149
- break;
150
- }
151
- }
152
- parent.send(event);
153
- },
154
- setState: (s) => parent.setState(s),
155
- getState: () => parent.getState(),
156
- clearState: () => parent.clearState(),
157
- remote: (funcName, data) => parent.remote(funcName, data),
158
- };
159
- return channel;
160
- }
161
- /**
162
- * Agents already warned about, so a per-request hook does not become a
163
- * per-request log line.
164
- */
165
- const warnedUnstreamedOutputHooks = new Set();
166
- /**
167
- * `modifyOutput` does not run on a streamed run at all. Nothing here could act
168
- * on what it returns — the text has already reached the client, and
169
- * `createPersistingChannel` flushes each step to storage as it goes, so by the
170
- * time the run ends the transcript is already written.
171
- *
172
- * Rewriting on this path belongs to `modifyOutputStream`, which genuinely
173
- * works: the stream middleware wraps the persisting channel, so what is stored
174
- * and accumulated is already what the client was sent. A middleware that
175
- * rewrites in `modifyOutput` only — a redaction hook, typically — is therefore
176
- * silently ineffective when the agent is streamed, and is told so once.
177
- */
178
- const warnUnstreamedOutputHooks = (agentName, aiMiddlewares, logger) => {
179
- if (warnedUnstreamedOutputHooks.has(agentName))
180
- return;
181
- const unstreamed = aiMiddlewares.some((mw) => mw.modifyOutput && !mw.modifyOutputStream);
182
- if (!unstreamed)
183
- return;
184
- warnedUnstreamedOutputHooks.add(agentName);
185
- logger?.warn(`Agent '${agentName}' has AI middleware with modifyOutput but no modifyOutputStream — modifyOutput does not apply to streamed runs. Implement modifyOutputStream to affect a streamed reply.`);
186
- };
187
- async function postStreamCleanup(persistingChannel, aiRunState, runId, run) {
188
- await finalizeAgentRun(aiRunState, {
189
- runId,
190
- agentName: run.agentName,
191
- threadId: run.threadId,
192
- resourceId: run.resourceId,
193
- input: run.input,
194
- // Already what the client received: the stream middleware wraps the
195
- // persisting channel, so both were accumulated post-rewrite.
196
- text: persistingChannel.fullText,
197
- steps: [
198
- {
199
- usage: persistingChannel.totalUsage,
200
- toolCalls: persistingChannel.runToolCalls,
201
- },
202
- ],
203
- usage: persistingChannel.totalUsage,
204
- });
205
- }
206
- async function runStreamStepLoop(params) {
207
- const { agent, runnerParams, maxSteps, agentRunner, streamChannel, channel, aiMiddlewares, } = params;
208
- const singletonServices = getSingletonServices();
209
- for (let step = 0; step < maxSteps; step++) {
210
- if (agent.prepareStep) {
211
- let stopped = false;
212
- await agent.prepareStep({
213
- stepNumber: step,
214
- messages: runnerParams.messages,
215
- tools: runnerParams.tools,
216
- toolChoice: runnerParams.toolChoice,
217
- model: runnerParams.model,
218
- stop: () => {
219
- stopped = true;
220
- },
221
- });
222
- if (stopped)
223
- break;
224
- }
225
- channel.send({ type: 'step-start', stepNumber: step });
226
- const stepResult = await agentRunner.stream(runnerParams, streamChannel);
227
- for (const mw of aiMiddlewares) {
228
- if (mw.afterStep) {
229
- await mw.afterStep(singletonServices, {
230
- stepNumber: step,
231
- text: stepResult.text,
232
- toolCalls: stepResult.toolCalls,
233
- toolResults: stepResult.toolResults,
234
- usage: stepResult.usage,
235
- finishReason: stepResult.finishReason,
236
- });
237
- }
238
- }
239
- if (stepResult.toolCalls.length === 0)
240
- break;
241
- const approvalsNeeded = checkForApprovals(stepResult, runnerParams.tools);
242
- if (approvalsNeeded.length > 0) {
243
- for (const approval of approvalsNeeded) {
244
- const toolDef = runnerParams.tools.find((t) => t.name === approval.toolName);
245
- if (toolDef?.approvalDescriptionFn && !approval.reason) {
246
- try {
247
- approval.reason = await toolDef.approvalDescriptionFn(approval.args);
248
- }
249
- catch { }
250
- }
251
- }
252
- return { outcome: 'approval', approvals: approvalsNeeded };
253
- }
254
- const credentialRequests = checkForCredentialRequests(stepResult);
255
- if (credentialRequests.length > 0) {
256
- appendStepMessages(runnerParams, stepResult);
257
- return { outcome: 'credential', credentialRequests };
258
- }
259
- appendStepMessages(runnerParams, stepResult);
260
- }
261
- return { outcome: 'done' };
262
- }
263
- export function checkForApprovals(stepResult, tools) {
264
- const approvals = [];
265
- for (const tc of stepResult.toolCalls) {
266
- const toolDef = tools.find((t) => t.name === tc.toolName);
267
- if (toolDef?.needsApproval) {
268
- approvals.push(new ToolApprovalRequired(tc.toolCallId, tc.toolName, tc.args));
269
- continue;
270
- }
271
- if (!toolDef?.forwardsApproval) {
272
- continue;
273
- }
274
- const tr = stepResult.toolResults.find((r) => r.toolCallId === tc.toolCallId);
275
- if (tr?.result &&
276
- typeof tr.result === 'object' &&
277
- APPROVAL_REQUIRED in tr.result) {
278
- const r = tr.result;
279
- if (r.subApprovals?.length) {
280
- for (const sub of r.subApprovals) {
281
- approvals.push(new ToolApprovalRequired(sub.toolCallId, r.toolName, r.args, sub.reason, sub.toolName, sub.args, r.agentRunId));
282
- }
283
- }
284
- else {
285
- approvals.push(new ToolApprovalRequired(tc.toolCallId, r.toolName, r.args, r.reason, r.displayToolName, r.displayArgs, r.agentRunId));
286
- }
287
- }
288
- }
289
- return approvals;
290
- }
291
- export function checkForCredentialRequests(stepResult) {
292
- const requests = [];
293
- for (const tr of stepResult.toolResults) {
294
- // knowledge: decisions/security/ai-agent-credential-requests-are-symbol-branded.md
295
- if (tr.result &&
296
- typeof tr.result === 'object' &&
297
- CREDENTIAL_REQUIRED in tr.result) {
298
- const r = tr.result;
299
- const tc = stepResult.toolCalls.find((t) => t.toolCallId === tr.toolCallId);
300
- requests.push(new ToolCredentialRequired(tr.toolCallId, tc?.toolName ?? 'unknown', tc?.args ?? {}, r.credentialName, r.credentialType, r.connectUrl));
301
- }
302
- }
303
- return requests;
304
- }
305
- export function appendStepMessages(runnerParams, stepResult) {
306
- const structuredOutput = stepResult.object && typeof stepResult.object === 'object'
307
- ? stepResult.object
308
- : null;
309
- const assistantContent = structuredOutput?.ui != null
310
- ? [
311
- ...(stepResult.text
312
- ? [{ type: 'text', text: stepResult.text }]
313
- : []),
314
- { type: 'generative-ui', spec: structuredOutput.ui },
315
- ]
316
- : stepResult.text || undefined;
317
- const assistantMsg = {
318
- id: randomUUID(),
319
- role: 'assistant',
320
- content: assistantContent,
321
- toolCalls: stepResult.toolCalls.length > 0
322
- ? stepResult.toolCalls.map((tc) => ({
323
- id: tc.toolCallId,
324
- name: tc.toolName,
325
- args: tc.args,
326
- }))
327
- : undefined,
328
- ...(stepResult.reasoningContent
329
- ? { reasoningContent: stepResult.reasoningContent }
330
- : {}),
331
- createdAt: new Date(),
332
- };
333
- runnerParams.messages.push(assistantMsg);
334
- if (stepResult.toolResults.length > 0) {
335
- const toolMsg = {
336
- id: randomUUID(),
337
- role: 'tool',
338
- toolResults: stepResult.toolResults.map((tr) => ({
339
- id: tr.toolCallId,
340
- name: tr.toolName,
341
- result: typeof tr.result === 'string' ? tr.result : JSON.stringify(tr.result),
342
- })),
343
- createdAt: new Date(),
344
- };
345
- runnerParams.messages.push(toolMsg);
346
- }
347
- }
348
- function handleApprovals(approvals, runId, channel, aiRunState, persistingChannel) {
349
- return (async () => {
350
- await persistingChannel.flush();
351
- const pendingApprovals = approvals.map((err) => err.agentRunId
352
- ? {
353
- type: 'agent-call',
354
- toolCallId: err.toolCallId,
355
- agentName: err.toolName,
356
- agentRunId: err.agentRunId,
357
- displayToolName: err.displayToolName ?? err.toolName,
358
- displayArgs: err.displayArgs ?? err.args,
359
- }
360
- : {
361
- type: 'tool-call',
362
- toolCallId: err.toolCallId,
363
- toolName: err.toolName,
364
- args: err.args,
365
- });
366
- await aiRunState.updateRun(runId, {
367
- status: 'suspended',
368
- suspendReason: 'approval',
369
- pendingApprovals,
370
- });
371
- for (const err of approvals) {
372
- const approvalEvent = {
373
- type: 'approval-request',
374
- toolCallId: err.toolCallId,
375
- toolName: err.displayToolName ?? err.toolName,
376
- args: err.displayArgs ?? err.args,
377
- reason: err.reason,
378
- runId,
379
- };
380
- channel.send(approvalEvent);
381
- }
382
- channel.send({ type: 'done' });
383
- channel.close();
384
- })();
385
- }
386
- function handleCredentialRequests(requests, runId, channel, aiRunState, persistingChannel) {
387
- return (async () => {
388
- await persistingChannel.flush();
389
- const pendingApprovals = requests.map((req) => ({
390
- type: 'credential-request',
391
- toolCallId: req.toolCallId,
392
- toolName: req.toolName,
393
- args: req.args,
394
- credentialName: req.credentialName,
395
- credentialType: req.credentialType,
396
- connectUrl: req.connectUrl,
397
- }));
398
- await aiRunState.updateRun(runId, {
399
- status: 'suspended',
400
- suspendReason: 'credential',
401
- pendingApprovals,
402
- });
403
- for (const req of requests) {
404
- channel.send({
405
- type: 'credential-request',
406
- toolCallId: req.toolCallId,
407
- toolName: req.toolName,
408
- args: req.args,
409
- credentialName: req.credentialName,
410
- credentialType: req.credentialType,
411
- connectUrl: req.connectUrl,
412
- runId,
413
- });
414
- }
415
- channel.send({ type: 'done' });
416
- channel.close();
417
- })();
418
- }
419
- export async function streamAIAgent(agentName, input, channel, params, agentSessionMap, options) {
420
- const sessionMap = agentSessionMap ?? new Map();
421
- const normalizedInput = {
422
- ...input,
423
- resourceId: resolveOwnerResourceId(params, agentSessionScope(agentName), input.resourceId),
424
- };
425
- const streamContext = { channel, options };
426
- const { agent, packageName, resolvedName, agentRunner, storage, memoryConfig, threadId, userMessage, runnerParams, maxSteps, missingRpcs, workingMemorySchemaName, } = await prepareAgentRun(agentName, normalizedInput, params, sessionMap, streamContext);
427
- runnerParams.agentId = agentName;
428
- const singletonServices = getSingletonServices();
429
- const { aiRunState } = singletonServices;
430
- if (!aiRunState) {
431
- throw new Error('AIRunStateService not available in singletonServices');
432
- }
433
- if (missingRpcs.length > 0) {
434
- await aiRunState.createRun({
435
- agentName,
436
- threadId,
437
- resourceId: normalizedInput.resourceId,
438
- status: 'suspended',
439
- suspendReason: 'rpc-missing',
440
- missingRpcs,
441
- usage: { inputTokens: 0, outputTokens: 0, model: agent.model },
442
- createdAt: new Date(),
443
- updatedAt: new Date(),
444
- });
445
- channel.send({ type: 'suspended', reason: 'rpc-missing', missingRpcs });
446
- channel.send({ type: 'done' });
447
- return '';
448
- }
449
- const aiMiddlewares = [
450
- ...getWorkingMemoryMiddleware(memoryConfig, storage, {
451
- threadId,
452
- workingMemorySchemaName,
453
- logger: singletonServices.logger,
454
- schemaService: singletonServices.schema,
455
- }),
456
- ...(agent.aiMiddleware ?? []),
457
- ];
458
- // One bag per run, shared by every middleware — see PikkuAIMiddlewareHooks.
459
- const sharedNotes = {};
460
- const { messages: modifiedMessages, instructions: modifiedInstructions } = await applyInputMiddleware(aiMiddlewares, singletonServices, {
461
- messages: runnerParams.messages,
462
- instructions: runnerParams.instructions,
463
- }, sharedNotes);
464
- runnerParams.messages = modifiedMessages;
465
- runnerParams.instructions = modifiedInstructions;
466
- // knowledge: decisions/internals/the-transcript-event-is-sent-ahead-of-the-run.md
467
- const transcript = sharedNotes[SPOKEN_TRANSCRIPT];
468
- if (typeof transcript === 'string') {
469
- channel.send({ type: 'transcript', text: transcript });
470
- }
471
- // knowledge: decisions/internals/thread-history-records-the-transcript-not-the-audio.md
472
- const lastModified = modifiedMessages[modifiedMessages.length - 1];
473
- const persistedUserMessage = lastModified?.id === userMessage.id ? lastModified : userMessage;
474
- const runId = await aiRunState.createRun({
475
- agentName,
476
- threadId,
477
- resourceId: normalizedInput.resourceId,
478
- status: 'running',
479
- usage: { inputTokens: 0, outputTokens: 0, model: agent.model },
480
- createdAt: new Date(),
481
- updatedAt: new Date(),
482
- });
483
- options?.onRunCreated?.(runId);
484
- // Registered before the first model call and released in `finally`, so the
485
- // window in which an interrupt can land is exactly the window in which there
486
- // is something to interrupt.
487
- const interruptHandle = registerInterruptibleRun(runId);
488
- runnerParams.abortSignal = interruptHandle.signal;
489
- runnerParams.tools = trackToolExecution(runnerParams.tools, interruptHandle);
490
- if (storage) {
491
- await storage.saveMessages(threadId, [persistedUserMessage]);
492
- }
493
- warnUnstreamedOutputHooks(agentName, aiMiddlewares, singletonServices.logger);
494
- const streamMiddleware = aiMiddlewares
495
- .filter((mw) => mw.modifyOutputStream)
496
- .map((mw) => {
497
- const state = {};
498
- const allEvents = [];
499
- return async (services, event, next) => {
500
- allEvents.push(event);
501
- const result = await mw.modifyOutputStream(services, {
502
- event,
503
- allEvents,
504
- state,
505
- shared: sharedNotes,
506
- // Sends downstream directly, so a hook can hand back the fast event
507
- // now and push the slow one when it is ready.
508
- emit: next,
509
- signal: interruptHandle.signal,
510
- });
511
- if (result == null)
512
- return;
513
- if (Array.isArray(result)) {
514
- for (const r of result)
515
- await next(r);
516
- }
517
- else {
518
- await next(result);
519
- }
520
- };
521
- });
522
- const agentsMeta = pikkuState(packageName, 'agent', 'agentsMeta');
523
- const meta = agentsMeta[resolvedName];
524
- const allChannelMiddleware = combineChannelMiddleware('agent', `stream:${agentName}`, {
525
- wireInheritedChannelMiddleware: meta?.channelMiddleware,
526
- wireChannelMiddleware: [
527
- ...(agent.channelMiddleware ?? []),
528
- ...streamMiddleware,
529
- ],
530
- });
531
- const persistingChannel = createPersistingChannel(channel, storage, threadId, singletonServices.logger);
532
- const wrappedChannel = allChannelMiddleware.length > 0
533
- ? wrapChannelWithMiddleware({ channel: persistingChannel }, singletonServices, allChannelMiddleware).channel
534
- : persistingChannel;
535
- const credentialFilteredChannel = {
536
- ...wrappedChannel,
537
- // knowledge: decisions/internals/an-agent-stream-send-must-return-the-inner-sends-promise.md
538
- send: (event) => {
539
- if (event.type === 'tool-result' &&
540
- event.result !== null &&
541
- typeof event.result === 'object' &&
542
- '__credentialRequired' in event.result) {
543
- return;
544
- }
545
- return wrappedChannel.send(event);
546
- },
547
- };
548
- const isDelegateMode = agent.agentMode !== 'supervise' && meta?.agents?.length;
549
- const delegateState = { delegated: false };
550
- if (isDelegateMode) {
551
- streamContext.delegateState = delegateState;
552
- }
553
- const outputChannel = isDelegateMode
554
- ? {
555
- ...credentialFilteredChannel,
556
- send: (event) => {
557
- if (delegateState.delegated &&
558
- (event.type === 'text-delta' || event.type === 'reasoning-delta'))
559
- return;
560
- return credentialFilteredChannel.send(event);
561
- },
562
- delegateState,
563
- }
564
- : credentialFilteredChannel;
565
- try {
566
- const loopResult = await runStreamStepLoop({
567
- agent,
568
- runnerParams,
569
- maxSteps,
570
- agentRunner,
571
- streamChannel: outputChannel,
572
- persistingChannel,
573
- channel,
574
- aiMiddlewares,
575
- });
576
- if (loopResult.outcome === 'approval') {
577
- await handleApprovals(loopResult.approvals, runId, channel, aiRunState, persistingChannel);
578
- return persistingChannel.fullText;
579
- }
580
- if (loopResult.outcome === 'credential') {
581
- await handleCredentialRequests(loopResult.credentialRequests, runId, channel, aiRunState, persistingChannel);
582
- return persistingChannel.fullText;
583
- }
584
- await postStreamCleanup(persistingChannel, aiRunState, runId, {
585
- agentName,
586
- threadId,
587
- resourceId: input.resourceId,
588
- input: lastUserMessageText(runnerParams.messages),
589
- });
590
- // knowledge: decisions/internals/the-agent-done-event-goes-through-the-middleware-and-is-awaited.md
591
- await outputChannel.send({ type: 'done' });
592
- channel.close();
593
- return persistingChannel.fullText;
594
- }
595
- catch (err) {
596
- // An interrupt is not a failure: the truncated text is real output the user
597
- // already heard part of, so it is persisted and marked rather than dropped.
598
- const interruption = interruptHandle.interruption;
599
- if (interruption || isAbortError(err)) {
600
- await persistingChannel.flush({ interrupted: true });
601
- await aiRunState.updateRun(runId, { status: 'interrupted' });
602
- // Deliberately not awaited. A tool still running must not hold the stream
603
- // open — the whole point of barge-in is that the agent stops now — so the
604
- // note lands later and the next run on this thread waits for it instead.
605
- if (storage) {
606
- trackInterruptNote(threadId, persistOrphanedToolResults(interruptHandle, storage, threadId));
607
- }
608
- channel.send({
609
- type: 'interrupted',
610
- runId,
611
- text: persistingChannel.fullText,
612
- reason: interruption?.reason ?? 'user',
613
- });
614
- channel.send({ type: 'done' });
615
- channel.close();
616
- return persistingChannel.fullText;
617
- }
618
- for (const mw of aiMiddlewares) {
619
- if (mw.onError) {
620
- try {
621
- await mw.onError(singletonServices, {
622
- error: err instanceof Error ? err : new Error(String(err)),
623
- stepNumber: -1,
624
- messages: runnerParams.messages,
625
- });
626
- }
627
- catch { }
628
- }
629
- }
630
- await aiRunState.updateRun(runId, {
631
- status: 'failed',
632
- errorMessage: err instanceof Error ? err.message : String(err),
633
- });
634
- channel.send({
635
- type: 'error',
636
- message: err instanceof Error ? err.message : String(err),
637
- });
638
- channel.send({ type: 'done' });
639
- channel.close();
640
- return persistingChannel.fullText;
641
- }
642
- finally {
643
- interruptHandle.release();
644
- }
645
- }
646
- /**
647
- * Stop an in-flight run on behalf of a caller, after checking the run is theirs.
648
- *
649
- * Unlike {@link resumeAIAgent} this needs no channel: it does not continue the
650
- * stream, it ends one. The `interrupted` event and the truncated message are
651
- * emitted by the original {@link streamAIAgent} call on its own channel, so this
652
- * can be reached over a plain RPC while the stream is held open elsewhere —
653
- * which is exactly the shape a voice barge-in needs.
654
- *
655
- * Ownership is the whole gate, deliberately without {@link assertAgentAuthorized}.
656
- * Resuming re-enters the agent and approves a tool call, so a revoked grant must
657
- * block it; stopping is the opposite — if a caller's access to an agent was
658
- * revoked mid-run, being able to stop their own run is harmless and arguably
659
- * what you want.
660
- *
661
- * Returns `false` when there was nothing to stop. Racing a run that finishes on
662
- * its own is the normal case in voice, so it is not an error.
663
- */
664
- export async function interruptAIAgent(input, params) {
665
- const { aiRunState, logger } = getSingletonServices();
666
- if (!aiRunState) {
667
- throw new Error('AIRunStateService not available in singletonServices');
668
- }
669
- const run = await aiRunState.getRun(input.runId);
670
- if (!run) {
671
- throw new Error(`No run found for runId ${input.runId}`);
672
- }
673
- assertResourceOwner(resolveOwnerResourceId(params, agentSessionScope(run.agentName), run.resourceId), run.resourceId, 'run');
674
- const stopped = signalRunInterrupt(input.runId, {
675
- reason: input.reason ?? 'user',
676
- });
677
- // knowledge: decisions/internals/an-agent-run-owned-by-another-instance-says-so.md
678
- if (!stopped && run.status === 'running') {
679
- logger?.warn(`Could not interrupt run ${input.runId}: it is running in another process. ` +
680
- 'Interrupts are process-local; fan them out over eventHub to support multiple instances.');
681
- }
682
- // Reported after the abort so it names what is *still* running: aborting the
683
- // model call does nothing to a tool already executing.
684
- return { stopped, inFlightTools: getInFlightTools(input.runId) };
685
- }
686
- export async function resumeAIAgent(input, channel, params, options) {
687
- const singletonServices = getSingletonServices();
688
- const { aiRunState } = singletonServices;
689
- if (!aiRunState) {
690
- throw new Error('AIRunStateService not available in singletonServices');
691
- }
692
- const run = await aiRunState.getRun(input.runId);
693
- if (!run) {
694
- throw new Error(`No run found for runId ${input.runId}`);
695
- }
696
- assertResourcePrincipalOwner(params, agentSessionScope(run.agentName), run.resourceId, 'run');
697
- const pending = run.pendingApprovals?.find((p) => p.toolCallId === input.toolCallId);
698
- if (!pending) {
699
- throw new Error(`No pending approval for toolCallId ${input.toolCallId} on run ${input.runId}`);
700
- }
701
- const { agent, packageName, resolvedName } = resolveAgent(run.agentName);
702
- await assertAgentAuthorized(agent, params, packageName);
703
- // The read above is not a claim — concurrent resumes all see the same pending
704
- // approval. `resolveApproval` is the claim, and the loser must not go on to
705
- // run the tool a second time.
706
- const claimed = await aiRunState.resolveApproval(input.toolCallId, input.approved ? 'approved' : 'denied');
707
- if (!claimed) {
708
- throw new Error(`Approval for toolCallId ${input.toolCallId} was already resolved by another caller`);
709
- }
710
- const { storage } = resolveMemoryServices(agent, singletonServices);
711
- const memoryConfig = agent.memory;
712
- const agentRunner = singletonServices.aiAgentRunner;
713
- if (!agentRunner) {
714
- throw new AIProviderNotConfiguredError();
715
- }
716
- if (!input.approved) {
717
- if (pending.type === 'agent-call') {
718
- await aiRunState.updateRun(pending.agentRunId, { status: 'failed' });
719
- }
720
- const denialResult = 'The user explicitly declined this action. Inform them that it was declined and do not retry.';
721
- if (storage) {
722
- await storage.saveMessages(run.threadId, [
723
- {
724
- id: randomUUID(),
725
- role: 'tool',
726
- toolResults: [
727
- {
728
- id: input.toolCallId,
729
- name: pending.type === 'tool-call' ||
730
- pending.type === 'credential-request'
731
- ? pending.toolName
732
- : pending.agentName,
733
- result: denialResult,
734
- },
735
- ],
736
- createdAt: new Date(),
737
- },
738
- ]);
739
- }
740
- channel.send({
741
- type: 'tool-result',
742
- toolCallId: input.toolCallId,
743
- toolName: pending.type === 'tool-call' || pending.type === 'credential-request'
744
- ? pending.toolName
745
- : pending.agentName,
746
- result: denialResult,
747
- });
748
- const updatedRun = await aiRunState.getRun(run.runId);
749
- const remaining = updatedRun?.pendingApprovals ?? [];
750
- if (remaining.length > 0) {
751
- channel.send({ type: 'done' });
752
- channel.close();
753
- return;
754
- }
755
- await aiRunState.updateRun(run.runId, { status: 'running' });
756
- await continueAfterToolResult(run, agent, packageName, resolvedName, storage, memoryConfig, agentRunner, channel, params, aiRunState, options);
757
- return;
758
- }
759
- if (pending.type === 'agent-call') {
760
- const subRun = await aiRunState.getRun(pending.agentRunId);
761
- if (!subRun) {
762
- throw new Error(`Sub-agent run not found: ${pending.agentRunId}`);
763
- }
764
- const subPending = subRun.pendingApprovals?.find((p) => p.toolCallId === input.toolCallId) ??
765
- subRun.pendingApprovals?.[0];
766
- if (!subPending) {
767
- throw new Error(`No pending approval on sub-agent run ${pending.agentRunId}`);
768
- }
769
- const subChannel = createScopedChannel(channel, subRun.agentName, 'resume');
770
- channel.send({
771
- type: 'agent-call',
772
- agentName: subRun.agentName,
773
- session: 'resume',
774
- input: null,
775
- });
776
- await resumeAIAgent({
777
- runId: pending.agentRunId,
778
- toolCallId: subPending.toolCallId,
779
- approved: true,
780
- }, subChannel, params, options);
781
- channel.send({
782
- type: 'agent-result',
783
- agentName: subRun.agentName,
784
- session: 'resume',
785
- result: null,
786
- });
787
- if (storage) {
788
- await storage.saveMessages(run.threadId, [
789
- {
790
- id: randomUUID(),
791
- role: 'tool',
792
- toolResults: [
793
- {
794
- id: input.toolCallId,
795
- name: pending.agentName,
796
- result: 'Sub-agent completed successfully',
797
- },
798
- ],
799
- createdAt: new Date(),
800
- },
801
- ]);
802
- }
803
- }
804
- else {
805
- const streamContext = {
806
- channel,
807
- options: { ...options, requiresToolApproval: false },
808
- };
809
- const aiMiddlewaresForResume = agent.aiMiddleware ?? [];
810
- const { tools } = await buildToolDefs(params, new Map(), run.resourceId, resolvedName, packageName, streamContext, aiMiddlewaresForResume);
811
- const matchingTool = tools.find((t) => t.name === pending.toolName);
812
- if (!matchingTool) {
813
- throw new Error(`Tool "${pending.toolName}" not found in agent definition`);
814
- }
815
- const toolArgs = typeof pending.args === 'string' ? JSON.parse(pending.args) : pending.args;
816
- let toolResult;
817
- let toolError;
818
- try {
819
- toolResult = await matchingTool.execute(toolArgs);
820
- }
821
- catch (execErr) {
822
- if (execErr?.payload?.error === 'missing_credential') {
823
- toolResult = execErr.payload;
824
- toolError = 'missing_credential';
825
- }
826
- else {
827
- toolError = execErr instanceof Error ? execErr.message : String(execErr);
828
- toolResult = `Error: ${toolError}`;
829
- }
830
- }
831
- const resultStr = typeof toolResult === 'string' ? toolResult : JSON.stringify(toolResult);
832
- if (storage) {
833
- await storage.saveMessages(run.threadId, [
834
- {
835
- id: randomUUID(),
836
- role: 'tool',
837
- toolResults: [
838
- {
839
- id: input.toolCallId,
840
- name: pending.toolName,
841
- result: resultStr,
842
- },
843
- ],
844
- createdAt: new Date(),
845
- },
846
- ]);
847
- }
848
- channel.send({
849
- type: 'tool-result',
850
- toolCallId: input.toolCallId,
851
- toolName: pending.toolName,
852
- result: toolResult,
853
- ...(toolError ? { error: toolError } : {}),
854
- });
855
- }
856
- const updatedRun = await aiRunState.getRun(run.runId);
857
- const remaining = updatedRun?.pendingApprovals ?? [];
858
- if (remaining.length > 0) {
859
- channel.send({ type: 'done' });
860
- channel.close();
861
- return;
862
- }
863
- await aiRunState.updateRun(run.runId, { status: 'running' });
864
- await continueAfterToolResult(run, agent, packageName, resolvedName, storage, memoryConfig, agentRunner, channel, params, aiRunState, options);
865
- }
866
- async function continueAfterToolResult(run, agent, packageName, resolvedName, storage, memoryConfig, agentRunner, channel, params, aiRunState, options) {
867
- const singletonServices = getSingletonServices();
868
- const agentsMeta = pikkuState(packageName, 'agent', 'agentsMeta');
869
- const meta = agentsMeta[resolvedName];
870
- const workingMemorySchemaName = meta?.workingMemorySchema ?? null;
871
- const messages = storage
872
- ? await storage.getMessages(run.threadId, {
873
- lastN: memoryConfig?.lastMessages ?? 20,
874
- })
875
- : [];
876
- const workingMemoryJsonSchema = workingMemorySchemaName
877
- ? pikkuState(packageName, 'misc', 'schemas').get(workingMemorySchemaName)
878
- : undefined;
879
- const contextMessages = await loadContextMessages(memoryConfig, storage, { message: '', threadId: run.threadId, resourceId: run.resourceId }, workingMemoryJsonSchema);
880
- const allMessages = [...contextMessages, ...messages];
881
- const trimmedMessages = trimMessages(allMessages);
882
- const instructions = await buildInstructions(resolvedName, packageName);
883
- const aiMiddlewares = [
884
- ...getWorkingMemoryMiddleware(memoryConfig, storage, {
885
- threadId: run.threadId,
886
- workingMemorySchemaName,
887
- logger: singletonServices.logger,
888
- schemaService: singletonServices.schema,
889
- }),
890
- ...(agent.aiMiddleware ?? []),
891
- ];
892
- // One bag per run, shared by every middleware — see PikkuAIMiddlewareHooks.
893
- const sharedNotes = {};
894
- const { messages: modifiedMessages, instructions: modifiedInstructions } = await applyInputMiddleware(aiMiddlewares, singletonServices, { messages: trimmedMessages, instructions: instructions }, sharedNotes);
895
- // knowledge: decisions/internals/a-resumed-agent-turn-is-as-interruptible-as-the-first.md
896
- const interruptHandle = registerInterruptibleRun(run.runId);
897
- warnUnstreamedOutputHooks(run.agentName, aiMiddlewares, singletonServices.logger);
898
- const streamMiddleware = aiMiddlewares
899
- .filter((mw) => mw.modifyOutputStream)
900
- .map((mw) => {
901
- const state = {};
902
- const allEvents = [];
903
- return async (services, event, next) => {
904
- allEvents.push(event);
905
- const result = await mw.modifyOutputStream(services, {
906
- event,
907
- allEvents,
908
- state,
909
- shared: sharedNotes,
910
- // Sends downstream directly, so a hook can hand back the fast event
911
- // now and push the slow one when it is ready.
912
- emit: next,
913
- signal: interruptHandle.signal,
914
- });
915
- if (result == null)
916
- return;
917
- if (Array.isArray(result)) {
918
- for (const r of result)
919
- await next(r);
920
- }
921
- else {
922
- await next(result);
923
- }
924
- };
925
- });
926
- const allChannelMiddleware = combineChannelMiddleware('agent', `stream:${run.agentName}`, {
927
- wireInheritedChannelMiddleware: meta?.channelMiddleware,
928
- wireChannelMiddleware: [
929
- ...(agent.channelMiddleware ?? []),
930
- ...streamMiddleware,
931
- ],
932
- });
933
- const persistingChannel = createPersistingChannel(channel, storage, run.threadId, singletonServices.logger);
934
- const wrappedChannel = allChannelMiddleware.length > 0
935
- ? wrapChannelWithMiddleware({ channel: persistingChannel }, singletonServices, allChannelMiddleware).channel
936
- : persistingChannel;
937
- const streamContext = { channel, options };
938
- const resumeTools = (await buildToolDefs(params, new Map(), run.resourceId, resolvedName, packageName, streamContext, aiMiddlewares)).tools;
939
- const resolved = resolveModelConfig(resolvedName, agent);
940
- const maxSteps = resolved.maxSteps ?? 10;
941
- const runnerParams = {
942
- model: resolved.model,
943
- temperature: resolved.temperature,
944
- instructions: modifiedInstructions,
945
- messages: modifiedMessages,
946
- tools: resumeTools,
947
- maxSteps: 1,
948
- toolChoice: (agent.toolChoice ?? 'auto'),
949
- providerOptions: agent.providerOptions,
950
- outputSchema: meta?.outputSchema
951
- ? pikkuState(packageName, 'misc', 'schemas').get(meta.outputSchema)
952
- : undefined,
953
- };
954
- runnerParams.abortSignal = interruptHandle.signal;
955
- runnerParams.tools = trackToolExecution(runnerParams.tools, interruptHandle);
956
- try {
957
- const loopResult = await runStreamStepLoop({
958
- agent,
959
- runnerParams,
960
- maxSteps,
961
- agentRunner,
962
- streamChannel: wrappedChannel,
963
- persistingChannel,
964
- channel,
965
- aiMiddlewares,
966
- });
967
- if (loopResult.outcome === 'approval') {
968
- await handleApprovals(loopResult.approvals, run.runId, channel, aiRunState, persistingChannel);
969
- return;
970
- }
971
- if (loopResult.outcome === 'credential') {
972
- await handleCredentialRequests(loopResult.credentialRequests, run.runId, channel, aiRunState, persistingChannel);
973
- return;
974
- }
975
- await postStreamCleanup(persistingChannel, aiRunState, run.runId, {
976
- ...run,
977
- input: lastUserMessageText(runnerParams.messages),
978
- });
979
- // knowledge: decisions/internals/the-agent-done-event-goes-through-the-middleware-and-is-awaited.md
980
- await wrappedChannel.send({ type: 'done' });
981
- channel.close();
982
- }
983
- catch (err) {
984
- // Same reasoning as the first turn: an interrupt is not a failure, and the
985
- // part of the reply the user already heard is real output.
986
- const interruption = interruptHandle.interruption;
987
- if (interruption || isAbortError(err)) {
988
- await persistingChannel.flush({ interrupted: true });
989
- await aiRunState.updateRun(run.runId, { status: 'interrupted' });
990
- if (storage) {
991
- trackInterruptNote(run.threadId, persistOrphanedToolResults(interruptHandle, storage, run.threadId));
992
- }
993
- channel.send({
994
- type: 'interrupted',
995
- runId: run.runId,
996
- text: persistingChannel.fullText,
997
- reason: interruption?.reason ?? 'user',
998
- });
999
- channel.send({ type: 'done' });
1000
- channel.close();
1001
- return;
1002
- }
1003
- for (const mw of aiMiddlewares) {
1004
- if (mw.onError) {
1005
- try {
1006
- await mw.onError(singletonServices, {
1007
- error: err instanceof Error ? err : new Error(String(err)),
1008
- stepNumber: -1,
1009
- messages: runnerParams.messages,
1010
- });
1011
- }
1012
- catch { }
1013
- }
1014
- }
1015
- await aiRunState.updateRun(run.runId, {
1016
- status: 'failed',
1017
- errorMessage: err instanceof Error ? err.message : String(err),
1018
- });
1019
- channel.send({
1020
- type: 'error',
1021
- message: err instanceof Error ? err.message : String(err),
1022
- });
1023
- channel.send({ type: 'done' });
1024
- channel.close();
1025
- }
1026
- finally {
1027
- interruptHandle.release();
1028
- }
1029
- }