@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
|
@@ -6,6 +6,7 @@ import type {
|
|
|
6
6
|
IncomingMessage,
|
|
7
7
|
OutgoingMessage,
|
|
8
8
|
IntegrationStatus,
|
|
9
|
+
PlatformDeliveryReceipt,
|
|
9
10
|
} from "../types.js";
|
|
10
11
|
|
|
11
12
|
/** Google Docs comment replies have no formal length limit but keep it reasonable */
|
|
@@ -303,7 +304,7 @@ export function googleDocsAdapter(): PlatformAdapter {
|
|
|
303
304
|
async sendResponse(
|
|
304
305
|
message: OutgoingMessage,
|
|
305
306
|
context: IncomingMessage,
|
|
306
|
-
): Promise<void> {
|
|
307
|
+
): Promise<void | PlatformDeliveryReceipt> {
|
|
307
308
|
const fileId = context.platformContext.fileId as string;
|
|
308
309
|
const commentId = context.platformContext.commentId as string;
|
|
309
310
|
|
|
@@ -319,8 +320,10 @@ export function googleDocsAdapter(): PlatformAdapter {
|
|
|
319
320
|
await replyToComment(fileId, commentId, chunk, accessToken);
|
|
320
321
|
} catch (err) {
|
|
321
322
|
console.error("[google-docs] Failed to send reply:", err);
|
|
323
|
+
throw err;
|
|
322
324
|
}
|
|
323
325
|
}
|
|
326
|
+
return { status: "delivered" };
|
|
324
327
|
},
|
|
325
328
|
|
|
326
329
|
formatAgentResponse(text: string): OutgoingMessage {
|
|
@@ -8,6 +8,7 @@ import type {
|
|
|
8
8
|
IntegrationStatus,
|
|
9
9
|
OutgoingMessage,
|
|
10
10
|
PlatformAdapter,
|
|
11
|
+
PlatformDeliveryReceipt,
|
|
11
12
|
} from "../types.js";
|
|
12
13
|
|
|
13
14
|
const BOT_FRAMEWORK_SCOPE = "https://api.botframework.com/.default";
|
|
@@ -201,7 +202,7 @@ export function microsoftTeamsAdapter(): PlatformAdapter {
|
|
|
201
202
|
async sendResponse(
|
|
202
203
|
message: OutgoingMessage,
|
|
203
204
|
context: IncomingMessage,
|
|
204
|
-
): Promise<void> {
|
|
205
|
+
): Promise<void | PlatformDeliveryReceipt> {
|
|
205
206
|
const serviceUrl = normalizeServiceUrl(
|
|
206
207
|
context.platformContext.serviceUrl,
|
|
207
208
|
);
|
|
@@ -242,6 +243,7 @@ export function microsoftTeamsAdapter(): PlatformAdapter {
|
|
|
242
243
|
`Microsoft Teams reply failed (HTTP ${response.status})`,
|
|
243
244
|
);
|
|
244
245
|
}
|
|
246
|
+
return { status: "delivered" };
|
|
245
247
|
},
|
|
246
248
|
|
|
247
249
|
formatAgentResponse(text: string): OutgoingMessage {
|
|
@@ -23,6 +23,7 @@ import type {
|
|
|
23
23
|
OutboundTarget,
|
|
24
24
|
PlatformRunProgress,
|
|
25
25
|
PlatformRunProgressRef,
|
|
26
|
+
PlatformDeliveryReceipt,
|
|
26
27
|
IntegrationContextMessage,
|
|
27
28
|
IntegrationFileReference,
|
|
28
29
|
} from "../types.js";
|
|
@@ -431,11 +432,12 @@ export function slackAdapter(
|
|
|
431
432
|
message: OutgoingMessage,
|
|
432
433
|
context: IncomingMessage,
|
|
433
434
|
opts?: { placeholderRef?: string },
|
|
434
|
-
): Promise<void> {
|
|
435
|
+
): Promise<void | PlatformDeliveryReceipt> {
|
|
435
436
|
const token = await resolveBotToken(context);
|
|
436
437
|
if (!token) {
|
|
437
|
-
|
|
438
|
-
|
|
438
|
+
throw new Error(
|
|
439
|
+
"[slack] Cannot deliver response: no Slack bot token is configured",
|
|
440
|
+
);
|
|
439
441
|
}
|
|
440
442
|
|
|
441
443
|
const channelId = context.platformContext.channelId as string;
|
|
@@ -458,6 +460,7 @@ export function slackAdapter(
|
|
|
458
460
|
return;
|
|
459
461
|
}
|
|
460
462
|
const restChunks = chunks.slice(1);
|
|
463
|
+
const messageRefs: string[] = [];
|
|
461
464
|
|
|
462
465
|
const finalBlocks =
|
|
463
466
|
blocks ??
|
|
@@ -489,14 +492,29 @@ export function slackAdapter(
|
|
|
489
492
|
const data = (await res.json()) as {
|
|
490
493
|
ok: boolean;
|
|
491
494
|
error?: string;
|
|
495
|
+
ts?: string;
|
|
492
496
|
};
|
|
493
497
|
if (!data.ok) {
|
|
494
498
|
console.error("[slack] chat.update error:", data.error);
|
|
495
499
|
// Fall back to a fresh post so the user still sees a reply
|
|
496
|
-
await postFresh(
|
|
500
|
+
const postedTs = await postFresh(
|
|
501
|
+
token,
|
|
502
|
+
channelId,
|
|
503
|
+
threadTs,
|
|
504
|
+
baseBody,
|
|
505
|
+
);
|
|
506
|
+
if (postedTs) messageRefs.push(postedTs);
|
|
507
|
+
} else {
|
|
508
|
+
messageRefs.push(data.ts || placeholderRef);
|
|
497
509
|
}
|
|
498
510
|
} else {
|
|
499
|
-
await postFresh(
|
|
511
|
+
const postedTs = await postFresh(
|
|
512
|
+
token,
|
|
513
|
+
channelId,
|
|
514
|
+
threadTs,
|
|
515
|
+
baseBody,
|
|
516
|
+
);
|
|
517
|
+
if (postedTs) messageRefs.push(postedTs);
|
|
500
518
|
}
|
|
501
519
|
|
|
502
520
|
// Clear the AI-assistant "is thinking…" status now that we've
|
|
@@ -507,14 +525,19 @@ export function slackAdapter(
|
|
|
507
525
|
|
|
508
526
|
// Overflow chunks (rare) — post as plain follow-ups in the same thread
|
|
509
527
|
for (const chunk of restChunks) {
|
|
510
|
-
await postFresh(token, channelId, threadTs, {
|
|
528
|
+
const postedTs = await postFresh(token, channelId, threadTs, {
|
|
511
529
|
channel: channelId,
|
|
512
530
|
text: chunk,
|
|
513
531
|
unfurl_links: false,
|
|
514
532
|
unfurl_media: false,
|
|
515
533
|
mrkdwn: true,
|
|
516
534
|
});
|
|
535
|
+
if (postedTs) messageRefs.push(postedTs);
|
|
517
536
|
}
|
|
537
|
+
return {
|
|
538
|
+
status: "delivered",
|
|
539
|
+
...(messageRefs.length > 0 ? { messageRefs } : {}),
|
|
540
|
+
};
|
|
518
541
|
} catch (err) {
|
|
519
542
|
console.error("[slack] Failed to send message:", err);
|
|
520
543
|
throw err;
|
|
@@ -992,7 +1015,7 @@ async function postFresh(
|
|
|
992
1015
|
channelId: string,
|
|
993
1016
|
threadTs: string | undefined,
|
|
994
1017
|
body: Record<string, unknown>,
|
|
995
|
-
): Promise<
|
|
1018
|
+
): Promise<string | undefined> {
|
|
996
1019
|
const hasBlocks =
|
|
997
1020
|
Array.isArray(body.blocks) && (body.blocks as unknown[]).length > 0;
|
|
998
1021
|
if (
|
|
@@ -1000,7 +1023,7 @@ async function postFresh(
|
|
|
1000
1023
|
body.text.trim().length === 0 &&
|
|
1001
1024
|
!hasBlocks
|
|
1002
1025
|
) {
|
|
1003
|
-
return;
|
|
1026
|
+
return undefined;
|
|
1004
1027
|
}
|
|
1005
1028
|
|
|
1006
1029
|
const payload: Record<string, unknown> = {
|
|
@@ -1016,11 +1039,16 @@ async function postFresh(
|
|
|
1016
1039
|
},
|
|
1017
1040
|
body: JSON.stringify(payload),
|
|
1018
1041
|
});
|
|
1019
|
-
const data = (await res.json()) as {
|
|
1042
|
+
const data = (await res.json()) as {
|
|
1043
|
+
ok: boolean;
|
|
1044
|
+
error?: string;
|
|
1045
|
+
ts?: string;
|
|
1046
|
+
};
|
|
1020
1047
|
if (!data.ok) {
|
|
1021
1048
|
console.error("[slack] chat.postMessage error:", data.error);
|
|
1022
1049
|
throw new Error(data.error || "chat.postMessage failed");
|
|
1023
1050
|
}
|
|
1051
|
+
return data.ts;
|
|
1024
1052
|
}
|
|
1025
1053
|
|
|
1026
1054
|
async function slackApiFetch(
|
|
@@ -1871,6 +1899,7 @@ function createSlackRunProgress(
|
|
|
1871
1899
|
: {}),
|
|
1872
1900
|
});
|
|
1873
1901
|
setSlackAssistantStatus(token, channel, threadTs, "");
|
|
1902
|
+
return { status: "delivered", messageRefs: [streamTs] };
|
|
1874
1903
|
},
|
|
1875
1904
|
async fail(message) {
|
|
1876
1905
|
if (pendingTimer) clearTimeout(pendingTimer);
|
|
@@ -10,6 +10,7 @@ import type {
|
|
|
10
10
|
OutgoingMessage,
|
|
11
11
|
IntegrationStatus,
|
|
12
12
|
OutboundTarget,
|
|
13
|
+
PlatformDeliveryReceipt,
|
|
13
14
|
} from "../types.js";
|
|
14
15
|
|
|
15
16
|
/** Telegram's max message length */
|
|
@@ -194,7 +195,7 @@ export function telegramAdapter(): PlatformAdapter {
|
|
|
194
195
|
async sendResponse(
|
|
195
196
|
message: OutgoingMessage,
|
|
196
197
|
context: IncomingMessage,
|
|
197
|
-
): Promise<void> {
|
|
198
|
+
): Promise<void | PlatformDeliveryReceipt> {
|
|
198
199
|
const token = await resolveSecret("TELEGRAM_BOT_TOKEN");
|
|
199
200
|
if (!token) {
|
|
200
201
|
console.error("[telegram] TELEGRAM_BOT_TOKEN not configured");
|
|
@@ -235,22 +236,43 @@ export function telegramAdapter(): PlatformAdapter {
|
|
|
235
236
|
if (!data.ok) {
|
|
236
237
|
// Retry without Markdown if parsing fails
|
|
237
238
|
if (data.description?.includes("parse")) {
|
|
238
|
-
await fetch(
|
|
239
|
-
|
|
240
|
-
|
|
241
|
-
|
|
242
|
-
|
|
243
|
-
|
|
244
|
-
|
|
245
|
-
|
|
239
|
+
const retry = await fetch(
|
|
240
|
+
`https://api.telegram.org/bot${token}/sendMessage`,
|
|
241
|
+
{
|
|
242
|
+
method: "POST",
|
|
243
|
+
headers: { "Content-Type": "application/json" },
|
|
244
|
+
body: JSON.stringify({
|
|
245
|
+
...body,
|
|
246
|
+
parse_mode: undefined,
|
|
247
|
+
}),
|
|
248
|
+
},
|
|
249
|
+
);
|
|
250
|
+
if (!retry.ok) {
|
|
251
|
+
throw new Error(
|
|
252
|
+
`Telegram sendMessage retry failed (HTTP ${retry.status})`,
|
|
253
|
+
);
|
|
254
|
+
}
|
|
255
|
+
const retryData = (await retry.json()) as {
|
|
256
|
+
ok: boolean;
|
|
257
|
+
description?: string;
|
|
258
|
+
};
|
|
259
|
+
if (!retryData.ok) {
|
|
260
|
+
throw new Error(
|
|
261
|
+
`Telegram sendMessage retry failed: ${retryData.description ?? "unknown error"}`,
|
|
262
|
+
);
|
|
263
|
+
}
|
|
246
264
|
} else {
|
|
247
|
-
|
|
265
|
+
throw new Error(
|
|
266
|
+
`Telegram sendMessage failed: ${data.description ?? "unknown error"}`,
|
|
267
|
+
);
|
|
248
268
|
}
|
|
249
269
|
}
|
|
250
270
|
} catch (err) {
|
|
251
271
|
console.error("[telegram] Failed to send message:", err);
|
|
272
|
+
throw err;
|
|
252
273
|
}
|
|
253
274
|
}
|
|
275
|
+
return { status: "delivered" };
|
|
254
276
|
},
|
|
255
277
|
|
|
256
278
|
async sendMessageToTarget(
|
|
@@ -8,6 +8,7 @@ import type {
|
|
|
8
8
|
IncomingMessage,
|
|
9
9
|
OutgoingMessage,
|
|
10
10
|
IntegrationStatus,
|
|
11
|
+
PlatformDeliveryReceipt,
|
|
11
12
|
} from "../types.js";
|
|
12
13
|
|
|
13
14
|
/** WhatsApp's max message length */
|
|
@@ -240,7 +241,7 @@ export function whatsappAdapter(): PlatformAdapter {
|
|
|
240
241
|
async sendResponse(
|
|
241
242
|
message: OutgoingMessage,
|
|
242
243
|
context: IncomingMessage,
|
|
243
|
-
): Promise<void> {
|
|
244
|
+
): Promise<void | PlatformDeliveryReceipt> {
|
|
244
245
|
const accessToken = await resolveSecret("WHATSAPP_ACCESS_TOKEN");
|
|
245
246
|
const phoneNumberId = await resolveSecret("WHATSAPP_PHONE_NUMBER_ID");
|
|
246
247
|
if (!accessToken || !phoneNumberId) {
|
|
@@ -279,11 +280,14 @@ export function whatsappAdapter(): PlatformAdapter {
|
|
|
279
280
|
if (!res.ok) {
|
|
280
281
|
const data = await res.json().catch(() => ({}));
|
|
281
282
|
console.error("[whatsapp] sendMessage error:", data);
|
|
283
|
+
throw new Error(`WhatsApp sendMessage failed (HTTP ${res.status})`);
|
|
282
284
|
}
|
|
283
285
|
} catch (err) {
|
|
284
286
|
console.error("[whatsapp] Failed to send message:", err);
|
|
287
|
+
throw err;
|
|
285
288
|
}
|
|
286
289
|
}
|
|
290
|
+
return { status: "delivered" };
|
|
287
291
|
},
|
|
288
292
|
|
|
289
293
|
formatAgentResponse(text: string): OutgoingMessage {
|
|
@@ -127,6 +127,13 @@ export interface OutgoingMessage {
|
|
|
127
127
|
platformContext: Record<string, unknown>;
|
|
128
128
|
}
|
|
129
129
|
|
|
130
|
+
/** Provider-confirmed references for a successfully delivered response. */
|
|
131
|
+
export interface PlatformDeliveryReceipt {
|
|
132
|
+
status: "delivered";
|
|
133
|
+
/** Opaque provider message ids/timestamps; never message content. */
|
|
134
|
+
messageRefs?: string[];
|
|
135
|
+
}
|
|
136
|
+
|
|
130
137
|
/**
|
|
131
138
|
* Proactive outbound message target for a platform.
|
|
132
139
|
* Used when the agent needs to send to a saved destination instead of replying
|
|
@@ -197,7 +204,7 @@ export interface PlatformRunProgress {
|
|
|
197
204
|
/** Receive normalized agent events. Implementations should throttle writes. */
|
|
198
205
|
onEvent(event: AgentChatEvent): Promise<void> | void;
|
|
199
206
|
/** Finalize the provider-native progress surface with the answer. */
|
|
200
|
-
complete(message: OutgoingMessage): Promise<void>;
|
|
207
|
+
complete(message: OutgoingMessage): Promise<void | PlatformDeliveryReceipt>;
|
|
201
208
|
/** Mark the provider-native surface failed and leave a retryable explanation. */
|
|
202
209
|
fail?(message: string): Promise<void>;
|
|
203
210
|
}
|
|
@@ -316,7 +323,7 @@ export interface PlatformAdapter {
|
|
|
316
323
|
message: OutgoingMessage,
|
|
317
324
|
context: IncomingMessage,
|
|
318
325
|
opts?: { placeholderRef?: string },
|
|
319
|
-
): Promise<void>;
|
|
326
|
+
): Promise<void | PlatformDeliveryReceipt>;
|
|
320
327
|
|
|
321
328
|
/**
|
|
322
329
|
* Send a short best-effort system notice to the conversation the incoming
|
|
@@ -2,6 +2,7 @@ import type { H3Event } from "h3";
|
|
|
2
2
|
|
|
3
3
|
import {
|
|
4
4
|
appendA2AArtifactLinks,
|
|
5
|
+
extractA2AArtifactIdentities,
|
|
5
6
|
type A2AToolResultSummary,
|
|
6
7
|
} from "../a2a/artifact-response.js";
|
|
7
8
|
import { collectFinalResponseTextFromAgentEvents } from "../a2a/response-text.js";
|
|
@@ -35,6 +36,7 @@ import {
|
|
|
35
36
|
import {
|
|
36
37
|
buildAssistantMessage,
|
|
37
38
|
extractThreadMeta,
|
|
39
|
+
threadDataToEngineMessages,
|
|
38
40
|
} from "../agent/thread-data-builder.js";
|
|
39
41
|
import { attachToolSearch } from "../agent/tool-search.js";
|
|
40
42
|
import { createThread, getThread } from "../chat-threads/store.js";
|
|
@@ -63,7 +65,11 @@ import {
|
|
|
63
65
|
} from "./pending-tasks-store.js";
|
|
64
66
|
import { integrationScopeSubjectKey } from "./scope-store.js";
|
|
65
67
|
import { getThreadMapping, saveThreadMapping } from "./thread-mapping-store.js";
|
|
66
|
-
import type {
|
|
68
|
+
import type {
|
|
69
|
+
PlatformAdapter,
|
|
70
|
+
IncomingMessage,
|
|
71
|
+
PlatformDeliveryReceipt,
|
|
72
|
+
} from "./types.js";
|
|
67
73
|
import {
|
|
68
74
|
listIntegrationUsageBudgets,
|
|
69
75
|
releaseIntegrationUsageBudget,
|
|
@@ -710,34 +716,7 @@ async function processIncomingMessage(
|
|
|
710
716
|
}
|
|
711
717
|
const existingMessages: EngineMessage[] = [];
|
|
712
718
|
if (thread?.threadData) {
|
|
713
|
-
|
|
714
|
-
const data = JSON.parse(thread.threadData);
|
|
715
|
-
if (Array.isArray(data.messages)) {
|
|
716
|
-
for (const msg of data.messages) {
|
|
717
|
-
const m = msg.message ?? msg;
|
|
718
|
-
const textContent =
|
|
719
|
-
typeof m.content === "string"
|
|
720
|
-
? m.content
|
|
721
|
-
: Array.isArray(m.content)
|
|
722
|
-
? m.content
|
|
723
|
-
.filter((c: any) => c.type === "text")
|
|
724
|
-
.map((c: any) => c.text)
|
|
725
|
-
.join("\n")
|
|
726
|
-
: "";
|
|
727
|
-
if (m.role === "user") {
|
|
728
|
-
existingMessages.push({
|
|
729
|
-
role: "user",
|
|
730
|
-
content: [{ type: "text", text: textContent }],
|
|
731
|
-
});
|
|
732
|
-
} else if (m.role === "assistant") {
|
|
733
|
-
existingMessages.push({
|
|
734
|
-
role: "assistant",
|
|
735
|
-
content: [{ type: "text", text: textContent }],
|
|
736
|
-
});
|
|
737
|
-
}
|
|
738
|
-
}
|
|
739
|
-
}
|
|
740
|
-
} catch {}
|
|
719
|
+
existingMessages.push(...threadDataToEngineMessages(thread.threadData));
|
|
741
720
|
}
|
|
742
721
|
|
|
743
722
|
// Add the new user message. Include verified platform identity as lightweight
|
|
@@ -988,12 +967,11 @@ async function processIncomingMessage(
|
|
|
988
967
|
// preview card.
|
|
989
968
|
const baseUrl = process.env.APP_URL || process.env.URL || "";
|
|
990
969
|
const appBaseUrl = baseUrl ? withConfiguredAppBasePath(baseUrl) : "";
|
|
970
|
+
const toolResults = collectToolResultSummaries(completedRun);
|
|
991
971
|
if (!suppressPlatformReply) {
|
|
992
|
-
responseText = appendA2AArtifactLinks(
|
|
993
|
-
|
|
994
|
-
|
|
995
|
-
{ baseUrl: appBaseUrl || undefined },
|
|
996
|
-
);
|
|
972
|
+
responseText = appendA2AArtifactLinks(responseText, toolResults, {
|
|
973
|
+
baseUrl: appBaseUrl || undefined,
|
|
974
|
+
});
|
|
997
975
|
}
|
|
998
976
|
const threadDeepLinkUrl =
|
|
999
977
|
appBaseUrl && threadId
|
|
@@ -1002,34 +980,60 @@ async function processIncomingMessage(
|
|
|
1002
980
|
|
|
1003
981
|
// Format and send back to platform — update the "thinking…"
|
|
1004
982
|
// placeholder in place if the adapter supplied one.
|
|
983
|
+
let deliveredResponse:
|
|
984
|
+
| {
|
|
985
|
+
platform: string;
|
|
986
|
+
status: "delivered";
|
|
987
|
+
text: string;
|
|
988
|
+
deliveredAt: string;
|
|
989
|
+
messageRefs?: string[];
|
|
990
|
+
}
|
|
991
|
+
| undefined;
|
|
1005
992
|
if (!suppressPlatformReply) {
|
|
1006
993
|
const outgoing = adapter.formatAgentResponse(responseText, {
|
|
1007
994
|
threadDeepLinkUrl,
|
|
1008
995
|
});
|
|
1009
|
-
let
|
|
996
|
+
let deliveryReceipt: void | PlatformDeliveryReceipt;
|
|
1010
997
|
if (queuedA2AContinuation && progress?.ref) {
|
|
1011
998
|
// Post substantive parent results as a normal thread reply while
|
|
1012
999
|
// the one continuation that claimed this resumable stream keeps
|
|
1013
1000
|
// it open for its eventual terminal result.
|
|
1014
|
-
await adapter.sendResponse(outgoing, incoming, {
|
|
1001
|
+
deliveryReceipt = await adapter.sendResponse(outgoing, incoming, {
|
|
1015
1002
|
placeholderRef: opts.placeholderRef,
|
|
1016
1003
|
});
|
|
1017
|
-
delivered = true;
|
|
1018
1004
|
} else if (progress) {
|
|
1019
1005
|
try {
|
|
1020
|
-
await progress.complete(outgoing);
|
|
1021
|
-
delivered = true;
|
|
1006
|
+
deliveryReceipt = await progress.complete(outgoing);
|
|
1022
1007
|
} catch {
|
|
1023
|
-
await adapter.sendResponse(
|
|
1024
|
-
|
|
1025
|
-
|
|
1026
|
-
|
|
1008
|
+
deliveryReceipt = await adapter.sendResponse(
|
|
1009
|
+
outgoing,
|
|
1010
|
+
incoming,
|
|
1011
|
+
{
|
|
1012
|
+
placeholderRef: opts.placeholderRef,
|
|
1013
|
+
},
|
|
1014
|
+
);
|
|
1027
1015
|
}
|
|
1028
1016
|
} else {
|
|
1029
|
-
await adapter.sendResponse(outgoing, incoming, {
|
|
1017
|
+
deliveryReceipt = await adapter.sendResponse(outgoing, incoming, {
|
|
1030
1018
|
placeholderRef: opts.placeholderRef,
|
|
1031
1019
|
});
|
|
1032
|
-
|
|
1020
|
+
}
|
|
1021
|
+
const delivered = deliveryReceipt?.status === "delivered";
|
|
1022
|
+
if (!delivered) {
|
|
1023
|
+
throw new Error(
|
|
1024
|
+
`${incoming.platform} response completed without delivery proof`,
|
|
1025
|
+
);
|
|
1026
|
+
}
|
|
1027
|
+
if (delivered) {
|
|
1028
|
+
deliveredResponse = {
|
|
1029
|
+
platform: incoming.platform,
|
|
1030
|
+
status: "delivered",
|
|
1031
|
+
text: outgoing.text,
|
|
1032
|
+
deliveredAt: new Date().toISOString(),
|
|
1033
|
+
...(deliveryReceipt?.messageRefs?.length
|
|
1034
|
+
? { messageRefs: deliveryReceipt.messageRefs }
|
|
1035
|
+
: {}),
|
|
1036
|
+
};
|
|
1033
1037
|
}
|
|
1034
1038
|
if (slackInputRequest && delivered && incoming.senderId) {
|
|
1035
1039
|
await setIntegrationAwaitingInput({
|
|
@@ -1078,6 +1082,8 @@ async function processIncomingMessage(
|
|
|
1078
1082
|
incoming.text,
|
|
1079
1083
|
completedRun,
|
|
1080
1084
|
thread,
|
|
1085
|
+
deliveredResponse,
|
|
1086
|
+
toolResults,
|
|
1081
1087
|
);
|
|
1082
1088
|
await recordIntegrationUsage({
|
|
1083
1089
|
usage,
|
|
@@ -1533,6 +1539,14 @@ async function persistThreadData(
|
|
|
1533
1539
|
userText: string,
|
|
1534
1540
|
completedRun: ActiveRun,
|
|
1535
1541
|
thread: any,
|
|
1542
|
+
deliveredResponse?: {
|
|
1543
|
+
platform: string;
|
|
1544
|
+
status: "delivered";
|
|
1545
|
+
text: string;
|
|
1546
|
+
deliveredAt: string;
|
|
1547
|
+
messageRefs?: string[];
|
|
1548
|
+
},
|
|
1549
|
+
toolResults: A2AToolResultSummary[] = [],
|
|
1536
1550
|
): Promise<void> {
|
|
1537
1551
|
try {
|
|
1538
1552
|
let repo: any;
|
|
@@ -1556,6 +1570,16 @@ async function persistThreadData(
|
|
|
1556
1570
|
completedRun.events ?? [],
|
|
1557
1571
|
completedRun.runId,
|
|
1558
1572
|
);
|
|
1573
|
+
if (assistantMsg) {
|
|
1574
|
+
assistantMsg.metadata.integrationDeliveryAttempted = true;
|
|
1575
|
+
if (deliveredResponse) {
|
|
1576
|
+
assistantMsg.metadata.integrationDelivery = deliveredResponse;
|
|
1577
|
+
const artifactIdentities = extractA2AArtifactIdentities(toolResults);
|
|
1578
|
+
if (artifactIdentities.length > 0) {
|
|
1579
|
+
assistantMsg.metadata.integrationArtifacts = artifactIdentities;
|
|
1580
|
+
}
|
|
1581
|
+
}
|
|
1582
|
+
}
|
|
1559
1583
|
|
|
1560
1584
|
repo.messages.push(userMsg);
|
|
1561
1585
|
if (assistantMsg) {
|
|
@@ -110,6 +110,7 @@ export function assembleA2AFinalResponse(
|
|
|
110
110
|
const finalText = appendA2AArtifactLinks(responseText, [...toolResults], {
|
|
111
111
|
baseUrl: options.baseUrl ?? resolveArtifactBaseUrl(options.event),
|
|
112
112
|
includeReferencedArtifacts: true,
|
|
113
|
+
includePersistedArtifactMarker: true,
|
|
113
114
|
});
|
|
114
115
|
if (terminalError && !finalText.trim()) {
|
|
115
116
|
throw new Error(formatA2ATerminalError(terminalError));
|
|
@@ -101,6 +101,37 @@ inferences for confirmation, then submit once. When required information is
|
|
|
101
101
|
missing, keep the clarification in the originating thread and retain earlier
|
|
102
102
|
answers as context.
|
|
103
103
|
|
|
104
|
+
### Slack Follow-ups And Corrections
|
|
105
|
+
|
|
106
|
+
A follow-up in an existing Slack thread is not automatically a new intake. Read
|
|
107
|
+
the thread context and inspect the prior Content artifact identity first,
|
|
108
|
+
including any returned document ID or `/page/<id>` path and the canonical
|
|
109
|
+
database row when available. Then choose the operation that matches the user's
|
|
110
|
+
intent:
|
|
111
|
+
|
|
112
|
+
- **Update** the same document for corrections, refinements, status changes, or
|
|
113
|
+
renames that still describe the same request. A rename changes the title, not
|
|
114
|
+
the artifact identity: preserve the stable Content document ID and page path.
|
|
115
|
+
- **Add** new details to the same document when the follow-up extends the
|
|
116
|
+
original request without replacing it.
|
|
117
|
+
- **Supersede** only when the user intends a replacement artifact or distinct
|
|
118
|
+
successor and the workspace's schema or instructions define how that
|
|
119
|
+
relationship is recorded. Preserve a concrete link to the prior artifact.
|
|
120
|
+
- **Create** only when the follow-up is genuinely a separate request or the user
|
|
121
|
+
explicitly asks for a new artifact. Do not blindly submit another row merely
|
|
122
|
+
because a new Slack message arrived.
|
|
123
|
+
|
|
124
|
+
Apply people fields from verified identity and intent, not from convenient
|
|
125
|
+
guesswork:
|
|
126
|
+
|
|
127
|
+
- When the database has a `Requester` field, default it to the verified Slack
|
|
128
|
+
sender unless the user explicitly identifies a different requester.
|
|
129
|
+
- A named doer such as "for Apoorva" maps to `Assignee` when that field exists.
|
|
130
|
+
Naming an assignee never changes or replaces `Requester`.
|
|
131
|
+
- Resolve named people to the database's accepted person identity before
|
|
132
|
+
writing. If a named person cannot be resolved unambiguously, clarify in the
|
|
133
|
+
originating Slack thread; never omit, downgrade, or silently drop the person.
|
|
134
|
+
|
|
104
135
|
## Local File Mode
|
|
105
136
|
|
|
106
137
|
Install into an existing repo with:
|
|
@@ -7,7 +7,10 @@ description: >-
|
|
|
7
7
|
|
|
8
8
|
# Document Editing
|
|
9
9
|
|
|
10
|
-
Documents are stored in the SQL database via Drizzle ORM. Each document has a
|
|
10
|
+
Documents are stored in the SQL database via Drizzle ORM. Each document has a
|
|
11
|
+
title, stable description, markdown content, optional parent (for nesting), and
|
|
12
|
+
a position for ordering. The description explains why the page exists and what
|
|
13
|
+
belongs there; it is not a changing synopsis of the current body.
|
|
11
14
|
|
|
12
15
|
## Scripts
|
|
13
16
|
|
|
@@ -48,6 +51,7 @@ Create a new document.
|
|
|
48
51
|
pnpm action create-document --title "Meeting Notes" --content "# Meeting Notes\n\nAttendees: ..."
|
|
49
52
|
pnpm action create-document --title "Sub Page" --parentId parent123
|
|
50
53
|
pnpm action create-document --title "My Page" --icon "📝"
|
|
54
|
+
pnpm action create-document --title "Research" --description "Evidence and source notes that support the current project"
|
|
51
55
|
```
|
|
52
56
|
|
|
53
57
|
### edit-document
|
|
@@ -73,6 +77,7 @@ Update an existing document. Use for **full rewrites or new content**, not for s
|
|
|
73
77
|
pnpm action update-document --id abc123 --title "New Title"
|
|
74
78
|
pnpm action update-document --id abc123 --content "# Updated Content\n\nNew text here"
|
|
75
79
|
pnpm action update-document --id abc123 --title "New Title" --content "New content"
|
|
80
|
+
pnpm action update-document --id abc123 --description "Stable guidance for what belongs on this page"
|
|
76
81
|
```
|
|
77
82
|
|
|
78
83
|
### delete-document
|
|
@@ -123,17 +128,18 @@ Always run this after any document modification to update the sidebar.
|
|
|
123
128
|
|
|
124
129
|
## Document Schema
|
|
125
130
|
|
|
126
|
-
| Column
|
|
127
|
-
|
|
|
128
|
-
| `id`
|
|
129
|
-
| `parent_id`
|
|
130
|
-
| `title`
|
|
131
|
-
| `
|
|
132
|
-
| `
|
|
133
|
-
| `
|
|
134
|
-
| `
|
|
135
|
-
| `
|
|
136
|
-
| `
|
|
131
|
+
| Column | Type | Description |
|
|
132
|
+
| ------------- | ------- | -------------------------------------- |
|
|
133
|
+
| `id` | text | Primary key (12-char hex string) |
|
|
134
|
+
| `parent_id` | text | Parent document ID (null for root) |
|
|
135
|
+
| `title` | text | Document title (default: "Untitled") |
|
|
136
|
+
| `description` | text | Stable semantic guidance for the page |
|
|
137
|
+
| `content` | text | Markdown content |
|
|
138
|
+
| `icon` | text | Emoji icon (optional) |
|
|
139
|
+
| `position` | integer | Sort order within parent (0-based) |
|
|
140
|
+
| `is_favorite` | integer | Whether document is favorited (0 or 1) |
|
|
141
|
+
| `created_at` | text | ISO timestamp |
|
|
142
|
+
| `updated_at` | text | ISO timestamp |
|
|
137
143
|
|
|
138
144
|
## Content Format
|
|
139
145
|
|
|
@@ -142,21 +148,29 @@ Documents use **markdown** for content. The editor renders markdown in real time
|
|
|
142
148
|
## Parent-Child Hierarchy
|
|
143
149
|
|
|
144
150
|
Documents form a tree via `parent_id`:
|
|
151
|
+
|
|
145
152
|
- Root documents have `parent_id = null`
|
|
146
153
|
- Child documents reference their parent's `id`
|
|
147
154
|
- Deleting a parent recursively deletes all children
|
|
148
155
|
- Position determines ordering within the same parent
|
|
149
156
|
|
|
157
|
+
Descriptions are owned; context is inherited. `get-document` and `view-screen`
|
|
158
|
+
return the focused page's own description plus a computed root-to-parent
|
|
159
|
+
`contextPath`. Use that path to understand where the page lives, but never copy
|
|
160
|
+
ancestor descriptions into the child. Database, property, and option
|
|
161
|
+
descriptions narrow the guidance further when working with structured values.
|
|
162
|
+
|
|
150
163
|
## Common Tasks
|
|
151
164
|
|
|
152
|
-
| User says
|
|
153
|
-
|
|
|
154
|
-
| "Create a page about X"
|
|
155
|
-
| "
|
|
156
|
-
| "
|
|
157
|
-
| "
|
|
158
|
-
| "
|
|
159
|
-
| "
|
|
160
|
-
| "
|
|
165
|
+
| User says | What to do |
|
|
166
|
+
| ---------------------------- | ----------------------------------------------------------------------------------- |
|
|
167
|
+
| "Create a page about X" | `create-document --title "X" --content "# X\n\n..."` |
|
|
168
|
+
| "Describe what belongs here" | `update-document --id ... --description "..."` |
|
|
169
|
+
| "Find my meeting notes" | `search-documents --query "meeting notes"` |
|
|
170
|
+
| "Fix a typo / edit a line" | `view-screen` to get ID, then `edit-document --id ... --find "old" --replace "new"` |
|
|
171
|
+
| "Rewrite this document" | `view-screen` to get ID, then `update-document --id ... --content ...` |
|
|
172
|
+
| "Delete this page" | `view-screen` to get ID, then `delete-document --id ...` |
|
|
173
|
+
| "Add a sub-page" | `create-document --title "Sub" --parentId <parentId>` |
|
|
174
|
+
| "Show me the document tree" | `list-documents` |
|
|
161
175
|
|
|
162
176
|
Always run `refresh-list` after any create, update, or delete operation.
|