@agent-native/core 0.98.6 → 0.98.8

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 (88) hide show
  1. package/corpus/README.md +1 -1
  2. package/corpus/core/CHANGELOG.md +12 -0
  3. package/corpus/core/docs/content/external-agents.mdx +22 -12
  4. package/corpus/core/docs/content/locales/ar-SA/external-agents.mdx +8 -2
  5. package/corpus/core/docs/content/locales/de-DE/external-agents.mdx +8 -2
  6. package/corpus/core/docs/content/locales/es-ES/external-agents.mdx +8 -2
  7. package/corpus/core/docs/content/locales/fr-FR/external-agents.mdx +8 -2
  8. package/corpus/core/docs/content/locales/hi-IN/external-agents.mdx +8 -2
  9. package/corpus/core/docs/content/locales/ja-JP/external-agents.mdx +8 -2
  10. package/corpus/core/docs/content/locales/ko-KR/external-agents.mdx +8 -2
  11. package/corpus/core/docs/content/locales/pt-BR/external-agents.mdx +8 -2
  12. package/corpus/core/docs/content/locales/zh-CN/external-agents.mdx +8 -2
  13. package/corpus/core/docs/content/locales/zh-TW/external-agents.mdx +8 -2
  14. package/corpus/core/package.json +1 -1
  15. package/corpus/core/src/a2a/handlers.ts +6 -5
  16. package/corpus/core/src/a2a/task-store.ts +63 -0
  17. package/corpus/core/src/client/session-replay.ts +90 -6
  18. package/corpus/core/src/integrations/adapters/slack.ts +110 -19
  19. package/corpus/core/src/integrations/identity.ts +103 -39
  20. package/corpus/core/src/integrations/pending-tasks-retry-job.ts +2 -1
  21. package/corpus/core/src/integrations/pending-tasks-store.ts +1 -1
  22. package/corpus/core/src/integrations/plugin.ts +215 -8
  23. package/corpus/core/src/integrations/types.ts +37 -0
  24. package/corpus/core/src/integrations/webhook-handler.ts +1 -0
  25. package/corpus/core/src/mcp/builtin-tools.ts +5 -2
  26. package/corpus/templates/analytics/.agents/skills/session-replay/SKILL.md +4 -1
  27. package/corpus/templates/analytics/AGENTS.md +9 -9
  28. package/corpus/templates/analytics/changelog/2026-07-12-analytics-uses-the-full-in-app-agent-for-multi-step-incident.md +6 -0
  29. package/corpus/templates/analytics/server/handlers/session-replay.ts +15 -9
  30. package/corpus/templates/analytics/server/lib/session-replay-agent-context.ts +23 -5
  31. package/corpus/templates/analytics/server/lib/session-replay.ts +26 -5
  32. package/corpus/templates/analytics/server/plugins/agent-chat.ts +4 -1
  33. package/corpus/templates/analytics/server/routes/api/session-replay/agent-diagnostics.json.get.ts +8 -5
  34. package/corpus/templates/analytics/server/routes/api/session-replay/agent-events.json.get.ts +8 -3
  35. package/corpus/templates/calendar/app/components/calendar/DeleteEventDialog.tsx +12 -2
  36. package/dist/a2a/handlers.d.ts.map +1 -1
  37. package/dist/a2a/handlers.js +6 -6
  38. package/dist/a2a/handlers.js.map +1 -1
  39. package/dist/a2a/task-store.d.ts +12 -0
  40. package/dist/a2a/task-store.d.ts.map +1 -1
  41. package/dist/a2a/task-store.js +54 -0
  42. package/dist/a2a/task-store.js.map +1 -1
  43. package/dist/client/session-replay.d.ts.map +1 -1
  44. package/dist/client/session-replay.js +71 -9
  45. package/dist/client/session-replay.js.map +1 -1
  46. package/dist/collab/routes.d.ts +1 -1
  47. package/dist/collab/struct-routes.d.ts +1 -1
  48. package/dist/file-upload/actions/upload-image.d.ts +1 -1
  49. package/dist/integrations/adapters/slack.d.ts.map +1 -1
  50. package/dist/integrations/adapters/slack.js +94 -11
  51. package/dist/integrations/adapters/slack.js.map +1 -1
  52. package/dist/integrations/identity.d.ts +19 -2
  53. package/dist/integrations/identity.d.ts.map +1 -1
  54. package/dist/integrations/identity.js +76 -34
  55. package/dist/integrations/identity.js.map +1 -1
  56. package/dist/integrations/pending-tasks-retry-job.d.ts.map +1 -1
  57. package/dist/integrations/pending-tasks-retry-job.js +2 -1
  58. package/dist/integrations/pending-tasks-retry-job.js.map +1 -1
  59. package/dist/integrations/pending-tasks-store.js +1 -1
  60. package/dist/integrations/pending-tasks-store.js.map +1 -1
  61. package/dist/integrations/plugin.d.ts.map +1 -1
  62. package/dist/integrations/plugin.js +165 -9
  63. package/dist/integrations/plugin.js.map +1 -1
  64. package/dist/integrations/types.d.ts +32 -0
  65. package/dist/integrations/types.d.ts.map +1 -1
  66. package/dist/integrations/types.js.map +1 -1
  67. package/dist/integrations/webhook-handler.js +1 -0
  68. package/dist/integrations/webhook-handler.js.map +1 -1
  69. package/dist/mcp/builtin-tools.d.ts.map +1 -1
  70. package/dist/mcp/builtin-tools.js +5 -2
  71. package/dist/mcp/builtin-tools.js.map +1 -1
  72. package/dist/notifications/routes.d.ts +1 -1
  73. package/dist/progress/routes.d.ts +1 -1
  74. package/dist/resources/handlers.d.ts +3 -3
  75. package/dist/server/agent-engine-api-key-route.d.ts +1 -1
  76. package/dist/server/transcribe-voice.d.ts +1 -1
  77. package/docs/content/external-agents.mdx +22 -12
  78. package/docs/content/locales/ar-SA/external-agents.mdx +8 -2
  79. package/docs/content/locales/de-DE/external-agents.mdx +8 -2
  80. package/docs/content/locales/es-ES/external-agents.mdx +8 -2
  81. package/docs/content/locales/fr-FR/external-agents.mdx +8 -2
  82. package/docs/content/locales/hi-IN/external-agents.mdx +8 -2
  83. package/docs/content/locales/ja-JP/external-agents.mdx +8 -2
  84. package/docs/content/locales/ko-KR/external-agents.mdx +8 -2
  85. package/docs/content/locales/pt-BR/external-agents.mdx +8 -2
  86. package/docs/content/locales/zh-CN/external-agents.mdx +8 -2
  87. package/docs/content/locales/zh-TW/external-agents.mdx +8 -2
  88. package/package.json +1 -1
@@ -26,12 +26,12 @@ import { ComputerSupervisionError } from "./computer-supervision.js";
26
26
  import { getIntegrationConfig, saveIntegrationConfig } from "./config-store.js";
27
27
  import { claimIntegrationControl } from "./controls-store.js";
28
28
  import { startGoogleDocsPoller, handlePushNotification, } from "./google-docs-poller.js";
29
- import { resolveDefaultIntegrationExecutionContext } from "./identity.js";
29
+ import { IntegrationIdentityDeclinedError, resolveDefaultIntegrationExecutionContext, } from "./identity.js";
30
30
  import { disconnectIntegrationInstallation, listIntegrationInstallations, resolveIntegrationTokenBundle, updateIntegrationInstallation, upsertIntegrationInstallation, } from "./installations-store.js";
31
31
  import { forgetIntegrationMemory, integrationMemoryActions, listIntegrationMemory, rememberForIntegrationScope, } from "./integration-memory.js";
32
32
  import { extractBearerToken, signInternalToken, verifyInternalToken, } from "./internal-token.js";
33
33
  import { startPendingTasksRetryJob } from "./pending-tasks-retry-job.js";
34
- import { claimPendingTask, getNextPendingTaskIdForThread, insertPendingTask, MAX_PENDING_TASK_ATTEMPTS, markTaskCompleted, markTaskFailed, markTaskRetryable, } from "./pending-tasks-store.js";
34
+ import { claimPendingTask, getNextPendingTaskIdForThread, insertPendingTask, isDuplicateEventError, MAX_PENDING_TASK_ATTEMPTS, markTaskCompleted, markTaskFailed, markTaskRetryable, } from "./pending-tasks-store.js";
35
35
  import { claimNextComputerCommand, claimNextRemoteCommand, enqueueComputerCommand, enqueueRemoteCommand as enqueueRemoteCommandRow, isRemoteCommandKind, listRemoteCommandsForOwner, updateRemoteCommandResult, } from "./remote-commands-store.js";
36
36
  import { authenticateRemoteDeviceToken, createRemoteDevice, getRemoteComputerCapabilities, getRemoteDeviceForOwner, listRemoteDevicesForOwner, revokeRemoteDeviceForOwner, toPublicRemoteDevice, unregisterRemoteDevice, updateRemoteDeviceDetails, } from "./remote-devices-store.js";
37
37
  import { listRemotePushNotificationsForOwner, listRemotePushRegistrationsForOwner, queueRemotePushNotifications, toPublicRemotePushRegistration, unregisterRemotePushRegistrationForOwner, upsertRemotePushRegistration, } from "./remote-push-store.js";
@@ -124,6 +124,14 @@ Keep responses concise — messaging platforms have character limits and users e
124
124
 
125
125
  If a task requires many steps, summarize what you did rather than streaming every detail.`;
126
126
  const REMOTE_DEVICE_ONLINE_MS = 90_000;
127
+ // One decline reply per sender + decline reason per window: during a Slack
128
+ // API outage every message would otherwise get another identical "try again"
129
+ // reply. Short enough that a persistent condition still reminds the sender.
130
+ const DECLINE_NOTICE_DEDUPE_TTL_MS = 5 * 60 * 1_000;
131
+ const SYSTEM_NOTICE_DEDUPE_TTL_MS = 24 * 60 * 60 * 1_000;
132
+ function systemNoticeEventKey(dedupeKey, ttlMs, now = Date.now()) {
133
+ return `system-notice:${dedupeKey}:${Math.floor(now / ttlMs)}`;
134
+ }
127
135
  export async function enqueueRemoteCommand(envelope) {
128
136
  const ownerEmail = readString(envelope.ownerEmail);
129
137
  if (!ownerEmail)
@@ -458,6 +466,61 @@ export function createIntegrationsPlugin(options) {
458
466
  const initialToolNames = Object.keys(localActions);
459
467
  const h3 = getH3App(nitroApp);
460
468
  const P = `${FRAMEWORK_ROUTE_PREFIX}/integrations`;
469
+ async function enqueueSystemNotice(event, incoming, text, opts) {
470
+ if (!text.trim())
471
+ return;
472
+ const taskId = `notice-${Date.now()}-${Math.random().toString(36).slice(2, 8)}`;
473
+ const dedupeTtlMs = Math.max(1, opts?.dedupeTtlMs ?? SYSTEM_NOTICE_DEDUPE_TTL_MS);
474
+ const noticeThreadId = `system-notice:${taskId}`;
475
+ const payload = {
476
+ kind: "system-notice",
477
+ incoming,
478
+ text,
479
+ ...(opts?.dedupeKey ? { dedupeKey: opts.dedupeKey } : {}),
480
+ ...(opts?.dedupeTtlMs ? { dedupeTtlMs: opts.dedupeTtlMs } : {}),
481
+ };
482
+ try {
483
+ await insertPendingTask({
484
+ id: taskId,
485
+ platform: incoming.platform,
486
+ // System notices are auxiliary delivery work, not the user's agent
487
+ // run. Give each notice its own queue lane so a retrying notice cannot
488
+ // block the real message task for this Slack/Telegram thread.
489
+ externalThreadId: noticeThreadId,
490
+ payload: JSON.stringify(payload),
491
+ ownerEmail: `integration@${incoming.platform}`,
492
+ externalEventKey: opts?.dedupeKey
493
+ ? systemNoticeEventKey(opts.dedupeKey, dedupeTtlMs)
494
+ : undefined,
495
+ });
496
+ }
497
+ catch (err) {
498
+ if (isDuplicateEventError(err))
499
+ return;
500
+ throw err;
501
+ }
502
+ // The SQL row is the durable source of truth. This best-effort self-call
503
+ // only reduces latency; the recurring pending-task sweep retries a row
504
+ // if the serverless host freezes this webhook execution immediately.
505
+ let token;
506
+ try {
507
+ token = signInternalToken(taskId);
508
+ }
509
+ catch (err) {
510
+ if (process.env.NODE_ENV === "production")
511
+ throw err;
512
+ }
513
+ void fetch(`${getBaseUrl(event)}${P}/process-task`, {
514
+ method: "POST",
515
+ headers: {
516
+ "Content-Type": "application/json",
517
+ ...(token ? { Authorization: `Bearer ${token}` } : {}),
518
+ },
519
+ body: JSON.stringify({ taskId }),
520
+ }).catch((err) => {
521
+ console.warn("[integrations] System notice dispatch failed:", err);
522
+ });
523
+ }
461
524
  async function requireSession(event) {
462
525
  const session = await getSession(event).catch(() => null);
463
526
  if (session?.email)
@@ -1234,6 +1297,23 @@ export function createIntegrationsPlugin(options) {
1234
1297
  ...(task.orgId ? { orgId: task.orgId } : {}),
1235
1298
  isIntegrationCaller: true,
1236
1299
  }, async () => {
1300
+ const taskPayload = JSON.parse(task.payload);
1301
+ if (taskPayload.kind === "system-notice") {
1302
+ if (!adapter.sendSystemNotice) {
1303
+ throw new Error(`Platform ${task.platform} cannot deliver system notices`);
1304
+ }
1305
+ const config = await getIntegrationConfig(task.platform);
1306
+ const credentialContext = await credentialContextForIntegrationConfig(config);
1307
+ await withCredentialContext(credentialContext, () => adapter.sendSystemNotice(taskPayload.incoming, taskPayload.text, {
1308
+ ...(taskPayload.dedupeKey
1309
+ ? { dedupeKey: taskPayload.dedupeKey }
1310
+ : {}),
1311
+ ...(taskPayload.dedupeTtlMs
1312
+ ? { dedupeTtlMs: taskPayload.dedupeTtlMs }
1313
+ : {}),
1314
+ }));
1315
+ return;
1316
+ }
1237
1317
  const resources = await loadResourcesForPrompt(task.ownerEmail, true, options?.appId, task.orgId);
1238
1318
  await processIntegrationTask(task, {
1239
1319
  adapter,
@@ -1907,19 +1987,41 @@ export function createIntegrationsPlugin(options) {
1907
1987
  }
1908
1988
  let defaultExecutionContext = null;
1909
1989
  if (incoming.platform === "slack" &&
1910
- incoming.conversationType === "dm") {
1990
+ incoming.conversationType === "dm" &&
1991
+ !options?.resolveExecutionContext) {
1911
1992
  try {
1912
1993
  defaultExecutionContext = await withCredentialContext(credentialContext, () => resolveDefaultIntegrationExecutionContext(incoming));
1913
1994
  }
1914
1995
  catch (err) {
1915
- // An app-specific resolver may intentionally support a legacy
1916
- // Slack setup. When no override exists, the branch below fails
1917
- // closed instead of falling back to a service principal.
1918
- if (!options?.resolveExecutionContext && !options?.resolveOwner) {
1996
+ // The legacy owner-only resolver predates org-bound identities
1997
+ // and must not turn a rejected Slack DM into an authenticated
1998
+ // owner run. Custom resolveExecutionContext is checked above and
1999
+ // skips this default ladder entirely so apps can fully own auth
2000
+ // without framework membership checks or identity side effects.
2001
+ const declined = err instanceof IntegrationIdentityDeclinedError ? err : null;
2002
+ if (declined) {
2003
+ console.warn(`[integrations] default Slack DM identity declined message:`, declined.message);
2004
+ if (adapter.sendSystemNotice) {
2005
+ try {
2006
+ await enqueueSystemNotice(event, incoming, declined.userFacingMessage, {
2007
+ dedupeKey: `decline:${incoming.tenantId ?? "unknown"}:${incoming.senderId ?? "unknown"}:${declined.reason}`,
2008
+ dedupeTtlMs: DECLINE_NOTICE_DEDUPE_TTL_MS,
2009
+ });
2010
+ }
2011
+ catch (noticeErr) {
2012
+ console.warn(`[integrations] could not persist decline notice:`, noticeErr instanceof Error
2013
+ ? noticeErr.message
2014
+ : noticeErr);
2015
+ setResponseStatus(event, 500);
2016
+ return { error: "notice enqueue failed" };
2017
+ }
2018
+ }
2019
+ }
2020
+ else {
1919
2021
  console.error(`[integrations] default Slack DM identity denied message:`, err);
1920
- setResponseStatus(event, 200);
1921
- return "ok";
1922
2022
  }
2023
+ setResponseStatus(event, 200);
2024
+ return "ok";
1923
2025
  }
1924
2026
  }
1925
2027
  let executionContext = {
@@ -1939,6 +2041,60 @@ export function createIntegrationsPlugin(options) {
1939
2041
  }
1940
2042
  else if (defaultExecutionContext) {
1941
2043
  executionContext = defaultExecutionContext;
2044
+ if (defaultExecutionContext.anonymousMember) {
2045
+ if (!options?.allowAnonymousOrgScopedSlackDm) {
2046
+ const senderEmail = typeof incoming.senderEmail === "string" &&
2047
+ incoming.senderEmail.trim()
2048
+ ? incoming.senderEmail.trim()
2049
+ : null;
2050
+ const noticeText = senderEmail
2051
+ ? `I couldn't match your Slack account to an organization member, so I can't run this request. Ask an organization admin to add ${senderEmail}, then try again.`
2052
+ : "I couldn't verify your Slack account email, so I can't run this request. Ask an organization admin to reconnect Slack with the users:read.email scope, then try again.";
2053
+ if (adapter.sendSystemNotice) {
2054
+ try {
2055
+ await enqueueSystemNotice(event, incoming, noticeText, {
2056
+ dedupeKey: `anonymous-tier-disabled:${incoming.tenantId ?? "unknown"}:${incoming.senderId ?? "unknown"}`,
2057
+ });
2058
+ }
2059
+ catch (noticeErr) {
2060
+ console.warn(`[integrations] could not persist unlinked-member notice:`, noticeErr instanceof Error
2061
+ ? noticeErr.message
2062
+ : noticeErr);
2063
+ setResponseStatus(event, 500);
2064
+ return { error: "notice enqueue failed" };
2065
+ }
2066
+ }
2067
+ setResponseStatus(event, 200);
2068
+ return "ok";
2069
+ }
2070
+ // The anonymous tier must never be silent. (1) The agent run
2071
+ // can tell: the note rides the serialized `incoming` into the
2072
+ // queued task and surfaces via <integration-context>.
2073
+ incoming.identityNote =
2074
+ "Caller is an unlinked Slack workspace member running with organization-wide visibility only; personal or privately-shared data is not accessible. They can get personal access by having an admin add their Slack email to the organization (or by reconnecting Slack with the users:read.email scope).";
2075
+ // (2) The sender gets a one-time heads-up through the same
2076
+ // durable SQL queue as agent work. The self-dispatch is only a
2077
+ // latency optimization; the retry sweep guarantees delivery.
2078
+ if (adapter.sendSystemNotice) {
2079
+ const senderEmail = typeof incoming.senderEmail === "string" &&
2080
+ incoming.senderEmail.trim()
2081
+ ? incoming.senderEmail.trim()
2082
+ : null;
2083
+ const noticeText = senderEmail
2084
+ ? `Heads up: I couldn't match your Slack account to an organization member, so I can only use org-wide data. Ask an admin to add ${senderEmail} to the organization for personal access.`
2085
+ : "Heads up: I couldn't verify your Slack account's email, so I can only use org-wide data. Ask an admin to update the Slack connection with the users:read.email scope for personal access.";
2086
+ try {
2087
+ await enqueueSystemNotice(event, incoming, noticeText, {
2088
+ dedupeKey: `anonymous-tier:${incoming.tenantId ?? "unknown"}:${incoming.senderId ?? "unknown"}`,
2089
+ });
2090
+ }
2091
+ catch (noticeErr) {
2092
+ console.warn(`[integrations] could not persist anonymous-tier notice:`, noticeErr instanceof Error ? noticeErr.message : noticeErr);
2093
+ setResponseStatus(event, 500);
2094
+ return { error: "notice enqueue failed" };
2095
+ }
2096
+ }
2097
+ }
1942
2098
  }
1943
2099
  else if (options?.resolveOwner) {
1944
2100
  try {