@gea-ai/agent-sdk 0.1.260924-alpha.0 → 0.1.260925-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.
Files changed (38) hide show
  1. package/THIRD_PARTY_NOTICES.md +21 -0
  2. package/dist/agent-channel-author-runtime.d.ts +13 -13
  3. package/dist/agent-channel-receiver.d.ts +2 -2
  4. package/dist/agent-channel-receiver.js +5 -1
  5. package/dist/agent-remote-run.d.ts +3 -1
  6. package/dist/agent-remote-run.js +25 -7
  7. package/dist/agent-run-host.js +8 -1
  8. package/dist/agent-session-child-store.d.ts +44 -0
  9. package/dist/agent-session-children.js +15 -0
  10. package/dist/agent-session-inbox.js +98 -18
  11. package/dist/agent-session-runner.d.ts +3 -2
  12. package/dist/agent-worker.d.ts +3 -11
  13. package/dist/agent-worker.js +27 -154
  14. package/dist/index.d.ts +23 -43
  15. package/dist/index.js +35 -139
  16. package/dist/mcp-tool-output.d.ts +4 -0
  17. package/dist/mcp-tool-output.js +26 -0
  18. package/dist/script-tools.d.ts +8 -0
  19. package/dist/script-tools.js +143 -0
  20. package/dist/session-authority.d.ts +14 -2
  21. package/dist/session-authority.js +9 -5
  22. package/dist/typescript.d.ts +8 -0
  23. package/dist/typescript.js +3 -0
  24. package/dist/worker-bundle-plugin.d.ts +8 -0
  25. package/dist/worker-bundle-plugin.js +94 -0
  26. package/dist/worker.d.ts +14 -0
  27. package/dist/worker.js +53 -0
  28. package/package.json +21 -3
  29. package/script-runtime/javascript.mjs +8 -0
  30. package/script-runtime/javascript.worker.json +7 -0
  31. package/script-runtime/python.mjs +50 -0
  32. package/script-runtime/result.mjs +60 -0
  33. package/script-runtime/typescript.mjs +15 -0
  34. package/script-runtime/typescript.worker.json +7 -0
  35. package/scripts/prepare-python.d.mts +9 -0
  36. package/scripts/prepare-python.mjs +192 -0
  37. package/dist/agent-http.d.ts +0 -13
  38. package/dist/agent-http.js +0 -30
@@ -65,3 +65,24 @@ inventory ship in [third-party/agent-core](./third-party/agent-core/NOTICE).
65
65
  This includes Serde/serde_json, httpdate, jsonschema and its validation/regex/hash
66
66
  dependencies, plus build dependencies. The inventory records upstream repository
67
67
  license provenance where a crate distribution omitted its license file. The Agent CLI embeds this asset into the immutable Worker bundle.
68
+
69
+ ## Optional developer-prepared Python environment
70
+
71
+ The Python preparation utility uses the developer-installed Pyodide (MPL-2.0),
72
+ CPython and the developer-selected packages from that distribution's
73
+ checksum-pinned package index. Its version is recorded in the generated lock.
74
+ These bytes are not included in the SDK tarball or Worker Runtime image.
75
+ When selected, NumPy, pandas and their dependency wheels remain
76
+ intact, including their metadata and license files, in the developer's private
77
+ bundle. Pyodide's glue is adapted by local environment-selection prefixes in
78
+ `scripts/prepare-python.mjs`; the runner is `script-runtime/python.mjs`.
79
+
80
+ Distribution/source references:
81
+
82
+ - https://github.com/pyodide/pyodide
83
+ - https://pyodide.org/en/stable/usage/downloading-and-deploying.html
84
+ - https://github.com/numpy/numpy
85
+ - https://github.com/pandas-dev/pandas
86
+
87
+ Optional TypeScript transpilation uses Sucrase 3.35.1 (MIT), bundled into the private
88
+ JavaScript/TypeScript child Worker by esbuild.
@@ -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;
@@ -1,4 +1,4 @@
1
- import type { DefinedAgentPackage } from "./index";
1
+ import { type DefinedAgent } from "./index";
2
2
  import type { AgentChannelAddressStorage } from "./agent-channel-worker";
3
3
  import type { AgentChannelFetchEnvironment } from "./agent-worker";
4
4
  export type AgentChannelReceiverEnvironment = AgentChannelFetchEnvironment & {
@@ -27,7 +27,7 @@ export declare class AgentChannelReceiver {
27
27
  private definition?;
28
28
  private starting;
29
29
  constructor(storage: AgentChannelAddressStorage, environment: AgentChannelReceiverEnvironment, agents: Record<string, {
30
- definition: DefinedAgentPackage;
30
+ definition: DefinedAgent;
31
31
  }>);
32
32
  private load;
33
33
  private persist;
@@ -1,6 +1,7 @@
1
1
  import { z } from "zod";
2
2
  import { TaggedError } from "better-result";
3
3
  import { agentChannelReceiverControlSchema, agentChannelReceiverInitializationSchema, agentChannelReceiverStateSchema, agentChannelReceiverErrorSchema, } from "@gea-ai/contract/agent-channel-receivers";
4
+ import { resolveAgentDefinition } from "./index.js";
4
5
  import { createChannelInputContext } from "./agent-channel-author-runtime.js";
5
6
  const recordSchema = z
6
7
  .object({
@@ -138,7 +139,10 @@ export class AgentChannelReceiver {
138
139
  async context(record, controller) {
139
140
  controller.signal.throwIfAborted();
140
141
  const initialized = agentChannelReceiverInitializationSchema.parse(await (await this.host(record.control, "initialize", {}, controller.signal)).json());
141
- const definition = this.agents[record.control.agentKey]?.definition.channels.find((entry) => entry.provider === "custom" && entry.key === record.control.channelKey);
142
+ const agent = this.agents[record.control.agentKey]?.definition;
143
+ const definition = agent &&
144
+ resolveAgentDefinition(agent).channels.find((entry) => entry.provider === "custom" &&
145
+ entry.key === record.control.channelKey);
142
146
  if (!definition?.receiver)
143
147
  throw new Error("Channel receiver definition is unavailable.");
144
148
  this.definition = definition;
@@ -1,4 +1,6 @@
1
1
  import { Result } from "better-result";
2
+ import { z } from "zod";
3
+ import { agentApplicationAssistantMessageSchema } from "@gea-ai/contract/agent-applications";
2
4
  import type { AgentCallMessage, AgentChildRun, AgentChildRunDestination, AgentChildRunOutcome } from "@gea-ai/contract/agent-worker-tools";
3
5
  import type { StudioArtifactInputFile } from "@gea-ai/contract/studio-artifacts";
4
6
  /** Internal transport shared by hosted and local Run dispatch; not a public API client. */
@@ -26,5 +28,5 @@ export declare function submitRemoteRun(fetch: RemoteRunFetch, input: {
26
28
  sessionId: string;
27
29
  }, RemoteRunAdmissionError>>;
28
30
  export declare function cancelRemoteRun(fetch: RemoteRunFetch, run: AgentChildRun): Promise<void>;
29
- export declare function observeRemoteRun(fetch: RemoteRunFetch, run: AgentChildRun, signal: AbortSignal): Promise<AgentChildRunOutcome>;
31
+ export declare function observeRemoteRun(fetch: RemoteRunFetch, run: AgentChildRun, signal: AbortSignal, onInputRequired?: (message: z.infer<typeof agentApplicationAssistantMessageSchema>) => Promise<void>): Promise<AgentChildRunOutcome>;
30
32
  export {};
@@ -1,5 +1,6 @@
1
1
  import { Result, TaggedError } from "better-result";
2
2
  import { z } from "zod";
3
+ import { agentApplicationAssistantMessageSchema, agentInputRequestKey, } from "@gea-ai/contract/agent-applications";
3
4
  import { agentHttpRunSchema, agentHttpMessageResourceSchema, } from "@gea-ai/contract/agent-http-api";
4
5
  const sessionSchema = z.object({ id: z.uuid() });
5
6
  const messagesSchema = z.object({
@@ -56,28 +57,45 @@ export async function cancelRemoteRun(fetch, run) {
56
57
  const response = await fetch(`/runs/${run.id}/cancel`, { method: "POST" });
57
58
  await responseJson(response);
58
59
  }
59
- export async function observeRemoteRun(fetch, run, signal) {
60
+ export async function observeRemoteRun(fetch, run, signal, onInputRequired) {
61
+ let reportedInput;
60
62
  while (true) {
61
63
  signal.throwIfAborted();
62
64
  const record = agentHttpRunSchema.parse(await responseJson(await fetch(`/runs/${run.id}`, { signal })));
63
65
  if (record.id !== run.id || record.session_id !== run.sessionId)
64
66
  throw new Error("Remote Run identity mismatch.");
65
- if (["finished", "error", "aborted", "interrupted"].includes(record.status)) {
67
+ const terminal = ["finished", "error", "aborted", "interrupted"].includes(record.status);
68
+ const inputMessageId = record.status === "waiting" ? record.input_message_id : null;
69
+ if (terminal || (onInputRequired && inputMessageId)) {
66
70
  const text = [];
67
71
  let cursor = null;
68
72
  do {
69
73
  const page = messagesSchema.parse(await responseJson(await fetch(`/sessions/${run.sessionId}/messages?limit=100${cursor ? `&cursor=${encodeURIComponent(cursor)}` : ""}`, { signal })));
70
74
  for (const message of page.items)
71
- if (message.run_id === run.id && message.role === "assistant")
75
+ if (message.run_id === run.id && message.role === "assistant") {
76
+ if (!terminal && message.id === inputMessageId && onInputRequired) {
77
+ const input = agentApplicationAssistantMessageSchema.parse({
78
+ id: message.id,
79
+ role: message.role,
80
+ parts: message.parts,
81
+ });
82
+ const key = agentInputRequestKey(input);
83
+ if (key !== reportedInput) {
84
+ await onInputRequired(input);
85
+ reportedInput = key;
86
+ }
87
+ }
72
88
  for (const part of message.parts)
73
89
  if (part.type === "text" && typeof part.text === "string")
74
90
  text.push(part.text);
91
+ }
75
92
  cursor = page.next_cursor;
76
93
  } while (cursor);
77
- return {
78
- status: record.status,
79
- text: text.join("\n").slice(0, 200_000),
80
- };
94
+ if (terminal)
95
+ return {
96
+ status: record.status,
97
+ text: text.join("\n").slice(0, 200_000),
98
+ };
81
99
  }
82
100
  if (!["queued", "running", "waiting"].includes(record.status))
83
101
  throw new Error("Unknown remote Run status.");
@@ -226,7 +226,14 @@ export class AgentRunHost {
226
226
  const fetch = (path, init) => this.host.remoteFetch(destination, path, init);
227
227
  if (run.cancelRequested)
228
228
  await cancelRemoteRun(fetch, run);
229
- const outcome = await observeRemoteRun(fetch, run, cancellation.signal);
229
+ const outcome = await observeRemoteRun(fetch, run, cancellation.signal, async (message) => {
230
+ const stored = await this.host.session("input", {
231
+ runId: run.id,
232
+ message,
233
+ });
234
+ if (!stored.ok)
235
+ throw new Error(`Remote input notification rejected (${stored.status}).`);
236
+ });
230
237
  const stored = await this.host.session("complete", {
231
238
  runId: run.id,
232
239
  ...outcome,
@@ -72,6 +72,17 @@ export declare function getChildRun(tx: AgentSessionTransactionStorage, runId: s
72
72
  status: "aborted" | "error" | "finished" | "interrupted";
73
73
  text: string;
74
74
  } | null;
75
+ inputRequest?: {
76
+ id: string;
77
+ role: "assistant";
78
+ parts: {
79
+ [x: string]: unknown;
80
+ type: string;
81
+ }[];
82
+ metadata?: unknown;
83
+ } | undefined;
84
+ observedInputRequest?: string | undefined;
85
+ explicitWaitOnly?: boolean | undefined;
75
86
  } | undefined>;
76
87
  export declare function getChildSession(tx: AgentSessionTransactionStorage, sessionId: string): Promise<{
77
88
  agentKey: string;
@@ -149,6 +160,17 @@ export declare function readChildRuns(tx: AgentSessionTransactionStorage, refere
149
160
  status: "aborted" | "error" | "finished" | "interrupted";
150
161
  text: string;
151
162
  } | null;
163
+ inputRequest?: {
164
+ id: string;
165
+ role: "assistant";
166
+ parts: {
167
+ [x: string]: unknown;
168
+ type: string;
169
+ }[];
170
+ metadata?: unknown;
171
+ } | undefined;
172
+ observedInputRequest?: string | undefined;
173
+ explicitWaitOnly?: boolean | undefined;
152
174
  }[]>;
153
175
  export declare function listChildRuns(tx: AgentSessionTransactionStorage, after: number, limit: number): Promise<{
154
176
  runs: {
@@ -204,6 +226,17 @@ export declare function listChildRuns(tx: AgentSessionTransactionStorage, after:
204
226
  status: "aborted" | "error" | "finished" | "interrupted";
205
227
  text: string;
206
228
  } | null;
229
+ inputRequest?: {
230
+ id: string;
231
+ role: "assistant";
232
+ parts: {
233
+ [x: string]: unknown;
234
+ type: string;
235
+ }[];
236
+ metadata?: unknown;
237
+ } | undefined;
238
+ observedInputRequest?: string | undefined;
239
+ explicitWaitOnly?: boolean | undefined;
207
240
  }[];
208
241
  nextCursor: null;
209
242
  subagentConcurrencyVersion?: undefined;
@@ -262,6 +295,17 @@ export declare function listChildRuns(tx: AgentSessionTransactionStorage, after:
262
295
  status: "aborted" | "error" | "finished" | "interrupted";
263
296
  text: string;
264
297
  } | null;
298
+ inputRequest?: {
299
+ id: string;
300
+ role: "assistant";
301
+ parts: {
302
+ [x: string]: unknown;
303
+ type: string;
304
+ }[];
305
+ metadata?: unknown;
306
+ } | undefined;
307
+ observedInputRequest?: string | undefined;
308
+ explicitWaitOnly?: boolean | undefined;
265
309
  }[];
266
310
  nextCursor: number | null;
267
311
  }>;
@@ -1,4 +1,5 @@
1
1
  import { z } from "zod";
2
+ import { agentApplicationAssistantMessageSchema } from "@gea-ai/contract/agent-applications";
2
3
  import { readTraceContext } from "./trace-context.js";
3
4
  import { traceContextSchema } from "@gea-ai/contract/traces";
4
5
  import { DEFAULT_MAX_CONCURRENT_SUBAGENTS, maxConcurrentSubagentsSchema, } from "@gea-ai/contract/agent-packages";
@@ -69,6 +70,20 @@ export async function handleAgentSessionChildren(request, storage) {
69
70
  await acceptAgentSessionChildRun(tx, runId);
70
71
  return Response.json({ accepted: true });
71
72
  }
73
+ case "input": {
74
+ const { runId, message } = runIdSchema
75
+ .extend({ message: agentApplicationAssistantMessageSchema })
76
+ .parse(value);
77
+ const run = await getChildRun(tx, runId);
78
+ if (!run)
79
+ return Response.json({ error: "unknown_run" }, { status: 404 });
80
+ if (!run.outcome) {
81
+ run.inputRequest = message;
82
+ await putChildRun(tx, run);
83
+ await scheduleAgentSessionChildRunChanges(tx);
84
+ }
85
+ return Response.json({ run });
86
+ }
72
87
  case "start":
73
88
  case "reserve": {
74
89
  const input = startSchema.parse(value);
@@ -1,7 +1,7 @@
1
1
  import { getChildRun, putChildRun, readChildRuns, readChildRunIndex, } from "./agent-session-child-store.js";
2
2
  import { z } from "zod";
3
3
  import { agentSessionExecutionTurnSchema } from "@gea-ai/contract/agent-session";
4
- import { agentApplicationAssistantMessageSchema } from "@gea-ai/contract/agent-applications";
4
+ import { agentApplicationAssistantMessageSchema, agentInputRequestKey, } from "@gea-ai/contract/agent-applications";
5
5
  import { canonicalAgentChannelJson } from "@gea-ai/contract/agent-channels";
6
6
  import { agentSessionIdentitySchema, agentSessionBootstrapSchema, agentSessionTransportSchema, agentSessionDispatchSchema, agentSessionInboxFinishSchema, agentSessionInboxExecutionSchema, agentSessionSendSchema, agentRunWaitInputSchema, agentRunWaitResultSchema, toAgentRunWaitOutput, } from "@gea-ai/contract/agent-session-inbox";
7
7
  import { combineTraceContexts, readTraceContext } from "./trace-context.js";
@@ -40,6 +40,14 @@ const stateSchema = z.strictObject({
40
40
  terminal: terminalSchema.nullable().default(null),
41
41
  draining: terminalSchema.nullable().default(null),
42
42
  terminalQueue: z.array(terminalSchema).max(8).default([]),
43
+ inputNotification: z
44
+ .strictObject({
45
+ execution: agentSessionInboxExecutionSchema,
46
+ transport: agentSessionTransportSchema,
47
+ message: agentApplicationAssistantMessageSchema,
48
+ })
49
+ .nullable()
50
+ .default(null),
43
51
  });
44
52
  /** Only private definitions/self copies share the owning Agent's business context. */
45
53
  export async function inheritedAgentSessionMetadata(tx, destination) {
@@ -101,6 +109,7 @@ async function skipInputWait(tx, state, runs) {
101
109
  transport: state.transport,
102
110
  };
103
111
  state.inputWait = null;
112
+ state.inputNotification = null;
104
113
  if (runs.some((run) => !run.outcome)) {
105
114
  state.stopped = true;
106
115
  for (const run of runs)
@@ -280,17 +289,33 @@ function waitResult(state, runs, cancelled = false) {
280
289
  ? "steering"
281
290
  : !pendingRunIds.length
282
291
  ? "completed"
283
- : state.wait.deadline !== null && state.wait.deadline <= Date.now()
284
- ? "timeout"
285
- : null;
292
+ : selected.some((run) => !run.outcome &&
293
+ run.inputRequest &&
294
+ agentInputRequestKey(run.inputRequest) !==
295
+ run.observedInputRequest)
296
+ ? "input_required"
297
+ : state.wait.deadline !== null && state.wait.deadline <= Date.now()
298
+ ? "timeout"
299
+ : null;
286
300
  return reason
287
301
  ? {
288
302
  toolCallId: state.wait.toolCallId ?? "join",
289
303
  reason,
290
304
  runs: selected
291
- .filter((run) => run.outcome !== null)
305
+ .filter((run) => run.outcome !== null ||
306
+ (reason === "input_required" && run.inputRequest))
292
307
  .map((run) => {
293
308
  const limit = Math.floor(170_000 / Math.max(1, selected.length) / 6);
309
+ if (!run.outcome)
310
+ return {
311
+ runId: run.id,
312
+ sessionId: run.sessionId,
313
+ status: "waiting",
314
+ text: "This Run needs external tool input. Reply through its Session API, then explicitly call runWait again to await its completion.",
315
+ input: z
316
+ .json()
317
+ .parse(JSON.parse(JSON.stringify(run.inputRequest))),
318
+ };
294
319
  return {
295
320
  runId: run.id,
296
321
  sessionId: run.sessionId,
@@ -305,6 +330,20 @@ function waitResult(state, runs, cancelled = false) {
305
330
  }
306
331
  : null;
307
332
  }
333
+ /** Returning input transfers further waiting to the caller, including the other selected Runs. */
334
+ async function consumeInputRequest(tx, state, runs, result) {
335
+ if (result?.reason !== "input_required" || !state.wait)
336
+ return;
337
+ for (const run of runs)
338
+ if (state.wait.runIds.includes(run.id)) {
339
+ run.explicitWaitOnly = true;
340
+ if (run.inputRequest)
341
+ run.observedInputRequest = agentInputRequestKey(run.inputRequest);
342
+ if (run.destination.type === "remote")
343
+ state.pendingStarts = state.pendingStarts.filter((id) => id !== run.id);
344
+ await putChildRun(tx, run);
345
+ }
346
+ }
308
347
  /** State and the single wake deadline always commit in the same storage transaction. */
309
348
  async function save(tx, state, runs) {
310
349
  // Completion bursts spill to individual records; only a bounded batch joins the inbox.
@@ -320,7 +359,8 @@ async function save(tx, state, runs) {
320
359
  await tx.delete(key);
321
360
  }
322
361
  }
323
- if (state.draining && !runs.some((run) => !run.outcome)) {
362
+ if (state.draining &&
363
+ !runs.some((run) => !run.outcome && (!run.explicitWaitOnly || state.stopped))) {
324
364
  if (state.terminal)
325
365
  state.terminalQueue.push(state.draining);
326
366
  else
@@ -349,7 +389,10 @@ async function save(tx, state, runs) {
349
389
  const leaseDeadline = turn?.runId === state.execution?.turnId && turn?.status === "running"
350
390
  ? Date.parse(turn.leaseExpiresAt)
351
391
  : null;
352
- const scheduled = cancelling || state.pendingStarts.length || state.terminal
392
+ const scheduled = cancelling ||
393
+ state.pendingStarts.length ||
394
+ state.terminal ||
395
+ state.inputNotification
353
396
  ? Date.now()
354
397
  : state.execution
355
398
  ? state.started || !state.transport
@@ -384,7 +427,7 @@ export async function notifyAgentSessionChildRun(tx, run) {
384
427
  await save(tx, state, runs);
385
428
  return;
386
429
  }
387
- if (!state.wait?.runIds.includes(run.id)) {
430
+ if (!state.wait?.runIds.includes(run.id) && !run.explicitWaitOnly) {
388
431
  const id = `run:${run.id}`;
389
432
  if (!(await tx.get(`inbox:message:${id}`))) {
390
433
  const message = agentSessionSendSchema.parse({
@@ -425,11 +468,18 @@ async function finishInbox(tx, state, runs, input) {
425
468
  return Response.json({ error: "execution_not_expired" }, { status: 409 });
426
469
  }
427
470
  }
428
- if (inputWait && !outcome && !state.stopped)
471
+ if (inputWait && !outcome && !state.stopped) {
429
472
  state.inputWait = {
430
473
  turnId: state.execution.turnId,
431
474
  messageId: inputWait.messageId,
432
475
  };
476
+ if (inputWait.message && state.transport)
477
+ state.inputNotification = {
478
+ execution: { ...state.execution, messages: [], waitResult: null },
479
+ transport: state.transport,
480
+ message: inputWait.message,
481
+ };
482
+ }
433
483
  // A transport disconnect does not revoke already accepted child runs.
434
484
  if (outcome?.status === "cancelled" &&
435
485
  !state.stopped &&
@@ -464,7 +514,7 @@ async function finishInbox(tx, state, runs, input) {
464
514
  },
465
515
  transport: state.transport,
466
516
  };
467
- if (runs.some((run) => !run.outcome))
517
+ if (runs.some((run) => !run.outcome && (!run.explicitWaitOnly || state.stopped)))
468
518
  state.draining = terminal;
469
519
  else if (state.terminal)
470
520
  state.terminalQueue.push(terminal);
@@ -530,6 +580,7 @@ export async function handleAgentSessionInboxRequest(request, storage) {
530
580
  execution: null,
531
581
  wait: null,
532
582
  inputWait: null,
583
+ inputNotification: null,
533
584
  alarmAt: null,
534
585
  transport: null,
535
586
  started: false,
@@ -628,6 +679,7 @@ export async function handleAgentSessionInboxRequest(request, storage) {
628
679
  };
629
680
  state.wait = null;
630
681
  state.inputWait = null;
682
+ state.inputNotification = null;
631
683
  state.resumeWait = null;
632
684
  if (!state.draining)
633
685
  state.stopped = false;
@@ -654,6 +706,14 @@ export async function handleAgentSessionInboxRequest(request, storage) {
654
706
  const input = agentSessionDispatchSchema.parse(body);
655
707
  if (input.cancelRunIds || input.startRunIds)
656
708
  return Response.json({ accepted: true });
709
+ if (input.inputRequired) {
710
+ if (input.execution.id === state.inputNotification?.execution.id &&
711
+ input.inputRequired.id === state.inputNotification.message.id) {
712
+ state.inputNotification = null;
713
+ await save(tx, state, runs);
714
+ }
715
+ return Response.json({ accepted: true });
716
+ }
657
717
  if (input.outcome &&
658
718
  input.execution.id === state.terminal?.execution.id) {
659
719
  state.terminal = state.terminalQueue.shift() ?? null;
@@ -717,6 +777,7 @@ export async function handleAgentSessionInboxRequest(request, storage) {
717
777
  return Response.json({ execution: null });
718
778
  const messages = takeMessages(state, state.wait !== null);
719
779
  const joining = state.wait?.toolCallId === null;
780
+ await consumeInputRequest(tx, state, runs, result);
720
781
  state.stopped = false;
721
782
  state.execution = {
722
783
  id: crypto.randomUUID(),
@@ -768,6 +829,7 @@ export async function handleAgentSessionInboxRequest(request, storage) {
768
829
  state.wait?.turnId ??
769
830
  state.execution?.turnId;
770
831
  state.inputWait = null;
832
+ state.inputNotification = null;
771
833
  state.wait = null;
772
834
  if (state.execution && !state.started) {
773
835
  state.resumeWait ??= state.execution.waitResult;
@@ -816,7 +878,7 @@ export async function handleAgentSessionInboxRequest(request, storage) {
816
878
  return Response.json({ error: "execution_cancelled" }, { status: 409 });
817
879
  if (path.endsWith("/join")) {
818
880
  const runIds = runs
819
- .filter((run) => !run.outcome ||
881
+ .filter((run) => (!run.outcome && !run.explicitWaitOnly) ||
820
882
  state.messages.some((entry) => entry.id === `run:${run.id}`))
821
883
  .map((run) => run.id)
822
884
  .slice(0, 128);
@@ -859,11 +921,19 @@ export async function handleAgentSessionInboxRequest(request, storage) {
859
921
  : Date.now() + input.timeoutMs,
860
922
  traceContext: readTraceContext(request.headers),
861
923
  };
924
+ // A remote observation may have stopped after returning input to the caller.
925
+ for (const run of runs)
926
+ if (input.runIds.includes(run.id) &&
927
+ run.destination.type === "remote" &&
928
+ !run.outcome &&
929
+ !state.pendingStarts.includes(run.id))
930
+ state.pendingStarts.push(run.id);
862
931
  for (const id of input.runIds)
863
932
  await tx.delete(`inbox:child-result:${id}`);
864
933
  // A completed result already queued as a notification is consumed by this wait.
865
934
  state.messages = state.messages.filter((entry) => !input.runIds.some((id) => entry.id === `run:${id}`));
866
935
  const result = waitResult(state, runs);
936
+ await consumeInputRequest(tx, state, runs, result);
867
937
  if (result)
868
938
  state.wait = null;
869
939
  await save(tx, state, runs);
@@ -932,9 +1002,14 @@ export async function handleAgentSessionAlarm(storage, environment) {
932
1002
  .filter((run) => !run.outcome && run.cancelRequested)
933
1003
  .map((run) => run.id);
934
1004
  const childCommands = cancelling.length > 0 || state.pendingStarts.length > 0;
935
- if (state.started && !childCommands && !state.terminal)
1005
+ if (state.started &&
1006
+ !childCommands &&
1007
+ !state.terminal &&
1008
+ !state.inputNotification)
936
1009
  return;
937
- let execution = state.terminal?.execution ?? state.execution;
1010
+ let execution = state.terminal?.execution ??
1011
+ state.inputNotification?.execution ??
1012
+ state.execution;
938
1013
  if (childCommands && !execution)
939
1014
  execution = {
940
1015
  id: crypto.randomUUID(),
@@ -959,9 +1034,10 @@ export async function handleAgentSessionAlarm(storage, environment) {
959
1034
  const current = await readState(tx);
960
1035
  if (!current ||
961
1036
  (!childCommands &&
962
- ((current.terminal?.execution.id ?? current.execution?.id) !==
963
- executionId ||
964
- (current.started && !current.terminal))))
1037
+ ((current.terminal?.execution.id ??
1038
+ current.inputNotification?.execution.id ??
1039
+ current.execution?.id) !== executionId ||
1040
+ (current.started && !current.terminal && !current.inputNotification))))
965
1041
  return false;
966
1042
  current.alarmAt = Date.now() + 30_000;
967
1043
  await tx.setAlarm(current.alarmAt);
@@ -972,7 +1048,9 @@ export async function handleAgentSessionAlarm(storage, environment) {
972
1048
  return;
973
1049
  const transport = !childCommands && state.terminal
974
1050
  ? state.terminal.transport
975
- : state.transport;
1051
+ : !childCommands && state.inputNotification
1052
+ ? state.inputNotification.transport
1053
+ : state.transport;
976
1054
  const target = environment[transport.namespace];
977
1055
  const response = await target.get(target.idFromName(transport.name)).fetch(new Request("https://agent-session.internal/v1/inbox/dispatch", {
978
1056
  method: "POST",
@@ -987,7 +1065,9 @@ export async function handleAgentSessionAlarm(storage, environment) {
987
1065
  ? { startRunIds: state.pendingStarts }
988
1066
  : state.terminal
989
1067
  ? { outcome: state.terminal.outcome }
990
- : {}),
1068
+ : state.inputNotification
1069
+ ? { inputRequired: state.inputNotification.message }
1070
+ : {}),
991
1071
  }),
992
1072
  }));
993
1073
  if (!response.ok)
@@ -14,12 +14,13 @@ export declare class AgentSessionRunner {
14
14
  join(): Promise<void>;
15
15
  private request;
16
16
  wait(input: z.infer<typeof agentRunWaitInputSchema>, toolCallId: string): Promise<{
17
- reason: "cancelled" | "completed" | "steering" | "timeout";
17
+ reason: "cancelled" | "completed" | "input_required" | "steering" | "timeout";
18
18
  runs: {
19
- status: "aborted" | "error" | "finished" | "interrupted";
20
19
  text: string;
20
+ status: "aborted" | "error" | "finished" | "interrupted" | "waiting";
21
21
  runId: string;
22
22
  sessionId: string;
23
+ input?: z.util.JSONType | undefined;
23
24
  textTruncated?: boolean | undefined;
24
25
  }[];
25
26
  pendingRunIds: string[];
@@ -17,7 +17,7 @@ import { type AgentExecutionEnvironment, type AgentPackageSnapshot, type AgentRu
17
17
  import { z } from "zod";
18
18
  export { AgentChannelReceiver } from "./agent-channel-receiver";
19
19
  import { type AgentChannelJournalAppend } from "@gea-ai/contract/agent-channel-runtime";
20
- import { type AgentDurableObjectNamespace, type AgentToolApprovalDecision, type AgentToolIdentity, type AgentToolRuntimeSdk, type DefinedAgentPackage } from "./index";
20
+ import { type AgentDurableObjectNamespace, type AgentToolApprovalDecision, type AgentToolIdentity, type AgentToolRuntimeSdk, type DefinedAgent } from "./index";
21
21
  import { type AgentChannelAddressEnvironment } from "./agent-channel-worker";
22
22
  export { handleAgentChannelAddressAlarm, handleAgentChannelAddressRequest, type AgentChannelAddressEnvironment, type AgentChannelAddressStorage, type AgentChannelAddressTransactionStorage, } from "./agent-channel-worker";
23
23
  export type AgentHostBinding = Partial<WorkerAI> & {
@@ -76,7 +76,6 @@ export type AgentWorkerComputerOptions = {
76
76
  signal?: AbortSignal;
77
77
  };
78
78
  export type AgentFetchEnvironment = Record<string, unknown> & {
79
- PROJECT?: import("@gea-ai/contract").WorkerProjectBinding;
80
79
  AGENT_SESSIONS: AgentSessionNamespace;
81
80
  AGENT_TASK_SESSIONS?: AgentSessionNamespace;
82
81
  AGENTS?: AgentHostBinding;
@@ -96,8 +95,8 @@ export type AgentChannelFetchEnvironment = AgentFetchEnvironment & AgentChannelA
96
95
  CHANNEL_SESSIONS: AgentSessionNamespace;
97
96
  CHANNEL_RECEIVERS?: AgentChannelAddressNamespace;
98
97
  };
99
- type AgentApplicationDefinition = {
100
- definition: DefinedAgentPackage;
98
+ export type AgentApplicationDefinition = {
99
+ definition: DefinedAgent;
101
100
  instructions?: string;
102
101
  skillMount?: AgentWorkerSkillMount;
103
102
  subagents?: Record<string, AgentApplicationDefinition>;
@@ -107,13 +106,6 @@ type AgentStartupTiming = {
107
106
  phases: Record<string, number>;
108
107
  };
109
108
  export declare function composeFetch<TEnvironment, TContext>(...handlers: ReadonlyArray<ComposableFetch<TEnvironment, TContext>>): (request: Request, environment: TEnvironment, context: TContext) => Promise<Response>;
110
- export declare function createAgentFetch<TEnvironment extends AgentFetchEnvironment = AgentFetchEnvironment, TContext = unknown>(definition: DefinedAgentPackage): ComposableFetch<TEnvironment, TContext>;
111
- /**
112
- * Creates the public SDK-first Agent application protocol. A root agent.ts is
113
- * addressed by /gea/agents/run; top-level agents/<key>/agent.ts entrypoints are
114
- * addressed by /gea/agents/<key>/run. All main Agents share the Worker
115
- * deployment lifecycle while keeping separate AgentSession namespaces.
116
- */
117
109
  export declare function createAgentApplicationFetch<TEnvironment extends AgentFetchEnvironment = AgentFetchEnvironment, TContext = unknown>(input: {
118
110
  agents: Record<string, AgentApplicationDefinition>;
119
111
  defaultAgentKey: string;