@gea-ai/agent-sdk 0.1.260920-alpha.0 → 0.1.260920-alpha.2
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 +10 -0
- package/dist/agent-core.wasm +0 -0
- package/dist/agent-session-execution.js +38 -2
- package/dist/agent-session-inbox.d.ts +2 -0
- package/dist/agent-session-inbox.js +95 -3
- package/dist/agent-session-stream.js +19 -4
- package/dist/agent-worker.js +202 -19
- package/dist/experimental/agent-core-messages.d.ts +1 -1
- package/dist/experimental/agent-core-messages.js +2 -2
- package/dist/experimental/agent-core-tools.js +3 -2
- package/dist/experimental/agent-core-ui.js +3 -2
- package/dist/experimental/agent-core-worker-tools.js +7 -3
- package/dist/experimental/agent-core.d.ts +3 -3
- package/dist/experimental/agent-core.js +1 -1
- package/package.json +2 -2
package/README.md
CHANGED
|
@@ -19,6 +19,16 @@ provider configuration, or environment values.
|
|
|
19
19
|
`toModelOutput({ toolCallId, input, output })`, following AI SDK's separation
|
|
20
20
|
between the result used by the application and the result shown to the model.
|
|
21
21
|
Both server and client tools retain output types in `InferAgentUITools`.
|
|
22
|
+
Both the default Agent Core engine and `aiSdk()` support client Tools without
|
|
23
|
+
`execute`. A client Tool waits for an `output-available` or `output-error` part
|
|
24
|
+
on the original assistant message and Tool call ID. Submit all pending client
|
|
25
|
+
results/approval decisions together through the existing continuation flow.
|
|
26
|
+
Core pauses server handlers in the same batch until those interactions complete,
|
|
27
|
+
following its existing whole-batch approval behavior.
|
|
28
|
+
Tool results resume the same Run while its interaction remains pending.
|
|
29
|
+
Waiting for client input releases execution: a new user message skips the old
|
|
30
|
+
interaction and starts a new Run in the same Session, without an explicit cancel.
|
|
31
|
+
Skipped calls become errors in the transcript/model context; late results are rejected.
|
|
22
32
|
|
|
23
33
|
`outputSchema` is a declaration/type hint. It never parses, coerces or validates
|
|
24
34
|
returned data, including client-supplied results. It is serialized in the Agent
|
package/dist/agent-core.wasm
CHANGED
|
Binary file
|
|
@@ -3,7 +3,7 @@ import { agentModelCallsSchema, } from "@gea-ai/contract/agent-usage";
|
|
|
3
3
|
import { AGENT_SESSION_COMPUTER_LEASE_MS, agentSessionComputerSchema, agentSessionComputerReferenceSchema, AGENT_SESSION_EXECUTION_LEASE_MS, AGENT_SESSION_EXECUTION_PROTOCOL_VERSION, MAX_AGENT_SESSION_RESPONSE_BYTES, MAX_AGENT_SESSION_EXECUTION_ATTEMPTS, MAX_AGENT_SESSION_EXECUTION_BODY_BYTES, MAX_AGENT_SESSION_PAGE_SIZE, agentSessionAttemptIdentitySchema, agentSessionExecutionRenewSchema, agentSessionExecutionCompleteSchema, agentSessionExecutionPauseSchema, agentSessionExecutionControlSchema, agentSessionExecutionMutationSchema, agentSessionExecutionStartSchema, } from "@gea-ai/contract/agent-session";
|
|
4
4
|
import { z } from "zod";
|
|
5
5
|
import { activeComputerOperation } from "./agent-session-computer.js";
|
|
6
|
-
import { isAgentSessionInboxBusy, scheduleAgentSessionInbox, getAgentSessionRunState, handleAgentSessionInboxRequest, hasAgentSessionInbox, } from "./agent-session-inbox.js";
|
|
6
|
+
import { isAgentSessionInboxBusy, scheduleAgentSessionInbox, getAgentSessionRunState, handleAgentSessionInboxRequest, hasAgentSessionInbox, skipAgentSessionInputWait, } from "./agent-session-inbox.js";
|
|
7
7
|
// Managed execution owns its own durable state; PostgreSQL copies the live stream.
|
|
8
8
|
const prefix = "execution-v2:";
|
|
9
9
|
const key = (name) => `${prefix}${name}`;
|
|
@@ -11,6 +11,8 @@ const sequenceKey = (name, sequence) => key(`${name}:${sequence.toString().padSt
|
|
|
11
11
|
const runEventRangeSchema = z.strictObject({
|
|
12
12
|
start: z.number().int().nonnegative(),
|
|
13
13
|
end: z.number().int().nonnegative().nullable(),
|
|
14
|
+
messageId: z.string().optional(),
|
|
15
|
+
messageStart: z.number().int().nonnegative().optional(),
|
|
14
16
|
});
|
|
15
17
|
class SessionProtocolError extends Error {
|
|
16
18
|
reason;
|
|
@@ -100,6 +102,21 @@ async function writeValues(tx, input) {
|
|
|
100
102
|
await tx.put(key("message-sequence"), messageSequence);
|
|
101
103
|
let eventSequence = (await tx.get(key("event-sequence"))) ?? 0;
|
|
102
104
|
for (const event of input.events) {
|
|
105
|
+
if (event.type === "start" &&
|
|
106
|
+
typeof event.messageId === "string" &&
|
|
107
|
+
input.runId) {
|
|
108
|
+
const rangeKey = key(`run-events:${encodeURIComponent(input.runId)}`);
|
|
109
|
+
const stored = await tx.get(rangeKey);
|
|
110
|
+
if (stored !== undefined) {
|
|
111
|
+
const range = runEventRangeSchema.parse(stored);
|
|
112
|
+
if (range.messageId !== event.messageId)
|
|
113
|
+
await tx.put(rangeKey, {
|
|
114
|
+
...range,
|
|
115
|
+
messageId: event.messageId,
|
|
116
|
+
messageStart: eventSequence,
|
|
117
|
+
});
|
|
118
|
+
}
|
|
119
|
+
}
|
|
103
120
|
const entry = { event, sequence: ++eventSequence };
|
|
104
121
|
await putSessionPayload(tx, sequenceKey("event", entry.sequence), entry);
|
|
105
122
|
}
|
|
@@ -449,6 +466,14 @@ export async function handleAgentSessionExecutionRequest(request, storage) {
|
|
|
449
466
|
return { duplicate: false, outcome };
|
|
450
467
|
}));
|
|
451
468
|
}
|
|
469
|
+
if (request.method === "POST" && url.pathname === "/v2/turns/skip-input") {
|
|
470
|
+
const input = z
|
|
471
|
+
.strictObject({ runId: z.uuid() })
|
|
472
|
+
.parse(await readBody(request));
|
|
473
|
+
return json(await storage.transaction(async (tx) => ({
|
|
474
|
+
requested: await skipAgentSessionInputWait(tx, input.runId),
|
|
475
|
+
})));
|
|
476
|
+
}
|
|
452
477
|
if (request.method === "POST" && url.pathname === "/v2/turns/cancel") {
|
|
453
478
|
const input = z
|
|
454
479
|
.object({ runId: agentSessionAttemptIdentitySchema.shape.runId })
|
|
@@ -497,6 +522,14 @@ export async function handleAgentSessionExecutionRequest(request, storage) {
|
|
|
497
522
|
return { duplicate: false, revision, turn };
|
|
498
523
|
}));
|
|
499
524
|
}
|
|
525
|
+
const messageMatch = /^\/v2\/messages\/([^/]+)$/u.exec(url.pathname);
|
|
526
|
+
if (request.method === "GET" && messageMatch) {
|
|
527
|
+
const id = decodeURIComponent(messageMatch[1]);
|
|
528
|
+
const sequence = await storage.get(key(`message-id:${encodeURIComponent(id)}`));
|
|
529
|
+
if (sequence === undefined)
|
|
530
|
+
throw new SessionProtocolError("message_not_found", 404);
|
|
531
|
+
return json(await storage.transaction((tx) => getSessionPayload(tx, sequenceKey("message", sequence))));
|
|
532
|
+
}
|
|
500
533
|
if (request.method === "GET" && url.pathname === "/v2/model-context")
|
|
501
534
|
return json({
|
|
502
535
|
context: (await storage.transaction((tx) => getSessionPayload(tx, key("model-context")))) ?? null,
|
|
@@ -520,7 +553,10 @@ export async function handleAgentSessionExecutionRequest(request, storage) {
|
|
|
520
553
|
const end = range.end ?? (await tx.get(key("event-sequence"))) ?? 0;
|
|
521
554
|
if (after > end || (after !== 0 && after < range.start))
|
|
522
555
|
throw new SessionProtocolError("invalid_cursor", 400);
|
|
523
|
-
const start = Math.max(after,
|
|
556
|
+
const start = Math.max(after, !url.searchParams.has("after") &&
|
|
557
|
+
url.searchParams.get("latestMessage") === "true"
|
|
558
|
+
? (range.messageStart ?? range.start)
|
|
559
|
+
: range.start);
|
|
524
560
|
const events = await entries(tx, "event", start, Math.min(limit, end - start));
|
|
525
561
|
const nextCursor = events.at(-1)?.sequence ?? start;
|
|
526
562
|
const receipt = await tx.get(key(`outcome:${encodeURIComponent(runId)}`));
|
|
@@ -1,5 +1,7 @@
|
|
|
1
1
|
import { type AgentChildRun } from "@gea-ai/contract/agent-worker-tools";
|
|
2
2
|
import type { AgentSessionStorage, AgentSessionTransactionStorage } from "./agent-session";
|
|
3
|
+
/** Atomically supersede only an idle input wait, never a running invocation. */
|
|
4
|
+
export declare function skipAgentSessionInputWait(tx: AgentSessionTransactionStorage, runId: string): Promise<boolean>;
|
|
3
5
|
export declare function hasAgentSessionInbox(tx: AgentSessionTransactionStorage): Promise<boolean>;
|
|
4
6
|
/** Read the logical Run independently of the currently leased invocation. */
|
|
5
7
|
export declare function getAgentSessionRunState(tx: AgentSessionTransactionStorage, runId: string): Promise<{
|
|
@@ -7,6 +7,7 @@ import { agentChildRunSchema, } from "@gea-ai/contract/agent-worker-tools";
|
|
|
7
7
|
import { combineTraceContexts, readTraceContext } from "./trace-context.js";
|
|
8
8
|
import { traceContextSchema } from "@gea-ai/contract/traces";
|
|
9
9
|
import { activeComputerOperation } from "./agent-session-computer.js";
|
|
10
|
+
import { getSessionPayload, putSessionPayload } from "./agent-session-payload.js";
|
|
10
11
|
const stateKey = "inbox:v1";
|
|
11
12
|
const waitSchema = agentRunWaitInputSchema.omit({ timeoutMs: true }).extend({
|
|
12
13
|
turnId: z.uuid(),
|
|
@@ -39,6 +40,82 @@ const stateSchema = z.strictObject({
|
|
|
39
40
|
terminalQueue: z.array(terminalSchema).max(8).default([]),
|
|
40
41
|
});
|
|
41
42
|
const executionInputSchema = z.strictObject({ executionId: z.uuid() });
|
|
43
|
+
async function skipInputWait(tx, state, runs) {
|
|
44
|
+
const pending = state.inputWait;
|
|
45
|
+
if (!pending || !state.transport)
|
|
46
|
+
return false;
|
|
47
|
+
// Both journals retain the same assistant ID. Seal visible pending parts as
|
|
48
|
+
// well as the Run so a late client result cannot resurrect the interaction.
|
|
49
|
+
for (const prefix of ["", "execution-v2:"]) {
|
|
50
|
+
const sequence = await tx.get(`${prefix}message-id:${encodeURIComponent(pending.messageId)}`);
|
|
51
|
+
if (sequence === undefined)
|
|
52
|
+
continue;
|
|
53
|
+
const key = `${prefix}message:${String(sequence).padStart(16, "0")}`;
|
|
54
|
+
const stored = await getSessionPayload(tx, key);
|
|
55
|
+
const envelope = prefix
|
|
56
|
+
? z
|
|
57
|
+
.object({ message: agentApplicationAssistantMessageSchema })
|
|
58
|
+
.passthrough()
|
|
59
|
+
.parse(stored)
|
|
60
|
+
: null;
|
|
61
|
+
const message = envelope?.message ?? agentApplicationAssistantMessageSchema.parse(stored);
|
|
62
|
+
const skipped = {
|
|
63
|
+
...message,
|
|
64
|
+
parts: message.parts.map((part) => (part.type === "dynamic-tool" || part.type.startsWith("tool-")) &&
|
|
65
|
+
[
|
|
66
|
+
"input-streaming",
|
|
67
|
+
"input-available",
|
|
68
|
+
"approval-requested",
|
|
69
|
+
"approval-responded",
|
|
70
|
+
].includes(String(part.state))
|
|
71
|
+
? {
|
|
72
|
+
...part,
|
|
73
|
+
state: "output-error",
|
|
74
|
+
errorText: "Tool interaction was skipped by a new user message; no result was provided.",
|
|
75
|
+
}
|
|
76
|
+
: part),
|
|
77
|
+
};
|
|
78
|
+
await putSessionPayload(tx, key, envelope ? { ...envelope, message: skipped } : skipped);
|
|
79
|
+
}
|
|
80
|
+
const terminal = {
|
|
81
|
+
execution: {
|
|
82
|
+
id: pending.turnId,
|
|
83
|
+
turnId: pending.turnId,
|
|
84
|
+
messages: [],
|
|
85
|
+
waitResult: null,
|
|
86
|
+
},
|
|
87
|
+
outcome: {
|
|
88
|
+
status: "interrupted",
|
|
89
|
+
text: "Pending tool interaction skipped by a new user message.",
|
|
90
|
+
},
|
|
91
|
+
transport: state.transport,
|
|
92
|
+
};
|
|
93
|
+
state.inputWait = null;
|
|
94
|
+
if (runs.some((run) => !run.outcome)) {
|
|
95
|
+
state.stopped = true;
|
|
96
|
+
for (const run of runs)
|
|
97
|
+
if (!run.outcome)
|
|
98
|
+
run.cancelRequested = true;
|
|
99
|
+
await tx.put("child-runs", { runs });
|
|
100
|
+
state.draining = terminal;
|
|
101
|
+
}
|
|
102
|
+
else if (state.terminal)
|
|
103
|
+
state.terminalQueue.push(terminal);
|
|
104
|
+
else
|
|
105
|
+
state.terminal = terminal;
|
|
106
|
+
await save(tx, state, runs);
|
|
107
|
+
return true;
|
|
108
|
+
}
|
|
109
|
+
/** Atomically supersede only an idle input wait, never a running invocation. */
|
|
110
|
+
export async function skipAgentSessionInputWait(tx, runId) {
|
|
111
|
+
const state = await readState(tx);
|
|
112
|
+
if (!state ||
|
|
113
|
+
state.execution ||
|
|
114
|
+
state.inputWait?.turnId !== runId ||
|
|
115
|
+
state.terminalQueue.length >= 8)
|
|
116
|
+
return false;
|
|
117
|
+
return skipInputWait(tx, state, await readChildRuns(tx));
|
|
118
|
+
}
|
|
42
119
|
// A turn admits a bounded batch. Messages beyond it retain their original order.
|
|
43
120
|
function takeMessages(state, steeringOnly) {
|
|
44
121
|
const messages = [];
|
|
@@ -219,7 +296,9 @@ async function save(tx, state, runs) {
|
|
|
219
296
|
});
|
|
220
297
|
const ready = state.wait
|
|
221
298
|
? waitResult(state, runs) !== null
|
|
222
|
-
:
|
|
299
|
+
: state.inputWait
|
|
300
|
+
? state.messages.some((message) => message.sender.type === "user")
|
|
301
|
+
: state.messages.length > 0;
|
|
223
302
|
const cancelling = runs.some((run) => !run.outcome && run.cancelRequested);
|
|
224
303
|
state.pendingStarts = state.pendingStarts.filter((id) => runs.some((run) => run.id === id && !run.outcome && !run.cancelRequested));
|
|
225
304
|
const storedTurn = state.execution
|
|
@@ -352,14 +431,20 @@ export async function handleAgentSessionInboxRequest(request, storage) {
|
|
|
352
431
|
state.terminalQueue.length >= 8 ||
|
|
353
432
|
(state.wait && input.message?.mode !== "steering") ||
|
|
354
433
|
(state.inputWait &&
|
|
355
|
-
input.continuation
|
|
434
|
+
input.continuation &&
|
|
435
|
+
input.continuation.id !== state.inputWait.messageId))
|
|
356
436
|
return Response.json({ error: "session_busy" }, { status: 409 });
|
|
357
|
-
state.transport = input.transport;
|
|
358
437
|
const previous = input.message
|
|
359
438
|
? await tx.get(`inbox:message:${input.message.id}`)
|
|
360
439
|
: undefined;
|
|
361
440
|
if (previous !== undefined)
|
|
362
441
|
return Response.json({ error: "message_already_accepted" }, { status: 409 });
|
|
442
|
+
if (input.message && state.inputWait) {
|
|
443
|
+
await skipInputWait(tx, state, runs);
|
|
444
|
+
if (state.draining)
|
|
445
|
+
return Response.json({ error: "session_busy" }, { status: 409 });
|
|
446
|
+
}
|
|
447
|
+
state.transport = input.transport;
|
|
363
448
|
let resumedWait = state.resumeWait;
|
|
364
449
|
if (state.wait && input.message) {
|
|
365
450
|
state.messages.push(input.message);
|
|
@@ -458,6 +543,12 @@ export async function handleAgentSessionInboxRequest(request, storage) {
|
|
|
458
543
|
await save(tx, state, runs);
|
|
459
544
|
return Response.json({ execution: null });
|
|
460
545
|
}
|
|
546
|
+
if (state.inputWait &&
|
|
547
|
+
state.messages.some((message) => message.sender.type === "user") &&
|
|
548
|
+
!state.execution &&
|
|
549
|
+
!state.draining &&
|
|
550
|
+
state.terminalQueue.length < 8)
|
|
551
|
+
await skipInputWait(tx, state, runs);
|
|
461
552
|
if (state.execution ||
|
|
462
553
|
state.terminal ||
|
|
463
554
|
state.draining ||
|
|
@@ -468,6 +559,7 @@ export async function handleAgentSessionInboxRequest(request, storage) {
|
|
|
468
559
|
return Response.json({ execution: null });
|
|
469
560
|
const messages = takeMessages(state, state.wait !== null);
|
|
470
561
|
const joining = state.wait?.toolCallId === null;
|
|
562
|
+
state.stopped = false;
|
|
471
563
|
state.execution = {
|
|
472
564
|
id: crypto.randomUUID(),
|
|
473
565
|
turnId: state.wait?.turnId ?? crypto.randomUUID(),
|
|
@@ -4,9 +4,10 @@ import { UI_MESSAGE_STREAM_HEADERS } from "ai";
|
|
|
4
4
|
export async function streamManagedAgentSessionRun(request, session) {
|
|
5
5
|
const url = new URL(request.url);
|
|
6
6
|
url.pathname = "/v2/run-events";
|
|
7
|
-
//
|
|
7
|
+
// Replay the active response. User steering starts a new response within the same Run.
|
|
8
8
|
url.searchParams.delete("after");
|
|
9
9
|
url.searchParams.delete("limit");
|
|
10
|
+
url.searchParams.set("latestMessage", "true");
|
|
10
11
|
const abort = new AbortController();
|
|
11
12
|
let cancelled = false;
|
|
12
13
|
const onDisconnect = () => abort.abort(request.signal.reason);
|
|
@@ -34,14 +35,28 @@ export async function streamManagedAgentSessionRun(request, session) {
|
|
|
34
35
|
return new Response(null, { status: 204 });
|
|
35
36
|
}
|
|
36
37
|
const encoder = new TextEncoder();
|
|
38
|
+
let messageId;
|
|
37
39
|
return new Response(new ReadableStream({
|
|
38
40
|
async pull(controller) {
|
|
39
41
|
try {
|
|
40
42
|
while (!abort.signal.aborted) {
|
|
41
43
|
if (page.events.length) {
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
.
|
|
44
|
+
let text = "";
|
|
45
|
+
for (const { event } of page.events) {
|
|
46
|
+
if (event.type === "start" &&
|
|
47
|
+
typeof event.messageId === "string") {
|
|
48
|
+
if (messageId && messageId !== event.messageId) {
|
|
49
|
+
// The old subscriber must never feed a second response into useChat's reducer.
|
|
50
|
+
controller.enqueue(encoder.encode(text + "data: [DONE]\n\n"));
|
|
51
|
+
controller.close();
|
|
52
|
+
dispose();
|
|
53
|
+
return;
|
|
54
|
+
}
|
|
55
|
+
messageId = event.messageId;
|
|
56
|
+
}
|
|
57
|
+
text += `data: ${JSON.stringify(event)}\n\n`;
|
|
58
|
+
}
|
|
59
|
+
controller.enqueue(encoder.encode(text));
|
|
45
60
|
page = { ...page, events: [] };
|
|
46
61
|
return;
|
|
47
62
|
}
|
package/dist/agent-worker.js
CHANGED
|
@@ -29,7 +29,7 @@ import { toAgentHttpFile, } from "@gea-ai/contract/agent-http-api";
|
|
|
29
29
|
import { handleHostedAgentHttp } from "./agent-http.js";
|
|
30
30
|
import { createWorkerAgentTool, createWorkerRunCancelTool } from "./agent-call.js";
|
|
31
31
|
import { AgentSessionRunner, resumeSessionWait } from "./agent-session-runner.js";
|
|
32
|
-
import { agentRunWaitInputSchema, agentSessionInboxExecutionSchema, sessionExecutionMessage, } from "@gea-ai/contract/agent-session-inbox";
|
|
32
|
+
import { agentRunWaitInputSchema, toAgentRunWaitOutput, agentSessionInboxExecutionSchema, sessionExecutionMessage, } from "@gea-ai/contract/agent-session-inbox";
|
|
33
33
|
import { agentWorkerInvocationIdentitySchema } from "@gea-ai/contract/agent-worker-tools";
|
|
34
34
|
import { MAX_AGENT_ARTIFACT_IMAGES, MAX_AGENT_ARTIFACT_IMAGE_BYTES, studioArtifactInputFileSchema, studioArtifactCreateUploadSchema, studioArtifactListSchema, } from "@gea-ai/contract/studio-artifacts";
|
|
35
35
|
import { parsePatch } from "./vendor/codex-patch/parser.js";
|
|
@@ -42,7 +42,7 @@ import { AGENT_APPLICATION_PREFIX, buildAgentApplicationPath, agentApplicationAs
|
|
|
42
42
|
import { AGENT_APPLICATION_CHAT_ID_HEADER, AGENT_APPLICATION_MESSAGE_ID_HEADER, AGENT_APPLICATION_RUN_ID_HEADER, agentApplicationKeySchema, agentApplicationRunRequestSchema, } from "@gea-ai/contract";
|
|
43
43
|
import { AGENT_CHANNEL_EXECUTION_TIMEOUT_MS, agentChannelInvocationContextSchema, agentChannelAddressSchema, agentChannelHttpMessageRequestSchema, agentChannelOpaqueIdSchema, agentChannelProjectTerminalCommandSchema, agentChannelRunTerminalCallbackSchema, agentChannelStartRunCommandSchema, } from "@gea-ai/contract/agent-channels";
|
|
44
44
|
import { agentSessionClaimChannelRunResultSchema, agentSessionCompleteChannelRunResultSchema, agentSessionGetModelContextResultSchema, agentSessionAttemptIdentitySchema, agentSessionExecutionStartResultSchema, agentSessionExecutionMutationResultSchema, agentSessionPutModelContextResultSchema, } from "@gea-ai/contract/agent-session";
|
|
45
|
-
import { InvalidResponseDataError, convertToModelMessages, isToolUIPart, createUIMessageStreamResponse, createUIMessageStream, asSchema, jsonSchema, stepCountIs, streamText, tool, } from "ai";
|
|
45
|
+
import { InvalidResponseDataError, convertToModelMessages, isToolUIPart, validateUIMessages, createUIMessageStreamResponse, createUIMessageStream, asSchema, jsonSchema, stepCountIs, streamText, tool, } from "ai";
|
|
46
46
|
import { agentWorkerConnectionDeleteOutputSchema, agentWorkerConnectionGetOutputSchema, agentWorkerConnectionSetOutputSchema, agentWorkerToolInitializationSchema, MAX_AGENT_WORKER_TOOLS, } from "@gea-ai/contract/agent-worker-tools";
|
|
47
47
|
import { agentToolIdentitySchema, } from "@gea-ai/contract/agent-packages";
|
|
48
48
|
import { z } from "zod";
|
|
@@ -475,7 +475,8 @@ async function runPreparedAgent(runtime, agentKey, request, environment, identif
|
|
|
475
475
|
const headers = new Headers(response.headers);
|
|
476
476
|
headers.set(AGENT_APPLICATION_CHAT_ID_HEADER, chatId);
|
|
477
477
|
headers.set(AGENT_APPLICATION_RUN_ID_HEADER, runId);
|
|
478
|
-
headers.set(AGENT_APPLICATION_MESSAGE_ID_HEADER,
|
|
478
|
+
headers.set(AGENT_APPLICATION_MESSAGE_ID_HEADER, response.headers.get(AGENT_APPLICATION_MESSAGE_ID_HEADER) ??
|
|
479
|
+
responseMessageId);
|
|
479
480
|
return new Response(response.body, {
|
|
480
481
|
headers,
|
|
481
482
|
status: response.status,
|
|
@@ -3404,6 +3405,14 @@ async function executeAgentWorkerRequest(input, waitUntil, ownComputer, ownSessi
|
|
|
3404
3405
|
path: sessionExecution ? "/v2/model-context" : "/v1/model-context",
|
|
3405
3406
|
schema: agentSessionGetModelContextResultSchema,
|
|
3406
3407
|
});
|
|
3408
|
+
if (submittedUser &&
|
|
3409
|
+
storedContext.context &&
|
|
3410
|
+
storedContext.context.runId !== payload.runId) {
|
|
3411
|
+
// A new user Run supersedes the previous idle tool interaction. Close its
|
|
3412
|
+
// missing results before appending user input; never execute the skipped batch.
|
|
3413
|
+
storedContext.context.messages = closeInterruptedToolCalls(storedContext.context.messages.map(decodeAgentModelProxyValue), "Tool interaction was skipped by a new user message; no result was provided.").map(encodeAgentModelProxyValue);
|
|
3414
|
+
delete storedContext.context.metadata.agentCore;
|
|
3415
|
+
}
|
|
3407
3416
|
// Reuse durable conversions when UI history is replayed, so file conversion
|
|
3408
3417
|
// does not upload another Artifact on every turn or approval continuation.
|
|
3409
3418
|
const retainedToolCalls = new Set();
|
|
@@ -3420,6 +3429,66 @@ async function executeAgentWorkerRequest(input, waitUntil, ownComputer, ownSessi
|
|
|
3420
3429
|
modelOutputs.set(part.toolCallId, part.output);
|
|
3421
3430
|
}
|
|
3422
3431
|
}
|
|
3432
|
+
const suspendedMessageId = storedContext.context?.runId === payload.runId
|
|
3433
|
+
? storedContext.context.metadata.suspendedMessageId
|
|
3434
|
+
: undefined;
|
|
3435
|
+
const resumesResponse = Boolean(suspendedMessageId &&
|
|
3436
|
+
input.inboxExecution &&
|
|
3437
|
+
!input.inboxExecution.messages.some((message) => message.sender.type === "user"));
|
|
3438
|
+
let responseBaseline;
|
|
3439
|
+
let interruptedResponse;
|
|
3440
|
+
let interruptedWait;
|
|
3441
|
+
if (suspendedMessageId && input.inboxExecution) {
|
|
3442
|
+
const saved = await requestSessionJson({
|
|
3443
|
+
chatId: payload.chatId,
|
|
3444
|
+
method: "GET",
|
|
3445
|
+
namespace: input.sessionNamespace,
|
|
3446
|
+
path: `${sessionExecution ? "/v2" : "/v1"}/messages/${encodeURIComponent(suspendedMessageId)}`,
|
|
3447
|
+
schema: z.object({ message: z.unknown() }),
|
|
3448
|
+
});
|
|
3449
|
+
const [message] = await validateUIMessages({
|
|
3450
|
+
messages: [saved.message],
|
|
3451
|
+
});
|
|
3452
|
+
if (!message || message.role !== "assistant")
|
|
3453
|
+
throw new Error("Suspended response is not an assistant message.");
|
|
3454
|
+
// Invocation completion is not message completion. Do not retain its waiting metadata.
|
|
3455
|
+
if (message.metadata) {
|
|
3456
|
+
delete message.metadata.sessionStatus;
|
|
3457
|
+
delete message.metadata.finishedAt;
|
|
3458
|
+
delete message.metadata.finishReason;
|
|
3459
|
+
}
|
|
3460
|
+
const result = input.inboxExecution.waitResult;
|
|
3461
|
+
if (result) {
|
|
3462
|
+
const part = message.parts.find((part) => isToolUIPart(part) && part.toolCallId === result.toolCallId);
|
|
3463
|
+
if (!part || !isToolUIPart(part))
|
|
3464
|
+
throw new Error("Suspended response is missing its wait tool.");
|
|
3465
|
+
if (resumesResponse) {
|
|
3466
|
+
Object.assign(part, {
|
|
3467
|
+
state: "output-available",
|
|
3468
|
+
output: toAgentRunWaitOutput(result),
|
|
3469
|
+
});
|
|
3470
|
+
}
|
|
3471
|
+
else {
|
|
3472
|
+
const errorText = "Wait interrupted by a new user message.";
|
|
3473
|
+
Object.assign(part, { state: "output-error", errorText });
|
|
3474
|
+
interruptedWait = {
|
|
3475
|
+
type: "tool-output-error",
|
|
3476
|
+
toolCallId: result.toolCallId,
|
|
3477
|
+
errorText,
|
|
3478
|
+
};
|
|
3479
|
+
}
|
|
3480
|
+
}
|
|
3481
|
+
if (resumesResponse)
|
|
3482
|
+
responseBaseline = message;
|
|
3483
|
+
else {
|
|
3484
|
+
message.metadata = {
|
|
3485
|
+
...message.metadata,
|
|
3486
|
+
finishedAt: new Date(),
|
|
3487
|
+
finishReason: "other",
|
|
3488
|
+
};
|
|
3489
|
+
interruptedResponse = message;
|
|
3490
|
+
}
|
|
3491
|
+
}
|
|
3423
3492
|
const modelMessages = replaceInputFiles(await convertToModelMessages(payload.originalMessages.map((message) => storedContext.context && message.role === "assistant"
|
|
3424
3493
|
? {
|
|
3425
3494
|
...message,
|
|
@@ -3480,7 +3549,10 @@ async function executeAgentWorkerRequest(input, waitUntil, ownComputer, ownSessi
|
|
|
3480
3549
|
}
|
|
3481
3550
|
}
|
|
3482
3551
|
else if (input.inboxExecution &&
|
|
3483
|
-
storedContext.context?.runId === payload.runId
|
|
3552
|
+
storedContext.context?.runId === payload.runId &&
|
|
3553
|
+
// Core resumes its persisted tool batch and validates approvals separately.
|
|
3554
|
+
// Replaying UI approval parts here would overwrite that model projection.
|
|
3555
|
+
!(engine && storedContext.context.metadata.agentCore)) {
|
|
3484
3556
|
// The Run survives a join/steering continuation, but its newly accepted input is still new context.
|
|
3485
3557
|
context.messages = input.inboxExecution.continuation
|
|
3486
3558
|
? replaceLastAssistantInteraction({
|
|
@@ -3489,6 +3561,12 @@ async function executeAgentWorkerRequest(input, waitUntil, ownComputer, ownSessi
|
|
|
3489
3561
|
})
|
|
3490
3562
|
: [...context.messages, ...currentTurn];
|
|
3491
3563
|
}
|
|
3564
|
+
if (responseBaseline) {
|
|
3565
|
+
payload.responseMessageId = responseBaseline.id;
|
|
3566
|
+
// Model context was restored independently above; this baseline is only for UI assembly.
|
|
3567
|
+
payload.originalMessages = [responseBaseline];
|
|
3568
|
+
}
|
|
3569
|
+
delete context.metadata.suspendedMessageId;
|
|
3492
3570
|
if (submittedUser && startsNewTurn)
|
|
3493
3571
|
context.metadata.turnImages = inputFiles
|
|
3494
3572
|
.filter((file) => file.mediaType.startsWith("image/"))
|
|
@@ -3907,16 +3985,18 @@ async function executeAgentWorkerRequest(input, waitUntil, ownComputer, ownSessi
|
|
|
3907
3985
|
})),
|
|
3908
3986
|
];
|
|
3909
3987
|
if (inboxRunner?.suspendedToolCallId) {
|
|
3910
|
-
responseMessage.parts = responseMessage.parts.map((part) =>
|
|
3911
|
-
part
|
|
3912
|
-
|
|
3913
|
-
|
|
3914
|
-
|
|
3915
|
-
state: "input-available"
|
|
3916
|
-
input: "input" in part ? part.input : {},
|
|
3988
|
+
responseMessage.parts = responseMessage.parts.map((part) => {
|
|
3989
|
+
if (isToolUIPart(part) &&
|
|
3990
|
+
part.toolCallId === inboxRunner.suspendedToolCallId &&
|
|
3991
|
+
part.state === "output-available") {
|
|
3992
|
+
const { output: _output, approval: _approval, ...pending } = part;
|
|
3993
|
+
return { ...pending, state: "input-available" };
|
|
3917
3994
|
}
|
|
3918
|
-
|
|
3995
|
+
return part;
|
|
3996
|
+
});
|
|
3919
3997
|
}
|
|
3998
|
+
if (inboxRunner?.suspended)
|
|
3999
|
+
context.metadata.suspendedMessageId = responseMessage.id;
|
|
3920
4000
|
responseMessage.metadata = {
|
|
3921
4001
|
...responseMessage.metadata,
|
|
3922
4002
|
...(await contextFinished),
|
|
@@ -4006,13 +4086,16 @@ async function executeAgentWorkerRequest(input, waitUntil, ownComputer, ownSessi
|
|
|
4006
4086
|
const pending = context.metadata.agentCore;
|
|
4007
4087
|
let lastCoreFinishReason = pending ? "tool-calls" : "stop";
|
|
4008
4088
|
if (pending && currentTurn.some((message) => message.role === "user"))
|
|
4009
|
-
throw new Error("Resolve pending
|
|
4089
|
+
throw new Error("Resolve pending tool interactions before starting another turn.");
|
|
4010
4090
|
const messageId = pending?.messageId ?? payload.responseMessageId;
|
|
4011
|
-
const
|
|
4091
|
+
const pendingMessage = pending
|
|
4012
4092
|
? z
|
|
4013
4093
|
.object({ parts: z.array(z.unknown()) })
|
|
4014
4094
|
.parse(payload.originalMessages.find((message) => message.id === pending.messageId))
|
|
4015
|
-
|
|
4095
|
+
: undefined;
|
|
4096
|
+
const responses = pendingMessage
|
|
4097
|
+
? pendingMessage.parts
|
|
4098
|
+
.flatMap((part) => {
|
|
4016
4099
|
const parsed = z
|
|
4017
4100
|
.object({
|
|
4018
4101
|
state: z.literal("approval-responded"),
|
|
@@ -4026,6 +4109,49 @@ async function executeAgentWorkerRequest(input, waitUntil, ownComputer, ownSessi
|
|
|
4026
4109
|
// Keep unknown IDs so Rust still rejects foreign/duplicate responses.
|
|
4027
4110
|
.filter((response) => !pending.pendingStep.approvals.some((approval) => approval.id === response.id && approval.response != null))
|
|
4028
4111
|
: [];
|
|
4112
|
+
// Model conversion already applies the Tool's toModelOutput (including
|
|
4113
|
+
// managed file adoption); retain the raw client output for the UI stream.
|
|
4114
|
+
const modelResults = new Map(fromModelMessages((pending?.pendingStep.client_calls?.length
|
|
4115
|
+
? modelMessages
|
|
4116
|
+
: []).flatMap((message) => message.role === "tool"
|
|
4117
|
+
? [
|
|
4118
|
+
{
|
|
4119
|
+
...message,
|
|
4120
|
+
content: message.content.filter((part) => part.type === "tool-result"),
|
|
4121
|
+
},
|
|
4122
|
+
]
|
|
4123
|
+
: [])).flatMap((message) => message.content.flatMap((part) => part.type === "tool_result" ? [[part.call_id, part]] : [])));
|
|
4124
|
+
const clientResults = (pendingMessage?.parts ?? []).flatMap((part) => {
|
|
4125
|
+
const result = z
|
|
4126
|
+
.discriminatedUnion("state", [
|
|
4127
|
+
z.object({
|
|
4128
|
+
state: z.literal("output-available"),
|
|
4129
|
+
toolCallId: z.string(),
|
|
4130
|
+
output: z.json(),
|
|
4131
|
+
}),
|
|
4132
|
+
z.object({
|
|
4133
|
+
state: z.literal("output-error"),
|
|
4134
|
+
toolCallId: z.string(),
|
|
4135
|
+
errorText: z.string(),
|
|
4136
|
+
}),
|
|
4137
|
+
])
|
|
4138
|
+
.safeParse(part);
|
|
4139
|
+
if (!result.success ||
|
|
4140
|
+
!pending?.pendingStep.client_calls?.includes(result.data.toolCallId))
|
|
4141
|
+
return [];
|
|
4142
|
+
const value = result.data;
|
|
4143
|
+
const modelResult = modelResults.get(value.toolCallId);
|
|
4144
|
+
if (!modelResult)
|
|
4145
|
+
throw new Error(`Missing client Tool model output: ${value.toolCallId}`);
|
|
4146
|
+
return [
|
|
4147
|
+
{
|
|
4148
|
+
call_id: value.toolCallId,
|
|
4149
|
+
output: value.state === "output-error" ? value.errorText : value.output,
|
|
4150
|
+
is_error: value.state === "output-error",
|
|
4151
|
+
model_output: modelResult.model_output,
|
|
4152
|
+
},
|
|
4153
|
+
];
|
|
4154
|
+
});
|
|
4029
4155
|
const coreTools = await createCoreWorkerTools({
|
|
4030
4156
|
tools,
|
|
4031
4157
|
approval: toolApproval,
|
|
@@ -4084,7 +4210,9 @@ async function executeAgentWorkerRequest(input, waitUntil, ownComputer, ownSessi
|
|
|
4084
4210
|
}
|
|
4085
4211
|
: undefined,
|
|
4086
4212
|
pending_step: pending?.pendingStep,
|
|
4087
|
-
}), pending
|
|
4213
|
+
}), pending
|
|
4214
|
+
? { type: "tool_response", responses, results: clientResults }
|
|
4215
|
+
: { type: "start" }, {
|
|
4088
4216
|
...coreTools,
|
|
4089
4217
|
onError: engine.onError,
|
|
4090
4218
|
fetch: (request, init) => coreConnection.fetch(request, init),
|
|
@@ -4188,7 +4316,8 @@ async function executeAgentWorkerRequest(input, waitUntil, ownComputer, ownSessi
|
|
|
4188
4316
|
? "stop"
|
|
4189
4317
|
: outcome.reason === "stopped"
|
|
4190
4318
|
? lastCoreFinishReason
|
|
4191
|
-
: outcome.reason === "awaiting_approval"
|
|
4319
|
+
: outcome.reason === "awaiting_approval" ||
|
|
4320
|
+
outcome.reason === "awaiting_tool_results"
|
|
4192
4321
|
? "tool-calls"
|
|
4193
4322
|
: "length",
|
|
4194
4323
|
});
|
|
@@ -4429,6 +4558,46 @@ async function executeAgentWorkerRequest(input, waitUntil, ownComputer, ownSessi
|
|
|
4429
4558
|
}
|
|
4430
4559
|
const durableStream = stream.pipeThrough(new TransformStream({
|
|
4431
4560
|
async transform(chunk, controller) {
|
|
4561
|
+
if (chunk.type === "start" && interruptedResponse) {
|
|
4562
|
+
const events = [
|
|
4563
|
+
...(interruptedWait ? [interruptedWait] : []),
|
|
4564
|
+
{ type: "finish", finishReason: "other" },
|
|
4565
|
+
];
|
|
4566
|
+
if (sessionExecution)
|
|
4567
|
+
await mutateSession({
|
|
4568
|
+
messages: [{ ...interruptedResponse }],
|
|
4569
|
+
events,
|
|
4570
|
+
});
|
|
4571
|
+
else {
|
|
4572
|
+
await appendSessionValue({
|
|
4573
|
+
body: { message: interruptedResponse, runId: payload.runId },
|
|
4574
|
+
chatId: payload.chatId,
|
|
4575
|
+
namespace: input.sessionNamespace,
|
|
4576
|
+
path: "/v1/messages",
|
|
4577
|
+
});
|
|
4578
|
+
for (const event of events)
|
|
4579
|
+
await appendStreamEvent(event);
|
|
4580
|
+
}
|
|
4581
|
+
}
|
|
4582
|
+
if (chunk.type === "start" && responseBaseline) {
|
|
4583
|
+
// Each internal invocation still has its own start/finish for accounting.
|
|
4584
|
+
// The public journal represents one response across those invocations.
|
|
4585
|
+
controller.enqueue(chunk);
|
|
4586
|
+
const result = input.inboxExecution?.waitResult;
|
|
4587
|
+
if (result) {
|
|
4588
|
+
const output = {
|
|
4589
|
+
type: "tool-output-available",
|
|
4590
|
+
toolCallId: result.toolCallId,
|
|
4591
|
+
output: toAgentRunWaitOutput(result),
|
|
4592
|
+
};
|
|
4593
|
+
if (sessionExecution)
|
|
4594
|
+
await mutateSession({ events: [output] });
|
|
4595
|
+
else
|
|
4596
|
+
await appendStreamEvent(output);
|
|
4597
|
+
controller.enqueue(output);
|
|
4598
|
+
}
|
|
4599
|
+
return;
|
|
4600
|
+
}
|
|
4432
4601
|
if (chunk.type === "tool-output-available" &&
|
|
4433
4602
|
chunk.toolCallId === inboxRunner?.suspendedToolCallId)
|
|
4434
4603
|
return;
|
|
@@ -4455,6 +4624,10 @@ async function executeAgentWorkerRequest(input, waitUntil, ownComputer, ownSessi
|
|
|
4455
4624
|
finishReason: metadata.finishReason,
|
|
4456
4625
|
messageMetadata: metadata,
|
|
4457
4626
|
};
|
|
4627
|
+
if (metadata.sessionStatus === "waiting") {
|
|
4628
|
+
controller.enqueue(chunk);
|
|
4629
|
+
return;
|
|
4630
|
+
}
|
|
4458
4631
|
}
|
|
4459
4632
|
if (sessionExecution) {
|
|
4460
4633
|
await mutateSession({ events: [chunk] });
|
|
@@ -4483,7 +4656,17 @@ async function executeAgentWorkerRequest(input, waitUntil, ownComputer, ownSessi
|
|
|
4483
4656
|
reader.releaseLock();
|
|
4484
4657
|
}
|
|
4485
4658
|
})());
|
|
4486
|
-
return createUIMessageStreamResponse({
|
|
4659
|
+
return createUIMessageStreamResponse({
|
|
4660
|
+
stream: response,
|
|
4661
|
+
headers: {
|
|
4662
|
+
[AGENT_APPLICATION_MESSAGE_ID_HEADER]: payload.responseMessageId,
|
|
4663
|
+
},
|
|
4664
|
+
});
|
|
4487
4665
|
}
|
|
4488
|
-
return createUIMessageStreamResponse({
|
|
4666
|
+
return createUIMessageStreamResponse({
|
|
4667
|
+
stream: durableStream,
|
|
4668
|
+
headers: {
|
|
4669
|
+
[AGENT_APPLICATION_MESSAGE_ID_HEADER]: payload.responseMessageId,
|
|
4670
|
+
},
|
|
4671
|
+
});
|
|
4489
4672
|
}
|
|
@@ -19,7 +19,7 @@ export declare function toFileData(data: AgentCoreFileData): {
|
|
|
19
19
|
export declare function toCoreModelOutput(output: ToolResultOutput): AgentCoreModelOutput;
|
|
20
20
|
export declare function fromModelMessages(messages: ModelMessage[]): AgentCoreMessage[];
|
|
21
21
|
/** Close only the interrupted trailing batch, after host operations have settled. */
|
|
22
|
-
export declare function closeInterruptedToolCalls(messages: ModelMessage[]): ModelMessage[];
|
|
22
|
+
export declare function closeInterruptedToolCalls(messages: ModelMessage[], reason?: string): ModelMessage[];
|
|
23
23
|
export declare function toToolModelOutput(part: Extract<AgentCoreMessage["content"][number], {
|
|
24
24
|
type: "tool_result";
|
|
25
25
|
}>): LanguageModelV4ToolResultOutput;
|
|
@@ -137,7 +137,7 @@ export function fromModelMessages(messages) {
|
|
|
137
137
|
}));
|
|
138
138
|
}
|
|
139
139
|
/** Close only the interrupted trailing batch, after host operations have settled. */
|
|
140
|
-
export function closeInterruptedToolCalls(messages) {
|
|
140
|
+
export function closeInterruptedToolCalls(messages, reason = "Agent execution was interrupted before a tool result was recorded. The operation may have taken effect; its outcome is unknown.") {
|
|
141
141
|
let index = messages.length - 1;
|
|
142
142
|
while (index >= 0 && messages[index]?.role === "tool")
|
|
143
143
|
index--;
|
|
@@ -165,7 +165,7 @@ export function closeInterruptedToolCalls(messages) {
|
|
|
165
165
|
toolName,
|
|
166
166
|
output: {
|
|
167
167
|
type: "error-text",
|
|
168
|
-
value:
|
|
168
|
+
value: reason,
|
|
169
169
|
},
|
|
170
170
|
})),
|
|
171
171
|
},
|
|
@@ -7,7 +7,7 @@ export function createAgentCoreTools(options) {
|
|
|
7
7
|
return {
|
|
8
8
|
async decideTool(call, signal) {
|
|
9
9
|
const action = options.actions[call.name];
|
|
10
|
-
if (!action
|
|
10
|
+
if (!action)
|
|
11
11
|
return { type: "denied", reason: `Tool is unavailable: ${call.name}` };
|
|
12
12
|
const parsed = await action.inputSchema.safeParseAsync(call.input);
|
|
13
13
|
if (!parsed.success)
|
|
@@ -35,8 +35,9 @@ export function createAgentCoreTools(options) {
|
|
|
35
35
|
return {
|
|
36
36
|
type: "approval_required",
|
|
37
37
|
approval_id: options.approvalId?.(call) ?? crypto.randomUUID(),
|
|
38
|
+
...(!action.handler ? { client: true } : {}),
|
|
38
39
|
};
|
|
39
|
-
return { type: "approved" };
|
|
40
|
+
return { type: action.handler ? "approved" : "client" };
|
|
40
41
|
},
|
|
41
42
|
async tool(call, signal) {
|
|
42
43
|
const action = options.actions[call.name];
|
|
@@ -158,7 +158,7 @@ export async function* projectAgentCoreEvents(turn, options) {
|
|
|
158
158
|
break;
|
|
159
159
|
case "tool_ready":
|
|
160
160
|
calls.set(event.call.id, event.call);
|
|
161
|
-
if (resumed)
|
|
161
|
+
if (resumed && !event.client)
|
|
162
162
|
break;
|
|
163
163
|
part = {
|
|
164
164
|
type: "tool-call",
|
|
@@ -267,7 +267,8 @@ export async function* projectAgentCoreEvents(turn, options) {
|
|
|
267
267
|
: outcome.reason === "model_limit"
|
|
268
268
|
? "length"
|
|
269
269
|
: outcome.reason === "step_limit" ||
|
|
270
|
-
outcome.reason === "awaiting_approval"
|
|
270
|
+
outcome.reason === "awaiting_approval" ||
|
|
271
|
+
outcome.reason === "awaiting_tool_results"
|
|
271
272
|
? "tool-calls"
|
|
272
273
|
: "error",
|
|
273
274
|
messageMetadata,
|
|
@@ -37,7 +37,7 @@ export async function createCoreWorkerTools(options) {
|
|
|
37
37
|
async decideTool(call, signal) {
|
|
38
38
|
signal.throwIfAborted();
|
|
39
39
|
const tool = options.tools[call.name];
|
|
40
|
-
if (!tool
|
|
40
|
+
if (!tool)
|
|
41
41
|
return { type: "denied", reason: `Tool is unavailable: ${call.name}` };
|
|
42
42
|
const parsed = await safeValidateTypes({
|
|
43
43
|
value: call.input,
|
|
@@ -69,8 +69,12 @@ export async function createCoreWorkerTools(options) {
|
|
|
69
69
|
: "Tool execution denied",
|
|
70
70
|
};
|
|
71
71
|
if (type === "user-approval" || guardrailType === "user-approval")
|
|
72
|
-
return {
|
|
73
|
-
|
|
72
|
+
return {
|
|
73
|
+
type: "approval_required",
|
|
74
|
+
approval_id: crypto.randomUUID(),
|
|
75
|
+
...(!tool.execute ? { client: true } : {}),
|
|
76
|
+
};
|
|
77
|
+
return { type: tool.execute ? "approved" : "client" };
|
|
74
78
|
},
|
|
75
79
|
async tool(call, signal) {
|
|
76
80
|
const tool = options.tools[call.name];
|
|
@@ -26,7 +26,7 @@ export interface AgentCoreHost {
|
|
|
26
26
|
prepareStep?(step: AgentCorePrepareStep, signal: AbortSignal): Promise<AgentCoreMessage[] | null | void>;
|
|
27
27
|
finishStep?(step: AgentCoreFinishedStep, signal: AbortSignal): Promise<AgentCoreMessage[] | null | void>;
|
|
28
28
|
/** Core awaits this even after cancellation. Drain retained work and persist
|
|
29
|
-
* final context here;
|
|
29
|
+
* final context here; approval/client-result waits are pauses, not completed turns. */
|
|
30
30
|
finishRun?(outcome: AgentCoreOutcome, signal: AbortSignal): Promise<void>;
|
|
31
31
|
model?(request: AgentCoreModelRequest, signal: AbortSignal): AsyncIterable<AgentCoreModelPart>;
|
|
32
32
|
tool(call: AgentCoreToolCall, signal: AbortSignal): Promise<AgentCoreToolResult>;
|
|
@@ -37,13 +37,13 @@ export interface AgentCoreHost {
|
|
|
37
37
|
export declare function createAgentCore(module: WebAssembly.Module, config: AgentCoreConfig): {
|
|
38
38
|
run(message: AgentCoreInput, host: AgentCoreHost, emit: (event: AgentCoreEvent) => Promise<void>, signal: AbortSignal): Promise<{
|
|
39
39
|
output?: z.util.JSONType | undefined;
|
|
40
|
-
reason: "awaiting_approval" | "cancelled" | "completed" | "failed" | "model_limit" | "step_limit" | "stopped";
|
|
40
|
+
reason: "awaiting_approval" | "awaiting_tool_results" | "cancelled" | "completed" | "failed" | "model_limit" | "step_limit" | "stopped";
|
|
41
41
|
error: string | null;
|
|
42
42
|
details?: AgentCoreErrorDetails | undefined;
|
|
43
43
|
}>;
|
|
44
44
|
runOutcome(message: AgentCoreInput, host: AgentCoreHost, emit: (event: AgentCoreEvent) => Promise<void>, signal: AbortSignal): Promise<{
|
|
45
45
|
output?: z.util.JSONType | undefined;
|
|
46
|
-
reason: "awaiting_approval" | "cancelled" | "completed" | "failed" | "model_limit" | "step_limit" | "stopped";
|
|
46
|
+
reason: "awaiting_approval" | "awaiting_tool_results" | "cancelled" | "completed" | "failed" | "model_limit" | "step_limit" | "stopped";
|
|
47
47
|
error: string | null;
|
|
48
48
|
details?: AgentCoreErrorDetails | undefined;
|
|
49
49
|
}>;
|
|
@@ -351,7 +351,7 @@ export function createAgentCore(module, config) {
|
|
|
351
351
|
})),
|
|
352
352
|
},
|
|
353
353
|
}).exports);
|
|
354
|
-
if (exports.agent_abi_version() !==
|
|
354
|
+
if (exports.agent_abi_version() !== 14)
|
|
355
355
|
throw new Error("Unsupported Agent Core ABI.");
|
|
356
356
|
const run = jspi.promising(exports.agent_run);
|
|
357
357
|
return {
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@gea-ai/agent-sdk",
|
|
3
|
-
"version": "0.1.260920-alpha.
|
|
3
|
+
"version": "0.1.260920-alpha.2",
|
|
4
4
|
"private": false,
|
|
5
5
|
"homepage": "https://musegea.com/developers",
|
|
6
6
|
"license": "Apache-2.0",
|
|
@@ -104,7 +104,7 @@
|
|
|
104
104
|
"@ai-sdk/otel": "1.0.9",
|
|
105
105
|
"@ai-sdk/provider": "4.0.1",
|
|
106
106
|
"@ai-sdk/provider-utils": "5.0.2",
|
|
107
|
-
"@gea-ai/contract": "0.1.260920-alpha.
|
|
107
|
+
"@gea-ai/contract": "0.1.260920-alpha.2",
|
|
108
108
|
"@opentelemetry/api": "1.9.1",
|
|
109
109
|
"@opentelemetry/context-async-hooks": "2.10.0",
|
|
110
110
|
"@opentelemetry/core": "2.10.0",
|