@pikku/core 0.11.2 → 0.12.0

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 (344) hide show
  1. package/CHANGELOG.md +33 -2
  2. package/dist/crypto-utils.d.ts +2 -0
  3. package/dist/crypto-utils.js +78 -0
  4. package/dist/errors/errors.d.ts +8 -0
  5. package/dist/errors/errors.js +16 -0
  6. package/dist/errors/index.d.ts +0 -1
  7. package/dist/errors/index.js +0 -1
  8. package/dist/function/function-runner.d.ts +7 -1
  9. package/dist/function/function-runner.js +82 -23
  10. package/dist/function/functions.types.d.ts +31 -87
  11. package/dist/function/functions.types.js +16 -46
  12. package/dist/function/index.d.ts +2 -2
  13. package/dist/function/index.js +1 -2
  14. package/dist/handle-error.js +1 -10
  15. package/dist/index.d.ts +28 -26
  16. package/dist/index.js +18 -29
  17. package/dist/middleware/auth-apikey.d.ts +2 -2
  18. package/dist/middleware/auth-apikey.js +3 -3
  19. package/dist/middleware/auth-bearer.d.ts +2 -2
  20. package/dist/middleware/auth-bearer.js +3 -6
  21. package/dist/middleware/auth-cookie.d.ts +2 -2
  22. package/dist/middleware/auth-cookie.js +6 -8
  23. package/dist/middleware/cors.d.ts +46 -0
  24. package/dist/middleware/cors.js +73 -0
  25. package/dist/middleware/index.d.ts +5 -3
  26. package/dist/middleware/index.js +5 -3
  27. package/dist/middleware/remote-auth.d.ts +5 -0
  28. package/dist/middleware/remote-auth.js +55 -0
  29. package/dist/middleware/timeout.d.ts +2 -2
  30. package/dist/middleware-runner.js +8 -10
  31. package/dist/permissions.js +11 -15
  32. package/dist/pikku-state.d.ts +1 -0
  33. package/dist/pikku-state.js +18 -8
  34. package/dist/schema.js +12 -11
  35. package/dist/services/ai-agent-runner-service.d.ts +24 -0
  36. package/dist/services/ai-agent-runner-service.js +1 -0
  37. package/dist/services/ai-run-state-service.d.ts +13 -0
  38. package/dist/services/ai-run-state-service.js +1 -0
  39. package/dist/services/ai-storage-service.d.ts +18 -0
  40. package/dist/services/ai-storage-service.js +1 -0
  41. package/dist/services/deployment-service.d.ts +19 -0
  42. package/dist/services/deployment-service.js +1 -0
  43. package/dist/services/gopass-secrets.d.ts +47 -0
  44. package/dist/services/gopass-secrets.js +94 -0
  45. package/dist/services/in-memory-trigger-service.d.ts +23 -0
  46. package/dist/services/in-memory-trigger-service.js +50 -0
  47. package/dist/services/in-memory-workflow-service.d.ts +59 -0
  48. package/dist/services/in-memory-workflow-service.js +287 -0
  49. package/dist/services/index.d.ts +25 -11
  50. package/dist/services/index.js +10 -13
  51. package/dist/services/local-secrets.d.ts +23 -0
  52. package/dist/services/local-secrets.js +45 -0
  53. package/dist/services/local-variables.d.ts +5 -0
  54. package/dist/services/local-variables.js +18 -0
  55. package/dist/services/scheduler-service.d.ts +21 -1
  56. package/dist/services/scheduler-service.js +14 -0
  57. package/dist/services/schema-service.d.ts +6 -0
  58. package/dist/services/scoped-secret-service.d.ts +16 -0
  59. package/dist/services/scoped-secret-service.js +35 -0
  60. package/dist/services/secret-service.d.ts +25 -4
  61. package/dist/services/trigger-service.d.ts +23 -0
  62. package/dist/services/trigger-service.js +1 -0
  63. package/dist/services/typed-secret-service.d.ts +30 -0
  64. package/dist/services/typed-secret-service.js +40 -0
  65. package/dist/services/typed-variables-service.d.ts +27 -0
  66. package/dist/services/typed-variables-service.js +46 -0
  67. package/dist/services/user-session-service.d.ts +13 -0
  68. package/dist/services/user-session-service.js +17 -0
  69. package/dist/services/variables-service.d.ts +5 -0
  70. package/dist/services/workflow-service.d.ts +9 -4
  71. package/dist/types/core.types.d.ts +48 -12
  72. package/dist/types/core.types.js +8 -1
  73. package/dist/types/state.types.d.ts +43 -51
  74. package/dist/utils.d.ts +3 -2
  75. package/dist/utils.js +15 -3
  76. package/dist/version.d.ts +6 -0
  77. package/dist/version.js +19 -0
  78. package/dist/wirings/ai-agent/ai-agent-memory.d.ts +29 -0
  79. package/dist/wirings/ai-agent/ai-agent-memory.js +199 -0
  80. package/dist/wirings/ai-agent/ai-agent-model-config.d.ts +9 -0
  81. package/dist/wirings/ai-agent/ai-agent-model-config.js +36 -0
  82. package/dist/wirings/ai-agent/ai-agent-prepare.d.ts +60 -0
  83. package/dist/wirings/ai-agent/ai-agent-prepare.js +312 -0
  84. package/dist/wirings/ai-agent/ai-agent-registry.d.ts +15 -0
  85. package/dist/wirings/ai-agent/ai-agent-registry.js +47 -0
  86. package/dist/wirings/ai-agent/ai-agent-runner.d.ts +3 -0
  87. package/dist/wirings/ai-agent/ai-agent-runner.js +96 -0
  88. package/dist/wirings/ai-agent/ai-agent-stream.d.ts +11 -0
  89. package/dist/wirings/ai-agent/ai-agent-stream.js +503 -0
  90. package/dist/wirings/ai-agent/ai-agent.types.d.ts +254 -0
  91. package/dist/wirings/ai-agent/ai-agent.types.js +1 -0
  92. package/dist/wirings/ai-agent/index.d.ts +5 -0
  93. package/dist/wirings/ai-agent/index.js +4 -0
  94. package/dist/wirings/channel/channel-common.d.ts +7 -4
  95. package/dist/wirings/channel/channel-common.js +15 -8
  96. package/dist/wirings/channel/channel-handler.js +12 -3
  97. package/dist/wirings/channel/channel-middleware-runner.d.ts +9 -0
  98. package/dist/wirings/channel/channel-middleware-runner.js +73 -0
  99. package/dist/wirings/channel/channel-runner.js +7 -7
  100. package/dist/wirings/channel/channel.types.d.ts +5 -1
  101. package/dist/wirings/channel/define-channel-routes.d.ts +21 -0
  102. package/dist/wirings/channel/define-channel-routes.js +23 -0
  103. package/dist/wirings/channel/index.d.ts +10 -8
  104. package/dist/wirings/channel/index.js +7 -8
  105. package/dist/wirings/channel/local/index.d.ts +3 -3
  106. package/dist/wirings/channel/local/index.js +3 -3
  107. package/dist/wirings/channel/local/local-channel-runner.js +18 -6
  108. package/dist/wirings/channel/serverless/index.d.ts +1 -1
  109. package/dist/wirings/channel/serverless/index.js +1 -1
  110. package/dist/wirings/channel/serverless/serverless-channel-runner.js +15 -4
  111. package/dist/wirings/cli/channel/index.d.ts +1 -1
  112. package/dist/wirings/cli/channel/index.js +1 -1
  113. package/dist/wirings/cli/cli-runner.d.ts +1 -1
  114. package/dist/wirings/cli/cli-runner.js +12 -8
  115. package/dist/wirings/cli/cli.types.d.ts +6 -6
  116. package/dist/wirings/cli/define-cli-commands.d.ts +19 -0
  117. package/dist/wirings/cli/define-cli-commands.js +20 -0
  118. package/dist/wirings/cli/index.d.ts +2 -3
  119. package/dist/wirings/cli/index.js +1 -3
  120. package/dist/wirings/http/http-routes.d.ts +69 -0
  121. package/dist/wirings/http/http-routes.js +139 -0
  122. package/dist/wirings/http/http-runner.js +49 -23
  123. package/dist/wirings/http/http.types.d.ts +78 -38
  124. package/dist/wirings/http/index.d.ts +5 -4
  125. package/dist/wirings/http/index.js +4 -3
  126. package/dist/wirings/mcp/index.d.ts +4 -3
  127. package/dist/wirings/mcp/index.js +3 -3
  128. package/dist/wirings/mcp/mcp-runner.d.ts +1 -3
  129. package/dist/wirings/mcp/mcp-runner.js +25 -39
  130. package/dist/wirings/mcp/mcp.types.d.ts +3 -3
  131. package/dist/wirings/node/index.d.ts +2 -0
  132. package/dist/wirings/node/index.js +1 -0
  133. package/dist/wirings/node/node.types.d.ts +20 -0
  134. package/dist/wirings/node/node.types.js +1 -0
  135. package/dist/wirings/oauth2/index.d.ts +3 -0
  136. package/dist/wirings/oauth2/index.js +2 -0
  137. package/dist/wirings/oauth2/oauth2-client.d.ts +47 -0
  138. package/dist/wirings/oauth2/oauth2-client.js +267 -0
  139. package/dist/wirings/oauth2/oauth2.types.d.ts +65 -0
  140. package/dist/wirings/oauth2/oauth2.types.js +4 -0
  141. package/dist/wirings/oauth2/wire-oauth2-credential.d.ts +20 -0
  142. package/dist/wirings/oauth2/wire-oauth2-credential.js +21 -0
  143. package/dist/wirings/queue/index.d.ts +2 -2
  144. package/dist/wirings/queue/index.js +1 -1
  145. package/dist/wirings/queue/queue-runner.d.ts +7 -0
  146. package/dist/wirings/queue/queue-runner.js +17 -6
  147. package/dist/wirings/queue/queue.types.d.ts +8 -2
  148. package/dist/wirings/rpc/index.d.ts +1 -1
  149. package/dist/wirings/rpc/index.js +1 -1
  150. package/dist/wirings/rpc/rpc-runner.d.ts +33 -5
  151. package/dist/wirings/rpc/rpc-runner.js +134 -16
  152. package/dist/wirings/rpc/rpc-types.d.ts +8 -4
  153. package/dist/wirings/scheduler/index.d.ts +3 -3
  154. package/dist/wirings/scheduler/index.js +2 -3
  155. package/dist/wirings/scheduler/scheduler-runner.d.ts +5 -0
  156. package/dist/wirings/scheduler/scheduler-runner.js +23 -5
  157. package/dist/wirings/secret/index.d.ts +3 -0
  158. package/dist/wirings/secret/index.js +2 -0
  159. package/dist/wirings/secret/secret.types.d.ts +27 -0
  160. package/dist/wirings/secret/secret.types.js +1 -0
  161. package/dist/wirings/secret/validate-secret-definitions.d.ts +6 -0
  162. package/dist/wirings/secret/validate-secret-definitions.js +55 -0
  163. package/dist/wirings/trigger/index.d.ts +3 -2
  164. package/dist/wirings/trigger/index.js +2 -2
  165. package/dist/wirings/trigger/pikku-trigger-service.d.ts +26 -0
  166. package/dist/wirings/trigger/pikku-trigger-service.js +75 -0
  167. package/dist/wirings/trigger/trigger-runner.d.ts +21 -8
  168. package/dist/wirings/trigger/trigger-runner.js +46 -18
  169. package/dist/wirings/trigger/trigger.types.d.ts +99 -11
  170. package/dist/wirings/trigger/trigger.types.js +40 -1
  171. package/dist/wirings/variable/index.d.ts +3 -0
  172. package/dist/wirings/variable/index.js +2 -0
  173. package/dist/wirings/variable/validate-variable-definitions.d.ts +6 -0
  174. package/dist/wirings/variable/validate-variable-definitions.js +45 -0
  175. package/dist/wirings/variable/variable.types.d.ts +18 -0
  176. package/dist/wirings/variable/variable.types.js +1 -0
  177. package/dist/wirings/workflow/dsl/index.d.ts +1 -1
  178. package/dist/wirings/workflow/dsl/workflow-dsl.types.d.ts +7 -1
  179. package/dist/wirings/workflow/dsl/workflow-runner.js +3 -2
  180. package/dist/wirings/workflow/graph/graph-node.d.ts +1 -6
  181. package/dist/wirings/workflow/graph/graph-runner.d.ts +5 -31
  182. package/dist/wirings/workflow/graph/graph-runner.js +384 -296
  183. package/dist/wirings/workflow/graph/index.d.ts +4 -3
  184. package/dist/wirings/workflow/graph/index.js +3 -3
  185. package/dist/wirings/workflow/graph/template.d.ts +15 -0
  186. package/dist/wirings/workflow/graph/template.js +27 -0
  187. package/dist/wirings/workflow/graph/wire-workflow-graph.d.ts +15 -0
  188. package/dist/wirings/workflow/graph/wire-workflow-graph.js +8 -0
  189. package/dist/wirings/workflow/graph/workflow-graph.types.d.ts +0 -13
  190. package/dist/wirings/workflow/index.d.ts +6 -6
  191. package/dist/wirings/workflow/index.js +6 -4
  192. package/dist/wirings/workflow/pikku-workflow-service.d.ts +28 -14
  193. package/dist/wirings/workflow/pikku-workflow-service.js +200 -56
  194. package/dist/wirings/workflow/workflow-helpers.d.ts +36 -0
  195. package/dist/wirings/workflow/workflow-helpers.js +38 -0
  196. package/dist/wirings/workflow/workflow-utils.d.ts +1 -23
  197. package/dist/wirings/workflow/workflow-utils.js +1 -66
  198. package/dist/wirings/workflow/workflow.types.d.ts +40 -93
  199. package/package.json +13 -15
  200. package/run-tests.sh +4 -1
  201. package/src/crypto-utils.ts +99 -0
  202. package/src/errors/errors.ts +17 -0
  203. package/src/errors/index.ts +0 -1
  204. package/src/factory-functions.test.ts +42 -2
  205. package/src/function/function-runner.test.ts +4 -11
  206. package/src/function/function-runner.ts +114 -29
  207. package/src/function/functions.types.ts +62 -132
  208. package/src/function/index.ts +5 -2
  209. package/src/handle-error.ts +2 -10
  210. package/src/index.ts +99 -25
  211. package/src/middleware/auth-apikey.test.ts +15 -12
  212. package/src/middleware/auth-apikey.ts +3 -3
  213. package/src/middleware/auth-bearer.test.ts +17 -14
  214. package/src/middleware/auth-bearer.ts +25 -27
  215. package/src/middleware/auth-cookie.test.ts +16 -13
  216. package/src/middleware/auth-cookie.ts +6 -8
  217. package/src/middleware/cors.ts +96 -0
  218. package/src/middleware/index.ts +5 -3
  219. package/src/middleware/remote-auth.ts +68 -0
  220. package/src/middleware-runner.test.ts +122 -0
  221. package/src/middleware-runner.ts +12 -12
  222. package/src/permissions.test.ts +81 -7
  223. package/src/permissions.ts +15 -17
  224. package/src/pikku-state.ts +20 -8
  225. package/src/run-tests-script.test.ts +49 -0
  226. package/src/schema.ts +18 -15
  227. package/src/services/ai-agent-runner-service.ts +29 -0
  228. package/src/services/ai-run-state-service.ts +20 -0
  229. package/src/services/ai-storage-service.ts +39 -0
  230. package/src/services/deployment-service.ts +22 -0
  231. package/src/services/gopass-secrets.ts +95 -0
  232. package/src/services/in-memory-trigger-service.ts +68 -0
  233. package/src/services/in-memory-workflow-service.ts +395 -0
  234. package/src/services/index.ts +49 -14
  235. package/src/services/local-secrets.ts +51 -0
  236. package/src/services/local-variables.ts +22 -0
  237. package/src/services/scheduler-service.ts +29 -1
  238. package/src/services/schema-service.ts +7 -0
  239. package/src/services/scoped-secret-service.ts +41 -0
  240. package/src/services/secret-service.ts +25 -4
  241. package/src/services/trigger-service.ts +38 -0
  242. package/src/services/typed-secret-service.ts +70 -0
  243. package/src/services/typed-variables-service.ts +81 -0
  244. package/src/services/user-session-service.ts +23 -0
  245. package/src/services/variables-service.ts +5 -0
  246. package/src/services/workflow-service.ts +22 -2
  247. package/src/types/core.types.ts +76 -11
  248. package/src/types/state.types.ts +37 -63
  249. package/src/utils.ts +20 -6
  250. package/src/version.test.ts +80 -0
  251. package/src/version.ts +25 -0
  252. package/src/wirings/ai-agent/ai-agent-memory.ts +300 -0
  253. package/src/wirings/ai-agent/ai-agent-model-config.ts +43 -0
  254. package/src/wirings/ai-agent/ai-agent-prepare.ts +470 -0
  255. package/src/wirings/ai-agent/ai-agent-registry.test.ts +37 -0
  256. package/src/wirings/ai-agent/ai-agent-registry.ts +82 -0
  257. package/src/wirings/ai-agent/ai-agent-runner.test.ts +75 -0
  258. package/src/wirings/ai-agent/ai-agent-runner.ts +137 -0
  259. package/src/wirings/ai-agent/ai-agent-stream.test.ts +246 -0
  260. package/src/wirings/ai-agent/ai-agent-stream.ts +774 -0
  261. package/src/wirings/ai-agent/ai-agent.types.ts +273 -0
  262. package/src/wirings/ai-agent/index.ts +28 -0
  263. package/src/wirings/channel/channel-common.ts +32 -19
  264. package/src/wirings/channel/channel-handler.ts +17 -4
  265. package/src/wirings/channel/channel-middleware-runner.ts +119 -0
  266. package/src/wirings/channel/channel-runner.ts +9 -10
  267. package/src/wirings/channel/channel.types.ts +17 -1
  268. package/src/wirings/channel/define-channel-routes.ts +25 -0
  269. package/src/wirings/channel/index.ts +23 -8
  270. package/src/wirings/channel/local/index.ts +3 -3
  271. package/src/wirings/channel/local/local-channel-runner.test.ts +44 -1
  272. package/src/wirings/channel/local/local-channel-runner.ts +22 -6
  273. package/src/wirings/channel/serverless/index.ts +5 -1
  274. package/src/wirings/channel/serverless/serverless-channel-runner.ts +20 -4
  275. package/src/wirings/cli/channel/index.ts +1 -1
  276. package/src/wirings/cli/cli-runner.test.ts +9 -9
  277. package/src/wirings/cli/cli-runner.ts +17 -10
  278. package/src/wirings/cli/cli.types.ts +11 -11
  279. package/src/wirings/cli/command-parser.test.ts +8 -8
  280. package/src/wirings/cli/define-cli-commands.ts +24 -0
  281. package/src/wirings/cli/index.ts +10 -4
  282. package/src/wirings/http/http-routes.test.ts +322 -0
  283. package/src/wirings/http/http-routes.ts +198 -0
  284. package/src/wirings/http/http-runner.test.ts +3 -3
  285. package/src/wirings/http/http-runner.ts +63 -29
  286. package/src/wirings/http/http.types.ts +121 -37
  287. package/src/wirings/http/index.ts +16 -4
  288. package/src/wirings/mcp/index.ts +27 -3
  289. package/src/wirings/mcp/mcp-endpoint-registry.test.ts +3 -3
  290. package/src/wirings/mcp/mcp-runner.ts +39 -50
  291. package/src/wirings/mcp/mcp.types.ts +3 -3
  292. package/src/wirings/node/index.ts +2 -0
  293. package/src/wirings/node/node.types.ts +23 -0
  294. package/src/wirings/oauth2/index.ts +3 -0
  295. package/src/wirings/oauth2/oauth2-client.test.ts +929 -0
  296. package/src/wirings/oauth2/oauth2-client.ts +335 -0
  297. package/src/wirings/oauth2/oauth2.types.ts +69 -0
  298. package/src/wirings/oauth2/wire-oauth2-credential.ts +23 -0
  299. package/src/wirings/queue/index.ts +14 -2
  300. package/src/wirings/queue/queue-runner.test.ts +97 -55
  301. package/src/wirings/queue/queue-runner.ts +33 -7
  302. package/src/wirings/queue/queue.types.ts +12 -2
  303. package/src/wirings/rpc/index.ts +1 -1
  304. package/src/wirings/rpc/rpc-runner.ts +187 -17
  305. package/src/wirings/rpc/rpc-types.ts +13 -4
  306. package/src/wirings/scheduler/index.ts +7 -4
  307. package/src/wirings/scheduler/scheduler-runner.test.ts +70 -26
  308. package/src/wirings/scheduler/scheduler-runner.ts +45 -5
  309. package/src/wirings/secret/index.ts +9 -0
  310. package/src/wirings/secret/secret.types.ts +32 -0
  311. package/src/wirings/secret/validate-secret-definitions.ts +82 -0
  312. package/src/wirings/trigger/index.ts +10 -2
  313. package/src/wirings/trigger/pikku-trigger-service.ts +134 -0
  314. package/src/wirings/trigger/trigger-runner.ts +80 -38
  315. package/src/wirings/trigger/trigger.types.ts +144 -22
  316. package/src/wirings/variable/index.ts +8 -0
  317. package/src/wirings/variable/validate-variable-definitions.ts +69 -0
  318. package/src/wirings/variable/variable.types.ts +22 -0
  319. package/src/wirings/workflow/dsl/index.ts +1 -0
  320. package/src/wirings/workflow/dsl/workflow-dsl.types.ts +9 -1
  321. package/src/wirings/workflow/dsl/workflow-runner.ts +4 -3
  322. package/src/wirings/workflow/graph/graph-node.ts +1 -6
  323. package/src/wirings/workflow/graph/graph-runner.test.ts +495 -0
  324. package/src/wirings/workflow/graph/graph-runner.ts +496 -374
  325. package/src/wirings/workflow/graph/index.ts +14 -3
  326. package/src/wirings/workflow/graph/template.ts +42 -0
  327. package/src/wirings/workflow/graph/wire-workflow-graph.ts +29 -0
  328. package/src/wirings/workflow/graph/workflow-graph.types.ts +0 -22
  329. package/src/wirings/workflow/index.ts +21 -10
  330. package/src/wirings/workflow/pikku-workflow-service.test.ts +212 -0
  331. package/src/wirings/workflow/pikku-workflow-service.ts +285 -66
  332. package/src/wirings/workflow/workflow-helpers.ts +79 -0
  333. package/src/wirings/workflow/workflow-utils.ts +0 -120
  334. package/src/wirings/workflow/workflow.types.ts +45 -90
  335. package/tsconfig.tsbuildinfo +1 -1
  336. package/dist/wirings/forge-node/forge-node.types.d.ts +0 -120
  337. package/dist/wirings/forge-node/forge-node.types.js +0 -38
  338. package/dist/wirings/forge-node/index.d.ts +0 -1
  339. package/dist/wirings/forge-node/index.js +0 -1
  340. package/dist/wirings/workflow/wire-workflow.d.ts +0 -42
  341. package/dist/wirings/workflow/wire-workflow.js +0 -53
  342. package/src/wirings/forge-node/forge-node.types.ts +0 -135
  343. package/src/wirings/forge-node/index.ts +0 -1
  344. package/src/wirings/workflow/wire-workflow.ts +0 -94
@@ -0,0 +1,470 @@
1
+ import {
2
+ type CoreSingletonServices,
3
+ type CoreServices,
4
+ type CoreUserSession,
5
+ type CreateWireServices,
6
+ PikkuWire,
7
+ } from '../../types/core.types.js'
8
+ import type {
9
+ CoreAIAgent,
10
+ AIAgentInput,
11
+ AIAgentToolDef,
12
+ AIMessage,
13
+ AIStreamChannel,
14
+ AIStreamEvent,
15
+ } from './ai-agent.types.js'
16
+ import type { AIAgentRunnerParams } from '../../services/ai-agent-runner-service.js'
17
+ import { PikkuError } from '../../errors/error-handler.js'
18
+ import { pikkuState } from '../../pikku-state.js'
19
+ import { runPikkuFunc } from '../../function/function-runner.js'
20
+ import { createMiddlewareSessionWireProps } from '../../services/user-session-service.js'
21
+ import type { SessionService } from '../../services/user-session-service.js'
22
+ import { randomUUID } from 'crypto'
23
+
24
+ import {
25
+ resolveMemoryServices,
26
+ loadContextMessages,
27
+ trimMessages,
28
+ } from './ai-agent-memory.js'
29
+ import { resolveModelConfig } from './ai-agent-model-config.js'
30
+
31
+ export type RunAIAgentParams = {
32
+ singletonServices: CoreSingletonServices
33
+ sessionService?: SessionService<CoreUserSession>
34
+ createWireServices?: CreateWireServices<
35
+ CoreSingletonServices,
36
+ CoreServices<CoreSingletonServices>,
37
+ CoreUserSession
38
+ >
39
+ }
40
+
41
+ export type StreamAIAgentOptions = {
42
+ requiresToolApproval?: 'all' | 'explicit' | false
43
+ }
44
+
45
+ export class ToolApprovalRequired extends PikkuError {
46
+ public readonly toolCallId: string
47
+ public readonly toolName: string
48
+ public readonly args: unknown
49
+ public readonly reason?: string
50
+ public readonly displayToolName?: string
51
+ public readonly displayArgs?: unknown
52
+ public readonly agentRunId?: string
53
+
54
+ constructor(
55
+ toolCallId: string,
56
+ toolName: string,
57
+ args: unknown,
58
+ reason?: string,
59
+ displayToolName?: string,
60
+ displayArgs?: unknown,
61
+ agentRunId?: string
62
+ ) {
63
+ super(`Tool '${displayToolName ?? toolName}' requires approval`)
64
+ this.toolCallId = toolCallId
65
+ this.toolName = toolName
66
+ this.args = args
67
+ this.reason = reason
68
+ this.displayToolName = displayToolName
69
+ this.displayArgs = displayArgs
70
+ this.agentRunId = agentRunId
71
+ }
72
+ }
73
+
74
+ export type StreamContext = {
75
+ channel: AIStreamChannel
76
+ options?: StreamAIAgentOptions
77
+ }
78
+
79
+ export const resolveAgent = (
80
+ agentName: string
81
+ ): { agent: CoreAIAgent; packageName: string | null; resolvedName: string } => {
82
+ const mainAgent = pikkuState(null, 'agent', 'agents').get(agentName)
83
+ if (mainAgent) {
84
+ return { agent: mainAgent, packageName: null, resolvedName: agentName }
85
+ }
86
+
87
+ const colonIndex = agentName.indexOf(':')
88
+ if (colonIndex !== -1) {
89
+ const namespace = agentName.substring(0, colonIndex)
90
+ const localName = agentName.substring(colonIndex + 1)
91
+ const externalPackages = pikkuState(null, 'rpc', 'externalPackages')
92
+ const pkgConfig = externalPackages.get(namespace)
93
+ if (pkgConfig) {
94
+ const extAgent = pikkuState(pkgConfig.package, 'agent', 'agents').get(
95
+ localName
96
+ )
97
+ if (extAgent) {
98
+ return {
99
+ agent: extAgent,
100
+ packageName: pkgConfig.package,
101
+ resolvedName: localName,
102
+ }
103
+ }
104
+ }
105
+ }
106
+
107
+ throw new Error(`AI agent not found: ${agentName}`)
108
+ }
109
+
110
+ export function buildInstructions(
111
+ agentName: string,
112
+ packageName: string | null
113
+ ): string {
114
+ const meta = pikkuState(packageName, 'agent', 'agentsMeta')[agentName]
115
+ const instructions = meta?.instructions ?? ''
116
+ const baseInstructions = Array.isArray(instructions)
117
+ ? instructions.join('\n')
118
+ : instructions
119
+
120
+ return meta?.agents?.length
121
+ ? baseInstructions +
122
+ '\n\nWhen calling a sub-agent, provide a short session name that describes the task. ' +
123
+ 'Use the same session name to continue a previous conversation with that agent. ' +
124
+ 'Use a new session name for a new independent task.'
125
+ : baseInstructions
126
+ }
127
+
128
+ export type ScopedChannel = AIStreamChannel & {
129
+ approval: {
130
+ toolCallId: string
131
+ toolName: string
132
+ args: unknown
133
+ runId: string
134
+ } | null
135
+ }
136
+
137
+ export function createScopedChannel(
138
+ parent: AIStreamChannel,
139
+ agentName: string,
140
+ session: string
141
+ ): ScopedChannel {
142
+ let capturedApproval: ScopedChannel['approval'] = null
143
+
144
+ return {
145
+ channelId: `${parent.channelId}:${agentName}:${session}`,
146
+ openingData: parent.openingData,
147
+ get state() {
148
+ return parent.state
149
+ },
150
+ get approval() {
151
+ return capturedApproval
152
+ },
153
+ close: () => {},
154
+ send: (event: AIStreamEvent) => {
155
+ if (event.type === 'done') return
156
+ if (event.type === 'approval-request') {
157
+ capturedApproval = {
158
+ toolCallId: event.toolCallId,
159
+ toolName: event.toolName,
160
+ args: event.args,
161
+ runId: (event as any).runId,
162
+ }
163
+ return
164
+ }
165
+ if (
166
+ event.type === 'text-delta' ||
167
+ event.type === 'reasoning-delta' ||
168
+ event.type === 'tool-call' ||
169
+ event.type === 'tool-result' ||
170
+ event.type === 'usage' ||
171
+ event.type === 'error'
172
+ ) {
173
+ parent.send({ ...event, agent: agentName, session } as AIStreamEvent)
174
+ } else {
175
+ parent.send(event)
176
+ }
177
+ },
178
+ }
179
+ }
180
+
181
+ export function buildToolDefs(
182
+ singletonServices: CoreSingletonServices,
183
+ params: RunAIAgentParams,
184
+ agentSessionMap: Map<string, string>,
185
+ resourceId: string,
186
+ agentName: string,
187
+ packageName: string | null,
188
+ streamContext?: StreamContext
189
+ ): { tools: AIAgentToolDef[]; missingRpcs: string[] } {
190
+ const tools: AIAgentToolDef[] = []
191
+ const missingRpcs: string[] = []
192
+ const approvalPolicy =
193
+ streamContext?.options?.requiresToolApproval ?? 'explicit'
194
+
195
+ const meta = pikkuState(packageName, 'agent', 'agentsMeta')[agentName]
196
+ if (!meta) return { tools, missingRpcs }
197
+
198
+ const metaTools = meta.tools
199
+ const metaAgents = meta.agents
200
+
201
+ if (metaTools?.length) {
202
+ const functionMeta = pikkuState(null, 'function', 'meta')
203
+ const schemas = pikkuState(null, 'misc', 'schemas')
204
+
205
+ for (const toolName of metaTools) {
206
+ const rpcMeta = pikkuState(null, 'rpc', 'meta')
207
+ const pikkuFuncId = rpcMeta[toolName]
208
+ if (!pikkuFuncId) {
209
+ missingRpcs.push(toolName)
210
+ continue
211
+ }
212
+
213
+ const fnMeta = functionMeta[pikkuFuncId]
214
+ const inputSchemaName = fnMeta?.inputSchemaName
215
+ let inputSchema = inputSchemaName
216
+ ? schemas.get(inputSchemaName)
217
+ : undefined
218
+ if (
219
+ !inputSchema ||
220
+ (typeof inputSchema === 'object' &&
221
+ inputSchema.type === 'object' &&
222
+ !inputSchema.properties)
223
+ ) {
224
+ inputSchema = { type: 'object', properties: {} }
225
+ }
226
+
227
+ const needsApproval =
228
+ approvalPolicy === 'all' ||
229
+ (approvalPolicy === 'explicit' && fnMeta?.requiresApproval)
230
+
231
+ if (needsApproval) {
232
+ inputSchema = {
233
+ ...inputSchema,
234
+ properties: {
235
+ ...((inputSchema.properties as Record<string, unknown>) || {}),
236
+ toolApprovalReason: {
237
+ type: 'string',
238
+ description: 'Brief explanation of why this action is needed',
239
+ },
240
+ },
241
+ }
242
+ }
243
+
244
+ tools.push({
245
+ name: pikkuFuncId,
246
+ description: fnMeta?.description || fnMeta?.title || toolName,
247
+ inputSchema,
248
+ execute: async (toolInput: unknown) => {
249
+ if (needsApproval) {
250
+ const { toolApprovalReason, ...cleanArgs } = toolInput as any
251
+ return {
252
+ __approvalRequired: true,
253
+ toolName: pikkuFuncId,
254
+ args: cleanArgs,
255
+ reason: toolApprovalReason,
256
+ }
257
+ }
258
+ const wire: PikkuWire = params.sessionService
259
+ ? { ...createMiddlewareSessionWireProps(params.sessionService) }
260
+ : {}
261
+ return runPikkuFunc('agent', `tool:${pikkuFuncId}`, pikkuFuncId, {
262
+ singletonServices,
263
+ createWireServices: params.createWireServices,
264
+ data: () => toolInput,
265
+ wire,
266
+ sessionService: params.sessionService,
267
+ })
268
+ },
269
+ })
270
+ }
271
+ }
272
+
273
+ if (metaAgents?.length) {
274
+ const allAgentsMeta = pikkuState(null, 'agent', 'agentsMeta')
275
+
276
+ for (const subAgentName of metaAgents) {
277
+ const subMeta = allAgentsMeta[subAgentName]
278
+ if (!subMeta) {
279
+ singletonServices.logger.warn(
280
+ `Sub-agent '${subAgentName}' not found in agent registry`
281
+ )
282
+ continue
283
+ }
284
+
285
+ tools.push({
286
+ name: subAgentName,
287
+ description: subMeta.description,
288
+ inputSchema: {
289
+ type: 'object',
290
+ properties: {
291
+ message: { type: 'string' },
292
+ session: {
293
+ type: 'string',
294
+ description: 'Short session label for thread continuity',
295
+ },
296
+ },
297
+ required: ['message', 'session'],
298
+ },
299
+ execute: async (toolInput: unknown) => {
300
+ const { message, session } = toolInput as {
301
+ message: string
302
+ session: string
303
+ }
304
+ const sessionKey = `${subAgentName}::${session}`
305
+ let threadId = agentSessionMap.get(sessionKey)
306
+ if (!threadId) {
307
+ threadId = `${subAgentName}-${session}-${Date.now()}`
308
+ agentSessionMap.set(sessionKey, threadId)
309
+ }
310
+
311
+ if (streamContext) {
312
+ const { streamAIAgent } = await import('./ai-agent-stream.js')
313
+ const { channel } = streamContext
314
+ channel.send({
315
+ type: 'agent-call',
316
+ agentName: subAgentName,
317
+ session,
318
+ input: message,
319
+ })
320
+ const subChannel = createScopedChannel(
321
+ channel,
322
+ subAgentName,
323
+ session
324
+ )
325
+ await streamAIAgent(
326
+ subAgentName,
327
+ { message, threadId, resourceId },
328
+ subChannel,
329
+ params,
330
+ agentSessionMap,
331
+ streamContext.options
332
+ )
333
+ if (subChannel.approval) {
334
+ return {
335
+ __approvalRequired: true,
336
+ toolName: subAgentName,
337
+ args: toolInput,
338
+ displayToolName: subChannel.approval.toolName,
339
+ displayArgs: subChannel.approval.args,
340
+ agentRunId: subChannel.approval.runId,
341
+ }
342
+ }
343
+ channel.send({
344
+ type: 'agent-result',
345
+ agentName: subAgentName,
346
+ session,
347
+ result: null,
348
+ })
349
+ return null
350
+ }
351
+
352
+ const { runAIAgent } = await import('./ai-agent-runner.js')
353
+ const result = await runAIAgent(
354
+ subAgentName,
355
+ { message, threadId, resourceId },
356
+ params,
357
+ agentSessionMap
358
+ )
359
+ return result.object ?? result.text
360
+ },
361
+ })
362
+ }
363
+ }
364
+
365
+ return { tools, missingRpcs }
366
+ }
367
+
368
+ export async function prepareAgentRun(
369
+ agentName: string,
370
+ input: AIAgentInput,
371
+ params: RunAIAgentParams,
372
+ agentSessionMap: Map<string, string>,
373
+ streamContext?: StreamContext
374
+ ) {
375
+ const { singletonServices } = params
376
+ const { agent, packageName, resolvedName } = resolveAgent(agentName)
377
+
378
+ const agentRunner = singletonServices.aiAgentRunner
379
+ if (!agentRunner) {
380
+ throw new Error('AIAgentRunnerService not available in singletonServices')
381
+ }
382
+
383
+ const { storage } = resolveMemoryServices(agent, singletonServices)
384
+ const memoryConfig = agent.memory
385
+ const threadId = input.threadId
386
+
387
+ const agentsMeta = pikkuState(packageName, 'agent', 'agentsMeta')
388
+ const meta = agentsMeta[resolvedName]
389
+ const outputSchemaName = meta?.outputSchema
390
+ const outputSchema = outputSchemaName
391
+ ? pikkuState(packageName, 'misc', 'schemas').get(outputSchemaName)
392
+ : undefined
393
+
394
+ const workingMemorySchemaName = meta?.workingMemorySchema ?? null
395
+ const workingMemoryJsonSchema = workingMemorySchemaName
396
+ ? pikkuState(packageName, 'misc', 'schemas').get(workingMemorySchemaName)
397
+ : undefined
398
+
399
+ if (storage) {
400
+ try {
401
+ await storage.getThread(threadId)
402
+ } catch {
403
+ await storage.createThread(input.resourceId, { threadId })
404
+ }
405
+ }
406
+
407
+ let messages: AIMessage[] = []
408
+ if (storage) {
409
+ messages = await storage.getMessages(threadId, {
410
+ lastN: memoryConfig?.lastMessages ?? 20,
411
+ })
412
+ }
413
+
414
+ const contextMessages = await loadContextMessages(
415
+ memoryConfig,
416
+ storage,
417
+ input,
418
+ workingMemoryJsonSchema
419
+ )
420
+
421
+ const userMessage: AIMessage = {
422
+ id: randomUUID(),
423
+ role: 'user',
424
+ content: input.message,
425
+ createdAt: new Date(),
426
+ }
427
+
428
+ const allMessages = [...contextMessages, ...messages, userMessage]
429
+ const trimmedMessages = trimMessages(allMessages)
430
+
431
+ const { tools, missingRpcs } = buildToolDefs(
432
+ singletonServices,
433
+ params,
434
+ agentSessionMap,
435
+ input.resourceId,
436
+ resolvedName,
437
+ packageName,
438
+ streamContext
439
+ )
440
+
441
+ const instructions = buildInstructions(resolvedName, packageName)
442
+
443
+ const resolved = resolveModelConfig(resolvedName, agent)
444
+
445
+ const runnerParams: AIAgentRunnerParams = {
446
+ model: resolved.model,
447
+ temperature: resolved.temperature,
448
+ instructions,
449
+ messages: trimmedMessages,
450
+ tools,
451
+ maxSteps: resolved.maxSteps ?? 10,
452
+ toolChoice: agent.toolChoice ?? 'auto',
453
+ outputSchema,
454
+ }
455
+
456
+ return {
457
+ agent,
458
+ packageName,
459
+ resolvedName,
460
+ agentRunner,
461
+ storage,
462
+ memoryConfig,
463
+ threadId,
464
+ userMessage,
465
+ runnerParams,
466
+ missingRpcs,
467
+ workingMemoryJsonSchema,
468
+ workingMemorySchemaName,
469
+ }
470
+ }
@@ -0,0 +1,37 @@
1
+ import { describe, test } from 'node:test'
2
+ import assert from 'node:assert/strict'
3
+
4
+ import { approveAIAgent } from './ai-agent-registry.js'
5
+
6
+ describe('approveAIAgent', () => {
7
+ test('rejects approval when run agent does not match expected agent', async () => {
8
+ const aiRunState = {
9
+ getRun: async () => ({
10
+ runId: 'run-1',
11
+ agentName: 'internal-agent',
12
+ threadId: 'thread-1',
13
+ resourceId: 'resource-1',
14
+ status: 'suspended',
15
+ pendingApprovals: [],
16
+ usage: { inputTokens: 0, outputTokens: 0, model: 'test-model' },
17
+ createdAt: new Date(),
18
+ updatedAt: new Date(),
19
+ }),
20
+ updateRun: async () => {},
21
+ } as any
22
+
23
+ await assert.rejects(
24
+ () =>
25
+ approveAIAgent(
26
+ aiRunState,
27
+ 'run-1',
28
+ [{ toolCallId: 'call-1', approved: true }],
29
+ 'public-agent'
30
+ ),
31
+ {
32
+ message:
33
+ "Run run-1 belongs to agent 'internal-agent', not 'public-agent'",
34
+ }
35
+ )
36
+ })
37
+ })
@@ -0,0 +1,82 @@
1
+ import type { CoreAIAgent, AgentRunState } from './ai-agent.types.js'
2
+ import { pikkuState } from '../../pikku-state.js'
3
+ import { AIRunStateService } from '../../services/ai-run-state-service.js'
4
+ import { PikkuMissingMetaError } from '../../errors/errors.js'
5
+
6
+ export const addAIAgent = (
7
+ agentName: string,
8
+ agent: CoreAIAgent<any, any>,
9
+ packageName: string | null = null
10
+ ) => {
11
+ const agentsMeta = pikkuState(packageName, 'agent', 'agentsMeta')
12
+ const agentMeta = agentsMeta[agentName]
13
+ if (!agentMeta) {
14
+ throw new PikkuMissingMetaError(
15
+ `Missing generated metadata for AI agent '${agentName}'`
16
+ )
17
+ }
18
+ const agents = pikkuState(packageName, 'agent', 'agents')
19
+ if (agents.has(agentName)) {
20
+ throw new Error(`AI agent already exists: ${agentName}`)
21
+ }
22
+ agents.set(agentName, agent)
23
+ }
24
+
25
+ export async function approveAIAgent(
26
+ aiRunState: AIRunStateService,
27
+ runId: string,
28
+ approvals: { toolCallId: string; approved: boolean }[],
29
+ expectedAgentName?: string
30
+ ): Promise<{
31
+ status: 'resumed' | 'suspended'
32
+ runId: string
33
+ approved: string[]
34
+ rejected: string[]
35
+ remainingApprovals: number
36
+ }> {
37
+ if (!aiRunState) throw new Error('AIRunStateService not available')
38
+
39
+ const run = await aiRunState.getRun(runId)
40
+ if (!run) throw new Error('Run not found: ' + runId)
41
+ if (expectedAgentName && run.agentName !== expectedAgentName) {
42
+ throw new Error(
43
+ `Run ${runId} belongs to agent '${run.agentName}', not '${expectedAgentName}'`
44
+ )
45
+ }
46
+ if (run.status !== 'suspended')
47
+ throw new Error('Run is not suspended: ' + run.status)
48
+
49
+ const approvedIds = new Set(
50
+ approvals.filter((a) => a.approved).map((a) => a.toolCallId)
51
+ )
52
+ const rejectedIds = new Set(
53
+ approvals.filter((a) => !a.approved).map((a) => a.toolCallId)
54
+ )
55
+
56
+ const remaining = (run.pendingApprovals ?? []).filter(
57
+ (p: NonNullable<AgentRunState['pendingApprovals']>[number]) =>
58
+ !approvedIds.has(p.toolCallId) && !rejectedIds.has(p.toolCallId)
59
+ )
60
+
61
+ const hasApproved = approvedIds.size > 0
62
+ await aiRunState.updateRun(runId, {
63
+ status: hasApproved ? 'running' : 'suspended',
64
+ pendingApprovals: remaining.length > 0 ? remaining : undefined,
65
+ })
66
+
67
+ return {
68
+ status: hasApproved ? 'resumed' : 'suspended',
69
+ runId,
70
+ approved: [...approvedIds],
71
+ rejected: [...rejectedIds],
72
+ remainingApprovals: remaining.length,
73
+ }
74
+ }
75
+
76
+ export const getAIAgents = () => {
77
+ return pikkuState(null, 'agent', 'agents')
78
+ }
79
+
80
+ export const getAIAgentsMeta = () => {
81
+ return pikkuState(null, 'agent', 'agentsMeta')
82
+ }
@@ -0,0 +1,75 @@
1
+ import { beforeEach, describe, test } from 'node:test'
2
+ import assert from 'node:assert/strict'
3
+
4
+ import { resetPikkuState, pikkuState } from '../../pikku-state.js'
5
+ import { runAIAgent } from './ai-agent-runner.js'
6
+ import type { CoreAIAgent } from './ai-agent.types.js'
7
+
8
+ beforeEach(() => {
9
+ resetPikkuState()
10
+ })
11
+
12
+ const addTestAgent = (agentName: string) => {
13
+ const agent: CoreAIAgent = {
14
+ name: agentName,
15
+ description: 'test agent',
16
+ instructions: 'be helpful',
17
+ model: 'test-model',
18
+ }
19
+
20
+ pikkuState(null, 'agent', 'agentsMeta')[agentName] = {
21
+ ...agent,
22
+ inputSchema: null,
23
+ outputSchema: null,
24
+ workingMemorySchema: null,
25
+ }
26
+ pikkuState(null, 'agent', 'agents').set(agentName, agent)
27
+ pikkuState(null, 'models', 'config', {
28
+ models: { 'test-model': 'test/test-model' },
29
+ })
30
+ }
31
+
32
+ describe('runAIAgent', () => {
33
+ test('marks run as failed when runner throws', async () => {
34
+ addTestAgent('failing-agent')
35
+
36
+ const updates: unknown[] = []
37
+ const expectedError = new Error('runner failed')
38
+
39
+ const singletonServices = {
40
+ logger: {
41
+ info: () => {},
42
+ warn: () => {},
43
+ error: () => {},
44
+ debug: () => {},
45
+ },
46
+ aiAgentRunner: {
47
+ run: async () => {
48
+ throw expectedError
49
+ },
50
+ },
51
+ aiRunState: {
52
+ createRun: async () => 'run-1',
53
+ updateRun: async (_runId: string, patch: unknown) => {
54
+ updates.push(patch)
55
+ },
56
+ },
57
+ } as any
58
+
59
+ await assert.rejects(
60
+ () =>
61
+ runAIAgent(
62
+ 'failing-agent',
63
+ {
64
+ message: 'hello',
65
+ threadId: 'thread-1',
66
+ resourceId: 'resource-1',
67
+ },
68
+ { singletonServices }
69
+ ),
70
+ expectedError
71
+ )
72
+
73
+ assert.deepEqual(updates, [{ status: 'failed' }])
74
+ })
75
+ })