mirror of
https://github.com/garrytan/gstack.git
synced 2026-11-04 07:47:27 +01:00
fix: relay normalizes CRLF across chunk boundaries (outside voice)
Co-authored-by: capy-ai[bot] <230910855+capy-ai[bot]@users.noreply.github.com>
This commit is contained in:
1 parent
7458ae67fa
commit
f6d81ddba8
2 files changed
+23
-2
No files matched your search
@@ -2,7 +2,7 @@ import { expect, test } from 'bun:test';
|
||||
import * as fs from 'node:fs';
|
||||
import * as os from 'node:os';
|
||||
import * as path from 'node:path';
|
||||
import { closePacketReadAfterNotice, dispatchPromptDelta, dispatchToken, prepareGuardSession, startDispatchRelay, stoppedAtPhaseBoundary } from './helpers/autoplan-guard-pty';
|
||||
import { closePacketReadAfterNotice, dispatchEventRewriter, dispatchPromptDelta, dispatchToken, prepareGuardSession, startDispatchRelay, stoppedAtPhaseBoundary } from './helpers/autoplan-guard-pty';
|
||||
import { codeOf, guardFixture } from './helpers/autoplan-guard-fixture';
|
||||
|
||||
const base = '/home/runner/.cache/gstack-paid-shard-x/tmp/ap-guard-pty-y';
|
||||
@@ -155,3 +155,24 @@ test('the dispatch relay hands the guard the exact dispatch for the declared tok
|
||||
f.cleanup(); retypedFixture.cleanup(); fs.rmSync(artifacts, { recursive: true, force: true });
|
||||
}
|
||||
});
|
||||
|
||||
// /ship outside voice on PR #3953: a CRLF stream whose \r and \n land in separate chunks kept the token unsubstituted.
|
||||
test('the event rewriter substitutes the token however LF or CRLF delimiters are split across chunks', () => {
|
||||
const artifacts = fs.mkdtempSync(path.join(os.tmpdir(), 'ap-guard-crlf-'));
|
||||
try {
|
||||
fs.writeFileSync(path.join(artifacts, 'dispatch-ceo.txt'), 'exact dispatch');
|
||||
const json = JSON.stringify({ description: 'CEO review', prompt: dispatchToken('ceo') });
|
||||
const lf = [{ type: 'content_block_start', index: 0, content_block: { type: 'tool_use', id: 't', name: 'Agent', input: {} } },
|
||||
{ type: 'content_block_delta', index: 0, delta: { type: 'input_json_delta', partial_json: json } },
|
||||
{ type: 'content_block_stop', index: 0 }].map(e => `event: ${e.type}\ndata: ${JSON.stringify(e)}\n\n`).join('');
|
||||
for (const stream of [lf, lf.replace(/\n/g, '\r\n')]) for (const size of [1, 2, 3, 7, stream.length]) {
|
||||
const substituted: string[] = [];
|
||||
const rewrite = dispatchEventRewriter(artifacts, phase => substituted.push(phase));
|
||||
let out = '';
|
||||
for (let i = 0; i < stream.length; i += size) out += rewrite.push(stream.slice(i, i + size));
|
||||
out += rewrite.end();
|
||||
expect({ crlf: stream !== lf, size, substituted }).toEqual({ crlf: stream !== lf, size, substituted: ['ceo'] });
|
||||
expect(out).toContain(JSON.stringify(JSON.stringify({ description: 'CEO review', prompt: 'exact dispatch' })).slice(1, -1));
|
||||
}
|
||||
} finally { fs.rmSync(artifacts, { recursive: true, force: true }); }
|
||||
});
|
||||
@@ -135,7 +135,7 @@ export function dispatchEventRewriter(artifacts: string, substituted: (phase: Di
|
||||
};
|
||||
return {
|
||||
push(chunk: string): string {
|
||||
pending += chunk.replace(/\r\n/g, '\n');
|
||||
pending = (pending + chunk).replace(/\r\n/g, '\n');
|
||||
const events = pending.split('\n\n');
|
||||
pending = events.pop()!;
|
||||
return events.map(e => take(e + '\n\n')).join('');
|
||||
|
||||
Reference in new issue
Block a user