@agent-native/core 0.206.0 → 0.206.1-nightly-20261009183700
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/agent/default-agent-engine.js +25 -0
- package/dist/agent/durable-background.d.ts +2 -0
- package/dist/agent/durable-background.js +2 -0
- package/dist/agent/engine/error-detail.d.ts +1 -0
- package/dist/agent/engine/error-detail.js +6 -1
- package/dist/agent/engine/translate-anthropic.d.ts +9 -0
- package/dist/agent/engine/translate-anthropic.js +18 -3
- package/dist/agent/production-agent.d.ts +4 -4
- package/dist/agent/production-agent.js +78 -74
- package/dist/agent/run-store.js +15 -3
- package/dist/agent/thread-data-builder.js +1 -2
- package/dist/automation/index.d.ts +1 -1
- package/dist/chat-threads/store.js +6 -2
- package/dist/client/chat/agentkit-agent-native.js +11 -10
- package/dist/client/chat/agentkit-protocol.js +5 -1
- package/dist/client/chat/runtime.d.ts +3 -1
- package/dist/client/chat/runtime.js +101 -43
- package/dist/client/use-action.d.ts +1 -0
- package/dist/client/use-action.js +2 -1
- package/dist/collab/routes.d.ts +1 -1
- package/dist/collab/struct-routes.d.ts +1 -1
- package/dist/deploy/build.js +1 -1
- package/dist/deploy/workspace-deploy.js +65 -6
- package/dist/file-upload/actions/upload-image.d.ts +3 -3
- package/dist/file-upload/registry.d.ts +7 -1
- package/dist/file-upload/registry.js +25 -7
- package/dist/mcp/build-server.js +64 -26
- package/dist/mcp/screen-memory-stdio.d.ts +7 -7
- package/dist/notifications/routes.d.ts +1 -1
- package/dist/observability/metrics.d.ts +5 -0
- package/dist/observability/metrics.js +62 -7
- package/dist/observability/routes.d.ts +4 -4
- package/dist/org/handlers.d.ts +1 -1
- package/dist/progress/routes.d.ts +1 -1
- package/dist/provider-api/actions/custom-provider-registration.d.ts +4 -4
- package/dist/resources/handlers.d.ts +1 -1
- package/dist/secrets/index.d.ts +1 -1
- package/dist/secrets/index.js +1 -1
- package/dist/secrets/storage.d.ts +1 -0
- package/dist/secrets/storage.js +8 -0
- package/dist/server/action-routes.js +1 -1
- package/dist/server/agent-engine-api-key-route.d.ts +2 -1
- package/dist/server/agent-engine-api-key-route.js +66 -2
- package/dist/server/agent-engine-default-model-route.d.ts +2 -2
- package/dist/server/agent-engine-ollama-models-route.d.ts +2 -2
- package/dist/server/agent-run-context.js +21 -1
- package/dist/server/agent-teams-run-queue.js +2 -1
- package/dist/server/auth.js +1 -0
- package/dist/server/core-routes-plugin.d.ts +1 -1
- package/dist/server/core-routes-plugin.js +12 -0
- package/dist/server/http-response-telemetry.js +37 -8
- package/dist/server/realtime-token.d.ts +1 -1
- package/dist/shared/inline-bytes.d.ts +29 -0
- package/dist/shared/inline-bytes.js +170 -0
- package/dist/shared/mcp-embed-headers.d.ts +1 -1
- package/dist/shared/mcp-embed-headers.js +1 -1
- package/dist/sharing/actions/list-resource-shares.d.ts +1 -1
- package/dist/testing.d.ts +1 -0
- package/dist/testing.js +1 -0
- package/dist/tracking/registry.d.ts +3 -2
- package/dist/tracking/registry.js +12 -3
- package/dist/triggers/actions/manage-automation.d.ts +3 -3
- package/dist/vite/client.js +6 -1
- package/docs/content/observability.mdx +15 -10
- package/docs/content/tracking.mdx +1 -1
- package/package.json +3 -3
|
@@ -11,6 +11,7 @@ import { getRequestOrgId, getRequestUserEmail, } from "../server/request-context
|
|
|
11
11
|
import { getOrgSetting, putOrgSetting } from "../settings/org-settings.js";
|
|
12
12
|
import { getSetting } from "../settings/store.js";
|
|
13
13
|
import { getUserSetting, putUserSetting } from "../settings/user-settings.js";
|
|
14
|
+
import { track } from "../tracking/registry.js";
|
|
14
15
|
import { canUpdateAgentAppModelDefaultSettings } from "./app-model-defaults.js";
|
|
15
16
|
export const DEFAULT_AGENT_ENGINE_SETTING_KEY = "agent-engine";
|
|
16
17
|
/** Audit target type for default-model changes and refused attempts. */
|
|
@@ -98,6 +99,7 @@ async function writeScopedRow(authority, value) {
|
|
|
98
99
|
* stores only that scope and records the change.
|
|
99
100
|
*/
|
|
100
101
|
export async function writeDefaultAgentEngineSelection(authority, selection, meta) {
|
|
102
|
+
const previous = await readDefaultAgentEngineSettingDetailed(authority);
|
|
101
103
|
await writeScopedRow(authority, {
|
|
102
104
|
engine: selection.engine,
|
|
103
105
|
model: selection.model,
|
|
@@ -112,12 +114,25 @@ export async function writeDefaultAgentEngineSelection(authority, selection, met
|
|
|
112
114
|
operation: "set",
|
|
113
115
|
selection,
|
|
114
116
|
});
|
|
117
|
+
track("llm_default_model_changed", {
|
|
118
|
+
engine: selection.engine,
|
|
119
|
+
model: selection.model,
|
|
120
|
+
...(typeof previous.value?.engine === "string"
|
|
121
|
+
? { previous_engine: previous.value.engine }
|
|
122
|
+
: {}),
|
|
123
|
+
...(typeof previous.value?.model === "string"
|
|
124
|
+
? { previous_model: previous.value.model }
|
|
125
|
+
: {}),
|
|
126
|
+
scope: authority.scope,
|
|
127
|
+
operation: "set",
|
|
128
|
+
}, { userId: authority.userEmail });
|
|
115
129
|
}
|
|
116
130
|
/**
|
|
117
131
|
* Clear the default for the authority's scope. Writes a cleared marker rather
|
|
118
132
|
* than deleting the row; see {@link readDefaultAgentEngineSettingDetailed}.
|
|
119
133
|
*/
|
|
120
134
|
export async function clearDefaultAgentEngineSelection(authority, meta) {
|
|
135
|
+
const previous = await readDefaultAgentEngineSettingDetailed(authority);
|
|
121
136
|
await writeScopedRow(authority, {
|
|
122
137
|
cleared: true,
|
|
123
138
|
updatedAt: Date.now(),
|
|
@@ -130,6 +145,16 @@ export async function clearDefaultAgentEngineSelection(authority, meta) {
|
|
|
130
145
|
status: "success",
|
|
131
146
|
operation: "clear",
|
|
132
147
|
});
|
|
148
|
+
track("llm_default_model_changed", {
|
|
149
|
+
...(typeof previous.value?.engine === "string"
|
|
150
|
+
? { previous_engine: previous.value.engine }
|
|
151
|
+
: {}),
|
|
152
|
+
...(typeof previous.value?.model === "string"
|
|
153
|
+
? { previous_model: previous.value.model }
|
|
154
|
+
: {}),
|
|
155
|
+
scope: authority.scope,
|
|
156
|
+
operation: "reset",
|
|
157
|
+
}, { userId: authority.userEmail });
|
|
133
158
|
}
|
|
134
159
|
/** Record a refused change so owners and admins can see who tried. */
|
|
135
160
|
export async function recordDefaultAgentEngineRefusal(ctx, authority, operation, meta, selection) {
|
|
@@ -51,6 +51,8 @@ export declare const AGENT_CHAT_DURABLE_BACKGROUND_ENV = "AGENT_CHAT_DURABLE_BAC
|
|
|
51
51
|
* verifies the HMAC token before invoking the handler.
|
|
52
52
|
*/
|
|
53
53
|
export declare const AGENT_CHAT_BACKGROUND_RUN_FIELD = "__backgroundRun";
|
|
54
|
+
/** Analytics-only source context captured before the run leaves its browser request. */
|
|
55
|
+
export declare const AGENT_CHAT_BROWSER_SESSION_ID_FIELD = "__agentNativeBrowserSessionId";
|
|
54
56
|
export declare function isHostedRuntimeForDurableBackground(): boolean;
|
|
55
57
|
export declare function isNetlifyHostedRuntimeForDurableBackground(): boolean;
|
|
56
58
|
export declare function isInBackgroundFunctionRuntime(): boolean;
|
|
@@ -131,6 +131,8 @@ export const AGENT_CHAT_DURABLE_BACKGROUND_ENV = "AGENT_CHAT_DURABLE_BACKGROUND"
|
|
|
131
131
|
* verifies the HMAC token before invoking the handler.
|
|
132
132
|
*/
|
|
133
133
|
export const AGENT_CHAT_BACKGROUND_RUN_FIELD = "__backgroundRun";
|
|
134
|
+
/** Analytics-only source context captured before the run leaves its browser request. */
|
|
135
|
+
export const AGENT_CHAT_BROWSER_SESSION_ID_FIELD = "__agentNativeBrowserSessionId";
|
|
134
136
|
export function isHostedRuntimeForDurableBackground() {
|
|
135
137
|
if (process.env.NETLIFY_LOCAL === "true")
|
|
136
138
|
return false;
|
|
@@ -16,6 +16,7 @@ export interface ProviderErrorClassification {
|
|
|
16
16
|
providerRetryable?: boolean;
|
|
17
17
|
retryAfterMs?: number;
|
|
18
18
|
}
|
|
19
|
+
export declare function isInvalidAttachmentProviderMessage(message: string): boolean;
|
|
19
20
|
export declare function extractRetryAfterMs(err: unknown): number | undefined;
|
|
20
21
|
export declare function classifyProviderError(err: unknown, timedOut?: boolean): ProviderErrorClassification;
|
|
21
22
|
/**
|
|
@@ -90,7 +90,12 @@ export function isContextOverflowCode(code) {
|
|
|
90
90
|
const MAX_RETRY_AFTER_MS = 60_000;
|
|
91
91
|
const PROVIDER_ATTACHMENT_FIELD_PATTERN = /\b(?:images?|files?|attachments?|media)[_-](?:url|data|source|type|format|size|input|content)\b|\b(?:images?|files?)\s+(?:url|data|source|type|format|size|content)\b|\b(?:url|data|source|type|format|size|input|content)\b.{0,40}\b(?:images?|files?|attachments?|media)\b|\bmime[\s_-]?type\b/gi;
|
|
92
92
|
const PROVIDER_ATTACHMENT_REJECTION_PATTERN = /\b(?:invalid|unsupported|not supported|does not support|too large|too big|too long|exceeds?|over(?:sized|size| the)? limit|maximum (?:allowed )?(?:size|length)|(?:size|length) limit|malformed)\b/i;
|
|
93
|
-
|
|
93
|
+
// Phrasings Anthropic, OpenAI, Google, and the Builder gateway use for an
|
|
94
|
+
// image they refused. Specific enough to classify without a status code.
|
|
95
|
+
const PROVIDER_IMAGE_REJECTION_PATTERN = /\bimage exceeds \d+(?:\.\d+)?\s*mb\b|\b(?:could not|unable to) process (?:the )?(?:input )?image\b|\binvalid image\b|\bunsupported image\b|\bimage (?:is )?too (?:large|big)\b|\bprovided image is not valid\b/i;
|
|
96
|
+
export function isInvalidAttachmentProviderMessage(message) {
|
|
97
|
+
if (PROVIDER_IMAGE_REJECTION_PATTERN.test(message))
|
|
98
|
+
return true;
|
|
94
99
|
for (const match of message.matchAll(PROVIDER_ATTACHMENT_FIELD_PATTERN)) {
|
|
95
100
|
const index = match.index ?? 0;
|
|
96
101
|
const start = Math.max(0, index - 100);
|
|
@@ -10,6 +10,15 @@ export declare function unmatchedToolResultReplayText(part: {
|
|
|
10
10
|
content: unknown;
|
|
11
11
|
isError?: boolean;
|
|
12
12
|
}): string;
|
|
13
|
+
/**
|
|
14
|
+
* Identity of a synthetic interrupted tool result in engine messages, thread
|
|
15
|
+
* data, and the client (`client/sse-event-processor.ts` matches it verbatim).
|
|
16
|
+
* The model never sees it bare: replay rewrites it to an error whose outcome
|
|
17
|
+
* is unknown, or the model treats it as "did not run" and repeats a write that
|
|
18
|
+
* may have landed.
|
|
19
|
+
*/
|
|
20
|
+
export declare const INTERRUPTED_TOOL_RESULT_MARKER = "Interrupted before this tool returned a result.";
|
|
21
|
+
export declare function isInterruptedToolResult(content: unknown): boolean;
|
|
13
22
|
export declare function backfillEngineMessagesToolResults(messages: EngineMessage[]): EngineMessage[];
|
|
14
23
|
export declare function engineMessageToAnthropic(msg: EngineMessage, opts?: {
|
|
15
24
|
builderGateway?: boolean;
|
|
@@ -74,13 +74,27 @@ export function unmatchedToolResultReplayText(part) {
|
|
|
74
74
|
const err = part.isError ? " isError=true" : "";
|
|
75
75
|
return `${UNMATCHED_TOOL_RESULT_REPLAY_PREFIX} [tool_use_id=${part.toolCallId}${err}] ${body}`;
|
|
76
76
|
}
|
|
77
|
+
/**
|
|
78
|
+
* Identity of a synthetic interrupted tool result in engine messages, thread
|
|
79
|
+
* data, and the client (`client/sse-event-processor.ts` matches it verbatim).
|
|
80
|
+
* The model never sees it bare: replay rewrites it to an error whose outcome
|
|
81
|
+
* is unknown, or the model treats it as "did not run" and repeats a write that
|
|
82
|
+
* may have landed.
|
|
83
|
+
*/
|
|
84
|
+
export const INTERRUPTED_TOOL_RESULT_MARKER = "Interrupted before this tool returned a result.";
|
|
85
|
+
const INTERRUPTED_TOOL_RESULT_FOR_MODEL = `${INTERRUPTED_TOOL_RESULT_MARKER} Its outcome is UNKNOWN: the tool may have run. Verify the current state before repeating any write.`;
|
|
86
|
+
export function isInterruptedToolResult(content) {
|
|
87
|
+
return (typeof content === "string" &&
|
|
88
|
+
content.startsWith(INTERRUPTED_TOOL_RESULT_MARKER));
|
|
89
|
+
}
|
|
77
90
|
function interruptedToolResultPart(part) {
|
|
78
91
|
return {
|
|
79
92
|
type: "tool-result",
|
|
80
93
|
toolCallId: part.id,
|
|
81
94
|
toolName: part.name,
|
|
82
95
|
toolInput: stringifyToolUseInputForGateway(part.input),
|
|
83
|
-
content:
|
|
96
|
+
content: INTERRUPTED_TOOL_RESULT_FOR_MODEL,
|
|
97
|
+
isError: true,
|
|
84
98
|
};
|
|
85
99
|
}
|
|
86
100
|
export function backfillEngineMessagesToolResults(messages) {
|
|
@@ -167,13 +181,14 @@ export function backfillEngineMessagesToolResults(messages) {
|
|
|
167
181
|
const toolInput = typeof part.toolInput === "string" && part.toolInput.length > 0
|
|
168
182
|
? part.toolInput
|
|
169
183
|
: stringifyToolUseInputForGateway(pendingLookup?.input ?? lookup?.input);
|
|
184
|
+
const interrupted = part.content === INTERRUPTED_TOOL_RESULT_MARKER;
|
|
170
185
|
const filled = {
|
|
171
186
|
type: "tool-result",
|
|
172
187
|
toolCallId: part.toolCallId,
|
|
173
188
|
toolName,
|
|
174
189
|
toolInput,
|
|
175
|
-
content: part.content,
|
|
176
|
-
...(part.isError ? { isError: true } : {}),
|
|
190
|
+
content: interrupted ? INTERRUPTED_TOOL_RESULT_FOR_MODEL : part.content,
|
|
191
|
+
...(part.isError || interrupted ? { isError: true } : {}),
|
|
177
192
|
...(part.images && part.images.length > 0
|
|
178
193
|
? { images: part.images }
|
|
179
194
|
: {}),
|
|
@@ -10,6 +10,7 @@ import { type BuilderGatewayAuth } from "../server/credential-provider.js";
|
|
|
10
10
|
import { fireInternalDispatch } from "../server/self-dispatch.js";
|
|
11
11
|
import { type RefusedTurnRetryContext } from "../shared/agent-chat-run-not-started.js";
|
|
12
12
|
import { type ContextStatus } from "../shared/context-status.js";
|
|
13
|
+
import { DurableAttachmentReferenceRequiredError } from "../shared/inline-bytes.js";
|
|
13
14
|
import { type ReasoningEffort } from "../shared/reasoning-effort.js";
|
|
14
15
|
import type { TrackingMeta } from "../tracking/registry.js";
|
|
15
16
|
import { type ResolveEngineConfig } from "./engine/index.js";
|
|
@@ -357,10 +358,7 @@ export declare function runCompletionCallbackWithDatabaseRetry(callback: () => v
|
|
|
357
358
|
}): Promise<void>;
|
|
358
359
|
export declare function resolveSourceSweepToolCallThreshold(): number;
|
|
359
360
|
declare function generateRunId(): string;
|
|
360
|
-
export
|
|
361
|
-
readonly code = "attachment_storage_required";
|
|
362
|
-
constructor();
|
|
363
|
-
}
|
|
361
|
+
export { DurableAttachmentReferenceRequiredError };
|
|
364
362
|
export declare function serializeDurableDispatchPayload(body: Record<string, unknown>): string;
|
|
365
363
|
/** Check if an error is transient and should be retried
|
|
366
364
|
* @internal exported for unit tests only
|
|
@@ -778,6 +776,8 @@ export declare function resolvePriorContinuationState(backgroundRunMarker: Recor
|
|
|
778
776
|
};
|
|
779
777
|
export declare function resolvePriorContinuationReason(backgroundRunMarker: Record<string, unknown> | null | undefined, body: Record<string, unknown>): string | undefined;
|
|
780
778
|
declare function readTurnStartedAt(threadId: string, turnId: string): Promise<number | null>;
|
|
779
|
+
/** @internal exported for unit tests only */
|
|
780
|
+
export declare function queuedPromotionAttachments(attachments: unknown[]): AgentChatAttachment[];
|
|
781
781
|
declare function emitRunText(run: ActiveRun, text: string): Promise<void>;
|
|
782
782
|
export interface ChainServerDrivenContinuationDeps {
|
|
783
783
|
countRunsForTurn?: typeof countRunsForTurn;
|
|
@@ -45,6 +45,7 @@ import { ANALYTICS_CLIENT_PLATFORM_BODY_FIELD } from "../shared/analytics-platfo
|
|
|
45
45
|
import { isContextDegraded, preloadedContextUnavailableNote, readReportedContextStatus, screenContextUnavailableNote, SELECTION_CONTEXT_UNAVAILABLE_NOTE, worstContextStatus, } from "../shared/context-status.js";
|
|
46
46
|
import { parseBase64DataUrl } from "../shared/data-url.js";
|
|
47
47
|
import { stripDiagnosticSnippets } from "../shared/diagnostic-snippet.js";
|
|
48
|
+
import { DurableAttachmentReferenceRequiredError, stripInlineBytes, } from "../shared/inline-bytes.js";
|
|
48
49
|
import { isReasoningEffort, normalizeReasoningEffortForRequest, stepDownReasoningEffort, } from "../shared/reasoning-effort.js";
|
|
49
50
|
import { SYNTHETIC_TRAFFIC_BETA_E2E, SYNTHETIC_TRAFFIC_HEADER, } from "../shared/test-traffic.js";
|
|
50
51
|
import { actionPreparationContinuationNote } from "./action-continuation-guidance.js";
|
|
@@ -53,17 +54,17 @@ import { CONTINUE_UNAVAILABLE_CODE, } from "./auto-continue.js";
|
|
|
53
54
|
import { clipHead } from "./clip-text.js";
|
|
54
55
|
import { connectionRequiredMessage, priorConnectionContextNote, resolvePriorConnectionNote, } from "./connection-required-note.js";
|
|
55
56
|
import { buildSystemManifestSections, readContextXraySystemSections, } from "./context-xray/manifest.js";
|
|
56
|
-
import { AGENT_CHAT_BACKGROUND_RUN_FIELD, AGENT_CHAT_PROCESS_RUN_PATH, backgroundRuntimeDiagnosticDetail, dispatchPathTargetsNetlifyBackgroundFunction, isAgentChatDurableBackgroundEnabled, isAgentChatForegroundSelfChainEnabled, isInBackgroundFunctionRuntime, resolveAgentChatProcessRunDispatchPath, shouldUseBackgroundFunctionTimeoutForWorker, } from "./durable-background.js";
|
|
57
|
+
import { AGENT_CHAT_BROWSER_SESSION_ID_FIELD, AGENT_CHAT_BACKGROUND_RUN_FIELD, AGENT_CHAT_PROCESS_RUN_PATH, backgroundRuntimeDiagnosticDetail, dispatchPathTargetsNetlifyBackgroundFunction, isAgentChatDurableBackgroundEnabled, isAgentChatForegroundSelfChainEnabled, isInBackgroundFunctionRuntime, resolveAgentChatProcessRunDispatchPath, shouldUseBackgroundFunctionTimeoutForWorker, } from "./durable-background.js";
|
|
57
58
|
import { applyContextXrayTransformForIteration } from "./engine/context-directives-transform.js";
|
|
58
59
|
import { attemptContinuationDispatch } from "./engine/continuation-dispatch-retry.js";
|
|
59
60
|
import { formatLlmCredentialErrorMessage, LLM_MISSING_CREDENTIALS_ERROR_CODE, LLM_MISSING_CREDENTIALS_MESSAGE, userFacingLlmCredentialError, } from "./engine/credential-errors.js";
|
|
60
|
-
import { BUILDER_GATEWAY_INTERNAL_ERROR_CODE, isContextOverflowCode, isContextOverflowMessage, isProviderConnectionErrorMessage, PROVIDER_RATE_LIMITED_ERROR_CODE, PROVIDER_TRANSIENT_REJECTION_ERROR_CODE, } from "./engine/error-detail.js";
|
|
61
|
+
import { BUILDER_GATEWAY_INTERNAL_ERROR_CODE, isContextOverflowCode, isContextOverflowMessage, isInvalidAttachmentProviderMessage, isProviderConnectionErrorMessage, PROVIDER_RATE_LIMITED_ERROR_CODE, PROVIDER_TRANSIENT_REJECTION_ERROR_CODE, } from "./engine/error-detail.js";
|
|
61
62
|
import { resolveEngine, explicitEngineName, registerBuiltinEngines, getStoredModelForEngine, normalizeModelForEngine, isResolvedEngineUsableForRequest, } from "./engine/index.js";
|
|
62
63
|
import { limitProviderTools, MAX_PROVIDER_TOOLS, } from "./engine/limit-provider-tools.js";
|
|
63
64
|
import { resolveEmptyResponseRetryMaxOutputTokens, resolveMainChatMaxOutputTokens, resolveMaxOutputTokensForEngine, } from "./engine/output-tokens.js";
|
|
64
65
|
import { PROVIDER_TO_ENV } from "./engine/provider-env-vars.js";
|
|
65
66
|
import { JOURNALED_TOOL_REPLAY_PREFIX, RECOVERED_TOOL_REPLAY_PREFIX, loadedSkillPagesContext, loadPriorTurnToolCallJournal, seedRepeatedToolErrorCountsFromJournal, seedRepeatedToolCallCountsFromJournal, } from "./engine/tool-call-journal-seed.js";
|
|
66
|
-
import { backfillEngineMessagesToolResults, stringifyToolUseInputForGateway, unmatchedToolResultReplayText, } from "./engine/translate-anthropic.js";
|
|
67
|
+
import { backfillEngineMessagesToolResults, INTERRUPTED_TOOL_RESULT_MARKER, isInterruptedToolResult, stringifyToolUseInputForGateway, unmatchedToolResultReplayText, } from "./engine/translate-anthropic.js";
|
|
67
68
|
import { EngineError } from "./engine/types.js";
|
|
68
69
|
import { FOLLOW_UP_SUGGESTIONS_COMPLETION_SYSTEM_PROMPT, FOLLOW_UP_SUGGESTIONS_INSTRUCTION, FOLLOW_UP_SUGGESTIONS_MAX_OUTPUT_TOKENS, FOLLOW_UP_SUGGESTIONS_TOOL_NAME, buildFollowUpCompletionMessages, followUpSuggestionsTool, identifyFollowUpSuggestions, parseFollowUpSuggestions, } from "./follow-up-suggestions.js";
|
|
69
70
|
import { filterHostedHarnessToolNames, normalizeHostedHarnessRuntime, } from "./harness/hosted.js";
|
|
@@ -1139,59 +1140,9 @@ function limitInventoryLines(lines, label) {
|
|
|
1139
1140
|
function generateRunId() {
|
|
1140
1141
|
return `run-${Date.now()}-${Math.random().toString(36).slice(2, 8)}`;
|
|
1141
1142
|
}
|
|
1142
|
-
export
|
|
1143
|
-
code = "attachment_storage_required";
|
|
1144
|
-
constructor() {
|
|
1145
|
-
super("An attachment has inline bytes but no durable file URL. Configure file storage and retry this background run.");
|
|
1146
|
-
this.name = "DurableAttachmentReferenceRequiredError";
|
|
1147
|
-
}
|
|
1148
|
-
}
|
|
1149
|
-
function isDataUrlReference(value) {
|
|
1150
|
-
return typeof value === "string" && /^\s*data:/i.test(value);
|
|
1151
|
-
}
|
|
1152
|
-
function hasDurableAttachmentUrl(value) {
|
|
1153
|
-
return (typeof value === "string" &&
|
|
1154
|
-
value.trim().length > 0 &&
|
|
1155
|
-
!isDataUrlReference(value));
|
|
1156
|
-
}
|
|
1157
|
-
function sanitizeDurableAttachment(value, attachmentContext = false) {
|
|
1158
|
-
if (Array.isArray(value)) {
|
|
1159
|
-
return value.map((item) => sanitizeDurableAttachment(item));
|
|
1160
|
-
}
|
|
1161
|
-
if (!value || typeof value !== "object")
|
|
1162
|
-
return value;
|
|
1163
|
-
const item = value;
|
|
1164
|
-
const isAttachment = attachmentContext ||
|
|
1165
|
-
item.type === "image" ||
|
|
1166
|
-
item.type === "file" ||
|
|
1167
|
-
item.type === "document";
|
|
1168
|
-
if (isAttachment &&
|
|
1169
|
-
(isDataUrlReference(item.url) || isDataUrlReference(item.referenceUrl))) {
|
|
1170
|
-
throw new DurableAttachmentReferenceRequiredError();
|
|
1171
|
-
}
|
|
1172
|
-
const hasInlineData = isAttachment && item.data !== undefined;
|
|
1173
|
-
if (hasInlineData &&
|
|
1174
|
-
!hasDurableAttachmentUrl(item.url) &&
|
|
1175
|
-
!hasDurableAttachmentUrl(item.referenceUrl)) {
|
|
1176
|
-
throw new DurableAttachmentReferenceRequiredError();
|
|
1177
|
-
}
|
|
1178
|
-
const sanitized = {};
|
|
1179
|
-
for (const [key, child] of Object.entries(item)) {
|
|
1180
|
-
if (key === "data" && hasInlineData)
|
|
1181
|
-
continue;
|
|
1182
|
-
if (key === "attachments" || key === "requestAttachments") {
|
|
1183
|
-
sanitized[key] = Array.isArray(child)
|
|
1184
|
-
? child.map((attachment) => sanitizeDurableAttachment(attachment, true))
|
|
1185
|
-
: sanitizeDurableAttachment(child);
|
|
1186
|
-
}
|
|
1187
|
-
else {
|
|
1188
|
-
sanitized[key] = sanitizeDurableAttachment(child);
|
|
1189
|
-
}
|
|
1190
|
-
}
|
|
1191
|
-
return sanitized;
|
|
1192
|
-
}
|
|
1143
|
+
export { DurableAttachmentReferenceRequiredError };
|
|
1193
1144
|
export function serializeDurableDispatchPayload(body) {
|
|
1194
|
-
const payload = JSON.stringify(
|
|
1145
|
+
const payload = JSON.stringify(stripInlineBytes(body, "reject"));
|
|
1195
1146
|
if (typeof payload !== "string") {
|
|
1196
1147
|
throw new TypeError("Durable dispatch payload could not be serialized");
|
|
1197
1148
|
}
|
|
@@ -1550,6 +1501,51 @@ export function buildUserContentWithAttachments(opts) {
|
|
|
1550
1501
|
});
|
|
1551
1502
|
return userContent;
|
|
1552
1503
|
}
|
|
1504
|
+
/** Images reach only a model that reads them; the model learns one was withheld. */
|
|
1505
|
+
function replaceImagesForModelWithoutVision(messages, model) {
|
|
1506
|
+
const note = `<chat-attachment-processing-error code="model-without-vision" model="${escapeAttachmentAttribute(model)}">An attached image was not sent because the active model cannot read images. Do not infer or describe its contents. Tell the user to switch to a vision-capable model if the image matters.</chat-attachment-processing-error>`;
|
|
1507
|
+
return messages.map((message) => message.content.some((part) => part.type === "image")
|
|
1508
|
+
? {
|
|
1509
|
+
...message,
|
|
1510
|
+
content: message.content.map((part) => part.type === "image"
|
|
1511
|
+
? { type: "text", text: note }
|
|
1512
|
+
: part),
|
|
1513
|
+
}
|
|
1514
|
+
: message);
|
|
1515
|
+
}
|
|
1516
|
+
/**
|
|
1517
|
+
* Every engine's stop error passes through here with the request in hand, so
|
|
1518
|
+
* this is where a provider's rejection of an inline attachment becomes the
|
|
1519
|
+
* non-retryable `invalid_attachment`: resending the same bytes fails the same
|
|
1520
|
+
* way, and an `http_400` lets Retry loop on it.
|
|
1521
|
+
*/
|
|
1522
|
+
function engineStopError(event, messages) {
|
|
1523
|
+
const error = event.error ?? "Engine stream error";
|
|
1524
|
+
const parts = messages.flatMap((message) => message.content);
|
|
1525
|
+
const hasInlineAttachment = parts.some((part) => part.type === "image" ||
|
|
1526
|
+
part.type === "file" ||
|
|
1527
|
+
(part.type === "tool-result" && (part.images?.length ?? 0) > 0));
|
|
1528
|
+
const attachmentRejected = event.errorCode === "invalid_attachment" ||
|
|
1529
|
+
(hasInlineAttachment &&
|
|
1530
|
+
(event.statusCode === 413 ||
|
|
1531
|
+
((event.statusCode === undefined ||
|
|
1532
|
+
event.statusCode === 400 ||
|
|
1533
|
+
event.statusCode === 422) &&
|
|
1534
|
+
isInvalidAttachmentProviderMessage(error))));
|
|
1535
|
+
const names = parts.flatMap((part) => part.type === "file" && part.filename ? [`"${part.filename}"`] : []);
|
|
1536
|
+
return new EngineError(attachmentRejected
|
|
1537
|
+
? `The model provider rejected an attachment in this request${names.length > 0 ? ` (${names.join(", ")})` : ""}: ${error}`
|
|
1538
|
+
: error, {
|
|
1539
|
+
errorCode: attachmentRejected ? "invalid_attachment" : event.errorCode,
|
|
1540
|
+
upgradeUrl: event.upgradeUrl,
|
|
1541
|
+
statusCode: event.statusCode,
|
|
1542
|
+
providerRetryable: attachmentRejected ? false : event.providerRetryable,
|
|
1543
|
+
contextOverflow: event.contextOverflow,
|
|
1544
|
+
requestId: event.requestId,
|
|
1545
|
+
requestShape: event.requestShape,
|
|
1546
|
+
retryAfterMs: event.retryAfterMs,
|
|
1547
|
+
});
|
|
1548
|
+
}
|
|
1553
1549
|
export function appendRequestAttachmentContextToResumedHistory(messages, attachments) {
|
|
1554
1550
|
if (!attachments?.length)
|
|
1555
1551
|
return;
|
|
@@ -2448,7 +2444,6 @@ export function isCachedToolResultVisibleInContext(contextMessages, toolCall, ca
|
|
|
2448
2444
|
}
|
|
2449
2445
|
return false;
|
|
2450
2446
|
}
|
|
2451
|
-
const INTERRUPTED_TOOL_RESULT_MARKER = "Interrupted before this tool returned a result.";
|
|
2452
2447
|
const INTERRUPTED_TOOL_LEDGER_RECOVERY_TIMEOUT_MS = 5_000;
|
|
2453
2448
|
const MAX_IDENTICAL_TOOL_ERRORS = 3;
|
|
2454
2449
|
export const MAX_SAME_ERROR_ACROSS_ARGUMENTS = 3;
|
|
@@ -2535,9 +2530,10 @@ function seedWriteToolInterruptionsFromHistory(messages, actions) {
|
|
|
2535
2530
|
const call = pendingToolCalls.get(part.toolCallId);
|
|
2536
2531
|
if (!call)
|
|
2537
2532
|
continue;
|
|
2538
|
-
if (
|
|
2539
|
-
(part.
|
|
2540
|
-
|
|
2533
|
+
if (isInterruptedToolResult(part.content) ||
|
|
2534
|
+
(part.isError === true &&
|
|
2535
|
+
typeof part.content === "string" &&
|
|
2536
|
+
isToolCallTimeoutResult(part.content))) {
|
|
2541
2537
|
const key = toolCallCacheKey(call.name, call.input);
|
|
2542
2538
|
interruptions.set(key, (interruptions.get(key) ?? 0) + 1);
|
|
2543
2539
|
}
|
|
@@ -3930,12 +3926,15 @@ export async function runAgentLoop(opts) {
|
|
|
3930
3926
|
const { thinking: _thinking, ...anthropic } = providerOptions.anthropic;
|
|
3931
3927
|
providerOptions = { ...providerOptions, anthropic };
|
|
3932
3928
|
}
|
|
3929
|
+
const engineMessages = engine.capabilities.vision === false
|
|
3930
|
+
? replaceImagesForModelWithoutVision(contextMessages, model)
|
|
3931
|
+
: contextMessages;
|
|
3933
3932
|
const streamOpts = {
|
|
3934
3933
|
model,
|
|
3935
3934
|
systemPrompt: completingFollowUpSuggestions
|
|
3936
3935
|
? FOLLOW_UP_SUGGESTIONS_COMPLETION_SYSTEM_PROMPT
|
|
3937
3936
|
: continuationSystemPrompt,
|
|
3938
|
-
messages:
|
|
3937
|
+
messages: engineMessages,
|
|
3939
3938
|
tools: loopBreakerCloseout
|
|
3940
3939
|
? []
|
|
3941
3940
|
: completingFollowUpSuggestions
|
|
@@ -3954,7 +3953,7 @@ export async function runAgentLoop(opts) {
|
|
|
3954
3953
|
providerOptions,
|
|
3955
3954
|
};
|
|
3956
3955
|
usage.llmCalls = (usage.llmCalls ?? 0) + 1;
|
|
3957
|
-
notifyModelInputObserver(opts.onModelInput,
|
|
3956
|
+
notifyModelInputObserver(opts.onModelInput, engineMessages);
|
|
3958
3957
|
const eventStream = engine.stream(streamOpts);
|
|
3959
3958
|
let thinkingBuffer = "";
|
|
3960
3959
|
const toolInputNames = new Map();
|
|
@@ -4224,16 +4223,7 @@ export async function runAgentLoop(opts) {
|
|
|
4224
4223
|
else if (event.type === "stop") {
|
|
4225
4224
|
terminalStopReason = event.reason;
|
|
4226
4225
|
if (event.reason === "error") {
|
|
4227
|
-
throw
|
|
4228
|
-
errorCode: event.errorCode,
|
|
4229
|
-
upgradeUrl: event.upgradeUrl,
|
|
4230
|
-
statusCode: event.statusCode,
|
|
4231
|
-
providerRetryable: event.providerRetryable,
|
|
4232
|
-
contextOverflow: event.contextOverflow,
|
|
4233
|
-
requestId: event.requestId,
|
|
4234
|
-
requestShape: event.requestShape,
|
|
4235
|
-
retryAfterMs: event.retryAfterMs,
|
|
4236
|
-
});
|
|
4226
|
+
throw engineStopError(event, engineMessages);
|
|
4237
4227
|
}
|
|
4238
4228
|
}
|
|
4239
4229
|
if (hasNoProgressStalled()) {
|
|
@@ -6500,11 +6490,20 @@ async function readAdmittedQueuedMessagePromotion(opts) {
|
|
|
6500
6490
|
...(options ? { options: options } : {}),
|
|
6501
6491
|
};
|
|
6502
6492
|
}
|
|
6503
|
-
|
|
6493
|
+
/** @internal exported for unit tests only */
|
|
6494
|
+
export function queuedPromotionAttachments(attachments) {
|
|
6504
6495
|
return attachments.map((value) => {
|
|
6505
6496
|
const file = queuedPromotionRecord(value);
|
|
6497
|
+
// A saved queue entry is always a file part; the media type decides whether
|
|
6498
|
+
// the model sees pixels (image) or a file reference.
|
|
6499
|
+
const isImage = typeof file.mediaType === "string" &&
|
|
6500
|
+
file.mediaType
|
|
6501
|
+
.split(";", 1)[0]
|
|
6502
|
+
.trim()
|
|
6503
|
+
.toLowerCase()
|
|
6504
|
+
.startsWith("image/");
|
|
6506
6505
|
return {
|
|
6507
|
-
type: "file",
|
|
6506
|
+
type: isImage ? "image" : "file",
|
|
6508
6507
|
name: file.name,
|
|
6509
6508
|
...(typeof file.fileId === "string" ? { id: file.fileId } : {}),
|
|
6510
6509
|
...(typeof file.mediaType === "string"
|
|
@@ -7063,6 +7062,9 @@ export function createProductionAgentHandler(options) {
|
|
|
7063
7062
|
});
|
|
7064
7063
|
const mutableBody = body;
|
|
7065
7064
|
if (!isBackgroundWorker) {
|
|
7065
|
+
// A client can submit this private field, so overwrite it at the boundary.
|
|
7066
|
+
mutableBody[AGENT_CHAT_BROWSER_SESSION_ID_FIELD] =
|
|
7067
|
+
getRequestContext()?.browserSessionId ?? null;
|
|
7066
7068
|
delete mutableBody[ANALYTICS_CLIENT_PLATFORM_BODY_FIELD];
|
|
7067
7069
|
}
|
|
7068
7070
|
if (dispatchToBackground) {
|
|
@@ -7531,7 +7533,9 @@ export function createProductionAgentHandler(options) {
|
|
|
7531
7533
|
const turnUsageLabel = normalizeUsageLabel(body.usageLabel);
|
|
7532
7534
|
let experimentAssignments = [];
|
|
7533
7535
|
try {
|
|
7534
|
-
|
|
7536
|
+
// A pinned request model suppresses the experiment's model override, so
|
|
7537
|
+
// resolving would assign (and persist) a variant this turn never ran.
|
|
7538
|
+
if (ownerEmail && !requestModelIsExplicit) {
|
|
7535
7539
|
const { resolveActiveExperimentConfig } = await import("../observability/experiments.js");
|
|
7536
7540
|
const expConfig = await resolveActiveExperimentConfig(ownerEmail);
|
|
7537
7541
|
if (expConfig) {
|
package/dist/agent/run-store.js
CHANGED
|
@@ -5,6 +5,7 @@ import { ensureColumnExists, ensureTableExists } from "../db/ddl-guard.js";
|
|
|
5
5
|
import { widenIntColumnsToBigInt } from "../db/widen-columns.js";
|
|
6
6
|
import { lockAndAssertServicePrincipalMayStartRun, prepareServicePrincipalRunStart, recordServicePrincipalDenial, } from "../org/service-principal-guard.js";
|
|
7
7
|
import { captureError } from "../server/capture-error.js";
|
|
8
|
+
import { stripInlineBytes, stripInlineBytesFromJson, } from "../shared/inline-bytes.js";
|
|
8
9
|
import { isRequestedStopAbortReason } from "./abort-reasons.js";
|
|
9
10
|
import { admitAutoContinue, admitManualContinue, } from "./auto-continue.js";
|
|
10
11
|
import { LLM_MISSING_CREDENTIALS_ERROR_CODE, LLM_MISSING_CREDENTIALS_MESSAGE, } from "./engine/credential-errors.js";
|
|
@@ -1335,7 +1336,7 @@ function staleRecoveryDispatchPayload(payload) {
|
|
|
1335
1336
|
return payload;
|
|
1336
1337
|
}
|
|
1337
1338
|
return JSON.stringify({
|
|
1338
|
-
...parsed,
|
|
1339
|
+
...stripInlineBytes(parsed, "placeholder"),
|
|
1339
1340
|
internalContinuation: true,
|
|
1340
1341
|
});
|
|
1341
1342
|
}
|
|
@@ -1823,7 +1824,13 @@ export async function insertRunEvent(runId, seq, eventData) {
|
|
|
1823
1824
|
WHERE id = ? AND status <> 'running'
|
|
1824
1825
|
)
|
|
1825
1826
|
ON CONFLICT (run_id, seq) DO NOTHING`,
|
|
1826
|
-
args: [
|
|
1827
|
+
args: [
|
|
1828
|
+
runId,
|
|
1829
|
+
seq,
|
|
1830
|
+
Date.now(),
|
|
1831
|
+
stripInlineBytesFromJson(eventData, "placeholder"),
|
|
1832
|
+
runId,
|
|
1833
|
+
],
|
|
1827
1834
|
});
|
|
1828
1835
|
}
|
|
1829
1836
|
export const CHECKPOINT_TERMINAL_EVENT_SEQ = 1_000_000_000;
|
|
@@ -2479,6 +2486,11 @@ async function appendTerminalRunEvent(runId, event) {
|
|
|
2479
2486
|
const nextSeq = last ? Number(last.seq ?? -1) + 1 : 0;
|
|
2480
2487
|
await client.execute({
|
|
2481
2488
|
sql: `INSERT INTO agent_run_events (run_id, seq, event_at, event_data) VALUES (?, ?, ?, ?) ON CONFLICT (run_id, seq) DO NOTHING`,
|
|
2482
|
-
args: [
|
|
2489
|
+
args: [
|
|
2490
|
+
runId,
|
|
2491
|
+
nextSeq,
|
|
2492
|
+
Date.now(),
|
|
2493
|
+
stripInlineBytesFromJson(JSON.stringify(event), "placeholder"),
|
|
2494
|
+
],
|
|
2483
2495
|
});
|
|
2484
2496
|
}
|
|
@@ -4,10 +4,9 @@ import { isCredentialGapCodeAgentEvent, normalizeCodeAgentTranscript, } from "..
|
|
|
4
4
|
import { stripAgentChatContextFromMessage } from "../shared/agent-chat-context.js";
|
|
5
5
|
import { RUN_NOT_STARTED_METADATA_KEY, } from "../shared/agent-chat-run-not-started.js";
|
|
6
6
|
import { BUILDER_GATEWAY_INTERNAL_ERROR_CODE } from "./engine/error-detail.js";
|
|
7
|
-
import { stringifyToolUseInputForGateway } from "./engine/translate-anthropic.js";
|
|
7
|
+
import { INTERRUPTED_TOOL_RESULT_MARKER as INTERRUPTED_TOOL_RESULT, stringifyToolUseInputForGateway, } from "./engine/translate-anthropic.js";
|
|
8
8
|
import { parseFollowUpSuggestions } from "./follow-up-suggestions.js";
|
|
9
9
|
import { isContinuationTerminalReason } from "./types.js";
|
|
10
|
-
const INTERRUPTED_TOOL_RESULT = "Interrupted before this tool returned a result.";
|
|
11
10
|
const INTERRUPTED_ACTIVITY_RESULT = "Stopped before this action started.";
|
|
12
11
|
export const ASSISTANT_RUN_DURATION_METADATA_KEY = "agentNativeRunDurationMs";
|
|
13
12
|
const MAX_STORED_ATTACHMENT_CHARS = 60_000;
|
|
@@ -139,8 +139,8 @@ export declare function createAutomationCallbackHandler(runtime: AutomationRunti
|
|
|
139
139
|
duplicate?: undefined;
|
|
140
140
|
eventId?: undefined;
|
|
141
141
|
} | {
|
|
142
|
-
error?: undefined;
|
|
143
142
|
accepted: boolean;
|
|
144
143
|
duplicate: boolean;
|
|
145
144
|
eventId: string;
|
|
145
|
+
error?: undefined;
|
|
146
146
|
}>;
|
|
@@ -7,6 +7,7 @@ import { createGetDb } from "../db/create-get-db.js";
|
|
|
7
7
|
import { ensureColumnExists, ensureIndexExists, ensureTableExists, } from "../db/ddl-guard.js";
|
|
8
8
|
import { widenIntColumnsToBigInt } from "../db/widen-columns.js";
|
|
9
9
|
import { getRequestOrgId } from "../server/request-context.js";
|
|
10
|
+
import { stripInlineBytes } from "../shared/inline-bytes.js";
|
|
10
11
|
import { resolveAccess } from "../sharing/access.js";
|
|
11
12
|
import { registerShareableResource } from "../sharing/registry.js";
|
|
12
13
|
import { roleSatisfies } from "../sharing/schema.js";
|
|
@@ -808,7 +809,7 @@ export async function forkThread(sourceId, ownerEmail, opts) {
|
|
|
808
809
|
return null;
|
|
809
810
|
}
|
|
810
811
|
const id = opts?.id ?? generateId();
|
|
811
|
-
const threadData = forkThreadData(source.threadData, id, snapshot?.fromMessageId);
|
|
812
|
+
const threadData = JSON.stringify(stripInlineBytes(JSON.parse(forkThreadData(source.threadData, id, snapshot?.fromMessageId)), "placeholder"));
|
|
812
813
|
const now = Date.now();
|
|
813
814
|
const title = source.title ? `${source.title} (fork)` : "";
|
|
814
815
|
const client = getDbExec();
|
|
@@ -1096,7 +1097,10 @@ export async function updateThreadData(id, threadData, title, preview, messageCo
|
|
|
1096
1097
|
preserveExistingTopLevelKeys: options.preserveExistingTopLevelKeys ?? true,
|
|
1097
1098
|
onAnnotationConflict: (conflict) => annotationConflicts.push(conflict),
|
|
1098
1099
|
});
|
|
1099
|
-
|
|
1100
|
+
// Client snapshots can predate their upload URL, so inline bytes become a
|
|
1101
|
+
// visible placeholder rather than failing the save; legacy rows are
|
|
1102
|
+
// scrubbed on their next write.
|
|
1103
|
+
nextThreadData = JSON.stringify(stripInlineBytes(merged, "placeholder"));
|
|
1100
1104
|
nextMessageCount = countThreadMessages(merged, messageCount);
|
|
1101
1105
|
const nextUpdatedAt = Math.max(Date.now(), current.updatedAt + 1);
|
|
1102
1106
|
// Completion persistence can race the separate generated-title save.
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import { AgentKitRunSlotBusyError } from "@agent-native/agentkit/client";
|
|
2
|
-
import { isAgentKitProtocolVersion, parseAgentQueuedMessage, parseAgentRunOptions, parseAgentThreadSnapshot, } from "@agent-native/agentkit/protocol";
|
|
2
|
+
import { isAgentKitProtocolVersion, parseAgentQueuedMessage, parseAgentRunOptions, parseAgentThreadSnapshot, persistableFilePart, } from "@agent-native/agentkit/protocol";
|
|
3
3
|
import { projectRootAssistantMessages } from "../../agent/thread-message-projection.js";
|
|
4
4
|
import { RUN_NOT_STARTED_METADATA_KEY, retryContextFromRequest, } from "../../shared/agent-chat-run-not-started.js";
|
|
5
5
|
import { agentEngineStatusUrlForChatApi, requireAgentEngineConfiguredForDispatch, } from "../agent-engine-readiness.js";
|
|
@@ -167,6 +167,9 @@ function messagePart(value, fallbackTextFormat) {
|
|
|
167
167
|
: typeof part.mimeType === "string"
|
|
168
168
|
? { mediaType: part.mimeType }
|
|
169
169
|
: {}),
|
|
170
|
+
...(part.omitted === "inline-bytes"
|
|
171
|
+
? { omitted: "inline-bytes" }
|
|
172
|
+
: {}),
|
|
170
173
|
};
|
|
171
174
|
}
|
|
172
175
|
return {
|
|
@@ -1046,9 +1049,6 @@ function assistantMessageIdsByRun(events) {
|
|
|
1046
1049
|
}
|
|
1047
1050
|
return uniqueMessageIdsByRun;
|
|
1048
1051
|
}
|
|
1049
|
-
function persistedFileUrl(url) {
|
|
1050
|
-
return url && !/^\s*data:/i.test(url) ? url : undefined;
|
|
1051
|
-
}
|
|
1052
1052
|
function persistedHistoryEvents(events = []) {
|
|
1053
1053
|
const sequenceByRun = new Map();
|
|
1054
1054
|
return events.flatMap((event) => {
|
|
@@ -1154,16 +1154,17 @@ function persistedMessages(messages) {
|
|
|
1154
1154
|
];
|
|
1155
1155
|
}
|
|
1156
1156
|
if (part.type === "file") {
|
|
1157
|
-
const
|
|
1158
|
-
if (!url && !
|
|
1157
|
+
const stored = persistableFilePart(part);
|
|
1158
|
+
if (!stored.url && !stored.fileId && !stored.omitted)
|
|
1159
1159
|
return [];
|
|
1160
1160
|
return [
|
|
1161
1161
|
{
|
|
1162
1162
|
type: "file",
|
|
1163
|
-
name:
|
|
1164
|
-
...(
|
|
1165
|
-
...(url ? { url } : {}),
|
|
1166
|
-
...(
|
|
1163
|
+
name: stored.name,
|
|
1164
|
+
...(stored.mediaType ? { mediaType: stored.mediaType } : {}),
|
|
1165
|
+
...(stored.url ? { url: stored.url } : {}),
|
|
1166
|
+
...(stored.fileId ? { fileId: stored.fileId } : {}),
|
|
1167
|
+
...(stored.omitted ? { omitted: stored.omitted } : {}),
|
|
1167
1168
|
},
|
|
1168
1169
|
];
|
|
1169
1170
|
}
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { AGENT_TOOL_CALL_HISTORY_MEDIA_TYPE, AGENT_TOOL_RESULT_HISTORY_MEDIA_TYPE, AGENTKIT_PROTOCOL_VERSION, AgentProtocolValidationError, approvalResponseFromResume, createCapabilityUnavailableError, createCapabilityUnsupportedError, inferAgentActivityKind, negotiateAgentKitProtocolVersion, resumeEntryFromApproval, resumeOptionId, } from "@agent-native/agentkit/protocol";
|
|
1
|
+
import { AGENT_TOOL_CALL_HISTORY_MEDIA_TYPE, AGENT_TOOL_RESULT_HISTORY_MEDIA_TYPE, AGENTKIT_PROTOCOL_VERSION, AgentProtocolValidationError, approvalResponseFromResume, createCapabilityUnavailableError, createCapabilityUnsupportedError, inferAgentActivityKind, isInlineDataUrl, negotiateAgentKitProtocolVersion, resumeEntryFromApproval, resumeOptionId, } from "@agent-native/agentkit/protocol";
|
|
2
2
|
import { AUTO_CONTINUE_OF_RUN_METADATA_KEY, AUTO_CONTINUE_PROMPT, AUTO_CONTINUE_REFUSAL_CODES, CONTINUE_OF_RUN_METADATA_KEY, CONTINUE_UNAVAILABLE_CODE, } from "../../agent/auto-continue.js";
|
|
3
3
|
import { parseBase64DataUrl } from "../../shared/data-url.js";
|
|
4
4
|
import { emitChatFirstOpenApp, emitChatFirstOpenBrowser, } from "../chat-first-state.js";
|
|
@@ -3255,10 +3255,14 @@ export function createAgentKitProtocolAdapter(runtime, options = {}) {
|
|
|
3255
3255
|
if (state.status === "missing" || !state.turnId) {
|
|
3256
3256
|
throw Object.assign(new Error(`Agent run ${input.runId} cannot be continued.`), { code: CONTINUE_UNAVAILABLE_CODE, retryable: false });
|
|
3257
3257
|
}
|
|
3258
|
+
const attachments = (input.attachments ?? [])
|
|
3259
|
+
.filter((part) => !isInlineDataUrl(part.url))
|
|
3260
|
+
.map(runtimeAttachmentFromFilePart);
|
|
3258
3261
|
const turn = await session.continueTurn({
|
|
3259
3262
|
turnId: state.turnId,
|
|
3260
3263
|
prompt: AUTO_CONTINUE_PROMPT,
|
|
3261
3264
|
metadata: { [CONTINUE_OF_RUN_METADATA_KEY]: state.runId },
|
|
3265
|
+
...(attachments.length ? { attachments } : {}),
|
|
3262
3266
|
abortSignal: readers.signal,
|
|
3263
3267
|
});
|
|
3264
3268
|
const runId = await openStartedRun(input.threadId, session, turn, undefined, { continuedRunId: input.runId });
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import type
|
|
1
|
+
import { type AgentSuggestion } from "@agent-native/agentkit/protocol";
|
|
2
2
|
import type { ActionChatUIConfig } from "../../action-ui.js";
|
|
3
3
|
import type { AgentMcpAppPayload } from "../../mcp-client/app-result.js";
|
|
4
4
|
import type { ReasoningEffort } from "../../shared/reasoning-effort.js";
|
|
@@ -240,6 +240,8 @@ export interface AgentChatRuntimeContinueInput {
|
|
|
240
240
|
readonly prompt?: string;
|
|
241
241
|
readonly approval?: AgentChatRuntimeApprovalResponse;
|
|
242
242
|
readonly connection?: AgentChatRuntimeConnectionResponse;
|
|
243
|
+
/** Durable references to the continued turn's attachments; never bytes. */
|
|
244
|
+
readonly attachments?: readonly AgentChatRuntimeAttachment[];
|
|
243
245
|
readonly metadata?: AgentChatRuntimeMetadata;
|
|
244
246
|
readonly abortSignal?: AbortSignal;
|
|
245
247
|
}
|