@xyne/workflow-sdk 2.2.0 → 2.4.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.
- package/dist/agents/agent-step.d.ts +115 -3
- package/dist/agents/agent-step.d.ts.map +1 -1
- package/dist/agents/agent-step.js +247 -85
- package/dist/agents/agent-step.js.map +1 -1
- package/dist/agents/builtin/run-code-tool.d.ts +29 -0
- package/dist/agents/builtin/run-code-tool.d.ts.map +1 -0
- package/dist/agents/builtin/run-code-tool.js +54 -0
- package/dist/agents/builtin/run-code-tool.js.map +1 -0
- package/dist/agents/index.d.ts +1 -0
- package/dist/agents/index.d.ts.map +1 -1
- package/dist/agents/index.js +1 -0
- package/dist/agents/index.js.map +1 -1
- package/dist/agents/pi-mono-runtime.d.ts +5 -6
- package/dist/agents/pi-mono-runtime.d.ts.map +1 -1
- package/dist/agents/pi-mono-runtime.js +4 -5
- package/dist/agents/pi-mono-runtime.js.map +1 -1
- package/dist/agents/tool-types.d.ts +5 -0
- package/dist/agents/tool-types.d.ts.map +1 -1
- package/dist/agents/types.d.ts +5 -1
- package/dist/agents/types.d.ts.map +1 -1
- package/dist/agents/types.js.map +1 -1
- package/dist/client/index.d.ts +1 -1
- package/dist/client/index.d.ts.map +1 -1
- package/dist/client/types.d.ts +20 -0
- package/dist/client/types.d.ts.map +1 -1
- package/dist/client/workflow-client.d.ts.map +1 -1
- package/dist/client/workflow-client.js +25 -0
- package/dist/client/workflow-client.js.map +1 -1
- package/dist/common/attachment.d.ts +31 -2
- package/dist/common/attachment.d.ts.map +1 -1
- package/dist/common/attachment.js +53 -0
- package/dist/common/attachment.js.map +1 -1
- package/dist/engine/config-validator.d.ts.map +1 -1
- package/dist/engine/config-validator.js +0 -1
- package/dist/engine/config-validator.js.map +1 -1
- package/dist/engine/workflow-executor.d.ts +8 -0
- package/dist/engine/workflow-executor.d.ts.map +1 -1
- package/dist/engine/workflow-executor.js +16 -4
- package/dist/engine/workflow-executor.js.map +1 -1
- package/dist/index.d.ts +3 -0
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +4 -0
- package/dist/index.js.map +1 -1
- package/dist/router/workflow-router.d.ts.map +1 -1
- package/dist/router/workflow-router.js +72 -0
- package/dist/router/workflow-router.js.map +1 -1
- package/dist/runtime/types.d.ts +7 -0
- package/dist/runtime/types.d.ts.map +1 -1
- package/dist/runtime/workflow-runtime.d.ts +24 -0
- package/dist/runtime/workflow-runtime.d.ts.map +1 -1
- package/dist/runtime/workflow-runtime.js +56 -0
- package/dist/runtime/workflow-runtime.js.map +1 -1
- package/dist/sandbox/types.d.ts +85 -0
- package/dist/sandbox/types.d.ts.map +1 -0
- package/dist/sandbox/types.js +22 -0
- package/dist/sandbox/types.js.map +1 -0
- package/dist/steps/base-step.d.ts +14 -0
- package/dist/steps/base-step.d.ts.map +1 -1
- package/dist/steps/base-step.js.map +1 -1
- package/dist/steps/builtin/http-request.step.d.ts +2 -2
- package/dist/testing/mock-step-context.d.ts +4 -0
- package/dist/testing/mock-step-context.d.ts.map +1 -1
- package/dist/testing/mock-step-context.js +2 -0
- package/dist/testing/mock-step-context.js.map +1 -1
- package/dist/triggers/builtin/default-manual-trigger.d.ts.map +1 -1
- package/dist/triggers/builtin/default-manual-trigger.js +17 -4
- package/dist/triggers/builtin/default-manual-trigger.js.map +1 -1
- package/package.json +1 -1
|
@@ -23,20 +23,54 @@ export declare const AgentStepConfigSchema: z.ZodObject<{
|
|
|
23
23
|
requireApproval: z.ZodOptional<z.ZodArray<z.ZodString, "many">>;
|
|
24
24
|
maxTurns: z.ZodDefault<z.ZodNumber>;
|
|
25
25
|
toolTimeout: z.ZodDefault<z.ZodNumber>;
|
|
26
|
+
attachments: z.ZodOptional<z.ZodUnion<[z.ZodArray<z.ZodObject<{
|
|
27
|
+
name: z.ZodString;
|
|
28
|
+
mimeType: z.ZodString;
|
|
29
|
+
data: z.ZodString;
|
|
30
|
+
size: z.ZodOptional<z.ZodNumber>;
|
|
31
|
+
}, "strip", z.ZodTypeAny, {
|
|
32
|
+
name: string;
|
|
33
|
+
mimeType: string;
|
|
34
|
+
data: string;
|
|
35
|
+
size?: number | undefined;
|
|
36
|
+
}, {
|
|
37
|
+
name: string;
|
|
38
|
+
mimeType: string;
|
|
39
|
+
data: string;
|
|
40
|
+
size?: number | undefined;
|
|
41
|
+
}>, "many">, z.ZodString]>>;
|
|
42
|
+
outputType: z.ZodDefault<z.ZodEnum<["string", "json"]>>;
|
|
43
|
+
outputSchema: z.ZodOptional<z.ZodRecord<z.ZodString, z.ZodUnknown>>;
|
|
26
44
|
}, "strip", z.ZodTypeAny, {
|
|
27
45
|
systemPrompt: string;
|
|
28
46
|
userMessage: string;
|
|
29
47
|
maxTurns: number;
|
|
30
48
|
toolTimeout: number;
|
|
49
|
+
outputType: "string" | "json";
|
|
50
|
+
attachments?: string | {
|
|
51
|
+
name: string;
|
|
52
|
+
mimeType: string;
|
|
53
|
+
data: string;
|
|
54
|
+
size?: number | undefined;
|
|
55
|
+
}[] | undefined;
|
|
56
|
+
outputSchema?: Record<string, unknown> | undefined;
|
|
31
57
|
tools?: string[] | undefined;
|
|
32
58
|
requireApproval?: string[] | undefined;
|
|
33
59
|
}, {
|
|
34
60
|
systemPrompt: string;
|
|
35
61
|
userMessage: string;
|
|
62
|
+
attachments?: string | {
|
|
63
|
+
name: string;
|
|
64
|
+
mimeType: string;
|
|
65
|
+
data: string;
|
|
66
|
+
size?: number | undefined;
|
|
67
|
+
}[] | undefined;
|
|
68
|
+
outputSchema?: Record<string, unknown> | undefined;
|
|
36
69
|
tools?: string[] | undefined;
|
|
37
70
|
requireApproval?: string[] | undefined;
|
|
38
71
|
maxTurns?: number | undefined;
|
|
39
72
|
toolTimeout?: number | undefined;
|
|
73
|
+
outputType?: "string" | "json" | undefined;
|
|
40
74
|
}>;
|
|
41
75
|
export type AgentStepConfig = z.infer<typeof AgentStepConfigSchema>;
|
|
42
76
|
export declare class AgentStep extends BaseActionStep<typeof AgentStepConfigSchema, AgentResult> {
|
|
@@ -52,23 +86,57 @@ export declare class AgentStep extends BaseActionStep<typeof AgentStepConfigSche
|
|
|
52
86
|
requireApproval: z.ZodOptional<z.ZodArray<z.ZodString, "many">>;
|
|
53
87
|
maxTurns: z.ZodDefault<z.ZodNumber>;
|
|
54
88
|
toolTimeout: z.ZodDefault<z.ZodNumber>;
|
|
89
|
+
attachments: z.ZodOptional<z.ZodUnion<[z.ZodArray<z.ZodObject<{
|
|
90
|
+
name: z.ZodString;
|
|
91
|
+
mimeType: z.ZodString;
|
|
92
|
+
data: z.ZodString;
|
|
93
|
+
size: z.ZodOptional<z.ZodNumber>;
|
|
94
|
+
}, "strip", z.ZodTypeAny, {
|
|
95
|
+
name: string;
|
|
96
|
+
mimeType: string;
|
|
97
|
+
data: string;
|
|
98
|
+
size?: number | undefined;
|
|
99
|
+
}, {
|
|
100
|
+
name: string;
|
|
101
|
+
mimeType: string;
|
|
102
|
+
data: string;
|
|
103
|
+
size?: number | undefined;
|
|
104
|
+
}>, "many">, z.ZodString]>>;
|
|
105
|
+
outputType: z.ZodDefault<z.ZodEnum<["string", "json"]>>;
|
|
106
|
+
outputSchema: z.ZodOptional<z.ZodRecord<z.ZodString, z.ZodUnknown>>;
|
|
55
107
|
}, "strip", z.ZodTypeAny, {
|
|
56
108
|
systemPrompt: string;
|
|
57
109
|
userMessage: string;
|
|
58
110
|
maxTurns: number;
|
|
59
111
|
toolTimeout: number;
|
|
112
|
+
outputType: "string" | "json";
|
|
113
|
+
attachments?: string | {
|
|
114
|
+
name: string;
|
|
115
|
+
mimeType: string;
|
|
116
|
+
data: string;
|
|
117
|
+
size?: number | undefined;
|
|
118
|
+
}[] | undefined;
|
|
119
|
+
outputSchema?: Record<string, unknown> | undefined;
|
|
60
120
|
tools?: string[] | undefined;
|
|
61
121
|
requireApproval?: string[] | undefined;
|
|
62
122
|
}, {
|
|
63
123
|
systemPrompt: string;
|
|
64
124
|
userMessage: string;
|
|
125
|
+
attachments?: string | {
|
|
126
|
+
name: string;
|
|
127
|
+
mimeType: string;
|
|
128
|
+
data: string;
|
|
129
|
+
size?: number | undefined;
|
|
130
|
+
}[] | undefined;
|
|
131
|
+
outputSchema?: Record<string, unknown> | undefined;
|
|
65
132
|
tools?: string[] | undefined;
|
|
66
133
|
requireApproval?: string[] | undefined;
|
|
67
134
|
maxTurns?: number | undefined;
|
|
68
135
|
toolTimeout?: number | undefined;
|
|
136
|
+
outputType?: "string" | "json" | undefined;
|
|
69
137
|
}>;
|
|
70
138
|
readonly outputSchema: z.ZodObject<{
|
|
71
|
-
response: z.ZodString
|
|
139
|
+
response: z.ZodUnion<[z.ZodString, z.ZodRecord<z.ZodString, z.ZodUnknown>]>;
|
|
72
140
|
toolCalls: z.ZodArray<z.ZodObject<{
|
|
73
141
|
name: z.ZodString;
|
|
74
142
|
args: z.ZodRecord<z.ZodString, z.ZodUnknown>;
|
|
@@ -96,8 +164,30 @@ export declare class AgentStep extends BaseActionStep<typeof AgentStepConfigSche
|
|
|
96
164
|
inputTokens: number;
|
|
97
165
|
outputTokens: number;
|
|
98
166
|
}>;
|
|
167
|
+
attachments: z.ZodArray<z.ZodObject<{
|
|
168
|
+
name: z.ZodString;
|
|
169
|
+
mimeType: z.ZodString;
|
|
170
|
+
data: z.ZodString;
|
|
171
|
+
size: z.ZodOptional<z.ZodNumber>;
|
|
172
|
+
}, "strip", z.ZodTypeAny, {
|
|
173
|
+
name: string;
|
|
174
|
+
mimeType: string;
|
|
175
|
+
data: string;
|
|
176
|
+
size?: number | undefined;
|
|
177
|
+
}, {
|
|
178
|
+
name: string;
|
|
179
|
+
mimeType: string;
|
|
180
|
+
data: string;
|
|
181
|
+
size?: number | undefined;
|
|
182
|
+
}>, "many">;
|
|
99
183
|
}, "strip", z.ZodTypeAny, {
|
|
100
|
-
|
|
184
|
+
attachments: {
|
|
185
|
+
name: string;
|
|
186
|
+
mimeType: string;
|
|
187
|
+
data: string;
|
|
188
|
+
size?: number | undefined;
|
|
189
|
+
}[];
|
|
190
|
+
response: string | Record<string, unknown>;
|
|
101
191
|
toolCalls: {
|
|
102
192
|
name: string;
|
|
103
193
|
args: Record<string, unknown>;
|
|
@@ -110,7 +200,13 @@ export declare class AgentStep extends BaseActionStep<typeof AgentStepConfigSche
|
|
|
110
200
|
outputTokens: number;
|
|
111
201
|
};
|
|
112
202
|
}, {
|
|
113
|
-
|
|
203
|
+
attachments: {
|
|
204
|
+
name: string;
|
|
205
|
+
mimeType: string;
|
|
206
|
+
data: string;
|
|
207
|
+
size?: number | undefined;
|
|
208
|
+
}[];
|
|
209
|
+
response: string | Record<string, unknown>;
|
|
114
210
|
toolCalls: {
|
|
115
211
|
name: string;
|
|
116
212
|
args: Record<string, unknown>;
|
|
@@ -126,6 +222,7 @@ export declare class AgentStep extends BaseActionStep<typeof AgentStepConfigSche
|
|
|
126
222
|
readonly category: StepCategory;
|
|
127
223
|
constructor(runtime: BaseAgentRuntime, toolRegistry: AgentToolRegistry);
|
|
128
224
|
decorateConfigSchema(jsonSchema: Record<string, unknown>): Record<string, unknown>;
|
|
225
|
+
resolveOutputJsonSchema(config: Record<string, unknown>): Record<string, unknown>;
|
|
129
226
|
execute(config: AgentStepConfig, ctx: StepExecutionContext): Promise<AgentResult>;
|
|
130
227
|
/**
|
|
131
228
|
* Continue a prior conversation. Called from `execute()` when
|
|
@@ -148,5 +245,20 @@ export declare class AgentStep extends BaseActionStep<typeof AgentStepConfigSche
|
|
|
148
245
|
private buildHooks;
|
|
149
246
|
private consumeEvents;
|
|
150
247
|
private buildResult;
|
|
248
|
+
/**
|
|
249
|
+
* Open the per-execution tool runtime: an artifact sink (backed by storage)
|
|
250
|
+
* and — when a SandboxAdapter is configured and the `run_code` tool is
|
|
251
|
+
* enabled — one sandbox session with the step's input attachments mounted at
|
|
252
|
+
* `/mnt/files`. Returns the extras to merge into each tool's context, the
|
|
253
|
+
* collected-artifacts array, and a `dispose()` that tears the sandbox down.
|
|
254
|
+
*/
|
|
255
|
+
/** Append the uploaded-files note to the system prompt when files were mounted. */
|
|
256
|
+
private withAttachmentsNote;
|
|
257
|
+
private openToolRuntime;
|
|
258
|
+
/**
|
|
259
|
+
* Attach collected artifacts and shape `response` per `outputType`
|
|
260
|
+
* (parse JSON when requested). Applied to every successful agent result.
|
|
261
|
+
*/
|
|
262
|
+
private finalizeResult;
|
|
151
263
|
}
|
|
152
264
|
//# sourceMappingURL=agent-step.d.ts.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"agent-step.d.ts","sourceRoot":"","sources":["../../src/agents/agent-step.ts"],"names":[],"mappings":"AAAA;;;;;;;;;GASG;AAEH,OAAO,EAAE,CAAC,EAAE,MAAM,KAAK,CAAC;AAGxB,OAAO,EAAE,cAAc,EAAE,MAAM,uBAAuB,CAAC;AACvD,OAAO,KAAK,EAAE,oBAAoB,EAAE,MAAM,uBAAuB,CAAC;AAClE,OAAO,KAAK,EAAE,QAAQ,EAAE,MAAM,wBAAwB,CAAC;AACvD,OAAO,KAAK,EAAE,YAAY,EAAE,MAAM,wBAAwB,CAAC;
|
|
1
|
+
{"version":3,"file":"agent-step.d.ts","sourceRoot":"","sources":["../../src/agents/agent-step.ts"],"names":[],"mappings":"AAAA;;;;;;;;;GASG;AAEH,OAAO,EAAE,CAAC,EAAE,MAAM,KAAK,CAAC;AAGxB,OAAO,EAAE,cAAc,EAAE,MAAM,uBAAuB,CAAC;AACvD,OAAO,KAAK,EAAE,oBAAoB,EAAE,MAAM,uBAAuB,CAAC;AAClE,OAAO,KAAK,EAAE,QAAQ,EAAE,MAAM,wBAAwB,CAAC;AACvD,OAAO,KAAK,EAAE,YAAY,EAAE,MAAM,wBAAwB,CAAC;AAE3D,OAAO,KAAK,EAAE,gBAAgB,EAAE,MAAM,mBAAmB,CAAC;AAC1D,OAAO,KAAK,EAAE,iBAAiB,EAAE,MAAM,oBAAoB,CAAC;AAa5D,OAAO,KAAK,EACV,WAAW,EAKZ,MAAM,YAAY,CAAC;AAIpB,eAAO,MAAM,qBAAqB;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;EAehC,CAAC;AAEH,MAAM,MAAM,eAAe,GAAG,CAAC,CAAC,KAAK,CAAC,OAAO,qBAAqB,CAAC,CAAC;AA2EpE,qBAAa,SAAU,SAAQ,cAAc,CAC3C,OAAO,qBAAqB,EAC5B,WAAW,CACZ;IASG,OAAO,CAAC,QAAQ,CAAC,OAAO;IACxB,OAAO,CAAC,QAAQ,CAAC,YAAY;IAT/B,SAAkB,IAAI,EAAE,QAAQ,CAAe;IAC/C,SAAkB,IAAI,eAAe;IACrC,SAAkB,WAAW,qCAAqC;IAClE,SAAkB,YAAY;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;OAAyB;IACvD,SAAkB,YAAY;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;OAAqB;IACnD,SAAkB,QAAQ,EAAE,YAAY,CAAQ;gBAG7B,OAAO,EAAE,gBAAgB,EACzB,YAAY,EAAE,iBAAiB;IAKzC,oBAAoB,CAAC,UAAU,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,GAAG,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC;IA8BlF,uBAAuB,CAAC,MAAM,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,GAAG,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC;IAmB3E,OAAO,CACpB,MAAM,EAAE,eAAe,EACvB,GAAG,EAAE,oBAAoB,GACxB,OAAO,CAAC,WAAW,CAAC;IA0CvB;;;;;;;OAOG;YACW,oBAAoB;IA0DnB,QAAQ,CACrB,OAAO,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,EAChC,MAAM,EAAE,eAAe,EACvB,GAAG,EAAE,oBAAoB,GACxB,OAAO,CAAC,WAAW,CAAC;IAwDvB,OAAO,CAAC,YAAY;IAgCpB;;;;OAIG;IACH,OAAO,CAAC,eAAe;IAWvB,OAAO,CAAC,iBAAiB;IAazB,OAAO,CAAC,UAAU;YAgBJ,aAAa;IA8H3B,OAAO,CAAC,WAAW;IAsBnB;;;;;;OAMG;IACH,mFAAmF;IACnF,OAAO,CAAC,mBAAmB;YAQb,eAAe;IAqE7B;;;OAGG;IACH,OAAO,CAAC,cAAc;CAWvB"}
|
|
@@ -10,6 +10,9 @@
|
|
|
10
10
|
*/
|
|
11
11
|
import { z } from 'zod';
|
|
12
12
|
import { BaseActionStep } from '../steps/base-step.js';
|
|
13
|
+
import { variableRef } from '../types/workflow-config.js';
|
|
14
|
+
import { AttachmentSchema, ATTACHMENT_ARRAY_JSON_SCHEMA, markAttachmentFields } from '../common/attachment.js';
|
|
15
|
+
import { bindSandboxSession } from '../sandbox/types.js';
|
|
13
16
|
import { runInterceptorChain, } from './interceptor.js';
|
|
14
17
|
import { ApprovalGateInterceptor } from './interceptors/approval-gate.js';
|
|
15
18
|
import { ToolLoggingInterceptor } from './interceptors/tool-logger.js';
|
|
@@ -21,10 +24,16 @@ export const AgentStepConfigSchema = z.object({
|
|
|
21
24
|
requireApproval: z.array(z.string()).optional().describe('Tool names that require human approval before execution'),
|
|
22
25
|
maxTurns: z.number().int().min(1).default(10).describe('Maximum number of agent reasoning turns'),
|
|
23
26
|
toolTimeout: z.number().int().positive().default(30_000).describe('Timeout per tool call in milliseconds'),
|
|
27
|
+
attachments: variableRef(z.array(AttachmentSchema)
|
|
28
|
+
.describe('Input documents mounted into the sandbox at /mnt/files. Bind to a ref, e.g. {{trigger.attachments}}')).optional(),
|
|
29
|
+
outputType: z.enum(['string', 'json']).default('string')
|
|
30
|
+
.describe('Shape of the agent response: free text, or a JSON object matching outputSchema'),
|
|
31
|
+
outputSchema: z.record(z.unknown()).optional()
|
|
32
|
+
.describe('When outputType is "json", the JSON Schema the response object should match'),
|
|
24
33
|
});
|
|
25
34
|
// ─── Result Schema ───
|
|
26
35
|
const AgentResultSchema = z.object({
|
|
27
|
-
response: z.string(),
|
|
36
|
+
response: z.union([z.string(), z.record(z.unknown())]),
|
|
28
37
|
toolCalls: z.array(z.object({
|
|
29
38
|
name: z.string(),
|
|
30
39
|
args: z.record(z.unknown()),
|
|
@@ -36,6 +45,7 @@ const AgentResultSchema = z.object({
|
|
|
36
45
|
inputTokens: z.number(),
|
|
37
46
|
outputTokens: z.number(),
|
|
38
47
|
}),
|
|
48
|
+
attachments: z.array(AttachmentSchema),
|
|
39
49
|
});
|
|
40
50
|
// ─── Utility ───
|
|
41
51
|
function withTimeout(promise, timeoutMs) {
|
|
@@ -50,13 +60,34 @@ function withTimeout(promise, timeoutMs) {
|
|
|
50
60
|
}),
|
|
51
61
|
]);
|
|
52
62
|
}
|
|
53
|
-
/** Extract text content from an AssistantMessage.
|
|
63
|
+
/** Extract text content from an AssistantMessage.
|
|
64
|
+
*
|
|
65
|
+
* Tolerant of the shapes different providers/runtimes return for `content`:
|
|
66
|
+
* an array of content blocks (pi-ai), a plain string (OpenAI/LiteLLM-style),
|
|
67
|
+
* or null/undefined (tool-call-only turns). */
|
|
54
68
|
function extractResponseText(message) {
|
|
55
|
-
|
|
56
|
-
|
|
69
|
+
const content = message.content;
|
|
70
|
+
if (typeof content === 'string')
|
|
71
|
+
return content;
|
|
72
|
+
if (!Array.isArray(content))
|
|
73
|
+
return '';
|
|
74
|
+
return content
|
|
75
|
+
.filter((c) => !!c && typeof c === 'object' && c.type === 'text'
|
|
76
|
+
&& typeof c.text === 'string')
|
|
57
77
|
.map((c) => c.text)
|
|
58
78
|
.join('');
|
|
59
79
|
}
|
|
80
|
+
/**
|
|
81
|
+
* Tell the agent which uploaded files are available, so it knows to read them
|
|
82
|
+
* via `run_code` (otherwise it has no idea attachments exist). Appended to the
|
|
83
|
+
* system prompt when input attachments were mounted into the sandbox.
|
|
84
|
+
*/
|
|
85
|
+
function attachmentsSystemNote(files) {
|
|
86
|
+
const list = files.map((f) => `/mnt/files/${f}`).join(', ');
|
|
87
|
+
return (`\n\nThe user attached ${files.length} file(s), available in the sandbox at: ${list}. `
|
|
88
|
+
+ `Use the run_code tool to read them (e.g. open the file under /mnt/files) before responding. `
|
|
89
|
+
+ `Write any files you generate to /mnt/output.`);
|
|
90
|
+
}
|
|
60
91
|
// ─── AgentStep ───
|
|
61
92
|
export class AgentStep extends BaseActionStep {
|
|
62
93
|
runtime;
|
|
@@ -73,12 +104,16 @@ export class AgentStep extends BaseActionStep {
|
|
|
73
104
|
this.toolRegistry = toolRegistry;
|
|
74
105
|
}
|
|
75
106
|
decorateConfigSchema(jsonSchema) {
|
|
76
|
-
const toolNames = this.toolRegistry.getNames();
|
|
77
|
-
if (toolNames.length === 0)
|
|
78
|
-
return jsonSchema;
|
|
79
107
|
const props = jsonSchema['properties'];
|
|
80
108
|
if (!props)
|
|
81
109
|
return jsonSchema;
|
|
110
|
+
if (props['outputSchema']) {
|
|
111
|
+
props['outputSchema'] = { ...props['outputSchema'], format: 'json-schema' };
|
|
112
|
+
}
|
|
113
|
+
markAttachmentFields(jsonSchema, ['attachments']);
|
|
114
|
+
const toolNames = this.toolRegistry.getNames();
|
|
115
|
+
if (toolNames.length === 0)
|
|
116
|
+
return jsonSchema;
|
|
82
117
|
// Inject enum into tools array items so the generic renderer shows multi-select
|
|
83
118
|
if (props['tools']?.['items']) {
|
|
84
119
|
props['tools'] = {
|
|
@@ -95,6 +130,22 @@ export class AgentStep extends BaseActionStep {
|
|
|
95
130
|
}
|
|
96
131
|
return jsonSchema;
|
|
97
132
|
}
|
|
133
|
+
resolveOutputJsonSchema(config) {
|
|
134
|
+
const outputType = config['outputType'] === 'json' ? 'json' : 'string';
|
|
135
|
+
const userSchema = config['outputSchema'];
|
|
136
|
+
const response = outputType === 'json'
|
|
137
|
+
? (userSchema && Object.keys(userSchema).length > 0 ? userSchema : { type: 'object' })
|
|
138
|
+
: { type: 'string' };
|
|
139
|
+
return {
|
|
140
|
+
type: 'object',
|
|
141
|
+
properties: {
|
|
142
|
+
response,
|
|
143
|
+
attachments: ATTACHMENT_ARRAY_JSON_SCHEMA,
|
|
144
|
+
toolCalls: { type: 'array' },
|
|
145
|
+
usage: { type: 'object' },
|
|
146
|
+
},
|
|
147
|
+
};
|
|
148
|
+
}
|
|
98
149
|
async execute(config, ctx) {
|
|
99
150
|
// Conversation continuity: when a prior run left messages (review-gate
|
|
100
151
|
// retry or rerunFromStep), continue the conversation with the new
|
|
@@ -107,24 +158,26 @@ export class AgentStep extends BaseActionStep {
|
|
|
107
158
|
const workflowTools = config.tools
|
|
108
159
|
? this.toolRegistry.resolve(config.tools)
|
|
109
160
|
: [];
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
|
|
161
|
+
const bag = await this.openToolRuntime(config, ctx);
|
|
162
|
+
try {
|
|
163
|
+
const runConfig = this.withAttachmentsNote(config, bag.mountedFiles);
|
|
164
|
+
const piTools = this.convertTools(workflowTools, config.toolTimeout, ctx, bag.extras);
|
|
165
|
+
const effectiveMaxTurns = this.runtime.capabilities.multiTurn
|
|
166
|
+
? config.maxTurns
|
|
167
|
+
: 1;
|
|
168
|
+
const interceptors = this.buildInterceptors(config, ctx);
|
|
169
|
+
const hooks = this.buildHooks(interceptors, ctx);
|
|
170
|
+
const events = this.runtime.execute(runConfig, {
|
|
171
|
+
tools: this.runtime.capabilities.toolUse ? piTools : [],
|
|
172
|
+
hooks: this.runtime.capabilities.hooks ? hooks : {},
|
|
173
|
+
signal: ctx.runtime.metadata['signal'] ?? new AbortController().signal,
|
|
174
|
+
}, ctx);
|
|
175
|
+
const result = await this.consumeEvents(events, ctx, effectiveMaxTurns);
|
|
176
|
+
return this.finalizeResult(result, config, bag.collected);
|
|
177
|
+
}
|
|
178
|
+
finally {
|
|
179
|
+
await bag.dispose();
|
|
180
|
+
}
|
|
128
181
|
}
|
|
129
182
|
/**
|
|
130
183
|
* Continue a prior conversation. Called from `execute()` when
|
|
@@ -139,42 +192,50 @@ export class AgentStep extends BaseActionStep {
|
|
|
139
192
|
const workflowTools = config.tools
|
|
140
193
|
? this.toolRegistry.resolve(config.tools)
|
|
141
194
|
: [];
|
|
142
|
-
const piTools = this.convertTools(workflowTools, config.toolTimeout, ctx);
|
|
143
|
-
const interceptors = this.buildInterceptors(config, ctx);
|
|
144
|
-
const hooks = this.buildHooks(interceptors, ctx);
|
|
145
195
|
const effectiveMaxTurns = this.runtime.capabilities.multiTurn
|
|
146
196
|
? config.maxTurns
|
|
147
197
|
: 1;
|
|
148
198
|
const completedToolCalls = priorState.completedToolCalls ?? [];
|
|
149
199
|
const usage = priorState.usage ?? { inputTokens: 0, outputTokens: 0 };
|
|
150
200
|
const currentTurn = priorState.currentTurn ?? 0;
|
|
151
|
-
const
|
|
152
|
-
|
|
153
|
-
|
|
154
|
-
|
|
155
|
-
|
|
156
|
-
|
|
157
|
-
|
|
158
|
-
|
|
159
|
-
|
|
160
|
-
|
|
161
|
-
|
|
162
|
-
|
|
163
|
-
|
|
164
|
-
|
|
165
|
-
|
|
166
|
-
|
|
201
|
+
const bag = await this.openToolRuntime(config, ctx);
|
|
202
|
+
try {
|
|
203
|
+
const runConfig = this.withAttachmentsNote(config, bag.mountedFiles);
|
|
204
|
+
const piTools = this.convertTools(workflowTools, config.toolTimeout, ctx, bag.extras);
|
|
205
|
+
const interceptors = this.buildInterceptors(config, ctx);
|
|
206
|
+
const hooks = this.buildHooks(interceptors, ctx);
|
|
207
|
+
const input = {
|
|
208
|
+
tools: this.runtime.capabilities.toolUse ? piTools : [],
|
|
209
|
+
hooks: this.runtime.capabilities.hooks ? hooks : {},
|
|
210
|
+
signal: ctx.runtime.metadata['signal'] ?? new AbortController().signal,
|
|
211
|
+
resume: {
|
|
212
|
+
messages: priorMessages,
|
|
213
|
+
payload: { action: 'approve', feedback: config.userMessage },
|
|
214
|
+
pauseState: {
|
|
215
|
+
type: 'awaiting_feedback',
|
|
216
|
+
reason: 'Continuing prior conversation',
|
|
217
|
+
prompt: config.userMessage,
|
|
218
|
+
state: {
|
|
219
|
+
messages: priorMessages,
|
|
220
|
+
currentTurn,
|
|
221
|
+
completedToolCalls,
|
|
222
|
+
usage,
|
|
223
|
+
},
|
|
167
224
|
},
|
|
168
225
|
},
|
|
169
|
-
}
|
|
170
|
-
|
|
171
|
-
|
|
172
|
-
|
|
173
|
-
|
|
174
|
-
|
|
175
|
-
|
|
176
|
-
|
|
177
|
-
|
|
226
|
+
};
|
|
227
|
+
const events = this.runtime.execute(runConfig, input, ctx);
|
|
228
|
+
const result = await this.consumeEvents(events, ctx, effectiveMaxTurns, {
|
|
229
|
+
messages: priorMessages,
|
|
230
|
+
currentTurn,
|
|
231
|
+
completedToolCalls,
|
|
232
|
+
usage,
|
|
233
|
+
});
|
|
234
|
+
return this.finalizeResult(result, config, bag.collected);
|
|
235
|
+
}
|
|
236
|
+
finally {
|
|
237
|
+
await bag.dispose();
|
|
238
|
+
}
|
|
178
239
|
}
|
|
179
240
|
async onResume(rowData, config, ctx) {
|
|
180
241
|
const pauseState = rowData;
|
|
@@ -188,36 +249,45 @@ export class AgentStep extends BaseActionStep {
|
|
|
188
249
|
toolCalls: pauseState.completedToolCalls,
|
|
189
250
|
turnCount: pauseState.currentTurn,
|
|
190
251
|
usage: pauseState.usage,
|
|
252
|
+
attachments: [],
|
|
191
253
|
};
|
|
192
254
|
}
|
|
193
255
|
// Resolve tools again (MCP tools may have changed)
|
|
194
256
|
const workflowTools = config.tools
|
|
195
257
|
? this.toolRegistry.resolve(config.tools)
|
|
196
258
|
: [];
|
|
197
|
-
const piTools = this.convertTools(workflowTools, config.toolTimeout, ctx);
|
|
198
|
-
const interceptors = this.buildInterceptors(config, ctx);
|
|
199
|
-
const hooks = this.buildHooks(interceptors, ctx);
|
|
200
259
|
const effectiveMaxTurns = this.runtime.capabilities.multiTurn
|
|
201
260
|
? config.maxTurns
|
|
202
261
|
: 1;
|
|
203
|
-
const
|
|
204
|
-
|
|
205
|
-
|
|
206
|
-
|
|
207
|
-
|
|
208
|
-
|
|
209
|
-
input
|
|
210
|
-
|
|
211
|
-
|
|
212
|
-
|
|
262
|
+
const bag = await this.openToolRuntime(config, ctx);
|
|
263
|
+
try {
|
|
264
|
+
const runConfig = this.withAttachmentsNote(config, bag.mountedFiles);
|
|
265
|
+
const piTools = this.convertTools(workflowTools, config.toolTimeout, ctx, bag.extras);
|
|
266
|
+
const interceptors = this.buildInterceptors(config, ctx);
|
|
267
|
+
const hooks = this.buildHooks(interceptors, ctx);
|
|
268
|
+
const input = {
|
|
269
|
+
tools: this.runtime.capabilities.toolUse ? piTools : [],
|
|
270
|
+
hooks: this.runtime.capabilities.hooks ? hooks : {},
|
|
271
|
+
signal: ctx.runtime.metadata['signal'] ?? new AbortController().signal,
|
|
213
272
|
};
|
|
273
|
+
if (pauseRequest) {
|
|
274
|
+
input.resume = {
|
|
275
|
+
messages: pauseState.messages,
|
|
276
|
+
payload: resumePayload ?? { action: 'approve' },
|
|
277
|
+
pauseState: pauseRequest,
|
|
278
|
+
};
|
|
279
|
+
}
|
|
280
|
+
const events = this.runtime.execute(runConfig, input, ctx);
|
|
281
|
+
// Consume events, carrying over state from before pause
|
|
282
|
+
const result = await this.consumeEvents(events, ctx, effectiveMaxTurns, pauseState);
|
|
283
|
+
return this.finalizeResult(result, config, bag.collected);
|
|
284
|
+
}
|
|
285
|
+
finally {
|
|
286
|
+
await bag.dispose();
|
|
214
287
|
}
|
|
215
|
-
const events = this.runtime.execute(config, input, ctx);
|
|
216
|
-
// Consume events, carrying over state from before pause
|
|
217
|
-
return this.consumeEvents(events, ctx, effectiveMaxTurns, pauseState);
|
|
218
288
|
}
|
|
219
289
|
// ─── Private: Tool conversion ───
|
|
220
|
-
convertTools(tools, timeoutMs, ctx) {
|
|
290
|
+
convertTools(tools, timeoutMs, ctx, extras) {
|
|
221
291
|
return tools.map((tool) => ({
|
|
222
292
|
name: tool.name,
|
|
223
293
|
label: tool.name,
|
|
@@ -227,6 +297,7 @@ export class AgentStep extends BaseActionStep {
|
|
|
227
297
|
const result = await withTimeout(tool.execute(params, {
|
|
228
298
|
signal: signal ?? new AbortController().signal,
|
|
229
299
|
log: ctx.log,
|
|
300
|
+
...extras,
|
|
230
301
|
}), timeoutMs);
|
|
231
302
|
return {
|
|
232
303
|
content: [{ type: 'text', text: JSON.stringify(result) }],
|
|
@@ -258,17 +329,14 @@ export class AgentStep extends BaseActionStep {
|
|
|
258
329
|
}
|
|
259
330
|
return interceptors;
|
|
260
331
|
}
|
|
261
|
-
buildHooks(interceptors,
|
|
332
|
+
buildHooks(interceptors, _ctx) {
|
|
262
333
|
return {
|
|
263
334
|
async beforeToolCall(tool, args) {
|
|
264
335
|
return runInterceptorChain(interceptors, tool, args);
|
|
265
336
|
},
|
|
266
|
-
|
|
267
|
-
|
|
268
|
-
|
|
269
|
-
data: { tool, args, result, phase: 'end' },
|
|
270
|
-
});
|
|
271
|
-
},
|
|
337
|
+
// Note: the tool_call end event is emitted from consumeEvents
|
|
338
|
+
// (tool_execution_end) with the toolCallId, so we don't emit it here too
|
|
339
|
+
// (that produced duplicate timeline entries).
|
|
272
340
|
};
|
|
273
341
|
}
|
|
274
342
|
// ─── Private: Event consumption ───
|
|
@@ -293,15 +361,9 @@ export class AgentStep extends BaseActionStep {
|
|
|
293
361
|
case 'message_start':
|
|
294
362
|
ctx.emit({ type: 'thinking', data: { phase: 'start' } });
|
|
295
363
|
break;
|
|
296
|
-
case 'message_update':
|
|
297
|
-
|
|
298
|
-
const partialText = extractResponseText(updateMsg);
|
|
299
|
-
ctx.emit({
|
|
300
|
-
type: 'progress',
|
|
301
|
-
data: { content: partialText },
|
|
302
|
-
});
|
|
364
|
+
case 'message_update':
|
|
365
|
+
// Partial streaming deltas — intentionally NOT emitted.
|
|
303
366
|
break;
|
|
304
|
-
}
|
|
305
367
|
case 'message_end': {
|
|
306
368
|
const endMsg = event.message;
|
|
307
369
|
state.messages.push(endMsg);
|
|
@@ -387,8 +449,85 @@ export class AgentStep extends BaseActionStep {
|
|
|
387
449
|
toolCalls: state.completedToolCalls,
|
|
388
450
|
turnCount: state.currentTurn,
|
|
389
451
|
usage: state.usage,
|
|
452
|
+
attachments: [],
|
|
390
453
|
};
|
|
391
454
|
}
|
|
455
|
+
// ─── Private: Sandbox session + artifact sink (per execution) ───
|
|
456
|
+
/**
|
|
457
|
+
* Open the per-execution tool runtime: an artifact sink (backed by storage)
|
|
458
|
+
* and — when a SandboxAdapter is configured and the `run_code` tool is
|
|
459
|
+
* enabled — one sandbox session with the step's input attachments mounted at
|
|
460
|
+
* `/mnt/files`. Returns the extras to merge into each tool's context, the
|
|
461
|
+
* collected-artifacts array, and a `dispose()` that tears the sandbox down.
|
|
462
|
+
*/
|
|
463
|
+
/** Append the uploaded-files note to the system prompt when files were mounted. */
|
|
464
|
+
withAttachmentsNote(config, mountedFiles) {
|
|
465
|
+
if (mountedFiles.length === 0)
|
|
466
|
+
return config;
|
|
467
|
+
return { ...config, systemPrompt: config.systemPrompt + attachmentsSystemNote(mountedFiles) };
|
|
468
|
+
}
|
|
469
|
+
async openToolRuntime(config, ctx) {
|
|
470
|
+
const collected = [];
|
|
471
|
+
const mountedFiles = [];
|
|
472
|
+
const scope = ctx.runtime.metadata;
|
|
473
|
+
const storage = ctx.storage;
|
|
474
|
+
const addArtifact = storage
|
|
475
|
+
? async (file) => {
|
|
476
|
+
const att = await storage.store(scope, file);
|
|
477
|
+
collected.push(att);
|
|
478
|
+
return att;
|
|
479
|
+
}
|
|
480
|
+
: undefined;
|
|
481
|
+
const usesRunCode = config.tools?.includes('run_code') ?? false;
|
|
482
|
+
let handle;
|
|
483
|
+
let session;
|
|
484
|
+
if (ctx.sandbox && usesRunCode) {
|
|
485
|
+
handle = await ctx.sandbox.create({ scope });
|
|
486
|
+
session = bindSandboxSession(ctx.sandbox, handle);
|
|
487
|
+
// Mount the step's resolved input documents at /mnt/files. The field is a
|
|
488
|
+
// `variableRef` union (Attachment[] | ref-string); by execute() the
|
|
489
|
+
// resolver has replaced any ref with the concrete array, so a non-array
|
|
490
|
+
// here means "nothing bound".
|
|
491
|
+
const inputs = Array.isArray(config.attachments) ? config.attachments : [];
|
|
492
|
+
if (inputs.length > 0 && storage) {
|
|
493
|
+
const files = await Promise.all(inputs.map(async (a) => ({
|
|
494
|
+
path: `/mnt/files/${a.name}`,
|
|
495
|
+
bytes: await storage.read(a),
|
|
496
|
+
})));
|
|
497
|
+
await session.writeFiles(files);
|
|
498
|
+
mountedFiles.push(...inputs.map((a) => a.name));
|
|
499
|
+
}
|
|
500
|
+
}
|
|
501
|
+
// Only the two narrow capabilities are exposed to tools; storage/scope/
|
|
502
|
+
// attachments stay internal to this step (used above to mount inputs and to
|
|
503
|
+
// back addArtifact).
|
|
504
|
+
const extras = {
|
|
505
|
+
...(session ? { sandbox: session } : {}),
|
|
506
|
+
...(addArtifact ? { addArtifact } : {}),
|
|
507
|
+
};
|
|
508
|
+
const dispose = async () => {
|
|
509
|
+
if (ctx.sandbox && handle) {
|
|
510
|
+
try {
|
|
511
|
+
await ctx.sandbox.destroy(handle);
|
|
512
|
+
}
|
|
513
|
+
catch (err) {
|
|
514
|
+
ctx.log.warn(`Failed to destroy sandbox: ${err instanceof Error ? err.message : String(err)}`);
|
|
515
|
+
}
|
|
516
|
+
}
|
|
517
|
+
};
|
|
518
|
+
return { extras, collected, mountedFiles, dispose };
|
|
519
|
+
}
|
|
520
|
+
/**
|
|
521
|
+
* Attach collected artifacts and shape `response` per `outputType`
|
|
522
|
+
* (parse JSON when requested). Applied to every successful agent result.
|
|
523
|
+
*/
|
|
524
|
+
finalizeResult(result, config, collected) {
|
|
525
|
+
let response = result.response;
|
|
526
|
+
if (config.outputType === 'json' && typeof response === 'string') {
|
|
527
|
+
response = parseJsonResponse(response);
|
|
528
|
+
}
|
|
529
|
+
return { ...result, response, attachments: collected };
|
|
530
|
+
}
|
|
392
531
|
}
|
|
393
532
|
/**
|
|
394
533
|
* Pull prior conversation messages out of an opaque `priorState` blob.
|
|
@@ -402,4 +541,27 @@ function extractPriorMessages(priorState) {
|
|
|
402
541
|
return null;
|
|
403
542
|
return candidate;
|
|
404
543
|
}
|
|
544
|
+
/**
|
|
545
|
+
* Parse a JSON-mode agent response into an object. Strips Markdown code fences
|
|
546
|
+
* the model may wrap around the JSON. Falls back to `{ text }` if the response
|
|
547
|
+
* isn't valid JSON or isn't an object, so the output always matches the
|
|
548
|
+
* declared object shape.
|
|
549
|
+
*/
|
|
550
|
+
function parseJsonResponse(raw) {
|
|
551
|
+
const trimmed = raw.trim();
|
|
552
|
+
const unfenced = trimmed
|
|
553
|
+
.replace(/^```(?:json)?\s*/i, '')
|
|
554
|
+
.replace(/\s*```$/, '')
|
|
555
|
+
.trim();
|
|
556
|
+
try {
|
|
557
|
+
const parsed = JSON.parse(unfenced);
|
|
558
|
+
if (parsed && typeof parsed === 'object' && !Array.isArray(parsed)) {
|
|
559
|
+
return parsed;
|
|
560
|
+
}
|
|
561
|
+
return { value: parsed };
|
|
562
|
+
}
|
|
563
|
+
catch {
|
|
564
|
+
return { text: raw };
|
|
565
|
+
}
|
|
566
|
+
}
|
|
405
567
|
//# sourceMappingURL=agent-step.js.map
|