|
|
@@ -402,16 +402,23 @@ export async function launchWebScaffold(options: LaunchOptions = {}): Promise<We
|
|
|
throw new Error('replayProvidersOnly requires replayFixture (its file supplies the header)')
|
|
|
}
|
|
|
const fixtureText = readFileSync(options.replayFixture, 'utf8')
|
|
|
+ // The consumption check is skipped for this mode, so no script source
|
|
|
+ // may carry callable entries: reject override/child sources outright
|
|
|
+ // and any call-bearing fixture.
|
|
|
+ if (options.replayOverride !== undefined || options.replayChildFixtures !== undefined) {
|
|
|
+ throw new Error('replayProvidersOnly cannot combine with replayOverride or replayChildFixtures')
|
|
|
+ }
|
|
|
// A fixture without a session header row must not mount the catalog
|
|
|
// silently: the consumption-skip assumes the header-only shape.
|
|
|
- if (!fixtureText.trimStart().startsWith('{"type":"session"')) {
|
|
|
- throw new Error('replayProvidersOnly fixture must open with a session header row')
|
|
|
+ let headerType: unknown
|
|
|
+ try {
|
|
|
+ headerType = (JSON.parse(fixtureText.trimStart().split('\n', 1)[0] ?? '') as { type?: unknown }).type
|
|
|
+ } catch {
|
|
|
+ headerType = undefined
|
|
|
}
|
|
|
- if (options.replayOverride !== undefined || options.replayChildFixtures !== undefined) {
|
|
|
- throw new Error('replayProvidersOnly cannot combine with replayOverride or replayChildFixtures')
|
|
|
+ if (headerType !== 'session') {
|
|
|
+ throw new Error('replayProvidersOnly fixture must open with a session header row')
|
|
|
}
|
|
|
- // The consumption check is skipped for this mode, so a fixture that
|
|
|
- // records model calls would silently go unconsumed: reject one here.
|
|
|
const recorded = parseSessionLog(fixtureText)
|
|
|
const hasModelCall = recorded.some(event => (
|
|
|
event.type === 'assistant/chunk' || event.type === 'request/header' || event.type === 'tool/call'
|