feat(messaging): read receipts + ack convention
Closes the fire-and-forget gap the user hit: the sender now sees when a message is read. When markMessageRead fires, the read event carries the reader, and the watch stream renders it as a receipt: AgentHub: ✓ Read MSG-0002 read by windows-claude Plus an ack convention in the implementer guide: when you act on a message- request, send a brief "on it …" + a result message — so the sender sees progress, not silence. (Board-side activity feed with messages is folded into TSK-0032.) Bump 0.8.0 -> 0.8.1. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
This commit is contained in:
parent
07afa717ac
commit
ed6c5b01d0
@ -1,6 +1,6 @@
|
|||||||
{
|
{
|
||||||
"name": "agenthub",
|
"name": "agenthub",
|
||||||
"version": "0.8.0",
|
"version": "0.8.1",
|
||||||
"description": "Local coordination layer for AI coding agents",
|
"description": "Local coordination layer for AI coding agents",
|
||||||
"type": "module",
|
"type": "module",
|
||||||
"main": "./dist/index.js",
|
"main": "./dist/index.js",
|
||||||
|
|||||||
@ -80,6 +80,10 @@ function describeEvent(event: AgentHubEvent): { label: string; detail: string }
|
|||||||
case 'memory':
|
case 'memory':
|
||||||
return { label: 'Memory', detail: event.title ?? '' };
|
return { label: 'Memory', detail: event.title ?? '' };
|
||||||
case 'message':
|
case 'message':
|
||||||
|
// A read message surfaces to the SENDER as a delivery/read receipt.
|
||||||
|
if (event.action === 'updated' && event.status === 'read') {
|
||||||
|
return { label: '✓ Read', detail: event.assignedTo ? `read by ${event.assignedTo}` : 'read' };
|
||||||
|
}
|
||||||
return { label: 'Message', detail: event.title ?? '' };
|
return { label: 'Message', detail: event.title ?? '' };
|
||||||
default:
|
default:
|
||||||
// 'agent' presence events are formatted in formatEvent() before reaching
|
// 'agent' presence events are formatted in formatEvent() before reaching
|
||||||
|
|||||||
@ -117,7 +117,7 @@ async function runRemote(serverUrl: string, fn: () => Promise<void>): Promise<vo
|
|||||||
export function createProgram(cwd: string): Command {
|
export function createProgram(cwd: string): Command {
|
||||||
const program = new Command('agenthub')
|
const program = new Command('agenthub')
|
||||||
.description('Local coordination layer for AI coding agents')
|
.description('Local coordination layer for AI coding agents')
|
||||||
.version('0.8.0')
|
.version('0.8.1')
|
||||||
.option('--server <url>', 'AgentHub server URL (env: AGENTHUB_SERVER)');
|
.option('--server <url>', 'AgentHub server URL (env: AGENTHUB_SERVER)');
|
||||||
|
|
||||||
program
|
program
|
||||||
|
|||||||
@ -59,6 +59,10 @@ claims it, and returns the task + its handoff. Then:
|
|||||||
then \`agenthub task review <id>\` + \`agenthub memory add …\`, then \`agenthub work\` again
|
then \`agenthub task review <id>\` + \`agenthub memory add …\`, then \`agenthub work\` again
|
||||||
(run it in the background so the wait doesn't tie up your turn).
|
(run it in the background so the wait doesn't tie up your turn).
|
||||||
|
|
||||||
|
**Messages:** \`agenthub_work\` surfaces messages addressed to you (or check \`agenthub_inbox\`).
|
||||||
|
When you act on a message-request, send a short ack with \`agenthub_message\` ("on it ...") and
|
||||||
|
a result message when done — so the sender sees progress + outcome, not silence.
|
||||||
|
|
||||||
⚠️ NEVER close a task yourself (no \`agenthub_task_done\` / \`agenthub task done\`) — only the
|
⚠️ NEVER close a task yourself (no \`agenthub_task_done\` / \`agenthub task done\`) — only the
|
||||||
architect approves and closes. You drive the tools yourself; the human doesn't type them for you.
|
architect approves and closes. You drive the tools yourself; the human doesn't type them for you.
|
||||||
`;
|
`;
|
||||||
|
|||||||
@ -300,7 +300,12 @@ export async function registerRoutes(app: FastifyInstance, cwd: string): Promise
|
|||||||
const { id } = request.params as { id: string };
|
const { id } = request.params as { id: string };
|
||||||
try {
|
try {
|
||||||
const message = markMessageRead(cwd, id);
|
const message = markMessageRead(cwd, id);
|
||||||
emitChange({ type: 'message', action: 'updated', id: message.id, status: 'read' }, message.updatedAt);
|
// Read receipt: carry the reader (to) so the sender's stream shows
|
||||||
|
// "✓ MSG-xxxx read by <agent>".
|
||||||
|
emitChange(
|
||||||
|
{ type: 'message', action: 'updated', id: message.id, status: 'read', title: `${message.from} → ${message.to}`, assignedTo: message.to },
|
||||||
|
message.updatedAt,
|
||||||
|
);
|
||||||
return message;
|
return message;
|
||||||
} catch (err) {
|
} catch (err) {
|
||||||
return badRequest(reply, err instanceof Error ? err.message : 'Message not found');
|
return badRequest(reply, err instanceof Error ? err.message : 'Message not found');
|
||||||
|
|||||||
Loading…
x
Reference in New Issue
Block a user