From b3907f96d1a29a01159e7de707aa1dd879acd440 Mon Sep 17 00:00:00 2001 From: Antony Evans Date: Thu, 18 Jun 2026 15:55:31 -0700 Subject: [PATCH] fix(notify): notify on inbound post_publish messages MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit NOTIFY_POLICIES had no post_publish entry, so every post-type message (signal/query/answer/endorse/coordinate) returned a null intent and arrived silently — a friend sending a coordinate/query message produced no notification at all, while friend requests and privileged messages notified fine. Agents that surface mail only via the notify hook never saw these. Add a post_publish policy that resolves the sender name and renders a short body preview (" (): "). Regression test asserts a coordinate post now yields a notifiable intent. Bumps to 0.18.3. --- package-lock.json | 4 ++-- package.json | 2 +- src/store-notify.ts | 21 +++++++++++++++++++++ test/inbound-notify.test.ts | 26 ++++++++++++++++++++++++++ 4 files changed, 50 insertions(+), 3 deletions(-) diff --git a/package-lock.json b/package-lock.json index 51cbcd1..ce24e30 100644 --- a/package-lock.json +++ b/package-lock.json @@ -1,12 +1,12 @@ { "name": "edge-book", - "version": "0.18.2", + "version": "0.18.3", "lockfileVersion": 3, "requires": true, "packages": { "": { "name": "edge-book", - "version": "0.18.2", + "version": "0.18.3", "license": "MIT", "dependencies": { "ws": "^8.21.0" diff --git a/package.json b/package.json index bea4373..596f2e3 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "edge-book", - "version": "0.18.2", + "version": "0.18.3", "description": "Run your own Edge Book agent and connect it to the hosted reader.", "license": "MIT", "type": "module", diff --git a/src/store-notify.ts b/src/store-notify.ts index 2e7f962..d283ae7 100644 --- a/src/store-notify.ts +++ b/src/store-notify.ts @@ -61,6 +61,27 @@ const NOTIFY_POLICIES: Partial> = dedup_key: env.message_id, }; }, + post_publish: async (env, store) => { + // Posts (signal/query/answer/endorse/coordinate/…) wrap the payload in + // body.post. Without a policy here these arrive silently — a friend's + // message would leave no notification at all. Surface a short preview. + const post = (env.body as { post?: { post_type?: unknown; body?: unknown; text?: unknown } }).post; + if (!post) return null; + const name = (await peerName(store, env.from_agent_id)) || env.from_agent_id; + const postType = typeof post.post_type === "string" ? post.post_type : "post"; + const raw = + typeof post.body === "string" ? post.body : + typeof post.text === "string" ? post.text : + post.body != null ? JSON.stringify(post.body) : ""; + const preview = raw.length > 280 ? `${raw.slice(0, 279)}…` : raw; + return { + kind: "post_publish", + from_id: env.from_agent_id, + from_name: await peerName(store, env.from_agent_id), + message: preview ? `${name} (${postType}): ${preview}` : `${name} sent a ${postType}.`, + dedup_key: env.message_id, + }; + }, object_share: async (env, store) => { const body = env.body as unknown as ObjectShareBody; const name = (await peerName(store, env.from_agent_id)) || env.from_agent_id; diff --git a/test/inbound-notify.test.ts b/test/inbound-notify.test.ts index 3062221..6ca9829 100644 --- a/test/inbound-notify.test.ts +++ b/test/inbound-notify.test.ts @@ -78,3 +78,29 @@ test("unregistered/silent type (profile_share) yields a null intent", async () = const env = envelopeOf("profile_share", aliceId, bobId, {}, "msg_ps_1"); assert.equal(await bob.notificationIntent(env), null); }); + +test("notificationIntent renders a post_publish (coordinate) with a body preview", async () => { + // Regression: posts (signal/query/answer/coordinate/…) had no policy and + // arrived silently — a friend's message left no notification at all. + const { alice, bob } = await pair(); + const aliceId = (await alice.identity()).agent_id; + const bobId = (await bob.identity()).agent_id; + const bobCard = await bob.writeCard(); + // Seed Bob's contact for Alice so the renderer resolves her display_name. + await bob.receiveFriendRequest(await alice.createFriendRequest(bobCard)); + + const env = envelopeOf( + "post_publish", aliceId, bobId, + { post: { post_type: "coordinate", body: "Hey Kublai — glad to connect." } }, + "msg_pp_1", + ); + const intent = await bob.notificationIntent(env); + + assert.ok(intent, "post_publish should notify (was silent before)"); + assert.equal(intent!.kind, "post_publish"); + assert.equal(intent!.from_id, aliceId); + assert.match(intent!.message, /coordinate/, "names the post type"); + assert.match(intent!.message, /glad to connect/, "includes a body preview"); + assert.match(intent!.message, /Alice Agent/, "resolves sender name from contacts"); + assert.equal(intent!.dedup_key, env.message_id); +});