@gea-ai/agent-sdk 0.1.261008-alpha.0 → 0.1.261009-alpha.1
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/THIRD_PARTY_NOTICES.md +7 -0
- package/dist/agent-core.wasm +0 -0
- package/dist/agent-session-inbox.js +12 -7
- package/dist/agent-worker-context.js +238 -8
- package/dist/agent-worker.js +89 -49
- package/dist/index.d.ts +3 -1
- package/dist/index.js +4 -0
- package/dist/version.d.ts +1 -1
- package/dist/version.js +1 -1
- package/package.json +2 -2
- package/third-party/anthropic-schema/LICENSE +13 -0
package/THIRD_PARTY_NOTICES.md
CHANGED
|
@@ -58,6 +58,13 @@ and packaged Worker conformance fixtures before changing the pin.
|
|
|
58
58
|
|
|
59
59
|
## Agent Core WASM
|
|
60
60
|
|
|
61
|
+
The bundled Core's Anthropic output-schema sanitizer is ported from
|
|
62
|
+
`@ai-sdk/anthropic` **4.0.67**, `src/sanitize-json-schema.ts` (Vercel AI SDK).
|
|
63
|
+
Copyright 2023 Vercel, Inc.; see
|
|
64
|
+
[license and notice](./third-party/anthropic-schema/LICENSE).
|
|
65
|
+
The Rust port retains the upstream transformation using `serde_json` values;
|
|
66
|
+
the original schema still controls final validation.
|
|
67
|
+
|
|
61
68
|
The optional Agent Core asset is built from this repository's Apache-2.0 Rust
|
|
62
69
|
crate with Rust 1.94.1. Its Cargo dependencies are pinned in
|
|
63
70
|
`crates/agent-core/Cargo.lock`; exact-version license copies and a dependency
|
package/dist/agent-core.wasm
CHANGED
|
Binary file
|
|
@@ -633,13 +633,18 @@ export async function handleAgentSessionInboxRequest(request, storage) {
|
|
|
633
633
|
canonicalAgentChannelJson(input.identity))
|
|
634
634
|
return Response.json({ error: "session_identity_conflict" }, { status: 409 });
|
|
635
635
|
if (state.execution?.id === input.executionId) {
|
|
636
|
-
if (
|
|
637
|
-
|
|
638
|
-
|
|
639
|
-
|
|
640
|
-
|
|
641
|
-
}
|
|
642
|
-
|
|
636
|
+
if (input.runId && input.runId !== state.execution.turnId)
|
|
637
|
+
return Response.json({ error: "execution_mismatch" }, { status: 409 });
|
|
638
|
+
if (path === "/v1/inbox/start" && state.started)
|
|
639
|
+
return Response.json({ error: "execution_already_started" }, {
|
|
640
|
+
status: 409,
|
|
641
|
+
headers: { "x-gea-session-execution": "already-started" },
|
|
642
|
+
});
|
|
643
|
+
if (!state.started) {
|
|
644
|
+
// An inbox claim retains the previous transport until the Host
|
|
645
|
+
// admits this execution with its current Run authority.
|
|
646
|
+
state.transport = input.transport;
|
|
647
|
+
state.started = path === "/v1/inbox/start";
|
|
643
648
|
await save(tx, state, runs);
|
|
644
649
|
}
|
|
645
650
|
return Response.json({ execution: state.execution });
|
|
@@ -66,6 +66,62 @@ function groupMessages(messages) {
|
|
|
66
66
|
}
|
|
67
67
|
return groups;
|
|
68
68
|
}
|
|
69
|
+
// Provider usage is request-wide, not per message. This deliberately estimates
|
|
70
|
+
// content only: UTF-8 bytes / 3 plus framing, without opaque reasoning signatures
|
|
71
|
+
// or base64 expansion. Media gets a fixed allowance, not an exact token count.
|
|
72
|
+
function estimateMessageTokens(message) {
|
|
73
|
+
const textTokens = (text) => Math.ceil(new TextEncoder().encode(text).length / 3);
|
|
74
|
+
const mediaTokens = 4_096;
|
|
75
|
+
if (typeof message.content === "string")
|
|
76
|
+
return 8 + textTokens(message.content);
|
|
77
|
+
return (8 +
|
|
78
|
+
message.content.reduce((total, part) => {
|
|
79
|
+
switch (part.type) {
|
|
80
|
+
case "text":
|
|
81
|
+
case "reasoning":
|
|
82
|
+
return total + 8 + textTokens(part.text);
|
|
83
|
+
case "image":
|
|
84
|
+
case "reasoning-file":
|
|
85
|
+
return total + mediaTokens;
|
|
86
|
+
case "file":
|
|
87
|
+
return (total +
|
|
88
|
+
(typeof part.data === "object" &&
|
|
89
|
+
"type" in part.data &&
|
|
90
|
+
part.data.type === "text"
|
|
91
|
+
? textTokens(part.data.text)
|
|
92
|
+
: mediaTokens));
|
|
93
|
+
case "tool-call":
|
|
94
|
+
return (total +
|
|
95
|
+
16 +
|
|
96
|
+
textTokens(part.toolName) +
|
|
97
|
+
textTokens(JSON.stringify(part.input) ?? ""));
|
|
98
|
+
case "tool-result": {
|
|
99
|
+
const output = part.output;
|
|
100
|
+
let tokens;
|
|
101
|
+
switch (output.type) {
|
|
102
|
+
case "content":
|
|
103
|
+
tokens = output.value.reduce((count, item) => count +
|
|
104
|
+
(item.type === "text"
|
|
105
|
+
? textTokens(item.text)
|
|
106
|
+
: item.type === "file" && item.data.type === "text"
|
|
107
|
+
? textTokens(item.data.text)
|
|
108
|
+
: mediaTokens), 0);
|
|
109
|
+
break;
|
|
110
|
+
case "text":
|
|
111
|
+
case "error-text":
|
|
112
|
+
tokens = textTokens(output.value);
|
|
113
|
+
break;
|
|
114
|
+
default:
|
|
115
|
+
tokens = textTokens(JSON.stringify(output));
|
|
116
|
+
}
|
|
117
|
+
return total + 16 + textTokens(part.toolName) + tokens;
|
|
118
|
+
}
|
|
119
|
+
default: {
|
|
120
|
+
return total + 8 + textTokens(JSON.stringify(part));
|
|
121
|
+
}
|
|
122
|
+
}
|
|
123
|
+
}, 0));
|
|
124
|
+
}
|
|
69
125
|
function getToolOutputText(part) {
|
|
70
126
|
switch (part.output.type) {
|
|
71
127
|
case "error-text":
|
|
@@ -224,7 +280,7 @@ export async function applyAgentWorkerToolOutputOffload(input) {
|
|
|
224
280
|
return input.messages;
|
|
225
281
|
const groups = groupMessages(input.messages);
|
|
226
282
|
const firstProtectedIndex = input.protectRecentGroups
|
|
227
|
-
? Math.max(0, groups.length - input.options.preserveRecentGroups)
|
|
283
|
+
? Math.max(0, groups.length - (input.options.preserveRecentGroups ?? 5))
|
|
228
284
|
: groups.length;
|
|
229
285
|
const messages = [];
|
|
230
286
|
for (const [groupIndex, group] of groups.entries()) {
|
|
@@ -263,13 +319,135 @@ function summarySchema() {
|
|
|
263
319
|
"## Critical Exact Details\n- Preserve exact IDs, commands, paths, API names, error messages, and config values.",
|
|
264
320
|
].join("\n\n");
|
|
265
321
|
}
|
|
322
|
+
// Summary input is a textual projection, not a provider request. Keep resource
|
|
323
|
+
// references for recovery but never stringify media bytes or opaque metadata.
|
|
324
|
+
function summaryFileData(data) {
|
|
325
|
+
if (data instanceof URL) {
|
|
326
|
+
return data.protocol === "data:"
|
|
327
|
+
? { omitted: "inline media bytes" }
|
|
328
|
+
: { url: data.href };
|
|
329
|
+
}
|
|
330
|
+
if (typeof data === "string" ||
|
|
331
|
+
data instanceof ArrayBuffer ||
|
|
332
|
+
ArrayBuffer.isView(data)) {
|
|
333
|
+
return { omitted: "inline media bytes; recover from raw chat history" };
|
|
334
|
+
}
|
|
335
|
+
if ("type" in data) {
|
|
336
|
+
switch (data.type) {
|
|
337
|
+
case "text":
|
|
338
|
+
return { text: data.text };
|
|
339
|
+
case "url": {
|
|
340
|
+
const url = String(data.url);
|
|
341
|
+
return url.startsWith("data:")
|
|
342
|
+
? { omitted: "inline media bytes" }
|
|
343
|
+
: { url };
|
|
344
|
+
}
|
|
345
|
+
case "reference":
|
|
346
|
+
return { reference: data.reference };
|
|
347
|
+
case "data":
|
|
348
|
+
return { omitted: "inline media bytes; recover from raw chat history" };
|
|
349
|
+
}
|
|
350
|
+
}
|
|
351
|
+
return { reference: data };
|
|
352
|
+
}
|
|
353
|
+
function summaryContentPart(part) {
|
|
354
|
+
switch (part.type) {
|
|
355
|
+
case "text":
|
|
356
|
+
case "reasoning":
|
|
357
|
+
return { type: part.type, text: part.text };
|
|
358
|
+
case "tool-call":
|
|
359
|
+
return {
|
|
360
|
+
type: part.type,
|
|
361
|
+
toolName: part.toolName,
|
|
362
|
+
toolCallId: part.toolCallId,
|
|
363
|
+
input: part.input,
|
|
364
|
+
providerExecuted: part.providerExecuted,
|
|
365
|
+
};
|
|
366
|
+
case "tool-result": {
|
|
367
|
+
const output = part.output;
|
|
368
|
+
return {
|
|
369
|
+
type: part.type,
|
|
370
|
+
toolName: part.toolName,
|
|
371
|
+
toolCallId: part.toolCallId,
|
|
372
|
+
output: output.type === "content"
|
|
373
|
+
? { type: output.type, value: output.value.map(summaryContentPart) }
|
|
374
|
+
: output.type === "execution-denied"
|
|
375
|
+
? { type: output.type, reason: output.reason }
|
|
376
|
+
: { type: output.type, value: output.value },
|
|
377
|
+
};
|
|
378
|
+
}
|
|
379
|
+
case "file":
|
|
380
|
+
case "file-data":
|
|
381
|
+
return {
|
|
382
|
+
type: part.type,
|
|
383
|
+
mediaType: part.mediaType,
|
|
384
|
+
filename: part.filename,
|
|
385
|
+
data: summaryFileData(part.data),
|
|
386
|
+
};
|
|
387
|
+
case "image":
|
|
388
|
+
return {
|
|
389
|
+
type: part.type,
|
|
390
|
+
mediaType: part.mediaType,
|
|
391
|
+
data: summaryFileData(part.image),
|
|
392
|
+
};
|
|
393
|
+
case "image-data":
|
|
394
|
+
case "reasoning-file":
|
|
395
|
+
return {
|
|
396
|
+
type: part.type,
|
|
397
|
+
mediaType: part.mediaType,
|
|
398
|
+
data: summaryFileData(part.data),
|
|
399
|
+
};
|
|
400
|
+
case "file-url":
|
|
401
|
+
case "image-url":
|
|
402
|
+
return {
|
|
403
|
+
type: part.type,
|
|
404
|
+
mediaType: "mediaType" in part ? part.mediaType : undefined,
|
|
405
|
+
url: part.url.startsWith("data:") ? "[inline media omitted]" : part.url,
|
|
406
|
+
};
|
|
407
|
+
case "file-id":
|
|
408
|
+
case "image-file-id":
|
|
409
|
+
return { type: part.type, fileId: part.fileId };
|
|
410
|
+
case "file-reference":
|
|
411
|
+
case "image-file-reference":
|
|
412
|
+
return { type: part.type, reference: part.providerReference };
|
|
413
|
+
case "custom":
|
|
414
|
+
return {
|
|
415
|
+
type: part.type,
|
|
416
|
+
kind: "kind" in part ? part.kind : undefined,
|
|
417
|
+
omitted: "opaque provider content; recover from raw chat history",
|
|
418
|
+
};
|
|
419
|
+
case "tool-approval-request":
|
|
420
|
+
case "tool-approval-response":
|
|
421
|
+
return part;
|
|
422
|
+
}
|
|
423
|
+
}
|
|
424
|
+
function summaryMessage(message) {
|
|
425
|
+
return {
|
|
426
|
+
role: message.role,
|
|
427
|
+
content: typeof message.content === "string"
|
|
428
|
+
? message.content
|
|
429
|
+
: message.content.map(summaryContentPart),
|
|
430
|
+
};
|
|
431
|
+
}
|
|
266
432
|
function buildSummaryMessages(input) {
|
|
433
|
+
const latestRequest = [...input.recentTailMessages]
|
|
434
|
+
.reverse()
|
|
435
|
+
.find((message) => message.role === "user");
|
|
436
|
+
const requestText = latestRequest
|
|
437
|
+
? JSON.stringify(summaryMessage(latestRequest))
|
|
438
|
+
: "(no new user request in the retained tail)";
|
|
439
|
+
// The tail remains verbatim in the main context. A bounded task excerpt is
|
|
440
|
+
// enough for the summarizer; duplicating retained tool payloads adds no memory.
|
|
441
|
+
const requestExcerpt = requestText.length <= 6_000
|
|
442
|
+
? requestText
|
|
443
|
+
: `${requestText.slice(0, 6_000)}\n[excerpt truncated; the complete request remains in the retained context]`;
|
|
267
444
|
return [
|
|
268
445
|
{
|
|
269
446
|
role: "system",
|
|
270
447
|
content: [
|
|
271
448
|
"Update the compact working-memory summary for an agent.",
|
|
272
449
|
"Merge the previous summary with the older messages being compacted.",
|
|
450
|
+
"The recent user request is orientation only. Recent messages remain verbatim; do not repeat them in the summary. Omitted media and provider content remain in raw chat history; do not infer their contents.",
|
|
273
451
|
"Preserve requirements, decisions, exact paths, commands, IDs, errors, tool observations, offloaded artifact references, open questions, and next steps.",
|
|
274
452
|
"Do not invent facts, expose hidden reasoning, or issue new instructions.",
|
|
275
453
|
"Do not copy full offloaded artifacts. Keep their paths and relevant preview facts.",
|
|
@@ -284,11 +462,11 @@ function buildSummaryMessages(input) {
|
|
|
284
462
|
input.previousSummary ?? "(none)",
|
|
285
463
|
"</previous_summary>",
|
|
286
464
|
"<messages>",
|
|
287
|
-
JSON.stringify(input.compactableMessages
|
|
465
|
+
JSON.stringify(input.compactableMessages.map(summaryMessage)),
|
|
288
466
|
"</messages>",
|
|
289
|
-
"<
|
|
290
|
-
|
|
291
|
-
"</
|
|
467
|
+
"<recent_user_request>",
|
|
468
|
+
requestExcerpt,
|
|
469
|
+
"</recent_user_request>",
|
|
292
470
|
].join("\n"),
|
|
293
471
|
},
|
|
294
472
|
];
|
|
@@ -389,7 +567,44 @@ export async function compactAgentWorkerContext(input) {
|
|
|
389
567
|
return { summaryUsage: null, messagesUpdated };
|
|
390
568
|
}
|
|
391
569
|
const groups = groupMessages(input.context.messages);
|
|
392
|
-
const
|
|
570
|
+
const recentTokenBudget = options.preserveRecentTokens ??
|
|
571
|
+
Math.max(1, Math.min(16_000, Math.floor(summarization.triggerAtTotalTokens / 4)));
|
|
572
|
+
const groupEstimates = groups.map((group) => group.messages.reduce((total, message) => total + estimateMessageTokens(message), 0));
|
|
573
|
+
const previousSummary = input.context.metadata.contextSummary?.summaryText;
|
|
574
|
+
const projectionEstimate = groupEstimates.reduce((total, tokens) => total + tokens, 0) +
|
|
575
|
+
(previousSummary
|
|
576
|
+
? estimateMessageTokens(makeSummaryMessage(previousSummary))
|
|
577
|
+
: 0);
|
|
578
|
+
// Usage includes fixed prompt overhead we cannot attribute to individual messages.
|
|
579
|
+
// Allocate it conservatively across the projection instead of treating small
|
|
580
|
+
// local estimates as proof that an over-threshold request already fits.
|
|
581
|
+
const usageScale = Math.max(1, (input.context.contextSize ?? 0) / Math.max(1, projectionEstimate));
|
|
582
|
+
let splitIndex = groups.length;
|
|
583
|
+
let retainedTokens = 0;
|
|
584
|
+
while (splitIndex > 0) {
|
|
585
|
+
const groupTokens = Math.ceil(groupEstimates[splitIndex - 1] * usageScale);
|
|
586
|
+
// Always keep the newest complete group, even when it exceeds the budget.
|
|
587
|
+
if (splitIndex < groups.length &&
|
|
588
|
+
(retainedTokens + groupTokens > recentTokenBudget ||
|
|
589
|
+
(options.preserveRecentGroups !== undefined &&
|
|
590
|
+
groups.length - splitIndex >= options.preserveRecentGroups)))
|
|
591
|
+
break;
|
|
592
|
+
retainedTokens += groupTokens;
|
|
593
|
+
splitIndex--;
|
|
594
|
+
}
|
|
595
|
+
// Fixed overhead alone is not a reason to pay for summarizing a tiny history.
|
|
596
|
+
// Explicit retention limits still take precedence over this default safeguard.
|
|
597
|
+
if (options.preserveRecentTokens === undefined &&
|
|
598
|
+
options.preserveRecentGroups === undefined &&
|
|
599
|
+
groupEstimates
|
|
600
|
+
.slice(0, splitIndex)
|
|
601
|
+
.reduce((total, tokens) => total + tokens, 0) <
|
|
602
|
+
Math.max(1, Math.min(1_024, Math.floor(recentTokenBudget / 4)))) {
|
|
603
|
+
createAgentTracing().currentSpan?.setAttributes({
|
|
604
|
+
"gea.context.compaction.skip_reason": "insufficient_reduction",
|
|
605
|
+
});
|
|
606
|
+
return { summaryUsage: null, messagesUpdated };
|
|
607
|
+
}
|
|
393
608
|
const compactableMessages = groups
|
|
394
609
|
.slice(0, splitIndex)
|
|
395
610
|
.flatMap((group) => group.messages)
|
|
@@ -433,7 +648,18 @@ export async function compactAgentWorkerContext(input) {
|
|
|
433
648
|
if (part.type === "content_delta" && kinds.get(part.id) === "text")
|
|
434
649
|
summaryText += part.delta;
|
|
435
650
|
}
|
|
436
|
-
|
|
651
|
+
const completion = await result.result;
|
|
652
|
+
summaryUsage = completion.usage;
|
|
653
|
+
if (completion.finishReason !== "stop") {
|
|
654
|
+
// The provider call still counts toward usage, but an incomplete
|
|
655
|
+
// summary must not replace the previous summary or active history.
|
|
656
|
+
span.setAttributes({
|
|
657
|
+
"gea.context.compaction.outcome": "rejected",
|
|
658
|
+
"gea.context.compaction.finish_reason": completion.finishReason,
|
|
659
|
+
"gea.context.messages.output_count": input.context.messages.length,
|
|
660
|
+
});
|
|
661
|
+
return { summaryUsage, messagesUpdated };
|
|
662
|
+
}
|
|
437
663
|
if (!summaryText.trim())
|
|
438
664
|
throw new Error("Context summary was empty.");
|
|
439
665
|
const missingHeadings = REQUIRED_SUMMARY_HEADINGS.filter((heading) => !summaryText.includes(heading));
|
|
@@ -480,7 +706,11 @@ export async function compactAgentWorkerContext(input) {
|
|
|
480
706
|
"gea.context.tokens.estimate": input.context.contextSize ?? undefined,
|
|
481
707
|
"gea.context.tokens.threshold": summarization.triggerAtTotalTokens,
|
|
482
708
|
"gea.context.messages.compacted_count": compactableMessages.length,
|
|
483
|
-
"gea.context.groups.preserved":
|
|
709
|
+
"gea.context.groups.preserved": groups.length - splitIndex,
|
|
710
|
+
"gea.context.tokens.retained_estimate": retainedTokens,
|
|
711
|
+
"gea.context.tokens.usage_scale": usageScale,
|
|
712
|
+
"gea.context.tokens.retained_budget": recentTokenBudget,
|
|
713
|
+
"gea.context.tokens.retained_over_budget": retainedTokens > recentTokenBudget,
|
|
484
714
|
},
|
|
485
715
|
});
|
|
486
716
|
}
|
package/dist/agent-worker.js
CHANGED
|
@@ -1,3 +1,4 @@
|
|
|
1
|
+
import { contextSystemInputSchema } from "@gea-ai/contract/agent-worker-tools";
|
|
1
2
|
import { getAgentProgrammaticToolCalling, isAgentConnectorConnectionHelperExposed, } from "@gea-ai/contract/agent-packages";
|
|
2
3
|
import { AGENT_WORKER_TOOL_METADATA_HEADER, AGENT_WORKER_TOOL_METADATA_VERSION, } from "@gea-ai/contract/agent-worker-tools";
|
|
3
4
|
import { isAgentConnectorExposed } from "@gea-ai/contract/agent-packages";
|
|
@@ -2232,6 +2233,21 @@ function createHostBackedAgentTools(input) {
|
|
|
2232
2233
|
inputSchema: webSearchInputSchema,
|
|
2233
2234
|
});
|
|
2234
2235
|
}
|
|
2236
|
+
else if (definition.id === "context_system") {
|
|
2237
|
+
registerAgentWorkerTool(tools, "contextSystem", tool({
|
|
2238
|
+
description: "Access the current OAuth user's GEA Context resources. Call action=list to discover operations and their input schemas, then action=call with tool and arguments. Documents use Markdown; read before editing and retain contentHash for conflict checks. Requires OAuth; Project keys, custom principals and local calls are unsupported.",
|
|
2239
|
+
inputSchema: contextSystemInputSchema,
|
|
2240
|
+
toModelOutput: mcpToolModelOutput,
|
|
2241
|
+
execute: async (toolInput, options) => invokeHostTool({
|
|
2242
|
+
host: input.host,
|
|
2243
|
+
messages: options.messages,
|
|
2244
|
+
signal: options.abortSignal,
|
|
2245
|
+
toolCallId: options.toolCallId,
|
|
2246
|
+
toolInput,
|
|
2247
|
+
toolName: definition.id,
|
|
2248
|
+
}),
|
|
2249
|
+
}));
|
|
2250
|
+
}
|
|
2235
2251
|
else if (definition.id === "agent") {
|
|
2236
2252
|
// The negotiated target catalog below owns registration, including declared subagents.
|
|
2237
2253
|
}
|
|
@@ -2570,7 +2586,6 @@ async function requestSessionJson(input) {
|
|
|
2570
2586
|
method: input.method,
|
|
2571
2587
|
})),
|
|
2572
2588
|
});
|
|
2573
|
-
input.onResponse?.(response);
|
|
2574
2589
|
if (!response.ok)
|
|
2575
2590
|
throw new Error(`AgentSession rejected ${input.path} (${response.status}).`);
|
|
2576
2591
|
return input.schema.parse(await response.json());
|
|
@@ -3027,15 +3042,9 @@ async function executeAgentWorkerRequest(input, waitUntil, ownComputer, ownSessi
|
|
|
3027
3042
|
}
|
|
3028
3043
|
const mutateSession = (values) => {
|
|
3029
3044
|
const queuedAt = performance.now();
|
|
3030
|
-
const mutationSpan = trace
|
|
3031
|
-
.getTracer("gea.agent-session")
|
|
3032
|
-
.startSpan("agent.session.mutation", {
|
|
3033
|
-
attributes: { "gea.agent.run.id": payload.runId },
|
|
3034
|
-
});
|
|
3035
3045
|
const result = writeTail.then(async () => {
|
|
3036
3046
|
const startedAt = performance.now();
|
|
3037
3047
|
const queueMs = startedAt - queuedAt;
|
|
3038
|
-
const sequence = mutationSequence;
|
|
3039
3048
|
let failed = true;
|
|
3040
3049
|
try {
|
|
3041
3050
|
if (writeFailure)
|
|
@@ -3047,12 +3056,6 @@ async function executeAgentWorkerRequest(input, waitUntil, ownComputer, ownSessi
|
|
|
3047
3056
|
namespace: input.sessionNamespace,
|
|
3048
3057
|
path: "/v2/mutations",
|
|
3049
3058
|
schema: agentSessionExecutionMutationResultSchema,
|
|
3050
|
-
onResponse(response) {
|
|
3051
|
-
mutationSpan.setAttribute("http.response.status_code", response.status);
|
|
3052
|
-
const timing = response.headers.get("server-timing");
|
|
3053
|
-
if (timing)
|
|
3054
|
-
mutationSpan.setAttribute("gea.session.mutation.server_timing", timing);
|
|
3055
|
-
},
|
|
3056
3059
|
});
|
|
3057
3060
|
mutationSequence += 1;
|
|
3058
3061
|
failed = false;
|
|
@@ -3073,22 +3076,6 @@ async function executeAgentWorkerRequest(input, waitUntil, ownComputer, ownSessi
|
|
|
3073
3076
|
"gea.session.persistence.queue_ms": Math.round(persistenceTotals.queueMs),
|
|
3074
3077
|
"gea.session.persistence.request_ms": Math.round(persistenceTotals.requestMs),
|
|
3075
3078
|
});
|
|
3076
|
-
mutationSpan.setAttributes({
|
|
3077
|
-
"gea.session.mutation.sequence": sequence,
|
|
3078
|
-
"gea.session.mutation.kinds": Object.keys(values),
|
|
3079
|
-
"gea.session.mutation.event_count": values.events?.length ?? 0,
|
|
3080
|
-
"gea.session.mutation.queue_ms": Math.round(queueMs),
|
|
3081
|
-
"gea.session.mutation.request_ms": Math.round(requestMs),
|
|
3082
|
-
...(invocationTiming.lastModelFinishedAt === undefined
|
|
3083
|
-
? {}
|
|
3084
|
-
: {
|
|
3085
|
-
"gea.session.mutation.start_after_model_ms": Math.round(startedAt - invocationTiming.lastModelFinishedAt),
|
|
3086
|
-
"gea.session.mutation.end_after_model_ms": Math.round(performance.now() - invocationTiming.lastModelFinishedAt),
|
|
3087
|
-
}),
|
|
3088
|
-
});
|
|
3089
|
-
if (failed)
|
|
3090
|
-
mutationSpan.setStatus({ code: SpanStatusCode.ERROR });
|
|
3091
|
-
mutationSpan.end();
|
|
3092
3079
|
if (failed || queueMs + requestMs >= 250)
|
|
3093
3080
|
console.info("AgentSession mutation timing", {
|
|
3094
3081
|
runId: payload.runId,
|
|
@@ -4220,6 +4207,9 @@ async function executeAgentWorkerRequest(input, waitUntil, ownComputer, ownSessi
|
|
|
4220
4207
|
};
|
|
4221
4208
|
let terminalMessage;
|
|
4222
4209
|
let terminalEvent;
|
|
4210
|
+
// Inbox suspension is a live protocol frame, not a journaled response end.
|
|
4211
|
+
// Hold it until the final context commit so a failed commit cannot publish waiting.
|
|
4212
|
+
let liveFinish;
|
|
4223
4213
|
const saveResponseMessage = async ({ responseMessage, }) => {
|
|
4224
4214
|
const saveStartedAt = performance.now();
|
|
4225
4215
|
const metadata = await contextFinished;
|
|
@@ -4237,6 +4227,21 @@ async function executeAgentWorkerRequest(input, waitUntil, ownComputer, ownSessi
|
|
|
4237
4227
|
}
|
|
4238
4228
|
if (inboxRunner?.suspended)
|
|
4239
4229
|
context.metadata.suspendedMessageId = responseMessage.id;
|
|
4230
|
+
const needsInput = !inboxRunner?.suspended &&
|
|
4231
|
+
responseMessage.parts.some((part) => "state" in part &&
|
|
4232
|
+
(part.state === "approval-requested" ||
|
|
4233
|
+
(part.state === "input-available" &&
|
|
4234
|
+
input.snapshot.tools.some((tool) => tool.type === "custom" &&
|
|
4235
|
+
tool.execution === "client" &&
|
|
4236
|
+
part.type === `tool-${tool.name}`))));
|
|
4237
|
+
// The same metadata object is held by the deferred finish frame. Do not
|
|
4238
|
+
// publish waiting until the final context commit succeeds; a failed commit
|
|
4239
|
+
// seals the call as failed and must not leave the message looking suspended.
|
|
4240
|
+
const markWaiting = !failure &&
|
|
4241
|
+
(needsInput ||
|
|
4242
|
+
Boolean(inboxRunner?.suspended && metadata.sessionStatus === "waiting"));
|
|
4243
|
+
if (markWaiting)
|
|
4244
|
+
delete metadata.sessionStatus;
|
|
4240
4245
|
responseMessage.metadata = {
|
|
4241
4246
|
...responseMessage.metadata,
|
|
4242
4247
|
...metadata,
|
|
@@ -4289,19 +4294,39 @@ async function executeAgentWorkerRequest(input, waitUntil, ownComputer, ownSessi
|
|
|
4289
4294
|
}
|
|
4290
4295
|
throw error;
|
|
4291
4296
|
}
|
|
4297
|
+
if (markWaiting) {
|
|
4298
|
+
metadata.sessionStatus = "waiting";
|
|
4299
|
+
responseMessage.metadata = {
|
|
4300
|
+
...responseMessage.metadata,
|
|
4301
|
+
sessionStatus: "waiting",
|
|
4302
|
+
};
|
|
4303
|
+
if (terminalMessage) {
|
|
4304
|
+
terminalMessage = {
|
|
4305
|
+
...terminalMessage,
|
|
4306
|
+
metadata: {
|
|
4307
|
+
...terminalMessage.metadata,
|
|
4308
|
+
sessionStatus: "waiting",
|
|
4309
|
+
},
|
|
4310
|
+
};
|
|
4311
|
+
}
|
|
4312
|
+
else if (sessionExecution) {
|
|
4313
|
+
await mutateSession({ messages: [{ ...responseMessage }] });
|
|
4314
|
+
}
|
|
4315
|
+
else {
|
|
4316
|
+
await appendSessionValue({
|
|
4317
|
+
body: { message: responseMessage, runId: payload.runId },
|
|
4318
|
+
chatId: payload.chatId,
|
|
4319
|
+
namespace: input.sessionNamespace,
|
|
4320
|
+
path: "/v1/messages",
|
|
4321
|
+
});
|
|
4322
|
+
}
|
|
4323
|
+
}
|
|
4292
4324
|
const contextPersistedAt = performance.now();
|
|
4293
4325
|
persistenceSpan?.setAttributes({
|
|
4294
4326
|
"gea.session.finish.context_wait_ms": Math.round(contextFinishedAt - saveStartedAt),
|
|
4295
4327
|
"gea.session.finish.transcript_ms": Math.round(transcriptFinishedAt - transcriptStartedAt),
|
|
4296
4328
|
"gea.session.finish.context_write_ms": Math.round(contextPersistedAt - transcriptFinishedAt),
|
|
4297
4329
|
});
|
|
4298
|
-
const needsInput = !inboxRunner?.suspended &&
|
|
4299
|
-
responseMessage.parts.some((part) => "state" in part &&
|
|
4300
|
-
(part.state === "approval-requested" ||
|
|
4301
|
-
(part.state === "input-available" &&
|
|
4302
|
-
input.snapshot.tools.some((tool) => tool.type === "custom" &&
|
|
4303
|
-
tool.execution === "client" &&
|
|
4304
|
-
part.type === `tool-${tool.name}`))));
|
|
4305
4330
|
ownSessionOutcome({
|
|
4306
4331
|
...(needsInput
|
|
4307
4332
|
? {
|
|
@@ -4970,14 +4995,19 @@ async function executeAgentWorkerRequest(input, waitUntil, ownComputer, ownSessi
|
|
|
4970
4995
|
finishReason: metadata.finishReason,
|
|
4971
4996
|
messageMetadata: metadata,
|
|
4972
4997
|
};
|
|
4973
|
-
if (metadata.
|
|
4974
|
-
|
|
4998
|
+
if (inboxRunner?.suspended && metadata.finishReason !== "error") {
|
|
4999
|
+
// onFinish commits context before this frame is published. The
|
|
5000
|
+
// journal keeps the resumed response's finish, not this boundary.
|
|
5001
|
+
liveFinish = chunk;
|
|
4975
5002
|
continue;
|
|
4976
5003
|
}
|
|
4977
|
-
if (
|
|
4978
|
-
|
|
4979
|
-
|
|
4980
|
-
|
|
5004
|
+
if (metadata.finishReason === "tool-calls" ||
|
|
5005
|
+
(sessionExecution &&
|
|
5006
|
+
!input.channelJournal &&
|
|
5007
|
+
!inboxRunner?.suspended &&
|
|
5008
|
+
metadata.finishReason === "stop")) {
|
|
5009
|
+
// onFinish commits context before publishing waiting metadata.
|
|
5010
|
+
// Flush emits this frame only after that commit returns.
|
|
4981
5011
|
terminalEvent = chunk;
|
|
4982
5012
|
continue;
|
|
4983
5013
|
}
|
|
@@ -4995,16 +5025,26 @@ async function executeAgentWorkerRequest(input, waitUntil, ownComputer, ownSessi
|
|
|
4995
5025
|
controller.enqueue(event);
|
|
4996
5026
|
},
|
|
4997
5027
|
async flush(controller) {
|
|
4998
|
-
if (!terminalMessage && !terminalEvent)
|
|
5028
|
+
if (!terminalMessage && !terminalEvent && !liveFinish)
|
|
4999
5029
|
return;
|
|
5000
|
-
|
|
5001
|
-
|
|
5002
|
-
|
|
5003
|
-
|
|
5004
|
-
|
|
5005
|
-
|
|
5030
|
+
if (terminalMessage || terminalEvent) {
|
|
5031
|
+
const startedAt = performance.now();
|
|
5032
|
+
if (sessionExecution) {
|
|
5033
|
+
await mutateSession({
|
|
5034
|
+
...(terminalMessage
|
|
5035
|
+
? { messages: [{ ...terminalMessage }] }
|
|
5036
|
+
: {}),
|
|
5037
|
+
...(terminalEvent ? { events: [terminalEvent] } : {}),
|
|
5038
|
+
});
|
|
5039
|
+
}
|
|
5040
|
+
else if (terminalEvent)
|
|
5041
|
+
await appendStreamEvent(terminalEvent);
|
|
5042
|
+
persistenceSpan?.setAttribute("gea.session.finish.transcript_ms", Math.round(performance.now() - startedAt));
|
|
5043
|
+
}
|
|
5006
5044
|
if (terminalEvent)
|
|
5007
5045
|
controller.enqueue(terminalEvent);
|
|
5046
|
+
if (liveFinish)
|
|
5047
|
+
controller.enqueue(liveFinish);
|
|
5008
5048
|
},
|
|
5009
5049
|
}));
|
|
5010
5050
|
if (engine || sessionExecution) {
|
package/dist/index.d.ts
CHANGED
|
@@ -429,7 +429,7 @@ type CustomAgentToolDefinition = {
|
|
|
429
429
|
readonly type: "custom";
|
|
430
430
|
readonly [agentToolConnectorAliases]?: Readonly<Record<string, string>>;
|
|
431
431
|
};
|
|
432
|
-
type ManagedToolId = "agent" | "chat_list" | "chat_read" | "chat_send" | "web_search";
|
|
432
|
+
type ManagedToolId = "agent" | "chat_list" | "chat_read" | "chat_send" | "web_search" | "context_system";
|
|
433
433
|
export type DefinedManagedTool<TId extends ManagedToolId = ManagedToolId> = {
|
|
434
434
|
readonly id: TId;
|
|
435
435
|
readonly type: "managed";
|
|
@@ -477,6 +477,8 @@ export declare function chatRead(): DefinedManagedTool<"chat_read">;
|
|
|
477
477
|
/** @deprecated Enable defineAgent({ sessionTools: true }) instead. */
|
|
478
478
|
export declare function chatSend(): DefinedManagedTool<"chat_send">;
|
|
479
479
|
export declare function webSearch(): DefinedManagedTool<"web_search">;
|
|
480
|
+
/** Access the current OAuth user's GEA Context without a separate Connection. */
|
|
481
|
+
export declare function contextSystem(): DefinedManagedTool<"context_system">;
|
|
480
482
|
type LegacyAgentToolPreset = {
|
|
481
483
|
readonly preset: "coding" | "default";
|
|
482
484
|
readonly type: "preset";
|
package/dist/index.js
CHANGED
|
@@ -368,6 +368,10 @@ export function chatSend() {
|
|
|
368
368
|
export function webSearch() {
|
|
369
369
|
return { id: "web_search", type: "managed" };
|
|
370
370
|
}
|
|
371
|
+
/** Access the current OAuth user's GEA Context without a separate Connection. */
|
|
372
|
+
export function contextSystem() {
|
|
373
|
+
return { id: "context_system", type: "managed" };
|
|
374
|
+
}
|
|
371
375
|
export function defineToolSet(tools) {
|
|
372
376
|
return { tools, type: "tool-set" };
|
|
373
377
|
}
|
package/dist/version.d.ts
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
export declare const agentSdkVersion = "0.1.
|
|
1
|
+
export declare const agentSdkVersion = "0.1.261009-alpha.1";
|
package/dist/version.js
CHANGED
|
@@ -1,2 +1,2 @@
|
|
|
1
1
|
// Generated by scripts/build-sdk-version.mjs from package.json.
|
|
2
|
-
export const agentSdkVersion = "0.1.
|
|
2
|
+
export const agentSdkVersion = "0.1.261009-alpha.1";
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@gea-ai/agent-sdk",
|
|
3
|
-
"version": "0.1.
|
|
3
|
+
"version": "0.1.261009-alpha.1",
|
|
4
4
|
"private": false,
|
|
5
5
|
"homepage": "https://musegea.com/developers",
|
|
6
6
|
"license": "Apache-2.0",
|
|
@@ -119,7 +119,7 @@
|
|
|
119
119
|
"@ai-sdk/otel": "1.0.119",
|
|
120
120
|
"@ai-sdk/provider": "4.0.19",
|
|
121
121
|
"@ai-sdk/provider-utils": "5.0.50",
|
|
122
|
-
"@gea-ai/contract": "0.1.
|
|
122
|
+
"@gea-ai/contract": "0.1.261009-alpha.1",
|
|
123
123
|
"@opentelemetry/api": "1.9.1",
|
|
124
124
|
"@opentelemetry/context-async-hooks": "2.10.0",
|
|
125
125
|
"@opentelemetry/core": "2.10.0",
|
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
Copyright 2023 Vercel, Inc.
|
|
2
|
+
|
|
3
|
+
Licensed under the Apache License, Version 2.0 (the "License");
|
|
4
|
+
you may not use this file except in compliance with the License.
|
|
5
|
+
You may obtain a copy of the License at
|
|
6
|
+
|
|
7
|
+
http://www.apache.org/licenses/LICENSE-2.0
|
|
8
|
+
|
|
9
|
+
Unless required by applicable law or agreed to in writing, software
|
|
10
|
+
distributed under the License is distributed on an "AS IS" BASIS,
|
|
11
|
+
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
12
|
+
See the License for the specific language governing permissions and
|
|
13
|
+
limitations under the License.
|