@agent-native/core 0.198.3 → 0.198.4

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 (96) hide show
  1. package/dist/a2a/types.d.ts +5 -0
  2. package/dist/agent/production-agent.js +26 -1
  3. package/dist/automation/index.d.ts +1 -1
  4. package/dist/catalog.json +1 -0
  5. package/dist/client/chat/agentkit-agent-native.js +15 -15
  6. package/dist/client/chat/runtime.js +5 -3
  7. package/dist/client/feature-flags/types.d.ts +2 -0
  8. package/dist/client/i18n.d.ts +2 -0
  9. package/dist/client/i18n.js +2 -0
  10. package/dist/client/integrations/index.d.ts +1 -0
  11. package/dist/client/integrations/index.js +1 -0
  12. package/dist/client/labs/index.d.ts +1 -1
  13. package/dist/client/labs/use-lab.d.ts +5 -0
  14. package/dist/client/labs/use-lab.js +7 -3
  15. package/dist/client/resources/mcp-connection-resume.d.ts +5 -3
  16. package/dist/client/resources/mcp-connection-resume.js +150 -13
  17. package/dist/client/sse-event-processor.d.ts +6 -0
  18. package/dist/client/uploads/index.d.ts +1 -0
  19. package/dist/client/uploads/index.js +1 -0
  20. package/dist/client/uploads/private-icon.d.ts +10 -0
  21. package/dist/client/uploads/private-icon.js +52 -0
  22. package/dist/collab/awareness.d.ts +2 -2
  23. package/dist/collab/routes.d.ts +2 -2
  24. package/dist/feature-flags/actions/get-feature-flags.js +20 -0
  25. package/dist/feature-flags/actions/list-feature-flags.d.ts +2 -0
  26. package/dist/feature-flags/actions/list-feature-flags.js +5 -0
  27. package/dist/feature-flags/actions/set-feature-flag.js +5 -0
  28. package/dist/file-upload/actions/upload-image.d.ts +2 -2
  29. package/dist/framework-tools.js +2 -0
  30. package/dist/icon-assets/index.d.ts +3 -0
  31. package/dist/icon-assets/index.js +3 -0
  32. package/dist/icon-assets/multipart.d.ts +9 -0
  33. package/dist/icon-assets/multipart.js +60 -0
  34. package/dist/icon-assets/schema.d.ts +194 -0
  35. package/dist/icon-assets/schema.js +16 -0
  36. package/dist/icon-assets/store.d.ts +38 -0
  37. package/dist/icon-assets/store.js +187 -0
  38. package/dist/icon-assets/svg.d.ts +2 -0
  39. package/dist/icon-assets/svg.js +146 -0
  40. package/dist/icon-assets/workspace-transport.d.ts +20 -0
  41. package/dist/icon-assets/workspace-transport.js +122 -0
  42. package/dist/identity/rekey.js +1 -0
  43. package/dist/jobs/actions/manage-recurring-job.d.ts +1 -1
  44. package/dist/labs/actions/get-lab-states.d.ts +2 -0
  45. package/dist/labs/actions/get-lab-states.js +14 -0
  46. package/dist/labs/actions/get-labs.js +2 -2
  47. package/dist/labs/actions/set-lab.js +4 -2
  48. package/dist/labs/index.d.ts +1 -1
  49. package/dist/labs/index.js +1 -1
  50. package/dist/labs/registry.d.ts +3 -0
  51. package/dist/labs/registry.js +14 -0
  52. package/dist/labs/server.d.ts +1 -1
  53. package/dist/labs/server.js +1 -1
  54. package/dist/labs/store.d.ts +13 -2
  55. package/dist/labs/store.js +124 -7
  56. package/dist/localization/default-messages.d.ts +2 -0
  57. package/dist/localization/default-messages.js +1 -1
  58. package/dist/localization/icon-picker-messages.d.ts +44 -0
  59. package/dist/localization/icon-picker-messages.js +22 -0
  60. package/dist/mcp-client/oauth-client.d.ts +1 -0
  61. package/dist/mcp-client/oauth-client.js +42 -4
  62. package/dist/mcp-client/oauth-routes.js +15 -2
  63. package/dist/notifications/routes.d.ts +1 -1
  64. package/dist/observability/routes.d.ts +9 -9
  65. package/dist/org/actions/list-workspace-icons.d.ts +7 -0
  66. package/dist/org/actions/list-workspace-icons.js +41 -0
  67. package/dist/org/handlers.d.ts +1 -1
  68. package/dist/org/handlers.js +28 -1
  69. package/dist/org/index.d.ts +1 -1
  70. package/dist/org/migrations.js +21 -0
  71. package/dist/org/plugin.js +42 -0
  72. package/dist/org/private-icon-handlers.d.ts +6 -0
  73. package/dist/org/private-icon-handlers.js +212 -0
  74. package/dist/progress/routes.d.ts +1 -1
  75. package/dist/provider-api/actions/custom-provider-registration.d.ts +8 -8
  76. package/dist/provider-api/actions/provider-api.d.ts +5 -5
  77. package/dist/provider-api/corpus-jobs.d.ts +2 -2
  78. package/dist/provider-api/index.d.ts +1 -1
  79. package/dist/provider-api/index.js +26 -6
  80. package/dist/secrets/routes.d.ts +4 -4
  81. package/dist/server/action-discovery.js +5 -0
  82. package/dist/server/agent-chat-plugin.js +14 -0
  83. package/dist/server/agent-engine-api-key-route.d.ts +1 -1
  84. package/dist/server/agent-engine-default-model-route.d.ts +1 -1
  85. package/dist/server/core-routes-plugin.d.ts +1 -1
  86. package/dist/server/core-routes-plugin.js +18 -3
  87. package/dist/server/index.d.ts +1 -1
  88. package/dist/server/index.js +1 -1
  89. package/dist/server/realtime-token.d.ts +1 -1
  90. package/dist/server/workspace-provider-oauth.d.ts +1 -0
  91. package/dist/server/workspace-provider-oauth.js +4 -2
  92. package/dist/templates/chat/app/components/chat/ChatRouteContent.tsx +9 -16
  93. package/dist/triggers/actions/manage-automation.d.ts +5 -5
  94. package/dist/vite/action-types-plugin.js +4 -0
  95. package/package.json +7 -9
  96. package/src/templates/chat/app/components/chat/ChatRouteContent.tsx +9 -16
@@ -49,6 +49,11 @@ export interface A2AConnectionRequestMetadata {
49
49
  reason: "connect" | "grant" | "reauthorize" | "admin_required";
50
50
  appId?: string;
51
51
  detail?: string;
52
+ source?: {
53
+ id: string;
54
+ kind: "workspace_connection";
55
+ label?: string;
56
+ };
52
57
  }
53
58
  export interface AgentSkill {
54
59
  id: string;
@@ -1656,7 +1656,11 @@ export async function callConnectedAgentReference(input) {
1656
1656
  ...(connectionRequest.appId
1657
1657
  ? { appId: connectionRequest.appId }
1658
1658
  : {}),
1659
- source: { id: input.agent, kind: "agent", label: input.agent },
1659
+ source: connectionRequest.source ?? {
1660
+ id: input.agent,
1661
+ kind: "agent",
1662
+ label: input.agent,
1663
+ },
1660
1664
  });
1661
1665
  }
1662
1666
  relay.finish("error");
@@ -1684,12 +1688,28 @@ function parseA2AConnectionRequest(error) {
1684
1688
  }
1685
1689
  const appId = typeof request.appId === "string" ? request.appId.trim() : "";
1686
1690
  const detail = typeof request.detail === "string" ? request.detail.trim() : "";
1691
+ const sourceValue = request.source && typeof request.source === "object"
1692
+ ? request.source
1693
+ : null;
1694
+ const sourceId = typeof sourceValue?.id === "string" ? sourceValue.id.trim() : "";
1695
+ const sourceLabel = typeof sourceValue?.label === "string" ? sourceValue.label.trim() : "";
1687
1696
  return {
1688
1697
  version: 1,
1689
1698
  provider,
1690
1699
  reason,
1691
1700
  ...(appId && appId.length <= 120 ? { appId } : {}),
1692
1701
  ...(detail && detail.length <= 1_000 ? { detail } : {}),
1702
+ ...(sourceValue?.kind === "workspace_connection" &&
1703
+ sourceId === provider &&
1704
+ sourceId.length <= 120
1705
+ ? {
1706
+ source: {
1707
+ id: sourceId,
1708
+ kind: "workspace_connection",
1709
+ ...(sourceLabel ? { label: sourceLabel.slice(0, 120) } : {}),
1710
+ },
1711
+ }
1712
+ : {}),
1693
1713
  };
1694
1714
  }
1695
1715
  const MAX_CONNECTED_AGENT_PROGRESS_DETAIL_CHARS = 200;
@@ -2648,6 +2668,11 @@ export function permanentPreconditionReason(toolName, message) {
2648
2668
  .trim()
2649
2669
  // The headline appends its own sentence punctuation.
2650
2670
  .replace(/[.。]+$/, "");
2671
+ const nestedPrecondition = reason
2672
+ .match(/\bI stopped because [\w-]+ can't run yet:\s*(.+?)\. That needs to be fixed outside this chat\b/i)?.[1]
2673
+ ?.trim();
2674
+ if (nestedPrecondition)
2675
+ return nestedPrecondition;
2651
2676
  if (!reason)
2652
2677
  return null;
2653
2678
  if (/\bI stopped because\b/i.test(reason) ||
@@ -139,8 +139,8 @@ export declare function createAutomationCallbackHandler(runtime: AutomationRunti
139
139
  duplicate?: undefined;
140
140
  eventId?: undefined;
141
141
  } | {
142
- error?: undefined;
143
142
  accepted: boolean;
144
143
  duplicate: boolean;
145
144
  eventId: string;
145
+ error?: undefined;
146
146
  }>;
package/dist/catalog.json CHANGED
@@ -51,6 +51,7 @@
51
51
  "playwright": "1.63.0",
52
52
  "react": "^19.2.7",
53
53
  "react-dom": "^19.2.7",
54
+ "react-router": "8.1.0",
54
55
  "recharts": "^3.9.2",
55
56
  "sonner": "^2.0.7",
56
57
  "tailwind-merge": "^3.5.0",
@@ -1,12 +1,13 @@
1
+ import { AgentKitRunSlotBusyError } from "@agent-native/agentkit/client";
1
2
  import { isAgentKitProtocolVersion, parseAgentThreadSnapshot, } from "@agent-native/agentkit/protocol";
2
3
  import { agentNativePath } from "../api-path.js";
3
4
  import { dispatchAgentChatRunning } from "../use-agent-chat-running-threads.js";
4
5
  import { appendChatThreadScopeParams, } from "../use-chat-threads.js";
5
6
  import { AGENT_NATIVE_PROTOCOL_METADATA_KEY, createAgentKitProtocolAdapter, } from "./agentkit-protocol.js";
6
7
  import { createAgentNativeChatRuntime, } from "./runtime.js";
7
- const RUN_SLOT_TIMEOUT_MS = 5_000;
8
8
  const RUN_SLOT_POLL_INTERVAL_MS = 150;
9
9
  const RUN_SLOT_STABLE_POLLS = 2;
10
+ const RUN_SLOT_MAX_POLLS = RUN_SLOT_STABLE_POLLS * 2;
10
11
  function asRecord(value) {
11
12
  return value && typeof value === "object" && !Array.isArray(value)
12
13
  ? value
@@ -954,9 +955,8 @@ export function createAgentNativeAgentKitTransport(options = {}) {
954
955
  };
955
956
  }
956
957
  async function waitForRunSlot(threadId) {
957
- const deadline = Date.now() + RUN_SLOT_TIMEOUT_MS;
958
958
  let consecutiveClearPolls = 0;
959
- while (Date.now() < deadline) {
959
+ for (let poll = 0; poll < RUN_SLOT_MAX_POLLS; poll += 1) {
960
960
  const response = await fetcher(`${apiUrl}/runs/active?threadId=${encodeURIComponent(threadId)}`, { headers: await headers({ sessionId: threadId }) });
961
961
  if (!response.ok)
962
962
  throw await responseError(response);
@@ -964,22 +964,22 @@ export function createAgentNativeAgentKitTransport(options = {}) {
964
964
  if (!status) {
965
965
  throw new TypeError("Agent chat active-run response must be an object.");
966
966
  }
967
- if (status.awaitingRedispatch === true) {
968
- throw new Error("The agent runtime owns a continuation for this thread; the queued message remains pending.");
969
- }
970
- const clear = status.active !== true ||
971
- status.status === "completed" ||
972
- status.status === "complete" ||
973
- status.status === "failed" ||
974
- status.status === "cancelled" ||
975
- status.status === "errored" ||
976
- status.status === "aborted";
967
+ const clear = status.awaitingRedispatch !== true &&
968
+ (status.active !== true ||
969
+ status.status === "completed" ||
970
+ status.status === "complete" ||
971
+ status.status === "failed" ||
972
+ status.status === "cancelled" ||
973
+ status.status === "errored" ||
974
+ status.status === "aborted");
977
975
  consecutiveClearPolls = clear ? consecutiveClearPolls + 1 : 0;
978
976
  if (consecutiveClearPolls >= RUN_SLOT_STABLE_POLLS)
979
977
  return;
980
- await new Promise((resolve) => setTimeout(resolve, RUN_SLOT_POLL_INTERVAL_MS));
978
+ if (poll + 1 < RUN_SLOT_MAX_POLLS) {
979
+ await new Promise((resolve) => setTimeout(resolve, RUN_SLOT_POLL_INTERVAL_MS));
980
+ }
981
981
  }
982
- throw new Error("The current agent run did not release the thread; the queued message remains pending.");
982
+ throw new AgentKitRunSlotBusyError();
983
983
  }
984
984
  async function readQueue(threadId) {
985
985
  const thread = await snapshot(threadId);
@@ -967,9 +967,11 @@ function mapAgentNativeEvent(raw, input) {
967
967
  reason: ev.connectionReason ?? "connect",
968
968
  appId: ev.appId,
969
969
  detail: ev.detail,
970
- source: ev.agent
971
- ? { id: ev.agent, kind: "agent", label: ev.agent }
972
- : undefined,
970
+ source: ev.source
971
+ ? { ...ev.source, kind: ev.source.kind ?? "connection" }
972
+ : ev.agent
973
+ ? { id: ev.agent, kind: "agent", label: ev.agent }
974
+ : undefined,
973
975
  },
974
976
  ];
975
977
  }
@@ -4,6 +4,8 @@ export interface FeatureFlagActor {
4
4
  }
5
5
  export interface FeatureFlagMetadata {
6
6
  key: string;
7
+ canManage?: boolean;
8
+ movedToLab?: string;
7
9
  displayName?: string | null;
8
10
  description?: string | null;
9
11
  defaultValue: boolean;
@@ -67,7 +67,9 @@ export declare function useIconPickerLabels(): {
67
67
  allCategories: string;
68
68
  loadError: string;
69
69
  saveError: string;
70
+ uploadFailed: string;
70
71
  uploadTooLarge: string;
72
+ uploadUnsupportedType: string;
71
73
  retry: string;
72
74
  uploadHint: string;
73
75
  colorNames: {
@@ -488,7 +488,9 @@ export function useIconPickerLabels() {
488
488
  allCategories: t("iconPicker.allCategories"),
489
489
  loadError: t("iconPicker.loadError"),
490
490
  saveError: t("iconPicker.saveError"),
491
+ uploadFailed: t("iconPicker.uploadFailed"),
491
492
  uploadTooLarge: t("iconPicker.uploadTooLarge"),
493
+ uploadUnsupportedType: t("iconPicker.uploadUnsupportedType"),
492
494
  retry: t("iconPicker.retry"),
493
495
  uploadHint: t("iconPicker.uploadHint"),
494
496
  colorNames: Object.fromEntries(Object.keys(defaultEnglishMessages.iconPicker.colorNames).map((color) => [
@@ -1,4 +1,5 @@
1
1
  export { startWorkspaceProviderOAuth, workspaceProviderOAuthUrl, type WorkspaceProviderOAuthOptions, type WorkspaceProviderOAuthScope, } from "./workspace-provider-oauth.js";
2
+ export { getWorkspaceConnectionProvider } from "../../connections/catalog.js";
2
3
  export { channelConnectionState, hasMissingRequiredCredentials, listChannelsForSettings, type ChannelConnectionState, type ChannelCredential, } from "./channel-setup.js";
3
4
  export { useIntegrationStatus } from "./useIntegrationStatus.js";
4
5
  export type { IntegrationStatus } from "./useIntegrationStatus.js";
@@ -1,4 +1,5 @@
1
1
  export { startWorkspaceProviderOAuth, workspaceProviderOAuthUrl, } from "./workspace-provider-oauth.js";
2
+ export { getWorkspaceConnectionProvider } from "../../connections/catalog.js";
2
3
  export { channelConnectionState, hasMissingRequiredCredentials, listChannelsForSettings, } from "./channel-setup.js";
3
4
  export { useIntegrationStatus } from "./useIntegrationStatus.js";
4
5
  export { listIntegrationEnvStatuses, listIntegrationStatuses, saveIntegrationEnvVars, setIntegrationEnabled, setupIntegration, disconnectManagedIntegrationInstallation, listManagedIntegrationInstallations, managedIntegrationOAuthUrl, managedSlackAgentManifestUrl, listManagedIntegrationScopes, saveManagedIntegrationScope, listManagedIntegrationBudgets, listManagedIntegrationMemory, forgetManagedIntegrationMemory, saveManagedIntegrationBudget, testManagedIntegrationInstallation, IntegrationClientError, } from "./api.js";
@@ -1 +1 @@
1
- export { useLab, useLabState, useLabs, type LabReference, type LabValues, } from "./use-lab.js";
1
+ export { useLab, useLabState, useLabs, type LabReference, type LabValues, type LabStates, } from "./use-lab.js";
@@ -1,5 +1,7 @@
1
1
  import type { LabDefinition } from "../../labs/registry.js";
2
+ import type { UserLabState } from "../../labs/store.js";
2
3
  export type LabValues = Record<string, boolean>;
4
+ export type LabStates = Record<string, UserLabState>;
3
5
  /**
4
6
  * A lab by key, or by its definition. Pass the definition so the lab reads as
5
7
  * its `defaultEnabled` until the server answers (and when it can't); a bare
@@ -8,6 +10,9 @@ export type LabValues = Record<string, boolean>;
8
10
  export type LabReference = string | Pick<LabDefinition, "key" | "defaultEnabled">;
9
11
  export declare function useLabState(lab: LabReference): {
10
12
  enabled: boolean;
13
+ source: UserLabState["source"] | null;
14
+ mixed: boolean;
15
+ legacyValues?: Record<string, boolean>;
11
16
  isLoading: boolean;
12
17
  isError: boolean;
13
18
  isSuccess: boolean;
@@ -6,13 +6,17 @@ function labKey(lab) {
6
6
  export function useLabState(lab) {
7
7
  const key = labKey(lab);
8
8
  const { status } = useSession();
9
- const query = useActionQuery("get-labs", undefined, {
9
+ const query = useActionQuery("get-lab-states", undefined, {
10
10
  enabled: status === "authenticated",
11
11
  });
12
+ const state = query.data?.[key];
12
13
  return {
13
- enabled: query.data
14
- ? query.data[key] === true
14
+ enabled: state
15
+ ? state.enabled
15
16
  : typeof lab !== "string" && lab.defaultEnabled === true,
17
+ source: state?.source ?? null,
18
+ mixed: state?.mixed ?? false,
19
+ legacyValues: state?.legacyValues,
16
20
  isLoading: query.isLoading || (status === "loading" && query.data === undefined),
17
21
  isError: query.isError,
18
22
  isSuccess: query.isSuccess,
@@ -2,14 +2,16 @@ export interface McpConnectionResumeRequest {
2
2
  message: string;
3
3
  returnUrl: string;
4
4
  createdAt: number;
5
+ completionId?: string;
5
6
  agentKit?: {
6
7
  threadId: string;
7
8
  runId: string;
8
9
  requestId: string;
9
10
  };
10
11
  }
11
- export declare function saveMcpConnectionResume(message: string, agentKit?: McpConnectionResumeRequest["agentKit"]): boolean;
12
- export declare function clearMcpConnectionResume(): void;
12
+ export declare function saveMcpConnectionResume(message: string, agentKit?: McpConnectionResumeRequest["agentKit"], completionId?: string): boolean;
13
+ export declare function getPendingMcpConnectionResume(returnUrl?: string): McpConnectionResumeRequest | null;
14
+ export declare function clearMcpConnectionResume(expected?: McpConnectionResumeRequest | string): void;
13
15
  export declare function consumeMcpConnectionResume(returnUrl?: string): McpConnectionResumeRequest | null;
14
- export declare function notifyMcpConnectionComplete(): void;
16
+ export declare function notifyMcpConnectionComplete(completionId?: string): void;
15
17
  export declare function addMcpConnectionCompleteListener(listener: () => void): () => void;
@@ -1,7 +1,9 @@
1
1
  const MCP_CONNECTION_RESUME_STORAGE_KEY = "agent-native:mcp-connection-resume";
2
2
  const MCP_CONNECTION_RESUME_EVENT = "agent-native:mcp-connection-complete";
3
+ const MCP_CONNECTION_RESUME_COMPLETION_STORAGE_PREFIX = "agent-native:mcp-connection-completion:";
3
4
  const MCP_CONNECTION_RESUME_TTL_MS = 10 * 60 * 1_000;
4
5
  const MCP_CONNECTION_RESUME_MAX_MESSAGE_LENGTH = 24_000;
6
+ const completedResumeIds = new Set();
5
7
  function currentReturnUrl() {
6
8
  if (typeof window === "undefined")
7
9
  return "/";
@@ -17,7 +19,27 @@ function getSessionStorage() {
17
19
  return null;
18
20
  }
19
21
  }
20
- export function saveMcpConnectionResume(message, agentKit) {
22
+ function getLocalStorage() {
23
+ if (typeof window === "undefined")
24
+ return null;
25
+ try {
26
+ return window.localStorage;
27
+ }
28
+ catch {
29
+ // coercion-ok: callers fail closed when local storage is unavailable.
30
+ return null;
31
+ }
32
+ }
33
+ function clearCompletionMarker(completionId) {
34
+ completedResumeIds.delete(completionId);
35
+ try {
36
+ getLocalStorage()?.removeItem(MCP_CONNECTION_RESUME_COMPLETION_STORAGE_PREFIX + completionId);
37
+ }
38
+ catch {
39
+ // coercion-ok: stale markers cannot complete the newer opaque attempt.
40
+ }
41
+ }
42
+ export function saveMcpConnectionResume(message, agentKit, completionId) {
21
43
  const trimmedMessage = message.trim();
22
44
  if (!trimmedMessage ||
23
45
  trimmedMessage.length > MCP_CONNECTION_RESUME_MAX_MESSAGE_LENGTH) {
@@ -30,28 +52,57 @@ export function saveMcpConnectionResume(message, agentKit) {
30
52
  message: trimmedMessage,
31
53
  returnUrl: currentReturnUrl(),
32
54
  createdAt: Date.now(),
55
+ ...(completionId ? { completionId } : {}),
33
56
  ...(agentKit ? { agentKit } : {}),
34
57
  };
58
+ const previousCompletionId = readStoredMcpConnectionResume()?.completionId;
35
59
  try {
36
60
  storage.setItem(MCP_CONNECTION_RESUME_STORAGE_KEY, JSON.stringify(request));
37
- return true;
38
61
  }
39
62
  catch {
40
63
  return false;
41
64
  }
65
+ if (previousCompletionId)
66
+ clearCompletionMarker(previousCompletionId);
67
+ return true;
42
68
  }
43
- export function clearMcpConnectionResume() {
69
+ function removeStoredMcpConnectionResume(completionId) {
44
70
  const storage = getSessionStorage();
45
71
  if (!storage)
46
72
  return;
73
+ let storedCompletionId;
74
+ let raw;
75
+ try {
76
+ raw = storage.getItem(MCP_CONNECTION_RESUME_STORAGE_KEY);
77
+ }
78
+ catch {
79
+ return;
80
+ }
81
+ if (raw) {
82
+ try {
83
+ const request = JSON.parse(raw);
84
+ if (typeof request.completionId === "string") {
85
+ storedCompletionId = request.completionId;
86
+ }
87
+ }
88
+ catch {
89
+ // coercion-ok: malformed records are removed below and cannot be resumed.
90
+ }
91
+ }
92
+ if (completionId && storedCompletionId !== completionId)
93
+ return;
47
94
  try {
48
95
  storage.removeItem(MCP_CONNECTION_RESUME_STORAGE_KEY);
49
96
  }
50
97
  catch {
51
- // Ignore storage-denied browsers; the pending request is best effort.
98
+ // Keep its completion marker so a readable request can still be retried.
99
+ return;
52
100
  }
101
+ const completedId = completionId ?? storedCompletionId;
102
+ if (completedId)
103
+ clearCompletionMarker(completedId);
53
104
  }
54
- export function consumeMcpConnectionResume(returnUrl = currentReturnUrl()) {
105
+ function readStoredMcpConnectionResume() {
55
106
  const storage = getSessionStorage();
56
107
  if (!storage)
57
108
  return null;
@@ -63,14 +114,16 @@ export function consumeMcpConnectionResume(returnUrl = currentReturnUrl()) {
63
114
  request = JSON.parse(raw);
64
115
  }
65
116
  catch {
66
- clearMcpConnectionResume();
117
+ removeStoredMcpConnectionResume();
67
118
  return null;
68
119
  }
69
120
  if (!request ||
70
121
  typeof request.message !== "string" ||
71
122
  typeof request.returnUrl !== "string" ||
72
- typeof request.createdAt !== "number") {
73
- clearMcpConnectionResume();
123
+ typeof request.createdAt !== "number" ||
124
+ (request.completionId !== undefined &&
125
+ (typeof request.completionId !== "string" || !request.completionId))) {
126
+ removeStoredMcpConnectionResume();
74
127
  return null;
75
128
  }
76
129
  if (request.agentKit !== undefined &&
@@ -81,26 +134,110 @@ export function consumeMcpConnectionResume(returnUrl = currentReturnUrl()) {
81
134
  !request.agentKit.runId.trim() ||
82
135
  typeof request.agentKit.requestId !== "string" ||
83
136
  !request.agentKit.requestId.trim())) {
84
- clearMcpConnectionResume();
137
+ removeStoredMcpConnectionResume();
85
138
  return null;
86
139
  }
87
140
  if (Date.now() - request.createdAt > MCP_CONNECTION_RESUME_TTL_MS) {
88
- clearMcpConnectionResume();
141
+ removeStoredMcpConnectionResume();
89
142
  return null;
90
143
  }
144
+ return request;
145
+ }
146
+ function isCompletionRecorded(completionId) {
147
+ if (completedResumeIds.has(completionId))
148
+ return true;
149
+ const storage = getLocalStorage();
150
+ if (!storage)
151
+ return false;
152
+ try {
153
+ return (storage.getItem(MCP_CONNECTION_RESUME_COMPLETION_STORAGE_PREFIX + completionId) === "1");
154
+ }
155
+ catch {
156
+ // coercion-ok: unreadable completion state fails closed and keeps the request pending.
157
+ return false;
158
+ }
159
+ }
160
+ export function getPendingMcpConnectionResume(returnUrl = currentReturnUrl()) {
161
+ const request = readStoredMcpConnectionResume();
162
+ if (!request)
163
+ return null;
91
164
  if (request.returnUrl !== returnUrl)
92
165
  return null;
93
- clearMcpConnectionResume();
166
+ if (request.completionId && !isCompletionRecorded(request.completionId)) {
167
+ return null;
168
+ }
94
169
  return request;
95
170
  }
96
- export function notifyMcpConnectionComplete() {
171
+ export function clearMcpConnectionResume(expected) {
172
+ const current = readStoredMcpConnectionResume();
173
+ if (typeof expected === "string" && current?.completionId !== expected) {
174
+ return;
175
+ }
176
+ if (expected &&
177
+ typeof expected !== "string" &&
178
+ (!current ||
179
+ current.createdAt !== expected.createdAt ||
180
+ current.returnUrl !== expected.returnUrl ||
181
+ current.message !== expected.message ||
182
+ current.completionId !== expected.completionId ||
183
+ current.agentKit?.threadId !== expected.agentKit?.threadId ||
184
+ current.agentKit?.runId !== expected.agentKit?.runId ||
185
+ current.agentKit?.requestId !== expected.agentKit?.requestId)) {
186
+ return;
187
+ }
188
+ removeStoredMcpConnectionResume(typeof expected === "string" ? expected : current?.completionId);
189
+ }
190
+ export function consumeMcpConnectionResume(returnUrl = currentReturnUrl()) {
191
+ const request = getPendingMcpConnectionResume(returnUrl);
192
+ if (request)
193
+ clearMcpConnectionResume(request);
194
+ return request;
195
+ }
196
+ export function notifyMcpConnectionComplete(completionId) {
97
197
  if (typeof window === "undefined")
98
198
  return;
199
+ if (completionId) {
200
+ const request = readStoredMcpConnectionResume();
201
+ if (request?.completionId !== completionId)
202
+ return;
203
+ completedResumeIds.add(completionId);
204
+ try {
205
+ getLocalStorage()?.setItem(MCP_CONNECTION_RESUME_COMPLETION_STORAGE_PREFIX + completionId, "1");
206
+ }
207
+ catch {
208
+ // coercion-ok: the active page can use this signal; reloads fail closed.
209
+ }
210
+ }
99
211
  window.dispatchEvent(new Event(MCP_CONNECTION_RESUME_EVENT));
100
212
  }
101
213
  export function addMcpConnectionCompleteListener(listener) {
102
214
  if (typeof window === "undefined")
103
215
  return () => { };
216
+ const onStorage = (event) => {
217
+ if (event.key?.startsWith(MCP_CONNECTION_RESUME_COMPLETION_STORAGE_PREFIX) &&
218
+ event.newValue !== null) {
219
+ listener();
220
+ }
221
+ };
222
+ const onMessage = (event) => {
223
+ if (event.origin !== window.location.origin ||
224
+ typeof event.data !== "object" ||
225
+ event.data === null ||
226
+ !("type" in event.data) ||
227
+ event.data.type !== "agent-native:workspace-connection-complete" ||
228
+ !("completionId" in event.data) ||
229
+ typeof event.data.completionId !== "string" ||
230
+ !event.source) {
231
+ return;
232
+ }
233
+ notifyMcpConnectionComplete(event.data.completionId);
234
+ };
104
235
  window.addEventListener(MCP_CONNECTION_RESUME_EVENT, listener);
105
- return () => window.removeEventListener(MCP_CONNECTION_RESUME_EVENT, listener);
236
+ window.addEventListener("storage", onStorage);
237
+ window.addEventListener("message", onMessage);
238
+ return () => {
239
+ window.removeEventListener(MCP_CONNECTION_RESUME_EVENT, listener);
240
+ window.removeEventListener("storage", onStorage);
241
+ window.removeEventListener("message", onMessage);
242
+ };
106
243
  }
@@ -108,6 +108,12 @@ export interface SSEEvent {
108
108
  provider?: string;
109
109
  connectionReason?: "connect" | "grant" | "reauthorize" | "admin_required";
110
110
  appId?: string;
111
+ source?: {
112
+ id: string;
113
+ kind?: string;
114
+ label?: string;
115
+ uri?: string;
116
+ };
111
117
  error?: string;
112
118
  message?: string;
113
119
  seq?: number;
@@ -2,3 +2,4 @@ export { uploadEditorImage, type EditorImageUploadFn, type EditorImageUploadResu
2
2
  export { useUploadResource } from "./use-upload-resource.js";
3
3
  export { useFileUploadStatus } from "./use-file-upload-status.js";
4
4
  export { fetchFileUploadStatus } from "../client-status-requests.js";
5
+ export { uploadWorkspacePrivateIcon, workspacePrivateIconLibraryUrl, workspacePrivateIconUrl, } from "./private-icon.js";
@@ -2,3 +2,4 @@ export { uploadEditorImage, } from "./upload-editor-image.js";
2
2
  export { useUploadResource } from "./use-upload-resource.js";
3
3
  export { useFileUploadStatus } from "./use-file-upload-status.js";
4
4
  export { fetchFileUploadStatus } from "../client-status-requests.js";
5
+ export { uploadWorkspacePrivateIcon, workspacePrivateIconLibraryUrl, workspacePrivateIconUrl, } from "./private-icon.js";
@@ -0,0 +1,10 @@
1
+ import type { IconValue } from "../../icons/index.js";
2
+ export declare function workspacePrivateIconUrl(orgId: string, image: Extract<IconValue, {
3
+ kind: "image";
4
+ }>): string | undefined;
5
+ export declare function workspacePrivateIconLibraryUrl(orgId: string, image: Extract<IconValue, {
6
+ kind: "image";
7
+ }>): string | undefined;
8
+ export declare function uploadWorkspacePrivateIcon(file: File): Promise<Extract<IconValue, {
9
+ kind: "image";
10
+ }>>;
@@ -0,0 +1,52 @@
1
+ import { agentNativePath } from "../api-path.js";
2
+ export function workspacePrivateIconUrl(orgId, image) {
3
+ if (image.authority === "url")
4
+ return image.assetId;
5
+ if (image.authority !== "private-icon" || !orgId)
6
+ return undefined;
7
+ return agentNativePath(`/_agent-native/org/private-icons/${encodeURIComponent(orgId)}/${encodeURIComponent(image.assetId)}`);
8
+ }
9
+ export function workspacePrivateIconLibraryUrl(orgId, image) {
10
+ if (image.authority === "url")
11
+ return image.assetId;
12
+ if (image.authority !== "private-icon" || !orgId)
13
+ return undefined;
14
+ return agentNativePath(`/_agent-native/org/private-icons/library/${encodeURIComponent(orgId)}/${encodeURIComponent(image.assetId)}`);
15
+ }
16
+ export async function uploadWorkspacePrivateIcon(file) {
17
+ const form = new FormData();
18
+ form.set("file", file);
19
+ const response = await fetch(agentNativePath("/_agent-native/org/private-icons"), {
20
+ method: "POST",
21
+ credentials: "include",
22
+ body: form,
23
+ });
24
+ if (!response.ok) {
25
+ const body = response.headers
26
+ .get("content-type")
27
+ ?.includes("application/json")
28
+ ? await response.json()
29
+ : null;
30
+ const message = body &&
31
+ typeof body === "object" &&
32
+ "message" in body &&
33
+ typeof body.message === "string"
34
+ ? body.message
35
+ : `Workspace icon upload failed (${response.status}).`;
36
+ throw new Error(message);
37
+ }
38
+ const body = await response.json();
39
+ if (!body ||
40
+ typeof body !== "object" ||
41
+ !("id" in body) ||
42
+ typeof body.id !== "string") {
43
+ throw new Error("Workspace icon upload returned an invalid asset ID.");
44
+ }
45
+ return {
46
+ version: 1,
47
+ kind: "image",
48
+ authority: "private-icon",
49
+ assetId: body.id,
50
+ alt: file.name.replace(/\.[^./\\]+$/, ""),
51
+ };
52
+ }
@@ -38,19 +38,19 @@ export declare const postAwareness: import("h3").EventHandlerWithFetch<import("h
38
38
  error: string;
39
39
  states?: undefined;
40
40
  } | {
41
+ error?: undefined;
41
42
  states: {
42
43
  clientId: number;
43
44
  state: string;
44
45
  }[];
45
- error?: undefined;
46
46
  }>>;
47
47
  export declare const getActiveUsers: import("h3").EventHandlerWithFetch<import("h3").EventHandlerRequest, Promise<{
48
48
  error: string;
49
49
  users?: undefined;
50
50
  } | {
51
+ error?: undefined;
51
52
  users: {
52
53
  clientId: number;
53
54
  lastSeen: number;
54
55
  }[];
55
- error?: undefined;
56
56
  }>>;
@@ -8,16 +8,16 @@ export declare const getCollabState: import("h3").EventHandlerWithFetch<import("
8
8
  state: string;
9
9
  }>>;
10
10
  export declare const postCollabUpdate: import("h3").EventHandlerWithFetch<import("h3").EventHandlerRequest, Promise<{
11
- error: string;
12
11
  ok?: undefined;
12
+ error: string;
13
13
  } | {
14
14
  error?: undefined;
15
15
  ok: boolean;
16
16
  }>>;
17
17
  export declare const postCollabText: import("h3").EventHandlerWithFetch<import("h3").EventHandlerRequest, Promise<{
18
18
  ok?: undefined;
19
- error: string;
20
19
  text?: undefined;
20
+ error: string;
21
21
  } | {
22
22
  error?: undefined;
23
23
  ok: boolean;