@gea-ai/agent-sdk 0.1.260922-alpha.0 → 0.1.260922-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/dist/agent-session-execution.js +31 -11
- package/dist/agent-worker.js +73 -37
- package/package.json +2 -2
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import { getSessionPayload, putSessionPayload } from "./agent-session-payload.js";
|
|
2
2
|
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, } from "@gea-ai/contract/agent-session";
|
|
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
4
|
import { z } from "zod";
|
|
5
5
|
import { activeComputerOperation } from "./agent-session-computer.js";
|
|
6
6
|
import { isAgentSessionInboxBusy, scheduleAgentSessionInbox, getAgentSessionRunState, handleAgentSessionInboxRequest, hasAgentSessionInbox, skipAgentSessionInputWait, } from "./agent-session-inbox.js";
|
|
@@ -335,17 +335,28 @@ export async function handleAgentSessionExecutionRequest(request, storage) {
|
|
|
335
335
|
return { computer };
|
|
336
336
|
}));
|
|
337
337
|
}
|
|
338
|
+
const capabilities = {
|
|
339
|
+
protocolVersion: AGENT_SESSION_EXECUTION_PROTOCOL_VERSION,
|
|
340
|
+
managedExecutionVersion: 1,
|
|
341
|
+
logicalRunVersion: 1,
|
|
342
|
+
};
|
|
338
343
|
if (request.method === "GET" && url.pathname === "/v2/capabilities")
|
|
339
|
-
return json(
|
|
340
|
-
protocolVersion: AGENT_SESSION_EXECUTION_PROTOCOL_VERSION,
|
|
341
|
-
managedExecutionVersion: 1,
|
|
342
|
-
logicalRunVersion: 1,
|
|
343
|
-
});
|
|
344
|
+
return json(capabilities);
|
|
344
345
|
if (request.method === "GET" && url.pathname === "/v2/run-state")
|
|
345
346
|
return json(await storage.transaction((tx) => getAgentSessionRunState(tx, url.searchParams.get("runId") ?? "")));
|
|
346
|
-
if (request.method === "POST" &&
|
|
347
|
-
|
|
348
|
-
|
|
347
|
+
if (request.method === "POST" &&
|
|
348
|
+
(url.pathname === "/v2/turns/start" ||
|
|
349
|
+
url.pathname === "/v2/turns/prepare")) {
|
|
350
|
+
const preparing = url.pathname === "/v2/turns/prepare";
|
|
351
|
+
const body = await readBody(request, storage);
|
|
352
|
+
const preparation = preparing
|
|
353
|
+
? agentSessionExecutionPrepareSchema.parse(body)
|
|
354
|
+
: undefined;
|
|
355
|
+
const { resumeWaiting = false, ...input } = preparation ?? {
|
|
356
|
+
...agentSessionExecutionStartSchema.parse(body),
|
|
357
|
+
resumeWaiting: false,
|
|
358
|
+
};
|
|
359
|
+
const result = await storage.transaction(async (tx) => {
|
|
349
360
|
if (await activeComputerOperation(tx))
|
|
350
361
|
throw new SessionProtocolError("computer_busy");
|
|
351
362
|
if (await tx.get(key(`outcome:${encodeURIComponent(input.runId)}`)))
|
|
@@ -353,6 +364,10 @@ export async function handleAgentSessionExecutionRequest(request, storage) {
|
|
|
353
364
|
const previousRevision = await readRevision(tx);
|
|
354
365
|
const active = await tx.get(key("active-turn"));
|
|
355
366
|
const resuming = active?.status === "waiting";
|
|
367
|
+
// Resolve only a waiting continuation under the admission lock. Expired
|
|
368
|
+
// running attempts still require an explicit expectedAttemptId.
|
|
369
|
+
const expectedAttemptId = input.expectedAttemptId ??
|
|
370
|
+
(resumeWaiting && resuming ? active.attemptId : undefined);
|
|
356
371
|
if (active) {
|
|
357
372
|
const sameSelection = active.runId === input.runId &&
|
|
358
373
|
active.packageVersionId === input.packageVersionId &&
|
|
@@ -367,7 +382,7 @@ export async function handleAgentSessionExecutionRequest(request, storage) {
|
|
|
367
382
|
throw new SessionProtocolError("turn_already_active");
|
|
368
383
|
}
|
|
369
384
|
if (!sameSelection ||
|
|
370
|
-
|
|
385
|
+
expectedAttemptId !== active.attemptId ||
|
|
371
386
|
input.requestId === active.requestId)
|
|
372
387
|
throw new SessionProtocolError("takeover_mismatch");
|
|
373
388
|
if (!resuming &&
|
|
@@ -406,7 +421,8 @@ export async function handleAgentSessionExecutionRequest(request, storage) {
|
|
|
406
421
|
});
|
|
407
422
|
const revision = await advanceRevision(tx);
|
|
408
423
|
return { duplicate: false, revision, turn };
|
|
409
|
-
})
|
|
424
|
+
});
|
|
425
|
+
return json(preparing ? { ...result, capabilities } : result);
|
|
410
426
|
}
|
|
411
427
|
if (request.method === "POST" && url.pathname === "/v2/turns/renew") {
|
|
412
428
|
const input = agentSessionExecutionRenewSchema.parse(await readBody(request, storage));
|
|
@@ -512,6 +528,10 @@ export async function handleAgentSessionExecutionRequest(request, storage) {
|
|
|
512
528
|
const state = await getAgentSessionRunState(tx, input.runId);
|
|
513
529
|
if (state?.status === "draining")
|
|
514
530
|
throw new SessionProtocolError("run_cleanup_pending");
|
|
531
|
+
// An invocation lease may still be live after a child wake claims its
|
|
532
|
+
// continuation. Only the inbox can seal that logical Run's outcome.
|
|
533
|
+
if (state?.status === "running" || state?.status === "waiting")
|
|
534
|
+
throw new SessionProtocolError("run_not_terminal");
|
|
515
535
|
const terminalStatus = state?.status === "terminal"
|
|
516
536
|
? {
|
|
517
537
|
completed: "finished",
|
package/dist/agent-worker.js
CHANGED
|
@@ -2724,6 +2724,7 @@ export async function runAgentWorkerRequest(input) {
|
|
|
2724
2724
|
if (finished)
|
|
2725
2725
|
return finished;
|
|
2726
2726
|
finished = (async () => {
|
|
2727
|
+
const cleanupStartedAt = performance.now();
|
|
2727
2728
|
// AI SDK's onEnd may still write context artifacts after a client
|
|
2728
2729
|
// disconnects. Settle the model/tools before releasing their filesystem.
|
|
2729
2730
|
const results = [];
|
|
@@ -2733,10 +2734,13 @@ export async function runAgentWorkerRequest(input) {
|
|
|
2733
2734
|
drained = background.length;
|
|
2734
2735
|
results.push(...(await Promise.allSettled(batch)));
|
|
2735
2736
|
}
|
|
2737
|
+
const drainedAt = performance.now();
|
|
2736
2738
|
if (stopComputer)
|
|
2737
2739
|
results.push(...(await Promise.allSettled([stopComputer()])));
|
|
2740
|
+
const computerStoppedAt = performance.now();
|
|
2738
2741
|
if (input.onSessionFinished)
|
|
2739
2742
|
results.push(...(await Promise.allSettled([input.onSessionFinished()])));
|
|
2743
|
+
const sessionFinishedAt = performance.now();
|
|
2740
2744
|
// Keep the Worker lease through draining and Computer cleanup. The
|
|
2741
2745
|
// host still owns terminal accounting; hand it a fresh lease only once
|
|
2742
2746
|
// this invocation has stopped writing, then stop all Worker heartbeats.
|
|
@@ -2756,6 +2760,12 @@ export async function runAgentWorkerRequest(input) {
|
|
|
2756
2760
|
}),
|
|
2757
2761
|
])));
|
|
2758
2762
|
}
|
|
2763
|
+
invocationSpan?.setAttributes({
|
|
2764
|
+
"gea.session.finish.background_ms": Math.round(drainedAt - cleanupStartedAt),
|
|
2765
|
+
"gea.session.finish.computer_stop_ms": Math.round(computerStoppedAt - drainedAt),
|
|
2766
|
+
"gea.session.finish.session_release_ms": Math.round(sessionFinishedAt - computerStoppedAt),
|
|
2767
|
+
"gea.session.finish.lease_handoff_ms": Math.round(performance.now() - sessionFinishedAt),
|
|
2768
|
+
});
|
|
2759
2769
|
if (input.inboxExecution) {
|
|
2760
2770
|
const status = cancellation.signal.aborted
|
|
2761
2771
|
? "cancelled"
|
|
@@ -2848,7 +2858,6 @@ export async function runAgentWorkerRequest(input) {
|
|
|
2848
2858
|
let response;
|
|
2849
2859
|
try {
|
|
2850
2860
|
if (input.sessionExecution) {
|
|
2851
|
-
await maintainManagedLease();
|
|
2852
2861
|
cancellation.signal.throwIfAborted();
|
|
2853
2862
|
watchManagedLease();
|
|
2854
2863
|
}
|
|
@@ -2971,6 +2980,10 @@ async function executeAgentWorkerRequest(input, waitUntil, ownComputer, ownSessi
|
|
|
2971
2980
|
path: "/v2/turns/renew",
|
|
2972
2981
|
schema: agentSessionExecutionStartResultSchema,
|
|
2973
2982
|
});
|
|
2983
|
+
// Renewal atomically validates the attempt and returns cancellation intent;
|
|
2984
|
+
// a separate metadata read before it only adds a network round trip.
|
|
2985
|
+
if (admission.turn.cancelRequestedAt)
|
|
2986
|
+
throw new Error("Agent Session cancelled.");
|
|
2974
2987
|
mutationSequence = admission.turn.nextMutationSequence;
|
|
2975
2988
|
}
|
|
2976
2989
|
const mutateSession = (values) => {
|
|
@@ -3508,25 +3521,28 @@ async function executeAgentWorkerRequest(input, waitUntil, ownComputer, ownSessi
|
|
|
3508
3521
|
? (await import("./agent-core.js")).agentCore()
|
|
3509
3522
|
: input.engine;
|
|
3510
3523
|
const coreModule = engine?.module();
|
|
3511
|
-
|
|
3512
|
-
|
|
3513
|
-
|
|
3514
|
-
|
|
3515
|
-
|
|
3516
|
-
|
|
3517
|
-
|
|
3518
|
-
|
|
3519
|
-
|
|
3524
|
+
const [initialCoreConnection, model, storedContext] = await Promise.all([
|
|
3525
|
+
engine
|
|
3526
|
+
? createGeaCoreConnection(host, configuredModel, requestOptions, input.request.signal, input.snapshot.maxOutputTokens)
|
|
3527
|
+
: undefined,
|
|
3528
|
+
engine
|
|
3529
|
+
? undefined
|
|
3530
|
+
: createGeaAI(host, configuredModel, requestOptions.extraBody, {
|
|
3531
|
+
kind: "agent-dev",
|
|
3532
|
+
packageName: input.snapshot.name,
|
|
3533
|
+
}, requestOptions.requestBody),
|
|
3534
|
+
requestSessionJson({
|
|
3535
|
+
chatId: payload.chatId,
|
|
3536
|
+
method: "GET",
|
|
3537
|
+
namespace: input.sessionNamespace,
|
|
3538
|
+
path: sessionExecution ? "/v2/model-context" : "/v1/model-context",
|
|
3539
|
+
schema: agentSessionGetModelContextResultSchema,
|
|
3540
|
+
}),
|
|
3541
|
+
]);
|
|
3542
|
+
let coreConnection = initialCoreConnection;
|
|
3520
3543
|
// Model file parts can contain URL and byte values that ordinary JSON cannot preserve.
|
|
3521
3544
|
const runtimeContext = decodeAgentModelProxyValue(payload.runtimeContext);
|
|
3522
3545
|
let currentTurn = decodeAgentModelProxyValue(payload.currentTurn);
|
|
3523
|
-
const storedContext = await requestSessionJson({
|
|
3524
|
-
chatId: payload.chatId,
|
|
3525
|
-
method: "GET",
|
|
3526
|
-
namespace: input.sessionNamespace,
|
|
3527
|
-
path: sessionExecution ? "/v2/model-context" : "/v1/model-context",
|
|
3528
|
-
schema: agentSessionGetModelContextResultSchema,
|
|
3529
|
-
});
|
|
3530
3546
|
if (submittedUser &&
|
|
3531
3547
|
storedContext.context &&
|
|
3532
3548
|
storedContext.context.runId !== payload.runId) {
|
|
@@ -3814,25 +3830,35 @@ async function executeAgentWorkerRequest(input, waitUntil, ownComputer, ownSessi
|
|
|
3814
3830
|
}));
|
|
3815
3831
|
}
|
|
3816
3832
|
let mergedResponseMessageCount = 0;
|
|
3817
|
-
|
|
3833
|
+
let contextCheckpoint;
|
|
3834
|
+
let reusedContextCheckpoints = 0;
|
|
3835
|
+
const persistModelContext = () => {
|
|
3818
3836
|
const modelContext = {
|
|
3819
3837
|
contextSize: context.contextSize,
|
|
3820
3838
|
messages: context.messages.map(encodeAgentModelProxyValue),
|
|
3821
|
-
metadata: context.metadata,
|
|
3839
|
+
metadata: structuredClone(context.metadata),
|
|
3822
3840
|
runId: payload.runId,
|
|
3823
3841
|
};
|
|
3824
|
-
|
|
3825
|
-
|
|
3826
|
-
|
|
3842
|
+
const serialized = JSON.stringify(modelContext);
|
|
3843
|
+
// Lifecycle callbacks can checkpoint the same projection several times.
|
|
3844
|
+
// Reuse the pending write too: an unchanged snapshot must still await its
|
|
3845
|
+
// durable commit, including propagating a failed commit to every waiter.
|
|
3846
|
+
if (contextCheckpoint?.serialized === serialized) {
|
|
3847
|
+
persistenceSpan?.setAttribute("gea.session.persistence.context_reused", ++reusedContextCheckpoints);
|
|
3848
|
+
return contextCheckpoint.pending;
|
|
3827
3849
|
}
|
|
3828
|
-
|
|
3829
|
-
|
|
3830
|
-
|
|
3831
|
-
|
|
3832
|
-
|
|
3833
|
-
|
|
3834
|
-
|
|
3835
|
-
|
|
3850
|
+
const pending = sessionExecution
|
|
3851
|
+
? mutateSession({ context: modelContext })
|
|
3852
|
+
: requestSessionJson({
|
|
3853
|
+
body: { context: modelContext },
|
|
3854
|
+
chatId: payload.chatId,
|
|
3855
|
+
method: "POST",
|
|
3856
|
+
namespace: input.sessionNamespace,
|
|
3857
|
+
path: "/v1/model-context",
|
|
3858
|
+
schema: agentSessionPutModelContextResultSchema,
|
|
3859
|
+
}).then(() => undefined);
|
|
3860
|
+
contextCheckpoint = { serialized, pending };
|
|
3861
|
+
return pending;
|
|
3836
3862
|
};
|
|
3837
3863
|
if (startsNewTurn && findLatestUserMessage(currentTurn)) {
|
|
3838
3864
|
await persistModelContext();
|
|
@@ -4095,9 +4121,16 @@ async function executeAgentWorkerRequest(input, waitUntil, ownComputer, ownSessi
|
|
|
4095
4121
|
cursor = page.next_cursor ?? undefined;
|
|
4096
4122
|
} while (cursor);
|
|
4097
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()));
|
|
4098
4129
|
const saveResponseMessage = async ({ responseMessage, }) => {
|
|
4099
4130
|
const saveStartedAt = performance.now();
|
|
4100
|
-
await
|
|
4131
|
+
const metadata = await contextFinished;
|
|
4132
|
+
const contextFinishedAt = performance.now();
|
|
4133
|
+
await refreshFinalArtifacts();
|
|
4101
4134
|
const artifactsFinishedAt = performance.now();
|
|
4102
4135
|
responseMessage.parts = [
|
|
4103
4136
|
...responseMessage.parts.filter((part) => part.type !== "data-artifact" ||
|
|
@@ -4123,9 +4156,9 @@ async function executeAgentWorkerRequest(input, waitUntil, ownComputer, ownSessi
|
|
|
4123
4156
|
context.metadata.suspendedMessageId = responseMessage.id;
|
|
4124
4157
|
responseMessage.metadata = {
|
|
4125
4158
|
...responseMessage.metadata,
|
|
4126
|
-
...
|
|
4159
|
+
...metadata,
|
|
4127
4160
|
};
|
|
4128
|
-
const
|
|
4161
|
+
const transcriptStartedAt = performance.now();
|
|
4129
4162
|
// Retain the generated transcript even if context settlement failed.
|
|
4130
4163
|
// A failed settlement must never produce a successful Run completion.
|
|
4131
4164
|
if (sessionExecution) {
|
|
@@ -4145,9 +4178,9 @@ async function executeAgentWorkerRequest(input, waitUntil, ownComputer, ownSessi
|
|
|
4145
4178
|
await persistModelContext();
|
|
4146
4179
|
const contextPersistedAt = performance.now();
|
|
4147
4180
|
persistenceSpan?.setAttributes({
|
|
4148
|
-
"gea.session.finish.artifacts_ms": Math.round(artifactsFinishedAt -
|
|
4149
|
-
"gea.session.finish.context_wait_ms": Math.round(contextFinishedAt -
|
|
4150
|
-
"gea.session.finish.transcript_ms": Math.round(transcriptFinishedAt -
|
|
4181
|
+
"gea.session.finish.artifacts_ms": Math.round(artifactsFinishedAt - contextFinishedAt),
|
|
4182
|
+
"gea.session.finish.context_wait_ms": Math.round(contextFinishedAt - saveStartedAt),
|
|
4183
|
+
"gea.session.finish.transcript_ms": Math.round(transcriptFinishedAt - transcriptStartedAt),
|
|
4151
4184
|
"gea.session.finish.context_write_ms": Math.round(contextPersistedAt - transcriptFinishedAt),
|
|
4152
4185
|
});
|
|
4153
4186
|
const needsInput = !inboxRunner?.suspended &&
|
|
@@ -4735,7 +4768,10 @@ async function executeAgentWorkerRequest(input, waitUntil, ownComputer, ownSessi
|
|
|
4735
4768
|
chunk.toolCallId === inboxRunner?.suspendedToolCallId)
|
|
4736
4769
|
return;
|
|
4737
4770
|
if (chunk.type === "finish-step" || chunk.type === "finish") {
|
|
4738
|
-
|
|
4771
|
+
if (chunk.type === "finish")
|
|
4772
|
+
await refreshFinalArtifacts();
|
|
4773
|
+
else
|
|
4774
|
+
await refreshArtifacts();
|
|
4739
4775
|
for (const data of publishedArtifacts.values()) {
|
|
4740
4776
|
if (emittedArtifacts.has(data.id))
|
|
4741
4777
|
continue;
|
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.2",
|
|
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.2",
|
|
112
112
|
"@opentelemetry/api": "1.9.1",
|
|
113
113
|
"@opentelemetry/context-async-hooks": "2.10.0",
|
|
114
114
|
"@opentelemetry/core": "2.10.0",
|