agentxchain 0.8.7 → 2.1.1
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 +123 -154
- package/bin/agentxchain.js +240 -8
- package/dashboard/app.js +305 -0
- package/dashboard/components/blocked.js +145 -0
- package/dashboard/components/cross-repo.js +126 -0
- package/dashboard/components/gate.js +311 -0
- package/dashboard/components/hooks.js +177 -0
- package/dashboard/components/initiative.js +147 -0
- package/dashboard/components/ledger.js +165 -0
- package/dashboard/components/timeline.js +222 -0
- package/dashboard/index.html +352 -0
- package/package.json +16 -7
- package/scripts/agentxchain-autonudge.applescript +32 -5
- package/scripts/live-api-proxy-preflight-smoke.sh +531 -0
- package/scripts/publish-from-tag.sh +88 -0
- package/scripts/release-postflight.sh +231 -0
- package/scripts/release-preflight.sh +167 -0
- package/scripts/run-autonudge.sh +1 -1
- package/src/adapters/claude-code.js +7 -14
- package/src/adapters/cursor-local.js +17 -16
- package/src/commands/accept-turn.js +160 -0
- package/src/commands/approve-completion.js +80 -0
- package/src/commands/approve-transition.js +85 -0
- package/src/commands/branch.js +2 -2
- package/src/commands/claim.js +84 -9
- package/src/commands/config.js +16 -0
- package/src/commands/dashboard.js +70 -0
- package/src/commands/doctor.js +9 -1
- package/src/commands/init.js +540 -5
- package/src/commands/migrate.js +348 -0
- package/src/commands/multi.js +549 -0
- package/src/commands/plugin.js +157 -0
- package/src/commands/reject-turn.js +204 -0
- package/src/commands/resume.js +389 -0
- package/src/commands/status.js +196 -3
- package/src/commands/step.js +947 -0
- package/src/commands/stop.js +65 -33
- package/src/commands/template-list.js +33 -0
- package/src/commands/template-set.js +279 -0
- package/src/commands/update.js +24 -3
- package/src/commands/validate.js +20 -11
- package/src/commands/verify.js +71 -0
- package/src/commands/watch.js +112 -25
- package/src/lib/adapters/api-proxy-adapter.js +1076 -0
- package/src/lib/adapters/local-cli-adapter.js +337 -0
- package/src/lib/adapters/manual-adapter.js +169 -0
- package/src/lib/blocked-state.js +94 -0
- package/src/lib/config.js +143 -12
- package/src/lib/context-compressor.js +121 -0
- package/src/lib/context-section-parser.js +220 -0
- package/src/lib/coordinator-acceptance.js +428 -0
- package/src/lib/coordinator-config.js +461 -0
- package/src/lib/coordinator-dispatch.js +276 -0
- package/src/lib/coordinator-gates.js +487 -0
- package/src/lib/coordinator-hooks.js +239 -0
- package/src/lib/coordinator-recovery.js +523 -0
- package/src/lib/coordinator-state.js +365 -0
- package/src/lib/cross-repo-context.js +247 -0
- package/src/lib/dashboard/bridge-server.js +284 -0
- package/src/lib/dashboard/file-watcher.js +93 -0
- package/src/lib/dashboard/state-reader.js +96 -0
- package/src/lib/dispatch-bundle.js +568 -0
- package/src/lib/dispatch-manifest.js +252 -0
- package/src/lib/filter-agents.js +12 -0
- package/src/lib/gate-evaluator.js +285 -0
- package/src/lib/generate-vscode.js +158 -68
- package/src/lib/governed-state.js +2139 -0
- package/src/lib/governed-templates.js +145 -0
- package/src/lib/hook-runner.js +788 -0
- package/src/lib/next-owner.js +61 -6
- package/src/lib/normalized-config.js +539 -0
- package/src/lib/notify.js +14 -12
- package/src/lib/plugin-config-schema.js +192 -0
- package/src/lib/plugins.js +692 -0
- package/src/lib/prompt-core.js +108 -0
- package/src/lib/protocol-conformance.js +291 -0
- package/src/lib/reference-conformance-adapter.js +717 -0
- package/src/lib/repo-observer.js +597 -0
- package/src/lib/repo.js +0 -31
- package/src/lib/safe-write.js +44 -0
- package/src/lib/schema.js +189 -0
- package/src/lib/schemas/turn-result.schema.json +205 -0
- package/src/lib/seed-prompt-polling.js +15 -73
- package/src/lib/seed-prompt.js +17 -63
- package/src/lib/token-budget.js +206 -0
- package/src/lib/token-counter.js +27 -0
- package/src/lib/turn-paths.js +67 -0
- package/src/lib/turn-result-validator.js +496 -0
- package/src/lib/validation.js +167 -19
- package/src/lib/verify-command.js +72 -0
- package/src/templates/governed/api-service.json +31 -0
- package/src/templates/governed/cli-tool.json +30 -0
- package/src/templates/governed/generic.json +10 -0
- package/src/templates/governed/web-app.json +30 -0
|
@@ -20,11 +20,13 @@ export function generateVSCodeFiles(dir, config) {
|
|
|
20
20
|
}
|
|
21
21
|
|
|
22
22
|
writeFileSync(join(hooksDir, 'agentxchain.json'), buildHooksJson());
|
|
23
|
+
writeFileSync(join(scriptsDir, 'agentxchain-hook-runtime.cjs'), buildHookRuntimeScript());
|
|
23
24
|
writeFileSync(join(scriptsDir, 'agentxchain-session-start.sh'), SESSION_START_SCRIPT);
|
|
24
|
-
writeFileSync(join(scriptsDir, 'agentxchain-stop.sh'), buildStopScript(
|
|
25
|
+
writeFileSync(join(scriptsDir, 'agentxchain-stop.sh'), buildStopScript());
|
|
25
26
|
writeFileSync(join(scriptsDir, 'agentxchain-pre-tool.sh'), PRE_TOOL_SCRIPT);
|
|
26
27
|
|
|
27
28
|
try {
|
|
29
|
+
chmodSync(join(scriptsDir, 'agentxchain-hook-runtime.cjs'), 0o755);
|
|
28
30
|
chmodSync(join(scriptsDir, 'agentxchain-session-start.sh'), 0o755);
|
|
29
31
|
chmodSync(join(scriptsDir, 'agentxchain-stop.sh'), 0o755);
|
|
30
32
|
chmodSync(join(scriptsDir, 'agentxchain-pre-tool.sh'), 0o755);
|
|
@@ -188,23 +190,150 @@ function buildHooksJson() {
|
|
|
188
190
|
return JSON.stringify(hooks, null, 2) + '\n';
|
|
189
191
|
}
|
|
190
192
|
|
|
191
|
-
function
|
|
192
|
-
|
|
193
|
-
|
|
194
|
-
|
|
195
|
-
|
|
196
|
-
|
|
197
|
-
|
|
198
|
-
|
|
199
|
-
|
|
200
|
-
|
|
201
|
-
|
|
202
|
-
|
|
203
|
-
|
|
204
|
-
|
|
205
|
-
|
|
206
|
-
|
|
193
|
+
function buildHookRuntimeScript() {
|
|
194
|
+
return `#!/usr/bin/env node
|
|
195
|
+
const fs = require('fs');
|
|
196
|
+
const path = require('path');
|
|
197
|
+
const { spawnSync } = require('child_process');
|
|
198
|
+
|
|
199
|
+
function readJson(file) {
|
|
200
|
+
try {
|
|
201
|
+
return JSON.parse(fs.readFileSync(path.join(process.cwd(), file), 'utf8'));
|
|
202
|
+
} catch {
|
|
203
|
+
return null;
|
|
204
|
+
}
|
|
205
|
+
}
|
|
206
|
+
|
|
207
|
+
function splitCommand(input) {
|
|
208
|
+
const out = [];
|
|
209
|
+
let current = '';
|
|
210
|
+
let quote = null;
|
|
211
|
+
let escape = false;
|
|
212
|
+
for (const char of String(input || '')) {
|
|
213
|
+
if (escape) {
|
|
214
|
+
current += char;
|
|
215
|
+
escape = false;
|
|
216
|
+
continue;
|
|
217
|
+
}
|
|
218
|
+
if (char === '\\\\') {
|
|
219
|
+
escape = true;
|
|
220
|
+
continue;
|
|
221
|
+
}
|
|
222
|
+
if (quote) {
|
|
223
|
+
if (char === quote) {
|
|
224
|
+
quote = null;
|
|
225
|
+
} else {
|
|
226
|
+
current += char;
|
|
227
|
+
}
|
|
228
|
+
continue;
|
|
229
|
+
}
|
|
230
|
+
if (char === '"' || char === "'") {
|
|
231
|
+
quote = char;
|
|
232
|
+
continue;
|
|
233
|
+
}
|
|
234
|
+
if (/\\s/.test(char)) {
|
|
235
|
+
if (current) {
|
|
236
|
+
out.push(current);
|
|
237
|
+
current = '';
|
|
238
|
+
}
|
|
239
|
+
continue;
|
|
240
|
+
}
|
|
241
|
+
current += char;
|
|
242
|
+
}
|
|
243
|
+
if (current) out.push(current);
|
|
244
|
+
return out;
|
|
245
|
+
}
|
|
246
|
+
|
|
247
|
+
function normalizeVerifyCommand(config) {
|
|
248
|
+
const raw = config?.rules?.verify_command;
|
|
249
|
+
if (Array.isArray(raw) && raw.every(part => typeof part === 'string' && part.length > 0)) {
|
|
250
|
+
return raw;
|
|
251
|
+
}
|
|
252
|
+
if (typeof raw !== 'string' || !raw.trim()) return null;
|
|
253
|
+
return splitCommand(raw.trim());
|
|
254
|
+
}
|
|
255
|
+
|
|
256
|
+
function normalizeAgentId(raw) {
|
|
257
|
+
if (!raw) return null;
|
|
258
|
+
let value = String(raw).trim();
|
|
259
|
+
value = value.replace(/[\`*_]/g, '').trim();
|
|
260
|
+
value = value.replace(/\\(.*?\\)/g, '').trim();
|
|
261
|
+
value = value.split(/[\\s,]+/)[0];
|
|
262
|
+
value = value.toLowerCase();
|
|
263
|
+
return /^[a-z0-9_-]+$/.test(value) ? value : null;
|
|
264
|
+
}
|
|
265
|
+
|
|
266
|
+
function parseNextOwnerFromTalk(talkPath, validAgentIds) {
|
|
267
|
+
try {
|
|
268
|
+
const text = fs.readFileSync(path.join(process.cwd(), talkPath), 'utf8');
|
|
269
|
+
const lines = text.split(/\\r?\\n/);
|
|
270
|
+
for (let i = lines.length - 1; i >= 0; i -= 1) {
|
|
271
|
+
const line = lines[i].trim();
|
|
272
|
+
const match = line.match(/^(?:-|\\*)?\\s*\\**next\\s*owner\\**\\s*:\\s*(.+)$/i);
|
|
273
|
+
if (!match) continue;
|
|
274
|
+
const candidate = normalizeAgentId(match[1]);
|
|
275
|
+
if (candidate && validAgentIds.includes(candidate)) return candidate;
|
|
276
|
+
}
|
|
277
|
+
} catch {}
|
|
278
|
+
return null;
|
|
279
|
+
}
|
|
280
|
+
|
|
281
|
+
function resolveNextAgent(config, lock) {
|
|
282
|
+
const agentIds = Object.keys(config?.agents || {});
|
|
283
|
+
if (agentIds.length === 0) return null;
|
|
284
|
+
const talkFile = config?.talk_file || 'TALK.md';
|
|
285
|
+
const fromTalk = parseNextOwnerFromTalk(talkFile, agentIds);
|
|
286
|
+
if (fromTalk) return fromTalk;
|
|
287
|
+
const last = lock?.last_released_by;
|
|
288
|
+
if (last && agentIds.includes(last)) {
|
|
289
|
+
const idx = agentIds.indexOf(last);
|
|
290
|
+
return agentIds[(idx + 1) % agentIds.length];
|
|
291
|
+
}
|
|
292
|
+
return agentIds[0];
|
|
293
|
+
}
|
|
294
|
+
|
|
295
|
+
function outputJson(value) {
|
|
296
|
+
process.stdout.write(JSON.stringify(value));
|
|
297
|
+
}
|
|
298
|
+
|
|
299
|
+
const command = process.argv[2];
|
|
300
|
+
const config = readJson('agentxchain.json');
|
|
301
|
+
const lock = readJson('lock.json');
|
|
302
|
+
const state = readJson('state.json');
|
|
303
|
+
|
|
304
|
+
if (command === 'verify') {
|
|
305
|
+
const verifyArgs = normalizeVerifyCommand(config);
|
|
306
|
+
if (!verifyArgs || verifyArgs.length === 0) process.exit(0);
|
|
307
|
+
const result = spawnSync(verifyArgs[0], verifyArgs.slice(1), { stdio: 'ignore', cwd: process.cwd() });
|
|
308
|
+
process.exit(result.status === 0 ? 0 : 2);
|
|
309
|
+
}
|
|
310
|
+
|
|
311
|
+
if (command === 'next') {
|
|
312
|
+
process.stdout.write(resolveNextAgent(config, lock) || '');
|
|
313
|
+
process.exit(0);
|
|
314
|
+
}
|
|
315
|
+
|
|
316
|
+
if (command === 'next-name') {
|
|
317
|
+
const next = resolveNextAgent(config, lock);
|
|
318
|
+
process.stdout.write(next ? (config?.agents?.[next]?.name || next) : '');
|
|
319
|
+
process.exit(0);
|
|
320
|
+
}
|
|
321
|
+
|
|
322
|
+
if (command === 'session-start') {
|
|
323
|
+
if (!lock || !state) {
|
|
324
|
+
outputJson({ continue: true });
|
|
325
|
+
process.exit(0);
|
|
326
|
+
}
|
|
327
|
+
const context = \`AgentXchain context: Project=\${state.project || 'unknown'} | Phase=\${state.phase || 'unknown'} | Turn=\${lock.turn_number ?? 0} | Lock=\${lock.holder ?? 'none'} | Last released by=\${lock.last_released_by ?? 'none'} | Blocked=\${state.blocked ?? false}\`;
|
|
328
|
+
outputJson({ hookSpecificOutput: { hookEventName: 'SessionStart', additionalContext: context } });
|
|
329
|
+
process.exit(0);
|
|
330
|
+
}
|
|
331
|
+
|
|
332
|
+
outputJson({ continue: true });
|
|
333
|
+
`;
|
|
334
|
+
}
|
|
207
335
|
|
|
336
|
+
function buildStopScript() {
|
|
208
337
|
return `#!/bin/bash
|
|
209
338
|
INPUT=$(cat)
|
|
210
339
|
STOP_HOOK_ACTIVE=$(echo "$INPUT" | jq -r '.stop_hook_active // false')
|
|
@@ -222,51 +351,28 @@ fi
|
|
|
222
351
|
LOCK=$(cat lock.json 2>/dev/null)
|
|
223
352
|
HOLDER=$(echo "$LOCK" | jq -r '.holder // empty')
|
|
224
353
|
TURN=$(echo "$LOCK" | jq -r '.turn_number // 0')
|
|
225
|
-
${verifyBlock}
|
|
226
354
|
if [ -z "$HOLDER" ] || [ "$HOLDER" = "null" ]; then
|
|
227
|
-
|
|
355
|
+
node "./scripts/agentxchain-hook-runtime.cjs" verify >/dev/null 2>&1
|
|
356
|
+
VERIFY_STATUS=$?
|
|
357
|
+
if [ "$VERIFY_STATUS" -eq 2 ]; then
|
|
358
|
+
echo '{"hookSpecificOutput":{"hookEventName":"Stop","decision":"block","reason":"Verification failed. Fix the issue and release the lock."}}'
|
|
359
|
+
exit 0
|
|
360
|
+
fi
|
|
361
|
+
fi
|
|
228
362
|
|
|
363
|
+
if [ -z "$HOLDER" ] || [ "$HOLDER" = "null" ]; then
|
|
229
364
|
if [ ! -f "agentxchain.json" ]; then
|
|
230
365
|
echo '{"continue":true}'
|
|
231
366
|
exit 0
|
|
232
367
|
fi
|
|
233
368
|
|
|
234
|
-
NEXT=$(node -
|
|
235
|
-
const fs = require('fs');
|
|
236
|
-
const cfg = JSON.parse(fs.readFileSync('agentxchain.json','utf8'));
|
|
237
|
-
const ids = Object.keys(cfg.agents || {});
|
|
238
|
-
const lock = JSON.parse(fs.readFileSync('lock.json','utf8'));
|
|
239
|
-
const talkFile = cfg.talk_file || 'TALK.md';
|
|
240
|
-
let fromTalk = '';
|
|
241
|
-
try {
|
|
242
|
-
const talk = fs.readFileSync(talkFile, 'utf8').split(/\\r?\\n/);
|
|
243
|
-
for (let i = talk.length - 1; i >= 0; i -= 1) {
|
|
244
|
-
const m = talk[i].trim().match(/^(?:-|\\*)?\\s*\\**next\\s*owner\\**\\s*:\\s*(.+)$/i);
|
|
245
|
-
if (!m) continue;
|
|
246
|
-
let candidate = String(m[1] || '').replace(/[\\*_]/g, '').replace(/\\(.*?\\)/g, '').trim().split(/[\\s,]+/)[0].toLowerCase();
|
|
247
|
-
if (ids.includes(candidate)) { fromTalk = candidate; break; }
|
|
248
|
-
}
|
|
249
|
-
} catch {}
|
|
250
|
-
if (fromTalk) {
|
|
251
|
-
process.stdout.write(fromTalk);
|
|
252
|
-
process.exit(0);
|
|
253
|
-
}
|
|
254
|
-
const last = lock.last_released_by || '';
|
|
255
|
-
const idx = ids.indexOf(last);
|
|
256
|
-
const next = idx >= 0 ? ids[(idx + 1) % ids.length] : ids[0];
|
|
257
|
-
process.stdout.write(next || '');
|
|
258
|
-
" -- "$LAST" 2>/dev/null)
|
|
259
|
-
|
|
369
|
+
NEXT=$(node "./scripts/agentxchain-hook-runtime.cjs" next 2>/dev/null)
|
|
260
370
|
if [ -z "$NEXT" ]; then
|
|
261
371
|
echo '{"continue":true}'
|
|
262
372
|
exit 0
|
|
263
373
|
fi
|
|
264
374
|
|
|
265
|
-
NEXT_NAME=$(node -
|
|
266
|
-
const cfg = JSON.parse(require('fs').readFileSync('agentxchain.json','utf8'));
|
|
267
|
-
const a = cfg.agents[process.argv[1]];
|
|
268
|
-
process.stdout.write(a ? a.name : process.argv[1]);
|
|
269
|
-
" -- "$NEXT" 2>/dev/null)
|
|
375
|
+
NEXT_NAME=$(node "./scripts/agentxchain-hook-runtime.cjs" next-name 2>/dev/null)
|
|
270
376
|
|
|
271
377
|
echo "{\\"hookSpecificOutput\\":{\\"hookEventName\\":\\"Stop\\",\\"decision\\":\\"block\\",\\"reason\\":\\"Turn $TURN complete. Next agent: $NEXT ($NEXT_NAME). Read lock.json, claim it, and do your work.\\"}}"
|
|
272
378
|
elif [ "$HOLDER" = "human" ]; then
|
|
@@ -278,24 +384,8 @@ fi
|
|
|
278
384
|
}
|
|
279
385
|
|
|
280
386
|
const SESSION_START_SCRIPT = `#!/bin/bash
|
|
281
|
-
|
|
282
|
-
|
|
283
|
-
exit 0
|
|
284
|
-
fi
|
|
285
|
-
|
|
286
|
-
LOCK=$(cat lock.json 2>/dev/null)
|
|
287
|
-
STATE=$(cat state.json 2>/dev/null)
|
|
288
|
-
|
|
289
|
-
HOLDER=$(echo "$LOCK" | jq -r '.holder // "none"')
|
|
290
|
-
TURN=$(echo "$LOCK" | jq -r '.turn_number // 0')
|
|
291
|
-
LAST=$(echo "$LOCK" | jq -r '.last_released_by // "none"')
|
|
292
|
-
PHASE=$(echo "$STATE" | jq -r '.phase // "unknown"')
|
|
293
|
-
BLOCKED=$(echo "$STATE" | jq -r '.blocked // false')
|
|
294
|
-
PROJECT=$(echo "$STATE" | jq -r '.project // "unknown"')
|
|
295
|
-
|
|
296
|
-
CONTEXT="AgentXchain context: Project=$PROJECT | Phase=$PHASE | Turn=$TURN | Lock=$HOLDER | Last released by=$LAST | Blocked=$BLOCKED"
|
|
297
|
-
|
|
298
|
-
echo "{\\"hookSpecificOutput\\":{\\"hookEventName\\":\\"SessionStart\\",\\"additionalContext\\":\\"$CONTEXT\\"}}"
|
|
387
|
+
CONTEXT_JSON=$(node "./scripts/agentxchain-hook-runtime.cjs" session-start 2>/dev/null || echo '{"continue":true}')
|
|
388
|
+
printf '%s\n' "$CONTEXT_JSON"
|
|
299
389
|
`;
|
|
300
390
|
|
|
301
391
|
const PRE_TOOL_SCRIPT = `#!/bin/bash
|