@agent-native/core 0.107.1 → 0.107.2

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (34) hide show
  1. package/corpus/core/CHANGELOG.md +6 -0
  2. package/corpus/core/package.json +1 -1
  3. package/corpus/core/src/a2a/artifact-response.ts +32 -10
  4. package/corpus/core/src/integrations/a2a-continuation-processor.ts +107 -3
  5. package/corpus/core/src/integrations/pending-tasks-store.ts +98 -1
  6. package/corpus/core/src/integrations/plugin.ts +129 -4
  7. package/corpus/core/src/integrations/webhook-handler.ts +339 -36
  8. package/corpus/core/src/server/agent-chat-plugin.ts +93 -8
  9. package/dist/a2a/artifact-response.d.ts +5 -1
  10. package/dist/a2a/artifact-response.d.ts.map +1 -1
  11. package/dist/a2a/artifact-response.js +18 -12
  12. package/dist/a2a/artifact-response.js.map +1 -1
  13. package/dist/collab/struct-routes.d.ts +1 -1
  14. package/dist/integrations/a2a-continuation-processor.js +62 -5
  15. package/dist/integrations/a2a-continuation-processor.js.map +1 -1
  16. package/dist/integrations/pending-tasks-store.d.ts +3 -0
  17. package/dist/integrations/pending-tasks-store.d.ts.map +1 -1
  18. package/dist/integrations/pending-tasks-store.js +75 -1
  19. package/dist/integrations/pending-tasks-store.js.map +1 -1
  20. package/dist/integrations/plugin.d.ts.map +1 -1
  21. package/dist/integrations/plugin.js +81 -5
  22. package/dist/integrations/plugin.js.map +1 -1
  23. package/dist/integrations/webhook-handler.d.ts +23 -2
  24. package/dist/integrations/webhook-handler.d.ts.map +1 -1
  25. package/dist/integrations/webhook-handler.js +257 -35
  26. package/dist/integrations/webhook-handler.js.map +1 -1
  27. package/dist/notifications/routes.d.ts +2 -2
  28. package/dist/resources/handlers.d.ts +1 -1
  29. package/dist/server/agent-chat-plugin.d.ts +6 -0
  30. package/dist/server/agent-chat-plugin.d.ts.map +1 -1
  31. package/dist/server/agent-chat-plugin.js +71 -6
  32. package/dist/server/agent-chat-plugin.js.map +1 -1
  33. package/dist/server/agent-engine-api-key-route.d.ts +1 -1
  34. package/package.json +1 -1
@@ -14,6 +14,7 @@ import {
14
14
  } from "h3";
15
15
 
16
16
  import {
17
+ appendA2AArtifactLinks,
17
18
  buildA2ARecoverableArtifactMessage,
18
19
  type A2AToolResultSummary,
19
20
  } from "../a2a/artifact-response.js";
@@ -27,6 +28,7 @@ import {
27
28
  createA2AApproval,
28
29
  updateTaskStatusMessage,
29
30
  } from "../a2a/task-store.js";
31
+ import type { Message as A2AMessage } from "../a2a/types.js";
30
32
  import type { ActionHttpConfig } from "../action.js";
31
33
  import {
32
34
  canUpdateAgentAppModelDefaultSettings,
@@ -275,6 +277,74 @@ export { loadRunCodeToolEntries };
275
277
  export { shouldDisableRecurringJobsRuntime };
276
278
  export { finalizeClaimedAgentChatProcessRunFailure };
277
279
 
280
+ export function createSerializedA2ATaskStatusWriter(
281
+ taskId: string,
282
+ writeStatus: (
283
+ taskId: string,
284
+ message: A2AMessage,
285
+ ) => Promise<void> = updateTaskStatusMessage,
286
+ onError: (error: unknown) => void = (error) => {
287
+ console.error(
288
+ `[A2A] Failed to persist recoverable artifact message for task ${taskId}:`,
289
+ error,
290
+ );
291
+ },
292
+ ): {
293
+ enqueue: (message: A2AMessage) => void;
294
+ flush: () => Promise<void>;
295
+ } {
296
+ const maxAttempts = 3;
297
+ let latestWrite: Promise<void> = Promise.resolve();
298
+
299
+ const persistWithRetry = async (message: A2AMessage): Promise<void> => {
300
+ let lastError: unknown;
301
+ for (let attempt = 0; attempt < maxAttempts; attempt += 1) {
302
+ try {
303
+ await writeStatus(taskId, message);
304
+ return;
305
+ } catch (error) {
306
+ lastError = error;
307
+ }
308
+ }
309
+ try {
310
+ onError(lastError);
311
+ } catch {
312
+ // The durable-write error below remains the authoritative failure.
313
+ }
314
+ throw lastError;
315
+ };
316
+
317
+ return {
318
+ enqueue(message) {
319
+ // A newer checkpoint supersedes an earlier failed checkpoint, so keep
320
+ // the queue moving. flush() still rejects when the latest write itself
321
+ // cannot be made durable after bounded retries.
322
+ latestWrite = latestWrite
323
+ .catch(() => undefined)
324
+ .then(() => {
325
+ return persistWithRetry(message);
326
+ });
327
+ },
328
+ flush() {
329
+ return latestWrite;
330
+ },
331
+ };
332
+ }
333
+
334
+ export async function resolveA2ARecoverableArtifactSecret(
335
+ orgId: string | null | undefined = getRequestOrgId(),
336
+ ): Promise<string | undefined> {
337
+ const globalSecret = process.env.A2A_SECRET?.trim();
338
+ if (globalSecret) return globalSecret;
339
+ if (!orgId) return undefined;
340
+ try {
341
+ const { getOrgA2ASecret } = await import("../org/context.js");
342
+ return (await getOrgA2ASecret(orgId))?.trim() || undefined;
343
+ } catch {
344
+ return undefined;
345
+ }
346
+ }
347
+
278
348
  export function buildLeanRunPolicyPrompt(
279
349
  codeEditingSurfaceRestriction: string,
280
350
  prodCodeExecPromptNote: string,
@@ -1468,6 +1538,10 @@ export function createAgentChatPlugin(
1468
1538
  const a2aEvents: AgentChatEvent[] = [];
1469
1539
  const a2aToolResults: A2AToolResultSummary[] = [];
1470
1540
  let lastRecoverableArtifactText = "";
1541
+ const recoverableArtifactSecret =
1542
+ await resolveA2ARecoverableArtifactSecret();
1543
+ const recoverableArtifactStatusWriter =
1544
+ createSerializedA2ATaskStatusWriter(context.taskId);
1471
1545
  const controller = new AbortController();
1472
1546
 
1473
1547
  console.log(
@@ -1504,16 +1578,28 @@ export function createAgentChatPlugin(
1504
1578
  isError: event.isError,
1505
1579
  completedSideEffect: event.completedSideEffect,
1506
1580
  });
1507
- const recoverableArtifactText =
1581
+ const artifactBaseUrl = resolveArtifactBaseUrl(context.event);
1582
+ const recoverableArtifactMessage =
1508
1583
  buildA2ARecoverableArtifactMessage(a2aToolResults, {
1509
- baseUrl: resolveArtifactBaseUrl(context.event),
1584
+ baseUrl: artifactBaseUrl,
1510
1585
  });
1586
+ const recoverableArtifactText = recoverableArtifactMessage
1587
+ ? appendA2AArtifactLinks(
1588
+ recoverableArtifactMessage,
1589
+ a2aToolResults,
1590
+ {
1591
+ baseUrl: artifactBaseUrl,
1592
+ includePersistedArtifactMarker: true,
1593
+ persistedArtifactSecret: recoverableArtifactSecret,
1594
+ },
1595
+ )
1596
+ : null;
1511
1597
  if (
1512
1598
  recoverableArtifactText &&
1513
1599
  recoverableArtifactText !== lastRecoverableArtifactText
1514
1600
  ) {
1515
1601
  lastRecoverableArtifactText = recoverableArtifactText;
1516
- updateTaskStatusMessage(context.taskId, {
1602
+ recoverableArtifactStatusWriter.enqueue({
1517
1603
  role: "agent",
1518
1604
  metadata: { agentNativeRecoverableArtifacts: true },
1519
1605
  parts: [
@@ -1522,11 +1608,6 @@ export function createAgentChatPlugin(
1522
1608
  text: recoverableArtifactText,
1523
1609
  },
1524
1610
  ],
1525
- }).catch((err) => {
1526
- console.error(
1527
- `[A2A] Failed to persist recoverable artifact message for task ${context.taskId}:`,
1528
- err,
1529
- );
1530
1611
  });
1531
1612
  }
1532
1613
  } else if (event.type === "error") {
@@ -1548,6 +1629,10 @@ export function createAgentChatPlugin(
1548
1629
  },
1549
1630
  );
1550
1631
 
1632
+ // The continuation can observe terminal output immediately, so make
1633
+ // its latest mutation checkpoint durable first.
1634
+ await recoverableArtifactStatusWriter.flush();
1635
+
1551
1636
  const approval = [...a2aEvents]
1552
1637
  .reverse()
1553
1638
  .find(
@@ -8,6 +8,10 @@ export interface A2AArtifactResponseOptions {
8
8
  baseUrl?: string;
9
9
  includeReferencedArtifacts?: boolean;
10
10
  includePersistedArtifactMarker?: boolean;
11
+ persistedArtifactSecret?: string;
12
+ }
13
+ export interface A2AArtifactIdentityOptions {
14
+ persistedArtifactSecrets?: readonly string[];
11
15
  }
12
16
  export interface A2AArtifactIdentity {
13
17
  resourceType: "document" | "deck" | "dashboard" | "analysis" | "image" | "design" | "monitor" | "form";
@@ -22,7 +26,7 @@ export declare function stripA2APersistedArtifactMarkers(text: string): string;
22
26
  * The ledger deliberately excludes raw tool results so it is safe to retain in
23
27
  * long-lived thread context and stable even when a resource is later renamed.
24
28
  */
25
- export declare function extractA2AArtifactIdentities(results: A2AToolResultSummary[]): A2AArtifactIdentity[];
29
+ export declare function extractA2AArtifactIdentities(results: A2AToolResultSummary[], options?: A2AArtifactIdentityOptions): A2AArtifactIdentity[];
26
30
  export declare function appendA2AArtifactLinks(responseText: string, toolResults: A2AToolResultSummary[], options?: A2AArtifactResponseOptions): string;
27
31
  export declare function buildA2ARecoverableArtifactMessage(toolResults: A2AToolResultSummary[], options?: A2AArtifactResponseOptions): string | null;
28
32
  /**
@@ -1 +1 @@
1
- {"version":3,"file":"artifact-response.d.ts","sourceRoot":"","sources":["../../src/a2a/artifact-response.ts"],"names":[],"mappings":"AAEA,MAAM,WAAW,oBAAoB;IACnC,IAAI,EAAE,MAAM,CAAC;IACb,MAAM,EAAE,MAAM,CAAC;IACf,OAAO,CAAC,EAAE,OAAO,CAAC;IAClB,mBAAmB,CAAC,EAAE,OAAO,CAAC;CAC/B;AAED,MAAM,WAAW,0BAA0B;IACzC,OAAO,CAAC,EAAE,MAAM,CAAC;IACjB,0BAA0B,CAAC,EAAE,OAAO,CAAC;IACrC,8BAA8B,CAAC,EAAE,OAAO,CAAC;CAC1C;AAED,MAAM,WAAW,mBAAmB;IAClC,YAAY,EACR,UAAU,GACV,MAAM,GACN,WAAW,GACX,UAAU,GACV,OAAO,GACP,QAAQ,GACR,SAAS,GACT,MAAM,CAAC;IACX,EAAE,EAAE,MAAM,CAAC;IACX,YAAY,EAAE,MAAM,CAAC;IACrB,aAAa,CAAC,EAAE,MAAM,CAAC;IACvB,GAAG,CAAC,EAAE,MAAM,CAAC;CACd;AAiGD,wBAAgB,gCAAgC,CAAC,IAAI,EAAE,MAAM,GAAG,MAAM,CAErE;AA4uBD;;;;GAIG;AACH,wBAAgB,4BAA4B,CAC1C,OAAO,EAAE,oBAAoB,EAAE,GAC9B,mBAAmB,EAAE,CAqGvB;AA2YD,wBAAgB,sBAAsB,CACpC,YAAY,EAAE,MAAM,EACpB,WAAW,EAAE,oBAAoB,EAAE,EACnC,OAAO,GAAE,0BAA+B,GACvC,MAAM,CA8IR;AAED,wBAAgB,kCAAkC,CAChD,WAAW,EAAE,oBAAoB,EAAE,EACnC,OAAO,GAAE,0BAA+B,GACvC,MAAM,GAAG,IAAI,CA8Bf;AAmCD;;;;;GAKG;AACH,wBAAgB,+BAA+B,CAC7C,WAAW,EAAE,oBAAoB,EAAE,EACnC,OAAO,GAAE,0BAA+B,GACvC,MAAM,GAAG,IAAI,CAqBf"}
1
+ {"version":3,"file":"artifact-response.d.ts","sourceRoot":"","sources":["../../src/a2a/artifact-response.ts"],"names":[],"mappings":"AAEA,MAAM,WAAW,oBAAoB;IACnC,IAAI,EAAE,MAAM,CAAC;IACb,MAAM,EAAE,MAAM,CAAC;IACf,OAAO,CAAC,EAAE,OAAO,CAAC;IAClB,mBAAmB,CAAC,EAAE,OAAO,CAAC;CAC/B;AAED,MAAM,WAAW,0BAA0B;IACzC,OAAO,CAAC,EAAE,MAAM,CAAC;IACjB,0BAA0B,CAAC,EAAE,OAAO,CAAC;IACrC,8BAA8B,CAAC,EAAE,OAAO,CAAC;IACzC,uBAAuB,CAAC,EAAE,MAAM,CAAC;CAClC;AAED,MAAM,WAAW,0BAA0B;IACzC,wBAAwB,CAAC,EAAE,SAAS,MAAM,EAAE,CAAC;CAC9C;AAED,MAAM,WAAW,mBAAmB;IAClC,YAAY,EACR,UAAU,GACV,MAAM,GACN,WAAW,GACX,UAAU,GACV,OAAO,GACP,QAAQ,GACR,SAAS,GACT,MAAM,CAAC;IACX,EAAE,EAAE,MAAM,CAAC;IACX,YAAY,EAAE,MAAM,CAAC;IACrB,aAAa,CAAC,EAAE,MAAM,CAAC;IACvB,GAAG,CAAC,EAAE,MAAM,CAAC;CACd;AA4GD,wBAAgB,gCAAgC,CAAC,IAAI,EAAE,MAAM,GAAG,MAAM,CAErE;AA4uBD;;;;GAIG;AACH,wBAAgB,4BAA4B,CAC1C,OAAO,EAAE,oBAAoB,EAAE,EAC/B,OAAO,GAAE,0BAA+B,GACvC,mBAAmB,EAAE,CAsGvB;AA2YD,wBAAgB,sBAAsB,CACpC,YAAY,EAAE,MAAM,EACpB,WAAW,EAAE,oBAAoB,EAAE,EACnC,OAAO,GAAE,0BAA+B,GACvC,MAAM,CAkJR;AAED,wBAAgB,kCAAkC,CAChD,WAAW,EAAE,oBAAoB,EAAE,EACnC,OAAO,GAAE,0BAA+B,GACvC,MAAM,GAAG,IAAI,CA8Bf;AAmCD;;;;;GAKG;AACH,wBAAgB,+BAA+B,CAC7C,WAAW,EAAE,oBAAoB,EAAE,EACnC,OAAO,GAAE,0BAA+B,GACvC,MAAM,GAAG,IAAI,CAqBf"}
@@ -39,19 +39,23 @@ const ARTIFACT_RESOURCE_TYPES = new Set([
39
39
  "monitor",
40
40
  "form",
41
41
  ]);
42
- function persistedArtifactIdentitiesFromMarker(result) {
43
- const secret = process.env.A2A_SECRET;
44
- if (!secret)
42
+ function persistedArtifactIdentitiesFromMarker(result, secrets = process.env.A2A_SECRET
43
+ ? [process.env.A2A_SECRET]
44
+ : []) {
45
+ if (secrets.length === 0)
45
46
  return [];
46
47
  const match = result.match(/<!--\s*agent-native:persisted-artifacts=([A-Za-z0-9_-]+)\.([a-f0-9]{64})\s*-->/);
47
48
  if (!match)
48
49
  return [];
49
50
  try {
50
51
  const payload = match[1];
51
- const expected = createHmac("sha256", secret).update(payload).digest();
52
52
  const supplied = Buffer.from(match[2], "hex");
53
- if (supplied.length !== expected.length ||
54
- !timingSafeEqual(supplied, expected)) {
53
+ const verified = secrets.some((secret) => {
54
+ const expected = createHmac("sha256", secret).update(payload).digest();
55
+ return (supplied.length === expected.length &&
56
+ timingSafeEqual(supplied, expected));
57
+ });
58
+ if (!verified) {
55
59
  return [];
56
60
  }
57
61
  const parsed = JSON.parse(Buffer.from(payload, "base64url").toString());
@@ -71,9 +75,11 @@ function persistedArtifactIdentitiesFromMarker(result) {
71
75
  return [];
72
76
  }
73
77
  }
74
- function withPersistedArtifactMarker(text, toolResults) {
75
- const identities = extractA2AArtifactIdentities(toolResults).slice(0, 12);
76
- const secret = process.env.A2A_SECRET;
78
+ function withPersistedArtifactMarker(text, toolResults, secret = process.env.A2A_SECRET) {
79
+ const verificationSecrets = [secret, process.env.A2A_SECRET].filter((value, index, values) => !!value && values.indexOf(value) === index);
80
+ const identities = extractA2AArtifactIdentities(toolResults, {
81
+ persistedArtifactSecrets: verificationSecrets,
82
+ }).slice(0, 12);
77
83
  if (identities.length === 0 || !secret)
78
84
  return text;
79
85
  const payload = Buffer.from(JSON.stringify(identities)).toString("base64url");
@@ -667,7 +673,7 @@ function collectArtifacts(results) {
667
673
  * The ledger deliberately excludes raw tool results so it is safe to retain in
668
674
  * long-lived thread context and stable even when a resource is later renamed.
669
675
  */
670
- export function extractA2AArtifactIdentities(results) {
676
+ export function extractA2AArtifactIdentities(results, options = {}) {
671
677
  const identities = new Map();
672
678
  const remember = (identity) => {
673
679
  identities.set(`${identity.resourceType}:${identity.id}`, identity);
@@ -676,7 +682,7 @@ export function extractA2AArtifactIdentities(results) {
676
682
  if (result.isError === true || result.completedSideEffect === false)
677
683
  continue;
678
684
  if (result.tool === "call-agent") {
679
- for (const identity of persistedArtifactIdentitiesFromMarker(result.result)) {
685
+ for (const identity of persistedArtifactIdentitiesFromMarker(result.result, options.persistedArtifactSecrets)) {
680
686
  remember({ ...identity, sourceAction: "call-agent" });
681
687
  }
682
688
  continue;
@@ -1070,7 +1076,7 @@ export function appendA2AArtifactLinks(responseText, toolResults, options = {})
1070
1076
  const baseUrl = normalizeBaseUrl(options.baseUrl);
1071
1077
  const includeReferencedArtifacts = options.includeReferencedArtifacts ?? false;
1072
1078
  const finalize = (value) => options.includePersistedArtifactMarker
1073
- ? withPersistedArtifactMarker(value, toolResults)
1079
+ ? withPersistedArtifactMarker(value, toolResults, options.persistedArtifactSecret ?? process.env.A2A_SECRET)
1074
1080
  : value;
1075
1081
  const { documents, decks, dashboards, analyses, images, designShells, generatedDesigns, monitors, forms, } = collectArtifacts(toolResults);
1076
1082
  const generatedDesignIds = new Set(generatedDesigns.map((design) => design.id));