mirror of
https://github.com/Ark0N/Codeman.git
synced 2026-11-15 10:59:32 +01:00
fix(story-mode): no input shape blocks the server: linear tag and commit scans
Every string Story Mode matches is agent-written (and an agent can be prompt-injected), and one regex call blocks the single-threaded server. Timing each helper at its own input cap with crafted shapes found five that were quadratic or worse: - A lazy `<tag>[\s\S]*?</tag>` rescans to the end for every opener that never closes: system reminders, teammate messages and task-notification fields took 1.5 to 29 s on 0.5 to 1 MB of prompt text. They are found with indexOf now (withoutBlocks, blocks, blockText1). - The commit message regex backtracked over many `git commit` tokens (290 ms at 4,000 characters, growing x8.7 per doubling) and ran on the whole command. It is a bounded line-by-line scan with the same rules. - The "This command modified …" note was searched with a lazy match per repeat of the phrase (7 s on 1 MB of output). It is one anchored try at its last occurrence, where Claude Code appends it. - firstSentence and planFromText now read only the start they need. The worst shape is now under 10 ms at 1 MB. A test times five of them at sizes where the old code took a second or more. Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
This commit is contained in:
1 parent
67595ea980
commit
21de40309d
2 files changed
+128
-20
No files matched your search
@@ -971,6 +971,31 @@ describe('buildStoryPage', () => {
|
||||
expect(pageOf([prompt('c'), ...bash('b1', 'git commit -qm x', 'Commit', ''), end(1000)]).events[1].commits).toBe(1);
|
||||
});
|
||||
|
||||
it('crafted agent text never takes seconds: every helper stays linear on its worst shapes', () => {
|
||||
// Each shape took one to several seconds before (a lazy regex rescanning per opener, or a
|
||||
// backtracking one over a long one-liner); a single call blocks the whole server.
|
||||
const rep = (str: string, n: number) => str.repeat(Math.ceil(n / str.length)).slice(0, n);
|
||||
const ms = (fn: () => unknown) => {
|
||||
const start = performance.now();
|
||||
fn();
|
||||
return performance.now() - start;
|
||||
};
|
||||
const prompt1 = (content: string) => () => buildStoryTurns(entriesOf([prompt(content), said('ok'), end(1000)]));
|
||||
const bashOut = (command: string, output: string) => () =>
|
||||
pageOf([prompt('go'), ...bash('b1', command, 'x', output), end(1000)]);
|
||||
const shapes: Array<[string, () => unknown]> = [
|
||||
['teammate openers', prompt1(rep('<teammate-message teammate_id="a">x ', 400_000))],
|
||||
['system-reminder openers', prompt1('hi ' + rep('<system-reminder>x ', 400_000))],
|
||||
['task-notification field openers', prompt1('<task-notification>\n' + rep('<summary>x ', 400_000))],
|
||||
['git commit tokens', bashOut(rep('git commit ', 8000), 'ok')],
|
||||
[
|
||||
'modified-note phrases',
|
||||
bashOut('python3 x.py', rep("This command modified 1 file you've previously read: a", 400_000)),
|
||||
],
|
||||
];
|
||||
for (const [name, fn] of shapes) expect([name, ms(fn) < 250]).toEqual([name, true]);
|
||||
});
|
||||
|
||||
it('a python or node script fed by a here-document: the files it opens for writing, never guessed', () => {
|
||||
const files = (command: string) => heredocWrites(command, '/w').map((d) => d.file);
|
||||
expect(
|
||||
|
||||
Reference in new issue
Block a user