@twelvehart/orcats 0.0.0-seed.0 → 0.2.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.
- package/LICENSE +48 -0
- package/NOTICE +5 -0
- package/README.md +399 -0
- package/bin/orcats +4 -0
- package/dist/backends/acp-client.d.ts +74 -0
- package/dist/backends/acp-client.d.ts.map +1 -0
- package/dist/backends/acp-run.d.ts +31 -0
- package/dist/backends/acp-run.d.ts.map +1 -0
- package/dist/backends/claude-run.d.ts +35 -0
- package/dist/backends/claude-run.d.ts.map +1 -0
- package/dist/backends/claude-stream-json.d.ts +15 -0
- package/dist/backends/claude-stream-json.d.ts.map +1 -0
- package/dist/backends/codex-jsonl.d.ts +34 -0
- package/dist/backends/codex-jsonl.d.ts.map +1 -0
- package/dist/backends/codex-run.d.ts +40 -0
- package/dist/backends/codex-run.d.ts.map +1 -0
- package/dist/backends/codex.d.ts +5 -0
- package/dist/backends/codex.d.ts.map +1 -0
- package/dist/backends/conversation-config.d.ts +12 -0
- package/dist/backends/conversation-config.d.ts.map +1 -0
- package/dist/backends/index.d.ts +14 -0
- package/dist/backends/index.d.ts.map +1 -0
- package/dist/backends/opencode-run.d.ts +48 -0
- package/dist/backends/opencode-run.d.ts.map +1 -0
- package/dist/backends/opencode-sse.d.ts +35 -0
- package/dist/backends/opencode-sse.d.ts.map +1 -0
- package/dist/backends/pi-rpc.d.ts +21 -0
- package/dist/backends/pi-rpc.d.ts.map +1 -0
- package/dist/backends/pi-run.d.ts +21 -0
- package/dist/backends/pi-run.d.ts.map +1 -0
- package/dist/backends/select.d.ts +17 -0
- package/dist/backends/select.d.ts.map +1 -0
- package/dist/backends/subprocess-run.d.ts +68 -0
- package/dist/backends/subprocess-run.d.ts.map +1 -0
- package/dist/backends/types.d.ts +17 -0
- package/dist/backends/types.d.ts.map +1 -0
- package/dist/backends/unsupported.d.ts +9 -0
- package/dist/backends/unsupported.d.ts.map +1 -0
- package/dist/baseline/index.d.ts +56 -0
- package/dist/baseline/index.d.ts.map +1 -0
- package/dist/cli/args.d.ts +22 -0
- package/dist/cli/args.d.ts.map +1 -0
- package/dist/cli/embedded.d.ts +7 -0
- package/dist/cli/embedded.d.ts.map +1 -0
- package/dist/cli/main.d.ts +6 -0
- package/dist/cli/main.d.ts.map +1 -0
- package/dist/cli/version.d.ts +2 -0
- package/dist/cli/version.d.ts.map +1 -0
- package/dist/conversation/ask-user.d.ts +17 -0
- package/dist/conversation/ask-user.d.ts.map +1 -0
- package/dist/conversation/conversation.d.ts +46 -0
- package/dist/conversation/conversation.d.ts.map +1 -0
- package/dist/conversation/harness.d.ts +14 -0
- package/dist/conversation/harness.d.ts.map +1 -0
- package/dist/conversation/index.d.ts +5 -0
- package/dist/conversation/index.d.ts.map +1 -0
- package/dist/conversation/queue.d.ts +15 -0
- package/dist/conversation/queue.d.ts.map +1 -0
- package/dist/flow/accessors.d.ts +13 -0
- package/dist/flow/accessors.d.ts.map +1 -0
- package/dist/flow/approvals.d.ts +6 -0
- package/dist/flow/approvals.d.ts.map +1 -0
- package/dist/flow/args.d.ts +4 -0
- package/dist/flow/args.d.ts.map +1 -0
- package/dist/flow/context.d.ts +36 -0
- package/dist/flow/context.d.ts.map +1 -0
- package/dist/flow/index.d.ts +5 -0
- package/dist/flow/index.d.ts.map +1 -0
- package/dist/index.d.ts +16 -0
- package/dist/index.d.ts.map +1 -0
- package/dist/loop/builder/index.d.ts +17 -0
- package/dist/loop/builder/index.d.ts.map +1 -0
- package/dist/loop/builder/types.d.ts +97 -0
- package/dist/loop/builder/types.d.ts.map +1 -0
- package/dist/loop/context/compaction.d.ts +38 -0
- package/dist/loop/context/compaction.d.ts.map +1 -0
- package/dist/loop/context/index.d.ts +4 -0
- package/dist/loop/context/index.d.ts.map +1 -0
- package/dist/loop/context/offload.d.ts +34 -0
- package/dist/loop/context/offload.d.ts.map +1 -0
- package/dist/loop/context/types.d.ts +13 -0
- package/dist/loop/context/types.d.ts.map +1 -0
- package/dist/loop/engine/bridge.d.ts +21 -0
- package/dist/loop/engine/bridge.d.ts.map +1 -0
- package/dist/loop/engine/index.d.ts +73 -0
- package/dist/loop/engine/index.d.ts.map +1 -0
- package/dist/loop/execution.d.ts +68 -0
- package/dist/loop/execution.d.ts.map +1 -0
- package/dist/loop/fanout.d.ts +148 -0
- package/dist/loop/fanout.d.ts.map +1 -0
- package/dist/loop/firing.d.ts +54 -0
- package/dist/loop/firing.d.ts.map +1 -0
- package/dist/loop/graph/analyze.d.ts +8 -0
- package/dist/loop/graph/analyze.d.ts.map +1 -0
- package/dist/loop/graph/index.d.ts +3 -0
- package/dist/loop/graph/index.d.ts.map +1 -0
- package/dist/loop/graph/types.d.ts +35 -0
- package/dist/loop/graph/types.d.ts.map +1 -0
- package/dist/loop/index.d.ts +7 -0
- package/dist/loop/index.d.ts.map +1 -0
- package/dist/loop/io/index.d.ts +4 -0
- package/dist/loop/io/index.d.ts.map +1 -0
- package/dist/loop/io/linear.d.ts +142 -0
- package/dist/loop/io/linear.d.ts.map +1 -0
- package/dist/loop/io/sink.d.ts +47 -0
- package/dist/loop/io/sink.d.ts.map +1 -0
- package/dist/loop/io/source.d.ts +64 -0
- package/dist/loop/io/source.d.ts.map +1 -0
- package/dist/loop/serve.d.ts +97 -0
- package/dist/loop/serve.d.ts.map +1 -0
- package/dist/loop/state/index.d.ts +5 -0
- package/dist/loop/state/index.d.ts.map +1 -0
- package/dist/loop/state/manifest.d.ts +36 -0
- package/dist/loop/state/manifest.d.ts.map +1 -0
- package/dist/loop/state/port.d.ts +33 -0
- package/dist/loop/state/port.d.ts.map +1 -0
- package/dist/loop/state/snapshot.d.ts +15 -0
- package/dist/loop/state/snapshot.d.ts.map +1 -0
- package/dist/loop/state/sqlite.d.ts +31 -0
- package/dist/loop/state/sqlite.d.ts.map +1 -0
- package/dist/loop/termination-contract.d.ts +28 -0
- package/dist/loop/termination-contract.d.ts.map +1 -0
- package/dist/loop/termination.d.ts +33 -0
- package/dist/loop/termination.d.ts.map +1 -0
- package/dist/model/backend-config.d.ts +30 -0
- package/dist/model/backend-config.d.ts.map +1 -0
- package/dist/model/brand.d.ts +9 -0
- package/dist/model/brand.d.ts.map +1 -0
- package/dist/model/errors.d.ts +11 -0
- package/dist/model/errors.d.ts.map +1 -0
- package/dist/model/index.d.ts +8 -0
- package/dist/model/index.d.ts.map +1 -0
- package/dist/model/json-schema.d.ts +4 -0
- package/dist/model/json-schema.d.ts.map +1 -0
- package/dist/model/result.d.ts +3 -0
- package/dist/model/result.d.ts.map +1 -0
- package/dist/model/schemas.d.ts +435 -0
- package/dist/model/schemas.d.ts.map +1 -0
- package/dist/model/structured.d.ts +9 -0
- package/dist/model/structured.d.ts.map +1 -0
- package/dist/monitor/index.d.ts +139 -0
- package/dist/monitor/index.d.ts.map +1 -0
- package/dist/plan/index.d.ts +3 -0
- package/dist/plan/index.d.ts.map +1 -0
- package/dist/plan/persistent.d.ts +23 -0
- package/dist/plan/persistent.d.ts.map +1 -0
- package/dist/plan/verdict.d.ts +9 -0
- package/dist/plan/verdict.d.ts.map +1 -0
- package/dist/review/index.d.ts +4 -0
- package/dist/review/index.d.ts.map +1 -0
- package/dist/review/loop.d.ts +117 -0
- package/dist/review/loop.d.ts.map +1 -0
- package/dist/review/reviewers.d.ts +13 -0
- package/dist/review/reviewers.d.ts.map +1 -0
- package/dist/review/tool.d.ts +20 -0
- package/dist/review/tool.d.ts.map +1 -0
- package/dist/run-output/index.d.ts +112 -0
- package/dist/run-output/index.d.ts.map +1 -0
- package/dist/runner/index.d.ts +2 -0
- package/dist/runner/index.d.ts.map +1 -0
- package/dist/runner/typecheck.d.ts +23 -0
- package/dist/runner/typecheck.d.ts.map +1 -0
- package/dist/test-utils/fakes.d.ts +76 -0
- package/dist/test-utils/fakes.d.ts.map +1 -0
- package/dist/test-utils/fixtures.d.ts +4 -0
- package/dist/test-utils/fixtures.d.ts.map +1 -0
- package/dist/test-utils/index.d.ts +4 -0
- package/dist/test-utils/index.d.ts.map +1 -0
- package/dist/test-utils/tier2.d.ts +8 -0
- package/dist/test-utils/tier2.d.ts.map +1 -0
- package/dist/tools/fs.d.ts +11 -0
- package/dist/tools/fs.d.ts.map +1 -0
- package/dist/tools/git.d.ts +10 -0
- package/dist/tools/git.d.ts.map +1 -0
- package/dist/tools/github.d.ts +13 -0
- package/dist/tools/github.d.ts.map +1 -0
- package/dist/tools/index.d.ts +7 -0
- package/dist/tools/index.d.ts.map +1 -0
- package/dist/tools/linear.d.ts +117 -0
- package/dist/tools/linear.d.ts.map +1 -0
- package/dist/tools/process.d.ts +40 -0
- package/dist/tools/process.d.ts.map +1 -0
- package/dist/tools/terminal.d.ts +23 -0
- package/dist/tools/terminal.d.ts.map +1 -0
- package/package.json +89 -9
- package/src/backends/acp-client.ts +324 -0
- package/src/backends/acp-run.ts +636 -0
- package/src/backends/claude-run.ts +226 -0
- package/src/backends/claude-stream-json.ts +273 -0
- package/src/backends/codex-jsonl.ts +339 -0
- package/src/backends/codex-run.ts +177 -0
- package/src/backends/codex.ts +84 -0
- package/src/backends/conversation-config.ts +26 -0
- package/src/backends/index.ts +13 -0
- package/src/backends/opencode-run.ts +432 -0
- package/src/backends/opencode-sse.ts +360 -0
- package/src/backends/pi-rpc.ts +242 -0
- package/src/backends/pi-run.ts +154 -0
- package/src/backends/select.ts +77 -0
- package/src/backends/subprocess-run.ts +305 -0
- package/src/backends/types.ts +29 -0
- package/src/backends/unsupported.ts +48 -0
- package/src/baseline/index.ts +437 -0
- package/src/cli/args.ts +109 -0
- package/src/cli/embedded.ts +206 -0
- package/src/cli/main.ts +332 -0
- package/src/cli/version.ts +1 -0
- package/src/conversation/ask-user.ts +105 -0
- package/src/conversation/conversation.ts +109 -0
- package/src/conversation/harness.ts +39 -0
- package/src/conversation/index.ts +4 -0
- package/src/conversation/queue.ts +86 -0
- package/src/flow/accessors.ts +43 -0
- package/src/flow/approvals.ts +10 -0
- package/src/flow/args.ts +26 -0
- package/src/flow/context.ts +106 -0
- package/src/flow/index.ts +4 -0
- package/src/index.ts +17 -0
- package/src/loop/builder/index.ts +346 -0
- package/src/loop/builder/types.ts +131 -0
- package/src/loop/context/compaction.ts +175 -0
- package/src/loop/context/index.ts +3 -0
- package/src/loop/context/offload.ts +79 -0
- package/src/loop/context/types.ts +17 -0
- package/src/loop/engine/bridge.ts +61 -0
- package/src/loop/engine/index.ts +166 -0
- package/src/loop/execution.ts +453 -0
- package/src/loop/fanout.ts +444 -0
- package/src/loop/firing.ts +188 -0
- package/src/loop/graph/analyze.ts +128 -0
- package/src/loop/graph/index.ts +2 -0
- package/src/loop/graph/types.ts +45 -0
- package/src/loop/index.ts +11 -0
- package/src/loop/io/index.ts +3 -0
- package/src/loop/io/linear.ts +714 -0
- package/src/loop/io/sink.ts +170 -0
- package/src/loop/io/source.ts +255 -0
- package/src/loop/serve.ts +307 -0
- package/src/loop/state/index.ts +4 -0
- package/src/loop/state/manifest.ts +56 -0
- package/src/loop/state/port.ts +43 -0
- package/src/loop/state/snapshot.ts +129 -0
- package/src/loop/state/sqlite.ts +265 -0
- package/src/loop/termination-contract.ts +89 -0
- package/src/loop/termination.ts +156 -0
- package/src/model/backend-config.ts +34 -0
- package/src/model/brand.ts +11 -0
- package/src/model/errors.ts +28 -0
- package/src/model/index.ts +7 -0
- package/src/model/json-schema.ts +14 -0
- package/src/model/result.ts +28 -0
- package/src/model/schemas.ts +218 -0
- package/src/model/structured.ts +26 -0
- package/src/monitor/index.ts +367 -0
- package/src/plan/index.ts +2 -0
- package/src/plan/persistent.ts +98 -0
- package/src/plan/verdict.ts +3 -0
- package/src/review/index.ts +3 -0
- package/src/review/loop.ts +365 -0
- package/src/review/reviewers.ts +92 -0
- package/src/review/tool.ts +41 -0
- package/src/run-output/index.ts +319 -0
- package/src/runner/index.ts +1 -0
- package/src/runner/typecheck.ts +72 -0
- package/src/test-utils/fakes.ts +260 -0
- package/src/test-utils/fixtures.ts +33 -0
- package/src/test-utils/index.ts +3 -0
- package/src/test-utils/tier2.ts +12 -0
- package/src/tools/fs.ts +44 -0
- package/src/tools/git.ts +34 -0
- package/src/tools/github.ts +25 -0
- package/src/tools/index.ts +6 -0
- package/src/tools/linear.ts +428 -0
- package/src/tools/process.ts +156 -0
- package/src/tools/terminal.ts +116 -0
|
@@ -0,0 +1,339 @@
|
|
|
1
|
+
import type { z } from "zod";
|
|
2
|
+
import {
|
|
3
|
+
collectConversation,
|
|
4
|
+
StreamConversation,
|
|
5
|
+
type ConversationCapture
|
|
6
|
+
} from "../conversation/index.ts";
|
|
7
|
+
import {
|
|
8
|
+
backendFailed,
|
|
9
|
+
parseStructuredOutput,
|
|
10
|
+
sessionId,
|
|
11
|
+
structuredOutputValidationFailed,
|
|
12
|
+
unsupportedFeature,
|
|
13
|
+
type BackendApprovalPolicy,
|
|
14
|
+
type BackendSandboxMode,
|
|
15
|
+
type RuntimeError,
|
|
16
|
+
type Usage
|
|
17
|
+
} from "../model/index.ts";
|
|
18
|
+
|
|
19
|
+
interface CodexLine {
|
|
20
|
+
readonly type?: string;
|
|
21
|
+
readonly thread_id?: string;
|
|
22
|
+
readonly usage?: {
|
|
23
|
+
readonly input_tokens?: number;
|
|
24
|
+
readonly output_tokens?: number;
|
|
25
|
+
readonly reasoning_output_tokens?: number;
|
|
26
|
+
};
|
|
27
|
+
readonly item?: {
|
|
28
|
+
readonly id?: string;
|
|
29
|
+
readonly type?: string;
|
|
30
|
+
readonly text?: string;
|
|
31
|
+
readonly name?: string;
|
|
32
|
+
readonly server?: string;
|
|
33
|
+
readonly tool?: string;
|
|
34
|
+
readonly command?: unknown;
|
|
35
|
+
readonly path?: string;
|
|
36
|
+
readonly changes?: unknown;
|
|
37
|
+
readonly output?: unknown;
|
|
38
|
+
readonly arguments?: unknown;
|
|
39
|
+
readonly result?: {
|
|
40
|
+
readonly content?: ReadonlyArray<{ readonly text?: string }>;
|
|
41
|
+
} | null;
|
|
42
|
+
readonly error?: string | null;
|
|
43
|
+
};
|
|
44
|
+
}
|
|
45
|
+
|
|
46
|
+
export type CodexParseResult = ConversationCapture<"codex">;
|
|
47
|
+
|
|
48
|
+
export interface CodexExecArgs {
|
|
49
|
+
readonly model?: string;
|
|
50
|
+
readonly approvalPolicy?: BackendApprovalPolicy;
|
|
51
|
+
readonly sandbox?: BackendSandboxMode;
|
|
52
|
+
readonly readOnly?: boolean;
|
|
53
|
+
readonly ignoreUserConfig?: boolean;
|
|
54
|
+
readonly outputSchemaPath?: string;
|
|
55
|
+
readonly resumeSessionId?: string;
|
|
56
|
+
readonly mcpServerUrl?: string;
|
|
57
|
+
readonly prompt?: string;
|
|
58
|
+
}
|
|
59
|
+
|
|
60
|
+
export function codexExecJsonlArgs(args: CodexExecArgs = {}): readonly string[] {
|
|
61
|
+
const sandbox = args.sandbox ?? (args.readOnly ? "read-only" : undefined);
|
|
62
|
+
const promptArgs = args.prompt === undefined ? [] : [args.prompt];
|
|
63
|
+
const commonArgs = [
|
|
64
|
+
"--json",
|
|
65
|
+
...(args.ignoreUserConfig ? ["--ignore-user-config"] : []),
|
|
66
|
+
...(args.model ? ["--model", args.model] : []),
|
|
67
|
+
...(args.approvalPolicy ? ["-c", `approval_policy="${args.approvalPolicy}"`] : []),
|
|
68
|
+
...(args.mcpServerUrl ? ["-c", `mcp_servers.orcats.url=${JSON.stringify(args.mcpServerUrl)}`] : []),
|
|
69
|
+
...(args.outputSchemaPath ? ["--output-schema", args.outputSchemaPath] : [])
|
|
70
|
+
];
|
|
71
|
+
|
|
72
|
+
if (args.resumeSessionId) {
|
|
73
|
+
return [
|
|
74
|
+
"exec",
|
|
75
|
+
"resume",
|
|
76
|
+
...commonArgs,
|
|
77
|
+
// `codex exec resume` rejects the exec-only `--sandbox` flag; express the
|
|
78
|
+
// sandbox as the equivalent config override instead (same form as
|
|
79
|
+
// `approval_policy` above).
|
|
80
|
+
...(sandbox ? ["-c", `sandbox_mode="${sandbox}"`] : []),
|
|
81
|
+
args.resumeSessionId,
|
|
82
|
+
...promptArgs
|
|
83
|
+
];
|
|
84
|
+
}
|
|
85
|
+
|
|
86
|
+
return [
|
|
87
|
+
"exec",
|
|
88
|
+
...commonArgs,
|
|
89
|
+
...(sandbox ? ["--sandbox", sandbox] : []),
|
|
90
|
+
...promptArgs
|
|
91
|
+
];
|
|
92
|
+
}
|
|
93
|
+
|
|
94
|
+
export interface CodexJsonlOptions<Output = unknown> {
|
|
95
|
+
readonly schema?: z.ZodType<Output>;
|
|
96
|
+
/** Interactive conversations surface the agent's `ask_user` call as a
|
|
97
|
+
* `user_question` event for the renderer. The answer is NOT routed here — Codex
|
|
98
|
+
* obtains it from the Orcats MCP HTTP bridge and re-emits it on the matching
|
|
99
|
+
* `item.completed`, which the tool-result branch turns into a `tool_result`.
|
|
100
|
+
* Autonomous conversations leave this false and reject `ask_user` explicitly. */
|
|
101
|
+
readonly interactive?: boolean;
|
|
102
|
+
}
|
|
103
|
+
|
|
104
|
+
export async function collectCodexJsonl<Output = unknown>(
|
|
105
|
+
lines: readonly string[],
|
|
106
|
+
options: CodexJsonlOptions<Output> = {}
|
|
107
|
+
): Promise<CodexParseResult> {
|
|
108
|
+
return collectConversation({
|
|
109
|
+
backend: "codex",
|
|
110
|
+
consume: async (conversation) => {
|
|
111
|
+
await consumeCodexJsonl(lines, conversation, options);
|
|
112
|
+
}
|
|
113
|
+
});
|
|
114
|
+
}
|
|
115
|
+
|
|
116
|
+
export async function consumeCodexJsonl<Output = unknown>(
|
|
117
|
+
lines: readonly string[],
|
|
118
|
+
conversation: StreamConversation<"codex">,
|
|
119
|
+
options: CodexJsonlOptions<Output> = {}
|
|
120
|
+
): Promise<void> {
|
|
121
|
+
const consumer = createCodexJsonlConsumer(conversation, options);
|
|
122
|
+
for (const raw of lines) {
|
|
123
|
+
await consumer.consume(raw);
|
|
124
|
+
}
|
|
125
|
+
consumer.finish();
|
|
126
|
+
}
|
|
127
|
+
|
|
128
|
+
export interface CodexJsonlConsumer {
|
|
129
|
+
readonly signal: AbortSignal;
|
|
130
|
+
consume(raw: string): Promise<void>;
|
|
131
|
+
finish(): void;
|
|
132
|
+
}
|
|
133
|
+
|
|
134
|
+
export function createCodexJsonlConsumer<Output = unknown>(
|
|
135
|
+
conversation: StreamConversation<"codex">,
|
|
136
|
+
options: CodexJsonlOptions<Output> = {}
|
|
137
|
+
): CodexJsonlConsumer {
|
|
138
|
+
let threadId = "";
|
|
139
|
+
let output = "";
|
|
140
|
+
let lastAgentText = "";
|
|
141
|
+
const controller = new AbortController();
|
|
142
|
+
|
|
143
|
+
return {
|
|
144
|
+
get signal() {
|
|
145
|
+
return controller.signal;
|
|
146
|
+
},
|
|
147
|
+
|
|
148
|
+
async consume(raw: string): Promise<void> {
|
|
149
|
+
if (controller.signal.aborted || raw.trim() === "") {
|
|
150
|
+
return;
|
|
151
|
+
}
|
|
152
|
+
|
|
153
|
+
let line: CodexLine;
|
|
154
|
+
try {
|
|
155
|
+
line = JSON.parse(raw) as CodexLine;
|
|
156
|
+
} catch (error) {
|
|
157
|
+
controller.abort();
|
|
158
|
+
const message = error instanceof Error ? error.message : String(error);
|
|
159
|
+
conversation.fail(backendFailed("codex", `invalid codex JSONL: ${message}`));
|
|
160
|
+
return;
|
|
161
|
+
}
|
|
162
|
+
|
|
163
|
+
if (line.type === "thread.started") {
|
|
164
|
+
threadId = line.thread_id ?? threadId;
|
|
165
|
+
return;
|
|
166
|
+
}
|
|
167
|
+
|
|
168
|
+
if (line.type === "item.started" && isToolItem(line.item)) {
|
|
169
|
+
if (line.item.server === "orcats" && line.item.tool === "ask_user") {
|
|
170
|
+
if (options.interactive) {
|
|
171
|
+
// Surface the question for the renderer only. The answer travels
|
|
172
|
+
// Codex <-> Orcats MCP HTTP bridge and arrives on the matching
|
|
173
|
+
// `item.completed`, which the tool-result branch emits below.
|
|
174
|
+
const question = askUserQuestion(line.item.arguments);
|
|
175
|
+
await conversation.emit({ type: "user_question", question });
|
|
176
|
+
return;
|
|
177
|
+
}
|
|
178
|
+
controller.abort();
|
|
179
|
+
conversation.fail(
|
|
180
|
+
unsupportedFeature(
|
|
181
|
+
"codex ask_user",
|
|
182
|
+
"Codex ask_user MCP bridge requires an explicit interactive conversation"
|
|
183
|
+
)
|
|
184
|
+
);
|
|
185
|
+
return;
|
|
186
|
+
}
|
|
187
|
+
await conversation.emit({
|
|
188
|
+
type: "assistant_tool_call",
|
|
189
|
+
id: line.item.id ?? "",
|
|
190
|
+
name: toolName(line.item),
|
|
191
|
+
input: toolInput(line.item)
|
|
192
|
+
});
|
|
193
|
+
return;
|
|
194
|
+
}
|
|
195
|
+
|
|
196
|
+
if (line.type === "item.completed" && isToolItem(line.item)) {
|
|
197
|
+
await conversation.emit({
|
|
198
|
+
type: "tool_result",
|
|
199
|
+
toolCallId: line.item.id ?? "",
|
|
200
|
+
output: toolOutput(line.item),
|
|
201
|
+
...(line.item.error ? { isError: true } : {})
|
|
202
|
+
});
|
|
203
|
+
if (line.item.error) {
|
|
204
|
+
controller.abort();
|
|
205
|
+
conversation.fail(backendFailed("codex", line.item.error));
|
|
206
|
+
}
|
|
207
|
+
return;
|
|
208
|
+
}
|
|
209
|
+
|
|
210
|
+
if (line.type === "item.completed" && line.item?.type === "agent_message") {
|
|
211
|
+
const text = line.item.text ?? "";
|
|
212
|
+
output += text;
|
|
213
|
+
lastAgentText = text;
|
|
214
|
+
await conversation.emit({ type: "assistant_text_delta", text });
|
|
215
|
+
return;
|
|
216
|
+
}
|
|
217
|
+
|
|
218
|
+
if (line.type === "turn.completed") {
|
|
219
|
+
await conversation.emit({ type: "assistant_turn_end" });
|
|
220
|
+
// Codex emits one agent_message per step (progress updates + final answer).
|
|
221
|
+
// Only the last message is the structured result; using `output` (all steps
|
|
222
|
+
// concatenated) produces invalid JSON when the schema is set.
|
|
223
|
+
const structured = parseCodexStructuredOutput(options.schema, lastAgentText || output);
|
|
224
|
+
if (structured.type === "failed") {
|
|
225
|
+
controller.abort();
|
|
226
|
+
conversation.fail(structured.error);
|
|
227
|
+
return;
|
|
228
|
+
}
|
|
229
|
+
|
|
230
|
+
controller.abort();
|
|
231
|
+
conversation.succeed({
|
|
232
|
+
backend: "codex",
|
|
233
|
+
sessionId: sessionId("codex", threadId),
|
|
234
|
+
output,
|
|
235
|
+
...(structured.value === undefined ? {} : { structured: structured.value }),
|
|
236
|
+
usage: normalizeCodexUsage(line.usage)
|
|
237
|
+
});
|
|
238
|
+
}
|
|
239
|
+
},
|
|
240
|
+
|
|
241
|
+
finish(): void {
|
|
242
|
+
if (controller.signal.aborted) {
|
|
243
|
+
return;
|
|
244
|
+
}
|
|
245
|
+
controller.abort();
|
|
246
|
+
conversation.fail(backendFailed("codex", "codex stream ended before turn.completed"));
|
|
247
|
+
}
|
|
248
|
+
};
|
|
249
|
+
}
|
|
250
|
+
|
|
251
|
+
function parseCodexStructuredOutput<Output>(
|
|
252
|
+
schema: z.ZodType<Output> | undefined,
|
|
253
|
+
output: string
|
|
254
|
+
):
|
|
255
|
+
| { readonly type: "success"; readonly value?: Output }
|
|
256
|
+
| { readonly type: "failed"; readonly error: RuntimeError } {
|
|
257
|
+
if (!schema) {
|
|
258
|
+
return { type: "success" };
|
|
259
|
+
}
|
|
260
|
+
|
|
261
|
+
let raw: unknown;
|
|
262
|
+
try {
|
|
263
|
+
raw = JSON.parse(output) as unknown;
|
|
264
|
+
} catch {
|
|
265
|
+
return {
|
|
266
|
+
type: "failed",
|
|
267
|
+
error: structuredOutputValidationFailed({
|
|
268
|
+
raw: output,
|
|
269
|
+
issues: ["Codex structured output was not valid JSON"]
|
|
270
|
+
})
|
|
271
|
+
};
|
|
272
|
+
}
|
|
273
|
+
|
|
274
|
+
const parsed = parseStructuredOutput(schema, raw);
|
|
275
|
+
if (parsed.isErr()) {
|
|
276
|
+
return { type: "failed", error: parsed.error };
|
|
277
|
+
}
|
|
278
|
+
|
|
279
|
+
return { type: "success", value: parsed.value.value };
|
|
280
|
+
}
|
|
281
|
+
|
|
282
|
+
function isToolItem(item: CodexLine["item"]): item is NonNullable<CodexLine["item"]> {
|
|
283
|
+
return (
|
|
284
|
+
item?.type === "mcp_tool_call" ||
|
|
285
|
+
item?.type === "command_execution" ||
|
|
286
|
+
item?.type === "file_change" ||
|
|
287
|
+
item?.type === "tool_call"
|
|
288
|
+
);
|
|
289
|
+
}
|
|
290
|
+
|
|
291
|
+
function toolName(item: NonNullable<CodexLine["item"]>): string {
|
|
292
|
+
if (item.type === "mcp_tool_call") {
|
|
293
|
+
return `${item.server ?? ""}.${item.tool ?? ""}`;
|
|
294
|
+
}
|
|
295
|
+
return item.name ?? item.tool ?? item.type ?? "tool_call";
|
|
296
|
+
}
|
|
297
|
+
|
|
298
|
+
function toolInput(item: NonNullable<CodexLine["item"]>): unknown {
|
|
299
|
+
if (item.arguments !== undefined) {
|
|
300
|
+
return item.arguments;
|
|
301
|
+
}
|
|
302
|
+
if (item.command !== undefined) {
|
|
303
|
+
return { command: item.command };
|
|
304
|
+
}
|
|
305
|
+
if (item.path !== undefined || item.changes !== undefined) {
|
|
306
|
+
return {
|
|
307
|
+
...(item.path === undefined ? {} : { path: item.path }),
|
|
308
|
+
...(item.changes === undefined ? {} : { changes: item.changes })
|
|
309
|
+
};
|
|
310
|
+
}
|
|
311
|
+
return {};
|
|
312
|
+
}
|
|
313
|
+
|
|
314
|
+
function toolOutput(item: NonNullable<CodexLine["item"]>): unknown {
|
|
315
|
+
if (item.output !== undefined) {
|
|
316
|
+
return item.output;
|
|
317
|
+
}
|
|
318
|
+
const content = item.result?.content?.map((part) => part.text ?? "").join("") ?? "";
|
|
319
|
+
if (content.length > 0) {
|
|
320
|
+
return content;
|
|
321
|
+
}
|
|
322
|
+
return item.error ?? "";
|
|
323
|
+
}
|
|
324
|
+
|
|
325
|
+
function askUserQuestion(input: unknown): string {
|
|
326
|
+
if (typeof input === "object" && input !== null && "question" in input) {
|
|
327
|
+
const question = (input as { readonly question?: unknown }).question;
|
|
328
|
+
return typeof question === "string" ? question : "";
|
|
329
|
+
}
|
|
330
|
+
return "";
|
|
331
|
+
}
|
|
332
|
+
|
|
333
|
+
function normalizeCodexUsage(usage: CodexLine["usage"]): Usage {
|
|
334
|
+
return {
|
|
335
|
+
input: usage?.input_tokens ?? 0,
|
|
336
|
+
output: usage?.output_tokens ?? 0,
|
|
337
|
+
...(usage?.reasoning_output_tokens ? { reasoning: usage.reasoning_output_tokens } : {})
|
|
338
|
+
};
|
|
339
|
+
}
|
|
@@ -0,0 +1,177 @@
|
|
|
1
|
+
import { mkdtemp, rm, writeFile } from "node:fs/promises";
|
|
2
|
+
import { tmpdir } from "node:os";
|
|
3
|
+
import { join } from "node:path";
|
|
4
|
+
import type { z } from "zod";
|
|
5
|
+
import {
|
|
6
|
+
codexExecJsonlArgs,
|
|
7
|
+
createCodexJsonlConsumer,
|
|
8
|
+
type CodexExecArgs
|
|
9
|
+
} from "./codex-jsonl.ts";
|
|
10
|
+
import { composeBackendPrompt } from "./conversation-config.ts";
|
|
11
|
+
import {
|
|
12
|
+
errorMessage,
|
|
13
|
+
runSubprocessConversation,
|
|
14
|
+
type SubprocessProcess,
|
|
15
|
+
type SubprocessSpawner
|
|
16
|
+
} from "./subprocess-run.ts";
|
|
17
|
+
import type { AutonomousRequest } from "./types.ts";
|
|
18
|
+
import {
|
|
19
|
+
createAskUserMcpServer,
|
|
20
|
+
type AskUserMcpServer,
|
|
21
|
+
type AskUserMcpServerOptions,
|
|
22
|
+
type AskUserResponder,
|
|
23
|
+
type StreamConversation
|
|
24
|
+
} from "../conversation/index.ts";
|
|
25
|
+
import {
|
|
26
|
+
backendFailed,
|
|
27
|
+
jsonSchemaFromZod,
|
|
28
|
+
type BackendApprovalPolicy,
|
|
29
|
+
type BackendConfig,
|
|
30
|
+
type BackendSandboxMode
|
|
31
|
+
} from "../model/index.ts";
|
|
32
|
+
|
|
33
|
+
export interface CodexBackendOptions
|
|
34
|
+
extends Omit<CodexExecArgs, "prompt" | "outputSchemaPath" | "resumeSessionId" | "mcpServerUrl"> {
|
|
35
|
+
readonly command?: string;
|
|
36
|
+
readonly cwd?: string;
|
|
37
|
+
readonly env?: NodeJS.ProcessEnv;
|
|
38
|
+
readonly capacity?: number;
|
|
39
|
+
readonly spawnProcess?: CodexProcessSpawner;
|
|
40
|
+
readonly config?: BackendConfig<"codex">;
|
|
41
|
+
readonly askUser?: AskUserResponder;
|
|
42
|
+
readonly createAskUserServer?: (options: AskUserMcpServerOptions) => AskUserMcpServer;
|
|
43
|
+
readonly inactivityTimeoutMs?: number;
|
|
44
|
+
readonly wallClockTimeoutMs?: number;
|
|
45
|
+
}
|
|
46
|
+
|
|
47
|
+
/** Codex's subprocess handle is the shared {@link SubprocessProcess}; the alias
|
|
48
|
+
* is kept for the public API (tests and flow code import `CodexProcess`). */
|
|
49
|
+
export type CodexProcess = SubprocessProcess;
|
|
50
|
+
|
|
51
|
+
export type CodexProcessSpawner = SubprocessSpawner;
|
|
52
|
+
|
|
53
|
+
export interface ResolvedCodexConfig<Output> {
|
|
54
|
+
model?: string;
|
|
55
|
+
systemPrompt?: string;
|
|
56
|
+
approvalPolicy?: BackendApprovalPolicy;
|
|
57
|
+
sandbox?: BackendSandboxMode;
|
|
58
|
+
readOnly?: boolean;
|
|
59
|
+
ignoreUserConfig?: boolean;
|
|
60
|
+
selfManagedGit?: boolean;
|
|
61
|
+
retryAttempts?: number;
|
|
62
|
+
schema?: z.ZodType<Output>;
|
|
63
|
+
outputSchemaPath?: string;
|
|
64
|
+
resumeSessionId?: string;
|
|
65
|
+
interactive?: boolean;
|
|
66
|
+
mcpServerUrl?: string;
|
|
67
|
+
}
|
|
68
|
+
|
|
69
|
+
export async function runCodexConversation<Output>(
|
|
70
|
+
request: AutonomousRequest<Output, "codex">,
|
|
71
|
+
options: CodexBackendOptions,
|
|
72
|
+
conversation: StreamConversation<"codex">,
|
|
73
|
+
setProcess: (process: CodexProcess) => void
|
|
74
|
+
): Promise<void> {
|
|
75
|
+
const command = options.command ?? "codex";
|
|
76
|
+
const config = resolveCodexConfig(request, options);
|
|
77
|
+
let schemaDir: string | undefined;
|
|
78
|
+
let askUserServer: AskUserMcpServer | undefined;
|
|
79
|
+
|
|
80
|
+
try {
|
|
81
|
+
if (config.interactive) {
|
|
82
|
+
askUserServer = (options.createAskUserServer ?? createAskUserMcpServer)({
|
|
83
|
+
responder:
|
|
84
|
+
options.askUser ??
|
|
85
|
+
(() => {
|
|
86
|
+
throw new Error("interactive ask_user responder is not configured");
|
|
87
|
+
})
|
|
88
|
+
});
|
|
89
|
+
config.mcpServerUrl = askUserServer.url;
|
|
90
|
+
}
|
|
91
|
+
|
|
92
|
+
if (config.schema) {
|
|
93
|
+
schemaDir = await mkdtemp(join(tmpdir(), "orca-codex-schema-"));
|
|
94
|
+
config.outputSchemaPath = join(schemaDir, "output-schema.json");
|
|
95
|
+
await writeFile(
|
|
96
|
+
config.outputSchemaPath,
|
|
97
|
+
`${JSON.stringify(jsonSchemaFromZod(config.schema), null, 2)}\n`
|
|
98
|
+
);
|
|
99
|
+
}
|
|
100
|
+
|
|
101
|
+
const args = codexExecJsonlArgs({
|
|
102
|
+
prompt: composeBackendPrompt(request.prompt, config),
|
|
103
|
+
...(config.model === undefined ? {} : { model: config.model }),
|
|
104
|
+
...(config.approvalPolicy === undefined ? {} : { approvalPolicy: config.approvalPolicy }),
|
|
105
|
+
...(config.sandbox === undefined ? {} : { sandbox: config.sandbox }),
|
|
106
|
+
...(config.readOnly === undefined ? {} : { readOnly: config.readOnly }),
|
|
107
|
+
...(config.ignoreUserConfig === undefined ? {} : { ignoreUserConfig: config.ignoreUserConfig }),
|
|
108
|
+
...(config.outputSchemaPath === undefined ? {} : { outputSchemaPath: config.outputSchemaPath }),
|
|
109
|
+
...(config.resumeSessionId === undefined ? {} : { resumeSessionId: config.resumeSessionId }),
|
|
110
|
+
...(config.mcpServerUrl === undefined ? {} : { mcpServerUrl: config.mcpServerUrl })
|
|
111
|
+
});
|
|
112
|
+
|
|
113
|
+
await runSubprocessConversation({
|
|
114
|
+
backend: "codex",
|
|
115
|
+
command,
|
|
116
|
+
args,
|
|
117
|
+
conversation,
|
|
118
|
+
setProcess,
|
|
119
|
+
...(options.cwd === undefined ? {} : { cwd: options.cwd }),
|
|
120
|
+
...(options.env === undefined ? {} : { env: options.env }),
|
|
121
|
+
...(options.spawnProcess === undefined ? {} : { spawnProcess: options.spawnProcess }),
|
|
122
|
+
...(options.inactivityTimeoutMs === undefined ? {} : { inactivityTimeoutMs: options.inactivityTimeoutMs }),
|
|
123
|
+
...(options.wallClockTimeoutMs === undefined ? {} : { wallClockTimeoutMs: options.wallClockTimeoutMs }),
|
|
124
|
+
createConsumer: () =>
|
|
125
|
+
createCodexJsonlConsumer(conversation, {
|
|
126
|
+
...(config.schema === undefined ? {} : { schema: config.schema }),
|
|
127
|
+
// The answer is routed by the MCP HTTP bridge (askUserServer), not the
|
|
128
|
+
// consumer; the consumer only surfaces the question for the renderer.
|
|
129
|
+
...(config.interactive ? { interactive: true } : {})
|
|
130
|
+
})
|
|
131
|
+
});
|
|
132
|
+
} catch (error) {
|
|
133
|
+
if (!conversation.signal.aborted) {
|
|
134
|
+
conversation.fail(backendFailed("codex", errorMessage(error)));
|
|
135
|
+
}
|
|
136
|
+
} finally {
|
|
137
|
+
await askUserServer?.close();
|
|
138
|
+
if (schemaDir) {
|
|
139
|
+
await rm(schemaDir, { recursive: true, force: true });
|
|
140
|
+
}
|
|
141
|
+
}
|
|
142
|
+
}
|
|
143
|
+
|
|
144
|
+
export function resolveCodexConfig<Output>(
|
|
145
|
+
request: AutonomousRequest<Output, "codex">,
|
|
146
|
+
options: CodexBackendOptions
|
|
147
|
+
): ResolvedCodexConfig<Output> {
|
|
148
|
+
const optionConfig = options.config;
|
|
149
|
+
const requestConfig = request.config;
|
|
150
|
+
const config: ResolvedCodexConfig<Output> = {};
|
|
151
|
+
const model = requestConfig?.model ?? optionConfig?.model ?? options.model;
|
|
152
|
+
if (model !== undefined) config.model = model;
|
|
153
|
+
const systemPrompt = requestConfig?.systemPrompt ?? optionConfig?.systemPrompt;
|
|
154
|
+
if (systemPrompt !== undefined) config.systemPrompt = systemPrompt;
|
|
155
|
+
const approvalPolicy =
|
|
156
|
+
requestConfig?.approvalPolicy ?? optionConfig?.approvalPolicy ?? options.approvalPolicy;
|
|
157
|
+
if (approvalPolicy !== undefined) config.approvalPolicy = approvalPolicy;
|
|
158
|
+
const sandbox = requestConfig?.sandbox ?? optionConfig?.sandbox ?? options.sandbox;
|
|
159
|
+
if (sandbox !== undefined) config.sandbox = sandbox;
|
|
160
|
+
const readOnly = requestConfig?.readOnly ?? optionConfig?.readOnly ?? options.readOnly;
|
|
161
|
+
if (readOnly !== undefined) config.readOnly = readOnly;
|
|
162
|
+
if (options.ignoreUserConfig !== undefined) config.ignoreUserConfig = options.ignoreUserConfig;
|
|
163
|
+
const selfManagedGit = requestConfig?.selfManagedGit ?? optionConfig?.selfManagedGit;
|
|
164
|
+
if (selfManagedGit !== undefined) config.selfManagedGit = selfManagedGit;
|
|
165
|
+
const retryAttempts = requestConfig?.retry?.attempts ?? optionConfig?.retry?.attempts;
|
|
166
|
+
if (retryAttempts !== undefined) config.retryAttempts = retryAttempts;
|
|
167
|
+
const schema =
|
|
168
|
+
requestConfig?.structuredOutput?.schema ??
|
|
169
|
+
request.schema ??
|
|
170
|
+
(optionConfig?.structuredOutput?.schema as z.ZodType<Output> | undefined);
|
|
171
|
+
if (schema !== undefined) config.schema = schema;
|
|
172
|
+
if (requestConfig?.resumeSessionId !== undefined)
|
|
173
|
+
config.resumeSessionId = String(requestConfig.resumeSessionId);
|
|
174
|
+
const interactive = requestConfig?.interactive ?? optionConfig?.interactive;
|
|
175
|
+
if (interactive !== undefined) config.interactive = interactive;
|
|
176
|
+
return config;
|
|
177
|
+
}
|
|
@@ -0,0 +1,84 @@
|
|
|
1
|
+
import type { AutonomousRequest, LlmBackend } from "./types.ts";
|
|
2
|
+
import {
|
|
3
|
+
codexAcpCommand,
|
|
4
|
+
experimentalAcpBackendEnabled,
|
|
5
|
+
runAcpConversation
|
|
6
|
+
} from "./acp-run.ts";
|
|
7
|
+
import {
|
|
8
|
+
resolveCodexConfig,
|
|
9
|
+
runCodexConversation,
|
|
10
|
+
type CodexBackendOptions,
|
|
11
|
+
type CodexProcess
|
|
12
|
+
} from "./codex-run.ts";
|
|
13
|
+
import { StreamConversation } from "../conversation/index.ts";
|
|
14
|
+
|
|
15
|
+
export type {
|
|
16
|
+
CodexBackendOptions,
|
|
17
|
+
CodexProcess,
|
|
18
|
+
CodexProcessSpawner
|
|
19
|
+
} from "./codex-run.ts";
|
|
20
|
+
|
|
21
|
+
export function codex(options: CodexBackendOptions = {}): LlmBackend<"codex"> {
|
|
22
|
+
return {
|
|
23
|
+
tag: "codex",
|
|
24
|
+
autonomous<Output = unknown>(request: AutonomousRequest<Output, "codex">) {
|
|
25
|
+
let child: CodexProcess | undefined;
|
|
26
|
+
let cancelAcp: (() => Promise<void>) | undefined;
|
|
27
|
+
const useAcp = experimentalAcpBackendEnabled("codex");
|
|
28
|
+
const conversation = new StreamConversation({
|
|
29
|
+
backend: "codex",
|
|
30
|
+
capacity: options.capacity ?? 256,
|
|
31
|
+
canAskUser: request.config?.interactive ?? options.config?.interactive ?? false,
|
|
32
|
+
onCancel: async () => {
|
|
33
|
+
if (useAcp && cancelAcp) {
|
|
34
|
+
await cancelAcp();
|
|
35
|
+
return;
|
|
36
|
+
}
|
|
37
|
+
child?.kill("SIGTERM");
|
|
38
|
+
}
|
|
39
|
+
});
|
|
40
|
+
|
|
41
|
+
queueMicrotask(() => {
|
|
42
|
+
if (conversation.signal.aborted) {
|
|
43
|
+
return;
|
|
44
|
+
}
|
|
45
|
+
if (useAcp) {
|
|
46
|
+
const acp = codexAcpCommand();
|
|
47
|
+
const config = resolveCodexConfig(request, options);
|
|
48
|
+
void runAcpConversation(
|
|
49
|
+
request,
|
|
50
|
+
{
|
|
51
|
+
backend: "codex",
|
|
52
|
+
command: acp.command,
|
|
53
|
+
args: acp.args,
|
|
54
|
+
config,
|
|
55
|
+
...(options.cwd === undefined ? {} : { cwd: options.cwd }),
|
|
56
|
+
...(options.env === undefined ? {} : { env: options.env }),
|
|
57
|
+
...(options.wallClockTimeoutMs === undefined ? {} : { requestTimeoutMs: options.wallClockTimeoutMs }),
|
|
58
|
+
...(options.inactivityTimeoutMs === undefined ? {} : { inactivityTimeoutMs: options.inactivityTimeoutMs }),
|
|
59
|
+
setProcess: (process) => {
|
|
60
|
+
child = process;
|
|
61
|
+
if (conversation.signal.aborted) {
|
|
62
|
+
process.kill("SIGTERM");
|
|
63
|
+
}
|
|
64
|
+
},
|
|
65
|
+
setCancel: (cancel) => {
|
|
66
|
+
cancelAcp = cancel;
|
|
67
|
+
}
|
|
68
|
+
},
|
|
69
|
+
conversation
|
|
70
|
+
);
|
|
71
|
+
return;
|
|
72
|
+
}
|
|
73
|
+
void runCodexConversation(request, options, conversation, (process) => {
|
|
74
|
+
child = process;
|
|
75
|
+
if (conversation.signal.aborted) {
|
|
76
|
+
process.kill("SIGTERM");
|
|
77
|
+
}
|
|
78
|
+
});
|
|
79
|
+
});
|
|
80
|
+
|
|
81
|
+
return conversation;
|
|
82
|
+
}
|
|
83
|
+
};
|
|
84
|
+
}
|
|
@@ -0,0 +1,26 @@
|
|
|
1
|
+
import type { z } from "zod";
|
|
2
|
+
|
|
3
|
+
export interface SharedBackendConfig<Output> {
|
|
4
|
+
model?: string;
|
|
5
|
+
systemPrompt?: string;
|
|
6
|
+
readOnly?: boolean;
|
|
7
|
+
selfManagedGit?: boolean;
|
|
8
|
+
retryAttempts?: number;
|
|
9
|
+
schema?: z.ZodType<Output>;
|
|
10
|
+
resumeSessionId?: string;
|
|
11
|
+
}
|
|
12
|
+
|
|
13
|
+
export function composeBackendPrompt(prompt: string, config: SharedBackendConfig<unknown>): string {
|
|
14
|
+
return [
|
|
15
|
+
config.systemPrompt ? `System instructions:\n${config.systemPrompt}` : "",
|
|
16
|
+
config.selfManagedGit === false
|
|
17
|
+
? "Git policy: Orca is the parent runtime. Do not create commits, branches, pushes, or pull requests; leave repository mutation to the parent workflow."
|
|
18
|
+
: "",
|
|
19
|
+
config.retryAttempts === undefined
|
|
20
|
+
? ""
|
|
21
|
+
: `Retry policy: maximum attempts ${String(config.retryAttempts)}.`,
|
|
22
|
+
prompt
|
|
23
|
+
]
|
|
24
|
+
.filter((part) => part.length > 0)
|
|
25
|
+
.join("\n\n");
|
|
26
|
+
}
|
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
export * from "./claude-run.ts";
|
|
2
|
+
export * from "./conversation-config.ts";
|
|
3
|
+
export * from "./claude-stream-json.ts";
|
|
4
|
+
export * from "./codex.ts";
|
|
5
|
+
export * from "./codex-jsonl.ts";
|
|
6
|
+
export * from "./opencode-run.ts";
|
|
7
|
+
export * from "./opencode-sse.ts";
|
|
8
|
+
export * from "./pi-rpc.ts";
|
|
9
|
+
export * from "./pi-run.ts";
|
|
10
|
+
export * from "./select.ts";
|
|
11
|
+
export * from "./subprocess-run.ts";
|
|
12
|
+
export * from "./types.ts";
|
|
13
|
+
export * from "./unsupported.ts";
|