@agent-native/core 0.207.0 → 0.207.1-nightly-20261012011809

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 (61) hide show
  1. package/dist/agent/thread-data-builder.d.ts +1 -0
  2. package/dist/agent/thread-data-builder.js +220 -45
  3. package/dist/agent/thread-message-projection.d.ts +3 -0
  4. package/dist/agent/thread-message-projection.js +136 -3
  5. package/dist/automation/index.d.ts +1 -1
  6. package/dist/chat-threads/store.d.ts +1 -0
  7. package/dist/chat-threads/store.js +14 -4
  8. package/dist/cli/connect.d.ts +1 -0
  9. package/dist/cli/connect.js +8 -3
  10. package/dist/client/chat/agentkit-agent-native.js +151 -77
  11. package/dist/client/chat/runtime.js +61 -2
  12. package/dist/client/onboarding/first-run-status.js +0 -4
  13. package/dist/collab/routes.d.ts +1 -1
  14. package/dist/file-upload/actions/upload-image.d.ts +1 -1
  15. package/dist/jobs/actions/manage-recurring-job.d.ts +1 -1
  16. package/dist/mcp/agent-connected.d.ts +13 -0
  17. package/dist/mcp/agent-connected.js +63 -0
  18. package/dist/mcp/connect-app-label.d.ts +11 -0
  19. package/dist/mcp/connect-app-label.js +33 -0
  20. package/dist/mcp/connect-route.js +41 -33
  21. package/dist/mcp/connect-store.d.ts +6 -1
  22. package/dist/mcp/connect-store.js +10 -2
  23. package/dist/mcp/oauth-route.js +17 -5
  24. package/dist/mcp/oauth-store.d.ts +2 -0
  25. package/dist/mcp/oauth-store.js +5 -1
  26. package/dist/notifications/routes.d.ts +1 -1
  27. package/dist/org/index.d.ts +2 -2
  28. package/dist/progress/routes.d.ts +1 -1
  29. package/dist/provider-api/actions/custom-provider-registration.d.ts +9 -9
  30. package/dist/provider-api/actions/provider-api.d.ts +11 -11
  31. package/dist/provider-api/corpus-jobs.d.ts +2 -2
  32. package/dist/resources/handlers.d.ts +1 -1
  33. package/dist/secrets/routes.d.ts +5 -5
  34. package/dist/server/agent-chat-plugin.js +1 -1
  35. package/dist/server/agent-engine-api-key-route.d.ts +1 -1
  36. package/dist/server/agent-engine-default-model-route.d.ts +2 -2
  37. package/dist/server/core-routes-plugin.d.ts +1 -1
  38. package/dist/shared/mcp-connect-content.d.ts +1 -0
  39. package/dist/shared/mcp-connect-content.js +3 -0
  40. package/dist/sharing/actions/list-resource-shares.d.ts +1 -1
  41. package/dist/tracking/registry.d.ts +1 -0
  42. package/dist/tracking/registry.js +1 -1
  43. package/dist/triggers/actions/manage-automation.d.ts +7 -7
  44. package/docs/content/agent-native-toolkit.mdx +42 -38
  45. package/docs/content/components.mdx +29 -18
  46. package/docs/content/notifications.mdx +1 -1
  47. package/docs/content/progress.mdx +1 -1
  48. package/docs/content/sharing.mdx +1 -1
  49. package/docs/content/toolkit-agent-ux.mdx +17 -2
  50. package/docs/content/toolkit-command-navigation.mdx +4 -2
  51. package/docs/content/toolkit-comments-review.mdx +1 -1
  52. package/docs/content/toolkit-context-knowledge.mdx +14 -16
  53. package/docs/content/toolkit-editors-canvases.mdx +24 -7
  54. package/docs/content/toolkit-history.mdx +13 -26
  55. package/docs/content/toolkit-org-team.mdx +4 -4
  56. package/docs/content/toolkit-resources.mdx +3 -1
  57. package/docs/content/toolkit-settings.mdx +1 -1
  58. package/docs/content/toolkit-setup-connections.mdx +1 -1
  59. package/docs/content/toolkit-sharing.mdx +6 -6
  60. package/docs/content/toolkit-ui.mdx +11 -0
  61. package/package.json +4 -4
@@ -57,6 +57,7 @@ export interface ConnectDeps {
57
57
  }
58
58
  export declare function runDeviceFlow(baseUrl: string, appSlug: string, clientArg: string, deps?: ConnectDeps, options?: {
59
59
  fullCatalog?: boolean;
60
+ purpose?: "agent" | "credential";
60
61
  }): Promise<{
61
62
  token?: string;
62
63
  mcpUrl: string;
@@ -329,7 +329,7 @@ async function resolveHostedAppsFromPrompt(deps) {
329
329
  return apps.filter((app) => selected.has(app.name));
330
330
  }
331
331
  function clientArgForDeviceFlow(clients) {
332
- return clients.length === 1 ? clients[0] : "all";
332
+ return clients.join(",");
333
333
  }
334
334
  export function supportsRemoteMcpOAuth(client) {
335
335
  return REMOTE_MCP_OAUTH_CLIENTS.has(client);
@@ -616,6 +616,7 @@ export async function runDeviceFlow(baseUrl, appSlug, clientArg, deps = {}, opti
616
616
  client: clientArg,
617
617
  app: appSlug,
618
618
  ...(options.fullCatalog ? { fullCatalog: true } : {}),
619
+ ...(options.purpose ? { purpose: options.purpose } : {}),
619
620
  });
620
621
  if (status >= 200 && status < 300 && json?.device_code) {
621
622
  start = json;
@@ -1637,7 +1638,9 @@ async function connectOne(rawUrl, parsed, clients, deps) {
1637
1638
  try {
1638
1639
  logOut("");
1639
1640
  logOut(` Minting a publish token for the local Plans server (device flow)…`);
1640
- const grant = await runDeviceFlow(baseUrl, appSlug, "codex", deps);
1641
+ const grant = await runDeviceFlow(baseUrl, appSlug, "codex", deps, {
1642
+ purpose: "credential",
1643
+ });
1641
1644
  if (grant?.token) {
1642
1645
  publishToken = grant.token;
1643
1646
  }
@@ -1781,7 +1784,9 @@ export async function runServiceTokenMint(parsed, deps = {}) {
1781
1784
  logOut("");
1782
1785
  logOut(` Creating org service token "${serviceName}" on ${baseUrl}`);
1783
1786
  logOut(" First, verify it's you (the token will belong to your org)…");
1784
- const grant = await runDeviceFlow(baseUrl, appSlug, "codex", deps);
1787
+ const grant = await runDeviceFlow(baseUrl, appSlug, "codex", deps, {
1788
+ purpose: "credential",
1789
+ });
1785
1790
  if (!grant?.token) {
1786
1791
  logErr(" Could not authenticate (the server returned no bearer token).");
1787
1792
  logErr(" Org service tokens require a deployed app with auth configured.");
@@ -1,6 +1,6 @@
1
1
  import { AgentKitRunSlotBusyError } from "@agent-native/agentkit/client";
2
- import { isAgentKitProtocolVersion, parseAgentQueuedMessage, parseAgentRunOptions, parseAgentThreadSnapshot, persistableFilePart, } from "@agent-native/agentkit/protocol";
3
- import { projectRootAssistantMessages } from "../../agent/thread-message-projection.js";
2
+ import { agentEventSequenceEnd, isAgentKitProtocolVersion, parseAgentQueuedMessage, parseAgentRunOptions, parseAgentThreadSnapshot, persistableFilePart, } from "@agent-native/agentkit/protocol";
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 { agentEngineStatusUrlForChatApi, requireAgentEngineConfiguredForDispatch, } from "../agent-engine-readiness.js";
@@ -278,18 +278,6 @@ function storedMessages(value, now, fallbackTextFormat) {
278
278
  ];
279
279
  });
280
280
  }
281
- /**
282
- * The server answers a turn it refused before any run started with a durable
283
- * notice and a failed run. The run renders as the recovery card, so showing the
284
- * notice too would print the same failure twice.
285
- */
286
- function isRenderedRunNotStartedNotice(message, failedRunIds) {
287
- const metadata = asRecord(message.metadata);
288
- return (message.role === "assistant" &&
289
- asRecord(metadata?.custom)?.[RUN_NOT_STARTED_METADATA_KEY] === true &&
290
- typeof metadata?.runId === "string" &&
291
- failedRunIds.has(metadata.runId));
292
- }
293
281
  function sameUserPrompt(left, right) {
294
282
  if (left.role !== "user" ||
295
283
  right.role !== "user" ||
@@ -667,8 +655,15 @@ function reconcileDurableMessages(messages, durable, events, runs, toolCalls) {
667
655
  });
668
656
  const representedDurableAssistantIds = rootAssistantProjection.representedRootMessageIds;
669
657
  const snapshotRunIdsByMessageId = rootAssistantProjection.snapshotRunIdsByMessageId;
658
+ const supersededSnapshotMessageIds = rootAssistantProjection.supersededSnapshotMessageIds;
670
659
  const rootMessageIdsBySnapshotMessageId = new Map([...rootAssistantProjection.snapshotMessageIdsByRootMessageId].map(([rootId, snapshotId]) => [snapshotId, rootId]));
671
- const deduplicatedMessages = messages.filter((message) => !representedDurableAssistantIds.has(message.id) &&
660
+ const snapshotUserIds = new Set(messages.flatMap((message) => message.role === "user" ? [message.id] : []));
661
+ const representedDurableUserIds = new Set([...submittedUsersByAgentKitMessageId].flatMap(([snapshotId, stored]) => stored && snapshotId !== stored.id && snapshotUserIds.has(snapshotId)
662
+ ? [stored.id]
663
+ : []));
664
+ const deduplicatedMessages = messages.filter((message) => !(message.role === "user" && representedDurableUserIds.has(message.id)) &&
665
+ !representedDurableAssistantIds.has(message.id) &&
666
+ !supersededSnapshotMessageIds.has(message.id) &&
672
667
  !supersededCompletedAssistantIds.has(message.id));
673
668
  const representedSubmittedUserIds = new Set();
674
669
  const storedUserBySnapshotId = new Map();
@@ -830,6 +825,13 @@ function reconcileDurableMessages(messages, durable, events, runs, toolCalls) {
830
825
  }
831
826
  }
832
827
  const runId = snapshotRunId(message);
828
+ const sameRunRoot = runId ? durableByRun.get(runId) : undefined;
829
+ if (sameRunRoot &&
830
+ rootAssistantProjection.snapshotMessageIdsByRootMessageId.has(sameRunRoot.id) &&
831
+ rootAssistantProjection.snapshotMessageIdsByRootMessageId.get(sameRunRoot.id) !== message.id &&
832
+ !durableById.has(message.id)) {
833
+ return message;
834
+ }
833
835
  const matched = durableById.get(message.id) ??
834
836
  (runId ? durableByRun.get(runId) : undefined);
835
837
  // An earlier run of a folded reply is completed with the continuation text
@@ -1135,77 +1137,162 @@ function persistedRetryRequestAttachments(value) {
1135
1137
  ];
1136
1138
  });
1137
1139
  }
1140
+ const MAX_PERSISTED_EVENT_OMISSION_RANGE = 256;
1141
+ const MAX_PERSISTED_EVENT_PREFIX_RANGES = 4_096;
1138
1142
  function persistedHistoryEvents(events = []) {
1139
- const sequenceByRun = new Map();
1140
- return events.flatMap((event) => {
1143
+ const persisted = [];
1144
+ const seenRuns = new Set();
1145
+ let generatedPrefixRanges = 0;
1146
+ const lastOmissionByRun = new Map();
1147
+ for (const event of events) {
1148
+ if (!seenRuns.has(event.runId)) {
1149
+ seenRuns.add(event.runId);
1150
+ const prefixRangeCount = Math.ceil(Math.max(0, event.sequence - 1) / MAX_PERSISTED_EVENT_OMISSION_RANGE);
1151
+ if (generatedPrefixRanges + prefixRangeCount >
1152
+ MAX_PERSISTED_EVENT_PREFIX_RANGES) {
1153
+ throw new RangeError("Agent chat retained history prefix exceeds the supported omission range count.");
1154
+ }
1155
+ generatedPrefixRanges += prefixRangeCount;
1156
+ for (let firstSequence = 1; firstSequence < event.sequence; firstSequence += MAX_PERSISTED_EVENT_OMISSION_RANGE) {
1157
+ const lastSequence = Math.min(event.sequence - 1, firstSequence + MAX_PERSISTED_EVENT_OMISSION_RANGE - 1);
1158
+ const index = persisted.length;
1159
+ persisted.push({
1160
+ id: `persisted-history-prefix:${event.runId}:${firstSequence}`,
1161
+ threadId: event.threadId,
1162
+ runId: event.runId,
1163
+ sequence: firstSequence,
1164
+ occurredAt: event.occurredAt,
1165
+ type: "x-agent-native.persisted-event-omission-range",
1166
+ payload: {
1167
+ lastSequence,
1168
+ sourceType: "runtime_history_prefix",
1169
+ },
1170
+ });
1171
+ lastOmissionByRun.set(event.runId, {
1172
+ index,
1173
+ firstSequence,
1174
+ lastSequence,
1175
+ });
1176
+ }
1177
+ }
1141
1178
  const base = () => {
1142
- const sequence = (sequenceByRun.get(event.runId) ?? 0) + 1;
1143
- sequenceByRun.set(event.runId, sequence);
1144
1179
  return {
1145
1180
  id: event.id,
1146
1181
  threadId: event.threadId,
1147
1182
  runId: event.runId,
1148
- sequence,
1183
+ sequence: event.sequence,
1149
1184
  occurredAt: event.occurredAt,
1150
1185
  };
1151
1186
  };
1152
1187
  if (event.type === "run.started") {
1153
- return [{ ...base(), type: event.type }];
1188
+ persisted.push({ ...base(), type: event.type });
1189
+ lastOmissionByRun.delete(event.runId);
1190
+ continue;
1154
1191
  }
1155
1192
  if (event.type === "run.completed" || event.type === "run.cancelled") {
1156
- return [{ ...base(), type: event.type }];
1193
+ persisted.push({ ...base(), type: event.type });
1194
+ lastOmissionByRun.delete(event.runId);
1195
+ continue;
1157
1196
  }
1158
1197
  if (event.type === "run.failed") {
1159
- return [
1160
- { ...base(), type: event.type, error: persistedError(event.error) },
1161
- ];
1198
+ persisted.push({
1199
+ ...base(),
1200
+ type: event.type,
1201
+ error: persistedError(event.error),
1202
+ });
1203
+ lastOmissionByRun.delete(event.runId);
1204
+ continue;
1162
1205
  }
1163
1206
  if (event.type === "run.status") {
1164
- return [{ ...base(), type: event.type, status: event.status }];
1207
+ persisted.push({ ...base(), type: event.type, status: event.status });
1208
+ lastOmissionByRun.delete(event.runId);
1209
+ continue;
1210
+ }
1211
+ if (event.type === "x-agent-native.persisted-event-omission" ||
1212
+ event.type === "x-agent-native.persisted-event-omission-range") {
1213
+ const index = persisted.length;
1214
+ persisted.push(event);
1215
+ lastOmissionByRun.set(event.runId, {
1216
+ index,
1217
+ firstSequence: event.sequence,
1218
+ lastSequence: agentEventSequenceEnd(event),
1219
+ });
1220
+ continue;
1165
1221
  }
1166
1222
  if (event.type === "activity.started" ||
1167
1223
  event.type === "activity.updated" ||
1168
1224
  event.type === "activity.completed") {
1169
1225
  const activity = event.activity;
1170
- return [
1171
- {
1172
- ...base(),
1173
- type: event.type,
1174
- activity: {
1175
- id: activity.id,
1176
- kind: activity.kind,
1177
- label: activity.label,
1178
- status: activity.status,
1179
- ...(activity.runId ? { runId: activity.runId } : {}),
1180
- ...(activity.agentId ? { agentId: activity.agentId } : {}),
1181
- ...(activity.startedAt ? { startedAt: activity.startedAt } : {}),
1182
- ...(activity.completedAt
1183
- ? { completedAt: activity.completedAt }
1184
- : {}),
1185
- },
1226
+ persisted.push({
1227
+ ...base(),
1228
+ type: event.type,
1229
+ activity: {
1230
+ id: activity.id,
1231
+ kind: activity.kind,
1232
+ label: activity.label,
1233
+ status: activity.status,
1234
+ ...(activity.runId ? { runId: activity.runId } : {}),
1235
+ ...(activity.agentId ? { agentId: activity.agentId } : {}),
1236
+ ...(activity.startedAt ? { startedAt: activity.startedAt } : {}),
1237
+ ...(activity.completedAt
1238
+ ? { completedAt: activity.completedAt }
1239
+ : {}),
1186
1240
  },
1187
- ];
1241
+ });
1242
+ lastOmissionByRun.delete(event.runId);
1243
+ continue;
1188
1244
  }
1189
1245
  if (event.type === "message.completed" &&
1190
1246
  event.message.role === "assistant") {
1191
- return [
1192
- {
1193
- ...base(),
1194
- type: event.type,
1195
- message: {
1196
- id: event.message.id,
1197
- role: event.message.role,
1198
- parts: event.message.parts.filter((part) => part.type === "text"),
1199
- ...(event.message.createdAt
1200
- ? { createdAt: event.message.createdAt }
1201
- : {}),
1202
- ...(event.message.status ? { status: event.message.status } : {}),
1203
- },
1247
+ persisted.push({
1248
+ ...base(),
1249
+ type: event.type,
1250
+ message: {
1251
+ id: event.message.id,
1252
+ role: event.message.role,
1253
+ parts: event.message.parts.filter((part) => part.type === "text"),
1254
+ ...(event.message.createdAt
1255
+ ? { createdAt: event.message.createdAt }
1256
+ : {}),
1257
+ ...(event.message.status ? { status: event.message.status } : {}),
1204
1258
  },
1205
- ];
1259
+ });
1260
+ lastOmissionByRun.delete(event.runId);
1261
+ continue;
1206
1262
  }
1207
- return [];
1208
- });
1263
+ const previous = lastOmissionByRun.get(event.runId);
1264
+ if (previous &&
1265
+ event.sequence === previous.lastSequence + 1 &&
1266
+ event.sequence - previous.firstSequence + 1 <=
1267
+ MAX_PERSISTED_EVENT_OMISSION_RANGE) {
1268
+ const omitted = persisted[previous.index];
1269
+ if (omitted) {
1270
+ persisted[previous.index] = {
1271
+ ...omitted,
1272
+ type: "x-agent-native.persisted-event-omission-range",
1273
+ payload: {
1274
+ ...(asRecord("payload" in omitted ? omitted.payload : undefined) ??
1275
+ {}),
1276
+ lastSequence: event.sequence,
1277
+ },
1278
+ };
1279
+ }
1280
+ previous.lastSequence = event.sequence;
1281
+ continue;
1282
+ }
1283
+ const index = persisted.length;
1284
+ persisted.push({
1285
+ ...base(),
1286
+ type: "x-agent-native.persisted-event-omission",
1287
+ payload: { sourceType: event.type },
1288
+ });
1289
+ lastOmissionByRun.set(event.runId, {
1290
+ index,
1291
+ firstSequence: event.sequence,
1292
+ lastSequence: event.sequence,
1293
+ });
1294
+ }
1295
+ return persisted;
1209
1296
  }
1210
1297
  function persistedMessages(messages) {
1211
1298
  return messages.map((message) => ({
@@ -1807,14 +1894,7 @@ export function createAgentNativeAgentKitTransport(options = {}) {
1807
1894
  const updatedAt = timestamp(stored.updatedAt, createdAt);
1808
1895
  const repository = storedRepository(stored);
1809
1896
  const agentKit = asRecord(repository.agentKit);
1810
- const failedRunIds = new Set((Array.isArray(agentKit?.runs) ? agentKit.runs : []).flatMap((value) => {
1811
- const run = asRecord(value);
1812
- return run?.status === "failed" &&
1813
- asRecord(run.error) &&
1814
- typeof run.id === "string"
1815
- ? [run.id]
1816
- : [];
1817
- }));
1897
+ const failedRunIds = failedRunIdsWithError(agentKit?.runs);
1818
1898
  const storedMessageProjection = storedMessages(repository.messages, now, options.adapter?.textFormat).filter((message) => !isRenderedRunNotStartedNotice(message, failedRunIds));
1819
1899
  const queuedMessages = storedQueue(repository.queuedMessages, threadId, updatedAt);
1820
1900
  const agentKitMessages = Array.isArray(agentKit?.messages)
@@ -2184,7 +2264,7 @@ export function createAgentNativeAgentKitTransport(options = {}) {
2184
2264
  .filter((run) => ["awaiting_approval", "awaiting_input"].includes(run.status))
2185
2265
  .map((run) => run.id)
2186
2266
  : [];
2187
- const failedRunIds = new Set(runs.flatMap((run) => run.status === "failed" && run.error ? [run.id] : []));
2267
+ const failedRunIds = failedRunIdsWithError(runs);
2188
2268
  const messages = reconcileDurableMessages(thread.messages, durableMessages.filter((message) => !isRenderedRunNotStartedNotice(message, failedRunIds)), thread.events, runs, thread.toolCalls);
2189
2269
  const replayFromStart = [
2190
2270
  "running",
@@ -2339,17 +2419,11 @@ export function createAgentNativeAgentKitTransport(options = {}) {
2339
2419
  }
2340
2420
  const changedEvents = compactEvents.filter((event) => changedEventRunIds.has(event.runId));
2341
2421
  const eventRunLastSequenceById = new Map();
2342
- for (const run of input.snapshot.runs ?? []) {
2343
- if (typeof run.id === "string" &&
2344
- Number.isSafeInteger(run.lastSequence) &&
2345
- run.lastSequence >= 0) {
2346
- eventRunLastSequenceById.set(run.id, run.lastSequence);
2347
- }
2348
- }
2349
2422
  for (const event of input.snapshot.events ?? []) {
2350
2423
  const previousSequence = eventRunLastSequenceById.get(event.runId) ?? 0;
2351
- if (Number.isSafeInteger(event.sequence)) {
2352
- eventRunLastSequenceById.set(event.runId, Math.max(previousSequence, event.sequence));
2424
+ const sequenceEnd = agentEventSequenceEnd(event);
2425
+ if (Number.isSafeInteger(sequenceEnd)) {
2426
+ eventRunLastSequenceById.set(event.runId, Math.max(previousSequence, sequenceEnd));
2353
2427
  }
2354
2428
  }
2355
2429
  const changedEventsByRun = new Map();
@@ -770,6 +770,53 @@ function isSyntheticToolHistoryOmissionMessage(message) {
770
770
  (message.id === AGENTKIT_TOOL_HISTORY_OMISSION_MESSAGE_ID_PREFIX ||
771
771
  /^agentkit-tool-history-omission-\d+$/.test(message.id)));
772
772
  }
773
+ function equivalentRuntimeContent(left, right) {
774
+ let remaining = 4096;
775
+ const leftAncestors = new WeakSet();
776
+ const rightAncestors = new WeakSet();
777
+ const compare = (a, b, depth) => {
778
+ if (--remaining < 0 || depth > 64)
779
+ return false;
780
+ if (Object.is(a, b))
781
+ return true;
782
+ if (!a || !b || typeof a !== "object" || typeof b !== "object")
783
+ return false;
784
+ const prototype = Object.getPrototypeOf(a);
785
+ if (prototype !== Object.getPrototypeOf(b) ||
786
+ (prototype !== Object.prototype &&
787
+ prototype !== Array.prototype &&
788
+ prototype !== null) ||
789
+ leftAncestors.has(a) ||
790
+ rightAncestors.has(b))
791
+ return false;
792
+ if (Array.isArray(a) !== Array.isArray(b))
793
+ return false;
794
+ if (Array.isArray(a) && (!Array.isArray(b) || a.length !== b.length))
795
+ return false;
796
+ const keys = Object.keys(a);
797
+ if (keys.length > remaining || keys.length !== Object.keys(b).length)
798
+ return false;
799
+ leftAncestors.add(a);
800
+ rightAncestors.add(b);
801
+ try {
802
+ return keys.every((key) => {
803
+ const leftValue = Object.getOwnPropertyDescriptor(a, key);
804
+ const rightValue = Object.getOwnPropertyDescriptor(b, key);
805
+ return Boolean(leftValue &&
806
+ rightValue &&
807
+ rightValue.enumerable &&
808
+ "value" in leftValue &&
809
+ "value" in rightValue &&
810
+ compare(leftValue.value, rightValue.value, depth + 1));
811
+ });
812
+ }
813
+ finally {
814
+ leftAncestors.delete(a);
815
+ rightAncestors.delete(b);
816
+ }
817
+ };
818
+ return compare(left, right, 0);
819
+ }
773
820
  function priorNativeHistoryMessages(messages, currentPrompt) {
774
821
  const source = messages ?? [];
775
822
  let currentPromptMessageIndex;
@@ -2864,6 +2911,18 @@ export function createAgentNativeChatRuntime(options = {}) {
2864
2911
  const latestUserMessage = [...(turn.messages ?? [])]
2865
2912
  .reverse()
2866
2913
  .find((message) => message.role === "user");
2914
+ const latestUserMessageIndex = latestUserMessage
2915
+ ? (turn.messages?.lastIndexOf(latestUserMessage) ?? -1)
2916
+ : -1;
2917
+ const messagesForHistory = latestUserMessage &&
2918
+ typeof latestUserMessage.id === "string" &&
2919
+ latestUserMessage.id.trim().length > 0 &&
2920
+ latestUserMessageIndex >= 0
2921
+ ? turn.messages.filter((message, index) => message.role !== "user" ||
2922
+ message.id !== latestUserMessage.id ||
2923
+ !equivalentRuntimeContent(message.content, latestUserMessage.content) ||
2924
+ index === latestUserMessageIndex)
2925
+ : turn.messages;
2867
2926
  const prompt = turn.prompt ??
2868
2927
  latestUserMessage?.content
2869
2928
  .map((part) => (part.type === "text" ? part.text : ""))
@@ -2884,12 +2943,12 @@ export function createAgentNativeChatRuntime(options = {}) {
2884
2943
  ? turnEngine
2885
2944
  : options.engine;
2886
2945
  const currentAttachmentUrlSet = currentTurnAttachmentUrls(turn.attachments);
2887
- const history = nativeHistoryFromMessages(turn.messages, prompt, currentAttachmentUrlSet);
2946
+ const history = nativeHistoryFromMessages(messagesForHistory, prompt, currentAttachmentUrlSet);
2888
2947
  const loadedSkillSlugs = loadedSkillSlugsFromMessages(turn.messages);
2889
2948
  const pendingApprovalHistory = approvedToolCalls && continuationMessageState
2890
2949
  ? pendingApprovalRuntimeMessages(continuationMessageState)
2891
2950
  : { messages: [], omitted: false, toolHistoryOmitted: false };
2892
- const structuredHistory = nativeStructuredHistoryFromMessages(turn.messages, prompt, currentAttachmentUrlSet, pendingApprovalHistory.messages, pendingApprovalHistory.omitted, pendingApprovalHistory.toolHistoryOmitted);
2951
+ const structuredHistory = nativeStructuredHistoryFromMessages(messagesForHistory, prompt, currentAttachmentUrlSet, pendingApprovalHistory.messages, pendingApprovalHistory.omitted, pendingApprovalHistory.toolHistoryOmitted);
2893
2952
  return {
2894
2953
  message: prompt,
2895
2954
  ...(latestUserMessage?.id
@@ -55,10 +55,6 @@ async function requestFirstRunOnboardingStatus() {
55
55
  dispatchFirstRunOnboardingStatus(firstRun);
56
56
  return firstRun;
57
57
  }
58
- catch (error) {
59
- dispatchFirstRunOnboardingStatus(false);
60
- throw error;
61
- }
62
58
  finally {
63
59
  if (timeoutId !== undefined)
64
60
  clearTimeout(timeoutId);
@@ -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
- error: string;
17
16
  ok?: undefined;
17
+ error: string;
18
18
  } | {
19
19
  error?: undefined;
20
20
  ok: boolean;
@@ -42,8 +42,8 @@ declare const _default: import("../../action.js").ActionDefinition<{
42
42
  url: string;
43
43
  id: string;
44
44
  provider: string;
45
- error?: undefined;
46
45
  configured?: undefined;
46
+ error?: undefined;
47
47
  } | {
48
48
  url?: undefined;
49
49
  id?: undefined;
@@ -11,9 +11,9 @@ declare const _default: import("../../action.js").ActionDefinition<{
11
11
  enabled?: undefined;
12
12
  nextRun?: undefined;
13
13
  } | {
14
- deleted?: undefined;
15
14
  name: string;
16
15
  enabled: boolean;
17
16
  nextRun: string;
17
+ deleted?: undefined;
18
18
  }>;
19
19
  export default _default;
@@ -0,0 +1,13 @@
1
+ export declare function connectionApp(origin: string, appId?: string): string;
2
+ /**
3
+ * The connect CLI sends the clients it sets up as comma-joined client ids.
4
+ * Any other device client value, such as "Example, Inc.", is one name.
5
+ */
6
+ export declare function deviceFlowClients(client: string | null): string | string[] | null;
7
+ export declare function trackAgentConnected(input: {
8
+ email: string;
9
+ origin: string;
10
+ appId?: string;
11
+ client: string | readonly string[] | null;
12
+ method: "mcp" | "oauth";
13
+ }): Promise<"emitted" | "duplicate" | "disabled" | "failed">;
@@ -0,0 +1,63 @@
1
+ import { createHash, randomUUID } from "node:crypto";
2
+ import { CLIENTS } from "../cli/mcp-config-writers.js";
3
+ import { mutateSetting } from "../settings/store.js";
4
+ import { normalizeTrackingDimension } from "../shared/analytics-events.js";
5
+ import { resolveMcpConnectHostId } from "../shared/mcp-connect-content.js";
6
+ import { isTrackingSuppressed, listTrackingProviders, track, } from "../tracking/registry.js";
7
+ import { detectVendorClient } from "./analytics.js";
8
+ import { connectAppLabel, connectEnvironment } from "./connect-app-label.js";
9
+ export function connectionApp(origin, appId) {
10
+ return normalizeTrackingDimension(connectAppLabel(origin, { appId }, connectEnvironment()));
11
+ }
12
+ /**
13
+ * The connect CLI sends the clients it sets up as comma-joined client ids.
14
+ * Any other device client value, such as "Example, Inc.", is one name.
15
+ */
16
+ export function deviceFlowClients(client) {
17
+ const ids = client?.split(",");
18
+ return ids?.every((id) => CLIENTS.includes(id)) ? ids : client;
19
+ }
20
+ export async function trackAgentConnected(input) {
21
+ // Everything runs inside the try: the connection is already committed, so a
22
+ // tracking or configuration failure must not turn it into a failed response.
23
+ try {
24
+ if (listTrackingProviders().length === 0 ||
25
+ isTrackingSuppressed(input.email))
26
+ return "disabled";
27
+ const app = connectionApp(input.origin, input.appId);
28
+ const raw = input.client;
29
+ const clients = new Set((typeof raw === "string" ? [raw] : (raw ?? ["unknown"])).map((name) => resolveMcpConnectHostId(name) ??
30
+ detectVendorClient(name) ??
31
+ (name.trim().toLowerCase().slice(0, 120) || "unknown")));
32
+ let emitted = false;
33
+ for (const client of clients) {
34
+ const email = input.email.trim().toLowerCase();
35
+ const key = createHash("sha256")
36
+ .update(JSON.stringify([email, app, client]))
37
+ .digest("hex");
38
+ const claimId = randomUUID();
39
+ // Both success paths share a durable CAS claim; process-local state
40
+ // cannot suppress reconnects across serverless invocations.
41
+ const claim = await mutateSetting(`agent-connected:${key}`, (current) => {
42
+ if (current && typeof current.claim_id !== "string") {
43
+ throw new Error("Invalid agent connection claim");
44
+ }
45
+ return current ?? { claim_id: claimId };
46
+ });
47
+ if (claim.claim_id !== claimId)
48
+ continue;
49
+ track("agent_connected", {
50
+ email,
51
+ app,
52
+ client,
53
+ connection_method: input.method,
54
+ }, { userId: email });
55
+ emitted = true;
56
+ }
57
+ return emitted ? "emitted" : "duplicate";
58
+ }
59
+ catch (error) {
60
+ console.error("[agent-connected] Failed to record connection:", error);
61
+ return "failed";
62
+ }
63
+ }
@@ -0,0 +1,11 @@
1
+ import type { McpConnectEnvironment } from "../shared/mcp-connect-content.js";
2
+ export declare function connectEnvironment(): McpConnectEnvironment;
3
+ /**
4
+ * The app a connection belongs to. The connect identity names its MCP server
5
+ * from this and the `agent_connected` event reports it, so the two cannot
6
+ * disagree about which app a client connected to.
7
+ */
8
+ export declare function connectAppLabel(origin: string, options: {
9
+ appId?: string;
10
+ appName?: string;
11
+ }, environment: McpConnectEnvironment): string;
@@ -0,0 +1,33 @@
1
+ import { getAppConfig } from "../app-config/index.js";
2
+ import { isAgentNativeDeploymentEnvironment } from "../config.js";
3
+ import { resolveDeployEnvironment } from "../server/deploy-environment.js";
4
+ export function connectEnvironment() {
5
+ const environment = resolveDeployEnvironment();
6
+ if (!isAgentNativeDeploymentEnvironment(environment)) {
7
+ throw new Error(`Unknown deployment environment "${environment}"`);
8
+ }
9
+ return environment;
10
+ }
11
+ /**
12
+ * The app a connection belongs to. The connect identity names its MCP server
13
+ * from this and the `agent_connected` event reports it, so the two cannot
14
+ * disagree about which app a client connected to.
15
+ */
16
+ export function connectAppLabel(origin, options, environment) {
17
+ const app = getAppConfig().app;
18
+ const declared = options.appId ?? app.id ?? app.template ?? app.slug;
19
+ if (declared)
20
+ return declared;
21
+ // Deployed functions run without npm's `npm_package_name`, so first-party
22
+ // apps usually land here. A beta lane is `beta.<app>.agent-native.com`: its
23
+ // leading label names the lane, and every beta app would share it. Only a
24
+ // beta deployment drops it, so no production name changes.
25
+ try {
26
+ const host = new URL(origin).hostname;
27
+ const h = environment === "beta" ? host.replace(/^beta\./, "") : host;
28
+ return h.split(".")[0] || h;
29
+ }
30
+ catch {
31
+ return options.appName || "app";
32
+ }
33
+ }