agentxchain 2.112.0 → 2.114.0
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/bin/agentxchain.js +54 -1
- package/dashboard/app.js +2 -1
- package/dashboard/components/mission.js +180 -1
- package/package.json +1 -1
- package/src/commands/mission.js +786 -1
- package/src/lib/dashboard/bridge-server.js +9 -0
- package/src/lib/dashboard/file-watcher.js +10 -6
- package/src/lib/dashboard/plan-reader.js +108 -0
- package/src/lib/dashboard/state-reader.js +11 -0
- package/src/lib/mission-plans.js +543 -0
- package/src/lib/run-chain.js +5 -3
package/bin/agentxchain.js
CHANGED
|
@@ -122,7 +122,7 @@ import { eventsCommand } from '../src/commands/events.js';
|
|
|
122
122
|
import { connectorCheckCommand } from '../src/commands/connector.js';
|
|
123
123
|
import { scheduleDaemonCommand, scheduleListCommand, scheduleRunDueCommand, scheduleStatusCommand } from '../src/commands/schedule.js';
|
|
124
124
|
import { chainLatestCommand, chainListCommand, chainShowCommand } from '../src/commands/chain.js';
|
|
125
|
-
import { missionAttachChainCommand, missionListCommand, missionShowCommand, missionStartCommand } from '../src/commands/mission.js';
|
|
125
|
+
import { missionAttachChainCommand, missionListCommand, missionPlanApproveCommand, missionPlanCommand, missionPlanLaunchCommand, missionPlanListCommand, missionPlanShowCommand, missionShowCommand, missionStartCommand } from '../src/commands/mission.js';
|
|
126
126
|
|
|
127
127
|
const __dirname = dirname(fileURLToPath(import.meta.url));
|
|
128
128
|
const pkg = JSON.parse(readFileSync(join(__dirname, '..', 'package.json'), 'utf8'));
|
|
@@ -430,6 +430,10 @@ missionCmd
|
|
|
430
430
|
.requiredOption('--title <text>', 'Mission title')
|
|
431
431
|
.requiredOption('--goal <text>', 'Mission goal')
|
|
432
432
|
.option('--id <mission_id>', 'Override the derived mission ID')
|
|
433
|
+
.option('--plan', 'Generate a proposed mission plan immediately after mission creation')
|
|
434
|
+
.option('--constraint <text>', 'Add a constraint to the planner when using --plan (repeatable)', collectOption, [])
|
|
435
|
+
.option('--role-hint <role>', 'Hint available roles to the planner when using --plan (repeatable)', collectOption, [])
|
|
436
|
+
.option('--planner-output-file <path>', 'Read planner JSON output from a file instead of calling the configured planner')
|
|
433
437
|
.option('-j, --json', 'Output as JSON')
|
|
434
438
|
.option('-d, --dir <path>', 'Project directory')
|
|
435
439
|
.action(missionStartCommand);
|
|
@@ -457,6 +461,55 @@ missionCmd
|
|
|
457
461
|
.option('-d, --dir <path>', 'Project directory')
|
|
458
462
|
.action(missionAttachChainCommand);
|
|
459
463
|
|
|
464
|
+
const missionPlanCmd = missionCmd
|
|
465
|
+
.command('plan [mission_id]')
|
|
466
|
+
.description('Generate a decomposition plan for a mission (default: latest mission)')
|
|
467
|
+
.option('--constraint <text>', 'Add a constraint to the planner (repeatable)', collectOption, [])
|
|
468
|
+
.option('--role-hint <role>', 'Hint available roles to the planner (repeatable)', collectOption, [])
|
|
469
|
+
.option('--planner-output-file <path>', 'Read planner JSON output from a file instead of calling the configured planner')
|
|
470
|
+
.option('-j, --json', 'Output as JSON')
|
|
471
|
+
.option('-d, --dir <path>', 'Project directory')
|
|
472
|
+
.action(missionPlanCommand);
|
|
473
|
+
|
|
474
|
+
missionPlanCmd
|
|
475
|
+
.command('show [plan_id]')
|
|
476
|
+
.description('Show a decomposition plan (default: latest plan)')
|
|
477
|
+
.option('-m, --mission <mission_id>', 'Explicit mission ID')
|
|
478
|
+
.option('-j, --json', 'Output as JSON')
|
|
479
|
+
.option('-d, --dir <path>', 'Project directory')
|
|
480
|
+
.action(missionPlanShowCommand);
|
|
481
|
+
|
|
482
|
+
missionPlanCmd
|
|
483
|
+
.command('approve [plan_id]')
|
|
484
|
+
.description('Approve a decomposition plan (default: latest plan)')
|
|
485
|
+
.option('-m, --mission <mission_id>', 'Explicit mission ID')
|
|
486
|
+
.option('-d, --dir <path>', 'Project directory')
|
|
487
|
+
.action(missionPlanApproveCommand);
|
|
488
|
+
|
|
489
|
+
missionPlanCmd
|
|
490
|
+
.command('launch [plan_id]')
|
|
491
|
+
.description('Launch workstream(s) from an approved plan (default: latest plan)')
|
|
492
|
+
.option('-w, --workstream <id>', 'Workstream ID to launch (mutually exclusive with --all-ready)')
|
|
493
|
+
.option('--all-ready', 'Launch all ready workstreams sequentially (mutually exclusive with --workstream)')
|
|
494
|
+
.option('-m, --mission <mission_id>', 'Explicit mission ID')
|
|
495
|
+
.option('--auto-approve', 'Auto-approve run gates while executing the launched workstream')
|
|
496
|
+
.option('-j, --json', 'Output as JSON')
|
|
497
|
+
.option('-d, --dir <path>', 'Project directory')
|
|
498
|
+
.action(missionPlanLaunchCommand);
|
|
499
|
+
|
|
500
|
+
missionPlanCmd
|
|
501
|
+
.command('list')
|
|
502
|
+
.description('List all plans for a mission')
|
|
503
|
+
.option('-m, --mission <mission_id>', 'Explicit mission ID')
|
|
504
|
+
.option('-l, --limit <n>', 'Max plans to show (default: 20)')
|
|
505
|
+
.option('-j, --json', 'Output as JSON')
|
|
506
|
+
.option('-d, --dir <path>', 'Project directory')
|
|
507
|
+
.action(missionPlanListCommand);
|
|
508
|
+
|
|
509
|
+
function collectOption(value, previous) {
|
|
510
|
+
return previous.concat([value]);
|
|
511
|
+
}
|
|
512
|
+
|
|
460
513
|
program
|
|
461
514
|
.command('validate')
|
|
462
515
|
.description('Validate project protocol artifacts')
|
package/dashboard/app.js
CHANGED
|
@@ -37,7 +37,7 @@ const VIEWS = {
|
|
|
37
37
|
'cross-repo': { fetch: ['coordinatorState', 'coordinatorHistory'], render: renderCrossRepo },
|
|
38
38
|
blockers: { fetch: ['coordinatorBlockers'], render: renderBlockers },
|
|
39
39
|
artifacts: { fetch: ['workflowKitArtifacts'], render: renderArtifacts },
|
|
40
|
-
mission: { fetch: ['missions'], render: renderMission },
|
|
40
|
+
mission: { fetch: ['missions', 'plans'], render: renderMission },
|
|
41
41
|
chain: { fetch: ['chainReports'], render: renderChain },
|
|
42
42
|
'run-history': { fetch: ['runHistory'], render: renderRunHistory },
|
|
43
43
|
timeouts: { fetch: ['timeouts'], render: renderTimeouts },
|
|
@@ -63,6 +63,7 @@ const API_MAP = {
|
|
|
63
63
|
coordinatorRepoStatusRows: '/api/coordinator/repo-status',
|
|
64
64
|
workflowKitArtifacts: '/api/workflow-kit-artifacts',
|
|
65
65
|
missions: '/api/missions',
|
|
66
|
+
plans: '/api/plans',
|
|
66
67
|
chainReports: '/api/chain-reports',
|
|
67
68
|
connectors: '/api/connectors',
|
|
68
69
|
runHistory: '/api/run-history',
|
|
@@ -132,7 +132,178 @@ function renderRecentMissions(missions) {
|
|
|
132
132
|
return html;
|
|
133
133
|
}
|
|
134
134
|
|
|
135
|
-
|
|
135
|
+
function formatPlanStatus(status) {
|
|
136
|
+
switch (status) {
|
|
137
|
+
case 'proposed':
|
|
138
|
+
return badge('proposed', '#38bdf8');
|
|
139
|
+
case 'approved':
|
|
140
|
+
return badge('approved', 'var(--green)');
|
|
141
|
+
case 'superseded':
|
|
142
|
+
return badge('superseded', 'var(--text-dim)');
|
|
143
|
+
case 'needs_attention':
|
|
144
|
+
return badge('needs attention', 'var(--yellow)');
|
|
145
|
+
default:
|
|
146
|
+
return badge(status || 'unknown', 'var(--text-dim)');
|
|
147
|
+
}
|
|
148
|
+
}
|
|
149
|
+
|
|
150
|
+
function formatLaunchStatus(status) {
|
|
151
|
+
switch (status) {
|
|
152
|
+
case 'ready':
|
|
153
|
+
return badge('ready', 'var(--green)');
|
|
154
|
+
case 'blocked':
|
|
155
|
+
return badge('blocked', 'var(--red)');
|
|
156
|
+
case 'launched':
|
|
157
|
+
return badge('launched', '#38bdf8');
|
|
158
|
+
case 'completed':
|
|
159
|
+
return badge('completed', 'var(--green)');
|
|
160
|
+
case 'needs_attention':
|
|
161
|
+
return badge('needs attention', 'var(--yellow)');
|
|
162
|
+
default:
|
|
163
|
+
return badge(status || 'unknown', 'var(--text-dim)');
|
|
164
|
+
}
|
|
165
|
+
}
|
|
166
|
+
|
|
167
|
+
function renderLatestPlan(plan) {
|
|
168
|
+
if (!plan) {
|
|
169
|
+
return `<div class="section"><h3>Latest Plan</h3><p class="section-subtitle">No decomposition plan found. Run <code>agentxchain mission plan <mission_id></code> to generate one.</p></div>`;
|
|
170
|
+
}
|
|
171
|
+
|
|
172
|
+
const workstreams = Array.isArray(plan.workstreams) ? plan.workstreams : [];
|
|
173
|
+
const launchRecords = Array.isArray(plan.launch_records) ? plan.launch_records : [];
|
|
174
|
+
const statusCounts = plan.workstream_status_counts || {};
|
|
175
|
+
|
|
176
|
+
let html = `<div class="section"><h3>Latest Plan</h3><dl class="detail-list">`;
|
|
177
|
+
html += `<dt>Plan ID</dt><dd class="mono" title="${esc(plan.plan_id || '')}">${esc(truncateId(plan.plan_id, 30))}</dd>`;
|
|
178
|
+
html += `<dt>Status</dt><dd>${formatPlanStatus(plan.status)}</dd>`;
|
|
179
|
+
html += `<dt>Mission</dt><dd class="mono">${esc(plan.mission_id || '—')}</dd>`;
|
|
180
|
+
html += `<dt>Created</dt><dd>${formatDate(plan.created_at)}</dd>`;
|
|
181
|
+
if (plan.approved_at) {
|
|
182
|
+
html += `<dt>Approved</dt><dd>${formatDate(plan.approved_at)}</dd>`;
|
|
183
|
+
}
|
|
184
|
+
html += `<dt>Workstreams</dt><dd>${plan.workstream_count || 0}</dd>`;
|
|
185
|
+
html += `<dt>Launched</dt><dd>${plan.launch_record_count || 0}</dd>`;
|
|
186
|
+
if (Object.keys(statusCounts).length > 0) {
|
|
187
|
+
const countsStr = Object.entries(statusCounts).map(([k, v]) => `${k}: ${v}`).join(', ');
|
|
188
|
+
html += `<dt>Status Breakdown</dt><dd>${esc(countsStr)}</dd>`;
|
|
189
|
+
}
|
|
190
|
+
if (plan.supersedes_plan_id) {
|
|
191
|
+
html += `<dt>Supersedes</dt><dd class="mono">${esc(truncateId(plan.supersedes_plan_id, 30))}</dd>`;
|
|
192
|
+
}
|
|
193
|
+
html += `</dl></div>`;
|
|
194
|
+
|
|
195
|
+
// Workstreams table
|
|
196
|
+
if (workstreams.length > 0) {
|
|
197
|
+
html += `<div class="section"><h3>Workstreams</h3>
|
|
198
|
+
<table class="data-table">
|
|
199
|
+
<thead>
|
|
200
|
+
<tr>
|
|
201
|
+
<th>#</th>
|
|
202
|
+
<th>ID</th>
|
|
203
|
+
<th>Title</th>
|
|
204
|
+
<th>Status</th>
|
|
205
|
+
<th>Roles</th>
|
|
206
|
+
<th>Phases</th>
|
|
207
|
+
<th>Dependencies</th>
|
|
208
|
+
</tr>
|
|
209
|
+
</thead>
|
|
210
|
+
<tbody>`;
|
|
211
|
+
|
|
212
|
+
workstreams.forEach((ws, index) => {
|
|
213
|
+
const roles = Array.isArray(ws.roles) ? ws.roles.join(', ') : '—';
|
|
214
|
+
const phases = Array.isArray(ws.phases) ? ws.phases.join(', ') : '—';
|
|
215
|
+
const deps = Array.isArray(ws.depends_on) && ws.depends_on.length > 0 ? ws.depends_on.join(', ') : 'none';
|
|
216
|
+
|
|
217
|
+
html += `<tr>
|
|
218
|
+
<td style="color:var(--text-dim)">${index + 1}</td>
|
|
219
|
+
<td class="mono">${esc(ws.workstream_id || '—')}</td>
|
|
220
|
+
<td>${esc(ws.title || '—')}</td>
|
|
221
|
+
<td>${formatLaunchStatus(ws.launch_status)}</td>
|
|
222
|
+
<td>${esc(roles)}</td>
|
|
223
|
+
<td>${esc(phases)}</td>
|
|
224
|
+
<td class="mono">${esc(deps)}</td>
|
|
225
|
+
</tr>`;
|
|
226
|
+
});
|
|
227
|
+
|
|
228
|
+
html += '</tbody></table></div>';
|
|
229
|
+
}
|
|
230
|
+
|
|
231
|
+
// Launch records table
|
|
232
|
+
if (launchRecords.length > 0) {
|
|
233
|
+
html += `<div class="section"><h3>Launch Records</h3>
|
|
234
|
+
<table class="data-table">
|
|
235
|
+
<thead>
|
|
236
|
+
<tr>
|
|
237
|
+
<th>#</th>
|
|
238
|
+
<th>Workstream</th>
|
|
239
|
+
<th>Chain ID</th>
|
|
240
|
+
<th>Status</th>
|
|
241
|
+
<th>Terminal</th>
|
|
242
|
+
<th>Launched</th>
|
|
243
|
+
<th>Completed</th>
|
|
244
|
+
</tr>
|
|
245
|
+
</thead>
|
|
246
|
+
<tbody>`;
|
|
247
|
+
|
|
248
|
+
launchRecords.forEach((lr, index) => {
|
|
249
|
+
html += `<tr>
|
|
250
|
+
<td style="color:var(--text-dim)">${index + 1}</td>
|
|
251
|
+
<td class="mono">${esc(lr.workstream_id || '—')}</td>
|
|
252
|
+
<td class="mono" title="${esc(lr.chain_id || '')}">${esc(truncateId(lr.chain_id))}</td>
|
|
253
|
+
<td>${formatLaunchStatus(lr.status)}</td>
|
|
254
|
+
<td>${esc(lr.terminal_reason || '—')}</td>
|
|
255
|
+
<td>${formatDate(lr.launched_at)}</td>
|
|
256
|
+
<td>${formatDate(lr.completed_at)}</td>
|
|
257
|
+
</tr>`;
|
|
258
|
+
});
|
|
259
|
+
|
|
260
|
+
html += '</tbody></table></div>';
|
|
261
|
+
}
|
|
262
|
+
|
|
263
|
+
return html;
|
|
264
|
+
}
|
|
265
|
+
|
|
266
|
+
function renderRecentPlans(plans) {
|
|
267
|
+
if (!Array.isArray(plans) || plans.length <= 1) {
|
|
268
|
+
return '';
|
|
269
|
+
}
|
|
270
|
+
|
|
271
|
+
// Skip the first (latest) since it's rendered in detail above
|
|
272
|
+
const olderPlans = plans.slice(1);
|
|
273
|
+
if (olderPlans.length === 0) return '';
|
|
274
|
+
|
|
275
|
+
let html = `<div class="section"><h3>Previous Plans</h3>
|
|
276
|
+
<table class="data-table">
|
|
277
|
+
<thead>
|
|
278
|
+
<tr>
|
|
279
|
+
<th>#</th>
|
|
280
|
+
<th>Plan ID</th>
|
|
281
|
+
<th>Mission</th>
|
|
282
|
+
<th>Status</th>
|
|
283
|
+
<th>Workstreams</th>
|
|
284
|
+
<th>Launched</th>
|
|
285
|
+
<th>Created</th>
|
|
286
|
+
</tr>
|
|
287
|
+
</thead>
|
|
288
|
+
<tbody>`;
|
|
289
|
+
|
|
290
|
+
olderPlans.forEach((plan, index) => {
|
|
291
|
+
html += `<tr>
|
|
292
|
+
<td style="color:var(--text-dim)">${index + 1}</td>
|
|
293
|
+
<td class="mono" title="${esc(plan.plan_id || '')}">${esc(truncateId(plan.plan_id, 20))}</td>
|
|
294
|
+
<td class="mono">${esc(truncateId(plan.mission_id, 18))}</td>
|
|
295
|
+
<td>${formatPlanStatus(plan.status)}</td>
|
|
296
|
+
<td>${plan.workstream_count || 0}</td>
|
|
297
|
+
<td>${plan.launch_record_count || 0}</td>
|
|
298
|
+
<td>${formatDate(plan.created_at)}</td>
|
|
299
|
+
</tr>`;
|
|
300
|
+
});
|
|
301
|
+
|
|
302
|
+
html += '</tbody></table></div>';
|
|
303
|
+
return html;
|
|
304
|
+
}
|
|
305
|
+
|
|
306
|
+
export function render({ missions, plans }) {
|
|
136
307
|
if (!missions || typeof missions !== 'object') {
|
|
137
308
|
return `<div class="placeholder"><h2>Mission</h2><p>No mission data available. Create a mission and bind a chain to populate this view.</p></div>`;
|
|
138
309
|
}
|
|
@@ -171,6 +342,14 @@ export function render({ missions }) {
|
|
|
171
342
|
}
|
|
172
343
|
|
|
173
344
|
html += renderAttachedChains(latest);
|
|
345
|
+
|
|
346
|
+
// Plan visibility
|
|
347
|
+
const planData = plans && typeof plans === 'object' ? plans : null;
|
|
348
|
+
const latestPlan = planData?.latest || null;
|
|
349
|
+
const allPlans = Array.isArray(planData?.plans) ? planData.plans : [];
|
|
350
|
+
html += renderLatestPlan(latestPlan);
|
|
351
|
+
html += renderRecentPlans(allPlans);
|
|
352
|
+
|
|
174
353
|
html += renderRecentMissions(missionList);
|
|
175
354
|
html += '</div>';
|
|
176
355
|
return html;
|