@sleep2agi/commhub-server 0.9.0-preview.49 → 0.9.0-preview.50
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/package.json +1 -1
- package/src/send-reply-attachments.test.ts +28 -7
- package/src/tools.ts +15 -1
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@sleep2agi/commhub-server",
|
|
3
|
-
"version": "0.9.0-preview.
|
|
3
|
+
"version": "0.9.0-preview.50",
|
|
4
4
|
"description": "CommHub Server — AI Agent communication hub with MCP protocol, multi-network isolation, user auth, and MCP tools (17 collaboration-core + node/provider ops tools; authoritative list: docs-site/docs/api/mcp-tools.md).",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"main": "src/index.ts",
|
|
@@ -175,8 +175,10 @@ describe("#507 — send_reply attachments (positive)", () => {
|
|
|
175
175
|
|
|
176
176
|
// Verify persistence — the echo must come from what actually landed on disk.
|
|
177
177
|
const taskMeta = readTaskMeta(taskId);
|
|
178
|
-
|
|
179
|
-
expect(taskMeta
|
|
178
|
+
// #1823 —— 任务行上回复附件在 reply_attachments;attachments 键留给提问者自己带的附件
|
|
179
|
+
expect(taskMeta?.attachments).toBeUndefined();
|
|
180
|
+
expect(taskMeta?.reply_attachments?.length).toBe(2);
|
|
181
|
+
expect(taskMeta.reply_attachments.map((a: any) => a.file_id).sort()).toEqual([a1.file_id, a2.file_id].sort());
|
|
180
182
|
|
|
181
183
|
const inboxMeta = readInboxMeta(reply.message_id!);
|
|
182
184
|
expect(inboxMeta?.attachments?.length).toBe(2);
|
|
@@ -196,8 +198,10 @@ describe("#507 — send_reply attachments (positive)", () => {
|
|
|
196
198
|
expect(reply.ok).toBe(true);
|
|
197
199
|
expect(reply.attachments_saved!.length).toBe(1);
|
|
198
200
|
const persisted = readTaskMeta(taskId);
|
|
199
|
-
expect(persisted?.
|
|
200
|
-
expect(persisted?.
|
|
201
|
+
expect(persisted?.reply_attachments?.[0]?.file_id).toBe(a1.file_id);
|
|
202
|
+
expect(persisted?.attachments).toBeUndefined();
|
|
203
|
+
// #1823 —— 回复 meta 的兄弟字段属于回复(inbox 行),不写进提问者的任务行
|
|
204
|
+
expect(readInboxMeta(reply.message_id)?.other_field).toBe("preserved");
|
|
201
205
|
});
|
|
202
206
|
|
|
203
207
|
test("P3: top-level attachments WIN over meta.attachments when both supplied (parity with REST /api/task L2101)", async () => {
|
|
@@ -217,9 +221,26 @@ describe("#507 — send_reply attachments (positive)", () => {
|
|
|
217
221
|
expect(reply.attachments_saved!.length).toBe(1);
|
|
218
222
|
expect(reply.attachments_saved![0].file_id).toBe(topLevel.file_id);
|
|
219
223
|
const persisted = readTaskMeta(taskId);
|
|
220
|
-
expect(persisted?.
|
|
221
|
-
expect(persisted?.
|
|
222
|
-
expect(
|
|
224
|
+
expect(persisted?.reply_attachments?.length).toBe(1);
|
|
225
|
+
expect(persisted?.reply_attachments?.[0]?.file_id).toBe(topLevel.file_id);
|
|
226
|
+
expect(readInboxMeta(reply.message_id)?.other).toBe("kept");
|
|
227
|
+
});
|
|
228
|
+
|
|
229
|
+
// #1823 —— 提问者随任务带的附件必须留在 attachments;回复附件另放 reply_attachments。
|
|
230
|
+
// (Vincent 2026-09-06:回复的图被画进提问者气泡;以前整体 COALESCE 替换还会把提问者的附件覆盖掉)
|
|
231
|
+
test("P6 (#1823): asker's attachments survive a reply with attachments; reply goes to reply_attachments", async () => {
|
|
232
|
+
const { taskId } = seed();
|
|
233
|
+
const asked = att("11111111111111111111111111111111", { name: "asked.png" });
|
|
234
|
+
db.run("UPDATE tasks SET meta_json = ?1 WHERE task_id = ?2", [JSON.stringify({ attachments: [asked], origin: "dashboard" }), taskId]);
|
|
235
|
+
const handler = await getSendReplyHandler();
|
|
236
|
+
const answer = att("22222222222222222222222222222222", { name: "answer.md" });
|
|
237
|
+
const reply = await callReply(handler, { alias: AGENT_ALIAS, text: "here", in_reply_to: taskId, status: "replied" as const, attachments: [answer] });
|
|
238
|
+
expect(reply.ok).toBe(true);
|
|
239
|
+
const persisted = readTaskMeta(taskId);
|
|
240
|
+
expect(persisted?.attachments?.map((a: any) => a.file_id)).toEqual([asked.file_id]);
|
|
241
|
+
expect(persisted?.origin).toBe("dashboard");
|
|
242
|
+
expect(persisted?.reply_attachments?.map((a: any) => a.file_id)).toEqual([answer.file_id]);
|
|
243
|
+
expect(readInboxMeta(reply.message_id)?.attachments?.[0]?.file_id).toBe(answer.file_id);
|
|
223
244
|
});
|
|
224
245
|
});
|
|
225
246
|
|
package/src/tools.ts
CHANGED
|
@@ -1981,7 +1981,21 @@ export function registerTools(server: McpServer, clientIP?: string, enforceNetwo
|
|
|
1981
1981
|
// against clobbering pre-existing meta_json when this reply
|
|
1982
1982
|
// brings no attachments (metaJson === null → keep the existing
|
|
1983
1983
|
// task meta_json unchanged). Reverse-(e) invariant.
|
|
1984
|
-
|
|
1984
|
+
// #1823 —— 回复的附件写到 tasks.meta_json.reply_attachments,**不碰** meta_json.attachments:
|
|
1985
|
+
// 那个键是提问者随任务带的附件,客户端把它画在提问者的气泡里。以前这里用回复的 metaJson
|
|
1986
|
+
// 整体 COALESCE 替换,于是 agent 回的图出现在提问者气泡下面、提问者自己带的附件被覆盖
|
|
1987
|
+
// (Vincent 2026-09-06 实测「好像没成功发送给我」)。inbox 行(收件方视角)照旧写 metaJson。
|
|
1988
|
+
const replyTaskMetaJson = (() => {
|
|
1989
|
+
if (!attachmentsResult.attachments.length) return null;
|
|
1990
|
+
const rowParams: any[] = [in_reply_to];
|
|
1991
|
+
let rowSql = "SELECT meta_json FROM tasks WHERE task_id = ?1";
|
|
1992
|
+
rowSql = addScope(rowSql, rowParams, effectiveNetId);
|
|
1993
|
+
const existing = db.get<{ meta_json: string | null }>(rowSql, ...rowParams)?.meta_json ?? null;
|
|
1994
|
+
let base: Record<string, unknown> = {};
|
|
1995
|
+
if (existing) { try { const parsed = JSON.parse(existing); if (parsed && typeof parsed === "object" && !Array.isArray(parsed)) base = parsed; } catch {} }
|
|
1996
|
+
return JSON.stringify({ ...base, reply_attachments: attachmentsResult.attachments });
|
|
1997
|
+
})();
|
|
1998
|
+
const updateParams: any[] = [replyStatus, text, replyTaskMetaJson, in_reply_to];
|
|
1985
1999
|
let updateSql = `UPDATE tasks
|
|
1986
2000
|
SET status = ?1,
|
|
1987
2001
|
result = ?2,
|