@gea-ai/agent-sdk 0.1.261009-alpha.1 → 0.1.261012-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.
@@ -1,6 +1,32 @@
1
+ import { z } from "zod";
1
2
  import { type AgentWorkerToolInitialization } from "@gea-ai/contract/agent-worker-tools";
2
3
  import type { AgentHostBinding } from "./agent-worker.js";
3
4
  import type { StudioArtifactScope } from "@gea-ai/contract/studio-artifacts";
5
+ /** Fresh Sessions keep the declared-target rule. A resume only needs sessionId. */
6
+ export declare function workerAgentToolInputSchema(input: {
7
+ agents: NonNullable<AgentWorkerToolInitialization["agents"]>;
8
+ canCopy?: boolean;
9
+ artifactScope?: StudioArtifactScope;
10
+ }): z.ZodObject<{
11
+ sessionId: z.ZodOptional<z.ZodUUID>;
12
+ target: z.ZodOptional<z.ZodString>;
13
+ message: z.ZodUnion<readonly [z.ZodString, z.ZodObject<{
14
+ role: z.ZodLiteral<"user">;
15
+ parts: z.ZodArray<z.ZodObject<{
16
+ type: z.ZodLiteral<"text">;
17
+ text: z.ZodString;
18
+ }, z.core.$strict>>;
19
+ }, z.core.$strict>]> | z.ZodUnion<readonly [z.ZodString, z.ZodObject<{
20
+ role: z.ZodLiteral<"user">;
21
+ parts: z.ZodArray<z.ZodUnion<readonly [z.ZodObject<{
22
+ type: z.ZodLiteral<"text">;
23
+ text: z.ZodString;
24
+ }, z.core.$strict>, z.ZodObject<{
25
+ type: z.ZodLiteral<"file">;
26
+ file_id: z.ZodUUID;
27
+ }, z.core.$strict>]>>;
28
+ }, z.core.$strict>]>;
29
+ }, z.core.$strip>;
4
30
  /** The host opens a fresh Worker invocation; only its visible answer returns to the caller. */
5
31
  export declare function createWorkerAgentTool(input: {
6
32
  agents: NonNullable<AgentWorkerToolInitialization["agents"]>;
@@ -14,7 +40,7 @@ export declare function createWorkerAgentTool(input: {
14
40
  metadata?: import("@ai-sdk/provider").JSONObject;
15
41
  inputSchema: import("ai").FlexibleSchema<{
16
42
  sessionId?: string | undefined;
17
- target: string | undefined;
43
+ target?: string | undefined;
18
44
  message: string | {
19
45
  role: "user";
20
46
  parts: ({
@@ -35,7 +61,7 @@ export declare function createWorkerAgentTool(input: {
35
61
  contextSchema?: import("ai").FlexibleSchema<import("@ai-sdk/provider-utils").Context> | undefined;
36
62
  needsApproval?: boolean | import("@ai-sdk/provider-utils").ToolNeedsApprovalFunction<{
37
63
  sessionId?: string | undefined;
38
- target: string | undefined;
64
+ target?: string | undefined;
39
65
  message: string | {
40
66
  role: "user";
41
67
  parts: ({
@@ -60,7 +86,7 @@ export declare function createWorkerAgentTool(input: {
60
86
  onInputAvailable?: ((options: {
61
87
  input: {
62
88
  sessionId?: string | undefined;
63
- target: string | undefined;
89
+ target?: string | undefined;
64
90
  message: string | {
65
91
  role: "user";
66
92
  parts: ({
@@ -83,7 +109,7 @@ export declare function createWorkerAgentTool(input: {
83
109
  toolCallId: string;
84
110
  input: {
85
111
  sessionId?: string | undefined;
86
- target: string | undefined;
112
+ target?: string | undefined;
87
113
  message: string | {
88
114
  role: "user";
89
115
  parts: ({
@@ -145,7 +171,7 @@ export declare function createWorkerAgentTool(input: {
145
171
  }> | undefined;
146
172
  execute: import("ai").ToolExecuteFunction<{
147
173
  sessionId?: string | undefined;
148
- target: string | undefined;
174
+ target?: string | undefined;
149
175
  message: string | {
150
176
  role: "user";
151
177
  parts: ({
@@ -191,7 +217,7 @@ export declare function createWorkerAgentTool(input: {
191
217
  inputExamples?: {
192
218
  input: NoInfer<{
193
219
  sessionId?: string | undefined;
194
- target: string | undefined;
220
+ target?: string | undefined;
195
221
  message: string | {
196
222
  role: "user";
197
223
  parts: ({
@@ -219,7 +245,7 @@ export declare function createWorkerAgentTool(input: {
219
245
  } & {
220
246
  execute: import("ai").ToolExecuteFunction<{
221
247
  sessionId?: string | undefined;
222
- target: string | undefined;
248
+ target?: string | undefined;
223
249
  message: string | {
224
250
  role: "user";
225
251
  parts: ({
@@ -263,7 +289,7 @@ export declare function createWorkerAgentTool(input: {
263
289
  metadata?: import("@ai-sdk/provider").JSONObject;
264
290
  inputSchema: import("ai").FlexibleSchema<{
265
291
  sessionId?: string | undefined;
266
- target: string | undefined;
292
+ target?: string | undefined;
267
293
  message: string | {
268
294
  role: "user";
269
295
  parts: ({
@@ -284,7 +310,7 @@ export declare function createWorkerAgentTool(input: {
284
310
  contextSchema?: import("ai").FlexibleSchema<import("@ai-sdk/provider-utils").Context> | undefined;
285
311
  needsApproval?: boolean | import("@ai-sdk/provider-utils").ToolNeedsApprovalFunction<{
286
312
  sessionId?: string | undefined;
287
- target: string | undefined;
313
+ target?: string | undefined;
288
314
  message: string | {
289
315
  role: "user";
290
316
  parts: ({
@@ -309,7 +335,7 @@ export declare function createWorkerAgentTool(input: {
309
335
  onInputAvailable?: ((options: {
310
336
  input: {
311
337
  sessionId?: string | undefined;
312
- target: string | undefined;
338
+ target?: string | undefined;
313
339
  message: string | {
314
340
  role: "user";
315
341
  parts: ({
@@ -332,7 +358,7 @@ export declare function createWorkerAgentTool(input: {
332
358
  toolCallId: string;
333
359
  input: {
334
360
  sessionId?: string | undefined;
335
- target: string | undefined;
361
+ target?: string | undefined;
336
362
  message: string | {
337
363
  role: "user";
338
364
  parts: ({
@@ -394,7 +420,7 @@ export declare function createWorkerAgentTool(input: {
394
420
  }> | undefined;
395
421
  execute: import("ai").ToolExecuteFunction<{
396
422
  sessionId?: string | undefined;
397
- target: string | undefined;
423
+ target?: string | undefined;
398
424
  message: string | {
399
425
  role: "user";
400
426
  parts: ({
@@ -440,7 +466,7 @@ export declare function createWorkerAgentTool(input: {
440
466
  inputExamples?: {
441
467
  input: NoInfer<{
442
468
  sessionId?: string | undefined;
443
- target: string | undefined;
469
+ target?: string | undefined;
444
470
  message: string | {
445
471
  role: "user";
446
472
  parts: ({
@@ -468,7 +494,7 @@ export declare function createWorkerAgentTool(input: {
468
494
  } & {
469
495
  execute: import("ai").ToolExecuteFunction<{
470
496
  sessionId?: string | undefined;
471
- target: string | undefined;
497
+ target?: string | undefined;
472
498
  message: string | {
473
499
  role: "user";
474
500
  parts: ({
@@ -512,7 +538,7 @@ export declare function createWorkerAgentTool(input: {
512
538
  metadata?: import("@ai-sdk/provider").JSONObject;
513
539
  inputSchema: import("ai").FlexibleSchema<{
514
540
  sessionId?: string | undefined;
515
- target: string | undefined;
541
+ target?: string | undefined;
516
542
  message: string | {
517
543
  role: "user";
518
544
  parts: ({
@@ -533,7 +559,7 @@ export declare function createWorkerAgentTool(input: {
533
559
  contextSchema?: import("ai").FlexibleSchema<import("@ai-sdk/provider-utils").Context> | undefined;
534
560
  needsApproval?: boolean | import("@ai-sdk/provider-utils").ToolNeedsApprovalFunction<{
535
561
  sessionId?: string | undefined;
536
- target: string | undefined;
562
+ target?: string | undefined;
537
563
  message: string | {
538
564
  role: "user";
539
565
  parts: ({
@@ -558,7 +584,7 @@ export declare function createWorkerAgentTool(input: {
558
584
  onInputAvailable?: ((options: {
559
585
  input: {
560
586
  sessionId?: string | undefined;
561
- target: string | undefined;
587
+ target?: string | undefined;
562
588
  message: string | {
563
589
  role: "user";
564
590
  parts: ({
@@ -581,7 +607,7 @@ export declare function createWorkerAgentTool(input: {
581
607
  toolCallId: string;
582
608
  input: {
583
609
  sessionId?: string | undefined;
584
- target: string | undefined;
610
+ target?: string | undefined;
585
611
  message: string | {
586
612
  role: "user";
587
613
  parts: ({
@@ -643,7 +669,7 @@ export declare function createWorkerAgentTool(input: {
643
669
  }> | undefined;
644
670
  execute: import("ai").ToolExecuteFunction<{
645
671
  sessionId?: string | undefined;
646
- target: string | undefined;
672
+ target?: string | undefined;
647
673
  message: string | {
648
674
  role: "user";
649
675
  parts: ({
@@ -693,7 +719,7 @@ export declare function createWorkerAgentTool(input: {
693
719
  } & {
694
720
  execute: import("ai").ToolExecuteFunction<{
695
721
  sessionId?: string | undefined;
696
- target: string | undefined;
722
+ target?: string | undefined;
697
723
  message: string | {
698
724
  role: "user";
699
725
  parts: ({
@@ -737,7 +763,7 @@ export declare function createWorkerAgentTool(input: {
737
763
  metadata?: import("@ai-sdk/provider").JSONObject;
738
764
  inputSchema: import("ai").FlexibleSchema<{
739
765
  sessionId?: string | undefined;
740
- target: string | undefined;
766
+ target?: string | undefined;
741
767
  message: string | {
742
768
  role: "user";
743
769
  parts: ({
@@ -758,7 +784,7 @@ export declare function createWorkerAgentTool(input: {
758
784
  contextSchema?: import("ai").FlexibleSchema<import("@ai-sdk/provider-utils").Context> | undefined;
759
785
  needsApproval?: boolean | import("@ai-sdk/provider-utils").ToolNeedsApprovalFunction<{
760
786
  sessionId?: string | undefined;
761
- target: string | undefined;
787
+ target?: string | undefined;
762
788
  message: string | {
763
789
  role: "user";
764
790
  parts: ({
@@ -783,7 +809,7 @@ export declare function createWorkerAgentTool(input: {
783
809
  onInputAvailable?: ((options: {
784
810
  input: {
785
811
  sessionId?: string | undefined;
786
- target: string | undefined;
812
+ target?: string | undefined;
787
813
  message: string | {
788
814
  role: "user";
789
815
  parts: ({
@@ -806,7 +832,7 @@ export declare function createWorkerAgentTool(input: {
806
832
  toolCallId: string;
807
833
  input: {
808
834
  sessionId?: string | undefined;
809
- target: string | undefined;
835
+ target?: string | undefined;
810
836
  message: string | {
811
837
  role: "user";
812
838
  parts: ({
@@ -868,7 +894,7 @@ export declare function createWorkerAgentTool(input: {
868
894
  }> | undefined;
869
895
  execute: import("ai").ToolExecuteFunction<{
870
896
  sessionId?: string | undefined;
871
- target: string | undefined;
897
+ target?: string | undefined;
872
898
  message: string | {
873
899
  role: "user";
874
900
  parts: ({
@@ -918,7 +944,7 @@ export declare function createWorkerAgentTool(input: {
918
944
  } & {
919
945
  execute: import("ai").ToolExecuteFunction<{
920
946
  sessionId?: string | undefined;
921
- target: string | undefined;
947
+ target?: string | undefined;
922
948
  message: string | {
923
949
  role: "user";
924
950
  parts: ({
@@ -1225,7 +1251,9 @@ export declare function createWorkerRunCancelTool({ host, }: {
1225
1251
  status: "abort_requested" | "not_running";
1226
1252
  }, NoInfer<import("@ai-sdk/provider-utils").Context>>;
1227
1253
  });
1228
- export declare function readWorkerAgentResponse(response: Response): Promise<{
1254
+ export declare function readWorkerAgentResponse(response: Response, options?: {
1255
+ persistedSessionWaiting?: () => Promise<boolean>;
1256
+ }): Promise<{
1229
1257
  status: "completed" | "suspended";
1230
1258
  text: string;
1231
1259
  }>;
@@ -5,20 +5,45 @@ import { parseJsonEventStream } from "@ai-sdk/provider-utils";
5
5
  import { tool, uiMessageChunkSchema } from "ai";
6
6
  import { agentSubagentConcurrencyErrorSchema, agentRemoteAdmissionErrorSchema, agentCallInputSchema, agentCallTextMessageSchema, agentRunCallResultSchema, getAgentCallFileIds, } from "@gea-ai/contract/agent-worker-tools";
7
7
  import { captureTraceContext } from "./trace-context.js";
8
+ /** Fresh Sessions keep the declared-target rule. A resume only needs sessionId. */
9
+ export function workerAgentToolInputSchema(input) {
10
+ const message = input.artifactScope
11
+ ? agentCallInputSchema.shape.message
12
+ : agentCallTextMessageSchema;
13
+ const keys = new Set(input.agents.map((agent) => agent.key));
14
+ return z
15
+ .object({
16
+ sessionId: z.uuid().optional(),
17
+ target: z.string().trim().min(1).max(80).optional(),
18
+ message,
19
+ })
20
+ .superRefine((value, context) => {
21
+ if (value.sessionId)
22
+ return;
23
+ if (value.target === undefined) {
24
+ if (keys.size > 0 && !input.canCopy) {
25
+ context.addIssue({
26
+ code: "custom",
27
+ path: ["target"],
28
+ message: "A new Session requires a declared target.",
29
+ });
30
+ }
31
+ return;
32
+ }
33
+ if (!keys.has(value.target)) {
34
+ context.addIssue({
35
+ code: "custom",
36
+ path: ["target"],
37
+ message: "Select a declared target for a new Session.",
38
+ });
39
+ }
40
+ });
41
+ }
8
42
  /** The host opens a fresh Worker invocation; only its visible answer returns to the caller. */
9
43
  export function createWorkerAgentTool(input) {
10
44
  return tool({
11
- description: `Start an Agent Run in a new or idle child Session. Use runWait with returned runIds and runCancel to cancel. Use sessionId to continue the same Session. ${input.canCopy ? "Omit target to create a copy of yourself." : "Select a declared target."} Available targets: ${JSON.stringify(input.agents)}. Supply context in message.${input.artifactScope ? " To send selected files, use a user message with file parts containing immutable Artifact file_id values. Refer to attachments by filename or order in task text; remote transfer may assign new IDs, exposed to the child in Attached Artifact metadata. IDs hardcoded in text are not rewritten." : ""}`,
12
- inputSchema: agentCallInputSchema.extend({
13
- target: input.agents.length
14
- ? input.canCopy
15
- ? z.enum(input.agents.map((agent) => agent.key)).optional()
16
- : z.enum(input.agents.map((agent) => agent.key))
17
- : z.never().optional(),
18
- message: input.artifactScope
19
- ? agentCallInputSchema.shape.message
20
- : agentCallTextMessageSchema,
21
- }),
45
+ description: `Start an Agent Run in a new or idle child Session. Use runWait with returned runIds and runCancel to cancel. Use sessionId to continue the same Session; target may be omitted or name the recorded Agent. ${input.canCopy ? "Omit target to create a copy of yourself." : "Select a declared target for a new Session."} Available targets: ${JSON.stringify(input.agents)}. Supply context in message.${input.artifactScope ? " To send selected files, use a user message with file parts containing immutable Artifact file_id values. Refer to attachments by filename or order in task text; remote transfer may assign new IDs, exposed to the child in Attached Artifact metadata. IDs hardcoded in text are not rewritten." : ""}`,
46
+ inputSchema: workerAgentToolInputSchema(input),
22
47
  execute: async (value, options) => {
23
48
  const fileIds = getAgentCallFileIds(value.message);
24
49
  const response = await input.host.fetch("/internal/agent-worker-host/v1/agents/runs", {
@@ -70,6 +95,23 @@ export function createWorkerAgentTool(input) {
70
95
  error: "child_busy",
71
96
  message: "This child Session has unfinished work. Wait for its Run before continuing the Session.",
72
97
  };
98
+ if (response.status === 403) {
99
+ const rejection = z
100
+ .object({
101
+ error: z
102
+ .string()
103
+ .max(80)
104
+ .regex(/^[A-Za-z0-9_.-]+$/),
105
+ })
106
+ .safeParse(value);
107
+ if (rejection.success) {
108
+ console.error("Child run rejected", {
109
+ status: 403,
110
+ error: rejection.data.error,
111
+ });
112
+ throw new Error(`Agent invocation was rejected (403 ${rejection.data.error}).`);
113
+ }
114
+ }
73
115
  }
74
116
  throw new Error(`Agent invocation was rejected (${response.status}).`);
75
117
  }
@@ -99,7 +141,15 @@ export function createWorkerRunCancelTool({ host, }) {
99
141
  }
100
142
  class AgentChildRunResponseError extends TaggedError("AgentChildRunResponseError")() {
101
143
  }
102
- export async function readWorkerAgentResponse(response) {
144
+ const childDiagnosticIdentifierPattern = /^[A-Za-z0-9_.-]+$/;
145
+ function childDiagnosticIdentifier(value) {
146
+ return typeof value === "string" &&
147
+ value.length <= 80 &&
148
+ childDiagnosticIdentifierPattern.test(value)
149
+ ? value
150
+ : undefined;
151
+ }
152
+ export async function readWorkerAgentResponse(response, options) {
103
153
  if (response.status === 409 &&
104
154
  response.headers.get("x-gea-session-execution") === "already-started") {
105
155
  await response.body?.cancel();
@@ -117,6 +167,9 @@ export async function readWorkerAgentResponse(response) {
117
167
  let completed = false;
118
168
  let suspended = false;
119
169
  let failure;
170
+ let legacyToolCalls = false;
171
+ let otherUnacceptableFinish = false;
172
+ let sawStreamFailure = false;
120
173
  const reader = parseJsonEventStream({
121
174
  stream: response.body,
122
175
  schema: uiMessageChunkSchema,
@@ -133,26 +186,46 @@ export async function readWorkerAgentResponse(response) {
133
186
  const chunk = event.value;
134
187
  if (chunk.type === "text-delta")
135
188
  text += chunk.delta;
136
- if (chunk.type === "error")
189
+ if (chunk.type === "error") {
190
+ sawStreamFailure = true;
137
191
  failure ??= new AgentChildRunResponseError({
138
192
  code: "child_stream_error",
139
193
  message: chunk.errorText,
140
194
  });
141
- if (chunk.type === "abort")
195
+ }
196
+ if (chunk.type === "abort") {
197
+ sawStreamFailure = true;
142
198
  failure ??= new AgentChildRunResponseError({
143
199
  code: "child_stream_aborted",
144
200
  message: "Child Agent was cancelled.",
145
201
  });
202
+ }
146
203
  if (chunk.type === "finish") {
147
204
  suspended = z
148
205
  .object({ sessionStatus: z.literal("waiting") })
149
206
  .safeParse(chunk.messageMetadata).success;
150
- if (chunk.finishReason !== "stop" &&
151
- !(suspended && chunk.finishReason === "tool-calls"))
207
+ const unacceptable = chunk.finishReason !== "stop" &&
208
+ !(suspended && chunk.finishReason === "tool-calls");
209
+ if (unacceptable) {
210
+ if (chunk.finishReason === "tool-calls" && !suspended)
211
+ legacyToolCalls = true;
212
+ else
213
+ otherUnacceptableFinish = true;
214
+ const parsedSession = z
215
+ .object({ sessionStatus: z.unknown() })
216
+ .safeParse(chunk.messageMetadata);
217
+ const rawSessionStatus = parsedSession.success
218
+ ? parsedSession.data.sessionStatus
219
+ : undefined;
220
+ const sessionStatus = childDiagnosticIdentifier(rawSessionStatus);
152
221
  failure ??= new AgentChildRunResponseError({
153
222
  code: "child_finish_reason",
154
223
  message: `Child Agent did not complete (${chunk.finishReason ?? "unknown"}).`,
224
+ finishReason: childDiagnosticIdentifier(chunk.finishReason) ?? "unknown",
225
+ hasSessionStatus: rawSessionStatus !== undefined,
226
+ ...(sessionStatus ? { sessionStatus } : {}),
155
227
  });
228
+ }
156
229
  completed = true;
157
230
  }
158
231
  }
@@ -161,6 +234,18 @@ export async function readWorkerAgentResponse(response) {
161
234
  await reader.cancel();
162
235
  reader.releaseLock();
163
236
  }
237
+ if (options?.persistedSessionWaiting &&
238
+ legacyToolCalls &&
239
+ !otherUnacceptableFinish &&
240
+ !sawStreamFailure &&
241
+ failure instanceof AgentChildRunResponseError &&
242
+ failure.code === "child_finish_reason") {
243
+ const waiting = await Result.tryPromise(() => options.persistedSessionWaiting());
244
+ if (waiting.isOk() && waiting.value) {
245
+ failure = undefined;
246
+ suspended = true;
247
+ }
248
+ }
164
249
  if (failure)
165
250
  throw failure;
166
251
  if (!completed)
@@ -40,7 +40,7 @@ export function extractChannelReply(messageValue, responseMessageId) {
40
40
  }
41
41
  /** Channel owns delivery; ordinary Agents API owns admission, execution and history. */
42
42
  export async function executeChannelAgentRun(input) {
43
- const fetchApi = async (path, body) => {
43
+ const fetchApi = async (path, body, headers) => {
44
44
  input.signal.throwIfAborted();
45
45
  const response = await input.binding.fetch(`/api/v1${path}`, {
46
46
  method: body === undefined ? "GET" : "POST",
@@ -48,7 +48,7 @@ export async function executeChannelAgentRun(input) {
48
48
  ...(body === undefined
49
49
  ? {}
50
50
  : {
51
- headers: { "content-type": "application/json" },
51
+ headers: { "content-type": "application/json", ...headers },
52
52
  body: JSON.stringify(body),
53
53
  }),
54
54
  });
@@ -67,7 +67,7 @@ export async function executeChannelAgentRun(input) {
67
67
  id: input.runId,
68
68
  input: input.message,
69
69
  stream: false,
70
- })).body?.cancel();
70
+ }, { "idempotency-key": input.runId })).body?.cancel();
71
71
  let stream = true;
72
72
  for (;;) {
73
73
  const current = runSchema.parse(await (await fetchApi(`/runs/${input.runId}`)).json());
@@ -19,19 +19,6 @@ 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
- } | {
35
22
  status: "sent";
36
23
  receipt?: AgentChannelJson | undefined;
37
24
  } | {
@@ -56,6 +43,19 @@ export declare function executeChannelBehavior(input: {
56
43
  message: string;
57
44
  retryable: boolean;
58
45
  };
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;
@@ -62,13 +62,42 @@ export async function submitRemoteRun(fetch, input, options = {}) {
62
62
  }))).id;
63
63
  signal.throwIfAborted();
64
64
  submitting = true;
65
- // No automatic replay: a lost response does not prove Run admission failed.
66
- const response = await fetch(`/sessions/${sessionId}/runs`, {
67
- method: "POST",
68
- headers: { "content-type": "application/json" },
69
- signal,
70
- body: JSON.stringify({ input: message, stream: false }),
65
+ // One key per logical submission. Retries reuse it so a lost 202 cannot
66
+ // start a second Run. A later call to submitRemoteRun is a new request.
67
+ const idempotencyKey = crypto.randomUUID();
68
+ const runBody = JSON.stringify({
69
+ id: idempotencyKey,
70
+ input: message,
71
+ stream: false,
71
72
  });
73
+ const runHeaders = {
74
+ "content-type": "application/json",
75
+ "idempotency-key": idempotencyKey,
76
+ };
77
+ let response;
78
+ for (let attempt = 0; attempt < 3; attempt++) {
79
+ signal.throwIfAborted();
80
+ try {
81
+ response = await fetch(`/sessions/${sessionId}/runs`, {
82
+ method: "POST",
83
+ headers: runHeaders,
84
+ signal,
85
+ body: runBody,
86
+ });
87
+ }
88
+ catch (error) {
89
+ if (signal.aborted || attempt === 2)
90
+ throw error;
91
+ continue;
92
+ }
93
+ const retryable = [502, 503, 504].includes(response.status);
94
+ const admitted = response.headers.has("x-gea-agent-run-id");
95
+ if (!retryable || admitted || attempt === 2)
96
+ break;
97
+ await response.body?.cancel();
98
+ }
99
+ if (!response)
100
+ throw new Error("Remote Run was not submitted.");
72
101
  rejected = [400, 401, 403, 404, 405, 409, 413, 415, 422, 429].includes(response.status);
73
102
  const run = agentHttpRunSchema.parse(await responseJson(response));
74
103
  if (run.session_id !== sessionId)
@@ -7,21 +7,33 @@ export type { RemoteRunFetch, RemoteRunDestination, RemoteInputFiles, RemoteFile
7
7
  export { readWorkerAgentResponse } from "./agent-call.js";
8
8
  export { streamManagedAgentSessionRun } from "./agent-session-stream.js";
9
9
  export type AgentChildRunSession = (operation: string, body: unknown, traceContext?: TraceContext) => Promise<Response>;
10
+ /**
11
+ * Renders child Run updates for a parent model turn. Upstream dropped the task-era
12
+ * helper without a replacement; App Server adapters still need one summary shape.
13
+ * JSON escaping can expand each character sixfold; leave room for Run identities.
14
+ */
15
+ export declare function formatAgentChildRunUpdates(runs: Pick<AgentChildRun, "id" | "sessionId" | "agentKey" | "target" | "outcome">[]): string;
10
16
  /** Invocation transport only. SDK Sessions own Run state, waits, completion and wakeups. */
11
17
  export declare class AgentRunHost {
12
18
  private readonly host;
13
19
  private readonly executions;
20
+ private readonly executionDone;
14
21
  private readonly work;
15
22
  private closed;
16
23
  private readonly shutdown;
17
24
  constructor(host: {
18
25
  session: AgentChildRunSession;
26
+ /** Native App Server owns Worker handles; this callback only admits the resolved target. */
27
+ dispatch?(run: AgentChildRun, execution?: AgentSessionInboxExecution): Promise<void>;
28
+ /** Authorize against the root Session and return its retained child binding. */
29
+ resolveSession?(sessionId: string): Promise<Response>;
19
30
  remoteFetch?(destination: RemoteRunDestination, path: string, init?: RequestInit): Promise<Response>;
20
31
  transferRemoteInputs?(destination: RemoteRunDestination, files: RemoteInputFiles, scope: StudioArtifactScope | undefined, signal: AbortSignal): Promise<RemoteFileMapping>;
21
32
  register(run: AgentChildRun): Promise<string>;
22
33
  invoke(run: AgentChildRun, signal: AbortSignal, continuation?: TraceContinuation, inboxExecution?: AgentSessionInboxExecution): Promise<{
23
34
  response: Response;
24
35
  dispose?: () => void;
36
+ persistedSessionWaiting?: () => Promise<boolean>;
25
37
  }>;
26
38
  cancelChild(run: AgentChildRun): Promise<boolean | void>;
27
39
  onError(error: unknown, identity?: {