@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
|
@@ -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":"
|
|
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);
|