@gea-ai/agent-sdk 0.1.260922-alpha.2 → 0.1.260922-alpha.3
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-session-execution.js +34 -5
- package/dist/agent-session-transport.d.ts +4 -0
- package/dist/agent-session-transport.js +9 -1
- package/dist/agent-stream-coalescing.d.ts +6 -0
- package/dist/agent-stream-coalescing.js +83 -0
- package/dist/agent-worker.js +116 -131
- package/dist/context-runtime.js +38 -6
- package/package.json +2 -2
|
@@ -1,6 +1,7 @@
|
|
|
1
|
+
import { digestAgentSessionMessage } from "./agent-session-transport.js";
|
|
1
2
|
import { getSessionPayload, putSessionPayload } from "./agent-session-payload.js";
|
|
2
3
|
import { agentModelCallsSchema, } from "@gea-ai/contract/agent-usage";
|
|
3
|
-
import { AGENT_SESSION_COMPUTER_LEASE_MS, MAX_AGENT_SESSION_TRANSFER_BYTES, agentSessionTransferChunkSchema, agentSessionTransferReferenceSchema, agentSessionTransferIdentitySchema, 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, agentSessionExecutionPrepareSchema, } from "@gea-ai/contract/agent-session";
|
|
4
|
+
import { AGENT_SESSION_COMPUTER_LEASE_MS, MAX_AGENT_SESSION_TRANSFER_BYTES, agentSessionTransferChunkSchema, agentSessionTransferReferenceSchema, agentSessionTransferIdentitySchema, 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, agentSessionExecutionCompleteStoredSchema, agentSessionExecutionPauseSchema, agentSessionExecutionControlSchema, agentSessionExecutionMutationSchema, agentSessionExecutionStartSchema, agentSessionExecutionPrepareSchema, } from "@gea-ai/contract/agent-session";
|
|
4
5
|
import { z } from "zod";
|
|
5
6
|
import { activeComputerOperation } from "./agent-session-computer.js";
|
|
6
7
|
import { isAgentSessionInboxBusy, scheduleAgentSessionInbox, getAgentSessionRunState, handleAgentSessionInboxRequest, hasAgentSessionInbox, skipAgentSessionInputWait, } from "./agent-session-inbox.js";
|
|
@@ -506,12 +507,39 @@ export async function handleAgentSessionExecutionRequest(request, storage) {
|
|
|
506
507
|
return { duplicate: false, turn, revision };
|
|
507
508
|
}));
|
|
508
509
|
}
|
|
509
|
-
if (request.method === "POST" &&
|
|
510
|
-
|
|
511
|
-
|
|
510
|
+
if (request.method === "POST" &&
|
|
511
|
+
(url.pathname === "/v2/turns/complete" ||
|
|
512
|
+
url.pathname === "/v2/turns/complete-stored")) {
|
|
513
|
+
const body = await readBody(request, storage);
|
|
514
|
+
const stored = url.pathname === "/v2/turns/complete-stored"
|
|
515
|
+
? agentSessionExecutionCompleteStoredSchema.parse(body)
|
|
516
|
+
: undefined;
|
|
517
|
+
const { messageReferences: _references, ...storedValues } = stored ?? {};
|
|
518
|
+
const input = agentSessionExecutionCompleteSchema.parse(stored ? storedValues : body);
|
|
519
|
+
const storedRequestDigest = stored ? await digest(stored) : undefined;
|
|
520
|
+
const standardDigest = stored ? undefined : await digest(input);
|
|
512
521
|
return json(await storage.transaction(async (tx) => {
|
|
513
522
|
const receiptKey = key(`outcome:${encodeURIComponent(input.runId)}`);
|
|
514
523
|
const existing = await tx.get(receiptKey);
|
|
524
|
+
if (stored && existing?.storedRequestDigest) {
|
|
525
|
+
if (existing.storedRequestDigest !== storedRequestDigest)
|
|
526
|
+
throw new SessionProtocolError("completion_mismatch");
|
|
527
|
+
return { duplicate: true, outcome: existing.outcome };
|
|
528
|
+
}
|
|
529
|
+
if (stored) {
|
|
530
|
+
for (const reference of stored.messageReferences) {
|
|
531
|
+
const sequence = await tx.get(key(`message-id:${encodeURIComponent(reference.id)}`));
|
|
532
|
+
const entry = sequence === undefined
|
|
533
|
+
? undefined
|
|
534
|
+
: await getSessionPayload(tx, sequenceKey("message", sequence));
|
|
535
|
+
if (!entry ||
|
|
536
|
+
(await digestAgentSessionMessage(entry.message)) !==
|
|
537
|
+
reference.sha256)
|
|
538
|
+
throw new SessionProtocolError("stored_message_mismatch", 412);
|
|
539
|
+
input.messages.push(entry.message);
|
|
540
|
+
}
|
|
541
|
+
}
|
|
542
|
+
const inputDigest = standardDigest ?? (await digest(input));
|
|
515
543
|
if (existing) {
|
|
516
544
|
if (existing.digest !== inputDigest)
|
|
517
545
|
throw new SessionProtocolError("completion_mismatch");
|
|
@@ -566,7 +594,7 @@ export async function handleAgentSessionExecutionRequest(request, storage) {
|
|
|
566
594
|
if (input.context && input.context.runId !== input.runId)
|
|
567
595
|
throw new SessionProtocolError("context_run_mismatch");
|
|
568
596
|
const previousRevision = await readRevision(tx);
|
|
569
|
-
await writeValues(tx, input);
|
|
597
|
+
await writeValues(tx, stored ? { ...input, messages: [] } : input);
|
|
570
598
|
const modelCalls = await tx.get(key(`model-calls:${encodeURIComponent(input.runId)}`));
|
|
571
599
|
const outcome = {
|
|
572
600
|
...(modelCalls === undefined
|
|
@@ -586,6 +614,7 @@ export async function handleAgentSessionExecutionRequest(request, storage) {
|
|
|
586
614
|
await tx.put(receiptKey, {
|
|
587
615
|
outcome,
|
|
588
616
|
digest: inputDigest,
|
|
617
|
+
...(storedRequestDigest ? { storedRequestDigest } : {}),
|
|
589
618
|
});
|
|
590
619
|
const rangeKey = key(`run-events:${encodeURIComponent(input.runId)}`);
|
|
591
620
|
const storedRange = await tx.get(rangeKey);
|
|
@@ -1,6 +1,10 @@
|
|
|
1
|
+
import { type AgentSessionMessage } from "@gea-ai/contract/agent-session";
|
|
1
2
|
/** Both hosted and in-Worker clients use the same bounded, non-retrying transport. */
|
|
2
3
|
export declare function sendAgentSessionRequest(input: {
|
|
3
4
|
path: string;
|
|
4
5
|
body?: unknown;
|
|
5
6
|
send: (path: string, body: string | undefined) => Promise<Response>;
|
|
6
7
|
}): Promise<Response>;
|
|
8
|
+
/** Compare message content independently of object-key insertion order. JSON
|
|
9
|
+
* serialization also gives Dates the same representation as persisted values. */
|
|
10
|
+
export declare function digestAgentSessionMessage(message: AgentSessionMessage): Promise<string>;
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { AGENT_SESSION_TRANSFER_CHARS, agentSessionExecutionMutationSchema, agentSessionExecutionPauseSchema, agentSessionExecutionCompleteSchema, MAX_AGENT_SESSION_EXECUTION_BODY_BYTES, MAX_AGENT_SESSION_TRANSFER_BYTES, agentSessionAttemptIdentitySchema, agentSessionTransferIdentitySchema, } from "@gea-ai/contract/agent-session";
|
|
1
|
+
import { AGENT_SESSION_TRANSFER_CHARS, agentSessionExecutionMutationSchema, agentSessionMessageSchema, agentSessionExecutionPauseSchema, agentSessionExecutionCompleteSchema, MAX_AGENT_SESSION_EXECUTION_BODY_BYTES, MAX_AGENT_SESSION_TRANSFER_BYTES, agentSessionAttemptIdentitySchema, agentSessionTransferIdentitySchema, } from "@gea-ai/contract/agent-session";
|
|
2
2
|
/** Both hosted and in-Worker clients use the same bounded, non-retrying transport. */
|
|
3
3
|
export async function sendAgentSessionRequest(input) {
|
|
4
4
|
const serialized = input.body === undefined ? undefined : JSON.stringify(input.body);
|
|
@@ -49,3 +49,11 @@ export async function sendAgentSessionRequest(input) {
|
|
|
49
49
|
}
|
|
50
50
|
return input.send(input.path, JSON.stringify({ $agentSessionTransfer: { ...identity, chunks } }));
|
|
51
51
|
}
|
|
52
|
+
/** Compare message content independently of object-key insertion order. JSON
|
|
53
|
+
* serialization also gives Dates the same representation as persisted values. */
|
|
54
|
+
export async function digestAgentSessionMessage(message) {
|
|
55
|
+
const bytes = await crypto.subtle.digest("SHA-256", new TextEncoder().encode(JSON.stringify(agentSessionMessageSchema.parse(message), (_key, value) => value !== null && typeof value === "object" && !Array.isArray(value)
|
|
56
|
+
? Object.fromEntries(Object.entries(value).sort(([a], [b]) => a < b ? -1 : a > b ? 1 : 0))
|
|
57
|
+
: value)));
|
|
58
|
+
return Array.from(new Uint8Array(bytes), (byte) => byte.toString(16).padStart(2, "0")).join("");
|
|
59
|
+
}
|
|
@@ -4,3 +4,9 @@ import type { UIMessageChunk } from "ai";
|
|
|
4
4
|
* Control events, tool results and part boundaries retain their exact order.
|
|
5
5
|
*/
|
|
6
6
|
export declare function coalesceAgentTextDeltas<T extends UIMessageChunk>(stream: ReadableStream<T>): ReadableStream<T>;
|
|
7
|
+
/** Group ready events for one durable mutation. Start/finish stay separate so
|
|
8
|
+
* first bytes are immediate and context settlement cannot hold back prior text.
|
|
9
|
+
* At most 5 ms, 64 events or 64 Ki serialized characters are collected. An
|
|
10
|
+
* oversized individual event uses the existing Session transfer protocol.
|
|
11
|
+
*/
|
|
12
|
+
export declare function batchAgentStreamEvents<T extends UIMessageChunk>(stream: ReadableStream<T>): ReadableStream<T[]>;
|
|
@@ -71,3 +71,86 @@ export function coalesceAgentTextDeltas(stream) {
|
|
|
71
71
|
},
|
|
72
72
|
});
|
|
73
73
|
}
|
|
74
|
+
/** Group ready events for one durable mutation. Start/finish stay separate so
|
|
75
|
+
* first bytes are immediate and context settlement cannot hold back prior text.
|
|
76
|
+
* At most 5 ms, 64 events or 64 Ki serialized characters are collected. An
|
|
77
|
+
* oversized individual event uses the existing Session transfer protocol.
|
|
78
|
+
*/
|
|
79
|
+
export function batchAgentStreamEvents(stream) {
|
|
80
|
+
const reader = stream.getReader();
|
|
81
|
+
let pending;
|
|
82
|
+
let held;
|
|
83
|
+
let failure;
|
|
84
|
+
let cancelled = false;
|
|
85
|
+
const read = () => (pending ??= reader.read());
|
|
86
|
+
const boundary = (chunk) => chunk.type === "start" ||
|
|
87
|
+
chunk.type === "finish" ||
|
|
88
|
+
chunk.type === "abort" ||
|
|
89
|
+
chunk.type === "error";
|
|
90
|
+
return new ReadableStream({
|
|
91
|
+
async pull(controller) {
|
|
92
|
+
let timer;
|
|
93
|
+
try {
|
|
94
|
+
if (failure)
|
|
95
|
+
throw failure.error;
|
|
96
|
+
const first = held ?? (await read());
|
|
97
|
+
held = undefined;
|
|
98
|
+
pending = undefined;
|
|
99
|
+
if (cancelled)
|
|
100
|
+
return;
|
|
101
|
+
if (first.done) {
|
|
102
|
+
reader.releaseLock();
|
|
103
|
+
controller.close();
|
|
104
|
+
return;
|
|
105
|
+
}
|
|
106
|
+
const batch = [first.value];
|
|
107
|
+
let size = JSON.stringify(first.value).length;
|
|
108
|
+
if (!boundary(first.value) && size < 64 * 1024) {
|
|
109
|
+
const deadline = new Promise((resolve) => {
|
|
110
|
+
timer = setTimeout(() => resolve(null), 5);
|
|
111
|
+
});
|
|
112
|
+
while (batch.length < 64 && size < 64 * 1024) {
|
|
113
|
+
let next;
|
|
114
|
+
try {
|
|
115
|
+
next = await Promise.race([read(), deadline]);
|
|
116
|
+
}
|
|
117
|
+
catch (error) {
|
|
118
|
+
// Received events still precede the failure in the durable journal.
|
|
119
|
+
failure = { error };
|
|
120
|
+
break;
|
|
121
|
+
}
|
|
122
|
+
if (next === null)
|
|
123
|
+
break;
|
|
124
|
+
pending = undefined;
|
|
125
|
+
if (next.done || boundary(next.value)) {
|
|
126
|
+
held = next;
|
|
127
|
+
break;
|
|
128
|
+
}
|
|
129
|
+
const nextSize = JSON.stringify(next.value).length;
|
|
130
|
+
if (size + nextSize > 64 * 1024) {
|
|
131
|
+
held = next;
|
|
132
|
+
break;
|
|
133
|
+
}
|
|
134
|
+
batch.push(next.value);
|
|
135
|
+
size += nextSize;
|
|
136
|
+
}
|
|
137
|
+
}
|
|
138
|
+
if (!cancelled)
|
|
139
|
+
controller.enqueue(batch);
|
|
140
|
+
}
|
|
141
|
+
catch (error) {
|
|
142
|
+
if (!cancelled)
|
|
143
|
+
controller.error(error);
|
|
144
|
+
reader.releaseLock();
|
|
145
|
+
}
|
|
146
|
+
finally {
|
|
147
|
+
clearTimeout(timer);
|
|
148
|
+
}
|
|
149
|
+
},
|
|
150
|
+
async cancel(reason) {
|
|
151
|
+
cancelled = true;
|
|
152
|
+
await reader.cancel(reason);
|
|
153
|
+
reader.releaseLock();
|
|
154
|
+
},
|
|
155
|
+
});
|
|
156
|
+
}
|
package/dist/agent-worker.js
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import { AGENT_SUBAGENT_CONCURRENCY_HEADER } from "@gea-ai/contract/agent-worker-tools";
|
|
2
2
|
import { DEFAULT_MAX_CONCURRENT_SUBAGENTS } from "@gea-ai/contract/agent-packages";
|
|
3
|
-
import { coalesceAgentTextDeltas } from "./agent-stream-coalescing.js";
|
|
3
|
+
import { batchAgentStreamEvents, coalesceAgentTextDeltas, } from "./agent-stream-coalescing.js";
|
|
4
4
|
import { sendAgentSessionRequest } from "./agent-session-transport.js";
|
|
5
5
|
import { agentSessionOverridesSchema } from "@gea-ai/contract/agent-session-overrides";
|
|
6
6
|
import { resolveModelOptions } from "@gea-ai/contract/model-options";
|
|
@@ -13,7 +13,6 @@ export { createGeaAI } from "./gea-ai.js";
|
|
|
13
13
|
import { mergeLlmJson, llmRuntimeRequestOptionsSchema, } from "@gea-ai/contract/llm-catalog";
|
|
14
14
|
import { executeChannelAgentRun, uploadChannelInput, } from "./agent-channel-api.js";
|
|
15
15
|
export { extractChannelReply } from "./agent-channel-api.js";
|
|
16
|
-
import { agentWorkerAttachmentOutputSchema } from "@gea-ai/contract/agent-api-host";
|
|
17
16
|
import { streamManagedAgentSessionRun } from "./agent-session-stream.js";
|
|
18
17
|
import { agentSessionReadToolOutputSchema, agentSessionReadToolOutputResultSchema, } from "@gea-ai/contract/agent-session";
|
|
19
18
|
import { handleAgentComputerRequest, readSessionComputer, } from "./agent-computer-http.js";
|
|
@@ -32,7 +31,7 @@ import { agentModelCallSchema, } from "@gea-ai/contract/agent-usage";
|
|
|
32
31
|
import { createAgentContextRuntime } from "./context-runtime.js";
|
|
33
32
|
import { studioExternalUserPrincipalSchema } from "@gea-ai/contract";
|
|
34
33
|
import { replaceInputFiles, withTurnImages } from "./agent-worker-attachments.js";
|
|
35
|
-
import { toAgentHttpFile
|
|
34
|
+
import { toAgentHttpFile } from "@gea-ai/contract/agent-http-api";
|
|
36
35
|
import { handleHostedAgentHttp } from "./agent-http.js";
|
|
37
36
|
import { createWorkerAgentTool, createWorkerRunCancelTool } from "./agent-call.js";
|
|
38
37
|
import { AgentSessionRunner, resumeSessionWait } from "./agent-session-runner.js";
|
|
@@ -1602,7 +1601,6 @@ function createAgentWorkerHostAdapter(bindings) {
|
|
|
1602
1601
|
}
|
|
1603
1602
|
if (path === `${HOST_PATH}/tools/initialize` ||
|
|
1604
1603
|
path === `${HOST_PATH}/tools/attachments/read` ||
|
|
1605
|
-
path === `${HOST_PATH}/tools/attachments/outputs` ||
|
|
1606
1604
|
path === `${HOST_PATH}/tools/attachments/prepare` ||
|
|
1607
1605
|
path === `${HOST_PATH}/tools/invoke` ||
|
|
1608
1606
|
path === `${HOST_PATH}/agents/runs` ||
|
|
@@ -2709,6 +2707,7 @@ export async function runAgentWorkerRequest(input) {
|
|
|
2709
2707
|
}, async (signal, waitUntil) => {
|
|
2710
2708
|
const invocationTraceContext = captureTraceContext();
|
|
2711
2709
|
const invocationSpan = trace.getActiveSpan();
|
|
2710
|
+
const invocationTiming = {};
|
|
2712
2711
|
const cancellation = new AbortController();
|
|
2713
2712
|
let stopComputer;
|
|
2714
2713
|
let inboxOutcome = {
|
|
@@ -2748,6 +2747,7 @@ export async function runAgentWorkerRequest(input) {
|
|
|
2748
2747
|
clearTimeout(cancellationTimer);
|
|
2749
2748
|
if (leaseMaintenance)
|
|
2750
2749
|
await leaseMaintenance;
|
|
2750
|
+
const leaseDrainedAt = performance.now();
|
|
2751
2751
|
if (input.sessionExecution) {
|
|
2752
2752
|
results.push(...(await Promise.allSettled([
|
|
2753
2753
|
requestSessionJson({
|
|
@@ -2764,9 +2764,11 @@ export async function runAgentWorkerRequest(input) {
|
|
|
2764
2764
|
"gea.session.finish.background_ms": Math.round(drainedAt - cleanupStartedAt),
|
|
2765
2765
|
"gea.session.finish.computer_stop_ms": Math.round(computerStoppedAt - drainedAt),
|
|
2766
2766
|
"gea.session.finish.session_release_ms": Math.round(sessionFinishedAt - computerStoppedAt),
|
|
2767
|
-
"gea.session.finish.
|
|
2767
|
+
"gea.session.finish.lease_observer_wait_ms": Math.round(leaseDrainedAt - sessionFinishedAt),
|
|
2768
|
+
"gea.session.finish.lease_handoff_ms": Math.round(performance.now() - leaseDrainedAt),
|
|
2768
2769
|
});
|
|
2769
2770
|
if (input.inboxExecution) {
|
|
2771
|
+
const inboxStartedAt = performance.now();
|
|
2770
2772
|
const status = cancellation.signal.aborted
|
|
2771
2773
|
? "cancelled"
|
|
2772
2774
|
: results.some((result) => result.status === "rejected")
|
|
@@ -2790,7 +2792,8 @@ export async function runAgentWorkerRequest(input) {
|
|
|
2790
2792
|
},
|
|
2791
2793
|
}),
|
|
2792
2794
|
}),
|
|
2793
|
-
}))
|
|
2795
|
+
}))
|
|
2796
|
+
.finally(() => invocationSpan?.setAttribute("gea.session.finish.inbox_ms", Math.round(performance.now() - inboxStartedAt)));
|
|
2794
2797
|
if (!sessionResponse.ok)
|
|
2795
2798
|
throw new Error(`Session finish rejected (${sessionResponse.status}).`);
|
|
2796
2799
|
}
|
|
@@ -2873,7 +2876,7 @@ export async function runAgentWorkerRequest(input) {
|
|
|
2873
2876
|
stopComputer = stop;
|
|
2874
2877
|
}, (outcome) => {
|
|
2875
2878
|
inboxOutcome = outcome;
|
|
2876
|
-
});
|
|
2879
|
+
}, invocationTiming);
|
|
2877
2880
|
}
|
|
2878
2881
|
catch (error) {
|
|
2879
2882
|
cancellation.abort(error);
|
|
@@ -2917,6 +2920,8 @@ export async function runAgentWorkerRequest(input) {
|
|
|
2917
2920
|
}
|
|
2918
2921
|
signal.removeEventListener("abort", onAbort);
|
|
2919
2922
|
await finish();
|
|
2923
|
+
if (invocationTiming.lastModelFinishedAt !== undefined)
|
|
2924
|
+
invocationSpan?.setAttribute("gea.session.finish.model_to_close_ms", Math.round(performance.now() - invocationTiming.lastModelFinishedAt));
|
|
2920
2925
|
controller.close();
|
|
2921
2926
|
}
|
|
2922
2927
|
catch (error) {
|
|
@@ -2938,7 +2943,7 @@ export async function runAgentWorkerRequest(input) {
|
|
|
2938
2943
|
});
|
|
2939
2944
|
});
|
|
2940
2945
|
}
|
|
2941
|
-
async function executeAgentWorkerRequest(input, waitUntil, ownComputer, ownSessionOutcome) {
|
|
2946
|
+
async function executeAgentWorkerRequest(input, waitUntil, ownComputer, ownSessionOutcome, invocationTiming) {
|
|
2942
2947
|
const invocationTrace = activeTraceRef();
|
|
2943
2948
|
const host = input.bindings
|
|
2944
2949
|
? createAgentWorkerHostAdapter(input.bindings)
|
|
@@ -2968,7 +2973,13 @@ async function executeAgentWorkerRequest(input, waitUntil, ownComputer, ownSessi
|
|
|
2968
2973
|
let writeTail = Promise.resolve();
|
|
2969
2974
|
let writeFailure;
|
|
2970
2975
|
const persistenceSpan = trace.getActiveSpan();
|
|
2971
|
-
const persistenceTotals = {
|
|
2976
|
+
const persistenceTotals = {
|
|
2977
|
+
writes: 0,
|
|
2978
|
+
queueMs: 0,
|
|
2979
|
+
requestMs: 0,
|
|
2980
|
+
events: 0,
|
|
2981
|
+
eventBatches: 0,
|
|
2982
|
+
};
|
|
2972
2983
|
if (sessionExecution) {
|
|
2973
2984
|
if (sessionExecution.runId !== payload.runId)
|
|
2974
2985
|
throw new Error("Session Run identity mismatch.");
|
|
@@ -3009,10 +3020,16 @@ async function executeAgentWorkerRequest(input, waitUntil, ownComputer, ownSessi
|
|
|
3009
3020
|
finally {
|
|
3010
3021
|
const requestMs = performance.now() - startedAt;
|
|
3011
3022
|
persistenceTotals.writes++;
|
|
3023
|
+
if (values.events?.length) {
|
|
3024
|
+
persistenceTotals.events += values.events.length;
|
|
3025
|
+
persistenceTotals.eventBatches++;
|
|
3026
|
+
}
|
|
3012
3027
|
persistenceTotals.queueMs += queueMs;
|
|
3013
3028
|
persistenceTotals.requestMs += requestMs;
|
|
3014
3029
|
persistenceSpan?.setAttributes({
|
|
3015
3030
|
"gea.session.persistence.writes": persistenceTotals.writes,
|
|
3031
|
+
"gea.session.persistence.events": persistenceTotals.events,
|
|
3032
|
+
"gea.session.persistence.event_batches": persistenceTotals.eventBatches,
|
|
3016
3033
|
"gea.session.persistence.queue_ms": Math.round(persistenceTotals.queueMs),
|
|
3017
3034
|
"gea.session.persistence.request_ms": Math.round(persistenceTotals.requestMs),
|
|
3018
3035
|
});
|
|
@@ -3832,6 +3849,7 @@ async function executeAgentWorkerRequest(input, waitUntil, ownComputer, ownSessi
|
|
|
3832
3849
|
let mergedResponseMessageCount = 0;
|
|
3833
3850
|
let contextCheckpoint;
|
|
3834
3851
|
let reusedContextCheckpoints = 0;
|
|
3852
|
+
let modelCallsInitialized = false;
|
|
3835
3853
|
const persistModelContext = () => {
|
|
3836
3854
|
const modelContext = {
|
|
3837
3855
|
contextSize: context.contextSize,
|
|
@@ -3848,7 +3866,10 @@ async function executeAgentWorkerRequest(input, waitUntil, ownComputer, ownSessi
|
|
|
3848
3866
|
return contextCheckpoint.pending;
|
|
3849
3867
|
}
|
|
3850
3868
|
const pending = sessionExecution
|
|
3851
|
-
? mutateSession({
|
|
3869
|
+
? mutateSession({
|
|
3870
|
+
context: modelContext,
|
|
3871
|
+
...(!modelCallsInitialized ? { modelCalls: [] } : {}),
|
|
3872
|
+
})
|
|
3852
3873
|
: requestSessionJson({
|
|
3853
3874
|
body: { context: modelContext },
|
|
3854
3875
|
chatId: payload.chatId,
|
|
@@ -3857,6 +3878,8 @@ async function executeAgentWorkerRequest(input, waitUntil, ownComputer, ownSessi
|
|
|
3857
3878
|
path: "/v1/model-context",
|
|
3858
3879
|
schema: agentSessionPutModelContextResultSchema,
|
|
3859
3880
|
}).then(() => undefined);
|
|
3881
|
+
if (sessionExecution)
|
|
3882
|
+
modelCallsInitialized = true;
|
|
3860
3883
|
contextCheckpoint = { serialized, pending };
|
|
3861
3884
|
return pending;
|
|
3862
3885
|
};
|
|
@@ -3902,8 +3925,10 @@ async function executeAgentWorkerRequest(input, waitUntil, ownComputer, ownSessi
|
|
|
3902
3925
|
await appendStreamEvent({ type: "data-model-call", data: call }, `model-call:${call.callId}`);
|
|
3903
3926
|
}
|
|
3904
3927
|
};
|
|
3905
|
-
if (sessionExecution)
|
|
3928
|
+
if (sessionExecution && !modelCallsInitialized) {
|
|
3906
3929
|
await mutateSession({ modelCalls: [] });
|
|
3930
|
+
modelCallsInitialized = true;
|
|
3931
|
+
}
|
|
3907
3932
|
let contextSignal = input.request.signal;
|
|
3908
3933
|
const contextRuntime = createAgentContextRuntime({
|
|
3909
3934
|
ai: host,
|
|
@@ -4073,12 +4098,16 @@ async function executeAgentWorkerRequest(input, waitUntil, ownComputer, ownSessi
|
|
|
4073
4098
|
catch (error) {
|
|
4074
4099
|
fail(error);
|
|
4075
4100
|
}
|
|
4101
|
+
const finalContextStartedAt = performance.now();
|
|
4076
4102
|
try {
|
|
4077
4103
|
await persistModelContext();
|
|
4078
4104
|
}
|
|
4079
4105
|
catch (error) {
|
|
4080
4106
|
fail(error);
|
|
4081
4107
|
}
|
|
4108
|
+
finally {
|
|
4109
|
+
persistenceSpan?.setAttribute("gea.session.finish.final_context_ms", Math.round(performance.now() - finalContextStartedAt));
|
|
4110
|
+
}
|
|
4082
4111
|
resolveContext({
|
|
4083
4112
|
...(!failure && inboxRunner?.suspended
|
|
4084
4113
|
? { sessionStatus: "waiting" }
|
|
@@ -4095,52 +4124,10 @@ async function executeAgentWorkerRequest(input, waitUntil, ownComputer, ownSessi
|
|
|
4095
4124
|
})();
|
|
4096
4125
|
return settling;
|
|
4097
4126
|
};
|
|
4098
|
-
const publishedArtifacts = new Map();
|
|
4099
|
-
const emittedArtifacts = new Set();
|
|
4100
|
-
const refreshArtifacts = async () => {
|
|
4101
|
-
if (!artifactScope || input.request.signal.aborted)
|
|
4102
|
-
return;
|
|
4103
|
-
let cursor;
|
|
4104
|
-
do {
|
|
4105
|
-
const response = await host.fetch(`${HOST_PATH}/tools/attachments/outputs`, {
|
|
4106
|
-
method: "POST",
|
|
4107
|
-
headers: { "content-type": "application/json" },
|
|
4108
|
-
body: JSON.stringify({ scope: artifactScope, cursor }),
|
|
4109
|
-
signal: input.request.signal,
|
|
4110
|
-
});
|
|
4111
|
-
if (!response.ok)
|
|
4112
|
-
throw new Error(`Could not list published Artifacts (${response.status}).`);
|
|
4113
|
-
const page = z
|
|
4114
|
-
.object({
|
|
4115
|
-
items: z.array(agentWorkerAttachmentOutputSchema),
|
|
4116
|
-
next_cursor: z.uuid().nullable(),
|
|
4117
|
-
})
|
|
4118
|
-
.parse(await response.json());
|
|
4119
|
-
for (const artifact of page.items)
|
|
4120
|
-
publishedArtifacts.set(artifact.id, artifact);
|
|
4121
|
-
cursor = page.next_cursor ?? undefined;
|
|
4122
|
-
} while (cursor);
|
|
4123
|
-
};
|
|
4124
|
-
let finalArtifacts;
|
|
4125
|
-
const refreshFinalArtifacts = () =>
|
|
4126
|
-
// Context callbacks can publish outputs. Both terminal consumers use the
|
|
4127
|
-
// same snapshot, taken only after those callbacks and their work settle.
|
|
4128
|
-
(finalArtifacts ??= contextFinished.then(() => refreshArtifacts()));
|
|
4129
4127
|
const saveResponseMessage = async ({ responseMessage, }) => {
|
|
4130
4128
|
const saveStartedAt = performance.now();
|
|
4131
4129
|
const metadata = await contextFinished;
|
|
4132
4130
|
const contextFinishedAt = performance.now();
|
|
4133
|
-
await refreshFinalArtifacts();
|
|
4134
|
-
const artifactsFinishedAt = performance.now();
|
|
4135
|
-
responseMessage.parts = [
|
|
4136
|
-
...responseMessage.parts.filter((part) => part.type !== "data-artifact" ||
|
|
4137
|
-
!publishedArtifacts.has(part.data.id)),
|
|
4138
|
-
...[...publishedArtifacts.values()].map((data) => ({
|
|
4139
|
-
type: "data-artifact",
|
|
4140
|
-
id: data.id,
|
|
4141
|
-
data,
|
|
4142
|
-
})),
|
|
4143
|
-
];
|
|
4144
4131
|
if (inboxRunner?.suspendedToolCallId) {
|
|
4145
4132
|
responseMessage.parts = responseMessage.parts.map((part) => {
|
|
4146
4133
|
if (isToolUIPart(part) &&
|
|
@@ -4178,7 +4165,6 @@ async function executeAgentWorkerRequest(input, waitUntil, ownComputer, ownSessi
|
|
|
4178
4165
|
await persistModelContext();
|
|
4179
4166
|
const contextPersistedAt = performance.now();
|
|
4180
4167
|
persistenceSpan?.setAttributes({
|
|
4181
|
-
"gea.session.finish.artifacts_ms": Math.round(artifactsFinishedAt - contextFinishedAt),
|
|
4182
4168
|
"gea.session.finish.context_wait_ms": Math.round(contextFinishedAt - saveStartedAt),
|
|
4183
4169
|
"gea.session.finish.transcript_ms": Math.round(transcriptFinishedAt - transcriptStartedAt),
|
|
4184
4170
|
"gea.session.finish.context_write_ms": Math.round(contextPersistedAt - transcriptFinishedAt),
|
|
@@ -4517,6 +4503,7 @@ async function executeAgentWorkerRequest(input, waitUntil, ownComputer, ownSessi
|
|
|
4517
4503
|
: part.finish_reason;
|
|
4518
4504
|
const usage = coreUsage(part.usage);
|
|
4519
4505
|
mainUsage = addAgentWorkerUsage(mainUsage, usage);
|
|
4506
|
+
invocationTiming.lastModelFinishedAt = performance.now();
|
|
4520
4507
|
await recordMainCall("completed", usage);
|
|
4521
4508
|
modelSpan?.setAttribute("gen_ai.output.messages", JSON.stringify(toModelMessages([part.message])));
|
|
4522
4509
|
if (usage.inputTokens !== undefined)
|
|
@@ -4607,6 +4594,9 @@ async function executeAgentWorkerRequest(input, waitUntil, ownComputer, ownSessi
|
|
|
4607
4594
|
throw new Error("Agent Worker model is missing.");
|
|
4608
4595
|
const result = streamText({
|
|
4609
4596
|
telemetry: agentModelTelemetry,
|
|
4597
|
+
onLanguageModelCallEnd() {
|
|
4598
|
+
invocationTiming.lastModelFinishedAt = performance.now();
|
|
4599
|
+
},
|
|
4610
4600
|
onError: ({ error }) => {
|
|
4611
4601
|
fail(error);
|
|
4612
4602
|
},
|
|
@@ -4722,89 +4712,84 @@ async function executeAgentWorkerRequest(input, waitUntil, ownComputer, ownSessi
|
|
|
4722
4712
|
},
|
|
4723
4713
|
});
|
|
4724
4714
|
}
|
|
4725
|
-
const durableStream = (sessionExecution
|
|
4726
|
-
|
|
4727
|
-
|
|
4728
|
-
|
|
4729
|
-
|
|
4730
|
-
|
|
4731
|
-
|
|
4732
|
-
|
|
4733
|
-
|
|
4734
|
-
|
|
4735
|
-
|
|
4736
|
-
|
|
4737
|
-
|
|
4738
|
-
|
|
4739
|
-
|
|
4740
|
-
chatId: payload.chatId,
|
|
4741
|
-
namespace: input.sessionNamespace,
|
|
4742
|
-
path: "/v1/messages",
|
|
4743
|
-
});
|
|
4744
|
-
for (const event of events)
|
|
4745
|
-
await appendStreamEvent(event);
|
|
4746
|
-
}
|
|
4747
|
-
}
|
|
4748
|
-
if (chunk.type === "start" && responseBaseline) {
|
|
4749
|
-
// Each internal invocation still has its own start/finish for accounting.
|
|
4750
|
-
// The public journal represents one response across those invocations.
|
|
4751
|
-
controller.enqueue(chunk);
|
|
4752
|
-
const result = input.inboxExecution?.waitResult;
|
|
4753
|
-
if (result) {
|
|
4754
|
-
const output = {
|
|
4755
|
-
type: "tool-output-available",
|
|
4756
|
-
toolCallId: result.toolCallId,
|
|
4757
|
-
output: toAgentRunWaitOutput(result),
|
|
4758
|
-
};
|
|
4759
|
-
if (sessionExecution)
|
|
4760
|
-
await mutateSession({ events: [output] });
|
|
4761
|
-
else
|
|
4762
|
-
await appendStreamEvent(output);
|
|
4763
|
-
controller.enqueue(output);
|
|
4764
|
-
}
|
|
4765
|
-
return;
|
|
4766
|
-
}
|
|
4767
|
-
if (chunk.type === "tool-output-available" &&
|
|
4768
|
-
chunk.toolCallId === inboxRunner?.suspendedToolCallId)
|
|
4769
|
-
return;
|
|
4770
|
-
if (chunk.type === "finish-step" || chunk.type === "finish") {
|
|
4771
|
-
if (chunk.type === "finish")
|
|
4772
|
-
await refreshFinalArtifacts();
|
|
4773
|
-
else
|
|
4774
|
-
await refreshArtifacts();
|
|
4775
|
-
for (const data of publishedArtifacts.values()) {
|
|
4776
|
-
if (emittedArtifacts.has(data.id))
|
|
4777
|
-
continue;
|
|
4778
|
-
const event = { type: "data-artifact", id: data.id, data };
|
|
4715
|
+
const durableStream = (sessionExecution
|
|
4716
|
+
? batchAgentStreamEvents(coalesceAgentTextDeltas(stream))
|
|
4717
|
+
: stream.pipeThrough(new TransformStream({
|
|
4718
|
+
transform(chunk, controller) {
|
|
4719
|
+
controller.enqueue([chunk]);
|
|
4720
|
+
},
|
|
4721
|
+
}))).pipeThrough(new TransformStream({
|
|
4722
|
+
async transform(chunks, controller) {
|
|
4723
|
+
const events = [];
|
|
4724
|
+
for (let chunk of chunks) {
|
|
4725
|
+
if (chunk.type === "start" && interruptedResponse) {
|
|
4726
|
+
const events = [
|
|
4727
|
+
...(interruptedWait ? [interruptedWait] : []),
|
|
4728
|
+
{ type: "finish", finishReason: "other" },
|
|
4729
|
+
];
|
|
4779
4730
|
if (sessionExecution)
|
|
4780
|
-
await mutateSession({
|
|
4781
|
-
|
|
4782
|
-
|
|
4783
|
-
|
|
4784
|
-
|
|
4731
|
+
await mutateSession({
|
|
4732
|
+
messages: [{ ...interruptedResponse }],
|
|
4733
|
+
events,
|
|
4734
|
+
});
|
|
4735
|
+
else {
|
|
4736
|
+
await appendSessionValue({
|
|
4737
|
+
body: { message: interruptedResponse, runId: payload.runId },
|
|
4738
|
+
chatId: payload.chatId,
|
|
4739
|
+
namespace: input.sessionNamespace,
|
|
4740
|
+
path: "/v1/messages",
|
|
4741
|
+
});
|
|
4742
|
+
for (const event of events)
|
|
4743
|
+
await appendStreamEvent(event);
|
|
4744
|
+
}
|
|
4785
4745
|
}
|
|
4786
|
-
|
|
4787
|
-
|
|
4788
|
-
|
|
4789
|
-
if (chunk.type === "finish") {
|
|
4790
|
-
const metadata = await contextFinished;
|
|
4791
|
-
chunk = {
|
|
4792
|
-
...chunk,
|
|
4793
|
-
finishReason: metadata.finishReason,
|
|
4794
|
-
messageMetadata: metadata,
|
|
4795
|
-
};
|
|
4796
|
-
if (metadata.sessionStatus === "waiting") {
|
|
4746
|
+
if (chunk.type === "start" && responseBaseline) {
|
|
4747
|
+
// Each internal invocation still has its own start/finish for accounting.
|
|
4748
|
+
// The public journal represents one response across those invocations.
|
|
4797
4749
|
controller.enqueue(chunk);
|
|
4798
|
-
|
|
4750
|
+
const result = input.inboxExecution?.waitResult;
|
|
4751
|
+
if (result) {
|
|
4752
|
+
const output = {
|
|
4753
|
+
type: "tool-output-available",
|
|
4754
|
+
toolCallId: result.toolCallId,
|
|
4755
|
+
output: toAgentRunWaitOutput(result),
|
|
4756
|
+
};
|
|
4757
|
+
if (sessionExecution)
|
|
4758
|
+
await mutateSession({ events: [output] });
|
|
4759
|
+
else
|
|
4760
|
+
await appendStreamEvent(output);
|
|
4761
|
+
controller.enqueue(output);
|
|
4762
|
+
}
|
|
4763
|
+
continue;
|
|
4799
4764
|
}
|
|
4765
|
+
if (chunk.type === "tool-output-available" &&
|
|
4766
|
+
chunk.toolCallId === inboxRunner?.suspendedToolCallId)
|
|
4767
|
+
continue;
|
|
4768
|
+
// AI SDK emits finish before awaiting onEnd. Publish success and usage
|
|
4769
|
+
// only after the context strategy and its registered work have settled.
|
|
4770
|
+
if (chunk.type === "finish") {
|
|
4771
|
+
const metadata = await contextFinished;
|
|
4772
|
+
chunk = {
|
|
4773
|
+
...chunk,
|
|
4774
|
+
finishReason: metadata.finishReason,
|
|
4775
|
+
messageMetadata: metadata,
|
|
4776
|
+
};
|
|
4777
|
+
if (metadata.sessionStatus === "waiting") {
|
|
4778
|
+
controller.enqueue(chunk);
|
|
4779
|
+
continue;
|
|
4780
|
+
}
|
|
4781
|
+
}
|
|
4782
|
+
events.push(chunk);
|
|
4800
4783
|
}
|
|
4801
|
-
if (
|
|
4802
|
-
await mutateSession({ events: [chunk] });
|
|
4803
|
-
controller.enqueue(chunk);
|
|
4784
|
+
if (events.length === 0)
|
|
4804
4785
|
return;
|
|
4805
|
-
|
|
4806
|
-
|
|
4807
|
-
|
|
4786
|
+
if (sessionExecution)
|
|
4787
|
+
await mutateSession({ events });
|
|
4788
|
+
else
|
|
4789
|
+
for (const event of events)
|
|
4790
|
+
await appendStreamEvent(event);
|
|
4791
|
+
for (const event of events)
|
|
4792
|
+
controller.enqueue(event);
|
|
4808
4793
|
},
|
|
4809
4794
|
}));
|
|
4810
4795
|
if (engine) {
|
package/dist/context-runtime.js
CHANGED
|
@@ -195,21 +195,53 @@ export function createAgentContextRuntime(input) {
|
|
|
195
195
|
});
|
|
196
196
|
},
|
|
197
197
|
async onEnd(stepNumber, end, runCallback = true) {
|
|
198
|
+
const endSpan = createAgentTracing().currentSpan;
|
|
198
199
|
const finalize = async () => {
|
|
200
|
+
const startedAt = performance.now();
|
|
201
|
+
let backgroundMs = 0;
|
|
202
|
+
let callbackMs = 0;
|
|
203
|
+
let compactMs = 0;
|
|
199
204
|
try {
|
|
200
|
-
const
|
|
201
|
-
|
|
202
|
-
await
|
|
203
|
-
|
|
204
|
-
|
|
205
|
-
|
|
205
|
+
const compactStartedAt = performance.now();
|
|
206
|
+
try {
|
|
207
|
+
const updated = await compact(false);
|
|
208
|
+
if (descriptor.type === "default" || offload !== false)
|
|
209
|
+
await persist(updated);
|
|
210
|
+
}
|
|
211
|
+
finally {
|
|
212
|
+
compactMs = performance.now() - compactStartedAt;
|
|
213
|
+
}
|
|
214
|
+
const backgroundStartedAt = performance.now();
|
|
215
|
+
try {
|
|
216
|
+
await settle();
|
|
217
|
+
}
|
|
218
|
+
finally {
|
|
219
|
+
backgroundMs += performance.now() - backgroundStartedAt;
|
|
220
|
+
}
|
|
221
|
+
if (runCallback && strategy.onEnd) {
|
|
222
|
+
const callbackStartedAt = performance.now();
|
|
223
|
+
try {
|
|
224
|
+
await invoke(stepNumber, (event) => strategy.onEnd({ ...event, ...end }));
|
|
225
|
+
}
|
|
226
|
+
finally {
|
|
227
|
+
callbackMs = performance.now() - callbackStartedAt;
|
|
228
|
+
}
|
|
229
|
+
}
|
|
206
230
|
}
|
|
207
231
|
finally {
|
|
232
|
+
const backgroundStartedAt = performance.now();
|
|
208
233
|
try {
|
|
209
234
|
await settle();
|
|
210
235
|
}
|
|
211
236
|
finally {
|
|
237
|
+
backgroundMs += performance.now() - backgroundStartedAt;
|
|
212
238
|
closed = true;
|
|
239
|
+
endSpan?.setAttributes({
|
|
240
|
+
"gea.session.finish.context_compact_ms": Math.round(compactMs),
|
|
241
|
+
"gea.session.finish.context_background_ms": Math.round(backgroundMs),
|
|
242
|
+
"gea.session.finish.context_callback_ms": Math.round(callbackMs),
|
|
243
|
+
"gea.session.finish.context_total_ms": Math.round(performance.now() - startedAt),
|
|
244
|
+
});
|
|
213
245
|
}
|
|
214
246
|
}
|
|
215
247
|
};
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@gea-ai/agent-sdk",
|
|
3
|
-
"version": "0.1.260922-alpha.
|
|
3
|
+
"version": "0.1.260922-alpha.3",
|
|
4
4
|
"private": false,
|
|
5
5
|
"homepage": "https://musegea.com/developers",
|
|
6
6
|
"license": "Apache-2.0",
|
|
@@ -108,7 +108,7 @@
|
|
|
108
108
|
"@ai-sdk/otel": "1.0.9",
|
|
109
109
|
"@ai-sdk/provider": "4.0.1",
|
|
110
110
|
"@ai-sdk/provider-utils": "5.0.2",
|
|
111
|
-
"@gea-ai/contract": "0.1.260922-alpha.
|
|
111
|
+
"@gea-ai/contract": "0.1.260922-alpha.3",
|
|
112
112
|
"@opentelemetry/api": "1.9.1",
|
|
113
113
|
"@opentelemetry/context-async-hooks": "2.10.0",
|
|
114
114
|
"@opentelemetry/core": "2.10.0",
|