@agent-native/core 0.164.3 → 0.164.4
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/templates/clips/.agents/skills/video-sharing/SKILL.md +1 -1
- package/corpus/templates/clips/app/routes/share.$shareId.tsx +3 -0
- package/corpus/templates/clips/changelog/2026-08-19-social-preview-video-frames.md +5 -0
- package/corpus/templates/clips/server/lib/slack-unfurls.ts +7 -13
- package/corpus/templates/clips/shared/share-meta.ts +67 -3
- package/corpus/templates/content/actions/list-content-databases.ts +95 -1
- package/corpus/templates/content/changelog/2026-08-11-content-mutations-delegated-from-verified-slack-dms-now-use-.md +6 -0
- package/corpus/templates/content/shared/api.ts +11 -0
- package/dist/a2a/artifact-response.d.ts +50 -0
- package/dist/a2a/artifact-response.js +266 -39
- package/dist/a2a/index.d.ts +2 -0
- package/dist/a2a/index.js +1 -0
- package/dist/collab/awareness.d.ts +2 -2
- package/dist/integrations/adapters/slack.js +58 -7
- package/dist/notifications/routes.d.ts +3 -3
- package/dist/progress/routes.d.ts +1 -1
- package/dist/server/agent-chat/action-filters-a2a.d.ts +2 -1
- package/dist/server/agent-chat/action-filters-a2a.js +10 -2
- package/dist/server/agent-chat-plugin.js +33 -11
- package/dist/server/realtime-token.d.ts +1 -1
- package/docs/content/drop-in-agent.mdx +4 -4
- package/docs/content/faq.mdx +15 -15
- package/docs/content/getting-started.mdx +173 -41
- package/docs/content/key-concepts.mdx +10 -10
- package/docs/content/using-your-agent.mdx +2 -2
- package/docs/content/what-is-agent-native.mdx +79 -228
- package/package.json +1 -1
|
@@ -1,9 +1,19 @@
|
|
|
1
1
|
import { createHmac, timingSafeEqual } from "node:crypto";
|
|
2
|
+
import { readDeployCredentialEnv } from "../server/credential-provider.js";
|
|
3
|
+
function a2aSecret() {
|
|
4
|
+
return readDeployCredentialEnv("A2A_SECRET");
|
|
5
|
+
}
|
|
6
|
+
function a2aSecrets() {
|
|
7
|
+
const secret = a2aSecret();
|
|
8
|
+
return secret ? [secret] : [];
|
|
9
|
+
}
|
|
2
10
|
const ARTIFACT_IDENTITY_WRITE_TOOLS = new Set([
|
|
3
11
|
"save-monitor",
|
|
4
12
|
"create-form",
|
|
5
13
|
"submit-content-database-form",
|
|
6
14
|
"add-database-item",
|
|
15
|
+
"upsert-database-item-by-key",
|
|
16
|
+
"mutate-content-database-block",
|
|
7
17
|
"create-document",
|
|
8
18
|
"update-document",
|
|
9
19
|
"set-document-property",
|
|
@@ -44,50 +54,79 @@ const ARTIFACT_RESOURCE_TYPES = new Set([
|
|
|
44
54
|
"monitor",
|
|
45
55
|
"form",
|
|
46
56
|
]);
|
|
47
|
-
function
|
|
48
|
-
? [process.env.A2A_SECRET]
|
|
49
|
-
: []) {
|
|
57
|
+
function persistedArtifactLedgerFromMarker(result, secrets = a2aSecrets(), expectedDelegatedTaskId) {
|
|
50
58
|
if (secrets.length === 0)
|
|
51
|
-
return
|
|
52
|
-
const
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
59
|
+
return null;
|
|
60
|
+
const matches = result.matchAll(/<!--\s*agent-native:persisted-artifacts=([A-Za-z0-9_-]+)\.([a-f0-9]{64})\s*-->/g);
|
|
61
|
+
for (const match of matches) {
|
|
62
|
+
try {
|
|
63
|
+
const payload = match[1];
|
|
64
|
+
const supplied = Buffer.from(match[2], "hex");
|
|
65
|
+
const verified = secrets.some((secret) => {
|
|
66
|
+
const expected = createHmac("sha256", secret).update(payload).digest();
|
|
67
|
+
return (supplied.length === expected.length &&
|
|
68
|
+
timingSafeEqual(supplied, expected));
|
|
69
|
+
});
|
|
70
|
+
if (!verified)
|
|
71
|
+
continue;
|
|
72
|
+
const parsed = JSON.parse(Buffer.from(payload, "base64url").toString());
|
|
73
|
+
if (Array.isArray(parsed)) {
|
|
74
|
+
return { version: 1, identities: parsed, mutationReceipts: [] };
|
|
75
|
+
}
|
|
76
|
+
const ledger = asRecord(parsed);
|
|
77
|
+
if (!ledger || ledger.version !== 1)
|
|
78
|
+
continue;
|
|
79
|
+
const parsedLedger = {
|
|
80
|
+
version: 1,
|
|
81
|
+
identities: Array.isArray(ledger.identities) ? ledger.identities : [],
|
|
82
|
+
mutationReceipts: Array.isArray(ledger.mutationReceipts)
|
|
83
|
+
? ledger.mutationReceipts
|
|
84
|
+
: [],
|
|
85
|
+
...(typeof ledger.delegatedTaskId === "string"
|
|
86
|
+
? { delegatedTaskId: ledger.delegatedTaskId }
|
|
87
|
+
: {}),
|
|
88
|
+
};
|
|
89
|
+
if (expectedDelegatedTaskId &&
|
|
90
|
+
parsedLedger.delegatedTaskId !== expectedDelegatedTaskId) {
|
|
91
|
+
continue;
|
|
92
|
+
}
|
|
93
|
+
return parsedLedger;
|
|
94
|
+
}
|
|
95
|
+
catch {
|
|
96
|
+
// coercion-ok: malformed signed-marker payloads are untrusted absence, never a successful receipt ledger
|
|
97
|
+
continue;
|
|
65
98
|
}
|
|
66
|
-
const parsed = JSON.parse(Buffer.from(payload, "base64url").toString());
|
|
67
|
-
if (!Array.isArray(parsed))
|
|
68
|
-
return [];
|
|
69
|
-
return parsed
|
|
70
|
-
.slice(0, 12)
|
|
71
|
-
.filter((identity) => {
|
|
72
|
-
const item = asRecord(identity);
|
|
73
|
-
return (!!item &&
|
|
74
|
-
ARTIFACT_RESOURCE_TYPES.has(item.resourceType) &&
|
|
75
|
-
typeof item.id === "string" &&
|
|
76
|
-
typeof item.sourceAction === "string");
|
|
77
|
-
});
|
|
78
|
-
}
|
|
79
|
-
catch {
|
|
80
|
-
return [];
|
|
81
99
|
}
|
|
100
|
+
return null;
|
|
82
101
|
}
|
|
83
|
-
function
|
|
84
|
-
|
|
102
|
+
function persistedArtifactIdentitiesFromMarker(result, secrets = a2aSecrets()) {
|
|
103
|
+
return (persistedArtifactLedgerFromMarker(result, secrets)?.identities ?? [])
|
|
104
|
+
.slice(0, 12)
|
|
105
|
+
.filter((identity) => {
|
|
106
|
+
const item = asRecord(identity);
|
|
107
|
+
return (!!item &&
|
|
108
|
+
ARTIFACT_RESOURCE_TYPES.has(item.resourceType) &&
|
|
109
|
+
typeof item.id === "string" &&
|
|
110
|
+
typeof item.sourceAction === "string");
|
|
111
|
+
});
|
|
112
|
+
}
|
|
113
|
+
function withPersistedArtifactMarker(text, toolResults, secret = a2aSecret(), delegatedTaskId) {
|
|
114
|
+
const verificationSecrets = [secret, a2aSecret()].filter((value, index, values) => !!value && values.indexOf(value) === index);
|
|
85
115
|
const identities = extractA2AArtifactIdentities(toolResults, {
|
|
86
116
|
persistedArtifactSecrets: verificationSecrets,
|
|
87
117
|
}).slice(0, 12);
|
|
88
|
-
|
|
118
|
+
const mutationReceipts = extractA2APersistedMutationReceipts(toolResults, {
|
|
119
|
+
persistedArtifactSecrets: secret ? [secret] : [],
|
|
120
|
+
expectedDelegatedTaskId: delegatedTaskId,
|
|
121
|
+
}).slice(0, 12);
|
|
122
|
+
if ((identities.length === 0 && mutationReceipts.length === 0) || !secret)
|
|
89
123
|
return text;
|
|
90
|
-
const payload = Buffer.from(JSON.stringify(
|
|
124
|
+
const payload = Buffer.from(JSON.stringify({
|
|
125
|
+
version: 1,
|
|
126
|
+
identities,
|
|
127
|
+
mutationReceipts,
|
|
128
|
+
...(delegatedTaskId ? { delegatedTaskId } : {}),
|
|
129
|
+
})).toString("base64url");
|
|
91
130
|
const signature = createHmac("sha256", secret).update(payload).digest("hex");
|
|
92
131
|
const marker = `<!-- ${PERSISTED_ARTIFACT_MARKER}${payload}.${signature} -->`;
|
|
93
132
|
return text ? `${text}\n\n${marker}` : marker;
|
|
@@ -474,6 +513,22 @@ function collectArtifacts(results) {
|
|
|
474
513
|
documents.set(artifact.id, artifact);
|
|
475
514
|
continue;
|
|
476
515
|
}
|
|
516
|
+
if (toolResult.tool === "upsert-database-item-by-key" ||
|
|
517
|
+
toolResult.tool === "mutate-content-database-block") {
|
|
518
|
+
const receipt = asRecord(parsed.receipt);
|
|
519
|
+
const receiptRow = asRecord(receipt?.row);
|
|
520
|
+
const receiptTarget = asRecord(receipt?.target);
|
|
521
|
+
const rowLink = asRecord(receipt?.rowLink);
|
|
522
|
+
const rowDocumentId = stringValue(receiptRow?.documentId) ??
|
|
523
|
+
stringValue(receiptTarget?.rowDocumentId);
|
|
524
|
+
if (rowDocumentId) {
|
|
525
|
+
documents.set(rowDocumentId, {
|
|
526
|
+
id: rowDocumentId,
|
|
527
|
+
url: stringValue(receiptRow?.urlPath) ?? stringValue(rowLink?.urlPath),
|
|
528
|
+
});
|
|
529
|
+
}
|
|
530
|
+
continue;
|
|
531
|
+
}
|
|
477
532
|
if (toolResult.tool === "create-document" ||
|
|
478
533
|
toolResult.tool === "update-document") {
|
|
479
534
|
if (parsed.conflict === true)
|
|
@@ -772,6 +827,175 @@ export function extractA2AArtifactIdentities(results, options = {}) {
|
|
|
772
827
|
}
|
|
773
828
|
return [...identities.values()];
|
|
774
829
|
}
|
|
830
|
+
function boundedStrings(value) {
|
|
831
|
+
if (!Array.isArray(value))
|
|
832
|
+
return undefined;
|
|
833
|
+
return value
|
|
834
|
+
.map(stringValue)
|
|
835
|
+
.filter((item) => !!item)
|
|
836
|
+
.slice(0, 50);
|
|
837
|
+
}
|
|
838
|
+
function parsePersistedMutationReceipt(value, sourceAction) {
|
|
839
|
+
const receipt = asRecord(value);
|
|
840
|
+
const target = asRecord(receipt?.target);
|
|
841
|
+
const authorityScope = asRecord(target?.authorityScope);
|
|
842
|
+
const row = asRecord(receipt?.row);
|
|
843
|
+
const rowLink = asRecord(receipt?.rowLink);
|
|
844
|
+
const idempotency = asRecord(receipt?.idempotency);
|
|
845
|
+
const revisions = asRecord(receipt?.revisions);
|
|
846
|
+
const rowRevisions = asRecord(revisions?.row);
|
|
847
|
+
const fieldRevisions = asRecord(revisions?.field);
|
|
848
|
+
const affected = asRecord(receipt?.affected);
|
|
849
|
+
const readback = asRecord(receipt?.readback);
|
|
850
|
+
const authorityScopeKind = stringValue(authorityScope?.kind) ??
|
|
851
|
+
stringValue(target?.authorityScopeKind);
|
|
852
|
+
const idempotencyResult = stringValue(idempotency?.result);
|
|
853
|
+
const rowDocumentId = stringValue(row?.documentId) ?? stringValue(target?.rowDocumentId);
|
|
854
|
+
const urlPath = stringValue(row?.urlPath) ?? stringValue(rowLink?.urlPath);
|
|
855
|
+
const receiptId = stringValue(receipt?.receiptId);
|
|
856
|
+
const operation = stringValue(receipt?.operation);
|
|
857
|
+
const outcome = stringValue(receipt?.outcome);
|
|
858
|
+
const authorityScopeId = stringValue(authorityScope?.id) ?? stringValue(target?.authorityScopeId);
|
|
859
|
+
const spaceId = stringValue(target?.spaceId);
|
|
860
|
+
const databaseId = stringValue(target?.databaseId);
|
|
861
|
+
const databaseDocumentId = stringValue(target?.databaseDocumentId);
|
|
862
|
+
const idempotencyKey = stringValue(idempotency?.key);
|
|
863
|
+
const payloadDigest = stringValue(idempotency?.payloadDigest);
|
|
864
|
+
if (!receipt ||
|
|
865
|
+
!target ||
|
|
866
|
+
(authorityScopeKind !== "personal" &&
|
|
867
|
+
authorityScopeKind !== "organization") ||
|
|
868
|
+
(idempotencyResult !== "applied" && idempotencyResult !== "replayed") ||
|
|
869
|
+
(readback?.verified !== true && receipt.readbackVerified !== true) ||
|
|
870
|
+
!receiptId ||
|
|
871
|
+
!operation ||
|
|
872
|
+
!outcome ||
|
|
873
|
+
!authorityScopeId ||
|
|
874
|
+
!spaceId ||
|
|
875
|
+
!databaseId ||
|
|
876
|
+
!databaseDocumentId ||
|
|
877
|
+
!idempotencyKey ||
|
|
878
|
+
!payloadDigest ||
|
|
879
|
+
!rowDocumentId ||
|
|
880
|
+
!urlPath) {
|
|
881
|
+
return null;
|
|
882
|
+
}
|
|
883
|
+
const propertyIds = boundedStrings(affected?.propertyIds);
|
|
884
|
+
const blockIds = boundedStrings(affected?.blockIds);
|
|
885
|
+
const deletedBlockIds = boundedStrings(affected?.deletedBlockIds);
|
|
886
|
+
const order = boundedStrings(affected?.order);
|
|
887
|
+
const compactAffected = {
|
|
888
|
+
...(typeof affected?.title === "boolean" ? { title: affected.title } : {}),
|
|
889
|
+
...(propertyIds ? { propertyIds } : {}),
|
|
890
|
+
...(blockIds ? { blockIds } : {}),
|
|
891
|
+
...(deletedBlockIds ? { deletedBlockIds } : {}),
|
|
892
|
+
...(order ? { order } : {}),
|
|
893
|
+
};
|
|
894
|
+
const itemId = stringValue(row?.itemId) ?? stringValue(target.itemId);
|
|
895
|
+
const targetItemId = stringValue(target.itemId);
|
|
896
|
+
const targetRowDocumentId = stringValue(target.rowDocumentId);
|
|
897
|
+
const targetPropertyId = stringValue(target.propertyId);
|
|
898
|
+
const after = stringValue(revisions?.after);
|
|
899
|
+
const rowBefore = stringValue(rowRevisions?.before) ?? stringValue(revisions?.rowBefore);
|
|
900
|
+
const rowAfter = stringValue(rowRevisions?.after) ?? stringValue(revisions?.rowAfter);
|
|
901
|
+
return {
|
|
902
|
+
receiptId,
|
|
903
|
+
sourceAction,
|
|
904
|
+
operation,
|
|
905
|
+
outcome,
|
|
906
|
+
target: {
|
|
907
|
+
authorityScopeKind,
|
|
908
|
+
authorityScopeId,
|
|
909
|
+
spaceId,
|
|
910
|
+
databaseId,
|
|
911
|
+
databaseDocumentId,
|
|
912
|
+
...(targetItemId ? { itemId: targetItemId } : {}),
|
|
913
|
+
...(targetRowDocumentId ? { rowDocumentId: targetRowDocumentId } : {}),
|
|
914
|
+
...(targetPropertyId ? { propertyId: targetPropertyId } : {}),
|
|
915
|
+
},
|
|
916
|
+
row: {
|
|
917
|
+
...(itemId ? { itemId } : {}),
|
|
918
|
+
documentId: rowDocumentId,
|
|
919
|
+
urlPath,
|
|
920
|
+
},
|
|
921
|
+
idempotency: {
|
|
922
|
+
key: idempotencyKey,
|
|
923
|
+
result: idempotencyResult,
|
|
924
|
+
payloadDigest,
|
|
925
|
+
},
|
|
926
|
+
revisions: {
|
|
927
|
+
...(revisions?.before === null || typeof revisions?.before === "string"
|
|
928
|
+
? { before: revisions.before }
|
|
929
|
+
: {}),
|
|
930
|
+
...(after ? { after } : {}),
|
|
931
|
+
...(rowBefore ? { rowBefore } : {}),
|
|
932
|
+
...(rowAfter ? { rowAfter } : {}),
|
|
933
|
+
...(typeof (fieldRevisions?.before ?? revisions?.fieldBefore) === "number"
|
|
934
|
+
? {
|
|
935
|
+
fieldBefore: (fieldRevisions?.before ??
|
|
936
|
+
revisions?.fieldBefore),
|
|
937
|
+
}
|
|
938
|
+
: {}),
|
|
939
|
+
...(typeof (fieldRevisions?.after ?? revisions?.fieldAfter) === "number"
|
|
940
|
+
? {
|
|
941
|
+
fieldAfter: (fieldRevisions?.after ??
|
|
942
|
+
revisions?.fieldAfter),
|
|
943
|
+
}
|
|
944
|
+
: {}),
|
|
945
|
+
},
|
|
946
|
+
...(Object.keys(compactAffected).length > 0
|
|
947
|
+
? { affected: compactAffected }
|
|
948
|
+
: {}),
|
|
949
|
+
readbackVerified: true,
|
|
950
|
+
};
|
|
951
|
+
}
|
|
952
|
+
/**
|
|
953
|
+
* Extract bounded, read-back-verified mutation receipts. Nested agent results
|
|
954
|
+
* are accepted only through the signed persisted ledger.
|
|
955
|
+
*/
|
|
956
|
+
export function extractA2APersistedMutationReceipts(results, options = {}) {
|
|
957
|
+
const receipts = new Map();
|
|
958
|
+
for (const result of results) {
|
|
959
|
+
if (result.isError === true || result.completedSideEffect === false)
|
|
960
|
+
continue;
|
|
961
|
+
if (result.tool === "call-agent") {
|
|
962
|
+
const nested = persistedArtifactLedgerFromMarker(result.result, options.persistedArtifactSecrets, options.expectedDelegatedTaskId);
|
|
963
|
+
for (const receipt of nested?.mutationReceipts ?? []) {
|
|
964
|
+
const parsed = parsePersistedMutationReceipt(receipt, "call-agent");
|
|
965
|
+
if (parsed)
|
|
966
|
+
receipts.set(parsed.receiptId, parsed);
|
|
967
|
+
}
|
|
968
|
+
continue;
|
|
969
|
+
}
|
|
970
|
+
if (result.tool !== "upsert-database-item-by-key" &&
|
|
971
|
+
result.tool !== "mutate-content-database-block") {
|
|
972
|
+
continue;
|
|
973
|
+
}
|
|
974
|
+
const parsedResult = parseToolResultJson(result.result);
|
|
975
|
+
const receipt = parsePersistedMutationReceipt(parsedResult?.receipt, result.tool);
|
|
976
|
+
if (receipt)
|
|
977
|
+
receipts.set(receipt.receiptId, receipt);
|
|
978
|
+
}
|
|
979
|
+
return [...receipts.values()].slice(0, 12);
|
|
980
|
+
}
|
|
981
|
+
export function appendA2APersistedMutationReceipts(text, toolResults, options = {}) {
|
|
982
|
+
const receiptSecret = options.persistedArtifactSecret ?? a2aSecret();
|
|
983
|
+
const receipts = extractA2APersistedMutationReceipts(toolResults, {
|
|
984
|
+
persistedArtifactSecrets: receiptSecret ? [receiptSecret] : [],
|
|
985
|
+
expectedDelegatedTaskId: options.delegatedTaskId,
|
|
986
|
+
}).filter((receipt) => !text.includes(receipt.receiptId));
|
|
987
|
+
if (receipts.length === 0)
|
|
988
|
+
return text;
|
|
989
|
+
const lines = receipts.map((receipt) => {
|
|
990
|
+
const url = artifactUrl(options.baseUrl, receipt.row.urlPath);
|
|
991
|
+
const item = receipt.row.itemId ? `; item ID: ${receipt.row.itemId}` : "";
|
|
992
|
+
return (`- ${receipt.receiptId}: ${receipt.outcome} via ${receipt.sourceAction}; ` +
|
|
993
|
+
`row ${url} (document ID: ${receipt.row.documentId}${item}); ` +
|
|
994
|
+
`idempotency: ${receipt.idempotency.result}`);
|
|
995
|
+
});
|
|
996
|
+
const section = ["Mutation receipts:", ...lines].join("\n");
|
|
997
|
+
return text.trim() ? `${text.trim()}\n\n${section}` : section;
|
|
998
|
+
}
|
|
775
999
|
function parseDownstreamArtifactBlock(result) {
|
|
776
1000
|
const artifacts = [];
|
|
777
1001
|
for (const line of downstreamArtifactLines(result)) {
|
|
@@ -1076,9 +1300,12 @@ function formatUnverifiedArtifactMessage(refs, documents, decks, dashboards, ana
|
|
|
1076
1300
|
export function guardA2AArtifactResponse(responseText, toolResults, options = {}) {
|
|
1077
1301
|
const baseUrl = normalizeBaseUrl(options.baseUrl);
|
|
1078
1302
|
const includeReferencedArtifacts = options.includeReferencedArtifacts ?? false;
|
|
1079
|
-
const finalize = (value) =>
|
|
1080
|
-
|
|
1081
|
-
|
|
1303
|
+
const finalize = (value) => {
|
|
1304
|
+
const withReceipts = appendA2APersistedMutationReceipts(value, toolResults, options);
|
|
1305
|
+
return options.includePersistedArtifactMarker
|
|
1306
|
+
? withPersistedArtifactMarker(withReceipts, toolResults, options.persistedArtifactSecret ?? a2aSecret(), options.delegatedTaskId)
|
|
1307
|
+
: withReceipts;
|
|
1308
|
+
};
|
|
1082
1309
|
const { documents, decks, dashboards, analyses, images, designShells, generatedDesigns, monitors, forms, } = collectArtifacts(toolResults);
|
|
1083
1310
|
const generatedDesignIds = new Set(generatedDesigns.map((design) => design.id));
|
|
1084
1311
|
const incompleteShells = designShells.filter((shell) => !generatedDesignIds.has(shell.id));
|
package/dist/a2a/index.d.ts
CHANGED
|
@@ -9,3 +9,5 @@ export type { A2ACallerAuth } from "./caller-auth.js";
|
|
|
9
9
|
export { AgentInvocationError, buildAgentInvocationPrompt, invokeAgent, invokeAgentAction, looksLikeAgentUrl, resolveAgentInvocationTarget, } from "./invoke.js";
|
|
10
10
|
export type { A2AConfig, A2AHandler, A2AHandlerContext, A2AHandlerResult, A2ASourceContext, AgentCard, AgentSkill, AgentCapabilities, Task, TaskState, TaskStatus, Message, Part, TextPart, FilePart, DataPart, Artifact, JsonRpcRequest, JsonRpcResponse, A2ACorrelationMetadata, A2AReadOnlyActionInvocation, A2AReadOnlyActionResult, A2AAgentActivityPhase, A2AAgentActivitySnapshot, A2AAgentActivityState, A2AAgentActivityToolCall, A2AAgentActivityToolStatus, } from "./types.js";
|
|
11
11
|
export type { AgentInvocationErrorCode, AgentActionInvocationResult, AgentInvocationResult, AgentInvocationRuntime, InvokeAgentActionOptions, InvokeAgentOptions, ResolveAgentInvocationTargetOptions, ResolvedAgentInvocationTarget, } from "./invoke.js";
|
|
12
|
+
export { extractA2APersistedMutationReceipts, stripA2APersistedArtifactMarkers, } from "./artifact-response.js";
|
|
13
|
+
export type { A2APersistedMutationReceipt } from "./artifact-response.js";
|
package/dist/a2a/index.js
CHANGED
|
@@ -7,3 +7,4 @@ export { A2AClient, callAction, callAgent, signA2AToken } from "./client.js";
|
|
|
7
7
|
export { canonicalA2AAudience } from "./audience.js";
|
|
8
8
|
export { resolveA2ACallerAuth } from "./caller-auth.js";
|
|
9
9
|
export { AgentInvocationError, buildAgentInvocationPrompt, invokeAgent, invokeAgentAction, looksLikeAgentUrl, resolveAgentInvocationTarget, } from "./invoke.js";
|
|
10
|
+
export { extractA2APersistedMutationReceipts, stripA2APersistedArtifactMarkers, } from "./artifact-response.js";
|
|
@@ -62,11 +62,11 @@ export declare const postAwareness: import("h3").EventHandlerWithFetch<import("h
|
|
|
62
62
|
error: string;
|
|
63
63
|
states?: undefined;
|
|
64
64
|
} | {
|
|
65
|
+
error?: undefined;
|
|
65
66
|
states: {
|
|
66
67
|
clientId: number;
|
|
67
68
|
state: string;
|
|
68
69
|
}[];
|
|
69
|
-
error?: undefined;
|
|
70
70
|
}>>;
|
|
71
71
|
/**
|
|
72
72
|
* GET /_agent-native/collab/:docId/users
|
|
@@ -77,9 +77,9 @@ export declare const getActiveUsers: import("h3").EventHandlerWithFetch<import("
|
|
|
77
77
|
error: string;
|
|
78
78
|
users?: undefined;
|
|
79
79
|
} | {
|
|
80
|
+
error?: undefined;
|
|
80
81
|
users: {
|
|
81
82
|
clientId: number;
|
|
82
83
|
lastSeen: number;
|
|
83
84
|
}[];
|
|
84
|
-
error?: undefined;
|
|
85
85
|
}>>;
|
|
@@ -53,6 +53,25 @@ function claimSlackSystemNoticeSlot(key, ttlMs = SLACK_SYSTEM_NOTICE_DEDUPE_TTL_
|
|
|
53
53
|
slackSystemNoticeCache.set(key, now + ttlMs);
|
|
54
54
|
return true;
|
|
55
55
|
}
|
|
56
|
+
function slackEventInstallationScope(payload) {
|
|
57
|
+
const teamId = typeof payload?.team_id === "string" ? payload.team_id : undefined;
|
|
58
|
+
const authorizations = Array.isArray(payload?.authorizations)
|
|
59
|
+
? payload.authorizations.filter((value) => !!value && typeof value === "object" && !Array.isArray(value))
|
|
60
|
+
: [];
|
|
61
|
+
const enterpriseAuthorization = authorizations.find((authorization) => authorization.is_enterprise_install === true &&
|
|
62
|
+
typeof authorization.enterprise_id === "string");
|
|
63
|
+
const teamAuthorization = authorizations.find((authorization) => authorization.team_id === teamId);
|
|
64
|
+
const authorization = teamAuthorization ?? enterpriseAuthorization;
|
|
65
|
+
return {
|
|
66
|
+
enterpriseId: typeof authorization?.enterprise_id === "string"
|
|
67
|
+
? authorization.enterprise_id
|
|
68
|
+
: typeof payload?.enterprise_id === "string"
|
|
69
|
+
? payload.enterprise_id
|
|
70
|
+
: undefined,
|
|
71
|
+
isEnterpriseInstall: authorization?.is_enterprise_install === true ||
|
|
72
|
+
payload?.is_enterprise_install === true,
|
|
73
|
+
};
|
|
74
|
+
}
|
|
56
75
|
/**
|
|
57
76
|
* Create a Slack platform adapter.
|
|
58
77
|
*
|
|
@@ -204,6 +223,7 @@ export function slackAdapter(options = {}) {
|
|
|
204
223
|
const apiAppId = typeof payload.api_app_id === "string"
|
|
205
224
|
? payload.api_app_id
|
|
206
225
|
: "unknown";
|
|
226
|
+
const installationScope = slackEventInstallationScope(payload);
|
|
207
227
|
const agentContext = normalizeSlackAgentContext(e.app_context, teamId);
|
|
208
228
|
const isDm = typeof e.channel_type === "string"
|
|
209
229
|
? e.channel_type === "im"
|
|
@@ -238,9 +258,8 @@ export function slackAdapter(options = {}) {
|
|
|
238
258
|
messageTs: e.ts,
|
|
239
259
|
teamId,
|
|
240
260
|
apiAppId,
|
|
241
|
-
enterpriseId:
|
|
242
|
-
|
|
243
|
-
: undefined,
|
|
261
|
+
enterpriseId: installationScope.enterpriseId,
|
|
262
|
+
isEnterpriseInstall: installationScope.isEnterpriseInstall,
|
|
244
263
|
eventId: payload.event_id,
|
|
245
264
|
...(agentContext
|
|
246
265
|
? {
|
|
@@ -614,6 +633,7 @@ async function resolveManagedSlackBotToken(incoming) {
|
|
|
614
633
|
const enterpriseId = typeof incoming.platformContext.enterpriseId === "string"
|
|
615
634
|
? incoming.platformContext.enterpriseId
|
|
616
635
|
: undefined;
|
|
636
|
+
const isEnterpriseInstall = incoming.platformContext.isEnterpriseInstall === true;
|
|
617
637
|
const installationKeyHint = typeof incoming.platformContext.installationKey === "string"
|
|
618
638
|
? incoming.platformContext.installationKey
|
|
619
639
|
: undefined;
|
|
@@ -624,7 +644,12 @@ async function resolveManagedSlackBotToken(incoming) {
|
|
|
624
644
|
? await getActiveIntegrationInstallationByKey("slack", installationKeyHint)
|
|
625
645
|
: null;
|
|
626
646
|
if (!installation && apiAppId) {
|
|
627
|
-
installation = await getActiveIntegrationInstallationByKey("slack", slackInstallationKey({
|
|
647
|
+
installation = await getActiveIntegrationInstallationByKey("slack", slackInstallationKey({
|
|
648
|
+
teamId,
|
|
649
|
+
enterpriseId,
|
|
650
|
+
apiAppId,
|
|
651
|
+
isEnterpriseInstall,
|
|
652
|
+
}));
|
|
628
653
|
}
|
|
629
654
|
if (!installation && !apiAppId) {
|
|
630
655
|
// Without an app id the tenant can match several connected Slack apps.
|
|
@@ -641,7 +666,12 @@ async function resolveManagedSlackBotToken(incoming) {
|
|
|
641
666
|
installation = candidates[0] ?? null;
|
|
642
667
|
}
|
|
643
668
|
const key = installation?.installationKey ??
|
|
644
|
-
slackInstallationKey({
|
|
669
|
+
slackInstallationKey({
|
|
670
|
+
teamId,
|
|
671
|
+
enterpriseId,
|
|
672
|
+
apiAppId,
|
|
673
|
+
isEnterpriseInstall,
|
|
674
|
+
});
|
|
645
675
|
return (await resolveIntegrationTokenBundle("slack", key))?.accessToken;
|
|
646
676
|
}
|
|
647
677
|
catch {
|
|
@@ -721,13 +751,19 @@ let _missingAllowlistWarned = false;
|
|
|
721
751
|
async function enforceWorkspaceAllowlist(payload) {
|
|
722
752
|
const teamId = typeof payload?.team_id === "string" ? payload.team_id : undefined;
|
|
723
753
|
const apiAppId = typeof payload?.api_app_id === "string" ? payload.api_app_id : undefined;
|
|
754
|
+
const installationScope = slackEventInstallationScope(payload);
|
|
724
755
|
const allowedTeamIds = parseAllowlistEnv("SLACK_ALLOWED_TEAM_IDS");
|
|
725
756
|
const allowedAppIds = parseAllowlistEnv("SLACK_ALLOWED_API_APP_IDS");
|
|
726
757
|
if (!allowedTeamIds) {
|
|
727
758
|
if (process.env.NODE_ENV === "production") {
|
|
728
759
|
let managed = false;
|
|
729
760
|
try {
|
|
730
|
-
const key = slackInstallationKey({
|
|
761
|
+
const key = slackInstallationKey({
|
|
762
|
+
teamId,
|
|
763
|
+
apiAppId,
|
|
764
|
+
enterpriseId: installationScope.enterpriseId,
|
|
765
|
+
isEnterpriseInstall: installationScope.isEnterpriseInstall,
|
|
766
|
+
});
|
|
731
767
|
managed = !!(await getActiveIntegrationInstallationByKey("slack", key));
|
|
732
768
|
}
|
|
733
769
|
catch { }
|
|
@@ -745,7 +781,22 @@ async function enforceWorkspaceAllowlist(payload) {
|
|
|
745
781
|
}
|
|
746
782
|
}
|
|
747
783
|
if (allowedTeamIds) {
|
|
748
|
-
|
|
784
|
+
let allowedEnterpriseInstall = false;
|
|
785
|
+
if (installationScope.isEnterpriseInstall) {
|
|
786
|
+
try {
|
|
787
|
+
const key = slackInstallationKey({
|
|
788
|
+
teamId,
|
|
789
|
+
apiAppId,
|
|
790
|
+
enterpriseId: installationScope.enterpriseId,
|
|
791
|
+
isEnterpriseInstall: true,
|
|
792
|
+
});
|
|
793
|
+
allowedEnterpriseInstall =
|
|
794
|
+
!!(await getActiveIntegrationInstallationByKey("slack", key));
|
|
795
|
+
// coercion-ok: lookup failure leaves the enterprise event denied below
|
|
796
|
+
}
|
|
797
|
+
catch { }
|
|
798
|
+
}
|
|
799
|
+
if ((!teamId || !allowedTeamIds.has(teamId)) && !allowedEnterpriseInstall) {
|
|
749
800
|
throw createError({
|
|
750
801
|
statusCode: 401,
|
|
751
802
|
statusMessage: "Unrecognized Slack workspace",
|
|
@@ -11,14 +11,14 @@
|
|
|
11
11
|
* DELETE /_agent-native/notifications/:id — delete
|
|
12
12
|
*/
|
|
13
13
|
export declare function createNotificationsHandler(): import("h3").EventHandlerWithFetch<import("h3").EventHandlerRequest, Promise<"" | import("./types.js").Notification[] | {
|
|
14
|
+
error?: undefined;
|
|
14
15
|
count: number;
|
|
15
16
|
updated?: undefined;
|
|
16
|
-
error?: undefined;
|
|
17
17
|
ok?: undefined;
|
|
18
18
|
} | {
|
|
19
|
+
error?: undefined;
|
|
19
20
|
count?: undefined;
|
|
20
21
|
updated: number;
|
|
21
|
-
error?: undefined;
|
|
22
22
|
ok?: undefined;
|
|
23
23
|
} | {
|
|
24
24
|
count?: undefined;
|
|
@@ -26,8 +26,8 @@ export declare function createNotificationsHandler(): import("h3").EventHandlerW
|
|
|
26
26
|
error: string;
|
|
27
27
|
ok?: undefined;
|
|
28
28
|
} | {
|
|
29
|
+
error?: undefined;
|
|
29
30
|
count?: undefined;
|
|
30
31
|
updated?: undefined;
|
|
31
|
-
error?: undefined;
|
|
32
32
|
ok: boolean;
|
|
33
33
|
}>>;
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { type A2AArtifactResponseOptions, type A2AToolResultSummary } from "../../a2a/artifact-response.js";
|
|
1
|
+
import { type A2AArtifactResponseOptions, type A2APersistedMutationReceipt, type A2AToolResultSummary } from "../../a2a/artifact-response.js";
|
|
2
2
|
import type { AgentSkill } from "../../a2a/types.js";
|
|
3
3
|
import type { EngineTool } from "../../agent/engine/types.js";
|
|
4
4
|
import { type ActionEntry, type AgentLoopOutcome } from "../../agent/production-agent.js";
|
|
@@ -74,6 +74,7 @@ export declare function assembleA2AFinalResponse(events: readonly AgentChatEvent
|
|
|
74
74
|
}): {
|
|
75
75
|
responseText: string;
|
|
76
76
|
finalText: string;
|
|
77
|
+
mutationReceipts: A2APersistedMutationReceipt[];
|
|
77
78
|
};
|
|
78
79
|
type A2AAgentLoopRunner = typeof runAgentLoopDirectWithSoftTimeout;
|
|
79
80
|
/**
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import { getHeader } from "h3";
|
|
2
|
-
import { appendA2AArtifactLinks, } from "../../a2a/artifact-response.js";
|
|
2
|
+
import { appendA2AArtifactLinks, extractA2APersistedMutationReceipts, } from "../../a2a/artifact-response.js";
|
|
3
3
|
import { collectFinalResponseTextFromAgentEvents } from "../../a2a/response-text.js";
|
|
4
4
|
import { resolveMainChatMaxOutputTokens } from "../../agent/engine/output-tokens.js";
|
|
5
5
|
import { filterInitialEngineTools, resolveAgentRequestReasoningEffort, } from "../../agent/production-agent.js";
|
|
@@ -200,7 +200,15 @@ export function assembleA2AFinalResponse(events, toolResults, options = {}) {
|
|
|
200
200
|
baseUrl: options.baseUrl ?? resolveArtifactBaseUrl(options.event),
|
|
201
201
|
includeReferencedArtifacts: true,
|
|
202
202
|
includePersistedArtifactMarker: true,
|
|
203
|
+
persistedArtifactSecret: options.persistedArtifactSecret,
|
|
204
|
+
delegatedTaskId: options.delegatedTaskId,
|
|
203
205
|
});
|
|
206
|
+
const mutationReceipts = extractA2APersistedMutationReceipts([...toolResults], options.persistedArtifactSecret
|
|
207
|
+
? {
|
|
208
|
+
persistedArtifactSecrets: [options.persistedArtifactSecret],
|
|
209
|
+
expectedDelegatedTaskId: options.delegatedTaskId,
|
|
210
|
+
}
|
|
211
|
+
: {});
|
|
204
212
|
if (terminalError) {
|
|
205
213
|
const partialResult = finalText.trim()
|
|
206
214
|
? `\n\nPartial verified results before the failure:\n${finalText.trim()}`
|
|
@@ -210,7 +218,7 @@ export function assembleA2AFinalResponse(events, toolResults, options = {}) {
|
|
|
210
218
|
if (!finalText.trim()) {
|
|
211
219
|
throw new Error("Agent completed without a response or verified artifact.\ncode: empty_agent_response");
|
|
212
220
|
}
|
|
213
|
-
return { responseText, finalText };
|
|
221
|
+
return { responseText, finalText, mutationReceipts };
|
|
214
222
|
}
|
|
215
223
|
function terminalErrorFromOutcome(outcome) {
|
|
216
224
|
if (outcome.state === "completed")
|