@agent-native/core 0.219.0 → 0.220.0-nightly-20261023212848
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/action.d.ts +23 -3
- package/dist/action.js +58 -3
- package/dist/agent/abort-reasons.d.ts +6 -0
- package/dist/agent/abort-reasons.js +6 -0
- package/dist/agent/execution-fence.js +5 -1
- package/dist/agent/production-agent.d.ts +22 -6
- package/dist/agent/production-agent.js +136 -37
- package/dist/agent/run-manager.js +92 -44
- package/dist/agent/run-store.d.ts +1 -0
- package/dist/agent/run-store.js +260 -31
- package/dist/agent/run-terminal-tracking.d.ts +29 -0
- package/dist/agent/run-terminal-tracking.js +56 -0
- package/dist/agent/run-turn-id-header.d.ts +9 -0
- package/dist/agent/run-turn-id-header.js +13 -0
- package/dist/agent/types.d.ts +11 -0
- package/dist/chat-threads/store.d.ts +23 -6
- package/dist/chat-threads/store.js +119 -36
- package/dist/cli/amplify-stream.js +1 -1
- package/dist/client/agent-chat/index.d.ts +2 -1
- package/dist/client/agent-chat/index.js +2 -1
- package/dist/client/agent-chat.d.ts +2 -0
- package/dist/client/agent-chat.js +31 -5
- package/dist/client/chat/agentkit-agent-native.js +8 -1
- package/dist/client/chat/agentkit-protocol.js +7 -1
- package/dist/client/chat/run-outcome-telemetry.js +1 -0
- package/dist/client/chat/run-outcome.d.ts +5 -0
- package/dist/client/chat/runtime.d.ts +34 -1
- package/dist/client/chat/runtime.js +96 -12
- package/dist/client/chat/turn-submitted-telemetry.d.ts +4 -0
- package/dist/client/chat/turn-submitted-telemetry.js +39 -0
- package/dist/client/chat/turn-submitted.d.ts +39 -0
- package/dist/client/chat/turn-submitted.js +16 -0
- package/dist/client/route-chunk-recovery.d.ts +3 -8
- package/dist/client/route-chunk-recovery.js +11 -9
- package/dist/client/use-chat-thread-list.d.ts +2 -0
- package/dist/client/use-chat-thread-list.js +48 -2
- package/dist/client/use-chat-threads.d.ts +13 -0
- package/dist/client/use-chat-threads.js +59 -4
- package/dist/collab/routes.d.ts +2 -2
- package/dist/db/client.d.ts +1 -0
- package/dist/db/client.js +28 -18
- package/dist/db/schema-snapshot.generated.js +5 -0
- package/dist/identity/rekey.d.ts +2 -0
- package/dist/identity/rekey.js +19 -0
- package/dist/index.d.ts +1 -1
- package/dist/index.js +1 -1
- package/dist/mcp/agent-connected.d.ts +5 -0
- package/dist/mcp/agent-connected.js +10 -3
- package/dist/mcp/analytics.d.ts +14 -1
- package/dist/mcp/analytics.js +54 -2
- package/dist/mcp/embed-app.js +2 -1
- package/dist/mcp/oauth-route.js +56 -1
- package/dist/notifications/routes.d.ts +1 -1
- package/dist/observability/routes.d.ts +7 -7
- package/dist/observability/traces.d.ts +2 -0
- package/dist/observability/traces.js +11 -2
- package/dist/provider-api/actions/custom-provider-registration.d.ts +16 -16
- package/dist/provider-api/actions/provider-api.d.ts +13 -13
- package/dist/provider-api/corpus-jobs.d.ts +2 -2
- package/dist/resources/handlers.d.ts +1 -1
- package/dist/review/index.d.ts +1 -1
- package/dist/review/index.js +1 -1
- package/dist/review/suggestions/actions.js +25 -17
- package/dist/review/suggestions/store.d.ts +14 -1
- package/dist/review/suggestions/store.js +16 -3
- package/dist/review/suggestions/types.d.ts +15 -1
- package/dist/scripts/chat/search-chats.js +14 -2
- package/dist/server/agent-chat/browser-team-tools.js +39 -6
- package/dist/server/agent-chat/script-entries.js +44 -9
- package/dist/server/agent-chat-plugin.d.ts +2 -0
- package/dist/server/agent-chat-plugin.js +66 -16
- package/dist/server/agent-engine-api-key-route.d.ts +1 -1
- package/dist/server/agent-engine-default-model-route.d.ts +1 -1
- package/dist/server/first-touch-route.d.ts +2 -2
- package/dist/shared/route-chunk-recovery-bootstrap.d.ts +9 -0
- package/dist/shared/route-chunk-recovery-bootstrap.js +25 -0
- package/dist/sharing/actions/list-resource-shares.d.ts +1 -1
- package/dist/templates/chat/.agents/skills/actions/SKILL.md +1 -0
- package/dist/templates/default/.agents/skills/actions/SKILL.md +1 -0
- package/dist/templates/factory/.agents/skills/review-prs/SKILL.md +27 -3
- package/dist/templates/headless/.agents/skills/actions/SKILL.md +1 -0
- package/dist/templates/workspace-core/.agents/skills/actions/SKILL.md +1 -0
- package/dist/templates/workspace-core/.agents/skills/external-agents/references/mcp-apps-embedding.md +12 -5
- package/dist/tracking/providers.js +3 -0
- package/dist/triggers/actions/manage-automation.d.ts +2 -2
- package/docs/content/external-agents.mdx +1 -1
- package/docs/content/mcp-apps.mdx +3 -3
- package/docs/content/mcp-protocol.mdx +1 -1
- package/docs/content/sharing.mdx +1 -0
- package/docs/content/template-assets-integrations.mdx +5 -3
- package/package.json +3 -3
- package/src/templates/chat/.agents/skills/actions/SKILL.md +1 -0
- package/src/templates/default/.agents/skills/actions/SKILL.md +1 -0
- package/src/templates/factory/.agents/skills/review-prs/SKILL.md +27 -3
- package/src/templates/headless/.agents/skills/actions/SKILL.md +1 -0
- package/src/templates/workspace-core/.agents/skills/actions/SKILL.md +1 -0
- package/src/templates/workspace-core/.agents/skills/external-agents/references/mcp-apps-embedding.md +12 -5
|
@@ -9,7 +9,7 @@ import { parseA2AAgentActivityPart } from "../a2a/activity.js";
|
|
|
9
9
|
import { actionCallIsRetrySafe, actionChangeResource, classifyActionCall, } from "../action-call-classification.js";
|
|
10
10
|
import { runOutsideActionInvocation, vouchedOutcome } from "../action-frame.js";
|
|
11
11
|
import { ACTION_CHAT_UI_RECORD_CHANGE_RENDERER, normalizeActionChangeResult, } from "../action-ui.js";
|
|
12
|
-
import { AgentConnectionRequiredError, describeToolParameterSignature, isActionContractError, isActionHiddenFromEveryAgentSurface, isAgentActionStopError, isAgentConnectionRequiredError, actionCallIntent, invocationEffect, runPreparedActionCall, boundActionCallIntent, preparedCallArguments, preparedCallNeedsApproval, recordInvalidActionCall, stripUnsupportedSchemaKeywords, } from "../action.js";
|
|
12
|
+
import { AgentConnectionRequiredError, describeToolParameterSignature, isActionContractError, isActionHiddenFromEveryAgentSurface, isAgentActionStopError, isAgentConnectionRequiredError, actionCallIntent, ActionInputValidationError, invocationEffect, runPreparedActionCall, boundActionCallIntent, preparedCallArguments, preparedCallNeedsApproval, recordInvalidActionCall, stripUnsupportedSchemaKeywords, } from "../action.js";
|
|
13
13
|
import { getAppConfig } from "../app-config/index.js";
|
|
14
14
|
import { MAX_BACKGROUND_RUN_CONTINUATIONS, MAX_CONSECUTIVE_NO_PROGRESS_CONTINUATIONS, MAX_TURN_WALL_CLOCK_MS, } from "../app-config/run-lifecycle-invariants.js";
|
|
15
15
|
import { readAppState } from "../application-state/script-helpers.js";
|
|
@@ -90,6 +90,7 @@ import { ProcessorChain, TripWire, toolCallsFromContent, } from "./processors.js
|
|
|
90
90
|
import { applyUncheckedDefaultModelReplacement } from "./provider-model-selection.js";
|
|
91
91
|
import { startRun, subscribeToRun, replayCompletedTurn, getActiveRunForThread, getActiveRunForThreadAsync, getRun, abortRun, abortRunDurably, abortRunAndTurnDurably, abortRunAtSafeBoundaryDurably, abortTurnByRefDurably, abortTurnDurably, getSlotHoldingRunId, tryClaimRunSlot, isHostedRuntime, resolveRunSoftTimeoutMs, resolveRunToolTimeoutCeilingMs, endsAfterToolResultWithoutAssistantFinal, endsDuringActionPreparation, } from "./run-manager.js";
|
|
92
92
|
import { writeLedgerEntry, readLedgerEntry, beginAgentExecutionStep, completeAgentExecutionStep, markAgentExecutionStepDispatched, markAgentExecutionStepUnknown, recordLateAgentExecutionStepOutcome, clearLedgerForThread, AgentTurnInitiatorMismatchError, AgentTurnInitiatorUnavailableError, insertRun, insertRunEvent, isRunAborted, isTurnAborted, markRunAborted, updateRunHeartbeat, updateRunStatusIfRunning, updateRunTerminalStateIfRunning, setRunError, setRunTerminalReason, claimBackgroundRun, readBackgroundRunClaim, recordRunDiagnostic, countRunsForTurn, RUN_DIAG_STAGE, UNCLAIMED_BACKGROUND_RUN_GRACE_MS, turnRunLedgerExhausted, } from "./run-store.js";
|
|
93
|
+
import { setRunTurnIdHeader } from "./run-turn-id-header.js";
|
|
93
94
|
import { buildCurrentTimeUserContext } from "./runtime-context.js";
|
|
94
95
|
import { claimSetupResume, setupResumeRefusedRunId, } from "./setup-resume-claim.js";
|
|
95
96
|
import { getStrictToolInputValidator } from "./strict-tool-input.js";
|
|
@@ -3130,6 +3131,7 @@ function* historyToolResults(turnMessages, actions, readOnlyCalls, priorCallIden
|
|
|
3130
3131
|
: {}),
|
|
3131
3132
|
readOnly: readOnlyCalls.has(callPart),
|
|
3132
3133
|
dedupe: actions[callPart.name]?.dedupe !== false,
|
|
3134
|
+
input: callPart.input,
|
|
3133
3135
|
},
|
|
3134
3136
|
};
|
|
3135
3137
|
}
|
|
@@ -3323,23 +3325,42 @@ const LOOP_BREAKER_STOP_CODES = new Set([
|
|
|
3323
3325
|
function isLoopBreakerStop(stop) {
|
|
3324
3326
|
return LOOP_BREAKER_STOP_CODES.has(stop.errorCode ?? "");
|
|
3325
3327
|
}
|
|
3326
|
-
function noteUnknownOutcome(outcomes, tool, argumentsHash) {
|
|
3328
|
+
function noteUnknownOutcome(outcomes, tool, argumentsHash, cause = "interruption", input) {
|
|
3327
3329
|
outcomes.tools.set(tool, (outcomes.tools.get(tool) ?? 0) + 1);
|
|
3328
|
-
|
|
3330
|
+
outcomes.causes.set(tool, cause);
|
|
3331
|
+
if (input !== undefined) {
|
|
3332
|
+
const inputKey = toolCallCacheKey(tool, input);
|
|
3333
|
+
outcomes.inputs.set(inputKey, (outcomes.inputs.get(inputKey) ?? 0) + 1);
|
|
3334
|
+
}
|
|
3335
|
+
if (!argumentsHash) {
|
|
3336
|
+
outcomes.unidentified.set(tool, (outcomes.unidentified.get(tool) ?? 0) + 1);
|
|
3329
3337
|
return;
|
|
3338
|
+
}
|
|
3330
3339
|
const key = preparedToolCallKey(tool, argumentsHash);
|
|
3331
3340
|
outcomes.calls.set(key, (outcomes.calls.get(key) ?? 0) + 1);
|
|
3332
3341
|
}
|
|
3342
|
+
/** Why a write of a tool with an unknown earlier call did not run. */
|
|
3343
|
+
function unknownSiblingResult(tool) {
|
|
3344
|
+
return `Not run: an earlier ${tool} call in this turn may have already applied, and its result is unknown. Running ${tool} again could apply the change twice. Check the current state with a read, then tell the user what you found instead of calling ${tool} again in this turn.`;
|
|
3345
|
+
}
|
|
3333
3346
|
function noUnknownOutcomes() {
|
|
3334
|
-
return {
|
|
3347
|
+
return {
|
|
3348
|
+
tools: new Map(),
|
|
3349
|
+
calls: new Map(),
|
|
3350
|
+
inputs: new Map(),
|
|
3351
|
+
unidentified: new Map(),
|
|
3352
|
+
causes: new Map(),
|
|
3353
|
+
};
|
|
3335
3354
|
}
|
|
3336
3355
|
/** Each count in `from` that is larger than the one in `into`. */
|
|
3337
3356
|
function mergeUnknownOutcomes(into, from) {
|
|
3338
|
-
for (const kind of ["tools", "calls"]) {
|
|
3357
|
+
for (const kind of ["tools", "calls", "inputs", "unidentified"]) {
|
|
3339
3358
|
for (const [key, count] of from[kind]) {
|
|
3340
3359
|
into[kind].set(key, Math.max(into[kind].get(key) ?? 0, count));
|
|
3341
3360
|
}
|
|
3342
3361
|
}
|
|
3362
|
+
for (const [tool, cause] of from.causes)
|
|
3363
|
+
into.causes.set(tool, cause);
|
|
3343
3364
|
}
|
|
3344
3365
|
/**
|
|
3345
3366
|
* Adds to `outcomes` those a continued turn starts with, from its journal's
|
|
@@ -3350,7 +3371,7 @@ function mergeUnknownOutcomes(into, from) {
|
|
|
3350
3371
|
function seedUnknownOutcomes(outcomes, messages, actions, readOnlyCalls, priorCallEffects, priorCallIdentities, interrupted) {
|
|
3351
3372
|
const fromJournal = noUnknownOutcomes();
|
|
3352
3373
|
for (const entry of interrupted) {
|
|
3353
|
-
noteUnknownOutcome(fromJournal, entry.tool, entry.argumentsHash);
|
|
3374
|
+
noteUnknownOutcome(fromJournal, entry.tool, entry.argumentsHash, "interruption", entry.input);
|
|
3354
3375
|
}
|
|
3355
3376
|
mergeUnknownOutcomes(outcomes, fromJournal);
|
|
3356
3377
|
if (!isInternalContinuationTurn(messages))
|
|
@@ -3379,7 +3400,7 @@ function seedUnknownOutcomes(outcomes, messages, actions, readOnlyCalls, priorCa
|
|
|
3379
3400
|
(part.isError === true &&
|
|
3380
3401
|
typeof part.content === "string" &&
|
|
3381
3402
|
isToolCallTimeoutResult(part.content)))) {
|
|
3382
|
-
noteUnknownOutcome(fromHistory, call.name, effect ? priorCallIdentities.get(call.identityKey ?? "") : undefined);
|
|
3403
|
+
noteUnknownOutcome(fromHistory, call.name, effect ? priorCallIdentities.get(call.identityKey ?? "") : undefined, "interruption", call.input);
|
|
3383
3404
|
}
|
|
3384
3405
|
}
|
|
3385
3406
|
const callsByProviderId = new Map();
|
|
@@ -3420,7 +3441,7 @@ function seedUnknownOutcomes(outcomes, messages, actions, readOnlyCalls, priorCa
|
|
|
3420
3441
|
}
|
|
3421
3442
|
}
|
|
3422
3443
|
for (const call of ambiguousUnknownCalls) {
|
|
3423
|
-
noteUnknownOutcome(fromHistory, call.name, undefined);
|
|
3444
|
+
noteUnknownOutcome(fromHistory, call.name, undefined, "interruption", call.input);
|
|
3424
3445
|
}
|
|
3425
3446
|
mergeUnknownOutcomes(outcomes, fromHistory);
|
|
3426
3447
|
}
|
|
@@ -4448,6 +4469,20 @@ async function prepareToolCall(entry, input, signal, timeoutMs) {
|
|
|
4448
4469
|
function intentIsReadOnly(intent) {
|
|
4449
4470
|
return intent?.valid === true && intent.readOnly === true;
|
|
4450
4471
|
}
|
|
4472
|
+
function isBoundedTaskWait(toolName, input) {
|
|
4473
|
+
if (toolName !== "agent-teams" || !input || typeof input !== "object") {
|
|
4474
|
+
return false;
|
|
4475
|
+
}
|
|
4476
|
+
const args = input;
|
|
4477
|
+
// Only these operations honor the task reader's bounded wait contract.
|
|
4478
|
+
return ((args.action === "status" || args.action === "read-result") &&
|
|
4479
|
+
typeof args.taskId === "string" &&
|
|
4480
|
+
args.taskId.trim().length > 0 &&
|
|
4481
|
+
typeof args.waitMs === "number" &&
|
|
4482
|
+
Number.isInteger(args.waitMs) &&
|
|
4483
|
+
args.waitMs > 0 &&
|
|
4484
|
+
args.waitMs <= 20_000);
|
|
4485
|
+
}
|
|
4451
4486
|
function normalizeOptionalToolPlaceholders(schema, input) {
|
|
4452
4487
|
if (!schema?.properties || !input || typeof input !== "object") {
|
|
4453
4488
|
return { input, changed: false };
|
|
@@ -4968,21 +5003,28 @@ export async function runAgentLoop(opts) {
|
|
|
4968
5003
|
const readOnlyToolResultCache = seedReadOnlyToolResultsFromHistory(messages, actions, historyReadOnlyCalls, priorCallEffects, priorCallIdentities);
|
|
4969
5004
|
const duplicateReadOnlyToolCalls = seedDuplicateReadOnlyToolCallsFromHistory(messages, actions, historyReadOnlyCalls, priorCallEffects, priorCallIdentities);
|
|
4970
5005
|
const unknownOutcomes = opts.unknownOutcomes ?? noUnknownOutcomes();
|
|
4971
|
-
|
|
5006
|
+
// A recovered retry (a team attempt after a crash, an A2A redelivery)
|
|
5007
|
+
// re-plans from the task text, so a repeated write gets a new step id and
|
|
5008
|
+
// only these journal entries show it may already have run. Approval
|
|
5009
|
+
// continuations stay out: their paused call is interrupted by design.
|
|
5010
|
+
seedUnknownOutcomes(unknownOutcomes, messages, actions, historyReadOnlyCalls, priorCallEffects, priorCallIdentities, internalContinuationTurn || opts.recoverCompletedExecutionSteps === true
|
|
5011
|
+
? (toolCallJournal?.interrupted ?? [])
|
|
5012
|
+
: []);
|
|
4972
5013
|
if (internalContinuationTurn) {
|
|
4973
5014
|
for (const entry of toolCallJournal?.completed ?? []) {
|
|
4974
5015
|
if (!journalEntriesAmbiguous.has(entry.key))
|
|
4975
5016
|
continue;
|
|
4976
|
-
|
|
4977
|
-
|
|
4978
|
-
|
|
4979
|
-
unknownOutcomes.calls.set(key, Math.max(unknownOutcomes.calls.get(key) ?? 0, 1));
|
|
4980
|
-
}
|
|
5017
|
+
const seeded = noUnknownOutcomes();
|
|
5018
|
+
noteUnknownOutcome(seeded, entry.tool, entry.argumentsHash, "interruption", entry.input);
|
|
5019
|
+
mergeUnknownOutcomes(unknownOutcomes, seeded);
|
|
4981
5020
|
}
|
|
4982
5021
|
}
|
|
4983
|
-
// Names a caller shares between its own loops
|
|
5022
|
+
// Names a caller shares between its own loops carry no identity, so they
|
|
5023
|
+
// hold back every write of the tool.
|
|
4984
5024
|
for (const tool of opts.unknownWriteToolNames ?? []) {
|
|
4985
|
-
|
|
5025
|
+
const seeded = noUnknownOutcomes();
|
|
5026
|
+
noteUnknownOutcome(seeded, tool, undefined);
|
|
5027
|
+
mergeUnknownOutcomes(unknownOutcomes, seeded);
|
|
4986
5028
|
}
|
|
4987
5029
|
const { sameArguments: repeatedToolErrors, sameTool: repeatedToolErrorsAnyArgs, } = seedRepeatedToolErrorCountsFromJournal(journalRead.status === "read" ? journalRead.priorToolCallSequence : [], toolCallCacheKey, normalizeToolErrorForBreaker);
|
|
4988
5030
|
const repeatedToolCalls = seedRepeatedToolCallCountsFromJournal(journalRead.status === "read" ? journalRead.priorToolCallSequence : [], toolCallCacheKey, (name, result) => result.startsWith(resurfacedDuplicateReadOnlyToolResultPrefix(name)));
|
|
@@ -5038,6 +5080,8 @@ export async function runAgentLoop(opts) {
|
|
|
5038
5080
|
error: stop.message,
|
|
5039
5081
|
errorCode: stop.errorCode ?? "tool_failed",
|
|
5040
5082
|
...(stop.details ? { details: stop.details } : {}),
|
|
5083
|
+
...(stop.blockedTool ? { blockedTool: stop.blockedTool } : {}),
|
|
5084
|
+
...(stop.blockedBy ? { blockedBy: stop.blockedBy } : {}),
|
|
5041
5085
|
recoverable: false,
|
|
5042
5086
|
});
|
|
5043
5087
|
};
|
|
@@ -5977,7 +6021,12 @@ export async function runAgentLoop(opts) {
|
|
|
5977
6021
|
const actionRetrySafe = actionEntry && intent?.valid
|
|
5978
6022
|
? actionCallIsRetrySafe(actionEntry, preparedCallArguments(intent))
|
|
5979
6023
|
: false;
|
|
5980
|
-
const repeatGuardStopFromThisCall =
|
|
6024
|
+
const repeatGuardStopFromThisCall = actionIsReadOnly &&
|
|
6025
|
+
actionEntry?.dedupe === false &&
|
|
6026
|
+
intent?.valid === true &&
|
|
6027
|
+
isBoundedTaskWait(toolCall.name, preparedCallArguments(intent))
|
|
6028
|
+
? null
|
|
6029
|
+
: noteRepeatedToolCall(toolCall.name, toolCall.input, toolCall.id);
|
|
5981
6030
|
const rollbackRepeatedToolCall = () => {
|
|
5982
6031
|
const repeatKey = toolCallCacheKey(toolCall.name, toolCall.input);
|
|
5983
6032
|
const repeatCall = inFlightRepeatToolCalls.get(toolCall.id);
|
|
@@ -6010,6 +6059,7 @@ export async function runAgentLoop(opts) {
|
|
|
6010
6059
|
let actionInvoked = false;
|
|
6011
6060
|
// Live once invoked, so an abandoned call still reads what ran in it.
|
|
6012
6061
|
const actionExecutionOutcome = { refused: false };
|
|
6062
|
+
let unknownOutcomeCause = "interruption";
|
|
6013
6063
|
const ranEffect = (settled) => actionInvoked && actionEntry
|
|
6014
6064
|
? invocationEffect(actionEntry, actionExecutionOutcome, {
|
|
6015
6065
|
readOnly: actionIsReadOnly,
|
|
@@ -6025,7 +6075,7 @@ export async function runAgentLoop(opts) {
|
|
|
6025
6075
|
// holds back its action's writes as a write's would.
|
|
6026
6076
|
if (event.outcomeUnknown) {
|
|
6027
6077
|
opts.unknownWriteToolNames?.add(toolCall.name);
|
|
6028
|
-
noteUnknownOutcome(unknownOutcomes, toolCall.name, argumentsHash);
|
|
6078
|
+
noteUnknownOutcome(unknownOutcomes, toolCall.name, argumentsHash, unknownOutcomeCause, toolCall.input);
|
|
6029
6079
|
}
|
|
6030
6080
|
// A write that errored after it was invoked may still have committed,
|
|
6031
6081
|
// retry-safe or not, so cached reads of its target are stale.
|
|
@@ -6188,7 +6238,7 @@ export async function runAgentLoop(opts) {
|
|
|
6188
6238
|
: "";
|
|
6189
6239
|
return `Error running ${toolCall.name}: ${message}${errorCode}${rateLimitRecoveryHint(message, err)}`;
|
|
6190
6240
|
};
|
|
6191
|
-
const declineToolCall = (guidance, isError = false) => {
|
|
6241
|
+
const declineToolCall = (guidance, isError = false, errorClass) => {
|
|
6192
6242
|
const result = finalizeToolErrorResult(guidance);
|
|
6193
6243
|
return (async () => {
|
|
6194
6244
|
await send({
|
|
@@ -6205,6 +6255,7 @@ export async function runAgentLoop(opts) {
|
|
|
6205
6255
|
result,
|
|
6206
6256
|
isError: true,
|
|
6207
6257
|
completedSideEffect: false,
|
|
6258
|
+
...(errorClass ? { errorClass } : {}),
|
|
6208
6259
|
});
|
|
6209
6260
|
recordToolResult(result, true);
|
|
6210
6261
|
return {
|
|
@@ -6296,11 +6347,11 @@ export async function runAgentLoop(opts) {
|
|
|
6296
6347
|
}
|
|
6297
6348
|
const toolCallSchemaError = toolCallSchemaErrorsByCall.get(toolCall);
|
|
6298
6349
|
if (toolCallSchemaError && !repairedInput.changed) {
|
|
6299
|
-
return declineToolCall(toolInputSchemaErrorResult(toolCall.name, toolCallSchemaError.input, toolCallSchemaError.error, actionEntry.tool.parameters, toolCallTruncatedByOutputCap), true);
|
|
6350
|
+
return declineToolCall(toolInputSchemaErrorResult(toolCall.name, toolCallSchemaError.input, toolCallSchemaError.error, actionEntry.tool.parameters, toolCallTruncatedByOutputCap), true, "input_invalid");
|
|
6300
6351
|
}
|
|
6301
6352
|
const rawToolInputError = validateRawToolInput(actionEntry, toolCall.input);
|
|
6302
6353
|
if (rawToolInputError) {
|
|
6303
|
-
return declineToolCall(toolInputSchemaErrorResult(toolCall.name, toolCall.input, rawToolInputError, actionEntry.tool.parameters, toolCallTruncatedByOutputCap), true);
|
|
6354
|
+
return declineToolCall(toolInputSchemaErrorResult(toolCall.name, toolCall.input, rawToolInputError, actionEntry.tool.parameters, toolCallTruncatedByOutputCap), true, "input_invalid");
|
|
6304
6355
|
}
|
|
6305
6356
|
if (!intent?.valid) {
|
|
6306
6357
|
await recordInvalidActionCall(actionEntry, toolCall.input, {
|
|
@@ -6309,7 +6360,7 @@ export async function runAgentLoop(opts) {
|
|
|
6309
6360
|
userEmail: userEmail ?? undefined,
|
|
6310
6361
|
orgId: opts.orgId ?? getRequestOrgId() ?? null,
|
|
6311
6362
|
}, intent?.error);
|
|
6312
|
-
return declineToolCall(thrownToolResult(intent?.error), true);
|
|
6363
|
+
return declineToolCall(thrownToolResult(intent?.error), true, "input_invalid");
|
|
6313
6364
|
}
|
|
6314
6365
|
const approvalKey = toolCallCacheKey(toolCall.name, toolCall.input);
|
|
6315
6366
|
// The exact arguments the call runs with, defaults applied, bind its
|
|
@@ -6601,6 +6652,8 @@ export async function runAgentLoop(opts) {
|
|
|
6601
6652
|
requestedActionStop ??= {
|
|
6602
6653
|
message: result,
|
|
6603
6654
|
errorCode: "write_tool_outcome_unknown",
|
|
6655
|
+
blockedTool: toolCall.name,
|
|
6656
|
+
blockedBy: "interruption",
|
|
6604
6657
|
};
|
|
6605
6658
|
return {
|
|
6606
6659
|
type: "tool-result",
|
|
@@ -6670,11 +6723,13 @@ export async function runAgentLoop(opts) {
|
|
|
6670
6723
|
};
|
|
6671
6724
|
}
|
|
6672
6725
|
}
|
|
6673
|
-
//
|
|
6674
|
-
//
|
|
6726
|
+
// Running an unknown call again exactly as it was prepared could apply
|
|
6727
|
+
// it twice. A write with no identity to compare cannot be told from a
|
|
6728
|
+
// repeat, whatever its arguments, nor can one whose tool pair was
|
|
6729
|
+
// elided from the replayed history.
|
|
6675
6730
|
const priorInterruptionsBeforeStepReplay = actionIsReadOnly
|
|
6676
6731
|
? (unknownOutcomes.calls.get(preparedCallKey) ?? 0)
|
|
6677
|
-
: (unknownOutcomes.
|
|
6732
|
+
: Math.max(unknownOutcomes.calls.get(preparedCallKey) ?? 0, unknownOutcomes.inputs.get(toolCallCacheKey(toolCall.name, toolCall.input)) ?? 0, unknownOutcomes.unidentified.get(toolCall.name) ?? 0);
|
|
6678
6733
|
const priorInterruptions = ambiguousLegacyWrite
|
|
6679
6734
|
? Math.max(1, priorInterruptionsBeforeStepReplay)
|
|
6680
6735
|
: (interruptedCallMatchesStep ||
|
|
@@ -6683,10 +6738,20 @@ export async function runAgentLoop(opts) {
|
|
|
6683
6738
|
priorInterruptionsBeforeStepReplay > 0
|
|
6684
6739
|
? 0
|
|
6685
6740
|
: priorInterruptionsBeforeStepReplay;
|
|
6741
|
+
// Another write of a tool whose earlier call may have applied could
|
|
6742
|
+
// apply the same change twice, so it does not run; the turn goes on,
|
|
6743
|
+
// and only an exact repeat stops it.
|
|
6744
|
+
if (!actionIsReadOnly &&
|
|
6745
|
+
!actionRetrySafe &&
|
|
6746
|
+
priorInterruptionsBeforeStepReplay === 0 &&
|
|
6747
|
+
(unknownOutcomes.tools.get(toolCall.name) ?? 0) > 0) {
|
|
6748
|
+
return declineToolCall(unknownSiblingResult(toolCall.name), true);
|
|
6749
|
+
}
|
|
6686
6750
|
if (priorInterruptions > 0) {
|
|
6687
|
-
// Only a result recorded for these exact arguments is recovered
|
|
6688
|
-
//
|
|
6689
|
-
// instead of
|
|
6751
|
+
// Only a result recorded for these exact arguments is recovered, so
|
|
6752
|
+
// a result found is the interrupted call's own. Any other leaves the
|
|
6753
|
+
// earlier attempt unknown, so the call stops below instead of
|
|
6754
|
+
// running again.
|
|
6690
6755
|
const ledgerResult = !ambiguousLegacyWrite && opts.threadId && !executionStepIntent
|
|
6691
6756
|
? await waitForInterruptedToolLedgerEntry({
|
|
6692
6757
|
threadId: opts.threadId,
|
|
@@ -6763,6 +6828,8 @@ export async function runAgentLoop(opts) {
|
|
|
6763
6828
|
message: `I stopped before running ${toolCall.name} again. ${priorInterruptions === 1 ? "An earlier attempt" : `${priorInterruptions} earlier attempts`} may have already saved changes and I could not confirm the result. ` +
|
|
6764
6829
|
`Check what was saved, then reply "continue" and I will verify it before doing anything else.`,
|
|
6765
6830
|
errorCode: "write_tool_outcome_unknown",
|
|
6831
|
+
blockedTool: toolCall.name,
|
|
6832
|
+
blockedBy: unknownOutcomes.causes.get(toolCall.name) ?? "interruption",
|
|
6766
6833
|
};
|
|
6767
6834
|
return {
|
|
6768
6835
|
type: "tool-result",
|
|
@@ -6882,6 +6949,8 @@ export async function runAgentLoop(opts) {
|
|
|
6882
6949
|
// commit, so a retry-safe action stays blocked for it; an error the
|
|
6883
6950
|
// handler returned does not.
|
|
6884
6951
|
let toolResultLost = false;
|
|
6952
|
+
// It returned an error result rather than throwing one.
|
|
6953
|
+
let toolReportedError = false;
|
|
6885
6954
|
let toolErrorCode;
|
|
6886
6955
|
let toolErrorDetailsLine = "";
|
|
6887
6956
|
let mcpApp;
|
|
@@ -7070,6 +7139,7 @@ export async function runAgentLoop(opts) {
|
|
|
7070
7139
|
typeof mcpResult.raw === "object" &&
|
|
7071
7140
|
mcpResult.raw.isError === true) {
|
|
7072
7141
|
isError = true;
|
|
7142
|
+
toolReportedError = true;
|
|
7073
7143
|
}
|
|
7074
7144
|
mcpApp = mcpResult?.mcpApp;
|
|
7075
7145
|
let resultForAgent = rawForAgent;
|
|
@@ -7189,6 +7259,7 @@ export async function runAgentLoop(opts) {
|
|
|
7189
7259
|
throw err;
|
|
7190
7260
|
}
|
|
7191
7261
|
thrown = err;
|
|
7262
|
+
toolReportedError = false;
|
|
7192
7263
|
toolResultLost =
|
|
7193
7264
|
signal.aborted ||
|
|
7194
7265
|
isToolCallTimeoutResult(String(err?.message ?? err));
|
|
@@ -7203,8 +7274,21 @@ export async function runAgentLoop(opts) {
|
|
|
7203
7274
|
}
|
|
7204
7275
|
const call = actionExecutionOutcome.record;
|
|
7205
7276
|
const { effect: callEffect, changed: callChanged } = ranEffect(isError
|
|
7206
|
-
? {
|
|
7277
|
+
? {
|
|
7278
|
+
failed: {
|
|
7279
|
+
error: thrown,
|
|
7280
|
+
lost: toolResultLost,
|
|
7281
|
+
reported: toolReportedError,
|
|
7282
|
+
},
|
|
7283
|
+
}
|
|
7207
7284
|
: { receipt });
|
|
7285
|
+
const toolErrorClass = !isError
|
|
7286
|
+
? undefined
|
|
7287
|
+
: thrown instanceof ActionInputValidationError
|
|
7288
|
+
? "input_invalid"
|
|
7289
|
+
: callEffect === "none" && !actionIsReadOnly
|
|
7290
|
+
? "rejected"
|
|
7291
|
+
: undefined;
|
|
7208
7292
|
if (isError) {
|
|
7209
7293
|
receipt = undefined;
|
|
7210
7294
|
if (result !== INTERRUPTED_TOOL_RESULT_MARKER) {
|
|
@@ -7285,6 +7369,7 @@ export async function runAgentLoop(opts) {
|
|
|
7285
7369
|
executionStepOutcomeRecorded = true;
|
|
7286
7370
|
}
|
|
7287
7371
|
toolDonePersistenceFailed = true;
|
|
7372
|
+
unknownOutcomeCause = toolResultLost ? "interruption" : "rejection";
|
|
7288
7373
|
await emitToolDone({
|
|
7289
7374
|
type: "tool_done",
|
|
7290
7375
|
id: toolCall.id,
|
|
@@ -7293,6 +7378,7 @@ export async function runAgentLoop(opts) {
|
|
|
7293
7378
|
result,
|
|
7294
7379
|
...(isError ? { isError: true } : {}),
|
|
7295
7380
|
...(toolErrorCode ? { errorCode: toolErrorCode } : {}),
|
|
7381
|
+
...(toolErrorClass ? { errorClass: toolErrorClass } : {}),
|
|
7296
7382
|
...(callEffect === "unknown"
|
|
7297
7383
|
? { outcomeUnknown: true }
|
|
7298
7384
|
: isError || receipt || callEffect === "committed"
|
|
@@ -9398,6 +9484,7 @@ export function createProductionAgentHandler(options) {
|
|
|
9398
9484
|
setResponseHeader(event, "Cache-Control", "no-cache");
|
|
9399
9485
|
setResponseHeader(event, "Connection", "keep-alive");
|
|
9400
9486
|
setResponseHeader(event, "X-Run-Id", result.submittedRunId);
|
|
9487
|
+
setRunTurnIdHeader(event, getRun(result.submittedRunId)?.turnId);
|
|
9401
9488
|
setResponseHeader(event, "X-Dispatch-Mode", "replay");
|
|
9402
9489
|
return stream;
|
|
9403
9490
|
}
|
|
@@ -9486,6 +9573,7 @@ export function createProductionAgentHandler(options) {
|
|
|
9486
9573
|
setResponseHeader(event, "Cache-Control", "no-cache");
|
|
9487
9574
|
setResponseHeader(event, "Connection", "keep-alive");
|
|
9488
9575
|
setResponseHeader(event, "X-Run-Id", submittedRunId);
|
|
9576
|
+
setRunTurnIdHeader(event, getRun(submittedRunId)?.turnId);
|
|
9489
9577
|
setResponseHeader(event, "X-Dispatch-Mode", "replay");
|
|
9490
9578
|
return stream;
|
|
9491
9579
|
}
|
|
@@ -10780,6 +10868,11 @@ export function createProductionAgentHandler(options) {
|
|
|
10780
10868
|
!queuedMessageId &&
|
|
10781
10869
|
!queuedMessageClaimId &&
|
|
10782
10870
|
!isBackgroundWorker);
|
|
10871
|
+
const queuedSubmissionStore = shouldPreserveQueuedOrder
|
|
10872
|
+
? await import("../chat-threads/store.js")
|
|
10873
|
+
: undefined;
|
|
10874
|
+
// The inbox read runs under the slot lock, but its schema must be ready first.
|
|
10875
|
+
await queuedSubmissionStore?.ensureChatThreadTables();
|
|
10783
10876
|
slot = await tryClaimRunSlot(threadId, runId, undefined, {
|
|
10784
10877
|
turnId: effectiveTurnId,
|
|
10785
10878
|
...(turnInitiator ? { turnInitiator } : {}),
|
|
@@ -10795,12 +10888,9 @@ export function createProductionAgentHandler(options) {
|
|
|
10795
10888
|
? { dispatchPayload: durableDispatchPayload }
|
|
10796
10889
|
: {}),
|
|
10797
10890
|
...(continueOf ? { continueOf } : {}),
|
|
10798
|
-
...(
|
|
10891
|
+
...(queuedSubmissionStore
|
|
10799
10892
|
? {
|
|
10800
|
-
hasPendingQueuedSubmission:
|
|
10801
|
-
const { hasPendingQueuedSubmissionReceipt } = await import("../chat-threads/store.js");
|
|
10802
|
-
return hasPendingQueuedSubmissionReceipt(tx, runThreadId);
|
|
10803
|
-
},
|
|
10893
|
+
hasPendingQueuedSubmission: (tx) => queuedSubmissionStore.hasPendingQueuedSubmissionReceipt(tx, runThreadId),
|
|
10804
10894
|
}
|
|
10805
10895
|
: {}),
|
|
10806
10896
|
});
|
|
@@ -10833,6 +10923,9 @@ export function createProductionAgentHandler(options) {
|
|
|
10833
10923
|
setResponseHeader(event, "Cache-Control", "no-cache");
|
|
10834
10924
|
setResponseHeader(event, "Connection", "keep-alive");
|
|
10835
10925
|
setResponseHeader(event, "X-Run-Id", slot.completedRunId);
|
|
10926
|
+
// The client leaves a replay's turn out unless named here, and this
|
|
10927
|
+
// replay was found by its turn, so the stored turn is known.
|
|
10928
|
+
setRunTurnIdHeader(event, effectiveTurnId);
|
|
10836
10929
|
setResponseHeader(event, "X-Dispatch-Mode", "replay");
|
|
10837
10930
|
return stream;
|
|
10838
10931
|
}
|
|
@@ -11074,6 +11167,7 @@ export function createProductionAgentHandler(options) {
|
|
|
11074
11167
|
setResponseHeader(event, "Cache-Control", "no-cache");
|
|
11075
11168
|
setResponseHeader(event, "Connection", "keep-alive");
|
|
11076
11169
|
setResponseHeader(event, "X-Run-Id", duplicateRunId);
|
|
11170
|
+
setRunTurnIdHeader(event, getRun(duplicateRunId)?.turnId);
|
|
11077
11171
|
setResponseHeader(event, "X-Dispatch-Mode", "replay");
|
|
11078
11172
|
return stream;
|
|
11079
11173
|
}
|
|
@@ -11174,6 +11268,7 @@ export function createProductionAgentHandler(options) {
|
|
|
11174
11268
|
setResponseHeader(event, "Cache-Control", "no-cache");
|
|
11175
11269
|
setResponseHeader(event, "Connection", "keep-alive");
|
|
11176
11270
|
setResponseHeader(event, "X-Run-Id", runId);
|
|
11271
|
+
setRunTurnIdHeader(event, effectiveTurnId);
|
|
11177
11272
|
setResponseHeader(event, "X-Dispatch-Mode", "background");
|
|
11178
11273
|
return stream;
|
|
11179
11274
|
}
|
|
@@ -11503,7 +11598,7 @@ export function createProductionAgentHandler(options) {
|
|
|
11503
11598
|
void retainRequestDbPoolScope(recordRunDiagnostic(runId, RUN_DIAG_STAGE.setupTimings, setupDetail).catch(() => { })).catch(() => { });
|
|
11504
11599
|
}
|
|
11505
11600
|
if (options.onRunStart) {
|
|
11506
|
-
await options.onRunStart(send, threadId ?? runId, runId);
|
|
11601
|
+
await options.onRunStart(send, threadId ?? runId, runId, effectiveTurnId);
|
|
11507
11602
|
}
|
|
11508
11603
|
const unknownOutcomes = noUnknownOutcomes();
|
|
11509
11604
|
if (customAgentRefs.length > 0) {
|
|
@@ -11938,6 +12033,7 @@ export function createProductionAgentHandler(options) {
|
|
|
11938
12033
|
loopOpts: agentLoopOpts,
|
|
11939
12034
|
runId,
|
|
11940
12035
|
threadId: threadId ?? null,
|
|
12036
|
+
turnId: effectiveTurnId,
|
|
11941
12037
|
userId: ownerEmail,
|
|
11942
12038
|
config: obsConfig,
|
|
11943
12039
|
spanName: turnUsageLabel && turnUsageLabel !== "chat"
|
|
@@ -12047,10 +12143,12 @@ export function createProductionAgentHandler(options) {
|
|
|
12047
12143
|
: "foreground",
|
|
12048
12144
|
runRowAlreadyInserted: foregroundRunRowInserted,
|
|
12049
12145
|
executionLease: true,
|
|
12146
|
+
// An anonymous run is not a user; `run_started` does not name one either.
|
|
12147
|
+
userId: turnInitiator?.anonymous
|
|
12148
|
+
? undefined
|
|
12149
|
+
: (ownerEmail ?? undefined),
|
|
12050
12150
|
// Resolved AFTER stored-model/experiment overrides — the same value
|
|
12051
12151
|
// actually sent to the engine, not the raw client-requested model.
|
|
12052
|
-
// No userId here: `ownerEmail` is the only identity known at this
|
|
12053
|
-
// scope and is PII (email), which the terminal event must not carry.
|
|
12054
12152
|
model: effectiveModel,
|
|
12055
12153
|
engineName: engine.name,
|
|
12056
12154
|
attemptCount: backgroundContinuationCount,
|
|
@@ -12068,6 +12166,7 @@ export function createProductionAgentHandler(options) {
|
|
|
12068
12166
|
setResponseHeader(event, "Cache-Control", "no-cache");
|
|
12069
12167
|
setResponseHeader(event, "Connection", "keep-alive");
|
|
12070
12168
|
setResponseHeader(event, "X-Run-Id", runId);
|
|
12169
|
+
setRunTurnIdHeader(event, effectiveTurnId);
|
|
12071
12170
|
setResponseHeader(event, "X-Dispatch-Mode", foregroundSelfChainEligible ? "foreground-self-chain" : "foreground");
|
|
12072
12171
|
return stream;
|
|
12073
12172
|
});
|