Skip to content

Commit 0ef9fe6

Browse files
committed
fix(mothership): leave an unencodable block output for the projection to refuse
The output bound handles size only. A block output JSON cannot encode cannot be checked, so it is no longer sized past the budget and replaced with a pointer; the projection refuses it as it did before this PR.
1 parent 8f92e6d commit 0ef9fe6

2 files changed

Lines changed: 39 additions & 5 deletions

File tree

‎apps/sim/lib/mothership/tools/handlers/workflow/run-workflow-result-budget.test.ts‎

Lines changed: 30 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -450,4 +450,34 @@ describe('run_workflow model-facing result budget', () => {
450450
expect(output.output).toEqual(expect.stringContaining(`logs get ${EXECUTION_ID} --trace`))
451451
expect(output.outputFrom).toEqual({ blockId: 'query', blockName: 'Query' })
452452
})
453+
454+
/**
455+
* The bound only handles size. An output JSON cannot encode cannot be checked, so the run is still
456+
* refused rather than the value being hidden behind a pointer.
457+
*/
458+
it('still refuses a run with an unencodable block output while bounding bulky ones', async () => {
459+
mocks.executeWorkflowUseCase.mockResolvedValue({
460+
success: true,
461+
output: {},
462+
logs: [
463+
{
464+
blockId: 'big',
465+
blockName: 'Big',
466+
success: true,
467+
output: { rows: tableRows(4_800, 3_200_000) },
468+
},
469+
{ blockId: 'odd', blockName: 'Odd', success: true, output: { count: 1n } },
470+
],
471+
metadata: { executionId: EXECUTION_ID },
472+
})
473+
474+
const settled = await executeRunWorkflowUntilBlock(
475+
{ workflowId: 'wf-1', stopAfterBlockId: 'odd' },
476+
context
477+
)
478+
const presented = settled.output as { output: unknown; logs: Array<{ output: unknown }> }
479+
expect(presented.output).toEqual({ count: 1n })
480+
expect(presented.logs[1]?.output).toEqual({ count: 1n })
481+
expect(inspectToolResultForCopilot(settled, registry, 'run_workflow').safe).toBe(false)
482+
})
453483
})

‎apps/sim/lib/mothership/tools/workflow-output.ts‎

Lines changed: 9 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -122,12 +122,14 @@ interface BlockOutputSize {
122122

123123
/**
124124
* Sizes one block output for the budgets. An output past a projection limit (values, bytes, or
125-
* depth) or one JSON cannot encode would be refused whatever else the result holds, so it is
126-
* sized past every budget and is always the first to be replaced.
125+
* depth) would be refused whatever else the result holds, so it is sized past every budget and is
126+
* always the first to be replaced. One JSON cannot encode is not a size problem: it returns
127+
* undefined and is left for the projection to refuse, as it always has been.
127128
*/
128-
function sizeBlockOutput(output: unknown): BlockOutputSize {
129+
function sizeBlockOutput(output: unknown): BlockOutputSize | undefined {
129130
const measure = measureModelContent(output)
130-
if (!measure || measure.exceeded) {
131+
if (!measure) return undefined
132+
if (measure.exceeded) {
131133
return {
132134
values: MAX_CONTENT_NODES + 1,
133135
bytes: MAX_MODEL_CONTENT_BYTES + 1,
@@ -158,6 +160,7 @@ function compactBlockLogOutputs(logs: unknown, executionId: string | undefined):
158160
for (const [index, entry] of logs.entries()) {
159161
if (!isPlainRecord(entry) || entry.output === undefined) continue
160162
const size = sizeBlockOutput(entry.output)
163+
if (!size) continue
161164
measured.push({ index, entry, ...size })
162165
values += size.values
163166
bytes += size.bytes
@@ -195,5 +198,6 @@ export function boundRunResultForModel(
195198
)
196199
// A result JSON cannot encode is refused whatever its size, so only one past a cap is bounded.
197200
if (!whole?.exceeded) return data
198-
return { ...data, output: blockOutputPointer(sizeBlockOutput(data.output).label, executionId) }
201+
const size = sizeBlockOutput(data.output)
202+
return size ? { ...data, output: blockOutputPointer(size.label, executionId) } : data
199203
}

0 commit comments

Comments
 (0)