@agent-native/core 0.101.6 → 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/corpus/README.md +1 -1
- package/corpus/core/CHANGELOG.md +6 -0
- 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/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/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/observability/routes.d.ts +1 -1
- package/dist/provider-api/corpus-jobs.d.ts +2 -2
- package/dist/resources/handlers.d.ts +1 -1
- 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/package.json +1 -1
package/corpus/README.md
CHANGED
package/corpus/core/CHANGELOG.md
CHANGED
|
@@ -1,5 +1,11 @@
|
|
|
1
1
|
# @agent-native/core
|
|
2
2
|
|
|
3
|
+
## 0.101.7
|
|
4
|
+
|
|
5
|
+
### Patch Changes
|
|
6
|
+
|
|
7
|
+
- 70caa6f: Preserve participant-visible integration replies and verified artifact identities so threaded corrections can reliably target resources after renames.
|
|
8
|
+
|
|
3
9
|
## 0.101.6
|
|
4
10
|
|
|
5
11
|
### Patch Changes
|
package/corpus/core/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@agent-native/core",
|
|
3
|
-
"version": "0.101.
|
|
3
|
+
"version": "0.101.7",
|
|
4
4
|
"description": "Framework for agent-native application development — where AI agents and UI share SQL state, actions, and context",
|
|
5
5
|
"homepage": "https://github.com/BuilderIO/agent-native#readme",
|
|
6
6
|
"bugs": {
|
|
@@ -1,3 +1,5 @@
|
|
|
1
|
+
import { createHmac, timingSafeEqual } from "node:crypto";
|
|
2
|
+
|
|
1
3
|
export interface A2AToolResultSummary {
|
|
2
4
|
tool: string;
|
|
3
5
|
result: string;
|
|
@@ -6,6 +8,121 @@ export interface A2AToolResultSummary {
|
|
|
6
8
|
export interface A2AArtifactResponseOptions {
|
|
7
9
|
baseUrl?: string;
|
|
8
10
|
includeReferencedArtifacts?: boolean;
|
|
11
|
+
includePersistedArtifactMarker?: boolean;
|
|
12
|
+
}
|
|
13
|
+
|
|
14
|
+
export interface A2AArtifactIdentity {
|
|
15
|
+
resourceType:
|
|
16
|
+
| "document"
|
|
17
|
+
| "deck"
|
|
18
|
+
| "dashboard"
|
|
19
|
+
| "analysis"
|
|
20
|
+
| "image"
|
|
21
|
+
| "design"
|
|
22
|
+
| "monitor"
|
|
23
|
+
| "form";
|
|
24
|
+
id: string;
|
|
25
|
+
sourceAction: string;
|
|
26
|
+
titleAtAction?: string;
|
|
27
|
+
url?: string;
|
|
28
|
+
}
|
|
29
|
+
|
|
30
|
+
const ARTIFACT_IDENTITY_WRITE_TOOLS = new Set([
|
|
31
|
+
"save-monitor",
|
|
32
|
+
"create-form",
|
|
33
|
+
"submit-content-database-form",
|
|
34
|
+
"add-database-item",
|
|
35
|
+
"create-document",
|
|
36
|
+
"update-document",
|
|
37
|
+
"create-deck",
|
|
38
|
+
"duplicate-deck",
|
|
39
|
+
"add-slide",
|
|
40
|
+
"update-dashboard",
|
|
41
|
+
"rename-dashboard",
|
|
42
|
+
"save-analysis",
|
|
43
|
+
"generate-image",
|
|
44
|
+
"edit-image",
|
|
45
|
+
"refine-image",
|
|
46
|
+
"restyle-image",
|
|
47
|
+
"save-generated-image",
|
|
48
|
+
"save-generated-asset",
|
|
49
|
+
"export-image",
|
|
50
|
+
"export-asset",
|
|
51
|
+
"generate-image-batch",
|
|
52
|
+
"create-design",
|
|
53
|
+
"generate-design",
|
|
54
|
+
"create-file",
|
|
55
|
+
"duplicate-design",
|
|
56
|
+
]);
|
|
57
|
+
|
|
58
|
+
const PERSISTED_ARTIFACT_MARKER = "agent-native:persisted-artifacts=";
|
|
59
|
+
const PERSISTED_ARTIFACT_MARKER_PATTERN =
|
|
60
|
+
/\s*<!--\s*agent-native:persisted-artifacts=[A-Za-z0-9_-]+\.[a-f0-9]{64}\s*-->/g;
|
|
61
|
+
const ARTIFACT_RESOURCE_TYPES = new Set<A2AArtifactIdentity["resourceType"]>([
|
|
62
|
+
"document",
|
|
63
|
+
"deck",
|
|
64
|
+
"dashboard",
|
|
65
|
+
"analysis",
|
|
66
|
+
"image",
|
|
67
|
+
"design",
|
|
68
|
+
"monitor",
|
|
69
|
+
"form",
|
|
70
|
+
]);
|
|
71
|
+
|
|
72
|
+
function persistedArtifactIdentitiesFromMarker(
|
|
73
|
+
result: string,
|
|
74
|
+
): A2AArtifactIdentity[] {
|
|
75
|
+
const secret = process.env.A2A_SECRET;
|
|
76
|
+
if (!secret) return [];
|
|
77
|
+
const match = result.match(
|
|
78
|
+
/<!--\s*agent-native:persisted-artifacts=([A-Za-z0-9_-]+)\.([a-f0-9]{64})\s*-->/,
|
|
79
|
+
);
|
|
80
|
+
if (!match) return [];
|
|
81
|
+
try {
|
|
82
|
+
const payload = match[1];
|
|
83
|
+
const expected = createHmac("sha256", secret).update(payload).digest();
|
|
84
|
+
const supplied = Buffer.from(match[2], "hex");
|
|
85
|
+
if (
|
|
86
|
+
supplied.length !== expected.length ||
|
|
87
|
+
!timingSafeEqual(supplied, expected)
|
|
88
|
+
) {
|
|
89
|
+
return [];
|
|
90
|
+
}
|
|
91
|
+
const parsed = JSON.parse(Buffer.from(payload, "base64url").toString());
|
|
92
|
+
if (!Array.isArray(parsed)) return [];
|
|
93
|
+
return parsed
|
|
94
|
+
.slice(0, 12)
|
|
95
|
+
.filter((identity): identity is A2AArtifactIdentity => {
|
|
96
|
+
const item = asRecord(identity);
|
|
97
|
+
return (
|
|
98
|
+
!!item &&
|
|
99
|
+
ARTIFACT_RESOURCE_TYPES.has(
|
|
100
|
+
item.resourceType as A2AArtifactIdentity["resourceType"],
|
|
101
|
+
) &&
|
|
102
|
+
typeof item.id === "string" &&
|
|
103
|
+
typeof item.sourceAction === "string"
|
|
104
|
+
);
|
|
105
|
+
});
|
|
106
|
+
} catch {
|
|
107
|
+
return [];
|
|
108
|
+
}
|
|
109
|
+
}
|
|
110
|
+
|
|
111
|
+
function withPersistedArtifactMarker(
|
|
112
|
+
text: string,
|
|
113
|
+
toolResults: A2AToolResultSummary[],
|
|
114
|
+
): string {
|
|
115
|
+
const identities = extractA2AArtifactIdentities(toolResults).slice(0, 12);
|
|
116
|
+
const secret = process.env.A2A_SECRET;
|
|
117
|
+
if (identities.length === 0 || !secret) return text;
|
|
118
|
+
const payload = Buffer.from(JSON.stringify(identities)).toString("base64url");
|
|
119
|
+
const signature = createHmac("sha256", secret).update(payload).digest("hex");
|
|
120
|
+
const marker = `<!-- ${PERSISTED_ARTIFACT_MARKER}${payload}.${signature} -->`;
|
|
121
|
+
return text ? `${text}\n\n${marker}` : marker;
|
|
122
|
+
}
|
|
123
|
+
|
|
124
|
+
export function stripA2APersistedArtifactMarkers(text: string): string {
|
|
125
|
+
return text.replace(PERSISTED_ARTIFACT_MARKER_PATTERN, "").trim();
|
|
9
126
|
}
|
|
10
127
|
|
|
11
128
|
interface CreatedDocumentArtifact {
|
|
@@ -617,15 +734,25 @@ function collectArtifacts(results: A2AToolResultSummary[]): {
|
|
|
617
734
|
|
|
618
735
|
if (
|
|
619
736
|
toolResult.tool === "generate-image" ||
|
|
737
|
+
toolResult.tool === "edit-image" ||
|
|
620
738
|
toolResult.tool === "refine-image" ||
|
|
739
|
+
toolResult.tool === "restyle-image" ||
|
|
621
740
|
toolResult.tool === "get-asset" ||
|
|
622
741
|
toolResult.tool === "save-generated-image" ||
|
|
742
|
+
toolResult.tool === "save-generated-asset" ||
|
|
623
743
|
toolResult.tool === "export-image"
|
|
624
744
|
) {
|
|
625
745
|
addImageArtifact(images, parsed);
|
|
626
746
|
continue;
|
|
627
747
|
}
|
|
628
748
|
|
|
749
|
+
if (toolResult.tool === "export-asset") {
|
|
750
|
+
if (stringValue(parsed.artifactType) === "image") {
|
|
751
|
+
addImageArtifact(images, parsed);
|
|
752
|
+
}
|
|
753
|
+
continue;
|
|
754
|
+
}
|
|
755
|
+
|
|
629
756
|
if (toolResult.tool === "generate-image-batch") {
|
|
630
757
|
if (Array.isArray(parsed.images)) {
|
|
631
758
|
for (const item of parsed.images) {
|
|
@@ -730,6 +857,114 @@ function collectArtifacts(results: A2AToolResultSummary[]): {
|
|
|
730
857
|
};
|
|
731
858
|
}
|
|
732
859
|
|
|
860
|
+
/**
|
|
861
|
+
* Extract a compact, verified identity ledger from successful artifact tools.
|
|
862
|
+
* The ledger deliberately excludes raw tool results so it is safe to retain in
|
|
863
|
+
* long-lived thread context and stable even when a resource is later renamed.
|
|
864
|
+
*/
|
|
865
|
+
export function extractA2AArtifactIdentities(
|
|
866
|
+
results: A2AToolResultSummary[],
|
|
867
|
+
): A2AArtifactIdentity[] {
|
|
868
|
+
const identities = new Map<string, A2AArtifactIdentity>();
|
|
869
|
+
|
|
870
|
+
const remember = (identity: A2AArtifactIdentity) => {
|
|
871
|
+
identities.set(`${identity.resourceType}:${identity.id}`, identity);
|
|
872
|
+
};
|
|
873
|
+
|
|
874
|
+
for (const result of results) {
|
|
875
|
+
if (result.tool === "call-agent") {
|
|
876
|
+
for (const identity of persistedArtifactIdentitiesFromMarker(
|
|
877
|
+
result.result,
|
|
878
|
+
)) {
|
|
879
|
+
remember({ ...identity, sourceAction: "call-agent" });
|
|
880
|
+
}
|
|
881
|
+
continue;
|
|
882
|
+
}
|
|
883
|
+
if (!ARTIFACT_IDENTITY_WRITE_TOOLS.has(result.tool)) continue;
|
|
884
|
+
const artifacts = collectArtifacts([result]);
|
|
885
|
+
for (const document of artifacts.documents) {
|
|
886
|
+
remember({
|
|
887
|
+
resourceType: "document",
|
|
888
|
+
id: document.id,
|
|
889
|
+
sourceAction: result.tool,
|
|
890
|
+
titleAtAction: document.title,
|
|
891
|
+
url: document.url,
|
|
892
|
+
});
|
|
893
|
+
}
|
|
894
|
+
for (const deck of artifacts.decks) {
|
|
895
|
+
remember({
|
|
896
|
+
resourceType: "deck",
|
|
897
|
+
id: deck.id,
|
|
898
|
+
sourceAction: result.tool,
|
|
899
|
+
url: deck.url,
|
|
900
|
+
});
|
|
901
|
+
}
|
|
902
|
+
for (const dashboard of artifacts.dashboards) {
|
|
903
|
+
remember({
|
|
904
|
+
resourceType: "dashboard",
|
|
905
|
+
id: dashboard.id,
|
|
906
|
+
sourceAction: result.tool,
|
|
907
|
+
titleAtAction: dashboard.title,
|
|
908
|
+
url: dashboard.url,
|
|
909
|
+
});
|
|
910
|
+
}
|
|
911
|
+
for (const analysis of artifacts.analyses) {
|
|
912
|
+
remember({
|
|
913
|
+
resourceType: "analysis",
|
|
914
|
+
id: analysis.id,
|
|
915
|
+
sourceAction: result.tool,
|
|
916
|
+
titleAtAction: analysis.title,
|
|
917
|
+
url: analysis.url,
|
|
918
|
+
});
|
|
919
|
+
}
|
|
920
|
+
for (const image of artifacts.images) {
|
|
921
|
+
remember({
|
|
922
|
+
resourceType: "image",
|
|
923
|
+
id: image.id,
|
|
924
|
+
sourceAction: result.tool,
|
|
925
|
+
titleAtAction: image.title,
|
|
926
|
+
url: image.url,
|
|
927
|
+
});
|
|
928
|
+
}
|
|
929
|
+
for (const design of artifacts.designShells) {
|
|
930
|
+
remember({
|
|
931
|
+
resourceType: "design",
|
|
932
|
+
id: design.id,
|
|
933
|
+
sourceAction: result.tool,
|
|
934
|
+
titleAtAction: design.title,
|
|
935
|
+
});
|
|
936
|
+
}
|
|
937
|
+
for (const design of artifacts.generatedDesigns) {
|
|
938
|
+
remember({
|
|
939
|
+
resourceType: "design",
|
|
940
|
+
id: design.id,
|
|
941
|
+
sourceAction: result.tool,
|
|
942
|
+
url: design.url,
|
|
943
|
+
});
|
|
944
|
+
}
|
|
945
|
+
for (const monitor of artifacts.monitors) {
|
|
946
|
+
remember({
|
|
947
|
+
resourceType: "monitor",
|
|
948
|
+
id: monitor.id,
|
|
949
|
+
sourceAction: result.tool,
|
|
950
|
+
titleAtAction: monitor.name,
|
|
951
|
+
url: monitor.url,
|
|
952
|
+
});
|
|
953
|
+
}
|
|
954
|
+
for (const form of artifacts.forms) {
|
|
955
|
+
remember({
|
|
956
|
+
resourceType: "form",
|
|
957
|
+
id: form.id,
|
|
958
|
+
sourceAction: result.tool,
|
|
959
|
+
titleAtAction: form.title,
|
|
960
|
+
url: form.url,
|
|
961
|
+
});
|
|
962
|
+
}
|
|
963
|
+
}
|
|
964
|
+
|
|
965
|
+
return [...identities.values()];
|
|
966
|
+
}
|
|
967
|
+
|
|
733
968
|
type DownstreamArtifact =
|
|
734
969
|
| { kind: "deck"; id: string; url: string }
|
|
735
970
|
| { kind: "document"; id: string; url: string; title?: string }
|
|
@@ -1131,6 +1366,10 @@ export function appendA2AArtifactLinks(
|
|
|
1131
1366
|
const baseUrl = normalizeBaseUrl(options.baseUrl);
|
|
1132
1367
|
const includeReferencedArtifacts =
|
|
1133
1368
|
options.includeReferencedArtifacts ?? false;
|
|
1369
|
+
const finalize = (value: string) =>
|
|
1370
|
+
options.includePersistedArtifactMarker
|
|
1371
|
+
? withPersistedArtifactMarker(value, toolResults)
|
|
1372
|
+
: value;
|
|
1134
1373
|
const {
|
|
1135
1374
|
documents,
|
|
1136
1375
|
decks,
|
|
@@ -1160,7 +1399,7 @@ export function appendA2AArtifactLinks(
|
|
|
1160
1399
|
) ||
|
|
1161
1400
|
/\b(?:done|created|ready|here(?:'s| is)|complete|finished)\b/i.test(text))
|
|
1162
1401
|
) {
|
|
1163
|
-
return formatIncompleteDesignMessage(incompleteShells);
|
|
1402
|
+
return finalize(formatIncompleteDesignMessage(incompleteShells));
|
|
1164
1403
|
}
|
|
1165
1404
|
|
|
1166
1405
|
const unverifiedRefs = findUnverifiedArtifactReferences(
|
|
@@ -1174,15 +1413,17 @@ export function appendA2AArtifactLinks(
|
|
|
1174
1413
|
generatedDesigns,
|
|
1175
1414
|
);
|
|
1176
1415
|
if (unverifiedRefs.length > 0) {
|
|
1177
|
-
return
|
|
1178
|
-
|
|
1179
|
-
|
|
1180
|
-
|
|
1181
|
-
|
|
1182
|
-
|
|
1183
|
-
|
|
1184
|
-
|
|
1185
|
-
|
|
1416
|
+
return finalize(
|
|
1417
|
+
formatUnverifiedArtifactMessage(
|
|
1418
|
+
unverifiedRefs,
|
|
1419
|
+
documents,
|
|
1420
|
+
decks,
|
|
1421
|
+
dashboards,
|
|
1422
|
+
analyses,
|
|
1423
|
+
images,
|
|
1424
|
+
generatedDesigns,
|
|
1425
|
+
baseUrl,
|
|
1426
|
+
),
|
|
1186
1427
|
);
|
|
1187
1428
|
}
|
|
1188
1429
|
|
|
@@ -1258,9 +1499,11 @@ export function appendA2AArtifactLinks(
|
|
|
1258
1499
|
}
|
|
1259
1500
|
}
|
|
1260
1501
|
|
|
1261
|
-
if (missingLines.length === 0)
|
|
1502
|
+
if (missingLines.length === 0) {
|
|
1503
|
+
return finalize(text);
|
|
1504
|
+
}
|
|
1262
1505
|
const artifactBlock = `Artifacts:\n${missingLines.join("\n")}`;
|
|
1263
|
-
return text ? `${text}\n\n${artifactBlock}` : artifactBlock;
|
|
1506
|
+
return finalize(text ? `${text}\n\n${artifactBlock}` : artifactBlock);
|
|
1264
1507
|
}
|
|
1265
1508
|
|
|
1266
1509
|
export function buildA2ARecoverableArtifactMessage(
|
|
@@ -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(
|