Skip to content

Commit 52ef4ca

Browse files
authored
fix(batch): preserve typed error recovery signals (#1761)
* fix(batch): preserve typed error recovery signals * test(batch): cover typed error recovery signals * fix(batch): omit unknown typed error signals * test(batch): keep unknown typed signals absent
1 parent 74eab2a commit 52ef4ca

2 files changed

Lines changed: 40 additions & 9 deletions

File tree

src/core/__tests__/batch.test.ts

Lines changed: 32 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -58,6 +58,38 @@ function batchRequest(commands: string[], responseLevel?: ResponseLevel): BatchR
5858
};
5959
}
6060

61+
test('batch preserves typed error recovery signals from a failing step', async () => {
62+
const response = await runBatch(batchRequest(['open']), 'session', async () => ({
63+
ok: false,
64+
error: {
65+
code: 'DEVICE_IN_USE',
66+
message: 'device busy',
67+
retriable: true,
68+
supportedOn: 'android, web',
69+
},
70+
}));
71+
72+
assert.equal(response.ok, false);
73+
if (response.ok) return;
74+
assert.equal(response.error.retriable, true);
75+
assert.equal(response.error.supportedOn, 'android, web');
76+
});
77+
78+
test('batch keeps unknown typed error recovery signals absent', async () => {
79+
const response = await runBatch(batchRequest(['open']), 'session', async () => ({
80+
ok: false,
81+
error: {
82+
code: 'INVALID_ARGS',
83+
message: 'bad args',
84+
},
85+
}));
86+
87+
assert.equal(response.ok, false);
88+
if (response.ok) return;
89+
assert.equal('retriable' in response.error, false);
90+
assert.equal('supportedOn' in response.error, false);
91+
});
92+
6193
test('batch elides intermediate steps to digest, final step keeps requested level (full)', async () => {
6294
const seen: (ResponseLevel | undefined)[] = [];
6395
const response = await runBatch(

src/core/batch.ts

Lines changed: 8 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -7,7 +7,7 @@ import {
77
type ResponseLevel,
88
isNonDefaultResponseLevel,
99
} from '@agent-device/kernel/contracts';
10-
import { AppError, asAppError } from '@agent-device/kernel/errors';
10+
import { AppError, asAppError, type DaemonError } from '@agent-device/kernel/errors';
1111
import { isRecord } from '../utils/parsing.ts';
1212
import {
1313
DEFAULT_BATCH_MAX_STEPS,
@@ -105,6 +105,12 @@ export async function runBatch(
105105
hint: stepResponse.error.hint,
106106
diagnosticId: stepResponse.error.diagnosticId,
107107
logPath: stepResponse.error.logPath,
108+
...(stepResponse.error.retriable === undefined
109+
? {}
110+
: { retriable: stepResponse.error.retriable }),
111+
...(stepResponse.error.supportedOn === undefined
112+
? {}
113+
: { supportedOn: stepResponse.error.supportedOn }),
108114
details: {
109115
...(stepResponse.error.details ?? {}),
110116
step: stepResponse.step,
@@ -250,14 +256,7 @@ async function runBatchStep(
250256
| {
251257
ok: false;
252258
step: number;
253-
error: {
254-
code: string;
255-
message: string;
256-
hint?: string;
257-
diagnosticId?: string;
258-
logPath?: string;
259-
details?: Record<string, unknown>;
260-
};
259+
error: DaemonError;
261260
}
262261
> {
263262
const stepStartedAt = Date.now();

0 commit comments

Comments
 (0)