@pikku/core 0.12.1 → 0.12.3
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.
- package/CHANGELOG.md +34 -0
- package/dist/crypto-utils.d.ts +10 -0
- package/dist/crypto-utils.js +62 -0
- package/dist/function/function-runner.js +4 -4
- package/dist/function/functions.types.d.ts +26 -1
- package/dist/function/functions.types.js +16 -0
- package/dist/handle-error.d.ts +1 -1
- package/dist/handle-error.js +7 -3
- package/dist/index.d.ts +14 -2
- package/dist/index.js +1 -1
- package/dist/middleware-runner.js +1 -0
- package/dist/permissions.js +1 -0
- package/dist/pikku-state.js +7 -1
- package/dist/schema.js +1 -1
- package/dist/services/gateway-service.d.ts +19 -0
- package/dist/services/gateway-service.js +1 -0
- package/dist/services/in-memory-workflow-service.d.ts +8 -2
- package/dist/services/in-memory-workflow-service.js +28 -1
- package/dist/services/index.d.ts +2 -0
- package/dist/services/index.js +1 -0
- package/dist/services/local-gateway-service.d.ts +22 -0
- package/dist/services/local-gateway-service.js +51 -0
- package/dist/services/workflow-service.d.ts +8 -2
- package/dist/testing/index.d.ts +2 -0
- package/dist/testing/index.js +1 -0
- package/dist/testing/service-tests.d.ts +31 -0
- package/dist/testing/service-tests.js +598 -0
- package/dist/types/core.types.d.ts +12 -2
- package/dist/types/state.types.d.ts +9 -2
- package/dist/utils/hash.d.ts +2 -0
- package/dist/utils/hash.js +23 -0
- package/dist/utils.js +1 -1
- package/dist/wirings/ai-agent/agent-dynamic-workflow.d.ts +6 -0
- package/dist/wirings/ai-agent/agent-dynamic-workflow.js +348 -0
- package/dist/wirings/ai-agent/ai-agent-memory.d.ts +1 -1
- package/dist/wirings/ai-agent/ai-agent-memory.js +19 -7
- package/dist/wirings/ai-agent/ai-agent-prepare.d.ts +10 -7
- package/dist/wirings/ai-agent/ai-agent-prepare.js +141 -48
- package/dist/wirings/ai-agent/ai-agent-runner.d.ts +4 -0
- package/dist/wirings/ai-agent/ai-agent-runner.js +381 -27
- package/dist/wirings/ai-agent/ai-agent-stream.d.ts +7 -2
- package/dist/wirings/ai-agent/ai-agent-stream.js +126 -48
- package/dist/wirings/ai-agent/ai-agent.types.d.ts +57 -3
- package/dist/wirings/ai-agent/index.d.ts +2 -3
- package/dist/wirings/ai-agent/index.js +1 -2
- package/dist/wirings/channel/channel-handler.d.ts +5 -2
- package/dist/wirings/channel/channel-handler.js +16 -2
- package/dist/wirings/channel/channel-middleware-runner.js +1 -0
- package/dist/wirings/channel/channel.types.d.ts +7 -7
- package/dist/wirings/channel/index.d.ts +1 -1
- package/dist/wirings/channel/local/local-channel-handler.d.ts +8 -1
- package/dist/wirings/channel/local/local-channel-handler.js +22 -3
- package/dist/wirings/channel/local/local-channel-runner.js +28 -5
- package/dist/wirings/channel/pikku-abstract-channel-handler.d.ts +2 -1
- package/dist/wirings/channel/pikku-abstract-channel-handler.js +1 -0
- package/dist/wirings/channel/serverless/serverless-channel-runner.js +5 -2
- package/dist/wirings/cli/cli-runner.js +13 -2
- package/dist/wirings/cli/cli.types.d.ts +1 -0
- package/dist/wirings/gateway/gateway-runner.d.ts +24 -0
- package/dist/wirings/gateway/gateway-runner.js +325 -0
- package/dist/wirings/gateway/gateway.types.d.ts +127 -0
- package/dist/wirings/gateway/gateway.types.js +1 -0
- package/dist/wirings/gateway/index.d.ts +2 -0
- package/dist/wirings/gateway/index.js +1 -0
- package/dist/wirings/http/http-routes.js +0 -1
- package/dist/wirings/http/http-runner.d.ts +1 -1
- package/dist/wirings/http/http-runner.js +28 -3
- package/dist/wirings/http/http.types.d.ts +3 -11
- package/dist/wirings/http/index.d.ts +2 -1
- package/dist/wirings/http/index.js +1 -0
- package/dist/wirings/http/pikku-fetch-http-request.d.ts +1 -0
- package/dist/wirings/http/pikku-fetch-http-request.js +3 -0
- package/dist/wirings/http/pikku-fetch-http-response.d.ts +1 -0
- package/dist/wirings/http/pikku-fetch-http-response.js +4 -4
- package/dist/wirings/http/web-request.d.ts +12 -0
- package/dist/wirings/http/web-request.js +85 -0
- package/dist/wirings/mcp/mcp-endpoint-registry.js +1 -1
- package/dist/wirings/mcp/mcp-runner.js +13 -1
- package/dist/wirings/rpc/rpc-runner.d.ts +30 -6
- package/dist/wirings/rpc/rpc-runner.js +36 -14
- package/dist/wirings/rpc/rpc-types.d.ts +6 -1
- package/dist/wirings/rpc/wire-addon.d.ts +1 -0
- package/dist/wirings/rpc/wire-addon.js +1 -1
- package/dist/wirings/scheduler/scheduler-runner.js +1 -0
- package/dist/wirings/workflow/graph/graph-validation.d.ts +3 -0
- package/dist/wirings/workflow/graph/graph-validation.js +175 -0
- package/dist/wirings/workflow/graph/index.d.ts +1 -0
- package/dist/wirings/workflow/graph/index.js +1 -0
- package/dist/wirings/workflow/index.d.ts +1 -1
- package/dist/wirings/workflow/pikku-workflow-service.d.ts +8 -2
- package/dist/wirings/workflow/pikku-workflow-service.js +1 -2
- package/dist/wirings/workflow/workflow.types.d.ts +10 -1
- package/package.json +6 -3
- package/src/crypto-utils.test.ts +99 -1
- package/src/crypto-utils.ts +114 -0
- package/src/function/function-runner.ts +5 -5
- package/src/function/functions.types.ts +38 -1
- package/src/handle-error.test.ts +65 -2
- package/src/handle-error.ts +8 -4
- package/src/index.ts +14 -0
- package/src/middleware-runner.ts +1 -0
- package/src/permissions.ts +1 -0
- package/src/pikku-state.ts +8 -1
- package/src/schema.ts +1 -1
- package/src/services/gateway-service.ts +20 -0
- package/src/services/in-memory-workflow-service.ts +45 -2
- package/src/services/index.ts +2 -0
- package/src/services/local-gateway-service.ts +62 -0
- package/src/services/workflow-service.ts +12 -1
- package/src/testing/index.ts +2 -0
- package/src/testing/service-tests.ts +873 -0
- package/src/types/core.types.ts +12 -1
- package/src/types/state.types.ts +12 -2
- package/src/utils/hash.test.ts +68 -0
- package/src/utils/hash.ts +26 -0
- package/src/utils.ts +1 -1
- package/src/wirings/ai-agent/agent-dynamic-workflow.ts +469 -0
- package/src/wirings/ai-agent/ai-agent-memory.ts +17 -7
- package/src/wirings/ai-agent/ai-agent-prepare.ts +194 -62
- package/src/wirings/ai-agent/ai-agent-runner.test.ts +3 -1
- package/src/wirings/ai-agent/ai-agent-runner.ts +553 -31
- package/src/wirings/ai-agent/ai-agent-stream.test.ts +209 -2
- package/src/wirings/ai-agent/ai-agent-stream.ts +195 -91
- package/src/wirings/ai-agent/ai-agent.types.ts +57 -3
- package/src/wirings/ai-agent/index.ts +3 -5
- package/src/wirings/channel/channel-handler.ts +41 -4
- package/src/wirings/channel/channel-middleware-runner.ts +1 -0
- package/src/wirings/channel/channel.types.ts +12 -7
- package/src/wirings/channel/index.ts +1 -0
- package/src/wirings/channel/local/local-channel-handler.ts +31 -3
- package/src/wirings/channel/local/local-channel-runner.ts +32 -8
- package/src/wirings/channel/pikku-abstract-channel-handler.test.ts +2 -0
- package/src/wirings/channel/pikku-abstract-channel-handler.ts +8 -1
- package/src/wirings/channel/serverless/serverless-channel-runner.ts +5 -2
- package/src/wirings/cli/cli-runner.ts +15 -2
- package/src/wirings/cli/cli.types.ts +1 -0
- package/src/wirings/gateway/gateway-runner.test.ts +474 -0
- package/src/wirings/gateway/gateway-runner.ts +411 -0
- package/src/wirings/gateway/gateway.types.ts +149 -0
- package/src/wirings/gateway/index.ts +13 -0
- package/src/wirings/http/http-routes.ts +0 -1
- package/src/wirings/http/http-runner.ts +38 -10
- package/src/wirings/http/http.types.ts +3 -12
- package/src/wirings/http/index.ts +3 -0
- package/src/wirings/http/pikku-fetch-http-request.ts +4 -0
- package/src/wirings/http/pikku-fetch-http-response.ts +4 -4
- package/src/wirings/http/web-request.test.ts +236 -0
- package/src/wirings/http/web-request.ts +104 -0
- package/src/wirings/mcp/mcp-endpoint-registry.ts +1 -1
- package/src/wirings/mcp/mcp-runner.ts +30 -12
- package/src/wirings/rpc/rpc-runner.ts +45 -24
- package/src/wirings/rpc/rpc-types.ts +6 -1
- package/src/wirings/rpc/wire-addon.ts +2 -1
- package/src/wirings/scheduler/scheduler-runner.ts +1 -0
- package/src/wirings/workflow/graph/graph-validation.test.ts +170 -0
- package/src/wirings/workflow/graph/graph-validation.ts +237 -0
- package/src/wirings/workflow/graph/index.ts +5 -0
- package/src/wirings/workflow/index.ts +1 -0
- package/src/wirings/workflow/pikku-workflow-service.ts +14 -3
- package/src/wirings/workflow/workflow.types.ts +9 -1
- package/tsconfig.tsbuildinfo +1 -1
- package/dist/wirings/ai-agent/ai-agent-assistant-ui.d.ts +0 -5
- package/dist/wirings/ai-agent/ai-agent-assistant-ui.js +0 -146
- package/src/wirings/ai-agent/ai-agent-assistant-ui.test.ts +0 -363
- package/src/wirings/ai-agent/ai-agent-assistant-ui.ts +0 -214
|
@@ -112,7 +112,7 @@ export async function saveMessages(
|
|
|
112
112
|
threadId: string,
|
|
113
113
|
resourceId: string,
|
|
114
114
|
memoryConfig: AIAgentMemoryConfig | undefined,
|
|
115
|
-
userMessage: AIMessage,
|
|
115
|
+
userMessage: AIMessage | null | undefined,
|
|
116
116
|
result: {
|
|
117
117
|
text: string
|
|
118
118
|
steps: {
|
|
@@ -133,15 +133,16 @@ export async function saveMessages(
|
|
|
133
133
|
let responseText = result.text
|
|
134
134
|
|
|
135
135
|
if (storage) {
|
|
136
|
-
const newMessages: AIMessage[] = [userMessage]
|
|
136
|
+
const newMessages: AIMessage[] = userMessage ? [userMessage] : []
|
|
137
137
|
|
|
138
138
|
for (const step of result.steps) {
|
|
139
139
|
if (step.toolCalls?.length) {
|
|
140
|
+
const toolCallIds = step.toolCalls.map(() => randomUUID())
|
|
140
141
|
newMessages.push({
|
|
141
142
|
id: randomUUID(),
|
|
142
143
|
role: 'assistant',
|
|
143
|
-
toolCalls: step.toolCalls.map((tc) => ({
|
|
144
|
-
id:
|
|
144
|
+
toolCalls: step.toolCalls.map((tc, i) => ({
|
|
145
|
+
id: toolCallIds[i],
|
|
145
146
|
name: tc.name,
|
|
146
147
|
args: tc.args,
|
|
147
148
|
})),
|
|
@@ -150,8 +151,8 @@ export async function saveMessages(
|
|
|
150
151
|
newMessages.push({
|
|
151
152
|
id: randomUUID(),
|
|
152
153
|
role: 'tool',
|
|
153
|
-
toolResults: step.toolCalls.map((tc) => ({
|
|
154
|
-
id:
|
|
154
|
+
toolResults: step.toolCalls.map((tc, i) => ({
|
|
155
|
+
id: toolCallIds[i],
|
|
155
156
|
name: tc.name,
|
|
156
157
|
result: tc.result,
|
|
157
158
|
})),
|
|
@@ -277,7 +278,16 @@ function sanitizeToolMessages(messages: AIMessage[]): AIMessage[] {
|
|
|
277
278
|
|
|
278
279
|
function estimateTokens(msg: AIMessage): number {
|
|
279
280
|
let chars = 0
|
|
280
|
-
if (msg.content)
|
|
281
|
+
if (msg.content) {
|
|
282
|
+
if (typeof msg.content === 'string') {
|
|
283
|
+
chars += msg.content.length
|
|
284
|
+
} else {
|
|
285
|
+
for (const part of msg.content) {
|
|
286
|
+
if (part.type === 'text') chars += part.text.length
|
|
287
|
+
else chars += 1000
|
|
288
|
+
}
|
|
289
|
+
}
|
|
290
|
+
}
|
|
281
291
|
if (msg.toolCalls) chars += JSON.stringify(msg.toolCalls).length
|
|
282
292
|
if (msg.toolResults) chars += JSON.stringify(msg.toolResults).length
|
|
283
293
|
return Math.ceil(chars / 4)
|
|
@@ -3,6 +3,7 @@ import type {
|
|
|
3
3
|
CoreAIAgent,
|
|
4
4
|
AIAgentInput,
|
|
5
5
|
AIAgentToolDef,
|
|
6
|
+
AIContentPart,
|
|
6
7
|
AIMessage,
|
|
7
8
|
AIStreamChannel,
|
|
8
9
|
AIStreamEvent,
|
|
@@ -10,16 +11,17 @@ import type {
|
|
|
10
11
|
} from './ai-agent.types.js'
|
|
11
12
|
import type { AIAgentRunnerParams } from '../../services/ai-agent-runner-service.js'
|
|
12
13
|
import { PikkuError } from '../../errors/error-handler.js'
|
|
13
|
-
import {
|
|
14
|
-
pikkuState,
|
|
15
|
-
getSingletonServices,
|
|
16
|
-
getCreateWireServices,
|
|
17
|
-
} from '../../pikku-state.js'
|
|
18
|
-
import { runPikkuFunc } from '../../function/function-runner.js'
|
|
14
|
+
import { pikkuState, getSingletonServices } from '../../pikku-state.js'
|
|
19
15
|
import { createMiddlewareSessionWireProps } from '../../services/user-session-service.js'
|
|
20
16
|
import type { SessionService } from '../../services/user-session-service.js'
|
|
21
17
|
import { randomUUID } from 'crypto'
|
|
22
|
-
|
|
18
|
+
import { streamAIAgent } from './ai-agent-stream.js'
|
|
19
|
+
import { runAIAgent } from './ai-agent-runner.js'
|
|
20
|
+
import {
|
|
21
|
+
resolveNamespace,
|
|
22
|
+
ContextAwareRPCService,
|
|
23
|
+
} from '../../wirings/rpc/rpc-runner.js'
|
|
24
|
+
import { buildDynamicWorkflowInstructions, buildWorkflowTools } from './agent-dynamic-workflow.js'
|
|
23
25
|
import {
|
|
24
26
|
resolveMemoryServices,
|
|
25
27
|
loadContextMessages,
|
|
@@ -39,7 +41,7 @@ export class ToolApprovalRequired extends PikkuError {
|
|
|
39
41
|
public readonly toolCallId: string
|
|
40
42
|
public readonly toolName: string
|
|
41
43
|
public readonly args: unknown
|
|
42
|
-
public
|
|
44
|
+
public reason?: string
|
|
43
45
|
public readonly displayToolName?: string
|
|
44
46
|
public readonly displayArgs?: unknown
|
|
45
47
|
public readonly agentRunId?: string
|
|
@@ -67,6 +69,7 @@ export class ToolApprovalRequired extends PikkuError {
|
|
|
67
69
|
export type StreamContext = {
|
|
68
70
|
channel: AIStreamChannel
|
|
69
71
|
options?: StreamAIAgentOptions
|
|
72
|
+
delegateState?: { delegated: boolean }
|
|
70
73
|
}
|
|
71
74
|
|
|
72
75
|
export const resolveAgent = (
|
|
@@ -81,7 +84,7 @@ export const resolveAgent = (
|
|
|
81
84
|
if (colonIndex !== -1) {
|
|
82
85
|
const namespace = agentName.substring(0, colonIndex)
|
|
83
86
|
const localName = agentName.substring(colonIndex + 1)
|
|
84
|
-
const addons = pikkuState(null, '
|
|
87
|
+
const addons = pikkuState(null, 'addons', 'packages')
|
|
85
88
|
const pkgConfig = addons.get(namespace)
|
|
86
89
|
if (pkgConfig) {
|
|
87
90
|
const extAgent = pikkuState(pkgConfig.package, 'agent', 'agents').get(
|
|
@@ -100,31 +103,38 @@ export const resolveAgent = (
|
|
|
100
103
|
throw new Error(`AI agent not found: ${agentName}`)
|
|
101
104
|
}
|
|
102
105
|
|
|
103
|
-
export function buildInstructions(
|
|
106
|
+
export async function buildInstructions(
|
|
104
107
|
agentName: string,
|
|
105
108
|
packageName: string | null
|
|
106
|
-
): string {
|
|
109
|
+
): Promise<string> {
|
|
107
110
|
const meta = pikkuState(packageName, 'agent', 'agentsMeta')[agentName]
|
|
108
|
-
const
|
|
109
|
-
|
|
110
|
-
?
|
|
111
|
-
:
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
|
|
111
|
+
const rawInstructions = meta?.instructions ?? ''
|
|
112
|
+
let instructions = Array.isArray(rawInstructions)
|
|
113
|
+
? rawInstructions.join('\n')
|
|
114
|
+
: rawInstructions
|
|
115
|
+
|
|
116
|
+
if (meta?.agents?.length) {
|
|
117
|
+
instructions +=
|
|
118
|
+
'\n\nWhen calling a sub-agent, provide a short session name that describes the task. ' +
|
|
119
|
+
'Use the same session name to continue a previous conversation with that agent. ' +
|
|
120
|
+
'Use a new session name for a new independent task. ' +
|
|
121
|
+
'When a request involves multiple actions for the same domain, combine them into a single sub-agent call rather than making separate calls.'
|
|
122
|
+
}
|
|
123
|
+
|
|
124
|
+
if (meta?.dynamicWorkflows && meta.tools?.length) {
|
|
125
|
+
instructions += buildDynamicWorkflowInstructions(meta.tools, meta.dynamicWorkflows)
|
|
126
|
+
}
|
|
127
|
+
|
|
128
|
+
return instructions
|
|
119
129
|
}
|
|
120
130
|
|
|
121
131
|
export type ScopedChannel = AIStreamChannel & {
|
|
122
|
-
|
|
132
|
+
approvals: Array<{
|
|
123
133
|
toolCallId: string
|
|
124
134
|
toolName: string
|
|
125
135
|
args: unknown
|
|
126
136
|
runId: string
|
|
127
|
-
}
|
|
137
|
+
}>
|
|
128
138
|
}
|
|
129
139
|
|
|
130
140
|
export function createScopedChannel(
|
|
@@ -132,7 +142,7 @@ export function createScopedChannel(
|
|
|
132
142
|
agentName: string,
|
|
133
143
|
session: string
|
|
134
144
|
): ScopedChannel {
|
|
135
|
-
|
|
145
|
+
const capturedApprovals: ScopedChannel['approvals'] = []
|
|
136
146
|
|
|
137
147
|
return {
|
|
138
148
|
channelId: `${parent.channelId}:${agentName}:${session}`,
|
|
@@ -140,19 +150,20 @@ export function createScopedChannel(
|
|
|
140
150
|
get state() {
|
|
141
151
|
return parent.state
|
|
142
152
|
},
|
|
143
|
-
get
|
|
144
|
-
return
|
|
153
|
+
get approvals() {
|
|
154
|
+
return capturedApprovals
|
|
145
155
|
},
|
|
146
156
|
close: () => {},
|
|
157
|
+
sendBinary: (data) => parent.sendBinary(data),
|
|
147
158
|
send: (event: AIStreamEvent) => {
|
|
148
159
|
if (event.type === 'done') return
|
|
149
160
|
if (event.type === 'approval-request') {
|
|
150
|
-
|
|
161
|
+
capturedApprovals.push({
|
|
151
162
|
toolCallId: event.toolCallId,
|
|
152
163
|
toolName: event.toolName,
|
|
153
164
|
args: event.args,
|
|
154
165
|
runId: (event as any).runId,
|
|
155
|
-
}
|
|
166
|
+
})
|
|
156
167
|
return
|
|
157
168
|
}
|
|
158
169
|
if (
|
|
@@ -162,7 +173,8 @@ export function createScopedChannel(
|
|
|
162
173
|
event.type === 'tool-call' ||
|
|
163
174
|
event.type === 'tool-result' ||
|
|
164
175
|
event.type === 'usage' ||
|
|
165
|
-
event.type === 'error'
|
|
176
|
+
event.type === 'error' ||
|
|
177
|
+
event.type === 'workflow-created'
|
|
166
178
|
) {
|
|
167
179
|
parent.send({ ...event, agent: agentName, session } as AIStreamEvent)
|
|
168
180
|
} else {
|
|
@@ -172,15 +184,16 @@ export function createScopedChannel(
|
|
|
172
184
|
}
|
|
173
185
|
}
|
|
174
186
|
|
|
175
|
-
export function buildToolDefs(
|
|
187
|
+
export async function buildToolDefs(
|
|
176
188
|
params: RunAIAgentParams,
|
|
177
189
|
agentSessionMap: Map<string, string>,
|
|
178
190
|
resourceId: string,
|
|
179
191
|
agentName: string,
|
|
180
192
|
packageName: string | null,
|
|
181
193
|
streamContext?: StreamContext,
|
|
182
|
-
aiMiddlewares?: PikkuAIMiddlewareHooks[]
|
|
183
|
-
|
|
194
|
+
aiMiddlewares?: PikkuAIMiddlewareHooks[],
|
|
195
|
+
agentMode?: 'delegate' | 'supervise'
|
|
196
|
+
): Promise<{ tools: AIAgentToolDef[]; missingRpcs: string[] }> {
|
|
184
197
|
const singletonServices = getSingletonServices()
|
|
185
198
|
const tools: AIAgentToolDef[] = []
|
|
186
199
|
const missingRpcs: string[] = []
|
|
@@ -194,18 +207,38 @@ export function buildToolDefs(
|
|
|
194
207
|
const metaAgents = meta.agents
|
|
195
208
|
|
|
196
209
|
if (metaTools?.length) {
|
|
197
|
-
const functionMeta = pikkuState(null, 'function', 'meta')
|
|
198
|
-
const schemas = pikkuState(null, 'misc', 'schemas')
|
|
199
|
-
|
|
200
210
|
for (const toolName of metaTools) {
|
|
201
|
-
|
|
202
|
-
|
|
203
|
-
|
|
211
|
+
let fnMeta: any
|
|
212
|
+
let resolvedPkg: string | null = null
|
|
213
|
+
let schemas: Map<string, any>
|
|
214
|
+
|
|
215
|
+
const resolved = toolName.includes(':')
|
|
216
|
+
? resolveNamespace(toolName)
|
|
217
|
+
: null
|
|
218
|
+
|
|
219
|
+
let pikkuFuncId: string | undefined
|
|
220
|
+
|
|
221
|
+
if (resolved) {
|
|
222
|
+
resolvedPkg = resolved.package
|
|
223
|
+
pikkuFuncId = resolved.function
|
|
224
|
+
fnMeta = pikkuState(resolvedPkg, 'function', 'meta')[pikkuFuncId]
|
|
225
|
+
schemas = pikkuState(resolvedPkg, 'misc', 'schemas')
|
|
226
|
+
} else {
|
|
227
|
+
const rpcMeta = pikkuState(null, 'rpc', 'meta')
|
|
228
|
+
pikkuFuncId = rpcMeta[toolName]
|
|
229
|
+
if (!pikkuFuncId) {
|
|
230
|
+
missingRpcs.push(toolName)
|
|
231
|
+
continue
|
|
232
|
+
}
|
|
233
|
+
fnMeta = pikkuState(null, 'function', 'meta')[pikkuFuncId]
|
|
234
|
+
schemas = pikkuState(null, 'misc', 'schemas')
|
|
235
|
+
}
|
|
236
|
+
|
|
237
|
+
if (!fnMeta) {
|
|
204
238
|
missingRpcs.push(toolName)
|
|
205
239
|
continue
|
|
206
240
|
}
|
|
207
241
|
|
|
208
|
-
const fnMeta = functionMeta[pikkuFuncId]
|
|
209
242
|
const inputSchemaName = fnMeta?.inputSchemaName
|
|
210
243
|
let inputSchema = inputSchemaName
|
|
211
244
|
? schemas.get(inputSchemaName)
|
|
@@ -221,24 +254,52 @@ export function buildToolDefs(
|
|
|
221
254
|
|
|
222
255
|
const needsApproval =
|
|
223
256
|
approvalPolicy === 'all' ||
|
|
224
|
-
(approvalPolicy === 'explicit' && fnMeta?.
|
|
257
|
+
(approvalPolicy === 'explicit' && fnMeta?.approvalRequired)
|
|
258
|
+
|
|
259
|
+
// Build approvalDescriptionFn if the function has an approvalDescription configured
|
|
260
|
+
let approvalDescriptionFn:
|
|
261
|
+
| ((input: unknown) => Promise<string>)
|
|
262
|
+
| undefined
|
|
263
|
+
if (needsApproval && pikkuFuncId) {
|
|
264
|
+
const funcConfig = pikkuState(resolvedPkg, 'function', 'functions').get(
|
|
265
|
+
pikkuFuncId
|
|
266
|
+
)
|
|
267
|
+
if (funcConfig?.approvalDescription) {
|
|
268
|
+
const descFn = funcConfig.approvalDescription
|
|
269
|
+
const capturedPkg = resolvedPkg
|
|
270
|
+
approvalDescriptionFn = async (input: unknown) => {
|
|
271
|
+
let services = singletonServices
|
|
272
|
+
if (capturedPkg) {
|
|
273
|
+
const pkgServices = pikkuState(
|
|
274
|
+
capturedPkg,
|
|
275
|
+
'package',
|
|
276
|
+
'singletonServices'
|
|
277
|
+
)
|
|
278
|
+
if (pkgServices) {
|
|
279
|
+
services = pkgServices
|
|
280
|
+
}
|
|
281
|
+
}
|
|
282
|
+
return descFn(services, input)
|
|
283
|
+
}
|
|
284
|
+
}
|
|
285
|
+
}
|
|
225
286
|
|
|
226
287
|
tools.push({
|
|
227
|
-
name:
|
|
288
|
+
name: toolName.replaceAll(':', '__'),
|
|
228
289
|
description: fnMeta?.description || fnMeta?.title || toolName,
|
|
229
290
|
inputSchema,
|
|
230
291
|
needsApproval: needsApproval || undefined,
|
|
292
|
+
approvalDescriptionFn,
|
|
231
293
|
execute: async (toolInput: unknown) => {
|
|
232
294
|
const wire: PikkuWire = params.sessionService
|
|
233
295
|
? { ...createMiddlewareSessionWireProps(params.sessionService) }
|
|
234
296
|
: {}
|
|
235
|
-
|
|
297
|
+
const rpcService = new ContextAwareRPCService(
|
|
236
298
|
singletonServices,
|
|
237
|
-
createWireServices: getCreateWireServices(),
|
|
238
|
-
data: () => toolInput,
|
|
239
299
|
wire,
|
|
240
|
-
sessionService: params.sessionService
|
|
241
|
-
|
|
300
|
+
{ sessionService: params.sessionService }
|
|
301
|
+
)
|
|
302
|
+
return rpcService.rpc(toolName, toolInput)
|
|
242
303
|
},
|
|
243
304
|
})
|
|
244
305
|
}
|
|
@@ -278,12 +339,15 @@ export function buildToolDefs(
|
|
|
278
339
|
const sessionKey = `${subAgentName}::${session}`
|
|
279
340
|
let threadId = agentSessionMap.get(sessionKey)
|
|
280
341
|
if (!threadId) {
|
|
281
|
-
threadId =
|
|
342
|
+
threadId = randomUUID()
|
|
282
343
|
agentSessionMap.set(sessionKey, threadId)
|
|
283
344
|
}
|
|
284
345
|
|
|
285
346
|
if (streamContext) {
|
|
286
|
-
const
|
|
347
|
+
const isDelegate = agentMode !== 'supervise'
|
|
348
|
+
if (isDelegate && streamContext.delegateState) {
|
|
349
|
+
streamContext.delegateState.delegated = true
|
|
350
|
+
}
|
|
287
351
|
const { channel } = streamContext
|
|
288
352
|
channel.send({
|
|
289
353
|
type: 'agent-call',
|
|
@@ -296,34 +360,48 @@ export function buildToolDefs(
|
|
|
296
360
|
subAgentName,
|
|
297
361
|
session
|
|
298
362
|
)
|
|
299
|
-
|
|
363
|
+
// In supervise mode, suppress sub-agent text from reaching the client.
|
|
364
|
+
// Approvals still flow through normally.
|
|
365
|
+
const effectiveChannel = isDelegate
|
|
366
|
+
? subChannel
|
|
367
|
+
: {
|
|
368
|
+
...subChannel,
|
|
369
|
+
send: (event: AIStreamEvent) => {
|
|
370
|
+
if (
|
|
371
|
+
event.type === 'text-delta' ||
|
|
372
|
+
event.type === 'reasoning-delta'
|
|
373
|
+
)
|
|
374
|
+
return
|
|
375
|
+
subChannel.send(event)
|
|
376
|
+
},
|
|
377
|
+
}
|
|
378
|
+
const resultText = await streamAIAgent(
|
|
300
379
|
subAgentName,
|
|
301
380
|
{ message, threadId, resourceId },
|
|
302
|
-
|
|
381
|
+
effectiveChannel,
|
|
303
382
|
params,
|
|
304
383
|
agentSessionMap,
|
|
305
384
|
streamContext.options
|
|
306
385
|
)
|
|
307
|
-
if (subChannel.
|
|
386
|
+
if (subChannel.approvals.length > 0) {
|
|
308
387
|
return {
|
|
309
388
|
__approvalRequired: true,
|
|
310
389
|
toolName: subAgentName,
|
|
311
390
|
args: toolInput,
|
|
312
|
-
|
|
313
|
-
|
|
314
|
-
agentRunId: subChannel.approval.runId,
|
|
391
|
+
agentRunId: subChannel.approvals[0].runId,
|
|
392
|
+
subApprovals: subChannel.approvals,
|
|
315
393
|
}
|
|
316
394
|
}
|
|
317
395
|
channel.send({
|
|
318
396
|
type: 'agent-result',
|
|
319
397
|
agentName: subAgentName,
|
|
320
398
|
session,
|
|
321
|
-
result:
|
|
399
|
+
result: resultText,
|
|
322
400
|
})
|
|
323
|
-
return
|
|
401
|
+
return resultText
|
|
324
402
|
}
|
|
325
403
|
|
|
326
|
-
|
|
404
|
+
// No stream context: sub-agent runs non-streaming
|
|
327
405
|
const result = await runAIAgent(
|
|
328
406
|
subAgentName,
|
|
329
407
|
{ message, threadId, resourceId },
|
|
@@ -336,6 +414,18 @@ export function buildToolDefs(
|
|
|
336
414
|
}
|
|
337
415
|
}
|
|
338
416
|
|
|
417
|
+
if (meta.dynamicWorkflows) {
|
|
418
|
+
const workflowTools = buildWorkflowTools(
|
|
419
|
+
agentName,
|
|
420
|
+
packageName,
|
|
421
|
+
meta.tools ?? [],
|
|
422
|
+
meta.dynamicWorkflows,
|
|
423
|
+
streamContext,
|
|
424
|
+
params.sessionService
|
|
425
|
+
)
|
|
426
|
+
tools.push(...workflowTools)
|
|
427
|
+
}
|
|
428
|
+
|
|
339
429
|
const hasToolHooks = aiMiddlewares?.some(
|
|
340
430
|
(mw) => mw.beforeToolCall || mw.afterToolCall
|
|
341
431
|
)
|
|
@@ -410,6 +500,19 @@ export async function prepareAgentRun(
|
|
|
410
500
|
throw new Error('AIAgentRunnerService not available in singletonServices')
|
|
411
501
|
}
|
|
412
502
|
|
|
503
|
+
if (agent.dynamicWorkflows && singletonServices.workflowService) {
|
|
504
|
+
const persisted =
|
|
505
|
+
await singletonServices.workflowService.getAIGeneratedWorkflows(
|
|
506
|
+
resolvedName
|
|
507
|
+
)
|
|
508
|
+
const allMeta = pikkuState(null, 'workflows', 'meta')
|
|
509
|
+
for (const wf of persisted) {
|
|
510
|
+
if (!allMeta[wf.workflowName]) {
|
|
511
|
+
allMeta[wf.workflowName] = wf.graph
|
|
512
|
+
}
|
|
513
|
+
}
|
|
514
|
+
}
|
|
515
|
+
|
|
413
516
|
const { storage } = resolveMemoryServices(agent, singletonServices)
|
|
414
517
|
const memoryConfig = agent.memory
|
|
415
518
|
const threadId = input.threadId
|
|
@@ -448,10 +551,26 @@ export async function prepareAgentRun(
|
|
|
448
551
|
workingMemoryJsonSchema
|
|
449
552
|
)
|
|
450
553
|
|
|
554
|
+
const userContent: AIMessage['content'] = input.attachments?.length
|
|
555
|
+
? [
|
|
556
|
+
{ type: 'text' as const, text: input.message },
|
|
557
|
+
...input.attachments.map(
|
|
558
|
+
(a) =>
|
|
559
|
+
({
|
|
560
|
+
type: a.type,
|
|
561
|
+
data: a.data,
|
|
562
|
+
url: a.url,
|
|
563
|
+
mediaType: a.mediaType,
|
|
564
|
+
...(a.filename ? { filename: a.filename } : {}),
|
|
565
|
+
}) as AIContentPart
|
|
566
|
+
),
|
|
567
|
+
]
|
|
568
|
+
: input.message
|
|
569
|
+
|
|
451
570
|
const userMessage: AIMessage = {
|
|
452
571
|
id: randomUUID(),
|
|
453
572
|
role: 'user',
|
|
454
|
-
content:
|
|
573
|
+
content: userContent,
|
|
455
574
|
createdAt: new Date(),
|
|
456
575
|
}
|
|
457
576
|
|
|
@@ -460,19 +579,32 @@ export async function prepareAgentRun(
|
|
|
460
579
|
|
|
461
580
|
const aiMiddlewares: PikkuAIMiddlewareHooks[] = agent.aiMiddleware ?? []
|
|
462
581
|
|
|
463
|
-
const { tools, missingRpcs } = buildToolDefs(
|
|
582
|
+
const { tools, missingRpcs } = await buildToolDefs(
|
|
464
583
|
params,
|
|
465
584
|
agentSessionMap,
|
|
466
585
|
input.resourceId,
|
|
467
586
|
resolvedName,
|
|
468
587
|
packageName,
|
|
469
588
|
streamContext,
|
|
470
|
-
aiMiddlewares
|
|
589
|
+
aiMiddlewares,
|
|
590
|
+
agent.agentMode
|
|
471
591
|
)
|
|
472
592
|
|
|
473
|
-
const instructions = buildInstructions(resolvedName, packageName)
|
|
593
|
+
const instructions = await buildInstructions(resolvedName, packageName)
|
|
474
594
|
|
|
475
595
|
const resolved = resolveModelConfig(resolvedName, agent)
|
|
596
|
+
|
|
597
|
+
// Per-request overrides
|
|
598
|
+
if (input.model) {
|
|
599
|
+
resolved.model = resolveModelConfig(resolvedName, {
|
|
600
|
+
...agent,
|
|
601
|
+
model: input.model,
|
|
602
|
+
}).model
|
|
603
|
+
}
|
|
604
|
+
if (input.temperature !== undefined) {
|
|
605
|
+
resolved.temperature = input.temperature
|
|
606
|
+
}
|
|
607
|
+
|
|
476
608
|
const maxSteps = resolved.maxSteps ?? 10
|
|
477
609
|
|
|
478
610
|
const runnerParams: AIAgentRunnerParams = {
|
|
@@ -84,7 +84,9 @@ describe('runAIAgent', () => {
|
|
|
84
84
|
expectedError
|
|
85
85
|
)
|
|
86
86
|
|
|
87
|
-
assert.deepEqual(updates, [
|
|
87
|
+
assert.deepEqual(updates, [
|
|
88
|
+
{ status: 'failed', errorMessage: 'runner failed' },
|
|
89
|
+
])
|
|
88
90
|
})
|
|
89
91
|
|
|
90
92
|
test('loops through multiple steps and accumulates usage', async () => {
|