@pikku/core 0.12.83 → 0.12.84

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 (343) hide show
  1. package/CHANGELOG.md +68 -0
  2. package/README.md +1 -1
  3. package/dist/ecosystem/agent-scorer.d.ts +12 -0
  4. package/dist/ecosystem/agent-scorer.js +5 -0
  5. package/dist/ecosystem/agent.d.ts +18 -0
  6. package/dist/ecosystem/agent.js +6 -0
  7. package/dist/ecosystem/ai-agent.d.ts +18 -0
  8. package/dist/ecosystem/ai-agent.js +6 -0
  9. package/dist/ecosystem/ai-scorer.d.ts +12 -0
  10. package/dist/ecosystem/ai-scorer.js +5 -0
  11. package/dist/ecosystem/channel/local.d.ts +1 -0
  12. package/dist/ecosystem/channel/local.js +1 -0
  13. package/dist/ecosystem/channel.d.ts +12 -0
  14. package/dist/ecosystem/channel.js +4 -0
  15. package/dist/ecosystem/cli/channel.d.ts +16 -0
  16. package/dist/ecosystem/cli/channel.js +2 -0
  17. package/dist/ecosystem/cli.d.ts +9 -0
  18. package/dist/ecosystem/cli.js +2 -0
  19. package/dist/ecosystem/credential.d.ts +9 -0
  20. package/dist/ecosystem/credential.js +1 -0
  21. package/dist/ecosystem/dev.d.ts +8 -0
  22. package/dist/ecosystem/dev.js +1 -0
  23. package/dist/ecosystem/function.d.ts +13 -0
  24. package/dist/ecosystem/function.js +2 -0
  25. package/dist/ecosystem/gateway.d.ts +1 -0
  26. package/dist/ecosystem/gateway.js +1 -0
  27. package/dist/ecosystem/hmac.d.ts +1 -0
  28. package/dist/ecosystem/hmac.js +1 -0
  29. package/dist/ecosystem/http.d.ts +11 -0
  30. package/dist/ecosystem/http.js +3 -0
  31. package/dist/ecosystem/mcp.d.ts +3 -0
  32. package/dist/ecosystem/mcp.js +2 -0
  33. package/dist/ecosystem/middleware.d.ts +17 -0
  34. package/dist/ecosystem/middleware.js +6 -0
  35. package/dist/ecosystem/node-host-resolver.d.ts +1 -0
  36. package/dist/ecosystem/node-host-resolver.js +1 -0
  37. package/dist/ecosystem/node.d.ts +1 -0
  38. package/dist/ecosystem/node.js +1 -0
  39. package/dist/ecosystem/oauth2.d.ts +1 -0
  40. package/dist/ecosystem/oauth2.js +1 -0
  41. package/dist/ecosystem/persona.d.ts +15 -0
  42. package/dist/ecosystem/persona.js +4 -0
  43. package/dist/ecosystem/queue.d.ts +11 -0
  44. package/dist/ecosystem/queue.js +2 -0
  45. package/dist/ecosystem/remote.d.ts +8 -0
  46. package/dist/ecosystem/remote.js +1 -0
  47. package/dist/ecosystem/role.d.ts +3 -0
  48. package/dist/ecosystem/role.js +2 -0
  49. package/dist/ecosystem/rpc.d.ts +4 -0
  50. package/dist/ecosystem/rpc.js +2 -0
  51. package/dist/ecosystem/safe-fetch.d.ts +7 -0
  52. package/dist/ecosystem/safe-fetch.js +1 -0
  53. package/dist/ecosystem/scenario.d.ts +15 -0
  54. package/dist/ecosystem/scenario.js +4 -0
  55. package/dist/ecosystem/scheduler.d.ts +2 -0
  56. package/dist/ecosystem/scheduler.js +1 -0
  57. package/dist/ecosystem/schema.d.ts +1 -0
  58. package/dist/ecosystem/schema.js +1 -0
  59. package/dist/ecosystem/scope.d.ts +3 -0
  60. package/dist/ecosystem/scope.js +2 -0
  61. package/dist/ecosystem/secret.d.ts +9 -0
  62. package/dist/ecosystem/secret.js +1 -0
  63. package/dist/ecosystem/services/istanbul-coverage.d.ts +1 -0
  64. package/dist/ecosystem/services/istanbul-coverage.js +1 -0
  65. package/dist/ecosystem/services/local-content-request-handler.d.ts +9 -0
  66. package/dist/ecosystem/services/local-content-request-handler.js +1 -0
  67. package/dist/ecosystem/services/local-content.d.ts +1 -0
  68. package/dist/ecosystem/services/local-content.js +1 -0
  69. package/dist/ecosystem/services/v8-coverage.d.ts +1 -0
  70. package/dist/ecosystem/services/v8-coverage.js +1 -0
  71. package/dist/ecosystem/services.d.ts +28 -0
  72. package/dist/ecosystem/services.js +6 -0
  73. package/dist/ecosystem/testing.d.ts +7 -0
  74. package/dist/ecosystem/testing.js +1 -0
  75. package/dist/ecosystem/trigger.d.ts +1 -0
  76. package/dist/ecosystem/trigger.js +1 -0
  77. package/dist/ecosystem/types.d.ts +9 -0
  78. package/dist/ecosystem/types.js +5 -0
  79. package/dist/ecosystem/variable.d.ts +8 -0
  80. package/dist/ecosystem/variable.js +1 -0
  81. package/dist/ecosystem/virtual-user.d.ts +21 -0
  82. package/dist/ecosystem/virtual-user.js +5 -0
  83. package/dist/ecosystem/workflow.d.ts +18 -0
  84. package/dist/ecosystem/workflow.js +4 -0
  85. package/dist/index.d.ts +8 -8
  86. package/dist/index.js +3 -3
  87. package/dist/middleware/auth-apikey.d.ts +1 -1
  88. package/dist/middleware/auth-bearer.d.ts +1 -1
  89. package/dist/middleware/auth-cookie.d.ts +1 -1
  90. package/dist/middleware/cors.d.ts +1 -1
  91. package/dist/middleware/remote-auth.d.ts +1 -1
  92. package/dist/middleware/telemetry.d.ts +2 -2
  93. package/dist/services/agent-run-state-service.d.ts +29 -0
  94. package/dist/services/agent-run-state-service.js +1 -0
  95. package/dist/services/agent-runner-service.d.ts +220 -0
  96. package/dist/services/agent-runner-service.js +1 -0
  97. package/dist/services/agent-storage-service.d.ts +18 -0
  98. package/dist/services/agent-storage-service.js +1 -0
  99. package/dist/services/http-personas.d.ts +1 -1
  100. package/dist/services/http-personas.js +3 -3
  101. package/dist/services/in-memory-agent-run-state-service.d.ts +19 -0
  102. package/dist/services/in-memory-agent-run-state-service.js +54 -0
  103. package/dist/services/index.d.ts +4 -4
  104. package/dist/services/index.js +1 -1
  105. package/dist/services/meta-service.d.ts +3 -3
  106. package/dist/testing/service-tests/agent-run-service-tests.d.ts +1 -1
  107. package/dist/testing/service-tests/agent-run-service-tests.js +3 -3
  108. package/dist/testing/service-tests/agent-storage-service-tests.d.ts +3 -0
  109. package/dist/testing/service-tests/agent-storage-service-tests.js +302 -0
  110. package/dist/testing/service-tests.d.ts +4 -4
  111. package/dist/testing/service-tests.js +4 -4
  112. package/dist/types/core.types.d.ts +10 -10
  113. package/dist/types/core.types.js +1 -1
  114. package/dist/types/state.types.d.ts +5 -5
  115. package/dist/wirings/actor-flow/run-conversation.d.ts +2 -2
  116. package/dist/wirings/agent/agent-agui.d.ts +79 -0
  117. package/dist/wirings/agent/agent-agui.js +327 -0
  118. package/dist/wirings/agent/agent-finalize.d.ts +58 -0
  119. package/dist/wirings/agent/agent-finalize.js +138 -0
  120. package/dist/wirings/agent/agent-helpers.d.ts +35 -0
  121. package/dist/wirings/agent/agent-helpers.js +47 -0
  122. package/dist/wirings/agent/agent-interrupt.d.ts +153 -0
  123. package/dist/wirings/agent/agent-interrupt.js +257 -0
  124. package/dist/wirings/agent/agent-memory.d.ts +42 -0
  125. package/dist/wirings/agent/agent-memory.js +333 -0
  126. package/dist/wirings/agent/agent-model-config.d.ts +16 -0
  127. package/dist/wirings/agent/agent-model-config.js +51 -0
  128. package/dist/wirings/agent/agent-prepare.d.ts +117 -0
  129. package/dist/wirings/agent/agent-prepare.js +728 -0
  130. package/dist/wirings/agent/agent-registry.d.ts +16 -0
  131. package/dist/wirings/agent/agent-registry.js +51 -0
  132. package/dist/wirings/agent/agent-rpc.d.ts +15 -0
  133. package/dist/wirings/agent/agent-rpc.js +53 -0
  134. package/dist/wirings/agent/agent-runner.d.ts +7 -0
  135. package/dist/wirings/agent/agent-runner.js +580 -0
  136. package/dist/wirings/agent/agent-stream.d.ts +41 -0
  137. package/dist/wirings/agent/agent-stream.js +1029 -0
  138. package/dist/wirings/agent/agent-turn.d.ts +57 -0
  139. package/dist/wirings/agent/agent-turn.js +82 -0
  140. package/dist/wirings/agent/agent-utils.d.ts +1 -0
  141. package/dist/wirings/agent/agent-utils.js +1 -0
  142. package/dist/wirings/agent/agent.types.d.ts +625 -0
  143. package/dist/wirings/agent/agent.types.js +1 -0
  144. package/dist/wirings/agent/index.d.ts +12 -0
  145. package/dist/wirings/agent/index.js +10 -0
  146. package/dist/wirings/agent/voice-input.d.ts +69 -0
  147. package/dist/wirings/agent/voice-input.js +150 -0
  148. package/dist/wirings/agent/voice-output.d.ts +85 -0
  149. package/dist/wirings/agent/voice-output.js +196 -0
  150. package/dist/wirings/agent-scorer/agent-scorer-grade.d.ts +26 -0
  151. package/dist/wirings/agent-scorer/agent-scorer-grade.js +33 -0
  152. package/dist/wirings/agent-scorer/agent-scorer-judge.d.ts +17 -0
  153. package/dist/wirings/agent-scorer/agent-scorer-judge.js +92 -0
  154. package/dist/wirings/agent-scorer/agent-scorer-live.d.ts +15 -0
  155. package/dist/wirings/agent-scorer/agent-scorer-live.js +38 -0
  156. package/dist/wirings/agent-scorer/agent-scorer-registry.d.ts +18 -0
  157. package/dist/wirings/agent-scorer/agent-scorer-registry.js +46 -0
  158. package/dist/wirings/agent-scorer/agent-scorer-sampling.d.ts +8 -0
  159. package/dist/wirings/agent-scorer/agent-scorer-sampling.js +31 -0
  160. package/dist/wirings/agent-scorer/agent-scorer-snapshots.d.ts +10 -0
  161. package/dist/wirings/agent-scorer/agent-scorer-snapshots.js +40 -0
  162. package/dist/wirings/agent-scorer/agent-scorer-worker.d.ts +15 -0
  163. package/dist/wirings/agent-scorer/agent-scorer-worker.js +60 -0
  164. package/dist/wirings/agent-scorer/agent-scorer.d.ts +39 -0
  165. package/dist/wirings/agent-scorer/agent-scorer.js +40 -0
  166. package/dist/wirings/agent-scorer/agent-scorer.types.d.ts +90 -0
  167. package/dist/wirings/agent-scorer/agent-scorer.types.js +4 -0
  168. package/dist/wirings/agent-scorer/index.d.ts +6 -0
  169. package/dist/wirings/agent-scorer/index.js +5 -0
  170. package/dist/wirings/ai-agent/voice-output.d.ts +1 -2
  171. package/dist/wirings/channel/channel-rpc.types.d.ts +2 -2
  172. package/dist/wirings/rpc/rpc-runner.d.ts +1 -1
  173. package/dist/wirings/rpc/rpc-runner.js +2 -2
  174. package/dist/wirings/rpc/rpc-types.d.ts +1 -1
  175. package/dist/wirings/virtual-user/prepare-virtual-user-run.js +1 -1
  176. package/dist/wirings/virtual-user/virtual-user-agents.d.ts +1 -1
  177. package/dist/wirings/workflow/run-timeline.d.ts +1 -2
  178. package/knowledge/decisions/internals/{a-non-streaming-agent-run-registers-with-airunstate-too.md → a-non-streaming-agent-run-registers-with-agentrunstate-too.md} +6 -6
  179. package/knowledge/decisions/internals/a-resumed-agent-turn-is-as-interruptible-as-the-first.md +1 -1
  180. package/knowledge/decisions/internals/actor-flow-drives-the-target-through-a-transport-seam.md +1 -1
  181. package/knowledge/decisions/internals/{ai-agent-agui-bridge-obeys-the-client-ordering-contract.md → agent-agui-bridge-obeys-the-client-ordering-contract.md} +5 -5
  182. package/knowledge/decisions/internals/{ai-agent-audio-chunks-carry-the-format-the-provider-returned.md → agent-audio-chunks-carry-the-format-the-provider-returned.md} +2 -2
  183. package/knowledge/decisions/internals/agent-context-waits-for-a-tool-result-still-being-written.md +1 -1
  184. package/knowledge/decisions/internals/{ai-agent-credential-suspensions-hide-the-tool-result.md → agent-credential-suspensions-hide-the-tool-result.md} +2 -2
  185. package/knowledge/decisions/internals/{ai-agent-delegate-and-supervise-hide-different-text.md → agent-delegate-and-supervise-hide-different-text.md} +4 -4
  186. package/knowledge/decisions/internals/{ai-agent-llm-tool-arguments-have-nulls-stripped.md → agent-llm-tool-arguments-have-nulls-stripped.md} +3 -3
  187. package/knowledge/decisions/internals/{ai-agent-model-config-stays-a-single-resolution-seam.md → agent-model-config-stays-a-single-resolution-seam.md} +3 -3
  188. package/knowledge/decisions/internals/{ai-agent-onerror-hooks-cannot-change-the-failure.md → agent-onerror-hooks-cannot-change-the-failure.md} +6 -6
  189. package/knowledge/decisions/internals/{ai-agent-runner-methods-must-keep-their-receiver.md → agent-runner-methods-must-keep-their-receiver.md} +4 -4
  190. package/knowledge/decisions/internals/agent-speech-travels-as-a-custom-agui-event.md +1 -1
  191. package/knowledge/decisions/internals/{ai-agent-stream-persistence-is-best-effort.md → agent-stream-persistence-is-best-effort.md} +4 -4
  192. package/knowledge/decisions/internals/{ai-agent-sub-agents-inherit-the-parent-context-block.md → agent-sub-agents-inherit-the-parent-context-block.md} +3 -3
  193. package/knowledge/decisions/internals/{ai-agent-tool-execute-failures-are-logged-unconditionally.md → agent-tool-execute-failures-are-logged-unconditionally.md} +3 -3
  194. package/knowledge/decisions/internals/{ai-agent-voice-input-transcribes-audio-parts-in-place.md → agent-voice-input-transcribes-audio-parts-in-place.md} +2 -2
  195. package/knowledge/decisions/internals/{ai-agent-working-memory-is-persisted-only-when-valid.md → agent-working-memory-is-persisted-only-when-valid.md} +2 -2
  196. package/knowledge/decisions/internals/an-agent-interrupt-is-not-a-failure.md +2 -2
  197. package/knowledge/decisions/internals/an-agent-run-owned-by-another-instance-says-so.md +3 -3
  198. package/knowledge/decisions/internals/an-agent-stream-send-must-return-the-inner-sends-promise.md +2 -2
  199. package/knowledge/decisions/internals/an-empty-text-part-is-omitted-from-an-agent-message.md +1 -1
  200. package/knowledge/decisions/internals/an-empty-transcript-is-not-recorded.md +1 -1
  201. package/knowledge/decisions/internals/speech-synthesis-picks-a-voice-per-sentence-and-warns-once.md +1 -1
  202. package/knowledge/decisions/internals/the-agent-done-event-goes-through-the-middleware-and-is-awaited.md +1 -1
  203. package/knowledge/decisions/internals/the-embedding-model-is-pinned-per-service-and-doc-query-embedding-is-split.md +1 -1
  204. package/knowledge/decisions/internals/the-per-invocation-rpc-view-is-a-class.md +5 -5
  205. package/knowledge/decisions/internals/the-transcript-event-is-sent-ahead-of-the-run.md +2 -2
  206. package/knowledge/decisions/internals/thread-history-records-the-transcript-not-the-audio.md +2 -2
  207. package/knowledge/decisions/internals/voice-output-speaks-unless-voice-input-explicitly-says-otherwise.md +1 -1
  208. package/knowledge/decisions/security/{ai-agent-approval-forwarding-requires-a-symbol-brand.md → agent-approval-forwarding-requires-a-symbol-brand.md} +3 -3
  209. package/knowledge/decisions/security/{ai-agent-credential-requests-are-symbol-branded.md → agent-credential-requests-are-symbol-branded.md} +4 -4
  210. package/knowledge/decisions/security/{ai-agent-gate-requires-a-session-only-when-auth-is-true.md → agent-gate-requires-a-session-only-when-auth-is-true.md} +3 -3
  211. package/knowledge/decisions/security/{ai-agent-ownership-failures-never-echo-the-resource.md → agent-ownership-failures-never-echo-the-resource.md} +2 -2
  212. package/knowledge/decisions/security/{ai-agent-resume-re-runs-the-authorization-gate.md → agent-resume-re-runs-the-authorization-gate.md} +4 -4
  213. package/knowledge/decisions/security/{ai-agent-sessionless-deployments-have-no-thread-ownership.md → agent-sessionless-deployments-have-no-thread-ownership.md} +5 -5
  214. package/knowledge/decisions/security/{ai-agent-thread-ownership-composes-the-session-principal.md → agent-thread-ownership-composes-the-session-principal.md} +2 -2
  215. package/knowledge/decisions/security/{ai-agent-tool-filtering-reads-the-live-function-config.md → agent-tool-filtering-reads-the-live-function-config.md} +2 -2
  216. package/knowledge/decisions/security/an-agent-approval-is-claimed-before-the-tool-runs.md +3 -3
  217. package/knowledge/decisions/security/an-empty-owners-constraint-matches-nothing.md +3 -3
  218. package/knowledge/decisions/security/console-addon-privileged-functions-gate-themselves.md +20 -13
  219. package/knowledge/decisions/security/index.md +1 -2
  220. package/package.json +4 -3
  221. package/scripts/generate-api-report.mts +32 -8
  222. package/src/agent-naming.test.ts +68 -0
  223. package/src/ecosystem/agent-scorer.ts +24 -0
  224. package/src/ecosystem/agent.ts +42 -0
  225. package/src/ecosystem/channel/local.ts +1 -0
  226. package/src/ecosystem/channel.ts +22 -0
  227. package/src/ecosystem/cli/channel.ts +25 -0
  228. package/src/ecosystem/cli.ts +21 -0
  229. package/src/ecosystem/credential.ts +10 -0
  230. package/src/ecosystem/dev.ts +12 -0
  231. package/src/ecosystem/function.ts +28 -0
  232. package/src/ecosystem/gateway.ts +4 -0
  233. package/src/ecosystem/hmac.ts +1 -0
  234. package/src/ecosystem/http.ts +22 -0
  235. package/src/ecosystem/mcp.ts +17 -0
  236. package/src/ecosystem/middleware.ts +27 -0
  237. package/src/ecosystem/node-host-resolver.ts +1 -0
  238. package/src/ecosystem/node.ts +5 -0
  239. package/src/ecosystem/oauth2.ts +4 -0
  240. package/src/ecosystem/persona.ts +30 -0
  241. package/src/ecosystem/queue.ts +23 -0
  242. package/src/ecosystem/remote.ts +9 -0
  243. package/src/ecosystem/role.ts +13 -0
  244. package/src/ecosystem/rpc.ts +7 -0
  245. package/src/ecosystem/safe-fetch.ts +8 -0
  246. package/src/ecosystem/scenario.ts +32 -0
  247. package/src/ecosystem/scheduler.ts +5 -0
  248. package/src/ecosystem/schema.ts +1 -0
  249. package/src/ecosystem/scope.ts +14 -0
  250. package/src/ecosystem/secret.ts +15 -0
  251. package/src/ecosystem/services/istanbul-coverage.ts +1 -0
  252. package/src/ecosystem/services/local-content-request-handler.ts +16 -0
  253. package/src/ecosystem/services/local-content.ts +1 -0
  254. package/src/ecosystem/services/v8-coverage.ts +1 -0
  255. package/src/ecosystem/services.ts +82 -0
  256. package/src/ecosystem/testing.ts +8 -0
  257. package/src/ecosystem/trigger.ts +7 -0
  258. package/src/ecosystem/types.ts +34 -0
  259. package/src/ecosystem/variable.ts +14 -0
  260. package/src/ecosystem/virtual-user.ts +48 -0
  261. package/src/ecosystem/workflow.ts +68 -0
  262. package/src/index.ts +8 -19
  263. package/src/pikku-state.ts +3 -3
  264. package/src/public-surface.json +173 -68
  265. package/src/public-surface.json.README +20 -2
  266. package/src/public-surface.test.ts +31 -3
  267. package/src/services/{ai-run-state-service.ts → agent-run-state-service.ts} +5 -5
  268. package/src/services/{ai-agent-runner-service.ts → agent-runner-service.ts} +18 -18
  269. package/src/services/{ai-storage-service.ts → agent-storage-service.ts} +7 -7
  270. package/src/services/http-personas-converse.test.ts +3 -3
  271. package/src/services/http-personas.ts +4 -4
  272. package/src/services/{in-memory-ai-run-state-service.ts → in-memory-agent-run-state-service.ts} +7 -7
  273. package/src/services/index.ts +9 -9
  274. package/src/services/meta-service.ts +3 -3
  275. package/src/testing/service-tests/agent-run-service-tests.ts +4 -4
  276. package/src/testing/service-tests/{ai-storage-service-tests.ts → agent-storage-service-tests.ts} +8 -8
  277. package/src/testing/service-tests.ts +10 -8
  278. package/src/types/core.types.ts +12 -12
  279. package/src/types/state.types.ts +6 -9
  280. package/src/wirings/actor-flow/run-conversation.test.ts +2 -2
  281. package/src/wirings/actor-flow/run-conversation.ts +7 -9
  282. package/src/wirings/{ai-agent/ai-agent-agui.test.ts → agent/agent-agui.test.ts} +126 -105
  283. package/src/wirings/{ai-agent/ai-agent-agui.ts → agent/agent-agui.ts} +7 -7
  284. package/src/wirings/{ai-agent/ai-agent-authorization.test.ts → agent/agent-authorization.test.ts} +6 -6
  285. package/src/wirings/{ai-agent/ai-agent-finalize.test.ts → agent/agent-finalize.test.ts} +17 -16
  286. package/src/wirings/{ai-agent/ai-agent-finalize.ts → agent/agent-finalize.ts} +22 -22
  287. package/src/wirings/{ai-agent/ai-agent-helpers.test.ts → agent/agent-helpers.test.ts} +1 -1
  288. package/src/wirings/{ai-agent/ai-agent-helpers.ts → agent/agent-helpers.ts} +1 -1
  289. package/src/wirings/{ai-agent/ai-agent-interrupt.test.ts → agent/agent-interrupt.test.ts} +67 -77
  290. package/src/wirings/{ai-agent/ai-agent-interrupt.ts → agent/agent-interrupt.ts} +5 -5
  291. package/src/wirings/{ai-agent/ai-agent-memory.test.ts → agent/agent-memory.test.ts} +12 -12
  292. package/src/wirings/{ai-agent/ai-agent-memory.ts → agent/agent-memory.ts} +36 -36
  293. package/src/wirings/{ai-agent/ai-agent-model-config.test.ts → agent/agent-model-config.test.ts} +2 -5
  294. package/src/wirings/{ai-agent/ai-agent-model-config.ts → agent/agent-model-config.ts} +1 -1
  295. package/src/wirings/{ai-agent/ai-agent-prepare.test.ts → agent/agent-prepare.test.ts} +18 -18
  296. package/src/wirings/{ai-agent/ai-agent-prepare.ts → agent/agent-prepare.ts} +52 -51
  297. package/src/wirings/{ai-agent/ai-agent-registry.test.ts → agent/agent-registry.test.ts} +49 -43
  298. package/src/wirings/{ai-agent/ai-agent-registry.ts → agent/agent-registry.ts} +14 -14
  299. package/src/wirings/{ai-agent/ai-agent-resume-authorization.test.ts → agent/agent-resume-authorization.test.ts} +18 -18
  300. package/src/wirings/{ai-agent → agent}/agent-rpc.ts +16 -20
  301. package/src/wirings/{ai-agent/ai-agent-runner.test.ts → agent/agent-runner.test.ts} +105 -106
  302. package/src/wirings/{ai-agent/ai-agent-runner.ts → agent/agent-runner.ts} +90 -80
  303. package/src/wirings/{ai-agent/ai-agent-stream-output-hooks.test.ts → agent/agent-stream-output-hooks.test.ts} +25 -25
  304. package/src/wirings/{ai-agent/ai-agent-stream.test.ts → agent/agent-stream.test.ts} +147 -143
  305. package/src/wirings/{ai-agent/ai-agent-stream.ts → agent/agent-stream.ts} +147 -146
  306. package/src/wirings/{ai-agent/ai-agent-thread-ownership.test.ts → agent/agent-thread-ownership.test.ts} +24 -28
  307. package/src/wirings/{ai-agent/ai-agent-turn.test.ts → agent/agent-turn.test.ts} +3 -5
  308. package/src/wirings/{ai-agent/ai-agent-turn.ts → agent/agent-turn.ts} +7 -7
  309. package/src/wirings/{ai-agent/ai-agent.types.ts → agent/agent.types.ts} +51 -48
  310. package/src/wirings/agent/index.ts +56 -0
  311. package/src/wirings/{ai-agent → agent}/voice-input.test.ts +15 -15
  312. package/src/wirings/{ai-agent → agent}/voice-input.ts +11 -11
  313. package/src/wirings/{ai-agent → agent}/voice-output.test.ts +23 -23
  314. package/src/wirings/{ai-agent → agent}/voice-output.ts +11 -11
  315. package/src/wirings/{ai-scorer/ai-scorer-grade.test.ts → agent-scorer/agent-scorer-grade.test.ts} +8 -8
  316. package/src/wirings/{ai-scorer/ai-scorer-grade.ts → agent-scorer/agent-scorer-grade.ts} +9 -9
  317. package/src/wirings/{ai-scorer/ai-scorer-judge.test.ts → agent-scorer/agent-scorer-judge.test.ts} +11 -14
  318. package/src/wirings/{ai-scorer/ai-scorer-judge.ts → agent-scorer/agent-scorer-judge.ts} +11 -11
  319. package/src/wirings/{ai-scorer/ai-scorer-live.test.ts → agent-scorer/agent-scorer-live.test.ts} +11 -11
  320. package/src/wirings/{ai-scorer/ai-scorer-live.ts → agent-scorer/agent-scorer-live.ts} +3 -3
  321. package/src/wirings/{ai-scorer/ai-scorer-registry.ts → agent-scorer/agent-scorer-registry.ts} +11 -8
  322. package/src/wirings/{ai-scorer/ai-scorer-sampling.test.ts → agent-scorer/agent-scorer-sampling.test.ts} +1 -1
  323. package/src/wirings/{ai-scorer/ai-scorer-snapshots.test.ts → agent-scorer/agent-scorer-snapshots.test.ts} +2 -2
  324. package/src/wirings/{ai-scorer/ai-scorer-snapshots.ts → agent-scorer/agent-scorer-snapshots.ts} +1 -1
  325. package/src/wirings/{ai-scorer/ai-scorer-worker.test.ts → agent-scorer/agent-scorer-worker.test.ts} +15 -15
  326. package/src/wirings/{ai-scorer/ai-scorer-worker.ts → agent-scorer/agent-scorer-worker.ts} +9 -7
  327. package/src/wirings/{ai-scorer/ai-scorer.ts → agent-scorer/agent-scorer.ts} +7 -7
  328. package/src/wirings/{ai-scorer/ai-scorer.types.ts → agent-scorer/agent-scorer.types.ts} +6 -6
  329. package/src/wirings/agent-scorer/index.ts +22 -0
  330. package/src/wirings/channel/channel-rpc.types.ts +2 -2
  331. package/src/wirings/rpc/rpc-runner.ts +2 -2
  332. package/src/wirings/rpc/rpc-types.ts +1 -1
  333. package/src/wirings/virtual-user/prepare-virtual-user-run.ts +1 -1
  334. package/src/wirings/virtual-user/run-virtual-user.test.ts +2 -2
  335. package/src/wirings/virtual-user/run-virtual-user.ts +3 -3
  336. package/src/wirings/virtual-user/virtual-user-agents.ts +1 -1
  337. package/src/wirings/workflow/run-timeline.ts +1 -1
  338. package/src/wirings-stay-decoupled.test.ts +3 -3
  339. package/tsconfig.tsbuildinfo +1 -1
  340. package/src/wirings/ai-agent/index.ts +0 -65
  341. package/src/wirings/ai-scorer/index.ts +0 -24
  342. /package/src/wirings/{ai-agent/ai-agent-utils.ts → agent/agent-utils.ts} +0 -0
  343. /package/src/wirings/{ai-scorer/ai-scorer-sampling.ts → agent-scorer/agent-scorer-sampling.ts} +0 -0
@@ -0,0 +1,728 @@
1
+ import { PikkuError } from '../../errors/error-handler.js';
2
+ import { checkAuthPermissions, runPermissions, } from '../../permissions.js';
3
+ import { AIProviderNotConfiguredError } from '../../errors/errors.js';
4
+ import { ForbiddenError } from '../../errors/errors.js';
5
+ import { verifyScopes } from '../../scopes.js';
6
+ import { pikkuState, getSingletonServices } from '../../pikku-state.js';
7
+ import { createMiddlewareSessionWireProps } from '../../services/user-session-service.js';
8
+ import { randomUUID } from './agent-utils.js';
9
+ import { awaitPendingInterruptNote } from './agent-interrupt.js';
10
+ import { streamAgent } from './agent-stream.js';
11
+ import { runAgent } from './agent-runner.js';
12
+ import { resolveNamespace, ContextAwareRPCService, } from '../../wirings/rpc/rpc-runner.js';
13
+ import { getOrCreatePackageSingletonServices } from '../../wirings/rpc/addon-runner.js';
14
+ import { resolveMemoryServices, loadContextMessages, trimMessages, } from './agent-memory.js';
15
+ import { resolveModelConfig } from './agent-model-config.js';
16
+ export function resolveSessionPrincipal(params, sessionScope) {
17
+ const session = params.sessionService?.get();
18
+ if ((sessionScope ?? 'user') === 'org') {
19
+ if (!session?.orgId) {
20
+ throw new ForbiddenError('This agent is org-scoped but the session has no organization');
21
+ }
22
+ return session.orgId;
23
+ }
24
+ return session?.userId;
25
+ }
26
+ export function resolveOwnerResourceId(params, sessionScope, requestedResourceId) {
27
+ const principal = resolveSessionPrincipal(params, sessionScope);
28
+ if (!principal) {
29
+ params.anonymousOwnerResourceId ??= `anon-${randomUUID()}`;
30
+ return params.anonymousOwnerResourceId;
31
+ }
32
+ if (requestedResourceId === principal ||
33
+ requestedResourceId.startsWith(`${principal}:`)) {
34
+ return requestedResourceId;
35
+ }
36
+ return `${principal}:${requestedResourceId}`;
37
+ }
38
+ export function assertResourcePrincipalOwner(params, sessionScope, storedResourceId, kind) {
39
+ const principal = resolveSessionPrincipal(params, sessionScope);
40
+ if (!principal || !isOwnedByPrincipal(storedResourceId, principal)) {
41
+ throw new ForbiddenError(`Not authorized to access this ${kind}`);
42
+ }
43
+ }
44
+ export function agentSessionScope(agentName) {
45
+ return resolveAgent(agentName).agent.sessionScope ?? 'user';
46
+ }
47
+ export function assertResourceOwner(ownerResourceId, storedResourceId, kind) {
48
+ if (storedResourceId !== ownerResourceId) {
49
+ throw new ForbiddenError(`Not authorized to access this ${kind}`);
50
+ }
51
+ }
52
+ export function sessionPrincipals(session) {
53
+ return [session?.userId, session?.orgId].filter((principal) => Boolean(principal));
54
+ }
55
+ export function isOwnedByPrincipal(storedResourceId, principal) {
56
+ return (storedResourceId === principal ||
57
+ storedResourceId.startsWith(`${principal}:`));
58
+ }
59
+ export function threadOwnerConstraint(session) {
60
+ return sessionPrincipals(session);
61
+ }
62
+ export function canAccessThread(storedResourceId, session) {
63
+ const principals = sessionPrincipals(session);
64
+ if (principals.length === 0)
65
+ return false;
66
+ return principals.some((principal) => isOwnedByPrincipal(storedResourceId, principal));
67
+ }
68
+ export const APPROVAL_REQUIRED = Symbol('pikku.ai.approvalRequired');
69
+ /**
70
+ * In-process brand proving a credential request was produced by pikku itself and
71
+ * not by tool output an attacker can influence. Never serialized — a value that
72
+ * has crossed a JSON boundary has lost the brand and is treated as untrusted.
73
+ */
74
+ export const CREDENTIAL_REQUIRED = Symbol('pikku.ai.credentialRequired');
75
+ export class ToolApprovalRequired extends PikkuError {
76
+ toolCallId;
77
+ toolName;
78
+ args;
79
+ reason;
80
+ displayToolName;
81
+ displayArgs;
82
+ agentRunId;
83
+ constructor(toolCallId, toolName, args, reason, displayToolName, displayArgs, agentRunId) {
84
+ super(`Tool '${displayToolName ?? toolName}' requires approval`);
85
+ this.toolCallId = toolCallId;
86
+ this.toolName = toolName;
87
+ this.args = args;
88
+ this.reason = reason;
89
+ this.displayToolName = displayToolName;
90
+ this.displayArgs = displayArgs;
91
+ this.agentRunId = agentRunId;
92
+ }
93
+ }
94
+ export class ToolCredentialRequired extends PikkuError {
95
+ toolCallId;
96
+ toolName;
97
+ args;
98
+ credentialName;
99
+ credentialType;
100
+ connectUrl;
101
+ constructor(toolCallId, toolName, args, credentialName, credentialType, connectUrl) {
102
+ super(`Tool '${toolName}' requires credential '${credentialName}'`);
103
+ this.toolCallId = toolCallId;
104
+ this.toolName = toolName;
105
+ this.args = args;
106
+ this.credentialName = credentialName;
107
+ this.credentialType = credentialType;
108
+ this.connectUrl = connectUrl;
109
+ }
110
+ }
111
+ export function getAddonCredentialRequirements(toolNames) {
112
+ const requirements = new Map();
113
+ for (const toolName of toolNames) {
114
+ if (!toolName.includes(':'))
115
+ continue;
116
+ const resolved = resolveNamespace(toolName);
117
+ if (!resolved)
118
+ continue;
119
+ const credsMeta = pikkuState(resolved.package, 'package', 'credentialsMeta');
120
+ if (!credsMeta)
121
+ continue;
122
+ for (const [name, meta] of Object.entries(credsMeta)) {
123
+ if (meta.type === 'wire' && meta.oauth2 && !requirements.has(name)) {
124
+ requirements.set(name, {
125
+ credentialName: name,
126
+ displayName: meta.displayName ?? name,
127
+ addonNamespace: toolName.split(':')[0],
128
+ type: 'wire',
129
+ oauth2: true,
130
+ });
131
+ }
132
+ }
133
+ }
134
+ return [...requirements.values()];
135
+ }
136
+ export const resolveAgent = (agentName) => {
137
+ if (!agentName) {
138
+ console.error('[resolveAgent] agentName is undefined/null! Stack:', new Error().stack);
139
+ throw new Error('resolveAgent called with undefined agentName');
140
+ }
141
+ const mainAgent = pikkuState(null, 'agent', 'agents').get(agentName);
142
+ if (mainAgent) {
143
+ return { agent: mainAgent, packageName: null, resolvedName: agentName };
144
+ }
145
+ const colonIndex = agentName.indexOf(':');
146
+ if (colonIndex !== -1) {
147
+ const namespace = agentName.substring(0, colonIndex);
148
+ const localName = agentName.substring(colonIndex + 1);
149
+ const addons = pikkuState(null, 'addons', 'packages');
150
+ const pkgConfig = addons.get(namespace);
151
+ if (pkgConfig) {
152
+ const extAgent = pikkuState(pkgConfig.package, 'agent', 'agents').get(localName);
153
+ if (extAgent) {
154
+ return {
155
+ agent: extAgent,
156
+ packageName: pkgConfig.package,
157
+ resolvedName: localName,
158
+ };
159
+ }
160
+ }
161
+ }
162
+ throw new Error(`AI agent not found: ${agentName}`);
163
+ };
164
+ export async function assertAgentAuthorized(agent, params, packageName) {
165
+ const session = params.sessionService
166
+ ? await params.sessionService.get()
167
+ : undefined;
168
+ if (agent.auth === true && !session) {
169
+ throw new ForbiddenError('Authentication required');
170
+ }
171
+ verifyScopes(agent.scopes, session);
172
+ const singletonServices = getSingletonServices();
173
+ const wire = params.sessionService
174
+ ? createMiddlewareSessionWireProps(params.sessionService)
175
+ : { session: undefined };
176
+ await runPermissions({
177
+ funcPermissions: agent.permissions,
178
+ services: singletonServices,
179
+ wire: wire,
180
+ data: {},
181
+ packageName,
182
+ label: 'agent',
183
+ });
184
+ }
185
+ export async function buildInstructions(agentName, packageName) {
186
+ const meta = pikkuState(packageName, 'agent', 'agentsMeta')[agentName];
187
+ const parts = [];
188
+ if (meta?.role)
189
+ parts.push(meta.role);
190
+ if (meta?.personality)
191
+ parts.push(meta.personality);
192
+ if (meta?.goal)
193
+ parts.push(meta.goal);
194
+ let instructions = parts.join('\n\n');
195
+ if (meta?.tools?.length || meta?.workflows?.length) {
196
+ instructions +=
197
+ '\n\nTool usage rules:\n' +
198
+ '- Act immediately with the information given. Do NOT ask clarifying questions unless a required field is truly missing.\n' +
199
+ '- Only use fields defined in your tool schemas. Never mention or ask for fields that do not exist.\n' +
200
+ '- Never fill optional fields with placeholder or zero values. Omit them entirely unless the user provides a real value.\n' +
201
+ '- Never stuff unrelated information into the wrong field.\n' +
202
+ '- Keep responses concise.';
203
+ }
204
+ if (meta?.agents?.length) {
205
+ instructions +=
206
+ '\n\nWhen calling a sub-agent, provide a short session name that describes the task. ' +
207
+ 'Use the same session name to continue a previous conversation with that agent. ' +
208
+ 'Use a new session name for a new independent task. ' +
209
+ 'When a request involves multiple actions for the same domain, combine them into a single sub-agent call rather than making separate calls.';
210
+ }
211
+ return instructions;
212
+ }
213
+ export function createScopedChannel(parent, agentName, session) {
214
+ const capturedApprovals = [];
215
+ return {
216
+ channelId: `${parent.channelId}:${agentName}:${session}`,
217
+ openingData: parent.openingData,
218
+ get state() {
219
+ return parent.state;
220
+ },
221
+ get approvals() {
222
+ return capturedApprovals;
223
+ },
224
+ close: () => { },
225
+ sendBinary: (data) => parent.sendBinary(data),
226
+ send: (event) => {
227
+ if (event.type === 'done')
228
+ return;
229
+ if (event.type === 'approval-request') {
230
+ capturedApprovals.push({
231
+ toolCallId: event.toolCallId,
232
+ toolName: event.toolName,
233
+ args: event.args,
234
+ ...(event.reason !== undefined ? { reason: event.reason } : {}),
235
+ runId: event.runId,
236
+ });
237
+ return;
238
+ }
239
+ if (event.type === 'step-start' ||
240
+ event.type === 'text-delta' ||
241
+ event.type === 'reasoning-delta' ||
242
+ event.type === 'tool-call' ||
243
+ event.type === 'tool-result' ||
244
+ event.type === 'usage' ||
245
+ event.type === 'error') {
246
+ parent.send({ ...event, agent: agentName, session });
247
+ }
248
+ else {
249
+ parent.send(event);
250
+ }
251
+ },
252
+ setState: (s) => parent.setState(s),
253
+ getState: () => parent.getState(),
254
+ clearState: () => parent.clearState(),
255
+ remote: (funcName, data) => parent.remote(funcName, data),
256
+ };
257
+ }
258
+ export function buildSubAgentRunInput(message, threadId, resourceId, parentContext) {
259
+ return { message, threadId, resourceId, context: parentContext };
260
+ }
261
+ export async function buildToolDefs(params, agentSessionMap, resourceId, agentName, packageName, streamContext, agentMiddlewares, agentMode, parentContext) {
262
+ const singletonServices = getSingletonServices();
263
+ const tools = [];
264
+ const missingRpcs = [];
265
+ const approvalPolicy = streamContext?.options?.requiresToolApproval ?? 'explicit';
266
+ const meta = pikkuState(packageName, 'agent', 'agentsMeta')[agentName];
267
+ if (!meta)
268
+ return { tools, missingRpcs };
269
+ /**
270
+ * A tool's description is what the model is told it does, and the main thing
271
+ * it chooses between tools on. It does not survive into the meta registered at
272
+ * boot: `description` is classed as a verbose field, so the bundled copy has
273
+ * it stripped and `fnMeta.description` is always undefined — every tool was
274
+ * being offered under its own bare name regardless of what its author wrote.
275
+ *
276
+ * `getFunctionsMeta` reads the verbose file and falls back to the minimal one,
277
+ * recovering the authored description wherever the generated `.pikku`
278
+ * directory is readable. Where it is not — no `metaService`, or a deployment
279
+ * shipping only the stripped copy — tools fall back to their name, which is
280
+ * what they did before this lookup existed.
281
+ */
282
+ const describedFunctions = await singletonServices.metaService
283
+ ?.getFunctionsMeta()
284
+ .catch(() => undefined);
285
+ const session = params.sessionService
286
+ ? await params.sessionService.get()
287
+ : null;
288
+ const metaTools = meta.tools;
289
+ const metaAgents = meta.agents;
290
+ if (metaTools?.length) {
291
+ for (const toolName of metaTools) {
292
+ let fnMeta;
293
+ let resolvedPkg = null;
294
+ let schemas;
295
+ const resolved = toolName.includes(':')
296
+ ? resolveNamespace(toolName)
297
+ : null;
298
+ let pikkuFuncId;
299
+ if (resolved) {
300
+ resolvedPkg = resolved.package;
301
+ pikkuFuncId = resolved.function;
302
+ fnMeta = pikkuState(resolvedPkg, 'function', 'meta')[pikkuFuncId];
303
+ schemas = pikkuState(resolvedPkg, 'misc', 'schemas');
304
+ }
305
+ else {
306
+ const rpcMeta = pikkuState(null, 'rpc', 'meta');
307
+ pikkuFuncId = rpcMeta[toolName];
308
+ if (!pikkuFuncId) {
309
+ missingRpcs.push(toolName);
310
+ continue;
311
+ }
312
+ fnMeta = pikkuState(null, 'function', 'meta')[pikkuFuncId];
313
+ schemas = pikkuState(null, 'misc', 'schemas');
314
+ }
315
+ if (!fnMeta) {
316
+ missingRpcs.push(toolName);
317
+ continue;
318
+ }
319
+ if (fnMeta.permissions?.length) {
320
+ if (!session)
321
+ continue;
322
+ const funcConfig = pikkuFuncId
323
+ ? pikkuState(resolvedPkg, 'function', 'functions').get(pikkuFuncId)
324
+ : undefined;
325
+ const allowed = await checkAuthPermissions(funcConfig?.permissions, session, singletonServices, resolvedPkg);
326
+ if (!allowed)
327
+ continue;
328
+ }
329
+ const inputSchemaName = fnMeta?.inputSchemaName;
330
+ let inputSchema = inputSchemaName
331
+ ? schemas.get(inputSchemaName)
332
+ : undefined;
333
+ if (!inputSchema ||
334
+ (typeof inputSchema === 'object' &&
335
+ inputSchema.type === 'object' &&
336
+ !inputSchema.properties)) {
337
+ inputSchema = { type: 'object', properties: {} };
338
+ }
339
+ const needsApproval = approvalPolicy === 'all' ||
340
+ (approvalPolicy === 'explicit' && fnMeta?.approvalRequired);
341
+ let approvalDescriptionFn;
342
+ if (needsApproval && pikkuFuncId) {
343
+ const funcConfig = pikkuState(resolvedPkg, 'function', 'functions').get(pikkuFuncId);
344
+ if (funcConfig?.approvalDescription) {
345
+ const descFn = funcConfig.approvalDescription;
346
+ const capturedPkg = resolvedPkg;
347
+ const capturedAddonConfig = resolved?.addonConfig;
348
+ const capturedNamespace = toolName.includes(':')
349
+ ? toolName.slice(0, toolName.indexOf(':'))
350
+ : null;
351
+ approvalDescriptionFn = async (input) => {
352
+ let services = singletonServices;
353
+ if (capturedPkg) {
354
+ const addonInstance = capturedNamespace
355
+ ? {
356
+ namespace: capturedNamespace,
357
+ secretOverrides: capturedAddonConfig?.secretOverrides,
358
+ variableOverrides: capturedAddonConfig?.variableOverrides,
359
+ credentialOverrides: capturedAddonConfig?.credentialOverrides,
360
+ secretGrants: capturedAddonConfig?.secretGrants,
361
+ credentialGrants: capturedAddonConfig?.credentialGrants,
362
+ globalSecrets: capturedAddonConfig?.globalSecrets,
363
+ globalCredentials: capturedAddonConfig?.globalCredentials,
364
+ }
365
+ : undefined;
366
+ services = await getOrCreatePackageSingletonServices(capturedPkg, singletonServices, addonInstance);
367
+ }
368
+ return descFn(services, input);
369
+ };
370
+ }
371
+ }
372
+ tools.push({
373
+ name: toolName.replaceAll('@', '_').replaceAll(':', '__'),
374
+ description: describedFunctions?.[pikkuFuncId]?.description ||
375
+ fnMeta?.description ||
376
+ toolName,
377
+ inputSchema,
378
+ needsApproval: needsApproval || undefined,
379
+ approvalDescriptionFn,
380
+ readonly: fnMeta?.readonly || undefined,
381
+ execute: async (toolInput) => {
382
+ const wire = params.sessionService
383
+ ? { ...createMiddlewareSessionWireProps(params.sessionService) }
384
+ : {};
385
+ const rpcService = new ContextAwareRPCService(singletonServices, wire, { sessionService: params.sessionService });
386
+ return rpcService.rpc(toolName, toolInput);
387
+ },
388
+ });
389
+ }
390
+ }
391
+ if (metaAgents?.length) {
392
+ const allAgentsMeta = pikkuState(null, 'agent', 'agentsMeta');
393
+ for (const subAgentName of metaAgents) {
394
+ const subMeta = allAgentsMeta[subAgentName];
395
+ if (!subMeta) {
396
+ singletonServices.logger.warn(`Sub-agent '${subAgentName}' not found in agent registry`);
397
+ continue;
398
+ }
399
+ if (subMeta.permissions?.length) {
400
+ if (!session)
401
+ continue;
402
+ const subAgent = pikkuState(null, 'agent', 'agents').get(subAgentName);
403
+ const allowed = await checkAuthPermissions(subAgent?.permissions, session, singletonServices);
404
+ if (!allowed)
405
+ continue;
406
+ }
407
+ tools.push({
408
+ name: subAgentName,
409
+ description: subMeta.description,
410
+ forwardsApproval: true,
411
+ inputSchema: {
412
+ type: 'object',
413
+ properties: {
414
+ message: { type: 'string' },
415
+ session: {
416
+ type: 'string',
417
+ description: 'Short session label for thread continuity',
418
+ },
419
+ },
420
+ required: ['message', 'session'],
421
+ },
422
+ execute: async (toolInput) => {
423
+ const { message, session } = toolInput;
424
+ const sessionKey = `${subAgentName}::${session}`;
425
+ let threadId = agentSessionMap.get(sessionKey);
426
+ if (!threadId) {
427
+ threadId = randomUUID();
428
+ agentSessionMap.set(sessionKey, threadId);
429
+ }
430
+ if (streamContext) {
431
+ const isDelegate = agentMode !== 'supervise';
432
+ if (isDelegate && streamContext.delegateState) {
433
+ streamContext.delegateState.delegated = true;
434
+ }
435
+ const { channel } = streamContext;
436
+ channel.send({
437
+ type: 'agent-call',
438
+ agentName: subAgentName,
439
+ session,
440
+ input: message,
441
+ });
442
+ const subChannel = createScopedChannel(channel, subAgentName, session);
443
+ const effectiveChannel = isDelegate
444
+ ? subChannel
445
+ : {
446
+ ...subChannel,
447
+ send: (event) => {
448
+ if (event.type === 'text-delta' ||
449
+ event.type === 'reasoning-delta')
450
+ return;
451
+ subChannel.send(event);
452
+ },
453
+ };
454
+ const resultText = await streamAgent(subAgentName, buildSubAgentRunInput(message, threadId, resourceId, parentContext), effectiveChannel, params, agentSessionMap, streamContext.options);
455
+ if (subChannel.approvals.length > 0) {
456
+ return {
457
+ [APPROVAL_REQUIRED]: true,
458
+ __approvalRequired: true,
459
+ toolName: subAgentName,
460
+ args: toolInput,
461
+ agentRunId: subChannel.approvals[0].runId,
462
+ subApprovals: subChannel.approvals,
463
+ };
464
+ }
465
+ channel.send({
466
+ type: 'agent-result',
467
+ agentName: subAgentName,
468
+ session,
469
+ result: resultText,
470
+ });
471
+ return resultText;
472
+ }
473
+ const result = await runAgent(subAgentName, buildSubAgentRunInput(message, threadId, resourceId, parentContext), params, agentSessionMap);
474
+ if (result.status === 'suspended' &&
475
+ result.pendingApprovals?.length) {
476
+ return {
477
+ [APPROVAL_REQUIRED]: true,
478
+ __approvalRequired: true,
479
+ toolName: subAgentName,
480
+ args: toolInput,
481
+ agentRunId: result.runId,
482
+ subApprovals: result.pendingApprovals.map((a) => ({
483
+ toolCallId: a.toolCallId,
484
+ toolName: a.toolName,
485
+ args: a.args,
486
+ reason: a.reason,
487
+ runId: a.runId,
488
+ })),
489
+ };
490
+ }
491
+ return result.object ?? result.text;
492
+ },
493
+ });
494
+ }
495
+ }
496
+ const metaWorkflows = meta.workflows;
497
+ if (metaWorkflows?.length) {
498
+ const workflowMetaMap = pikkuState(null, 'workflows', 'meta');
499
+ const functionMeta = pikkuState(null, 'function', 'meta');
500
+ const schemas = pikkuState(null, 'misc', 'schemas');
501
+ for (const workflowName of metaWorkflows) {
502
+ const wfMeta = workflowMetaMap[workflowName];
503
+ if (!wfMeta) {
504
+ missingRpcs.push(workflowName);
505
+ continue;
506
+ }
507
+ const inputSchemaName = wfMeta.pikkuFuncId
508
+ ? functionMeta[wfMeta.pikkuFuncId]?.inputSchemaName
509
+ : undefined;
510
+ let inputSchema = inputSchemaName
511
+ ? schemas.get(inputSchemaName)
512
+ : undefined;
513
+ if (!inputSchema ||
514
+ (typeof inputSchema === 'object' &&
515
+ inputSchema.type === 'object' &&
516
+ !inputSchema.properties)) {
517
+ inputSchema = { type: 'object', properties: {} };
518
+ }
519
+ tools.push({
520
+ name: workflowName,
521
+ description: wfMeta.description || workflowName,
522
+ inputSchema,
523
+ execute: async (toolInput) => {
524
+ const workflowService = singletonServices.workflowService;
525
+ if (!workflowService) {
526
+ throw new Error(`workflowService is not configured — cannot run workflow tool '${workflowName}'`);
527
+ }
528
+ const wire = params.sessionService
529
+ ? { ...createMiddlewareSessionWireProps(params.sessionService) }
530
+ : {};
531
+ const rpcService = new ContextAwareRPCService(singletonServices, wire, { sessionService: params.sessionService });
532
+ return workflowService.runToCompletion(workflowName, toolInput, rpcService, { wire: { type: 'internal' } });
533
+ },
534
+ });
535
+ }
536
+ }
537
+ for (const tool of tools) {
538
+ const originalExecute = tool.execute;
539
+ tool.execute = async (toolInput) => {
540
+ try {
541
+ return await originalExecute(toolInput);
542
+ }
543
+ catch (err) {
544
+ if (err?.payload?.error === 'missing_credential') {
545
+ return {
546
+ [CREDENTIAL_REQUIRED]: true,
547
+ __credentialRequired: true,
548
+ ...err.payload,
549
+ };
550
+ }
551
+ singletonServices.logger.error(`AI agent tool '${tool.name}' threw during execute()`, err);
552
+ throw err;
553
+ }
554
+ };
555
+ }
556
+ const hasToolHooks = agentMiddlewares?.some((mw) => mw.beforeToolCall || mw.afterToolCall);
557
+ if (hasToolHooks) {
558
+ for (const tool of tools) {
559
+ const originalExecute = tool.execute;
560
+ tool.execute = async (toolInput) => {
561
+ const toolCallId = randomUUID();
562
+ let args = (toolInput ?? {});
563
+ for (const mw of agentMiddlewares) {
564
+ if (mw.beforeToolCall) {
565
+ const beforeResult = await mw.beforeToolCall(singletonServices, {
566
+ toolName: tool.name,
567
+ toolCallId,
568
+ args,
569
+ });
570
+ if (beforeResult && 'args' in beforeResult) {
571
+ args = beforeResult.args;
572
+ }
573
+ }
574
+ }
575
+ const startTime = Date.now();
576
+ let result;
577
+ let execError;
578
+ try {
579
+ result = await originalExecute(args);
580
+ }
581
+ catch (err) {
582
+ execError = err;
583
+ if (err?.payload?.error === 'missing_credential')
584
+ throw err;
585
+ result = err instanceof Error ? err.message : String(err);
586
+ }
587
+ const durationMs = Date.now() - startTime;
588
+ for (let i = agentMiddlewares.length - 1; i >= 0; i--) {
589
+ const mw = agentMiddlewares[i];
590
+ if (mw.afterToolCall) {
591
+ const afterResult = await mw.afterToolCall(singletonServices, {
592
+ toolName: tool.name,
593
+ toolCallId,
594
+ args,
595
+ result,
596
+ durationMs,
597
+ });
598
+ if (afterResult && 'result' in afterResult) {
599
+ result = afterResult.result;
600
+ }
601
+ }
602
+ }
603
+ if (execError)
604
+ throw execError;
605
+ return result;
606
+ };
607
+ }
608
+ }
609
+ return { tools, missingRpcs };
610
+ }
611
+ export async function prepareAgentRun(agentName, input, params, agentSessionMap, streamContext) {
612
+ const singletonServices = getSingletonServices();
613
+ const { agent, packageName, resolvedName } = resolveAgent(agentName);
614
+ await assertAgentAuthorized(agent, params, packageName);
615
+ let agentRunner = singletonServices.agentRunner;
616
+ if (!agentRunner) {
617
+ throw new AIProviderNotConfiguredError();
618
+ }
619
+ if (params.getCredential && agentRunner.withApiKey) {
620
+ const aiCredential = await params.getCredential('AI_API_KEY');
621
+ if (aiCredential?.apiKey?.trim()) {
622
+ agentRunner = agentRunner.withApiKey(aiCredential.apiKey);
623
+ }
624
+ }
625
+ const { storage } = resolveMemoryServices(agent, singletonServices);
626
+ const memoryConfig = agent.memory;
627
+ const threadId = input.threadId;
628
+ const agentsMeta = pikkuState(packageName, 'agent', 'agentsMeta');
629
+ const meta = agentsMeta[resolvedName];
630
+ const outputSchemaName = meta?.outputSchema;
631
+ const outputSchema = outputSchemaName
632
+ ? pikkuState(packageName, 'misc', 'schemas').get(outputSchemaName)
633
+ : undefined;
634
+ const workingMemorySchemaName = meta?.workingMemorySchema ?? null;
635
+ const workingMemoryJsonSchema = workingMemorySchemaName
636
+ ? pikkuState(packageName, 'misc', 'schemas').get(workingMemorySchemaName)
637
+ : undefined;
638
+ if (storage) {
639
+ let thread = null;
640
+ try {
641
+ thread = await storage.getThread(threadId);
642
+ }
643
+ catch {
644
+ thread = null;
645
+ }
646
+ if (thread) {
647
+ assertResourceOwner(input.resourceId, thread.resourceId, 'thread');
648
+ }
649
+ else {
650
+ await storage.createThread(input.resourceId, { threadId });
651
+ }
652
+ }
653
+ let messages = [];
654
+ if (storage) {
655
+ // knowledge: decisions/internals/agent-context-waits-for-a-tool-result-still-being-written.md
656
+ await awaitPendingInterruptNote(threadId);
657
+ messages = await storage.getMessages(threadId, {
658
+ lastN: memoryConfig?.lastMessages ?? 20,
659
+ });
660
+ }
661
+ const contextMessages = await loadContextMessages(memoryConfig, storage, input, workingMemoryJsonSchema);
662
+ const userContent = input.attachments?.length
663
+ ? [
664
+ // knowledge: decisions/internals/an-empty-text-part-is-omitted-from-an-agent-message.md
665
+ ...(input.message
666
+ ? [{ type: 'text', text: input.message }]
667
+ : []),
668
+ ...input.attachments.map((a) => ({
669
+ type: a.type,
670
+ data: a.data,
671
+ url: a.url,
672
+ mediaType: a.mediaType,
673
+ ...(a.filename ? { filename: a.filename } : {}),
674
+ })),
675
+ ]
676
+ : input.message;
677
+ const userMessage = {
678
+ id: randomUUID(),
679
+ role: 'user',
680
+ content: userContent,
681
+ createdAt: new Date(),
682
+ };
683
+ const allMessages = [...contextMessages, ...messages, userMessage];
684
+ const trimmedMessages = trimMessages(allMessages);
685
+ const agentMiddlewares = agent.agentMiddleware ?? [];
686
+ const { tools, missingRpcs } = await buildToolDefs(params, agentSessionMap, input.resourceId, resolvedName, packageName, streamContext, agentMiddlewares, agent.agentMode, input.context);
687
+ let instructions = await buildInstructions(resolvedName, packageName);
688
+ if (input.context) {
689
+ instructions = `${instructions}\n\nCurrent context (use these identifiers directly in tool calls — do not ask the user for them):\n${input.context}`;
690
+ }
691
+ const resolved = resolveModelConfig(resolvedName, agent);
692
+ if (input.model) {
693
+ resolved.model = resolveModelConfig(resolvedName, {
694
+ ...agent,
695
+ model: input.model,
696
+ }).model;
697
+ }
698
+ if (input.temperature !== undefined) {
699
+ resolved.temperature = input.temperature;
700
+ }
701
+ const maxSteps = resolved.maxSteps ?? 10;
702
+ const runnerParams = {
703
+ model: resolved.model,
704
+ temperature: resolved.temperature,
705
+ instructions,
706
+ messages: trimmedMessages,
707
+ tools,
708
+ maxSteps: 1,
709
+ toolChoice: agent.toolChoice ?? 'auto',
710
+ providerOptions: agent.providerOptions,
711
+ outputSchema,
712
+ };
713
+ return {
714
+ agent,
715
+ packageName,
716
+ resolvedName,
717
+ agentRunner,
718
+ storage,
719
+ memoryConfig,
720
+ threadId,
721
+ userMessage,
722
+ runnerParams,
723
+ maxSteps,
724
+ missingRpcs,
725
+ workingMemoryJsonSchema,
726
+ workingMemorySchemaName,
727
+ };
728
+ }