ocpipe 0.6.9 → 0.6.10
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/README.md +10 -3
- package/package.json +10 -10
- package/src/agent.ts +8 -2
- package/src/index.ts +1 -0
- package/src/omp.ts +313 -0
- package/src/pipeline.ts +1 -0
- package/src/predict.ts +3 -0
- package/src/types.ts +39 -1
package/README.md
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
<p align="center"><strong>ocpipe</strong></p>
|
|
2
|
-
<p align="center">Build LLM pipelines with <a href="https://github.com/sst/opencode">OpenCode</a>, <a href="https://github.com/anthropics/claude-code">Claude Code</a>, Pi, and <a href="https://zod.dev">Zod</a>.</p>
|
|
2
|
+
<p align="center">Build LLM pipelines with <a href="https://github.com/sst/opencode">OpenCode</a>, <a href="https://github.com/anthropics/claude-code">Claude Code</a>, Oh My Pi, Pi, and <a href="https://zod.dev">Zod</a>.</p>
|
|
3
3
|
<p align="center">Inspired by <a href="https://github.com/stanfordnlp/dspy">DSPy</a>.</p>
|
|
4
4
|
<p align="center">
|
|
5
5
|
<a href="https://www.npmjs.com/package/ocpipe"><img alt="npm" src="https://img.shields.io/npm/v/ocpipe?style=flat-square" /></a>
|
|
@@ -11,7 +11,7 @@
|
|
|
11
11
|
- **Type-safe** Define inputs and outputs with Zod schemas
|
|
12
12
|
- **Modular** Compose modules into complex pipelines
|
|
13
13
|
- **Checkpoints** Resume from any step
|
|
14
|
-
- **Multi-backend** Choose between OpenCode (75+ providers), Claude Code SDK, Codex SDK, or Pi
|
|
14
|
+
- **Multi-backend** Choose between OpenCode (75+ providers), Claude Code SDK, Codex SDK, Oh My Pi, or Pi
|
|
15
15
|
- **Auto-correction** Fixes schema mismatches automatically
|
|
16
16
|
|
|
17
17
|
### Quick Start
|
|
@@ -49,7 +49,7 @@ type GreetOut = InferOutputs<typeof Greet> // { greeting: string }
|
|
|
49
49
|
|
|
50
50
|
### Backends
|
|
51
51
|
|
|
52
|
-
ocpipe supports
|
|
52
|
+
ocpipe supports five backends for running LLM agents:
|
|
53
53
|
|
|
54
54
|
**OpenCode** (default) - Requires `opencode` CLI in your PATH. Supports 75+ providers.
|
|
55
55
|
|
|
@@ -83,6 +83,13 @@ defaultModel: { backend: 'codex', modelID: 'gpt-5.4' },
|
|
|
83
83
|
codex: { sandbox: 'read-only', reasoningEffort: 'high' },
|
|
84
84
|
```
|
|
85
85
|
|
|
86
|
+
**Oh My Pi** - Uses the `omp` CLI in headless JSON print mode.
|
|
87
|
+
|
|
88
|
+
```typescript
|
|
89
|
+
defaultModel: { backend: 'omp', modelID: 'gpt-5.5' },
|
|
90
|
+
omp: { command: 'omp', approvalMode: 'yolo', thinking: 'high' },
|
|
91
|
+
```
|
|
92
|
+
|
|
86
93
|
**Pi** - Uses the `pi` coding-agent CLI JSONL RPC mode.
|
|
87
94
|
|
|
88
95
|
```typescript
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "ocpipe",
|
|
3
|
-
"version": "0.6.
|
|
3
|
+
"version": "0.6.10",
|
|
4
4
|
"description": "SDK for LLM pipelines with OpenCode, Codex, and Zod",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"main": "src/index.ts",
|
|
@@ -43,18 +43,18 @@
|
|
|
43
43
|
}
|
|
44
44
|
},
|
|
45
45
|
"devDependencies": {
|
|
46
|
-
"@anthropic-ai/claude-agent-sdk": "^0.3.
|
|
46
|
+
"@anthropic-ai/claude-agent-sdk": "^0.3.197",
|
|
47
47
|
"@eslint/js": "^10.0.1",
|
|
48
|
-
"@openai/codex-sdk": "0.
|
|
49
|
-
"@typescript/native-preview": "^7.0.0-dev.
|
|
50
|
-
"bun-types": "^1.3.
|
|
51
|
-
"eslint": "^10.
|
|
52
|
-
"globals": "^17.
|
|
48
|
+
"@openai/codex-sdk": "0.142.5",
|
|
49
|
+
"@typescript/native-preview": "^7.0.0-dev.20260630.1",
|
|
50
|
+
"bun-types": "^1.3.14",
|
|
51
|
+
"eslint": "^10.6.0",
|
|
52
|
+
"globals": "^17.7.0",
|
|
53
53
|
"jiti": "^2.7.0",
|
|
54
|
-
"prettier": "^3.
|
|
54
|
+
"prettier": "^3.9.4",
|
|
55
55
|
"typescript": "^6.0.3",
|
|
56
|
-
"typescript-eslint": "^8.
|
|
57
|
-
"vitest": "^4.1.
|
|
56
|
+
"typescript-eslint": "^8.62.1",
|
|
57
|
+
"vitest": "^4.1.9"
|
|
58
58
|
},
|
|
59
59
|
"scripts": {
|
|
60
60
|
"lint": "eslint .",
|
package/src/agent.ts
CHANGED
|
@@ -9,6 +9,8 @@ import { spawn } from 'child_process'
|
|
|
9
9
|
import { mkdir, writeFile, unlink } from 'fs/promises'
|
|
10
10
|
import { join } from 'path'
|
|
11
11
|
import { PROJECT_ROOT, TMP_DIR } from './paths.js'
|
|
12
|
+
import { runOmpAgent } from './omp.js'
|
|
13
|
+
import { runPiAgent } from './pi.js'
|
|
12
14
|
import type { RunAgentOptions, RunAgentResult } from './types.js'
|
|
13
15
|
|
|
14
16
|
/** Get command and args to invoke opencode from PATH */
|
|
@@ -23,21 +25,25 @@ export async function runAgent(
|
|
|
23
25
|
const backend = options.model.backend ?? 'opencode'
|
|
24
26
|
|
|
25
27
|
if (backend === 'claude-code') {
|
|
26
|
-
// Dynamic import
|
|
28
|
+
// Dynamic import is required: @anthropic-ai/claude-agent-sdk is an optional peer absent for other backends.
|
|
27
29
|
const { runClaudeCodeAgent } = await import('./claude-code.js')
|
|
28
30
|
return runClaudeCodeAgent(options)
|
|
29
31
|
}
|
|
30
32
|
|
|
31
33
|
if (backend === 'codex') {
|
|
34
|
+
// Dynamic import is required: @openai/codex-sdk is an optional peer absent for other backends.
|
|
32
35
|
const { runCodexAgent } = await import('./codex.js')
|
|
33
36
|
return runCodexAgent(options)
|
|
34
37
|
}
|
|
35
38
|
|
|
36
39
|
if (backend === 'pi') {
|
|
37
|
-
const { runPiAgent } = await import('./pi.js')
|
|
38
40
|
return runPiAgent(options)
|
|
39
41
|
}
|
|
40
42
|
|
|
43
|
+
if (backend === 'omp') {
|
|
44
|
+
return runOmpAgent(options)
|
|
45
|
+
}
|
|
46
|
+
|
|
41
47
|
return runOpencodeAgent(options)
|
|
42
48
|
}
|
|
43
49
|
|
package/src/index.ts
CHANGED
package/src/omp.ts
ADDED
|
@@ -0,0 +1,313 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* ocpipe Oh My Pi integration.
|
|
3
|
+
*
|
|
4
|
+
* Runs Oh My Pi through its headless JSON print mode.
|
|
5
|
+
*/
|
|
6
|
+
|
|
7
|
+
import { spawn, type ChildProcess } from 'child_process'
|
|
8
|
+
import { isAbsolute, join } from 'path'
|
|
9
|
+
import { PROJECT_ROOT } from './paths.js'
|
|
10
|
+
import type { OmpOptions, RunAgentOptions, RunAgentResult } from './types.js'
|
|
11
|
+
|
|
12
|
+
interface OmpProcessRequest {
|
|
13
|
+
command: string
|
|
14
|
+
args: string[]
|
|
15
|
+
cwd: string
|
|
16
|
+
env: NodeJS.ProcessEnv
|
|
17
|
+
signal?: AbortSignal
|
|
18
|
+
}
|
|
19
|
+
|
|
20
|
+
interface OmpProcessResult {
|
|
21
|
+
stdout: string
|
|
22
|
+
stderr: string
|
|
23
|
+
exitCode: number | null
|
|
24
|
+
signal: NodeJS.Signals | null
|
|
25
|
+
}
|
|
26
|
+
|
|
27
|
+
export interface OmpProcess {
|
|
28
|
+
run(req: OmpProcessRequest): Promise<OmpProcessResult>
|
|
29
|
+
}
|
|
30
|
+
|
|
31
|
+
interface OmpRunSummary {
|
|
32
|
+
sawJsonEvent: boolean
|
|
33
|
+
sessionId: string
|
|
34
|
+
finalMessage: string
|
|
35
|
+
}
|
|
36
|
+
|
|
37
|
+
const defaultOmpCommand = 'omp'
|
|
38
|
+
const defaultOmpThinking = 'high'
|
|
39
|
+
const defaultOmpApprovalMode = 'yolo'
|
|
40
|
+
|
|
41
|
+
/** runOmpAgent executes an Oh My Pi coding-agent turn. */
|
|
42
|
+
export async function runOmpAgent(
|
|
43
|
+
options: RunAgentOptions,
|
|
44
|
+
processRunner: OmpProcess = commandOmpProcess,
|
|
45
|
+
): Promise<RunAgentResult> {
|
|
46
|
+
const {
|
|
47
|
+
prompt,
|
|
48
|
+
model,
|
|
49
|
+
sessionId,
|
|
50
|
+
timeoutSec = 3600,
|
|
51
|
+
workdir,
|
|
52
|
+
omp,
|
|
53
|
+
signal,
|
|
54
|
+
} = options
|
|
55
|
+
|
|
56
|
+
if (signal?.aborted) {
|
|
57
|
+
throw new Error('Request aborted')
|
|
58
|
+
}
|
|
59
|
+
|
|
60
|
+
const cwd = workdir ?? PROJECT_ROOT
|
|
61
|
+
const sessionInfo = sessionId ? `[session:${sessionId}]` : '[new session]'
|
|
62
|
+
const promptPreview = prompt.slice(0, 50).replace(/\n/g, ' ')
|
|
63
|
+
console.error(
|
|
64
|
+
`\n>>> OMP [${model.modelID}] ${sessionInfo}: ${promptPreview}...`,
|
|
65
|
+
)
|
|
66
|
+
|
|
67
|
+
const abort = new AbortController()
|
|
68
|
+
const abortHandler = () => abort.abort()
|
|
69
|
+
signal?.addEventListener('abort', abortHandler, { once: true })
|
|
70
|
+
let timedOut = false
|
|
71
|
+
const timeout =
|
|
72
|
+
timeoutSec > 0 ?
|
|
73
|
+
setTimeout(() => {
|
|
74
|
+
timedOut = true
|
|
75
|
+
abort.abort()
|
|
76
|
+
}, timeoutSec * 1000)
|
|
77
|
+
: null
|
|
78
|
+
|
|
79
|
+
try {
|
|
80
|
+
const result = await processRunner.run({
|
|
81
|
+
command: omp?.command ?? defaultOmpCommand,
|
|
82
|
+
args: buildOmpArgs(model.modelID, cwd, sessionId, prompt, omp),
|
|
83
|
+
cwd: omp?.processCwd ?? cwd,
|
|
84
|
+
env: buildOmpEnv(omp),
|
|
85
|
+
signal: abort.signal,
|
|
86
|
+
})
|
|
87
|
+
const summary = parseOmpOutput(result.stdout)
|
|
88
|
+
const detail =
|
|
89
|
+
result.signal ? `signal ${result.signal}` : `status ${result.exitCode}`
|
|
90
|
+
if (result.exitCode !== 0 || result.signal) {
|
|
91
|
+
const message = firstNonEmpty(
|
|
92
|
+
summary.finalMessage,
|
|
93
|
+
result.stderr.trim(),
|
|
94
|
+
result.stdout.trim(),
|
|
95
|
+
detail,
|
|
96
|
+
)
|
|
97
|
+
throw new Error(`OMP exited with ${detail}: ${message}`)
|
|
98
|
+
}
|
|
99
|
+
|
|
100
|
+
const response = firstNonEmpty(
|
|
101
|
+
summary.finalMessage,
|
|
102
|
+
summary.sawJsonEvent ? '' : result.stdout.trim(),
|
|
103
|
+
)
|
|
104
|
+
if (!response) {
|
|
105
|
+
throw new Error('OMP returned an empty final message')
|
|
106
|
+
}
|
|
107
|
+
|
|
108
|
+
const nextSessionId = firstNonEmpty(summary.sessionId, sessionId ?? '')
|
|
109
|
+
console.error(
|
|
110
|
+
`<<< OMP done (${response.length} chars)${nextSessionId ? ` [session:${nextSessionId}]` : ''}`,
|
|
111
|
+
)
|
|
112
|
+
return {
|
|
113
|
+
text: response,
|
|
114
|
+
sessionId: nextSessionId,
|
|
115
|
+
}
|
|
116
|
+
} catch (err) {
|
|
117
|
+
if (timedOut) {
|
|
118
|
+
throw new Error(`Timeout after ${timeoutSec}s`, { cause: err })
|
|
119
|
+
}
|
|
120
|
+
if (signal?.aborted) {
|
|
121
|
+
throw new Error('Request aborted', { cause: err })
|
|
122
|
+
}
|
|
123
|
+
throw err
|
|
124
|
+
} finally {
|
|
125
|
+
if (timeout) clearTimeout(timeout)
|
|
126
|
+
signal?.removeEventListener('abort', abortHandler)
|
|
127
|
+
}
|
|
128
|
+
}
|
|
129
|
+
|
|
130
|
+
function buildOmpArgs(
|
|
131
|
+
modelID: string,
|
|
132
|
+
cwd: string,
|
|
133
|
+
sessionId: string | undefined,
|
|
134
|
+
prompt: string,
|
|
135
|
+
omp: OmpOptions | undefined,
|
|
136
|
+
): string[] {
|
|
137
|
+
const args = ['--print', '--mode', 'json', '--cwd', cwd]
|
|
138
|
+
if (modelID) {
|
|
139
|
+
args.push('--model', modelID)
|
|
140
|
+
}
|
|
141
|
+
if (hasOwn(omp, 'codexHome')) {
|
|
142
|
+
args.push('--codex-home', omp?.codexHome ?? '')
|
|
143
|
+
}
|
|
144
|
+
if (sessionId) {
|
|
145
|
+
args.push('--resume', sessionId)
|
|
146
|
+
}
|
|
147
|
+
if (omp?.goalMode || firstNonEmpty(omp?.goalObjective ?? '')) {
|
|
148
|
+
const objective = firstNonEmpty(omp?.goalObjective ?? '', prompt)
|
|
149
|
+
if (objective) {
|
|
150
|
+
args.push('--goal', objective)
|
|
151
|
+
}
|
|
152
|
+
}
|
|
153
|
+
if (
|
|
154
|
+
typeof omp?.contextStopPercent === 'number' &&
|
|
155
|
+
omp.contextStopPercent > 0
|
|
156
|
+
) {
|
|
157
|
+
args.push('--context-stop-percent', String(omp.contextStopPercent))
|
|
158
|
+
}
|
|
159
|
+
if (typeof omp?.contextStopTokens === 'number' && omp.contextStopTokens > 0) {
|
|
160
|
+
args.push('--context-stop-tokens', String(omp.contextStopTokens))
|
|
161
|
+
}
|
|
162
|
+
if (omp?.scratchHandoffFile) {
|
|
163
|
+
args.push(
|
|
164
|
+
'--scratch-handoff-file',
|
|
165
|
+
resolveOmpPath(cwd, omp.scratchHandoffFile),
|
|
166
|
+
)
|
|
167
|
+
}
|
|
168
|
+
if (omp?.autoApprove ?? true) {
|
|
169
|
+
args.push('--auto-approve')
|
|
170
|
+
}
|
|
171
|
+
const approvalMode = omp?.approvalMode ?? defaultOmpApprovalMode
|
|
172
|
+
if (approvalMode) {
|
|
173
|
+
args.push('--approval-mode', approvalMode)
|
|
174
|
+
}
|
|
175
|
+
const thinking = omp?.thinking ?? defaultOmpThinking
|
|
176
|
+
if (thinking) {
|
|
177
|
+
args.push('--thinking', thinking)
|
|
178
|
+
}
|
|
179
|
+
args.push(...(omp?.extraArgs ?? []))
|
|
180
|
+
if (prompt) {
|
|
181
|
+
args.push('--', prompt)
|
|
182
|
+
}
|
|
183
|
+
return args
|
|
184
|
+
}
|
|
185
|
+
|
|
186
|
+
function buildOmpEnv(omp: OmpOptions | undefined): NodeJS.ProcessEnv {
|
|
187
|
+
const env: NodeJS.ProcessEnv = { ...process.env, ...omp?.env }
|
|
188
|
+
if (hasOwn(omp, 'codexHome')) {
|
|
189
|
+
delete env.CODEX_HOME
|
|
190
|
+
}
|
|
191
|
+
if (omp?.home) {
|
|
192
|
+
env.OMP_HOME = omp.home
|
|
193
|
+
}
|
|
194
|
+
return env
|
|
195
|
+
}
|
|
196
|
+
|
|
197
|
+
function resolveOmpPath(cwd: string, path: string): string {
|
|
198
|
+
return isAbsolute(path) ? path : join(cwd, path)
|
|
199
|
+
}
|
|
200
|
+
|
|
201
|
+
function parseOmpOutput(stdout: string): OmpRunSummary {
|
|
202
|
+
const summary: OmpRunSummary = {
|
|
203
|
+
sawJsonEvent: false,
|
|
204
|
+
sessionId: '',
|
|
205
|
+
finalMessage: '',
|
|
206
|
+
}
|
|
207
|
+
for (const rawLine of stdout.split('\n')) {
|
|
208
|
+
const line = rawLine.trim()
|
|
209
|
+
if (!line) continue
|
|
210
|
+
let event: unknown
|
|
211
|
+
try {
|
|
212
|
+
event = JSON.parse(line)
|
|
213
|
+
} catch {
|
|
214
|
+
continue
|
|
215
|
+
}
|
|
216
|
+
if (!isRecord(event)) continue
|
|
217
|
+
summary.sawJsonEvent = true
|
|
218
|
+
const type = ompString(event.type)
|
|
219
|
+
if (type === 'session') {
|
|
220
|
+
summary.sessionId = firstNonEmpty(
|
|
221
|
+
ompString(event.id),
|
|
222
|
+
ompString(event.sessionId),
|
|
223
|
+
ompString(event.session_id),
|
|
224
|
+
summary.sessionId,
|
|
225
|
+
)
|
|
226
|
+
continue
|
|
227
|
+
}
|
|
228
|
+
if (type === 'message_end') {
|
|
229
|
+
const text = ompAssistantText(event.message)
|
|
230
|
+
if (text) {
|
|
231
|
+
summary.finalMessage = text
|
|
232
|
+
}
|
|
233
|
+
}
|
|
234
|
+
}
|
|
235
|
+
if (!summary.sawJsonEvent) {
|
|
236
|
+
summary.finalMessage = stdout.trim()
|
|
237
|
+
}
|
|
238
|
+
return summary
|
|
239
|
+
}
|
|
240
|
+
|
|
241
|
+
function ompAssistantText(raw: unknown): string {
|
|
242
|
+
if (!isRecord(raw) || ompString(raw.role) !== 'assistant') return ''
|
|
243
|
+
if (Array.isArray(raw.content)) {
|
|
244
|
+
const parts: string[] = []
|
|
245
|
+
for (const rawPart of raw.content) {
|
|
246
|
+
if (!isRecord(rawPart) || ompString(rawPart.type) !== 'text') continue
|
|
247
|
+
const text = ompString(rawPart.text).trim()
|
|
248
|
+
if (text) parts.push(text)
|
|
249
|
+
}
|
|
250
|
+
return parts.join('\n\n')
|
|
251
|
+
}
|
|
252
|
+
return ompString(raw.content).trim()
|
|
253
|
+
}
|
|
254
|
+
|
|
255
|
+
const commandOmpProcess: OmpProcess = {
|
|
256
|
+
run(req) {
|
|
257
|
+
const { promise, resolve, reject } =
|
|
258
|
+
Promise.withResolvers<OmpProcessResult>()
|
|
259
|
+
let child: ChildProcess
|
|
260
|
+
try {
|
|
261
|
+
child = spawn(req.command, req.args, {
|
|
262
|
+
cwd: req.cwd,
|
|
263
|
+
env: req.env,
|
|
264
|
+
stdio: ['ignore', 'pipe', 'pipe'],
|
|
265
|
+
})
|
|
266
|
+
} catch (err) {
|
|
267
|
+
reject(err)
|
|
268
|
+
return promise
|
|
269
|
+
}
|
|
270
|
+
|
|
271
|
+
const stdoutChunks: Buffer[] = []
|
|
272
|
+
const stderrChunks: Buffer[] = []
|
|
273
|
+
const abortHandler = () => child.kill()
|
|
274
|
+
req.signal?.addEventListener('abort', abortHandler, { once: true })
|
|
275
|
+
|
|
276
|
+
child.stdout?.on('data', (chunk: Buffer) => stdoutChunks.push(chunk))
|
|
277
|
+
child.stderr?.on('data', (chunk: Buffer) => stderrChunks.push(chunk))
|
|
278
|
+
child.on('error', reject)
|
|
279
|
+
child.on('close', (exitCode, signal) => {
|
|
280
|
+
req.signal?.removeEventListener('abort', abortHandler)
|
|
281
|
+
resolve({
|
|
282
|
+
stdout: Buffer.concat(stdoutChunks).toString('utf8'),
|
|
283
|
+
stderr: Buffer.concat(stderrChunks).toString('utf8'),
|
|
284
|
+
exitCode,
|
|
285
|
+
signal,
|
|
286
|
+
})
|
|
287
|
+
})
|
|
288
|
+
return promise
|
|
289
|
+
},
|
|
290
|
+
}
|
|
291
|
+
|
|
292
|
+
function hasOwn<T extends object, K extends PropertyKey>(
|
|
293
|
+
value: T | undefined,
|
|
294
|
+
key: K,
|
|
295
|
+
): value is T & Record<K, unknown> {
|
|
296
|
+
return !!value && Object.prototype.hasOwnProperty.call(value, key)
|
|
297
|
+
}
|
|
298
|
+
|
|
299
|
+
function isRecord(value: unknown): value is Record<string, unknown> {
|
|
300
|
+
return typeof value === 'object' && value !== null && !Array.isArray(value)
|
|
301
|
+
}
|
|
302
|
+
|
|
303
|
+
function ompString(value: unknown): string {
|
|
304
|
+
return typeof value === 'string' ? value : ''
|
|
305
|
+
}
|
|
306
|
+
|
|
307
|
+
function firstNonEmpty(...values: string[]): string {
|
|
308
|
+
for (const value of values) {
|
|
309
|
+
const trimmed = value.trim()
|
|
310
|
+
if (trimmed) return trimmed
|
|
311
|
+
}
|
|
312
|
+
return ''
|
|
313
|
+
}
|
package/src/pipeline.ts
CHANGED
package/src/predict.ts
CHANGED
|
@@ -79,6 +79,7 @@ export class Predict<S extends AnySignature> {
|
|
|
79
79
|
claudeCode: ctx.claudeCode,
|
|
80
80
|
codex: ctx.codex,
|
|
81
81
|
pi: ctx.pi,
|
|
82
|
+
omp: ctx.omp,
|
|
82
83
|
signal: ctx.signal,
|
|
83
84
|
})
|
|
84
85
|
|
|
@@ -211,6 +212,7 @@ export class Predict<S extends AnySignature> {
|
|
|
211
212
|
claudeCode: ctx.claudeCode,
|
|
212
213
|
codex: ctx.codex,
|
|
213
214
|
pi: ctx.pi,
|
|
215
|
+
omp: ctx.omp,
|
|
214
216
|
signal: ctx.signal,
|
|
215
217
|
})
|
|
216
218
|
|
|
@@ -290,6 +292,7 @@ export class Predict<S extends AnySignature> {
|
|
|
290
292
|
claudeCode: ctx.claudeCode,
|
|
291
293
|
codex: ctx.codex,
|
|
292
294
|
pi: ctx.pi,
|
|
295
|
+
omp: ctx.omp,
|
|
293
296
|
signal: ctx.signal,
|
|
294
297
|
})
|
|
295
298
|
|
package/src/types.ts
CHANGED
|
@@ -9,7 +9,7 @@ import type { z } from 'zod/v4'
|
|
|
9
9
|
// ============================================================================
|
|
10
10
|
|
|
11
11
|
/** Backend type for running agents. */
|
|
12
|
-
export type BackendType = 'opencode' | 'claude-code' | 'codex' | 'pi'
|
|
12
|
+
export type BackendType = 'opencode' | 'claude-code' | 'codex' | 'pi' | 'omp'
|
|
13
13
|
|
|
14
14
|
/** Reasoning effort for Codex SDK threads. */
|
|
15
15
|
export type CodexReasoningEffort =
|
|
@@ -133,6 +133,38 @@ export interface PiOptions {
|
|
|
133
133
|
env?: Record<string, string>
|
|
134
134
|
}
|
|
135
135
|
|
|
136
|
+
/** Oh My Pi CLI specific session options. */
|
|
137
|
+
export interface OmpOptions {
|
|
138
|
+
/** Path to the Oh My Pi executable (default: `omp` from PATH). */
|
|
139
|
+
command?: string
|
|
140
|
+
/** Process cwd used to launch Oh My Pi; defaults to the target workdir. */
|
|
141
|
+
processCwd?: string
|
|
142
|
+
/** OMP_HOME passed to the Oh My Pi subprocess. */
|
|
143
|
+
home?: string
|
|
144
|
+
/** CODEX_HOME value passed via `--codex-home`; set to empty string to clear it. */
|
|
145
|
+
codexHome?: string
|
|
146
|
+
/** Extra environment variables passed to the Oh My Pi subprocess. */
|
|
147
|
+
env?: Record<string, string>
|
|
148
|
+
/** Add `--auto-approve` (default: true). */
|
|
149
|
+
autoApprove?: boolean
|
|
150
|
+
/** Approval mode passed with `--approval-mode` (default: `yolo`). */
|
|
151
|
+
approvalMode?: string
|
|
152
|
+
/** Thinking effort passed with `--thinking` (default: `high`). */
|
|
153
|
+
thinking?: string
|
|
154
|
+
/** Run Oh My Pi as a continuing headless goal. */
|
|
155
|
+
goalMode?: boolean
|
|
156
|
+
/** Goal objective; setting this also enables goal mode. */
|
|
157
|
+
goalObjective?: string
|
|
158
|
+
/** Stop when context reaches this percent of the model window. */
|
|
159
|
+
contextStopPercent?: number
|
|
160
|
+
/** Stop when context reaches this token count. */
|
|
161
|
+
contextStopTokens?: number
|
|
162
|
+
/** Scratch handoff path passed to Oh My Pi; relative paths resolve against workdir. */
|
|
163
|
+
scratchHandoffFile?: string
|
|
164
|
+
/** Extra raw Oh My Pi arguments appended before the prompt separator. */
|
|
165
|
+
extraArgs?: string[]
|
|
166
|
+
}
|
|
167
|
+
|
|
136
168
|
/** Model configuration for LLM backends. */
|
|
137
169
|
export interface ModelConfig {
|
|
138
170
|
/** Backend to use (default: 'opencode'). */
|
|
@@ -168,6 +200,8 @@ export interface ExecutionContext {
|
|
|
168
200
|
codex?: CodexOptions
|
|
169
201
|
/** Pi coding agent specific options. */
|
|
170
202
|
pi?: PiOptions
|
|
203
|
+
/** Oh My Pi specific options. */
|
|
204
|
+
omp?: OmpOptions
|
|
171
205
|
/** AbortSignal for cancelling in-flight backend requests. */
|
|
172
206
|
signal?: AbortSignal
|
|
173
207
|
}
|
|
@@ -390,6 +424,8 @@ export interface PipelineConfig {
|
|
|
390
424
|
codex?: CodexOptions
|
|
391
425
|
/** Pi coding agent specific options. */
|
|
392
426
|
pi?: PiOptions
|
|
427
|
+
/** Oh My Pi specific options. */
|
|
428
|
+
omp?: OmpOptions
|
|
393
429
|
}
|
|
394
430
|
|
|
395
431
|
/** Options for running a pipeline step. */
|
|
@@ -428,6 +464,8 @@ export interface RunAgentOptions {
|
|
|
428
464
|
codex?: CodexOptions
|
|
429
465
|
/** Pi coding agent specific options. */
|
|
430
466
|
pi?: PiOptions
|
|
467
|
+
/** Oh My Pi specific options. */
|
|
468
|
+
omp?: OmpOptions
|
|
431
469
|
/** AbortSignal for cancelling the request. */
|
|
432
470
|
signal?: AbortSignal
|
|
433
471
|
}
|