@agent-native/core 0.209.1-nightly-20261015153252 → 0.209.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 (37) hide show
  1. package/dist/a2a/index.d.ts +1 -1
  2. package/dist/a2a/index.js +1 -1
  3. package/dist/a2a/server.d.ts +10 -0
  4. package/dist/a2a/server.js +96 -0
  5. package/dist/agent/production-agent.d.ts +2 -0
  6. package/dist/agent/production-agent.js +43 -6
  7. package/dist/agent/submission-deadline.d.ts +1 -0
  8. package/dist/agent/submission-deadline.js +27 -0
  9. package/dist/agent/thread-data-builder.d.ts +2 -1
  10. package/dist/agent/thread-data-builder.js +37 -6
  11. package/dist/authorization/check-action.js +4 -3
  12. package/dist/chat-threads/store.d.ts +1 -0
  13. package/dist/chat-threads/store.js +34 -2
  14. package/dist/client/agent-chat.d.ts +3 -0
  15. package/dist/client/agent-chat.js +9 -0
  16. package/dist/client/chat/agentkit-agent-native.js +126 -3
  17. package/dist/client/error-format.d.ts +2 -0
  18. package/dist/client/error-format.js +10 -0
  19. package/dist/collab/routes.d.ts +1 -1
  20. package/dist/mcp/screen-memory-stdio.d.ts +7 -7
  21. package/dist/org/index.d.ts +1 -1
  22. package/dist/org/index.js +1 -1
  23. package/dist/org/workspace-app-access-outcome.d.ts +45 -0
  24. package/dist/org/workspace-app-access-outcome.js +80 -0
  25. package/dist/org/workspace-app-access-request.d.ts +2 -2
  26. package/dist/org/workspace-app-access-request.js +2 -2
  27. package/dist/org/workspace-app-access.d.ts +8 -3
  28. package/dist/org/workspace-app-access.js +122 -30
  29. package/dist/provider-api/actions/custom-provider-registration.d.ts +4 -4
  30. package/dist/provider-api/actions/provider-api.d.ts +6 -6
  31. package/dist/server/action-routes.js +10 -7
  32. package/dist/server/agent-chat-plugin.js +28 -2
  33. package/dist/server/agent-engine-ollama-models-route.d.ts +2 -2
  34. package/dist/server/auth.js +3 -3
  35. package/dist/server/realtime-token.d.ts +1 -1
  36. package/dist/triggers/actions/manage-automation.d.ts +3 -3
  37. package/package.json +4 -4
@@ -1,12 +1,12 @@
1
1
  import { AgentKitRunSlotBusyError } from "@agent-native/agentkit/client";
2
- import { agentEventSequenceEnd, isAgentKitProtocolVersion, parseAgentQueuedMessage, parseAgentRunOptions, parseAgentThreadSnapshot, persistableFilePart, } from "@agent-native/agentkit/protocol";
2
+ import { AGENT_NATIVE_SUBMISSION_DEADLINES_CAPABILITY_ID, createCapabilityUnavailableError, createCapabilityUnsupportedError, agentEventSequenceEnd, isAgentKitProtocolVersion, negotiateAgentKitProtocolVersion, parseAgentQueuedMessage, parseAgentRunOptions, parseAgentThreadSnapshot, persistableFilePart, } from "@agent-native/agentkit/protocol";
3
3
  import { failedRunIdsWithError, isRenderedRunNotStartedNotice, 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 { assertNoInlineImageBytes, stripInlineBytes, } from "../../shared/inline-bytes.js";
6
6
  import { toolInputFingerprint } from "../../shared/tool-inputs.js";
7
7
  import { agentEngineStatusUrlForChatApi, requireAgentEngineConfiguredForDispatch, } from "../agent-engine-readiness.js";
8
8
  import { agentNativePath } from "../api-path.js";
9
- import { CHAT_REQUEST_TOO_LARGE_MESSAGE } from "../error-format.js";
9
+ import { CHAT_CAPABILITY_UNAVAILABLE_MESSAGE, CHAT_CAPABILITY_UNSUPPORTED_MESSAGE, CHAT_REQUEST_TOO_LARGE_MESSAGE, } from "../error-format.js";
10
10
  import { dispatchAgentChatRunning } from "../use-agent-chat-running-threads.js";
11
11
  import { appendChatThreadScopeParams, } from "../use-chat-threads.js";
12
12
  import { AGENT_NATIVE_PROTOCOL_METADATA_KEY, createAgentKitProtocolAdapter, } from "./agentkit-protocol.js";
@@ -2801,7 +2801,7 @@ export function createAgentNativeAgentKitTransport(options = {}) {
2801
2801
  await waitForSnapshotRetry(attempt, signal);
2802
2802
  };
2803
2803
  throwIfRequestAborted(signal);
2804
- let response;
2804
+ let response = undefined;
2805
2805
  try {
2806
2806
  response = await fetcher(threadUrl, {
2807
2807
  method: "PUT",
@@ -2894,6 +2894,7 @@ export function createAgentNativeAgentKitTransport(options = {}) {
2894
2894
  queuedMessages: storedQueue(value.queuedMessages, threadId, now()),
2895
2895
  ...(message ? { message } : {}),
2896
2896
  ...(claimedMessage ? { claimedMessage } : {}),
2897
+ ...(value.submissionExpired === true ? { submissionExpired: true } : {}),
2897
2898
  };
2898
2899
  }
2899
2900
  // A server that predates `active` meaning "in flight" reports a run inside
@@ -3040,6 +3041,9 @@ export function createAgentNativeAgentKitTransport(options = {}) {
3040
3041
  type: "append",
3041
3042
  message,
3042
3043
  });
3044
+ if (result.submissionExpired) {
3045
+ throw Object.assign(new Error("The prompt expired before it could be queued."), { code: "submission_expired", retryable: false });
3046
+ }
3043
3047
  if (!result.message) {
3044
3048
  throw new TypeError("Agent chat queue mutation omitted its message.");
3045
3049
  }
@@ -3093,6 +3097,10 @@ export function createAgentNativeAgentKitTransport(options = {}) {
3093
3097
  throw error;
3094
3098
  }
3095
3099
  try {
3100
+ if (claim.submissionExpired) {
3101
+ clearPromotionClaimId(threadId, messageId);
3102
+ return { alreadyRemoved: true };
3103
+ }
3096
3104
  if (!claim.claimedMessage) {
3097
3105
  throw new TypeError("Agent chat queue claim response is invalid.");
3098
3106
  }
@@ -3151,6 +3159,19 @@ export function createAgentNativeAgentKitTransport(options = {}) {
3151
3159
  return run;
3152
3160
  }
3153
3161
  catch (error) {
3162
+ if (asRecord(error)?.code === "submission_expired") {
3163
+ try {
3164
+ await persistQueueMutation(threadId, {
3165
+ type: "remove",
3166
+ messageId,
3167
+ });
3168
+ }
3169
+ catch (removeError) {
3170
+ throw new AggregateError([error, removeError], "An expired queued prompt could not be removed.");
3171
+ }
3172
+ clearPromotionClaimId(threadId, messageId);
3173
+ return { alreadyRemoved: true };
3174
+ }
3154
3175
  try {
3155
3176
  await persistQueueMutation(threadId, {
3156
3177
  type: "release",
@@ -3349,10 +3370,112 @@ export function createAgentNativeAgentKitTransport(options = {}) {
3349
3370
  }
3350
3371
  };
3351
3372
  const subscribeToRun = protocolTransport.subscribeToRun.bind(protocolTransport);
3373
+ const discoverCapabilities = protocolTransport.discoverCapabilities?.bind(protocolTransport);
3374
+ const serverBackedCapabilities = isAgentNativeChatRuntime(runtime);
3375
+ const capabilitiesUrl = apiUrl.endsWith("/")
3376
+ ? `${apiUrl}capabilities`
3377
+ : `${apiUrl}/capabilities`;
3352
3378
  transport = {
3353
3379
  ...protocolTransport,
3354
3380
  assertAiSetupReady,
3355
3381
  startRun: (input, context) => startRunTrackingRunningState(input, context),
3382
+ async discoverCapabilities(input, context) {
3383
+ const capability = AGENT_NATIVE_SUBMISSION_DEADLINES_CAPABILITY_ID;
3384
+ const discovery = discoverCapabilities
3385
+ ? await discoverCapabilities(input, context)
3386
+ : (() => {
3387
+ const legacy = protocolTransport.capabilities ?? {};
3388
+ return {
3389
+ protocol: negotiateAgentKitProtocolVersion(input.protocol, {
3390
+ correlationId: context?.correlationId,
3391
+ }),
3392
+ capabilities: Object.entries(legacy).reduce((descriptors, [id, state]) => {
3393
+ if (typeof state !== "boolean")
3394
+ return descriptors;
3395
+ const capabilityId = id;
3396
+ descriptors.push(state
3397
+ ? { id: capabilityId, state: "available" }
3398
+ : {
3399
+ id: capabilityId,
3400
+ state: "unsupported",
3401
+ error: createCapabilityUnsupportedError(capabilityId),
3402
+ });
3403
+ return descriptors;
3404
+ }, []),
3405
+ discoveredAt: new Date().toISOString(),
3406
+ legacy,
3407
+ };
3408
+ })();
3409
+ let supported = false;
3410
+ let unavailableError;
3411
+ if (serverBackedCapabilities) {
3412
+ const createUnavailableError = () => createCapabilityUnavailableError(capability, {
3413
+ message: CHAT_CAPABILITY_UNAVAILABLE_MESSAGE,
3414
+ });
3415
+ let response = undefined;
3416
+ try {
3417
+ response = await fetcher(capabilitiesUrl, {
3418
+ method: "GET",
3419
+ headers: { Accept: "application/json" },
3420
+ credentials: "same-origin",
3421
+ cache: "no-store",
3422
+ ...(context?.signal ? { signal: context.signal } : {}),
3423
+ });
3424
+ }
3425
+ catch (error) {
3426
+ if (context?.signal?.aborted)
3427
+ throw error;
3428
+ unavailableError = createUnavailableError();
3429
+ }
3430
+ if (!unavailableError &&
3431
+ response &&
3432
+ !response.ok &&
3433
+ response.status !== 404 &&
3434
+ response.status !== 405) {
3435
+ unavailableError = createUnavailableError();
3436
+ }
3437
+ if (!unavailableError && response?.ok) {
3438
+ try {
3439
+ const serverCapabilities = asRecord(await response.json());
3440
+ if (!serverCapabilities) {
3441
+ throw new TypeError("Capability response must be an object.");
3442
+ }
3443
+ supported = serverCapabilities?.[capability] === true;
3444
+ }
3445
+ catch (error) {
3446
+ if (context?.signal?.aborted)
3447
+ throw error;
3448
+ unavailableError = createUnavailableError();
3449
+ }
3450
+ }
3451
+ }
3452
+ const descriptor = supported
3453
+ ? { id: capability, state: "available" }
3454
+ : unavailableError
3455
+ ? { id: capability, state: "unavailable", error: unavailableError }
3456
+ : {
3457
+ id: capability,
3458
+ state: "unsupported",
3459
+ error: createCapabilityUnsupportedError(capability, {
3460
+ message: CHAT_CAPABILITY_UNSUPPORTED_MESSAGE,
3461
+ }),
3462
+ };
3463
+ const legacy = { ...discovery.legacy };
3464
+ if (unavailableError) {
3465
+ delete legacy[capability];
3466
+ }
3467
+ else {
3468
+ legacy[capability] = supported;
3469
+ }
3470
+ return {
3471
+ ...discovery,
3472
+ capabilities: [
3473
+ ...discovery.capabilities.filter((candidate) => candidate.id !== capability),
3474
+ descriptor,
3475
+ ],
3476
+ legacy,
3477
+ };
3478
+ },
3356
3479
  async *subscribeToRun(input) {
3357
3480
  dispatchAgentChatRunning({
3358
3481
  isRunning: true,
@@ -17,6 +17,8 @@ export declare const NEW_CHAT_ACTION_HREF = "agent-native:new-chat";
17
17
  export declare const PROVIDER_CREDENTIAL_REJECTED_MESSAGE = "The provider rejected the credential used for this request; it is skipped on the next attempt. Retry, or update your provider key if it keeps failing.";
18
18
  export declare const SERVER_AUTHORED_CREDENTIAL_CODES: ReadonlySet<string>;
19
19
  export declare 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.";
20
+ export declare const CHAT_CAPABILITY_UNAVAILABLE_MESSAGE = "We couldn't send your message right now. Please try again shortly.";
21
+ export declare const CHAT_CAPABILITY_UNSUPPORTED_MESSAGE = "This app can't safely retry your saved prompt yet. Update the app, then try again.";
20
22
  export declare function formatChatErrorText(errorMessage: string, upgradeUrl?: string, errorCode?: string): string;
21
23
  export interface NormalizedChatError {
22
24
  message: string;
@@ -54,6 +54,8 @@ const INVALID_ATTACHMENT_MESSAGE = "The model provider rejected this attachment'
54
54
  const MALFORMED_REQUEST_ATTACHMENT_MESSAGE = "The model rejected an attached file, so this message was never sent. Remove the attachment and retry — a PDF, a plain-text file, or a JPEG, PNG, GIF, or WebP image is read directly; other formats have to be uploaded and linked instead.";
55
55
  const MALFORMED_REQUEST_MESSAGE = "The model provider rejected this request as malformed, so it was not retried. Retry, or start a new chat if it keeps happening.";
56
56
  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.";
57
+ export const CHAT_CAPABILITY_UNAVAILABLE_MESSAGE = "We couldn't send your message right now. Please try again shortly.";
58
+ export const CHAT_CAPABILITY_UNSUPPORTED_MESSAGE = "This app can't safely retry your saved prompt yet. Update the app, then try again.";
57
59
  const MALFORMED_REQUEST_CODES = new Set([
58
60
  "invalid_request",
59
61
  "invalid_request_error",
@@ -108,6 +110,14 @@ const KNOWN_CHAT_ERROR_KEYS = new Map([
108
110
  ],
109
111
  [INVALID_ATTACHMENT_MESSAGE, "agentChat.errorMessages.invalidAttachment"],
110
112
  [CHAT_REQUEST_TOO_LARGE_MESSAGE, "agentChat.errorMessages.requestTooLarge"],
113
+ [
114
+ CHAT_CAPABILITY_UNAVAILABLE_MESSAGE,
115
+ "agentChat.errorMessages.capabilityUnavailable",
116
+ ],
117
+ [
118
+ CHAT_CAPABILITY_UNSUPPORTED_MESSAGE,
119
+ "agentChat.errorMessages.capabilityUnsupported",
120
+ ],
111
121
  [
112
122
  "No LLM provider is connected. Open this app's Manage agent > LLM, then use Builder.io or add a provider key.",
113
123
  "agentChat.errorMessages.noProviderConnected",
@@ -13,8 +13,8 @@ export declare const getCollabState: import("h3").EventHandlerWithFetch<import("
13
13
  };
14
14
  }>>;
15
15
  export declare const postCollabUpdate: import("h3").EventHandlerWithFetch<import("h3").EventHandlerRequest, Promise<{
16
- ok?: undefined;
17
16
  error: string;
17
+ ok?: undefined;
18
18
  } | {
19
19
  error?: undefined;
20
20
  ok: boolean;
@@ -140,13 +140,13 @@ export declare function screenMemoryMcpToolDefinitions(): ({
140
140
  clientHint?: undefined;
141
141
  timestamp?: undefined;
142
142
  chapterId?: undefined;
143
- count?: undefined;
144
143
  startAt?: undefined;
145
144
  endAt?: undefined;
146
145
  reason?: undefined;
147
146
  includeMicrophone?: undefined;
148
147
  includeSystemAudio?: undefined;
149
148
  requestId?: undefined;
149
+ count?: undefined;
150
150
  };
151
151
  required?: undefined;
152
152
  };
@@ -171,13 +171,13 @@ export declare function screenMemoryMcpToolDefinitions(): ({
171
171
  clientHint?: undefined;
172
172
  timestamp?: undefined;
173
173
  chapterId?: undefined;
174
- count?: undefined;
175
174
  startAt?: undefined;
176
175
  endAt?: undefined;
177
176
  reason?: undefined;
178
177
  includeMicrophone?: undefined;
179
178
  includeSystemAudio?: undefined;
180
179
  requestId?: undefined;
180
+ count?: undefined;
181
181
  };
182
182
  required?: undefined;
183
183
  };
@@ -196,13 +196,13 @@ export declare function screenMemoryMcpToolDefinitions(): ({
196
196
  clientHint?: undefined;
197
197
  timestamp?: undefined;
198
198
  chapterId?: undefined;
199
- count?: undefined;
200
199
  startAt?: undefined;
201
200
  endAt?: undefined;
202
201
  reason?: undefined;
203
202
  includeMicrophone?: undefined;
204
203
  includeSystemAudio?: undefined;
205
204
  requestId?: undefined;
205
+ count?: undefined;
206
206
  };
207
207
  required?: undefined;
208
208
  };
@@ -231,13 +231,13 @@ export declare function screenMemoryMcpToolDefinitions(): ({
231
231
  };
232
232
  timestamp?: undefined;
233
233
  chapterId?: undefined;
234
- count?: undefined;
235
234
  startAt?: undefined;
236
235
  endAt?: undefined;
237
236
  reason?: undefined;
238
237
  includeMicrophone?: undefined;
239
238
  includeSystemAudio?: undefined;
240
239
  requestId?: undefined;
240
+ count?: undefined;
241
241
  };
242
242
  };
243
243
  } | {
@@ -260,12 +260,12 @@ export declare function screenMemoryMcpToolDefinitions(): ({
260
260
  description: string;
261
261
  };
262
262
  chapterId?: undefined;
263
- count?: undefined;
264
263
  startAt?: undefined;
265
264
  endAt?: undefined;
266
265
  includeMicrophone?: undefined;
267
266
  includeSystemAudio?: undefined;
268
267
  requestId?: undefined;
268
+ count?: undefined;
269
269
  };
270
270
  };
271
271
  } | {
@@ -317,7 +317,6 @@ export declare function screenMemoryMcpToolDefinitions(): ({
317
317
  clientHint?: undefined;
318
318
  timestamp?: undefined;
319
319
  chapterId?: undefined;
320
- count?: undefined;
321
320
  startAt: {
322
321
  type: string;
323
322
  description: string;
@@ -337,6 +336,7 @@ export declare function screenMemoryMcpToolDefinitions(): ({
337
336
  type: string;
338
337
  };
339
338
  requestId?: undefined;
339
+ count?: undefined;
340
340
  };
341
341
  };
342
342
  } | {
@@ -352,7 +352,6 @@ export declare function screenMemoryMcpToolDefinitions(): ({
352
352
  clientHint?: undefined;
353
353
  timestamp?: undefined;
354
354
  chapterId?: undefined;
355
- count?: undefined;
356
355
  startAt?: undefined;
357
356
  endAt?: undefined;
358
357
  reason?: undefined;
@@ -361,6 +360,7 @@ export declare function screenMemoryMcpToolDefinitions(): ({
361
360
  requestId: {
362
361
  type: string;
363
362
  };
363
+ count?: undefined;
364
364
  };
365
365
  };
366
366
  })[];
@@ -22,7 +22,7 @@ export { offboardMember } from "../identity/offboard.js";
22
22
  export type { OffboardMemberOptions, OffboardMemberResult, } from "../identity/offboard.js";
23
23
  export { registerIdentityColumns } from "../identity/rekey.js";
24
24
  export type { AppIdentityColumn, IdentityEmailChange, IdentityOffboard, IdentityOrgScope, } from "../identity/rekey.js";
25
- export { claimWorkspaceAppForOrganization, isStandaloneDispatchRuntime, isWorkspaceAppAccessAllowed, } from "./workspace-app-access.js";
25
+ export { checkWorkspaceAppAccess, claimWorkspaceAppForOrganization, isStandaloneDispatchRuntime, isWorkspaceAppAccessAllowed, isWorkspaceAppAccessUnavailable, type WorkspaceAppAccessCheck, type WorkspaceAppAccessOutcome, type WorkspaceAppAccessUnavailable, } from "./workspace-app-access.js";
26
26
  export { defineAppRoles, getRegisteredAppRoles, listRegisteredAppRoles, listAppMemberRoles, resolveAppRole, setAppMemberRole, setAppMemberRoles, applyInvitationAppRoles, getAppPermissionOverrides, setAppPermissionRoles, resolveAppAuthorizationContext, } from "./app-roles.js";
27
27
  export type { AppRoles, AppRolesDescriptor, AppRoleCaller, AppPermissionPolicy, AppRoleLookup, AppMemberRoleRow, AppAuthorizationContext, } from "./app-roles.js";
28
28
  export { ORG_MIGRATIONS } from "./migrations.js";
package/dist/org/index.js CHANGED
@@ -32,7 +32,7 @@ export { invalidateMemberOrgCaches } from "./request-org-cache.js";
32
32
  export { isMissingOrganizationTableError } from "./membership.js";
33
33
  export { offboardMember } from "../identity/offboard.js";
34
34
  export { registerIdentityColumns } from "../identity/rekey.js";
35
- export { claimWorkspaceAppForOrganization, isStandaloneDispatchRuntime, isWorkspaceAppAccessAllowed, } from "./workspace-app-access.js";
35
+ export { checkWorkspaceAppAccess, claimWorkspaceAppForOrganization, isStandaloneDispatchRuntime, isWorkspaceAppAccessAllowed, isWorkspaceAppAccessUnavailable, } from "./workspace-app-access.js";
36
36
  export { defineAppRoles, getRegisteredAppRoles, listRegisteredAppRoles, listAppMemberRoles, resolveAppRole, setAppMemberRole, setAppMemberRoles, applyInvitationAppRoles, getAppPermissionOverrides, setAppPermissionRoles, resolveAppAuthorizationContext, } from "./app-roles.js";
37
37
  export { ORG_MIGRATIONS } from "./migrations.js";
38
38
  export { CROSS_APP_ORG_FEDERATION_FLAG, CROSS_APP_ORG_FEDERATION_SCOPE, } from "./feature-flags.js";
@@ -0,0 +1,45 @@
1
+ export declare const WORKSPACE_APP_ACCESS_UNAVAILABLE: "unavailable";
2
+ export declare const WORKSPACE_APP_ACCESS_UNAVAILABLE_MESSAGE = "Workspace app access is temporarily unavailable.";
3
+ export type WorkspaceAppAccessUnavailableReason = "directory-url-invalid" | "org-domain-lookup-failed" | "org-domain-missing" | "a2a-secret-missing" | "token-signing-failed" | "registry-timeout" | "registry-fetch-failed" | "registry-http-error" | "registry-malformed-response" | "claim-http-error" | "claim-malformed-response";
4
+ /**
5
+ * The access check could not reach a verdict. Distinct from `false`: the
6
+ * caller is not refused, the answer is unknown, and `reason` says which step
7
+ * failed so a 503 can be traced without server logs.
8
+ */
9
+ export interface WorkspaceAppAccessUnavailable {
10
+ status: typeof WORKSPACE_APP_ACCESS_UNAVAILABLE;
11
+ reason: WorkspaceAppAccessUnavailableReason;
12
+ /** HTTP status of the Dispatch registry reply, when one arrived. */
13
+ upstreamStatus?: number;
14
+ /** Audience the caller signed for: origin and path only, never userinfo. */
15
+ audience?: string;
16
+ /** Short text from the upstream reply or error class. Untrusted. */
17
+ detail?: string;
18
+ }
19
+ /** What `isWorkspaceAppAccessAllowed` has always returned. */
20
+ export type WorkspaceAppAccessOutcome = boolean | typeof WORKSPACE_APP_ACCESS_UNAVAILABLE;
21
+ /** A verdict, or the cause when none could be reached. */
22
+ export type WorkspaceAppAccessCheck = boolean | WorkspaceAppAccessUnavailable;
23
+ export declare function isWorkspaceAppAccessUnavailable(outcome: unknown): outcome is WorkspaceAppAccessUnavailable;
24
+ export declare function workspaceAppAccessUnavailable(reason: WorkspaceAppAccessUnavailableReason, extra?: Pick<WorkspaceAppAccessUnavailable, "upstreamStatus" | "audience" | "detail">): WorkspaceAppAccessUnavailable;
25
+ export declare function workspaceAppAccessUnavailableBody(outcome: WorkspaceAppAccessUnavailable): {
26
+ error: string;
27
+ code: WorkspaceAppAccessUnavailableReason;
28
+ upstreamStatus?: number;
29
+ audience?: string;
30
+ detail?: string;
31
+ };
32
+ /**
33
+ * Names a thrown error without echoing its message: driver and upstream
34
+ * messages can carry hostnames or connection strings.
35
+ */
36
+ export declare function describeFetchFailure(error: unknown): {
37
+ timedOut: boolean;
38
+ detail: string;
39
+ };
40
+ /**
41
+ * Reads the human-readable refusal out of a registry error reply, if it sent
42
+ * JSON. A non-JSON or unreadable body yields no detail; the HTTP status on the
43
+ * outcome already says the call failed.
44
+ */
45
+ export declare function registryErrorDetail(body: unknown): string | undefined;
@@ -0,0 +1,80 @@
1
+ export const WORKSPACE_APP_ACCESS_UNAVAILABLE = "unavailable";
2
+ export const WORKSPACE_APP_ACCESS_UNAVAILABLE_MESSAGE = "Workspace app access is temporarily unavailable.";
3
+ const MAX_DETAIL_LENGTH = 160;
4
+ export function isWorkspaceAppAccessUnavailable(outcome) {
5
+ return (typeof outcome === "object" &&
6
+ outcome !== null &&
7
+ outcome.status ===
8
+ WORKSPACE_APP_ACCESS_UNAVAILABLE);
9
+ }
10
+ export function workspaceAppAccessUnavailable(reason, extra = {}) {
11
+ const detail = boundedDetail(extra.detail);
12
+ return {
13
+ status: WORKSPACE_APP_ACCESS_UNAVAILABLE,
14
+ reason,
15
+ ...(extra.upstreamStatus === undefined
16
+ ? {}
17
+ : { upstreamStatus: extra.upstreamStatus }),
18
+ ...(extra.audience ? { audience: extra.audience } : {}),
19
+ ...(detail ? { detail } : {}),
20
+ };
21
+ }
22
+ export function workspaceAppAccessUnavailableBody(outcome) {
23
+ return {
24
+ error: WORKSPACE_APP_ACCESS_UNAVAILABLE_MESSAGE,
25
+ code: outcome.reason,
26
+ ...(outcome.upstreamStatus === undefined
27
+ ? {}
28
+ : { upstreamStatus: outcome.upstreamStatus }),
29
+ ...(outcome.audience ? { audience: outcome.audience } : {}),
30
+ ...(outcome.detail ? { detail: outcome.detail } : {}),
31
+ };
32
+ }
33
+ /**
34
+ * Names a thrown error without echoing its message: driver and upstream
35
+ * messages can carry hostnames or connection strings.
36
+ */
37
+ export function describeFetchFailure(error) {
38
+ const name = error instanceof Error ? error.name : "UnknownError";
39
+ const cause = error instanceof Error ? error.cause : undefined;
40
+ const code = cause && typeof cause.code === "string"
41
+ ? cause.code
42
+ : "";
43
+ return {
44
+ timedOut: name === "AbortError" || name === "TimeoutError",
45
+ detail: code ? `${name}:${code}` : name,
46
+ };
47
+ }
48
+ /**
49
+ * Reads the human-readable refusal out of a registry error reply, if it sent
50
+ * JSON. A non-JSON or unreadable body yields no detail; the HTTP status on the
51
+ * outcome already says the call failed.
52
+ */
53
+ export function registryErrorDetail(body) {
54
+ if (!body || typeof body !== "object")
55
+ return undefined;
56
+ const record = body;
57
+ for (const candidate of [
58
+ record.error,
59
+ record.statusMessage,
60
+ record.message,
61
+ ]) {
62
+ if (typeof candidate === "string" && candidate.trim())
63
+ return candidate;
64
+ }
65
+ return undefined;
66
+ }
67
+ function boundedDetail(value) {
68
+ if (!value)
69
+ return undefined;
70
+ const collapsed = value
71
+ // eslint-disable-next-line no-control-regex
72
+ .replace(/[\u0000-\u001f\u007f]+/g, " ")
73
+ .replace(/\s+/g, " ")
74
+ .trim();
75
+ if (!collapsed)
76
+ return undefined;
77
+ return collapsed.length > MAX_DETAIL_LENGTH
78
+ ? `${collapsed.slice(0, MAX_DETAIL_LENGTH)}…`
79
+ : collapsed;
80
+ }
@@ -1,8 +1,8 @@
1
- import { type WorkspaceAppAccessOutcome } from "./workspace-app-access.js";
1
+ import { type WorkspaceAppAccessCheck } from "./workspace-app-access.js";
2
2
  export type WorkspaceAppAccessRequest = {
3
3
  path: string;
4
4
  method: string;
5
5
  email: string;
6
6
  orgId?: string | null;
7
7
  };
8
- export declare function checkWorkspaceAppAccessForRequest(request: WorkspaceAppAccessRequest): Promise<WorkspaceAppAccessOutcome | null>;
8
+ export declare function checkWorkspaceAppAccessForRequest(request: WorkspaceAppAccessRequest): Promise<WorkspaceAppAccessCheck | null>;
@@ -1,4 +1,4 @@
1
- import { isWorkspaceAppAccessAllowed, } from "./workspace-app-access.js";
1
+ import { checkWorkspaceAppAccess, } from "./workspace-app-access.js";
2
2
  import { resolveWorkspaceAccessAppId } from "./workspace-app-identity.js";
3
3
  function isWorkspaceAccessRecoveryPath(path, method) {
4
4
  return (path === "/_agent-native/org/me" ||
@@ -13,7 +13,7 @@ export async function checkWorkspaceAppAccessForRequest(request) {
13
13
  if (!appId || isWorkspaceAccessRecoveryPath(request.path, request.method)) {
14
14
  return null;
15
15
  }
16
- return isWorkspaceAppAccessAllowed(appId, {
16
+ return checkWorkspaceAppAccess(appId, {
17
17
  email: request.email,
18
18
  orgId: request.orgId,
19
19
  });
@@ -1,10 +1,15 @@
1
- export declare const WORKSPACE_APP_ACCESS_UNAVAILABLE: "unavailable";
2
- export declare const WORKSPACE_APP_ACCESS_UNAVAILABLE_MESSAGE = "Workspace app access is temporarily unavailable.";
3
- export type WorkspaceAppAccessOutcome = boolean | typeof WORKSPACE_APP_ACCESS_UNAVAILABLE;
1
+ import { type WorkspaceAppAccessCheck, type WorkspaceAppAccessOutcome } from "./workspace-app-access-outcome.js";
2
+ export { WORKSPACE_APP_ACCESS_UNAVAILABLE, WORKSPACE_APP_ACCESS_UNAVAILABLE_MESSAGE, isWorkspaceAppAccessUnavailable, workspaceAppAccessUnavailableBody, type WorkspaceAppAccessCheck, type WorkspaceAppAccessOutcome, type WorkspaceAppAccessUnavailable, type WorkspaceAppAccessUnavailableReason, } from "./workspace-app-access-outcome.js";
4
3
  export interface WorkspaceAppAccessContext {
5
4
  email: string;
6
5
  orgId?: string | null;
7
6
  }
8
7
  export declare function isStandaloneDispatchRuntime(): boolean;
9
8
  export declare function claimWorkspaceAppForOrganization(appId: string, context: WorkspaceAppAccessContext): Promise<boolean>;
9
+ /**
10
+ * Keeps the contract this function has always had: `true`, `false`, or the
11
+ * `"unavailable"` sentinel. Use `checkWorkspaceAppAccess` to learn why a verdict
12
+ * could not be reached.
13
+ */
10
14
  export declare function isWorkspaceAppAccessAllowed(appId: string, context: WorkspaceAppAccessContext): Promise<WorkspaceAppAccessOutcome>;
15
+ export declare function checkWorkspaceAppAccess(appId: string, context: WorkspaceAppAccessContext): Promise<WorkspaceAppAccessCheck>;