@agent-native/core 0.223.0-nightly-20261102152511 → 0.223.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/agent/actions/get-agentkit-capabilities.js +2 -2
- package/dist/agent/engine/error-detail.d.ts +13 -0
- package/dist/agent/engine/error-detail.js +92 -0
- package/dist/agent/production-agent.js +3 -5
- package/dist/agent/run-failure-retry.d.ts +7 -0
- package/dist/agent/run-failure-retry.js +21 -0
- package/dist/agent/thread-data-builder.js +2 -3
- package/dist/agentkit/capabilities.d.ts +1 -1
- package/dist/cli/templates-meta.js +0 -14
- package/dist/client/agent-run-status.d.ts +2 -0
- package/dist/client/agent-run-status.js +3 -0
- package/dist/client/error-format.js +2 -6
- package/dist/client/sse-event-processor.js +2 -75
- package/dist/collab/routes.d.ts +1 -1
- package/dist/collab/struct-routes.d.ts +1 -1
- package/dist/connections/catalog.d.ts +5 -5
- package/dist/connections/catalog.js +3 -22
- package/dist/file-upload/actions/upload-image.d.ts +2 -2
- package/dist/file-upload/builder.js +4 -5
- package/dist/mcp/analytics.js +7 -0
- package/dist/mcp/build-server.js +7 -2
- package/dist/mcp/direct-action-call.js +3 -1
- package/dist/mcp/embed-redaction.d.ts +3 -0
- package/dist/mcp/embed-redaction.js +5 -0
- package/dist/mcp/execute-action-call.js +6 -2
- package/dist/org/index.d.ts +1 -1
- package/dist/progress/routes.d.ts +1 -1
- package/dist/provider-api/actions/custom-provider-registration.d.ts +6 -6
- package/dist/provider-api/actions/provider-api.d.ts +2 -2
- package/dist/provider-api/index.js +2 -9
- package/dist/server/agent-chat-plugin.js +7 -0
- package/dist/server/agent-engine-ollama-models-route.d.ts +1 -1
- package/dist/server/first-touch-route.d.ts +1 -1
- package/dist/server/transcribe-voice.d.ts +1 -1
- package/dist/vite/bounded-task-plugin.d.ts +12 -2
- package/dist/vite/bounded-task-plugin.js +34 -26
- package/dist/vite/client.js +162 -106
- package/dist/vite/plugin-hook.test-helpers.d.ts +10 -0
- package/dist/vite/plugin-hook.test-helpers.js +19 -0
- package/package.json +4 -4
|
@@ -10,8 +10,8 @@ export default defineAction({
|
|
|
10
10
|
toolCallable: false,
|
|
11
11
|
run: async (_args, ctx) => {
|
|
12
12
|
const appId = ctx?.appId;
|
|
13
|
-
if (appId !== "design" && appId !== "slides"
|
|
14
|
-
fail("AgentKit capabilities are only available in Design
|
|
13
|
+
if (appId !== "design" && appId !== "slides") {
|
|
14
|
+
fail("AgentKit capabilities are only available in Design and Slides.", {
|
|
15
15
|
errorCode: "agentkit_capabilities_unsupported_app",
|
|
16
16
|
statusCode: 400,
|
|
17
17
|
});
|
|
@@ -6,6 +6,19 @@ export declare const BUILDER_GATEWAY_INTERNAL_ERROR_CODE = "builder_gateway_inte
|
|
|
6
6
|
export declare const PROVIDER_TRANSIENT_REJECTION_ERROR_CODE = "provider_transient_rejection";
|
|
7
7
|
export declare const PROVIDER_RATE_LIMITED_ERROR_CODE = "provider_rate_limited";
|
|
8
8
|
export declare function isCreditsLimitErrorCode(errorCode?: string): boolean;
|
|
9
|
+
/**
|
|
10
|
+
* The provider refused the request itself (a tool schema, an image source), so
|
|
11
|
+
* the model never ran and sending the same request again fails the same way.
|
|
12
|
+
*/
|
|
13
|
+
export declare function isRejectedRequestErrorCode(errorCode?: string): boolean;
|
|
14
|
+
/**
|
|
15
|
+
* Codes after which sending the same request again cannot help: the request,
|
|
16
|
+
* credential, or model selection is at fault, the server already spent its own
|
|
17
|
+
* recovery path, or the run's outcome is unknown and a replay could duplicate
|
|
18
|
+
* side effects. The SSE auto-continue gate and the run-status retry verdict
|
|
19
|
+
* both read this, so the two cannot drift.
|
|
20
|
+
*/
|
|
21
|
+
export declare function isTerminalRecoveryErrorCode(errorCode?: string): boolean;
|
|
9
22
|
export declare function isBareProviderRejectionMessage(message: string): boolean;
|
|
10
23
|
export declare function isBuilderGatewayInternalErrorMessage(message: string): boolean;
|
|
11
24
|
export declare function isGenericInternalServerErrorMessage(message: string): boolean;
|
|
@@ -60,6 +60,98 @@ export function isCreditsLimitErrorCode(errorCode) {
|
|
|
60
60
|
const code = errorCode?.trim().toLowerCase();
|
|
61
61
|
return code === "http_402" || code?.startsWith("credits-limit") === true;
|
|
62
62
|
}
|
|
63
|
+
/**
|
|
64
|
+
* The provider refused the request itself (a tool schema, an image source), so
|
|
65
|
+
* the model never ran and sending the same request again fails the same way.
|
|
66
|
+
*/
|
|
67
|
+
export function isRejectedRequestErrorCode(errorCode) {
|
|
68
|
+
const code = errorCode?.trim().toLowerCase();
|
|
69
|
+
return code === "invalid_request" || code === "invalid_request_error";
|
|
70
|
+
}
|
|
71
|
+
/**
|
|
72
|
+
* Codes after which sending the same request again cannot help: the request,
|
|
73
|
+
* credential, or model selection is at fault, the server already spent its own
|
|
74
|
+
* recovery path, or the run's outcome is unknown and a replay could duplicate
|
|
75
|
+
* side effects. The SSE auto-continue gate and the run-status retry verdict
|
|
76
|
+
* both read this, so the two cannot drift.
|
|
77
|
+
*/
|
|
78
|
+
export function isTerminalRecoveryErrorCode(errorCode) {
|
|
79
|
+
const code = errorCode?.trim().toLowerCase() ?? "";
|
|
80
|
+
return (code === "context_length_exceeded" ||
|
|
81
|
+
code === "input_too_long" ||
|
|
82
|
+
isCreditsLimitErrorCode(code) ||
|
|
83
|
+
code === "billing_error" ||
|
|
84
|
+
code === "unauthorized" ||
|
|
85
|
+
code === "authentication_error" ||
|
|
86
|
+
code === "permission_error" ||
|
|
87
|
+
code === "builder_auth_error" ||
|
|
88
|
+
// The account cannot use this model; another POST picks the same one.
|
|
89
|
+
code === "builder_model_unauthorized" ||
|
|
90
|
+
code === "http_401" ||
|
|
91
|
+
code === "http_403" ||
|
|
92
|
+
code === "rate_limit_exceeded" ||
|
|
93
|
+
code === "gateway_not_enabled" ||
|
|
94
|
+
code === "missing_api_key" ||
|
|
95
|
+
code === "missing_credentials" ||
|
|
96
|
+
isRejectedRequestErrorCode(code) ||
|
|
97
|
+
code === "request_too_large" ||
|
|
98
|
+
code === "not_found_error" ||
|
|
99
|
+
code === "model_not_found" ||
|
|
100
|
+
code === "invalid_attachment" ||
|
|
101
|
+
// The server now owns rate-limit recovery end to end (in-loop retries,
|
|
102
|
+
// sibling-model fallback, one cooled continuation, then a terminal
|
|
103
|
+
// `provider_rate_limited`) and caps the continuation chain it hands back
|
|
104
|
+
// to the client. Auto-recovering a bare `http_429`/`http_529` here would
|
|
105
|
+
// let an exhausted rate-limit error re-POST as a client continuation,
|
|
106
|
+
// bypassing that one-hop cap and restarting the retry/fallback budget
|
|
107
|
+
// the server just spent. Render with the manual Retry affordance like
|
|
108
|
+
// `provider_rate_limited` below, not auto-continued.
|
|
109
|
+
code === "http_429" ||
|
|
110
|
+
code === "http_529" ||
|
|
111
|
+
// The gateway's own throttle codes, same reasoning.
|
|
112
|
+
code === "rate_limited" ||
|
|
113
|
+
code === "too_many_concurrent_requests" ||
|
|
114
|
+
code === "provider_rate_limited" ||
|
|
115
|
+
// The server already retried the bare-403 load-shedding signature before
|
|
116
|
+
// this reached the client; another automatic POST would just hammer the
|
|
117
|
+
// same throttle. Renders with the manual Retry affordance like
|
|
118
|
+
// `provider_rate_limited` above, not auto-continued.
|
|
119
|
+
code === PROVIDER_TRANSIENT_REJECTION_ERROR_CODE ||
|
|
120
|
+
// `builder_gateway_error` is the no-detail fallback the Builder engine
|
|
121
|
+
// emits when the gateway returns `{type:"stop",reason:"error"}` with no
|
|
122
|
+
// explanation — almost always the upstream provider giving up (model
|
|
123
|
+
// quota hit, account misconfiguration, opaque downstream failure). The
|
|
124
|
+
// production-agent already retries this synchronously up to MAX_RETRIES
|
|
125
|
+
// before the error escapes to the SSE stream, so by the time the client
|
|
126
|
+
// sees it, retrying again with another POST /agent-chat will hit the
|
|
127
|
+
// same wall. This used to send the chat into a 32-continuation runaway
|
|
128
|
+
// (each turn cleared+regenerated visible content) for users hitting a
|
|
129
|
+
// misbehaving Builder route. Surface the error instead.
|
|
130
|
+
code === "builder_gateway_error" ||
|
|
131
|
+
// The hosted run exhausted its in-invocation continuation budget without
|
|
132
|
+
// finishing (run-loop-with-resume.ts). It's flagged `recoverable: true` so
|
|
133
|
+
// the recovery banner reads "stopped before finishing", but it must NOT
|
|
134
|
+
// auto-continue: another POST would hit the same ~40s wall and churn. The
|
|
135
|
+
// user retries deliberately (ideally as a single bulk action).
|
|
136
|
+
code === "run_budget_exhausted" ||
|
|
137
|
+
// `aborted_<reason>` is emitted by `terminalEventForAbortReason` for every
|
|
138
|
+
// abort that is neither a user stop nor a continuation boundary — a Slack
|
|
139
|
+
// cancel, a stuck-banner auto-retry, an operator kill. Whoever aborted the
|
|
140
|
+
// run owns the retry; auto-continuing here restarts work someone just
|
|
141
|
+
// stopped (and double-fires alongside the stuck banner's own retry). They
|
|
142
|
+
// stay `recoverable: true` so the banner still reads "stopped before
|
|
143
|
+
// finishing".
|
|
144
|
+
code.startsWith("aborted_") ||
|
|
145
|
+
// The run's outcome is genuinely UNKNOWN: its row is gone, or it left
|
|
146
|
+
// 'running' in a state the server has no terminal event for. Both stay
|
|
147
|
+
// `recoverable: true` so the banner offers a manual Retry, but an
|
|
148
|
+
// automatic re-POST would assert the turn did not finish — and it may
|
|
149
|
+
// well have, side effects included. Replaying it would duplicate them.
|
|
150
|
+
// The user decides, which is exactly what these errors say to do.
|
|
151
|
+
code === "run_record_missing" ||
|
|
152
|
+
code === "unknown_run_status" ||
|
|
153
|
+
code === "run_terminal_lookup_failed");
|
|
154
|
+
}
|
|
63
155
|
export function isBareProviderRejectionMessage(message) {
|
|
64
156
|
const trimmed = message.trim();
|
|
65
157
|
return (trimmed === "" ||
|
|
@@ -78,7 +78,7 @@ import { applyContextXrayTransformForIteration } from "./engine/context-directiv
|
|
|
78
78
|
import { attemptContinuationDispatch } from "./engine/continuation-dispatch-retry.js";
|
|
79
79
|
import { formatLlmCredentialErrorMessage, LLM_MISSING_CREDENTIALS_ERROR_CODE, LLM_MISSING_CREDENTIALS_MESSAGE, userFacingLlmCredentialError, } from "./engine/credential-errors.js";
|
|
80
80
|
import { getEngineCredentialOwner } from "./engine/credential-owner.js";
|
|
81
|
-
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";
|
|
81
|
+
import { BUILDER_GATEWAY_INTERNAL_ERROR_CODE, isContextOverflowCode, isContextOverflowMessage, isInvalidAttachmentProviderMessage, isProviderConnectionErrorMessage, isRejectedRequestErrorCode, PROVIDER_RATE_LIMITED_ERROR_CODE, PROVIDER_TRANSIENT_REJECTION_ERROR_CODE, } from "./engine/error-detail.js";
|
|
82
82
|
import { resolveEngine, explicitEngineName, registerBuiltinEngines, getStoredModelForEngine, normalizeModelForEngine, isResolvedEngineUsableForRequest, } from "./engine/index.js";
|
|
83
83
|
import { limitProviderTools, MAX_PROVIDER_TOOLS, } from "./engine/limit-provider-tools.js";
|
|
84
84
|
import { resolveEmptyResponseRetryMaxOutputTokens, resolveMainChatMaxOutputTokens, resolveMaxOutputTokensForEngine, } from "./engine/output-tokens.js";
|
|
@@ -3065,8 +3065,7 @@ export function isResumableEngineError(err) {
|
|
|
3065
3065
|
return false;
|
|
3066
3066
|
const code = err instanceof EngineError ? (err.errorCode ?? "").toLowerCase() : "";
|
|
3067
3067
|
if ((err instanceof EngineError && err.providerRetryable === false) ||
|
|
3068
|
-
code
|
|
3069
|
-
code === "invalid_request_error") {
|
|
3068
|
+
isRejectedRequestErrorCode(code)) {
|
|
3070
3069
|
return false;
|
|
3071
3070
|
}
|
|
3072
3071
|
if (code === "builder_gateway_timeout" ||
|
|
@@ -8571,8 +8570,7 @@ export function isRecoverableContinuationError(event) {
|
|
|
8571
8570
|
if (event.providerRetryable === false ||
|
|
8572
8571
|
event.recoverable === false ||
|
|
8573
8572
|
code === "builder_gateway_error" ||
|
|
8574
|
-
code
|
|
8575
|
-
code === "invalid_request_error") {
|
|
8573
|
+
isRejectedRequestErrorCode(code)) {
|
|
8576
8574
|
return false;
|
|
8577
8575
|
}
|
|
8578
8576
|
return (event.recoverable === true ||
|
|
@@ -0,0 +1,7 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Whether resending the same prompt can succeed after an errored run, judged
|
|
3
|
+
* from its stored `terminal_reason` (`error:<code>`). Clients use it to decide
|
|
4
|
+
* on an automatic retry; a manual retry stays available either way, since the
|
|
5
|
+
* user may change the model or the prompt first.
|
|
6
|
+
*/
|
|
7
|
+
export declare function isRunFailureRetryable(terminalReason: string): boolean;
|
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
import { isLlmCredentialError } from "./engine/credential-errors.js";
|
|
2
|
+
import { isTerminalRecoveryErrorCode } from "./engine/error-detail.js";
|
|
3
|
+
/**
|
|
4
|
+
* Whether resending the same prompt can succeed after an errored run, judged
|
|
5
|
+
* from its stored `terminal_reason` (`error:<code>`). Clients use it to decide
|
|
6
|
+
* on an automatic retry; a manual retry stays available either way, since the
|
|
7
|
+
* user may change the model or the prompt first.
|
|
8
|
+
*/
|
|
9
|
+
export function isRunFailureRetryable(terminalReason) {
|
|
10
|
+
const code = (terminalReason.startsWith("error:")
|
|
11
|
+
? terminalReason.slice("error:".length)
|
|
12
|
+
: terminalReason)
|
|
13
|
+
.trim()
|
|
14
|
+
.toLowerCase();
|
|
15
|
+
// The gateway's masked "internal error" stays retryable on purpose: it is
|
|
16
|
+
// sometimes transient, and a missed retry costs more than an extra one.
|
|
17
|
+
return (!isTerminalRecoveryErrorCode(code) &&
|
|
18
|
+
// Covers the bare `missing_api_key` reason, which has no `error:` prefix.
|
|
19
|
+
code !== "missing_api_key" &&
|
|
20
|
+
!isLlmCredentialError(undefined, code));
|
|
21
|
+
}
|
|
@@ -7,7 +7,7 @@ import { parseBase64DataUrl } from "../shared/data-url.js";
|
|
|
7
7
|
import { toolInputFingerprint, toolInputsMatch, } from "../shared/tool-inputs.js";
|
|
8
8
|
import { approvalKeyForHistory, approvalKeyForPrivateHistory, } from "./approval-key.js";
|
|
9
9
|
import { isAgentInternalContinuationPrompt } from "./continuation-prompt.js";
|
|
10
|
-
import { BUILDER_GATEWAY_INTERNAL_ERROR_CODE } from "./engine/error-detail.js";
|
|
10
|
+
import { BUILDER_GATEWAY_INTERNAL_ERROR_CODE, isRejectedRequestErrorCode, } from "./engine/error-detail.js";
|
|
11
11
|
import { INTERRUPTED_TOOL_RESULT_MARKER as INTERRUPTED_TOOL_RESULT, stringifyToolUseInputForGateway, } from "./engine/translate-anthropic.js";
|
|
12
12
|
import { parseFollowUpSuggestions } from "./follow-up-suggestions.js";
|
|
13
13
|
import { isContinuationTerminalReason, isUnknownWriteCause } from "./types.js";
|
|
@@ -107,8 +107,7 @@ function isInternalContinuationError(event) {
|
|
|
107
107
|
if (event.providerRetryable === false ||
|
|
108
108
|
event.recoverable === false ||
|
|
109
109
|
code === "builder_gateway_error" ||
|
|
110
|
-
code
|
|
111
|
-
code === "invalid_request_error") {
|
|
110
|
+
isRejectedRequestErrorCode(code)) {
|
|
112
111
|
return false;
|
|
113
112
|
}
|
|
114
113
|
return (event.recoverable === true ||
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import type { CredentialContext } from "../credentials/index.js";
|
|
2
|
-
export type AgentKitCapabilityAppId = "design" | "slides"
|
|
2
|
+
export type AgentKitCapabilityAppId = "design" | "slides";
|
|
3
3
|
export interface AgentKitIntegrationCapability {
|
|
4
4
|
id: string;
|
|
5
5
|
label: string;
|
|
@@ -50,20 +50,6 @@ export const TEMPLATES = [
|
|
|
50
50
|
requiredPackages: ["creative-context"],
|
|
51
51
|
core: true,
|
|
52
52
|
},
|
|
53
|
-
{
|
|
54
|
-
name: "slides-2",
|
|
55
|
-
label: "Slides 2",
|
|
56
|
-
hint: "Agent-Native presentation editor for generating and editing React presentations",
|
|
57
|
-
icon: "GalleryHorizontal",
|
|
58
|
-
color: "#EC4899",
|
|
59
|
-
colorRgb: "236 72 153",
|
|
60
|
-
devPort: 8087,
|
|
61
|
-
prodUrl: "https://slides-2.agent-native.com",
|
|
62
|
-
defaultMode: "dev",
|
|
63
|
-
hidden: true,
|
|
64
|
-
requiredPackages: ["creative-context"],
|
|
65
|
-
core: false,
|
|
66
|
-
},
|
|
67
53
|
{
|
|
68
54
|
name: "analytics",
|
|
69
55
|
label: "Analytics",
|
|
@@ -7,6 +7,8 @@ export type AgentRunStatusRead = {
|
|
|
7
7
|
terminalReason?: string;
|
|
8
8
|
/** A truncated run whose continuation the server has not started yet. */
|
|
9
9
|
awaitingRedispatch?: boolean;
|
|
10
|
+
/** False when resending the same prompt would fail the same way. */
|
|
11
|
+
retryable?: boolean;
|
|
10
12
|
} | {
|
|
11
13
|
ok: false;
|
|
12
14
|
};
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import { GATEWAY_UNAVAILABLE_VISITOR_MESSAGE, LLM_MISSING_CREDENTIALS_MESSAGE, } from "../agent/engine/credential-errors.js";
|
|
2
2
|
import { AMBIGUOUS_HTTP_CREDENTIAL_CODES, credentialStateForErrorCode, } from "../agent/engine/credential-state.js";
|
|
3
|
-
import { BUILDER_GATEWAY_INTERNAL_ERROR_CODE, isBuilderGatewayInternalErrorMessage, isContextOverflowMessage, PROVIDER_TRANSIENT_REJECTION_ERROR_CODE, } from "../agent/engine/error-detail.js";
|
|
3
|
+
import { BUILDER_GATEWAY_INTERNAL_ERROR_CODE, isBuilderGatewayInternalErrorMessage, isContextOverflowMessage, isRejectedRequestErrorCode, PROVIDER_TRANSIENT_REJECTION_ERROR_CODE, } from "../agent/engine/error-detail.js";
|
|
4
4
|
import { RUN_FAILED_MESSAGE, RUN_INTERRUPTED_MESSAGE, RUN_SIGNED_OUT_MESSAGE, RUN_UNVERIFIED_MESSAGE, } from "./chat/run-outcome.js";
|
|
5
5
|
export { isCreditsLimitErrorCode } from "../agent/engine/error-detail.js";
|
|
6
6
|
export const BUILDER_SPACE_SETTINGS_URL = "https://builder.io/account/space?utm_source=agent-native&utm_medium=product&utm_campaign=onboarding&utm_content=space_settings";
|
|
@@ -58,10 +58,6 @@ const MALFORMED_REQUEST_MESSAGE = "The model provider rejected this request as m
|
|
|
58
58
|
export const CHAT_REQUEST_TOO_LARGE_MESSAGE = "This request exceeded the server's size limit (HTTP 413). Start a new chat or remove large attachments or references, then retry.";
|
|
59
59
|
export const CHAT_CAPABILITY_UNAVAILABLE_MESSAGE = "We couldn't send your message right now. Please try again shortly.";
|
|
60
60
|
export const CHAT_CAPABILITY_UNSUPPORTED_MESSAGE = "This app can't safely retry your saved prompt yet. Update the app, then try again.";
|
|
61
|
-
const MALFORMED_REQUEST_CODES = new Set([
|
|
62
|
-
"invalid_request",
|
|
63
|
-
"invalid_request_error",
|
|
64
|
-
]);
|
|
65
61
|
const ATTACHMENT_REJECTION_PATTERN = /\b(?:file_url|image_url|file_data|input_file|media_type|mime\s?type|image\.source|document\s+block)\b/i;
|
|
66
62
|
function isSafeUpgradeUrl(url) {
|
|
67
63
|
try {
|
|
@@ -417,7 +413,7 @@ export function normalizeChatError(errorMessage, errorCode) {
|
|
|
417
413
|
details: text,
|
|
418
414
|
};
|
|
419
415
|
}
|
|
420
|
-
if (
|
|
416
|
+
if (isRejectedRequestErrorCode(code) && !isContextOverflowMessage(text)) {
|
|
421
417
|
return {
|
|
422
418
|
message: ATTACHMENT_REJECTION_PATTERN.test(text)
|
|
423
419
|
? MALFORMED_REQUEST_ATTACHMENT_MESSAGE
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import { LLM_MISSING_CREDENTIALS_ERROR_CODE, LLM_MISSING_CREDENTIALS_MESSAGE, } from "../agent/engine/credential-errors.js";
|
|
2
|
-
import { BUILDER_GATEWAY_INTERNAL_ERROR_CODE,
|
|
2
|
+
import { BUILDER_GATEWAY_INTERNAL_ERROR_CODE, isTerminalRecoveryErrorCode, } from "../agent/engine/error-detail.js";
|
|
3
3
|
import { normalizeConnectRequiredResult } from "../shared/connect-required.js";
|
|
4
4
|
import { isNonProgressEventType } from "../shared/progress-events.js";
|
|
5
5
|
import { toolInputsMatch } from "../shared/tool-inputs.js";
|
|
@@ -565,80 +565,7 @@ function isAutoRecoverableError(ev, errMsg) {
|
|
|
565
565
|
msg.includes("stopped before it finished")) {
|
|
566
566
|
return false;
|
|
567
567
|
}
|
|
568
|
-
if (code
|
|
569
|
-
code === "input_too_long" ||
|
|
570
|
-
code.startsWith("credits-limit") ||
|
|
571
|
-
code === "billing_error" ||
|
|
572
|
-
code === "unauthorized" ||
|
|
573
|
-
code === "authentication_error" ||
|
|
574
|
-
code === "permission_error" ||
|
|
575
|
-
code === "builder_auth_error" ||
|
|
576
|
-
// The account cannot use this model; another POST picks the same one.
|
|
577
|
-
code === "builder_model_unauthorized" ||
|
|
578
|
-
code === "http_401" ||
|
|
579
|
-
code === "http_403" ||
|
|
580
|
-
code === "rate_limit_exceeded" ||
|
|
581
|
-
code === "gateway_not_enabled" ||
|
|
582
|
-
code === "missing_api_key" ||
|
|
583
|
-
code === "missing_credentials" ||
|
|
584
|
-
code === "invalid_request" ||
|
|
585
|
-
code === "invalid_request_error" ||
|
|
586
|
-
code === "request_too_large" ||
|
|
587
|
-
code === "not_found_error" ||
|
|
588
|
-
code === "model_not_found" ||
|
|
589
|
-
// The server now owns rate-limit recovery end to end (in-loop retries,
|
|
590
|
-
// sibling-model fallback, one cooled continuation, then a terminal
|
|
591
|
-
// `provider_rate_limited`) and caps the continuation chain it hands back
|
|
592
|
-
// to the client. Auto-recovering a bare `http_429`/`http_529` here would
|
|
593
|
-
// let an exhausted rate-limit error re-POST as a client continuation,
|
|
594
|
-
// bypassing that one-hop cap and restarting the retry/fallback budget
|
|
595
|
-
// the server just spent. Render with the manual Retry affordance like
|
|
596
|
-
// `provider_rate_limited` below, not auto-continued.
|
|
597
|
-
code === "http_429" ||
|
|
598
|
-
code === "http_529" ||
|
|
599
|
-
// The gateway's own throttle codes, same reasoning.
|
|
600
|
-
code === "rate_limited" ||
|
|
601
|
-
code === "too_many_concurrent_requests" ||
|
|
602
|
-
code === "provider_rate_limited" ||
|
|
603
|
-
// The server already retried the bare-403 load-shedding signature before
|
|
604
|
-
// this reached the client; another automatic POST would just hammer the
|
|
605
|
-
// same throttle. Renders with the manual Retry affordance like
|
|
606
|
-
// `provider_rate_limited` above, not auto-continued.
|
|
607
|
-
code === PROVIDER_TRANSIENT_REJECTION_ERROR_CODE ||
|
|
608
|
-
// `builder_gateway_error` is the no-detail fallback the Builder engine
|
|
609
|
-
// emits when the gateway returns `{type:"stop",reason:"error"}` with no
|
|
610
|
-
// explanation — almost always the upstream provider giving up (model
|
|
611
|
-
// quota hit, account misconfiguration, opaque downstream failure). The
|
|
612
|
-
// production-agent already retries this synchronously up to MAX_RETRIES
|
|
613
|
-
// before the error escapes to the SSE stream, so by the time the client
|
|
614
|
-
// sees it, retrying again with another POST /agent-chat will hit the
|
|
615
|
-
// same wall. This used to send the chat into a 32-continuation runaway
|
|
616
|
-
// (each turn cleared+regenerated visible content) for users hitting a
|
|
617
|
-
// misbehaving Builder route. Surface the error instead.
|
|
618
|
-
code === "builder_gateway_error" ||
|
|
619
|
-
// The hosted run exhausted its in-invocation continuation budget without
|
|
620
|
-
// finishing (run-loop-with-resume.ts). It's flagged `recoverable: true` so
|
|
621
|
-
// the recovery banner reads "stopped before finishing", but it must NOT
|
|
622
|
-
// auto-continue: another POST would hit the same ~40s wall and churn. The
|
|
623
|
-
// user retries deliberately (ideally as a single bulk action).
|
|
624
|
-
code === "run_budget_exhausted" ||
|
|
625
|
-
// `aborted_<reason>` is emitted by `terminalEventForAbortReason` for every
|
|
626
|
-
// abort that is neither a user stop nor a continuation boundary — a Slack
|
|
627
|
-
// cancel, a stuck-banner auto-retry, an operator kill. Whoever aborted the
|
|
628
|
-
// run owns the retry; auto-continuing here restarts work someone just
|
|
629
|
-
// stopped (and double-fires alongside the stuck banner's own retry). They
|
|
630
|
-
// stay `recoverable: true` so the banner still reads "stopped before
|
|
631
|
-
// finishing".
|
|
632
|
-
code.startsWith("aborted_") ||
|
|
633
|
-
// The run's outcome is genuinely UNKNOWN: its row is gone, or it left
|
|
634
|
-
// 'running' in a state the server has no terminal event for. Both stay
|
|
635
|
-
// `recoverable: true` so the banner offers a manual Retry, but an
|
|
636
|
-
// automatic re-POST would assert the turn did not finish — and it may
|
|
637
|
-
// well have, side effects included. Replaying it would duplicate them.
|
|
638
|
-
// The user decides, which is exactly what these errors say to do.
|
|
639
|
-
code === "run_record_missing" ||
|
|
640
|
-
code === "unknown_run_status" ||
|
|
641
|
-
code === "run_terminal_lookup_failed") {
|
|
568
|
+
if (isTerminalRecoveryErrorCode(code)) {
|
|
642
569
|
return false;
|
|
643
570
|
}
|
|
644
571
|
if (code === "builder_gateway_network_error" ||
|
package/dist/collab/routes.d.ts
CHANGED
|
@@ -25,9 +25,9 @@ export declare const postCollabUpdate: import("h3").EventHandlerWithFetch<import
|
|
|
25
25
|
ok: boolean;
|
|
26
26
|
}>>;
|
|
27
27
|
export declare const postCollabText: import("h3").EventHandlerWithFetch<import("h3").EventHandlerRequest, Promise<{
|
|
28
|
+
text?: undefined;
|
|
28
29
|
ok?: undefined;
|
|
29
30
|
error: string;
|
|
30
|
-
text?: undefined;
|
|
31
31
|
} | {
|
|
32
32
|
error?: undefined;
|
|
33
33
|
ok: boolean;
|
|
@@ -13,9 +13,9 @@ export declare const postCollabPatch: import("h3").EventHandlerWithFetch<import(
|
|
|
13
13
|
ok: boolean;
|
|
14
14
|
}>>;
|
|
15
15
|
export declare const getCollabJson: import("h3").EventHandlerWithFetch<import("h3").EventHandlerRequest, Promise<{
|
|
16
|
-
data?: undefined;
|
|
17
16
|
error: string;
|
|
18
17
|
docId?: undefined;
|
|
18
|
+
data?: undefined;
|
|
19
19
|
} | {
|
|
20
20
|
error?: undefined;
|
|
21
21
|
docId: string;
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
export type WorkspaceConnectionCapability = "search" | "import" | "messages" | "meetings" | "crm" | "code" | "docs";
|
|
2
|
-
export type WorkspaceConnectionTemplateUse = "analytics" | "brain" | "calendar" | "clips" | "content" | "crm" | "design" | "dispatch" | "factory" | "forms" | "mail" | "slides"
|
|
2
|
+
export type WorkspaceConnectionTemplateUse = "analytics" | "brain" | "calendar" | "clips" | "content" | "crm" | "design" | "dispatch" | "factory" | "forms" | "mail" | "slides";
|
|
3
3
|
export type WorkspaceConnectionProviderId = "slack" | "github" | "figma" | "notion" | "gmail" | "google_calendar" | "google_drive" | "bigquery" | "google_docs" | "google_sheets" | "google_slides" | "hubspot" | "salesforce" | "jira" | "sentry" | "granola" | "clips" | "dbt" | "sigma" | "anthropic-managed-agents" | "generic";
|
|
4
4
|
export interface WorkspaceConnectionCredentialKey {
|
|
5
5
|
key: string;
|
|
@@ -84,7 +84,7 @@ export declare const WORKSPACE_CONNECTION_PROVIDERS: readonly [{
|
|
|
84
84
|
readonly scopes: readonly ["current_user:read", "file_content:read", "file_metadata:read", "projects:read"];
|
|
85
85
|
};
|
|
86
86
|
readonly capabilities: readonly ["search", "import", "docs"];
|
|
87
|
-
readonly recommendedTemplateUses: readonly ["brain", "design", "slides", "
|
|
87
|
+
readonly recommendedTemplateUses: readonly ["brain", "design", "slides", "content"];
|
|
88
88
|
}, {
|
|
89
89
|
readonly id: "notion";
|
|
90
90
|
readonly label: "Notion";
|
|
@@ -184,7 +184,7 @@ export declare const WORKSPACE_CONNECTION_PROVIDERS: readonly [{
|
|
|
184
184
|
readonly required: true;
|
|
185
185
|
}];
|
|
186
186
|
readonly capabilities: readonly ["search", "import", "docs"];
|
|
187
|
-
readonly recommendedTemplateUses: readonly ["brain", "content", "slides", "
|
|
187
|
+
readonly recommendedTemplateUses: readonly ["brain", "content", "slides", "dispatch", "analytics"];
|
|
188
188
|
readonly oauth: {
|
|
189
189
|
authorizationUrl: "https://accounts.google.com/o/oauth2/v2/auth";
|
|
190
190
|
tokenUrl: "https://oauth2.googleapis.com/token";
|
|
@@ -207,7 +207,7 @@ export declare const WORKSPACE_CONNECTION_PROVIDERS: readonly [{
|
|
|
207
207
|
readonly required: true;
|
|
208
208
|
}];
|
|
209
209
|
readonly capabilities: readonly ["search", "import", "docs"];
|
|
210
|
-
readonly recommendedTemplateUses: readonly ["brain", "content", "slides", "
|
|
210
|
+
readonly recommendedTemplateUses: readonly ["brain", "content", "slides", "dispatch"];
|
|
211
211
|
readonly oauth: {
|
|
212
212
|
authorizationUrl: "https://accounts.google.com/o/oauth2/v2/auth";
|
|
213
213
|
tokenUrl: "https://oauth2.googleapis.com/token";
|
|
@@ -253,7 +253,7 @@ export declare const WORKSPACE_CONNECTION_PROVIDERS: readonly [{
|
|
|
253
253
|
readonly required: true;
|
|
254
254
|
}];
|
|
255
255
|
readonly capabilities: readonly ["search", "import", "docs"];
|
|
256
|
-
readonly recommendedTemplateUses: readonly ["brain", "content", "slides", "
|
|
256
|
+
readonly recommendedTemplateUses: readonly ["brain", "content", "slides", "dispatch"];
|
|
257
257
|
readonly oauth: {
|
|
258
258
|
authorizationUrl: "https://accounts.google.com/o/oauth2/v2/auth";
|
|
259
259
|
tokenUrl: "https://oauth2.googleapis.com/token";
|
|
@@ -92,13 +92,7 @@ export const WORKSPACE_CONNECTION_PROVIDERS = [
|
|
|
92
92
|
],
|
|
93
93
|
},
|
|
94
94
|
capabilities: ["search", "import", "docs"],
|
|
95
|
-
recommendedTemplateUses: [
|
|
96
|
-
"brain",
|
|
97
|
-
"design",
|
|
98
|
-
"slides",
|
|
99
|
-
"slides-2",
|
|
100
|
-
"content",
|
|
101
|
-
],
|
|
95
|
+
recommendedTemplateUses: ["brain", "design", "slides", "content"],
|
|
102
96
|
}),
|
|
103
97
|
defineWorkspaceConnectionProvider({
|
|
104
98
|
id: "notion",
|
|
@@ -167,7 +161,6 @@ export const WORKSPACE_CONNECTION_PROVIDERS = [
|
|
|
167
161
|
"brain",
|
|
168
162
|
"content",
|
|
169
163
|
"slides",
|
|
170
|
-
"slides-2",
|
|
171
164
|
"dispatch",
|
|
172
165
|
"analytics",
|
|
173
166
|
],
|
|
@@ -183,13 +176,7 @@ export const WORKSPACE_CONNECTION_PROVIDERS = [
|
|
|
183
176
|
description: "Google Docs files for source material, document search, and agent-assisted writing workflows.",
|
|
184
177
|
credentialKeys: GOOGLE_CREDENTIAL_KEYS,
|
|
185
178
|
capabilities: ["search", "import", "docs"],
|
|
186
|
-
recommendedTemplateUses: [
|
|
187
|
-
"brain",
|
|
188
|
-
"content",
|
|
189
|
-
"slides",
|
|
190
|
-
"slides-2",
|
|
191
|
-
"dispatch",
|
|
192
|
-
],
|
|
179
|
+
recommendedTemplateUses: ["brain", "content", "slides", "dispatch"],
|
|
193
180
|
oauth: googleOAuth([
|
|
194
181
|
...GOOGLE_IDENTITY_SCOPES,
|
|
195
182
|
"https://www.googleapis.com/auth/documents.readonly",
|
|
@@ -210,13 +197,7 @@ export const WORKSPACE_CONNECTION_PROVIDERS = [
|
|
|
210
197
|
description: "Google Slides presentations for reference imports, visual context, and presentation workflows.",
|
|
211
198
|
credentialKeys: GOOGLE_CREDENTIAL_KEYS,
|
|
212
199
|
capabilities: ["search", "import", "docs"],
|
|
213
|
-
recommendedTemplateUses: [
|
|
214
|
-
"brain",
|
|
215
|
-
"content",
|
|
216
|
-
"slides",
|
|
217
|
-
"slides-2",
|
|
218
|
-
"dispatch",
|
|
219
|
-
],
|
|
200
|
+
recommendedTemplateUses: ["brain", "content", "slides", "dispatch"],
|
|
220
201
|
oauth: googleOAuth([
|
|
221
202
|
...GOOGLE_IDENTITY_SCOPES,
|
|
222
203
|
"https://www.googleapis.com/auth/presentations.readonly",
|
|
@@ -33,9 +33,9 @@ declare const _default: import("../../action.js").ActionDefinition<{
|
|
|
33
33
|
idempotencyKey: string;
|
|
34
34
|
deleted: boolean;
|
|
35
35
|
} | {
|
|
36
|
-
id?: undefined;
|
|
37
36
|
error: string;
|
|
38
37
|
url?: undefined;
|
|
38
|
+
id?: undefined;
|
|
39
39
|
provider?: undefined;
|
|
40
40
|
configured?: undefined;
|
|
41
41
|
} | {
|
|
@@ -45,8 +45,8 @@ declare const _default: import("../../action.js").ActionDefinition<{
|
|
|
45
45
|
provider: string;
|
|
46
46
|
configured?: undefined;
|
|
47
47
|
} | {
|
|
48
|
-
id?: undefined;
|
|
49
48
|
url?: undefined;
|
|
49
|
+
id?: undefined;
|
|
50
50
|
provider?: undefined;
|
|
51
51
|
error: string;
|
|
52
52
|
configured: boolean;
|
|
@@ -70,12 +70,11 @@ async function withAssetAuthorization(run, held = {}) {
|
|
|
70
70
|
if (error?.status !== 401)
|
|
71
71
|
throw error;
|
|
72
72
|
const refreshed = await assetAuthorization({ forceRefresh: true });
|
|
73
|
-
//
|
|
74
|
-
//
|
|
73
|
+
// A refresh that hands back the same token (no refresh token, a key
|
|
74
|
+
// credential) cannot change the outcome: Builder has refused the only
|
|
75
|
+
// credential we hold, so the connection needs reconnecting.
|
|
75
76
|
if (refreshed.authorization === auth.authorization) {
|
|
76
|
-
|
|
77
|
-
throw confirmedBuilderAuthorizationFailure(error);
|
|
78
|
-
throw error;
|
|
77
|
+
throw confirmedBuilderAuthorizationFailure(error);
|
|
79
78
|
}
|
|
80
79
|
held.auth = refreshed;
|
|
81
80
|
try {
|
package/dist/mcp/analytics.js
CHANGED
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
import { getAppConfig } from "../app-config/store.js";
|
|
2
2
|
import { safeValue } from "../tracking/redaction.js";
|
|
3
3
|
import { listTrackingProviders, track } from "../tracking/registry.js";
|
|
4
|
+
import { DATABASE_FAILURE_MESSAGE, isDatabaseQueryFailure, } from "./embed-redaction.js";
|
|
4
5
|
const CLIENT_INFO_META_KEY = "io.modelcontextprotocol/clientInfo";
|
|
5
6
|
const PROTOCOL_VERSION_META_KEY = "io.modelcontextprotocol/protocolVersion";
|
|
6
7
|
export const MCP_ANALYTICS_EVENTS = {
|
|
@@ -203,6 +204,12 @@ export function trackMcpOAuthLoginWallShown(ctx, args) {
|
|
|
203
204
|
});
|
|
204
205
|
}
|
|
205
206
|
export function describeMcpError(err) {
|
|
207
|
+
if (isDatabaseQueryFailure(err)) {
|
|
208
|
+
return {
|
|
209
|
+
errorType: "DrizzleQueryError",
|
|
210
|
+
errorMessage: DATABASE_FAILURE_MESSAGE,
|
|
211
|
+
};
|
|
212
|
+
}
|
|
206
213
|
if (err && typeof err === "object") {
|
|
207
214
|
const record = err;
|
|
208
215
|
const code = record.errorCode ?? record.code;
|
package/dist/mcp/build-server.js
CHANGED
|
@@ -2570,7 +2570,9 @@ export async function createMCPServerForRequest(config, identity, requestMeta) {
|
|
|
2570
2570
|
catch (err) {
|
|
2571
2571
|
failure = describeMcpError(err);
|
|
2572
2572
|
return {
|
|
2573
|
-
content: [
|
|
2573
|
+
content: [
|
|
2574
|
+
{ type: "text", text: `Error: ${failure.errorMessage}` },
|
|
2575
|
+
],
|
|
2574
2576
|
isError: true,
|
|
2575
2577
|
};
|
|
2576
2578
|
}
|
|
@@ -2604,7 +2606,10 @@ export async function createMCPServerForRequest(config, identity, requestMeta) {
|
|
|
2604
2606
|
failure = describeMcpError(err);
|
|
2605
2607
|
return {
|
|
2606
2608
|
content: [
|
|
2607
|
-
{
|
|
2609
|
+
{
|
|
2610
|
+
type: "text",
|
|
2611
|
+
text: `Error: ${failure.errorMessage}`,
|
|
2612
|
+
},
|
|
2608
2613
|
],
|
|
2609
2614
|
isError: true,
|
|
2610
2615
|
};
|
|
@@ -8,7 +8,7 @@ import { getRequestContext, getRequestOrgId, getRequestUserEmail, } from "../ser
|
|
|
8
8
|
import { describeMcpError } from "./analytics.js";
|
|
9
9
|
import { actionApprovalError, actionApprovalReviewResult, buildLinkArtifacts, conciseMcpAppToolText, conciseToolResultText, getMcpDirectoryWidgetResourceUri, hasVerifiedMcpUserIdentity, MCP_ACTION_APPROVAL_INPUT_KEY, mcpAppEmbedOpenLinkMeta, mcpAppOnlyEmbedStartMeta, mcpAppStructuredContent, mcpAppWidgetsEnabledForIdentity, mcpDirectoryWidgetCapabilityForTool, mcpDirectoryWidgetSourceMeta, mcpResponseStructuredContent, mcpResultHasContent, metadataObject, modelVisibleToolResponse, openAiToolResultMeta, PRESERVE_MCP_OBJECT_RESULT, resolveMcpAppResourceSafely, returnsQueryPayload, textPagingInputs, upstreamStatusText, withheldWriteResponse, withoutMcpAppEmbedTicket, withServerMintedMcpAppEmbedStart, } from "./build-server.js";
|
|
10
10
|
import { mcpCallAttribution } from "./call-attribution.js";
|
|
11
|
-
import { describeThrownForLog, HIDDEN_EMBED_URL, isEmbedStartUrl, redactEmbedText, } from "./embed-redaction.js";
|
|
11
|
+
import { DATABASE_FAILURE_MESSAGE, describeThrownForLog, HIDDEN_EMBED_URL, isDatabaseQueryFailure, isEmbedStartUrl, redactEmbedText, } from "./embed-redaction.js";
|
|
12
12
|
import { embedCredentialSources, mcpActionErrorCode, redactEmbedCredentials, returnedResult, runMcpActionCallUnredacted, toJsonData, UnreadableResultError, } from "./execute-action-call.js";
|
|
13
13
|
import { mcpResponseOutputCheck } from "./output-contract.js";
|
|
14
14
|
export function createDirectActionCall(scope) {
|
|
@@ -44,6 +44,8 @@ export function createDirectActionCall(scope) {
|
|
|
44
44
|
? undefined
|
|
45
45
|
: callableActions[name]?.mcpOutputContract;
|
|
46
46
|
const errorText = (err) => {
|
|
47
|
+
if (isDatabaseQueryFailure(err))
|
|
48
|
+
return DATABASE_FAILURE_MESSAGE;
|
|
47
49
|
const projectedError = directoryCatalog && config.directoryProfile?.projectResult
|
|
48
50
|
? config.directoryProfile.projectResult(name, err.message)
|
|
49
51
|
: err.message;
|
|
@@ -10,6 +10,9 @@
|
|
|
10
10
|
* that a value was hidden rather than finding its key missing.
|
|
11
11
|
*/
|
|
12
12
|
export declare const HIDDEN_EMBED_URL = "[hidden embed URL]";
|
|
13
|
+
export declare const DATABASE_FAILURE_MESSAGE = "The database rejected the request.";
|
|
14
|
+
/** Drizzle puts a failed query's SQL and bound parameters in its message. */
|
|
15
|
+
export declare function isDatabaseQueryFailure(error: unknown): boolean;
|
|
13
16
|
/** Replaces a value already being serialized higher up the same result. */
|
|
14
17
|
export declare const CIRCULAR_RESULT = "[circular result]";
|
|
15
18
|
/** Keys removed from every result object, whatever their value. */
|
|
@@ -11,6 +11,11 @@ import { decodeHTML } from "entities/decode";
|
|
|
11
11
|
* that a value was hidden rather than finding its key missing.
|
|
12
12
|
*/
|
|
13
13
|
export const HIDDEN_EMBED_URL = "[hidden embed URL]";
|
|
14
|
+
export const DATABASE_FAILURE_MESSAGE = "The database rejected the request.";
|
|
15
|
+
/** Drizzle puts a failed query's SQL and bound parameters in its message. */
|
|
16
|
+
export function isDatabaseQueryFailure(error) {
|
|
17
|
+
return error instanceof Error && error.message.startsWith("Failed query:");
|
|
18
|
+
}
|
|
14
19
|
/** Replaces a value already being serialized higher up the same result. */
|
|
15
20
|
export const CIRCULAR_RESULT = "[circular result]";
|
|
16
21
|
/** Keys removed from every result object, whatever their value. */
|