@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.
Files changed (149) hide show
  1. package/README.md +1 -1
  2. package/corpus/README.md +1 -1
  3. package/corpus/core/CHANGELOG.md +12 -0
  4. package/corpus/core/docs/content/locales/ar-SA/pure-agent-apps.mdx +2 -2
  5. package/corpus/core/docs/content/locales/de-DE/pure-agent-apps.mdx +2 -2
  6. package/corpus/core/docs/content/locales/es-ES/pure-agent-apps.mdx +2 -2
  7. package/corpus/core/docs/content/locales/fr-FR/pure-agent-apps.mdx +2 -2
  8. package/corpus/core/docs/content/locales/hi-IN/pure-agent-apps.mdx +2 -2
  9. package/corpus/core/docs/content/locales/ja-JP/pure-agent-apps.mdx +2 -2
  10. package/corpus/core/docs/content/locales/ko-KR/pure-agent-apps.mdx +2 -2
  11. package/corpus/core/docs/content/locales/pt-BR/pure-agent-apps.mdx +2 -2
  12. package/corpus/core/docs/content/locales/zh-CN/pure-agent-apps.mdx +2 -2
  13. package/corpus/core/docs/content/locales/zh-TW/pure-agent-apps.mdx +2 -2
  14. package/corpus/core/docs/content/pure-agent-apps.mdx +58 -14
  15. package/corpus/core/package.json +1 -1
  16. package/corpus/core/src/a2a/artifact-response.ts +255 -12
  17. package/corpus/core/src/agent/thread-data-builder.ts +84 -11
  18. package/corpus/core/src/integrations/a2a-continuation-processor.ts +99 -7
  19. package/corpus/core/src/integrations/adapters/discord.ts +3 -1
  20. package/corpus/core/src/integrations/adapters/email.ts +4 -1
  21. package/corpus/core/src/integrations/adapters/google-docs.ts +4 -1
  22. package/corpus/core/src/integrations/adapters/microsoft-teams.ts +3 -1
  23. package/corpus/core/src/integrations/adapters/slack.ts +38 -9
  24. package/corpus/core/src/integrations/adapters/telegram.ts +32 -10
  25. package/corpus/core/src/integrations/adapters/whatsapp.ts +5 -1
  26. package/corpus/core/src/integrations/index.ts +1 -0
  27. package/corpus/core/src/integrations/types.ts +9 -2
  28. package/corpus/core/src/integrations/webhook-handler.ts +69 -45
  29. package/corpus/core/src/server/agent-chat/action-filters-a2a.ts +1 -0
  30. package/corpus/templates/analytics/AGENTS.md +6 -0
  31. package/corpus/templates/analytics/README.md +1 -1
  32. package/corpus/templates/analytics/actions/data-source-status.ts +19 -1
  33. package/corpus/templates/analytics/app/pages/adhoc/sql-dashboard/index.tsx +5 -1
  34. package/corpus/templates/analytics/app/routes/_index.tsx +2 -2
  35. package/corpus/templates/analytics/changelog/2026-07-14-analytics-chat-guides-you-to-connect-a-data-source-with-a-di.md +6 -0
  36. package/corpus/templates/analytics/changelog/2026-07-14-analytics-landing-pages-now-clearly-position-the-app-as-an-o.md +6 -0
  37. package/corpus/templates/analytics/changelog/2026-07-15-daily-dashboard-emails-now-include-the-complete-dashboard-sc.md +6 -0
  38. package/corpus/templates/analytics/server/jobs/dashboard-report.ts +8 -1
  39. package/corpus/templates/analytics/server/lib/dashboard-report.ts +64 -12
  40. package/corpus/templates/analytics/server/lib/real-data-actions.ts +15 -3
  41. package/corpus/templates/analytics/server/plugins/agent-chat.ts +364 -4
  42. package/corpus/templates/clips/app/components/dictate/vocabulary-section.tsx +1 -0
  43. package/corpus/templates/clips/app/components/library/folder-tree.tsx +12 -9
  44. package/corpus/templates/clips/app/components/library/library-layout.tsx +19 -16
  45. package/corpus/templates/clips/app/routes/_app.spaces._index.tsx +13 -9
  46. package/corpus/templates/clips/changelog/2026-07-15-folders-and-sidebar-links-are-clickable-again-immediately-af.md +6 -0
  47. package/corpus/templates/clips/changelog/2026-07-15-hovering-a-truncated-folder-name-now-shows-the-full-name.md +6 -0
  48. package/corpus/templates/clips/changelog/2026-07-15-space-creation-controls-are-now-shown-only-to-organization-a.md +6 -0
  49. package/corpus/templates/clips/changelog/2026-07-15-vocabulary-terms-can-now-be-deleted-from-the-dictation-dicti.md +6 -0
  50. package/corpus/templates/clips/desktop/src/styles.css +23 -1
  51. package/corpus/templates/clips/desktop/src-tauri/src/whisper_speech.rs +49 -7
  52. package/corpus/templates/clips/server/lib/recordings.ts +5 -2
  53. package/corpus/templates/content/.agents/skills/content/SKILL.md +31 -0
  54. package/corpus/templates/content/.agents/skills/document-editing/SKILL.md +35 -21
  55. package/corpus/templates/content/AGENTS.md +29 -7
  56. package/corpus/templates/content/actions/_database-source-utils.ts +30 -16
  57. package/corpus/templates/content/actions/_database-utils.ts +15 -1
  58. package/corpus/templates/content/actions/_local-file-documents.ts +53 -1
  59. package/corpus/templates/content/actions/_property-utils.ts +7 -1
  60. package/corpus/templates/content/actions/configure-document-property.ts +11 -0
  61. package/corpus/templates/content/actions/create-content-database.ts +11 -0
  62. package/corpus/templates/content/actions/create-document.ts +9 -0
  63. package/corpus/templates/content/actions/create-inline-content-database.ts +3 -0
  64. package/corpus/templates/content/actions/export-content-source.ts +1 -0
  65. package/corpus/templates/content/actions/get-content-database.ts +13 -1
  66. package/corpus/templates/content/actions/get-document.ts +12 -2
  67. package/corpus/templates/content/actions/import-content-source.ts +6 -0
  68. package/corpus/templates/content/actions/list-documents.ts +3 -0
  69. package/corpus/templates/content/actions/pull-document.ts +2 -0
  70. package/corpus/templates/content/actions/search-documents.ts +5 -1
  71. package/corpus/templates/content/actions/update-document.ts +16 -1
  72. package/corpus/templates/content/actions/view-screen.ts +15 -2
  73. package/corpus/templates/content/app/components/editor/DescriptionField.tsx +121 -0
  74. package/corpus/templates/content/app/components/editor/DocumentEditor.tsx +14 -0
  75. package/corpus/templates/content/app/components/editor/DocumentProperties.tsx +223 -34
  76. package/corpus/templates/content/app/components/editor/database/DatabaseView.tsx +56 -8
  77. package/corpus/templates/content/app/i18n/zh-TW.ts +5 -0
  78. package/corpus/templates/content/app/i18n-data.ts +52 -0
  79. package/corpus/templates/content/changelog/2026-07-14-pages-and-databases-can-describe-themselves-with-guidance-fo.md +6 -0
  80. package/corpus/templates/content/changelog/2026-07-14-slack-follow-ups-now-retain-created-content-identity-and-per.md +6 -0
  81. package/corpus/templates/content/server/db/schema.ts +4 -0
  82. package/corpus/templates/content/server/lib/document-context.ts +76 -0
  83. package/corpus/templates/content/server/lib/public-documents.ts +3 -0
  84. package/corpus/templates/content/server/plugins/db.ts +6 -0
  85. package/corpus/templates/content/server/routes/api/document-agent-context.json.get.ts +5 -0
  86. package/corpus/templates/content/shared/api.ts +17 -0
  87. package/corpus/templates/content/shared/content-source.ts +7 -0
  88. package/corpus/templates/content/shared/properties.ts +2 -0
  89. package/dist/a2a/artifact-response.d.ts +15 -0
  90. package/dist/a2a/artifact-response.d.ts.map +1 -1
  91. package/dist/a2a/artifact-response.js +204 -5
  92. package/dist/a2a/artifact-response.js.map +1 -1
  93. package/dist/agent/thread-data-builder.d.ts +6 -0
  94. package/dist/agent/thread-data-builder.d.ts.map +1 -1
  95. package/dist/agent/thread-data-builder.js +71 -8
  96. package/dist/agent/thread-data-builder.js.map +1 -1
  97. package/dist/collab/struct-routes.d.ts +1 -1
  98. package/dist/integrations/a2a-continuation-processor.d.ts.map +1 -1
  99. package/dist/integrations/a2a-continuation-processor.js +72 -6
  100. package/dist/integrations/a2a-continuation-processor.js.map +1 -1
  101. package/dist/integrations/adapters/discord.d.ts.map +1 -1
  102. package/dist/integrations/adapters/discord.js +1 -0
  103. package/dist/integrations/adapters/discord.js.map +1 -1
  104. package/dist/integrations/adapters/email.d.ts.map +1 -1
  105. package/dist/integrations/adapters/email.js +2 -0
  106. package/dist/integrations/adapters/email.js.map +1 -1
  107. package/dist/integrations/adapters/google-docs.d.ts.map +1 -1
  108. package/dist/integrations/adapters/google-docs.js +2 -0
  109. package/dist/integrations/adapters/google-docs.js.map +1 -1
  110. package/dist/integrations/adapters/microsoft-teams.d.ts.map +1 -1
  111. package/dist/integrations/adapters/microsoft-teams.js +1 -0
  112. package/dist/integrations/adapters/microsoft-teams.js.map +1 -1
  113. package/dist/integrations/adapters/slack.d.ts.map +1 -1
  114. package/dist/integrations/adapters/slack.js +21 -6
  115. package/dist/integrations/adapters/slack.js.map +1 -1
  116. package/dist/integrations/adapters/telegram.d.ts.map +1 -1
  117. package/dist/integrations/adapters/telegram.js +11 -2
  118. package/dist/integrations/adapters/telegram.js.map +1 -1
  119. package/dist/integrations/adapters/whatsapp.d.ts.map +1 -1
  120. package/dist/integrations/adapters/whatsapp.js +3 -0
  121. package/dist/integrations/adapters/whatsapp.js.map +1 -1
  122. package/dist/integrations/index.d.ts +1 -1
  123. package/dist/integrations/index.d.ts.map +1 -1
  124. package/dist/integrations/index.js.map +1 -1
  125. package/dist/integrations/types.d.ts +8 -2
  126. package/dist/integrations/types.d.ts.map +1 -1
  127. package/dist/integrations/types.js.map +1 -1
  128. package/dist/integrations/webhook-handler.d.ts.map +1 -1
  129. package/dist/integrations/webhook-handler.js +40 -43
  130. package/dist/integrations/webhook-handler.js.map +1 -1
  131. package/dist/progress/routes.d.ts +1 -1
  132. package/dist/provider-api/corpus-jobs.d.ts +2 -2
  133. package/dist/resources/handlers.d.ts +2 -2
  134. package/dist/server/agent-chat/action-filters-a2a.d.ts.map +1 -1
  135. package/dist/server/agent-chat/action-filters-a2a.js +1 -0
  136. package/dist/server/agent-chat/action-filters-a2a.js.map +1 -1
  137. package/dist/server/agent-engine-api-key-route.d.ts +1 -1
  138. package/docs/content/locales/ar-SA/pure-agent-apps.mdx +2 -2
  139. package/docs/content/locales/de-DE/pure-agent-apps.mdx +2 -2
  140. package/docs/content/locales/es-ES/pure-agent-apps.mdx +2 -2
  141. package/docs/content/locales/fr-FR/pure-agent-apps.mdx +2 -2
  142. package/docs/content/locales/hi-IN/pure-agent-apps.mdx +2 -2
  143. package/docs/content/locales/ja-JP/pure-agent-apps.mdx +2 -2
  144. package/docs/content/locales/ko-KR/pure-agent-apps.mdx +2 -2
  145. package/docs/content/locales/pt-BR/pure-agent-apps.mdx +2 -2
  146. package/docs/content/locales/zh-CN/pure-agent-apps.mdx +2 -2
  147. package/docs/content/locales/zh-TW/pure-agent-apps.mdx +2 -2
  148. package/docs/content/pure-agent-apps.mdx +58 -14
  149. package/package.json +3 -3
@@ -12,6 +12,9 @@ export const documents = table("documents", {
12
12
  parentId: text("parent_id"),
13
13
  title: text("title").notNull().default("Untitled"),
14
14
  content: text("content").notNull().default(""),
15
+ // Stable semantic guidance for this page. Ancestry is computed at read time;
16
+ // never copy a parent's description here.
17
+ description: text("description").notNull().default(""),
15
18
  icon: text("icon"),
16
19
  position: integer("position").notNull().default(0),
17
20
  isFavorite: integer("is_favorite").notNull().default(0),
@@ -114,6 +117,7 @@ export const documentPropertyDefinitions = table(
114
117
  databaseId: text("database_id"),
115
118
  name: text("name").notNull(),
116
119
  type: text("type").notNull(),
120
+ description: text("description").notNull().default(""),
117
121
  visibility: text("visibility").notNull().default("always_show"),
118
122
  optionsJson: text("options_json").notNull().default("{}"),
119
123
  position: integer("position").notNull().default(0),
@@ -0,0 +1,76 @@
1
+ import { resolveAccess } from "@agent-native/core/sharing";
2
+ import { and, eq, isNull } from "drizzle-orm";
3
+
4
+ import { getDb, schema } from "../db/index.js";
5
+
6
+ export type DocumentContextPathEntry = {
7
+ id: string;
8
+ kind: "page" | "database";
9
+ title: string;
10
+ description: string;
11
+ };
12
+
13
+ /** Focused-read context only: owned descriptions stay on their objects; this
14
+ * assembles the live path without copying ancestor prose into descendants. */
15
+ export async function getDocumentContextPath(
16
+ document: Pick<typeof schema.documents.$inferSelect, "id" | "parentId">,
17
+ ): Promise<DocumentContextPathEntry[]> {
18
+ const db = getDb();
19
+ const path: DocumentContextPathEntry[] = [];
20
+ const seen = new Set<string>([document.id]);
21
+ let parentId = document.parentId;
22
+ while (parentId && !seen.has(parentId)) {
23
+ seen.add(parentId);
24
+ const parentAccess = await resolveAccess("document", parentId);
25
+ // A child share must not disclose prose from an inaccessible ancestor.
26
+ if (!parentAccess) break;
27
+ const parent = parentAccess.resource;
28
+ const [database] = await db
29
+ .select()
30
+ .from(schema.contentDatabases)
31
+ .where(
32
+ and(
33
+ eq(schema.contentDatabases.documentId, parent.id),
34
+ isNull(schema.contentDatabases.deletedAt),
35
+ ),
36
+ );
37
+ path.unshift({
38
+ id: database?.id ?? parent.id,
39
+ kind: database ? "database" : "page",
40
+ title: database?.title ?? parent.title,
41
+ description: parent.description,
42
+ });
43
+ parentId = parent.parentId;
44
+ }
45
+
46
+ const [membership] = await db
47
+ .select({ database: schema.contentDatabases })
48
+ .from(schema.contentDatabaseItems)
49
+ .innerJoin(
50
+ schema.contentDatabases,
51
+ eq(schema.contentDatabases.id, schema.contentDatabaseItems.databaseId),
52
+ )
53
+ .where(
54
+ and(
55
+ eq(schema.contentDatabaseItems.documentId, document.id),
56
+ isNull(schema.contentDatabases.deletedAt),
57
+ ),
58
+ );
59
+ if (
60
+ membership &&
61
+ !path.some((entry) => entry.id === membership.database.id)
62
+ ) {
63
+ const databaseDocumentAccess = await resolveAccess(
64
+ "document",
65
+ membership.database.documentId,
66
+ );
67
+ if (!databaseDocumentAccess) return path;
68
+ path.push({
69
+ id: membership.database.id,
70
+ kind: "database",
71
+ title: membership.database.title,
72
+ description: databaseDocumentAccess.resource.description,
73
+ });
74
+ }
75
+ return path;
76
+ }
@@ -9,6 +9,7 @@ export const PUBLIC_DOCUMENT_CONTEXT_EXCERPT_CHARS = 2_400;
9
9
  type PublicDocumentPromptInput = {
10
10
  id: string;
11
11
  title: string;
12
+ description?: string;
12
13
  content: string;
13
14
  updatedAt: string | Date;
14
15
  };
@@ -44,6 +45,7 @@ ${fullDocumentGuidance}
44
45
 
45
46
  Document ID: ${doc.id}
46
47
  Title: ${doc.title}
48
+ Description: ${doc.description || "(none)"}
47
49
  Updated at: ${doc.updatedAt}
48
50
 
49
51
  Markdown excerpt:
@@ -92,6 +94,7 @@ async function getPublicDocumentForEvent(event: H3Event) {
92
94
  .select({
93
95
  id: documents.id,
94
96
  title: documents.title,
97
+ description: documents.description,
95
98
  content: documents.content,
96
99
  updatedAt: documents.updatedAt,
97
100
  visibility: documents.visibility,
@@ -635,6 +635,12 @@ const runContentMigrations = runMigrations(
635
635
  sql: `CREATE INDEX IF NOT EXISTS document_property_values_document_property_idx ON document_property_values (document_id, property_id);
636
636
  CREATE INDEX IF NOT EXISTS document_property_values_property_document_idx ON document_property_values (property_id, document_id)`,
637
637
  },
638
+ {
639
+ version: 65,
640
+ name: "content-owned-descriptions",
641
+ sql: `ALTER TABLE documents ADD COLUMN IF NOT EXISTS description TEXT NOT NULL DEFAULT '';
642
+ ALTER TABLE document_property_definitions ADD COLUMN IF NOT EXISTS description TEXT NOT NULL DEFAULT ''`,
643
+ },
638
644
  ],
639
645
  { table: "content_migrations" },
640
646
  );
@@ -16,6 +16,7 @@ import {
16
16
  DOCUMENT_AGENT_RESOURCE_KIND,
17
17
  } from "../../../shared/agent-readable.js";
18
18
  import { getDb, schema } from "../../db/index.js";
19
+ import { getDocumentContextPath } from "../../lib/document-context.js";
19
20
 
20
21
  function queryString(value: unknown): string {
21
22
  if (typeof value === "string") return value;
@@ -45,7 +46,9 @@ export default defineEventHandler(async (event) => {
45
46
  const [document] = await db
46
47
  .select({
47
48
  id: schema.documents.id,
49
+ parentId: schema.documents.parentId,
48
50
  title: schema.documents.title,
51
+ description: schema.documents.description,
49
52
  content: schema.documents.content,
50
53
  icon: schema.documents.icon,
51
54
  visibility: schema.documents.visibility,
@@ -77,11 +80,13 @@ export default defineEventHandler(async (event) => {
77
80
  resourceType: "document",
78
81
  id: document.id,
79
82
  title: document.title,
83
+ description: document.description,
80
84
  icon: document.icon,
81
85
  content: document.content,
82
86
  visibility: document.visibility,
83
87
  createdAt: document.createdAt,
84
88
  updatedAt: document.updatedAt,
89
+ contextPath: await getDocumentContextPath(document),
85
90
  url: buildContentPublicDocumentUrl(document.id, {
86
91
  basePath: getConfiguredAppBasePath(),
87
92
  token: tokenAccess ? token : null,
@@ -8,11 +8,19 @@ import type {
8
8
 
9
9
  export type DocumentAccessRole = "owner" | "viewer" | "editor" | "admin";
10
10
 
11
+ export interface ContentContextPathEntry {
12
+ id: string;
13
+ kind: "page" | "database";
14
+ title: string;
15
+ description: string;
16
+ }
17
+
11
18
  export interface Document {
12
19
  id: string;
13
20
  parentId: string | null;
14
21
  title: string;
15
22
  content: string;
23
+ description?: string;
16
24
  icon: string | null;
17
25
  position: number;
18
26
  isFavorite: boolean;
@@ -27,6 +35,7 @@ export interface Document {
27
35
  properties?: DocumentProperty[];
28
36
  database?: ContentDatabase;
29
37
  databaseMembership?: ContentDatabaseMembership;
38
+ contextPath?: ContentContextPathEntry[];
30
39
  createdAt: string;
31
40
  updatedAt: string;
32
41
  }
@@ -90,12 +99,14 @@ export interface DocumentCreateRequest {
90
99
  title?: string;
91
100
  parentId?: string | null;
92
101
  content?: string;
102
+ description?: string;
93
103
  icon?: string;
94
104
  }
95
105
 
96
106
  export interface DocumentUpdateRequest {
97
107
  title?: string;
98
108
  content?: string;
109
+ description?: string;
99
110
  icon?: string | null;
100
111
  isFavorite?: boolean;
101
112
  loadedUpdatedAt?: string;
@@ -158,6 +169,7 @@ export interface DocumentPropertyDefinition {
158
169
  databaseId: string | null;
159
170
  name: string;
160
171
  type: DocumentPropertyType;
172
+ description?: string;
161
173
  visibility: DocumentPropertyVisibility;
162
174
  options: DocumentPropertyOptions;
163
175
  position: number;
@@ -182,6 +194,7 @@ export interface ConfigureDocumentPropertyRequest {
182
194
  documentId: string;
183
195
  name: string;
184
196
  type: DocumentPropertyType;
197
+ description?: string;
185
198
  visibility?: DocumentPropertyVisibility;
186
199
  options?: DocumentPropertyOptions;
187
200
  }
@@ -213,6 +226,7 @@ export interface ContentDatabase {
213
226
  id: string;
214
227
  documentId: string;
215
228
  title: string;
229
+ description?: string;
216
230
  viewConfig: ContentDatabaseViewConfig;
217
231
  createdAt: string;
218
232
  updatedAt: string;
@@ -692,6 +706,7 @@ export interface ContentDatabaseResponse {
692
706
  properties: DocumentProperty[];
693
707
  items: ContentDatabaseItem[];
694
708
  source: ContentDatabaseSource | null;
709
+ contextPath?: ContentContextPathEntry[];
695
710
  // All attached sources (NEXT). `source` stays as `sources[0] ?? null` for
696
711
  // back-compat; multi-source consumers read `sources`.
697
712
  sources?: ContentDatabaseSource[];
@@ -737,11 +752,13 @@ export interface CreateDatabaseRequest {
737
752
  documentId?: string;
738
753
  parentId?: string | null;
739
754
  title?: string;
755
+ description?: string;
740
756
  }
741
757
 
742
758
  export interface CreateInlineDatabaseRequest {
743
759
  hostDocumentId: string;
744
760
  title?: string;
761
+ description?: string;
745
762
  }
746
763
 
747
764
  export interface CreateInlineDatabaseResponse {
@@ -6,6 +6,7 @@ export interface ContentSourceDocument {
6
6
  parentId: string | null;
7
7
  title: string;
8
8
  content: string;
9
+ description?: string;
9
10
  icon: string | null;
10
11
  position: number;
11
12
  isFavorite: boolean;
@@ -31,6 +32,7 @@ export interface ParsedContentSourceFile {
31
32
  parentId?: string | null;
32
33
  title: string;
33
34
  content: string;
35
+ description?: string;
34
36
  icon?: string | null;
35
37
  position?: number;
36
38
  isFavorite?: boolean;
@@ -152,6 +154,7 @@ export function serializeContentSourceDocument(
152
154
  const frontmatter = [
153
155
  frontmatterLine("id", doc.id),
154
156
  frontmatterLine("title", doc.title || "Untitled"),
157
+ frontmatterLine("description", doc.description),
155
158
  frontmatterLine("parentId", doc.parentId),
156
159
  frontmatterLine("icon", doc.icon),
157
160
  frontmatterLine("position", doc.position),
@@ -196,6 +199,10 @@ export function parseContentSourceFile(
196
199
  parentId,
197
200
  title,
198
201
  content,
202
+ description:
203
+ typeof metadata.description === "string"
204
+ ? metadata.description
205
+ : undefined,
199
206
  icon: hasOwn(metadata, "icon")
200
207
  ? typeof metadata.icon === "string"
201
208
  ? metadata.icon
@@ -89,6 +89,8 @@ export interface DocumentPropertyOption {
89
89
  id: string;
90
90
  name: string;
91
91
  color: DocumentPropertyOptionColor;
92
+ /** Stable guidance for when this select/status value should be chosen. */
93
+ description?: string;
92
94
  }
93
95
 
94
96
  export interface DocumentPropertyOptions {
@@ -5,7 +5,22 @@ export interface A2AToolResultSummary {
5
5
  export interface A2AArtifactResponseOptions {
6
6
  baseUrl?: string;
7
7
  includeReferencedArtifacts?: boolean;
8
+ includePersistedArtifactMarker?: boolean;
8
9
  }
10
+ export interface A2AArtifactIdentity {
11
+ resourceType: "document" | "deck" | "dashboard" | "analysis" | "image" | "design" | "monitor" | "form";
12
+ id: string;
13
+ sourceAction: string;
14
+ titleAtAction?: string;
15
+ url?: string;
16
+ }
17
+ export declare function stripA2APersistedArtifactMarkers(text: string): string;
18
+ /**
19
+ * Extract a compact, verified identity ledger from successful artifact tools.
20
+ * The ledger deliberately excludes raw tool results so it is safe to retain in
21
+ * long-lived thread context and stable even when a resource is later renamed.
22
+ */
23
+ export declare function extractA2AArtifactIdentities(results: A2AToolResultSummary[]): A2AArtifactIdentity[];
9
24
  export declare function appendA2AArtifactLinks(responseText: string, toolResults: A2AToolResultSummary[], options?: A2AArtifactResponseOptions): string;
10
25
  export declare function buildA2ARecoverableArtifactMessage(toolResults: A2AToolResultSummary[], options?: A2AArtifactResponseOptions): string | null;
11
26
  //# sourceMappingURL=artifact-response.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"artifact-response.d.ts","sourceRoot":"","sources":["../../src/a2a/artifact-response.ts"],"names":[],"mappings":"AAAA,MAAM,WAAW,oBAAoB;IACnC,IAAI,EAAE,MAAM,CAAC;IACb,MAAM,EAAE,MAAM,CAAC;CAChB;AAED,MAAM,WAAW,0BAA0B;IACzC,OAAO,CAAC,EAAE,MAAM,CAAC;IACjB,0BAA0B,CAAC,EAAE,OAAO,CAAC;CACtC;AA6lCD,wBAAgB,sBAAsB,CACpC,YAAY,EAAE,MAAM,EACpB,WAAW,EAAE,oBAAoB,EAAE,EACnC,OAAO,GAAE,0BAA+B,GACvC,MAAM,CAsIR;AAED,wBAAgB,kCAAkC,CAChD,WAAW,EAAE,oBAAoB,EAAE,EACnC,OAAO,GAAE,0BAA+B,GACvC,MAAM,GAAG,IAAI,CA8Bf"}
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;CAChB;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;AAgGD,wBAAgB,gCAAgC,CAAC,IAAI,EAAE,MAAM,GAAG,MAAM,CAErE;AA8tBD;;;;GAIG;AACH,wBAAgB,4BAA4B,CAC1C,OAAO,EAAE,oBAAoB,EAAE,GAC9B,mBAAmB,EAAE,CAmGvB;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"}
@@ -1,3 +1,88 @@
1
+ import { createHmac, timingSafeEqual } from "node:crypto";
2
+ const ARTIFACT_IDENTITY_WRITE_TOOLS = new Set([
3
+ "save-monitor",
4
+ "create-form",
5
+ "submit-content-database-form",
6
+ "add-database-item",
7
+ "create-document",
8
+ "update-document",
9
+ "create-deck",
10
+ "duplicate-deck",
11
+ "add-slide",
12
+ "update-dashboard",
13
+ "rename-dashboard",
14
+ "save-analysis",
15
+ "generate-image",
16
+ "edit-image",
17
+ "refine-image",
18
+ "restyle-image",
19
+ "save-generated-image",
20
+ "save-generated-asset",
21
+ "export-image",
22
+ "export-asset",
23
+ "generate-image-batch",
24
+ "create-design",
25
+ "generate-design",
26
+ "create-file",
27
+ "duplicate-design",
28
+ ]);
29
+ const PERSISTED_ARTIFACT_MARKER = "agent-native:persisted-artifacts=";
30
+ const PERSISTED_ARTIFACT_MARKER_PATTERN = /\s*<!--\s*agent-native:persisted-artifacts=[A-Za-z0-9_-]+\.[a-f0-9]{64}\s*-->/g;
31
+ const ARTIFACT_RESOURCE_TYPES = new Set([
32
+ "document",
33
+ "deck",
34
+ "dashboard",
35
+ "analysis",
36
+ "image",
37
+ "design",
38
+ "monitor",
39
+ "form",
40
+ ]);
41
+ function persistedArtifactIdentitiesFromMarker(result) {
42
+ const secret = process.env.A2A_SECRET;
43
+ if (!secret)
44
+ return [];
45
+ const match = result.match(/<!--\s*agent-native:persisted-artifacts=([A-Za-z0-9_-]+)\.([a-f0-9]{64})\s*-->/);
46
+ if (!match)
47
+ return [];
48
+ try {
49
+ const payload = match[1];
50
+ const expected = createHmac("sha256", secret).update(payload).digest();
51
+ const supplied = Buffer.from(match[2], "hex");
52
+ if (supplied.length !== expected.length ||
53
+ !timingSafeEqual(supplied, expected)) {
54
+ return [];
55
+ }
56
+ const parsed = JSON.parse(Buffer.from(payload, "base64url").toString());
57
+ if (!Array.isArray(parsed))
58
+ return [];
59
+ return parsed
60
+ .slice(0, 12)
61
+ .filter((identity) => {
62
+ const item = asRecord(identity);
63
+ return (!!item &&
64
+ ARTIFACT_RESOURCE_TYPES.has(item.resourceType) &&
65
+ typeof item.id === "string" &&
66
+ typeof item.sourceAction === "string");
67
+ });
68
+ }
69
+ catch {
70
+ return [];
71
+ }
72
+ }
73
+ function withPersistedArtifactMarker(text, toolResults) {
74
+ const identities = extractA2AArtifactIdentities(toolResults).slice(0, 12);
75
+ const secret = process.env.A2A_SECRET;
76
+ if (identities.length === 0 || !secret)
77
+ return text;
78
+ const payload = Buffer.from(JSON.stringify(identities)).toString("base64url");
79
+ const signature = createHmac("sha256", secret).update(payload).digest("hex");
80
+ const marker = `<!-- ${PERSISTED_ARTIFACT_MARKER}${payload}.${signature} -->`;
81
+ return text ? `${text}\n\n${marker}` : marker;
82
+ }
83
+ export function stripA2APersistedArtifactMarkers(text) {
84
+ return text.replace(PERSISTED_ARTIFACT_MARKER_PATTERN, "").trim();
85
+ }
1
86
  function asRecord(value) {
2
87
  return value && typeof value === "object" && !Array.isArray(value)
3
88
  ? value
@@ -450,13 +535,22 @@ function collectArtifacts(results) {
450
535
  continue;
451
536
  }
452
537
  if (toolResult.tool === "generate-image" ||
538
+ toolResult.tool === "edit-image" ||
453
539
  toolResult.tool === "refine-image" ||
540
+ toolResult.tool === "restyle-image" ||
454
541
  toolResult.tool === "get-asset" ||
455
542
  toolResult.tool === "save-generated-image" ||
543
+ toolResult.tool === "save-generated-asset" ||
456
544
  toolResult.tool === "export-image") {
457
545
  addImageArtifact(images, parsed);
458
546
  continue;
459
547
  }
548
+ if (toolResult.tool === "export-asset") {
549
+ if (stringValue(parsed.artifactType) === "image") {
550
+ addImageArtifact(images, parsed);
551
+ }
552
+ continue;
553
+ }
460
554
  if (toolResult.tool === "generate-image-batch") {
461
555
  if (Array.isArray(parsed.images)) {
462
556
  for (const item of parsed.images) {
@@ -553,6 +647,107 @@ function collectArtifacts(results) {
553
647
  forms: [...forms.values()],
554
648
  };
555
649
  }
650
+ /**
651
+ * Extract a compact, verified identity ledger from successful artifact tools.
652
+ * The ledger deliberately excludes raw tool results so it is safe to retain in
653
+ * long-lived thread context and stable even when a resource is later renamed.
654
+ */
655
+ export function extractA2AArtifactIdentities(results) {
656
+ const identities = new Map();
657
+ const remember = (identity) => {
658
+ identities.set(`${identity.resourceType}:${identity.id}`, identity);
659
+ };
660
+ for (const result of results) {
661
+ if (result.tool === "call-agent") {
662
+ for (const identity of persistedArtifactIdentitiesFromMarker(result.result)) {
663
+ remember({ ...identity, sourceAction: "call-agent" });
664
+ }
665
+ continue;
666
+ }
667
+ if (!ARTIFACT_IDENTITY_WRITE_TOOLS.has(result.tool))
668
+ continue;
669
+ const artifacts = collectArtifacts([result]);
670
+ for (const document of artifacts.documents) {
671
+ remember({
672
+ resourceType: "document",
673
+ id: document.id,
674
+ sourceAction: result.tool,
675
+ titleAtAction: document.title,
676
+ url: document.url,
677
+ });
678
+ }
679
+ for (const deck of artifacts.decks) {
680
+ remember({
681
+ resourceType: "deck",
682
+ id: deck.id,
683
+ sourceAction: result.tool,
684
+ url: deck.url,
685
+ });
686
+ }
687
+ for (const dashboard of artifacts.dashboards) {
688
+ remember({
689
+ resourceType: "dashboard",
690
+ id: dashboard.id,
691
+ sourceAction: result.tool,
692
+ titleAtAction: dashboard.title,
693
+ url: dashboard.url,
694
+ });
695
+ }
696
+ for (const analysis of artifacts.analyses) {
697
+ remember({
698
+ resourceType: "analysis",
699
+ id: analysis.id,
700
+ sourceAction: result.tool,
701
+ titleAtAction: analysis.title,
702
+ url: analysis.url,
703
+ });
704
+ }
705
+ for (const image of artifacts.images) {
706
+ remember({
707
+ resourceType: "image",
708
+ id: image.id,
709
+ sourceAction: result.tool,
710
+ titleAtAction: image.title,
711
+ url: image.url,
712
+ });
713
+ }
714
+ for (const design of artifacts.designShells) {
715
+ remember({
716
+ resourceType: "design",
717
+ id: design.id,
718
+ sourceAction: result.tool,
719
+ titleAtAction: design.title,
720
+ });
721
+ }
722
+ for (const design of artifacts.generatedDesigns) {
723
+ remember({
724
+ resourceType: "design",
725
+ id: design.id,
726
+ sourceAction: result.tool,
727
+ url: design.url,
728
+ });
729
+ }
730
+ for (const monitor of artifacts.monitors) {
731
+ remember({
732
+ resourceType: "monitor",
733
+ id: monitor.id,
734
+ sourceAction: result.tool,
735
+ titleAtAction: monitor.name,
736
+ url: monitor.url,
737
+ });
738
+ }
739
+ for (const form of artifacts.forms) {
740
+ remember({
741
+ resourceType: "form",
742
+ id: form.id,
743
+ sourceAction: result.tool,
744
+ titleAtAction: form.title,
745
+ url: form.url,
746
+ });
747
+ }
748
+ }
749
+ return [...identities.values()];
750
+ }
556
751
  function parseDownstreamArtifactBlock(result) {
557
752
  const artifacts = [];
558
753
  for (const line of downstreamArtifactLines(result)) {
@@ -857,6 +1052,9 @@ function formatUnverifiedArtifactMessage(refs, documents, decks, dashboards, ana
857
1052
  export function appendA2AArtifactLinks(responseText, toolResults, options = {}) {
858
1053
  const baseUrl = normalizeBaseUrl(options.baseUrl);
859
1054
  const includeReferencedArtifacts = options.includeReferencedArtifacts ?? false;
1055
+ const finalize = (value) => options.includePersistedArtifactMarker
1056
+ ? withPersistedArtifactMarker(value, toolResults)
1057
+ : value;
860
1058
  const { documents, decks, dashboards, analyses, images, designShells, generatedDesigns, monitors, forms, } = collectArtifacts(toolResults);
861
1059
  const generatedDesignIds = new Set(generatedDesigns.map((design) => design.id));
862
1060
  const incompleteShells = designShells.filter((shell) => !generatedDesignIds.has(shell.id));
@@ -866,11 +1064,11 @@ export function appendA2AArtifactLinks(responseText, toolResults, options = {})
866
1064
  !responseAlreadyWarnsIncompleteDesign(text) &&
867
1065
  (incompleteShells.some((shell) => responseMentionsDesignShell(text, shell)) ||
868
1066
  /\b(?:done|created|ready|here(?:'s| is)|complete|finished)\b/i.test(text))) {
869
- return formatIncompleteDesignMessage(incompleteShells);
1067
+ return finalize(formatIncompleteDesignMessage(incompleteShells));
870
1068
  }
871
1069
  const unverifiedRefs = findUnverifiedArtifactReferences(text, baseUrl, documents, decks, dashboards, analyses, images, generatedDesigns);
872
1070
  if (unverifiedRefs.length > 0) {
873
- return formatUnverifiedArtifactMessage(unverifiedRefs, documents, decks, dashboards, analyses, images, generatedDesigns, baseUrl);
1071
+ return finalize(formatUnverifiedArtifactMessage(unverifiedRefs, documents, decks, dashboards, analyses, images, generatedDesigns, baseUrl));
874
1072
  }
875
1073
  const missingLines = [];
876
1074
  for (const document of documents) {
@@ -927,10 +1125,11 @@ export function appendA2AArtifactLinks(responseText, toolResults, options = {})
927
1125
  missingLines.push(formatFormLine(form));
928
1126
  }
929
1127
  }
930
- if (missingLines.length === 0)
931
- return text;
1128
+ if (missingLines.length === 0) {
1129
+ return finalize(text);
1130
+ }
932
1131
  const artifactBlock = `Artifacts:\n${missingLines.join("\n")}`;
933
- return text ? `${text}\n\n${artifactBlock}` : artifactBlock;
1132
+ return finalize(text ? `${text}\n\n${artifactBlock}` : artifactBlock);
934
1133
  }
935
1134
  export function buildA2ARecoverableArtifactMessage(toolResults, options = {}) {
936
1135
  const baseUrl = normalizeBaseUrl(options.baseUrl);