@desplega.ai/agent-swarm 1.86.0 → 1.87.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/openapi.json +72 -1
- package/package.json +3 -1
- package/src/be/db-queries/tracker.ts +21 -0
- package/src/be/db.ts +235 -14
- package/src/be/migrations/079_task_followup_config.sql +1 -0
- package/src/be/modelsdev-cache.json +77663 -74073
- package/src/cli.tsx +26 -0
- package/src/commands/context-preamble.ts +272 -0
- package/src/commands/e2b.ts +728 -0
- package/src/commands/resume-session.ts +35 -78
- package/src/commands/runner.ts +125 -13
- package/src/e2b/dispatch.ts +429 -0
- package/src/e2b/env.ts +206 -0
- package/src/heartbeat/heartbeat.ts +145 -30
- package/src/heartbeat/templates.ts +11 -7
- package/src/http/session-data.ts +8 -1
- package/src/http/tasks.ts +152 -3
- package/src/jira/sync.ts +4 -4
- package/src/linear/sync.ts +6 -5
- package/src/providers/claude-adapter.ts +10 -76
- package/src/providers/claude-managed-adapter.ts +61 -75
- package/src/providers/codex-adapter.ts +15 -18
- package/src/providers/codex-oauth/auth-json.ts +18 -1
- package/src/providers/codex-oauth/flow.ts +24 -1
- package/src/providers/types.ts +6 -0
- package/src/tasks/worker-follow-up.ts +162 -2
- package/src/telemetry.ts +11 -1
- package/src/tests/claude-adapter.test.ts +5 -27
- package/src/tests/claude-managed-adapter.test.ts +38 -52
- package/src/tests/codex-adapter.test.ts +6 -31
- package/src/tests/codex-oauth.test.ts +149 -3
- package/src/tests/codex-pool.test.ts +14 -3
- package/src/tests/e2b-dispatch.test.ts +330 -0
- package/src/tests/heartbeat-supersede-resume.test.ts +285 -0
- package/src/tests/heartbeat.test.ts +26 -16
- package/src/tests/prompt-template-remaining.test.ts +4 -0
- package/src/tests/resume-session.test.ts +42 -50
- package/src/tests/structured-output.test.ts +69 -0
- package/src/tests/task-completion-idempotency.test.ts +185 -2
- package/src/tests/task-supersede-resume.test.ts +722 -0
- package/src/tests/telemetry-init.test.ts +69 -0
- package/src/tests/vcs-tracking.test.ts +39 -0
- package/src/tools/send-task.ts +12 -1
- package/src/tools/store-progress.ts +2 -2
- package/src/tools/templates.ts +14 -2
- package/src/types.ts +46 -1
- package/src/workflows/executors/agent-task.ts +3 -0
|
@@ -17,6 +17,7 @@ describe("initTelemetry", () => {
|
|
|
17
17
|
// Tests below set MCP_BASE_URL to assert classification — clear between
|
|
18
18
|
// tests so cases that expect "unset" don't inherit a prior test's value.
|
|
19
19
|
delete process.env.MCP_BASE_URL;
|
|
20
|
+
delete process.env.DESPLEGA_TELEMETRY_ENV;
|
|
20
21
|
});
|
|
21
22
|
|
|
22
23
|
test("without generateIfMissing + missing config → installationId stays null (track no-ops)", async () => {
|
|
@@ -390,4 +391,72 @@ describe("initTelemetry", () => {
|
|
|
390
391
|
expect(properties.is_cloud).toBe(true);
|
|
391
392
|
});
|
|
392
393
|
});
|
|
394
|
+
|
|
395
|
+
describe("track() metadata.environment", () => {
|
|
396
|
+
const originalFetch = globalThis.fetch;
|
|
397
|
+
const originalNodeEnv = process.env.NODE_ENV;
|
|
398
|
+
let captured: Record<string, unknown> | null = null;
|
|
399
|
+
|
|
400
|
+
beforeEach(() => {
|
|
401
|
+
captured = null;
|
|
402
|
+
globalThis.fetch = (async (_url: string, init?: { body?: string }) => {
|
|
403
|
+
captured = init?.body ? JSON.parse(init.body) : null;
|
|
404
|
+
return new Response(null, { status: 204 });
|
|
405
|
+
}) as typeof fetch;
|
|
406
|
+
delete process.env.DESPLEGA_TELEMETRY_ENV;
|
|
407
|
+
});
|
|
408
|
+
|
|
409
|
+
afterEach(() => {
|
|
410
|
+
globalThis.fetch = originalFetch;
|
|
411
|
+
delete process.env.DESPLEGA_TELEMETRY_ENV;
|
|
412
|
+
if (originalNodeEnv === undefined) delete process.env.NODE_ENV;
|
|
413
|
+
else process.env.NODE_ENV = originalNodeEnv;
|
|
414
|
+
});
|
|
415
|
+
|
|
416
|
+
test("defaults to production even when NODE_ENV is development", async () => {
|
|
417
|
+
process.env.NODE_ENV = "development";
|
|
418
|
+
await initTelemetry(
|
|
419
|
+
"api-server",
|
|
420
|
+
async () => "install_default_env",
|
|
421
|
+
async () => {},
|
|
422
|
+
);
|
|
423
|
+
|
|
424
|
+
track({ event: "test.event", properties: {} });
|
|
425
|
+
await new Promise((r) => setTimeout(r, 0));
|
|
426
|
+
|
|
427
|
+
const metadata = (captured as { metadata: Record<string, unknown> }).metadata;
|
|
428
|
+
expect(metadata.environment).toBe("production");
|
|
429
|
+
});
|
|
430
|
+
|
|
431
|
+
test("uses DESPLEGA_TELEMETRY_ENV when set", async () => {
|
|
432
|
+
process.env.NODE_ENV = "production";
|
|
433
|
+
process.env.DESPLEGA_TELEMETRY_ENV = "development";
|
|
434
|
+
await initTelemetry(
|
|
435
|
+
"api-server",
|
|
436
|
+
async () => "install_explicit_env",
|
|
437
|
+
async () => {},
|
|
438
|
+
);
|
|
439
|
+
|
|
440
|
+
track({ event: "test.event", properties: {} });
|
|
441
|
+
await new Promise((r) => setTimeout(r, 0));
|
|
442
|
+
|
|
443
|
+
const metadata = (captured as { metadata: Record<string, unknown> }).metadata;
|
|
444
|
+
expect(metadata.environment).toBe("development");
|
|
445
|
+
});
|
|
446
|
+
|
|
447
|
+
test("preserves NODE_ENV=test when telemetry env is unset", async () => {
|
|
448
|
+
process.env.NODE_ENV = "test";
|
|
449
|
+
await initTelemetry(
|
|
450
|
+
"api-server",
|
|
451
|
+
async () => "install_test_env",
|
|
452
|
+
async () => {},
|
|
453
|
+
);
|
|
454
|
+
|
|
455
|
+
track({ event: "test.event", properties: {} });
|
|
456
|
+
await new Promise((r) => setTimeout(r, 0));
|
|
457
|
+
|
|
458
|
+
const metadata = (captured as { metadata: Record<string, unknown> }).metadata;
|
|
459
|
+
expect(metadata.environment).toBe("test");
|
|
460
|
+
});
|
|
461
|
+
});
|
|
393
462
|
});
|
|
@@ -1,11 +1,16 @@
|
|
|
1
1
|
import { afterAll, beforeAll, describe, expect, test } from "bun:test";
|
|
2
2
|
import { unlink } from "node:fs/promises";
|
|
3
3
|
import {
|
|
4
|
+
cancelTask,
|
|
4
5
|
closeDb,
|
|
6
|
+
completeTask,
|
|
5
7
|
createAgent,
|
|
6
8
|
createTaskExtended,
|
|
9
|
+
failTask,
|
|
7
10
|
findTaskByVcs,
|
|
8
11
|
initDb,
|
|
12
|
+
startTask,
|
|
13
|
+
supersedeTask,
|
|
9
14
|
updateTaskVcs,
|
|
10
15
|
} from "../be/db";
|
|
11
16
|
|
|
@@ -133,6 +138,40 @@ describe("updateTaskVcs", () => {
|
|
|
133
138
|
expect(found!.id).toBe(task.id);
|
|
134
139
|
});
|
|
135
140
|
|
|
141
|
+
test("findTaskByVcs excludes ALL terminal statuses (completed, failed, cancelled, superseded)", () => {
|
|
142
|
+
// PR #594 review: missing `cancelled` / `superseded` in the filter
|
|
143
|
+
// meant webhooks for a terminated PR/MR still routed to the dead task.
|
|
144
|
+
// Guard against any one of the four terminal statuses being missed.
|
|
145
|
+
const TERMINAL_CASES = [
|
|
146
|
+
{ name: "completed", number: 200, terminate: (id: string) => completeTask(id, "done") },
|
|
147
|
+
{ name: "failed", number: 201, terminate: (id: string) => failTask(id, "boom") },
|
|
148
|
+
{ name: "cancelled", number: 202, terminate: (id: string) => cancelTask(id) },
|
|
149
|
+
{
|
|
150
|
+
name: "superseded",
|
|
151
|
+
number: 203,
|
|
152
|
+
terminate: (id: string) =>
|
|
153
|
+
supersedeTask(id, { reason: "manual_supersede", resumeTaskId: null }),
|
|
154
|
+
},
|
|
155
|
+
];
|
|
156
|
+
for (const c of TERMINAL_CASES) {
|
|
157
|
+
const task = createTaskExtended(`Terminal=${c.name}`, {
|
|
158
|
+
agentId: "vcs-track-agent-001",
|
|
159
|
+
source: "api",
|
|
160
|
+
});
|
|
161
|
+
updateTaskVcs(task.id, {
|
|
162
|
+
vcsProvider: "github",
|
|
163
|
+
vcsRepo: "owner/terminal",
|
|
164
|
+
vcsNumber: c.number,
|
|
165
|
+
vcsUrl: `https://github.com/owner/terminal/pull/${c.number}`,
|
|
166
|
+
});
|
|
167
|
+
startTask(task.id);
|
|
168
|
+
c.terminate(task.id);
|
|
169
|
+
|
|
170
|
+
const found = findTaskByVcs("owner/terminal", c.number);
|
|
171
|
+
expect(found).toBeNull();
|
|
172
|
+
}
|
|
173
|
+
});
|
|
174
|
+
|
|
136
175
|
test("idempotent: calling twice with same data both succeed", () => {
|
|
137
176
|
const task = createTaskExtended("Test idempotency", {
|
|
138
177
|
agentId: "vcs-track-agent-001",
|
package/src/tools/send-task.ts
CHANGED
|
@@ -14,7 +14,7 @@ import {
|
|
|
14
14
|
import { findDuplicateTask } from "@/tools/task-dedup";
|
|
15
15
|
import { ownerCtx, type ToolCtx } from "@/tools/task-tool-ctx";
|
|
16
16
|
import { createToolRegistrar } from "@/tools/utils";
|
|
17
|
-
import { AgentTaskSchema } from "@/types";
|
|
17
|
+
import { AgentTaskSchema, FollowUpConfigSchema } from "@/types";
|
|
18
18
|
|
|
19
19
|
export const sendTaskInputSchema = z.object({
|
|
20
20
|
agentId: z
|
|
@@ -83,6 +83,9 @@ export const sendTaskInputSchema = z.object({
|
|
|
83
83
|
.describe(
|
|
84
84
|
"ID of the human user who originally requested this task chain. When omitted, inherited from the caller's current task so the attribution flows through multi-hop delegation automatically.",
|
|
85
85
|
),
|
|
86
|
+
followUpConfig: FollowUpConfigSchema.optional().describe(
|
|
87
|
+
"Control the lead follow-up created when this task finishes. When to use `followUpConfig`: set `disabled: true` when you'll wait for this task to complete inline and no follow-up is needed; set `onCompleted` / `onFailed` with specific instructions when you need to follow up effectively on a particular outcome of a long-running flow; for normal one-shot tasks, leave it unset because defaults are fine. It is most valuable for long-running / complex flows.",
|
|
88
|
+
),
|
|
86
89
|
});
|
|
87
90
|
|
|
88
91
|
export const sendTaskOutputSchema = z.object({
|
|
@@ -113,6 +116,7 @@ export async function sendTaskHandler(
|
|
|
113
116
|
slackThreadTs,
|
|
114
117
|
slackUserId,
|
|
115
118
|
requestedByUserId: inputRequestedByUserId,
|
|
119
|
+
followUpConfig,
|
|
116
120
|
}: SendTaskArgs,
|
|
117
121
|
): Promise<CallToolResult> {
|
|
118
122
|
if (ctx.kind === "owner" && !ctx.agentId) {
|
|
@@ -200,6 +204,10 @@ export async function sendTaskHandler(
|
|
|
200
204
|
// interrupted — re-dispatch is the correct response, not a deduped no-op.
|
|
201
205
|
// Without this bypass, a cancelled worker permanently jams the thread
|
|
202
206
|
// against re-delegation when an earlier completed sibling exists.
|
|
207
|
+
//
|
|
208
|
+
// NOTE: `taskType === "resume"` (created by createResumeFollowUp on
|
|
209
|
+
// supersede) is intentionally NOT in this guard — a resume IS the legitimate
|
|
210
|
+
// re-dispatch and bypassing the check is correct. Do not add "resume" here.
|
|
203
211
|
if (sourceTaskId) {
|
|
204
212
|
const sourceTask = getTaskById(sourceTaskId);
|
|
205
213
|
if (
|
|
@@ -259,6 +267,7 @@ export async function sendTaskHandler(
|
|
|
259
267
|
slackChannelId,
|
|
260
268
|
slackThreadTs,
|
|
261
269
|
slackUserId,
|
|
270
|
+
followUpConfig,
|
|
262
271
|
});
|
|
263
272
|
|
|
264
273
|
return {
|
|
@@ -311,6 +320,7 @@ export async function sendTaskHandler(
|
|
|
311
320
|
slackChannelId,
|
|
312
321
|
slackThreadTs,
|
|
313
322
|
slackUserId,
|
|
323
|
+
followUpConfig,
|
|
314
324
|
});
|
|
315
325
|
|
|
316
326
|
return {
|
|
@@ -337,6 +347,7 @@ export async function sendTaskHandler(
|
|
|
337
347
|
slackChannelId,
|
|
338
348
|
slackThreadTs,
|
|
339
349
|
slackUserId,
|
|
350
|
+
followUpConfig,
|
|
340
351
|
});
|
|
341
352
|
|
|
342
353
|
return {
|
|
@@ -18,7 +18,7 @@ import { getRetrievalsForTask } from "@/be/memory/raters/retrieval";
|
|
|
18
18
|
import { runServerRaters } from "@/be/memory/raters/run-server-raters";
|
|
19
19
|
import { createWorkerTaskFollowUp } from "@/tasks/worker-follow-up";
|
|
20
20
|
import { createToolRegistrar } from "@/tools/utils";
|
|
21
|
-
import { AgentTaskSchema, AttachmentInputSchema } from "@/types";
|
|
21
|
+
import { AgentTaskSchema, AttachmentInputSchema, isTerminalTaskStatus } from "@/types";
|
|
22
22
|
import { validateJsonSchema } from "@/workflows/json-schema-validator";
|
|
23
23
|
|
|
24
24
|
// Phase 11: the `cost` / `costData` field was removed from this tool's input
|
|
@@ -115,7 +115,7 @@ export const registerStoreProgressTool = (server: McpServer) => {
|
|
|
115
115
|
}
|
|
116
116
|
|
|
117
117
|
let updatedTask = existingTask;
|
|
118
|
-
const isTerminal =
|
|
118
|
+
const isTerminal = isTerminalTaskStatus(existingTask.status);
|
|
119
119
|
|
|
120
120
|
// Attachments — pointer-based, append-only. Insert each row inside
|
|
121
121
|
// this transaction; the helper dedups by sha256 (when present) or by
|
package/src/tools/templates.ts
CHANGED
|
@@ -52,10 +52,11 @@ registerTemplate({
|
|
|
52
52
|
defaultBody: `Worker task completed \u2014 review needed.
|
|
53
53
|
|
|
54
54
|
Agent: {{agent_name}}
|
|
55
|
+
Original task created by agent {{creator_agent}}
|
|
55
56
|
Task: "{{task_desc}}"
|
|
56
57
|
|
|
57
58
|
Output:
|
|
58
|
-
{{output_summary}}
|
|
59
|
+
{{output_summary}}{{follow_up_instructions}}
|
|
59
60
|
|
|
60
61
|
IMPORTANT: Do NOT re-delegate or re-answer the original request. The worker has already handled it. Your job is ONLY to:
|
|
61
62
|
1. Review the output above
|
|
@@ -65,8 +66,13 @@ IMPORTANT: Do NOT re-delegate or re-answer the original request. The worker has
|
|
|
65
66
|
Use \`get-task-details\` with taskId "{{task_id}}" for full details.`,
|
|
66
67
|
variables: [
|
|
67
68
|
{ name: "agent_name", description: "Worker agent name or ID prefix" },
|
|
69
|
+
{ name: "creator_agent", description: "Agent ID that originally created the worker task" },
|
|
68
70
|
{ name: "task_desc", description: "Task description (truncated to 200 chars)" },
|
|
69
71
|
{ name: "output_summary", description: "Task output (truncated to 500 chars)" },
|
|
72
|
+
{
|
|
73
|
+
name: "follow_up_instructions",
|
|
74
|
+
description: "Optional per-task instructions from followUpConfig for this completion",
|
|
75
|
+
},
|
|
70
76
|
{ name: "task_id", description: "Original task ID" },
|
|
71
77
|
],
|
|
72
78
|
category: "task_lifecycle",
|
|
@@ -106,15 +112,21 @@ registerTemplate({
|
|
|
106
112
|
defaultBody: `Worker task failed \u2014 action needed.
|
|
107
113
|
|
|
108
114
|
Agent: {{agent_name}}
|
|
115
|
+
Original task created by agent {{creator_agent}}
|
|
109
116
|
Task: "{{task_desc}}"
|
|
110
117
|
|
|
111
|
-
Failure reason: {{failure_reason}}
|
|
118
|
+
Failure reason: {{failure_reason}}{{follow_up_instructions}}
|
|
112
119
|
|
|
113
120
|
Decide whether to reassign, retry, or handle the failure. Use \`get-task-details\` with taskId "{{task_id}}" for full details.`,
|
|
114
121
|
variables: [
|
|
115
122
|
{ name: "agent_name", description: "Worker agent name or ID prefix" },
|
|
123
|
+
{ name: "creator_agent", description: "Agent ID that originally created the worker task" },
|
|
116
124
|
{ name: "task_desc", description: "Task description (truncated to 200 chars)" },
|
|
117
125
|
{ name: "failure_reason", description: "Failure reason text" },
|
|
126
|
+
{
|
|
127
|
+
name: "follow_up_instructions",
|
|
128
|
+
description: "Optional per-task instructions from followUpConfig for this failure",
|
|
129
|
+
},
|
|
118
130
|
{ name: "task_id", description: "Original task ID" },
|
|
119
131
|
],
|
|
120
132
|
category: "task_lifecycle",
|
package/src/types.ts
CHANGED
|
@@ -8,12 +8,36 @@ export const AgentTaskStatusSchema = z.enum([
|
|
|
8
8
|
"reviewing", // Agent is reviewing an offered task
|
|
9
9
|
"pending", // Assigned/accepted, waiting to start
|
|
10
10
|
"in_progress",
|
|
11
|
-
"paused", // Interrupted by graceful shutdown, can resume
|
|
11
|
+
"paused", // Interrupted by graceful shutdown (legacy), can resume
|
|
12
12
|
"completed",
|
|
13
13
|
"failed",
|
|
14
14
|
"cancelled", // Task was cancelled by lead or creator
|
|
15
|
+
"superseded", // Original terminated, replaced by a follow-up "resume" task
|
|
15
16
|
]);
|
|
16
17
|
|
|
18
|
+
/**
|
|
19
|
+
* Terminal task statuses — a task in one of these is done. No further state
|
|
20
|
+
* transitions, no re-assignment, no follow-up creation on the same id.
|
|
21
|
+
*
|
|
22
|
+
* Single source of truth for JS-side checks (sync handlers, store-progress,
|
|
23
|
+
* db mutator guards, HTTP cancel guard).
|
|
24
|
+
*
|
|
25
|
+
* **SQL drift watch**: `src/be/db.ts` has ~8 prepared statements that inline
|
|
26
|
+
* these strings — SQL can't import a TS const. When adding a new terminal
|
|
27
|
+
* status, grep across `src/be/db.ts` for:
|
|
28
|
+
* - `status NOT IN ('completed'` — non-terminal filters (findTaskByVcs,
|
|
29
|
+
* findRecentSimilarTasks, mutator guards, hasNonTerminalChildTask)
|
|
30
|
+
* - `status IN ('completed', 'failed'` — intent-terminal lookups
|
|
31
|
+
* - `status = CASE WHEN status IN ('completed'` — setProgress guard
|
|
32
|
+
* and update every site.
|
|
33
|
+
*/
|
|
34
|
+
export const TERMINAL_TASK_STATUSES = ["completed", "failed", "cancelled", "superseded"] as const;
|
|
35
|
+
export type TerminalTaskStatus = (typeof TERMINAL_TASK_STATUSES)[number];
|
|
36
|
+
|
|
37
|
+
export function isTerminalTaskStatus(status: string): status is TerminalTaskStatus {
|
|
38
|
+
return (TERMINAL_TASK_STATUSES as readonly string[]).includes(status);
|
|
39
|
+
}
|
|
40
|
+
|
|
17
41
|
// ============================================================================
|
|
18
42
|
// Lead Inbox Types
|
|
19
43
|
// ============================================================================
|
|
@@ -103,6 +127,13 @@ export type ProviderMetaMap = {
|
|
|
103
127
|
opencode: NoProviderMeta;
|
|
104
128
|
};
|
|
105
129
|
|
|
130
|
+
export const FollowUpConfigSchema = z.object({
|
|
131
|
+
disabled: z.boolean().optional(),
|
|
132
|
+
onCompleted: z.string().max(4000).optional(),
|
|
133
|
+
onFailed: z.string().max(4000).optional(),
|
|
134
|
+
});
|
|
135
|
+
export type FollowUpConfig = z.infer<typeof FollowUpConfigSchema>;
|
|
136
|
+
|
|
106
137
|
export const AgentTaskSchema = z.object({
|
|
107
138
|
id: z.uuid(),
|
|
108
139
|
agentId: z.uuid().nullable(), // Nullable for unassigned tasks
|
|
@@ -186,6 +217,9 @@ export const AgentTaskSchema = z.object({
|
|
|
186
217
|
// Structured output schema (optional — JSON Schema that task output must conform to)
|
|
187
218
|
outputSchema: z.record(z.string(), z.unknown()).optional(),
|
|
188
219
|
|
|
220
|
+
// Lead follow-up control (optional — null/undefined preserves default behavior)
|
|
221
|
+
followUpConfig: FollowUpConfigSchema.optional(),
|
|
222
|
+
|
|
189
223
|
// Pause tracking
|
|
190
224
|
wasPaused: z.boolean().default(false),
|
|
191
225
|
|
|
@@ -660,7 +694,18 @@ export const AgentLogEventTypeSchema = z.enum([
|
|
|
660
694
|
"budget.deleted",
|
|
661
695
|
"pricing.inserted",
|
|
662
696
|
"pricing.deleted",
|
|
697
|
+
// Graceful pause/resume via follow-up
|
|
698
|
+
"task_superseded",
|
|
699
|
+
]);
|
|
700
|
+
|
|
701
|
+
// Reasons a task can be superseded (terminal) and replaced by a "resume" follow-up.
|
|
702
|
+
export const ResumeReasonSchema = z.enum([
|
|
703
|
+
"graceful_shutdown", // Worker received SIGTERM / SIGINT
|
|
704
|
+
"context_limits", // Provider session approaching context-window limits (Phase 6)
|
|
705
|
+
"manual_supersede", // Operator-triggered (e.g. dashboard button)
|
|
706
|
+
"crash_recovery", // Heartbeat sweep detected dead/stalled worker (DES-523)
|
|
663
707
|
]);
|
|
708
|
+
export type ResumeReason = z.infer<typeof ResumeReasonSchema>;
|
|
664
709
|
|
|
665
710
|
export const AgentLogSchema = z.object({
|
|
666
711
|
id: z.uuid(),
|
|
@@ -2,6 +2,7 @@ import { z } from "zod";
|
|
|
2
2
|
import { workflowContextKey } from "../../tasks/context-key";
|
|
3
3
|
import { withSiblingAwareness } from "../../tasks/sibling-awareness";
|
|
4
4
|
import type { ExecutorMeta } from "../../types";
|
|
5
|
+
import { FollowUpConfigSchema } from "../../types";
|
|
5
6
|
import type { ExecutorResult } from "./base";
|
|
6
7
|
import { BaseExecutor } from "./base";
|
|
7
8
|
|
|
@@ -18,6 +19,7 @@ const AgentTaskConfigSchema = z.object({
|
|
|
18
19
|
model: z.string().min(1).optional(),
|
|
19
20
|
parentTaskId: z.string().uuid().optional(),
|
|
20
21
|
outputSchema: z.record(z.string(), z.unknown()).optional(),
|
|
22
|
+
followUpConfig: FollowUpConfigSchema.optional(),
|
|
21
23
|
});
|
|
22
24
|
|
|
23
25
|
const AgentTaskOutputSchema = z.object({
|
|
@@ -94,6 +96,7 @@ export class AgentTaskExecutor extends BaseExecutor<
|
|
|
94
96
|
model: config.model,
|
|
95
97
|
parentTaskId: config.parentTaskId,
|
|
96
98
|
outputSchema: config.outputSchema,
|
|
99
|
+
followUpConfig: config.followUpConfig,
|
|
97
100
|
contextKey: workflowContextKey({ workflowRunId: meta.runId }),
|
|
98
101
|
},
|
|
99
102
|
);
|