@agent-native/core 0.227.0 → 0.228.0-nightly-20261111004142
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 +6 -2
- package/dist/agent/engine/builder-engine.js +6 -1
- package/dist/agent/engine/tool-call-journal-seed.d.ts +2 -0
- package/dist/agent/engine/tool-call-journal-seed.js +14 -0
- package/dist/agent/harness/store.js +14 -0
- package/dist/agent/production-agent.js +257 -17
- package/dist/agent/reaped-run-thread.js +6 -0
- package/dist/agent/run-manager.js +6 -0
- package/dist/agent/tool-call-journal.d.ts +4 -0
- package/dist/agent/tool-call-journal.js +12 -0
- package/dist/agent/types.d.ts +7 -1
- package/dist/app-config/agent.d.ts +3 -0
- package/dist/app-config/agent.js +7 -0
- package/dist/app-config/schema.d.ts +3 -0
- package/dist/application-state/handlers.d.ts +3 -3
- package/dist/application-state/handlers.js +6 -1
- package/dist/application-state/index.d.ts +3 -2
- package/dist/application-state/index.js +3 -2
- package/dist/application-state/last-used-tab.d.ts +11 -0
- package/dist/application-state/last-used-tab.js +43 -0
- package/dist/application-state/script-helpers.d.ts +26 -0
- package/dist/application-state/script-helpers.js +83 -15
- package/dist/application-state/store.d.ts +8 -0
- package/dist/application-state/store.js +16 -0
- package/dist/cli/agent.js +7 -3
- package/dist/cli/code-agent-executor.d.ts +1 -0
- package/dist/cli/code-agent-executor.js +37 -2
- package/dist/cli/code.js +3 -1
- package/dist/client/agent-sidebar-state.d.ts +2 -0
- package/dist/client/browser-tab-use.d.ts +9 -0
- package/dist/client/browser-tab-use.js +112 -0
- package/dist/client/chat/runtime.js +3 -1
- package/dist/client/error-capture.js +33 -1
- package/dist/client/navigation/index.d.ts +1 -0
- package/dist/client/navigation/index.js +1 -0
- package/dist/client/resources/mcp-integration-logos.js +16 -0
- package/dist/client/review/use-review.d.ts +28 -0
- package/dist/client/review/use-review.js +41 -12
- package/dist/client/route-state.js +2 -0
- package/dist/client/sse-event-processor.d.ts +1 -0
- package/dist/client/sse-event-processor.js +7 -1
- package/dist/client/use-db-sync.d.ts +8 -1
- package/dist/client/use-db-sync.js +6 -5
- package/dist/coding-tools/run-code.js +12 -2
- package/dist/coding-tools/sandbox/executions-store.d.ts +1 -1
- package/dist/coding-tools/sandbox/executions-store.js +9 -3
- package/dist/collab/routes.d.ts +1 -1
- package/dist/data-programs/execute.js +1 -1
- package/dist/db/schema-snapshot.generated.js +17 -3
- package/dist/extensions/url-safety.d.ts +1 -3
- package/dist/extensions/url-safety.js +23 -23
- package/dist/file-upload/actions/upload-image.d.ts +5 -5
- package/dist/file-upload/index.d.ts +1 -1
- package/dist/file-upload/index.js +1 -1
- package/dist/file-upload/s3-client.d.ts +1 -0
- package/dist/file-upload/s3-client.js +70 -13
- package/dist/file-upload/s3.d.ts +9 -0
- package/dist/file-upload/s3.js +14 -1
- package/dist/framework-package-versions.json +8 -8
- package/dist/jobs/run-history.js +10 -3
- package/dist/jobs/scheduler.js +5 -36
- package/dist/localization/core-messages/supplemental/ar-SA.js +35 -0
- package/dist/localization/core-messages/supplemental/de-DE.js +35 -0
- package/dist/localization/core-messages/supplemental/en-US.js +35 -0
- package/dist/localization/core-messages/supplemental/es-ES.js +35 -0
- package/dist/localization/core-messages/supplemental/fr-FR.js +35 -0
- package/dist/localization/core-messages/supplemental/hi-IN.js +35 -0
- package/dist/localization/core-messages/supplemental/ja-JP.js +35 -0
- package/dist/localization/core-messages/supplemental/ko-KR.js +35 -0
- package/dist/localization/core-messages/supplemental/pt-BR.js +35 -0
- package/dist/localization/core-messages/supplemental/zh-CN.js +35 -0
- package/dist/localization/core-messages/supplemental/zh-TW.js +35 -0
- package/dist/localization/default-messages.d.ts +35 -0
- package/dist/localization/default-messages.js +35 -0
- package/dist/localization/mcp-settings-messages.d.ts +35 -0
- package/dist/mcp/actions/list-mcp-client-connections.d.ts +8 -0
- package/dist/mcp/actions/list-mcp-client-connections.js +39 -0
- package/dist/mcp/browser-tab-target.d.ts +16 -0
- package/dist/mcp/browser-tab-target.js +58 -0
- package/dist/mcp/build-server.d.ts +1 -0
- package/dist/mcp/build-server.js +38 -7
- package/dist/mcp/call-attribution.d.ts +4 -1
- package/dist/mcp/call-attribution.js +3 -1
- package/dist/mcp/client-connections.d.ts +6 -0
- package/dist/mcp/client-connections.js +91 -0
- package/dist/mcp/connect-route.js +12 -2
- package/dist/mcp/connect-store.d.ts +3 -0
- package/dist/mcp/connect-store.js +10 -1
- package/dist/mcp/direct-action-call.js +1 -0
- package/dist/mcp/oauth-store.d.ts +6 -0
- package/dist/mcp/oauth-store.js +23 -0
- package/dist/mcp/oauth-token.d.ts +1 -0
- package/dist/mcp/oauth-token.js +1 -0
- package/dist/mcp/server.js +9 -20
- package/dist/mcp-client/config.d.ts +2 -0
- package/dist/mcp-client/manager.js +23 -0
- package/dist/mcp-client/workspace-servers.js +42 -10
- package/dist/notifications/routes.d.ts +3 -3
- package/dist/org/handlers.d.ts +1 -1
- package/dist/org/handlers.js +21 -17
- package/dist/provider-api/actions/custom-provider-registration.d.ts +12 -12
- package/dist/provider-api/actions/provider-api.d.ts +8 -8
- package/dist/provider-api/corpus-jobs.d.ts +2 -2
- package/dist/provider-api/index.js +17 -36
- package/dist/review/actions/create-review-comment.d.ts +7 -0
- package/dist/review/actions/create-review-comment.js +29 -3
- package/dist/review/actions/delete-review-comment.js +2 -2
- package/dist/review/actions/list-review-comments.d.ts +3 -0
- package/dist/review/actions/list-review-comments.js +41 -17
- package/dist/review/actions/react-to-review-comment.js +2 -2
- package/dist/review/actions/reply-review-comment.d.ts +4 -0
- package/dist/review/actions/reply-review-comment.js +15 -6
- package/dist/review/actions/update-review-comment-anchor.d.ts +6 -0
- package/dist/review/actions/update-review-comment-anchor.js +39 -10
- package/dist/review/actions/update-review-comment.d.ts +3 -0
- package/dist/review/actions/update-review-comment.js +28 -3
- package/dist/review/errors.d.ts +33 -0
- package/dist/review/errors.js +51 -0
- package/dist/review/index.d.ts +5 -4
- package/dist/review/index.js +3 -2
- package/dist/review/registry.d.ts +51 -1
- package/dist/review/registry.js +117 -0
- package/dist/review/store.d.ts +59 -14
- package/dist/review/store.js +622 -101
- package/dist/review/suggestions/actions.js +65 -10
- package/dist/review/suggestions/actor-kind.d.ts +5 -0
- package/dist/review/suggestions/actor-kind.js +20 -0
- package/dist/review/suggestions/store.js +16 -2
- package/dist/review/suggestions/types.d.ts +13 -0
- package/dist/review/types.d.ts +89 -0
- package/dist/scripts/db/dev-query-proxy.d.ts +2 -0
- package/dist/scripts/db/dev-query-proxy.js +4 -1
- package/dist/scripts/db/query.js +3 -0
- package/dist/scripts/dev/shell.js +4 -0
- package/dist/scripts/dev-session.d.ts +31 -0
- package/dist/scripts/dev-session.js +58 -0
- package/dist/scripts/resources/list.js +53 -4
- package/dist/scripts/runner.js +22 -4
- package/dist/search/query.d.ts +11 -5
- package/dist/search/query.js +52 -15
- package/dist/search/tokenize.d.ts +1 -0
- package/dist/search/tokenize.js +3 -0
- package/dist/secrets/api-keys.d.ts +4 -0
- package/dist/secrets/api-keys.js +5 -0
- package/dist/secrets/register.d.ts +2 -0
- package/dist/secrets/routes.d.ts +2 -2
- package/dist/secrets/routes.js +85 -6
- package/dist/secrets/storage.d.ts +2 -0
- package/dist/secrets/storage.js +34 -9
- package/dist/secrets/substitution.d.ts +3 -2
- package/dist/secrets/substitution.js +56 -7
- package/dist/server/action-discovery.js +5 -0
- package/dist/server/action-routes.js +8 -6
- package/dist/server/agent-chat/prompt-resources.js +117 -79
- package/dist/server/agent-chat/script-entries.js +15 -8
- package/dist/server/agent-chat-plugin.js +9 -4
- package/dist/server/agent-engine-api-key-route.d.ts +1 -1
- package/dist/server/agent-engine-ollama-models-route.d.ts +1 -1
- package/dist/server/agents-bundle.d.ts +3 -1
- package/dist/server/agents-bundle.js +4 -4
- package/dist/server/csrf.js +1 -0
- package/dist/server/dev-action-bridge.d.ts +2 -0
- package/dist/server/dev-action-bridge.js +34 -5
- package/dist/server/first-touch-route.d.ts +1 -1
- package/dist/server/http-response-telemetry.js +4 -0
- package/dist/server/loopback.js +3 -1
- package/dist/server/request-context.d.ts +26 -0
- package/dist/shared/app-state-routes.d.ts +7 -0
- package/dist/shared/app-state-routes.js +7 -0
- package/dist/shared/mcp-client-connections.d.ts +41 -0
- package/dist/shared/mcp-client-connections.js +18 -0
- package/dist/shared/mcp-connect-content.d.ts +18 -0
- package/dist/shared/mcp-connect-content.js +200 -172
- package/dist/shared/runtime-config.js +5 -1
- package/dist/templates/chat/.agents/skills/context-awareness/SKILL.md +13 -0
- package/dist/templates/default/.agents/skills/context-awareness/SKILL.md +13 -0
- package/dist/templates/workspace-core/.agents/skills/context-awareness/SKILL.md +13 -0
- package/dist/tracking/providers.d.ts +7 -0
- package/dist/tracking/providers.js +39 -17
- package/dist/triggers/condition-evaluator.d.ts +2 -0
- package/dist/triggers/condition-evaluator.js +93 -3
- package/dist/triggers/dispatcher.js +24 -6
- package/docs/AGENTS.md +16 -16
- package/docs/content/create-with-an-agent.mdx +10 -4
- package/docs/content/durable-resume.mdx +11 -2
- package/docs/content/toolkit-comments-review.mdx +54 -1
- package/package.json +5 -4
- package/src/templates/chat/.agents/skills/context-awareness/SKILL.md +13 -0
- package/src/templates/default/.agents/skills/context-awareness/SKILL.md +13 -0
- package/src/templates/workspace-core/.agents/skills/context-awareness/SKILL.md +13 -0
package/dist/action.d.ts
CHANGED
|
@@ -38,6 +38,8 @@ export declare function resolveToolMaxResultChars(action: {
|
|
|
38
38
|
}): number;
|
|
39
39
|
export interface ActionRunContext {
|
|
40
40
|
send?: (event: AgentChatEvent) => void;
|
|
41
|
+
/** Request a clean empty final after this invocation succeeds. Available only to agent tool calls; does not stop the model or override errors, receipts, or later instructions. */
|
|
42
|
+
settle?: () => void;
|
|
41
43
|
/**
|
|
42
44
|
* Result cap the agent loop enforces on this call, from
|
|
43
45
|
* `resolveToolMaxResultChars`. Absent outside the agent loop.
|
|
@@ -92,12 +94,14 @@ export interface ActionRunContext {
|
|
|
92
94
|
approvedToolCallKey?: string;
|
|
93
95
|
/**
|
|
94
96
|
* The client behind a `caller: "mcp"` call. `name` and `version` are its
|
|
95
|
-
* self-reported `clientInfo
|
|
96
|
-
* credential was issued
|
|
97
|
+
* self-reported `clientInfo`, and `userAgent` its self-reported HTTP
|
|
98
|
+
* User-Agent. `oauthClientId` is the OAuth client its credential was issued
|
|
99
|
+
* to.
|
|
97
100
|
*/
|
|
98
101
|
mcpClient?: {
|
|
99
102
|
name?: string;
|
|
100
103
|
version?: string;
|
|
104
|
+
userAgent?: string;
|
|
101
105
|
oauthClientId?: string;
|
|
102
106
|
};
|
|
103
107
|
}
|
|
@@ -194,7 +194,12 @@ class BuilderEngine {
|
|
|
194
194
|
const toolNameMap = createProviderToolNameMap(opts.tools, opts.messages);
|
|
195
195
|
const providerTools = limitProviderTools(opts.tools);
|
|
196
196
|
const messages = engineMessagesToBuilderGatewayAnthropic(await inlineOwnedToolResultImages(opts.messages), toolNameMap);
|
|
197
|
-
const
|
|
197
|
+
const anthropicTools = engineToolsToAnthropic(providerTools, toolNameMap);
|
|
198
|
+
// The gateway sends GPT tools in OpenAI strict mode unless told otherwise,
|
|
199
|
+
// and strict mode makes the model fill every optional action argument.
|
|
200
|
+
const tools = /^gpt-/i.test(model)
|
|
201
|
+
? anthropicTools.map((tool) => ({ ...tool, strict: false }))
|
|
202
|
+
: anthropicTools;
|
|
198
203
|
const thinkingBudget = opts.providerOptions?.anthropic?.thinking?.budgetTokens;
|
|
199
204
|
const reasoningEffort = normalizeReasoningEffortForModel(model, opts.reasoningEffort ??
|
|
200
205
|
(typeof thinkingBudget === "number"
|
|
@@ -1,3 +1,4 @@
|
|
|
1
|
+
import type { WriteReceipt } from "../../action.js";
|
|
1
2
|
import { type ArtifactReceipt } from "../../artifacts/detect.js";
|
|
2
3
|
import { type ToolCallJournal } from "../tool-call-journal.js";
|
|
3
4
|
import type { AgentChatEvent } from "../types.js";
|
|
@@ -25,6 +26,7 @@ export interface PriorTurnToolResultSummary {
|
|
|
25
26
|
content: string;
|
|
26
27
|
isError: boolean;
|
|
27
28
|
artifacts?: ArtifactReceipt[];
|
|
29
|
+
receipt?: WriteReceipt;
|
|
28
30
|
}
|
|
29
31
|
export type PriorTurnToolCallJournalRead = {
|
|
30
32
|
status: "read";
|
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
import { isArtifactReceipt, } from "../../artifacts/detect.js";
|
|
2
2
|
import { AgentRunJournalUnreadableError, getCompletedAgentExecutionSteps, getCurrentTurnEventsForThread, } from "../run-store.js";
|
|
3
3
|
import { classifyToolCallJournal, isConsistentToolCallInputFingerprint, toolCallInputFingerprint, } from "../tool-call-journal.js";
|
|
4
|
+
import { readWriteReceipt } from "../write-receipts.js";
|
|
4
5
|
const LEDGER_READ_RETRY_MS = 250;
|
|
5
6
|
function isRecord(value) {
|
|
6
7
|
return !!value && typeof value === "object" && !Array.isArray(value);
|
|
@@ -75,6 +76,7 @@ function durableStepToolDone(event, row, unreadable) {
|
|
|
75
76
|
const mcpApp = durableStepMcpApp(outcome.mcpApp);
|
|
76
77
|
const chatUI = durableStepChatUI(outcome.chatUI);
|
|
77
78
|
const fileMutation = durableStepFileMutation(outcome.fileMutation);
|
|
79
|
+
const receipt = readWriteReceipt({ _receipt: outcome.receipt });
|
|
78
80
|
return {
|
|
79
81
|
type: "tool_done",
|
|
80
82
|
tool: event.tool,
|
|
@@ -95,6 +97,16 @@ function durableStepToolDone(event, row, unreadable) {
|
|
|
95
97
|
? { errorCode: outcome.errorCode }
|
|
96
98
|
: {}),
|
|
97
99
|
completedSideEffect: outcome.completedSideEffect,
|
|
100
|
+
...(outcome.hasAgentWarnings === true
|
|
101
|
+
? { hasAgentWarnings: true }
|
|
102
|
+
: {}),
|
|
103
|
+
...(receipt ? { receipt } : {}),
|
|
104
|
+
...(!outcome.isError &&
|
|
105
|
+
outcome.actionSettled === true &&
|
|
106
|
+
outcome.settledOnCleanToolStop === true &&
|
|
107
|
+
outcome.replayTruncated !== true
|
|
108
|
+
? { actionSettled: true, settledOnCleanToolStop: true }
|
|
109
|
+
: {}),
|
|
98
110
|
...(outcome.replayed === true ? { replayed: true } : {}),
|
|
99
111
|
argumentsHash: input.argumentsHash,
|
|
100
112
|
...(artifacts.length > 0 ? { artifacts } : {}),
|
|
@@ -351,11 +363,13 @@ export async function loadPriorTurnToolCallJournal(threadId, turnId, options = {
|
|
|
351
363
|
matchedStart: call !== undefined,
|
|
352
364
|
});
|
|
353
365
|
const artifacts = event.artifacts?.filter(isArtifactReceipt);
|
|
366
|
+
const receipt = readWriteReceipt({ _receipt: event.receipt });
|
|
354
367
|
priorToolResults.push({
|
|
355
368
|
name: event.tool,
|
|
356
369
|
input: event.input ?? call?.input,
|
|
357
370
|
content: event.result,
|
|
358
371
|
isError: event.isError === true,
|
|
372
|
+
...(receipt ? { receipt } : {}),
|
|
359
373
|
...(artifacts && artifacts.length > 0 ? { artifacts } : {}),
|
|
360
374
|
});
|
|
361
375
|
}
|
|
@@ -151,6 +151,20 @@ export const agentHarnessSessionsStore = defineStore({
|
|
|
151
151
|
name: "agent-harness-sessions-run-policy",
|
|
152
152
|
sql: "ALTER TABLE agent_harness_sessions ADD COLUMN IF NOT EXISTS run_policy TEXT",
|
|
153
153
|
},
|
|
154
|
+
{
|
|
155
|
+
// Local runtimes skip legacy v6/v7 and never re-run a recorded baseline.
|
|
156
|
+
// The injected client keeps a rolled-back caller transaction's catalog
|
|
157
|
+
// view from marking this done without the columns.
|
|
158
|
+
name: "agent-harness-sessions-approval-and-expiry-columns",
|
|
159
|
+
run: async (exec) => {
|
|
160
|
+
for (const column of [
|
|
161
|
+
"approval_resolution_id",
|
|
162
|
+
"expiry_cleanup_id",
|
|
163
|
+
]) {
|
|
164
|
+
await ensureColumnExists("agent_harness_sessions", column, `ALTER TABLE agent_harness_sessions ADD COLUMN IF NOT EXISTS ${column} TEXT`, { injectedClient: exec });
|
|
165
|
+
}
|
|
166
|
+
},
|
|
167
|
+
},
|
|
154
168
|
],
|
|
155
169
|
});
|
|
156
170
|
export function ensureAgentHarnessSessionTables() {
|
|
@@ -7,7 +7,7 @@ import { defineEventHandler, getHeader, setResponseHeader, setResponseStatus, ge
|
|
|
7
7
|
import { parseA2AAgentActivityPart } from "../a2a/activity.js";
|
|
8
8
|
import "../authorization/check-action.js";
|
|
9
9
|
import { actionCallIsRetrySafe, actionChangeResource, classifyActionCall, } from "../action-call-classification.js";
|
|
10
|
-
import { runOutsideActionInvocation, vouchedOutcome } from "../action-frame.js";
|
|
10
|
+
import { actionExecutionStorage, runOutsideActionInvocation, vouchedOutcome, } from "../action-frame.js";
|
|
11
11
|
import { ACTION_CHAT_UI_RECORD_CHANGE_RENDERER, normalizeActionChangeResult, } from "../action-ui.js";
|
|
12
12
|
import { AgentConnectionRequiredError, describeToolParameterSignature, isActionContractError, isActionHiddenFromEveryAgentSurface, isAgentActionStopError, isAgentConnectionRequiredError, actionCallIntent, ActionInputValidationError, telemetryInvalidPaths, invocationEffect, runPreparedActionCall, boundActionCallIntent, preparedCallArguments, preparedCallNeedsApproval, recordInvalidActionCall, resolveToolMaxResultChars, stripUnsupportedSchemaKeywords, } from "../action.js";
|
|
13
13
|
import { resolveAgentKitIntegrationReferences } from "../agentkit/capabilities.js";
|
|
@@ -109,7 +109,7 @@ import { getStrictToolInputValidator } from "./strict-tool-input.js";
|
|
|
109
109
|
import { normalizeSubmissionDeadlineMetadata } from "./submission-deadline.js";
|
|
110
110
|
import { consumeAgentToolApproval, createAgentToolApproval, ensureAgentToolApprovalTable, hasPendingCurrentTurnApproval, isAgentToolAlwaysAllowed, resolveAgentToolApprovalTurnId, } from "./tool-approval-store.js";
|
|
111
111
|
import { toolCallInputSignature } from "./tool-call-input-signature.js";
|
|
112
|
-
import { ambiguousLegacyCallEffectKey, findAmbiguousLegacyJournalEntryForCall, findUnresolvedJournalStepForCall, findInterruptedLegacyJournalEntryForCall, findReplayableJournalEntry, hasJournalStepForCall, isUnavailableToolCallInput, journalEntriesAmbiguousInHistory, journalEntriesRepresentedInHistory, journalCallEffects, journalCallIdentities, matchToolCallResultsInHistory, toolCallIdentityKey, toolCallInputFingerprint, unmatchedAmbiguousLegacyToolCallResultsInHistory, } from "./tool-call-journal.js";
|
|
112
|
+
import { ambiguousLegacyCallEffectKey, buildResumeJournalNote, findAmbiguousLegacyJournalEntryForCall, findUnresolvedJournalStepForCall, findInterruptedLegacyJournalEntryForCall, findReplayableJournalEntry, hasJournalStepForCall, isUnavailableToolCallInput, journalEntriesAmbiguousInHistory, journalEntriesRepresentedInHistory, journalCallEffects, journalCallIdentities, matchToolCallResultsInHistory, toolCallIdentityKey, toolCallInputFingerprint, unmatchedAmbiguousLegacyToolCallResultsInHistory, } from "./tool-call-journal.js";
|
|
113
113
|
import { redactSensitiveFields, sanitizeToolErrorText, sanitizeToolErrorValue, } from "./tool-error-redaction.js";
|
|
114
114
|
import { formatToolIndex } from "./tool-index.js";
|
|
115
115
|
import { describeToolResultImages, extractAgentImagesFromActionResult, } from "./tool-result-images.js";
|
|
@@ -2875,6 +2875,7 @@ function serializeDurableExecutionStepOutcome(outcome) {
|
|
|
2875
2875
|
replayTruncated: true,
|
|
2876
2876
|
replayOmittedFields: omittedFields,
|
|
2877
2877
|
...(outcome.errorCode ? { errorCode: outcome.errorCode } : {}),
|
|
2878
|
+
...(outcome.hasAgentWarnings ? { hasAgentWarnings: true } : {}),
|
|
2878
2879
|
...(outcome.receipt ? { receipt: outcome.receipt } : {}),
|
|
2879
2880
|
};
|
|
2880
2881
|
let serialized;
|
|
@@ -5028,6 +5029,9 @@ export async function runAgentLoop(opts) {
|
|
|
5028
5029
|
if (followUpRunId)
|
|
5029
5030
|
send({ type: "suggestions", suggestions: [] });
|
|
5030
5031
|
let model = normalizeModelForEngine(engine, opts.model);
|
|
5032
|
+
let settlementCandidate = false;
|
|
5033
|
+
let settledEmptyFinal = false;
|
|
5034
|
+
let turnHasActionWarnings = false;
|
|
5031
5035
|
let outcomeReported = false;
|
|
5032
5036
|
const reportOutcome = (outcome) => {
|
|
5033
5037
|
if (outcomeReported)
|
|
@@ -5045,11 +5049,11 @@ export async function runAgentLoop(opts) {
|
|
|
5045
5049
|
// provider reads alongside those results.
|
|
5046
5050
|
const deliverSteeredMessagesAtToolBoundary = async (toolResultParts) => {
|
|
5047
5051
|
if (signal.aborted)
|
|
5048
|
-
return;
|
|
5052
|
+
return 0;
|
|
5049
5053
|
const threadId = opts.threadId;
|
|
5050
5054
|
const runId = opts.runId;
|
|
5051
5055
|
if (!threadId || !runId)
|
|
5052
|
-
return;
|
|
5056
|
+
return 0;
|
|
5053
5057
|
const { claimSteeredQueuedMessages } = await import("../chat-threads/store.js");
|
|
5054
5058
|
let delivered;
|
|
5055
5059
|
try {
|
|
@@ -5059,7 +5063,7 @@ export async function runAgentLoop(opts) {
|
|
|
5059
5063
|
// The claim is one transaction, so a failure leaves the message queued
|
|
5060
5064
|
// and on screen; it sends at a later boundary or after this reply.
|
|
5061
5065
|
console.warn("[agent-chat] Send now delivery skipped at a tool boundary:", error instanceof Error ? error.message : String(error));
|
|
5062
|
-
return;
|
|
5066
|
+
return 0;
|
|
5063
5067
|
}
|
|
5064
5068
|
for (const { queued, record } of delivered) {
|
|
5065
5069
|
toolResultParts.push(...buildUserContentWithAttachments({
|
|
@@ -5073,6 +5077,7 @@ export async function runAgentLoop(opts) {
|
|
|
5073
5077
|
continuationMessageId: record.continuationMessageId,
|
|
5074
5078
|
});
|
|
5075
5079
|
}
|
|
5080
|
+
return delivered.length;
|
|
5076
5081
|
};
|
|
5077
5082
|
const deferSteeredMessagesPastFinalResponse = async () => {
|
|
5078
5083
|
if (!opts.threadId || !opts.runId)
|
|
@@ -5260,6 +5265,11 @@ export async function runAgentLoop(opts) {
|
|
|
5260
5265
|
recoverCompletedExecutionSteps: opts.recoverCompletedExecutionSteps === true,
|
|
5261
5266
|
});
|
|
5262
5267
|
const toolCallJournal = journalRead.status === "read" ? journalRead.toolCallJournal : null;
|
|
5268
|
+
turnHasActionWarnings =
|
|
5269
|
+
journalRead.status === "read" &&
|
|
5270
|
+
journalRead.events.some((event) => event.type === "tool_done" &&
|
|
5271
|
+
(event.hasAgentWarnings === true ||
|
|
5272
|
+
event.result.includes("<agent-warning ")));
|
|
5263
5273
|
const journalHistory = messages.slice(findCurrentTurnStartForContinuation(messages));
|
|
5264
5274
|
const journalEntriesAlreadyRepresented = toolCallJournal
|
|
5265
5275
|
? journalEntriesRepresentedInHistory(toolCallJournal, journalHistory)
|
|
@@ -5308,6 +5318,111 @@ export async function runAgentLoop(opts) {
|
|
|
5308
5318
|
};
|
|
5309
5319
|
toolCallHistory.push(...journaledPriorToolCalls);
|
|
5310
5320
|
toolResultHistory.push(...journaledPriorToolResults);
|
|
5321
|
+
if ((internalContinuationTurn ||
|
|
5322
|
+
opts.recoverCompletedExecutionSteps === true) &&
|
|
5323
|
+
toolCallJournal &&
|
|
5324
|
+
toolCallJournal.interrupted.length === 0) {
|
|
5325
|
+
const tail = journalHistory.at(-1);
|
|
5326
|
+
const continuationText = tail?.role === "user" &&
|
|
5327
|
+
tail.content.length === 1 &&
|
|
5328
|
+
tail.content[0]?.type === "text"
|
|
5329
|
+
? tail.content[0].text
|
|
5330
|
+
: undefined;
|
|
5331
|
+
const journalNote = buildResumeJournalNote(toolCallJournal);
|
|
5332
|
+
const safeContinuation = continuationText !== undefined &&
|
|
5333
|
+
[
|
|
5334
|
+
"run_timeout",
|
|
5335
|
+
"loop_limit",
|
|
5336
|
+
"stream_ended",
|
|
5337
|
+
"gateway_timeout",
|
|
5338
|
+
"rate_limited",
|
|
5339
|
+
"network_interrupted",
|
|
5340
|
+
"no_progress",
|
|
5341
|
+
].some((reason) => [undefined, journalNote ?? undefined].some((note) => {
|
|
5342
|
+
const expected = [];
|
|
5343
|
+
appendAgentLoopContinuation(expected, reason, note ? { journalNote: note } : {});
|
|
5344
|
+
return (expected[0]?.content[0]?.type === "text" &&
|
|
5345
|
+
expected[0].content[0].text === continuationText);
|
|
5346
|
+
}));
|
|
5347
|
+
const lastMessage = journalHistory.at(safeContinuation ? -2 : -1);
|
|
5348
|
+
const previousMessage = journalHistory.at(safeContinuation ? -3 : -2);
|
|
5349
|
+
const pairs = matchToolCallResultsInHistory(journalHistory);
|
|
5350
|
+
const calls = previousMessage?.role === "assistant"
|
|
5351
|
+
? previousMessage.content.filter((part) => part.type === "tool-call")
|
|
5352
|
+
: [];
|
|
5353
|
+
const events = journalRead.status === "read" ? journalRead.events : [];
|
|
5354
|
+
const batchIndex = events
|
|
5355
|
+
.map((event) => event.type === "rich_event" &&
|
|
5356
|
+
event.event.namespace === "agent-native.tool-batch" &&
|
|
5357
|
+
event.event.name === "declared" &&
|
|
5358
|
+
event.event.version === 1)
|
|
5359
|
+
.lastIndexOf(true);
|
|
5360
|
+
const batchEvent = batchIndex < 0 ? undefined : events.at(batchIndex);
|
|
5361
|
+
const batchData = batchEvent?.type === "rich_event" ? batchEvent.event.data : undefined;
|
|
5362
|
+
const batchCalls = isRecord(batchData) && Array.isArray(batchData.calls)
|
|
5363
|
+
? batchData.calls
|
|
5364
|
+
: [];
|
|
5365
|
+
const afterBatch = events.slice(batchIndex + 1);
|
|
5366
|
+
const starts = afterBatch.filter((event) => event.type === "tool_start");
|
|
5367
|
+
const dones = afterBatch.filter((event) => event.type === "tool_done");
|
|
5368
|
+
if (batchIndex >= 0 &&
|
|
5369
|
+
batchCalls.length > 0 &&
|
|
5370
|
+
batchCalls.every((call) => isRecord(call) &&
|
|
5371
|
+
typeof call.id === "string" &&
|
|
5372
|
+
call.id.length > 0 &&
|
|
5373
|
+
typeof call.name === "string" &&
|
|
5374
|
+
call.name.length > 0) &&
|
|
5375
|
+
new Set(batchCalls.map((call) => call.id)).size === batchCalls.length &&
|
|
5376
|
+
starts.length === batchCalls.length &&
|
|
5377
|
+
dones.length === batchCalls.length &&
|
|
5378
|
+
!afterBatch.some((event) => event.type === "user_message" ||
|
|
5379
|
+
event.type === "tool_input_start" ||
|
|
5380
|
+
event.type === "tool_input_delta" ||
|
|
5381
|
+
(event.type === "model_stream" &&
|
|
5382
|
+
event.status === "end" &&
|
|
5383
|
+
event.reason !== "end_turn")) &&
|
|
5384
|
+
lastMessage?.role === "user" &&
|
|
5385
|
+
lastMessage.content.length > 0 &&
|
|
5386
|
+
lastMessage.content.every((part) => part.type === "tool-result" &&
|
|
5387
|
+
!part.isError &&
|
|
5388
|
+
part.outcome !== "unknown" &&
|
|
5389
|
+
!part.approvalPause) &&
|
|
5390
|
+
batchCalls.every((call) => {
|
|
5391
|
+
if (!isRecord(call) ||
|
|
5392
|
+
typeof call.id !== "string" ||
|
|
5393
|
+
typeof call.name !== "string")
|
|
5394
|
+
return false;
|
|
5395
|
+
const matchingStarts = starts.filter((start) => start.id === call.id && start.tool === call.name);
|
|
5396
|
+
if (matchingStarts.length !== 1 || !matchingStarts[0]?.executionStepId)
|
|
5397
|
+
return false;
|
|
5398
|
+
const stepId = matchingStarts[0].executionStepId;
|
|
5399
|
+
const matchingDones = dones.filter((done) => done.executionStepId === stepId &&
|
|
5400
|
+
done.tool === call.name &&
|
|
5401
|
+
done.id === call.id);
|
|
5402
|
+
const matchingPairs = pairs.filter((pair) => calls.includes(pair.call) &&
|
|
5403
|
+
lastMessage.content.includes(pair.result) &&
|
|
5404
|
+
pair.call.executionStepId === stepId &&
|
|
5405
|
+
pair.result.executionStepId === stepId &&
|
|
5406
|
+
pair.call.name === call.name &&
|
|
5407
|
+
pair.result.toolName === call.name);
|
|
5408
|
+
const entries = toolCallJournal.completed.filter((entry) => entry.executionStepId === stepId && entry.tool === call.name);
|
|
5409
|
+
return (matchingDones.length === 1 &&
|
|
5410
|
+
matchingDones[0]?.actionSettled === true &&
|
|
5411
|
+
matchingDones[0].settledOnCleanToolStop === true &&
|
|
5412
|
+
!matchingDones[0].isError &&
|
|
5413
|
+
matchingDones[0].outcomeUnknown !== true &&
|
|
5414
|
+
matchingPairs.length === 1 &&
|
|
5415
|
+
entries.length === 1 &&
|
|
5416
|
+
entries[0]?.actionSettled === true &&
|
|
5417
|
+
entries[0].settledOnCleanToolStop === true &&
|
|
5418
|
+
!entries[0].isError);
|
|
5419
|
+
}) &&
|
|
5420
|
+
!toolResultHistory.some((result) => result.isError) &&
|
|
5421
|
+
!turnHasActionWarnings &&
|
|
5422
|
+
!writeReceiptGuard(turnReceipts(), false)) {
|
|
5423
|
+
settlementCandidate = true;
|
|
5424
|
+
}
|
|
5425
|
+
}
|
|
5311
5426
|
const unreadableJournalStop = journalRead.status === "unreadable" &&
|
|
5312
5427
|
(journalRead.code === "private_history_unrecoverable" ||
|
|
5313
5428
|
internalContinuationTurn ||
|
|
@@ -6181,6 +6296,9 @@ export async function runAgentLoop(opts) {
|
|
|
6181
6296
|
}
|
|
6182
6297
|
if (followUpResults.length > 0)
|
|
6183
6298
|
messages.push({ role: "user", content: followUpResults });
|
|
6299
|
+
if (terminalStopReason === "max_tokens" || followUpResults.length > 0) {
|
|
6300
|
+
settlementCandidate = false;
|
|
6301
|
+
}
|
|
6184
6302
|
if (terminalStopReason === "max_tokens") {
|
|
6185
6303
|
if (hasEmptyFinalResponse) {
|
|
6186
6304
|
preserveReasoningEffortForEmptyResponseRetries = true;
|
|
@@ -6222,7 +6340,13 @@ export async function runAgentLoop(opts) {
|
|
|
6222
6340
|
flushUnstreamedAssistantText();
|
|
6223
6341
|
continue;
|
|
6224
6342
|
}
|
|
6225
|
-
|
|
6343
|
+
const canSettleEmptyFinal = hasEmptyFinalResponse &&
|
|
6344
|
+
settlementCandidate &&
|
|
6345
|
+
terminalStopReason === "end_turn" &&
|
|
6346
|
+
followUpResults.length === 0 &&
|
|
6347
|
+
!isSignalAborted(signal) &&
|
|
6348
|
+
!writeReceiptGuard(turnReceipts(), receiptGuardRetried);
|
|
6349
|
+
if (hasEmptyFinalResponse && !canSettleEmptyFinal) {
|
|
6226
6350
|
completedFollowUpSuggestions = undefined;
|
|
6227
6351
|
if (emptyFinalResponseRetries < EMPTY_FINAL_RESPONSE_RETRY_LIMIT) {
|
|
6228
6352
|
emptyFinalResponseRetries += 1;
|
|
@@ -6277,6 +6401,7 @@ export async function runAgentLoop(opts) {
|
|
|
6277
6401
|
: receiptGuard;
|
|
6278
6402
|
}
|
|
6279
6403
|
if (guard) {
|
|
6404
|
+
settlementCandidate = false;
|
|
6280
6405
|
completedFollowUpSuggestions = undefined;
|
|
6281
6406
|
const retryMessage = typeof guard === "string" ? guard : guard.retryMessage;
|
|
6282
6407
|
const fallbackMessage = typeof guard === "string" ? guard : guard.fallbackMessage;
|
|
@@ -6318,7 +6443,8 @@ export async function runAgentLoop(opts) {
|
|
|
6318
6443
|
}
|
|
6319
6444
|
else {
|
|
6320
6445
|
flushUnstreamedAssistantText();
|
|
6321
|
-
if (
|
|
6446
|
+
if (!canSettleEmptyFinal &&
|
|
6447
|
+
followUpRunId &&
|
|
6322
6448
|
completedFollowUpSuggestions === undefined &&
|
|
6323
6449
|
(terminalStopReason === "end_turn" || followUpResults.length > 0)) {
|
|
6324
6450
|
completingFollowUpSuggestions = true;
|
|
@@ -6331,6 +6457,7 @@ export async function runAgentLoop(opts) {
|
|
|
6331
6457
|
continue;
|
|
6332
6458
|
}
|
|
6333
6459
|
}
|
|
6460
|
+
settledEmptyFinal = canSettleEmptyFinal && !guard;
|
|
6334
6461
|
emptyFinalResponseRetries = 0;
|
|
6335
6462
|
preserveReasoningEffortForEmptyResponseRetries = false;
|
|
6336
6463
|
consecutiveMaxTokensContinuations = 0;
|
|
@@ -6346,6 +6473,7 @@ export async function runAgentLoop(opts) {
|
|
|
6346
6473
|
preserveReasoningEffortForEmptyResponseRetries = false;
|
|
6347
6474
|
consecutiveMaxTokensContinuations = 0;
|
|
6348
6475
|
const toolCallTruncatedByOutputCap = terminalStopReason === "max_tokens";
|
|
6476
|
+
const toolCallHasCleanStop = terminalStopReason === "tool_use";
|
|
6349
6477
|
if (toolCallTruncatedByOutputCap) {
|
|
6350
6478
|
if (truncatedToolCallRetries < TRUNCATED_TOOL_CALL_RETRY_LIMIT) {
|
|
6351
6479
|
truncatedToolCallRetries += 1;
|
|
@@ -6361,6 +6489,25 @@ export async function runAgentLoop(opts) {
|
|
|
6361
6489
|
effectiveMaxOutputTokens = opts.maxOutputTokens;
|
|
6362
6490
|
}
|
|
6363
6491
|
flushUnstreamedAssistantText();
|
|
6492
|
+
settlementCandidate = false;
|
|
6493
|
+
const batchCallIds = toolCallParts.map((call) => call.id);
|
|
6494
|
+
if (currentExecutionFence() &&
|
|
6495
|
+
toolCallHasCleanStop &&
|
|
6496
|
+
followUpResults.length === 0 &&
|
|
6497
|
+
!send.isPrivateTurn() &&
|
|
6498
|
+
batchCallIds.every(Boolean) &&
|
|
6499
|
+
new Set(batchCallIds).size === batchCallIds.length) {
|
|
6500
|
+
await send({
|
|
6501
|
+
type: "rich_event",
|
|
6502
|
+
event: {
|
|
6503
|
+
namespace: "agent-native.tool-batch",
|
|
6504
|
+
name: "declared",
|
|
6505
|
+
version: 1,
|
|
6506
|
+
data: { calls: toolCallParts.map(({ id, name }) => ({ id, name })) },
|
|
6507
|
+
},
|
|
6508
|
+
});
|
|
6509
|
+
}
|
|
6510
|
+
const settledToolCalls = new Set();
|
|
6364
6511
|
let requestedActionStop = null;
|
|
6365
6512
|
// Tool closures set the stop, so control flow still reads it as null here.
|
|
6366
6513
|
const actionStopRequested = () => requestedActionStop !== null;
|
|
@@ -7103,6 +7250,8 @@ export async function runAgentLoop(opts) {
|
|
|
7103
7250
|
throw new Error(`Execution step ${toolCall.id} has no stored result text`);
|
|
7104
7251
|
}
|
|
7105
7252
|
const isError = outcome.isError === true;
|
|
7253
|
+
if (outcome.hasAgentWarnings === true)
|
|
7254
|
+
turnHasActionWarnings = true;
|
|
7106
7255
|
const completedSideEffect = outcome.completedSideEffect === true;
|
|
7107
7256
|
const artifacts = Array.isArray(outcome.artifacts)
|
|
7108
7257
|
? outcome.artifacts.filter(isArtifactReceipt)
|
|
@@ -7130,8 +7279,21 @@ export async function runAgentLoop(opts) {
|
|
|
7130
7279
|
...(errorCode ? { errorCode } : {}),
|
|
7131
7280
|
completedSideEffect,
|
|
7132
7281
|
replayed: true,
|
|
7282
|
+
...(outcome.hasAgentWarnings === true
|
|
7283
|
+
? { hasAgentWarnings: true }
|
|
7284
|
+
: {}),
|
|
7133
7285
|
...(artifacts.length > 0 ? { artifacts } : {}),
|
|
7134
7286
|
...(receipt ? { receipt } : {}),
|
|
7287
|
+
...(outcome.isError === false &&
|
|
7288
|
+
outcome.actionSettled === true &&
|
|
7289
|
+
outcome.settledOnCleanToolStop === true &&
|
|
7290
|
+
toolCallHasCleanStop &&
|
|
7291
|
+
outcome.replayTruncated !== true
|
|
7292
|
+
? {
|
|
7293
|
+
actionSettled: true,
|
|
7294
|
+
settledOnCleanToolStop: true,
|
|
7295
|
+
}
|
|
7296
|
+
: {}),
|
|
7135
7297
|
...(mcpApp ? { mcpApp } : {}),
|
|
7136
7298
|
...(chatUI ? { chatUI } : {}),
|
|
7137
7299
|
...(chatUI && outcome.chatUIResult !== undefined
|
|
@@ -7140,6 +7302,14 @@ export async function runAgentLoop(opts) {
|
|
|
7140
7302
|
...(fileMutation ? { fileMutation } : {}),
|
|
7141
7303
|
});
|
|
7142
7304
|
recordToolResult(result, isError, artifacts, receipt, false);
|
|
7305
|
+
if (!isError &&
|
|
7306
|
+
outcome.isError === false &&
|
|
7307
|
+
typeof outcome.completedSideEffect === "boolean" &&
|
|
7308
|
+
outcome.actionSettled === true &&
|
|
7309
|
+
outcome.settledOnCleanToolStop === true &&
|
|
7310
|
+
outcome.replayTruncated !== true) {
|
|
7311
|
+
settledToolCalls.add(originalToolCall);
|
|
7312
|
+
}
|
|
7143
7313
|
if (!isError) {
|
|
7144
7314
|
noteToolCallSucceeded(actionEntry, toolCall.name, toolCall.input);
|
|
7145
7315
|
}
|
|
@@ -7251,6 +7421,16 @@ export async function runAgentLoop(opts) {
|
|
|
7251
7421
|
result,
|
|
7252
7422
|
...(replayedFailure ? { isError: true } : {}),
|
|
7253
7423
|
...(journaled.errorCode ? { errorCode: journaled.errorCode } : {}),
|
|
7424
|
+
...(journaled.receipt ? { receipt: journaled.receipt } : {}),
|
|
7425
|
+
...(!replayedFailure &&
|
|
7426
|
+
journaled.actionSettled &&
|
|
7427
|
+
journaled.settledOnCleanToolStop &&
|
|
7428
|
+
toolCallHasCleanStop
|
|
7429
|
+
? {
|
|
7430
|
+
actionSettled: true,
|
|
7431
|
+
settledOnCleanToolStop: true,
|
|
7432
|
+
}
|
|
7433
|
+
: {}),
|
|
7254
7434
|
completedSideEffect: true,
|
|
7255
7435
|
replayed: true,
|
|
7256
7436
|
...(journaled.artifacts?.length
|
|
@@ -7267,7 +7447,11 @@ export async function runAgentLoop(opts) {
|
|
|
7267
7447
|
}
|
|
7268
7448
|
: {}),
|
|
7269
7449
|
});
|
|
7270
|
-
recordToolResult(result, replayedFailure, journaled.artifacts,
|
|
7450
|
+
recordToolResult(result, replayedFailure, journaled.artifacts, journaled.receipt, false);
|
|
7451
|
+
if (!replayedFailure &&
|
|
7452
|
+
journaled.actionSettled &&
|
|
7453
|
+
journaled.settledOnCleanToolStop)
|
|
7454
|
+
settledToolCalls.add(originalToolCall);
|
|
7271
7455
|
if (!replayedFailure) {
|
|
7272
7456
|
noteToolCallSucceeded(actionEntry, toolCall.name, toolCall.input);
|
|
7273
7457
|
}
|
|
@@ -7548,6 +7732,10 @@ export async function runAgentLoop(opts) {
|
|
|
7548
7732
|
let toolArtifacts = [];
|
|
7549
7733
|
let fileMutation;
|
|
7550
7734
|
let receipt;
|
|
7735
|
+
let requestedSettlement = false;
|
|
7736
|
+
const settlementWasRequested = () => requestedSettlement;
|
|
7737
|
+
let settlementOnReturn = false;
|
|
7738
|
+
let requiresNextAction = false;
|
|
7551
7739
|
try {
|
|
7552
7740
|
// The run may have been aborted while we waited above for an
|
|
7553
7741
|
// interrupted tool's ledger result (the wait can poll for minutes).
|
|
@@ -7597,6 +7785,13 @@ export async function runAgentLoop(opts) {
|
|
|
7597
7785
|
}
|
|
7598
7786
|
const actionContext = {
|
|
7599
7787
|
send,
|
|
7788
|
+
settle: () => {
|
|
7789
|
+
if (actionExecutionOutcome.evidence &&
|
|
7790
|
+
actionExecutionStorage?.getStore()?.frame ===
|
|
7791
|
+
actionExecutionOutcome.evidence) {
|
|
7792
|
+
requestedSettlement = true;
|
|
7793
|
+
}
|
|
7794
|
+
},
|
|
7600
7795
|
userEmail: actionUserEmail ?? undefined,
|
|
7601
7796
|
orgId: actionOrgId,
|
|
7602
7797
|
appId: opts.appId,
|
|
@@ -7750,8 +7945,12 @@ export async function runAgentLoop(opts) {
|
|
|
7750
7945
|
signal.addEventListener("abort", () => reject(new Error("Run aborted")), { once: true });
|
|
7751
7946
|
}),
|
|
7752
7947
|
]);
|
|
7948
|
+
settlementOnReturn = settlementWasRequested();
|
|
7753
7949
|
const mcpResult = isMcpActionResult(raw) ? raw : null;
|
|
7754
7950
|
const rawForAgent = mcpResult ? mcpResult.text : raw;
|
|
7951
|
+
requiresNextAction =
|
|
7952
|
+
isRecord(rawForAgent) &&
|
|
7953
|
+
typeof rawForAgent.nextRequiredAction === "string";
|
|
7755
7954
|
if (mcpResult &&
|
|
7756
7955
|
mcpResult.raw &&
|
|
7757
7956
|
typeof mcpResult.raw === "object" &&
|
|
@@ -7942,8 +8141,18 @@ export async function runAgentLoop(opts) {
|
|
|
7942
8141
|
}
|
|
7943
8142
|
const agentWarnings = drainAgentWarnings();
|
|
7944
8143
|
if (agentWarnings.length > 0) {
|
|
8144
|
+
turnHasActionWarnings = true;
|
|
7945
8145
|
result = `${result}\n\n${formatAgentWarningsForToolResult(agentWarnings)}`;
|
|
7946
8146
|
}
|
|
8147
|
+
const actionSettled = Boolean(!isError &&
|
|
8148
|
+
!toolResultLost &&
|
|
8149
|
+
!isSignalAborted(signal) &&
|
|
8150
|
+
settlementOnReturn &&
|
|
8151
|
+
!requiresNextAction &&
|
|
8152
|
+
callEffect !== "unknown" &&
|
|
8153
|
+
toolCallHasCleanStop &&
|
|
8154
|
+
agentWarnings.length === 0 &&
|
|
8155
|
+
!writeReceiptGuard(receipt ? [{ tool: toolCall.name, ...receipt }] : [], false));
|
|
7947
8156
|
const resolvedChatUI = actionChatUIForResult(toolCall.name, actionEntry, toolCall.input, chatUIResult, isError);
|
|
7948
8157
|
// A write refreshes other sessions, including one that failed after
|
|
7949
8158
|
// it ran: its changes are applied all the same.
|
|
@@ -7989,6 +8198,15 @@ export async function runAgentLoop(opts) {
|
|
|
7989
8198
|
? { errorCode: toolErrorCode }
|
|
7990
8199
|
: {}),
|
|
7991
8200
|
completedSideEffect: callEffect === "committed",
|
|
8201
|
+
...(!isPrivateCall() && actionSettled
|
|
8202
|
+
? {
|
|
8203
|
+
actionSettled: true,
|
|
8204
|
+
settledOnCleanToolStop: true,
|
|
8205
|
+
}
|
|
8206
|
+
: {}),
|
|
8207
|
+
...(agentWarnings.length > 0
|
|
8208
|
+
? { hasAgentWarnings: true }
|
|
8209
|
+
: {}),
|
|
7992
8210
|
...(isPrivateCall() ? {} : { artifacts: toolArtifacts }),
|
|
7993
8211
|
...(!isPrivateCall() && receipt ? { receipt } : {}),
|
|
7994
8212
|
...(!isPrivateCall() && mcpApp ? { mcpApp } : {}),
|
|
@@ -8033,9 +8251,21 @@ export async function runAgentLoop(opts) {
|
|
|
8033
8251
|
...(fileMutation ? { fileMutation } : {}),
|
|
8034
8252
|
...(toolArtifacts.length > 0 ? { artifacts: toolArtifacts } : {}),
|
|
8035
8253
|
...(receipt ? { receipt } : {}),
|
|
8254
|
+
...(actionSettled
|
|
8255
|
+
? {
|
|
8256
|
+
actionSettled: true,
|
|
8257
|
+
settledOnCleanToolStop: true,
|
|
8258
|
+
}
|
|
8259
|
+
: {}),
|
|
8260
|
+
...(agentWarnings.length > 0
|
|
8261
|
+
? { hasAgentWarnings: true }
|
|
8262
|
+
: {}),
|
|
8036
8263
|
});
|
|
8037
8264
|
toolDonePersistenceFailed = false;
|
|
8038
8265
|
recordToolResult(result, isError, toolArtifacts, receipt);
|
|
8266
|
+
if (actionSettled) {
|
|
8267
|
+
settledToolCalls.add(originalToolCall);
|
|
8268
|
+
}
|
|
8039
8269
|
if (isError) {
|
|
8040
8270
|
if (callEffect === "committed") {
|
|
8041
8271
|
resetAfterSuccessfulWrite(toolCall.name, toolCall.input);
|
|
@@ -8222,7 +8452,15 @@ export async function runAgentLoop(opts) {
|
|
|
8222
8452
|
: toolResultParts,
|
|
8223
8453
|
});
|
|
8224
8454
|
if (!actionStopRequested()) {
|
|
8225
|
-
await deliverSteeredMessagesAtToolBoundary(toolResultParts);
|
|
8455
|
+
const delivered = await deliverSteeredMessagesAtToolBoundary(toolResultParts);
|
|
8456
|
+
settlementCandidate =
|
|
8457
|
+
delivered === 0 &&
|
|
8458
|
+
toolCallHasCleanStop &&
|
|
8459
|
+
followUpResults.length === 0 &&
|
|
8460
|
+
toolCallParts.length > 0 &&
|
|
8461
|
+
toolCallParts.every((call) => settledToolCalls.has(call)) &&
|
|
8462
|
+
!turnHasActionWarnings &&
|
|
8463
|
+
!toolResultHistory.some((result) => result.isError);
|
|
8226
8464
|
}
|
|
8227
8465
|
if (requestedActionStop) {
|
|
8228
8466
|
const stop = requestedActionStop;
|
|
@@ -8294,14 +8532,16 @@ export async function runAgentLoop(opts) {
|
|
|
8294
8532
|
send({ type: "suggestions", suggestions: completedFollowUpSuggestions });
|
|
8295
8533
|
send({
|
|
8296
8534
|
type: "done",
|
|
8297
|
-
...(
|
|
8298
|
-
? {
|
|
8299
|
-
|
|
8300
|
-
|
|
8301
|
-
|
|
8302
|
-
|
|
8303
|
-
|
|
8304
|
-
|
|
8535
|
+
...(settledEmptyFinal
|
|
8536
|
+
? { reason: "action_settled" }
|
|
8537
|
+
: loopBreakerStopped
|
|
8538
|
+
? {
|
|
8539
|
+
reason: "loop_breaker",
|
|
8540
|
+
...(loopBreakerCloseout
|
|
8541
|
+
? { message: loopBreakerCloseout.message }
|
|
8542
|
+
: {}),
|
|
8543
|
+
}
|
|
8544
|
+
: {}),
|
|
8305
8545
|
});
|
|
8306
8546
|
if (opts.threadId) {
|
|
8307
8547
|
void retainRequestDbPoolScope(clearLedgerForThread(opts.threadId).catch(() => { }));
|
|
@@ -87,6 +87,12 @@ export async function foldReapedRunIntoThread(runId, terminalError) {
|
|
|
87
87
|
if (!thread)
|
|
88
88
|
return "superseded";
|
|
89
89
|
const repo = readThreadRepository(run.threadId, run.turnId, thread.threadData);
|
|
90
|
+
// A thread with no messages has no tail for the retry check below to wait
|
|
91
|
+
// on, so retrying would defer forever and fail every sweep.
|
|
92
|
+
if (repo.messages.length === 0) {
|
|
93
|
+
console.warn(`[agent-chat] dropping stopped run ${runId}: thread ${run.threadId} has no messages (turn ${run.turnId})`);
|
|
94
|
+
return "superseded";
|
|
95
|
+
}
|
|
90
96
|
const folded = foldAssistantTurnWithSteers(repo, assistant, steered, {
|
|
91
97
|
runId,
|
|
92
98
|
turnId: run.turnId,
|
|
@@ -320,6 +320,10 @@ function isBatchableRunEvent(event) {
|
|
|
320
320
|
export function endsAfterToolResultWithoutAssistantFinal(run, privateCompletedUiTools = new Set()) {
|
|
321
321
|
let toolResultAfterLastAssistantText = false;
|
|
322
322
|
for (const { event, seq } of run.events) {
|
|
323
|
+
if (event.type === "done" && event.reason === "action_settled") {
|
|
324
|
+
toolResultAfterLastAssistantText = false;
|
|
325
|
+
continue;
|
|
326
|
+
}
|
|
323
327
|
if (event.type === "text" && event.text.trim().length > 0) {
|
|
324
328
|
toolResultAfterLastAssistantText = false;
|
|
325
329
|
continue;
|
|
@@ -1907,6 +1911,8 @@ export function startRun(runId, threadId, runFn, onComplete, options) {
|
|
|
1907
1911
|
const shouldAutoContinueAfterUnfinishedTurn = finalStatus === "completed" &&
|
|
1908
1912
|
!checkpointStatusReadFailed &&
|
|
1909
1913
|
checkpointUnknownStatus === null &&
|
|
1914
|
+
!(terminalEventForCompletion?.event.type === "done" &&
|
|
1915
|
+
terminalEventForCompletion.event.reason === "action_settled") &&
|
|
1910
1916
|
(endsAfterToolResultWithoutAssistantFinal(runForCompletionChecks, privateCompletedUiTools) ||
|
|
1911
1917
|
endsDuringActionPreparation(runForCompletionChecks)) &&
|
|
1912
1918
|
(!terminalEventForCompletion ||
|
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
import type { ActionChatUIConfig } from "../action-ui.js";
|
|
2
|
+
import type { WriteReceipt } from "../action.js";
|
|
2
3
|
import { type ArtifactReceipt } from "../artifacts/detect.js";
|
|
3
4
|
import type { AgentMcpAppPayload } from "../mcp-client/app-result.js";
|
|
4
5
|
import type { EngineMessage, EngineToolCallPart, EngineToolResultPart } from "./engine/types.js";
|
|
@@ -16,6 +17,9 @@ export interface ToolCallJournalEntry {
|
|
|
16
17
|
/** The call failed after its side effect completed. */
|
|
17
18
|
isError?: true;
|
|
18
19
|
errorCode?: string;
|
|
20
|
+
receipt?: WriteReceipt;
|
|
21
|
+
actionSettled?: true;
|
|
22
|
+
settledOnCleanToolStop?: true;
|
|
19
23
|
/** The call recorded a completed side effect, its own or a nested call's. */
|
|
20
24
|
committed?: true;
|
|
21
25
|
/** Exact identity of the arguments the call ran with, defaults applied. */
|