@pikku/core 0.12.79 → 0.12.82

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (479) hide show
  1. package/CHANGELOG.md +473 -0
  2. package/dist/dev/hot-reload.js +1 -6
  3. package/dist/ecosystem.d.ts +27 -0
  4. package/dist/ecosystem.js +26 -0
  5. package/dist/errors/error-handler.js +4 -2
  6. package/dist/errors/index.d.ts +1 -1
  7. package/dist/errors/index.js +1 -1
  8. package/dist/function/function-runner.js +22 -47
  9. package/dist/function/functions.types.d.ts +9 -5
  10. package/dist/function/index.d.ts +1 -1
  11. package/dist/index.d.ts +14 -19
  12. package/dist/index.js +6 -11
  13. package/dist/middleware/auth-cookie.d.ts +0 -4
  14. package/dist/middleware/auth-cookie.js +38 -2
  15. package/dist/middleware/cors.js +1 -0
  16. package/dist/middleware/index.d.ts +1 -1
  17. package/dist/middleware/index.js +1 -1
  18. package/dist/middleware/remote-auth.js +14 -3
  19. package/dist/permissions.d.ts +8 -10
  20. package/dist/permissions.js +0 -11
  21. package/dist/pikku-state.js +16 -5
  22. package/dist/schema.js +35 -1
  23. package/dist/services/ai-agent-runner-service.d.ts +7 -0
  24. package/dist/services/ai-run-state-service.d.ts +17 -1
  25. package/dist/services/in-memory-ai-run-state-service.d.ts +6 -2
  26. package/dist/services/in-memory-ai-run-state-service.js +11 -1
  27. package/dist/services/in-memory-workflow-service.js +2 -0
  28. package/dist/services/index.d.ts +15 -15
  29. package/dist/services/index.js +5 -5
  30. package/dist/services/local-content-request-handler.d.ts +21 -0
  31. package/dist/services/local-content-request-handler.js +72 -53
  32. package/dist/services/local-content.d.ts +6 -0
  33. package/dist/services/local-content.js +14 -1
  34. package/dist/services/scoped-credential-service.d.ts +21 -0
  35. package/dist/services/scoped-credential-service.js +53 -0
  36. package/dist/services/workflow-service.d.ts +4 -2
  37. package/dist/testing/service-tests/agent-run-service-tests.d.ts +10 -0
  38. package/dist/testing/service-tests/agent-run-service-tests.js +72 -0
  39. package/dist/testing/service-tests/ai-storage-service-tests.d.ts +3 -0
  40. package/dist/testing/service-tests/ai-storage-service-tests.js +302 -0
  41. package/dist/testing/service-tests/channel-store-tests.d.ts +3 -0
  42. package/dist/testing/service-tests/channel-store-tests.js +72 -0
  43. package/dist/testing/service-tests/credential-service-tests.d.ts +3 -0
  44. package/dist/testing/service-tests/credential-service-tests.js +109 -0
  45. package/dist/testing/service-tests/deployment-service-tests.d.ts +3 -0
  46. package/dist/testing/service-tests/deployment-service-tests.js +21 -0
  47. package/dist/testing/service-tests/event-hub-store-tests.d.ts +3 -0
  48. package/dist/testing/service-tests/event-hub-store-tests.js +34 -0
  49. package/dist/testing/service-tests/secret-service-tests.d.ts +3 -0
  50. package/dist/testing/service-tests/secret-service-tests.js +80 -0
  51. package/dist/testing/service-tests/session-store-tests.d.ts +3 -0
  52. package/dist/testing/service-tests/session-store-tests.js +43 -0
  53. package/dist/testing/service-tests/workflow-run-service-tests.d.ts +3 -0
  54. package/dist/testing/service-tests/workflow-run-service-tests.js +42 -0
  55. package/dist/testing/service-tests/workflow-service-tests.d.ts +3 -0
  56. package/dist/testing/service-tests/workflow-service-tests.js +150 -0
  57. package/dist/testing/service-tests.d.ts +6 -0
  58. package/dist/testing/service-tests.js +26 -791
  59. package/dist/types/core.types.d.ts +11 -3
  60. package/dist/types/state.types.d.ts +14 -1
  61. package/dist/wirings/actor-flow/index.d.ts +1 -1
  62. package/dist/wirings/actor-flow/run-conversation.js +1 -6
  63. package/dist/wirings/ai-agent/agent-rpc.d.ts +15 -0
  64. package/dist/wirings/ai-agent/agent-rpc.js +53 -0
  65. package/dist/wirings/ai-agent/ai-agent-agui.js +1 -5
  66. package/dist/wirings/ai-agent/ai-agent-finalize.d.ts +58 -0
  67. package/dist/wirings/ai-agent/ai-agent-finalize.js +138 -0
  68. package/dist/wirings/ai-agent/ai-agent-interrupt.js +1 -0
  69. package/dist/wirings/ai-agent/ai-agent-memory.d.ts +2 -8
  70. package/dist/wirings/ai-agent/ai-agent-memory.js +37 -18
  71. package/dist/wirings/ai-agent/ai-agent-model-config.d.ts +7 -0
  72. package/dist/wirings/ai-agent/ai-agent-model-config.js +44 -1
  73. package/dist/wirings/ai-agent/ai-agent-prepare.js +5 -9
  74. package/dist/wirings/ai-agent/ai-agent-runner.js +89 -147
  75. package/dist/wirings/ai-agent/ai-agent-stream.js +109 -97
  76. package/dist/wirings/ai-agent/ai-agent-turn.d.ts +57 -0
  77. package/dist/wirings/ai-agent/ai-agent-turn.js +82 -0
  78. package/dist/wirings/ai-agent/ai-agent.types.d.ts +47 -2
  79. package/dist/wirings/ai-agent/index.d.ts +8 -7
  80. package/dist/wirings/ai-agent/index.js +5 -4
  81. package/dist/wirings/ai-agent/voice-input.js +1 -6
  82. package/dist/wirings/ai-agent/voice-output.js +2 -12
  83. package/dist/wirings/ai-scorer/ai-scorer-grade.d.ts +26 -0
  84. package/dist/wirings/ai-scorer/ai-scorer-grade.js +33 -0
  85. package/dist/wirings/ai-scorer/ai-scorer-judge.d.ts +17 -0
  86. package/dist/wirings/ai-scorer/ai-scorer-judge.js +92 -0
  87. package/dist/wirings/ai-scorer/ai-scorer-live.d.ts +15 -0
  88. package/dist/wirings/ai-scorer/ai-scorer-live.js +38 -0
  89. package/dist/wirings/ai-scorer/ai-scorer-registry.d.ts +18 -0
  90. package/dist/wirings/ai-scorer/ai-scorer-registry.js +46 -0
  91. package/dist/wirings/ai-scorer/ai-scorer-sampling.d.ts +8 -0
  92. package/dist/wirings/ai-scorer/ai-scorer-sampling.js +31 -0
  93. package/dist/wirings/ai-scorer/ai-scorer-snapshots.d.ts +10 -0
  94. package/dist/wirings/ai-scorer/ai-scorer-snapshots.js +40 -0
  95. package/dist/wirings/ai-scorer/ai-scorer-worker.d.ts +15 -0
  96. package/dist/wirings/ai-scorer/ai-scorer-worker.js +58 -0
  97. package/dist/wirings/ai-scorer/ai-scorer.d.ts +39 -0
  98. package/dist/wirings/ai-scorer/ai-scorer.js +40 -0
  99. package/dist/wirings/ai-scorer/ai-scorer.types.d.ts +90 -0
  100. package/dist/wirings/ai-scorer/ai-scorer.types.js +4 -0
  101. package/dist/wirings/ai-scorer/index.d.ts +6 -0
  102. package/dist/wirings/ai-scorer/index.js +5 -0
  103. package/dist/wirings/channel/channel-common.js +1 -0
  104. package/dist/wirings/channel/channel-handler.js +3 -5
  105. package/dist/wirings/channel/channel-rpc-service.d.ts +0 -6
  106. package/dist/wirings/channel/channel-rpc-service.js +0 -8
  107. package/dist/wirings/channel/channel-rpc.types.d.ts +6 -0
  108. package/dist/wirings/channel/channel-rpc.types.js +8 -0
  109. package/dist/wirings/channel/channel-runner.d.ts +1 -3
  110. package/dist/wirings/channel/channel-runner.js +16 -8
  111. package/dist/wirings/channel/channel.types.d.ts +2 -0
  112. package/dist/wirings/channel/index.d.ts +5 -6
  113. package/dist/wirings/channel/index.js +3 -4
  114. package/dist/wirings/channel/local/local-channel-runner.js +8 -1
  115. package/dist/wirings/channel/pikku-abstract-channel-handler.js +1 -0
  116. package/dist/wirings/channel/serverless/serverless-channel-runner.js +3 -0
  117. package/dist/wirings/cli/channel/cli-channel-runner.js +2 -0
  118. package/dist/wirings/cli/channel/cli-raw-channel-runner.js +9 -1
  119. package/dist/wirings/cli/channel/index.d.ts +1 -2
  120. package/dist/wirings/cli/channel/index.js +0 -1
  121. package/dist/wirings/cli/cli-runner.js +17 -3
  122. package/dist/wirings/cli/cli.types.d.ts +0 -8
  123. package/dist/wirings/cli/command-parser.js +13 -0
  124. package/dist/wirings/credential/index.d.ts +1 -1
  125. package/dist/wirings/gateway/gateway-runner.js +9 -2
  126. package/dist/wirings/gateway/index.d.ts +1 -1
  127. package/dist/wirings/http/http-routes.js +2 -0
  128. package/dist/wirings/http/http-runner.d.ts +0 -10
  129. package/dist/wirings/http/http-runner.js +14 -15
  130. package/dist/wirings/http/http.types.d.ts +0 -10
  131. package/dist/wirings/http/index.d.ts +2 -3
  132. package/dist/wirings/http/index.js +1 -1
  133. package/dist/wirings/mcp/index.d.ts +1 -1
  134. package/dist/wirings/mcp/mcp-runner.d.ts +15 -7
  135. package/dist/wirings/mcp/mcp-runner.js +18 -11
  136. package/dist/wirings/persona/index.d.ts +3 -4
  137. package/dist/wirings/persona/index.js +2 -3
  138. package/dist/wirings/queue/index.d.ts +1 -3
  139. package/dist/wirings/queue/index.js +1 -3
  140. package/dist/wirings/rpc/addon-runner.d.ts +4 -0
  141. package/dist/wirings/rpc/addon-runner.js +19 -3
  142. package/dist/wirings/rpc/rpc-runner.d.ts +8 -51
  143. package/dist/wirings/rpc/rpc-runner.js +55 -86
  144. package/dist/wirings/rpc/rpc-types.d.ts +7 -0
  145. package/dist/wirings/rpc/wire-addon.d.ts +13 -0
  146. package/dist/wirings/rpc/wire-addon.js +4 -0
  147. package/dist/wirings/scheduler/index.d.ts +1 -1
  148. package/dist/wirings/secret/validate-secret-definitions.js +2 -2
  149. package/dist/wirings/trigger/index.d.ts +1 -1
  150. package/dist/wirings/trigger/pikku-trigger-service.d.ts +0 -4
  151. package/dist/wirings/trigger/trigger-runner.js +1 -0
  152. package/dist/wirings/virtual-user/index.d.ts +5 -6
  153. package/dist/wirings/virtual-user/index.js +2 -4
  154. package/dist/wirings/virtual-user/run-virtual-user.js +11 -11
  155. package/dist/wirings/virtual-user/virtual-user-derive.js +4 -25
  156. package/dist/wirings/virtual-user/virtual-user-dispositions.js +1 -4
  157. package/dist/wirings/workflow/dsl/workflow-dsl.types.d.ts +100 -3
  158. package/dist/wirings/workflow/feature.js +7 -4
  159. package/dist/wirings/workflow/graph/graph-runner.d.ts +1 -2
  160. package/dist/wirings/workflow/graph/graph-runner.js +8 -7
  161. package/dist/wirings/workflow/graph/workflow-graph.types.d.ts +0 -4
  162. package/dist/wirings/workflow/index.d.ts +12 -7
  163. package/dist/wirings/workflow/index.js +7 -3
  164. package/dist/wirings/workflow/pikku-scenario-service.d.ts +17 -3
  165. package/dist/wirings/workflow/pikku-scenario-service.js +64 -10
  166. package/dist/wirings/workflow/pikku-workflow-service.d.ts +28 -147
  167. package/dist/wirings/workflow/pikku-workflow-service.js +85 -493
  168. package/dist/wirings/workflow/scenario-step.types.d.ts +8 -0
  169. package/dist/wirings/workflow/workflow-approval-audit.d.ts +16 -0
  170. package/dist/wirings/workflow/workflow-approval-audit.js +40 -0
  171. package/dist/wirings/workflow/workflow-approval-policy.d.ts +20 -0
  172. package/dist/wirings/workflow/workflow-approval-policy.js +48 -0
  173. package/dist/wirings/workflow/workflow-approval.d.ts +67 -0
  174. package/dist/wirings/workflow/workflow-approval.js +177 -0
  175. package/dist/wirings/workflow/workflow-constants.d.ts +26 -0
  176. package/dist/wirings/workflow/workflow-constants.js +35 -0
  177. package/dist/wirings/workflow/workflow-errors.d.ts +58 -0
  178. package/dist/wirings/workflow/workflow-errors.js +112 -0
  179. package/dist/wirings/workflow/workflow-meta-resolver.d.ts +11 -0
  180. package/dist/wirings/workflow/workflow-meta-resolver.js +31 -0
  181. package/dist/wirings/workflow/workflow-queue-routing.d.ts +8 -0
  182. package/dist/wirings/workflow/workflow-queue-routing.js +38 -0
  183. package/dist/wirings/workflow/workflow-queue-wiring.d.ts +20 -0
  184. package/dist/wirings/workflow/workflow-queue-wiring.js +79 -0
  185. package/dist/wirings/workflow/workflow-recovery.d.ts +68 -0
  186. package/dist/wirings/workflow/workflow-recovery.js +101 -0
  187. package/dist/wirings/workflow/workflow-run-engine.types.d.ts +54 -0
  188. package/dist/wirings/workflow/workflow-run-ownership.d.ts +17 -0
  189. package/dist/wirings/workflow/workflow-run-ownership.js +30 -0
  190. package/dist/wirings/workflow/workflow-suspend.d.ts +12 -0
  191. package/dist/wirings/workflow/workflow-suspend.js +33 -0
  192. package/dist/wirings/workflow/workflow.types.d.ts +8 -1
  193. package/knowledge/decisions/internals/a-non-streaming-agent-run-registers-with-airunstate-too.md +22 -0
  194. package/knowledge/decisions/internals/a-resumed-agent-turn-is-as-interruptible-as-the-first.md +20 -0
  195. package/knowledge/decisions/internals/a-scenario-step-template-is-offered-unfilled.md +21 -0
  196. package/knowledge/decisions/internals/a-virtual-user-decides-whether-to-trust-memory-once-per-turn.md +21 -0
  197. package/knowledge/decisions/internals/a-wall-clock-threshold-is-a-load-test-in-disguise.md +46 -0
  198. package/knowledge/decisions/internals/a-workflow-wire-is-built-from-the-run-not-from-the-rpc-service.md +45 -0
  199. package/knowledge/decisions/internals/addon-pikku-meta-ships-at-the-package-root-or-under-dist.md +32 -0
  200. package/knowledge/decisions/internals/agent-context-waits-for-a-tool-result-still-being-written.md +21 -0
  201. package/knowledge/decisions/internals/agent-speech-travels-as-a-custom-agui-event.md +22 -0
  202. package/knowledge/decisions/internals/an-addon-scope-root-loses-to-a-root-the-host-already-declares.md +39 -0
  203. package/knowledge/decisions/internals/an-agent-interrupt-is-not-a-failure.md +23 -0
  204. package/knowledge/decisions/internals/an-agent-run-owned-by-another-instance-says-so.md +25 -0
  205. package/knowledge/decisions/internals/an-agent-stream-send-must-return-the-inner-sends-promise.md +22 -0
  206. package/knowledge/decisions/internals/an-empty-text-part-is-omitted-from-an-agent-message.md +20 -0
  207. package/knowledge/decisions/internals/an-empty-transcript-is-not-recorded.md +22 -0
  208. package/knowledge/decisions/internals/an-unref-d-timer-cannot-be-awaited-under-node-test.md +66 -0
  209. package/knowledge/decisions/internals/channel-state-accessors-are-unsound-generics-that-every-implementation-asserts.md +33 -0
  210. package/knowledge/decisions/internals/gateway-listener-middleware-runs-without-an-rpc-on-the-wire.md +43 -0
  211. package/knowledge/decisions/internals/hot-reload-writes-into-the-function-map-captured-at-startup.md +26 -0
  212. package/knowledge/decisions/internals/index.md +42 -3
  213. package/knowledge/decisions/internals/only-exposed-functions-enter-a-virtual-user-catalogue.md +21 -0
  214. package/knowledge/decisions/internals/scenario-given-and-when-are-sugar-but-then-is-not.md +24 -0
  215. package/knowledge/decisions/internals/side-effects-are-an-allowlist-not-a-boolean.md +34 -0
  216. package/knowledge/decisions/internals/speech-synthesis-picks-a-voice-per-sentence-and-warns-once.md +24 -0
  217. package/knowledge/decisions/internals/the-actor-prompt-says-json-because-of-json-object-mode.md +22 -0
  218. package/knowledge/decisions/internals/the-agent-done-event-goes-through-the-middleware-and-is-awaited.md +26 -0
  219. package/knowledge/decisions/internals/the-api-report-pins-members-not-just-names.md +43 -0
  220. package/knowledge/decisions/internals/the-ecosystem-entry-point-carries-the-adapter-surface.md +58 -0
  221. package/knowledge/decisions/internals/the-middleware-resolution-cache-is-deliberately-unbounded.md +40 -0
  222. package/knowledge/decisions/internals/the-per-invocation-rpc-view-is-a-class.md +40 -0
  223. package/knowledge/decisions/internals/the-persona-runtime-is-exported-from-the-persona-entry-point.md +28 -0
  224. package/knowledge/decisions/internals/the-transcript-event-is-sent-ahead-of-the-run.md +25 -0
  225. package/knowledge/decisions/internals/the-virtual-user-catalogue-is-the-only-gate-on-what-may-be-called.md +21 -0
  226. package/knowledge/decisions/internals/the-worker-disposition-is-the-one-that-is-not-testing.md +22 -0
  227. package/knowledge/decisions/internals/thread-history-records-the-transcript-not-the-audio.md +25 -0
  228. package/knowledge/decisions/internals/validate-runs-checks-by-precondition.md +115 -0
  229. package/knowledge/decisions/internals/virtual-user-step-order-comes-from-insertion-order.md +21 -0
  230. package/knowledge/decisions/internals/voice-output-speaks-unless-voice-input-explicitly-says-otherwise.md +24 -0
  231. package/knowledge/decisions/internals/wiring-registries-erase-the-generics-their-wire-functions-capture.md +35 -0
  232. package/knowledge/decisions/security/a-dropped-audit-write-is-always-logged.md +4 -2
  233. package/knowledge/decisions/security/a-function-never-receives-the-secret-service.md +37 -0
  234. package/knowledge/decisions/security/a-graph-run-starts-at-an-entry-node-the-graph-declared.md +33 -0
  235. package/knowledge/decisions/security/a-permission-gets-a-wire-it-cannot-reply-on.md +33 -0
  236. package/knowledge/decisions/security/a-step-runs-the-function-the-workflow-dispatched-it-with.md +37 -0
  237. package/knowledge/decisions/security/a-virtual-user-is-never-offered-a-step-that-would-forge-its-own-oracle.md +28 -0
  238. package/knowledge/decisions/security/a-workflow-run-is-read-and-approved-by-its-owner.md +50 -0
  239. package/knowledge/decisions/security/an-agent-approval-is-claimed-before-the-tool-runs.md +33 -0
  240. package/knowledge/decisions/security/an-approval-answer-outlives-the-run-it-answered.md +59 -0
  241. package/knowledge/decisions/security/an-upload-is-counted-as-it-arrives-not-buffered-then-measured.md +24 -0
  242. package/knowledge/decisions/security/index.md +9 -0
  243. package/knowledge/questions/channel-middleware-accepts-bare-factories-that-nothing-resolves.md +35 -0
  244. package/knowledge/questions/index.md +2 -1
  245. package/knowledge/questions/unauthorized-channel-replies-escape-the-declared-out-type.md +44 -0
  246. package/package.json +15 -3
  247. package/scripts/generate-api-report.d.mts +1 -0
  248. package/scripts/generate-api-report.mjs +89 -0
  249. package/scripts/generate-api-report.mts +343 -0
  250. package/src/api-report.test.ts +69 -0
  251. package/src/column-form.ts +1 -4
  252. package/src/crypto-utils.test.ts +15 -2
  253. package/src/dev/hot-reload.ts +2 -7
  254. package/src/ecosystem.ts +40 -0
  255. package/src/errors/error-handler.ts +5 -3
  256. package/src/errors/error.test.ts +4 -1
  257. package/src/errors/index.ts +1 -1
  258. package/src/function/function-runner.test.ts +53 -1
  259. package/src/function/function-runner.ts +33 -48
  260. package/src/function/functions.types.ts +15 -9
  261. package/src/function/index.ts +0 -2
  262. package/src/handle-error.ts +1 -1
  263. package/src/index.ts +3 -57
  264. package/src/middleware/auth-cookie.test.ts +50 -0
  265. package/src/middleware/auth-cookie.ts +38 -2
  266. package/src/middleware/cors.ts +2 -1
  267. package/src/middleware/index.ts +1 -1
  268. package/src/middleware/remote-auth.test.ts +43 -0
  269. package/src/middleware/remote-auth.ts +17 -3
  270. package/src/middleware-runner.ts +4 -2
  271. package/src/no-any-casts.test.ts +57 -0
  272. package/src/permissions.test.ts +3 -1
  273. package/src/permissions.ts +22 -24
  274. package/src/pikku-state.ts +22 -6
  275. package/src/public-surface.json +554 -0
  276. package/src/public-surface.json.README +25 -0
  277. package/src/public-surface.test.ts +105 -0
  278. package/src/removed-legacy-exports.test.ts +62 -0
  279. package/src/schema.test.ts +78 -0
  280. package/src/schema.ts +36 -1
  281. package/src/services/ai-agent-runner-service.ts +12 -1
  282. package/src/services/ai-run-state-service.ts +18 -1
  283. package/src/services/audit-service.ts +2 -2
  284. package/src/services/in-memory-ai-run-state-service.ts +16 -2
  285. package/src/services/in-memory-workflow-service.ts +2 -0
  286. package/src/services/index.ts +4 -47
  287. package/src/services/local-content-request-handler.test.ts +43 -5
  288. package/src/services/local-content-request-handler.ts +103 -74
  289. package/src/services/local-content.ts +15 -1
  290. package/src/services/local-email-service.ts +5 -1
  291. package/src/services/scoped-credential-service.test.ts +86 -0
  292. package/src/services/scoped-credential-service.ts +63 -0
  293. package/src/services/system-role-guard.test.ts +4 -1
  294. package/src/services/workflow-service.ts +9 -2
  295. package/src/side-effects-are-declared.test.ts +84 -0
  296. package/src/source-files-stay-composable.test.ts +41 -0
  297. package/src/testing/service-tests/agent-run-service-tests.ts +98 -0
  298. package/src/testing/service-tests/ai-storage-service-tests.ts +379 -0
  299. package/src/testing/service-tests/channel-store-tests.ts +98 -0
  300. package/src/testing/service-tests/credential-service-tests.ts +143 -0
  301. package/src/testing/service-tests/deployment-service-tests.ts +33 -0
  302. package/src/testing/service-tests/event-hub-store-tests.ts +48 -0
  303. package/src/testing/service-tests/secret-service-tests.ts +105 -0
  304. package/src/testing/service-tests/session-store-tests.ts +62 -0
  305. package/src/testing/service-tests/workflow-run-service-tests.ts +59 -0
  306. package/src/testing/service-tests/workflow-service-tests.ts +308 -0
  307. package/src/testing/service-tests.ts +31 -1111
  308. package/src/types/core.types.ts +14 -3
  309. package/src/types/state.types.ts +17 -1
  310. package/src/wirings/actor-flow/index.ts +0 -3
  311. package/src/wirings/actor-flow/run-conversation.ts +1 -6
  312. package/src/wirings/ai-agent/agent-rpc.ts +120 -0
  313. package/src/wirings/ai-agent/ai-agent-agui.ts +1 -5
  314. package/src/wirings/ai-agent/ai-agent-finalize.test.ts +186 -0
  315. package/src/wirings/ai-agent/ai-agent-finalize.ts +197 -0
  316. package/src/wirings/ai-agent/ai-agent-interrupt.test.ts +2 -1
  317. package/src/wirings/ai-agent/ai-agent-interrupt.ts +1 -0
  318. package/src/wirings/ai-agent/ai-agent-memory.ts +62 -39
  319. package/src/wirings/ai-agent/ai-agent-model-config.test.ts +72 -3
  320. package/src/wirings/ai-agent/ai-agent-model-config.ts +49 -1
  321. package/src/wirings/ai-agent/ai-agent-prepare.ts +12 -12
  322. package/src/wirings/ai-agent/ai-agent-resume-authorization.test.ts +1 -0
  323. package/src/wirings/ai-agent/ai-agent-runner.test.ts +82 -2
  324. package/src/wirings/ai-agent/ai-agent-runner.ts +120 -160
  325. package/src/wirings/ai-agent/ai-agent-stream-output-hooks.test.ts +353 -0
  326. package/src/wirings/ai-agent/ai-agent-stream.test.ts +2 -0
  327. package/src/wirings/ai-agent/ai-agent-stream.ts +152 -117
  328. package/src/wirings/ai-agent/ai-agent-thread-ownership.test.ts +1 -1
  329. package/src/wirings/ai-agent/ai-agent-turn.test.ts +67 -0
  330. package/src/wirings/ai-agent/ai-agent-turn.ts +122 -0
  331. package/src/wirings/ai-agent/ai-agent.types.ts +65 -5
  332. package/src/wirings/ai-agent/index.ts +2 -16
  333. package/src/wirings/ai-agent/voice-input.ts +1 -6
  334. package/src/wirings/ai-agent/voice-output.ts +2 -12
  335. package/src/wirings/ai-scorer/ai-scorer-grade.test.ts +106 -0
  336. package/src/wirings/ai-scorer/ai-scorer-grade.ts +55 -0
  337. package/src/wirings/ai-scorer/ai-scorer-judge.test.ts +143 -0
  338. package/src/wirings/ai-scorer/ai-scorer-judge.ts +120 -0
  339. package/src/wirings/ai-scorer/ai-scorer-live.test.ts +174 -0
  340. package/src/wirings/ai-scorer/ai-scorer-live.ts +56 -0
  341. package/src/wirings/ai-scorer/ai-scorer-registry.ts +63 -0
  342. package/src/wirings/ai-scorer/ai-scorer-sampling.test.ts +34 -0
  343. package/src/wirings/ai-scorer/ai-scorer-sampling.ts +36 -0
  344. package/src/wirings/ai-scorer/ai-scorer-snapshots.test.ts +49 -0
  345. package/src/wirings/ai-scorer/ai-scorer-snapshots.ts +46 -0
  346. package/src/wirings/ai-scorer/ai-scorer-worker.test.ts +122 -0
  347. package/src/wirings/ai-scorer/ai-scorer-worker.ts +69 -0
  348. package/src/wirings/ai-scorer/ai-scorer.ts +76 -0
  349. package/src/wirings/ai-scorer/ai-scorer.types.ts +107 -0
  350. package/src/wirings/ai-scorer/index.ts +24 -0
  351. package/src/wirings/channel/channel-common.ts +5 -2
  352. package/src/wirings/channel/channel-handler-shapes.test.ts +72 -0
  353. package/src/wirings/channel/channel-handler.ts +7 -7
  354. package/src/wirings/channel/channel-rpc-service.ts +0 -14
  355. package/src/wirings/channel/channel-rpc.test.ts +25 -4
  356. package/src/wirings/channel/channel-rpc.types.ts +14 -0
  357. package/src/wirings/channel/channel-runner.ts +33 -20
  358. package/src/wirings/channel/channel.types.ts +2 -0
  359. package/src/wirings/channel/index.ts +1 -20
  360. package/src/wirings/channel/local/local-channel-runner.test.ts +68 -0
  361. package/src/wirings/channel/local/local-channel-runner.ts +9 -2
  362. package/src/wirings/channel/pikku-abstract-channel-handler.ts +2 -1
  363. package/src/wirings/channel/serverless/serverless-channel-runner.ts +6 -3
  364. package/src/wirings/cli/channel/cli-channel-runner.ts +3 -1
  365. package/src/wirings/cli/channel/cli-raw-channel-runner.test.ts +23 -0
  366. package/src/wirings/cli/channel/cli-raw-channel-runner.ts +12 -1
  367. package/src/wirings/cli/channel/cli-raw-client-runner.ts +4 -1
  368. package/src/wirings/cli/channel/index.ts +0 -7
  369. package/src/wirings/cli/cli-runner.test.ts +68 -0
  370. package/src/wirings/cli/cli-runner.ts +25 -5
  371. package/src/wirings/cli/cli.types.ts +0 -39
  372. package/src/wirings/cli/command-parser.test.ts +19 -0
  373. package/src/wirings/cli/command-parser.ts +17 -1
  374. package/src/wirings/credential/index.ts +0 -1
  375. package/src/wirings/gateway/gateway-channel-meta.test.ts +44 -0
  376. package/src/wirings/gateway/gateway-runner.ts +27 -19
  377. package/src/wirings/gateway/index.ts +0 -3
  378. package/src/wirings/http/http-routes.ts +4 -1
  379. package/src/wirings/http/http-runner.test.ts +66 -0
  380. package/src/wirings/http/http-runner.ts +21 -28
  381. package/src/wirings/http/http.types.ts +0 -15
  382. package/src/wirings/http/index.ts +2 -8
  383. package/src/wirings/http/pikku-fetch-http-request.ts +2 -2
  384. package/src/wirings/http/web-request.ts +1 -1
  385. package/src/wirings/mcp/index.ts +0 -1
  386. package/src/wirings/mcp/mcp-runner.test.ts +181 -0
  387. package/src/wirings/mcp/mcp-runner.ts +37 -21
  388. package/src/wirings/persona/index.ts +0 -8
  389. package/src/wirings/persona/persona-environments.test.ts +14 -3
  390. package/src/wirings/persona/persona.test.ts +13 -3
  391. package/src/wirings/persona/validate-personas.ts +5 -1
  392. package/src/wirings/queue/index.ts +0 -14
  393. package/src/wirings/queue/queue-runner.ts +1 -1
  394. package/src/wirings/rpc/addon-auth-tags.test.ts +1 -5
  395. package/src/wirings/rpc/addon-runner.ts +34 -3
  396. package/src/wirings/rpc/addon-secrets.test.ts +261 -0
  397. package/src/wirings/rpc/rpc-runner.test.ts +2 -0
  398. package/src/wirings/rpc/rpc-runner.ts +60 -136
  399. package/src/wirings/rpc/rpc-types.ts +11 -0
  400. package/src/wirings/rpc/wire-addon.ts +20 -1
  401. package/src/wirings/scheduler/index.ts +0 -1
  402. package/src/wirings/secret/validate-secret-definitions.test.ts +22 -0
  403. package/src/wirings/secret/validate-secret-definitions.ts +2 -2
  404. package/src/wirings/trigger/index.ts +0 -1
  405. package/src/wirings/trigger/pikku-trigger-service.ts +0 -5
  406. package/src/wirings/trigger/trigger-runner.ts +7 -5
  407. package/src/wirings/virtual-user/index.ts +0 -16
  408. package/src/wirings/virtual-user/run-virtual-user.test.ts +18 -9
  409. package/src/wirings/virtual-user/run-virtual-user.ts +28 -15
  410. package/src/wirings/virtual-user/virtual-user-agents.test.ts +4 -1
  411. package/src/wirings/virtual-user/virtual-user-derive.ts +4 -25
  412. package/src/wirings/virtual-user/virtual-user-dispositions.test.ts +7 -2
  413. package/src/wirings/virtual-user/virtual-user-dispositions.ts +1 -4
  414. package/src/wirings/virtual-user/virtual-user-intents.test.ts +13 -3
  415. package/src/wirings/workflow/dsl/workflow-dsl.types.ts +117 -4
  416. package/src/wirings/workflow/feature.ts +7 -4
  417. package/src/wirings/workflow/graph/graph-node.ts +1 -1
  418. package/src/wirings/workflow/graph/graph-runner.test.ts +76 -2
  419. package/src/wirings/workflow/graph/graph-runner.ts +13 -16
  420. package/src/wirings/workflow/graph/workflow-graph.types.ts +0 -5
  421. package/src/wirings/workflow/index.ts +14 -24
  422. package/src/wirings/workflow/pikku-scenario-service.ts +101 -27
  423. package/src/wirings/workflow/pikku-workflow-service.test.ts +15 -14
  424. package/src/wirings/workflow/pikku-workflow-service.ts +214 -743
  425. package/src/wirings/workflow/scenario-expectations.test.ts +75 -0
  426. package/src/wirings/workflow/scenario-hooks.test.ts +52 -0
  427. package/src/wirings/workflow/scenario-step.types.ts +8 -0
  428. package/src/wirings/workflow/workflow-approval-audit.ts +47 -0
  429. package/src/wirings/workflow/workflow-approval-policy.test.ts +524 -0
  430. package/src/wirings/workflow/workflow-approval-policy.ts +68 -0
  431. package/src/wirings/workflow/workflow-approval.ts +289 -0
  432. package/src/wirings/workflow/workflow-child-run-session.test.ts +89 -0
  433. package/src/wirings/workflow/workflow-constants.ts +47 -0
  434. package/src/wirings/workflow/workflow-dispatch-durability.test.ts +1 -1
  435. package/src/wirings/workflow/workflow-errors.ts +128 -0
  436. package/src/wirings/workflow/workflow-inline-authority.test.ts +12 -6
  437. package/src/wirings/workflow/workflow-meta-resolver.ts +45 -0
  438. package/src/wirings/workflow/workflow-missing-meta.test.ts +92 -0
  439. package/src/wirings/workflow/workflow-queue-routing.ts +85 -0
  440. package/src/wirings/workflow/workflow-queue-wiring.ts +149 -0
  441. package/src/wirings/workflow/workflow-recovery.ts +162 -0
  442. package/src/wirings/workflow/workflow-retry-policy.test.ts +1 -1
  443. package/src/wirings/workflow/workflow-run-authority.test.ts +212 -0
  444. package/src/wirings/workflow/workflow-run-engine.types.ts +91 -0
  445. package/src/wirings/workflow/workflow-run-ownership.ts +37 -0
  446. package/src/wirings/workflow/workflow-suspend.ts +61 -0
  447. package/src/wirings/workflow/workflow.types.ts +7 -9
  448. package/src/wirings-stay-decoupled.test.ts +127 -0
  449. package/tsconfig.json +1 -1
  450. package/tsconfig.tsbuildinfo +1 -1
  451. package/dist/internal.d.ts +0 -3
  452. package/dist/internal.js +0 -2
  453. package/dist/middleware/timeout.d.ts +0 -9
  454. package/dist/middleware/timeout.js +0 -15
  455. package/dist/pikku-response.d.ts +0 -6
  456. package/dist/pikku-response.js +0 -6
  457. package/dist/services/gopass-secrets.d.ts +0 -15
  458. package/dist/services/gopass-secrets.js +0 -76
  459. package/dist/services/http-scenario-actors.d.ts +0 -75
  460. package/dist/services/http-scenario-actors.js +0 -195
  461. package/dist/services/http-user-flow-actors.d.ts +0 -67
  462. package/dist/services/http-user-flow-actors.js +0 -193
  463. package/dist/services/scenario-actors-service.d.ts +0 -127
  464. package/dist/services/scenario-actors-service.js +0 -40
  465. package/dist/services/user-flow-actors-service.d.ts +0 -39
  466. package/dist/wirings/credential/wire-credential.d.ts +0 -48
  467. package/dist/wirings/credential/wire-credential.js +0 -47
  468. package/dist/wirings/oauth2/oauth2-client.d.ts +0 -47
  469. package/dist/wirings/oauth2/oauth2-client.js +0 -263
  470. package/dist/wirings/oauth2/oauth2-routes.d.ts +0 -35
  471. package/dist/wirings/oauth2/oauth2-routes.js +0 -146
  472. package/dist/wirings/scope/wire-scope.d.ts +0 -33
  473. package/dist/wirings/scope/wire-scope.js +0 -32
  474. package/dist/wirings/workflow/dsl/index.d.ts +0 -5
  475. package/dist/wirings/workflow/dsl/index.js +0 -4
  476. package/dist/wirings/workflow/graph/index.d.ts +0 -5
  477. package/dist/wirings/workflow/graph/index.js +0 -4
  478. package/src/internal.ts +0 -10
  479. /package/dist/{services/user-flow-actors-service.js → wirings/workflow/workflow-run-engine.types.js} +0 -0
@@ -43,6 +43,17 @@ import {
43
43
  trackToolExecution,
44
44
  } from './ai-agent-interrupt.js'
45
45
  import { pikkuState, getSingletonServices } from '../../pikku-state.js'
46
+ import {
47
+ applyInputMiddleware,
48
+ describeApprovals,
49
+ notifyAfterStep,
50
+ toAccumulatedStep,
51
+ } from './ai-agent-turn.js'
52
+ import {
53
+ applyOutputMiddleware,
54
+ finalizeAgentRun,
55
+ lastUserMessageText,
56
+ } from './ai-agent-finalize.js'
46
57
  import { resolveModelConfig } from './ai-agent-model-config.js'
47
58
  import { AIProviderNotConfiguredError } from '../../errors/errors.js'
48
59
  import { randomUUID } from './ai-agent-utils.js'
@@ -147,26 +158,20 @@ export async function runAIAgent(
147
158
  // One bag per run, shared by every middleware — see PikkuAIMiddlewareHooks.
148
159
  const sharedNotes: Record<string, unknown> = {}
149
160
 
150
- let modifiedMessages = runnerParams.messages
151
- let modifiedInstructions = runnerParams.instructions
152
- for (const mw of aiMiddlewares) {
153
- if (mw.modifyInput) {
154
- const result = await mw.modifyInput(singletonServices, {
155
- messages: modifiedMessages,
156
- instructions: modifiedInstructions,
157
- shared: sharedNotes,
158
- })
159
- modifiedMessages = result.messages
160
- modifiedInstructions = result.instructions
161
- }
162
- }
161
+ const { messages: modifiedMessages, instructions: modifiedInstructions } =
162
+ await applyInputMiddleware(
163
+ aiMiddlewares,
164
+ singletonServices,
165
+ {
166
+ messages: runnerParams.messages,
167
+ instructions: runnerParams.instructions,
168
+ },
169
+ sharedNotes
170
+ )
163
171
  runnerParams.messages = modifiedMessages
164
172
  runnerParams.instructions = modifiedInstructions
165
173
 
166
- // History records what the model was asked, which for a spoken turn is the
167
- // transcript rather than the base64 audio that arrived — see the same note on
168
- // the streaming path. Identity-checked, because a middleware may legitimately
169
- // replace the message list with something unrelated to this turn.
174
+ // knowledge: decisions/internals/thread-history-records-the-transcript-not-the-audio.md
170
175
  const lastModified = modifiedMessages[modifiedMessages.length - 1]
171
176
  const persistedUserMessage =
172
177
  lastModified?.id === userMessage.id ? lastModified : userMessage
@@ -181,10 +186,7 @@ export async function runAIAgent(
181
186
  updatedAt: new Date(),
182
187
  })
183
188
 
184
- // Registered on the same terms as `streamAIAgent`. A run created here is
185
- // visible to `interruptAIAgent` through `aiRunState` either way, so skipping
186
- // this would leave that call finding the run, passing the ownership check and
187
- // then failing to stop it — reported as if it were running on another host.
189
+ // knowledge: decisions/internals/a-non-streaming-agent-run-registers-with-airunstate-too.md
188
190
  const interruptHandle = registerInterruptibleRun(runId)
189
191
  runnerParams.abortSignal = interruptHandle.signal
190
192
  runnerParams.tools = trackToolExecution(runnerParams.tools, interruptHandle)
@@ -216,52 +218,15 @@ export async function runAIAgent(
216
218
  totalUsage.inputTokens += stepResult.usage.inputTokens
217
219
  totalUsage.outputTokens += stepResult.usage.outputTokens
218
220
 
219
- for (const mw of aiMiddlewares) {
220
- if (mw.afterStep) {
221
- await mw.afterStep(singletonServices, {
222
- stepNumber: step,
223
- text: stepResult.text,
224
- toolCalls: stepResult.toolCalls,
225
- toolResults: stepResult.toolResults,
226
- usage: stepResult.usage,
227
- finishReason: stepResult.finishReason,
228
- })
229
- }
230
- }
221
+ await notifyAfterStep(aiMiddlewares, singletonServices, step, stepResult)
231
222
 
232
- accumulatedSteps.push({
233
- usage: stepResult.usage,
234
- toolCalls: stepResult.toolCalls.map((tc) => {
235
- const tr = stepResult.toolResults.find(
236
- (r) => r.toolCallId === tc.toolCallId
237
- )
238
- return {
239
- name: tc.toolName,
240
- args: tc.args as Record<string, unknown>,
241
- result:
242
- typeof tr?.result === 'string'
243
- ? tr.result
244
- : JSON.stringify(tr?.result ?? ''),
245
- }
246
- }),
247
- })
223
+ accumulatedSteps.push(toAccumulatedStep(stepResult))
248
224
 
249
225
  if (stepResult.toolCalls.length === 0) break
250
226
 
251
227
  const approvalsNeeded = checkForApprovals(stepResult, runnerParams.tools)
252
228
  if (approvalsNeeded.length > 0) {
253
- for (const approval of approvalsNeeded) {
254
- const toolDef = runnerParams.tools.find(
255
- (t) => t.name === approval.toolName
256
- )
257
- if (toolDef?.approvalDescriptionFn && !approval.reason) {
258
- try {
259
- approval.reason = await toolDef.approvalDescriptionFn(
260
- approval.args
261
- )
262
- } catch {}
263
- }
264
- }
229
+ await describeApprovals(approvalsNeeded, runnerParams.tools)
265
230
 
266
231
  const pendingApprovals = approvalsNeeded.map((a) =>
267
232
  a.agentRunId
@@ -343,20 +308,13 @@ export async function runAIAgent(
343
308
  steps: accumulatedSteps,
344
309
  }
345
310
 
346
- let outputText = finalText
347
- let outputMessages = runnerParams.messages
348
- for (let i = aiMiddlewares.length - 1; i >= 0; i--) {
349
- const mw = aiMiddlewares[i]
350
- if (mw.modifyOutput) {
351
- const modResult = await mw.modifyOutput(singletonServices, {
352
- text: outputText,
353
- messages: outputMessages,
354
- usage: totalUsage,
355
- })
356
- outputText = modResult.text
357
- outputMessages = modResult.messages
358
- }
359
- }
311
+ const { text: outputText, steps: outputSteps } =
312
+ await applyOutputMiddleware(aiMiddlewares, singletonServices, {
313
+ text: finalText,
314
+ messages: runnerParams.messages,
315
+ steps: result.steps,
316
+ usage: totalUsage,
317
+ })
360
318
 
361
319
  await saveMessages(
362
320
  storage,
@@ -365,14 +323,20 @@ export async function runAIAgent(
365
323
  memoryConfig,
366
324
  persistedUserMessage,
367
325
  {
368
- ...result,
369
326
  text: outputText,
327
+ steps: outputSteps,
370
328
  uiSpec: structuredOutput.uiSpec,
371
329
  }
372
330
  )
373
331
 
374
- await aiRunState.updateRun(runId, {
375
- status: 'completed',
332
+ await finalizeAgentRun(aiRunState, {
333
+ runId,
334
+ agentName,
335
+ threadId,
336
+ resourceId: input.resourceId,
337
+ input: lastUserMessageText(runnerParams.messages),
338
+ text: outputText,
339
+ steps: outputSteps,
376
340
  usage: { ...totalUsage, model: agent.model },
377
341
  })
378
342
 
@@ -381,14 +345,11 @@ export async function runAIAgent(
381
345
  text: outputText,
382
346
  object: finalObject,
383
347
  threadId,
384
- steps: accumulatedSteps,
348
+ steps: outputSteps,
385
349
  usage: totalUsage,
386
350
  }
387
351
  } catch (error) {
388
- // An interrupt is not a failure, so it skips the `onError` hooks and never
389
- // becomes an `errorMessage`. Unlike the streaming path there is no partial
390
- // reply to hand back — nothing was delivered — so the caller gets a typed
391
- // throw it can tell apart from a provider outage instead of a result.
352
+ // knowledge: decisions/internals/an-agent-interrupt-is-not-a-failure.md
392
353
  const interruption = interruptHandle.interruption
393
354
  if (interruption || isAbortError(error)) {
394
355
  await aiRunState.updateRun(runId, { status: 'interrupted' })
@@ -470,11 +431,24 @@ export async function resumeAIAgentSync(
470
431
 
471
432
  const savedPendingApprovals = [...(run.pendingApprovals ?? [])]
472
433
 
434
+ // The read above is not a claim — concurrent resumes all see the same pending
435
+ // list. `resolveApproval` is the claim, and only the caller it returns true
436
+ // for may run the tool.
437
+ const claimedIds = new Set<string>()
473
438
  for (const { toolCallId, approved } of approvals) {
474
- await aiRunState.resolveApproval(
439
+ const claimed = await aiRunState.resolveApproval(
475
440
  toolCallId,
476
441
  approved ? 'approved' : 'denied'
477
442
  )
443
+ if (claimed) {
444
+ claimedIds.add(toolCallId)
445
+ }
446
+ }
447
+
448
+ if (approvals.length > 0 && claimedIds.size === 0) {
449
+ throw new Error(
450
+ `Approvals for run ${runId} were already resolved by another caller`
451
+ )
478
452
  }
479
453
 
480
454
  const { tools } = await buildToolDefs(
@@ -492,13 +466,17 @@ export async function resumeAIAgentSync(
492
466
  toolName: string
493
467
  args: any
494
468
  result: string
469
+ error?: string
495
470
  }[] = []
496
471
 
497
472
  for (const pending of savedPendingApprovals) {
498
473
  if (pending.type !== 'tool-call') continue
499
474
 
500
475
  const toolCallId = pending.toolCallId
476
+ if (!claimedIds.has(toolCallId)) continue
477
+
501
478
  let resultStr: string
479
+ let toolError: string | undefined
502
480
 
503
481
  if (rejectedIds.has(toolCallId)) {
504
482
  resultStr =
@@ -525,7 +503,8 @@ export async function resumeAIAgentSync(
525
503
  if (err?.payload?.error === 'missing_credential') {
526
504
  resultStr = JSON.stringify(err.payload)
527
505
  } else {
528
- resultStr = `Error: ${err instanceof Error ? err.message : String(err)}`
506
+ toolError = err instanceof Error ? err.message : String(err)
507
+ resultStr = `Error: ${toolError}`
529
508
  }
530
509
  }
531
510
  } else {
@@ -540,6 +519,7 @@ export async function resumeAIAgentSync(
540
519
  ? JSON.parse(pending.args)
541
520
  : pending.args,
542
521
  result: resultStr,
522
+ ...(toolError ? { error: toolError } : {}),
543
523
  })
544
524
  }
545
525
 
@@ -569,7 +549,21 @@ export async function resumeAIAgentSync(
569
549
  memoryConfig,
570
550
  agentRunner,
571
551
  params,
572
- aiRunState
552
+ aiRunState,
553
+ // The approved tools were executed here, before the model was re-entered,
554
+ // so they belong to the run's step record — otherwise a tool that failed
555
+ // after approval leaves no trace on the run at all.
556
+ toolCallMessages.length > 0
557
+ ? {
558
+ usage: { inputTokens: 0, outputTokens: 0 },
559
+ toolCalls: toolCallMessages.map((tc) => ({
560
+ name: tc.toolName,
561
+ args: (tc.args ?? {}) as Record<string, unknown>,
562
+ result: tc.result,
563
+ ...(tc.error ? { error: tc.error } : {}),
564
+ })),
565
+ }
566
+ : undefined
573
567
  )
574
568
  }
575
569
 
@@ -582,7 +576,8 @@ async function continueAfterToolResultSync(
582
576
  memoryConfig: AIAgentMemoryConfig | undefined,
583
577
  agentRunner: AIAgentRunnerService,
584
578
  params: RunAIAgentParams,
585
- aiRunState: AIRunStateService
579
+ aiRunState: AIRunStateService,
580
+ resumedToolStep?: AIAgentStep
586
581
  ): Promise<AIAgentOutput> {
587
582
  const singletonServices = getSingletonServices()
588
583
  const agentsMeta = pikkuState(packageName, 'agent', 'agentsMeta')
@@ -623,19 +618,13 @@ async function continueAfterToolResultSync(
623
618
  // One bag per run, shared by every middleware — see PikkuAIMiddlewareHooks.
624
619
  const sharedNotes: Record<string, unknown> = {}
625
620
 
626
- let modifiedMessages = trimmedMessages
627
- let modifiedInstructions = instructions
628
- for (const mw of aiMiddlewares) {
629
- if (mw.modifyInput) {
630
- const result = await mw.modifyInput(singletonServices, {
631
- messages: modifiedMessages,
632
- instructions: modifiedInstructions,
633
- shared: sharedNotes,
634
- })
635
- modifiedMessages = result.messages
636
- modifiedInstructions = result.instructions
637
- }
638
- }
621
+ const { messages: modifiedMessages, instructions: modifiedInstructions } =
622
+ await applyInputMiddleware(
623
+ aiMiddlewares,
624
+ singletonServices,
625
+ { messages: trimmedMessages, instructions: instructions },
626
+ sharedNotes
627
+ )
639
628
 
640
629
  const { tools: resumeTools } = await buildToolDefs(
641
630
  params,
@@ -673,6 +662,13 @@ async function continueAfterToolResultSync(
673
662
  runnerParams.tools = trackToolExecution(runnerParams.tools, interruptHandle)
674
663
 
675
664
  try {
665
+ // Kept out of `accumulatedSteps` deliberately: that array drives
666
+ // `saveMessages`, and the approved tool's messages were already written to
667
+ // the thread before the model was re-entered. It belongs to the run's step
668
+ // record, not to persistence.
669
+ const withResumedStep = (steps: AIAgentStep[]): AIAgentStep[] =>
670
+ resumedToolStep ? [resumedToolStep, ...steps] : steps
671
+
676
672
  const accumulatedSteps: AIAgentStep[] = []
677
673
  const totalUsage = { inputTokens: 0, outputTokens: 0 }
678
674
  let lastStepResult: AIAgentStepResult | null = null
@@ -684,52 +680,15 @@ async function continueAfterToolResultSync(
684
680
  totalUsage.inputTokens += stepResult.usage.inputTokens
685
681
  totalUsage.outputTokens += stepResult.usage.outputTokens
686
682
 
687
- for (const mw of aiMiddlewares) {
688
- if (mw.afterStep) {
689
- await mw.afterStep(singletonServices, {
690
- stepNumber: step,
691
- text: stepResult.text,
692
- toolCalls: stepResult.toolCalls,
693
- toolResults: stepResult.toolResults,
694
- usage: stepResult.usage,
695
- finishReason: stepResult.finishReason,
696
- })
697
- }
698
- }
683
+ await notifyAfterStep(aiMiddlewares, singletonServices, step, stepResult)
699
684
 
700
- accumulatedSteps.push({
701
- usage: stepResult.usage,
702
- toolCalls: stepResult.toolCalls.map((tc) => {
703
- const tr = stepResult.toolResults.find(
704
- (r) => r.toolCallId === tc.toolCallId
705
- )
706
- return {
707
- name: tc.toolName,
708
- args: tc.args as Record<string, unknown>,
709
- result:
710
- typeof tr?.result === 'string'
711
- ? tr.result
712
- : JSON.stringify(tr?.result ?? ''),
713
- }
714
- }),
715
- })
685
+ accumulatedSteps.push(toAccumulatedStep(stepResult))
716
686
 
717
687
  if (stepResult.toolCalls.length === 0) break
718
688
 
719
689
  const approvalsNeeded = checkForApprovals(stepResult, runnerParams.tools)
720
690
  if (approvalsNeeded.length > 0) {
721
- for (const approval of approvalsNeeded) {
722
- const toolDef = runnerParams.tools.find(
723
- (t) => t.name === approval.toolName
724
- )
725
- if (toolDef?.approvalDescriptionFn && !approval.reason) {
726
- try {
727
- approval.reason = await toolDef.approvalDescriptionFn(
728
- approval.args
729
- )
730
- } catch {}
731
- }
732
- }
691
+ await describeApprovals(approvalsNeeded, runnerParams.tools)
733
692
 
734
693
  const pendingApprovals = approvalsNeeded.map((a) =>
735
694
  a.agentRunId
@@ -788,7 +747,7 @@ async function continueAfterToolResultSync(
788
747
  runId: run.runId,
789
748
  text: suspendedText,
790
749
  threadId: run.threadId,
791
- steps: accumulatedSteps,
750
+ steps: withResumedStep(accumulatedSteps),
792
751
  usage: totalUsage,
793
752
  status: 'suspended',
794
753
  pendingApprovals: approvalsNeeded.map((a) => ({
@@ -812,35 +771,36 @@ async function continueAfterToolResultSync(
812
771
  steps: accumulatedSteps,
813
772
  }
814
773
 
815
- let outputText = finalText
816
- let outputMessages = runnerParams.messages
817
- for (let i = aiMiddlewares.length - 1; i >= 0; i--) {
818
- const mw = aiMiddlewares[i]
819
- if (mw.modifyOutput) {
820
- const modResult = await mw.modifyOutput(singletonServices, {
821
- text: outputText,
822
- messages: outputMessages,
823
- usage: totalUsage,
824
- })
825
- outputText = modResult.text
826
- outputMessages = modResult.messages
827
- }
828
- }
774
+ const { text: outputText, steps: outputSteps } =
775
+ await applyOutputMiddleware(aiMiddlewares, singletonServices, {
776
+ text: finalText,
777
+ messages: runnerParams.messages,
778
+ steps: withResumedStep(result.steps),
779
+ usage: totalUsage,
780
+ })
829
781
 
830
782
  await saveMessages(
831
783
  storage,
832
784
  run.threadId,
833
785
  run.resourceId,
834
786
  memoryConfig,
835
- null as any,
787
+ null,
836
788
  {
837
- ...result,
838
789
  text: outputText,
790
+ // The approved tool's messages were written before the model was
791
+ // re-entered, so only the steps this leg generated are persisted here.
792
+ steps: accumulatedSteps,
839
793
  }
840
794
  )
841
795
 
842
- await aiRunState.updateRun(run.runId, {
843
- status: 'completed',
796
+ await finalizeAgentRun(aiRunState, {
797
+ runId: run.runId,
798
+ agentName: resolvedName,
799
+ threadId: run.threadId,
800
+ resourceId: run.resourceId,
801
+ input: lastUserMessageText(runnerParams.messages),
802
+ text: outputText,
803
+ steps: outputSteps,
844
804
  usage: { ...totalUsage, model: agent.model },
845
805
  })
846
806
 
@@ -849,7 +809,7 @@ async function continueAfterToolResultSync(
849
809
  text: outputText,
850
810
  object: finalObject,
851
811
  threadId: run.threadId,
852
- steps: accumulatedSteps,
812
+ steps: outputSteps,
853
813
  usage: totalUsage,
854
814
  }
855
815
  } catch (error) {