ultracode-for-codex 0.2.3 → 0.2.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/README.md +19 -6
- package/ULTRACODE_INSTALL.md +17 -4
- package/dist/cli.js +21 -0
- package/dist/runtime/workflow-runtime.d.ts +20 -0
- package/dist/runtime/workflow-runtime.js +280 -17
- package/package.json +1 -1
- package/settings.json +1 -1
- package/skills/ultracode-for-codex/SKILL.md +16 -4
package/README.md
CHANGED
|
@@ -26,7 +26,7 @@ npm run pack:ultracode-for-codex
|
|
|
26
26
|
Install the tarball from a target project:
|
|
27
27
|
|
|
28
28
|
```bash
|
|
29
|
-
npm install --save-dev /path/to/ultracode-for-codex-0.2.
|
|
29
|
+
npm install --save-dev /path/to/ultracode-for-codex-0.2.5.tgz
|
|
30
30
|
```
|
|
31
31
|
|
|
32
32
|
Run a workflow:
|
|
@@ -60,10 +60,19 @@ plugin workflow folders, and built-ins:
|
|
|
60
60
|
npm exec -- ultracode-for-codex run \
|
|
61
61
|
--accept-llm-guide=v1 \
|
|
62
62
|
--cwd /path/to/target-repo \
|
|
63
|
-
--name
|
|
64
|
-
--args '{"prompt":"review correctness risks"}'
|
|
63
|
+
--name task \
|
|
64
|
+
--args '{"prompt":"review correctness risks and propose fixes"}'
|
|
65
65
|
```
|
|
66
66
|
|
|
67
|
+
The built-in `task` and `code-review` workflows use an LLM planner first, then
|
|
68
|
+
run work phase by phase. Within each phase, multiple focused Codex subagents run
|
|
69
|
+
in parallel by default, followed by phase and final synthesis. The planner may
|
|
70
|
+
choose a single-agent path only when parallel execution would add risk or waste.
|
|
71
|
+
Planner guidance includes dynamic workflow patterns such as classify-and-act,
|
|
72
|
+
fan-out-and-synthesize, adversarial verification, generate-and-filter,
|
|
73
|
+
tournament, and loop-until-done, so different work types can use different
|
|
74
|
+
phase shapes.
|
|
75
|
+
|
|
67
76
|
## Settings
|
|
68
77
|
|
|
69
78
|
Package defaults live in `settings.json`:
|
|
@@ -75,7 +84,7 @@ Package defaults live in `settings.json`:
|
|
|
75
84
|
"progress": "jsonl",
|
|
76
85
|
"permission": "ask",
|
|
77
86
|
"retryLimit": 0,
|
|
78
|
-
"timeoutMs":
|
|
87
|
+
"timeoutMs": 900000,
|
|
79
88
|
"background": {
|
|
80
89
|
"runDir": ".ultracode-for-codex/background/{jobId}",
|
|
81
90
|
"resultFile": "result.json",
|
|
@@ -89,6 +98,7 @@ Package defaults live in `settings.json`:
|
|
|
89
98
|
|
|
90
99
|
Use `--execution attached`, `--progress`, `--permission`, `--retry-limit`, and
|
|
91
100
|
`--timeout-ms` to override settings for one run.
|
|
101
|
+
The package default workflow timeout is `900000` ms, or 15 minutes.
|
|
92
102
|
|
|
93
103
|
## CLI Controls
|
|
94
104
|
|
|
@@ -96,6 +106,8 @@ Use `--execution attached`, `--progress`, `--permission`, `--retry-limit`, and
|
|
|
96
106
|
- The final workflow result is printed as JSON to stdout.
|
|
97
107
|
- JSONL records include `kind`, `version`, `event`, `status`, and `summary`;
|
|
98
108
|
agent records also include stable agent identity and label fields.
|
|
109
|
+
- Built-in `task` and `code-review` emit `workflow.plan.ready` before phase
|
|
110
|
+
agents start, including phase titles and planned agent role labels.
|
|
99
111
|
- Press `Ctrl-C` once to cancel the active workflow.
|
|
100
112
|
- Use `--retry-limit <n>` to retry failed workflows inside the same process.
|
|
101
113
|
- `--timeout-ms` is the workflow timeout and the default per-agent silence
|
|
@@ -122,8 +134,9 @@ want Codex to auto-load the package boundaries and verification routine.
|
|
|
122
134
|
environment.
|
|
123
135
|
- Codex subagents run against the requested workflow cwd and receive bounded
|
|
124
136
|
read-only workspace tools for text file reads and directory listings.
|
|
125
|
-
- Built-in `code-review`
|
|
126
|
-
|
|
137
|
+
- Built-in `task` and `code-review` inject deterministic workspace context into
|
|
138
|
+
planner-selected phase-wise parallel subagents, then synthesize each phase and
|
|
139
|
+
the final result.
|
|
127
140
|
- Workflow execution is local and command-owned; settings default to OS
|
|
128
141
|
background execution and `--execution attached` keeps the process connected
|
|
129
142
|
until completion.
|
package/ULTRACODE_INSTALL.md
CHANGED
|
@@ -31,7 +31,7 @@ npm exec -- ultracode-for-codex --llm-guide
|
|
|
31
31
|
For source-checkout validation, install the generated tarball instead:
|
|
32
32
|
|
|
33
33
|
```bash
|
|
34
|
-
npm install --save-dev ./ultracode-for-codex-0.2.
|
|
34
|
+
npm install --save-dev ./ultracode-for-codex-0.2.5.tgz
|
|
35
35
|
```
|
|
36
36
|
|
|
37
37
|
Optional Codex companion skill:
|
|
@@ -58,6 +58,15 @@ npm exec -- ultracode-for-codex run \
|
|
|
58
58
|
The default run prints a background launch record to stdout. Use
|
|
59
59
|
`--execution attached` when Codex should read progress until completion.
|
|
60
60
|
|
|
61
|
+
Use built-in `task` for general work and `code-review` for review-specific work.
|
|
62
|
+
Both start with an LLM planner, execute phase by phase, run multiple focused
|
|
63
|
+
Codex subagents in parallel within each phase by default, and synthesize phase
|
|
64
|
+
and final results. The planner chooses a single-agent path only when parallel
|
|
65
|
+
execution would add risk or waste.
|
|
66
|
+
Planner guidance includes classify-and-act, fan-out-and-synthesize,
|
|
67
|
+
adversarial verification, generate-and-filter, tournament, and loop-until-done
|
|
68
|
+
patterns so different work types can use different phase shapes.
|
|
69
|
+
|
|
61
70
|
Settings defaults:
|
|
62
71
|
|
|
63
72
|
```json
|
|
@@ -67,7 +76,7 @@ Settings defaults:
|
|
|
67
76
|
"progress": "jsonl",
|
|
68
77
|
"permission": "ask",
|
|
69
78
|
"retryLimit": 0,
|
|
70
|
-
"timeoutMs":
|
|
79
|
+
"timeoutMs": 900000,
|
|
71
80
|
"background": {
|
|
72
81
|
"runDir": ".ultracode-for-codex/background/{jobId}",
|
|
73
82
|
"resultFile": "result.json",
|
|
@@ -83,8 +92,11 @@ Useful controls:
|
|
|
83
92
|
|
|
84
93
|
- Progress events are printed to stderr as JSONL by default.
|
|
85
94
|
- The final workflow result is printed as JSON to stdout.
|
|
95
|
+
- The package default workflow timeout is `900000` ms, or 15 minutes.
|
|
86
96
|
- JSONL records include `kind`, `version`, `event`, `status`, and `summary`;
|
|
87
97
|
agent records also include stable agent identity and label fields.
|
|
98
|
+
- Built-in `task` and `code-review` emit `workflow.plan.ready` before phase
|
|
99
|
+
agents start, including phase titles and planned agent role labels.
|
|
88
100
|
- Press `Ctrl-C` once to cancel the running workflow.
|
|
89
101
|
- Use `--retry-limit <n>` to retry failed runs in the same process.
|
|
90
102
|
- `--timeout-ms` is the workflow timeout and the default per-agent silence
|
|
@@ -108,8 +120,9 @@ Useful controls:
|
|
|
108
120
|
- Strip direct provider credentials from child CLI environments.
|
|
109
121
|
- Run Codex subagents against the requested workflow cwd and provide bounded
|
|
110
122
|
read-only workspace tools for text file reads and directory listings.
|
|
111
|
-
- Built-in `code-review`
|
|
112
|
-
|
|
123
|
+
- Built-in `task` and `code-review` add deterministic workspace context to
|
|
124
|
+
planner-selected phase-wise parallel subagents, then synthesize each phase and
|
|
125
|
+
the final result.
|
|
113
126
|
- Install consumers from a packaged artifact.
|
|
114
127
|
- Keep `journalPath`, `journal.jsonl`, and journal contents out of CLI output.
|
|
115
128
|
Local runtime state may still contain runtime-owned
|
package/dist/cli.js
CHANGED
|
@@ -350,6 +350,15 @@ function renderWorkflowEvent(event, progressMode) {
|
|
|
350
350
|
case 'workflow.phase.started':
|
|
351
351
|
process.stderr.write(`[phase] ${event.title}${event.detail ? ` - ${event.detail}` : ''}\n`);
|
|
352
352
|
return;
|
|
353
|
+
case 'workflow.plan.ready':
|
|
354
|
+
process.stderr.write(`[plan] mode=${event.mode} phases=${event.phases.length}${event.rationale ? ` - ${event.rationale}` : ''}\n`);
|
|
355
|
+
for (const [phaseIndex, phase] of event.phases.entries()) {
|
|
356
|
+
process.stderr.write(`[plan] ${phaseIndex + 1}. ${phase.title}${phase.goal ? ` - ${phase.goal}` : ''}\n`);
|
|
357
|
+
for (const agent of phase.agents) {
|
|
358
|
+
process.stderr.write(`[plan] - ${agent.title}${agent.label ? ` (${agent.label})` : ''}${agent.focus ? `: ${agent.focus}` : ''}\n`);
|
|
359
|
+
}
|
|
360
|
+
}
|
|
361
|
+
return;
|
|
353
362
|
case 'workflow.log':
|
|
354
363
|
process.stderr.write(`[log] ${event.message}\n`);
|
|
355
364
|
return;
|
|
@@ -425,6 +434,18 @@ function progressPayloadForEvent(event) {
|
|
|
425
434
|
title: event.title,
|
|
426
435
|
detail: event.detail,
|
|
427
436
|
};
|
|
437
|
+
case 'workflow.plan.ready':
|
|
438
|
+
return {
|
|
439
|
+
event: event.type,
|
|
440
|
+
status: 'planned',
|
|
441
|
+
summary: `Workflow plan ready: ${event.phases.length} phase${event.phases.length === 1 ? '' : 's'}, mode=${event.mode}`,
|
|
442
|
+
taskId: event.taskId,
|
|
443
|
+
runId: event.runId,
|
|
444
|
+
mode: event.mode,
|
|
445
|
+
rationale: event.rationale,
|
|
446
|
+
phaseCount: event.phases.length,
|
|
447
|
+
planPhases: event.phases,
|
|
448
|
+
};
|
|
428
449
|
case 'workflow.log':
|
|
429
450
|
return {
|
|
430
451
|
event: event.type,
|
|
@@ -4,6 +4,18 @@ export type WorkflowTaskStatus = 'running' | 'completed' | 'failed';
|
|
|
4
4
|
export type WorkflowTaskType = 'local_workflow';
|
|
5
5
|
export type WorkflowSource = 'inline' | 'script_path' | 'project' | 'user' | 'plugin' | 'built_in';
|
|
6
6
|
export type WorkflowPermissionDecision = 'allow' | 'deny';
|
|
7
|
+
export interface WorkflowPlanAgent {
|
|
8
|
+
readonly id?: string;
|
|
9
|
+
readonly title: string;
|
|
10
|
+
readonly focus?: string;
|
|
11
|
+
readonly label?: string;
|
|
12
|
+
}
|
|
13
|
+
export interface WorkflowPlanPhase {
|
|
14
|
+
readonly id?: string;
|
|
15
|
+
readonly title: string;
|
|
16
|
+
readonly goal?: string;
|
|
17
|
+
readonly agents: readonly WorkflowPlanAgent[];
|
|
18
|
+
}
|
|
7
19
|
export type WorkflowEvent = {
|
|
8
20
|
readonly type: 'workflow.started';
|
|
9
21
|
readonly taskId: string;
|
|
@@ -20,6 +32,13 @@ export type WorkflowEvent = {
|
|
|
20
32
|
readonly phaseIndex: number;
|
|
21
33
|
readonly title: string;
|
|
22
34
|
readonly detail?: string;
|
|
35
|
+
} | {
|
|
36
|
+
readonly type: 'workflow.plan.ready';
|
|
37
|
+
readonly taskId: string;
|
|
38
|
+
readonly runId: string;
|
|
39
|
+
readonly mode: string;
|
|
40
|
+
readonly rationale?: string;
|
|
41
|
+
readonly phases: readonly WorkflowPlanPhase[];
|
|
23
42
|
} | {
|
|
24
43
|
readonly type: 'workflow.log';
|
|
25
44
|
readonly taskId: string;
|
|
@@ -250,6 +269,7 @@ export declare class WorkflowTaskRegistry implements WorkflowRuntime {
|
|
|
250
269
|
private runAgentAttempt;
|
|
251
270
|
private parallel;
|
|
252
271
|
private pipeline;
|
|
272
|
+
private announcePlan;
|
|
253
273
|
private phase;
|
|
254
274
|
private completeTask;
|
|
255
275
|
private failTask;
|
|
@@ -98,40 +98,239 @@ const WORKSPACE_CONTEXT_PRIORITY_FILES = new Set([
|
|
|
98
98
|
'package.json',
|
|
99
99
|
'tsconfig.json',
|
|
100
100
|
]);
|
|
101
|
+
const DYNAMIC_WORKFLOW_PATTERN_GUIDANCE = [
|
|
102
|
+
'Use dynamic workflow patterns intentionally:',
|
|
103
|
+
'- classify-and-act: classify the request, risk, or repository area before choosing phase shape.',
|
|
104
|
+
'- fan-out-and-synthesize: split independent lenses across parallel agents, then merge evidence.',
|
|
105
|
+
'- adversarial verification: assign at least one agent to challenge correctness, security, or assumptions on high-risk work.',
|
|
106
|
+
'- generate-and-filter: create candidate approaches or fixes, then select by evidence and constraints.',
|
|
107
|
+
'- tournament: compare competing alternatives when the best path is unclear.',
|
|
108
|
+
'- loop-until-done: iterate repair and verification only when there is a clear stop condition.',
|
|
109
|
+
'Prefer pipelines when later phases need earlier summaries; prefer parallel agents when independent evidence can be gathered at the same time.',
|
|
110
|
+
].join('\n');
|
|
101
111
|
const DEFAULT_BUILTIN_WORKFLOWS = [
|
|
112
|
+
{
|
|
113
|
+
name: 'task',
|
|
114
|
+
script: phaseWiseBuiltinWorkflowScript({
|
|
115
|
+
name: 'task',
|
|
116
|
+
description: 'Run an LLM-planned phase-wise parallel task workflow',
|
|
117
|
+
defaultPrompt: 'Complete the requested repository task.',
|
|
118
|
+
plannerKind: 'general task',
|
|
119
|
+
plannerGuidance: 'Plan phases that make the work faster and more accurate through parallel agents. Default to phase_parallel. Choose single only for tiny changes, strictly sequential investigations, or one indivisible failure mode. Pick workflow patterns that match the request instead of using one fixed shape.',
|
|
120
|
+
agentGuidance: 'Complete the assigned phase work. Prefer concrete evidence, file paths, commands, and risks over broad narration.',
|
|
121
|
+
finalGuidance: 'Return the completed task result, key evidence, decisions made, verification status, and residual risk.',
|
|
122
|
+
}),
|
|
123
|
+
},
|
|
102
124
|
{
|
|
103
125
|
name: 'code-review',
|
|
104
|
-
script:
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
return await agent([
|
|
114
|
-
prompt,
|
|
115
|
-
"",
|
|
116
|
-
"Use the deterministic workspace context below as primary evidence. Mention any missing evidence explicitly.",
|
|
117
|
-
"",
|
|
118
|
-
context
|
|
119
|
-
].join("\\n"), { label: "code-review" });`,
|
|
126
|
+
script: phaseWiseBuiltinWorkflowScript({
|
|
127
|
+
name: 'code-review',
|
|
128
|
+
description: 'Run an LLM-planned phase-wise parallel code review workflow',
|
|
129
|
+
defaultPrompt: 'Review the current repository for correctness risks.',
|
|
130
|
+
plannerKind: 'code review',
|
|
131
|
+
plannerGuidance: 'Plan an effective code review. Default to phase_parallel with multiple focused reviewers per phase. Commonly useful lenses include runtime correctness, security/capability boundaries, API/CLI contracts, persistence/retry/cancel behavior, and test coverage. Prefer fan-out-and-synthesize plus adversarial verification unless the diff is tiny or one indivisible failure mode.',
|
|
132
|
+
agentGuidance: 'Return material findings only. Prioritize root cause, severity, exact file/line evidence, reproduction or impact, and residual risk.',
|
|
133
|
+
finalGuidance: 'Return findings ordered by severity with exact file/line references. Deduplicate overlaps, preserve dissent or uncertainty, and say clearly if there are no material findings.',
|
|
134
|
+
}),
|
|
120
135
|
},
|
|
121
136
|
{
|
|
122
137
|
name: 'batch',
|
|
123
138
|
script: `export const meta = {
|
|
124
139
|
name: "batch",
|
|
125
|
-
description: "Run
|
|
140
|
+
description: "Run explicitly supplied prompts in one parallel phase"
|
|
126
141
|
};
|
|
127
142
|
const input = args && typeof args === "object" ? args : {};
|
|
128
143
|
const prompts = Array.isArray(input.prompts) ? input.prompts : [];
|
|
144
|
+
phase("Batch");
|
|
129
145
|
return await parallel(prompts.map((prompt, index) => () => agent(
|
|
130
146
|
prompt == null ? "" : "" + prompt,
|
|
131
147
|
{ label: "batch-" + (index + 1) }
|
|
132
148
|
)));`,
|
|
133
149
|
},
|
|
134
150
|
];
|
|
151
|
+
function phaseWiseBuiltinWorkflowScript(input) {
|
|
152
|
+
return `export const meta = {
|
|
153
|
+
name: ${JSON.stringify(input.name)},
|
|
154
|
+
description: ${JSON.stringify(input.description)}
|
|
155
|
+
};
|
|
156
|
+
const workflowInput = args && typeof args === "object" ? args : {};
|
|
157
|
+
const prompt = typeof workflowInput.prompt === "string" && workflowInput.prompt.trim()
|
|
158
|
+
? workflowInput.prompt
|
|
159
|
+
: ${JSON.stringify(input.defaultPrompt)};
|
|
160
|
+
const context = await workspaceContext({ query: prompt });
|
|
161
|
+
const plan = await agent([
|
|
162
|
+
${JSON.stringify(`Plan the phase-wise execution strategy for ${input.plannerKind}.`)},
|
|
163
|
+
"",
|
|
164
|
+
${JSON.stringify(input.plannerGuidance)},
|
|
165
|
+
"",
|
|
166
|
+
${JSON.stringify(DYNAMIC_WORKFLOW_PATTERN_GUIDANCE)},
|
|
167
|
+
"A phase runs after previous phase summaries are available. Within each phase, use parallel agents by default.",
|
|
168
|
+
"Return 1 to 4 phases. For ordinary work, prefer 2 phases with 2 to 4 parallel agents each. Use concise stable ids.",
|
|
169
|
+
"",
|
|
170
|
+
"User request:",
|
|
171
|
+
prompt,
|
|
172
|
+
"",
|
|
173
|
+
context
|
|
174
|
+
].join("\\n"), {
|
|
175
|
+
label: ${JSON.stringify(`${input.name}-planner`)},
|
|
176
|
+
schema: {
|
|
177
|
+
type: "object",
|
|
178
|
+
additionalProperties: false,
|
|
179
|
+
properties: {
|
|
180
|
+
mode: { type: "string", enum: ["phase_parallel", "single"] },
|
|
181
|
+
rationale: { type: "string", minLength: 1 },
|
|
182
|
+
phases: {
|
|
183
|
+
type: "array",
|
|
184
|
+
minItems: 1,
|
|
185
|
+
maxItems: 4,
|
|
186
|
+
items: {
|
|
187
|
+
type: "object",
|
|
188
|
+
additionalProperties: false,
|
|
189
|
+
properties: {
|
|
190
|
+
id: { type: "string", minLength: 1, maxLength: 32 },
|
|
191
|
+
title: { type: "string", minLength: 1, maxLength: 80 },
|
|
192
|
+
goal: { type: "string", minLength: 1, maxLength: 800 },
|
|
193
|
+
agents: {
|
|
194
|
+
type: "array",
|
|
195
|
+
minItems: 1,
|
|
196
|
+
maxItems: 4,
|
|
197
|
+
items: {
|
|
198
|
+
type: "object",
|
|
199
|
+
additionalProperties: false,
|
|
200
|
+
properties: {
|
|
201
|
+
id: { type: "string", minLength: 1, maxLength: 32 },
|
|
202
|
+
title: { type: "string", minLength: 1, maxLength: 80 },
|
|
203
|
+
focus: { type: "string", minLength: 1, maxLength: 800 }
|
|
204
|
+
},
|
|
205
|
+
required: ["id", "title", "focus"]
|
|
206
|
+
}
|
|
207
|
+
}
|
|
208
|
+
},
|
|
209
|
+
required: ["id", "title", "goal", "agents"]
|
|
210
|
+
}
|
|
211
|
+
}
|
|
212
|
+
},
|
|
213
|
+
required: ["mode", "rationale", "phases"]
|
|
214
|
+
}
|
|
215
|
+
});
|
|
216
|
+
const selectedPhases = plan.mode === "single" ? [plan.phases[0]] : plan.phases;
|
|
217
|
+
announcePlan({
|
|
218
|
+
mode: plan.mode,
|
|
219
|
+
rationale: plan.rationale,
|
|
220
|
+
phases: selectedPhases.map((phasePlan) => ({
|
|
221
|
+
id: phasePlan.id,
|
|
222
|
+
title: phasePlan.title,
|
|
223
|
+
goal: phasePlan.goal,
|
|
224
|
+
agents: (plan.mode === "single" ? [phasePlan.agents[0]] : phasePlan.agents).map((phaseAgent) => ({
|
|
225
|
+
id: phaseAgent.id,
|
|
226
|
+
title: phaseAgent.title,
|
|
227
|
+
focus: phaseAgent.focus,
|
|
228
|
+
label: plan.mode === "single"
|
|
229
|
+
? ${JSON.stringify(`${input.name}-single`)}
|
|
230
|
+
: ${JSON.stringify(`${input.name}-`)} + phasePlan.id + "-" + phaseAgent.id
|
|
231
|
+
}))
|
|
232
|
+
}))
|
|
233
|
+
});
|
|
234
|
+
if (plan.mode === "single") {
|
|
235
|
+
const singlePhase = selectedPhases[0];
|
|
236
|
+
const singleAgent = singlePhase.agents[0];
|
|
237
|
+
phase(singlePhase.title);
|
|
238
|
+
return await agent([
|
|
239
|
+
"Single-agent execution selected by the LLM planner.",
|
|
240
|
+
"Planner rationale: " + plan.rationale,
|
|
241
|
+
"Phase goal: " + singlePhase.goal,
|
|
242
|
+
"Agent focus: " + singleAgent.title + " - " + singleAgent.focus,
|
|
243
|
+
"",
|
|
244
|
+
"Original request:",
|
|
245
|
+
prompt,
|
|
246
|
+
"",
|
|
247
|
+
${JSON.stringify(input.agentGuidance)},
|
|
248
|
+
"",
|
|
249
|
+
"Use the deterministic workspace context below as primary evidence. Mention any missing evidence explicitly.",
|
|
250
|
+
"",
|
|
251
|
+
context
|
|
252
|
+
].join("\\n"), { label: ${JSON.stringify(`${input.name}-single`)}, phase: singlePhase.title });
|
|
253
|
+
}
|
|
254
|
+
const phaseOutputs = [];
|
|
255
|
+
const priorSummaries = [];
|
|
256
|
+
for (const phasePlan of selectedPhases) {
|
|
257
|
+
phase(phasePlan.title);
|
|
258
|
+
const agents = phasePlan.agents;
|
|
259
|
+
const agentOutputs = agents.length < 2
|
|
260
|
+
? [await agent([
|
|
261
|
+
"Parallel phase agent: " + agents[0].title,
|
|
262
|
+
"Phase: " + phasePlan.title,
|
|
263
|
+
"Phase goal: " + phasePlan.goal,
|
|
264
|
+
"Agent focus: " + agents[0].focus,
|
|
265
|
+
"",
|
|
266
|
+
"Previous phase summaries:",
|
|
267
|
+
JSON.stringify(priorSummaries, null, 2),
|
|
268
|
+
"",
|
|
269
|
+
"Original request:",
|
|
270
|
+
prompt,
|
|
271
|
+
"",
|
|
272
|
+
${JSON.stringify(input.agentGuidance)},
|
|
273
|
+
"",
|
|
274
|
+
"Use the deterministic workspace context below as primary evidence. Mention any missing evidence explicitly.",
|
|
275
|
+
"",
|
|
276
|
+
context
|
|
277
|
+
].join("\\n"), { label: ${JSON.stringify(`${input.name}-`)} + phasePlan.id + "-" + agents[0].id, phase: phasePlan.title })]
|
|
278
|
+
: await parallel(agents.map((phaseAgent) => () => agent([
|
|
279
|
+
"Parallel phase agent: " + phaseAgent.title,
|
|
280
|
+
"Phase: " + phasePlan.title,
|
|
281
|
+
"Phase goal: " + phasePlan.goal,
|
|
282
|
+
"Agent focus: " + phaseAgent.focus,
|
|
283
|
+
"",
|
|
284
|
+
"Previous phase summaries:",
|
|
285
|
+
JSON.stringify(priorSummaries, null, 2),
|
|
286
|
+
"",
|
|
287
|
+
"Original request:",
|
|
288
|
+
prompt,
|
|
289
|
+
"",
|
|
290
|
+
${JSON.stringify(input.agentGuidance)},
|
|
291
|
+
"",
|
|
292
|
+
"Use the deterministic workspace context below as primary evidence. Mention any missing evidence explicitly.",
|
|
293
|
+
"",
|
|
294
|
+
context
|
|
295
|
+
].join("\\n"), { label: ${JSON.stringify(`${input.name}-`)} + phasePlan.id + "-" + phaseAgent.id, phase: phasePlan.title })));
|
|
296
|
+
const phaseSummary = await agent([
|
|
297
|
+
"Synthesize this phase.",
|
|
298
|
+
"Phase: " + phasePlan.title,
|
|
299
|
+
"Phase goal: " + phasePlan.goal,
|
|
300
|
+
"",
|
|
301
|
+
"Original request:",
|
|
302
|
+
prompt,
|
|
303
|
+
"",
|
|
304
|
+
"Agent outputs:",
|
|
305
|
+
JSON.stringify(agentOutputs, null, 2),
|
|
306
|
+
"",
|
|
307
|
+
"Return a concise phase summary with material findings, completed work, open questions, and what the next phase should know."
|
|
308
|
+
].join("\\n"), { label: ${JSON.stringify(`${input.name}-phase-`)} + phasePlan.id + "-synthesis", phase: phasePlan.title });
|
|
309
|
+
const phaseRecord = {
|
|
310
|
+
id: phasePlan.id,
|
|
311
|
+
title: phasePlan.title,
|
|
312
|
+
goal: phasePlan.goal,
|
|
313
|
+
results: agentOutputs,
|
|
314
|
+
summary: phaseSummary
|
|
315
|
+
};
|
|
316
|
+
phaseOutputs.push(phaseRecord);
|
|
317
|
+
priorSummaries.push({ id: phaseRecord.id, title: phaseRecord.title, summary: phaseRecord.summary });
|
|
318
|
+
}
|
|
319
|
+
return await agent([
|
|
320
|
+
${JSON.stringify(`Synthesize the phase-wise ${input.plannerKind} workflow into the final result.`)},
|
|
321
|
+
"",
|
|
322
|
+
"Original request:",
|
|
323
|
+
prompt,
|
|
324
|
+
"",
|
|
325
|
+
"Planner rationale:",
|
|
326
|
+
plan.rationale,
|
|
327
|
+
"",
|
|
328
|
+
"Phase outputs:",
|
|
329
|
+
JSON.stringify(phaseOutputs, null, 2),
|
|
330
|
+
"",
|
|
331
|
+
${JSON.stringify(input.finalGuidance)}
|
|
332
|
+
].join("\\n"), { label: ${JSON.stringify(`${input.name}-final-synthesis`)} });`;
|
|
333
|
+
}
|
|
135
334
|
export class WorkflowTaskRegistry {
|
|
136
335
|
options;
|
|
137
336
|
tasks = new Map();
|
|
@@ -878,6 +1077,7 @@ export class WorkflowTaskRegistry {
|
|
|
878
1077
|
host.workspaceContext = hardenCallable((options) => {
|
|
879
1078
|
return this.trackWorkflowPromise(ctx, this.workspaceContext(ctx, options));
|
|
880
1079
|
});
|
|
1080
|
+
host.announcePlan = hardenCallable((plan) => this.announcePlan(ctx, plan));
|
|
881
1081
|
host.phase = hardenCallable((title) => this.phase(ctx, title));
|
|
882
1082
|
host.log = hardenCallable(log);
|
|
883
1083
|
host.consoleLog = hardenCallable((...values) => {
|
|
@@ -1324,6 +1524,18 @@ export class WorkflowTaskRegistry {
|
|
|
1324
1524
|
return current;
|
|
1325
1525
|
});
|
|
1326
1526
|
}
|
|
1527
|
+
announcePlan(ctx, plan) {
|
|
1528
|
+
if (ctx.controller.signal.aborted || ctx.task.status !== 'running') {
|
|
1529
|
+
throw workflowInputError('Workflow is aborted.');
|
|
1530
|
+
}
|
|
1531
|
+
const normalized = normalizeWorkflowExecutionPlan(plan);
|
|
1532
|
+
this.emit(ctx.task, {
|
|
1533
|
+
type: 'workflow.plan.ready',
|
|
1534
|
+
taskId: ctx.task.taskId,
|
|
1535
|
+
runId: ctx.task.runId,
|
|
1536
|
+
...normalized,
|
|
1537
|
+
});
|
|
1538
|
+
}
|
|
1327
1539
|
phase(ctx, title) {
|
|
1328
1540
|
if (typeof title !== 'string' || title.trim() === '') {
|
|
1329
1541
|
throw workflowInputError('phase() requires a non-empty string title.');
|
|
@@ -2759,6 +2971,7 @@ function installWorkflowVmGlobals(context, globals) {
|
|
|
2759
2971
|
' define(globalThis, "parallel", { value: (...values) => __host.parallel(...values), writable: false, configurable: false });',
|
|
2760
2972
|
' define(globalThis, "pipeline", { value: (...values) => __host.pipeline(...values), writable: false, configurable: false });',
|
|
2761
2973
|
' define(globalThis, "workspaceContext", { value: (...values) => __host.workspaceContext(...values), writable: false, configurable: false });',
|
|
2974
|
+
' define(globalThis, "announcePlan", { value: (...values) => __host.announcePlan(...values), writable: false, configurable: false });',
|
|
2762
2975
|
' define(globalThis, "phase", { value: (...values) => __host.phase(...values), writable: false, configurable: false });',
|
|
2763
2976
|
' define(globalThis, "log", { value: (...values) => __host.log(...values), writable: false, configurable: false });',
|
|
2764
2977
|
' define(globalThis, "workflow", { value: (...values) => __host.workflow(...values), writable: false, configurable: false });',
|
|
@@ -3442,6 +3655,56 @@ function previewValue(value, limit) {
|
|
|
3442
3655
|
: JSON.stringify(value) ?? String(value);
|
|
3443
3656
|
return preview(text, limit);
|
|
3444
3657
|
}
|
|
3658
|
+
function normalizeWorkflowExecutionPlan(value) {
|
|
3659
|
+
const record = asRecord(value);
|
|
3660
|
+
if (!record)
|
|
3661
|
+
throw workflowInputError('announcePlan(plan) requires a plan object.');
|
|
3662
|
+
const mode = boundedPlanString(record.mode, 'phase_parallel', 48);
|
|
3663
|
+
const rationale = optionalBoundedPlanString(record.rationale, 400);
|
|
3664
|
+
const rawPhases = Array.isArray(record.phases) ? Array.from(record.phases) : [];
|
|
3665
|
+
if (rawPhases.length === 0)
|
|
3666
|
+
throw workflowInputError('announcePlan(plan) requires at least one phase.');
|
|
3667
|
+
const phases = rawPhases.slice(0, 16).map((phaseValue, phaseIndex) => {
|
|
3668
|
+
const phase = asRecord(phaseValue);
|
|
3669
|
+
if (!phase)
|
|
3670
|
+
throw workflowInputError(`announcePlan(plan).phases[${phaseIndex}] must be an object.`);
|
|
3671
|
+
const rawAgents = Array.isArray(phase.agents) ? Array.from(phase.agents) : [];
|
|
3672
|
+
if (rawAgents.length === 0) {
|
|
3673
|
+
throw workflowInputError(`announcePlan(plan).phases[${phaseIndex}].agents requires at least one agent.`);
|
|
3674
|
+
}
|
|
3675
|
+
return {
|
|
3676
|
+
...(typeof phase.id === 'string' && phase.id.trim() ? { id: boundedPlanString(phase.id, '', 48) } : {}),
|
|
3677
|
+
title: boundedPlanString(phase.title, `Phase ${phaseIndex + 1}`, 96),
|
|
3678
|
+
...(typeof phase.goal === 'string' && phase.goal.trim() ? { goal: boundedPlanString(phase.goal, '', 600) } : {}),
|
|
3679
|
+
agents: rawAgents.slice(0, 16).map((agentValue, agentIndex) => {
|
|
3680
|
+
const agent = asRecord(agentValue);
|
|
3681
|
+
if (!agent) {
|
|
3682
|
+
throw workflowInputError(`announcePlan(plan).phases[${phaseIndex}].agents[${agentIndex}] must be an object.`);
|
|
3683
|
+
}
|
|
3684
|
+
return {
|
|
3685
|
+
...(typeof agent.id === 'string' && agent.id.trim() ? { id: boundedPlanString(agent.id, '', 48) } : {}),
|
|
3686
|
+
title: boundedPlanString(agent.title, `Agent ${agentIndex + 1}`, 96),
|
|
3687
|
+
...(typeof agent.focus === 'string' && agent.focus.trim() ? { focus: boundedPlanString(agent.focus, '', 600) } : {}),
|
|
3688
|
+
...(typeof agent.label === 'string' && agent.label.trim() ? { label: boundedPlanString(agent.label, '', 96) } : {}),
|
|
3689
|
+
};
|
|
3690
|
+
}),
|
|
3691
|
+
};
|
|
3692
|
+
});
|
|
3693
|
+
return {
|
|
3694
|
+
mode,
|
|
3695
|
+
...(rationale ? { rationale } : {}),
|
|
3696
|
+
phases,
|
|
3697
|
+
};
|
|
3698
|
+
}
|
|
3699
|
+
function boundedPlanString(value, fallback, limit) {
|
|
3700
|
+
const text = typeof value === 'string' && value.trim() ? value.trim() : fallback;
|
|
3701
|
+
return preview(text, limit);
|
|
3702
|
+
}
|
|
3703
|
+
function optionalBoundedPlanString(value, limit) {
|
|
3704
|
+
if (typeof value !== 'string' || !value.trim())
|
|
3705
|
+
return undefined;
|
|
3706
|
+
return boundedPlanString(value, '', limit);
|
|
3707
|
+
}
|
|
3445
3708
|
function asRecord(value) {
|
|
3446
3709
|
if (!value || typeof value !== 'object' || Array.isArray(value))
|
|
3447
3710
|
return null;
|
package/package.json
CHANGED
package/settings.json
CHANGED
|
@@ -17,6 +17,15 @@ command process. `settings.json` defaults runs to OS background execution.
|
|
|
17
17
|
Attached runs stream stderr JSONL for Codex-readable status, while stdout
|
|
18
18
|
remains the final workflow result JSON.
|
|
19
19
|
|
|
20
|
+
The default Ultracode work shape is phase-wise parallel execution: built-in
|
|
21
|
+
`task` and `code-review` first call a planner agent, then execute each planned
|
|
22
|
+
phase with parallel focused subagents by default, followed by phase and final
|
|
23
|
+
synthesis. A single-agent path is reserved for cases where the planner judges
|
|
24
|
+
parallel execution risky or wasteful.
|
|
25
|
+
Planner guidance includes classify-and-act, fan-out-and-synthesize,
|
|
26
|
+
adversarial verification, generate-and-filter, tournament, and loop-until-done
|
|
27
|
+
patterns so workflow shape can follow the task instead of a fixed template.
|
|
28
|
+
|
|
20
29
|
## Install And Run
|
|
21
30
|
|
|
22
31
|
Use the npm package for consumer installs.
|
|
@@ -35,7 +44,7 @@ For source-checkout validation before publish:
|
|
|
35
44
|
|
|
36
45
|
```bash
|
|
37
46
|
npm run pack:ultracode-for-codex
|
|
38
|
-
npm install --save-dev ./artifacts/ultracode-for-codex-0.2.
|
|
47
|
+
npm install --save-dev ./artifacts/ultracode-for-codex-0.2.5.tgz
|
|
39
48
|
```
|
|
40
49
|
|
|
41
50
|
CLI behavior:
|
|
@@ -47,10 +56,13 @@ CLI behavior:
|
|
|
47
56
|
- attached final workflow result prints as JSON to stdout;
|
|
48
57
|
- JSONL records include `kind`, `version`, `event`, `status`, and `summary`,
|
|
49
58
|
with agent identity and label fields on agent records;
|
|
59
|
+
- built-in `task` and `code-review` emit `workflow.plan.ready` before phase
|
|
60
|
+
agents start, including phase titles and planned agent role labels;
|
|
50
61
|
- `Ctrl-C` cancels the active attached workflow;
|
|
51
62
|
- `--retry-limit <n>` retries failed workflows inside the same process;
|
|
52
63
|
- `--timeout-ms` is the workflow timeout and the default per-agent silence
|
|
53
|
-
budget;
|
|
64
|
+
budget; the package default is `900000` ms, or 15 minutes, and it is not
|
|
65
|
+
divided by the retry budget.
|
|
54
66
|
- `--permission ask|allow|deny` handles project/user/plugin/scriptPath reviews.
|
|
55
67
|
- `--progress plain` switches to human-readable progress lines.
|
|
56
68
|
- background file locations are controlled by `workflow.background` in
|
|
@@ -62,8 +74,8 @@ CLI behavior:
|
|
|
62
74
|
- Keep direct provider credentials out of Codex child process environments.
|
|
63
75
|
- Codex subagents run against the requested workflow cwd and have bounded
|
|
64
76
|
read-only workspace tools for text file reads and directory listings.
|
|
65
|
-
- Built-in `code-review`
|
|
66
|
-
|
|
77
|
+
- Built-in `task` and `code-review` inject deterministic workspace context into
|
|
78
|
+
planner-selected phase-wise parallel subagents.
|
|
67
79
|
- Keep workflow execution local and command-owned; settings default to OS
|
|
68
80
|
background execution and `--execution attached` keeps the process connected
|
|
69
81
|
until completion.
|