@zhivex-ai/core 0.4.0 → 0.5.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/agent-approval.d.ts +6 -0
- package/dist/agent-approval.d.ts.map +1 -0
- package/dist/agent-approval.js +38 -0
- package/dist/agent-approval.js.map +1 -0
- package/dist/agent-handoff.d.ts +11 -0
- package/dist/agent-handoff.d.ts.map +1 -0
- package/dist/agent-handoff.js +20 -0
- package/dist/agent-handoff.js.map +1 -0
- package/dist/agent-store.d.ts +20 -0
- package/dist/agent-store.d.ts.map +1 -0
- package/dist/agent-store.js +252 -0
- package/dist/agent-store.js.map +1 -0
- package/dist/agent.d.ts +8 -0
- package/dist/agent.d.ts.map +1 -0
- package/dist/agent.js +701 -0
- package/dist/agent.js.map +1 -0
- package/dist/catalog.js +1 -1
- package/dist/catalog.js.map +1 -1
- package/dist/errors.d.ts +8 -0
- package/dist/errors.d.ts.map +1 -1
- package/dist/errors.js +9 -0
- package/dist/errors.js.map +1 -1
- package/dist/generate-text.d.ts.map +1 -1
- package/dist/generate-text.js +87 -9
- package/dist/generate-text.js.map +1 -1
- package/dist/index.d.ts +13 -1
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +10 -1
- package/dist/index.js.map +1 -1
- package/dist/live-agent.d.ts +3 -0
- package/dist/live-agent.d.ts.map +1 -0
- package/dist/live-agent.js +565 -0
- package/dist/live-agent.js.map +1 -0
- package/dist/mcp.d.ts +40 -0
- package/dist/mcp.d.ts.map +1 -0
- package/dist/mcp.js +167 -0
- package/dist/mcp.js.map +1 -0
- package/dist/messages.d.ts +9 -1
- package/dist/messages.d.ts.map +1 -1
- package/dist/messages.js +55 -0
- package/dist/messages.js.map +1 -1
- package/dist/observability.d.ts +51 -0
- package/dist/observability.d.ts.map +1 -0
- package/dist/observability.js +276 -0
- package/dist/observability.js.map +1 -0
- package/dist/realtime.d.ts +52 -0
- package/dist/realtime.d.ts.map +1 -0
- package/dist/realtime.js +304 -0
- package/dist/realtime.js.map +1 -0
- package/dist/stream.d.ts +5 -2
- package/dist/stream.d.ts.map +1 -1
- package/dist/stream.js +1 -0
- package/dist/stream.js.map +1 -1
- package/dist/tool-registry.d.ts +17 -0
- package/dist/tool-registry.d.ts.map +1 -0
- package/dist/tool-registry.js +74 -0
- package/dist/tool-registry.js.map +1 -0
- package/dist/types.d.ts +589 -6
- package/dist/types.d.ts.map +1 -1
- package/dist/ui.d.ts +4 -2
- package/dist/ui.d.ts.map +1 -1
- package/dist/ui.js +47 -0
- package/dist/ui.js.map +1 -1
- package/package.json +1 -1
package/dist/agent.js
ADDED
|
@@ -0,0 +1,701 @@
|
|
|
1
|
+
import { createAgentApprovalMessage, getAgentApprovalRequests } from "./agent-approval.js";
|
|
2
|
+
import { createAgentHandoffMessage } from "./agent-handoff.js";
|
|
3
|
+
import { GuardrailTriggeredError, ValidationError } from "./errors.js";
|
|
4
|
+
import { generateText, normalizeMessages, streamText } from "./generate-text.js";
|
|
5
|
+
import { toToolSet } from "./tool-registry.js";
|
|
6
|
+
const randomId = (prefix) => `${prefix}_${Math.random().toString(36).slice(2, 10)}`;
|
|
7
|
+
const joinInstructions = (...parts) => {
|
|
8
|
+
const content = parts.map((part) => part?.trim()).filter((part) => Boolean(part));
|
|
9
|
+
return content.length ? content.join("\n\n") : undefined;
|
|
10
|
+
};
|
|
11
|
+
const hasToolCalls = (messages) => messages.some((message) => message.parts.some((part) => part.type === "tool-call"));
|
|
12
|
+
const snapshotRequest = (request) => ({
|
|
13
|
+
messages: request.messages,
|
|
14
|
+
toolChoice: request.toolChoice,
|
|
15
|
+
toolExecution: request.toolExecution,
|
|
16
|
+
temperature: request.temperature,
|
|
17
|
+
maxTokens: request.maxTokens,
|
|
18
|
+
reasoning: request.reasoning,
|
|
19
|
+
providerOptions: request.providerOptions,
|
|
20
|
+
timeoutMs: request.timeoutMs,
|
|
21
|
+
maxRetries: request.maxRetries,
|
|
22
|
+
retryBackoffMs: request.retryBackoffMs
|
|
23
|
+
});
|
|
24
|
+
const snapshotResponse = (response) => ({
|
|
25
|
+
messages: response.messages ?? (response.message ? [response.message] : []),
|
|
26
|
+
text: response.text,
|
|
27
|
+
finishReason: response.finishReason,
|
|
28
|
+
providerFinishReason: response.providerFinishReason,
|
|
29
|
+
usage: response.usage
|
|
30
|
+
});
|
|
31
|
+
const countToolCalls = (messages) => messages.reduce((total, message) => total + message.parts.filter((part) => part.type === "tool-call").length, 0);
|
|
32
|
+
const mapSteps = (steps, offset, toolResults) => {
|
|
33
|
+
let toolResultCursor = 0;
|
|
34
|
+
return steps.map((step, index) => {
|
|
35
|
+
const response = snapshotResponse(step.response);
|
|
36
|
+
const toolCallCount = countToolCalls(response.messages);
|
|
37
|
+
const stepToolResults = toolResults.slice(toolResultCursor, toolResultCursor + toolCallCount);
|
|
38
|
+
toolResultCursor += toolCallCount;
|
|
39
|
+
const finishedAt = Date.now();
|
|
40
|
+
return {
|
|
41
|
+
index: offset + index + 1,
|
|
42
|
+
status: "completed",
|
|
43
|
+
startedAt: finishedAt,
|
|
44
|
+
finishedAt,
|
|
45
|
+
request: snapshotRequest(step.request),
|
|
46
|
+
response,
|
|
47
|
+
toolResults: stepToolResults
|
|
48
|
+
};
|
|
49
|
+
});
|
|
50
|
+
};
|
|
51
|
+
const cloneMetadata = (...values) => {
|
|
52
|
+
const merged = Object.assign({}, ...values.filter(Boolean));
|
|
53
|
+
return Object.keys(merged).length ? merged : undefined;
|
|
54
|
+
};
|
|
55
|
+
const toOutput = (state) => ({
|
|
56
|
+
status: state.status,
|
|
57
|
+
outputText: state.outputText,
|
|
58
|
+
finishReason: state.finishReason,
|
|
59
|
+
providerFinishReason: state.providerFinishReason,
|
|
60
|
+
usage: state.usage,
|
|
61
|
+
messages: state.messages,
|
|
62
|
+
steps: state.steps,
|
|
63
|
+
toolResults: state.toolResults,
|
|
64
|
+
state,
|
|
65
|
+
error: state.error
|
|
66
|
+
});
|
|
67
|
+
const cloneState = (state) => JSON.parse(JSON.stringify(state));
|
|
68
|
+
const createBaseState = (provider, modelId, initialMessages, maxSteps, metadata, agentId, runId, handoff) => {
|
|
69
|
+
const startedAt = Date.now();
|
|
70
|
+
return {
|
|
71
|
+
runId,
|
|
72
|
+
agentId,
|
|
73
|
+
parentRunId: handoff?.fromRunId,
|
|
74
|
+
provider,
|
|
75
|
+
modelId,
|
|
76
|
+
status: "running",
|
|
77
|
+
messages: initialMessages,
|
|
78
|
+
steps: [],
|
|
79
|
+
toolResults: [],
|
|
80
|
+
currentStep: 0,
|
|
81
|
+
maxSteps,
|
|
82
|
+
outputText: "",
|
|
83
|
+
pendingApprovals: [],
|
|
84
|
+
metadata,
|
|
85
|
+
handoff,
|
|
86
|
+
startedAt,
|
|
87
|
+
updatedAt: startedAt
|
|
88
|
+
};
|
|
89
|
+
};
|
|
90
|
+
const ensureValidStateInput = (input) => {
|
|
91
|
+
if (input.approvals?.length && !input.state) {
|
|
92
|
+
throw new ValidationError('The "approvals" option requires an existing agent "state".');
|
|
93
|
+
}
|
|
94
|
+
if (!input.state) {
|
|
95
|
+
return;
|
|
96
|
+
}
|
|
97
|
+
if (input.prompt !== undefined || input.messages !== undefined || input.system !== undefined || input.handoff !== undefined) {
|
|
98
|
+
throw new ValidationError('Pass either "state" or a fresh "prompt"/"messages" input, but not both.');
|
|
99
|
+
}
|
|
100
|
+
};
|
|
101
|
+
const injectContextMessages = (messages, extraMessages) => {
|
|
102
|
+
if (!extraMessages.length) {
|
|
103
|
+
return messages;
|
|
104
|
+
}
|
|
105
|
+
if (messages[0]?.role === "system") {
|
|
106
|
+
return [messages[0], ...extraMessages, ...messages.slice(1)];
|
|
107
|
+
}
|
|
108
|
+
return [...extraMessages, ...messages];
|
|
109
|
+
};
|
|
110
|
+
const prepareFreshMessages = async (agent, input, runId) => {
|
|
111
|
+
let messages = normalizeMessages({
|
|
112
|
+
prompt: input.prompt,
|
|
113
|
+
messages: input.messages,
|
|
114
|
+
system: joinInstructions(agent.instructions, input.system)
|
|
115
|
+
});
|
|
116
|
+
const handoffMessages = input.handoff
|
|
117
|
+
? [createAgentHandoffMessage(input.handoff), ...input.handoff.contextMessages.filter((message) => message.role !== "system")]
|
|
118
|
+
: [];
|
|
119
|
+
messages = injectContextMessages(messages, handoffMessages);
|
|
120
|
+
const memoryMessages = agent.memory
|
|
121
|
+
? await agent.memory.load({
|
|
122
|
+
runId,
|
|
123
|
+
agentId: agent.id,
|
|
124
|
+
metadata: cloneMetadata(agent.metadata, input.metadata)
|
|
125
|
+
})
|
|
126
|
+
: [];
|
|
127
|
+
messages = injectContextMessages(messages, memoryMessages);
|
|
128
|
+
return {
|
|
129
|
+
messages,
|
|
130
|
+
memoryMessages
|
|
131
|
+
};
|
|
132
|
+
};
|
|
133
|
+
const applyApprovalResponses = (messages, approvals, pendingApprovals) => {
|
|
134
|
+
if (!approvals?.length) {
|
|
135
|
+
return {
|
|
136
|
+
messages,
|
|
137
|
+
pendingApprovals
|
|
138
|
+
};
|
|
139
|
+
}
|
|
140
|
+
const pendingById = new Map(pendingApprovals.map((approval) => [approval.id, approval]));
|
|
141
|
+
for (const approval of approvals) {
|
|
142
|
+
const pending = pendingById.get(approval.approvalRequestId);
|
|
143
|
+
if (!pending) {
|
|
144
|
+
throw new ValidationError(`Unknown approval request "${approval.approvalRequestId}".`);
|
|
145
|
+
}
|
|
146
|
+
if (pending.provider !== approval.provider) {
|
|
147
|
+
throw new ValidationError(`Approval request "${approval.approvalRequestId}" belongs to provider "${pending.provider}", not "${approval.provider}".`);
|
|
148
|
+
}
|
|
149
|
+
}
|
|
150
|
+
return {
|
|
151
|
+
messages: [...messages, createAgentApprovalMessage(approvals)],
|
|
152
|
+
pendingApprovals: pendingApprovals.filter((pending) => !approvals.some((approval) => approval.approvalRequestId === pending.id))
|
|
153
|
+
};
|
|
154
|
+
};
|
|
155
|
+
const finalizeState = (state, result, newSteps, newToolResults) => {
|
|
156
|
+
const nextCurrentStep = state.currentStep + newSteps.length;
|
|
157
|
+
const exhausted = nextCurrentStep >= state.maxSteps;
|
|
158
|
+
const lastStep = newSteps.at(-1);
|
|
159
|
+
const unresolvedToolCalls = lastStep?.response ? hasToolCalls(lastStep.response.messages) : false;
|
|
160
|
+
const pendingApprovals = getAgentApprovalRequests(newSteps.flatMap((step) => step.response?.messages ?? []));
|
|
161
|
+
if (pendingApprovals.length) {
|
|
162
|
+
state.status = "suspended";
|
|
163
|
+
state.error = undefined;
|
|
164
|
+
if (lastStep) {
|
|
165
|
+
lastStep.status = "suspended";
|
|
166
|
+
}
|
|
167
|
+
}
|
|
168
|
+
else if (exhausted && unresolvedToolCalls) {
|
|
169
|
+
state.status = "failed";
|
|
170
|
+
state.error = {
|
|
171
|
+
message: "Agent exhausted maxSteps before reaching a terminal response."
|
|
172
|
+
};
|
|
173
|
+
if (lastStep) {
|
|
174
|
+
lastStep.status = "failed";
|
|
175
|
+
lastStep.error = state.error;
|
|
176
|
+
}
|
|
177
|
+
}
|
|
178
|
+
else {
|
|
179
|
+
state.status = "completed";
|
|
180
|
+
state.error = undefined;
|
|
181
|
+
}
|
|
182
|
+
state.messages = result.messages;
|
|
183
|
+
state.steps = [...state.steps, ...newSteps];
|
|
184
|
+
state.toolResults = [...state.toolResults, ...newToolResults];
|
|
185
|
+
state.currentStep = nextCurrentStep;
|
|
186
|
+
state.outputText = result.text;
|
|
187
|
+
state.finishReason = result.finishReason;
|
|
188
|
+
state.providerFinishReason = result.providerFinishReason;
|
|
189
|
+
state.usage = result.usage;
|
|
190
|
+
state.pendingApprovals = pendingApprovals;
|
|
191
|
+
state.updatedAt = Date.now();
|
|
192
|
+
return toOutput(state);
|
|
193
|
+
};
|
|
194
|
+
const emitTelemetryEvent = async (agent, event) => {
|
|
195
|
+
await agent.onTelemetryEvent?.(event);
|
|
196
|
+
};
|
|
197
|
+
const persistState = async (agent, state) => {
|
|
198
|
+
state.updatedAt = Date.now();
|
|
199
|
+
await agent.store?.save(cloneState(state));
|
|
200
|
+
await emitTelemetryEvent(agent, {
|
|
201
|
+
type: "state-saved",
|
|
202
|
+
runId: state.runId,
|
|
203
|
+
agentId: state.agentId,
|
|
204
|
+
status: state.status
|
|
205
|
+
});
|
|
206
|
+
await agent.memory?.save?.({
|
|
207
|
+
runId: state.runId,
|
|
208
|
+
agentId: state.agentId,
|
|
209
|
+
state: cloneState(state),
|
|
210
|
+
metadata: state.metadata
|
|
211
|
+
});
|
|
212
|
+
};
|
|
213
|
+
const approvalsFromEvents = (messages) => getAgentApprovalRequests(messages);
|
|
214
|
+
const emitFinalizedStepTelemetry = async (agent, state, steps) => {
|
|
215
|
+
for (const step of steps) {
|
|
216
|
+
await emitTelemetryEvent(agent, {
|
|
217
|
+
type: "step-finish",
|
|
218
|
+
runId: state.runId,
|
|
219
|
+
agentId: state.agentId,
|
|
220
|
+
step
|
|
221
|
+
});
|
|
222
|
+
}
|
|
223
|
+
};
|
|
224
|
+
const emitApprovalTelemetry = async (agent, state, approvals) => {
|
|
225
|
+
for (const approval of approvals) {
|
|
226
|
+
await emitTelemetryEvent(agent, {
|
|
227
|
+
type: "approval-request",
|
|
228
|
+
runId: state.runId,
|
|
229
|
+
agentId: state.agentId,
|
|
230
|
+
approval
|
|
231
|
+
});
|
|
232
|
+
}
|
|
233
|
+
};
|
|
234
|
+
const emitToolApprovalTelemetry = async (agent, state, event) => {
|
|
235
|
+
await emitTelemetryEvent(agent, {
|
|
236
|
+
type: "tool-approval",
|
|
237
|
+
runId: state.runId,
|
|
238
|
+
agentId: state.agentId,
|
|
239
|
+
toolCall: event.request.toolCall,
|
|
240
|
+
approved: event.decision.approved,
|
|
241
|
+
reason: event.decision.reason,
|
|
242
|
+
metadata: event.decision.metadata
|
|
243
|
+
});
|
|
244
|
+
};
|
|
245
|
+
const normalizeGuardrailTrigger = (value) => value?.triggered ? value : undefined;
|
|
246
|
+
const applyGuardrailFailure = (state, stage, trigger) => ({
|
|
247
|
+
...state,
|
|
248
|
+
status: "failed",
|
|
249
|
+
error: {
|
|
250
|
+
message: trigger.reason ?? `Agent ${stage} guardrail triggered.`
|
|
251
|
+
},
|
|
252
|
+
updatedAt: Date.now()
|
|
253
|
+
});
|
|
254
|
+
const runGuardrails = async (agent, state, stage, guardrails, requestFactory) => {
|
|
255
|
+
for (const [index, guardrail] of (guardrails ?? []).entries()) {
|
|
256
|
+
const trigger = normalizeGuardrailTrigger(await guardrail(requestFactory(index)));
|
|
257
|
+
if (!trigger) {
|
|
258
|
+
continue;
|
|
259
|
+
}
|
|
260
|
+
await emitTelemetryEvent(agent, {
|
|
261
|
+
type: "guardrail-triggered",
|
|
262
|
+
runId: state.runId,
|
|
263
|
+
agentId: state.agentId,
|
|
264
|
+
stage,
|
|
265
|
+
reason: trigger.reason ?? `Agent ${stage} guardrail #${index + 1} triggered.`,
|
|
266
|
+
metadata: trigger.metadata
|
|
267
|
+
});
|
|
268
|
+
return trigger;
|
|
269
|
+
}
|
|
270
|
+
return undefined;
|
|
271
|
+
};
|
|
272
|
+
const resolveContext = async (agent, input) => {
|
|
273
|
+
let loadedState = input.state;
|
|
274
|
+
if (!loadedState && input.runId && agent.store) {
|
|
275
|
+
loadedState = await agent.store.load(input.runId);
|
|
276
|
+
}
|
|
277
|
+
const normalizedInput = loadedState ? { ...input, state: loadedState } : input;
|
|
278
|
+
ensureValidStateInput(normalizedInput);
|
|
279
|
+
const metadata = cloneMetadata(agent.metadata, loadedState?.metadata, input.metadata, input.handoff?.metadata);
|
|
280
|
+
if (loadedState) {
|
|
281
|
+
const maxSteps = input.maxSteps ?? loadedState.maxSteps;
|
|
282
|
+
const resumed = applyApprovalResponses(loadedState.messages, input.approvals, loadedState.pendingApprovals);
|
|
283
|
+
return {
|
|
284
|
+
state: {
|
|
285
|
+
...loadedState,
|
|
286
|
+
agentId: loadedState.agentId ?? agent.id,
|
|
287
|
+
provider: agent.model.provider,
|
|
288
|
+
modelId: agent.model.modelId,
|
|
289
|
+
maxSteps,
|
|
290
|
+
messages: resumed.messages,
|
|
291
|
+
pendingApprovals: resumed.pendingApprovals,
|
|
292
|
+
metadata,
|
|
293
|
+
updatedAt: Date.now()
|
|
294
|
+
},
|
|
295
|
+
messages: resumed.messages,
|
|
296
|
+
remainingSteps: Math.max(0, maxSteps - loadedState.currentStep),
|
|
297
|
+
memoryMessages: []
|
|
298
|
+
};
|
|
299
|
+
}
|
|
300
|
+
const runId = input.runId ?? randomId("run");
|
|
301
|
+
const maxSteps = Math.max(1, input.maxSteps ?? agent.maxSteps ?? 1);
|
|
302
|
+
const prepared = await prepareFreshMessages(agent, input, runId);
|
|
303
|
+
return {
|
|
304
|
+
state: createBaseState(agent.model.provider, agent.model.modelId, prepared.messages, maxSteps, metadata, agent.id, runId, input.handoff),
|
|
305
|
+
messages: prepared.messages,
|
|
306
|
+
remainingSteps: maxSteps,
|
|
307
|
+
memoryMessages: prepared.memoryMessages
|
|
308
|
+
};
|
|
309
|
+
};
|
|
310
|
+
const createGenerateOptions = (agent, state, input, messages, maxSteps) => ({
|
|
311
|
+
model: agent.model,
|
|
312
|
+
messages,
|
|
313
|
+
tools: toToolSet(input.tools ?? agent.tools),
|
|
314
|
+
toolChoice: input.toolChoice,
|
|
315
|
+
toolExecution: input.toolExecution ?? agent.toolExecution,
|
|
316
|
+
toolApprovalPolicy: input.toolApprovalPolicy ?? agent.toolApprovalPolicy,
|
|
317
|
+
onToolApprovalDecision: async (event) => {
|
|
318
|
+
await emitToolApprovalTelemetry(agent, state, event);
|
|
319
|
+
},
|
|
320
|
+
maxSteps,
|
|
321
|
+
temperature: input.temperature ?? agent.temperature,
|
|
322
|
+
maxTokens: input.maxTokens ?? agent.maxTokens,
|
|
323
|
+
reasoning: input.reasoning ?? agent.reasoning,
|
|
324
|
+
providerOptions: input.providerOptions ?? agent.providerOptions,
|
|
325
|
+
abortSignal: input.abortSignal,
|
|
326
|
+
timeoutMs: input.timeoutMs,
|
|
327
|
+
maxRetries: input.maxRetries,
|
|
328
|
+
retryBackoffMs: input.retryBackoffMs
|
|
329
|
+
});
|
|
330
|
+
const emptyAsyncIterable = async function* () {
|
|
331
|
+
return;
|
|
332
|
+
};
|
|
333
|
+
const createFailedState = (state, message) => ({
|
|
334
|
+
...state,
|
|
335
|
+
status: "failed",
|
|
336
|
+
error: {
|
|
337
|
+
message
|
|
338
|
+
},
|
|
339
|
+
updatedAt: Date.now()
|
|
340
|
+
});
|
|
341
|
+
const emitRunStartTelemetry = async (agent, state, memoryMessages, approvals) => {
|
|
342
|
+
await emitTelemetryEvent(agent, {
|
|
343
|
+
type: "run-start",
|
|
344
|
+
runId: state.runId,
|
|
345
|
+
agentId: state.agentId,
|
|
346
|
+
provider: state.provider,
|
|
347
|
+
modelId: state.modelId,
|
|
348
|
+
maxSteps: state.maxSteps
|
|
349
|
+
});
|
|
350
|
+
if (state.handoff) {
|
|
351
|
+
await emitTelemetryEvent(agent, {
|
|
352
|
+
type: "handoff",
|
|
353
|
+
runId: state.runId,
|
|
354
|
+
agentId: state.agentId,
|
|
355
|
+
handoff: state.handoff
|
|
356
|
+
});
|
|
357
|
+
}
|
|
358
|
+
if (memoryMessages.length) {
|
|
359
|
+
await emitTelemetryEvent(agent, {
|
|
360
|
+
type: "memory-loaded",
|
|
361
|
+
runId: state.runId,
|
|
362
|
+
agentId: state.agentId,
|
|
363
|
+
messageCount: memoryMessages.length
|
|
364
|
+
});
|
|
365
|
+
}
|
|
366
|
+
for (const approval of approvals ?? []) {
|
|
367
|
+
await emitTelemetryEvent(agent, {
|
|
368
|
+
type: "approval-resolved",
|
|
369
|
+
runId: state.runId,
|
|
370
|
+
agentId: state.agentId,
|
|
371
|
+
approval
|
|
372
|
+
});
|
|
373
|
+
}
|
|
374
|
+
};
|
|
375
|
+
const emitRunFinishTelemetry = async (agent, state) => {
|
|
376
|
+
await emitTelemetryEvent(agent, {
|
|
377
|
+
type: "run-finish",
|
|
378
|
+
runId: state.runId,
|
|
379
|
+
agentId: state.agentId,
|
|
380
|
+
status: state.status,
|
|
381
|
+
state: cloneState(state)
|
|
382
|
+
});
|
|
383
|
+
};
|
|
384
|
+
export const createAgent = (definition) => ({
|
|
385
|
+
...definition,
|
|
386
|
+
metadata: cloneMetadata(definition.metadata)
|
|
387
|
+
});
|
|
388
|
+
export const runAgent = async (agent, input = {}) => {
|
|
389
|
+
const context = await resolveContext(agent, input);
|
|
390
|
+
await emitRunStartTelemetry(agent, context.state, context.memoryMessages, input.approvals);
|
|
391
|
+
if (context.state.status === "completed" || context.state.status === "cancelled") {
|
|
392
|
+
return toOutput(context.state);
|
|
393
|
+
}
|
|
394
|
+
if (context.state.status === "suspended" && context.state.pendingApprovals.length > 0 && !input.approvals?.length) {
|
|
395
|
+
return toOutput(context.state);
|
|
396
|
+
}
|
|
397
|
+
if (context.remainingSteps === 0) {
|
|
398
|
+
const state = createFailedState(context.state, "Agent exhausted maxSteps before reaching a terminal response.");
|
|
399
|
+
await persistState(agent, state);
|
|
400
|
+
await emitRunFinishTelemetry(agent, state);
|
|
401
|
+
return toOutput(state);
|
|
402
|
+
}
|
|
403
|
+
const inputGuardrail = await runGuardrails(agent, context.state, "input", agent.inputGuardrails, () => ({
|
|
404
|
+
runId: context.state.runId,
|
|
405
|
+
agentId: context.state.agentId,
|
|
406
|
+
messages: context.messages,
|
|
407
|
+
metadata: context.state.metadata
|
|
408
|
+
}));
|
|
409
|
+
if (inputGuardrail) {
|
|
410
|
+
const failedState = applyGuardrailFailure(context.state, "input", inputGuardrail);
|
|
411
|
+
await persistState(agent, failedState);
|
|
412
|
+
await emitRunFinishTelemetry(agent, failedState);
|
|
413
|
+
return toOutput(failedState);
|
|
414
|
+
}
|
|
415
|
+
await emitTelemetryEvent(agent, {
|
|
416
|
+
type: "step-start",
|
|
417
|
+
runId: context.state.runId,
|
|
418
|
+
agentId: context.state.agentId,
|
|
419
|
+
stepIndex: context.state.currentStep + 1
|
|
420
|
+
});
|
|
421
|
+
try {
|
|
422
|
+
const result = await generateText(createGenerateOptions(agent, context.state, input, context.messages, context.remainingSteps));
|
|
423
|
+
const newSteps = mapSteps(result.steps, context.state.currentStep, result.toolResults);
|
|
424
|
+
let output = finalizeState(context.state, result, newSteps, result.toolResults);
|
|
425
|
+
const outputGuardrail = await runGuardrails(agent, output.state, "output", agent.outputGuardrails, () => ({
|
|
426
|
+
runId: output.state.runId,
|
|
427
|
+
agentId: output.state.agentId,
|
|
428
|
+
state: cloneState(output.state),
|
|
429
|
+
output,
|
|
430
|
+
metadata: output.state.metadata
|
|
431
|
+
}));
|
|
432
|
+
if (outputGuardrail) {
|
|
433
|
+
output = toOutput(applyGuardrailFailure(output.state, "output", outputGuardrail));
|
|
434
|
+
}
|
|
435
|
+
await emitFinalizedStepTelemetry(agent, output.state, newSteps);
|
|
436
|
+
await emitApprovalTelemetry(agent, output.state, approvalsFromEvents(newSteps.flatMap((step) => step.response?.messages ?? [])));
|
|
437
|
+
await persistState(agent, output.state);
|
|
438
|
+
await emitRunFinishTelemetry(agent, output.state);
|
|
439
|
+
return output;
|
|
440
|
+
}
|
|
441
|
+
catch (error) {
|
|
442
|
+
const failedState = createFailedState(context.state, error instanceof Error ? error.message : String(error));
|
|
443
|
+
await persistState(agent, failedState);
|
|
444
|
+
await emitRunFinishTelemetry(agent, failedState);
|
|
445
|
+
throw error;
|
|
446
|
+
}
|
|
447
|
+
};
|
|
448
|
+
export const streamAgent = (agent, input = {}) => {
|
|
449
|
+
const subscribers = new Set();
|
|
450
|
+
const history = [];
|
|
451
|
+
let done = false;
|
|
452
|
+
const publish = (value) => {
|
|
453
|
+
history.push(value);
|
|
454
|
+
for (const subscriber of subscribers) {
|
|
455
|
+
subscriber(value);
|
|
456
|
+
}
|
|
457
|
+
if (value.done) {
|
|
458
|
+
done = true;
|
|
459
|
+
}
|
|
460
|
+
};
|
|
461
|
+
const createEventStream = async function* () {
|
|
462
|
+
let cursor = 0;
|
|
463
|
+
while (true) {
|
|
464
|
+
while (cursor < history.length) {
|
|
465
|
+
const item = history[cursor];
|
|
466
|
+
cursor += 1;
|
|
467
|
+
if (item.done) {
|
|
468
|
+
return;
|
|
469
|
+
}
|
|
470
|
+
yield item.value;
|
|
471
|
+
}
|
|
472
|
+
if (done) {
|
|
473
|
+
return;
|
|
474
|
+
}
|
|
475
|
+
await new Promise((resolve) => {
|
|
476
|
+
const subscriber = (value) => {
|
|
477
|
+
subscribers.delete(subscriber);
|
|
478
|
+
resolve(value);
|
|
479
|
+
};
|
|
480
|
+
subscribers.add(subscriber);
|
|
481
|
+
});
|
|
482
|
+
}
|
|
483
|
+
};
|
|
484
|
+
const runner = (async () => {
|
|
485
|
+
const context = await resolveContext(agent, input);
|
|
486
|
+
await emitRunStartTelemetry(agent, context.state, context.memoryMessages, input.approvals);
|
|
487
|
+
if (context.state.status === "completed" || context.state.status === "cancelled") {
|
|
488
|
+
publish({ done: true, value: undefined });
|
|
489
|
+
return {
|
|
490
|
+
output: toOutput(context.state),
|
|
491
|
+
textStream: emptyAsyncIterable()
|
|
492
|
+
};
|
|
493
|
+
}
|
|
494
|
+
if (context.state.status === "suspended" && context.state.pendingApprovals.length > 0 && !input.approvals?.length) {
|
|
495
|
+
publish({ done: true, value: undefined });
|
|
496
|
+
return {
|
|
497
|
+
output: toOutput(context.state),
|
|
498
|
+
textStream: emptyAsyncIterable()
|
|
499
|
+
};
|
|
500
|
+
}
|
|
501
|
+
if (context.remainingSteps === 0) {
|
|
502
|
+
const state = createFailedState(context.state, "Agent exhausted maxSteps before reaching a terminal response.");
|
|
503
|
+
await persistState(agent, state);
|
|
504
|
+
await emitRunFinishTelemetry(agent, state);
|
|
505
|
+
publish({ done: true, value: undefined });
|
|
506
|
+
return {
|
|
507
|
+
output: toOutput(state),
|
|
508
|
+
textStream: emptyAsyncIterable()
|
|
509
|
+
};
|
|
510
|
+
}
|
|
511
|
+
const inputGuardrail = await runGuardrails(agent, context.state, "input", agent.inputGuardrails, () => ({
|
|
512
|
+
runId: context.state.runId,
|
|
513
|
+
agentId: context.state.agentId,
|
|
514
|
+
messages: context.messages,
|
|
515
|
+
metadata: context.state.metadata
|
|
516
|
+
}));
|
|
517
|
+
if (inputGuardrail) {
|
|
518
|
+
const failedState = applyGuardrailFailure(context.state, "input", inputGuardrail);
|
|
519
|
+
await persistState(agent, failedState);
|
|
520
|
+
await emitRunFinishTelemetry(agent, failedState);
|
|
521
|
+
publish({
|
|
522
|
+
done: false,
|
|
523
|
+
value: {
|
|
524
|
+
type: "error",
|
|
525
|
+
error: new GuardrailTriggeredError("input", failedState.error?.message ?? "Agent input guardrail triggered.", {
|
|
526
|
+
metadata: inputGuardrail.metadata
|
|
527
|
+
})
|
|
528
|
+
}
|
|
529
|
+
});
|
|
530
|
+
publish({
|
|
531
|
+
done: false,
|
|
532
|
+
value: {
|
|
533
|
+
type: "agent-run-finish",
|
|
534
|
+
status: failedState.status,
|
|
535
|
+
state: failedState
|
|
536
|
+
}
|
|
537
|
+
});
|
|
538
|
+
publish({ done: true, value: undefined });
|
|
539
|
+
return {
|
|
540
|
+
output: toOutput(failedState),
|
|
541
|
+
textStream: emptyAsyncIterable()
|
|
542
|
+
};
|
|
543
|
+
}
|
|
544
|
+
publish({
|
|
545
|
+
done: false,
|
|
546
|
+
value: {
|
|
547
|
+
type: "agent-run-start",
|
|
548
|
+
currentStep: context.state.currentStep + 1,
|
|
549
|
+
maxSteps: context.state.maxSteps
|
|
550
|
+
}
|
|
551
|
+
});
|
|
552
|
+
for (const approval of input.approvals ?? []) {
|
|
553
|
+
publish({
|
|
554
|
+
done: false,
|
|
555
|
+
value: {
|
|
556
|
+
type: "agent-approval-resolved",
|
|
557
|
+
approval
|
|
558
|
+
}
|
|
559
|
+
});
|
|
560
|
+
}
|
|
561
|
+
publish({
|
|
562
|
+
done: false,
|
|
563
|
+
value: {
|
|
564
|
+
type: "agent-step-start",
|
|
565
|
+
stepIndex: context.state.currentStep + 1
|
|
566
|
+
}
|
|
567
|
+
});
|
|
568
|
+
await emitTelemetryEvent(agent, {
|
|
569
|
+
type: "step-start",
|
|
570
|
+
runId: context.state.runId,
|
|
571
|
+
agentId: context.state.agentId,
|
|
572
|
+
stepIndex: context.state.currentStep + 1
|
|
573
|
+
});
|
|
574
|
+
const streamResult = streamText(createGenerateOptions(agent, context.state, input, context.messages, context.remainingSteps));
|
|
575
|
+
const approvalRequests = [];
|
|
576
|
+
const eventRelay = (async () => {
|
|
577
|
+
for await (const event of streamResult.eventStream) {
|
|
578
|
+
publish({ done: false, value: event });
|
|
579
|
+
if (event.type === "provider-data" &&
|
|
580
|
+
typeof event.data === "object" &&
|
|
581
|
+
event.data !== null &&
|
|
582
|
+
!Array.isArray(event.data) &&
|
|
583
|
+
event.data.type === "mcp_approval_request" &&
|
|
584
|
+
typeof event.data.id === "string" &&
|
|
585
|
+
typeof event.data.name === "string" &&
|
|
586
|
+
typeof event.data.arguments === "string") {
|
|
587
|
+
const approval = {
|
|
588
|
+
provider: event.provider,
|
|
589
|
+
id: event.data.id,
|
|
590
|
+
name: event.data.name,
|
|
591
|
+
arguments: event.data.arguments,
|
|
592
|
+
serverLabel: typeof event.data.server_label === "string" ? event.data.server_label : undefined,
|
|
593
|
+
rawData: event.data
|
|
594
|
+
};
|
|
595
|
+
approvalRequests.push(approval);
|
|
596
|
+
publish({
|
|
597
|
+
done: false,
|
|
598
|
+
value: {
|
|
599
|
+
type: "agent-approval-request",
|
|
600
|
+
approval
|
|
601
|
+
}
|
|
602
|
+
});
|
|
603
|
+
await emitTelemetryEvent(agent, {
|
|
604
|
+
type: "approval-request",
|
|
605
|
+
runId: context.state.runId,
|
|
606
|
+
agentId: context.state.agentId,
|
|
607
|
+
approval
|
|
608
|
+
});
|
|
609
|
+
}
|
|
610
|
+
}
|
|
611
|
+
})();
|
|
612
|
+
const output = (async () => {
|
|
613
|
+
try {
|
|
614
|
+
await eventRelay;
|
|
615
|
+
const final = await streamResult.collect();
|
|
616
|
+
const newSteps = mapSteps(final.steps, context.state.currentStep, final.toolResults);
|
|
617
|
+
let result = finalizeState(context.state, final, newSteps, final.toolResults);
|
|
618
|
+
const outputGuardrail = await runGuardrails(agent, result.state, "output", agent.outputGuardrails, () => ({
|
|
619
|
+
runId: result.state.runId,
|
|
620
|
+
agentId: result.state.agentId,
|
|
621
|
+
state: cloneState(result.state),
|
|
622
|
+
output: result,
|
|
623
|
+
metadata: result.state.metadata
|
|
624
|
+
}));
|
|
625
|
+
if (outputGuardrail) {
|
|
626
|
+
result = toOutput(applyGuardrailFailure(result.state, "output", outputGuardrail));
|
|
627
|
+
publish({
|
|
628
|
+
done: false,
|
|
629
|
+
value: {
|
|
630
|
+
type: "error",
|
|
631
|
+
error: new GuardrailTriggeredError("output", result.state.error?.message ?? "Agent output guardrail triggered.", { metadata: outputGuardrail.metadata })
|
|
632
|
+
}
|
|
633
|
+
});
|
|
634
|
+
}
|
|
635
|
+
for (const step of newSteps) {
|
|
636
|
+
publish({
|
|
637
|
+
done: false,
|
|
638
|
+
value: {
|
|
639
|
+
type: "agent-step-finish",
|
|
640
|
+
step
|
|
641
|
+
}
|
|
642
|
+
});
|
|
643
|
+
}
|
|
644
|
+
await emitFinalizedStepTelemetry(agent, result.state, newSteps);
|
|
645
|
+
if (!approvalRequests.length) {
|
|
646
|
+
await emitApprovalTelemetry(agent, result.state, approvalsFromEvents(newSteps.flatMap((step) => step.response?.messages ?? [])));
|
|
647
|
+
}
|
|
648
|
+
await persistState(agent, result.state);
|
|
649
|
+
await emitRunFinishTelemetry(agent, result.state);
|
|
650
|
+
publish({
|
|
651
|
+
done: false,
|
|
652
|
+
value: {
|
|
653
|
+
type: "agent-run-finish",
|
|
654
|
+
status: result.status,
|
|
655
|
+
state: result.state
|
|
656
|
+
}
|
|
657
|
+
});
|
|
658
|
+
publish({ done: true, value: undefined });
|
|
659
|
+
return result;
|
|
660
|
+
}
|
|
661
|
+
catch (error) {
|
|
662
|
+
const failedState = createFailedState(context.state, error instanceof Error ? error.message : String(error));
|
|
663
|
+
await persistState(agent, failedState);
|
|
664
|
+
await emitRunFinishTelemetry(agent, failedState);
|
|
665
|
+
publish({
|
|
666
|
+
done: false,
|
|
667
|
+
value: {
|
|
668
|
+
type: "error",
|
|
669
|
+
error: error instanceof Error ? error : new Error(String(error))
|
|
670
|
+
}
|
|
671
|
+
});
|
|
672
|
+
publish({
|
|
673
|
+
done: false,
|
|
674
|
+
value: {
|
|
675
|
+
type: "agent-run-finish",
|
|
676
|
+
status: failedState.status,
|
|
677
|
+
state: failedState
|
|
678
|
+
}
|
|
679
|
+
});
|
|
680
|
+
publish({ done: true, value: undefined });
|
|
681
|
+
throw error;
|
|
682
|
+
}
|
|
683
|
+
})();
|
|
684
|
+
return {
|
|
685
|
+
output,
|
|
686
|
+
textStream: streamResult.textStream
|
|
687
|
+
};
|
|
688
|
+
})();
|
|
689
|
+
return {
|
|
690
|
+
eventStream: createEventStream(),
|
|
691
|
+
textStream: (async function* () {
|
|
692
|
+
const started = await runner;
|
|
693
|
+
for await (const chunk of started.textStream) {
|
|
694
|
+
yield chunk;
|
|
695
|
+
}
|
|
696
|
+
})(),
|
|
697
|
+
collect: async () => (await runner).output
|
|
698
|
+
};
|
|
699
|
+
};
|
|
700
|
+
export const resumeAgent = async (agent, input) => runAgent(agent, input);
|
|
701
|
+
//# sourceMappingURL=agent.js.map
|