@pikku/core 0.12.2 → 0.12.4

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 (282) hide show
  1. package/CHANGELOG.md +24 -0
  2. package/dist/crypto-utils.d.ts +10 -0
  3. package/dist/crypto-utils.js +62 -0
  4. package/dist/function/function-runner.js +4 -4
  5. package/dist/function/functions.types.d.ts +26 -1
  6. package/dist/function/functions.types.js +16 -0
  7. package/dist/handle-error.d.ts +1 -1
  8. package/dist/handle-error.js +7 -3
  9. package/dist/index.d.ts +14 -2
  10. package/dist/index.js +1 -1
  11. package/dist/pikku-state.js +3 -1
  12. package/dist/schema.js +1 -1
  13. package/dist/services/in-memory-workflow-service.d.ts +22 -3
  14. package/dist/services/in-memory-workflow-service.js +77 -1
  15. package/dist/services/workflow-service.d.ts +8 -2
  16. package/dist/testing/index.d.ts +2 -0
  17. package/dist/testing/index.js +1 -0
  18. package/dist/testing/service-tests.d.ts +31 -0
  19. package/dist/testing/service-tests.js +598 -0
  20. package/dist/types/core.types.d.ts +9 -1
  21. package/dist/types/state.types.d.ts +4 -2
  22. package/dist/utils/hash.d.ts +2 -0
  23. package/dist/utils/hash.js +23 -0
  24. package/dist/wirings/ai-agent/agent-dynamic-workflow.d.ts +6 -0
  25. package/dist/wirings/ai-agent/agent-dynamic-workflow.js +348 -0
  26. package/dist/wirings/ai-agent/ai-agent-memory.d.ts +1 -1
  27. package/dist/wirings/ai-agent/ai-agent-memory.js +6 -5
  28. package/dist/wirings/ai-agent/ai-agent-prepare.d.ts +10 -7
  29. package/dist/wirings/ai-agent/ai-agent-prepare.js +127 -47
  30. package/dist/wirings/ai-agent/ai-agent-runner.d.ts +4 -0
  31. package/dist/wirings/ai-agent/ai-agent-runner.js +381 -27
  32. package/dist/wirings/ai-agent/ai-agent-stream.d.ts +7 -2
  33. package/dist/wirings/ai-agent/ai-agent-stream.js +109 -46
  34. package/dist/wirings/ai-agent/ai-agent.types.d.ts +22 -1
  35. package/dist/wirings/ai-agent/index.d.ts +1 -2
  36. package/dist/wirings/ai-agent/index.js +1 -2
  37. package/dist/wirings/channel/channel-handler.js +5 -1
  38. package/dist/wirings/channel/channel.types.d.ts +1 -7
  39. package/dist/wirings/channel/local/local-channel-handler.d.ts +1 -1
  40. package/dist/wirings/channel/local/local-channel-handler.js +5 -3
  41. package/dist/wirings/channel/local/local-channel-runner.js +14 -4
  42. package/dist/wirings/channel/serverless/serverless-channel-runner.js +4 -1
  43. package/dist/wirings/cli/cli-runner.js +10 -2
  44. package/dist/wirings/cli/cli.types.d.ts +1 -0
  45. package/dist/wirings/http/http-routes.js +0 -1
  46. package/dist/wirings/http/http-runner.d.ts +1 -1
  47. package/dist/wirings/http/http-runner.js +25 -3
  48. package/dist/wirings/http/http.types.d.ts +3 -11
  49. package/dist/wirings/http/index.d.ts +2 -1
  50. package/dist/wirings/http/index.js +1 -0
  51. package/dist/wirings/http/pikku-fetch-http-request.d.ts +1 -0
  52. package/dist/wirings/http/pikku-fetch-http-request.js +3 -0
  53. package/dist/wirings/http/pikku-fetch-http-response.d.ts +1 -0
  54. package/dist/wirings/http/pikku-fetch-http-response.js +4 -4
  55. package/dist/wirings/http/web-request.d.ts +12 -0
  56. package/dist/wirings/http/web-request.js +85 -0
  57. package/dist/wirings/mcp/mcp-endpoint-registry.js +1 -1
  58. package/dist/wirings/mcp/mcp-runner.js +13 -1
  59. package/dist/wirings/rpc/rpc-runner.d.ts +30 -6
  60. package/dist/wirings/rpc/rpc-runner.js +36 -14
  61. package/dist/wirings/rpc/rpc-types.d.ts +6 -1
  62. package/dist/wirings/rpc/wire-addon.d.ts +1 -0
  63. package/dist/wirings/rpc/wire-addon.js +1 -1
  64. package/dist/wirings/scheduler/scheduler-runner.js +1 -0
  65. package/dist/wirings/workflow/graph/graph-validation.d.ts +3 -0
  66. package/dist/wirings/workflow/graph/graph-validation.js +175 -0
  67. package/dist/wirings/workflow/graph/index.d.ts +1 -0
  68. package/dist/wirings/workflow/graph/index.js +1 -0
  69. package/dist/wirings/workflow/index.d.ts +1 -1
  70. package/dist/wirings/workflow/pikku-workflow-service.d.ts +8 -2
  71. package/dist/wirings/workflow/pikku-workflow-service.js +1 -2
  72. package/dist/wirings/workflow/workflow.types.d.ts +10 -1
  73. package/lcov.info +18891 -0
  74. package/package.json +4 -2
  75. package/dist/wirings/ai-agent/ai-agent-assistant-ui.d.ts +0 -5
  76. package/dist/wirings/ai-agent/ai-agent-assistant-ui.js +0 -168
  77. package/src/crypto-utils.test.ts +0 -116
  78. package/src/crypto-utils.ts +0 -99
  79. package/src/errors/error-handler.ts +0 -62
  80. package/src/errors/error.test.ts +0 -195
  81. package/src/errors/errors.ts +0 -374
  82. package/src/errors/index.ts +0 -2
  83. package/src/factory-functions.test.ts +0 -109
  84. package/src/function/function-runner.test.ts +0 -536
  85. package/src/function/function-runner.ts +0 -365
  86. package/src/function/functions.types.ts +0 -267
  87. package/src/function/index.ts +0 -5
  88. package/src/handle-error.test.ts +0 -361
  89. package/src/handle-error.ts +0 -65
  90. package/src/index.ts +0 -104
  91. package/src/internal.ts +0 -6
  92. package/src/middleware/auth-apikey.test.ts +0 -363
  93. package/src/middleware/auth-apikey.ts +0 -47
  94. package/src/middleware/auth-bearer.test.ts +0 -450
  95. package/src/middleware/auth-bearer.ts +0 -72
  96. package/src/middleware/auth-cookie.test.ts +0 -528
  97. package/src/middleware/auth-cookie.ts +0 -80
  98. package/src/middleware/cors.test.ts +0 -424
  99. package/src/middleware/cors.ts +0 -104
  100. package/src/middleware/index.ts +0 -5
  101. package/src/middleware/remote-auth.test.ts +0 -488
  102. package/src/middleware/remote-auth.ts +0 -68
  103. package/src/middleware/timeout.ts +0 -15
  104. package/src/middleware-runner.test.ts +0 -418
  105. package/src/middleware-runner.ts +0 -234
  106. package/src/permissions.test.ts +0 -434
  107. package/src/permissions.ts +0 -319
  108. package/src/pikku-request.ts +0 -23
  109. package/src/pikku-response.ts +0 -5
  110. package/src/pikku-state.test.ts +0 -224
  111. package/src/pikku-state.ts +0 -214
  112. package/src/run-tests-script.test.ts +0 -49
  113. package/src/schema.test.ts +0 -249
  114. package/src/schema.ts +0 -151
  115. package/src/services/ai-agent-runner-service.ts +0 -41
  116. package/src/services/ai-run-state-service.ts +0 -20
  117. package/src/services/ai-storage-service.ts +0 -39
  118. package/src/services/content-service.ts +0 -76
  119. package/src/services/deployment-service.ts +0 -22
  120. package/src/services/gateway-service.ts +0 -20
  121. package/src/services/gopass-secrets.ts +0 -98
  122. package/src/services/in-memory-ai-run-state-service.ts +0 -73
  123. package/src/services/in-memory-trigger-service.ts +0 -64
  124. package/src/services/in-memory-workflow-service.test.ts +0 -351
  125. package/src/services/in-memory-workflow-service.ts +0 -395
  126. package/src/services/index.ts +0 -56
  127. package/src/services/jwt-service.ts +0 -30
  128. package/src/services/local-content.ts +0 -120
  129. package/src/services/local-gateway-service.ts +0 -62
  130. package/src/services/local-secrets.test.ts +0 -80
  131. package/src/services/local-secrets.ts +0 -94
  132. package/src/services/local-variables.test.ts +0 -61
  133. package/src/services/local-variables.ts +0 -39
  134. package/src/services/logger-console.test.ts +0 -118
  135. package/src/services/logger-console.ts +0 -98
  136. package/src/services/logger.ts +0 -57
  137. package/src/services/scheduler-service.ts +0 -86
  138. package/src/services/schema-service.ts +0 -33
  139. package/src/services/scoped-secret-service.test.ts +0 -74
  140. package/src/services/scoped-secret-service.ts +0 -41
  141. package/src/services/secret-service.ts +0 -38
  142. package/src/services/trigger-service.ts +0 -17
  143. package/src/services/typed-secret-service.test.ts +0 -93
  144. package/src/services/typed-secret-service.ts +0 -70
  145. package/src/services/typed-variables-service.test.ts +0 -73
  146. package/src/services/typed-variables-service.ts +0 -81
  147. package/src/services/user-session-service.test.ts +0 -113
  148. package/src/services/user-session-service.ts +0 -86
  149. package/src/services/variables-service.ts +0 -11
  150. package/src/services/workflow-service.ts +0 -84
  151. package/src/time-utils.test.ts +0 -56
  152. package/src/time-utils.ts +0 -107
  153. package/src/types/core.types.ts +0 -470
  154. package/src/types/state.types.ts +0 -186
  155. package/src/utils.test.ts +0 -350
  156. package/src/utils.ts +0 -129
  157. package/src/version.test.ts +0 -80
  158. package/src/version.ts +0 -25
  159. package/src/wirings/ai-agent/ai-agent-assistant-ui.test.ts +0 -363
  160. package/src/wirings/ai-agent/ai-agent-assistant-ui.ts +0 -238
  161. package/src/wirings/ai-agent/ai-agent-helpers.test.ts +0 -152
  162. package/src/wirings/ai-agent/ai-agent-helpers.ts +0 -76
  163. package/src/wirings/ai-agent/ai-agent-memory.ts +0 -309
  164. package/src/wirings/ai-agent/ai-agent-model-config.test.ts +0 -115
  165. package/src/wirings/ai-agent/ai-agent-model-config.ts +0 -43
  166. package/src/wirings/ai-agent/ai-agent-prepare.ts +0 -522
  167. package/src/wirings/ai-agent/ai-agent-registry.test.ts +0 -37
  168. package/src/wirings/ai-agent/ai-agent-registry.ts +0 -82
  169. package/src/wirings/ai-agent/ai-agent-runner.test.ts +0 -317
  170. package/src/wirings/ai-agent/ai-agent-runner.ts +0 -251
  171. package/src/wirings/ai-agent/ai-agent-stream.test.ts +0 -652
  172. package/src/wirings/ai-agent/ai-agent-stream.ts +0 -1060
  173. package/src/wirings/ai-agent/ai-agent.types.ts +0 -360
  174. package/src/wirings/ai-agent/index.ts +0 -41
  175. package/src/wirings/channel/channel-common.ts +0 -90
  176. package/src/wirings/channel/channel-handler.ts +0 -241
  177. package/src/wirings/channel/channel-middleware-runner.ts +0 -120
  178. package/src/wirings/channel/channel-runner.ts +0 -166
  179. package/src/wirings/channel/channel-store.ts +0 -29
  180. package/src/wirings/channel/channel.types.ts +0 -178
  181. package/src/wirings/channel/define-channel-routes.ts +0 -25
  182. package/src/wirings/channel/eventhub-service.ts +0 -34
  183. package/src/wirings/channel/eventhub-store.ts +0 -13
  184. package/src/wirings/channel/index.ts +0 -24
  185. package/src/wirings/channel/local/index.ts +0 -3
  186. package/src/wirings/channel/local/local-channel-handler.ts +0 -79
  187. package/src/wirings/channel/local/local-channel-runner.test.ts +0 -215
  188. package/src/wirings/channel/local/local-channel-runner.ts +0 -200
  189. package/src/wirings/channel/local/local-eventhub-service.test.ts +0 -95
  190. package/src/wirings/channel/local/local-eventhub-service.ts +0 -101
  191. package/src/wirings/channel/log-channels.ts +0 -20
  192. package/src/wirings/channel/pikku-abstract-channel-handler.test.ts +0 -54
  193. package/src/wirings/channel/pikku-abstract-channel-handler.ts +0 -45
  194. package/src/wirings/channel/serverless/index.ts +0 -5
  195. package/src/wirings/channel/serverless/serverless-channel-runner.ts +0 -286
  196. package/src/wirings/cli/channel/cli-channel-runner.ts +0 -136
  197. package/src/wirings/cli/channel/index.ts +0 -1
  198. package/src/wirings/cli/cli-runner.test.ts +0 -403
  199. package/src/wirings/cli/cli-runner.ts +0 -519
  200. package/src/wirings/cli/cli.types.ts +0 -357
  201. package/src/wirings/cli/command-parser.test.ts +0 -445
  202. package/src/wirings/cli/command-parser.ts +0 -504
  203. package/src/wirings/cli/define-cli-commands.ts +0 -24
  204. package/src/wirings/cli/index.ts +0 -19
  205. package/src/wirings/gateway/gateway-runner.test.ts +0 -474
  206. package/src/wirings/gateway/gateway-runner.ts +0 -411
  207. package/src/wirings/gateway/gateway.types.ts +0 -149
  208. package/src/wirings/gateway/index.ts +0 -13
  209. package/src/wirings/http/http-routes.test.ts +0 -322
  210. package/src/wirings/http/http-routes.ts +0 -198
  211. package/src/wirings/http/http-runner.test.ts +0 -144
  212. package/src/wirings/http/http-runner.ts +0 -563
  213. package/src/wirings/http/http.types.ts +0 -378
  214. package/src/wirings/http/index.ts +0 -25
  215. package/src/wirings/http/log-http-routes.ts +0 -22
  216. package/src/wirings/http/pikku-fetch-http-request.test.ts +0 -237
  217. package/src/wirings/http/pikku-fetch-http-request.ts +0 -166
  218. package/src/wirings/http/pikku-fetch-http-response.test.ts +0 -82
  219. package/src/wirings/http/pikku-fetch-http-response.ts +0 -147
  220. package/src/wirings/http/routers/http-router.ts +0 -13
  221. package/src/wirings/http/routers/path-to-regex.test.ts +0 -319
  222. package/src/wirings/http/routers/path-to-regex.ts +0 -126
  223. package/src/wirings/mcp/index.ts +0 -27
  224. package/src/wirings/mcp/mcp-endpoint-registry.test.ts +0 -389
  225. package/src/wirings/mcp/mcp-endpoint-registry.ts +0 -159
  226. package/src/wirings/mcp/mcp-runner.ts +0 -305
  227. package/src/wirings/mcp/mcp.types.ts +0 -216
  228. package/src/wirings/node/index.ts +0 -2
  229. package/src/wirings/node/node.types.ts +0 -23
  230. package/src/wirings/oauth2/index.ts +0 -3
  231. package/src/wirings/oauth2/oauth2-client.test.ts +0 -929
  232. package/src/wirings/oauth2/oauth2-client.ts +0 -335
  233. package/src/wirings/oauth2/oauth2.types.ts +0 -69
  234. package/src/wirings/oauth2/wire-oauth2-credential.ts +0 -23
  235. package/src/wirings/queue/index.ts +0 -31
  236. package/src/wirings/queue/queue-runner.test.ts +0 -710
  237. package/src/wirings/queue/queue-runner.ts +0 -192
  238. package/src/wirings/queue/queue.types.ts +0 -189
  239. package/src/wirings/queue/register-queue-helper.ts +0 -60
  240. package/src/wirings/queue/validate-worker-config.test.ts +0 -108
  241. package/src/wirings/queue/validate-worker-config.ts +0 -116
  242. package/src/wirings/rpc/index.ts +0 -4
  243. package/src/wirings/rpc/rpc-runner.ts +0 -439
  244. package/src/wirings/rpc/rpc-types.ts +0 -51
  245. package/src/wirings/rpc/wire-addon.ts +0 -20
  246. package/src/wirings/scheduler/index.ts +0 -11
  247. package/src/wirings/scheduler/log-schedulers.ts +0 -20
  248. package/src/wirings/scheduler/scheduler-runner.test.ts +0 -660
  249. package/src/wirings/scheduler/scheduler-runner.ts +0 -132
  250. package/src/wirings/scheduler/scheduler.types.ts +0 -53
  251. package/src/wirings/secret/index.ts +0 -9
  252. package/src/wirings/secret/secret.types.ts +0 -32
  253. package/src/wirings/secret/validate-secret-definitions.test.ts +0 -140
  254. package/src/wirings/secret/validate-secret-definitions.ts +0 -82
  255. package/src/wirings/trigger/index.ts +0 -10
  256. package/src/wirings/trigger/pikku-trigger-service.ts +0 -112
  257. package/src/wirings/trigger/trigger-runner.test.ts +0 -79
  258. package/src/wirings/trigger/trigger-runner.ts +0 -135
  259. package/src/wirings/trigger/trigger.types.ts +0 -178
  260. package/src/wirings/variable/index.ts +0 -8
  261. package/src/wirings/variable/validate-variable-definitions.test.ts +0 -91
  262. package/src/wirings/variable/validate-variable-definitions.ts +0 -69
  263. package/src/wirings/variable/variable.types.ts +0 -22
  264. package/src/wirings/workflow/dsl/index.ts +0 -31
  265. package/src/wirings/workflow/dsl/workflow-dsl.types.ts +0 -320
  266. package/src/wirings/workflow/dsl/workflow-runner.ts +0 -28
  267. package/src/wirings/workflow/graph/graph-node.ts +0 -222
  268. package/src/wirings/workflow/graph/graph-runner.test.ts +0 -487
  269. package/src/wirings/workflow/graph/graph-runner.ts +0 -816
  270. package/src/wirings/workflow/graph/index.ts +0 -14
  271. package/src/wirings/workflow/graph/template.test.ts +0 -49
  272. package/src/wirings/workflow/graph/template.ts +0 -42
  273. package/src/wirings/workflow/graph/wire-workflow-graph.ts +0 -29
  274. package/src/wirings/workflow/graph/workflow-graph.types.ts +0 -104
  275. package/src/wirings/workflow/index.ts +0 -81
  276. package/src/wirings/workflow/pikku-workflow-service.test.ts +0 -200
  277. package/src/wirings/workflow/pikku-workflow-service.ts +0 -1165
  278. package/src/wirings/workflow/workflow-helpers.test.ts +0 -129
  279. package/src/wirings/workflow/workflow-helpers.ts +0 -79
  280. package/src/wirings/workflow/workflow.types.ts +0 -266
  281. package/tsconfig.json +0 -14
  282. package/tsconfig.tsbuildinfo +0 -1
@@ -0,0 +1,23 @@
1
+ import { createHash } from 'crypto';
2
+ export function canonicalJSON(obj) {
3
+ return JSON.stringify(sortDeep(obj));
4
+ }
5
+ function sortDeep(value) {
6
+ if (value === null || value === undefined) {
7
+ return value;
8
+ }
9
+ if (Array.isArray(value)) {
10
+ return value.map(sortDeep);
11
+ }
12
+ if (typeof value === 'object') {
13
+ const sorted = {};
14
+ for (const key of Object.keys(value).sort()) {
15
+ sorted[key] = sortDeep(value[key]);
16
+ }
17
+ return sorted;
18
+ }
19
+ return value;
20
+ }
21
+ export function hashString(input, length = 16) {
22
+ return createHash('sha256').update(input).digest('hex').slice(0, length);
23
+ }
@@ -0,0 +1,6 @@
1
+ import type { AIAgentToolDef } from './ai-agent.types.js';
2
+ import type { StreamContext } from './ai-agent-prepare.js';
3
+ import type { CoreUserSession } from '../../types/core.types.js';
4
+ import type { SessionService } from '../../services/user-session-service.js';
5
+ export declare function buildDynamicWorkflowInstructions(tools: string[], mode: 'read' | 'always' | 'ask'): string;
6
+ export declare function buildWorkflowTools(agentName: string, packageName: string | null, toolNames: string[], mode: 'read' | 'always' | 'ask', streamContext?: StreamContext, sessionService?: SessionService<CoreUserSession>): AIAgentToolDef[];
@@ -0,0 +1,348 @@
1
+ import { pikkuState, getSingletonServices } from '../../pikku-state.js';
2
+ import { canonicalJSON, hashString } from '../../utils/hash.js';
3
+ import { runWorkflowGraph } from '../workflow/graph/graph-runner.js';
4
+ import { ContextAwareRPCService, resolveNamespace } from '../rpc/rpc-runner.js';
5
+ import { createMiddlewareSessionWireProps } from '../../services/user-session-service.js';
6
+ import { validateWorkflowWiring, computeEntryNodeIds, } from '../workflow/graph/graph-validation.js';
7
+ export function buildDynamicWorkflowInstructions(tools, mode) {
8
+ if (mode === 'read') {
9
+ return ('\n\n## Existing Workflows\n\n' +
10
+ 'You can list and execute previously saved workflows using listAgentWorkflows and executeAgentWorkflow.\n' +
11
+ 'Use listAgentWorkflows to discover what\'s available, then executeAgentWorkflow to run them.');
12
+ }
13
+ const modeGuidance = mode === 'always'
14
+ ? 'When a user requests a complex multi-step task, prefer creating a workflow over making sequential tool calls.\n' +
15
+ 'Check if a suitable workflow already exists with listAgentWorkflows first.\n' +
16
+ 'If none exists, create one with createAgentWorkflow, save with saveAgentWorkflow, then execute.\n\n'
17
+ : 'When you receive a request that would benefit from a reusable multi-step workflow,\n' +
18
+ 'suggest creating one to the user. Explain the benefits (reusability, reliability,\n' +
19
+ 'can run in background) and wait for confirmation before creating.\n' +
20
+ 'Do NOT create workflows automatically — always propose and get user approval first.\n' +
21
+ 'For one-off tasks, just use the tools directly.\n\n';
22
+ const toolSchemaLines = [];
23
+ for (const toolName of tools) {
24
+ let fnMeta;
25
+ let schemas;
26
+ const resolved = toolName.includes(':') ? resolveNamespace(toolName) : null;
27
+ if (resolved) {
28
+ fnMeta = pikkuState(resolved.package, 'function', 'meta')[resolved.function];
29
+ schemas = pikkuState(resolved.package, 'misc', 'schemas');
30
+ }
31
+ else {
32
+ const rpcMeta = pikkuState(null, 'rpc', 'meta');
33
+ const pikkuFuncId = rpcMeta[toolName];
34
+ if (!pikkuFuncId)
35
+ continue;
36
+ fnMeta = pikkuState(null, 'function', 'meta')[pikkuFuncId];
37
+ schemas = pikkuState(null, 'misc', 'schemas');
38
+ }
39
+ if (!fnMeta)
40
+ continue;
41
+ const inputSchema = fnMeta.inputSchemaName
42
+ ? schemas.get(fnMeta.inputSchemaName)
43
+ : null;
44
+ const outputSchema = fnMeta.outputSchemaName
45
+ ? schemas.get(fnMeta.outputSchemaName)
46
+ : null;
47
+ const toolDescription = fnMeta.description || fnMeta.title || '';
48
+ const inputProps = inputSchema?.properties
49
+ ? Object.entries(inputSchema.properties)
50
+ .map(([k, v]) => `${k}${inputSchema.required?.includes(k) ? '' : '?'}: ${v.type || 'any'}`)
51
+ .join(', ')
52
+ : '';
53
+ const outputProps = outputSchema?.properties
54
+ ? Object.entries(outputSchema.properties)
55
+ .map(([k, v]) => `${k}: ${v.type || 'any'}`)
56
+ .join(', ')
57
+ : 'any';
58
+ toolSchemaLines.push(`- ${toolName}(input: {${inputProps}}) → {${outputProps}}${toolDescription ? ` — ${toolDescription}` : ''}`);
59
+ }
60
+ return ('\n\n## Workflow Creation\n\n' +
61
+ modeGuidance +
62
+ 'You can create workflows that chain your tools together. Use createAgentWorkflow to validate and preview a workflow, then saveAgentWorkflow to save it, then executeAgentWorkflow to run it.\n\n' +
63
+ '### Tool Schemas:\n' +
64
+ toolSchemaLines.join('\n') +
65
+ '\n\n### Workflow Format:\n' +
66
+ '- Each node has: rpcName (tool name), input (with $ref to wire outputs), next (flow control), onError\n' +
67
+ '- Use {"$ref": "nodeId", "path": "fieldName"} to wire a previous node\'s output field to this node\'s input\n' +
68
+ '- Use {"$ref": "trigger", "path": "fieldName"} to extract a field from the workflow\'s trigger input. IMPORTANT: Always include "path" — never pass {"$ref": "trigger"} without "path", or the entire trigger object will be used as the value.\n' +
69
+ '- Use {"$ref": "nodeId", "path": "fieldName"} to extract a field from a previous node\'s output. Always include "path" to select the specific field.\n' +
70
+ '- next can be a string (single next node), array (parallel), or object {branchKey: nextNode} for branching\n' +
71
+ '\n### Example (add todo from trigger title, then list):\n' +
72
+ '{"add":{"rpcName":"todos:addTodo","input":{"title":{"$ref":"trigger","path":"title"}},"next":"list"},"list":{"rpcName":"todos:listTodos","input":{}}}\n' +
73
+ '\n### Example (add todo, sleep, complete using addTodo result id):\n' +
74
+ '{"add":{"rpcName":"todos:addTodo","input":{"title":{"$ref":"trigger","path":"title"}},"next":"wait"},"wait":{"rpcName":"sleep","input":{"seconds":5},"next":"complete"},"complete":{"rpcName":"todos:completeTodo","input":{"id":{"$ref":"add","path":"id"}}}}');
75
+ }
76
+ export function buildWorkflowTools(agentName, packageName, toolNames, mode, streamContext, sessionService) {
77
+ const tools = [];
78
+ if (mode !== 'read') {
79
+ tools.push({
80
+ name: 'createAgentWorkflow',
81
+ description: 'Validate and create a draft workflow graph that chains tools together. Call saveAgentWorkflow to activate it after the user approves.',
82
+ inputSchema: {
83
+ type: 'object',
84
+ properties: {
85
+ name: {
86
+ type: 'string',
87
+ description: 'Short name for the workflow (will be prefixed with ai:{agentName}:)',
88
+ },
89
+ description: {
90
+ type: 'string',
91
+ description: 'What this workflow does',
92
+ },
93
+ nodes: {
94
+ type: 'string',
95
+ description: 'JSON string of a map of nodeId to node config. Each node has: rpcName (tool name), input (with $ref to wire outputs), next (string|string[]|{branchKey: string}), onError (string|string[]). Example: {"list":{"rpcName":"todos:listTodos","input":{}}}',
96
+ },
97
+ },
98
+ required: ['name', 'nodes'],
99
+ },
100
+ execute: async (input) => {
101
+ const raw = input;
102
+ const name = raw.name.replace(/[^a-zA-Z0-9_-]/g, '-');
103
+ let nodes;
104
+ if (typeof raw.nodes === 'string') {
105
+ try {
106
+ nodes = JSON.parse(raw.nodes);
107
+ }
108
+ catch {
109
+ return { error: 'Invalid JSON in nodes field' };
110
+ }
111
+ }
112
+ else {
113
+ nodes = raw.nodes;
114
+ }
115
+ if (Object.keys(nodes).length < 2) {
116
+ return {
117
+ error: 'A workflow must have at least 2 nodes. A single node is just a tool call — use the tool directly instead.',
118
+ };
119
+ }
120
+ const validationErrors = validateWorkflowWiring(nodes, toolNames);
121
+ if (validationErrors.length > 0) {
122
+ return {
123
+ error: 'Workflow validation failed',
124
+ errors: validationErrors,
125
+ };
126
+ }
127
+ const entryNodeIds = computeEntryNodeIds(nodes);
128
+ if (entryNodeIds.length === 0) {
129
+ return {
130
+ error: 'No entry nodes found. Every node is referenced by another node, creating a cycle.',
131
+ };
132
+ }
133
+ const fullName = `ai:${agentName}:${name}`;
134
+ const graphHash = hashString(canonicalJSON({ nodes, entryNodeIds }));
135
+ const graph = {
136
+ name: fullName,
137
+ pikkuFuncId: fullName,
138
+ source: 'ai-agent',
139
+ description: raw.description,
140
+ nodes,
141
+ entryNodeIds,
142
+ graphHash,
143
+ };
144
+ const singletonServices = getSingletonServices();
145
+ if (!singletonServices?.workflowService) {
146
+ return { error: 'Workflow service not available' };
147
+ }
148
+ await singletonServices.workflowService.upsertWorkflowVersion(fullName, graphHash, graph, 'ai-agent', 'draft');
149
+ return {
150
+ valid: true,
151
+ workflowName: fullName,
152
+ graphHash,
153
+ entryNodes: entryNodeIds,
154
+ nodeCount: Object.keys(nodes).length,
155
+ message: `Workflow '${fullName}' validated and saved as draft. Present this to the user and call saveAgentWorkflow to activate it.`,
156
+ };
157
+ },
158
+ });
159
+ tools.push({
160
+ name: 'saveAgentWorkflow',
161
+ description: 'Activate a previously created draft workflow so it can be executed. Requires user approval.',
162
+ inputSchema: {
163
+ type: 'object',
164
+ properties: {
165
+ name: {
166
+ type: 'string',
167
+ description: 'Full workflow name returned by createAgentWorkflow (e.g. ai:agentName:myWorkflow)',
168
+ },
169
+ graphHash: {
170
+ type: 'string',
171
+ description: 'Graph hash returned by createAgentWorkflow',
172
+ },
173
+ },
174
+ required: ['name', 'graphHash'],
175
+ },
176
+ needsApproval: true,
177
+ approvalDescriptionFn: async (input) => {
178
+ const raw = input;
179
+ const fullName = raw.name.startsWith(`ai:${agentName}:`)
180
+ ? raw.name
181
+ : `ai:${agentName}:${raw.name}`;
182
+ const singletonServices = getSingletonServices();
183
+ if (!singletonServices?.workflowService) {
184
+ return `Activate workflow '${fullName}'`;
185
+ }
186
+ const version = await singletonServices.workflowService.getWorkflowVersion(fullName, raw.graphHash);
187
+ if (version) {
188
+ const graph = version.graph;
189
+ if (graph.nodes) {
190
+ const desc = graph.description ? `\n${graph.description}` : '';
191
+ return `Activate workflow '${fullName}' (${Object.keys(graph.nodes).length} nodes)${desc}`;
192
+ }
193
+ }
194
+ return `Activate workflow '${fullName}'`;
195
+ },
196
+ execute: async (input) => {
197
+ const raw = input;
198
+ const fullName = raw.name.startsWith(`ai:${agentName}:`)
199
+ ? raw.name
200
+ : `ai:${agentName}:${raw.name}`;
201
+ const singletonServices = getSingletonServices();
202
+ if (!singletonServices?.workflowService) {
203
+ return { error: 'Workflow service not available' };
204
+ }
205
+ const version = await singletonServices.workflowService.getWorkflowVersion(fullName, raw.graphHash);
206
+ if (!version) {
207
+ return {
208
+ error: `Workflow '${fullName}' with hash '${raw.graphHash}' not found. Use createAgentWorkflow first.`,
209
+ };
210
+ }
211
+ await singletonServices.workflowService.updateWorkflowVersionStatus(fullName, raw.graphHash, 'active');
212
+ const graph = version.graph;
213
+ const allMeta = pikkuState(null, 'workflows', 'meta');
214
+ allMeta[fullName] = graph;
215
+ if (streamContext) {
216
+ streamContext.channel.send({
217
+ type: 'workflow-created',
218
+ workflowName: fullName,
219
+ graph,
220
+ });
221
+ }
222
+ return {
223
+ success: true,
224
+ workflowName: fullName,
225
+ message: `Workflow '${fullName}' activated and ready to execute.`,
226
+ };
227
+ },
228
+ });
229
+ } // end if (mode !== 'read')
230
+ tools.push({
231
+ name: 'listAgentWorkflows',
232
+ description: 'List previously saved workflows for this agent that can be executed.',
233
+ inputSchema: {
234
+ type: 'object',
235
+ properties: {},
236
+ },
237
+ execute: async () => {
238
+ const singletonServices = getSingletonServices();
239
+ if (!singletonServices?.workflowService) {
240
+ return { error: 'Workflow service not available' };
241
+ }
242
+ const results = [];
243
+ const allMeta = pikkuState(null, 'workflows', 'meta');
244
+ const prefix = `ai:${agentName}:`;
245
+ for (const [name, meta] of Object.entries(allMeta)) {
246
+ if (name.startsWith(prefix) && meta.source === 'ai-agent') {
247
+ results.push({
248
+ name,
249
+ description: meta.description,
250
+ graphHash: meta.graphHash,
251
+ });
252
+ }
253
+ }
254
+ if (results.length === 0) {
255
+ const persisted = await singletonServices.workflowService.getAIGeneratedWorkflows(agentName);
256
+ for (const wf of persisted) {
257
+ results.push({
258
+ name: wf.workflowName,
259
+ graphHash: wf.graphHash,
260
+ });
261
+ }
262
+ }
263
+ return { workflows: results };
264
+ },
265
+ });
266
+ tools.push({
267
+ name: 'executeAgentWorkflow',
268
+ description: 'Execute a previously saved workflow with the given input.',
269
+ inputSchema: {
270
+ type: 'object',
271
+ properties: {
272
+ name: {
273
+ type: 'string',
274
+ description: 'Workflow name (will be auto-prefixed with ai:{agentName}: if needed)',
275
+ },
276
+ input: {
277
+ type: 'object',
278
+ description: 'Input data for the workflow trigger',
279
+ },
280
+ },
281
+ required: ['name'],
282
+ },
283
+ needsApproval: true,
284
+ approvalDescriptionFn: async (input) => {
285
+ const { name, input: workflowInput } = input;
286
+ const fullName = name.startsWith(`ai:${agentName}:`)
287
+ ? name
288
+ : `ai:${agentName}:${name}`;
289
+ const inputStr = workflowInput
290
+ ? `\nInput: ${JSON.stringify(workflowInput)}`
291
+ : '';
292
+ return `Execute workflow '${fullName}'${inputStr}`;
293
+ },
294
+ execute: async (toolInput) => {
295
+ const { name, input: workflowInput } = toolInput;
296
+ const fullName = name.startsWith(`ai:${agentName}:`)
297
+ ? name
298
+ : `ai:${agentName}:${name}`;
299
+ const singletonServices = getSingletonServices();
300
+ if (!singletonServices?.workflowService) {
301
+ return { error: 'Workflow service not available' };
302
+ }
303
+ const allMeta = pikkuState(null, 'workflows', 'meta');
304
+ if (!allMeta[fullName]) {
305
+ return {
306
+ error: `Workflow '${fullName}' not found. Use listAgentWorkflows to see available workflows, or createAgentWorkflow to make a new one.`,
307
+ };
308
+ }
309
+ const workflowService = singletonServices.workflowService;
310
+ const wire = sessionService
311
+ ? { ...createMiddlewareSessionWireProps(sessionService) }
312
+ : {};
313
+ const rpcService = new ContextAwareRPCService(singletonServices, wire, {
314
+ sessionService,
315
+ });
316
+ const { runId } = await runWorkflowGraph(workflowService, fullName, workflowInput ?? {}, rpcService, true, undefined, { type: 'ai-agent' });
317
+ const maxWait = 45000;
318
+ const startTime = Date.now();
319
+ let pollInterval = 100;
320
+ while (Date.now() - startTime < maxWait) {
321
+ const run = await workflowService.getRun(runId);
322
+ if (!run) {
323
+ return { error: `Workflow run '${runId}' not found` };
324
+ }
325
+ if (run.status === 'completed' ||
326
+ run.status === 'failed' ||
327
+ run.status === 'cancelled' ||
328
+ run.status === 'suspended') {
329
+ if (run.status === 'failed') {
330
+ return {
331
+ error: `Workflow failed: ${run.error?.message || 'Unknown error'}`,
332
+ runId,
333
+ };
334
+ }
335
+ return { result: run.output, runId, status: run.status };
336
+ }
337
+ await new Promise((resolve) => setTimeout(resolve, pollInterval));
338
+ pollInterval = Math.min(pollInterval * 1.5, 2000);
339
+ }
340
+ return {
341
+ runId,
342
+ status: 'running',
343
+ message: 'Workflow is still running after timeout. It will continue in the background.',
344
+ };
345
+ },
346
+ });
347
+ return tools;
348
+ }
@@ -9,7 +9,7 @@ export declare function resolveMemoryServices(agent: CoreAIAgent, singletonServi
9
9
  export declare function deepMergeWorkingMemory(existing: Record<string, unknown>, updates: Record<string, unknown>): Record<string, unknown>;
10
10
  export declare function buildWorkingMemoryPrompt(currentState: Record<string, unknown> | null, jsonSchema?: Record<string, unknown>): string;
11
11
  export declare function loadContextMessages(memoryConfig: AIAgentMemoryConfig | undefined, storage: AIStorageService | undefined, input: AIAgentInput, workingMemoryJsonSchema?: Record<string, unknown>): Promise<AIMessage[]>;
12
- export declare function saveMessages(storage: AIStorageService | undefined, threadId: string, resourceId: string, memoryConfig: AIAgentMemoryConfig | undefined, userMessage: AIMessage, result: {
12
+ export declare function saveMessages(storage: AIStorageService | undefined, threadId: string, resourceId: string, memoryConfig: AIAgentMemoryConfig | undefined, userMessage: AIMessage | null | undefined, result: {
13
13
  text: string;
14
14
  steps: {
15
15
  toolCalls?: {
@@ -66,14 +66,15 @@ export async function loadContextMessages(memoryConfig, storage, input, workingM
66
66
  export async function saveMessages(storage, threadId, resourceId, memoryConfig, userMessage, result, options) {
67
67
  let responseText = result.text;
68
68
  if (storage) {
69
- const newMessages = [userMessage];
69
+ const newMessages = userMessage ? [userMessage] : [];
70
70
  for (const step of result.steps) {
71
71
  if (step.toolCalls?.length) {
72
+ const toolCallIds = step.toolCalls.map(() => randomUUID());
72
73
  newMessages.push({
73
74
  id: randomUUID(),
74
75
  role: 'assistant',
75
- toolCalls: step.toolCalls.map((tc) => ({
76
- id: randomUUID(),
76
+ toolCalls: step.toolCalls.map((tc, i) => ({
77
+ id: toolCallIds[i],
77
78
  name: tc.name,
78
79
  args: tc.args,
79
80
  })),
@@ -82,8 +83,8 @@ export async function saveMessages(storage, threadId, resourceId, memoryConfig,
82
83
  newMessages.push({
83
84
  id: randomUUID(),
84
85
  role: 'tool',
85
- toolResults: step.toolCalls.map((tc) => ({
86
- id: randomUUID(),
86
+ toolResults: step.toolCalls.map((tc, i) => ({
87
+ id: toolCallIds[i],
87
88
  name: tc.name,
88
89
  result: tc.result,
89
90
  })),
@@ -13,7 +13,7 @@ export declare class ToolApprovalRequired extends PikkuError {
13
13
  readonly toolCallId: string;
14
14
  readonly toolName: string;
15
15
  readonly args: unknown;
16
- readonly reason?: string;
16
+ reason?: string;
17
17
  readonly displayToolName?: string;
18
18
  readonly displayArgs?: unknown;
19
19
  readonly agentRunId?: string;
@@ -22,32 +22,35 @@ export declare class ToolApprovalRequired extends PikkuError {
22
22
  export type StreamContext = {
23
23
  channel: AIStreamChannel;
24
24
  options?: StreamAIAgentOptions;
25
+ delegateState?: {
26
+ delegated: boolean;
27
+ };
25
28
  };
26
29
  export declare const resolveAgent: (agentName: string) => {
27
30
  agent: CoreAIAgent;
28
31
  packageName: string | null;
29
32
  resolvedName: string;
30
33
  };
31
- export declare function buildInstructions(agentName: string, packageName: string | null): string;
34
+ export declare function buildInstructions(agentName: string, packageName: string | null): Promise<string>;
32
35
  export type ScopedChannel = AIStreamChannel & {
33
- approval: {
36
+ approvals: Array<{
34
37
  toolCallId: string;
35
38
  toolName: string;
36
39
  args: unknown;
37
40
  runId: string;
38
- } | null;
41
+ }>;
39
42
  };
40
43
  export declare function createScopedChannel(parent: AIStreamChannel, agentName: string, session: string): ScopedChannel;
41
- export declare function buildToolDefs(params: RunAIAgentParams, agentSessionMap: Map<string, string>, resourceId: string, agentName: string, packageName: string | null, streamContext?: StreamContext, aiMiddlewares?: PikkuAIMiddlewareHooks[]): {
44
+ export declare function buildToolDefs(params: RunAIAgentParams, agentSessionMap: Map<string, string>, resourceId: string, agentName: string, packageName: string | null, streamContext?: StreamContext, aiMiddlewares?: PikkuAIMiddlewareHooks[], agentMode?: 'delegate' | 'supervise'): Promise<{
42
45
  tools: AIAgentToolDef[];
43
46
  missingRpcs: string[];
44
- };
47
+ }>;
45
48
  export declare function prepareAgentRun(agentName: string, input: AIAgentInput, params: RunAIAgentParams, agentSessionMap: Map<string, string>, streamContext?: StreamContext): Promise<{
46
49
  agent: CoreAIAgent;
47
50
  packageName: string | null;
48
51
  resolvedName: string;
49
52
  agentRunner: import("../../services/ai-agent-runner-service.js").AIAgentRunnerService;
50
- storage: import("../../services/ai-storage-service.js").AIStorageService | undefined;
53
+ storage: import("../../index.js").AIStorageService | undefined;
51
54
  memoryConfig: import("./ai-agent.types.js").AIAgentMemoryConfig | undefined;
52
55
  threadId: string;
53
56
  userMessage: AIMessage;