@agent-native/core 0.206.0 → 0.206.1-nightly-20261009213000

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 (93) hide show
  1. package/dist/action.d.ts +2 -0
  2. package/dist/agent/default-agent-engine.js +25 -0
  3. package/dist/agent/durable-background.d.ts +2 -0
  4. package/dist/agent/durable-background.js +2 -0
  5. package/dist/agent/engine/error-detail.d.ts +1 -0
  6. package/dist/agent/engine/error-detail.js +6 -1
  7. package/dist/agent/engine/tool-call-journal-seed.d.ts +1 -1
  8. package/dist/agent/engine/tool-call-journal-seed.js +47 -11
  9. package/dist/agent/engine/translate-anthropic.d.ts +9 -0
  10. package/dist/agent/engine/translate-anthropic.js +18 -3
  11. package/dist/agent/production-agent.d.ts +6 -4
  12. package/dist/agent/production-agent.js +126 -85
  13. package/dist/agent/run-store.js +15 -3
  14. package/dist/agent/thread-data-builder.js +1 -2
  15. package/dist/agent/types.d.ts +1 -0
  16. package/dist/automation/index.d.ts +1 -1
  17. package/dist/chat-threads/store.js +6 -2
  18. package/dist/cli/design-connect.d.ts +1 -0
  19. package/dist/cli/design-connect.js +72 -19
  20. package/dist/client/analytics.js +48 -58
  21. package/dist/client/chat/agentkit-agent-native.js +11 -10
  22. package/dist/client/chat/agentkit-protocol.js +5 -1
  23. package/dist/client/chat/runtime.d.ts +4 -1
  24. package/dist/client/chat/runtime.js +135 -43
  25. package/dist/client/session-replay-endpoint.d.ts +1 -0
  26. package/dist/client/session-replay-endpoint.js +35 -0
  27. package/dist/client/session-replay.js +2 -24
  28. package/dist/client/sharing/share-controller-helpers.d.ts +1 -1
  29. package/dist/client/sharing/share-controller-helpers.js +2 -1
  30. package/dist/client/sharing/useShareButtonController.js +2 -9
  31. package/dist/client/use-action.d.ts +1 -0
  32. package/dist/client/use-action.js +2 -1
  33. package/dist/deploy/build.js +1 -1
  34. package/dist/deploy/workspace-deploy.js +65 -6
  35. package/dist/file-upload/actions/upload-image.d.ts +2 -2
  36. package/dist/file-upload/registry.d.ts +7 -1
  37. package/dist/file-upload/registry.js +25 -7
  38. package/dist/mcp/build-server.d.ts +6 -0
  39. package/dist/mcp/build-server.js +97 -28
  40. package/dist/mcp/embed-app.js +9 -8
  41. package/dist/mcp/screen-memory-stdio.d.ts +7 -7
  42. package/dist/notifications/routes.d.ts +3 -3
  43. package/dist/observability/metrics.d.ts +5 -0
  44. package/dist/observability/metrics.js +62 -7
  45. package/dist/observability/routes.d.ts +4 -4
  46. package/dist/org/handlers.d.ts +1 -1
  47. package/dist/org/index.d.ts +1 -1
  48. package/dist/provider-api/actions/custom-provider-registration.d.ts +13 -13
  49. package/dist/provider-api/actions/provider-api.d.ts +5 -5
  50. package/dist/scripts/docs/search.d.ts +1 -0
  51. package/dist/scripts/docs/search.js +17 -4
  52. package/dist/secrets/index.d.ts +1 -1
  53. package/dist/secrets/index.js +1 -1
  54. package/dist/secrets/storage.d.ts +1 -0
  55. package/dist/secrets/storage.js +8 -0
  56. package/dist/server/action-routes.js +62 -29
  57. package/dist/server/agent-chat/mcp-options.d.ts +2 -0
  58. package/dist/server/agent-chat/prompt-resources.js +2 -2
  59. package/dist/server/agent-engine-api-key-route.d.ts +2 -1
  60. package/dist/server/agent-engine-api-key-route.js +66 -2
  61. package/dist/server/agent-engine-default-model-route.d.ts +1 -1
  62. package/dist/server/agent-engine-ollama-models-route.d.ts +2 -2
  63. package/dist/server/agent-run-context.js +21 -1
  64. package/dist/server/agent-teams-run-queue.js +2 -1
  65. package/dist/server/agents-bundle.js +1 -1
  66. package/dist/server/auth.js +1 -0
  67. package/dist/server/core-routes-plugin.js +12 -0
  68. package/dist/server/embed-session.js +15 -1
  69. package/dist/server/http-response-telemetry.js +37 -8
  70. package/dist/server/realtime-token.d.ts +1 -1
  71. package/dist/shared/embed-auth.d.ts +4 -0
  72. package/dist/shared/embed-auth.js +156 -23
  73. package/dist/shared/inline-bytes.d.ts +29 -0
  74. package/dist/shared/inline-bytes.js +170 -0
  75. package/dist/shared/mcp-embed-headers.d.ts +1 -1
  76. package/dist/shared/mcp-embed-headers.js +1 -1
  77. package/dist/sharing/actions/list-resource-shares.d.ts +1 -1
  78. package/dist/sharing/actions/list-resource-shares.js +10 -4
  79. package/dist/sharing/actions/set-resource-visibility.js +3 -1
  80. package/dist/sharing/actions/share-resource.js +4 -2
  81. package/dist/sharing/actions/unshare-resource.js +3 -1
  82. package/dist/sharing/widget-grant.d.ts +19 -0
  83. package/dist/sharing/widget-grant.js +55 -0
  84. package/dist/templates/factory/.agents/skills/review-latest-feedback/SKILL.md +20 -21
  85. package/dist/testing.d.ts +1 -0
  86. package/dist/testing.js +1 -0
  87. package/dist/tracking/registry.d.ts +3 -2
  88. package/dist/tracking/registry.js +12 -3
  89. package/dist/vite/client.js +6 -1
  90. package/docs/content/observability.mdx +15 -10
  91. package/docs/content/tracking.mdx +1 -1
  92. package/package.json +4 -4
  93. package/src/templates/factory/.agents/skills/review-latest-feedback/SKILL.md +20 -21
@@ -14,6 +14,7 @@ import { getAnalyticsPageLoadId, getOrCreateAnalyticsAnonymousId, getOrCreateAna
14
14
  import { injectedAgentNativeConfig } from "./app-config.js";
15
15
  import { clientBuildId } from "./build-compatibility.js";
16
16
  import { clientFailureContext } from "./failure-report.js";
17
+ import { replayEndpointFromAnalyticsEndpoint } from "./session-replay-endpoint.js";
17
18
  import { scheduleAfterPaint } from "./use-after-paint.js";
18
19
  export { clearAnalyticsSessionId, setAnalyticsSessionId, } from "./analytics-session.js";
19
20
  import { fetchAgentEngineStatus, fetchAuthSessionStatus, } from "./client-status-requests.js";
@@ -62,6 +63,7 @@ const APP_ENTRY_TRACKING_STATE_KEY = Symbol.for("agent-native.client.appEntryTra
62
63
  const AGENT_CHAT_TRACKING_STATE_KEY = Symbol.for("agent-native.client.agentChatTracking");
63
64
  const AGENT_CHAT_LIFECYCLE_DEDUPE_TTL_MS = 10 * 60 * 1_000;
64
65
  const MAX_AGENT_CHAT_LIFECYCLE_DEDUPE_KEYS = 1_000;
66
+ const PAGEVIEW_STARTUP_WAIT_MS = 250;
65
67
  const LLM_CONNECTION_STORAGE_KEY = "agent-native.llm_connection_status";
66
68
  const LLM_CONNECTION_CACHE_TTL_MS = 5 * 60 * 1000;
67
69
  const FIRST_TOUCH_STORAGE_KEY = "an_attribution";
@@ -1113,15 +1115,15 @@ export function configureTracking(options) {
1113
1115
  if (options.authSessionRefresh !== false) {
1114
1116
  installTrackingAuthSessionRefresh();
1115
1117
  }
1118
+ maybeInstallSessionReplay(options.sessionReplay, {
1119
+ endpoint: options.endpoint,
1120
+ publicKey,
1121
+ }, _trackingContentCaptureEnabled);
1116
1122
  if (options.pageviewTracking !== false) {
1117
1123
  installPageviewTracking();
1118
1124
  if (options.webVitals !== false)
1119
1125
  installWebVitalsTracking();
1120
1126
  }
1121
- maybeInstallSessionReplay(options.sessionReplay, {
1122
- endpoint: options.endpoint,
1123
- publicKey,
1124
- }, _trackingContentCaptureEnabled);
1125
1127
  maybeInstallErrorCapture(options.errorCapture);
1126
1128
  }
1127
1129
  }
@@ -1354,15 +1356,18 @@ function sessionReplayRequiresSignedInUserFromEnv() {
1354
1356
  }
1355
1357
  function configuredSessionReplayOptions(config, tracking = {}) {
1356
1358
  const env = import.meta.env ?? {};
1359
+ const runtimeConfig = typeof window === "undefined" ? undefined : window.__AGENT_NATIVE_CONFIG__;
1357
1360
  const publicKey = tracking.publicKey ||
1358
1361
  _agentNativeAnalyticsPublicKey ||
1362
+ runtimeConfig?.agentNativeAnalyticsPublicKey ||
1359
1363
  env.VITE_AGENT_NATIVE_ANALYTICS_PUBLIC_KEY;
1360
1364
  const trackingEndpoint = tracking.endpoint ||
1361
1365
  _agentNativeAnalyticsEndpoint ||
1366
+ runtimeConfig?.agentNativeAnalyticsEndpoint ||
1362
1367
  env.VITE_AGENT_NATIVE_ANALYTICS_ENDPOINT ||
1363
1368
  (publicKey ? AGENT_NATIVE_ANALYTICS_DEFAULT_ENDPOINT : undefined);
1364
1369
  const endpoint = trackingEndpoint
1365
- ? replayEndpointFromTrackingEndpoint(trackingEndpoint)
1370
+ ? (replayEndpointFromAnalyticsEndpoint(trackingEndpoint, typeof document === "undefined" ? undefined : document.baseURI) ?? undefined)
1366
1371
  : undefined;
1367
1372
  const withTrackingDefaults = (options) => {
1368
1373
  const extraProperties = replayExtraPropertiesWithDefaults(options.extraProperties);
@@ -1439,29 +1444,6 @@ function replayExtraPropertiesWithDefaults(source) {
1439
1444
  return applyTrackingIdentity(withDefaults, isQaTrackingIdentity(identity) ? null : identity);
1440
1445
  };
1441
1446
  }
1442
- function replayEndpointFromTrackingEndpoint(value) {
1443
- try {
1444
- const url = new URL(value);
1445
- if (url.pathname.endsWith("/api/analytics/track")) {
1446
- url.pathname = url.pathname.replace(/\/api\/analytics\/track$/, "/api/analytics/replay");
1447
- return url.toString();
1448
- }
1449
- if (url.pathname.endsWith("/track")) {
1450
- url.pathname = url.pathname.replace(/\/track$/, "/api/analytics/replay");
1451
- return url.toString();
1452
- }
1453
- }
1454
- catch {
1455
- // Fall through to relative-path handling below.
1456
- }
1457
- if (value.endsWith("/api/analytics/track")) {
1458
- return value.replace(/\/api\/analytics\/track$/, "/api/analytics/replay");
1459
- }
1460
- if (value.endsWith("/track")) {
1461
- return value.replace(/\/track$/, "/api/analytics/replay");
1462
- }
1463
- return undefined;
1464
- }
1465
1447
  function maybeInstallSessionReplay(config, tracking, start = true) {
1466
1448
  if (typeof window === "undefined")
1467
1449
  return;
@@ -1674,24 +1656,21 @@ function sessionReplayTrackingProperties() {
1674
1656
  : {}),
1675
1657
  };
1676
1658
  }
1677
- function pageviewKey() {
1678
- return window.location.href;
1679
- }
1680
- function pageviewProperties(reason) {
1659
+ function snapshotPageview(reason) {
1660
+ const { href, origin, pathname, hostname, search } = window.location;
1661
+ const contentCaptureEnabled = _trackingContentCaptureEnabled;
1681
1662
  const properties = {
1682
- url: !_trackingContentCaptureEnabled
1683
- ? window.location.origin + window.location.pathname
1684
- : scrubUrl(window.location.href),
1685
- path: window.location.pathname,
1686
- hostname: window.location.hostname,
1663
+ url: !contentCaptureEnabled ? origin + pathname : scrubUrl(href),
1664
+ path: pathname,
1665
+ hostname,
1687
1666
  navigation_type: reason,
1688
1667
  [AGENT_SIGNALS_PAGEVIEW_PROPERTY]: AGENT_SIGNALS_VERSION,
1689
1668
  [PAGE_LOAD_PAGEVIEW_PROPERTY]: getAnalyticsPageLoadId(),
1690
1669
  };
1691
- if (_trackingContentCaptureEnabled && window.location.search) {
1692
- properties.search = scrubUrl(window.location.search);
1670
+ if (contentCaptureEnabled && search) {
1671
+ properties.search = scrubUrl(search);
1693
1672
  }
1694
- if (_trackingContentCaptureEnabled && typeof document !== "undefined") {
1673
+ if (contentCaptureEnabled && typeof document !== "undefined") {
1695
1674
  if (document.referrer) {
1696
1675
  properties.referrer = scrubUrl(document.referrer);
1697
1676
  }
@@ -1699,7 +1678,7 @@ function pageviewProperties(reason) {
1699
1678
  properties.title = document.title;
1700
1679
  }
1701
1680
  }
1702
- return properties;
1681
+ return { key: href, pathname, hostname, properties };
1703
1682
  }
1704
1683
  function readAppEntryKeys() {
1705
1684
  const stored = safeStorageGet(APP_ENTRY_STORAGE_KEY);
@@ -1744,7 +1723,7 @@ function rememberLastAppEntry(appName, now) {
1744
1723
  return Number.isFinite(previous) ? previous : null;
1745
1724
  }
1746
1725
  let _appEntryAuthRetry = null;
1747
- function waitForTrackingIdentityBeforeAppEntry() {
1726
+ function waitForTrackingIdentityBeforeAppEntry(entryPath) {
1748
1727
  const pending = _trackingSessionRefresh;
1749
1728
  if (!pending || _trackingIdentityResolved)
1750
1729
  return false;
@@ -1753,18 +1732,19 @@ function waitForTrackingIdentityBeforeAppEntry() {
1753
1732
  .catch(() => { })
1754
1733
  .then(() => {
1755
1734
  _appEntryAuthRetry = null;
1756
- emitAppEntered();
1735
+ emitAppEntered(entryPath);
1757
1736
  });
1758
1737
  }
1759
1738
  return true;
1760
1739
  }
1761
- function emitAppEntered() {
1740
+ function emitAppEntered(entryPath) {
1762
1741
  if (typeof window === "undefined" || !_getDefaultProps)
1763
1742
  return;
1764
- if (waitForTrackingIdentityBeforeAppEntry())
1743
+ const scheduledEntryPath = entryPath ?? window.location.pathname;
1744
+ if (waitForTrackingIdentityBeforeAppEntry(scheduledEntryPath))
1765
1745
  return;
1766
1746
  const properties = resolveProps(AGENT_NATIVE_LIFECYCLE_EVENTS.appEntered, {
1767
- entry_path: window.location.pathname,
1747
+ entry_path: scheduledEntryPath,
1768
1748
  });
1769
1749
  const appName = normalizeTrackingDimension(properties.app_name ?? properties.app);
1770
1750
  const sessionId = typeof properties.session_id === "string"
@@ -1780,7 +1760,7 @@ function emitAppEntered() {
1780
1760
  const attribution = getFirstTouchAttribution();
1781
1761
  trackEvent(AGENT_NATIVE_LIFECYCLE_EVENTS.appEntered, {
1782
1762
  app_name: appName,
1783
- entry_path: window.location.pathname,
1763
+ entry_path: scheduledEntryPath,
1784
1764
  ...(attribution?.ref ? { source: attribution.ref } : {}),
1785
1765
  ...(attribution?.landing_referrer
1786
1766
  ? { referrer: attribution.landing_referrer }
@@ -1796,27 +1776,28 @@ function emitAppEntered() {
1796
1776
  }
1797
1777
  }
1798
1778
  }
1799
- function emitPageview(reason) {
1779
+ function emitPageview(snapshot) {
1800
1780
  if (typeof window === "undefined")
1801
1781
  return;
1802
- if (isLocalAnalyticsHostname(window.location.hostname))
1782
+ if (isLocalAnalyticsHostname(snapshot.hostname))
1803
1783
  return;
1804
1784
  const state = getPageviewTrackingState();
1805
- const key = pageviewKey();
1806
- if (state.lastPageviewKey === key)
1785
+ if (state.lastPageviewKey === snapshot.key)
1807
1786
  return;
1808
- state.lastPageviewKey = key;
1809
- trackEvent("pageview", pageviewProperties(reason));
1810
- emitAppEntered();
1787
+ state.lastPageviewKey = snapshot.key;
1788
+ trackEvent("pageview", snapshot.properties);
1789
+ emitAppEntered(snapshot.pathname);
1811
1790
  }
1812
1791
  function schedulePageview(reason) {
1813
1792
  if (!_trackingContentCaptureEnabled) {
1814
1793
  void stopSessionReplay("local-plan-privacy");
1815
1794
  }
1816
- const run = () => emitPageview(reason);
1795
+ const snapshot = snapshotPageview(reason);
1796
+ const run = () => emitPageview(snapshot);
1817
1797
  const deferredBootRefresh = _llmConnectionBootRefresh && !_llmConnectionStatus
1818
1798
  ? _llmConnectionBootRefresh
1819
1799
  : null;
1800
+ const replayStart = _sessionReplayStartPromise;
1820
1801
  const pendingStartupContext = [];
1821
1802
  if (_llmConnectionRefresh && !_llmConnectionStatus) {
1822
1803
  pendingStartupContext.push(_llmConnectionRefresh);
@@ -1825,17 +1806,26 @@ function schedulePageview(reason) {
1825
1806
  pendingStartupContext.push(_trackingSessionRefresh);
1826
1807
  }
1827
1808
  if (deferredBootRefresh !== null) {
1828
- if (pendingStartupContext.length > 0) {
1829
- const timeout = new Promise((resolve) => window.setTimeout(resolve, 250));
1809
+ if (pendingStartupContext.length > 0 || replayStart) {
1810
+ const timeout = new Promise((resolve) => window.setTimeout(resolve, PAGEVIEW_STARTUP_WAIT_MS));
1811
+ const startupContext = [
1812
+ ...pendingStartupContext,
1813
+ ...(replayStart ? [replayStart.then(() => undefined)] : []),
1814
+ ];
1830
1815
  void Promise.all([
1831
1816
  deferredBootRefresh,
1832
- Promise.race([Promise.allSettled(pendingStartupContext), timeout]),
1817
+ Promise.race([Promise.allSettled(startupContext), timeout]),
1833
1818
  ]).finally(run);
1834
1819
  return;
1835
1820
  }
1836
1821
  void deferredBootRefresh.finally(run);
1837
1822
  return;
1838
1823
  }
1824
+ if (replayStart) {
1825
+ const timeout = new Promise((resolve) => window.setTimeout(resolve, PAGEVIEW_STARTUP_WAIT_MS));
1826
+ void Promise.race([replayStart.then(() => undefined), timeout]).finally(run);
1827
+ return;
1828
+ }
1839
1829
  if (typeof queueMicrotask === "function") {
1840
1830
  queueMicrotask(run);
1841
1831
  return;
@@ -1,5 +1,5 @@
1
1
  import { AgentKitRunSlotBusyError } from "@agent-native/agentkit/client";
2
- import { isAgentKitProtocolVersion, parseAgentQueuedMessage, parseAgentRunOptions, parseAgentThreadSnapshot, } from "@agent-native/agentkit/protocol";
2
+ import { isAgentKitProtocolVersion, parseAgentQueuedMessage, parseAgentRunOptions, parseAgentThreadSnapshot, persistableFilePart, } from "@agent-native/agentkit/protocol";
3
3
  import { projectRootAssistantMessages } from "../../agent/thread-message-projection.js";
4
4
  import { RUN_NOT_STARTED_METADATA_KEY, retryContextFromRequest, } from "../../shared/agent-chat-run-not-started.js";
5
5
  import { agentEngineStatusUrlForChatApi, requireAgentEngineConfiguredForDispatch, } from "../agent-engine-readiness.js";
@@ -167,6 +167,9 @@ function messagePart(value, fallbackTextFormat) {
167
167
  : typeof part.mimeType === "string"
168
168
  ? { mediaType: part.mimeType }
169
169
  : {}),
170
+ ...(part.omitted === "inline-bytes"
171
+ ? { omitted: "inline-bytes" }
172
+ : {}),
170
173
  };
171
174
  }
172
175
  return {
@@ -1046,9 +1049,6 @@ function assistantMessageIdsByRun(events) {
1046
1049
  }
1047
1050
  return uniqueMessageIdsByRun;
1048
1051
  }
1049
- function persistedFileUrl(url) {
1050
- return url && !/^\s*data:/i.test(url) ? url : undefined;
1051
- }
1052
1052
  function persistedHistoryEvents(events = []) {
1053
1053
  const sequenceByRun = new Map();
1054
1054
  return events.flatMap((event) => {
@@ -1154,16 +1154,17 @@ function persistedMessages(messages) {
1154
1154
  ];
1155
1155
  }
1156
1156
  if (part.type === "file") {
1157
- const url = persistedFileUrl(part.url);
1158
- if (!url && !part.fileId)
1157
+ const stored = persistableFilePart(part);
1158
+ if (!stored.url && !stored.fileId && !stored.omitted)
1159
1159
  return [];
1160
1160
  return [
1161
1161
  {
1162
1162
  type: "file",
1163
- name: part.name,
1164
- ...(part.mediaType ? { mediaType: part.mediaType } : {}),
1165
- ...(url ? { url } : {}),
1166
- ...(part.fileId ? { fileId: part.fileId } : {}),
1163
+ name: stored.name,
1164
+ ...(stored.mediaType ? { mediaType: stored.mediaType } : {}),
1165
+ ...(stored.url ? { url: stored.url } : {}),
1166
+ ...(stored.fileId ? { fileId: stored.fileId } : {}),
1167
+ ...(stored.omitted ? { omitted: stored.omitted } : {}),
1167
1168
  },
1168
1169
  ];
1169
1170
  }
@@ -1,4 +1,4 @@
1
- import { AGENT_TOOL_CALL_HISTORY_MEDIA_TYPE, AGENT_TOOL_RESULT_HISTORY_MEDIA_TYPE, AGENTKIT_PROTOCOL_VERSION, AgentProtocolValidationError, approvalResponseFromResume, createCapabilityUnavailableError, createCapabilityUnsupportedError, inferAgentActivityKind, negotiateAgentKitProtocolVersion, resumeEntryFromApproval, resumeOptionId, } from "@agent-native/agentkit/protocol";
1
+ import { AGENT_TOOL_CALL_HISTORY_MEDIA_TYPE, AGENT_TOOL_RESULT_HISTORY_MEDIA_TYPE, AGENTKIT_PROTOCOL_VERSION, AgentProtocolValidationError, approvalResponseFromResume, createCapabilityUnavailableError, createCapabilityUnsupportedError, inferAgentActivityKind, isInlineDataUrl, negotiateAgentKitProtocolVersion, resumeEntryFromApproval, resumeOptionId, } from "@agent-native/agentkit/protocol";
2
2
  import { AUTO_CONTINUE_OF_RUN_METADATA_KEY, AUTO_CONTINUE_PROMPT, AUTO_CONTINUE_REFUSAL_CODES, CONTINUE_OF_RUN_METADATA_KEY, CONTINUE_UNAVAILABLE_CODE, } from "../../agent/auto-continue.js";
3
3
  import { parseBase64DataUrl } from "../../shared/data-url.js";
4
4
  import { emitChatFirstOpenApp, emitChatFirstOpenBrowser, } from "../chat-first-state.js";
@@ -3255,10 +3255,14 @@ export function createAgentKitProtocolAdapter(runtime, options = {}) {
3255
3255
  if (state.status === "missing" || !state.turnId) {
3256
3256
  throw Object.assign(new Error(`Agent run ${input.runId} cannot be continued.`), { code: CONTINUE_UNAVAILABLE_CODE, retryable: false });
3257
3257
  }
3258
+ const attachments = (input.attachments ?? [])
3259
+ .filter((part) => !isInlineDataUrl(part.url))
3260
+ .map(runtimeAttachmentFromFilePart);
3258
3261
  const turn = await session.continueTurn({
3259
3262
  turnId: state.turnId,
3260
3263
  prompt: AUTO_CONTINUE_PROMPT,
3261
3264
  metadata: { [CONTINUE_OF_RUN_METADATA_KEY]: state.runId },
3265
+ ...(attachments.length ? { attachments } : {}),
3262
3266
  abortSignal: readers.signal,
3263
3267
  });
3264
3268
  const runId = await openStartedRun(input.threadId, session, turn, undefined, { continuedRunId: input.runId });
@@ -1,4 +1,4 @@
1
- import type { AgentSuggestion } from "@agent-native/agentkit/protocol";
1
+ import { type AgentSuggestion } from "@agent-native/agentkit/protocol";
2
2
  import type { ActionChatUIConfig } from "../../action-ui.js";
3
3
  import type { AgentMcpAppPayload } from "../../mcp-client/app-result.js";
4
4
  import type { ReasoningEffort } from "../../shared/reasoning-effort.js";
@@ -240,6 +240,8 @@ export interface AgentChatRuntimeContinueInput {
240
240
  readonly prompt?: string;
241
241
  readonly approval?: AgentChatRuntimeApprovalResponse;
242
242
  readonly connection?: AgentChatRuntimeConnectionResponse;
243
+ /** Durable references to the continued turn's attachments; never bytes. */
244
+ readonly attachments?: readonly AgentChatRuntimeAttachment[];
243
245
  readonly metadata?: AgentChatRuntimeMetadata;
244
246
  readonly abortSignal?: AbortSignal;
245
247
  }
@@ -654,5 +656,6 @@ export interface CreateAgentNativeChatRuntimeOptions {
654
656
  readonly scope?: unknown;
655
657
  }
656
658
  export declare function createHttpAgentChatRuntime<TEvent extends AgentChatRuntimeEventBase = AgentChatRuntimeKnownEvent>(options: CreateHttpAgentChatRuntimeOptions<TEvent>): AgentChatRuntime<TEvent>;
659
+ export declare function loadedSkillSlugsFromMessages(messages: readonly AgentChatRuntimeMessage[] | undefined): string[];
657
660
  export declare function createAgentNativeChatRuntime(options?: CreateAgentNativeChatRuntimeOptions): AgentChatRuntime<AgentChatRuntimeKnownEvent>;
658
661
  export declare function isAgentNativeChatRuntime(runtime: AgentChatRuntime | undefined): boolean;
@@ -1,4 +1,5 @@
1
1
  import { AgentKitRunSlotBusyError } from "@agent-native/agentkit/client";
2
+ import { isInlineDataUrl, } from "@agent-native/agentkit/protocol";
2
3
  import { AUTO_CONTINUE_OF_RUN_METADATA_KEY, CONTINUE_OF_RUN_METADATA_KEY, } from "../../agent/auto-continue.js";
3
4
  import { agentEngineStatusUrlForChatApi, requireAgentEngineConfiguredForDispatch, } from "../agent-engine-readiness.js";
4
5
  import { getOrCreateAnalyticsSessionId } from "../analytics-session.js";
@@ -667,12 +668,39 @@ export function createHttpAgentChatRuntime(options) {
667
668
  };
668
669
  return runtime;
669
670
  }
671
+ /**
672
+ * What an earlier user turn's file or image becomes in replayed history: its
673
+ * name, type, and durable URL, so a later turn can still refer to it. Inline
674
+ * bytes are never replayed.
675
+ */
676
+ function historyAttachmentStub(message, part) {
677
+ if (message.role !== "user")
678
+ return undefined;
679
+ if (part.type !== "file" && part.type !== "image")
680
+ return undefined;
681
+ const name = part.type === "file" ? part.filename : part.alt;
682
+ const url = typeof part.url === "string" && !isInlineDataUrl(part.url)
683
+ ? part.url
684
+ : undefined;
685
+ return `[attached: ${[name || part.type, part.mediaType, url].filter(Boolean).join(" ")}]`;
686
+ }
687
+ /** Reasoning is the model's own scratch work and is never replayed as history. */
670
688
  function runtimeMessageText(message) {
671
689
  return message.content
672
- .map((part) => part.type === "text" || part.type === "reasoning" ? part.text : "")
690
+ .map((part) => part.type === "text"
691
+ ? part.text
692
+ : (historyAttachmentStub(message, part) ?? ""))
673
693
  .filter(Boolean)
674
694
  .join("\n");
675
695
  }
696
+ function isUserAsk(message) {
697
+ return (message.role === "user" &&
698
+ message.content.some((part) => (part.type === "text" && part.text.trim()) ||
699
+ historyAttachmentStub(message, part) !== undefined));
700
+ }
701
+ function hasHistoryAttachment(message) {
702
+ return message.content.some((part) => historyAttachmentStub(message, part) !== undefined);
703
+ }
676
704
  function isSyntheticToolHistoryOmissionMessage(message) {
677
705
  return (message.role === "assistant" &&
678
706
  (message.id === AGENTKIT_TOOL_HISTORY_OMISSION_MESSAGE_ID_PREFIX ||
@@ -708,6 +736,38 @@ function nativeHistoryFromMessages(messages, currentPrompt) {
708
736
  }))
709
737
  .filter((message) => message.content.trim());
710
738
  }
739
+ const MAX_LOADED_SKILL_SLUGS = 16;
740
+ function runtimeToolResultText(part) {
741
+ if (typeof part.resultText === "string")
742
+ return part.resultText;
743
+ return typeof part.result === "string" ? part.result : "";
744
+ }
745
+ // Scans the whole thread because structured history is capped and drops older
746
+ // skill reads; the server rebuilds those pages from these slugs.
747
+ export function loadedSkillSlugsFromMessages(messages) {
748
+ const slugByCallId = new Map();
749
+ const loaded = [];
750
+ for (const message of messages ?? []) {
751
+ for (const part of message.content) {
752
+ if (part.type === "tool-call" && part.toolName === "docs-search") {
753
+ const slug = part.input?.slug;
754
+ if (typeof slug === "string" && slug.startsWith("skill-")) {
755
+ slugByCallId.set(part.toolCallId, slug);
756
+ }
757
+ }
758
+ else if (part.type === "tool-result" && !part.isError) {
759
+ const slug = slugByCallId.get(part.toolCallId);
760
+ if (slug && runtimeToolResultText(part).startsWith("# Skill:")) {
761
+ const index = loaded.indexOf(slug);
762
+ if (index >= 0)
763
+ loaded.splice(index, 1);
764
+ loaded.push(slug);
765
+ }
766
+ }
767
+ }
768
+ }
769
+ return loaded.slice(-MAX_LOADED_SKILL_SLUGS);
770
+ }
711
771
  const MAX_TOOL_HISTORY_VALUE_BYTES = 64 * 1024;
712
772
  const MAX_TOOL_HISTORY_SERIALIZATION_STEPS = 64 * 1024;
713
773
  const MAX_TOOL_HISTORY_SERIALIZATION_DEPTH = 512;
@@ -719,6 +779,9 @@ const MAX_STRUCTURED_HISTORY_TEXT_SOURCE_PARTS = MAX_TOOL_HISTORY_CALLS * 2;
719
779
  const MAX_STRUCTURED_HISTORY_SOURCE_PARTS = MAX_STRUCTURED_HISTORY_TOOL_SOURCE_PARTS +
720
780
  MAX_STRUCTURED_HISTORY_TEXT_SOURCE_PARTS;
721
781
  const MAX_STRUCTURED_HISTORY_SOURCE_MESSAGES = 1024;
782
+ const MAX_PINNED_ATTACHMENT_PROMPTS = 32;
783
+ /** Above every other candidate, so the byte budget drops pinned asks last. */
784
+ const PINNED_USER_PROMPT_TEXT_PRIORITY = 4;
722
785
  const MAX_STRUCTURED_HISTORY_SOURCE_SCAN_STEPS = MAX_STRUCTURED_HISTORY_SOURCE_PARTS * 4;
723
786
  const TOOL_INPUT_OMISSION_TEXT = "Tool input omitted from history because it could not be serialized.";
724
787
  const TOOL_INPUT_SIZE_OMISSION_TEXT = "Tool input omitted from history because it exceeds 64 KiB.";
@@ -1177,7 +1240,7 @@ function runtimeMessageTextMatches(message, expected) {
1177
1240
  return { matches: true, complete: false };
1178
1241
  }
1179
1242
  scannedParts++;
1180
- if (part.type !== "text" && part.type !== "reasoning")
1243
+ if (part.type !== "text")
1181
1244
  continue;
1182
1245
  if (!part.text)
1183
1246
  continue;
@@ -1229,16 +1292,19 @@ function boundedStructuredHistorySources(messages, currentPrompt, supplementalMe
1229
1292
  }
1230
1293
  }
1231
1294
  let firstUserPromptMessageIndex;
1295
+ const attachmentPromptMessageIndices = [];
1232
1296
  const historyWindowStart = Math.max(0, historyMessages.length - MAX_STRUCTURED_HISTORY_SOURCE_MESSAGES);
1233
1297
  for (let index = historyWindowStart; index < historyMessages.length; index++) {
1234
1298
  if (index === currentPromptMessageIndex)
1235
1299
  continue;
1236
1300
  const message = historyMessages[index];
1237
- if (message.role === "user" &&
1238
- message.content.some((part) => (part.type === "text" || part.type === "reasoning") &&
1239
- part.text.trim())) {
1301
+ if (message.role !== "user")
1302
+ continue;
1303
+ if (firstUserPromptMessageIndex === undefined && isUserAsk(message)) {
1240
1304
  firstUserPromptMessageIndex = index;
1241
- break;
1305
+ }
1306
+ if (hasHistoryAttachment(message)) {
1307
+ attachmentPromptMessageIndices.push(index);
1242
1308
  }
1243
1309
  }
1244
1310
  let previousUserPromptMessageIndex;
@@ -1246,25 +1312,43 @@ function boundedStructuredHistorySources(messages, currentPrompt, supplementalMe
1246
1312
  for (let index = historyMessages.length - 1; index >= 0; index--) {
1247
1313
  if (index === currentPromptMessageIndex)
1248
1314
  continue;
1249
- const message = historyMessages[index];
1250
- if (message.role === "user" &&
1251
- message.content.some((part) => (part.type === "text" || part.type === "reasoning") &&
1252
- part.text.trim())) {
1315
+ if (isUserAsk(historyMessages[index])) {
1253
1316
  previousUserPromptMessageIndex = index;
1254
1317
  break;
1255
1318
  }
1256
1319
  }
1257
1320
  }
1258
- const protectedUserPromptMessageIndices = new Set([firstUserPromptMessageIndex, previousUserPromptMessageIndex].filter((index) => index !== undefined));
1321
+ // The first ask ("LinkedIn ad 1200x627 PNG") and every turn that attached a
1322
+ // file stay in the window however long the agentic tail after them grows.
1323
+ // ponytail: only the newest 32 attachment turns are pinned; older ones fall
1324
+ // back to the newest-first window.
1325
+ const pinnedUserPromptMessageIndices = new Set([
1326
+ firstUserPromptMessageIndex,
1327
+ previousUserPromptMessageIndex,
1328
+ ...attachmentPromptMessageIndices.slice(-MAX_PINNED_ATTACHMENT_PROMPTS),
1329
+ ].filter((index) => index !== undefined));
1259
1330
  const regularTextPartLimit = MAX_STRUCTURED_HISTORY_TEXT_SOURCE_PARTS -
1260
- protectedUserPromptMessageIndices.size;
1331
+ pinnedUserPromptMessageIndices.size;
1261
1332
  const selectedReversed = [];
1333
+ const pinnedPromptsAdded = new Set();
1262
1334
  let selectedToolPartCount = 0;
1263
1335
  let selectedTextPartCount = 0;
1264
1336
  let scannedPartCount = 0;
1265
1337
  let visitedMessageCount = 0;
1266
1338
  let toolBoundary;
1267
1339
  let stop = false;
1340
+ const addPinnedPromptText = (parts, messageIndex) => {
1341
+ pinnedPromptsAdded.add(messageIndex);
1342
+ const text = runtimeMessageText(historyMessages[messageIndex]);
1343
+ if (!text.trim())
1344
+ return;
1345
+ if (selectedTextPartCount >= MAX_STRUCTURED_HISTORY_TEXT_SOURCE_PARTS) {
1346
+ omitted = true;
1347
+ return;
1348
+ }
1349
+ selectedTextPartCount++;
1350
+ parts.push({ type: "text", text });
1351
+ };
1268
1352
  const visitMessage = (message, list, messageIndex) => {
1269
1353
  if (visitedMessageCount >= MAX_STRUCTURED_HISTORY_SOURCE_MESSAGES) {
1270
1354
  omitted = true;
@@ -1280,12 +1364,12 @@ function boundedStructuredHistorySources(messages, currentPrompt, supplementalMe
1280
1364
  visitedMessageCount++;
1281
1365
  if (message.role !== "user" && message.role !== "assistant")
1282
1366
  return;
1283
- const isFirstUserPrompt = list === "messages" && messageIndex === firstUserPromptMessageIndex;
1284
- const isPreviousUserPrompt = list === "messages" && messageIndex === previousUserPromptMessageIndex;
1285
- const isProtectedUserPrompt = isFirstUserPrompt || isPreviousUserPrompt;
1367
+ const isPinnedUserPrompt = list === "messages" && pinnedUserPromptMessageIndices.has(messageIndex);
1286
1368
  const partsReversed = [];
1287
- let protectedPromptTextAdded = false;
1288
1369
  for (let partIndex = message.content.length - 1; partIndex >= 0; partIndex--) {
1370
+ const part = message.content[partIndex];
1371
+ if (part.type === "reasoning")
1372
+ continue;
1289
1373
  if (scannedPartCount >= MAX_STRUCTURED_HISTORY_SOURCE_SCAN_STEPS) {
1290
1374
  omitted = true;
1291
1375
  toolHistoryOmitted = true;
@@ -1294,30 +1378,15 @@ function boundedStructuredHistorySources(messages, currentPrompt, supplementalMe
1294
1378
  break;
1295
1379
  }
1296
1380
  scannedPartCount++;
1297
- const part = message.content[partIndex];
1381
+ const attachmentStub = historyAttachmentStub(message, part);
1298
1382
  const isToolPart = (part.type === "tool-call" && message.role === "assistant") ||
1299
1383
  part.type === "tool-result";
1300
- const isTextPart = part.type === "text" || part.type === "reasoning";
1384
+ const isTextPart = part.type === "text" || attachmentStub !== undefined;
1301
1385
  if (!isToolPart && !isTextPart)
1302
1386
  continue;
1303
- if (isProtectedUserPrompt && isTextPart) {
1304
- if (!protectedPromptTextAdded) {
1305
- const text = message.content
1306
- .filter((candidate) => candidate.type === "text" || candidate.type === "reasoning")
1307
- .map((candidate) => candidate.type === "text" || candidate.type === "reasoning"
1308
- ? candidate.text
1309
- : "")
1310
- .join("\n");
1311
- if (text.trim()) {
1312
- if (selectedTextPartCount >= MAX_STRUCTURED_HISTORY_TEXT_SOURCE_PARTS) {
1313
- omitted = true;
1314
- }
1315
- else {
1316
- selectedTextPartCount++;
1317
- partsReversed.push({ type: "text", text });
1318
- }
1319
- }
1320
- protectedPromptTextAdded = true;
1387
+ if (isPinnedUserPrompt && isTextPart) {
1388
+ if (!pinnedPromptsAdded.has(messageIndex)) {
1389
+ addPinnedPromptText(partsReversed, messageIndex);
1321
1390
  }
1322
1391
  continue;
1323
1392
  }
@@ -1345,15 +1414,17 @@ function boundedStructuredHistorySources(messages, currentPrompt, supplementalMe
1345
1414
  }
1346
1415
  selectedTextPartCount++;
1347
1416
  }
1348
- partsReversed.push(part);
1417
+ partsReversed.push(attachmentStub === undefined
1418
+ ? part
1419
+ : { type: "text", text: attachmentStub });
1349
1420
  }
1350
1421
  if (partsReversed.length) {
1351
1422
  selectedReversed.push({
1352
1423
  message,
1353
1424
  parts: partsReversed.reverse(),
1354
1425
  priority: list === "supplemental",
1355
- textPriority: isPreviousUserPrompt
1356
- ? 2
1426
+ textPriority: isPinnedUserPrompt
1427
+ ? PINNED_USER_PROMPT_TEXT_PRIORITY
1357
1428
  : list === "supplemental"
1358
1429
  ? 1
1359
1430
  : 0,
@@ -1370,6 +1441,22 @@ function boundedStructuredHistorySources(messages, currentPrompt, supplementalMe
1370
1441
  continue;
1371
1442
  visitMessage(historyMessages[index], "messages", index);
1372
1443
  }
1444
+ // The newest-first scan stops long before the first turns of a long agentic
1445
+ // thread; the pinned asks it never reached are replayed anyway.
1446
+ for (const index of [...pinnedUserPromptMessageIndices].sort((left, right) => right - left)) {
1447
+ if (pinnedPromptsAdded.has(index))
1448
+ continue;
1449
+ const parts = [];
1450
+ addPinnedPromptText(parts, index);
1451
+ if (parts.length) {
1452
+ selectedReversed.push({
1453
+ message: historyMessages[index],
1454
+ parts,
1455
+ priority: false,
1456
+ textPriority: PINNED_USER_PROMPT_TEXT_PRIORITY,
1457
+ });
1458
+ }
1459
+ }
1373
1460
  return {
1374
1461
  messages: selectedReversed.reverse(),
1375
1462
  omitted,
@@ -1520,7 +1607,7 @@ function nativeStructuredHistoryFromMessages(messages, currentPrompt, supplement
1520
1607
  results = [];
1521
1608
  };
1522
1609
  for (const part of parts) {
1523
- if (part.type === "text" || part.type === "reasoning") {
1610
+ if (part.type === "text") {
1524
1611
  if (jsonStringByteLengthWithinLimit(part.text, MAX_ADDED_TOOL_HISTORY_BYTES) === undefined ||
1525
1612
  part.text.trim()) {
1526
1613
  flushResults();
@@ -2625,6 +2712,7 @@ export function createAgentNativeChatRuntime(options = {}) {
2625
2712
  ? turnEngine
2626
2713
  : options.engine;
2627
2714
  const history = nativeHistoryFromMessages(turn.messages, prompt);
2715
+ const loadedSkillSlugs = loadedSkillSlugsFromMessages(turn.messages);
2628
2716
  const pendingApprovalHistory = approvedToolCalls && continuationMessageState
2629
2717
  ? pendingApprovalRuntimeMessages(continuationMessageState)
2630
2718
  : { messages: [], omitted: false, toolHistoryOmitted: false };
@@ -2638,6 +2726,7 @@ export function createAgentNativeChatRuntime(options = {}) {
2638
2726
  displayMessage: prompt,
2639
2727
  history,
2640
2728
  ...(structuredHistory?.length ? { structuredHistory } : {}),
2729
+ ...(loadedSkillSlugs.length ? { loadedSkillSlugs } : {}),
2641
2730
  turnId: continuationTurnId ?? turn.queuePromotion?.turnId ?? turnId,
2642
2731
  threadId: session.threadId ?? options.threadId,
2643
2732
  ...(turn.queuePromotion
@@ -2700,6 +2789,9 @@ export function createAgentNativeChatRuntime(options = {}) {
2700
2789
  // Only the continuation that names a stopped run continues it; an approval
2701
2790
  // or connection answered later is not another continuation of that run.
2702
2791
  const { [AUTO_CONTINUE_OF_RUN_METADATA_KEY]: _autoContinueOf, [CONTINUE_OF_RUN_METADATA_KEY]: _continueOf, ...previousMetadata } = previousTurn?.metadata ?? {};
2792
+ const turnToContinue = continuation.attachments?.length
2793
+ ? { ...previousTurn, attachments: continuation.attachments }
2794
+ : previousTurn;
2703
2795
  const approval = continuation.approval;
2704
2796
  const connection = continuation.connection;
2705
2797
  const messageStateKey = continuation.turnId ?? session.id;
@@ -2716,7 +2808,7 @@ export function createAgentNativeChatRuntime(options = {}) {
2716
2808
  if (messageState)
2717
2809
  messageState.message.connectionPending = false;
2718
2810
  return startTurn({
2719
- ...previousTurn,
2811
+ ...turnToContinue,
2720
2812
  prompt: continuation.prompt ??
2721
2813
  `The ${connection.id} connection is now available. Continue the requested work.`,
2722
2814
  metadata: {
@@ -2736,7 +2828,7 @@ export function createAgentNativeChatRuntime(options = {}) {
2736
2828
  if (messageState)
2737
2829
  messageState.message.approvalPending = false;
2738
2830
  return startTurn({
2739
- ...previousTurn,
2831
+ ...turnToContinue,
2740
2832
  prompt: continuation.prompt,
2741
2833
  metadata: {
2742
2834
  ...previousMetadata,
@@ -2759,7 +2851,7 @@ export function createAgentNativeChatRuntime(options = {}) {
2759
2851
  if (messageState)
2760
2852
  messageState.message.approvalPending = false;
2761
2853
  return startTurn({
2762
- ...previousTurn,
2854
+ ...turnToContinue,
2763
2855
  prompt: continuation.prompt ??
2764
2856
  "Approved. Go ahead and run the requested action.",
2765
2857
  metadata: {
@@ -0,0 +1 @@
1
+ export declare function replayEndpointFromAnalyticsEndpoint(endpoint: string, baseUrl?: string): string | null;