@zhivex-ai/core 0.7.0 → 0.8.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/README.md +5 -0
- package/dist/advanced-tool-registry.d.ts +112 -0
- package/dist/advanced-tool-registry.d.ts.map +1 -0
- package/dist/advanced-tool-registry.js +407 -0
- package/dist/advanced-tool-registry.js.map +1 -0
- package/dist/agent-evaluation.d.ts +176 -0
- package/dist/agent-evaluation.d.ts.map +1 -0
- package/dist/agent-evaluation.js +334 -0
- package/dist/agent-evaluation.js.map +1 -0
- package/dist/agent-store.d.ts.map +1 -1
- package/dist/agent-store.js +226 -8
- package/dist/agent-store.js.map +1 -1
- package/dist/agent-trace.d.ts +127 -0
- package/dist/agent-trace.d.ts.map +1 -0
- package/dist/agent-trace.js +331 -0
- package/dist/agent-trace.js.map +1 -0
- package/dist/agent.d.ts +12 -1
- package/dist/agent.d.ts.map +1 -1
- package/dist/agent.js +469 -36
- package/dist/agent.js.map +1 -1
- package/dist/api-stability.d.ts +9 -0
- package/dist/api-stability.d.ts.map +1 -0
- package/dist/api-stability.js +261 -0
- package/dist/api-stability.js.map +1 -0
- package/dist/artifact.d.ts +165 -0
- package/dist/artifact.d.ts.map +1 -0
- package/dist/artifact.js +994 -0
- package/dist/artifact.js.map +1 -0
- package/dist/errors.d.ts +2 -0
- package/dist/errors.d.ts.map +1 -1
- package/dist/errors.js +2 -0
- package/dist/errors.js.map +1 -1
- package/dist/generate-text.d.ts.map +1 -1
- package/dist/generate-text.js +6 -2
- package/dist/generate-text.js.map +1 -1
- package/dist/index.d.ts +27 -1
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +14 -1
- package/dist/index.js.map +1 -1
- package/dist/live-agent.d.ts.map +1 -1
- package/dist/live-agent.js +7 -1
- package/dist/live-agent.js.map +1 -1
- package/dist/provider-parity.d.ts +63 -0
- package/dist/provider-parity.d.ts.map +1 -0
- package/dist/provider-parity.js +175 -0
- package/dist/provider-parity.js.map +1 -0
- package/dist/runner.d.ts +111 -0
- package/dist/runner.d.ts.map +1 -0
- package/dist/runner.js +635 -0
- package/dist/runner.js.map +1 -0
- package/dist/safety-policy.d.ts +65 -0
- package/dist/safety-policy.d.ts.map +1 -0
- package/dist/safety-policy.js +308 -0
- package/dist/safety-policy.js.map +1 -0
- package/dist/types.d.ts +119 -3
- package/dist/types.d.ts.map +1 -1
- package/dist/workflow-artifacts.d.ts +28 -0
- package/dist/workflow-artifacts.d.ts.map +1 -0
- package/dist/workflow-artifacts.js +86 -0
- package/dist/workflow-artifacts.js.map +1 -0
- package/dist/workflow-evaluation-diff.d.ts +51 -0
- package/dist/workflow-evaluation-diff.d.ts.map +1 -0
- package/dist/workflow-evaluation-diff.js +141 -0
- package/dist/workflow-evaluation-diff.js.map +1 -0
- package/dist/workflow-evaluation.d.ts +94 -0
- package/dist/workflow-evaluation.d.ts.map +1 -0
- package/dist/workflow-evaluation.js +210 -0
- package/dist/workflow-evaluation.js.map +1 -0
- package/dist/workflow-state-service.d.ts +67 -0
- package/dist/workflow-state-service.d.ts.map +1 -0
- package/dist/workflow-state-service.js +498 -0
- package/dist/workflow-state-service.js.map +1 -0
- package/dist/workflow.d.ts +206 -0
- package/dist/workflow.d.ts.map +1 -0
- package/dist/workflow.js +727 -0
- package/dist/workflow.js.map +1 -0
- package/package.json +1 -1
package/dist/agent.js
CHANGED
|
@@ -2,8 +2,19 @@ import { createAgentApprovalMessage, getAgentApprovalRequests } from "./agent-ap
|
|
|
2
2
|
import { createAgentHandoffMessage } from "./agent-handoff.js";
|
|
3
3
|
import { GuardrailTriggeredError, ValidationError } from "./errors.js";
|
|
4
4
|
import { generateText, normalizeMessages, streamText } from "./generate-text.js";
|
|
5
|
+
import { serializeJsonValue } from "./messages.js";
|
|
6
|
+
import { mergeAbortSignals } from "./runtime.js";
|
|
5
7
|
import { toToolSet } from "./tool-registry.js";
|
|
8
|
+
import { z } from "zod";
|
|
6
9
|
const randomId = (prefix) => `${prefix}_${Math.random().toString(36).slice(2, 10)}`;
|
|
10
|
+
const AGENT_RUN_STATE_SCHEMA_VERSION = 1;
|
|
11
|
+
const AGENT_GROUP_FAIL_FAST_ABORT_MESSAGE = "Agent group member aborted after fail-fast.";
|
|
12
|
+
class AgentPolicyTimeoutError extends Error {
|
|
13
|
+
constructor(timeoutMs) {
|
|
14
|
+
super(`Agent run timed out after ${timeoutMs}ms.`);
|
|
15
|
+
this.name = "AgentPolicyTimeoutError";
|
|
16
|
+
}
|
|
17
|
+
}
|
|
7
18
|
const joinInstructions = (...parts) => {
|
|
8
19
|
const content = parts.map((part) => part?.trim()).filter((part) => Boolean(part));
|
|
9
20
|
return content.length ? content.join("\n\n") : undefined;
|
|
@@ -64,13 +75,20 @@ const toOutput = (state) => ({
|
|
|
64
75
|
state,
|
|
65
76
|
error: state.error
|
|
66
77
|
});
|
|
67
|
-
const
|
|
68
|
-
|
|
78
|
+
const normalizeRunState = (state) => ({
|
|
79
|
+
...state,
|
|
80
|
+
schemaVersion: AGENT_RUN_STATE_SCHEMA_VERSION
|
|
81
|
+
});
|
|
82
|
+
const normalizeApprovalStatus = (status) => status === "suspended" ? "waiting_approval" : status;
|
|
83
|
+
const cloneState = (state) => JSON.parse(JSON.stringify(normalizeRunState(state)));
|
|
84
|
+
const createBaseState = (provider, modelId, initialMessages, maxSteps, metadata, agentId, runId, handoff, parentRunId, idempotencyKey) => {
|
|
69
85
|
const startedAt = Date.now();
|
|
70
86
|
return {
|
|
87
|
+
schemaVersion: AGENT_RUN_STATE_SCHEMA_VERSION,
|
|
71
88
|
runId,
|
|
89
|
+
idempotencyKey,
|
|
72
90
|
agentId,
|
|
73
|
-
parentRunId: handoff?.fromRunId,
|
|
91
|
+
parentRunId: parentRunId ?? handoff?.fromRunId,
|
|
74
92
|
provider,
|
|
75
93
|
modelId,
|
|
76
94
|
status: "running",
|
|
@@ -98,6 +116,17 @@ const ensureValidStateInput = (input) => {
|
|
|
98
116
|
throw new ValidationError('Pass either "state" or a fresh "prompt"/"messages" input, but not both.');
|
|
99
117
|
}
|
|
100
118
|
};
|
|
119
|
+
const ensureValidIdempotencyInput = (input, store) => {
|
|
120
|
+
if (!input.idempotencyKey) {
|
|
121
|
+
return;
|
|
122
|
+
}
|
|
123
|
+
if (!store) {
|
|
124
|
+
throw new ValidationError('The "idempotencyKey" option requires an agent run "store".');
|
|
125
|
+
}
|
|
126
|
+
if (!store.findByIdempotencyKey) {
|
|
127
|
+
throw new ValidationError('The agent run "store" must implement "findByIdempotencyKey()" to use "idempotencyKey".');
|
|
128
|
+
}
|
|
129
|
+
};
|
|
101
130
|
const injectContextMessages = (messages, extraMessages) => {
|
|
102
131
|
if (!extraMessages.length) {
|
|
103
132
|
return messages;
|
|
@@ -159,10 +188,10 @@ const finalizeState = (state, result, newSteps, newToolResults) => {
|
|
|
159
188
|
const unresolvedToolCalls = lastStep?.response ? hasToolCalls(lastStep.response.messages) : false;
|
|
160
189
|
const pendingApprovals = getAgentApprovalRequests(newSteps.flatMap((step) => step.response?.messages ?? []));
|
|
161
190
|
if (pendingApprovals.length) {
|
|
162
|
-
state.status = "
|
|
191
|
+
state.status = "waiting_approval";
|
|
163
192
|
state.error = undefined;
|
|
164
193
|
if (lastStep) {
|
|
165
|
-
lastStep.status = "
|
|
194
|
+
lastStep.status = "waiting_approval";
|
|
166
195
|
}
|
|
167
196
|
}
|
|
168
197
|
else if (exhausted && unresolvedToolCalls) {
|
|
@@ -194,6 +223,94 @@ const finalizeState = (state, result, newSteps, newToolResults) => {
|
|
|
194
223
|
const emitTelemetryEvent = async (agent, event) => {
|
|
195
224
|
await agent.onTelemetryEvent?.(event);
|
|
196
225
|
};
|
|
226
|
+
const subAgentToolInputSchema = z.object({
|
|
227
|
+
prompt: z.string().min(1),
|
|
228
|
+
system: z.string().optional()
|
|
229
|
+
});
|
|
230
|
+
const defaultSubAgentToolName = (agent) => {
|
|
231
|
+
const id = agent.id ?? `${agent.model.provider}_${agent.model.modelId}`;
|
|
232
|
+
return `subagent_${id.replace(/[^A-Za-z0-9_]+/g, "_").replace(/^_+|_+$/g, "") || "agent"}`;
|
|
233
|
+
};
|
|
234
|
+
const countToolCallsInSteps = (steps) => steps.reduce((total, step) => total + countToolCalls(step.response?.messages ?? []), 0);
|
|
235
|
+
const countToolErrors = (toolResults) => toolResults.filter((result) => result.isError).length;
|
|
236
|
+
export const createSubAgentTool = (options) => {
|
|
237
|
+
const toolName = options.toolName ?? options.name ?? defaultSubAgentToolName(options.agent);
|
|
238
|
+
const metadata = {
|
|
239
|
+
type: "subagent"
|
|
240
|
+
};
|
|
241
|
+
if (options.agent.id) {
|
|
242
|
+
metadata.childAgentId = options.agent.id;
|
|
243
|
+
}
|
|
244
|
+
if (options.parentRunId) {
|
|
245
|
+
metadata.parentRunId = options.parentRunId;
|
|
246
|
+
}
|
|
247
|
+
if (options.parentAgentId) {
|
|
248
|
+
metadata.parentAgentId = options.parentAgentId;
|
|
249
|
+
}
|
|
250
|
+
return {
|
|
251
|
+
name: toolName,
|
|
252
|
+
description: options.description ??
|
|
253
|
+
`Delegate the task to ${options.agent.id ? `subagent "${options.agent.id}"` : "a subagent"} and return its result.`,
|
|
254
|
+
schema: subAgentToolInputSchema,
|
|
255
|
+
requiresApproval: options.requiresApproval,
|
|
256
|
+
metadata: cloneMetadata(metadata, options.metadata),
|
|
257
|
+
execute: async (input) => {
|
|
258
|
+
await options.onStart?.({
|
|
259
|
+
toolName,
|
|
260
|
+
childAgentId: options.agent.id,
|
|
261
|
+
parentRunId: options.parentRunId
|
|
262
|
+
});
|
|
263
|
+
const childMetadata = {
|
|
264
|
+
subagentToolName: toolName
|
|
265
|
+
};
|
|
266
|
+
if (options.parentRunId) {
|
|
267
|
+
childMetadata.parentRunId = options.parentRunId;
|
|
268
|
+
}
|
|
269
|
+
if (options.parentAgentId) {
|
|
270
|
+
childMetadata.parentAgentId = options.parentAgentId;
|
|
271
|
+
}
|
|
272
|
+
const output = await runAgent(options.agent, {
|
|
273
|
+
prompt: input.prompt,
|
|
274
|
+
system: joinInstructions(options.system, input.system),
|
|
275
|
+
parentRunId: options.parentRunId,
|
|
276
|
+
maxSteps: options.maxSteps,
|
|
277
|
+
metadata: cloneMetadata(options.metadata, childMetadata)
|
|
278
|
+
});
|
|
279
|
+
const childRun = {
|
|
280
|
+
runId: output.state.runId,
|
|
281
|
+
status: output.status,
|
|
282
|
+
outputText: output.outputText,
|
|
283
|
+
steps: output.state.currentStep,
|
|
284
|
+
toolCalls: countToolCallsInSteps(output.steps),
|
|
285
|
+
toolErrors: countToolErrors(output.toolResults)
|
|
286
|
+
};
|
|
287
|
+
if (output.state.agentId) {
|
|
288
|
+
childRun.agentId = output.state.agentId;
|
|
289
|
+
}
|
|
290
|
+
if (options.parentRunId) {
|
|
291
|
+
childRun.parentRunId = options.parentRunId;
|
|
292
|
+
}
|
|
293
|
+
childRun.toolName = toolName;
|
|
294
|
+
if (output.usage) {
|
|
295
|
+
childRun.usage = output.usage;
|
|
296
|
+
}
|
|
297
|
+
if (output.state.startedAt !== undefined) {
|
|
298
|
+
childRun.startedAt = output.state.startedAt;
|
|
299
|
+
}
|
|
300
|
+
if (output.state.updatedAt !== undefined) {
|
|
301
|
+
childRun.updatedAt = output.state.updatedAt;
|
|
302
|
+
}
|
|
303
|
+
if (output.error) {
|
|
304
|
+
childRun.error = output.error;
|
|
305
|
+
}
|
|
306
|
+
if (output.state.metadata) {
|
|
307
|
+
childRun.metadata = output.state.metadata;
|
|
308
|
+
}
|
|
309
|
+
await options.onFinish?.(childRun);
|
|
310
|
+
return serializeJsonValue(childRun);
|
|
311
|
+
}
|
|
312
|
+
};
|
|
313
|
+
};
|
|
197
314
|
const persistState = async (agent, state) => {
|
|
198
315
|
state.updatedAt = Date.now();
|
|
199
316
|
await agent.store?.save(cloneState(state));
|
|
@@ -270,11 +387,27 @@ const runGuardrails = async (agent, state, stage, guardrails, requestFactory) =>
|
|
|
270
387
|
return undefined;
|
|
271
388
|
};
|
|
272
389
|
const resolveContext = async (agent, input) => {
|
|
273
|
-
|
|
390
|
+
ensureValidIdempotencyInput(input, agent.store);
|
|
391
|
+
let loadedState = input.state ? normalizeRunState(input.state) : undefined;
|
|
392
|
+
let loadedByIdempotencyKey = false;
|
|
393
|
+
if (!loadedState && input.idempotencyKey) {
|
|
394
|
+
loadedState = await agent.store?.findByIdempotencyKey?.(input.idempotencyKey);
|
|
395
|
+
if (loadedState) {
|
|
396
|
+
loadedState = normalizeRunState(loadedState);
|
|
397
|
+
loadedByIdempotencyKey = true;
|
|
398
|
+
}
|
|
399
|
+
}
|
|
274
400
|
if (!loadedState && input.runId && agent.store) {
|
|
275
401
|
loadedState = await agent.store.load(input.runId);
|
|
402
|
+
if (loadedState) {
|
|
403
|
+
loadedState = normalizeRunState(loadedState);
|
|
404
|
+
}
|
|
276
405
|
}
|
|
277
|
-
const normalizedInput = loadedState
|
|
406
|
+
const normalizedInput = loadedState && loadedByIdempotencyKey
|
|
407
|
+
? { ...input, prompt: undefined, messages: undefined, system: undefined, handoff: undefined, state: loadedState }
|
|
408
|
+
: loadedState
|
|
409
|
+
? { ...input, state: loadedState }
|
|
410
|
+
: input;
|
|
278
411
|
ensureValidStateInput(normalizedInput);
|
|
279
412
|
const metadata = cloneMetadata(agent.metadata, loadedState?.metadata, input.metadata, input.handoff?.metadata);
|
|
280
413
|
if (loadedState) {
|
|
@@ -283,7 +416,10 @@ const resolveContext = async (agent, input) => {
|
|
|
283
416
|
return {
|
|
284
417
|
state: {
|
|
285
418
|
...loadedState,
|
|
419
|
+
schemaVersion: AGENT_RUN_STATE_SCHEMA_VERSION,
|
|
420
|
+
idempotencyKey: loadedState.idempotencyKey ?? input.idempotencyKey,
|
|
286
421
|
agentId: loadedState.agentId ?? agent.id,
|
|
422
|
+
parentRunId: loadedState.parentRunId ?? input.parentRunId,
|
|
287
423
|
provider: agent.model.provider,
|
|
288
424
|
modelId: agent.model.modelId,
|
|
289
425
|
maxSteps,
|
|
@@ -301,32 +437,65 @@ const resolveContext = async (agent, input) => {
|
|
|
301
437
|
const maxSteps = Math.max(1, input.maxSteps ?? agent.maxSteps ?? 1);
|
|
302
438
|
const prepared = await prepareFreshMessages(agent, input, runId);
|
|
303
439
|
return {
|
|
304
|
-
state: createBaseState(agent.model.provider, agent.model.modelId, prepared.messages, maxSteps, metadata, agent.id, runId, input.handoff),
|
|
440
|
+
state: createBaseState(agent.model.provider, agent.model.modelId, prepared.messages, maxSteps, metadata, agent.id, runId, input.handoff, input.parentRunId, input.idempotencyKey),
|
|
305
441
|
messages: prepared.messages,
|
|
306
442
|
remainingSteps: maxSteps,
|
|
307
443
|
memoryMessages: prepared.memoryMessages
|
|
308
444
|
};
|
|
309
445
|
};
|
|
310
|
-
const createGenerateOptions = (agent, state, input, messages, maxSteps) =>
|
|
311
|
-
|
|
312
|
-
|
|
313
|
-
|
|
314
|
-
|
|
315
|
-
|
|
316
|
-
|
|
317
|
-
|
|
318
|
-
|
|
319
|
-
|
|
320
|
-
|
|
321
|
-
|
|
322
|
-
|
|
323
|
-
|
|
324
|
-
|
|
325
|
-
|
|
326
|
-
|
|
327
|
-
|
|
328
|
-
|
|
329
|
-
|
|
446
|
+
const createGenerateOptions = (agent, state, input, messages, maxSteps, abortSignal = input.abortSignal) => {
|
|
447
|
+
const tools = { ...(toToolSet(input.tools ?? agent.tools) ?? {}) };
|
|
448
|
+
for (const subagent of agent.subagents ?? []) {
|
|
449
|
+
const subagentTool = createSubAgentTool({
|
|
450
|
+
...subagent,
|
|
451
|
+
parentRunId: state.runId,
|
|
452
|
+
parentAgentId: state.agentId,
|
|
453
|
+
onStart: async ({ toolName, childAgentId }) => {
|
|
454
|
+
await emitTelemetryEvent(agent, {
|
|
455
|
+
type: "subagent-start",
|
|
456
|
+
runId: state.runId,
|
|
457
|
+
agentId: state.agentId,
|
|
458
|
+
childAgentId,
|
|
459
|
+
toolName
|
|
460
|
+
});
|
|
461
|
+
},
|
|
462
|
+
onFinish: async (childRun) => {
|
|
463
|
+
state.childRuns = [...(state.childRuns ?? []), childRun];
|
|
464
|
+
await emitTelemetryEvent(agent, {
|
|
465
|
+
type: "subagent-finish",
|
|
466
|
+
runId: state.runId,
|
|
467
|
+
agentId: state.agentId,
|
|
468
|
+
childRun
|
|
469
|
+
});
|
|
470
|
+
}
|
|
471
|
+
});
|
|
472
|
+
if (tools[subagentTool.name]) {
|
|
473
|
+
throw new ValidationError(`Subagent tool "${subagentTool.name}" conflicts with an existing tool.`);
|
|
474
|
+
}
|
|
475
|
+
tools[subagentTool.name] = subagentTool;
|
|
476
|
+
}
|
|
477
|
+
const finalTools = Object.keys(tools).length ? tools : undefined;
|
|
478
|
+
return {
|
|
479
|
+
model: agent.model,
|
|
480
|
+
messages,
|
|
481
|
+
tools: finalTools,
|
|
482
|
+
toolChoice: input.toolChoice,
|
|
483
|
+
toolExecution: input.toolExecution ?? agent.toolExecution,
|
|
484
|
+
toolApprovalPolicy: input.toolApprovalPolicy ?? agent.toolApprovalPolicy,
|
|
485
|
+
onToolApprovalDecision: async (event) => {
|
|
486
|
+
await emitToolApprovalTelemetry(agent, state, event);
|
|
487
|
+
},
|
|
488
|
+
maxSteps,
|
|
489
|
+
temperature: input.temperature ?? agent.temperature,
|
|
490
|
+
maxTokens: input.maxTokens ?? agent.maxTokens,
|
|
491
|
+
reasoning: input.reasoning ?? agent.reasoning,
|
|
492
|
+
providerOptions: input.providerOptions ?? agent.providerOptions,
|
|
493
|
+
abortSignal,
|
|
494
|
+
timeoutMs: input.timeoutMs,
|
|
495
|
+
maxRetries: input.maxRetries,
|
|
496
|
+
retryBackoffMs: input.retryBackoffMs
|
|
497
|
+
};
|
|
498
|
+
};
|
|
330
499
|
const emptyAsyncIterable = async function* () {
|
|
331
500
|
return;
|
|
332
501
|
};
|
|
@@ -338,6 +507,64 @@ const createFailedState = (state, message) => ({
|
|
|
338
507
|
},
|
|
339
508
|
updatedAt: Date.now()
|
|
340
509
|
});
|
|
510
|
+
const createTerminalState = (state, status, message) => ({
|
|
511
|
+
...state,
|
|
512
|
+
status,
|
|
513
|
+
error: {
|
|
514
|
+
message
|
|
515
|
+
},
|
|
516
|
+
cancellationReason: status === "cancel_requested" ? message : state.cancellationReason,
|
|
517
|
+
cancelledAt: status === "cancel_requested" ? Date.now() : state.cancelledAt,
|
|
518
|
+
updatedAt: Date.now()
|
|
519
|
+
});
|
|
520
|
+
const resolveRunPolicy = (agent, input) => {
|
|
521
|
+
const policy = {
|
|
522
|
+
...(agent.policy ?? {}),
|
|
523
|
+
...(input.policy ?? {})
|
|
524
|
+
};
|
|
525
|
+
return Object.keys(policy).length ? policy : undefined;
|
|
526
|
+
};
|
|
527
|
+
const withAgentPolicyTimeout = async (operation, timeout) => {
|
|
528
|
+
if (!timeout.timeoutPromise) {
|
|
529
|
+
return operation;
|
|
530
|
+
}
|
|
531
|
+
try {
|
|
532
|
+
return await Promise.race([operation, timeout.timeoutPromise]);
|
|
533
|
+
}
|
|
534
|
+
finally {
|
|
535
|
+
timeout.cleanup();
|
|
536
|
+
}
|
|
537
|
+
};
|
|
538
|
+
const createAgentAbortContext = (inputAbortSignal, policy) => {
|
|
539
|
+
if (!policy?.timeoutMs) {
|
|
540
|
+
return {
|
|
541
|
+
signal: inputAbortSignal,
|
|
542
|
+
timeoutPromise: undefined,
|
|
543
|
+
cleanup: () => undefined,
|
|
544
|
+
isTimedOut: () => false
|
|
545
|
+
};
|
|
546
|
+
}
|
|
547
|
+
const controller = new AbortController();
|
|
548
|
+
let timedOut = false;
|
|
549
|
+
let timeout;
|
|
550
|
+
const timeoutPromise = new Promise((_, reject) => {
|
|
551
|
+
timeout = setTimeout(() => {
|
|
552
|
+
timedOut = true;
|
|
553
|
+
controller.abort();
|
|
554
|
+
reject(new AgentPolicyTimeoutError(policy.timeoutMs));
|
|
555
|
+
}, policy.timeoutMs);
|
|
556
|
+
});
|
|
557
|
+
return {
|
|
558
|
+
signal: mergeAbortSignals(inputAbortSignal, controller.signal),
|
|
559
|
+
timeoutPromise,
|
|
560
|
+
cleanup: () => {
|
|
561
|
+
if (timeout) {
|
|
562
|
+
clearTimeout(timeout);
|
|
563
|
+
}
|
|
564
|
+
},
|
|
565
|
+
isTimedOut: () => timedOut
|
|
566
|
+
};
|
|
567
|
+
};
|
|
341
568
|
const emitRunStartTelemetry = async (agent, state, memoryMessages, approvals) => {
|
|
342
569
|
await emitTelemetryEvent(agent, {
|
|
343
570
|
type: "run-start",
|
|
@@ -385,13 +612,178 @@ export const createAgent = (definition) => ({
|
|
|
385
612
|
...definition,
|
|
386
613
|
metadata: cloneMetadata(definition.metadata)
|
|
387
614
|
});
|
|
615
|
+
export const prepareSubagentsForAgent = (agent, options = {}) => {
|
|
616
|
+
const store = options.store ?? agent.store;
|
|
617
|
+
const memory = options.memory ?? agent.memory;
|
|
618
|
+
const onTelemetryEvent = options.onTelemetryEvent ?? agent.onTelemetryEvent;
|
|
619
|
+
const toolApprovalPolicy = options.toolApprovalPolicy ?? agent.toolApprovalPolicy;
|
|
620
|
+
const toolExecution = options.toolExecution ?? agent.toolExecution;
|
|
621
|
+
const defaultMetadata = cloneMetadata(agent.metadata, options.metadata);
|
|
622
|
+
return {
|
|
623
|
+
...agent,
|
|
624
|
+
metadata: cloneMetadata(agent.metadata),
|
|
625
|
+
subagents: (agent.subagents ?? []).map((subagent) => ({
|
|
626
|
+
...subagent,
|
|
627
|
+
metadata: cloneMetadata(defaultMetadata, subagent.metadata),
|
|
628
|
+
agent: {
|
|
629
|
+
...subagent.agent,
|
|
630
|
+
store: subagent.agent.store ?? store,
|
|
631
|
+
memory: subagent.agent.memory ?? memory,
|
|
632
|
+
onTelemetryEvent: subagent.agent.onTelemetryEvent ?? onTelemetryEvent,
|
|
633
|
+
toolApprovalPolicy: subagent.agent.toolApprovalPolicy ?? toolApprovalPolicy,
|
|
634
|
+
toolExecution: subagent.agent.toolExecution ?? toolExecution,
|
|
635
|
+
metadata: cloneMetadata(defaultMetadata, subagent.agent.metadata)
|
|
636
|
+
}
|
|
637
|
+
}))
|
|
638
|
+
};
|
|
639
|
+
};
|
|
640
|
+
export const runAgentGroup = async (agents, input = {}) => {
|
|
641
|
+
const { stopOnError, runId: _runId, state: _state, approvals: _approvals, handoff: _handoff, ...sharedInput } = input;
|
|
642
|
+
const parentRunId = input.parentRunId;
|
|
643
|
+
const controllers = agents.map(() => new AbortController());
|
|
644
|
+
let failFastTriggered = false;
|
|
645
|
+
const isFailingOutput = (output) => output.status === "failed" || output.status === "timed_out";
|
|
646
|
+
const abortPending = (currentIndex) => {
|
|
647
|
+
if (!stopOnError || failFastTriggered) {
|
|
648
|
+
return;
|
|
649
|
+
}
|
|
650
|
+
failFastTriggered = true;
|
|
651
|
+
controllers.forEach((controller, index) => {
|
|
652
|
+
if (index !== currentIndex) {
|
|
653
|
+
controller.abort();
|
|
654
|
+
}
|
|
655
|
+
});
|
|
656
|
+
};
|
|
657
|
+
const runs = agents.map(async (member, index) => {
|
|
658
|
+
const runInput = {
|
|
659
|
+
...sharedInput,
|
|
660
|
+
...(member.input ?? {}),
|
|
661
|
+
parentRunId: member.input?.parentRunId ?? parentRunId,
|
|
662
|
+
abortSignal: mergeAbortSignals(input.abortSignal, member.input?.abortSignal, controllers[index].signal),
|
|
663
|
+
metadata: cloneMetadata(input.metadata, member.input?.metadata, {
|
|
664
|
+
...(member.name ? { agentGroupMember: member.name } : {})
|
|
665
|
+
})
|
|
666
|
+
};
|
|
667
|
+
try {
|
|
668
|
+
const output = await runAgent(member.agent, runInput);
|
|
669
|
+
if (isFailingOutput(output)) {
|
|
670
|
+
abortPending(index);
|
|
671
|
+
}
|
|
672
|
+
return output;
|
|
673
|
+
}
|
|
674
|
+
catch (error) {
|
|
675
|
+
abortPending(index);
|
|
676
|
+
throw error;
|
|
677
|
+
}
|
|
678
|
+
});
|
|
679
|
+
const settled = await Promise.allSettled(runs);
|
|
680
|
+
const outputs = settled.map((result, index) => {
|
|
681
|
+
const member = agents[index];
|
|
682
|
+
if (result.status === "fulfilled") {
|
|
683
|
+
return {
|
|
684
|
+
name: member.name,
|
|
685
|
+
agentId: result.value.state.agentId ?? member.agent.id,
|
|
686
|
+
status: "fulfilled",
|
|
687
|
+
output: result.value
|
|
688
|
+
};
|
|
689
|
+
}
|
|
690
|
+
return {
|
|
691
|
+
name: member.name,
|
|
692
|
+
agentId: member.agent.id,
|
|
693
|
+
status: "rejected",
|
|
694
|
+
error: {
|
|
695
|
+
message: stopOnError && failFastTriggered && controllers[index].signal.aborted
|
|
696
|
+
? AGENT_GROUP_FAIL_FAST_ABORT_MESSAGE
|
|
697
|
+
: result.reason instanceof Error
|
|
698
|
+
? result.reason.message
|
|
699
|
+
: String(result.reason)
|
|
700
|
+
}
|
|
701
|
+
};
|
|
702
|
+
});
|
|
703
|
+
const failed = outputs.some((output) => output.status === "rejected" || output.output?.status === "failed" || output.output?.status === "timed_out");
|
|
704
|
+
return {
|
|
705
|
+
status: stopOnError && failed ? "failed" : failed ? "failed" : "completed",
|
|
706
|
+
parentRunId,
|
|
707
|
+
outputs
|
|
708
|
+
};
|
|
709
|
+
};
|
|
710
|
+
export const cancelAgentRun = async (store, runId, options = {}) => {
|
|
711
|
+
const loadedState = await store.load(runId);
|
|
712
|
+
if (!loadedState) {
|
|
713
|
+
return undefined;
|
|
714
|
+
}
|
|
715
|
+
const cancelledAt = Date.now();
|
|
716
|
+
const status = options.mode === "final" ? "cancelled" : "cancel_requested";
|
|
717
|
+
const state = normalizeRunState({
|
|
718
|
+
...loadedState,
|
|
719
|
+
status,
|
|
720
|
+
cancelledAt,
|
|
721
|
+
cancellationReason: options.reason,
|
|
722
|
+
updatedAt: cancelledAt,
|
|
723
|
+
error: undefined
|
|
724
|
+
});
|
|
725
|
+
await store.save(cloneState(state));
|
|
726
|
+
return cloneState(state);
|
|
727
|
+
};
|
|
728
|
+
export const cancelAgentRunTree = async (store, runId, options = {}) => {
|
|
729
|
+
if (!store.findByParentRunId) {
|
|
730
|
+
throw new ValidationError('The agent run "store" must implement "findByParentRunId()" to cancel an agent run tree.');
|
|
731
|
+
}
|
|
732
|
+
const cancelledAt = Date.now();
|
|
733
|
+
const status = options.mode === "final" ? "cancelled" : "cancel_requested";
|
|
734
|
+
const cancelState = (state) => normalizeRunState({
|
|
735
|
+
...state,
|
|
736
|
+
status,
|
|
737
|
+
cancelledAt,
|
|
738
|
+
cancellationReason: options.reason,
|
|
739
|
+
updatedAt: cancelledAt,
|
|
740
|
+
error: undefined
|
|
741
|
+
});
|
|
742
|
+
const parent = await store.load(runId);
|
|
743
|
+
if (!parent) {
|
|
744
|
+
return {
|
|
745
|
+
parent: undefined,
|
|
746
|
+
children: []
|
|
747
|
+
};
|
|
748
|
+
}
|
|
749
|
+
const visited = new Set([runId]);
|
|
750
|
+
const children = [];
|
|
751
|
+
const collectChildren = async (parentRunId) => {
|
|
752
|
+
const directChildren = await store.findByParentRunId?.(parentRunId);
|
|
753
|
+
for (const child of directChildren ?? []) {
|
|
754
|
+
if (visited.has(child.runId)) {
|
|
755
|
+
continue;
|
|
756
|
+
}
|
|
757
|
+
visited.add(child.runId);
|
|
758
|
+
children.push(child);
|
|
759
|
+
await collectChildren(child.runId);
|
|
760
|
+
}
|
|
761
|
+
};
|
|
762
|
+
await collectChildren(runId);
|
|
763
|
+
const cancelledParent = cancelState(parent);
|
|
764
|
+
const cancelledChildren = children.map(cancelState);
|
|
765
|
+
await store.save(cloneState(cancelledParent));
|
|
766
|
+
for (const child of cancelledChildren) {
|
|
767
|
+
await store.save(cloneState(child));
|
|
768
|
+
}
|
|
769
|
+
return {
|
|
770
|
+
parent: cloneState(cancelledParent),
|
|
771
|
+
children: cancelledChildren.map(cloneState)
|
|
772
|
+
};
|
|
773
|
+
};
|
|
388
774
|
export const runAgent = async (agent, input = {}) => {
|
|
389
775
|
const context = await resolveContext(agent, input);
|
|
390
776
|
await emitRunStartTelemetry(agent, context.state, context.memoryMessages, input.approvals);
|
|
391
|
-
|
|
777
|
+
const currentStatus = normalizeApprovalStatus(context.state.status);
|
|
778
|
+
if (currentStatus === "completed" ||
|
|
779
|
+
currentStatus === "cancelled" ||
|
|
780
|
+
currentStatus === "cancel_requested" ||
|
|
781
|
+
currentStatus === "timed_out") {
|
|
782
|
+
context.state.status = currentStatus;
|
|
392
783
|
return toOutput(context.state);
|
|
393
784
|
}
|
|
394
|
-
if (
|
|
785
|
+
if (currentStatus === "waiting_approval" && context.state.pendingApprovals.length > 0 && !input.approvals?.length) {
|
|
786
|
+
context.state.status = currentStatus;
|
|
395
787
|
return toOutput(context.state);
|
|
396
788
|
}
|
|
397
789
|
if (context.remainingSteps === 0) {
|
|
@@ -418,8 +810,10 @@ export const runAgent = async (agent, input = {}) => {
|
|
|
418
810
|
agentId: context.state.agentId,
|
|
419
811
|
stepIndex: context.state.currentStep + 1
|
|
420
812
|
});
|
|
813
|
+
const policy = resolveRunPolicy(agent, input);
|
|
814
|
+
const abortContext = createAgentAbortContext(input.abortSignal, policy);
|
|
421
815
|
try {
|
|
422
|
-
const result = await generateText(createGenerateOptions(agent, context.state, input, context.messages, context.remainingSteps));
|
|
816
|
+
const result = await withAgentPolicyTimeout(generateText(createGenerateOptions(agent, context.state, input, context.messages, context.remainingSteps, abortContext.signal)), abortContext);
|
|
423
817
|
const newSteps = mapSteps(result.steps, context.state.currentStep, result.toolResults);
|
|
424
818
|
let output = finalizeState(context.state, result, newSteps, result.toolResults);
|
|
425
819
|
const outputGuardrail = await runGuardrails(agent, output.state, "output", agent.outputGuardrails, () => ({
|
|
@@ -439,6 +833,14 @@ export const runAgent = async (agent, input = {}) => {
|
|
|
439
833
|
return output;
|
|
440
834
|
}
|
|
441
835
|
catch (error) {
|
|
836
|
+
if (error instanceof AgentPolicyTimeoutError || abortContext.isTimedOut()) {
|
|
837
|
+
const status = policy?.onTimeout === "cancel-requested" ? "cancel_requested" : "timed_out";
|
|
838
|
+
const message = error instanceof Error ? error.message : `Agent run timed out after ${policy?.timeoutMs}ms.`;
|
|
839
|
+
const timedOutState = createTerminalState(context.state, status, message);
|
|
840
|
+
await persistState(agent, timedOutState);
|
|
841
|
+
await emitRunFinishTelemetry(agent, timedOutState);
|
|
842
|
+
return toOutput(timedOutState);
|
|
843
|
+
}
|
|
442
844
|
const failedState = createFailedState(context.state, error instanceof Error ? error.message : String(error));
|
|
443
845
|
await persistState(agent, failedState);
|
|
444
846
|
await emitRunFinishTelemetry(agent, failedState);
|
|
@@ -484,14 +886,20 @@ export const streamAgent = (agent, input = {}) => {
|
|
|
484
886
|
const runner = (async () => {
|
|
485
887
|
const context = await resolveContext(agent, input);
|
|
486
888
|
await emitRunStartTelemetry(agent, context.state, context.memoryMessages, input.approvals);
|
|
487
|
-
|
|
889
|
+
const currentStatus = normalizeApprovalStatus(context.state.status);
|
|
890
|
+
if (currentStatus === "completed" ||
|
|
891
|
+
currentStatus === "cancelled" ||
|
|
892
|
+
currentStatus === "cancel_requested" ||
|
|
893
|
+
currentStatus === "timed_out") {
|
|
894
|
+
context.state.status = currentStatus;
|
|
488
895
|
publish({ done: true, value: undefined });
|
|
489
896
|
return {
|
|
490
897
|
output: toOutput(context.state),
|
|
491
898
|
textStream: emptyAsyncIterable()
|
|
492
899
|
};
|
|
493
900
|
}
|
|
494
|
-
if (
|
|
901
|
+
if (currentStatus === "waiting_approval" && context.state.pendingApprovals.length > 0 && !input.approvals?.length) {
|
|
902
|
+
context.state.status = currentStatus;
|
|
495
903
|
publish({ done: true, value: undefined });
|
|
496
904
|
return {
|
|
497
905
|
output: toOutput(context.state),
|
|
@@ -571,7 +979,9 @@ export const streamAgent = (agent, input = {}) => {
|
|
|
571
979
|
agentId: context.state.agentId,
|
|
572
980
|
stepIndex: context.state.currentStep + 1
|
|
573
981
|
});
|
|
574
|
-
const
|
|
982
|
+
const policy = resolveRunPolicy(agent, input);
|
|
983
|
+
const abortContext = createAgentAbortContext(input.abortSignal, policy);
|
|
984
|
+
const streamResult = streamText(createGenerateOptions(agent, context.state, input, context.messages, context.remainingSteps, abortContext.signal));
|
|
575
985
|
const approvalRequests = [];
|
|
576
986
|
const eventRelay = (async () => {
|
|
577
987
|
for await (const event of streamResult.eventStream) {
|
|
@@ -611,8 +1021,7 @@ export const streamAgent = (agent, input = {}) => {
|
|
|
611
1021
|
})();
|
|
612
1022
|
const output = (async () => {
|
|
613
1023
|
try {
|
|
614
|
-
await eventRelay;
|
|
615
|
-
const final = await streamResult.collect();
|
|
1024
|
+
const final = await withAgentPolicyTimeout(eventRelay.then(() => streamResult.collect()), abortContext);
|
|
616
1025
|
const newSteps = mapSteps(final.steps, context.state.currentStep, final.toolResults);
|
|
617
1026
|
let result = finalizeState(context.state, final, newSteps, final.toolResults);
|
|
618
1027
|
const outputGuardrail = await runGuardrails(agent, result.state, "output", agent.outputGuardrails, () => ({
|
|
@@ -659,6 +1068,30 @@ export const streamAgent = (agent, input = {}) => {
|
|
|
659
1068
|
return result;
|
|
660
1069
|
}
|
|
661
1070
|
catch (error) {
|
|
1071
|
+
if (error instanceof AgentPolicyTimeoutError || abortContext.isTimedOut()) {
|
|
1072
|
+
const status = policy?.onTimeout === "cancel-requested" ? "cancel_requested" : "timed_out";
|
|
1073
|
+
const message = error instanceof Error ? error.message : `Agent run timed out after ${policy?.timeoutMs}ms.`;
|
|
1074
|
+
const timedOutState = createTerminalState(context.state, status, message);
|
|
1075
|
+
await persistState(agent, timedOutState);
|
|
1076
|
+
await emitRunFinishTelemetry(agent, timedOutState);
|
|
1077
|
+
publish({
|
|
1078
|
+
done: false,
|
|
1079
|
+
value: {
|
|
1080
|
+
type: "error",
|
|
1081
|
+
error: new AgentPolicyTimeoutError(policy?.timeoutMs ?? 0)
|
|
1082
|
+
}
|
|
1083
|
+
});
|
|
1084
|
+
publish({
|
|
1085
|
+
done: false,
|
|
1086
|
+
value: {
|
|
1087
|
+
type: "agent-run-finish",
|
|
1088
|
+
status: timedOutState.status,
|
|
1089
|
+
state: timedOutState
|
|
1090
|
+
}
|
|
1091
|
+
});
|
|
1092
|
+
publish({ done: true, value: undefined });
|
|
1093
|
+
return toOutput(timedOutState);
|
|
1094
|
+
}
|
|
662
1095
|
const failedState = createFailedState(context.state, error instanceof Error ? error.message : String(error));
|
|
663
1096
|
await persistState(agent, failedState);
|
|
664
1097
|
await emitRunFinishTelemetry(agent, failedState);
|