@agent-native/core 0.101.5 → 0.101.7
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/README.md +1 -1
- package/corpus/README.md +1 -1
- package/corpus/core/CHANGELOG.md +12 -0
- package/corpus/core/docs/content/locales/ar-SA/pure-agent-apps.mdx +2 -2
- package/corpus/core/docs/content/locales/de-DE/pure-agent-apps.mdx +2 -2
- package/corpus/core/docs/content/locales/es-ES/pure-agent-apps.mdx +2 -2
- package/corpus/core/docs/content/locales/fr-FR/pure-agent-apps.mdx +2 -2
- package/corpus/core/docs/content/locales/hi-IN/pure-agent-apps.mdx +2 -2
- package/corpus/core/docs/content/locales/ja-JP/pure-agent-apps.mdx +2 -2
- package/corpus/core/docs/content/locales/ko-KR/pure-agent-apps.mdx +2 -2
- package/corpus/core/docs/content/locales/pt-BR/pure-agent-apps.mdx +2 -2
- package/corpus/core/docs/content/locales/zh-CN/pure-agent-apps.mdx +2 -2
- package/corpus/core/docs/content/locales/zh-TW/pure-agent-apps.mdx +2 -2
- package/corpus/core/docs/content/pure-agent-apps.mdx +58 -14
- package/corpus/core/package.json +1 -1
- package/corpus/core/src/a2a/artifact-response.ts +255 -12
- package/corpus/core/src/agent/thread-data-builder.ts +84 -11
- package/corpus/core/src/integrations/a2a-continuation-processor.ts +99 -7
- package/corpus/core/src/integrations/adapters/discord.ts +3 -1
- package/corpus/core/src/integrations/adapters/email.ts +4 -1
- package/corpus/core/src/integrations/adapters/google-docs.ts +4 -1
- package/corpus/core/src/integrations/adapters/microsoft-teams.ts +3 -1
- package/corpus/core/src/integrations/adapters/slack.ts +38 -9
- package/corpus/core/src/integrations/adapters/telegram.ts +32 -10
- package/corpus/core/src/integrations/adapters/whatsapp.ts +5 -1
- package/corpus/core/src/integrations/index.ts +1 -0
- package/corpus/core/src/integrations/types.ts +9 -2
- package/corpus/core/src/integrations/webhook-handler.ts +69 -45
- package/corpus/core/src/server/agent-chat/action-filters-a2a.ts +1 -0
- package/corpus/templates/analytics/AGENTS.md +6 -0
- package/corpus/templates/analytics/README.md +1 -1
- package/corpus/templates/analytics/actions/data-source-status.ts +19 -1
- package/corpus/templates/analytics/app/pages/adhoc/sql-dashboard/index.tsx +5 -1
- package/corpus/templates/analytics/app/routes/_index.tsx +2 -2
- package/corpus/templates/analytics/changelog/2026-07-14-analytics-chat-guides-you-to-connect-a-data-source-with-a-di.md +6 -0
- package/corpus/templates/analytics/changelog/2026-07-14-analytics-landing-pages-now-clearly-position-the-app-as-an-o.md +6 -0
- package/corpus/templates/analytics/changelog/2026-07-15-daily-dashboard-emails-now-include-the-complete-dashboard-sc.md +6 -0
- package/corpus/templates/analytics/server/jobs/dashboard-report.ts +8 -1
- package/corpus/templates/analytics/server/lib/dashboard-report.ts +64 -12
- package/corpus/templates/analytics/server/lib/real-data-actions.ts +15 -3
- package/corpus/templates/analytics/server/plugins/agent-chat.ts +364 -4
- package/corpus/templates/clips/app/components/dictate/vocabulary-section.tsx +1 -0
- package/corpus/templates/clips/app/components/library/folder-tree.tsx +12 -9
- package/corpus/templates/clips/app/components/library/library-layout.tsx +19 -16
- package/corpus/templates/clips/app/routes/_app.spaces._index.tsx +13 -9
- package/corpus/templates/clips/changelog/2026-07-15-folders-and-sidebar-links-are-clickable-again-immediately-af.md +6 -0
- package/corpus/templates/clips/changelog/2026-07-15-hovering-a-truncated-folder-name-now-shows-the-full-name.md +6 -0
- package/corpus/templates/clips/changelog/2026-07-15-space-creation-controls-are-now-shown-only-to-organization-a.md +6 -0
- package/corpus/templates/clips/changelog/2026-07-15-vocabulary-terms-can-now-be-deleted-from-the-dictation-dicti.md +6 -0
- package/corpus/templates/clips/desktop/src/styles.css +23 -1
- package/corpus/templates/clips/desktop/src-tauri/src/whisper_speech.rs +49 -7
- package/corpus/templates/clips/server/lib/recordings.ts +5 -2
- package/corpus/templates/content/.agents/skills/content/SKILL.md +31 -0
- package/corpus/templates/content/.agents/skills/document-editing/SKILL.md +35 -21
- package/corpus/templates/content/AGENTS.md +29 -7
- package/corpus/templates/content/actions/_database-source-utils.ts +30 -16
- package/corpus/templates/content/actions/_database-utils.ts +15 -1
- package/corpus/templates/content/actions/_local-file-documents.ts +53 -1
- package/corpus/templates/content/actions/_property-utils.ts +7 -1
- package/corpus/templates/content/actions/configure-document-property.ts +11 -0
- package/corpus/templates/content/actions/create-content-database.ts +11 -0
- package/corpus/templates/content/actions/create-document.ts +9 -0
- package/corpus/templates/content/actions/create-inline-content-database.ts +3 -0
- package/corpus/templates/content/actions/export-content-source.ts +1 -0
- package/corpus/templates/content/actions/get-content-database.ts +13 -1
- package/corpus/templates/content/actions/get-document.ts +12 -2
- package/corpus/templates/content/actions/import-content-source.ts +6 -0
- package/corpus/templates/content/actions/list-documents.ts +3 -0
- package/corpus/templates/content/actions/pull-document.ts +2 -0
- package/corpus/templates/content/actions/search-documents.ts +5 -1
- package/corpus/templates/content/actions/update-document.ts +16 -1
- package/corpus/templates/content/actions/view-screen.ts +15 -2
- package/corpus/templates/content/app/components/editor/DescriptionField.tsx +121 -0
- package/corpus/templates/content/app/components/editor/DocumentEditor.tsx +14 -0
- package/corpus/templates/content/app/components/editor/DocumentProperties.tsx +223 -34
- package/corpus/templates/content/app/components/editor/database/DatabaseView.tsx +56 -8
- package/corpus/templates/content/app/i18n/zh-TW.ts +5 -0
- package/corpus/templates/content/app/i18n-data.ts +52 -0
- package/corpus/templates/content/changelog/2026-07-14-pages-and-databases-can-describe-themselves-with-guidance-fo.md +6 -0
- package/corpus/templates/content/changelog/2026-07-14-slack-follow-ups-now-retain-created-content-identity-and-per.md +6 -0
- package/corpus/templates/content/server/db/schema.ts +4 -0
- package/corpus/templates/content/server/lib/document-context.ts +76 -0
- package/corpus/templates/content/server/lib/public-documents.ts +3 -0
- package/corpus/templates/content/server/plugins/db.ts +6 -0
- package/corpus/templates/content/server/routes/api/document-agent-context.json.get.ts +5 -0
- package/corpus/templates/content/shared/api.ts +17 -0
- package/corpus/templates/content/shared/content-source.ts +7 -0
- package/corpus/templates/content/shared/properties.ts +2 -0
- package/dist/a2a/artifact-response.d.ts +15 -0
- package/dist/a2a/artifact-response.d.ts.map +1 -1
- package/dist/a2a/artifact-response.js +204 -5
- package/dist/a2a/artifact-response.js.map +1 -1
- package/dist/agent/thread-data-builder.d.ts +6 -0
- package/dist/agent/thread-data-builder.d.ts.map +1 -1
- package/dist/agent/thread-data-builder.js +71 -8
- package/dist/agent/thread-data-builder.js.map +1 -1
- package/dist/collab/struct-routes.d.ts +1 -1
- package/dist/integrations/a2a-continuation-processor.d.ts.map +1 -1
- package/dist/integrations/a2a-continuation-processor.js +72 -6
- package/dist/integrations/a2a-continuation-processor.js.map +1 -1
- package/dist/integrations/adapters/discord.d.ts.map +1 -1
- package/dist/integrations/adapters/discord.js +1 -0
- package/dist/integrations/adapters/discord.js.map +1 -1
- package/dist/integrations/adapters/email.d.ts.map +1 -1
- package/dist/integrations/adapters/email.js +2 -0
- package/dist/integrations/adapters/email.js.map +1 -1
- package/dist/integrations/adapters/google-docs.d.ts.map +1 -1
- package/dist/integrations/adapters/google-docs.js +2 -0
- package/dist/integrations/adapters/google-docs.js.map +1 -1
- package/dist/integrations/adapters/microsoft-teams.d.ts.map +1 -1
- package/dist/integrations/adapters/microsoft-teams.js +1 -0
- package/dist/integrations/adapters/microsoft-teams.js.map +1 -1
- package/dist/integrations/adapters/slack.d.ts.map +1 -1
- package/dist/integrations/adapters/slack.js +21 -6
- package/dist/integrations/adapters/slack.js.map +1 -1
- package/dist/integrations/adapters/telegram.d.ts.map +1 -1
- package/dist/integrations/adapters/telegram.js +11 -2
- package/dist/integrations/adapters/telegram.js.map +1 -1
- package/dist/integrations/adapters/whatsapp.d.ts.map +1 -1
- package/dist/integrations/adapters/whatsapp.js +3 -0
- package/dist/integrations/adapters/whatsapp.js.map +1 -1
- package/dist/integrations/index.d.ts +1 -1
- package/dist/integrations/index.d.ts.map +1 -1
- package/dist/integrations/index.js.map +1 -1
- package/dist/integrations/types.d.ts +8 -2
- package/dist/integrations/types.d.ts.map +1 -1
- package/dist/integrations/types.js.map +1 -1
- package/dist/integrations/webhook-handler.d.ts.map +1 -1
- package/dist/integrations/webhook-handler.js +40 -43
- package/dist/integrations/webhook-handler.js.map +1 -1
- package/dist/progress/routes.d.ts +1 -1
- package/dist/provider-api/corpus-jobs.d.ts +2 -2
- package/dist/resources/handlers.d.ts +2 -2
- package/dist/server/agent-chat/action-filters-a2a.d.ts.map +1 -1
- package/dist/server/agent-chat/action-filters-a2a.js +1 -0
- package/dist/server/agent-chat/action-filters-a2a.js.map +1 -1
- package/dist/server/agent-engine-api-key-route.d.ts +1 -1
- package/docs/content/locales/ar-SA/pure-agent-apps.mdx +2 -2
- package/docs/content/locales/de-DE/pure-agent-apps.mdx +2 -2
- package/docs/content/locales/es-ES/pure-agent-apps.mdx +2 -2
- package/docs/content/locales/fr-FR/pure-agent-apps.mdx +2 -2
- package/docs/content/locales/hi-IN/pure-agent-apps.mdx +2 -2
- package/docs/content/locales/ja-JP/pure-agent-apps.mdx +2 -2
- package/docs/content/locales/ko-KR/pure-agent-apps.mdx +2 -2
- package/docs/content/locales/pt-BR/pure-agent-apps.mdx +2 -2
- package/docs/content/locales/zh-CN/pure-agent-apps.mdx +2 -2
- package/docs/content/locales/zh-TW/pure-agent-apps.mdx +2 -2
- package/docs/content/pure-agent-apps.mdx +58 -14
- package/package.json +3 -3
|
@@ -637,23 +637,96 @@ export function threadDataToEngineMessages(
|
|
|
637
637
|
for (const entry of data.messages) {
|
|
638
638
|
const m = entry?.message ?? entry;
|
|
639
639
|
if (!m || (m.role !== "user" && m.role !== "assistant")) continue;
|
|
640
|
-
const text =
|
|
641
|
-
typeof m.content === "string"
|
|
642
|
-
? m.content
|
|
643
|
-
: Array.isArray(m.content)
|
|
644
|
-
? m.content
|
|
645
|
-
.filter(
|
|
646
|
-
(c: any) => c?.type === "text" && typeof c.text === "string",
|
|
647
|
-
)
|
|
648
|
-
.map((c: any) => c.text)
|
|
649
|
-
.join("\n")
|
|
650
|
-
: "";
|
|
640
|
+
const text = threadMessageTextForEngine(m);
|
|
651
641
|
if (!text.trim()) continue;
|
|
652
642
|
messages.push({ role: m.role, content: [{ type: "text", text }] });
|
|
653
643
|
}
|
|
654
644
|
return messages;
|
|
655
645
|
}
|
|
656
646
|
|
|
647
|
+
const MAX_INTEGRATION_ARTIFACTS_IN_CONTEXT = 12;
|
|
648
|
+
const MAX_INTEGRATION_ARTIFACT_FIELD_CHARS = 500;
|
|
649
|
+
|
|
650
|
+
function boundedString(value: unknown): string | undefined {
|
|
651
|
+
if (typeof value !== "string") return undefined;
|
|
652
|
+
const trimmed = value.trim();
|
|
653
|
+
return trimmed
|
|
654
|
+
? trimmed.slice(0, MAX_INTEGRATION_ARTIFACT_FIELD_CHARS)
|
|
655
|
+
: undefined;
|
|
656
|
+
}
|
|
657
|
+
|
|
658
|
+
function promptSafeJson(value: unknown): string {
|
|
659
|
+
return JSON.stringify(value)
|
|
660
|
+
.replaceAll("&", "\\u0026")
|
|
661
|
+
.replaceAll("<", "\\u003c")
|
|
662
|
+
.replaceAll(">", "\\u003e");
|
|
663
|
+
}
|
|
664
|
+
|
|
665
|
+
function messageTextContent(message: any): string {
|
|
666
|
+
if (typeof message?.content === "string") return message.content;
|
|
667
|
+
if (!Array.isArray(message?.content)) return "";
|
|
668
|
+
return message.content
|
|
669
|
+
.filter(
|
|
670
|
+
(part: any) => part?.type === "text" && typeof part.text === "string",
|
|
671
|
+
)
|
|
672
|
+
.map((part: any) => part.text)
|
|
673
|
+
.join("\n");
|
|
674
|
+
}
|
|
675
|
+
|
|
676
|
+
/**
|
|
677
|
+
* Select the participant-visible delivery for integration turns while keeping
|
|
678
|
+
* a compact, trusted resource ledger available to the agent. Raw tool results
|
|
679
|
+
* remain in thread_data for UI/audit use but are not replayed into the prompt.
|
|
680
|
+
*/
|
|
681
|
+
export function threadMessageTextForEngine(message: any): string {
|
|
682
|
+
const delivery = message?.metadata?.integrationDelivery;
|
|
683
|
+
const deliveryAttempted =
|
|
684
|
+
message?.metadata?.integrationDeliveryAttempted === true;
|
|
685
|
+
const deliveredText =
|
|
686
|
+
message?.role === "assistant" &&
|
|
687
|
+
delivery?.status === "delivered" &&
|
|
688
|
+
typeof delivery.text === "string" &&
|
|
689
|
+
delivery.text.trim()
|
|
690
|
+
? delivery.text
|
|
691
|
+
: undefined;
|
|
692
|
+
let text =
|
|
693
|
+
deliveredText ??
|
|
694
|
+
(message?.role === "assistant" && deliveryAttempted
|
|
695
|
+
? ""
|
|
696
|
+
: messageTextContent(message));
|
|
697
|
+
|
|
698
|
+
if (message?.role !== "assistant") return text;
|
|
699
|
+
const storedArtifacts = message?.metadata?.integrationArtifacts;
|
|
700
|
+
if (!Array.isArray(storedArtifacts)) return text;
|
|
701
|
+
|
|
702
|
+
const artifacts = storedArtifacts
|
|
703
|
+
.slice(0, MAX_INTEGRATION_ARTIFACTS_IN_CONTEXT)
|
|
704
|
+
.map((artifact: any) => ({
|
|
705
|
+
resourceType: boundedString(artifact?.resourceType),
|
|
706
|
+
id: boundedString(artifact?.id),
|
|
707
|
+
sourceAction: boundedString(artifact?.sourceAction),
|
|
708
|
+
titleAtAction: boundedString(artifact?.titleAtAction),
|
|
709
|
+
url: boundedString(artifact?.url),
|
|
710
|
+
}))
|
|
711
|
+
.filter(
|
|
712
|
+
(artifact: {
|
|
713
|
+
resourceType?: string;
|
|
714
|
+
id?: string;
|
|
715
|
+
sourceAction?: string;
|
|
716
|
+
}) => artifact.resourceType && artifact.id && artifact.sourceAction,
|
|
717
|
+
);
|
|
718
|
+
if (artifacts.length === 0) return text;
|
|
719
|
+
|
|
720
|
+
const context = [
|
|
721
|
+
"<integration_artifact_context>",
|
|
722
|
+
"Trusted action history for this conversation. Resource IDs remain stable if participants rename the resource. Use these IDs when a follow-up refers to an earlier artifact, while still deciding from the request whether to update, add, supersede, or create.",
|
|
723
|
+
promptSafeJson(artifacts),
|
|
724
|
+
"</integration_artifact_context>",
|
|
725
|
+
].join("\n");
|
|
726
|
+
text = text.trim() ? `${text.trim()}\n\n${context}` : context;
|
|
727
|
+
return text;
|
|
728
|
+
}
|
|
729
|
+
|
|
657
730
|
export interface CodeAgentThreadTranscriptEvent {
|
|
658
731
|
id: string;
|
|
659
732
|
runId: string;
|
|
@@ -1,4 +1,8 @@
|
|
|
1
|
-
import {
|
|
1
|
+
import {
|
|
2
|
+
appendA2AArtifactLinks,
|
|
3
|
+
extractA2AArtifactIdentities,
|
|
4
|
+
stripA2APersistedArtifactMarkers,
|
|
5
|
+
} from "../a2a/artifact-response.js";
|
|
2
6
|
import { A2AClient, signA2AToken } from "../a2a/client.js";
|
|
3
7
|
import type { Task } from "../a2a/types.js";
|
|
4
8
|
import {
|
|
@@ -6,6 +10,8 @@ import {
|
|
|
6
10
|
isLlmCredentialError,
|
|
7
11
|
LLM_MISSING_CREDENTIALS_ERROR_CODE,
|
|
8
12
|
} from "../agent/engine/credential-errors.js";
|
|
13
|
+
import { extractThreadMeta } from "../agent/thread-data-builder.js";
|
|
14
|
+
import { getThread, updateThreadData } from "../chat-threads/store.js";
|
|
9
15
|
import { withConfiguredAppBasePath } from "../server/app-base-path.js";
|
|
10
16
|
import { FRAMEWORK_ROUTE_PREFIX } from "../server/core-routes-plugin.js";
|
|
11
17
|
import {
|
|
@@ -19,9 +25,11 @@ import {
|
|
|
19
25
|
type A2AContinuation,
|
|
20
26
|
} from "./a2a-continuations-store.js";
|
|
21
27
|
import { signInternalToken } from "./internal-token.js";
|
|
28
|
+
import { getThreadMapping } from "./thread-mapping-store.js";
|
|
22
29
|
import type {
|
|
23
30
|
OutgoingMessage,
|
|
24
31
|
PlatformAdapter,
|
|
32
|
+
PlatformDeliveryReceipt,
|
|
25
33
|
PlatformRunProgress,
|
|
26
34
|
} from "./types.js";
|
|
27
35
|
|
|
@@ -359,8 +367,10 @@ async function deliverAndCompleteA2AContinuation(
|
|
|
359
367
|
if (!deliveryContinuation) return;
|
|
360
368
|
|
|
361
369
|
try {
|
|
362
|
-
const outgoing = adapter.formatAgentResponse(
|
|
363
|
-
|
|
370
|
+
const outgoing = adapter.formatAgentResponse(
|
|
371
|
+
stripA2APersistedArtifactMarkers(text),
|
|
372
|
+
);
|
|
373
|
+
const deliveryReceipt = await withTimeout(
|
|
364
374
|
deliverA2AContinuationResponse(
|
|
365
375
|
adapter,
|
|
366
376
|
deliveryContinuation,
|
|
@@ -371,6 +381,27 @@ async function deliverAndCompleteA2AContinuation(
|
|
|
371
381
|
PLATFORM_SEND_TIMEOUT_MS,
|
|
372
382
|
`${deliveryContinuation.platform} response delivery timed out`,
|
|
373
383
|
);
|
|
384
|
+
let persistenceError: unknown;
|
|
385
|
+
for (let attempt = 0; attempt < 3; attempt += 1) {
|
|
386
|
+
try {
|
|
387
|
+
await persistA2AContinuationDelivery(
|
|
388
|
+
deliveryContinuation,
|
|
389
|
+
outgoing,
|
|
390
|
+
deliveryReceipt,
|
|
391
|
+
text,
|
|
392
|
+
);
|
|
393
|
+
persistenceError = undefined;
|
|
394
|
+
break;
|
|
395
|
+
} catch (err) {
|
|
396
|
+
persistenceError = err;
|
|
397
|
+
}
|
|
398
|
+
}
|
|
399
|
+
if (persistenceError) {
|
|
400
|
+
console.error(
|
|
401
|
+
`[integrations] Delivered A2A continuation ${deliveryContinuation.id} but could not persist its thread history:`,
|
|
402
|
+
persistenceError,
|
|
403
|
+
);
|
|
404
|
+
}
|
|
374
405
|
} catch (err) {
|
|
375
406
|
if (deliveryContinuation.attempts >= MAX_ATTEMPTS) {
|
|
376
407
|
await failA2AContinuation(
|
|
@@ -392,7 +423,7 @@ async function deliverA2AContinuationResponse(
|
|
|
392
423
|
message: OutgoingMessage,
|
|
393
424
|
progress: PlatformRunProgress | null,
|
|
394
425
|
status: "done" | "error",
|
|
395
|
-
): Promise<
|
|
426
|
+
): Promise<PlatformDeliveryReceipt> {
|
|
396
427
|
if (progress) {
|
|
397
428
|
try {
|
|
398
429
|
await progress.onEvent({
|
|
@@ -400,8 +431,9 @@ async function deliverA2AContinuationResponse(
|
|
|
400
431
|
agent: continuation.agentName,
|
|
401
432
|
status,
|
|
402
433
|
});
|
|
403
|
-
await progress.complete(message);
|
|
404
|
-
return;
|
|
434
|
+
const receipt = await progress.complete(message);
|
|
435
|
+
if (receipt?.status === "delivered") return receipt;
|
|
436
|
+
throw new Error("Continuation progress completed without delivery proof");
|
|
405
437
|
} catch {
|
|
406
438
|
// A resumed Slack stream can no longer be finalized (for example when
|
|
407
439
|
// chat.stopStream rejects). Preserve the final answer with the same
|
|
@@ -417,9 +449,69 @@ async function deliverA2AContinuationResponse(
|
|
|
417
449
|
}
|
|
418
450
|
}
|
|
419
451
|
}
|
|
420
|
-
await adapter.sendResponse(message, continuation.incoming, {
|
|
452
|
+
const receipt = await adapter.sendResponse(message, continuation.incoming, {
|
|
421
453
|
placeholderRef: continuation.placeholderRef ?? undefined,
|
|
422
454
|
});
|
|
455
|
+
if (receipt?.status !== "delivered") {
|
|
456
|
+
throw new Error("Continuation response completed without delivery proof");
|
|
457
|
+
}
|
|
458
|
+
return receipt;
|
|
459
|
+
}
|
|
460
|
+
|
|
461
|
+
async function persistA2AContinuationDelivery(
|
|
462
|
+
continuation: A2AContinuation,
|
|
463
|
+
outgoing: OutgoingMessage,
|
|
464
|
+
receipt: PlatformDeliveryReceipt,
|
|
465
|
+
artifactText: string,
|
|
466
|
+
): Promise<void> {
|
|
467
|
+
const mapping = await getThreadMapping(
|
|
468
|
+
continuation.platform,
|
|
469
|
+
continuation.externalThreadId,
|
|
470
|
+
);
|
|
471
|
+
if (!mapping) return;
|
|
472
|
+
const thread = await getThread(mapping.internalThreadId);
|
|
473
|
+
if (!thread) return;
|
|
474
|
+
|
|
475
|
+
let repo: any;
|
|
476
|
+
try {
|
|
477
|
+
repo = JSON.parse(thread.threadData || "{}");
|
|
478
|
+
} catch {
|
|
479
|
+
repo = {};
|
|
480
|
+
}
|
|
481
|
+
if (!Array.isArray(repo.messages)) repo.messages = [];
|
|
482
|
+
|
|
483
|
+
const artifacts = extractA2AArtifactIdentities([
|
|
484
|
+
{ tool: "call-agent", result: artifactText },
|
|
485
|
+
]);
|
|
486
|
+
const metadata: Record<string, unknown> = {
|
|
487
|
+
integrationDeliveryAttempted: true,
|
|
488
|
+
integrationDelivery: {
|
|
489
|
+
platform: continuation.platform,
|
|
490
|
+
status: "delivered",
|
|
491
|
+
text: outgoing.text,
|
|
492
|
+
deliveredAt: new Date().toISOString(),
|
|
493
|
+
...(receipt.messageRefs?.length
|
|
494
|
+
? { messageRefs: receipt.messageRefs }
|
|
495
|
+
: {}),
|
|
496
|
+
},
|
|
497
|
+
};
|
|
498
|
+
if (artifacts.length > 0) metadata.integrationArtifacts = artifacts;
|
|
499
|
+
|
|
500
|
+
repo.messages.push({
|
|
501
|
+
id: `msg-${Date.now()}-assistant-continuation`,
|
|
502
|
+
role: "assistant",
|
|
503
|
+
content: [{ type: "text", text: outgoing.text }],
|
|
504
|
+
createdAt: new Date().toISOString(),
|
|
505
|
+
metadata,
|
|
506
|
+
});
|
|
507
|
+
const meta = extractThreadMeta(repo);
|
|
508
|
+
await updateThreadData(
|
|
509
|
+
mapping.internalThreadId,
|
|
510
|
+
JSON.stringify(repo),
|
|
511
|
+
meta.title || thread.title || "Integration Chat",
|
|
512
|
+
meta.preview || thread.preview || "",
|
|
513
|
+
repo.messages.length,
|
|
514
|
+
);
|
|
423
515
|
}
|
|
424
516
|
|
|
425
517
|
async function rescheduleAndRedispatchA2AContinuation(
|
|
@@ -9,6 +9,7 @@ import type {
|
|
|
9
9
|
IntegrationStatus,
|
|
10
10
|
OutgoingMessage,
|
|
11
11
|
PlatformAdapter,
|
|
12
|
+
PlatformDeliveryReceipt,
|
|
12
13
|
} from "../types.js";
|
|
13
14
|
|
|
14
15
|
const DISCORD_MAX_CONTENT_LENGTH = 2_000;
|
|
@@ -209,7 +210,7 @@ export function discordAdapter(): PlatformAdapter {
|
|
|
209
210
|
async sendResponse(
|
|
210
211
|
message: OutgoingMessage,
|
|
211
212
|
context: IncomingMessage,
|
|
212
|
-
): Promise<void> {
|
|
213
|
+
): Promise<void | PlatformDeliveryReceipt> {
|
|
213
214
|
const applicationId = readString(context.platformContext.applicationId);
|
|
214
215
|
const interactionToken = readString(
|
|
215
216
|
context.responseContext?.interactionToken,
|
|
@@ -236,6 +237,7 @@ export function discordAdapter(): PlatformAdapter {
|
|
|
236
237
|
chunk,
|
|
237
238
|
);
|
|
238
239
|
}
|
|
240
|
+
return { status: "delivered" };
|
|
239
241
|
},
|
|
240
242
|
|
|
241
243
|
formatAgentResponse(text: string): OutgoingMessage {
|
|
@@ -18,6 +18,7 @@ import type {
|
|
|
18
18
|
OutgoingMessage,
|
|
19
19
|
IntegrationStatus,
|
|
20
20
|
OutboundTarget,
|
|
21
|
+
PlatformDeliveryReceipt,
|
|
21
22
|
} from "../types.js";
|
|
22
23
|
|
|
23
24
|
/** Max body length before truncation */
|
|
@@ -249,7 +250,7 @@ export function emailAdapter(): PlatformAdapter {
|
|
|
249
250
|
async sendResponse(
|
|
250
251
|
message: OutgoingMessage,
|
|
251
252
|
context: IncomingMessage,
|
|
252
|
-
): Promise<void> {
|
|
253
|
+
): Promise<void | PlatformDeliveryReceipt> {
|
|
253
254
|
const agentAddress = await resolveSecret("EMAIL_AGENT_ADDRESS");
|
|
254
255
|
if (!agentAddress) {
|
|
255
256
|
console.error("[email] EMAIL_AGENT_ADDRESS not configured");
|
|
@@ -284,7 +285,9 @@ export function emailAdapter(): PlatformAdapter {
|
|
|
284
285
|
});
|
|
285
286
|
} catch (err) {
|
|
286
287
|
console.error("[email] Failed to send response:", err);
|
|
288
|
+
throw err;
|
|
287
289
|
}
|
|
290
|
+
return { status: "delivered" };
|
|
288
291
|
},
|
|
289
292
|
|
|
290
293
|
async sendMessageToTarget(
|
|
@@ -6,6 +6,7 @@ import type {
|
|
|
6
6
|
IncomingMessage,
|
|
7
7
|
OutgoingMessage,
|
|
8
8
|
IntegrationStatus,
|
|
9
|
+
PlatformDeliveryReceipt,
|
|
9
10
|
} from "../types.js";
|
|
10
11
|
|
|
11
12
|
/** Google Docs comment replies have no formal length limit but keep it reasonable */
|
|
@@ -303,7 +304,7 @@ export function googleDocsAdapter(): PlatformAdapter {
|
|
|
303
304
|
async sendResponse(
|
|
304
305
|
message: OutgoingMessage,
|
|
305
306
|
context: IncomingMessage,
|
|
306
|
-
): Promise<void> {
|
|
307
|
+
): Promise<void | PlatformDeliveryReceipt> {
|
|
307
308
|
const fileId = context.platformContext.fileId as string;
|
|
308
309
|
const commentId = context.platformContext.commentId as string;
|
|
309
310
|
|
|
@@ -319,8 +320,10 @@ export function googleDocsAdapter(): PlatformAdapter {
|
|
|
319
320
|
await replyToComment(fileId, commentId, chunk, accessToken);
|
|
320
321
|
} catch (err) {
|
|
321
322
|
console.error("[google-docs] Failed to send reply:", err);
|
|
323
|
+
throw err;
|
|
322
324
|
}
|
|
323
325
|
}
|
|
326
|
+
return { status: "delivered" };
|
|
324
327
|
},
|
|
325
328
|
|
|
326
329
|
formatAgentResponse(text: string): OutgoingMessage {
|
|
@@ -8,6 +8,7 @@ import type {
|
|
|
8
8
|
IntegrationStatus,
|
|
9
9
|
OutgoingMessage,
|
|
10
10
|
PlatformAdapter,
|
|
11
|
+
PlatformDeliveryReceipt,
|
|
11
12
|
} from "../types.js";
|
|
12
13
|
|
|
13
14
|
const BOT_FRAMEWORK_SCOPE = "https://api.botframework.com/.default";
|
|
@@ -201,7 +202,7 @@ export function microsoftTeamsAdapter(): PlatformAdapter {
|
|
|
201
202
|
async sendResponse(
|
|
202
203
|
message: OutgoingMessage,
|
|
203
204
|
context: IncomingMessage,
|
|
204
|
-
): Promise<void> {
|
|
205
|
+
): Promise<void | PlatformDeliveryReceipt> {
|
|
205
206
|
const serviceUrl = normalizeServiceUrl(
|
|
206
207
|
context.platformContext.serviceUrl,
|
|
207
208
|
);
|
|
@@ -242,6 +243,7 @@ export function microsoftTeamsAdapter(): PlatformAdapter {
|
|
|
242
243
|
`Microsoft Teams reply failed (HTTP ${response.status})`,
|
|
243
244
|
);
|
|
244
245
|
}
|
|
246
|
+
return { status: "delivered" };
|
|
245
247
|
},
|
|
246
248
|
|
|
247
249
|
formatAgentResponse(text: string): OutgoingMessage {
|
|
@@ -23,6 +23,7 @@ import type {
|
|
|
23
23
|
OutboundTarget,
|
|
24
24
|
PlatformRunProgress,
|
|
25
25
|
PlatformRunProgressRef,
|
|
26
|
+
PlatformDeliveryReceipt,
|
|
26
27
|
IntegrationContextMessage,
|
|
27
28
|
IntegrationFileReference,
|
|
28
29
|
} from "../types.js";
|
|
@@ -431,11 +432,12 @@ export function slackAdapter(
|
|
|
431
432
|
message: OutgoingMessage,
|
|
432
433
|
context: IncomingMessage,
|
|
433
434
|
opts?: { placeholderRef?: string },
|
|
434
|
-
): Promise<void> {
|
|
435
|
+
): Promise<void | PlatformDeliveryReceipt> {
|
|
435
436
|
const token = await resolveBotToken(context);
|
|
436
437
|
if (!token) {
|
|
437
|
-
|
|
438
|
-
|
|
438
|
+
throw new Error(
|
|
439
|
+
"[slack] Cannot deliver response: no Slack bot token is configured",
|
|
440
|
+
);
|
|
439
441
|
}
|
|
440
442
|
|
|
441
443
|
const channelId = context.platformContext.channelId as string;
|
|
@@ -458,6 +460,7 @@ export function slackAdapter(
|
|
|
458
460
|
return;
|
|
459
461
|
}
|
|
460
462
|
const restChunks = chunks.slice(1);
|
|
463
|
+
const messageRefs: string[] = [];
|
|
461
464
|
|
|
462
465
|
const finalBlocks =
|
|
463
466
|
blocks ??
|
|
@@ -489,14 +492,29 @@ export function slackAdapter(
|
|
|
489
492
|
const data = (await res.json()) as {
|
|
490
493
|
ok: boolean;
|
|
491
494
|
error?: string;
|
|
495
|
+
ts?: string;
|
|
492
496
|
};
|
|
493
497
|
if (!data.ok) {
|
|
494
498
|
console.error("[slack] chat.update error:", data.error);
|
|
495
499
|
// Fall back to a fresh post so the user still sees a reply
|
|
496
|
-
await postFresh(
|
|
500
|
+
const postedTs = await postFresh(
|
|
501
|
+
token,
|
|
502
|
+
channelId,
|
|
503
|
+
threadTs,
|
|
504
|
+
baseBody,
|
|
505
|
+
);
|
|
506
|
+
if (postedTs) messageRefs.push(postedTs);
|
|
507
|
+
} else {
|
|
508
|
+
messageRefs.push(data.ts || placeholderRef);
|
|
497
509
|
}
|
|
498
510
|
} else {
|
|
499
|
-
await postFresh(
|
|
511
|
+
const postedTs = await postFresh(
|
|
512
|
+
token,
|
|
513
|
+
channelId,
|
|
514
|
+
threadTs,
|
|
515
|
+
baseBody,
|
|
516
|
+
);
|
|
517
|
+
if (postedTs) messageRefs.push(postedTs);
|
|
500
518
|
}
|
|
501
519
|
|
|
502
520
|
// Clear the AI-assistant "is thinking…" status now that we've
|
|
@@ -507,14 +525,19 @@ export function slackAdapter(
|
|
|
507
525
|
|
|
508
526
|
// Overflow chunks (rare) — post as plain follow-ups in the same thread
|
|
509
527
|
for (const chunk of restChunks) {
|
|
510
|
-
await postFresh(token, channelId, threadTs, {
|
|
528
|
+
const postedTs = await postFresh(token, channelId, threadTs, {
|
|
511
529
|
channel: channelId,
|
|
512
530
|
text: chunk,
|
|
513
531
|
unfurl_links: false,
|
|
514
532
|
unfurl_media: false,
|
|
515
533
|
mrkdwn: true,
|
|
516
534
|
});
|
|
535
|
+
if (postedTs) messageRefs.push(postedTs);
|
|
517
536
|
}
|
|
537
|
+
return {
|
|
538
|
+
status: "delivered",
|
|
539
|
+
...(messageRefs.length > 0 ? { messageRefs } : {}),
|
|
540
|
+
};
|
|
518
541
|
} catch (err) {
|
|
519
542
|
console.error("[slack] Failed to send message:", err);
|
|
520
543
|
throw err;
|
|
@@ -992,7 +1015,7 @@ async function postFresh(
|
|
|
992
1015
|
channelId: string,
|
|
993
1016
|
threadTs: string | undefined,
|
|
994
1017
|
body: Record<string, unknown>,
|
|
995
|
-
): Promise<
|
|
1018
|
+
): Promise<string | undefined> {
|
|
996
1019
|
const hasBlocks =
|
|
997
1020
|
Array.isArray(body.blocks) && (body.blocks as unknown[]).length > 0;
|
|
998
1021
|
if (
|
|
@@ -1000,7 +1023,7 @@ async function postFresh(
|
|
|
1000
1023
|
body.text.trim().length === 0 &&
|
|
1001
1024
|
!hasBlocks
|
|
1002
1025
|
) {
|
|
1003
|
-
return;
|
|
1026
|
+
return undefined;
|
|
1004
1027
|
}
|
|
1005
1028
|
|
|
1006
1029
|
const payload: Record<string, unknown> = {
|
|
@@ -1016,11 +1039,16 @@ async function postFresh(
|
|
|
1016
1039
|
},
|
|
1017
1040
|
body: JSON.stringify(payload),
|
|
1018
1041
|
});
|
|
1019
|
-
const data = (await res.json()) as {
|
|
1042
|
+
const data = (await res.json()) as {
|
|
1043
|
+
ok: boolean;
|
|
1044
|
+
error?: string;
|
|
1045
|
+
ts?: string;
|
|
1046
|
+
};
|
|
1020
1047
|
if (!data.ok) {
|
|
1021
1048
|
console.error("[slack] chat.postMessage error:", data.error);
|
|
1022
1049
|
throw new Error(data.error || "chat.postMessage failed");
|
|
1023
1050
|
}
|
|
1051
|
+
return data.ts;
|
|
1024
1052
|
}
|
|
1025
1053
|
|
|
1026
1054
|
async function slackApiFetch(
|
|
@@ -1871,6 +1899,7 @@ function createSlackRunProgress(
|
|
|
1871
1899
|
: {}),
|
|
1872
1900
|
});
|
|
1873
1901
|
setSlackAssistantStatus(token, channel, threadTs, "");
|
|
1902
|
+
return { status: "delivered", messageRefs: [streamTs] };
|
|
1874
1903
|
},
|
|
1875
1904
|
async fail(message) {
|
|
1876
1905
|
if (pendingTimer) clearTimeout(pendingTimer);
|
|
@@ -10,6 +10,7 @@ import type {
|
|
|
10
10
|
OutgoingMessage,
|
|
11
11
|
IntegrationStatus,
|
|
12
12
|
OutboundTarget,
|
|
13
|
+
PlatformDeliveryReceipt,
|
|
13
14
|
} from "../types.js";
|
|
14
15
|
|
|
15
16
|
/** Telegram's max message length */
|
|
@@ -194,7 +195,7 @@ export function telegramAdapter(): PlatformAdapter {
|
|
|
194
195
|
async sendResponse(
|
|
195
196
|
message: OutgoingMessage,
|
|
196
197
|
context: IncomingMessage,
|
|
197
|
-
): Promise<void> {
|
|
198
|
+
): Promise<void | PlatformDeliveryReceipt> {
|
|
198
199
|
const token = await resolveSecret("TELEGRAM_BOT_TOKEN");
|
|
199
200
|
if (!token) {
|
|
200
201
|
console.error("[telegram] TELEGRAM_BOT_TOKEN not configured");
|
|
@@ -235,22 +236,43 @@ export function telegramAdapter(): PlatformAdapter {
|
|
|
235
236
|
if (!data.ok) {
|
|
236
237
|
// Retry without Markdown if parsing fails
|
|
237
238
|
if (data.description?.includes("parse")) {
|
|
238
|
-
await fetch(
|
|
239
|
-
|
|
240
|
-
|
|
241
|
-
|
|
242
|
-
|
|
243
|
-
|
|
244
|
-
|
|
245
|
-
|
|
239
|
+
const retry = await fetch(
|
|
240
|
+
`https://api.telegram.org/bot${token}/sendMessage`,
|
|
241
|
+
{
|
|
242
|
+
method: "POST",
|
|
243
|
+
headers: { "Content-Type": "application/json" },
|
|
244
|
+
body: JSON.stringify({
|
|
245
|
+
...body,
|
|
246
|
+
parse_mode: undefined,
|
|
247
|
+
}),
|
|
248
|
+
},
|
|
249
|
+
);
|
|
250
|
+
if (!retry.ok) {
|
|
251
|
+
throw new Error(
|
|
252
|
+
`Telegram sendMessage retry failed (HTTP ${retry.status})`,
|
|
253
|
+
);
|
|
254
|
+
}
|
|
255
|
+
const retryData = (await retry.json()) as {
|
|
256
|
+
ok: boolean;
|
|
257
|
+
description?: string;
|
|
258
|
+
};
|
|
259
|
+
if (!retryData.ok) {
|
|
260
|
+
throw new Error(
|
|
261
|
+
`Telegram sendMessage retry failed: ${retryData.description ?? "unknown error"}`,
|
|
262
|
+
);
|
|
263
|
+
}
|
|
246
264
|
} else {
|
|
247
|
-
|
|
265
|
+
throw new Error(
|
|
266
|
+
`Telegram sendMessage failed: ${data.description ?? "unknown error"}`,
|
|
267
|
+
);
|
|
248
268
|
}
|
|
249
269
|
}
|
|
250
270
|
} catch (err) {
|
|
251
271
|
console.error("[telegram] Failed to send message:", err);
|
|
272
|
+
throw err;
|
|
252
273
|
}
|
|
253
274
|
}
|
|
275
|
+
return { status: "delivered" };
|
|
254
276
|
},
|
|
255
277
|
|
|
256
278
|
async sendMessageToTarget(
|
|
@@ -8,6 +8,7 @@ import type {
|
|
|
8
8
|
IncomingMessage,
|
|
9
9
|
OutgoingMessage,
|
|
10
10
|
IntegrationStatus,
|
|
11
|
+
PlatformDeliveryReceipt,
|
|
11
12
|
} from "../types.js";
|
|
12
13
|
|
|
13
14
|
/** WhatsApp's max message length */
|
|
@@ -240,7 +241,7 @@ export function whatsappAdapter(): PlatformAdapter {
|
|
|
240
241
|
async sendResponse(
|
|
241
242
|
message: OutgoingMessage,
|
|
242
243
|
context: IncomingMessage,
|
|
243
|
-
): Promise<void> {
|
|
244
|
+
): Promise<void | PlatformDeliveryReceipt> {
|
|
244
245
|
const accessToken = await resolveSecret("WHATSAPP_ACCESS_TOKEN");
|
|
245
246
|
const phoneNumberId = await resolveSecret("WHATSAPP_PHONE_NUMBER_ID");
|
|
246
247
|
if (!accessToken || !phoneNumberId) {
|
|
@@ -279,11 +280,14 @@ export function whatsappAdapter(): PlatformAdapter {
|
|
|
279
280
|
if (!res.ok) {
|
|
280
281
|
const data = await res.json().catch(() => ({}));
|
|
281
282
|
console.error("[whatsapp] sendMessage error:", data);
|
|
283
|
+
throw new Error(`WhatsApp sendMessage failed (HTTP ${res.status})`);
|
|
282
284
|
}
|
|
283
285
|
} catch (err) {
|
|
284
286
|
console.error("[whatsapp] Failed to send message:", err);
|
|
287
|
+
throw err;
|
|
285
288
|
}
|
|
286
289
|
}
|
|
290
|
+
return { status: "delivered" };
|
|
287
291
|
},
|
|
288
292
|
|
|
289
293
|
formatAgentResponse(text: string): OutgoingMessage {
|
|
@@ -127,6 +127,13 @@ export interface OutgoingMessage {
|
|
|
127
127
|
platformContext: Record<string, unknown>;
|
|
128
128
|
}
|
|
129
129
|
|
|
130
|
+
/** Provider-confirmed references for a successfully delivered response. */
|
|
131
|
+
export interface PlatformDeliveryReceipt {
|
|
132
|
+
status: "delivered";
|
|
133
|
+
/** Opaque provider message ids/timestamps; never message content. */
|
|
134
|
+
messageRefs?: string[];
|
|
135
|
+
}
|
|
136
|
+
|
|
130
137
|
/**
|
|
131
138
|
* Proactive outbound message target for a platform.
|
|
132
139
|
* Used when the agent needs to send to a saved destination instead of replying
|
|
@@ -197,7 +204,7 @@ export interface PlatformRunProgress {
|
|
|
197
204
|
/** Receive normalized agent events. Implementations should throttle writes. */
|
|
198
205
|
onEvent(event: AgentChatEvent): Promise<void> | void;
|
|
199
206
|
/** Finalize the provider-native progress surface with the answer. */
|
|
200
|
-
complete(message: OutgoingMessage): Promise<void>;
|
|
207
|
+
complete(message: OutgoingMessage): Promise<void | PlatformDeliveryReceipt>;
|
|
201
208
|
/** Mark the provider-native surface failed and leave a retryable explanation. */
|
|
202
209
|
fail?(message: string): Promise<void>;
|
|
203
210
|
}
|
|
@@ -316,7 +323,7 @@ export interface PlatformAdapter {
|
|
|
316
323
|
message: OutgoingMessage,
|
|
317
324
|
context: IncomingMessage,
|
|
318
325
|
opts?: { placeholderRef?: string },
|
|
319
|
-
): Promise<void>;
|
|
326
|
+
): Promise<void | PlatformDeliveryReceipt>;
|
|
320
327
|
|
|
321
328
|
/**
|
|
322
329
|
* Send a short best-effort system notice to the conversation the incoming
|