@positronic/core 0.0.1 → 0.0.3

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (79) hide show
  1. package/CLAUDE.md +141 -0
  2. package/dist/src/adapters/types.js +1 -16
  3. package/dist/src/clients/types.js +4 -1
  4. package/dist/src/dsl/brain-runner.js +487 -0
  5. package/dist/src/dsl/brain-runner.test.js +733 -0
  6. package/dist/src/dsl/brain.js +1128 -0
  7. package/dist/src/dsl/brain.test.js +4225 -0
  8. package/dist/src/dsl/constants.js +6 -6
  9. package/dist/src/dsl/json-patch.js +37 -9
  10. package/dist/src/index.js +11 -10
  11. package/dist/src/resources/resources.js +371 -0
  12. package/dist/src/test-utils.js +474 -0
  13. package/dist/src/testing.js +3 -0
  14. package/dist/types/adapters/types.d.ts +3 -8
  15. package/dist/types/adapters/types.d.ts.map +1 -1
  16. package/dist/types/clients/types.d.ts +46 -6
  17. package/dist/types/clients/types.d.ts.map +1 -1
  18. package/dist/types/dsl/brain-runner.d.ts +24 -0
  19. package/dist/types/dsl/brain-runner.d.ts.map +1 -0
  20. package/dist/types/dsl/brain.d.ts +136 -0
  21. package/dist/types/dsl/brain.d.ts.map +1 -0
  22. package/dist/types/dsl/constants.d.ts +5 -5
  23. package/dist/types/dsl/constants.d.ts.map +1 -1
  24. package/dist/types/dsl/json-patch.d.ts +2 -1
  25. package/dist/types/dsl/json-patch.d.ts.map +1 -1
  26. package/dist/types/index.d.ts +13 -11
  27. package/dist/types/index.d.ts.map +1 -1
  28. package/dist/types/resources/resource-loader.d.ts +6 -0
  29. package/dist/types/resources/resource-loader.d.ts.map +1 -0
  30. package/dist/types/resources/resources.d.ts +23 -0
  31. package/dist/types/resources/resources.d.ts.map +1 -0
  32. package/dist/types/test-utils.d.ts +94 -0
  33. package/dist/types/test-utils.d.ts.map +1 -0
  34. package/dist/types/testing.d.ts +2 -0
  35. package/dist/types/testing.d.ts.map +1 -0
  36. package/docs/core-testing-guide.md +289 -0
  37. package/package.json +26 -7
  38. package/src/adapters/types.ts +3 -22
  39. package/src/clients/types.ts +50 -10
  40. package/src/dsl/brain-runner.test.ts +384 -0
  41. package/src/dsl/brain-runner.ts +111 -0
  42. package/src/dsl/brain.test.ts +1981 -0
  43. package/src/dsl/brain.ts +740 -0
  44. package/src/dsl/constants.ts +6 -6
  45. package/src/dsl/json-patch.ts +24 -9
  46. package/src/dsl/types.ts +1 -1
  47. package/src/index.ts +30 -16
  48. package/src/resources/resource-loader.ts +8 -0
  49. package/src/resources/resources.ts +267 -0
  50. package/src/test-utils.ts +254 -0
  51. package/test/resources.test.ts +248 -0
  52. package/tsconfig.json +2 -2
  53. package/.swcrc +0 -31
  54. package/dist/src/dsl/extensions.js +0 -19
  55. package/dist/src/dsl/workflow-runner.js +0 -93
  56. package/dist/src/dsl/workflow.js +0 -308
  57. package/dist/src/file-stores/local-file-store.js +0 -12
  58. package/dist/src/utils/temp-files.js +0 -27
  59. package/dist/types/dsl/extensions.d.ts +0 -18
  60. package/dist/types/dsl/extensions.d.ts.map +0 -1
  61. package/dist/types/dsl/workflow-runner.d.ts +0 -28
  62. package/dist/types/dsl/workflow-runner.d.ts.map +0 -1
  63. package/dist/types/dsl/workflow.d.ts +0 -118
  64. package/dist/types/dsl/workflow.d.ts.map +0 -1
  65. package/dist/types/file-stores/local-file-store.d.ts +0 -7
  66. package/dist/types/file-stores/local-file-store.d.ts.map +0 -1
  67. package/dist/types/file-stores/types.d.ts +0 -4
  68. package/dist/types/file-stores/types.d.ts.map +0 -1
  69. package/dist/types/utils/temp-files.d.ts +0 -12
  70. package/dist/types/utils/temp-files.d.ts.map +0 -1
  71. package/src/dsl/extensions.ts +0 -58
  72. package/src/dsl/workflow-runner.test.ts +0 -203
  73. package/src/dsl/workflow-runner.ts +0 -146
  74. package/src/dsl/workflow.test.ts +0 -1435
  75. package/src/dsl/workflow.ts +0 -554
  76. package/src/file-stores/local-file-store.ts +0 -11
  77. package/src/file-stores/types.ts +0 -3
  78. package/src/utils/temp-files.ts +0 -46
  79. /package/dist/src/{file-stores/types.js → resources/resource-loader.js} +0 -0
@@ -0,0 +1,136 @@
1
+ import { z } from 'zod';
2
+ import type { ObjectGenerator } from '../clients/types.js';
3
+ import type { State, JsonPatch } from './types.js';
4
+ import { STATUS, BRAIN_EVENTS } from './constants.js';
5
+ import type { Resources } from '../resources/resources.js';
6
+ export type SerializedError = {
7
+ name: string;
8
+ message: string;
9
+ stack?: string;
10
+ };
11
+ interface BaseEvent<TOptions extends object = object> {
12
+ type: (typeof BRAIN_EVENTS)[keyof typeof BRAIN_EVENTS];
13
+ options: TOptions;
14
+ brainRunId: string;
15
+ }
16
+ interface BrainBaseEvent<TOptions extends object = object> extends BaseEvent<TOptions> {
17
+ brainTitle: string;
18
+ brainDescription?: string;
19
+ }
20
+ export interface BrainStartEvent<TOptions extends object = object> extends BrainBaseEvent<TOptions> {
21
+ type: typeof BRAIN_EVENTS.START | typeof BRAIN_EVENTS.RESTART;
22
+ initialState: State;
23
+ status: typeof STATUS.RUNNING;
24
+ }
25
+ export interface BrainCompleteEvent<TOptions extends object = object> extends BrainBaseEvent<TOptions> {
26
+ type: typeof BRAIN_EVENTS.COMPLETE;
27
+ status: typeof STATUS.COMPLETE;
28
+ }
29
+ export interface BrainErrorEvent<TOptions extends object = object> extends BrainBaseEvent<TOptions> {
30
+ type: typeof BRAIN_EVENTS.ERROR;
31
+ status: typeof STATUS.ERROR;
32
+ error: SerializedError;
33
+ }
34
+ export interface StepStatusEvent<TOptions extends object = object> extends BaseEvent<TOptions> {
35
+ type: typeof BRAIN_EVENTS.STEP_STATUS;
36
+ steps: SerializedStepStatus[];
37
+ }
38
+ export interface StepStartedEvent<TOptions extends object = object> extends BaseEvent<TOptions> {
39
+ type: typeof BRAIN_EVENTS.STEP_START;
40
+ status: typeof STATUS.RUNNING;
41
+ stepTitle: string;
42
+ stepId: string;
43
+ }
44
+ export interface StepCompletedEvent<TOptions extends object = object> extends BaseEvent<TOptions> {
45
+ type: typeof BRAIN_EVENTS.STEP_COMPLETE;
46
+ status: typeof STATUS.RUNNING;
47
+ stepTitle: string;
48
+ stepId: string;
49
+ patch: JsonPatch;
50
+ }
51
+ export type BrainEvent<TOptions extends object = object> = BrainStartEvent<TOptions> | BrainCompleteEvent<TOptions> | BrainErrorEvent<TOptions> | StepStatusEvent<TOptions> | StepStartedEvent<TOptions> | StepCompletedEvent<TOptions>;
52
+ export interface SerializedStep {
53
+ title: string;
54
+ status: (typeof STATUS)[keyof typeof STATUS];
55
+ id: string;
56
+ patch?: JsonPatch;
57
+ }
58
+ export type SerializedStepStatus = Omit<SerializedStep, 'patch'>;
59
+ export interface BrainStructure {
60
+ title: string;
61
+ description?: string;
62
+ steps: Array<{
63
+ type: 'step' | 'brain';
64
+ title: string;
65
+ innerBrain?: BrainStructure;
66
+ }>;
67
+ }
68
+ export interface BrainFactory {
69
+ <TOptions extends object = object, TState extends State = object, TServices extends object = object>(brainConfig: string | {
70
+ title: string;
71
+ description?: string;
72
+ }): Brain<TOptions, TState, TServices>;
73
+ }
74
+ interface BaseRunParams<TOptions extends object = object> {
75
+ client: ObjectGenerator;
76
+ resources?: Resources;
77
+ options?: TOptions;
78
+ }
79
+ export interface InitialRunParams<TOptions extends object = object> extends BaseRunParams<TOptions> {
80
+ initialState?: State;
81
+ initialCompletedSteps?: never;
82
+ brainRunId?: string;
83
+ }
84
+ export interface RerunParams<TOptions extends object = object> extends BaseRunParams<TOptions> {
85
+ initialState: State;
86
+ initialCompletedSteps: SerializedStep[];
87
+ brainRunId: string;
88
+ }
89
+ export declare class Brain<TOptions extends object = object, TState extends State = object, TServices extends object = object> {
90
+ readonly title: string;
91
+ private description?;
92
+ private blocks;
93
+ type: 'brain';
94
+ private defaultOptions;
95
+ private services;
96
+ constructor(title: string, description?: string | undefined);
97
+ get structure(): BrainStructure;
98
+ withOptions(options: Partial<TOptions>): this;
99
+ withServices<TNewServices extends object>(services: TNewServices): Brain<TOptions, TState, TNewServices>;
100
+ step<TNewState extends State>(title: string, action: (params: {
101
+ state: TState;
102
+ options: TOptions;
103
+ client: ObjectGenerator;
104
+ resources: Resources;
105
+ } & TServices) => TNewState | Promise<TNewState>): Brain<TOptions, TNewState, TServices>;
106
+ brain<TInnerState extends State, TNewState extends State>(title: string, innerBrain: Brain<TOptions, TInnerState, TServices>, action: (params: {
107
+ state: TState;
108
+ brainState: TInnerState;
109
+ services: TServices;
110
+ }) => TNewState, initialState?: State | ((state: TState) => State)): Brain<TOptions, TNewState, TServices>;
111
+ prompt<TResponseKey extends string & {
112
+ readonly brand?: unique symbol;
113
+ }, TSchema extends z.ZodObject<any>, TNewState extends State = TState & {
114
+ [K in TResponseKey]: z.infer<TSchema>;
115
+ }>(title: string, config: {
116
+ template: (state: TState, resources: Resources) => string | Promise<string>;
117
+ outputSchema: {
118
+ schema: TSchema;
119
+ name: TResponseKey & (string extends TResponseKey ? never : unknown);
120
+ };
121
+ client?: ObjectGenerator;
122
+ }, reduce?: (params: {
123
+ state: TState;
124
+ response: z.infer<TSchema>;
125
+ options: TOptions;
126
+ prompt: string;
127
+ resources: Resources;
128
+ } & TServices) => TNewState | Promise<TNewState>): Brain<TOptions, TNewState, TServices>;
129
+ run(params: InitialRunParams<TOptions>): AsyncGenerator<BrainEvent<TOptions>>;
130
+ run(params: RerunParams<TOptions>): AsyncGenerator<BrainEvent<TOptions>>;
131
+ private withBlocks;
132
+ private nextBrain;
133
+ }
134
+ export declare const brain: BrainFactory;
135
+ export {};
136
+ //# sourceMappingURL=brain.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"brain.d.ts","sourceRoot":"","sources":["../../../src/dsl/brain.ts"],"names":[],"mappings":"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"}
@@ -1,11 +1,11 @@
1
- export declare const WORKFLOW_EVENTS: {
2
- readonly START: "workflow:start";
3
- readonly RESTART: "workflow:restart";
1
+ export declare const BRAIN_EVENTS: {
2
+ readonly START: "brain:start";
3
+ readonly RESTART: "brain:restart";
4
4
  readonly STEP_START: "step:start";
5
5
  readonly STEP_COMPLETE: "step:complete";
6
6
  readonly STEP_STATUS: "step:status";
7
- readonly ERROR: "workflow:error";
8
- readonly COMPLETE: "workflow:complete";
7
+ readonly ERROR: "brain:error";
8
+ readonly COMPLETE: "brain:complete";
9
9
  };
10
10
  export declare const STATUS: {
11
11
  readonly PENDING: "pending";
@@ -1 +1 @@
1
- {"version":3,"file":"constants.d.ts","sourceRoot":"","sources":["../../../src/dsl/constants.ts"],"names":[],"mappings":"AAAA,eAAO,MAAM,eAAe;;;;;;;;CAQlB,CAAC;AAEX,eAAO,MAAM,MAAM;;;;;CAKT,CAAC"}
1
+ {"version":3,"file":"constants.d.ts","sourceRoot":"","sources":["../../../src/dsl/constants.ts"],"names":[],"mappings":"AAAA,eAAO,MAAM,YAAY;;;;;;;;CAQf,CAAC;AAEX,eAAO,MAAM,MAAM;;;;;CAKT,CAAC"}
@@ -1,4 +1,5 @@
1
- import { JsonPatch, State } from './types';
1
+ import { JsonPatch, State } from './types.js';
2
+ export type { JsonPatch };
2
3
  /**
3
4
  * Creates a JSON Patch that describes the changes needed to transform prevState into nextState.
4
5
  */
@@ -1 +1 @@
1
- {"version":3,"file":"json-patch.d.ts","sourceRoot":"","sources":["../../../src/dsl/json-patch.ts"],"names":[],"mappings":"AAEA,OAAO,EAAE,SAAS,EAAE,KAAK,EAAE,MAAM,SAAS,CAAC;AAE3C;;GAEG;AACH,wBAAgB,WAAW,CAAC,SAAS,EAAE,KAAK,EAAE,SAAS,EAAE,KAAK,GAAG,SAAS,CAKzE;AAED;;;GAGG;AACH,wBAAgB,YAAY,CAAC,KAAK,EAAE,KAAK,EAAE,OAAO,EAAE,SAAS,GAAG,SAAS,EAAE,GAAG,KAAK,CAQlF"}
1
+ {"version":3,"file":"json-patch.d.ts","sourceRoot":"","sources":["../../../src/dsl/json-patch.ts"],"names":[],"mappings":"AAEA,OAAO,EAAE,SAAS,EAAE,KAAK,EAAE,MAAM,YAAY,CAAC;AAE9C,YAAY,EAAE,SAAS,EAAE,CAAC;AAE1B;;GAEG;AACH,wBAAgB,WAAW,CAAC,SAAS,EAAE,KAAK,EAAE,SAAS,EAAE,KAAK,GAAG,SAAS,CAKzE;AAED;;;GAGG;AACH,wBAAgB,YAAY,CAC1B,KAAK,EAAE,KAAK,EACZ,OAAO,EAAE,SAAS,GAAG,SAAS,EAAE,GAC/B,KAAK,CAkBP"}
@@ -1,12 +1,14 @@
1
- export { Workflow, workflow } from "./dsl/workflow";
2
- export { WorkflowRunner } from "./dsl/workflow-runner";
3
- export { createExtension } from "./dsl/extensions";
4
- export { STATUS, WORKFLOW_EVENTS } from "./dsl/constants";
5
- export { Adapter } from "./adapters/types";
6
- export type { WorkflowEvent, SerializedStep, InitialRunParams, RerunParams, WorkflowStartEvent, WorkflowCompleteEvent, WorkflowErrorEvent, StepStatusEvent, StepStartedEvent, StepCompletedEvent } from "./dsl/workflow";
7
- export type { PromptClient, ResponseModel } from "./clients/types";
8
- export type { State } from "./dsl/types";
9
- export type { FileStore } from "./file-stores/types";
10
- export { createPatch, applyPatches } from "./dsl/json-patch";
11
- export { LocalFileStore } from "./file-stores/local-file-store";
1
+ export { Brain, brain } from './dsl/brain.js';
2
+ export { BrainRunner } from './dsl/brain-runner.js';
3
+ export { STATUS, BRAIN_EVENTS } from './dsl/constants.js';
4
+ export type { Adapter } from './adapters/types.js';
5
+ export type { BrainEvent, SerializedStep, InitialRunParams, RerunParams, BrainStartEvent, BrainCompleteEvent, BrainErrorEvent, StepStatusEvent, StepStartedEvent, StepCompletedEvent, BrainStructure, BrainFactory, } from './dsl/brain.js';
6
+ export type { ObjectGenerator, Message } from './clients/types.js';
7
+ export type { State } from './dsl/types.js';
8
+ export { createPatch, applyPatches } from './dsl/json-patch.js';
9
+ export { z } from 'zod';
10
+ export type { ResourceLoader } from './resources/resource-loader.js';
11
+ export { createResources, type Resources } from './resources/resources.js';
12
+ export type { Manifest as ResourceManifest, Entry as ResourceEntry, ResourceType, } from './resources/resources.js';
13
+ export { RESOURCE_TYPES } from './resources/resources.js';
12
14
  //# sourceMappingURL=index.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/index.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,QAAQ,EAAE,QAAQ,EAAE,MAAM,gBAAgB,CAAC;AACpD,OAAO,EAAE,cAAc,EAAE,MAAM,uBAAuB,CAAC;AACvD,OAAO,EAAE,eAAe,EAAE,MAAM,kBAAkB,CAAC;AACnD,OAAO,EAAE,MAAM,EAAE,eAAe,EAAE,MAAM,iBAAiB,CAAC;AAC1D,OAAO,EAAE,OAAO,EAAE,MAAM,kBAAkB,CAAC;AAC3C,YAAY,EACV,aAAa,EACb,cAAc,EACd,gBAAgB,EAChB,WAAW,EACX,kBAAkB,EAClB,qBAAqB,EACrB,kBAAkB,EAClB,eAAe,EACf,gBAAgB,EAChB,kBAAkB,EACnB,MAAM,gBAAgB,CAAC;AACxB,YAAY,EAAE,YAAY,EAAE,aAAa,EAAE,MAAM,iBAAiB,CAAC;AACnE,YAAY,EAAE,KAAK,EAAE,MAAM,aAAa,CAAC;AACzC,YAAY,EAAE,SAAS,EAAE,MAAM,qBAAqB,CAAC;AACrD,OAAO,EAAE,WAAW,EAAE,YAAY,EAAE,MAAM,kBAAkB,CAAC;AAC7D,OAAO,EAAE,cAAc,EAAE,MAAM,gCAAgC,CAAC"}
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/index.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,KAAK,EAAE,KAAK,EAAE,MAAM,gBAAgB,CAAC;AAC9C,OAAO,EAAE,WAAW,EAAE,MAAM,uBAAuB,CAAC;AACpD,OAAO,EAAE,MAAM,EAAE,YAAY,EAAE,MAAM,oBAAoB,CAAC;AAC1D,YAAY,EAAE,OAAO,EAAE,MAAM,qBAAqB,CAAC;AACnD,YAAY,EACV,UAAU,EACV,cAAc,EACd,gBAAgB,EAChB,WAAW,EACX,eAAe,EACf,kBAAkB,EAClB,eAAe,EACf,eAAe,EACf,gBAAgB,EAChB,kBAAkB,EAClB,cAAc,EACd,YAAY,GACb,MAAM,gBAAgB,CAAC;AACxB,YAAY,EAAE,eAAe,EAAE,OAAO,EAAE,MAAM,oBAAoB,CAAC;AACnE,YAAY,EAAE,KAAK,EAAE,MAAM,gBAAgB,CAAC;AAC5C,OAAO,EAAE,WAAW,EAAE,YAAY,EAAE,MAAM,qBAAqB,CAAC;AAMhE,OAAO,EAAE,CAAC,EAAE,MAAM,KAAK,CAAC;AAExB,YAAY,EAAE,cAAc,EAAE,MAAM,gCAAgC,CAAC;AACrE,OAAO,EAAE,eAAe,EAAE,KAAK,SAAS,EAAE,MAAM,0BAA0B,CAAC;AAC3E,YAAY,EACV,QAAQ,IAAI,gBAAgB,EAC5B,KAAK,IAAI,aAAa,EACtB,YAAY,GACb,MAAM,0BAA0B,CAAC;AAClC,OAAO,EAAE,cAAc,EAAE,MAAM,0BAA0B,CAAC"}
@@ -0,0 +1,6 @@
1
+ export interface ResourceLoader {
2
+ load(resourceName: string, type?: 'text'): Promise<string>;
3
+ load(resourceName: string, type: 'binary'): Promise<Buffer>;
4
+ load(resourceName: string, type?: 'text' | 'binary'): Promise<string | Buffer>;
5
+ }
6
+ //# sourceMappingURL=resource-loader.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"resource-loader.d.ts","sourceRoot":"","sources":["../../../src/resources/resource-loader.ts"],"names":[],"mappings":"AAAA,MAAM,WAAW,cAAc;IAC7B,IAAI,CAAC,YAAY,EAAE,MAAM,EAAE,IAAI,CAAC,EAAE,MAAM,GAAG,OAAO,CAAC,MAAM,CAAC,CAAC;IAC3D,IAAI,CAAC,YAAY,EAAE,MAAM,EAAE,IAAI,EAAE,QAAQ,GAAG,OAAO,CAAC,MAAM,CAAC,CAAC;IAC5D,IAAI,CACF,YAAY,EAAE,MAAM,EACpB,IAAI,CAAC,EAAE,MAAM,GAAG,QAAQ,GACvB,OAAO,CAAC,MAAM,GAAG,MAAM,CAAC,CAAC;CAC7B"}
@@ -0,0 +1,23 @@
1
+ import { ResourceLoader } from './resource-loader.js';
2
+ export declare const RESOURCE_TYPES: readonly ["text", "binary"];
3
+ export type ResourceType = (typeof RESOURCE_TYPES)[number];
4
+ export interface Entry {
5
+ type: ResourceType;
6
+ path: string;
7
+ key: string;
8
+ }
9
+ export interface Manifest {
10
+ [key: string]: ManifestEntry;
11
+ }
12
+ type ManifestEntry = Entry | Manifest;
13
+ interface Resource {
14
+ load: () => Promise<string | Buffer>;
15
+ loadText: () => Promise<string>;
16
+ loadBinary: () => Promise<Buffer>;
17
+ }
18
+ export interface Resources {
19
+ [key: string]: Resource | Resources;
20
+ }
21
+ export declare function createResources<M extends Manifest>(loader: ResourceLoader, initialManifest: M): Resources;
22
+ export {};
23
+ //# sourceMappingURL=resources.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"resources.d.ts","sourceRoot":"","sources":["../../../src/resources/resources.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,cAAc,EAAE,MAAM,sBAAsB,CAAC;AAGtD,eAAO,MAAM,cAAc,6BAA8B,CAAC;AAC1D,MAAM,MAAM,YAAY,GAAG,CAAC,OAAO,cAAc,CAAC,CAAC,MAAM,CAAC,CAAC;AAE3D,MAAM,WAAW,KAAK;IACpB,IAAI,EAAE,YAAY,CAAC;IACnB,IAAI,EAAE,MAAM,CAAC;IACb,GAAG,EAAE,MAAM,CAAC;CACb;AAED,MAAM,WAAW,QAAQ;IACvB,CAAC,GAAG,EAAE,MAAM,GAAG,aAAa,CAAC;CAC9B;AAED,KAAK,aAAa,GAAG,KAAK,GAAG,QAAQ,CAAC;AAEtC,UAAU,QAAQ;IAChB,IAAI,EAAE,MAAM,OAAO,CAAC,MAAM,GAAG,MAAM,CAAC,CAAC;IACrC,QAAQ,EAAE,MAAM,OAAO,CAAC,MAAM,CAAC,CAAC;IAChC,UAAU,EAAE,MAAM,OAAO,CAAC,MAAM,CAAC,CAAC;CACnC;AAED,MAAM,WAAW,SAAS;IACxB,CAAC,GAAG,EAAE,MAAM,GAAG,QAAQ,GAAG,SAAS,CAAC;CACrC;AAMD,wBAAgB,eAAe,CAAC,CAAC,SAAS,QAAQ,EAChD,MAAM,EAAE,cAAc,EACtB,eAAe,EAAE,CAAC,aAwOnB"}
@@ -0,0 +1,94 @@
1
+ import type { ObjectGenerator, Message } from './clients/types.js';
2
+ import { z } from 'zod';
3
+ import { jest } from '@jest/globals';
4
+ import type { Brain } from './dsl/brain.js';
5
+ import type { State } from './dsl/types.js';
6
+ import type { Resources } from './resources/resources.js';
7
+ /**
8
+ * Mock implementation of ObjectGenerator for testing
9
+ */
10
+ export declare class MockObjectGenerator implements ObjectGenerator {
11
+ private generateObjectMock;
12
+ private calls;
13
+ constructor();
14
+ generateObject<T extends z.AnyZodObject>(params: Parameters<ObjectGenerator['generateObject']>[0]): Promise<z.infer<T>>;
15
+ /**
16
+ * Mock a response for the next generateObject call
17
+ */
18
+ mockNextResponse<T>(response: T): void;
19
+ /**
20
+ * Mock multiple responses in sequence
21
+ */
22
+ mockResponses(...responses: any[]): void;
23
+ /**
24
+ * Mock an error for the next generateObject call
25
+ */
26
+ mockNextError(error: Error | string): void;
27
+ /**
28
+ * Get the underlying jest.Mock for advanced mocking scenarios
29
+ */
30
+ get mock(): jest.Mock<any>;
31
+ /**
32
+ * Clear all mocks and call history
33
+ */
34
+ clear(): void;
35
+ /**
36
+ * Reset mock to initial state
37
+ */
38
+ reset(): void;
39
+ /**
40
+ * Get all calls made to generateObject
41
+ */
42
+ getCalls(): {
43
+ params: Parameters<ObjectGenerator["generateObject"]>[0];
44
+ timestamp: Date;
45
+ }[];
46
+ /**
47
+ * Get the last call made to generateObject
48
+ */
49
+ getLastCall(): {
50
+ params: Parameters<ObjectGenerator["generateObject"]>[0];
51
+ timestamp: Date;
52
+ };
53
+ /**
54
+ * Assert that generateObject was called with specific parameters
55
+ */
56
+ expectCalledWith(expected: {
57
+ prompt?: string | ((actual: string) => boolean);
58
+ schemaName?: string;
59
+ messages?: Message[];
60
+ system?: string;
61
+ }): void;
62
+ /**
63
+ * Assert that generateObject was called N times
64
+ */
65
+ expectCallCount(count: number): void;
66
+ }
67
+ /**
68
+ * Creates a mock client for testing brains
69
+ */
70
+ export declare function createMockClient(): MockObjectGenerator;
71
+ /**
72
+ * Test runner options
73
+ */
74
+ export interface TestRunnerOptions<TState extends State = State> {
75
+ client?: ObjectGenerator;
76
+ resources?: Resources;
77
+ initialState?: TState;
78
+ }
79
+ /**
80
+ * Result of running a brain test
81
+ */
82
+ export interface TestRunResult<TState extends State = State> {
83
+ finalState: TState;
84
+ steps: string[];
85
+ error: Error | null;
86
+ completed: boolean;
87
+ }
88
+ /**
89
+ * Runs a brain with test utilities and returns collected data
90
+ */
91
+ export declare function runBrainTest<TOptions extends object = {}, TState extends State = {}>(brain: Brain<TOptions, TState, any>, options?: TestRunnerOptions<TState> & {
92
+ brainOptions?: TOptions;
93
+ }): Promise<TestRunResult<TState>>;
94
+ //# sourceMappingURL=test-utils.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"test-utils.d.ts","sourceRoot":"","sources":["../../src/test-utils.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,2 @@
1
+ export { createMockClient, MockObjectGenerator, runBrainTest, type TestRunnerOptions, type TestRunResult, } from './test-utils.js';
2
+ //# sourceMappingURL=testing.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"testing.d.ts","sourceRoot":"","sources":["../../src/testing.ts"],"names":[],"mappings":"AAGA,OAAO,EACL,gBAAgB,EAChB,mBAAmB,EACnB,YAAY,EACZ,KAAK,iBAAiB,EACtB,KAAK,aAAa,GACnB,MAAM,iBAAiB,CAAC"}
@@ -0,0 +1,289 @@
1
+ # Core Testing Guide
2
+
3
+ ## Overview
4
+
5
+ The Core package tests focus on Brain DSL workflows, event sequences, and state management. This guide helps you write effective tests while avoiding common pitfalls.
6
+
7
+ ## Key Testing Patterns
8
+
9
+ ### 1. Brain Event Collection Pattern
10
+
11
+ **The Challenge**: Brains emit async events that need to be collected and verified.
12
+
13
+ **Solution**: Always collect ALL events before making assertions:
14
+
15
+ ```typescript
16
+ // CORRECT: Collect all events first
17
+ const events = [];
18
+ for await (const event of brain.run({ client: mockClient })) {
19
+ events.push(event);
20
+ }
21
+
22
+ // Now make assertions
23
+ expect(events.map(e => e.type)).toContain(BRAIN_EVENTS.COMPLETE);
24
+
25
+ // WRONG: Don't try to assert while iterating
26
+ for await (const event of brain.run()) {
27
+ expect(event).toBeDefined(); // This can miss events!
28
+ }
29
+ ```
30
+
31
+ ### 2. State Reconstruction from Patches
32
+
33
+ **The Challenge**: Brain state is represented as JSON patches, not direct state objects.
34
+
35
+ **Solution**: Apply patches to reconstruct final state:
36
+
37
+ ```typescript
38
+ import { applyPatches } from '@positronic/core';
39
+
40
+ // Helper to reconstruct state
41
+ function reconstructState(events: BrainEvent[]) {
42
+ let state = {};
43
+ for (const event of events) {
44
+ if (event.type === BRAIN_EVENTS.STEP_COMPLETE) {
45
+ state = applyPatches(state, [event.patch]);
46
+ }
47
+ }
48
+ return state;
49
+ }
50
+
51
+ // Usage
52
+ const events = await collectAllEvents(brain.run());
53
+ const finalState = reconstructState(events);
54
+ expect(finalState).toEqual({ expected: 'state' });
55
+ ```
56
+
57
+ ### 3. Mock Setup for AI Clients
58
+
59
+ **The Challenge**: Brains often depend on AI client calls that need specific mock setup.
60
+
61
+ **Solution**: Create properly typed mocks:
62
+
63
+ ```typescript
64
+ // Setup mock client with proper typing
65
+ const mockGenerateObject = jest.fn<ObjectGenerator['generateObject']>();
66
+ const mockClient: jest.Mocked<ObjectGenerator> = {
67
+ generateObject: mockGenerateObject,
68
+ };
69
+
70
+ // Configure response BEFORE running brain
71
+ mockGenerateObject.mockResolvedValue({ result: 'test data' });
72
+
73
+ // Now run the brain
74
+ const brain = brain('test').step('Gen', async ({ client }) => {
75
+ const res = await client.generateObject({ prompt: 'test' });
76
+ return { data: res.result };
77
+ });
78
+ ```
79
+
80
+ ### 4. Resource Loading Mocks
81
+
82
+ **The Challenge**: Resources use a proxy API that's tricky to mock.
83
+
84
+ **Solution**: Mock the underlying loader, not the proxy:
85
+
86
+ ```typescript
87
+ const mockResourceLoad = jest.fn();
88
+ const mockResourceLoader: ResourceLoader = {
89
+ load: mockResourceLoad,
90
+ };
91
+
92
+ // Setup resource responses
93
+ const mockResources = {
94
+ 'example.txt': { type: 'text', content: 'Hello' },
95
+ 'data.json': { type: 'text', content: '{"key": "value"}' },
96
+ };
97
+
98
+ mockResourceLoad.mockImplementation(async (path) => {
99
+ const resource = mockResources[path];
100
+ if (!resource) throw new Error(`Resource not found: ${path}`);
101
+ return resource;
102
+ });
103
+
104
+ // Use in brain - the proxy API will call your mock
105
+ const brain = brain('test').step('Load', async ({ resources }) => {
106
+ const text = await resources.example.loadText(); // Proxy API
107
+ return { content: text };
108
+ });
109
+ ```
110
+
111
+ ### 5. Testing Error Events
112
+
113
+ **The Challenge**: Errors emit special events but brain execution continues.
114
+
115
+ **Solution**: Look for ERROR events, not exceptions:
116
+
117
+ ```typescript
118
+ const errorBrain = brain('test').step('Fail', () => {
119
+ throw new Error('Step failed');
120
+ });
121
+
122
+ const events = [];
123
+ for await (const event of errorBrain.run()) {
124
+ events.push(event);
125
+ }
126
+
127
+ // Find the error event
128
+ const errorEvent = events.find(e => e.type === BRAIN_EVENTS.ERROR);
129
+ expect(errorEvent).toBeDefined();
130
+ expect(errorEvent?.error.message).toBe('Step failed');
131
+
132
+ // Brain still completes!
133
+ expect(events.some(e => e.type === BRAIN_EVENTS.COMPLETE)).toBe(true);
134
+ ```
135
+
136
+ ### 6. Type Inference Testing
137
+
138
+ **The Challenge**: Brain DSL uses complex TypeScript inference that needs testing.
139
+
140
+ **Solution**: Use compile-time type assertions:
141
+
142
+ ```typescript
143
+ // Define a type equality helper
144
+ type AssertEquals<T, U> = T extends U ? (U extends T ? true : false) : false;
145
+
146
+ // Test that state types are inferred correctly
147
+ const typedBrain = brain('test')
148
+ .step('Init', () => ({ count: 0 }))
149
+ .step('Inc', ({ state }) => ({ count: state.count + 1 }));
150
+
151
+ // Extract the inferred state type
152
+ type InferredState = typeof typedBrain extends Brain<infer S> ? S : never;
153
+
154
+ // This line will fail compilation if types don't match
155
+ type Test = AssertEquals<InferredState, { count: number }>;
156
+ const _: Test = true;
157
+ ```
158
+
159
+ ## Common Pitfalls & Solutions
160
+
161
+ ### Pitfall 1: Forgetting to Mock Client Methods
162
+
163
+ ```typescript
164
+ // WRONG: Forgot to mock generateObject
165
+ const brain = brain('test').step('Gen', async ({ client }) => {
166
+ const res = await client.generateObject({ prompt: 'test' });
167
+ return res;
168
+ });
169
+
170
+ // This will fail with "mockGenerateObject is not a function"
171
+ await brain.run({ client: mockClient });
172
+
173
+ // CORRECT: Always mock methods before use
174
+ mockGenerateObject.mockResolvedValue({ data: 'test' });
175
+ ```
176
+
177
+ ### Pitfall 2: Testing During Event Iteration
178
+
179
+ ```typescript
180
+ // WRONG: Testing while iterating can miss events
181
+ let foundComplete = false;
182
+ for await (const event of brain.run()) {
183
+ if (event.type === BRAIN_EVENTS.COMPLETE) {
184
+ foundComplete = true;
185
+ break; // Stops iteration early!
186
+ }
187
+ }
188
+
189
+ // CORRECT: Collect all events first
190
+ const events = await collectAllEvents(brain.run());
191
+ const foundComplete = events.some(e => e.type === BRAIN_EVENTS.COMPLETE);
192
+ ```
193
+
194
+ ### Pitfall 3: Not Handling Async Steps
195
+
196
+ ```typescript
197
+ // WRONG: Synchronous step function when async is needed
198
+ const brain = brain('test').step('Async', ({ client }) => {
199
+ // This won't wait for the promise!
200
+ client.generateObject({ prompt: 'test' });
201
+ return { done: true };
202
+ });
203
+
204
+ // CORRECT: Use async/await
205
+ const brain = brain('test').step('Async', async ({ client }) => {
206
+ const result = await client.generateObject({ prompt: 'test' });
207
+ return { done: true, result };
208
+ });
209
+ ```
210
+
211
+ ### Pitfall 4: Incorrect Event Sequence Expectations
212
+
213
+ ```typescript
214
+ // WRONG: Expecting only main events
215
+ expect(events.map(e => e.type)).toEqual([
216
+ BRAIN_EVENTS.START,
217
+ BRAIN_EVENTS.COMPLETE
218
+ ]);
219
+
220
+ // CORRECT: Include all events in sequence
221
+ expect(events.map(e => e.type)).toEqual([
222
+ BRAIN_EVENTS.START,
223
+ BRAIN_EVENTS.STEP_STATUS, // Don't forget status events!
224
+ BRAIN_EVENTS.STEP_START,
225
+ BRAIN_EVENTS.STEP_COMPLETE,
226
+ BRAIN_EVENTS.STEP_STATUS,
227
+ BRAIN_EVENTS.COMPLETE
228
+ ]);
229
+ ```
230
+
231
+ ## Quick Test Template
232
+
233
+ ```typescript
234
+ import { brain, BRAIN_EVENTS, applyPatches } from '@positronic/core';
235
+ import type { ObjectGenerator, ResourceLoader } from '@positronic/core';
236
+
237
+ describe('my brain feature', () => {
238
+ // Setup mocks
239
+ const mockGenerateObject = jest.fn<ObjectGenerator['generateObject']>();
240
+ const mockClient: jest.Mocked<ObjectGenerator> = {
241
+ generateObject: mockGenerateObject,
242
+ };
243
+
244
+ beforeEach(() => {
245
+ jest.clearAllMocks();
246
+ });
247
+
248
+ it('should do something', async () => {
249
+ // Configure mocks
250
+ mockGenerateObject.mockResolvedValue({ result: 'test' });
251
+
252
+ // Define brain
253
+ const testBrain = brain('test')
254
+ .step('Process', async ({ client }) => {
255
+ const res = await client.generateObject({ prompt: 'test' });
256
+ return { processed: res.result };
257
+ });
258
+
259
+ // Collect all events
260
+ const events = [];
261
+ for await (const event of testBrain.run({ client: mockClient })) {
262
+ events.push(event);
263
+ }
264
+
265
+ // Verify final state
266
+ let finalState = {};
267
+ for (const event of events) {
268
+ if (event.type === BRAIN_EVENTS.STEP_COMPLETE) {
269
+ finalState = applyPatches(finalState, [event.patch]);
270
+ }
271
+ }
272
+ expect(finalState).toEqual({ processed: 'test' });
273
+
274
+ // Verify completion
275
+ expect(events.some(e => e.type === BRAIN_EVENTS.COMPLETE)).toBe(true);
276
+ });
277
+ });
278
+ ```
279
+
280
+ ## Running Tests
281
+
282
+ ```bash
283
+ # From monorepo root (required!)
284
+ npm test -- packages/core # Run all core tests
285
+ npm test -- brain.test.ts # Run specific test file
286
+ npm test -- -t "should process" # Run tests matching pattern
287
+ npm run test:watch # Watch mode
288
+ npm run build:workspaces # Ensure TypeScript compiles
289
+ ```