@gea-ai/agent-sdk 0.1.260925-alpha.1 → 0.1.261003-alpha.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/agent-core.js +3 -0
- package/dist/agent-message-history.d.ts +3 -0
- package/dist/agent-message-history.js +38 -0
- package/dist/agent-remote-run.js +104 -50
- package/dist/agent-run-host.d.ts +1 -1
- package/dist/agent-run-host.js +16 -7
- package/dist/agent-session-execution.js +18 -1
- package/dist/agent-session-inbox.js +14 -0
- package/dist/agent-worker.d.ts +1 -0
- package/dist/agent-worker.js +82 -27
- package/dist/code-mode-discovery.d.ts +38 -0
- package/dist/code-mode-discovery.js +259 -0
- package/dist/code-mode.d.ts +469 -0
- package/dist/code-mode.js +225 -0
- package/dist/experimental/agent-core-ui.d.ts +8 -0
- package/dist/experimental/agent-core-ui.js +14 -3
- package/dist/experimental/agent-core.d.ts +9 -0
- package/dist/experimental/agent-core.js +1 -0
- package/dist/index.d.ts +20 -11
- package/dist/index.js +37 -3
- package/dist/script-tools.d.ts +10 -0
- package/dist/script-tools.js +30 -8
- package/dist/tool-model-output.d.ts +7 -1
- package/dist/tool-model-output.js +4 -8
- package/dist/tracing.js +6 -3
- package/dist/version.d.ts +1 -0
- package/dist/version.js +2 -0
- package/dist/worker-script-runner.d.ts +1 -0
- package/dist/worker-script-runner.js +97 -0
- package/dist/worker-script.d.ts +17 -0
- package/dist/worker-script.js +154 -0
- package/package.json +7 -3
- package/script-runtime/javascript.mjs +9 -2
- package/script-runtime/typescript.mjs +7 -2
package/dist/agent-core.js
CHANGED
|
@@ -7,6 +7,9 @@ let compiled;
|
|
|
7
7
|
export function agentCore(options = {}) {
|
|
8
8
|
return {
|
|
9
9
|
...options,
|
|
10
|
+
// A silent model stream must not retain a Run until the provider hard deadline.
|
|
11
|
+
// Explicit timeout options (including {}) retain caller-owned policy.
|
|
12
|
+
timeout: options.timeout ?? { chunkMs: 5 * 60_000 },
|
|
10
13
|
output: options.output === undefined
|
|
11
14
|
? undefined
|
|
12
15
|
: toAgentCoreOutput(options.output),
|
|
@@ -0,0 +1,38 @@
|
|
|
1
|
+
import { z } from "zod";
|
|
2
|
+
const assistantHistorySchema = z
|
|
3
|
+
.object({
|
|
4
|
+
role: z.literal("assistant"),
|
|
5
|
+
parts: z.array(z.unknown()),
|
|
6
|
+
})
|
|
7
|
+
.passthrough();
|
|
8
|
+
const legacyDenialSchema = z
|
|
9
|
+
.object({
|
|
10
|
+
type: z.union([z.literal("dynamic-tool"), z.string().startsWith("tool-")]),
|
|
11
|
+
toolCallId: z.string(),
|
|
12
|
+
state: z.literal("output-denied"),
|
|
13
|
+
approval: z
|
|
14
|
+
.object({ id: z.string(), approved: z.literal(true) })
|
|
15
|
+
.passthrough()
|
|
16
|
+
.optional(),
|
|
17
|
+
})
|
|
18
|
+
.passthrough();
|
|
19
|
+
/** Old Core policy denials lacked the rejected approval required by AI SDK.
|
|
20
|
+
* Repair only their presentation; persisted Core decisions still own execution. */
|
|
21
|
+
export function repairLegacyToolDenials(message) {
|
|
22
|
+
const history = assistantHistorySchema.safeParse(message);
|
|
23
|
+
if (!history.success)
|
|
24
|
+
return message;
|
|
25
|
+
return {
|
|
26
|
+
...history.data,
|
|
27
|
+
parts: history.data.parts.map((part) => {
|
|
28
|
+
const denied = legacyDenialSchema.safeParse(part);
|
|
29
|
+
return denied.success
|
|
30
|
+
? {
|
|
31
|
+
...denied.data,
|
|
32
|
+
state: "output-error",
|
|
33
|
+
errorText: "Tool execution was denied by policy.",
|
|
34
|
+
}
|
|
35
|
+
: part;
|
|
36
|
+
}),
|
|
37
|
+
};
|
|
38
|
+
}
|
package/dist/agent-remote-run.js
CHANGED
|
@@ -57,60 +57,114 @@ export async function cancelRemoteRun(fetch, run) {
|
|
|
57
57
|
const response = await fetch(`/runs/${run.id}/cancel`, { method: "POST" });
|
|
58
58
|
await responseJson(response);
|
|
59
59
|
}
|
|
60
|
+
class RemoteRunReadError extends TaggedError("RemoteRunReadError")() {
|
|
61
|
+
}
|
|
62
|
+
async function waitForRemoteRead(ms, signal) {
|
|
63
|
+
signal.throwIfAborted();
|
|
64
|
+
await new Promise((resolve, reject) => {
|
|
65
|
+
const abort = () => {
|
|
66
|
+
clearTimeout(timer);
|
|
67
|
+
reject(signal.reason);
|
|
68
|
+
};
|
|
69
|
+
const timer = setTimeout(() => {
|
|
70
|
+
signal.removeEventListener("abort", abort);
|
|
71
|
+
resolve();
|
|
72
|
+
}, ms);
|
|
73
|
+
signal.addEventListener("abort", abort, { once: true });
|
|
74
|
+
if (signal.aborted)
|
|
75
|
+
abort();
|
|
76
|
+
});
|
|
77
|
+
}
|
|
78
|
+
/** Only idempotent reads retry. Admission and Tool execution are never repeated here. */
|
|
79
|
+
async function readRemoteRunPage(fetch, path, signal) {
|
|
80
|
+
for (let attempt = 0;; attempt++) {
|
|
81
|
+
signal.throwIfAborted();
|
|
82
|
+
const response = await Result.tryPromise(() => fetch(path, { signal }));
|
|
83
|
+
signal.throwIfAborted();
|
|
84
|
+
let status;
|
|
85
|
+
let delay = 250 * 2 ** attempt;
|
|
86
|
+
if (response.isOk()) {
|
|
87
|
+
if (response.value.ok)
|
|
88
|
+
return response.value.json();
|
|
89
|
+
status = response.value.status;
|
|
90
|
+
const retryAfter = response.value.headers.get("retry-after");
|
|
91
|
+
if (retryAfter) {
|
|
92
|
+
const requestedDelay = /^\d+$/u.test(retryAfter)
|
|
93
|
+
? Number(retryAfter) * 1000
|
|
94
|
+
: Date.parse(retryAfter) - Date.now();
|
|
95
|
+
if (Number.isFinite(requestedDelay))
|
|
96
|
+
delay = Math.max(delay, requestedDelay);
|
|
97
|
+
}
|
|
98
|
+
await response.value.body?.cancel();
|
|
99
|
+
}
|
|
100
|
+
if (attempt >= 2 ||
|
|
101
|
+
delay > 5000 ||
|
|
102
|
+
(status !== undefined && status !== 408 && status !== 429 && status < 500))
|
|
103
|
+
throw new RemoteRunReadError({
|
|
104
|
+
message: "Remote Run read failed.",
|
|
105
|
+
status,
|
|
106
|
+
});
|
|
107
|
+
await waitForRemoteRead(delay, signal);
|
|
108
|
+
}
|
|
109
|
+
}
|
|
60
110
|
export async function observeRemoteRun(fetch, run, signal, onInputRequired) {
|
|
61
111
|
let reportedInput;
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
if (
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
112
|
+
try {
|
|
113
|
+
while (true) {
|
|
114
|
+
signal.throwIfAborted();
|
|
115
|
+
const record = agentHttpRunSchema.parse(await readRemoteRunPage(fetch, `/runs/${run.id}`, signal));
|
|
116
|
+
if (record.id !== run.id || record.session_id !== run.sessionId)
|
|
117
|
+
throw new Error("Remote Run identity mismatch.");
|
|
118
|
+
const terminal = ["finished", "error", "aborted", "interrupted"].includes(record.status);
|
|
119
|
+
const inputMessageId = record.status === "waiting" ? record.input_message_id : null;
|
|
120
|
+
if (terminal || (onInputRequired && inputMessageId)) {
|
|
121
|
+
const text = [];
|
|
122
|
+
let cursor = null;
|
|
123
|
+
do {
|
|
124
|
+
const page = messagesSchema.parse(await readRemoteRunPage(fetch, `/sessions/${run.sessionId}/messages?limit=100${cursor ? `&cursor=${encodeURIComponent(cursor)}` : ""}`, signal));
|
|
125
|
+
for (const message of page.items)
|
|
126
|
+
if (message.run_id === run.id && message.role === "assistant") {
|
|
127
|
+
if (!terminal &&
|
|
128
|
+
message.id === inputMessageId &&
|
|
129
|
+
onInputRequired) {
|
|
130
|
+
const input = agentApplicationAssistantMessageSchema.parse({
|
|
131
|
+
id: message.id,
|
|
132
|
+
role: message.role,
|
|
133
|
+
parts: message.parts,
|
|
134
|
+
});
|
|
135
|
+
const key = agentInputRequestKey(input);
|
|
136
|
+
if (key !== reportedInput) {
|
|
137
|
+
await onInputRequired(input);
|
|
138
|
+
reportedInput = key;
|
|
139
|
+
}
|
|
86
140
|
}
|
|
141
|
+
for (const part of message.parts)
|
|
142
|
+
if (part.type === "text" && typeof part.text === "string")
|
|
143
|
+
text.push(part.text);
|
|
87
144
|
}
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
145
|
+
cursor = page.next_cursor;
|
|
146
|
+
} while (cursor);
|
|
147
|
+
if (terminal)
|
|
148
|
+
return {
|
|
149
|
+
status: record.status,
|
|
150
|
+
text: text.join("\n").slice(0, 200_000),
|
|
151
|
+
};
|
|
152
|
+
}
|
|
153
|
+
if (!["queued", "running", "waiting"].includes(record.status))
|
|
154
|
+
throw new Error("Unknown remote Run status.");
|
|
155
|
+
await waitForRemoteRead(1000, signal);
|
|
99
156
|
}
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
}
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
},
|
|
111
|
-
|
|
112
|
-
if (signal.aborted)
|
|
113
|
-
abort();
|
|
114
|
-
});
|
|
157
|
+
}
|
|
158
|
+
catch (error) {
|
|
159
|
+
// Stop only this parent-owned observation. The remote execution/directory is untouched.
|
|
160
|
+
// Host shutdown leaves the relation pending so another dispatcher can resume it.
|
|
161
|
+
signal.throwIfAborted();
|
|
162
|
+
const status = RemoteRunReadError.is(error) && error.status
|
|
163
|
+
? ` (HTTP ${error.status})`
|
|
164
|
+
: "";
|
|
165
|
+
return {
|
|
166
|
+
status: "interrupted",
|
|
167
|
+
text: `Remote observation interrupted${status}; execution outcome is unknown. Run ${run.id}, Session ${run.sessionId}. Do not resubmit the task; inspect this existing Run.`,
|
|
168
|
+
};
|
|
115
169
|
}
|
|
116
170
|
}
|
package/dist/agent-run-host.d.ts
CHANGED
|
@@ -20,7 +20,7 @@ export declare class AgentRunHost {
|
|
|
20
20
|
response: Response;
|
|
21
21
|
dispose?: () => void;
|
|
22
22
|
}>;
|
|
23
|
-
cancelChild(run: AgentChildRun): Promise<void>;
|
|
23
|
+
cancelChild(run: AgentChildRun): Promise<boolean | void>;
|
|
24
24
|
onError(error: unknown, identity?: {
|
|
25
25
|
runId?: string;
|
|
26
26
|
sessionId?: string;
|
package/dist/agent-run-host.js
CHANGED
|
@@ -161,9 +161,7 @@ export class AgentRunHost {
|
|
|
161
161
|
dispatch(run, inboxExecution) {
|
|
162
162
|
if (this.closed)
|
|
163
163
|
throw new Error("Run dispatcher is stopping.");
|
|
164
|
-
if (this.executions.has(run.id) ||
|
|
165
|
-
run.outcome ||
|
|
166
|
-
(run.cancelRequested && run.destination.type !== "remote"))
|
|
164
|
+
if (this.executions.has(run.id) || run.outcome)
|
|
167
165
|
return;
|
|
168
166
|
const cancellation = new AbortController();
|
|
169
167
|
this.executions.set(run.id, cancellation);
|
|
@@ -207,8 +205,18 @@ export class AgentRunHost {
|
|
|
207
205
|
});
|
|
208
206
|
}
|
|
209
207
|
else {
|
|
210
|
-
cancellation.
|
|
211
|
-
|
|
208
|
+
// Keep admission alive until the Session has received cancellation.
|
|
209
|
+
// Aborting the request earlier can strand an uninitialized child.
|
|
210
|
+
const cancelled = await Result.tryPromise(() => this.host.cancelChild(run));
|
|
211
|
+
if (cancelled.isOk() && cancelled.value !== false) {
|
|
212
|
+
cancellation.abort();
|
|
213
|
+
return;
|
|
214
|
+
}
|
|
215
|
+
if (cancelled.isErr())
|
|
216
|
+
this.host.onError(cancelled.error, {
|
|
217
|
+
runId: run.id,
|
|
218
|
+
sessionId: run.sessionId,
|
|
219
|
+
});
|
|
212
220
|
}
|
|
213
221
|
}
|
|
214
222
|
timer = setTimeout(() => {
|
|
@@ -284,8 +292,9 @@ export class AgentRunHost {
|
|
|
284
292
|
this.dispatch(run);
|
|
285
293
|
}
|
|
286
294
|
else {
|
|
287
|
-
this.
|
|
288
|
-
await this.host.cancelChild(run)
|
|
295
|
+
this.dispatch(run);
|
|
296
|
+
if ((await this.host.cancelChild(run)) !== false)
|
|
297
|
+
this.executions.get(runId)?.abort();
|
|
289
298
|
}
|
|
290
299
|
}
|
|
291
300
|
return Response.json({
|
|
@@ -444,6 +444,15 @@ export async function handleAgentSessionExecutionRequest(request, storage) {
|
|
|
444
444
|
handlerMs = performance.now() - handlerStartedAt;
|
|
445
445
|
return response;
|
|
446
446
|
});
|
|
447
|
+
console.info("AgentSession lease release completed", {
|
|
448
|
+
event: "agent.session.lease_released",
|
|
449
|
+
runId: input.runId,
|
|
450
|
+
attemptId: input.attemptId,
|
|
451
|
+
executionId: input.inbox.executionId,
|
|
452
|
+
inputWait: Boolean(input.inbox.inputWait),
|
|
453
|
+
status: response.status,
|
|
454
|
+
elapsedMs: Math.round(performance.now() - startedAt),
|
|
455
|
+
});
|
|
447
456
|
response.headers.set("server-timing", `session-handler;dur=${handlerMs.toFixed(1)}, session-transaction;dur=${(performance.now() - startedAt).toFixed(1)}`);
|
|
448
457
|
return response;
|
|
449
458
|
}
|
|
@@ -683,7 +692,15 @@ export async function handleAgentSessionExecutionRequest(request, storage) {
|
|
|
683
692
|
.parse(await readBody(request, storage));
|
|
684
693
|
const result = await storage.transaction(async (tx) => {
|
|
685
694
|
const active = await tx.get(key("active-turn"));
|
|
686
|
-
if (!active
|
|
695
|
+
if (!active) {
|
|
696
|
+
// A local logical Run can execute or wait without a managed lease. The
|
|
697
|
+
// inbox still owns its descendants and fences cancellation by Run ID.
|
|
698
|
+
const state = await getAgentSessionRunState(tx, input.runId);
|
|
699
|
+
return {
|
|
700
|
+
requested: state?.status === "waiting" || state?.status === "running",
|
|
701
|
+
};
|
|
702
|
+
}
|
|
703
|
+
if (active.runId !== input.runId)
|
|
687
704
|
return { requested: false };
|
|
688
705
|
if (!active.cancelRequestedAt) {
|
|
689
706
|
const turn = {
|
|
@@ -467,6 +467,20 @@ async function finishInbox(tx, state, runs, input) {
|
|
|
467
467
|
await save(tx, state, runs);
|
|
468
468
|
return Response.json({ error: "execution_not_expired" }, { status: 409 });
|
|
469
469
|
}
|
|
470
|
+
console.warn("AgentSession execution lease expired", {
|
|
471
|
+
event: "agent.session.lease_expired",
|
|
472
|
+
sessionId: state.identity.sessionId,
|
|
473
|
+
runId: turn.runId,
|
|
474
|
+
attemptId: turn.attemptId,
|
|
475
|
+
requestId: turn.requestId,
|
|
476
|
+
executionId: state.execution.id,
|
|
477
|
+
leaseOwner: turn.leaseOwner,
|
|
478
|
+
leaseExpiresAt: turn.leaseExpiresAt,
|
|
479
|
+
started: state.started,
|
|
480
|
+
waiting: state.wait !== null,
|
|
481
|
+
pendingChildren: runs.filter((run) => !run.outcome).length,
|
|
482
|
+
pendingStarts: state.pendingStarts.length,
|
|
483
|
+
});
|
|
470
484
|
}
|
|
471
485
|
if (inputWait && !outcome && !state.stopped) {
|
|
472
486
|
state.inputWait = {
|
package/dist/agent-worker.d.ts
CHANGED
|
@@ -135,6 +135,7 @@ export type AgentToolRuntimeDefinition = {
|
|
|
135
135
|
input: unknown;
|
|
136
136
|
sdk: AgentToolRuntimeSdk;
|
|
137
137
|
}): Promise<unknown>;
|
|
138
|
+
scriptRuntime?: ReturnType<typeof import("./script-tools").getScriptToolRuntime>;
|
|
138
139
|
inputSchema: z.ZodType;
|
|
139
140
|
toModelOutput?: import("./index").DefinedCustomTool["toModelOutput"];
|
|
140
141
|
}>;
|
package/dist/agent-worker.js
CHANGED
|
@@ -1,6 +1,10 @@
|
|
|
1
|
+
import { AGENT_WORKER_TOOL_METADATA_HEADER, AGENT_WORKER_TOOL_METADATA_VERSION, } from "@gea-ai/contract/agent-worker-tools";
|
|
2
|
+
import { isAgentConnectorExposed } from "@gea-ai/contract/agent-packages";
|
|
3
|
+
import { createCodeMode } from "./code-mode.js";
|
|
1
4
|
import { AGENT_SUBAGENT_CONCURRENCY_HEADER } from "@gea-ai/contract/agent-worker-tools";
|
|
2
5
|
import { DEFAULT_MAX_CONCURRENT_SUBAGENTS } from "@gea-ai/contract/agent-packages";
|
|
3
6
|
import { batchAgentStreamEvents, coalesceAgentTextDeltas, } from "./agent-stream-coalescing.js";
|
|
7
|
+
import { repairLegacyToolDenials } from "./agent-message-history.js";
|
|
4
8
|
import { sendAgentSessionRequest } from "./agent-session-transport.js";
|
|
5
9
|
import { agentSessionOverridesSchema } from "@gea-ai/contract/agent-session-overrides";
|
|
6
10
|
import { resolveModelOptions } from "@gea-ai/contract/model-options";
|
|
@@ -184,7 +188,11 @@ async function runPreparedConnectorAuthorization(input, environment) {
|
|
|
184
188
|
}
|
|
185
189
|
const [identityValue, initializationValue] = await Promise.all([
|
|
186
190
|
requireHostJson(environment.IDENTITY, "/context"),
|
|
187
|
-
requireHostJson(environment.TOOLS, "/tools/initialize"
|
|
191
|
+
requireHostJson(environment.TOOLS, "/tools/initialize", {
|
|
192
|
+
headers: {
|
|
193
|
+
[AGENT_WORKER_TOOL_METADATA_HEADER]: AGENT_WORKER_TOOL_METADATA_VERSION,
|
|
194
|
+
},
|
|
195
|
+
}),
|
|
188
196
|
]);
|
|
189
197
|
const identity = agentToolIdentitySchema.parse(identityValue);
|
|
190
198
|
const initialization = agentWorkerToolInitializationSchema.parse(initializationValue);
|
|
@@ -299,6 +307,7 @@ async function runPreparedAgent(runtime, agentKey, request, environment, startup
|
|
|
299
307
|
"content-type": "application/json",
|
|
300
308
|
"x-gea-agent-runs": "1",
|
|
301
309
|
"x-gea-session-inbox": "1",
|
|
310
|
+
[AGENT_WORKER_TOOL_METADATA_HEADER]: AGENT_WORKER_TOOL_METADATA_VERSION,
|
|
302
311
|
...Object.fromEntries([...request.headers.entries()].filter(([name]) => ["traceparent", "tracestate", "x-gea-request-id"].includes(name))),
|
|
303
312
|
},
|
|
304
313
|
body: JSON.stringify({ metadata: parsed.data.metadata }),
|
|
@@ -2184,6 +2193,7 @@ function createHostBackedAgentTools(input) {
|
|
|
2184
2193
|
execute: async (toolInput, options) => await invokeHostTool({
|
|
2185
2194
|
host: input.host,
|
|
2186
2195
|
messages: options.messages,
|
|
2196
|
+
signal: options.abortSignal,
|
|
2187
2197
|
toolCallId: options.toolCallId,
|
|
2188
2198
|
toolInput,
|
|
2189
2199
|
toolName: definition.hostToolName ?? name,
|
|
@@ -2250,7 +2260,7 @@ function createHostBackedAgentTools(input) {
|
|
|
2250
2260
|
continue;
|
|
2251
2261
|
}
|
|
2252
2262
|
const declaredOperation = grant.definition.operations.find((operation) => operation.name === descriptor.operation);
|
|
2253
|
-
if (!grant.
|
|
2263
|
+
if (!isAgentConnectorExposed(grant, input.snapshot.codeMode) ||
|
|
2254
2264
|
(grant.definition.runtime.type !== "mcp" && !declaredOperation)) {
|
|
2255
2265
|
throw new Error("Agent Worker Connector operation is not granted.");
|
|
2256
2266
|
}
|
|
@@ -2264,11 +2274,21 @@ function createHostBackedAgentTools(input) {
|
|
|
2264
2274
|
.join("\n"),
|
|
2265
2275
|
inputSchema: jsonSchema(descriptor.inputSchema),
|
|
2266
2276
|
...(grant.definition.runtime.type === "mcp"
|
|
2267
|
-
? {
|
|
2277
|
+
? {
|
|
2278
|
+
toModelOutput: mcpToolModelOutput,
|
|
2279
|
+
...(descriptor.outputSchema === undefined
|
|
2280
|
+
? {}
|
|
2281
|
+
: {
|
|
2282
|
+
providerOptions: {
|
|
2283
|
+
gea: { mcpOutputSchema: descriptor.outputSchema },
|
|
2284
|
+
},
|
|
2285
|
+
}),
|
|
2286
|
+
}
|
|
2268
2287
|
: {}),
|
|
2269
2288
|
execute: async (toolInput, options) => await invokeHostTool({
|
|
2270
2289
|
host: input.host,
|
|
2271
2290
|
messages: options.messages,
|
|
2291
|
+
signal: options.abortSignal,
|
|
2272
2292
|
toolCallId: options.toolCallId,
|
|
2273
2293
|
toolInput,
|
|
2274
2294
|
toolName,
|
|
@@ -3089,7 +3109,10 @@ async function executeAgentWorkerRequest(input, waitUntil, ownComputer, ownSessi
|
|
|
3089
3109
|
!!Object.keys(input.snapshot.agentReferences ?? {}).length ||
|
|
3090
3110
|
requiresToolInitialization(input.snapshot, input.hookRuntime)
|
|
3091
3111
|
? startupTracing.withSpan("agent.startup.tools", () => requireHostJson(host, "/tools/initialize", {
|
|
3092
|
-
headers: {
|
|
3112
|
+
headers: {
|
|
3113
|
+
"x-gea-agent-runs": "1",
|
|
3114
|
+
[AGENT_WORKER_TOOL_METADATA_HEADER]: AGENT_WORKER_TOOL_METADATA_VERSION,
|
|
3115
|
+
},
|
|
3093
3116
|
}).then((value) => agentWorkerToolInitializationSchema.parse(value)))
|
|
3094
3117
|
: Promise.resolve(agentWorkerToolInitializationSchema.parse({
|
|
3095
3118
|
connectors: [],
|
|
@@ -3146,13 +3169,19 @@ async function executeAgentWorkerRequest(input, waitUntil, ownComputer, ownSessi
|
|
|
3146
3169
|
const submittedUser = payload.currentTurn.some((message) => message.role === "user")
|
|
3147
3170
|
? inputUser
|
|
3148
3171
|
: undefined;
|
|
3149
|
-
if (input.inboxExecution && submittedUser)
|
|
3150
|
-
|
|
3151
|
-
|
|
3152
|
-
|
|
3153
|
-
|
|
3154
|
-
|
|
3155
|
-
|
|
3172
|
+
if (input.inboxExecution && submittedUser) {
|
|
3173
|
+
if (sessionExecution) {
|
|
3174
|
+
await mutateSession({ messages: [{ ...submittedUser }] });
|
|
3175
|
+
}
|
|
3176
|
+
else {
|
|
3177
|
+
await appendSessionValue({
|
|
3178
|
+
body: { message: submittedUser, runId: payload.runId },
|
|
3179
|
+
chatId: payload.chatId,
|
|
3180
|
+
namespace: input.sessionNamespace,
|
|
3181
|
+
path: "/v1/messages",
|
|
3182
|
+
});
|
|
3183
|
+
}
|
|
3184
|
+
}
|
|
3156
3185
|
// Tool continuations still need the current user's durable file references
|
|
3157
3186
|
// when reconstructing model messages from saved UI history.
|
|
3158
3187
|
const submittedFiles = inputUser?.parts.flatMap((part) => (part.type === "file" ? [part] : [])) ??
|
|
@@ -3306,7 +3335,7 @@ async function executeAgentWorkerRequest(input, waitUntil, ownComputer, ownSessi
|
|
|
3306
3335
|
if (!runtime) {
|
|
3307
3336
|
throw new Error(`Custom Connector runtime is missing: ${definition.alias}`);
|
|
3308
3337
|
}
|
|
3309
|
-
if (!definition.
|
|
3338
|
+
if (!isAgentConnectorExposed(definition, input.snapshot.codeMode))
|
|
3310
3339
|
continue;
|
|
3311
3340
|
if (definition.authorization === "interactive") {
|
|
3312
3341
|
registerAgentWorkerTool(tools, `${definition.alias}__connect`, tool({
|
|
@@ -3615,7 +3644,7 @@ async function executeAgentWorkerRequest(input, waitUntil, ownComputer, ownSessi
|
|
|
3615
3644
|
schema: z.object({ message: z.unknown() }),
|
|
3616
3645
|
});
|
|
3617
3646
|
const [message] = await validateUIMessages({
|
|
3618
|
-
messages: [saved.message],
|
|
3647
|
+
messages: [repairLegacyToolDenials(saved.message)],
|
|
3619
3648
|
});
|
|
3620
3649
|
if (!message || message.role !== "assistant")
|
|
3621
3650
|
throw new Error("Suspended response is not an assistant message.");
|
|
@@ -3740,14 +3769,9 @@ async function executeAgentWorkerRequest(input, waitUntil, ownComputer, ownSessi
|
|
|
3740
3769
|
.filter((file) => file.mediaType.startsWith("image/"))
|
|
3741
3770
|
.map(({ url: _url, ...file }) => file);
|
|
3742
3771
|
const turnImages = new Map();
|
|
3743
|
-
const
|
|
3744
|
-
const reference = file.id ?? file.key;
|
|
3745
|
-
if (turnImages.has(reference))
|
|
3746
|
-
return;
|
|
3772
|
+
const readImage = async (file) => {
|
|
3747
3773
|
if (!artifactScope)
|
|
3748
3774
|
throw new Error("Images require Agent Artifacts.");
|
|
3749
|
-
if (turnImages.size >= MAX_AGENT_ARTIFACT_IMAGES)
|
|
3750
|
-
throw new Error(`At most ${MAX_AGENT_ARTIFACT_IMAGES} images can be viewed in one user turn.`);
|
|
3751
3775
|
const response = await host.fetch(`${HOST_PATH}/tools/attachments/read`, {
|
|
3752
3776
|
method: "POST",
|
|
3753
3777
|
headers: { "content-type": "application/json" },
|
|
@@ -3762,11 +3786,19 @@ async function executeAgentWorkerRequest(input, waitUntil, ownComputer, ownSessi
|
|
|
3762
3786
|
const bytes = new Uint8Array(await response.arrayBuffer());
|
|
3763
3787
|
if (bytes.byteLength > MAX_AGENT_ARTIFACT_IMAGE_BYTES)
|
|
3764
3788
|
throw new Error("Prepared image exceeds the model image limit.");
|
|
3765
|
-
|
|
3789
|
+
return {
|
|
3766
3790
|
type: "file",
|
|
3767
3791
|
data: { type: "data", data: bytes },
|
|
3768
3792
|
mediaType: response.headers.get("content-type") ?? file.mediaType,
|
|
3769
|
-
}
|
|
3793
|
+
};
|
|
3794
|
+
};
|
|
3795
|
+
const addImage = async (file) => {
|
|
3796
|
+
const reference = file.id ?? file.key;
|
|
3797
|
+
if (turnImages.has(reference))
|
|
3798
|
+
return;
|
|
3799
|
+
if (turnImages.size >= MAX_AGENT_ARTIFACT_IMAGES)
|
|
3800
|
+
throw new Error(`At most ${MAX_AGENT_ARTIFACT_IMAGES} images can be viewed in one user turn.`);
|
|
3801
|
+
turnImages.set(reference, await readImage(file));
|
|
3770
3802
|
context.metadata.turnImages ??= [];
|
|
3771
3803
|
if (!context.metadata.turnImages.some((image) => (image.id ?? image.key) === reference))
|
|
3772
3804
|
context.metadata.turnImages.push(file);
|
|
@@ -4040,12 +4072,7 @@ async function executeAgentWorkerRequest(input, waitUntil, ownComputer, ownSessi
|
|
|
4040
4072
|
const coreConnections = new Map([[configuredModel, coreConnection]]);
|
|
4041
4073
|
const coreCacheStates = new Map([[configuredModel, promptCacheState]]);
|
|
4042
4074
|
const preparePrompt = async (messages) => {
|
|
4043
|
-
messages = await resolveToolArtifacts(messages, (
|
|
4044
|
-
? createAgentArtifacts({
|
|
4045
|
-
scope: artifactScope,
|
|
4046
|
-
invoke: (operation) => invokeArtifact(operation, toolName, { toolCallId }),
|
|
4047
|
-
})
|
|
4048
|
-
: undefined);
|
|
4075
|
+
messages = await resolveToolArtifacts(messages, (id, mediaType) => readImage({ id, mediaType }));
|
|
4049
4076
|
const instructionCount = payload.instructions.length;
|
|
4050
4077
|
const prepared = addCacheControlToMessages([...payload.instructions, ...messages], (coreModel === configuredModel
|
|
4051
4078
|
? payload.cacheControlProvider
|
|
@@ -4293,6 +4320,33 @@ async function executeAgentWorkerRequest(input, waitUntil, ownComputer, ownSessi
|
|
|
4293
4320
|
},
|
|
4294
4321
|
};
|
|
4295
4322
|
};
|
|
4323
|
+
if (input.snapshot.codeMode) {
|
|
4324
|
+
const configured = await createCodeMode({
|
|
4325
|
+
scriptRuntime: input.toolRuntime.actions.executeJavaScript?.scriptRuntime,
|
|
4326
|
+
scriptToolNames: Object.entries(input.toolRuntime.actions)
|
|
4327
|
+
.filter(([, action]) => action.scriptRuntime)
|
|
4328
|
+
.map(([name]) => name),
|
|
4329
|
+
readArtifact: artifactScope
|
|
4330
|
+
? async (id, execution) => createAgentArtifacts({
|
|
4331
|
+
scope: artifactScope,
|
|
4332
|
+
invoke: (operation) => invokeArtifact(operation, "executeJavaScript", execution),
|
|
4333
|
+
}).readText({ id })
|
|
4334
|
+
: undefined,
|
|
4335
|
+
tools,
|
|
4336
|
+
snapshot: input.snapshot,
|
|
4337
|
+
approval: toolApproval,
|
|
4338
|
+
messages: () => context.messages,
|
|
4339
|
+
guardrail: engine?.guardrail,
|
|
4340
|
+
sdk: {
|
|
4341
|
+
tracing: createAgentTracing(),
|
|
4342
|
+
auth: initialization.runtime.auth,
|
|
4343
|
+
env: agentEnvironment,
|
|
4344
|
+
environment: initialization.runtime.environment,
|
|
4345
|
+
identity,
|
|
4346
|
+
},
|
|
4347
|
+
});
|
|
4348
|
+
tools = configured.tools;
|
|
4349
|
+
}
|
|
4296
4350
|
let stream;
|
|
4297
4351
|
if (engine) {
|
|
4298
4352
|
if (!coreConnection || !coreModule)
|
|
@@ -4657,6 +4711,7 @@ async function executeAgentWorkerRequest(input, waitUntil, ownComputer, ownSessi
|
|
|
4657
4711
|
async execute({ writer }) {
|
|
4658
4712
|
for await (const chunk of projectAgentCoreEvents(tracked, {
|
|
4659
4713
|
messageId,
|
|
4714
|
+
approvals: pending?.pendingStep.approvals,
|
|
4660
4715
|
tools,
|
|
4661
4716
|
messageMetadata: {
|
|
4662
4717
|
trace: invocationTrace,
|
|
@@ -0,0 +1,38 @@
|
|
|
1
|
+
import type { AgentToolNamespace } from "@gea-ai/contract/agent-packages";
|
|
2
|
+
export type CodeModeCatalogEntry = {
|
|
3
|
+
name: string;
|
|
4
|
+
namespace: string;
|
|
5
|
+
description: string;
|
|
6
|
+
inputSchema: unknown;
|
|
7
|
+
outputSchema?: unknown;
|
|
8
|
+
structuredContentSchema?: unknown;
|
|
9
|
+
declaration: string;
|
|
10
|
+
mcp: boolean;
|
|
11
|
+
};
|
|
12
|
+
type Namespace = AgentToolNamespace & {
|
|
13
|
+
name: string;
|
|
14
|
+
};
|
|
15
|
+
/** Bounded hints, never a substitute for validation by the original tool schema. */
|
|
16
|
+
export declare function codeModeDeclaration(name: string, input: unknown, output: unknown, mcp: boolean): string;
|
|
17
|
+
/** Pi-compatible helper shapes, implemented against the GEA callable snapshot. */
|
|
18
|
+
export declare function createCodeModeDiscovery(catalog: CodeModeCatalogEntry[], namespaces: Namespace[]): {
|
|
19
|
+
allTools: {
|
|
20
|
+
name: string;
|
|
21
|
+
description: string;
|
|
22
|
+
}[];
|
|
23
|
+
searchTools(query: string, options?: {
|
|
24
|
+
limit?: number;
|
|
25
|
+
namespace?: string | null;
|
|
26
|
+
} | null): {
|
|
27
|
+
name: string;
|
|
28
|
+
description: string;
|
|
29
|
+
}[];
|
|
30
|
+
describeTool(name: string): string | undefined;
|
|
31
|
+
describeNamespace(name: string): {
|
|
32
|
+
description?: string | undefined;
|
|
33
|
+
instructions?: string | undefined;
|
|
34
|
+
name: string;
|
|
35
|
+
tools: string[];
|
|
36
|
+
} | undefined;
|
|
37
|
+
};
|
|
38
|
+
export {};
|