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 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.3.tgz
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 code-review \
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": 180000,
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` injects deterministic workspace context before calling
126
- its subagent so file-specific prompts carry local evidence into the review.
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.
@@ -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.3.tgz
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": 180000,
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` adds deterministic workspace context before the agent
112
- call, prioritizing files explicitly mentioned in the prompt.
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: `export const meta = {
105
- name: "code-review",
106
- description: "Run a code review workflow"
107
- };
108
- const input = args && typeof args === "object" ? args : {};
109
- const prompt = typeof input.prompt === "string" && input.prompt.trim()
110
- ? input.prompt
111
- : "Review the current repository for correctness risks.";
112
- const context = await workspaceContext({ query: prompt });
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 multiple prompts in parallel"
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
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "ultracode-for-codex",
3
- "version": "0.2.3",
3
+ "version": "0.2.5",
4
4
  "description": "Run local Codex-backed workflows from a command-owned CLI runtime.",
5
5
  "keywords": [
6
6
  "codex",
package/settings.json CHANGED
@@ -4,7 +4,7 @@
4
4
  "progress": "jsonl",
5
5
  "permission": "ask",
6
6
  "retryLimit": 0,
7
- "timeoutMs": 180000,
7
+ "timeoutMs": 900000,
8
8
  "background": {
9
9
  "runDir": ".ultracode-for-codex/background/{jobId}",
10
10
  "resultFile": "result.json",
@@ -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.3.tgz
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; it is not divided by the retry 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` injects deterministic workspace context and prioritizes
66
- files explicitly mentioned in the prompt.
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.