@agent-native/core 0.210.1-nightly-20261020093350 → 0.210.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (59) hide show
  1. package/dist/a2a/server.js +2 -1
  2. package/dist/agent/contain-principal.js +2 -3
  3. package/dist/agent/engine/ai-sdk-engine.js +29 -20
  4. package/dist/agent/engine/anthropic-engine.js +10 -1
  5. package/dist/agent/engine/builder-engine.js +28 -14
  6. package/dist/agent/engine/types.d.ts +11 -0
  7. package/dist/agent/engine/types.js +8 -0
  8. package/dist/agent/execution-fence.d.ts +5 -0
  9. package/dist/agent/execution-fence.js +15 -0
  10. package/dist/agent/harness/background.js +28 -7
  11. package/dist/agent/production-agent.d.ts +4 -3
  12. package/dist/agent/production-agent.js +20 -3
  13. package/dist/agent/run-manager.d.ts +9 -2
  14. package/dist/agent/run-manager.js +290 -43
  15. package/dist/agent/run-store.d.ts +3 -2
  16. package/dist/agent/run-store.fencing.worker.js +19 -1
  17. package/dist/agent/run-store.js +228 -68
  18. package/dist/chat-threads/schema-migrations.js +8 -0
  19. package/dist/chat-threads/store.d.ts +33 -0
  20. package/dist/chat-threads/store.js +151 -16
  21. package/dist/collab/routes.d.ts +1 -1
  22. package/dist/collab/struct-routes.d.ts +1 -1
  23. package/dist/db/schema-snapshot.generated.js +2 -1
  24. package/dist/file-upload/actions/upload-image.d.ts +2 -2
  25. package/dist/localization/core-messages/supplemental/ar-SA.js +1 -0
  26. package/dist/localization/core-messages/supplemental/de-DE.js +1 -0
  27. package/dist/localization/core-messages/supplemental/en-US.js +1 -0
  28. package/dist/localization/core-messages/supplemental/es-ES.js +1 -0
  29. package/dist/localization/core-messages/supplemental/fr-FR.js +1 -0
  30. package/dist/localization/core-messages/supplemental/hi-IN.js +1 -0
  31. package/dist/localization/core-messages/supplemental/ja-JP.js +1 -0
  32. package/dist/localization/core-messages/supplemental/ko-KR.js +1 -0
  33. package/dist/localization/core-messages/supplemental/pt-BR.js +1 -0
  34. package/dist/localization/core-messages/supplemental/zh-CN.js +1 -0
  35. package/dist/localization/core-messages/supplemental/zh-TW.js +1 -0
  36. package/dist/localization/default-messages.d.ts +14 -0
  37. package/dist/localization/default-messages.js +15 -1
  38. package/dist/localization/mcp-settings-messages.d.ts +1 -0
  39. package/dist/mcp/screen-memory-stdio.d.ts +7 -7
  40. package/dist/mcp-client/actions/get-managed-mcp-oauth-readiness.d.ts +5 -0
  41. package/dist/mcp-client/actions/get-managed-mcp-oauth-readiness.js +17 -0
  42. package/dist/mcp-client/oauth-routes.d.ts +3 -0
  43. package/dist/mcp-client/oauth-routes.js +15 -4
  44. package/dist/notifications/routes.d.ts +3 -3
  45. package/dist/org/index.d.ts +1 -1
  46. package/dist/progress/routes.d.ts +1 -1
  47. package/dist/provider-api/actions/custom-provider-registration.d.ts +8 -8
  48. package/dist/provider-api/actions/provider-api.d.ts +10 -10
  49. package/dist/secrets/register-framework-secrets.js +25 -0
  50. package/dist/secrets/routes.d.ts +6 -6
  51. package/dist/server/action-discovery.js +6 -0
  52. package/dist/server/agent-chat-plugin.js +100 -16
  53. package/dist/server/agent-engine-api-key-route.d.ts +1 -1
  54. package/dist/server/agent-engine-default-model-route.d.ts +1 -1
  55. package/dist/server/agent-teams.d.ts +3 -0
  56. package/dist/server/agent-teams.js +192 -30
  57. package/dist/server/google-oauth.d.ts +4 -1
  58. package/dist/server/google-oauth.js +4 -2
  59. package/package.json +4 -4
@@ -1,5 +1,7 @@
1
1
  import crypto from "node:crypto";
2
2
  import { deleteCookie, defineEventHandler, getMethod, getQuery, getRequestHeader, setChunkedCookie, setResponseStatus, } from "h3";
3
+ import { mcpSettingsMessagesForLocale } from "../localization/mcp-settings-messages.js";
4
+ import { resolveLocaleFromRequest } from "../localization/server.js";
3
5
  import { encryptSecretValue } from "../secrets/crypto.js";
4
6
  import { CredentialStoreUnavailableError, resolveSecretPairs, } from "../server/credential-provider.js";
5
7
  import { getH3App } from "../server/framework-request-handler.js";
@@ -17,7 +19,8 @@ import { normalizeMcpPrincipal } from "./principal.js";
17
19
  import { addOAuthRemoteServer, listRemoteServers, normalizeServerName, replaceOAuthRemoteServer, validateRemoteUrl, } from "./remote-store.js";
18
20
  const getOrgContext = (...args) => import("../org/context.js").then(({ getOrgContext }) => getOrgContext(...args));
19
21
  function safeReturnPath(raw) {
20
- return normalizeAppPath(raw) ?? "/";
22
+ const basePath = getAppBasePath();
23
+ return normalizeAppPath(raw, basePath) ?? (basePath || "/");
21
24
  }
22
25
  async function getSessionForEvent(event) {
23
26
  const { getSession } = await import("../server/auth.js");
@@ -214,7 +217,15 @@ async function handleMcpOAuthStart(event) {
214
217
  }
215
218
  if (managedMcpOAuthClientFor(urlCheck.url)?.requireClientCredentials &&
216
219
  !clientInformation) {
217
- return refuse(event, 400, MCP_OAUTH_MANAGED_CLIENT_MISSING_MESSAGE);
220
+ return mcpOAuthStartFailureResponse(event, {
221
+ status: 400,
222
+ body: { error: MCP_OAUTH_MANAGED_CLIENT_MISSING_MESSAGE },
223
+ }, {
224
+ backHref: safeReturnPath(returnUrl || `${getAppBasePath()}/settings/integrations`),
225
+ backLabel: mcpSettingsMessagesForLocale(resolveLocaleFromRequest({
226
+ acceptLanguage: getRequestHeader(event, "accept-language"),
227
+ }).locale).mcpOAuthBackToApp,
228
+ });
218
229
  }
219
230
  // A pre-registered client has a fixed redirect URI on the shared workspace
220
231
  // callback. A dynamically registered client registers whatever URI this
@@ -350,12 +361,12 @@ function authorizationServerLabel(error) {
350
361
  return raw;
351
362
  }
352
363
  }
353
- export function mcpOAuthStartFailureResponse(event, failure) {
364
+ export function mcpOAuthStartFailureResponse(event, failure, htmlOptions) {
354
365
  if (!wantsHtmlResponse(event)) {
355
366
  setResponseStatus(event, failure.status);
356
367
  return failure.body;
357
368
  }
358
- return withStagedCookies(event, oauthErrorPage(failure.body.error, failure.status));
369
+ return withStagedCookies(event, oauthErrorPage(failure.body.error, failure.status, htmlOptions));
359
370
  }
360
371
  function refuse(event, status, error) {
361
372
  return mcpOAuthStartFailureResponse(event, { status, body: { error } });
@@ -1,13 +1,13 @@
1
1
  export declare function createNotificationsHandler(): import("h3").EventHandlerWithFetch<import("h3").EventHandlerRequest, Promise<"" | import("./types.js").Notification[] | {
2
2
  count: number;
3
3
  updated?: undefined;
4
- ok?: undefined;
5
4
  error?: undefined;
5
+ ok?: undefined;
6
6
  } | {
7
7
  count?: undefined;
8
8
  updated: number;
9
- ok?: undefined;
10
9
  error?: undefined;
10
+ ok?: undefined;
11
11
  } | {
12
12
  count?: undefined;
13
13
  updated?: undefined;
@@ -16,6 +16,6 @@ export declare function createNotificationsHandler(): import("h3").EventHandlerW
16
16
  } | {
17
17
  count?: undefined;
18
18
  updated?: undefined;
19
- ok: boolean;
20
19
  error?: undefined;
20
+ ok: boolean;
21
21
  }>>;
@@ -164,8 +164,8 @@ export declare const getMyOrgHandler: import("h3").EventHandlerWithFetch<import(
164
164
  export declare const createOrgHandler: import("h3").EventHandlerWithFetch<import("h3").EventHandlerRequest, Promise<{
165
165
  name?: undefined;
166
166
  success: boolean;
167
- role?: undefined;
168
167
  id?: undefined;
168
+ role?: undefined;
169
169
  } | {
170
170
  success?: undefined;
171
171
  id: string;
@@ -2,6 +2,6 @@ export declare function createProgressHandler(): import("h3").EventHandlerWithFe
2
2
  error: string;
3
3
  ok?: undefined;
4
4
  } | {
5
- error?: undefined;
6
5
  ok: boolean;
6
+ error?: undefined;
7
7
  }>>;
@@ -75,6 +75,7 @@ export declare function createCustomProviderRegistrationAction<TSchema extends Z
75
75
  user: "user";
76
76
  }>>;
77
77
  }, z.core.$strip>>, {
78
+ provider?: undefined;
78
79
  id?: undefined;
79
80
  deleted?: undefined;
80
81
  found?: undefined;
@@ -88,46 +89,45 @@ export declare function createCustomProviderRegistrationAction<TSchema extends Z
88
89
  updatedAt: number;
89
90
  }[];
90
91
  count: number;
91
- provider?: undefined;
92
92
  registered?: undefined;
93
93
  label?: undefined;
94
94
  message?: undefined;
95
95
  } | {
96
96
  id?: undefined;
97
+ count?: undefined;
97
98
  deleted?: undefined;
98
99
  providers?: undefined;
99
- count?: undefined;
100
100
  found: boolean;
101
101
  provider: import("../custom-registry.js").CustomProviderConfig;
102
102
  registered?: undefined;
103
103
  label?: undefined;
104
104
  message?: undefined;
105
105
  } | {
106
+ provider?: undefined;
107
+ count?: undefined;
106
108
  deleted?: undefined;
107
109
  providers?: undefined;
108
- count?: undefined;
109
- provider?: undefined;
110
110
  found: boolean;
111
111
  id: string;
112
112
  registered?: undefined;
113
113
  label?: undefined;
114
114
  message?: undefined;
115
115
  } | {
116
+ provider?: undefined;
117
+ count?: undefined;
116
118
  found?: undefined;
117
119
  providers?: undefined;
118
- count?: undefined;
119
- provider?: undefined;
120
120
  deleted: boolean;
121
121
  id: string;
122
122
  registered?: undefined;
123
123
  label?: undefined;
124
124
  message?: undefined;
125
125
  } | {
126
+ provider?: undefined;
127
+ count?: undefined;
126
128
  deleted?: undefined;
127
129
  found?: undefined;
128
130
  providers?: undefined;
129
- count?: undefined;
130
- provider?: undefined;
131
131
  registered: boolean;
132
132
  id: string;
133
133
  label: string;
@@ -316,10 +316,10 @@ export declare function createProviderApiActions(runtime: Pick<ProviderApiRuntim
316
316
  notes?: string | undefined;
317
317
  scope?: "org" | "user" | undefined;
318
318
  }, {
319
- message?: undefined;
320
- deleted?: undefined;
321
319
  id?: undefined;
322
320
  provider?: undefined;
321
+ message?: undefined;
322
+ deleted?: undefined;
323
323
  found?: undefined;
324
324
  providers: {
325
325
  id: string;
@@ -334,41 +334,41 @@ export declare function createProviderApiActions(runtime: Pick<ProviderApiRuntim
334
334
  registered?: undefined;
335
335
  label?: undefined;
336
336
  } | {
337
+ id?: undefined;
338
+ count?: undefined;
337
339
  message?: undefined;
338
340
  deleted?: undefined;
339
- id?: undefined;
340
341
  providers?: undefined;
341
- count?: undefined;
342
342
  found: boolean;
343
343
  provider: import("../custom-registry.js").CustomProviderConfig;
344
344
  registered?: undefined;
345
345
  label?: undefined;
346
346
  } | {
347
+ provider?: undefined;
348
+ count?: undefined;
347
349
  message?: undefined;
348
350
  deleted?: undefined;
349
- provider?: undefined;
350
351
  providers?: undefined;
351
- count?: undefined;
352
352
  found: boolean;
353
353
  id: string;
354
354
  registered?: undefined;
355
355
  label?: undefined;
356
356
  } | {
357
- message?: undefined;
358
357
  provider?: undefined;
358
+ count?: undefined;
359
+ message?: undefined;
359
360
  found?: undefined;
360
361
  providers?: undefined;
361
- count?: undefined;
362
362
  deleted: boolean;
363
363
  id: string;
364
364
  registered?: undefined;
365
365
  label?: undefined;
366
366
  } | {
367
- deleted?: undefined;
368
367
  provider?: undefined;
368
+ count?: undefined;
369
+ deleted?: undefined;
369
370
  found?: undefined;
370
371
  providers?: undefined;
371
- count?: undefined;
372
372
  registered: boolean;
373
373
  id: string;
374
374
  label: string;
@@ -203,6 +203,31 @@ export function registerFrameworkSecrets() {
203
203
  docsUrl: "https://developers.notion.com/docs/authorization",
204
204
  },
205
205
  ];
206
+ const hubspotMcpAuthAppDocsUrl = "https://developers.hubspot.com/docs/apps/developer-platform/build-apps/integrate-with-the-remote-hubspot-mcp-server";
207
+ for (const credential of [
208
+ { key: "HUBSPOT_MCP_CLIENT_ID", label: "HubSpot MCP OAuth client ID" },
209
+ {
210
+ key: "HUBSPOT_MCP_CLIENT_SECRET",
211
+ label: "HubSpot MCP OAuth client secret",
212
+ },
213
+ ]) {
214
+ registerSecretUsage(credential.key, [
215
+ {
216
+ feature: "HubSpot MCP connections",
217
+ effectWhenRemoved: "Workspace members cannot start new HubSpot MCP connections.",
218
+ },
219
+ ]);
220
+ if (!getRequiredSecret(credential.key)) {
221
+ registerRequiredSecret({
222
+ ...credential,
223
+ description: "Workspace-managed HubSpot MCP Auth App credential. Each member authorizes their own HubSpot account separately.",
224
+ docsUrl: hubspotMcpAuthAppDocsUrl,
225
+ scope: "workspace",
226
+ kind: "api-key",
227
+ required: false,
228
+ });
229
+ }
230
+ }
206
231
  for (const provider of workspaceOAuthProviders) {
207
232
  const prefix = provider.credentialPrefix;
208
233
  for (const credential of [
@@ -65,31 +65,31 @@ export declare function createWriteSecretHandler(): import("h3").EventHandlerWit
65
65
  status?: undefined;
66
66
  ok?: undefined;
67
67
  } | {
68
+ error?: undefined;
68
69
  errorCode?: undefined;
69
70
  ok: boolean;
70
71
  status: string;
71
- error?: undefined;
72
72
  } | {
73
73
  error: string;
74
74
  removed?: undefined;
75
75
  ok?: undefined;
76
76
  } | {
77
+ error?: undefined;
77
78
  ok: boolean;
78
79
  removed: boolean;
79
- error?: undefined;
80
80
  }>>;
81
81
  export declare function createTestSecretHandler(): import("h3").EventHandlerWithFetch<import("h3").EventHandlerRequest, Promise<{
82
82
  error: string;
83
83
  note?: undefined;
84
84
  ok?: undefined;
85
85
  } | {
86
+ error?: undefined;
86
87
  ok: boolean;
87
88
  note?: undefined;
88
- error?: undefined;
89
89
  } | {
90
+ error?: undefined;
90
91
  ok: boolean;
91
92
  note: string;
92
- error?: undefined;
93
93
  } | {
94
94
  note?: undefined;
95
95
  ok: boolean;
@@ -117,11 +117,11 @@ export declare function createAdHocSecretHandler(): import("h3").EventHandlerWit
117
117
  } | {
118
118
  error: string;
119
119
  } | {
120
+ error?: undefined;
120
121
  ok: boolean;
121
122
  key: string;
122
- error?: undefined;
123
123
  } | {
124
+ error?: undefined;
124
125
  ok: boolean;
125
126
  removed: boolean;
126
- error?: undefined;
127
127
  }>>;
@@ -386,6 +386,8 @@ export const ALWAYS_ON_CORE_ACTIONS = new Set([
386
386
  "upload-image",
387
387
  "list-mcp-tools",
388
388
  "call-mcp-tool",
389
+ // Connection setup UI needs the workspace prerequisite even in minimal apps.
390
+ "get-managed-mcp-oauth-readiness",
389
391
  "get-agentkit-capabilities",
390
392
  "get-hosted-harness-config",
391
393
  "set-hosted-harness-enabled",
@@ -750,6 +752,10 @@ export function coreActionLoaderEntries() {
750
752
  "get-auth-methods",
751
753
  () => import("../user-profile/actions/get-auth-methods.js"),
752
754
  ],
755
+ [
756
+ "get-managed-mcp-oauth-readiness",
757
+ () => import("../mcp-client/actions/get-managed-mcp-oauth-readiness.js"),
758
+ ],
753
759
  ["set-password", () => import("../user-profile/actions/set-password.js")],
754
760
  [
755
761
  "change-password",
@@ -20,11 +20,11 @@ import { SYSTEM_PROMPT_CACHE_SPLIT } from "../agent/engine/prompt-cache.js";
20
20
  import { PROVIDER_TO_ENV } from "../agent/engine/provider-env-vars.js";
21
21
  import { currentExecutionFence, withExecutionFence, } from "../agent/execution-fence.js";
22
22
  import { hostedHarnessSystemPrompt } from "../agent/harness/hosted.js";
23
- import { createProductionAgentHandler, endsAtContinuationBoundary, actionsToEngineTools, executeAgentToolCall, filterActionsByAllowedNames, normalizeAgentActionSurfaceResolution, getActiveRunForThreadAsync, abortRunDurably, abortRunAtSafeBoundaryDurably, abortTurnByRefDurably, abortTurnDurably, subscribeToRun, } from "../agent/production-agent.js";
23
+ import { createProductionAgentHandler, endsAtContinuationBoundary, actionsToEngineTools, executeAgentToolCall, filterActionsByAllowedNames, normalizeAgentActionSurfaceResolution, getActiveRunForThreadAsync, abortRunDurably, abortRunAndTurnDurably, abortRunAtSafeBoundaryDurably, abortTurnByRefDurably, abortTurnDurably, subscribeToRun, } from "../agent/production-agent.js";
24
24
  import { applyProviderModelSelection, providerForEngineName, resolveProviderModelSelectionAtScope, } from "../agent/provider-model-selection.js";
25
25
  import { startRun } from "../agent/run-manager.js";
26
26
  import { callerHasRunAccess, callerHasThreadAccess, } from "../agent/run-ownership.js";
27
- import { readBackgroundRunClaim } from "../agent/run-store.js";
27
+ import { getRunById, readBackgroundRunClaim } from "../agent/run-store.js";
28
28
  import { buildAssistantMessageFromRunJournal } from "../agent/run-thread-projector.js";
29
29
  import { buildCurrentTimeUserContext, buildRuntimeContextPrompt, } from "../agent/runtime-context.js";
30
30
  import { buildAssistantMessage, buildUserMessage, applySubmittedUserMessage, containsInlineAttachmentPayload, foldAgentChatRunCompletion, extractThreadMeta, foldUnstartedTurnFailure, mergeThreadDataForClientSave, } from "../agent/thread-data-builder.js";
@@ -33,7 +33,7 @@ import { attachToolSearch } from "../agent/tool-search.js";
33
33
  import { getAppConfig } from "../app-config/index.js";
34
34
  import { readAppStateForCurrentTab } from "../application-state/script-helpers.js";
35
35
  import { runChatThreadDataMigrations } from "../chat-threads/migrations.js";
36
- import { InlineAttachmentDataNotPersistableError, QueuedSubmissionAdmissionError, adoptThreadScopeIfUnscoped, blockQueuedSubmissionReceipt, createThread, forkThread, getThread, getQueuedSubmissionReceipt, listQueuedSubmissionReceipts, markQueuedSubmissionSubmitted, queuedSubmissionBehaviorMatches, registerChatThreadsShareable, retryQueuedSubmissionReceipt, resolveRunThreadScope, resolveThreadAccess, listThreads, searchThreads, renameThread, createThreadShareLink, getThreadByShareToken, getThreadShareState, revokeThreadShareLink, setThreadArchived, setThreadPinned, setThreadScope, updateThreadData, withThreadDataLock, deleteThread, mutateThreadQueuedMessages, setThreadSourceIfMissing, isAppOwnedChatScope, threadScopeMismatch, } from "../chat-threads/store.js";
36
+ import { InlineAttachmentDataNotPersistableError, QueuedSubmissionAdmissionError, adoptThreadScopeIfUnscoped, blockQueuedSubmissionReceipt, clearQueuedSubmissionInterruptRun, createThread, forkThread, getThread, getQueuedSubmissionReceipt, listQueuedSubmissionReceipts, markQueuedSubmissionSubmitted, queuedSubmissionBehaviorMatches, recordQueuedSubmissionInterruptRun, renewQueuedSubmissionInterruptClaim, registerChatThreadsShareable, retryQueuedSubmissionReceipt, resolveRunThreadScope, resolveThreadAccess, listThreads, searchThreads, renameThread, createThreadShareLink, getThreadByShareToken, getThreadShareState, revokeThreadShareLink, setThreadArchived, setThreadPinned, setThreadScope, updateThreadData, withThreadDataLock, deleteThread, mutateThreadQueuedMessages, setThreadSourceIfMissing, isAppOwnedChatScope, threadScopeMismatch, } from "../chat-threads/store.js";
37
37
  import { isCheckpointRestorePath } from "../checkpoints/route-match.js";
38
38
  import { createDbAdminAgentTools } from "../db-admin/agent-tools.js";
39
39
  import { isTransientDatabaseError } from "../db/client.js";
@@ -420,7 +420,7 @@ export async function runA2AAgentLoopWithRunManager(runOptions, pluginOptions, t
420
420
  model: runOptions.model,
421
421
  engineName: runOptions.engine.name,
422
422
  });
423
- let cancellation = Promise.resolve(false);
423
+ let cancellation = Promise.resolve({ status: "fulfilled", aborted: false });
424
424
  const abortFromTask = () => {
425
425
  const reason = taskSignal?.reason;
426
426
  const reasonCode = reason && typeof reason === "object" && "code" in reason
@@ -431,7 +431,7 @@ export async function runA2AAgentLoopWithRunManager(runOptions, pluginOptions, t
431
431
  run.abort.abort("execution_lease_lost");
432
432
  return;
433
433
  }
434
- cancellation = abortRunDurably(runId, clientAbortReason(reason));
434
+ cancellation = abortRunDurably(runId, clientAbortReason(reason)).then((aborted) => ({ status: "fulfilled", aborted }), (error) => ({ status: "rejected", error }));
435
435
  };
436
436
  if (taskSignal?.aborted) {
437
437
  abortFromTask();
@@ -440,8 +440,17 @@ export async function runA2AAgentLoopWithRunManager(runOptions, pluginOptions, t
440
440
  taskSignal?.addEventListener("abort", abortFromTask, { once: true });
441
441
  }
442
442
  try {
443
- await run.finalized;
444
- await cancellation;
443
+ const finalization = await run.finalized.then(() => ({ status: "fulfilled" }), (error) => ({ status: "rejected", error }));
444
+ const cancellationResult = await cancellation;
445
+ if (finalization.status === "rejected" &&
446
+ cancellationResult.status === "rejected") {
447
+ throw new AggregateError([finalization.error, cancellationResult.error], "A2A run finalization and durable cancellation both failed");
448
+ }
449
+ if (finalization.status === "rejected")
450
+ throw finalization.error;
451
+ if (cancellationResult.status === "rejected") {
452
+ throw cancellationResult.error;
453
+ }
445
454
  }
446
455
  finally {
447
456
  taskSignal?.removeEventListener("abort", abortFromTask);
@@ -5262,18 +5271,28 @@ Non-code requests are still fine on this surface: read data, navigate the UI, su
5262
5271
  // Empty/invalid body — keep the default user abort reason.
5263
5272
  }
5264
5273
  if (safeBoundary) {
5265
- const aborted = await abortRunAtSafeBoundaryDurably(runId, reason);
5266
- if (aborted && USER_STOP_ABORT_REASONS.has(reason)) {
5267
- await abortTurnDurably(runId, reason);
5274
+ const isUserStop = USER_STOP_ABORT_REASONS.has(reason);
5275
+ const aborted = isUserStop
5276
+ ? await abortRunAndTurnDurably(runId, reason, {
5277
+ safeBoundary: true,
5278
+ })
5279
+ : await abortRunAtSafeBoundaryDurably(runId, reason);
5280
+ if (isUserStop && !aborted) {
5281
+ const latest = await getRunById(runId);
5282
+ if (latest?.status === "aborted") {
5283
+ await abortTurnDurably(runId, reason);
5284
+ }
5268
5285
  }
5269
5286
  return { ok: true, aborted };
5270
5287
  }
5271
5288
  // Recovery starts as soon as this response resolves, so wait for
5272
5289
  // the cross-isolate abort + terminal event to be durable. Returning
5273
5290
  // early lets Retry collide with the still-running row.
5274
- await abortRunDurably(runId, reason);
5275
5291
  if (USER_STOP_ABORT_REASONS.has(reason)) {
5276
- await abortTurnDurably(runId, reason);
5292
+ await abortRunAndTurnDurably(runId, reason);
5293
+ }
5294
+ else {
5295
+ await abortRunDurably(runId, reason);
5277
5296
  }
5278
5297
  return { ok: true };
5279
5298
  }
@@ -6539,6 +6558,67 @@ Non-code requests are still fine on this surface: read data, navigate the UI, su
6539
6558
  }
6540
6559
  const claimedMessage = claim.claimedMessage;
6541
6560
  result.claimed += 1;
6561
+ const claimedReceipt = await getQueuedSubmissionReceipt(receipt.threadId, receipt.messageId);
6562
+ if (!claimedReceipt || claimedReceipt.state !== "queued") {
6563
+ if (claimedReceipt?.state === "submitted") {
6564
+ result.submitted += 1;
6565
+ }
6566
+ else {
6567
+ result.deferred += 1;
6568
+ }
6569
+ await mutateThreadQueuedMessages(receipt.threadId, {
6570
+ type: "release",
6571
+ messageId: receipt.messageId,
6572
+ claimId,
6573
+ });
6574
+ claimId = undefined;
6575
+ continue;
6576
+ }
6577
+ const finishInterrupt = async (runId) => {
6578
+ if (!(await renewQueuedSubmissionInterruptClaim({
6579
+ threadId: receipt.threadId,
6580
+ messageId: receipt.messageId,
6581
+ runId,
6582
+ claimId: claimId,
6583
+ }))) {
6584
+ return false;
6585
+ }
6586
+ const aborted = await abortRunAtSafeBoundaryDurably(runId, "user");
6587
+ if (!aborted) {
6588
+ const latest = await getRunById(runId);
6589
+ if (latest?.threadId !== receipt.threadId ||
6590
+ !["completed", "truncated", "errored", "aborted"].includes(latest.status)) {
6591
+ return false;
6592
+ }
6593
+ }
6594
+ await abortTurnDurably(runId, "user");
6595
+ if (!(await waitForStableIdle(receipt.threadId)))
6596
+ return false;
6597
+ const cleared = await clearQueuedSubmissionInterruptRun({
6598
+ threadId: receipt.threadId,
6599
+ messageId: receipt.messageId,
6600
+ runId,
6601
+ claimId: claimId,
6602
+ });
6603
+ if (!cleared) {
6604
+ throw new Error(`Queued interruption target ${runId} could not be cleared by its current claim`);
6605
+ }
6606
+ return true;
6607
+ };
6608
+ if (claimedReceipt.interruptRunId) {
6609
+ const stableIdle = await finishInterrupt(claimedReceipt.interruptRunId);
6610
+ if (!stableIdle) {
6611
+ await retryQueuedSubmissionReceipt(receipt.threadId, receipt.messageId, Date.now() + 10_000, { claimId });
6612
+ await mutateThreadQueuedMessages(receipt.threadId, {
6613
+ type: "release",
6614
+ messageId: receipt.messageId,
6615
+ claimId,
6616
+ });
6617
+ claimId = undefined;
6618
+ result.deferred += 1;
6619
+ continue;
6620
+ }
6621
+ }
6542
6622
  const active = await getActiveRunForThreadAsync(receipt.threadId);
6543
6623
  if (active?.inFlight) {
6544
6624
  if (claimedMessage.interruptActiveRun !== true) {
@@ -6552,8 +6632,13 @@ Non-code requests are still fine on this surface: read data, navigate the UI, su
6552
6632
  result.deferred += 1;
6553
6633
  continue;
6554
6634
  }
6555
- const aborted = await abortRunAtSafeBoundaryDurably(active.runId, "user");
6556
- if (!aborted) {
6635
+ const recorded = await recordQueuedSubmissionInterruptRun({
6636
+ threadId: receipt.threadId,
6637
+ messageId: receipt.messageId,
6638
+ runId: active.runId,
6639
+ claimId,
6640
+ });
6641
+ if (!recorded) {
6557
6642
  await retryQueuedSubmissionReceipt(receipt.threadId, receipt.messageId, Date.now() + 10_000, { claimId });
6558
6643
  await mutateThreadQueuedMessages(receipt.threadId, {
6559
6644
  type: "release",
@@ -6564,8 +6649,7 @@ Non-code requests are still fine on this surface: read data, navigate the UI, su
6564
6649
  result.deferred += 1;
6565
6650
  continue;
6566
6651
  }
6567
- await abortTurnDurably(active.runId, "user");
6568
- if (!(await waitForStableIdle(receipt.threadId))) {
6652
+ if (!(await finishInterrupt(active.runId))) {
6569
6653
  await retryQueuedSubmissionReceipt(receipt.threadId, receipt.messageId, Date.now() + 10_000, { claimId });
6570
6654
  await mutateThreadQueuedMessages(receipt.threadId, {
6571
6655
  type: "release",
@@ -66,7 +66,6 @@ export declare function createAgentEngineApiKeyHandler(): import("h3").EventHand
66
66
  error: any;
67
67
  code: any;
68
68
  } | {
69
- error?: undefined;
70
69
  errorCode?: undefined;
71
70
  code?: undefined;
72
71
  ok: boolean;
@@ -74,5 +73,6 @@ export declare function createAgentEngineApiKeyHandler(): import("h3").EventHand
74
73
  baseUrlKey?: string;
75
74
  scope: AgentEngineApiKeyScope;
76
75
  defaultModel?: import("./agent-engine-default-model-route.js").SavedKeyDefaultModelOutcome;
76
+ error?: undefined;
77
77
  }>>;
78
78
  export {};
@@ -16,9 +16,9 @@ export declare function createAgentEngineDisconnectHandler(): import("h3").Event
16
16
  error: any;
17
17
  scope?: undefined;
18
18
  } | {
19
+ error?: undefined;
19
20
  ok: boolean;
20
21
  scope: "org" | "user";
21
- error?: undefined;
22
22
  }>>;
23
23
  export interface DefaultModelSelectionRequest {
24
24
  engine: string;
@@ -44,6 +44,9 @@ export interface AgentTask {
44
44
  terminalEffectsVersion?: 1;
45
45
  terminalEffectsReconciled?: boolean;
46
46
  terminalProgressStatus?: TerminalProgressStatus;
47
+ stopRequestedAt?: number;
48
+ stopRequestedRunId?: string;
49
+ stopRequestedReason?: string;
47
50
  }
48
51
  export interface AgentTeamOwnerScope {
49
52
  ownerEmail: string | null;