@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.
- package/corpus/README.md +1 -1
- package/corpus/core/CHANGELOG.md +12 -0
- package/corpus/core/docs/content/external-agents.mdx +22 -12
- package/corpus/core/docs/content/locales/ar-SA/external-agents.mdx +8 -2
- package/corpus/core/docs/content/locales/de-DE/external-agents.mdx +8 -2
- package/corpus/core/docs/content/locales/es-ES/external-agents.mdx +8 -2
- package/corpus/core/docs/content/locales/fr-FR/external-agents.mdx +8 -2
- package/corpus/core/docs/content/locales/hi-IN/external-agents.mdx +8 -2
- package/corpus/core/docs/content/locales/ja-JP/external-agents.mdx +8 -2
- package/corpus/core/docs/content/locales/ko-KR/external-agents.mdx +8 -2
- package/corpus/core/docs/content/locales/pt-BR/external-agents.mdx +8 -2
- package/corpus/core/docs/content/locales/zh-CN/external-agents.mdx +8 -2
- package/corpus/core/docs/content/locales/zh-TW/external-agents.mdx +8 -2
- package/corpus/core/package.json +1 -1
- package/corpus/core/src/a2a/handlers.ts +6 -5
- package/corpus/core/src/a2a/task-store.ts +63 -0
- package/corpus/core/src/client/session-replay.ts +90 -6
- package/corpus/core/src/integrations/adapters/slack.ts +110 -19
- package/corpus/core/src/integrations/identity.ts +103 -39
- package/corpus/core/src/integrations/pending-tasks-retry-job.ts +2 -1
- package/corpus/core/src/integrations/pending-tasks-store.ts +1 -1
- package/corpus/core/src/integrations/plugin.ts +215 -8
- package/corpus/core/src/integrations/types.ts +37 -0
- package/corpus/core/src/integrations/webhook-handler.ts +1 -0
- package/corpus/core/src/mcp/builtin-tools.ts +5 -2
- package/corpus/templates/analytics/.agents/skills/session-replay/SKILL.md +4 -1
- package/corpus/templates/analytics/AGENTS.md +9 -9
- package/corpus/templates/analytics/changelog/2026-07-12-analytics-uses-the-full-in-app-agent-for-multi-step-incident.md +6 -0
- package/corpus/templates/analytics/server/handlers/session-replay.ts +15 -9
- package/corpus/templates/analytics/server/lib/session-replay-agent-context.ts +23 -5
- package/corpus/templates/analytics/server/lib/session-replay.ts +26 -5
- package/corpus/templates/analytics/server/plugins/agent-chat.ts +4 -1
- package/corpus/templates/analytics/server/routes/api/session-replay/agent-diagnostics.json.get.ts +8 -5
- package/corpus/templates/analytics/server/routes/api/session-replay/agent-events.json.get.ts +8 -3
- package/corpus/templates/calendar/app/components/calendar/DeleteEventDialog.tsx +12 -2
- package/dist/a2a/handlers.d.ts.map +1 -1
- package/dist/a2a/handlers.js +6 -6
- package/dist/a2a/handlers.js.map +1 -1
- package/dist/a2a/task-store.d.ts +12 -0
- package/dist/a2a/task-store.d.ts.map +1 -1
- package/dist/a2a/task-store.js +54 -0
- package/dist/a2a/task-store.js.map +1 -1
- package/dist/client/session-replay.d.ts.map +1 -1
- package/dist/client/session-replay.js +71 -9
- package/dist/client/session-replay.js.map +1 -1
- package/dist/collab/routes.d.ts +1 -1
- package/dist/collab/struct-routes.d.ts +1 -1
- package/dist/file-upload/actions/upload-image.d.ts +1 -1
- package/dist/integrations/adapters/slack.d.ts.map +1 -1
- package/dist/integrations/adapters/slack.js +94 -11
- package/dist/integrations/adapters/slack.js.map +1 -1
- package/dist/integrations/identity.d.ts +19 -2
- package/dist/integrations/identity.d.ts.map +1 -1
- package/dist/integrations/identity.js +76 -34
- package/dist/integrations/identity.js.map +1 -1
- package/dist/integrations/pending-tasks-retry-job.d.ts.map +1 -1
- package/dist/integrations/pending-tasks-retry-job.js +2 -1
- package/dist/integrations/pending-tasks-retry-job.js.map +1 -1
- package/dist/integrations/pending-tasks-store.js +1 -1
- package/dist/integrations/pending-tasks-store.js.map +1 -1
- package/dist/integrations/plugin.d.ts.map +1 -1
- package/dist/integrations/plugin.js +165 -9
- package/dist/integrations/plugin.js.map +1 -1
- package/dist/integrations/types.d.ts +32 -0
- package/dist/integrations/types.d.ts.map +1 -1
- package/dist/integrations/types.js.map +1 -1
- package/dist/integrations/webhook-handler.js +1 -0
- package/dist/integrations/webhook-handler.js.map +1 -1
- package/dist/mcp/builtin-tools.d.ts.map +1 -1
- package/dist/mcp/builtin-tools.js +5 -2
- package/dist/mcp/builtin-tools.js.map +1 -1
- package/dist/notifications/routes.d.ts +1 -1
- package/dist/progress/routes.d.ts +1 -1
- package/dist/resources/handlers.d.ts +3 -3
- package/dist/server/agent-engine-api-key-route.d.ts +1 -1
- package/dist/server/transcribe-voice.d.ts +1 -1
- package/docs/content/external-agents.mdx +22 -12
- package/docs/content/locales/ar-SA/external-agents.mdx +8 -2
- package/docs/content/locales/de-DE/external-agents.mdx +8 -2
- package/docs/content/locales/es-ES/external-agents.mdx +8 -2
- package/docs/content/locales/fr-FR/external-agents.mdx +8 -2
- package/docs/content/locales/hi-IN/external-agents.mdx +8 -2
- package/docs/content/locales/ja-JP/external-agents.mdx +8 -2
- package/docs/content/locales/ko-KR/external-agents.mdx +8 -2
- package/docs/content/locales/pt-BR/external-agents.mdx +8 -2
- package/docs/content/locales/zh-CN/external-agents.mdx +8 -2
- package/docs/content/locales/zh-TW/external-agents.mdx +8 -2
- 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
|
-
//
|
|
1916
|
-
//
|
|
1917
|
-
//
|
|
1918
|
-
|
|
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 {
|