mirror of
https://github.com/Ark0N/Codeman.git
synced 2026-11-14 02:29:33 +01:00
fix(omp): an omp alert stays up until omp says the dialog closed (#798)
The four edits promised in the review of #798: - An omp alert could clear while omp's dialog was still up. omp items got the same pane `capture` as Claude's, which the inbox re-reads with Claude's numbered-dialog parser. omp's dialog has no numbered rows, but an agent's plan above it does, so the list was parsed as the item's options, and once it scrolled out of the frame the item was resolved as answered in the terminal. An approval-only item now takes the frame once as `context` and gets no `capture`, like a screen-read item (web/screen-blocked.ts), so the module's `elicitation_complete` is the only thing that clears it. A route test puts a numbered list above an omp-shaped dialog and fails on the old code at both steps (options parsed, alert cleared). - The reboot-restore install is dropped: the restore plan skips every CLI whose transcript is not `claude-jsonl` (omp's is `omp-jsonl`), so it never ran, and it sat after the start, when omp has already scanned its hooks. - The docs that say which CLIs raise `blocked` and open alert-only items now name omp: api-reference, CLAUDE.md, architecture-invariants, an entry for `approvalHookFile` in cli-registry, and the skill with its plugin mirror (verbs.md listed omp among the modes where `blocked` is a 400, one line above the paragraph that says it works). - Two comments: the schema's file-name rule is a pattern, not a closed list, and the marker check takes the marker anywhere in the file. Co-Authored-By: Claude Fable 5.2 <noreply@anthropic.com>
This commit is contained in:
1 parent
13f1b986af
commit
3eb0689f6d
15 files changed
+79
-46
No files matched your search
@@ -595,6 +595,34 @@ describe('approval routes: approval-only hook module (omp) alert-only items', ()
|
||||
expect(await listApprovals(harness)).toHaveLength(1);
|
||||
});
|
||||
|
||||
it('keeps the alert while a numbered list above the dialog scrolls out of the frame', async () => {
|
||||
// A stand-in for omp's dialog: it has no numbered rows. An agent's plan above it
|
||||
// does, and Claude's dialog parser took that list for the item's options. Once the
|
||||
// list scrolled out of the frame, the next read took the dialog for answered and
|
||||
// cleared the alert while omp was still waiting.
|
||||
const dialog = [' Allow bash?', ' rm -rf build', ' > Approve', ' Deny', ' Deny and send message'].join('\n');
|
||||
const plan = ['Plan:', ' 1. Remove the build folder', ' 2. Run the build', ' 3. Run the tests', ''].join('\n');
|
||||
session.terminalBuffer = `${plan}\n${dialog}`;
|
||||
await postHook(harness, 'permission_prompt', { tool_name: 'bash' });
|
||||
const [item] = await listApprovals(harness);
|
||||
// The frame is kept once, as the card's context, and never read for options.
|
||||
expect(item.options).toBeUndefined();
|
||||
expect(item.context).toContain('Allow bash?');
|
||||
|
||||
const resolved: Array<Record<string, unknown>> = [];
|
||||
approvalInbox.onResolved = (info) => resolved.push({ ...info });
|
||||
session.terminalBuffer = dialog;
|
||||
// Past the delay `notePrompt` waits before it judges a new dialog.
|
||||
vi.spyOn(Date, 'now').mockReturnValue(Date.now() + 3_100);
|
||||
expect(await listApprovals(harness)).toHaveLength(1);
|
||||
expect(await listApprovals(harness)).toHaveLength(1);
|
||||
expect(resolved).toEqual([]);
|
||||
|
||||
// The module's own report is what clears it.
|
||||
await postHook(harness, 'elicitation_complete', { tool_name: 'bash', approved: true });
|
||||
expect(await listApprovals(harness)).toHaveLength(0);
|
||||
});
|
||||
|
||||
it('opens nothing for a remote omp session, where no module was installed', async () => {
|
||||
(session as unknown as { remote: unknown }).remote = { host: 'box' };
|
||||
await postHook(harness, 'permission_prompt', { tool_name: 'bash' });
|
||||
|
||||
Reference in new issue
Block a user