@xyne/workflow-sdk 2.2.0 → 2.3.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (88) hide show
  1. package/dist/agents/agent-step.d.ts +115 -3
  2. package/dist/agents/agent-step.d.ts.map +1 -1
  3. package/dist/agents/agent-step.js +247 -85
  4. package/dist/agents/agent-step.js.map +1 -1
  5. package/dist/agents/builtin/run-code-tool.d.ts +29 -0
  6. package/dist/agents/builtin/run-code-tool.d.ts.map +1 -0
  7. package/dist/agents/builtin/run-code-tool.js +54 -0
  8. package/dist/agents/builtin/run-code-tool.js.map +1 -0
  9. package/dist/agents/index.d.ts +1 -0
  10. package/dist/agents/index.d.ts.map +1 -1
  11. package/dist/agents/index.js +1 -0
  12. package/dist/agents/index.js.map +1 -1
  13. package/dist/agents/pi-mono-runtime.d.ts +5 -6
  14. package/dist/agents/pi-mono-runtime.d.ts.map +1 -1
  15. package/dist/agents/pi-mono-runtime.js +4 -5
  16. package/dist/agents/pi-mono-runtime.js.map +1 -1
  17. package/dist/agents/tool-types.d.ts +5 -0
  18. package/dist/agents/tool-types.d.ts.map +1 -1
  19. package/dist/agents/types.d.ts +5 -1
  20. package/dist/agents/types.d.ts.map +1 -1
  21. package/dist/agents/types.js.map +1 -1
  22. package/dist/client/index.d.ts +1 -1
  23. package/dist/client/index.d.ts.map +1 -1
  24. package/dist/client/types.d.ts +10 -0
  25. package/dist/client/types.d.ts.map +1 -1
  26. package/dist/client/workflow-client.d.ts.map +1 -1
  27. package/dist/client/workflow-client.js +15 -0
  28. package/dist/client/workflow-client.js.map +1 -1
  29. package/dist/common/attachment.d.ts +31 -2
  30. package/dist/common/attachment.d.ts.map +1 -1
  31. package/dist/common/attachment.js +53 -0
  32. package/dist/common/attachment.js.map +1 -1
  33. package/dist/common/principal.d.ts +45 -0
  34. package/dist/common/principal.d.ts.map +1 -0
  35. package/dist/common/principal.js +9 -0
  36. package/dist/common/principal.js.map +1 -0
  37. package/dist/engine/config-validator.d.ts.map +1 -1
  38. package/dist/engine/config-validator.js +0 -1
  39. package/dist/engine/config-validator.js.map +1 -1
  40. package/dist/engine/workflow-executor.d.ts +8 -0
  41. package/dist/engine/workflow-executor.d.ts.map +1 -1
  42. package/dist/engine/workflow-executor.js +16 -4
  43. package/dist/engine/workflow-executor.js.map +1 -1
  44. package/dist/index.d.ts +3 -0
  45. package/dist/index.d.ts.map +1 -1
  46. package/dist/index.js +4 -0
  47. package/dist/index.js.map +1 -1
  48. package/dist/router/workflow-router.d.ts.map +1 -1
  49. package/dist/router/workflow-router.js +62 -0
  50. package/dist/router/workflow-router.js.map +1 -1
  51. package/dist/runtime/types.d.ts +7 -0
  52. package/dist/runtime/types.d.ts.map +1 -1
  53. package/dist/runtime/workflow-runtime.d.ts +13 -0
  54. package/dist/runtime/workflow-runtime.d.ts.map +1 -1
  55. package/dist/runtime/workflow-runtime.js +10 -0
  56. package/dist/runtime/workflow-runtime.js.map +1 -1
  57. package/dist/sandbox/types.d.ts +85 -0
  58. package/dist/sandbox/types.d.ts.map +1 -0
  59. package/dist/sandbox/types.js +22 -0
  60. package/dist/sandbox/types.js.map +1 -0
  61. package/dist/steps/base-step.d.ts +14 -0
  62. package/dist/steps/base-step.d.ts.map +1 -1
  63. package/dist/steps/base-step.js.map +1 -1
  64. package/dist/steps/builtin/http-request.step.d.ts +2 -2
  65. package/dist/steps/builtin/transform.step.d.ts +247 -0
  66. package/dist/steps/builtin/transform.step.d.ts.map +1 -0
  67. package/dist/steps/builtin/transform.step.js +135 -0
  68. package/dist/steps/builtin/transform.step.js.map +1 -0
  69. package/dist/testing/mock-step-context.d.ts +4 -0
  70. package/dist/testing/mock-step-context.d.ts.map +1 -1
  71. package/dist/testing/mock-step-context.js +2 -0
  72. package/dist/testing/mock-step-context.js.map +1 -1
  73. package/dist/triggers/builtin/default-manual-trigger.d.ts.map +1 -1
  74. package/dist/triggers/builtin/default-manual-trigger.js +17 -4
  75. package/dist/triggers/builtin/default-manual-trigger.js.map +1 -1
  76. package/dist/types/attachment.d.ts +23 -0
  77. package/dist/types/attachment.d.ts.map +1 -0
  78. package/dist/types/attachment.js +2 -0
  79. package/dist/types/attachment.js.map +1 -0
  80. package/dist/types/resume-payload.d.ts +34 -0
  81. package/dist/types/resume-payload.d.ts.map +1 -0
  82. package/dist/types/resume-payload.js +12 -0
  83. package/dist/types/resume-payload.js.map +1 -0
  84. package/dist/util/executable-check.d.ts +42 -0
  85. package/dist/util/executable-check.d.ts.map +1 -0
  86. package/dist/util/executable-check.js +115 -0
  87. package/dist/util/executable-check.js.map +1 -0
  88. 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
- response: string;
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
- response: string;
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;AAC3D,OAAO,KAAK,EAAE,gBAAgB,EAAE,MAAM,mBAAmB,CAAC;AAC1D,OAAO,KAAK,EAAE,iBAAiB,EAAE,MAAM,oBAAoB,CAAC;AAQ5D,OAAO,KAAK,EACV,WAAW,EAKZ,MAAM,YAAY,CAAC;AAIpB,eAAO,MAAM,qBAAqB;;;;;;;;;;;;;;;;;;;;;EAOhC,CAAC;AAEH,MAAM,MAAM,eAAe,GAAG,CAAC,CAAC,KAAK,CAAC,OAAO,qBAAqB,CAAC,CAAC;AAiDpE,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;IAyB5E,OAAO,CACpB,MAAM,EAAE,eAAe,EACvB,GAAG,EAAE,oBAAoB,GACxB,OAAO,CAAC,WAAW,CAAC;IA2CvB;;;;;;;OAOG;YACW,oBAAoB;IAkDnB,QAAQ,CACrB,OAAO,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,EAChC,MAAM,EAAE,eAAe,EACvB,GAAG,EAAE,oBAAoB,GACxB,OAAO,CAAC,WAAW,CAAC;IAiDvB,OAAO,CAAC,YAAY;IA8BpB;;;;OAIG;IACH,OAAO,CAAC,eAAe;IAWvB,OAAO,CAAC,iBAAiB;IAazB,OAAO,CAAC,UAAU;YAmBJ,aAAa;IAoI3B,OAAO,CAAC,WAAW;CAkBpB"}
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
- return message.content
56
- .filter((c) => c.type === 'text')
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
- // 2. Convert WorkflowTools pi-mono AgentTools
111
- const piTools = this.convertTools(workflowTools, config.toolTimeout, ctx);
112
- // 3. Clamp maxTurns if runtime doesn't support multi-turn
113
- const effectiveMaxTurns = this.runtime.capabilities.multiTurn
114
- ? config.maxTurns
115
- : 1;
116
- // 4. Build interceptor chain
117
- const interceptors = this.buildInterceptors(config, ctx);
118
- // 5. Map interceptors → AgentHooks
119
- const hooks = this.buildHooks(interceptors, ctx);
120
- // 6. Execute runtime
121
- const events = this.runtime.execute(config, {
122
- tools: this.runtime.capabilities.toolUse ? piTools : [],
123
- hooks: this.runtime.capabilities.hooks ? hooks : {},
124
- signal: ctx.runtime.metadata['signal'] ?? new AbortController().signal,
125
- }, ctx);
126
- // 7. Consume event stream
127
- return this.consumeEvents(events, ctx, effectiveMaxTurns);
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 input = {
152
- tools: this.runtime.capabilities.toolUse ? piTools : [],
153
- hooks: this.runtime.capabilities.hooks ? hooks : {},
154
- signal: ctx.runtime.metadata['signal'] ?? new AbortController().signal,
155
- resume: {
156
- messages: priorMessages,
157
- payload: { action: 'approve', feedback: config.userMessage },
158
- pauseState: {
159
- type: 'awaiting_feedback',
160
- reason: 'Continuing prior conversation',
161
- prompt: config.userMessage,
162
- state: {
163
- messages: priorMessages,
164
- currentTurn,
165
- completedToolCalls,
166
- usage,
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
- const events = this.runtime.execute(config, input, ctx);
172
- return this.consumeEvents(events, ctx, effectiveMaxTurns, {
173
- messages: priorMessages,
174
- currentTurn,
175
- completedToolCalls,
176
- usage,
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 input = {
204
- tools: this.runtime.capabilities.toolUse ? piTools : [],
205
- hooks: this.runtime.capabilities.hooks ? hooks : {},
206
- signal: ctx.runtime.metadata['signal'] ?? new AbortController().signal,
207
- };
208
- if (pauseRequest) {
209
- input.resume = {
210
- messages: pauseState.messages,
211
- payload: resumePayload ?? { action: 'approve' },
212
- pauseState: pauseRequest,
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, ctx) {
332
+ buildHooks(interceptors, _ctx) {
262
333
  return {
263
334
  async beforeToolCall(tool, args) {
264
335
  return runInterceptorChain(interceptors, tool, args);
265
336
  },
266
- async afterToolCall(tool, args, result) {
267
- ctx.emit({
268
- type: 'tool_call',
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
- const updateMsg = event.message;
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