mirror of
https://github.com/Ark0N/Codeman.git
synced 2026-10-05 15:09:42 +02:00
chore: version packages
Persist the 'I checked it' state of yellow idle tab alerts across reloads and devices. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
This commit is contained in:
@@ -216,6 +216,44 @@ describe('ApprovalInbox', () => {
|
||||
expect(inbox.getForSession('s1')?.id).toBe(newer.id);
|
||||
});
|
||||
|
||||
it('acknowledge marks an idle item seen without resolving it, and emits onUpdated once', () => {
|
||||
const { updated, resolved } = collect(inbox);
|
||||
const item = inbox.notePrompt({ sessionId: 's1', sessionName: 'w1', kind: 'idle' });
|
||||
const acked = inbox.acknowledge('s1');
|
||||
expect(acked?.id).toBe(item.id);
|
||||
expect(acked?.acknowledgedAt).toBeGreaterThan(0);
|
||||
// Still pending and still answerable: the human looked, they did not answer.
|
||||
expect(inbox.getById(item.id)?.acknowledgedAt).toBeGreaterThan(0);
|
||||
expect(inbox.listPending()).toHaveLength(1);
|
||||
expect(resolved).toHaveLength(0);
|
||||
expect(updated).toEqual([expect.objectContaining({ id: item.id })]);
|
||||
// Idempotent: a second view does not re-broadcast.
|
||||
expect(inbox.acknowledge('s1')).toBeUndefined();
|
||||
expect(updated).toHaveLength(1);
|
||||
});
|
||||
|
||||
it('acknowledge never touches a permission/question item (viewing is not answering)', () => {
|
||||
const { updated } = collect(inbox);
|
||||
const permission = inbox.notePrompt({ sessionId: 's1', sessionName: 'w1', kind: 'permission' });
|
||||
expect(inbox.acknowledge('s1')).toBeUndefined();
|
||||
expect(inbox.getById(permission.id)?.acknowledgedAt).toBeUndefined();
|
||||
|
||||
const question = inbox.notePrompt({ sessionId: 's2', sessionName: 'w2', kind: 'question' });
|
||||
expect(inbox.acknowledge('s2')).toBeUndefined();
|
||||
expect(inbox.getById(question.id)?.acknowledgedAt).toBeUndefined();
|
||||
expect(updated).toHaveLength(0);
|
||||
|
||||
expect(inbox.acknowledge('nope')).toBeUndefined();
|
||||
});
|
||||
|
||||
it('a new prompt after an acknowledgement arms the alert again', () => {
|
||||
inbox.notePrompt({ sessionId: 's1', sessionName: 'w1', kind: 'idle' });
|
||||
inbox.acknowledge('s1');
|
||||
const next = inbox.notePrompt({ sessionId: 's1', sessionName: 'w1', kind: 'idle' });
|
||||
expect(next.acknowledgedAt).toBeUndefined();
|
||||
expect(inbox.getForSession('s1')?.acknowledgedAt).toBeUndefined();
|
||||
});
|
||||
|
||||
it('dismiss removes without answering', () => {
|
||||
const { resolved } = collect(inbox);
|
||||
const item = inbox.notePrompt({ sessionId: 's1', sessionName: 'w1', kind: 'question' });
|
||||
|
||||
@@ -283,6 +283,59 @@ describe('approval routes', () => {
|
||||
expect(await listApprovals(harness)).toHaveLength(0);
|
||||
});
|
||||
|
||||
it('viewing a session acknowledges its idle prompt (item stays pending) and broadcasts it', async () => {
|
||||
session.terminalBuffer = 'claude> waiting at the composer';
|
||||
await postHook(harness, 'idle_prompt', {});
|
||||
const [before] = await listApprovals(harness);
|
||||
expect(before.acknowledgedAt).toBeUndefined();
|
||||
|
||||
const res = await harness.app.inject({
|
||||
method: 'POST',
|
||||
url: `/api/approvals/session/${SESSION_ID}/viewed`,
|
||||
payload: {},
|
||||
});
|
||||
expect(res.statusCode).toBe(200);
|
||||
expect(res.json().data).toMatchObject({ sessionId: SESSION_ID, acknowledged: before.id });
|
||||
|
||||
// Seen, not answered: still listed (so it stays answerable), no keystrokes,
|
||||
// and clients skip re-arming the tab alert because of acknowledgedAt.
|
||||
const [after] = await listApprovals(harness);
|
||||
expect(after.id).toBe(before.id);
|
||||
expect(after.acknowledgedAt).toBeGreaterThan(0);
|
||||
expect(session.writeBuffer).toEqual([]);
|
||||
|
||||
// Second view is a no-op (nothing new to tell the other devices).
|
||||
const again = await harness.app.inject({
|
||||
method: 'POST',
|
||||
url: `/api/approvals/session/${SESSION_ID}/viewed`,
|
||||
payload: {},
|
||||
});
|
||||
expect(again.json().data.acknowledged).toBeNull();
|
||||
});
|
||||
|
||||
it('viewing a session leaves a permission dialog alerting (looking is not answering)', async () => {
|
||||
await postHook(harness, 'permission_prompt', {});
|
||||
const res = await harness.app.inject({
|
||||
method: 'POST',
|
||||
url: `/api/approvals/session/${SESSION_ID}/viewed`,
|
||||
payload: {},
|
||||
});
|
||||
expect(res.statusCode).toBe(200);
|
||||
expect(res.json().data.acknowledged).toBeNull();
|
||||
const [item] = await listApprovals(harness);
|
||||
expect(item.kind).toBe('permission');
|
||||
expect(item.acknowledgedAt).toBeUndefined();
|
||||
});
|
||||
|
||||
it('viewing an unknown session 404s', async () => {
|
||||
const res = await harness.app.inject({
|
||||
method: 'POST',
|
||||
url: '/api/approvals/session/not-a-session/viewed',
|
||||
payload: {},
|
||||
});
|
||||
expect(res.statusCode).toBe(404);
|
||||
});
|
||||
|
||||
it('non-claude sessions never get inbox items', async () => {
|
||||
session.mode = 'codex';
|
||||
await postHook(harness, 'permission_prompt', {});
|
||||
|
||||
Reference in New Issue
Block a user