Skip to content
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion packages/kernel-agents/src/agent.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
import { mergeDisjointRecords } from '@metamask/kernel-utils';
import { mergeDisjointRecords } from '@metamask/kernel-utils/merge-disjoint-records';
import type { Logger } from '@metamask/logger';
import type { LanguageModel } from '@ocap/kernel-language-model-service';

Expand Down
4 changes: 2 additions & 2 deletions packages/kernel-agents/src/attempt.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
import { SampleGenerationError } from '@metamask/kernel-errors';
import { isSampleGenerationError } from '@metamask/kernel-errors/bundleable';
import type { Logger } from '@metamask/logger';
import type { LanguageModel } from '@ocap/kernel-language-model-service';

Expand Down Expand Up @@ -44,7 +44,7 @@ export const doAttempt = async <
return [action, outcome];
},
maxRetries,
(error) => error instanceof SampleGenerationError,
(error) => isSampleGenerationError(error),
);

// If done, exit
Expand Down
3 changes: 2 additions & 1 deletion packages/kernel-agents/src/capabilities/discover.ts
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
import { E } from '@endo/eventual-send';
import type { DiscoverableExo, MethodSchema } from '@metamask/kernel-utils';
import type { DiscoverableExo } from '@metamask/kernel-utils/discoverable';
import type { MethodSchema } from '@metamask/kernel-utils/schema';

import type { CapabilityRecord, CapabilitySpec } from '../types.ts';

Expand Down
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
import { mergeDisjointRecords } from '@metamask/kernel-utils';
import { mergeDisjointRecords } from '@metamask/kernel-utils/merge-disjoint-records';
import type { Logger } from '@metamask/logger';

import { makeEvaluator } from './evaluator.ts';
Expand Down
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
import { SampleGenerationError } from '@metamask/kernel-errors';
import { SampleGenerationError } from '@metamask/kernel-errors/bundleable';
import type { Logger } from '@metamask/logger';

import type { SampleCollector } from '../../types.ts';
Expand Down
2 changes: 1 addition & 1 deletion packages/kernel-agents/src/task.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
import { makeCounter } from '@metamask/kernel-utils';
import { makeCounter } from '@metamask/kernel-utils/counter';

import type { Task, CapabilityRecord } from './types.ts';

Expand Down
2 changes: 1 addition & 1 deletion packages/kernel-agents/src/types/capability.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
import type { JsonSchema } from '@metamask/kernel-utils';
import type { JsonSchema } from '@metamask/kernel-utils/schema';

export type Capability<Args extends Record<string, unknown>, Return = null> = (
args: Args,
Expand Down
11 changes: 10 additions & 1 deletion packages/kernel-errors/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -29,6 +29,16 @@
"default": "./dist/index.cjs"
}
},
"./bundleable": {
"import": {
"types": "./dist/bundleable/index.d.mts",
"default": "./dist/bundleable/index.mjs"
},
"require": {
"types": "./dist/bundleable/index.d.cts",
"default": "./dist/bundleable/index.cjs"
}
},
"./package.json": "./package.json"
},
"main": "./dist/index.cjs",
Expand Down Expand Up @@ -57,7 +67,6 @@
"test:dev:quiet": "yarn test:dev --reporter dot"
},
"dependencies": {
"@libp2p/interface": "2.11.0",
"@metamask/superstruct": "^3.2.1",
"@metamask/utils": "^11.9.0"
},
Expand Down
2 changes: 1 addition & 1 deletion packages/kernel-errors/src/BaseError.ts
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
import type { Json } from '@metamask/utils';

import { ErrorCode } from './constants.ts';
import { ErrorCode } from './error-codes.ts';
import type {
MarshaledOcapError,
OcapError,
Expand Down
46 changes: 46 additions & 0 deletions packages/kernel-errors/src/bundleable/SampleGenerationError.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,46 @@
import { ErrorCode } from '../error-codes.ts';

/**
* Error options with optional stack trace.
*/
type ErrorOptionsWithStack = {
cause?: Error;
stack?: string;
};

/**
* Bundleable version of SampleGenerationError.
* This version does not include marshaling logic to avoid dependencies
* on @metamask/utils that prevent vat bundling.
*
* An error indicating that the LLM generated invalid response.
* This error should trigger resampling from the LLM.
*/
export class SampleGenerationError extends Error {
public readonly code: typeof ErrorCode.SampleGenerationError;

public readonly data: { sample: string };

/**
* Creates a new SampleGenerationError.
*
* @param sample - The invalid sample text generated by the LLM.
* @param cause - The underlying error that caused sample generation to fail.
* @param options - Additional error options including stack.
* @param options.stack - The stack trace of the error.
*/
constructor(sample: string, cause: Error, options?: ErrorOptionsWithStack) {
super('LLM generated invalid response.', { cause });

this.name = 'SampleGenerationError';
this.code = ErrorCode.SampleGenerationError;
this.data = { sample };

if (options?.stack) {
this.stack = options.stack;
}

harden(this);
}
}
harden(SampleGenerationError);
15 changes: 15 additions & 0 deletions packages/kernel-errors/src/bundleable/index.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
/**
* Bundleable versions of kernel-errors.
*
* These versions do not include marshaling logic to avoid dependencies
* on @metamask/utils that prevent vat bundling with @endo/bundle-source.
*
* Use these exports when bundling vat code:
* import { SampleGenerationError } from '@metamask/kernel-errors/bundleable';
*/
export { SampleGenerationError } from './SampleGenerationError.ts';
export {
ErrorCode,
ErrorSentinel,
isSampleGenerationError,
} from '../error-codes.ts';
31 changes: 8 additions & 23 deletions packages/kernel-errors/src/constants.ts
Original file line number Diff line number Diff line change
Expand Up @@ -10,8 +10,16 @@ import {
import { JsonStruct, object } from '@metamask/utils';
import type { NonEmptyArray } from '@metamask/utils';

import { ErrorCode, ErrorSentinel } from './error-codes.ts';
import type { MarshaledError, MarshaledOcapError } from './types.ts';

// Re-export for backward compatibility
export {
ErrorCode,
ErrorSentinel,
isSampleGenerationError,
} from './error-codes.ts';

/**
* Struct to validate plain {@link Error} objects.
*/
Expand All @@ -20,29 +28,6 @@ export const ErrorStruct = define<Error>(
(value) => value instanceof Error,
);

/**
* Enum defining all error codes for Ocap errors.
*/
export const ErrorCode = {
AbortError: 'ABORT_ERROR',
DuplicateEndowment: 'DUPLICATE_ENDOWMENT',
StreamReadError: 'STREAM_READ_ERROR',
VatAlreadyExists: 'VAT_ALREADY_EXISTS',
VatDeleted: 'VAT_DELETED',
VatNotFound: 'VAT_NOT_FOUND',
SubclusterNotFound: 'SUBCLUSTER_NOT_FOUND',
SampleGenerationError: 'SAMPLE_GENERATION_ERROR',
InternalError: 'INTERNAL_ERROR',
ResourceLimitError: 'RESOURCE_LIMIT_ERROR',
} as const;

export type ErrorCode = (typeof ErrorCode)[keyof typeof ErrorCode];

/**
* A sentinel value used to identify marshaled errors.
*/
export const ErrorSentinel = '@@MARSHALED_ERROR';

const ErrorCodeStruct = union(
Object.values(ErrorCode).map((code) => literal(code)) as NonEmptyArray<
Struct<ErrorCode>
Expand Down
57 changes: 57 additions & 0 deletions packages/kernel-errors/src/error-codes.test.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,57 @@
import { describe, it, expect } from 'vitest';

import { SampleGenerationError as BundleableSampleGenerationError } from './bundleable/SampleGenerationError.ts';
import { ErrorCode, isSampleGenerationError } from './error-codes.ts';
import { SampleGenerationError as MainSampleGenerationError } from './errors/SampleGenerationError.ts';

describe('error-codes', () => {
describe('isSampleGenerationError', () => {
it('returns true for bundleable SampleGenerationError', () => {
const error = new BundleableSampleGenerationError(
'invalid sample',
new Error('parse failed'),
);
expect(isSampleGenerationError(error)).toBe(true);
});

it('returns true for main SampleGenerationError', () => {
const error = new MainSampleGenerationError(
'invalid sample',
new Error('parse failed'),
);
expect(isSampleGenerationError(error)).toBe(true);
});

it('returns false for plain Error', () => {
const error = new Error('some error');
expect(isSampleGenerationError(error)).toBe(false);
});

it('returns false for Error with different code', () => {
const error = new Error('some error');
(error as Error & { code: string }).code = ErrorCode.AbortError;
expect(isSampleGenerationError(error)).toBe(false);
});

it('returns false for non-Error values', () => {
expect(isSampleGenerationError(null)).toBe(false);
expect(isSampleGenerationError(undefined)).toBe(false);
expect(isSampleGenerationError('error string')).toBe(false);
expect(
isSampleGenerationError({ code: ErrorCode.SampleGenerationError }),
).toBe(false);
});

it('narrows type correctly', () => {
const error = new BundleableSampleGenerationError(
'test sample',
new Error('cause'),
);

expect(isSampleGenerationError(error)).toBe(true);
// After the check above passes, TypeScript allows accessing these properties
expect(error.code).toBe(ErrorCode.SampleGenerationError);
expect(error.data.sample).toBe('test sample');
});
});
});
43 changes: 43 additions & 0 deletions packages/kernel-errors/src/error-codes.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,43 @@
/**
* Enum defining all error codes for Ocap errors.
*/
export const ErrorCode = {
AbortError: 'ABORT_ERROR',
DuplicateEndowment: 'DUPLICATE_ENDOWMENT',
StreamReadError: 'STREAM_READ_ERROR',
VatAlreadyExists: 'VAT_ALREADY_EXISTS',
VatDeleted: 'VAT_DELETED',
VatNotFound: 'VAT_NOT_FOUND',
SubclusterNotFound: 'SUBCLUSTER_NOT_FOUND',
SampleGenerationError: 'SAMPLE_GENERATION_ERROR',
InternalError: 'INTERNAL_ERROR',
ResourceLimitError: 'RESOURCE_LIMIT_ERROR',
} as const;

export type ErrorCode = (typeof ErrorCode)[keyof typeof ErrorCode];

/**
* A sentinel value used to identify marshaled errors.
*/
export const ErrorSentinel = '@@MARSHALED_ERROR';

/**
* Type guard to check if an error is a SampleGenerationError.
* Uses error code checking instead of instanceof to work across
* package boundaries where different class objects may be used.
*
* @param error - The error to check.
* @returns True if the error has the SampleGenerationError code.
*/
export const isSampleGenerationError = (
error: unknown,
): error is Error & {
code: typeof ErrorCode.SampleGenerationError;
data: { sample: string };
} => {
return (
error instanceof Error &&
'code' in error &&
error.code === ErrorCode.SampleGenerationError
);
};
2 changes: 1 addition & 1 deletion packages/kernel-errors/src/index.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -23,7 +23,7 @@ describe('index', () => {
'isMarshaledError',
'isMarshaledOcapError',
'isOcapError',
'isRetryableNetworkError',
'isSampleGenerationError',
'marshalError',
'toError',
'unmarshalError',
Expand Down
2 changes: 1 addition & 1 deletion packages/kernel-errors/src/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,7 @@ export {
ErrorCode,
ErrorSentinel,
ErrorStruct,
isSampleGenerationError,
MarshaledErrorStruct,
MarshaledOcapErrorStruct,
} from './constants.ts';
Expand All @@ -22,4 +23,3 @@ export { marshalError } from './marshal/marshalError.ts';
export { unmarshalError } from './marshal/unmarshalError.ts';
export { isMarshaledError } from './marshal/isMarshaledError.ts';
export { isMarshaledOcapError } from './marshal/isMarshaledOcapError.ts';
export { isRetryableNetworkError } from './utils/isRetryableNetworkError.ts';
30 changes: 30 additions & 0 deletions packages/kernel-utils/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -49,6 +49,36 @@
"default": "./dist/discoverable.cjs"
}
},
"./counter": {
"import": {
"types": "./dist/counter.d.mts",
"default": "./dist/counter.mjs"
},
"require": {
"types": "./dist/counter.d.cts",
"default": "./dist/counter.cjs"
}
},
"./merge-disjoint-records": {
"import": {
"types": "./dist/merge-disjoint-records.d.mts",
"default": "./dist/merge-disjoint-records.mjs"
},
"require": {
"types": "./dist/merge-disjoint-records.d.cts",
"default": "./dist/merge-disjoint-records.cjs"
}
},
"./schema": {
"import": {
"types": "./dist/schema.d.mts",
"default": "./dist/schema.mjs"
},
"require": {
"types": "./dist/schema.d.cts",
"default": "./dist/schema.cjs"
}
},
"./package.json": "./package.json"
},
"main": "./dist/index.cjs",
Expand Down
13 changes: 13 additions & 0 deletions packages/kernel-utils/src/counter.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
/**
* A simple counter which increments and returns when called.
*
* @param start - One less than the first returned number.
* @returns A counter.
*/
export const makeCounter = (start: number = 0) => {
let counter: number = start;
return () => {
counter += 1;
return counter;
};
};
15 changes: 2 additions & 13 deletions packages/kernel-utils/src/misc.ts
Original file line number Diff line number Diff line change
@@ -1,18 +1,7 @@
import { AbortError } from '@metamask/kernel-errors';

/**
* A simple counter which increments and returns when called.
*
* @param start - One less than the first returned number.
* @returns A counter.
*/
export const makeCounter = (start: number = 0) => {
let counter: number = start;
return () => {
counter += 1;
return counter;
};
};
// Re-export makeCounter for backward compatibility
export { makeCounter } from './counter.ts';

/**
* Delay execution by the specified number of milliseconds.
Expand Down
Loading
Loading