parallel-codex-tui 0.1.3 → 0.1.5
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/.parallel-codex/config.example.toml +136 -3
- package/README.md +299 -21
- package/dist/bootstrap.js +37 -17
- package/dist/cli-args.js +34 -3
- package/dist/cli-help.js +20 -0
- package/dist/cli-startup-preflight.js +18 -0
- package/dist/cli-startup-recovery.js +82 -0
- package/dist/cli-workspace-picker.js +330 -0
- package/dist/cli-workspace-transition.js +33 -0
- package/dist/cli-workspace.js +7 -71
- package/dist/cli.js +234 -24
- package/dist/core/collaboration-timeline.js +268 -0
- package/dist/core/config-errors.js +14 -0
- package/dist/core/config.js +297 -109
- package/dist/core/file-store.js +119 -6
- package/dist/core/lease-finalization.js +22 -0
- package/dist/core/paths.js +7 -0
- package/dist/core/process-identity.js +48 -0
- package/dist/core/process-mutation-turn.js +128 -0
- package/dist/core/process-ownership.js +276 -0
- package/dist/core/process-tree.js +90 -0
- package/dist/core/router-audit.js +155 -0
- package/dist/core/router-redaction.js +31 -0
- package/dist/core/router.js +462 -35
- package/dist/core/session-index.js +412 -88
- package/dist/core/session-manager.js +1110 -40
- package/dist/core/task-session-details.js +175 -0
- package/dist/core/task-state-machine.js +18 -0
- package/dist/core/workspace-commit-recovery.js +118 -0
- package/dist/core/workspace.js +19 -11
- package/dist/doctor.js +373 -34
- package/dist/domain/schemas.js +142 -7
- package/dist/orchestrator/collaboration-channel.js +289 -6
- package/dist/orchestrator/feature-plan.js +70 -0
- package/dist/orchestrator/final-acceptance.js +86 -0
- package/dist/orchestrator/judge-artifacts.js +236 -0
- package/dist/orchestrator/orchestrator.js +2086 -203
- package/dist/orchestrator/prompts.js +168 -5
- package/dist/orchestrator/supervisor-summary.js +56 -2
- package/dist/orchestrator/workspace-sandbox.js +927 -0
- package/dist/tui/App.js +3187 -161
- package/dist/tui/AppShell.js +196 -25
- package/dist/tui/CollaborationTimelineView.js +327 -0
- package/dist/tui/FeatureBoardView.js +232 -0
- package/dist/tui/InputBar.js +581 -57
- package/dist/tui/RouterDiagnosticsView.js +469 -0
- package/dist/tui/StatusBar.js +610 -57
- package/dist/tui/StatusDetailView.js +164 -0
- package/dist/tui/TaskSessionDetailView.js +222 -0
- package/dist/tui/TaskSessionsView.js +210 -0
- package/dist/tui/TerminalOutput.js +53 -9
- package/dist/tui/WorkerOutputView.js +1404 -161
- package/dist/tui/WorkerOverviewView.js +269 -0
- package/dist/tui/chat-history.js +25 -0
- package/dist/tui/chat-input.js +67 -19
- package/dist/tui/chat-paste.js +76 -0
- package/dist/tui/display-width.js +41 -3
- package/dist/tui/incremental-text-file.js +101 -0
- package/dist/tui/keyboard.js +49 -0
- package/dist/tui/markdown-text.js +14 -0
- package/dist/tui/raw-input-decoder.js +3 -0
- package/dist/tui/scrolling.js +2 -1
- package/dist/tui/status-line.js +360 -11
- package/dist/tui/task-memory.js +13 -1
- package/dist/tui/task-result.js +105 -0
- package/dist/tui/terminal-screen.js +13 -1
- package/dist/tui/theme-contrast.js +144 -0
- package/dist/tui/theme-preview.js +109 -0
- package/dist/tui/theme.js +158 -0
- package/dist/version.js +1 -1
- package/dist/workers/capabilities.js +213 -0
- package/dist/workers/live-probe.js +177 -0
- package/dist/workers/mock-adapter.js +73 -8
- package/dist/workers/native-attach.js +106 -16
- package/dist/workers/process-adapter.js +572 -77
- package/dist/workers/provider.js +26 -0
- package/dist/workers/registry.js +12 -20
- package/package.json +11 -2
package/dist/core/config.js
CHANGED
|
@@ -1,7 +1,10 @@
|
|
|
1
|
-
import { parse
|
|
1
|
+
import { parse } from "@iarna/toml";
|
|
2
|
+
import { readFile } from "node:fs/promises";
|
|
2
3
|
import { join } from "node:path";
|
|
3
4
|
import { z } from "zod";
|
|
5
|
+
import { EngineNameSchema } from "../domain/schemas.js";
|
|
4
6
|
import { pathExists, readTextIfExists, writeText } from "./file-store.js";
|
|
7
|
+
import { normalizeTuiThemeColorValue, normalizeTuiThemeName, TUI_THEME_FIELDS, TUI_THEME_NAMES } from "../tui/theme.js";
|
|
5
8
|
const NativeSessionConfigSchema = z.object({
|
|
6
9
|
enabled: z.boolean().default(true),
|
|
7
10
|
resumeArgs: z.array(z.string()).default([]),
|
|
@@ -14,27 +17,92 @@ const WorkerModelConfigSchema = z.object({
|
|
|
14
17
|
args: z.array(z.string()).default([]),
|
|
15
18
|
env: z.record(z.string()).default({})
|
|
16
19
|
});
|
|
20
|
+
const WorkerCapabilitiesConfigSchema = z.object({
|
|
21
|
+
profile: z.enum(["codex", "claude", "generic"]),
|
|
22
|
+
writableDirArgs: z.array(z.string()).default([]),
|
|
23
|
+
freshSessionArgs: z.array(z.string()).default([])
|
|
24
|
+
}).strict().superRefine((value, context) => {
|
|
25
|
+
if (value.writableDirArgs.length > 0 && !value.writableDirArgs.some((arg) => arg.includes("{dir}"))) {
|
|
26
|
+
context.addIssue({
|
|
27
|
+
code: z.ZodIssueCode.custom,
|
|
28
|
+
path: ["writableDirArgs"],
|
|
29
|
+
message: "writableDirArgs must include a {dir} template"
|
|
30
|
+
});
|
|
31
|
+
}
|
|
32
|
+
if (value.freshSessionArgs.length > 0 && !value.freshSessionArgs.some((arg) => arg.includes("{sessionId}"))) {
|
|
33
|
+
context.addIssue({
|
|
34
|
+
code: z.ZodIssueCode.custom,
|
|
35
|
+
path: ["freshSessionArgs"],
|
|
36
|
+
message: "freshSessionArgs must include a {sessionId} template"
|
|
37
|
+
});
|
|
38
|
+
}
|
|
39
|
+
});
|
|
17
40
|
const InteractiveCommandSchema = z.object({
|
|
18
41
|
command: z.string().min(1),
|
|
19
|
-
args: z.array(z.string()).default([])
|
|
42
|
+
args: z.array(z.string()).default([]),
|
|
43
|
+
forkArgs: z.array(z.string()).default([])
|
|
44
|
+
}).superRefine((value, context) => {
|
|
45
|
+
if (value.forkArgs.length > 0 && !value.forkArgs.some((arg) => arg.includes("{sessionId}"))) {
|
|
46
|
+
context.addIssue({
|
|
47
|
+
code: z.ZodIssueCode.custom,
|
|
48
|
+
path: ["forkArgs"],
|
|
49
|
+
message: "forkArgs must include a {sessionId} template"
|
|
50
|
+
});
|
|
51
|
+
}
|
|
20
52
|
});
|
|
21
53
|
const RolePromptConfigSchema = z.object({
|
|
22
54
|
title: z.string().min(1),
|
|
23
55
|
instructions: z.array(z.string()).default([])
|
|
24
56
|
});
|
|
57
|
+
const TuiColorValueSchema = z.string().min(1).refine((value) => normalizeTuiThemeColorValue(value) !== null, {
|
|
58
|
+
message: "Invalid TUI color value. Use a Chalk color name, #rgb/#rrggbb, rgb(r,g,b), or ansi256(0..255)."
|
|
59
|
+
}).transform((value) => normalizeTuiThemeColorValue(value) ?? value.trim());
|
|
60
|
+
const TuiThemeNameSchema = z.preprocess((value) => typeof value === "string" ? normalizeTuiThemeName(value) ?? value.trim() : value, z.enum(TUI_THEME_NAMES)).default("codex");
|
|
61
|
+
const UiColorOverridesSchema = z.object(Object.fromEntries(TUI_THEME_FIELDS.map((field) => [field, TuiColorValueSchema.optional()]))).strict().default({});
|
|
25
62
|
const CodexRouterConfigSchema = z.object({
|
|
26
63
|
command: z.string().min(1).default("codex"),
|
|
27
|
-
args: z.array(z.string()).default([
|
|
28
|
-
|
|
29
|
-
|
|
64
|
+
args: z.array(z.string()).default([
|
|
65
|
+
"exec",
|
|
66
|
+
"--ephemeral",
|
|
67
|
+
"--ignore-rules",
|
|
68
|
+
"-c",
|
|
69
|
+
"model_reasoning_effort=low",
|
|
70
|
+
"--skip-git-repo-check",
|
|
71
|
+
"--sandbox",
|
|
72
|
+
"read-only",
|
|
73
|
+
"--color",
|
|
74
|
+
"never",
|
|
75
|
+
"-"
|
|
76
|
+
]),
|
|
77
|
+
timeoutMs: z.number().int().positive().default(30000),
|
|
78
|
+
firstOutputTimeoutMs: z.number().int().positive().default(15000),
|
|
79
|
+
idleTimeoutMs: z.number().int().positive().default(15000),
|
|
80
|
+
maxOutputBytes: z.number().int().min(1024).max(16 * 1024 * 1024).default(1024 * 1024),
|
|
81
|
+
maxAttempts: z.number().int().min(1).max(3).default(2),
|
|
82
|
+
retryDelayMs: z.number().int().min(0).max(10000).default(500),
|
|
83
|
+
followUpTimeoutMs: z.number().int().positive().max(120000).default(20000),
|
|
84
|
+
fallback: z.enum(["simple", "complex"]).default("simple"),
|
|
85
|
+
env: z.record(z.string()).default({})
|
|
30
86
|
});
|
|
87
|
+
const OrchestrationConfigSchema = z.object({
|
|
88
|
+
maxParallelFeatures: z.number().int().min(1).max(8),
|
|
89
|
+
maxRevisionRounds: z.number().int().min(1).max(10)
|
|
90
|
+
}).strict();
|
|
91
|
+
const UiConfigSchema = z.object({
|
|
92
|
+
showStatusBar: z.boolean(),
|
|
93
|
+
autoOpenFailedWorker: z.boolean(),
|
|
94
|
+
theme: TuiThemeNameSchema,
|
|
95
|
+
colors: UiColorOverridesSchema
|
|
96
|
+
}).strict();
|
|
31
97
|
const WorkerCommandSchema = z.object({
|
|
32
98
|
command: z.string().min(1),
|
|
33
99
|
args: z.array(z.string()).default([]),
|
|
100
|
+
assignable: z.boolean().default(true),
|
|
34
101
|
timeoutMs: z.number().int().positive().optional(),
|
|
35
102
|
idleTimeoutMs: z.number().int().positive().optional(),
|
|
36
103
|
firstOutputTimeoutMs: z.number().int().positive().optional(),
|
|
37
104
|
model: WorkerModelConfigSchema.default({}),
|
|
105
|
+
capabilities: WorkerCapabilitiesConfigSchema,
|
|
38
106
|
nativeSession: NativeSessionConfigSchema,
|
|
39
107
|
interactive: InteractiveCommandSchema
|
|
40
108
|
});
|
|
@@ -45,16 +113,17 @@ const AppConfigSchema = z.object({
|
|
|
45
113
|
defaultMode: z.enum(["auto", "simple", "complex"]),
|
|
46
114
|
codex: CodexRouterConfigSchema.default({})
|
|
47
115
|
}),
|
|
116
|
+
orchestration: OrchestrationConfigSchema,
|
|
48
117
|
workers: z.object({
|
|
49
118
|
codex: WorkerCommandSchema,
|
|
50
119
|
claude: WorkerCommandSchema,
|
|
51
120
|
mock: WorkerCommandSchema
|
|
52
|
-
}),
|
|
121
|
+
}).catchall(WorkerCommandSchema),
|
|
53
122
|
pairing: z.object({
|
|
54
|
-
main:
|
|
55
|
-
judge:
|
|
56
|
-
actor:
|
|
57
|
-
critic:
|
|
123
|
+
main: EngineNameSchema,
|
|
124
|
+
judge: EngineNameSchema,
|
|
125
|
+
actor: EngineNameSchema,
|
|
126
|
+
critic: EngineNameSchema
|
|
58
127
|
}),
|
|
59
128
|
roles: z.object({
|
|
60
129
|
main: RolePromptConfigSchema,
|
|
@@ -62,10 +131,17 @@ const AppConfigSchema = z.object({
|
|
|
62
131
|
actor: RolePromptConfigSchema,
|
|
63
132
|
critic: RolePromptConfigSchema
|
|
64
133
|
}),
|
|
65
|
-
ui:
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
134
|
+
ui: UiConfigSchema
|
|
135
|
+
}).superRefine((config, context) => {
|
|
136
|
+
for (const [role, workerId] of Object.entries(config.pairing)) {
|
|
137
|
+
if (!config.workers[workerId]) {
|
|
138
|
+
context.addIssue({
|
|
139
|
+
code: z.ZodIssueCode.custom,
|
|
140
|
+
path: ["pairing", role],
|
|
141
|
+
message: `Unknown Worker profile: ${workerId}`
|
|
142
|
+
});
|
|
143
|
+
}
|
|
144
|
+
}
|
|
69
145
|
});
|
|
70
146
|
export function defaultConfig(projectRoot) {
|
|
71
147
|
return {
|
|
@@ -75,15 +151,39 @@ export function defaultConfig(projectRoot) {
|
|
|
75
151
|
defaultMode: "auto",
|
|
76
152
|
codex: {
|
|
77
153
|
command: "codex",
|
|
78
|
-
args: [
|
|
79
|
-
|
|
80
|
-
|
|
154
|
+
args: [
|
|
155
|
+
"exec",
|
|
156
|
+
"--ephemeral",
|
|
157
|
+
"--ignore-rules",
|
|
158
|
+
"-c",
|
|
159
|
+
"model_reasoning_effort=low",
|
|
160
|
+
"--skip-git-repo-check",
|
|
161
|
+
"--sandbox",
|
|
162
|
+
"read-only",
|
|
163
|
+
"--color",
|
|
164
|
+
"never",
|
|
165
|
+
"-"
|
|
166
|
+
],
|
|
167
|
+
timeoutMs: 30000,
|
|
168
|
+
firstOutputTimeoutMs: 15000,
|
|
169
|
+
idleTimeoutMs: 15000,
|
|
170
|
+
maxOutputBytes: 1024 * 1024,
|
|
171
|
+
maxAttempts: 2,
|
|
172
|
+
retryDelayMs: 500,
|
|
173
|
+
followUpTimeoutMs: 20000,
|
|
174
|
+
fallback: "simple",
|
|
175
|
+
env: {}
|
|
81
176
|
}
|
|
82
177
|
},
|
|
178
|
+
orchestration: {
|
|
179
|
+
maxParallelFeatures: 3,
|
|
180
|
+
maxRevisionRounds: 3
|
|
181
|
+
},
|
|
83
182
|
workers: {
|
|
84
183
|
codex: {
|
|
85
184
|
command: "codex",
|
|
86
185
|
args: ["exec", "--skip-git-repo-check", "--sandbox", "workspace-write", "--color", "never", "-"],
|
|
186
|
+
assignable: true,
|
|
87
187
|
timeoutMs: 45 * 60 * 1000,
|
|
88
188
|
idleTimeoutMs: 5 * 60 * 1000,
|
|
89
189
|
firstOutputTimeoutMs: 2 * 60 * 1000,
|
|
@@ -93,6 +193,11 @@ export function defaultConfig(projectRoot) {
|
|
|
93
193
|
args: [],
|
|
94
194
|
env: {}
|
|
95
195
|
},
|
|
196
|
+
capabilities: {
|
|
197
|
+
profile: "codex",
|
|
198
|
+
writableDirArgs: ["--add-dir", "{dir}"],
|
|
199
|
+
freshSessionArgs: []
|
|
200
|
+
},
|
|
96
201
|
nativeSession: {
|
|
97
202
|
enabled: true,
|
|
98
203
|
resumeArgs: ["exec", "resume", "{sessionId}", "--skip-git-repo-check", "-"],
|
|
@@ -101,12 +206,14 @@ export function defaultConfig(projectRoot) {
|
|
|
101
206
|
},
|
|
102
207
|
interactive: {
|
|
103
208
|
command: "codex",
|
|
104
|
-
args: ["resume", "{sessionId}"]
|
|
209
|
+
args: ["resume", "{sessionId}"],
|
|
210
|
+
forkArgs: ["fork", "{sessionId}"]
|
|
105
211
|
}
|
|
106
212
|
},
|
|
107
213
|
claude: {
|
|
108
214
|
command: "claude",
|
|
109
215
|
args: ["--print", "--permission-mode", "acceptEdits", "--output-format", "text"],
|
|
216
|
+
assignable: true,
|
|
110
217
|
timeoutMs: 45 * 60 * 1000,
|
|
111
218
|
idleTimeoutMs: 5 * 60 * 1000,
|
|
112
219
|
firstOutputTimeoutMs: 2 * 60 * 1000,
|
|
@@ -116,6 +223,11 @@ export function defaultConfig(projectRoot) {
|
|
|
116
223
|
args: [],
|
|
117
224
|
env: {}
|
|
118
225
|
},
|
|
226
|
+
capabilities: {
|
|
227
|
+
profile: "claude",
|
|
228
|
+
writableDirArgs: ["--add-dir", "{dir}"],
|
|
229
|
+
freshSessionArgs: ["--session-id", "{sessionId}"]
|
|
230
|
+
},
|
|
119
231
|
nativeSession: {
|
|
120
232
|
enabled: true,
|
|
121
233
|
resumeArgs: ["--print", "--resume", "{sessionId}", "--permission-mode", "acceptEdits", "--output-format", "text"],
|
|
@@ -124,18 +236,25 @@ export function defaultConfig(projectRoot) {
|
|
|
124
236
|
},
|
|
125
237
|
interactive: {
|
|
126
238
|
command: "claude",
|
|
127
|
-
args: ["--resume", "{sessionId}"]
|
|
239
|
+
args: ["--resume", "{sessionId}"],
|
|
240
|
+
forkArgs: ["--resume", "{sessionId}", "--fork-session"]
|
|
128
241
|
}
|
|
129
242
|
},
|
|
130
243
|
mock: {
|
|
131
244
|
command: "mock",
|
|
132
245
|
args: [],
|
|
246
|
+
assignable: false,
|
|
133
247
|
model: {
|
|
134
248
|
name: "",
|
|
135
249
|
provider: "",
|
|
136
250
|
args: [],
|
|
137
251
|
env: {}
|
|
138
252
|
},
|
|
253
|
+
capabilities: {
|
|
254
|
+
profile: "generic",
|
|
255
|
+
writableDirArgs: [],
|
|
256
|
+
freshSessionArgs: []
|
|
257
|
+
},
|
|
139
258
|
nativeSession: {
|
|
140
259
|
enabled: true,
|
|
141
260
|
resumeArgs: ["resume", "{sessionId}", "-"],
|
|
@@ -144,7 +263,8 @@ export function defaultConfig(projectRoot) {
|
|
|
144
263
|
},
|
|
145
264
|
interactive: {
|
|
146
265
|
command: "mock",
|
|
147
|
-
args: ["resume", "{sessionId}"]
|
|
266
|
+
args: ["resume", "{sessionId}"],
|
|
267
|
+
forkArgs: []
|
|
148
268
|
}
|
|
149
269
|
}
|
|
150
270
|
},
|
|
@@ -174,7 +294,9 @@ export function defaultConfig(projectRoot) {
|
|
|
174
294
|
},
|
|
175
295
|
ui: {
|
|
176
296
|
showStatusBar: true,
|
|
177
|
-
autoOpenFailedWorker: true
|
|
297
|
+
autoOpenFailedWorker: true,
|
|
298
|
+
theme: "codex",
|
|
299
|
+
colors: {}
|
|
178
300
|
}
|
|
179
301
|
};
|
|
180
302
|
}
|
|
@@ -198,71 +320,18 @@ export async function loadConfig(projectRoot) {
|
|
|
198
320
|
...(parsed.router ?? {}),
|
|
199
321
|
codex: {
|
|
200
322
|
...base.router.codex,
|
|
201
|
-
...(parsed.router?.codex ?? {})
|
|
202
|
-
|
|
203
|
-
|
|
204
|
-
|
|
205
|
-
codex: {
|
|
206
|
-
...base.workers.codex,
|
|
207
|
-
...(parsed.workers?.codex ?? {}),
|
|
208
|
-
model: {
|
|
209
|
-
...base.workers.codex.model,
|
|
210
|
-
...(parsed.workers?.codex?.model ?? {}),
|
|
211
|
-
env: {
|
|
212
|
-
...base.workers.codex.model.env,
|
|
213
|
-
...(parsed.workers?.codex?.model?.env ?? {})
|
|
214
|
-
}
|
|
215
|
-
},
|
|
216
|
-
nativeSession: {
|
|
217
|
-
...base.workers.codex.nativeSession,
|
|
218
|
-
...(parsed.workers?.codex?.nativeSession ?? {})
|
|
219
|
-
},
|
|
220
|
-
interactive: {
|
|
221
|
-
...base.workers.codex.interactive,
|
|
222
|
-
...(parsed.workers?.codex?.interactive ?? {})
|
|
223
|
-
}
|
|
224
|
-
},
|
|
225
|
-
claude: {
|
|
226
|
-
...base.workers.claude,
|
|
227
|
-
...(parsed.workers?.claude ?? {}),
|
|
228
|
-
model: {
|
|
229
|
-
...base.workers.claude.model,
|
|
230
|
-
...(parsed.workers?.claude?.model ?? {}),
|
|
231
|
-
env: {
|
|
232
|
-
...base.workers.claude.model.env,
|
|
233
|
-
...(parsed.workers?.claude?.model?.env ?? {})
|
|
234
|
-
}
|
|
235
|
-
},
|
|
236
|
-
nativeSession: {
|
|
237
|
-
...base.workers.claude.nativeSession,
|
|
238
|
-
...(parsed.workers?.claude?.nativeSession ?? {})
|
|
239
|
-
},
|
|
240
|
-
interactive: {
|
|
241
|
-
...base.workers.claude.interactive,
|
|
242
|
-
...(parsed.workers?.claude?.interactive ?? {})
|
|
243
|
-
}
|
|
244
|
-
},
|
|
245
|
-
mock: {
|
|
246
|
-
...base.workers.mock,
|
|
247
|
-
...(parsed.workers?.mock ?? {}),
|
|
248
|
-
model: {
|
|
249
|
-
...base.workers.mock.model,
|
|
250
|
-
...(parsed.workers?.mock?.model ?? {}),
|
|
251
|
-
env: {
|
|
252
|
-
...base.workers.mock.model.env,
|
|
253
|
-
...(parsed.workers?.mock?.model?.env ?? {})
|
|
254
|
-
}
|
|
255
|
-
},
|
|
256
|
-
nativeSession: {
|
|
257
|
-
...base.workers.mock.nativeSession,
|
|
258
|
-
...(parsed.workers?.mock?.nativeSession ?? {})
|
|
259
|
-
},
|
|
260
|
-
interactive: {
|
|
261
|
-
...base.workers.mock.interactive,
|
|
262
|
-
...(parsed.workers?.mock?.interactive ?? {})
|
|
323
|
+
...(parsed.router?.codex ?? {}),
|
|
324
|
+
env: {
|
|
325
|
+
...base.router.codex.env,
|
|
326
|
+
...(parsed.router?.codex?.env ?? {})
|
|
263
327
|
}
|
|
264
328
|
}
|
|
265
329
|
},
|
|
330
|
+
orchestration: {
|
|
331
|
+
...base.orchestration,
|
|
332
|
+
...(parsed.orchestration ?? {})
|
|
333
|
+
},
|
|
334
|
+
workers: resolveWorkerConfigs(base.workers, parsed.workers ?? {}),
|
|
266
335
|
pairing: {
|
|
267
336
|
...base.pairing,
|
|
268
337
|
...(parsed.pairing ?? {})
|
|
@@ -287,39 +356,163 @@ export async function loadConfig(projectRoot) {
|
|
|
287
356
|
},
|
|
288
357
|
ui: {
|
|
289
358
|
...base.ui,
|
|
290
|
-
...(parsed.ui ?? {})
|
|
359
|
+
...(parsed.ui ?? {}),
|
|
360
|
+
colors: {
|
|
361
|
+
...base.ui.colors,
|
|
362
|
+
...(parsed.ui?.colors ?? {})
|
|
363
|
+
}
|
|
291
364
|
}
|
|
292
365
|
};
|
|
293
366
|
return AppConfigSchema.parse(merged);
|
|
294
367
|
}
|
|
368
|
+
function resolveWorkerConfigs(builtins, configured) {
|
|
369
|
+
const resolved = new Map();
|
|
370
|
+
const resolving = new Set();
|
|
371
|
+
const ids = [...new Set([...Object.keys(builtins), ...Object.keys(configured)])];
|
|
372
|
+
const resolve = (id) => {
|
|
373
|
+
const cached = resolved.get(id);
|
|
374
|
+
if (cached) {
|
|
375
|
+
return cached;
|
|
376
|
+
}
|
|
377
|
+
if (!EngineNameSchema.safeParse(id).success) {
|
|
378
|
+
throw new Error(`Invalid Worker profile id: ${id}`);
|
|
379
|
+
}
|
|
380
|
+
if (resolving.has(id)) {
|
|
381
|
+
throw new Error(`Circular Worker profile inheritance: ${[...resolving, id].join(" -> ")}`);
|
|
382
|
+
}
|
|
383
|
+
resolving.add(id);
|
|
384
|
+
try {
|
|
385
|
+
const override = configured[id] ?? {};
|
|
386
|
+
const builtin = builtins[id];
|
|
387
|
+
if (builtin && override.extends) {
|
|
388
|
+
throw new Error(`Built-in Worker profile ${id} cannot declare extends`);
|
|
389
|
+
}
|
|
390
|
+
let parent = builtin;
|
|
391
|
+
if (!parent) {
|
|
392
|
+
const parentId = override.extends?.trim();
|
|
393
|
+
if (!parentId || parentId === "generic") {
|
|
394
|
+
parent = genericWorkerConfig(id, override.command);
|
|
395
|
+
}
|
|
396
|
+
else {
|
|
397
|
+
if (!builtins[parentId] && !configured[parentId]) {
|
|
398
|
+
throw new Error(`Unknown Worker profile inherited by ${id}: ${parentId}`);
|
|
399
|
+
}
|
|
400
|
+
parent = resolve(parentId);
|
|
401
|
+
}
|
|
402
|
+
}
|
|
403
|
+
const worker = mergeWorkerConfig(parent, override);
|
|
404
|
+
resolved.set(id, worker);
|
|
405
|
+
return worker;
|
|
406
|
+
}
|
|
407
|
+
finally {
|
|
408
|
+
resolving.delete(id);
|
|
409
|
+
}
|
|
410
|
+
};
|
|
411
|
+
for (const id of ids) {
|
|
412
|
+
resolve(id);
|
|
413
|
+
}
|
|
414
|
+
return Object.fromEntries(resolved);
|
|
415
|
+
}
|
|
416
|
+
function genericWorkerConfig(id, command = id) {
|
|
417
|
+
return {
|
|
418
|
+
command,
|
|
419
|
+
args: [],
|
|
420
|
+
assignable: true,
|
|
421
|
+
timeoutMs: 45 * 60 * 1000,
|
|
422
|
+
idleTimeoutMs: 5 * 60 * 1000,
|
|
423
|
+
firstOutputTimeoutMs: 2 * 60 * 1000,
|
|
424
|
+
model: {
|
|
425
|
+
name: "",
|
|
426
|
+
provider: "",
|
|
427
|
+
args: [],
|
|
428
|
+
env: {}
|
|
429
|
+
},
|
|
430
|
+
capabilities: {
|
|
431
|
+
profile: "generic",
|
|
432
|
+
writableDirArgs: [],
|
|
433
|
+
freshSessionArgs: []
|
|
434
|
+
},
|
|
435
|
+
nativeSession: {
|
|
436
|
+
enabled: false,
|
|
437
|
+
resumeArgs: [],
|
|
438
|
+
detectSessionId: false,
|
|
439
|
+
fallback: "fail"
|
|
440
|
+
},
|
|
441
|
+
interactive: {
|
|
442
|
+
command,
|
|
443
|
+
args: [],
|
|
444
|
+
forkArgs: []
|
|
445
|
+
}
|
|
446
|
+
};
|
|
447
|
+
}
|
|
448
|
+
function mergeWorkerConfig(base, override) {
|
|
449
|
+
const { extends: _extends, ...values } = override;
|
|
450
|
+
return {
|
|
451
|
+
...base,
|
|
452
|
+
...values,
|
|
453
|
+
model: {
|
|
454
|
+
...base.model,
|
|
455
|
+
...(override.model ?? {}),
|
|
456
|
+
env: {
|
|
457
|
+
...base.model.env,
|
|
458
|
+
...(override.model?.env ?? {})
|
|
459
|
+
}
|
|
460
|
+
},
|
|
461
|
+
capabilities: {
|
|
462
|
+
...base.capabilities,
|
|
463
|
+
...(override.capabilities ?? {})
|
|
464
|
+
},
|
|
465
|
+
nativeSession: {
|
|
466
|
+
...base.nativeSession,
|
|
467
|
+
...(override.nativeSession ?? {})
|
|
468
|
+
},
|
|
469
|
+
interactive: {
|
|
470
|
+
...base.interactive,
|
|
471
|
+
...(override.interactive ?? {})
|
|
472
|
+
}
|
|
473
|
+
};
|
|
474
|
+
}
|
|
475
|
+
export function withUiThemeOverride(config, theme) {
|
|
476
|
+
if (!theme) {
|
|
477
|
+
return config;
|
|
478
|
+
}
|
|
479
|
+
return {
|
|
480
|
+
...config,
|
|
481
|
+
ui: {
|
|
482
|
+
...config.ui,
|
|
483
|
+
theme
|
|
484
|
+
}
|
|
485
|
+
};
|
|
486
|
+
}
|
|
295
487
|
function assertObjectSections(parsed) {
|
|
296
488
|
const sections = [
|
|
297
489
|
["router", parsed.router],
|
|
298
490
|
["router.codex", parsed.router?.codex],
|
|
491
|
+
["router.codex.env", parsed.router?.codex?.env],
|
|
492
|
+
["orchestration", parsed.orchestration],
|
|
299
493
|
["workers", parsed.workers],
|
|
300
|
-
["workers.codex", parsed.workers?.codex],
|
|
301
|
-
["workers.codex.model", parsed.workers?.codex?.model],
|
|
302
|
-
["workers.codex.model.env", parsed.workers?.codex?.model?.env],
|
|
303
|
-
["workers.codex.nativeSession", parsed.workers?.codex?.nativeSession],
|
|
304
|
-
["workers.codex.interactive", parsed.workers?.codex?.interactive],
|
|
305
|
-
["workers.claude", parsed.workers?.claude],
|
|
306
|
-
["workers.claude.model", parsed.workers?.claude?.model],
|
|
307
|
-
["workers.claude.model.env", parsed.workers?.claude?.model?.env],
|
|
308
|
-
["workers.claude.nativeSession", parsed.workers?.claude?.nativeSession],
|
|
309
|
-
["workers.claude.interactive", parsed.workers?.claude?.interactive],
|
|
310
|
-
["workers.mock", parsed.workers?.mock],
|
|
311
|
-
["workers.mock.model", parsed.workers?.mock?.model],
|
|
312
|
-
["workers.mock.model.env", parsed.workers?.mock?.model?.env],
|
|
313
|
-
["workers.mock.nativeSession", parsed.workers?.mock?.nativeSession],
|
|
314
|
-
["workers.mock.interactive", parsed.workers?.mock?.interactive],
|
|
315
494
|
["pairing", parsed.pairing],
|
|
316
495
|
["roles", parsed.roles],
|
|
317
496
|
["roles.main", parsed.roles?.main],
|
|
318
497
|
["roles.judge", parsed.roles?.judge],
|
|
319
498
|
["roles.actor", parsed.roles?.actor],
|
|
320
499
|
["roles.critic", parsed.roles?.critic],
|
|
321
|
-
["ui", parsed.ui]
|
|
500
|
+
["ui", parsed.ui],
|
|
501
|
+
["ui.colors", parsed.ui?.colors]
|
|
322
502
|
];
|
|
503
|
+
if (isPlainObject(parsed.workers)) {
|
|
504
|
+
for (const [id, value] of Object.entries(parsed.workers)) {
|
|
505
|
+
sections.push([`workers.${id}`, value]);
|
|
506
|
+
if (!isPlainObject(value)) {
|
|
507
|
+
continue;
|
|
508
|
+
}
|
|
509
|
+
const worker = value;
|
|
510
|
+
sections.push([`workers.${id}.model`, worker.model], [`workers.${id}.capabilities`, worker.capabilities], [`workers.${id}.nativeSession`, worker.nativeSession], [`workers.${id}.interactive`, worker.interactive]);
|
|
511
|
+
if (isPlainObject(worker.model)) {
|
|
512
|
+
sections.push([`workers.${id}.model.env`, worker.model.env]);
|
|
513
|
+
}
|
|
514
|
+
}
|
|
515
|
+
}
|
|
323
516
|
for (const [path, value] of sections) {
|
|
324
517
|
if (value !== undefined && !isPlainObject(value)) {
|
|
325
518
|
throw new Error(`Invalid config section [${path}]: expected a table`);
|
|
@@ -330,13 +523,8 @@ function isPlainObject(value) {
|
|
|
330
523
|
return Boolean(value) && typeof value === "object" && !Array.isArray(value);
|
|
331
524
|
}
|
|
332
525
|
export async function writeDefaultConfig(projectRoot) {
|
|
333
|
-
|
|
334
|
-
|
|
335
|
-
|
|
336
|
-
|
|
337
|
-
pairing: config.pairing,
|
|
338
|
-
roles: config.roles,
|
|
339
|
-
ui: config.ui
|
|
340
|
-
});
|
|
341
|
-
await writeText(configPath(projectRoot), tomlText);
|
|
526
|
+
await writeText(configPath(projectRoot), await readExampleConfig());
|
|
527
|
+
}
|
|
528
|
+
async function readExampleConfig() {
|
|
529
|
+
return readFile(new URL("../../.parallel-codex/config.example.toml", import.meta.url), "utf8");
|
|
342
530
|
}
|