Skip to content

Commit 23637c2

Browse files
grypezclaude
andcommitted
address class identity mismatch
Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
1 parent 89da1db commit 23637c2

7 files changed

Lines changed: 92 additions & 4 deletions

File tree

packages/kernel-agents/src/attempt.ts

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
import { SampleGenerationError } from '@metamask/kernel-errors/bundleable';
1+
import { isSampleGenerationError } from '@metamask/kernel-errors/bundleable';
22
import type { Logger } from '@metamask/logger';
33
import type { LanguageModel } from '@ocap/kernel-language-model-service';
44

@@ -44,7 +44,7 @@ export const doAttempt = async <
4444
return [action, outcome];
4545
},
4646
maxRetries,
47-
(error) => error instanceof SampleGenerationError,
47+
(error) => isSampleGenerationError(error),
4848
);
4949

5050
// If done, exit

packages/kernel-errors/src/bundleable/index.ts

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -8,4 +8,8 @@
88
* import { SampleGenerationError } from '@metamask/kernel-errors/bundleable';
99
*/
1010
export { SampleGenerationError } from './SampleGenerationError.ts';
11-
export { ErrorCode, ErrorSentinel } from '../error-codes.ts';
11+
export {
12+
ErrorCode,
13+
ErrorSentinel,
14+
isSampleGenerationError,
15+
} from '../error-codes.ts';

packages/kernel-errors/src/constants.ts

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -14,7 +14,11 @@ import { ErrorCode, ErrorSentinel } from './error-codes.ts';
1414
import type { MarshaledError, MarshaledOcapError } from './types.ts';
1515

1616
// Re-export for backward compatibility
17-
export { ErrorCode, ErrorSentinel } from './error-codes.ts';
17+
export {
18+
ErrorCode,
19+
ErrorSentinel,
20+
isSampleGenerationError,
21+
} from './error-codes.ts';
1822

1923
/**
2024
* Struct to validate plain {@link Error} objects.
Lines changed: 57 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,57 @@
1+
import { describe, it, expect } from 'vitest';
2+
3+
import { SampleGenerationError as BundleableSampleGenerationError } from './bundleable/SampleGenerationError.ts';
4+
import { ErrorCode, isSampleGenerationError } from './error-codes.ts';
5+
import { SampleGenerationError as MainSampleGenerationError } from './errors/SampleGenerationError.ts';
6+
7+
describe('error-codes', () => {
8+
describe('isSampleGenerationError', () => {
9+
it('returns true for bundleable SampleGenerationError', () => {
10+
const error = new BundleableSampleGenerationError(
11+
'invalid sample',
12+
new Error('parse failed'),
13+
);
14+
expect(isSampleGenerationError(error)).toBe(true);
15+
});
16+
17+
it('returns true for main SampleGenerationError', () => {
18+
const error = new MainSampleGenerationError(
19+
'invalid sample',
20+
new Error('parse failed'),
21+
);
22+
expect(isSampleGenerationError(error)).toBe(true);
23+
});
24+
25+
it('returns false for plain Error', () => {
26+
const error = new Error('some error');
27+
expect(isSampleGenerationError(error)).toBe(false);
28+
});
29+
30+
it('returns false for Error with different code', () => {
31+
const error = new Error('some error');
32+
(error as Error & { code: string }).code = ErrorCode.AbortError;
33+
expect(isSampleGenerationError(error)).toBe(false);
34+
});
35+
36+
it('returns false for non-Error values', () => {
37+
expect(isSampleGenerationError(null)).toBe(false);
38+
expect(isSampleGenerationError(undefined)).toBe(false);
39+
expect(isSampleGenerationError('error string')).toBe(false);
40+
expect(
41+
isSampleGenerationError({ code: ErrorCode.SampleGenerationError }),
42+
).toBe(false);
43+
});
44+
45+
it('narrows type correctly', () => {
46+
const error = new BundleableSampleGenerationError(
47+
'test sample',
48+
new Error('cause'),
49+
);
50+
51+
expect(isSampleGenerationError(error)).toBe(true);
52+
// After the check above passes, TypeScript allows accessing these properties
53+
expect(error.code).toBe(ErrorCode.SampleGenerationError);
54+
expect(error.data.sample).toBe('test sample');
55+
});
56+
});
57+
});

packages/kernel-errors/src/error-codes.ts

Lines changed: 21 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -20,3 +20,24 @@ export type ErrorCode = (typeof ErrorCode)[keyof typeof ErrorCode];
2020
* A sentinel value used to identify marshaled errors.
2121
*/
2222
export const ErrorSentinel = '@@MARSHALED_ERROR';
23+
24+
/**
25+
* Type guard to check if an error is a SampleGenerationError.
26+
* Uses error code checking instead of instanceof to work across
27+
* package boundaries where different class objects may be used.
28+
*
29+
* @param error - The error to check.
30+
* @returns True if the error has the SampleGenerationError code.
31+
*/
32+
export const isSampleGenerationError = (
33+
error: unknown,
34+
): error is Error & {
35+
code: typeof ErrorCode.SampleGenerationError;
36+
data: { sample: string };
37+
} => {
38+
return (
39+
error instanceof Error &&
40+
'code' in error &&
41+
error.code === ErrorCode.SampleGenerationError
42+
);
43+
};

packages/kernel-errors/src/index.test.ts

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -23,6 +23,7 @@ describe('index', () => {
2323
'isMarshaledError',
2424
'isMarshaledOcapError',
2525
'isOcapError',
26+
'isSampleGenerationError',
2627
'marshalError',
2728
'toError',
2829
'unmarshalError',

packages/kernel-errors/src/index.ts

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -13,6 +13,7 @@ export {
1313
ErrorCode,
1414
ErrorSentinel,
1515
ErrorStruct,
16+
isSampleGenerationError,
1617
MarshaledErrorStruct,
1718
MarshaledOcapErrorStruct,
1819
} from './constants.ts';

0 commit comments

Comments
 (0)