ocpipe 0.1.0

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.
package/testing.ts ADDED
@@ -0,0 +1,180 @@
1
+ /**
2
+ * DSTS SDK testing utilities.
3
+ *
4
+ * Provides mock backends and test helpers for unit testing DSTS components.
5
+ */
6
+
7
+ import type { RunAgentOptions, RunAgentResult, FieldConfig } from './types.js'
8
+
9
+ /** MockResponse defines a mock LLM response for testing. */
10
+ export interface MockResponse {
11
+ /** Pattern to match against prompts (string or regex). */
12
+ match?: string | RegExp
13
+ /** The mock response text to return (required unless error is set). */
14
+ response?: string
15
+ /** Optional session ID to return. */
16
+ sessionId?: string
17
+ /** Optional delay in ms to simulate network latency. */
18
+ delay?: number
19
+ /** Optional error to throw instead of returning response. */
20
+ error?: Error
21
+ }
22
+
23
+ /** MockAgentBackend provides a mock implementation of runAgent for testing. */
24
+ export class MockAgentBackend {
25
+ private responses: MockResponse[] = []
26
+ private calls: RunAgentOptions[] = []
27
+ private defaultSessionId = 'mock-session-001'
28
+ private callCount = 0
29
+
30
+ /** addResponse adds a mock response to the queue. */
31
+ addResponse(response: MockResponse): this {
32
+ this.responses.push(response)
33
+ return this
34
+ }
35
+
36
+ /** addJsonResponse adds a mock JSON response. */
37
+ addJsonResponse(data: Record<string, unknown>, options?: Partial<MockResponse>): this {
38
+ return this.addResponse({
39
+ response: JSON.stringify(data, null, 2),
40
+ ...options,
41
+ })
42
+ }
43
+
44
+ /** getCalls returns all recorded calls. */
45
+ getCalls(): RunAgentOptions[] {
46
+ return this.calls
47
+ }
48
+
49
+ /** getLastCall returns the most recent call. */
50
+ getLastCall(): RunAgentOptions | undefined {
51
+ return this.calls[this.calls.length - 1]
52
+ }
53
+
54
+ /** getCallCount returns the number of calls made. */
55
+ getCallCount(): number {
56
+ return this.callCount
57
+ }
58
+
59
+ /** reset clears all responses and calls. */
60
+ reset(): this {
61
+ this.responses = []
62
+ this.calls = []
63
+ this.callCount = 0
64
+ return this
65
+ }
66
+
67
+ /** runAgent is the mock implementation. */
68
+ async runAgent(options: RunAgentOptions): Promise<RunAgentResult> {
69
+ this.calls.push(options)
70
+ this.callCount++
71
+
72
+ // Find matching response and consume it
73
+ let responseIndex = -1
74
+ let response: MockResponse | undefined
75
+
76
+ for (let i = 0; i < this.responses.length; i++) {
77
+ const r = this.responses[i]
78
+ if (!r) continue
79
+
80
+ if (!r.match) {
81
+ response = r
82
+ responseIndex = i
83
+ break
84
+ }
85
+ if (typeof r.match === 'string' && options.prompt.includes(r.match)) {
86
+ response = r
87
+ responseIndex = i
88
+ break
89
+ }
90
+ if (r.match instanceof RegExp && r.match.test(options.prompt)) {
91
+ response = r
92
+ responseIndex = i
93
+ break
94
+ }
95
+ }
96
+
97
+ // Consume the response (remove from queue)
98
+ if (responseIndex >= 0) {
99
+ this.responses.splice(responseIndex, 1)
100
+ }
101
+
102
+ if (!response) {
103
+ // Return default empty JSON response
104
+ response = { response: '{}' }
105
+ }
106
+
107
+ // Simulate delay
108
+ if (response.delay) {
109
+ await new Promise((resolve) => setTimeout(resolve, response.delay))
110
+ }
111
+
112
+ // Throw error if specified
113
+ if (response.error) {
114
+ throw response.error
115
+ }
116
+
117
+ return {
118
+ text: response.response ?? '',
119
+ sessionId: response.sessionId ?? options.sessionId ?? this.defaultSessionId,
120
+ }
121
+ }
122
+
123
+ /** createRunner returns a bound runAgent function for use with vi.mock. */
124
+ createRunner(): (options: RunAgentOptions) => Promise<RunAgentResult> {
125
+ return this.runAgent.bind(this)
126
+ }
127
+ }
128
+
129
+ /** createMockContext creates a test execution context. */
130
+ export function createMockContext(overrides?: Partial<{
131
+ sessionId: string
132
+ defaultModel: { providerID: string; modelID: string }
133
+ defaultAgent: string
134
+ timeoutSec: number
135
+ }>) {
136
+ return {
137
+ sessionId: overrides?.sessionId,
138
+ defaultModel: overrides?.defaultModel ?? {
139
+ providerID: 'github-copilot',
140
+ modelID: 'grok-code-fast-1',
141
+ },
142
+ defaultAgent: overrides?.defaultAgent ?? 'general',
143
+ timeoutSec: overrides?.timeoutSec ?? 60,
144
+ }
145
+ }
146
+
147
+ /** generateMockOutputs creates mock output data based on a schema. */
148
+ export function generateMockOutputs(schema: Record<string, FieldConfig>): Record<string, unknown> {
149
+ const result: Record<string, unknown> = {}
150
+ for (const [name, config] of Object.entries(schema)) {
151
+ // Use constructor name for type detection (works across zod versions)
152
+ const typeName = config.type.constructor.name
153
+
154
+ switch (typeName) {
155
+ case 'ZodString':
156
+ result[name] = `mock_${name}`
157
+ break
158
+ case 'ZodNumber':
159
+ result[name] = 42
160
+ break
161
+ case 'ZodBoolean':
162
+ result[name] = true
163
+ break
164
+ case 'ZodArray':
165
+ result[name] = []
166
+ break
167
+ case 'ZodObject':
168
+ result[name] = {}
169
+ break
170
+ case 'ZodEnum':
171
+ // Get first enum value via options property
172
+ const enumType = config.type as { options?: readonly string[] }
173
+ result[name] = enumType.options?.[0] ?? 'unknown'
174
+ break
175
+ default:
176
+ result[name] = null
177
+ }
178
+ }
179
+ return result
180
+ }
package/types.ts ADDED
@@ -0,0 +1,260 @@
1
+ /**
2
+ * DSTS SDK shared types.
3
+ *
4
+ * Core type definitions for the Declarative Self-Improving TypeScript SDK.
5
+ */
6
+
7
+ import type { z } from 'zod'
8
+
9
+ // ============================================================================
10
+ // Model Configuration
11
+ // ============================================================================
12
+
13
+ /** Model configuration for OpenCode. */
14
+ export interface ModelConfig {
15
+ providerID: string
16
+ modelID: string
17
+ }
18
+
19
+ // ============================================================================
20
+ // Execution Context
21
+ // ============================================================================
22
+
23
+ /** Execution context passed through pipeline execution. */
24
+ export interface ExecutionContext {
25
+ /** Current OpenCode session ID (for continuity). */
26
+ sessionId?: string
27
+ /** Default model for predictions. */
28
+ defaultModel: ModelConfig
29
+ /** Default agent for predictions. */
30
+ defaultAgent: string
31
+ /** Timeout in seconds for agent calls. */
32
+ timeoutSec: number
33
+ }
34
+
35
+ // ============================================================================
36
+ // Step Results
37
+ // ============================================================================
38
+
39
+ /** Result from a single pipeline step. */
40
+ export interface StepResult<T> {
41
+ /** Parsed and validated output data. */
42
+ data: T
43
+ /** Step name for logging. */
44
+ stepName: string
45
+ /** Execution duration in milliseconds. */
46
+ duration: number
47
+ /** OpenCode session ID used. */
48
+ sessionId: string
49
+ /** Model used for this step. */
50
+ model: ModelConfig
51
+ /** Which retry attempt succeeded (1-based). */
52
+ attempt: number
53
+ }
54
+
55
+ /** Record of a completed step for checkpointing. */
56
+ export interface StepRecord {
57
+ stepName: string
58
+ timestamp: string
59
+ result: StepResult<unknown>
60
+ }
61
+
62
+ /** Record of a sub-pipeline execution. */
63
+ export interface SubPipelineRecord {
64
+ name: string
65
+ sessionId: string
66
+ timestamp: string
67
+ state: BaseState
68
+ }
69
+
70
+ // ============================================================================
71
+ // State
72
+ // ============================================================================
73
+
74
+ /** Base state interface for all pipeline states. */
75
+ export interface BaseState {
76
+ /** Unique ID for this pipeline run. */
77
+ sessionId: string
78
+ /** ISO timestamp when pipeline started. */
79
+ startedAt: string
80
+ /** Current OpenCode session ID (for continuity). */
81
+ opencodeSessionId?: string
82
+ /** Current phase name (for resume). */
83
+ phase: string
84
+ /** All completed steps. */
85
+ steps: StepRecord[]
86
+ /** References to sub-pipelines. */
87
+ subPipelines: SubPipelineRecord[]
88
+ }
89
+
90
+ // ============================================================================
91
+ // Predict Results
92
+ // ============================================================================
93
+
94
+ /** Result from a Predict.execute() call. */
95
+ export interface PredictResult<T> {
96
+ /** Parsed and validated output data. */
97
+ data: T
98
+ /** Raw response text from the LLM. */
99
+ raw: string
100
+ /** OpenCode session ID. */
101
+ sessionId: string
102
+ /** Execution duration in milliseconds. */
103
+ duration: number
104
+ /** Model used. */
105
+ model: ModelConfig
106
+ }
107
+
108
+ // ============================================================================
109
+ // Signature Types
110
+ // ============================================================================
111
+
112
+ /** Configuration for a signature field. */
113
+ export interface FieldConfig<T extends z.ZodType = z.ZodType> {
114
+ /** Zod type for validation. */
115
+ type: T
116
+ /** Description of the field (used in prompt generation). */
117
+ desc?: string
118
+ }
119
+
120
+ /** Signature definition with typed inputs and outputs. */
121
+ export interface SignatureDef<
122
+ I extends Record<string, FieldConfig>,
123
+ O extends Record<string, FieldConfig>,
124
+ > {
125
+ /** Documentation/instruction for the LLM. */
126
+ doc: string
127
+ /** Input field definitions. */
128
+ inputs: I
129
+ /** Output field definitions. */
130
+ outputs: O
131
+ }
132
+
133
+ /** Infer the input type from a signature definition. */
134
+ export type InferInputs<S extends SignatureDef<any, any>> =
135
+ S extends SignatureDef<infer I, any>
136
+ ? { [K in keyof I]: z.infer<I[K]['type']> }
137
+ : never
138
+
139
+ /** Infer the output type from a signature definition. */
140
+ export type InferOutputs<S extends SignatureDef<any, any>> =
141
+ S extends SignatureDef<any, infer O>
142
+ ? { [K in keyof O]: z.infer<O[K]['type']> }
143
+ : never
144
+
145
+ // ============================================================================
146
+ // Retry Configuration
147
+ // ============================================================================
148
+
149
+ /** Retry configuration for pipeline steps. */
150
+ export interface RetryConfig {
151
+ /** Maximum number of attempts. */
152
+ maxAttempts: number
153
+ /** Whether to retry on parse errors (JSON failures). */
154
+ onParseError?: boolean
155
+ }
156
+
157
+ // ============================================================================
158
+ // Schema Correction
159
+ // ============================================================================
160
+
161
+ /** Correction method for fixing schema validation errors. */
162
+ export type CorrectionMethod = 'json-patch' | 'jq'
163
+
164
+ /** Configuration for automatic schema correction on parse failures. */
165
+ export interface CorrectionConfig {
166
+ /** Correction method to use (default: 'json-patch'). */
167
+ method?: CorrectionMethod
168
+ /** Use a different model for corrections (default: same model, same session). */
169
+ model?: ModelConfig
170
+ /** Maximum number of fields to attempt correcting per round (default: 5). */
171
+ maxFields?: number
172
+ /** Maximum number of correction rounds before giving up (default: 3). */
173
+ maxRounds?: number
174
+ }
175
+
176
+ /** A field-level error from schema validation. */
177
+ export interface FieldError {
178
+ /** The field path that failed (e.g., "issues.0.issue_type"). */
179
+ path: string
180
+ /** Human-readable error message. */
181
+ message: string
182
+ /** Expected type description. */
183
+ expectedType: string
184
+ /** If a similar field was found, its name. */
185
+ foundField?: string
186
+ /** The value found at the wrong field. */
187
+ foundValue?: unknown
188
+ }
189
+
190
+ /** Result from trying to parse a response. */
191
+ export interface TryParseResult<T> {
192
+ /** Whether parsing succeeded. */
193
+ ok: boolean
194
+ /** Parsed data if successful. */
195
+ data?: T
196
+ /** Field errors if validation failed. */
197
+ errors?: FieldError[]
198
+ /** The extracted JSON object (even if validation failed). */
199
+ json?: Record<string, unknown>
200
+ }
201
+
202
+ // ============================================================================
203
+ // Pipeline Configuration
204
+ // ============================================================================
205
+
206
+ /** Configuration for a pipeline. */
207
+ export interface PipelineConfig {
208
+ /** Pipeline name (used in checkpoints). */
209
+ name: string
210
+ /** Default model for predictions. */
211
+ defaultModel: ModelConfig
212
+ /** Default agent for predictions. */
213
+ defaultAgent: string
214
+ /** Directory for checkpoint files. */
215
+ checkpointDir: string
216
+ /** Directory for log files. */
217
+ logDir: string
218
+ /** Default retry configuration. */
219
+ retry?: RetryConfig
220
+ /** Default timeout in seconds. */
221
+ timeoutSec?: number
222
+ }
223
+
224
+ /** Options for running a pipeline step. */
225
+ export interface RunOptions {
226
+ /** Step name for logging (defaults to module class name). */
227
+ name?: string
228
+ /** Override model for this step. */
229
+ model?: ModelConfig
230
+ /** Start fresh session (don't reuse existing). */
231
+ newSession?: boolean
232
+ /** Override retry config for this step. */
233
+ retry?: RetryConfig
234
+ }
235
+
236
+ // ============================================================================
237
+ // Agent Types
238
+ // ============================================================================
239
+
240
+ /** Options for running an OpenCode agent. */
241
+ export interface RunAgentOptions {
242
+ /** The prompt to send to the agent. */
243
+ prompt: string
244
+ /** Agent type (e.g., "journey-creator", "explore", "general"). */
245
+ agent: string
246
+ /** Model to use. */
247
+ model: ModelConfig
248
+ /** Existing session ID to continue. */
249
+ sessionId?: string
250
+ /** Timeout in seconds. */
251
+ timeoutSec?: number
252
+ }
253
+
254
+ /** Result from running an OpenCode agent. */
255
+ export interface RunAgentResult {
256
+ /** The text response from the agent. */
257
+ text: string
258
+ /** Session ID (for continuing the conversation). */
259
+ sessionId: string
260
+ }