mirror of
https://github.com/only-cli/oc.git
synced 2026-09-15 10:40:56 +02:00
Drop fill and submit from the actions footer, they always throw
The footer is the line an agent reads to pick its next command, so a name in it that cannot run costs a turn and returns nothing. fill and submit are still stubs, so every page with an input was offering two commands that throw NotImplemented. Removing them also takes 7 tokens off the rendered news fixture and 6 off login. The test probes act.js for handlers that throw NotImplemented rather than listing them by name, so the next stub to land is covered without anyone remembering to come back here. It checks all three footers: the render, and both of find's paths. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
This commit is contained in:
co-authored by
Claude Opus 5
parent
65322bc5b6
commit
21566143d1
+3
-4
@@ -101,7 +101,6 @@ export function render(page, { budget = 500, from = 0 } = {}) {
|
||||
const lines = [...head];
|
||||
let spent = estimateTokens(lines.join('\n'));
|
||||
let hasLinks = false;
|
||||
let hasInputs = false;
|
||||
let i = Math.max(0, from);
|
||||
|
||||
// What the rest of the page would cost if it were all printed. When that is
|
||||
@@ -129,7 +128,6 @@ export function render(page, { budget = 500, from = 0 } = {}) {
|
||||
spent += cost;
|
||||
lines.push(line);
|
||||
if (block.type === 'link' || block.type === 'button') hasLinks = true;
|
||||
if (block.type === 'input') hasInputs = true;
|
||||
}
|
||||
|
||||
const rest = blocks.slice(i);
|
||||
@@ -140,10 +138,11 @@ export function render(page, { budget = 500, from = 0 } = {}) {
|
||||
if (rest.length) {
|
||||
lines.push(`... ${num(rest.length)} more blocks (~${num(leftTokens)} tokens): 'oc next' for the next ~${num(budget)}, 'oc raw' for all`);
|
||||
}
|
||||
// An input on the page adds no action. 'fill' and 'submit' are still stubs
|
||||
// that throw, and this footer is the line an agent trusts for what to run
|
||||
// next, so naming one of them costs a turn and returns nothing.
|
||||
const actions = [
|
||||
hasLinks && 'do <n>',
|
||||
hasInputs && 'fill <n> <text>',
|
||||
hasInputs && 'submit',
|
||||
'read <n>',
|
||||
rest.length && 'next',
|
||||
'raw',
|
||||
|
||||
@@ -244,3 +244,41 @@ test('a snippet stays one line even when the block it came from is code', () =>
|
||||
assert.match(lines[0], /^2 matches for "needle"/);
|
||||
assert.equal(lines[1], '[1] first(); needle(); third();');
|
||||
});
|
||||
|
||||
test('no footer names a command that is not available yet', async () => {
|
||||
// The footer is the line an agent reads to decide what to run next, so a
|
||||
// name in it that always throws costs a turn and returns nothing. Which
|
||||
// commands are stubs is probed here rather than listed, so the next stub to
|
||||
// land is covered without anyone remembering to come back and add it.
|
||||
const act = await import('../src/act.js');
|
||||
const stubs = Object.entries(act)
|
||||
.filter(([, value]) => typeof value === 'function')
|
||||
.filter(([, fn]) => {
|
||||
try {
|
||||
fn();
|
||||
return false;
|
||||
} catch (err) {
|
||||
return err instanceof act.NotImplemented;
|
||||
}
|
||||
})
|
||||
.map(([name]) => name);
|
||||
assert.ok(stubs.length, 'the probe found no stubs, so it is no longer testing anything');
|
||||
|
||||
open();
|
||||
const loginHTML = readFileSync(new URL('./pages/login.html', import.meta.url), 'utf8');
|
||||
// One output per place that builds a footer: a render with inputs, which is
|
||||
// what used to offer fill and submit, and both of find's paths.
|
||||
const outputs = [
|
||||
render(page(), { budget: 500 }).text,
|
||||
render(distill(loginHTML, 'https://example.test/login'), { budget: 500 }).text,
|
||||
find('postgres'),
|
||||
find('a'),
|
||||
];
|
||||
const footers = outputs.flatMap((out) => out.split('\n').filter((line) => line.startsWith('actions:')));
|
||||
assert.equal(footers.length, outputs.length, `every output should carry one footer:\n${footers.join('\n')}`);
|
||||
for (const footer of footers) {
|
||||
for (const stub of stubs) {
|
||||
assert.ok(!footer.includes(stub), `footer offers '${stub}', which throws NotImplemented:\n${footer}`);
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
Reference in New Issue
Block a user