@agent-native/core 0.208.0 → 0.209.0-nightly-20261014070126

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 (74) hide show
  1. package/agent-native.eject.json +1 -0
  2. package/dist/agent/approval-key.d.ts +2 -0
  3. package/dist/agent/approval-key.js +12 -0
  4. package/dist/agent/context-xray/tokenize.js +12 -3
  5. package/dist/agent/engine/translate-anthropic.js +4 -0
  6. package/dist/agent/engine/types.d.ts +2 -0
  7. package/dist/agent/production-agent.d.ts +20 -5
  8. package/dist/agent/production-agent.js +227 -34
  9. package/dist/agent/run-manager.js +57 -11
  10. package/dist/agent/run-store.d.ts +10 -0
  11. package/dist/agent/run-store.js +49 -4
  12. package/dist/agent/thread-data-builder.d.ts +2 -0
  13. package/dist/agent/thread-data-builder.js +288 -14
  14. package/dist/agent/tool-approval-store.d.ts +3 -1
  15. package/dist/agent/tool-approval-store.js +26 -12
  16. package/dist/agent/tool-call-journal.d.ts +1 -1
  17. package/dist/agent/tool-call-journal.js +5 -3
  18. package/dist/agent/types.d.ts +7 -0
  19. package/dist/client/chat/agentkit-agent-native.js +18 -0
  20. package/dist/client/chat/agentkit-protocol.js +56 -5
  21. package/dist/client/chat/runtime.d.ts +10 -0
  22. package/dist/client/chat/runtime.js +257 -26
  23. package/dist/client/sse-event-processor.d.ts +22 -0
  24. package/dist/client/sse-event-processor.js +91 -11
  25. package/dist/collab/struct-routes.d.ts +1 -1
  26. package/dist/connections/catalog.d.ts +24 -1
  27. package/dist/connections/catalog.js +12 -0
  28. package/dist/data-programs/schema.d.ts +1 -0
  29. package/dist/data-programs/schema.js +3 -0
  30. package/dist/data-programs/store.js +2 -1
  31. package/dist/deploy/build.js +16 -14
  32. package/dist/eject/workspace-connections.js +1 -0
  33. package/dist/extensions/fetch-tool.js +114 -23
  34. package/dist/extensions/schema.d.ts +1 -0
  35. package/dist/extensions/schema.js +3 -0
  36. package/dist/extensions/store.js +2 -1
  37. package/dist/extensions/web-content.d.ts +5 -1
  38. package/dist/extensions/web-content.js +24 -6
  39. package/dist/file-upload/actions/upload-image.d.ts +1 -1
  40. package/dist/notifications/routes.d.ts +1 -1
  41. package/dist/provider-api/actions/custom-provider-registration.d.ts +11 -11
  42. package/dist/provider-api/actions/provider-api.d.ts +9 -9
  43. package/dist/provider-api/corpus-jobs.d.ts +2 -2
  44. package/dist/provider-api/index.d.ts +2 -0
  45. package/dist/provider-api/index.js +95 -12
  46. package/dist/resources/handlers.d.ts +1 -1
  47. package/dist/server/agent-engine-default-model-route.d.ts +2 -2
  48. package/dist/server/agent-engine-ollama-models-route.d.ts +1 -1
  49. package/dist/server/auth.js +1 -0
  50. package/dist/server/core-routes-plugin.js +1 -0
  51. package/dist/server/index.d.ts +2 -1
  52. package/dist/server/index.js +2 -1
  53. package/dist/server/realtime-token.d.ts +1 -1
  54. package/dist/server/transcribe-voice.d.ts +1 -1
  55. package/dist/server/workspace-provider-oauth.d.ts +1 -1
  56. package/dist/server/workspace-provider-oauth.js +2 -0
  57. package/dist/shared/context-status.js +1 -1
  58. package/dist/shared/optional-peer-ssr.d.ts +2 -0
  59. package/dist/shared/optional-peer-ssr.js +123 -0
  60. package/dist/shared/tool-inputs.d.ts +2 -0
  61. package/dist/shared/tool-inputs.js +56 -0
  62. package/dist/sharing/actions/list-resource-shares.d.ts +1 -1
  63. package/dist/tracking/index.d.ts +1 -0
  64. package/dist/tracking/index.js +1 -0
  65. package/dist/vite/client.js +46 -3
  66. package/dist/workspace-connections/store.d.ts +3 -1
  67. package/dist/workspace-connections/store.js +18 -8
  68. package/dist/workspace-files/constants.d.ts +3 -0
  69. package/dist/workspace-files/constants.js +3 -0
  70. package/dist/workspace-files/store.d.ts +2 -3
  71. package/dist/workspace-files/store.js +2 -3
  72. package/docs/content/automations.mdx +19 -13
  73. package/package.json +3 -3
  74. package/src/eject/workspace-connections.ts +1 -0
@@ -18,6 +18,7 @@
18
18
  "notion",
19
19
  "gmail",
20
20
  "google_calendar",
21
+ "bigquery",
21
22
  "google_docs",
22
23
  "google_drive",
23
24
  "google_sheets",
@@ -0,0 +1,2 @@
1
+ export declare function hashAgentToolApprovalKey(approvalKey: string): string;
2
+ export declare function approvalKeyForHistory(approvalKey: string): string;
@@ -0,0 +1,12 @@
1
+ import { sha256 } from "@noble/hashes/sha2.js";
2
+ import { bytesToHex } from "@noble/hashes/utils.js";
3
+ const APPROVAL_KEY_HISTORY_PREFIX = "agent-tool-approval-key-sha256:";
4
+ const HASHED_APPROVAL_KEY_PATTERN = /^agent-tool-approval-key-sha256:[a-f0-9]{64}$/;
5
+ export function hashAgentToolApprovalKey(approvalKey) {
6
+ return bytesToHex(sha256(new TextEncoder().encode(approvalKey)));
7
+ }
8
+ export function approvalKeyForHistory(approvalKey) {
9
+ if (HASHED_APPROVAL_KEY_PATTERN.test(approvalKey))
10
+ return approvalKey;
11
+ return `${APPROVAL_KEY_HISTORY_PREFIX}${hashAgentToolApprovalKey(approvalKey)}`;
12
+ }
@@ -7,9 +7,18 @@ async function loadTokenizer() {
7
7
  // browser fallback, while the documented estimate below is sufficient
8
8
  // when an edge runtime cannot resolve the tokenizer.
9
9
  tokenizerPromise = import(/* @vite-ignore */ TOKENIZER_MODULE_ID)
10
- .then((mod) => typeof mod.countTokens === "function"
11
- ? mod.countTokens
12
- : null)
10
+ .then((mod) => {
11
+ // The package's countTokens rebuilds the BPE encoder on every call
12
+ // (~20 ms); counting a whole context part by part before each model
13
+ // call turned that into seconds of dead time per turn.
14
+ if (typeof mod.getTokenizer === "function") {
15
+ const encoder = mod.getTokenizer();
16
+ return (text) => encoder.encode(text.normalize("NFKC"), "all").length;
17
+ }
18
+ return typeof mod.countTokens === "function"
19
+ ? mod.countTokens
20
+ : null;
21
+ })
13
22
  .catch(() => null);
14
23
  }
15
24
  return tokenizerPromise;
@@ -189,6 +189,10 @@ export function backfillEngineMessagesToolResults(messages) {
189
189
  toolInput,
190
190
  content: interrupted ? INTERRUPTED_TOOL_RESULT_FOR_MODEL : part.content,
191
191
  ...(part.isError || interrupted ? { isError: true } : {}),
192
+ ...(part.approvalPause === undefined
193
+ ? {}
194
+ : { approvalPause: part.approvalPause }),
195
+ ...(part.approvalAskId ? { approvalAskId: part.approvalAskId } : {}),
192
196
  ...(part.images && part.images.length > 0
193
197
  ? { images: part.images }
194
198
  : {}),
@@ -64,6 +64,8 @@ export interface EngineToolResultPart {
64
64
  toolInput: string;
65
65
  content: string;
66
66
  isError?: boolean;
67
+ approvalPause?: boolean;
68
+ approvalAskId?: string;
67
69
  outcome?: "unknown";
68
70
  images?: EngineToolResultImagePart[];
69
71
  }
@@ -530,6 +530,7 @@ export declare const MAX_SAME_ERROR_ACROSS_ARGUMENTS = 3;
530
530
  export declare const MAX_IDENTICAL_TOOL_CALLS = 8;
531
531
  export interface TerminalActionStop {
532
532
  message: string;
533
+ transcriptMessage?: string;
533
534
  errorCode?: string;
534
535
  details?: string;
535
536
  }
@@ -545,6 +546,9 @@ export type AgentToolCallExecutionResult = {
545
546
  } | {
546
547
  status: "failed";
547
548
  output: string;
549
+ terminalError?: Extract<AgentChatEvent, {
550
+ type: "error";
551
+ }>;
548
552
  };
549
553
  export interface ExecuteAgentToolCallOptions {
550
554
  actions: Record<string, ActionEntry>;
@@ -564,6 +568,7 @@ export interface ExecuteAgentToolCallOptions {
564
568
  threadId?: string;
565
569
  turnId?: string;
566
570
  approvedToolCalls?: string[];
571
+ approvedToolCallAskId?: string;
567
572
  onApprovalRequired?: (binding: AgentApprovalBinding) => Promise<string | void>;
568
573
  consumeApproval?: (binding: AgentApprovalBinding) => Promise<boolean>;
569
574
  isToolAlwaysAllowed?: (binding: AgentApprovalBinding) => Promise<boolean>;
@@ -574,6 +579,7 @@ export interface AgentApprovalBinding {
574
579
  input: unknown;
575
580
  callId: string;
576
581
  approvalKey: string;
582
+ approvalId?: string;
577
583
  }
578
584
  export declare function executeAgentToolCall(options: ExecuteAgentToolCallOptions): Promise<AgentToolCallExecutionResult>;
579
585
  export declare function filterInitialEngineTools(tools: EngineTool[], initialToolNames?: string[]): EngineTool[];
@@ -592,11 +598,19 @@ export declare function buildFirstRequestPayloadDetail(input: {
592
598
  availableToolCount: number;
593
599
  }): string;
594
600
  export declare function toolCallCacheKey(toolName: string, input: unknown): string;
595
- export declare function findApprovedStructuredToolCall(history: AgentChatStructuredMessage[] | undefined, approvedToolCalls: readonly string[] | undefined): {
596
- name: string;
597
- input: unknown;
598
- callId: string;
599
- } | undefined;
601
+ export declare function findApprovedStructuredToolCall(history: AgentChatStructuredMessage[] | undefined, approvedToolCalls: readonly string[] | undefined, approvedToolCallAskIds?: readonly string[]): {
602
+ status: "found";
603
+ toolCall: {
604
+ name: string;
605
+ input: unknown;
606
+ callId: string;
607
+ approvalAskId?: string;
608
+ };
609
+ } | {
610
+ status: "ambiguous";
611
+ } | {
612
+ status: "missing";
613
+ };
600
614
  export declare function normalizeToolErrorForBreaker(error: string): string;
601
615
  /**
602
616
  * Tool errors the model has no way to clear: the missing thing lives outside
@@ -695,6 +709,7 @@ export declare function runAgentLoop(opts: {
695
709
  hardMaxResultChars?: number;
696
710
  };
697
711
  approvedToolCalls?: string[];
712
+ approvedToolCallAskId?: string;
698
713
  /**
699
714
  * Server-side approval persistence hooks. The HTTP transport supplies these
700
715
  * so client-supplied history is never the authorization source; direct loop
@@ -83,7 +83,7 @@ import { writeLedgerEntry, readLedgerEntry, clearLedgerForThread, AgentTurnIniti
83
83
  import { buildCurrentTimeUserContext } from "./runtime-context.js";
84
84
  import { claimSetupResume, setupResumeRefusedRunId, } from "./setup-resume-claim.js";
85
85
  import { consumeAgentToolApproval, createAgentToolApproval, isAgentToolAlwaysAllowed, resolveAgentToolApprovalTurnId, } from "./tool-approval-store.js";
86
- import { findCompletedJournalEntry } from "./tool-call-journal.js";
86
+ import { findCompletedJournalEntry, isUnavailableToolCallInput, } from "./tool-call-journal.js";
87
87
  import { redactSensitiveFields, sanitizeToolErrorText, sanitizeToolErrorValue, } from "./tool-error-redaction.js";
88
88
  import { describeToolResultImages, extractAgentImagesFromActionResult, } from "./tool-result-images.js";
89
89
  import { createToolSearchEntry, extractToolSearchResultNames, filterActionsForAgentDiscovery, readLoadedToolNames, searchToolRegistry, TOOL_SEARCH_ACTION_NAME, withLoadedToolNames, } from "./tool-search.js";
@@ -1332,6 +1332,17 @@ export function serializeDurableDispatchPayload(body) {
1332
1332
  }
1333
1333
  return payload;
1334
1334
  }
1335
+ function approvedInputRequiresForeground(input) {
1336
+ const originalPayload = JSON.stringify({ input });
1337
+ try {
1338
+ return serializeDurableDispatchPayload({ input }) !== originalPayload;
1339
+ }
1340
+ catch (error) {
1341
+ if (error instanceof DurableAttachmentReferenceRequiredError)
1342
+ return true;
1343
+ throw error;
1344
+ }
1345
+ }
1335
1346
  function toolInputActivityLabel(toolName) {
1336
1347
  return toolName ? `Preparing ${toolName} action` : "Preparing action input";
1337
1348
  }
@@ -2165,6 +2176,12 @@ export function structuredHistoryToEngineMessages(history, imageResolutions) {
2165
2176
  toolInput,
2166
2177
  content: wire.content,
2167
2178
  ...(part.isError ? { isError: true } : {}),
2179
+ ...(typeof part.approvalPause === "boolean"
2180
+ ? { approvalPause: part.approvalPause }
2181
+ : {}),
2182
+ ...(typeof part.approvalAskId === "string"
2183
+ ? { approvalAskId: part.approvalAskId }
2184
+ : {}),
2168
2185
  });
2169
2186
  }
2170
2187
  }
@@ -3100,6 +3117,9 @@ export async function executeAgentToolCall(options) {
3100
3117
  const events = [];
3101
3118
  const send = (event) => {
3102
3119
  events.push(event);
3120
+ if (event.type === "text" && event.text === "Tool execution complete.") {
3121
+ return;
3122
+ }
3103
3123
  options.send?.(event);
3104
3124
  };
3105
3125
  const controller = options.signal ? null : new AbortController();
@@ -3138,6 +3158,7 @@ export async function executeAgentToolCall(options) {
3138
3158
  threadId: options.threadId,
3139
3159
  turnId: options.turnId,
3140
3160
  approvedToolCalls: options.approvedToolCalls,
3161
+ approvedToolCallAskId: options.approvedToolCallAskId,
3141
3162
  onApprovalRequired: options.onApprovalRequired,
3142
3163
  consumeApproval: options.consumeApproval,
3143
3164
  isToolAlwaysAllowed: options.isToolAlwaysAllowed,
@@ -3153,6 +3174,7 @@ export async function executeAgentToolCall(options) {
3153
3174
  const completed = [...events]
3154
3175
  .reverse()
3155
3176
  .find((event) => event.type === "tool_done" && event.tool === options.name);
3177
+ const terminalError = events.find((event) => event.type === "error" && event.recoverable === false);
3156
3178
  if (approval) {
3157
3179
  return {
3158
3180
  status: "approval_required",
@@ -3160,6 +3182,13 @@ export async function executeAgentToolCall(options) {
3160
3182
  output: completed?.result ?? `Awaiting human approval to run ${options.name}.`,
3161
3183
  };
3162
3184
  }
3185
+ if (terminalError) {
3186
+ return {
3187
+ status: "failed",
3188
+ output: completed?.result ?? terminalError.error,
3189
+ terminalError,
3190
+ };
3191
+ }
3163
3192
  if (!completed) {
3164
3193
  return {
3165
3194
  status: "failed",
@@ -3304,12 +3333,14 @@ function stableStringify(value) {
3304
3333
  export function toolCallCacheKey(toolName, input) {
3305
3334
  return `${toolName}:${stableStringify(normalizeToolCallInputForHistory(input, toolName))}`;
3306
3335
  }
3307
- export function findApprovedStructuredToolCall(history, approvedToolCalls) {
3308
- if (!Array.isArray(history) || !approvedToolCalls?.length)
3309
- return undefined;
3336
+ export function findApprovedStructuredToolCall(history, approvedToolCalls, approvedToolCallAskIds) {
3337
+ if (!Array.isArray(history) || !approvedToolCalls?.length) {
3338
+ return { status: "missing" };
3339
+ }
3310
3340
  const approved = new Set(approvedToolCalls);
3341
+ const approvedAskIds = new Set(approvedToolCallAskIds ?? []);
3311
3342
  const calls = new Map();
3312
- let match;
3343
+ const matches = [];
3313
3344
  for (const message of history) {
3314
3345
  if (message.role === "assistant") {
3315
3346
  for (const part of message.content) {
@@ -3327,7 +3358,14 @@ export function findApprovedStructuredToolCall(history, approvedToolCalls) {
3327
3358
  : "";
3328
3359
  if (!callId || !name)
3329
3360
  continue;
3330
- calls.set(callId, { name, input: part.input ?? part.args ?? {} });
3361
+ calls.set(callId, {
3362
+ name,
3363
+ input: part.input !== undefined
3364
+ ? part.input
3365
+ : (part.args ?? { omitted: "tool-input" }),
3366
+ ...(part.approvalKey ? { approvalKey: part.approvalKey } : {}),
3367
+ ...(part.approvalAskId ? { approvalAskId: part.approvalAskId } : {}),
3368
+ });
3331
3369
  }
3332
3370
  continue;
3333
3371
  }
@@ -3335,19 +3373,53 @@ export function findApprovedStructuredToolCall(history, approvedToolCalls) {
3335
3373
  if (part.type !== "tool-result")
3336
3374
  continue;
3337
3375
  const call = calls.get(part.toolCallId);
3338
- if (!call || !approved.has(toolCallCacheKey(call.name, call.input))) {
3376
+ if (!call) {
3339
3377
  continue;
3340
3378
  }
3341
- const result = part.content.toLowerCase();
3342
- if (!result.includes("did not execute") &&
3343
- !result.includes("awaiting human approval") &&
3344
- !result.includes("waiting for your approval")) {
3379
+ const matchesApproval = approvedAskIds.size > 0
3380
+ ? Boolean(call.approvalAskId && approvedAskIds.has(call.approvalAskId))
3381
+ : approved.has(call.approvalKey ?? toolCallCacheKey(call.name, call.input));
3382
+ if (!matchesApproval) {
3345
3383
  continue;
3346
3384
  }
3347
- match = { ...call, callId: part.toolCallId };
3385
+ if (part.approvalPause !== true)
3386
+ continue;
3387
+ if (approvedAskIds.size > 0 &&
3388
+ (!call.approvalAskId ||
3389
+ !approvedAskIds.has(call.approvalAskId) ||
3390
+ part.approvalAskId !== call.approvalAskId)) {
3391
+ continue;
3392
+ }
3393
+ if (call.approvalAskId &&
3394
+ part.approvalAskId &&
3395
+ call.approvalAskId !== part.approvalAskId) {
3396
+ continue;
3397
+ }
3398
+ matches.push({
3399
+ name: call.name,
3400
+ input: call.input,
3401
+ callId: part.toolCallId,
3402
+ ...((part.approvalAskId ?? call.approvalAskId)
3403
+ ? { approvalAskId: part.approvalAskId ?? call.approvalAskId }
3404
+ : {}),
3405
+ });
3348
3406
  }
3349
3407
  }
3350
- return match;
3408
+ if (approvedAskIds.size > 0) {
3409
+ const distinctAskIds = new Set(matches.map((match) => match.approvalAskId).filter(Boolean));
3410
+ if (distinctAskIds.size !== 1 ||
3411
+ distinctAskIds.size !== approvedAskIds.size) {
3412
+ return { status: matches.length === 0 ? "missing" : "ambiguous" };
3413
+ }
3414
+ const exactMatches = matches.filter((match) => approvedAskIds.has(match.approvalAskId ?? ""));
3415
+ if (exactMatches.length === 0)
3416
+ return { status: "missing" };
3417
+ return { status: "found", toolCall: exactMatches.at(-1) };
3418
+ }
3419
+ if (matches.length > 1)
3420
+ return { status: "ambiguous" };
3421
+ const toolCall = matches[0];
3422
+ return toolCall ? { status: "found", toolCall } : { status: "missing" };
3351
3423
  }
3352
3424
  const INTERRUPTED_TOOL_LEDGER_POLL_MS = process.env.NODE_ENV === "test" ? 0 : 2_000;
3353
3425
  async function waitForInterruptedToolLedgerEntry(opts) {
@@ -4330,7 +4402,7 @@ export async function runAgentLoop(opts) {
4330
4402
  const sendTerminalActionStop = (stop) => {
4331
4403
  if (stop.errorCode === "needs-approval" ||
4332
4404
  stop.errorCode === "awaiting-user-input") {
4333
- send({ type: "text", text: stop.message });
4405
+ send({ type: "text", text: stop.transcriptMessage ?? stop.message });
4334
4406
  return;
4335
4407
  }
4336
4408
  send({
@@ -5132,12 +5204,23 @@ export async function runAgentLoop(opts) {
5132
5204
  let requestedActionStop = null;
5133
5205
  let requestedConnection;
5134
5206
  let turnYieldedToUser = false;
5135
- const noteToolCallSucceeded = (actionEntry) => {
5207
+ const noteToolCallSucceeded = (actionEntry, toolName, toolInput) => {
5136
5208
  if (actionEntry.endsTurn !== true)
5137
5209
  return;
5138
5210
  turnYieldedToUser = true;
5211
+ const question = toolName === "ask-question" &&
5212
+ typeof toolInput === "object" &&
5213
+ toolInput !== null &&
5214
+ "question" in toolInput &&
5215
+ typeof toolInput.question === "string"
5216
+ ? toolInput.question.trim()
5217
+ : "";
5218
+ const waitingMessage = "Waiting for your answer before continuing";
5139
5219
  requestedActionStop ??= {
5140
- message: "Waiting for your answer before continuing.",
5220
+ message: question
5221
+ ? `${waitingMessage}: ${question}`
5222
+ : `${waitingMessage}.`,
5223
+ transcriptMessage: `${waitingMessage}.`,
5141
5224
  errorCode: "awaiting-user-input",
5142
5225
  };
5143
5226
  };
@@ -5396,13 +5479,16 @@ export async function runAgentLoop(opts) {
5396
5479
  }
5397
5480
  }
5398
5481
  const approvalKey = toolCallCacheKey(toolCall.name, toolCall.input);
5482
+ const requestedApproval = approvedToolCallKeys.delete(approvalKey);
5399
5483
  const approvalBinding = {
5400
5484
  toolName: toolCall.name,
5401
5485
  input: toolCall.input,
5402
5486
  callId: toolCall.id,
5403
5487
  approvalKey,
5488
+ ...(requestedApproval && opts.approvedToolCallAskId
5489
+ ? { approvalId: opts.approvedToolCallAskId }
5490
+ : {}),
5404
5491
  };
5405
- const requestedApproval = approvedToolCallKeys.delete(approvalKey);
5406
5492
  const wasApproved = requestedApproval && opts.consumeApproval
5407
5493
  ? await opts.consumeApproval(approvalBinding)
5408
5494
  : requestedApproval;
@@ -5489,6 +5575,7 @@ export async function runAgentLoop(opts) {
5489
5575
  input: journalToolInput,
5490
5576
  result,
5491
5577
  completedSideEffect: false,
5578
+ approvalPause: true,
5492
5579
  });
5493
5580
  recordToolResult(result, false);
5494
5581
  turnYieldedToUser = true;
@@ -5550,7 +5637,7 @@ export async function runAgentLoop(opts) {
5550
5637
  : {}),
5551
5638
  });
5552
5639
  recordToolResult(result, false, journaled.artifacts);
5553
- noteToolCallSucceeded(actionEntry);
5640
+ noteToolCallSucceeded(actionEntry, toolCall.name, toolCall.input);
5554
5641
  resetAfterSuccessfulWrite(toolCall.name, toolCall.input);
5555
5642
  return {
5556
5643
  type: "tool-result",
@@ -5614,7 +5701,7 @@ export async function runAgentLoop(opts) {
5614
5701
  : {}),
5615
5702
  });
5616
5703
  recordToolResult(result, false, ledgerResult.artifacts);
5617
- noteToolCallSucceeded(actionEntry);
5704
+ noteToolCallSucceeded(actionEntry, toolCall.name, toolCall.input);
5618
5705
  resetAfterSuccessfulWrite(toolCall.name, toolCall.input);
5619
5706
  return {
5620
5707
  type: "tool-result",
@@ -6160,7 +6247,7 @@ export async function runAgentLoop(opts) {
6160
6247
  });
6161
6248
  recordToolResult(result, isError, toolArtifacts, receipt);
6162
6249
  if (!isError) {
6163
- noteToolCallSucceeded(actionEntry);
6250
+ noteToolCallSucceeded(actionEntry, toolCall.name, toolCall.input);
6164
6251
  if (cacheKey) {
6165
6252
  readOnlyToolResultCache.set(cacheKey, {
6166
6253
  content: result,
@@ -7364,6 +7451,9 @@ export async function chainServerDrivenContinuation(opts) {
7364
7451
  }
7365
7452
  : {}),
7366
7453
  };
7454
+ // Approval selectors apply only to the request that consumes their one-shot grants.
7455
+ delete continuationBody.approvedToolCalls;
7456
+ delete continuationBody.approvedToolCallAskIds;
7367
7457
  delete continuationBody[AGENT_CHAT_BACKGROUND_RUN_FIELD];
7368
7458
  try {
7369
7459
  if (await d.isTurnAborted(effectiveThreadId, effectiveTurnId)) {
@@ -7769,7 +7859,7 @@ export function createProductionAgentHandler(options) {
7769
7859
  }
7770
7860
  backgroundRunClaimedEarly = true;
7771
7861
  }
7772
- const dispatchToBackground = !isBackgroundWorker &&
7862
+ let dispatchToBackground = !isBackgroundWorker &&
7773
7863
  isAgentChatDurableBackgroundEnabled({
7774
7864
  appOptIn: options.durableBackgroundRuns,
7775
7865
  });
@@ -7826,23 +7916,117 @@ export function createProductionAgentHandler(options) {
7826
7916
  setResponseStatus(event, 401);
7827
7917
  return { error: "Background agent runs require a persisted initiator" };
7828
7918
  }
7829
- const requestedApprovedToolCalls = Array.isArray(body.approvedToolCalls) && body.approvedToolCalls.length > 0
7830
- ? body.approvedToolCalls
7831
- .filter((key) => typeof key === "string")
7832
- .slice(0, 200)
7919
+ const submittedApprovedToolCalls = body.approvedToolCalls;
7920
+ if (submittedApprovedToolCalls !== undefined &&
7921
+ (!Array.isArray(submittedApprovedToolCalls) ||
7922
+ submittedApprovedToolCalls.length > 200 ||
7923
+ submittedApprovedToolCalls.some((key) => typeof key !== "string" || key.length === 0))) {
7924
+ setResponseStatus(event, 422);
7925
+ return {
7926
+ error: "The agent run failed.",
7927
+ code: "approved_tool_call_invalid",
7928
+ retryable: false,
7929
+ };
7930
+ }
7931
+ const requestedApprovedToolCalls = Array.isArray(submittedApprovedToolCalls) &&
7932
+ submittedApprovedToolCalls.length > 0
7933
+ ? submittedApprovedToolCalls
7833
7934
  : undefined;
7834
- const resolvedApprovalTurnId = !isBackgroundWorker &&
7835
- ownerEmail &&
7836
- threadId &&
7837
- requestedApprovedToolCalls?.length
7935
+ const submittedApprovedToolCallAskIds = body.approvedToolCallAskIds;
7936
+ if (submittedApprovedToolCallAskIds !== undefined &&
7937
+ (!Array.isArray(submittedApprovedToolCallAskIds) ||
7938
+ submittedApprovedToolCallAskIds.length > 200 ||
7939
+ submittedApprovedToolCallAskIds.some((id) => typeof id !== "string" || id.length === 0 || id.trim() !== id))) {
7940
+ setResponseStatus(event, 422);
7941
+ return {
7942
+ error: "The agent run failed.",
7943
+ code: "approved_tool_call_ask_id_invalid",
7944
+ retryable: false,
7945
+ };
7946
+ }
7947
+ const requestedApprovedToolCallAskIds = Array.isArray(submittedApprovedToolCallAskIds) &&
7948
+ submittedApprovedToolCallAskIds.length > 0
7949
+ ? submittedApprovedToolCallAskIds
7950
+ : undefined;
7951
+ // The approval fast path replays one call, so reject batches before any key can be dropped.
7952
+ if (requestedApprovedToolCalls && requestedApprovedToolCalls.length > 1) {
7953
+ setResponseStatus(event, 422);
7954
+ return {
7955
+ error: "The agent run failed.",
7956
+ code: "approved_tool_call_count_unsupported",
7957
+ retryable: false,
7958
+ };
7959
+ }
7960
+ if (requestedApprovedToolCallAskIds &&
7961
+ (requestedApprovedToolCallAskIds.length !== 1 ||
7962
+ requestedApprovedToolCalls?.length !== 1)) {
7963
+ setResponseStatus(event, 422);
7964
+ return {
7965
+ error: "The agent run failed.",
7966
+ code: "approved_tool_call_ask_id_count_unsupported",
7967
+ retryable: false,
7968
+ };
7969
+ }
7970
+ const approvedToolCallLookup = findApprovedStructuredToolCall(requestStructuredHistory, requestedApprovedToolCalls, requestedApprovedToolCallAskIds);
7971
+ if (approvedToolCallLookup.status === "ambiguous") {
7972
+ setResponseStatus(event, 422);
7973
+ return {
7974
+ error: "The agent run failed.",
7975
+ code: "approved_tool_call_history_ambiguous",
7976
+ retryable: false,
7977
+ };
7978
+ }
7979
+ const exactApprovedToolCall = approvedToolCallLookup.status === "found"
7980
+ ? approvedToolCallLookup.toolCall
7981
+ : undefined;
7982
+ if (requestedApprovedToolCalls?.length && !exactApprovedToolCall) {
7983
+ setResponseStatus(event, 422);
7984
+ return {
7985
+ error: "The agent run failed.",
7986
+ code: "approved_tool_call_history_missing",
7987
+ retryable: false,
7988
+ };
7989
+ }
7990
+ if (exactApprovedToolCall &&
7991
+ isUnavailableToolCallInput(exactApprovedToolCall.input)) {
7992
+ setResponseStatus(event, 422);
7993
+ return {
7994
+ error: "The agent run failed.",
7995
+ code: "approved_tool_call_input_unavailable",
7996
+ retryable: false,
7997
+ };
7998
+ }
7999
+ if (dispatchToBackground && exactApprovedToolCall) {
8000
+ if (approvedInputRequiresForeground(exactApprovedToolCall.input)) {
8001
+ // The dispatch payload must not persist raw bytes; execute while this request still has them.
8002
+ dispatchToBackground = false;
8003
+ }
8004
+ }
8005
+ const approvedToolCallsForExecution = exactApprovedToolCall
8006
+ ? [
8007
+ toolCallCacheKey(exactApprovedToolCall.name, exactApprovedToolCall.input),
8008
+ ]
8009
+ : requestedApprovedToolCalls;
8010
+ const resolvedApprovalTurnId = ownerEmail && threadId && approvedToolCallsForExecution?.length
7838
8011
  ? await resolveAgentToolApprovalTurnId({
7839
8012
  ownerEmail,
7840
8013
  orgId: getRequestOrgId() ?? null,
7841
8014
  threadId,
7842
8015
  requestedTurnId: requestTurnId,
7843
- approvalKeys: requestedApprovedToolCalls,
8016
+ approvalKeys: approvedToolCallsForExecution,
8017
+ ...(requestedApprovedToolCallAskIds
8018
+ ? { approvalId: requestedApprovedToolCallAskIds[0] }
8019
+ : {}),
7844
8020
  })
7845
8021
  : null;
8022
+ if (requestedApprovedToolCallAskIds?.length && !resolvedApprovalTurnId) {
8023
+ setResponseStatus(event, 409);
8024
+ return {
8025
+ error: "The agent run failed.",
8026
+ code: "approved_tool_call_not_pending",
8027
+ retryable: false,
8028
+ };
8029
+ }
7846
8030
  // The continuation marker is client-controlled. Defer setup admission only
7847
8031
  // for automatic successors when the named stop and turn can be checked
7848
8032
  // under the run-slot lock. A user's manual Continue starts new work and
@@ -8836,7 +9020,6 @@ export function createProductionAgentHandler(options) {
8836
9020
  // may truncate tool arguments and intentionally assigns fresh replay ids.
8837
9021
  // The loop still consumes only a matching server-created grant for the
8838
9022
  // current owner/org/thread/turn/tool/input tuple.
8839
- const exactApprovedToolCall = findApprovedStructuredToolCall(requestStructuredHistory, requestedApprovedToolCalls);
8840
9023
  const firstRequestPayloadDetail = buildFirstRequestPayloadDetail({
8841
9024
  isFirstRequest: requestHistory.length === 0,
8842
9025
  systemPrompt: requestSystemPrompt,
@@ -8851,7 +9034,7 @@ export function createProductionAgentHandler(options) {
8851
9034
  const isClientTurnContinuation = !isBackgroundWorker &&
8852
9035
  internalContinuation === true &&
8853
9036
  !continueOf &&
8854
- !requestedApprovedToolCalls?.length &&
9037
+ !approvedToolCallsForExecution?.length &&
8855
9038
  typeof message === "string" &&
8856
9039
  message.startsWith(AGENT_INTERNAL_CONTINUE_PROMPT) &&
8857
9040
  typeof threadId === "string" &&
@@ -8921,7 +9104,7 @@ export function createProductionAgentHandler(options) {
8921
9104
  ...(turnInitiator ? { turnInitiator } : {}),
8922
9105
  replayCompletedTurn: typeof requestTurnId === "string" &&
8923
9106
  Boolean(requestTurnId.trim()) &&
8924
- !requestedApprovedToolCalls,
9107
+ !approvedToolCallsForExecution,
8925
9108
  dispatchMode: dispatchToBackground
8926
9109
  ? "background"
8927
9110
  : foregroundSelfChainEligible
@@ -8982,6 +9165,7 @@ export function createProductionAgentHandler(options) {
8982
9165
  toolName: binding.toolName,
8983
9166
  callId: binding.callId,
8984
9167
  approvalKey: binding.approvalKey,
9168
+ ...(binding.approvalId ? { approvalId: binding.approvalId } : {}),
8985
9169
  };
8986
9170
  };
8987
9171
  const approvalHooks = {
@@ -9003,7 +9187,6 @@ export function createProductionAgentHandler(options) {
9003
9187
  });
9004
9188
  },
9005
9189
  };
9006
- const approvedToolCallsForExecution = requestedApprovedToolCalls;
9007
9190
  if (isBackgroundWorker &&
9008
9191
  (await isTurnAborted(effectiveThreadId, effectiveTurnId))) {
9009
9192
  await markRunAborted(runId, "user").catch(() => { });
@@ -9779,6 +9962,11 @@ export function createProductionAgentHandler(options) {
9779
9962
  ...(approvedToolCallsForExecution
9780
9963
  ? { approvedToolCalls: approvedToolCallsForExecution }
9781
9964
  : {}),
9965
+ ...(requestedApprovedToolCallAskIds
9966
+ ? {
9967
+ approvedToolCallAskId: requestedApprovedToolCallAskIds[0],
9968
+ }
9969
+ : {}),
9782
9970
  onApprovalRequired: approvalHooks.onApprovalRequired,
9783
9971
  consumeApproval: approvalHooks.consumeApproval,
9784
9972
  isToolAlwaysAllowed: approvalHooks.isToolAlwaysAllowed,
@@ -9816,6 +10004,11 @@ export function createProductionAgentHandler(options) {
9816
10004
  threadId: effectiveThreadId,
9817
10005
  turnId: effectiveTurnId,
9818
10006
  approvedToolCalls: approvedToolCallsForExecution,
10007
+ ...(requestedApprovedToolCallAskIds
10008
+ ? {
10009
+ approvedToolCallAskId: requestedApprovedToolCallAskIds[0],
10010
+ }
10011
+ : {}),
9819
10012
  onApprovalRequired: approvalHooks.onApprovalRequired,
9820
10013
  consumeApproval: approvalHooks.consumeApproval,
9821
10014
  isToolAlwaysAllowed: approvalHooks.isToolAlwaysAllowed,