@gea-ai/agent-sdk 0.1.261002-alpha.0 → 0.1.261003-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.
@@ -19,6 +19,19 @@ export declare function executeChannelBehavior(input: {
19
19
  environment: AgentChannelFetchEnvironment;
20
20
  signal: AbortSignal;
21
21
  }): Promise<{
22
+ sequence: number;
23
+ duplicate: boolean;
24
+ } | {
25
+ state: AgentChannelJson;
26
+ intents: {
27
+ key: string;
28
+ payload: AgentChannelJson;
29
+ eventId: string;
30
+ operationId: string;
31
+ }[];
32
+ cursor: number;
33
+ idempotencyWindowMs?: number | undefined;
34
+ } | {
22
35
  status: "sent";
23
36
  receipt?: AgentChannelJson | undefined;
24
37
  } | {
@@ -43,19 +56,6 @@ export declare function executeChannelBehavior(input: {
43
56
  message: string;
44
57
  retryable: boolean;
45
58
  };
46
- } | {
47
- sequence: number;
48
- duplicate: boolean;
49
- } | {
50
- state: AgentChannelJson;
51
- intents: {
52
- key: string;
53
- payload: AgentChannelJson;
54
- eventId: string;
55
- operationId: string;
56
- }[];
57
- cursor: number;
58
- idempotencyWindowMs?: number | undefined;
59
59
  } | {
60
60
  events: {
61
61
  id: string;
@@ -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
  }>;
@@ -1,3 +1,6 @@
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";
@@ -185,7 +188,11 @@ async function runPreparedConnectorAuthorization(input, environment) {
185
188
  }
186
189
  const [identityValue, initializationValue] = await Promise.all([
187
190
  requireHostJson(environment.IDENTITY, "/context"),
188
- 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
+ }),
189
196
  ]);
190
197
  const identity = agentToolIdentitySchema.parse(identityValue);
191
198
  const initialization = agentWorkerToolInitializationSchema.parse(initializationValue);
@@ -300,6 +307,7 @@ async function runPreparedAgent(runtime, agentKey, request, environment, startup
300
307
  "content-type": "application/json",
301
308
  "x-gea-agent-runs": "1",
302
309
  "x-gea-session-inbox": "1",
310
+ [AGENT_WORKER_TOOL_METADATA_HEADER]: AGENT_WORKER_TOOL_METADATA_VERSION,
303
311
  ...Object.fromEntries([...request.headers.entries()].filter(([name]) => ["traceparent", "tracestate", "x-gea-request-id"].includes(name))),
304
312
  },
305
313
  body: JSON.stringify({ metadata: parsed.data.metadata }),
@@ -2185,6 +2193,7 @@ function createHostBackedAgentTools(input) {
2185
2193
  execute: async (toolInput, options) => await invokeHostTool({
2186
2194
  host: input.host,
2187
2195
  messages: options.messages,
2196
+ signal: options.abortSignal,
2188
2197
  toolCallId: options.toolCallId,
2189
2198
  toolInput,
2190
2199
  toolName: definition.hostToolName ?? name,
@@ -2251,7 +2260,7 @@ function createHostBackedAgentTools(input) {
2251
2260
  continue;
2252
2261
  }
2253
2262
  const declaredOperation = grant.definition.operations.find((operation) => operation.name === descriptor.operation);
2254
- if (!grant.exposeToModel ||
2263
+ if (!isAgentConnectorExposed(grant, input.snapshot.codeMode) ||
2255
2264
  (grant.definition.runtime.type !== "mcp" && !declaredOperation)) {
2256
2265
  throw new Error("Agent Worker Connector operation is not granted.");
2257
2266
  }
@@ -2265,11 +2274,21 @@ function createHostBackedAgentTools(input) {
2265
2274
  .join("\n"),
2266
2275
  inputSchema: jsonSchema(descriptor.inputSchema),
2267
2276
  ...(grant.definition.runtime.type === "mcp"
2268
- ? { toModelOutput: mcpToolModelOutput }
2277
+ ? {
2278
+ toModelOutput: mcpToolModelOutput,
2279
+ ...(descriptor.outputSchema === undefined
2280
+ ? {}
2281
+ : {
2282
+ providerOptions: {
2283
+ gea: { mcpOutputSchema: descriptor.outputSchema },
2284
+ },
2285
+ }),
2286
+ }
2269
2287
  : {}),
2270
2288
  execute: async (toolInput, options) => await invokeHostTool({
2271
2289
  host: input.host,
2272
2290
  messages: options.messages,
2291
+ signal: options.abortSignal,
2273
2292
  toolCallId: options.toolCallId,
2274
2293
  toolInput,
2275
2294
  toolName,
@@ -3090,7 +3109,10 @@ async function executeAgentWorkerRequest(input, waitUntil, ownComputer, ownSessi
3090
3109
  !!Object.keys(input.snapshot.agentReferences ?? {}).length ||
3091
3110
  requiresToolInitialization(input.snapshot, input.hookRuntime)
3092
3111
  ? startupTracing.withSpan("agent.startup.tools", () => requireHostJson(host, "/tools/initialize", {
3093
- headers: { "x-gea-agent-runs": "1" },
3112
+ headers: {
3113
+ "x-gea-agent-runs": "1",
3114
+ [AGENT_WORKER_TOOL_METADATA_HEADER]: AGENT_WORKER_TOOL_METADATA_VERSION,
3115
+ },
3094
3116
  }).then((value) => agentWorkerToolInitializationSchema.parse(value)))
3095
3117
  : Promise.resolve(agentWorkerToolInitializationSchema.parse({
3096
3118
  connectors: [],
@@ -3247,6 +3269,11 @@ async function executeAgentWorkerRequest(input, waitUntil, ownComputer, ownSessi
3247
3269
  mountedRoot: mountedSkillRoot,
3248
3270
  snapshot: input.snapshot,
3249
3271
  });
3272
+ const skills = skillTool
3273
+ ? {
3274
+ readText: ({ name, path }) => skillTool.execute({ skill: name, path }).content,
3275
+ }
3276
+ : undefined;
3250
3277
  const agentEnvironment = Object.fromEntries(input.snapshot.environment.flatMap((definition) => {
3251
3278
  const value = initialization.runtime.variables[definition.name];
3252
3279
  if (value !== undefined)
@@ -3313,7 +3340,7 @@ async function executeAgentWorkerRequest(input, waitUntil, ownComputer, ownSessi
3313
3340
  if (!runtime) {
3314
3341
  throw new Error(`Custom Connector runtime is missing: ${definition.alias}`);
3315
3342
  }
3316
- if (!definition.exposeToModel)
3343
+ if (!isAgentConnectorExposed(definition, input.snapshot.codeMode))
3317
3344
  continue;
3318
3345
  if (definition.authorization === "interactive") {
3319
3346
  registerAgentWorkerTool(tools, `${definition.alias}__connect`, tool({
@@ -3440,6 +3467,7 @@ async function executeAgentWorkerRequest(input, waitUntil, ownComputer, ownSessi
3440
3467
  }),
3441
3468
  }
3442
3469
  : {}),
3470
+ skills,
3443
3471
  auth: initialization.runtime.auth,
3444
3472
  agentTools: {
3445
3473
  commands: createComputerCommands({
@@ -4298,6 +4326,34 @@ async function executeAgentWorkerRequest(input, waitUntil, ownComputer, ownSessi
4298
4326
  },
4299
4327
  };
4300
4328
  };
4329
+ if (input.snapshot.codeMode) {
4330
+ const configured = await createCodeMode({
4331
+ skills,
4332
+ scriptRuntime: input.toolRuntime.actions.executeJavaScript?.scriptRuntime,
4333
+ scriptToolNames: Object.entries(input.toolRuntime.actions)
4334
+ .filter(([, action]) => action.scriptRuntime)
4335
+ .map(([name]) => name),
4336
+ readArtifact: artifactScope
4337
+ ? async (id, execution) => createAgentArtifacts({
4338
+ scope: artifactScope,
4339
+ invoke: (operation) => invokeArtifact(operation, "executeJavaScript", execution),
4340
+ }).readText({ id })
4341
+ : undefined,
4342
+ tools,
4343
+ snapshot: input.snapshot,
4344
+ approval: toolApproval,
4345
+ messages: () => context.messages,
4346
+ guardrail: engine?.guardrail,
4347
+ sdk: {
4348
+ tracing: createAgentTracing(),
4349
+ auth: initialization.runtime.auth,
4350
+ env: agentEnvironment,
4351
+ environment: initialization.runtime.environment,
4352
+ identity,
4353
+ },
4354
+ });
4355
+ tools = configured.tools;
4356
+ }
4301
4357
  let stream;
4302
4358
  if (engine) {
4303
4359
  if (!coreConnection || !coreModule)
@@ -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 {};
@@ -0,0 +1,259 @@
1
+ import { z } from "zod";
2
+ const encoder = new TextEncoder();
3
+ const bytes = (value) => encoder.encode(JSON.stringify(value)).length;
4
+ const nameSchema = z.string().max(256);
5
+ const searchSchema = z.object({
6
+ query: z.string().max(512),
7
+ options: z
8
+ .object({
9
+ limit: z.number().int().positive().optional(),
10
+ namespace: nameSchema.nullish(),
11
+ })
12
+ .nullish(),
13
+ });
14
+ /** Bounded hints, never a substitute for validation by the original tool schema. */
15
+ export function codeModeDeclaration(name, input, output, mcp) {
16
+ let remaining = 256;
17
+ function type(value, depth = 0) {
18
+ if (--remaining < 0 || depth > 8 || !value || typeof value !== "object")
19
+ return "unknown";
20
+ const schema = value;
21
+ if (schema.$ref || schema.allOf || schema.not || schema.if)
22
+ return "unknown";
23
+ if ("const" in schema) {
24
+ const literal = JSON.stringify(schema.const);
25
+ return literal.length <= 1024 ? literal : "unknown";
26
+ }
27
+ if (Array.isArray(schema.enum)) {
28
+ const text = schema.enum.length <= 32
29
+ ? schema.enum.map((value) => JSON.stringify(value)).join(" | ")
30
+ : "unknown";
31
+ return text.length <= 1024 ? text || "never" : "unknown";
32
+ }
33
+ const union = schema.anyOf ?? schema.oneOf;
34
+ if (Array.isArray(union))
35
+ return union.length <= 16
36
+ ? union.map((value) => type(value, depth + 1)).join(" | ")
37
+ : "unknown";
38
+ if (Array.isArray(schema.type))
39
+ return schema.type.length <= 8
40
+ ? schema.type
41
+ .map((kind) => type({ ...schema, type: kind }, depth + 1))
42
+ .join(" | ")
43
+ : "unknown";
44
+ switch (schema.type) {
45
+ case "string":
46
+ case "boolean":
47
+ case "null":
48
+ return schema.type;
49
+ case "integer":
50
+ case "number":
51
+ return "number";
52
+ case "array":
53
+ return `Array<${type(schema.items, depth + 1)}>`;
54
+ case "object": {
55
+ const required = new Set(Array.isArray(schema.required) ? schema.required : []);
56
+ const properties = schema.properties && typeof schema.properties === "object"
57
+ ? Object.entries(schema.properties)
58
+ : [];
59
+ if (properties.length > 64 ||
60
+ properties.some(([key]) => key.length > 128))
61
+ return "Record<string, unknown>";
62
+ const fields = properties.map(([key, value]) => `${JSON.stringify(key)}${required.has(key) ? "" : "?"}: ${type(value, depth + 1)}`);
63
+ if (schema.additionalProperties !== false)
64
+ fields.push("[key: string]: unknown");
65
+ return `{ ${fields.join("; ")} }`;
66
+ }
67
+ default:
68
+ return "unknown";
69
+ }
70
+ }
71
+ const result = mcp
72
+ ? "{ content: Array<unknown>; structuredContent?: unknown; isError?: boolean }"
73
+ : type(output);
74
+ const prefix = `tools[${JSON.stringify(name)}]`;
75
+ const text = `${prefix}(input: ${type(input)}): Promise<${result}>`;
76
+ return text.length <= 4096
77
+ ? text
78
+ : `${prefix}(input: unknown): Promise<unknown>`;
79
+ }
80
+ /** No semantic search: ASCII words/identifier pieces, plus Han unigrams and bigrams. */
81
+ function terms(text) {
82
+ const normalized = text
83
+ .slice(0, 16384)
84
+ .replace(/([a-z0-9])([A-Z])/g, "$1 $2")
85
+ .replace(/([A-Z])([A-Z][a-z])/g, "$1 $2")
86
+ .toLowerCase();
87
+ const stop = new Set("a an and are as at be by for from in is it of on or that the this to with".split(" "));
88
+ const result = [];
89
+ for (const part of normalized.match(/[a-z0-9]+|\p{Script=Han}+/gu) ?? []) {
90
+ if (/\p{Script=Han}/u.test(part)) {
91
+ const chars = [...part];
92
+ for (let i = 0; i < chars.length; i++) {
93
+ result.push(chars[i]);
94
+ if (i + 1 < chars.length)
95
+ result.push(chars[i] + chars[i + 1]);
96
+ }
97
+ }
98
+ else if (!stop.has(part)) {
99
+ result.push(part.length > 4 && part.endsWith("ies")
100
+ ? part.slice(0, -3) + "y"
101
+ : part.length > 4 && /(?:ches|shes|sses|xes|zes)$/.test(part)
102
+ ? part.slice(0, -2)
103
+ : part.length > 3 && part.endsWith("s") && !part.endsWith("ss")
104
+ ? part.slice(0, -1)
105
+ : part);
106
+ }
107
+ if (result.length >= 2048)
108
+ break;
109
+ }
110
+ return result.slice(0, 2048);
111
+ }
112
+ /** Only parameter names/descriptions; bounded traversal of untrusted MCP schemas. */
113
+ function parameterText(input) {
114
+ const pieces = [];
115
+ let visited = 0, length = 0;
116
+ function visit(value, depth) {
117
+ if (++visited > 256 ||
118
+ depth > 8 ||
119
+ length >= 8192 ||
120
+ !value ||
121
+ typeof value !== "object")
122
+ return;
123
+ const schema = value;
124
+ if (typeof schema.description === "string") {
125
+ const text = schema.description.slice(0, 512);
126
+ pieces.push(text);
127
+ length += text.length;
128
+ }
129
+ if (schema.properties && typeof schema.properties === "object") {
130
+ for (const [key, property] of Object.entries(schema.properties).slice(0, 64)) {
131
+ if (visited > 256 || length >= 8192)
132
+ break;
133
+ pieces.push(key.slice(0, 128));
134
+ length += Math.min(key.length, 128);
135
+ visit(property, depth + 1);
136
+ }
137
+ }
138
+ visit(schema.items, depth + 1);
139
+ for (const key of ["anyOf", "oneOf", "allOf"]) {
140
+ const variants = schema[key];
141
+ if (Array.isArray(variants))
142
+ for (const variant of variants.slice(0, 16))
143
+ visit(variant, depth + 1);
144
+ }
145
+ }
146
+ visit(input, 0);
147
+ return pieces.join(" ").slice(0, 8192);
148
+ }
149
+ /** Pi-compatible helper shapes, implemented against the GEA callable snapshot. */
150
+ export function createCodeModeDiscovery(catalog, namespaces) {
151
+ const summary = (entry) => ({
152
+ name: entry.name,
153
+ description: `${entry.description.slice(0, 1024)}\n\n${entry.declaration}\n${entry.mcp ? "Returns the full MCP result envelope (content, optional structuredContent/isError)." : "Returns the original JSON tool result; unknown means no known output schema."}`,
154
+ });
155
+ const allTools = catalog.map(summary);
156
+ if (bytes(allTools) > 256 * 1024)
157
+ throw new Error("Code mode catalog exceeds the 256 KiB discovery budget; expose fewer tools.");
158
+ const documents = catalog.map((entry) => {
159
+ const namespace = namespaces.find((item) => item.name === entry.namespace);
160
+ const text = [
161
+ entry.name,
162
+ entry.name.replaceAll("_", " "),
163
+ entry.description.slice(0, 2048),
164
+ namespace?.name,
165
+ namespace?.description,
166
+ namespace?.instructions,
167
+ parameterText(entry.inputSchema),
168
+ ].join(" ");
169
+ const tokens = terms(text);
170
+ const frequencies = new Map();
171
+ for (const token of tokens)
172
+ frequencies.set(token, (frequencies.get(token) ?? 0) + 1);
173
+ return { entry, frequencies, length: tokens.length };
174
+ });
175
+ function resolveNamespace(name) {
176
+ const exact = namespaces.find((item) => item.name === name);
177
+ if (exact)
178
+ return exact;
179
+ const identifier = (value) => value.replace(/[^a-zA-Z0-9_$]/g, "_");
180
+ const matches = namespaces.filter((item) => identifier(item.name) === identifier(name) ||
181
+ (item.name.includes("__") &&
182
+ identifier(item.name.slice(item.name.lastIndexOf("__") + 2)) ===
183
+ identifier(name)));
184
+ if (matches.length > 1)
185
+ throw new Error("Ambiguous namespace; use its full alias.");
186
+ return matches[0];
187
+ }
188
+ return {
189
+ allTools,
190
+ searchTools(query, options) {
191
+ const parsed = searchSchema.parse({ query, options });
192
+ const queryTerms = [...new Set(terms(parsed.query))].slice(0, 32);
193
+ if (!queryTerms.length)
194
+ return [];
195
+ const namespace = parsed.options?.namespace
196
+ ? resolveNamespace(parsed.options.namespace)
197
+ : undefined;
198
+ const candidates = parsed.options?.namespace
199
+ ? documents.filter((doc) => doc.entry.namespace === namespace?.name)
200
+ : documents;
201
+ if (!candidates.length)
202
+ return [];
203
+ const average = candidates.reduce((sum, doc) => sum + doc.length, 0) /
204
+ candidates.length || 1;
205
+ const weights = new Map(queryTerms.map((term) => {
206
+ const frequency = candidates.filter((doc) => doc.frequencies.has(term)).length;
207
+ return [
208
+ term,
209
+ Math.log(1 + (candidates.length - frequency + 0.5) / (frequency + 0.5)),
210
+ ];
211
+ }));
212
+ const ranked = candidates
213
+ .map((doc) => ({
214
+ doc,
215
+ score: queryTerms.reduce((score, term) => {
216
+ const tf = doc.frequencies.get(term) ?? 0;
217
+ return (score +
218
+ ((weights.get(term) ?? 0) * tf * 2.2) /
219
+ (tf + 1.2 * (0.25 + (0.75 * doc.length) / average)));
220
+ }, 0),
221
+ }))
222
+ .filter((item) => item.score > 0)
223
+ .sort((a, b) => b.score - a.score);
224
+ const results = [];
225
+ for (const { doc } of ranked.slice(0, Math.min(parsed.options?.limit ?? 8, 20))) {
226
+ const next = summary(doc.entry);
227
+ if (bytes([...results, next]) > 32 * 1024)
228
+ break;
229
+ results.push(next);
230
+ }
231
+ return results;
232
+ },
233
+ describeTool(name) {
234
+ nameSchema.parse(name);
235
+ const entry = catalog.find((item) => item.name === name);
236
+ if (!entry)
237
+ return undefined;
238
+ const detail = `${summary(entry).description}\nInput JSON Schema: ${JSON.stringify(entry.inputSchema)}${entry.outputSchema === undefined ? "" : `\nOutput JSON Schema: ${JSON.stringify(entry.outputSchema)}`}${entry.structuredContentSchema === undefined ? "" : `\nMCP structuredContent JSON Schema: ${JSON.stringify(entry.structuredContentSchema)}`}`;
239
+ if (bytes(detail) > 64 * 1024)
240
+ throw new Error("Tool description exceeds the 64 KiB discovery budget; use searchTools for its bounded declaration.");
241
+ return detail;
242
+ },
243
+ describeNamespace(name) {
244
+ nameSchema.parse(name);
245
+ const namespace = resolveNamespace(name);
246
+ if (!namespace)
247
+ return undefined;
248
+ const result = {
249
+ ...namespace,
250
+ tools: catalog
251
+ .filter((entry) => entry.namespace === namespace.name)
252
+ .map((entry) => entry.name),
253
+ };
254
+ if (bytes(result) > 32 * 1024)
255
+ throw new Error("Namespace exceeds the 32 KiB discovery budget; use searchTools with its full namespace.");
256
+ return result;
257
+ },
258
+ };
259
+ }