agentxchain 2.133.0 → 2.134.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/package.json
CHANGED
|
@@ -2,8 +2,9 @@
|
|
|
2
2
|
# Release postflight — run this after publish succeeds.
|
|
3
3
|
# Verifies: release tag exists, npm registry serves the version, metadata is present,
|
|
4
4
|
# the published package resolves through npx, the published package can execute its
|
|
5
|
-
# CLI entrypoint from the tarball,
|
|
6
|
-
# clean consumer project
|
|
5
|
+
# CLI entrypoint from the tarball, runner package exports are importable in a
|
|
6
|
+
# clean consumer project, and the installed CLI can scaffold and validate a fresh
|
|
7
|
+
# governed workspace.
|
|
7
8
|
# Usage: bash scripts/release-postflight.sh --target-version <semver> [--tag vX.Y.Z]
|
|
8
9
|
set -uo pipefail
|
|
9
10
|
|
|
@@ -260,6 +261,64 @@ EOF
|
|
|
260
261
|
return "$node_status"
|
|
261
262
|
}
|
|
262
263
|
|
|
264
|
+
run_operator_frontdoor_smoke() {
|
|
265
|
+
if [[ -z "$TARBALL_URL" ]]; then
|
|
266
|
+
echo "registry tarball metadata unavailable for operator front-door smoke" >&2
|
|
267
|
+
return 1
|
|
268
|
+
fi
|
|
269
|
+
|
|
270
|
+
local smoke_root
|
|
271
|
+
local workspace
|
|
272
|
+
local bin_path
|
|
273
|
+
local smoke_npmrc
|
|
274
|
+
local install_status
|
|
275
|
+
local init_output
|
|
276
|
+
local init_status
|
|
277
|
+
local validate_output
|
|
278
|
+
local validate_status
|
|
279
|
+
|
|
280
|
+
smoke_root="$(mktemp -d "${TMPDIR:-/tmp}/agentxchain-operator-postflight.XXXXXX")"
|
|
281
|
+
workspace="${smoke_root}/workspace"
|
|
282
|
+
bin_path="${smoke_root}/bin/${PACKAGE_BIN_NAME}"
|
|
283
|
+
smoke_npmrc="${smoke_root}/.npmrc"
|
|
284
|
+
echo "registry=https://registry.npmjs.org/" > "$smoke_npmrc"
|
|
285
|
+
|
|
286
|
+
env -u NODE_AUTH_TOKEN NPM_CONFIG_USERCONFIG="$smoke_npmrc" \
|
|
287
|
+
npm install --global --prefix "$smoke_root" "$TARBALL_URL" >/dev/null 2>&1
|
|
288
|
+
install_status=$?
|
|
289
|
+
if [[ "$install_status" -ne 0 ]]; then
|
|
290
|
+
rm -rf "$smoke_root"
|
|
291
|
+
return "$install_status"
|
|
292
|
+
fi
|
|
293
|
+
|
|
294
|
+
if [[ ! -x "$bin_path" ]]; then
|
|
295
|
+
echo "installed binary missing at ${bin_path}" >&2
|
|
296
|
+
rm -rf "$smoke_root"
|
|
297
|
+
return 1
|
|
298
|
+
fi
|
|
299
|
+
|
|
300
|
+
init_output="$(
|
|
301
|
+
env -u NODE_AUTH_TOKEN NPM_CONFIG_USERCONFIG="$smoke_npmrc" \
|
|
302
|
+
"$bin_path" init --governed --template cli-tool --goal "Release operator smoke" --dir "$workspace" -y 2>&1
|
|
303
|
+
)"
|
|
304
|
+
init_status=$?
|
|
305
|
+
if [[ "$init_status" -ne 0 ]]; then
|
|
306
|
+
printf '%s\n' "$init_output"
|
|
307
|
+
rm -rf "$smoke_root"
|
|
308
|
+
return "$init_status"
|
|
309
|
+
fi
|
|
310
|
+
|
|
311
|
+
validate_output="$(
|
|
312
|
+
cd "$workspace" || exit 1
|
|
313
|
+
env -u NODE_AUTH_TOKEN NPM_CONFIG_USERCONFIG="$smoke_npmrc" \
|
|
314
|
+
"$bin_path" validate --mode kickoff --json 2>&1
|
|
315
|
+
)"
|
|
316
|
+
validate_status=$?
|
|
317
|
+
printf '%s\n' "$validate_output"
|
|
318
|
+
rm -rf "$smoke_root"
|
|
319
|
+
return "$validate_status"
|
|
320
|
+
}
|
|
321
|
+
|
|
263
322
|
run_with_retry() {
|
|
264
323
|
local __output_var="$1"
|
|
265
324
|
local description="$2"
|
|
@@ -313,17 +372,17 @@ run_with_retry() {
|
|
|
313
372
|
|
|
314
373
|
echo "AgentXchain v${TARGET_VERSION} Release Postflight"
|
|
315
374
|
echo "====================================="
|
|
316
|
-
echo "Checks release truth after publish: tag, registry visibility, metadata, npx smoke, CLI install smoke,
|
|
375
|
+
echo "Checks release truth after publish: tag, registry visibility, metadata, npx smoke, CLI install smoke, package export smoke, and operator front-door smoke."
|
|
317
376
|
echo ""
|
|
318
377
|
|
|
319
|
-
echo "[1/
|
|
378
|
+
echo "[1/8] Git tag"
|
|
320
379
|
if git rev-parse -q --verify "refs/tags/${TAG}" >/dev/null 2>&1; then
|
|
321
380
|
pass "Git tag ${TAG} exists locally"
|
|
322
381
|
else
|
|
323
382
|
fail "Git tag ${TAG} is missing locally"
|
|
324
383
|
fi
|
|
325
384
|
|
|
326
|
-
echo "[2/
|
|
385
|
+
echo "[2/8] Registry version"
|
|
327
386
|
if run_with_retry VERSION_OUTPUT "registry version" equals "${TARGET_VERSION}" npm view "${PACKAGE_NAME}@${TARGET_VERSION}" version; then
|
|
328
387
|
PUBLISHED_VERSION="$(trim_last_line "$VERSION_OUTPUT")"
|
|
329
388
|
if [[ "$PUBLISHED_VERSION" == "$TARGET_VERSION" ]]; then
|
|
@@ -336,7 +395,7 @@ else
|
|
|
336
395
|
printf '%s\n' "$VERSION_OUTPUT" | tail -20
|
|
337
396
|
fi
|
|
338
397
|
|
|
339
|
-
echo "[3/
|
|
398
|
+
echo "[3/8] Registry tarball metadata"
|
|
340
399
|
if run_with_retry TARBALL_OUTPUT "registry tarball metadata" nonempty "" npm view "${PACKAGE_NAME}@${TARGET_VERSION}" dist.tarball; then
|
|
341
400
|
TARBALL_URL="$(trim_last_line "$TARBALL_OUTPUT")"
|
|
342
401
|
if [[ -n "$TARBALL_URL" ]]; then
|
|
@@ -349,7 +408,7 @@ else
|
|
|
349
408
|
printf '%s\n' "$TARBALL_OUTPUT" | tail -20
|
|
350
409
|
fi
|
|
351
410
|
|
|
352
|
-
echo "[4/
|
|
411
|
+
echo "[4/8] Registry checksum metadata"
|
|
353
412
|
if run_with_retry INTEGRITY_OUTPUT "registry checksum metadata" nonempty "" npm view "${PACKAGE_NAME}@${TARGET_VERSION}" dist.integrity; then
|
|
354
413
|
REGISTRY_CHECKSUM="$(trim_last_line "$INTEGRITY_OUTPUT")"
|
|
355
414
|
fi
|
|
@@ -364,7 +423,7 @@ else
|
|
|
364
423
|
fail "registry did not return checksum metadata"
|
|
365
424
|
fi
|
|
366
425
|
|
|
367
|
-
echo "[5/
|
|
426
|
+
echo "[5/8] npx smoke"
|
|
368
427
|
if run_with_retry NPX_OUTPUT "npx smoke" nonempty "" run_npx_smoke; then
|
|
369
428
|
NPX_VERSION="$(extract_matching_line "$NPX_OUTPUT" "$TARGET_VERSION" 2>/dev/null || trim_last_line "$NPX_OUTPUT")"
|
|
370
429
|
if [[ "$NPX_VERSION" == "$TARGET_VERSION" ]]; then
|
|
@@ -377,7 +436,7 @@ else
|
|
|
377
436
|
printf '%s\n' "$NPX_OUTPUT" | tail -20
|
|
378
437
|
fi
|
|
379
438
|
|
|
380
|
-
echo "[6/
|
|
439
|
+
echo "[6/8] Install smoke"
|
|
381
440
|
if run_with_retry EXEC_OUTPUT "install smoke" nonempty "" run_install_smoke; then
|
|
382
441
|
EXEC_VERSION="$(trim_last_line "$EXEC_OUTPUT")"
|
|
383
442
|
if [[ "$EXEC_VERSION" == "$TARGET_VERSION" ]]; then
|
|
@@ -390,7 +449,7 @@ else
|
|
|
390
449
|
printf '%s\n' "$EXEC_OUTPUT" | tail -20
|
|
391
450
|
fi
|
|
392
451
|
|
|
393
|
-
echo "[7/
|
|
452
|
+
echo "[7/8] Package export smoke"
|
|
394
453
|
if run_with_retry RUNNER_EXPORT_OUTPUT "runner export smoke" nonempty "" run_runner_export_smoke; then
|
|
395
454
|
RUNNER_EXPORT_JSON="$(trim_last_line "$RUNNER_EXPORT_OUTPUT")"
|
|
396
455
|
RUNNER_EXPORT_VERSION="$(printf '%s' "$RUNNER_EXPORT_JSON" | node --input-type=module -e "process.stdin.setEncoding('utf8'); let raw=''; process.stdin.on('data', (chunk) => raw += chunk); process.stdin.on('end', () => { const parsed = JSON.parse(raw); console.log(parsed.runner_interface_version || ''); });")"
|
|
@@ -410,6 +469,20 @@ else
|
|
|
410
469
|
printf '%s\n' "$RUNNER_EXPORT_OUTPUT" | tail -20
|
|
411
470
|
fi
|
|
412
471
|
|
|
472
|
+
echo "[8/8] Operator front-door smoke"
|
|
473
|
+
if run_with_retry OPERATOR_OUTPUT "operator front-door smoke" nonempty "" run_operator_frontdoor_smoke; then
|
|
474
|
+
OPERATOR_OK="$(printf '%s' "$OPERATOR_OUTPUT" | node --input-type=module -e "process.stdin.setEncoding('utf8'); let raw=''; process.stdin.on('data', (chunk) => raw += chunk); process.stdin.on('end', () => { const parsed = JSON.parse(raw); console.log(parsed.ok ? 'true' : 'false'); });" 2>/dev/null || true)"
|
|
475
|
+
OPERATOR_PROTOCOL_MODE="$(printf '%s' "$OPERATOR_OUTPUT" | node --input-type=module -e "process.stdin.setEncoding('utf8'); let raw=''; process.stdin.on('data', (chunk) => raw += chunk); process.stdin.on('end', () => { const parsed = JSON.parse(raw); console.log(parsed.protocol_mode || ''); });" 2>/dev/null || true)"
|
|
476
|
+
if [[ "$OPERATOR_OK" == "true" && "$OPERATOR_PROTOCOL_MODE" == "governed" ]]; then
|
|
477
|
+
pass "published CLI scaffolds and validates a governed workspace"
|
|
478
|
+
else
|
|
479
|
+
fail "published operator front-door smoke did not validate a governed workspace"
|
|
480
|
+
fi
|
|
481
|
+
else
|
|
482
|
+
fail "published operator front-door smoke failed"
|
|
483
|
+
printf '%s\n' "$OPERATOR_OUTPUT" | tail -20
|
|
484
|
+
fi
|
|
485
|
+
|
|
413
486
|
echo ""
|
|
414
487
|
echo "====================================="
|
|
415
488
|
echo "Results: ${PASS} passed, ${FAIL} failed"
|
package/src/commands/mission.js
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import chalk from 'chalk';
|
|
2
2
|
import { readFileSync } from 'fs';
|
|
3
|
-
import { resolve } from 'path';
|
|
3
|
+
import { join, resolve } from 'path';
|
|
4
4
|
import { findProjectRoot, loadProjectContext } from '../lib/config.js';
|
|
5
5
|
import {
|
|
6
6
|
attachChainToMission,
|
|
@@ -37,6 +37,7 @@ import { executeChainedRun } from '../lib/run-chain.js';
|
|
|
37
37
|
import { executeGovernedRun } from './run.js';
|
|
38
38
|
import { dispatchCoordinatorTurn, selectAssignmentForWorkstream } from '../lib/coordinator-dispatch.js';
|
|
39
39
|
import { loadCoordinatorState } from '../lib/coordinator-state.js';
|
|
40
|
+
import { projectRepoAcceptance } from '../lib/coordinator-acceptance.js';
|
|
40
41
|
|
|
41
42
|
export async function missionStartCommand(opts) {
|
|
42
43
|
const root = findProjectRoot(opts.dir || process.cwd());
|
|
@@ -144,6 +145,43 @@ export async function missionStartCommand(opts) {
|
|
|
144
145
|
renderMissionSnapshot(snapshot);
|
|
145
146
|
}
|
|
146
147
|
|
|
148
|
+
function loadAcceptedRepoTurn(repoPath, turnId) {
|
|
149
|
+
try {
|
|
150
|
+
const historyPath = join(repoPath, '.agentxchain', 'history.jsonl');
|
|
151
|
+
const content = readFileSync(historyPath, 'utf8').trim();
|
|
152
|
+
if (!content) return null;
|
|
153
|
+
const entries = content
|
|
154
|
+
.split('\n')
|
|
155
|
+
.map((line) => JSON.parse(line))
|
|
156
|
+
.filter((entry) => entry?.turn_id === turnId)
|
|
157
|
+
.filter((entry) => Boolean(entry?.accepted_at) || entry?.status === 'accepted' || entry?.status === 'completed');
|
|
158
|
+
return entries.at(-1) || null;
|
|
159
|
+
} catch {
|
|
160
|
+
return null;
|
|
161
|
+
}
|
|
162
|
+
}
|
|
163
|
+
|
|
164
|
+
function projectAcceptedCoordinatorTurn(workspacePath, coordinatorConfig, repoId, turnId, workstreamId, fallbackState) {
|
|
165
|
+
const acceptedTurn = loadAcceptedRepoTurn(coordinatorConfig.repos?.[repoId]?.resolved_path, turnId);
|
|
166
|
+
if (!acceptedTurn) {
|
|
167
|
+
return { ok: false, error: `Accepted turn ${turnId} not found in repo-local history for ${repoId}.` };
|
|
168
|
+
}
|
|
169
|
+
|
|
170
|
+
const currentState = loadCoordinatorState(workspacePath) || fallbackState;
|
|
171
|
+
if (!currentState) {
|
|
172
|
+
return { ok: false, error: `Coordinator state not found at ${workspacePath}.` };
|
|
173
|
+
}
|
|
174
|
+
|
|
175
|
+
return projectRepoAcceptance(
|
|
176
|
+
workspacePath,
|
|
177
|
+
currentState,
|
|
178
|
+
coordinatorConfig,
|
|
179
|
+
repoId,
|
|
180
|
+
acceptedTurn,
|
|
181
|
+
workstreamId,
|
|
182
|
+
);
|
|
183
|
+
}
|
|
184
|
+
|
|
147
185
|
export async function missionListCommand(opts) {
|
|
148
186
|
const root = findProjectRoot(opts.dir || process.cwd());
|
|
149
187
|
if (!root) {
|
|
@@ -578,6 +616,7 @@ export async function missionPlanLaunchCommand(planTarget, opts) {
|
|
|
578
616
|
}
|
|
579
617
|
|
|
580
618
|
const executor = opts._executeGovernedRun || executeGovernedRun;
|
|
619
|
+
const childLog = opts.json ? noop : (opts._log || console.log);
|
|
581
620
|
const repoContext = loadProjectContext(retry.retryResult.repo_path);
|
|
582
621
|
if (!repoContext) {
|
|
583
622
|
console.error(chalk.red(`Cannot load project context for retried repo at ${retry.retryResult.repo_path}.`));
|
|
@@ -586,6 +625,7 @@ export async function missionPlanLaunchCommand(planTarget, opts) {
|
|
|
586
625
|
|
|
587
626
|
const runOpts = {
|
|
588
627
|
autoApprove: !!opts.autoApprove,
|
|
628
|
+
log: childLog,
|
|
589
629
|
provenance: {
|
|
590
630
|
trigger: 'manual',
|
|
591
631
|
created_by: 'operator',
|
|
@@ -619,6 +659,20 @@ export async function missionPlanLaunchCommand(planTarget, opts) {
|
|
|
619
659
|
process.exit(1);
|
|
620
660
|
}
|
|
621
661
|
|
|
662
|
+
if ((execution?.exitCode ?? 0) === 0) {
|
|
663
|
+
const projection = projectAcceptedCoordinatorTurn(
|
|
664
|
+
mission.coordinator.workspace_path,
|
|
665
|
+
coordinatorConfigResult.config,
|
|
666
|
+
retry.retryResult.repo_id,
|
|
667
|
+
retry.retryResult.reissued_turn_id,
|
|
668
|
+
opts.workstream,
|
|
669
|
+
loadCoordinatorState(mission.coordinator.workspace_path),
|
|
670
|
+
);
|
|
671
|
+
if (!projection.ok) {
|
|
672
|
+
console.error(chalk.yellow(`Coordinator retry projection warning: ${projection.error}`));
|
|
673
|
+
}
|
|
674
|
+
}
|
|
675
|
+
|
|
622
676
|
const syncedRetry = synchronizeCoordinatorPlanState(root, mission, retry.plan);
|
|
623
677
|
const retriedPlan = syncedRetry.ok ? syncedRetry.plan : retry.plan;
|
|
624
678
|
const retriedWorkstream = retriedPlan.workstreams.find((ws) => ws.workstream_id === opts.workstream);
|
|
@@ -1385,7 +1439,7 @@ async function dispatchAndExecuteCoordinatorWorkstream(
|
|
|
1385
1439
|
root, mission, plan, workstreamId, coordinatorConfig, coordinatorState, opts,
|
|
1386
1440
|
) {
|
|
1387
1441
|
const executor = opts._executeGovernedRun || executeGovernedRun;
|
|
1388
|
-
const logger = opts._log || console.log;
|
|
1442
|
+
const logger = opts.json ? noop : (opts._log || console.log);
|
|
1389
1443
|
|
|
1390
1444
|
// 1. Select assignment
|
|
1391
1445
|
const assignment = selectAssignmentForWorkstream(
|
|
@@ -1456,6 +1510,26 @@ async function dispatchAndExecuteCoordinatorWorkstream(
|
|
|
1456
1510
|
};
|
|
1457
1511
|
}
|
|
1458
1512
|
|
|
1513
|
+
if ((execution?.exitCode ?? 0) === 0) {
|
|
1514
|
+
const projection = projectAcceptedCoordinatorTurn(
|
|
1515
|
+
mission.coordinator.workspace_path,
|
|
1516
|
+
coordinatorConfig,
|
|
1517
|
+
dispatch.repo_id,
|
|
1518
|
+
dispatch.turn_id,
|
|
1519
|
+
workstreamId,
|
|
1520
|
+
loadCoordinatorState(mission.coordinator.workspace_path) || coordinatorState,
|
|
1521
|
+
);
|
|
1522
|
+
if (!projection.ok) {
|
|
1523
|
+
return {
|
|
1524
|
+
ok: false,
|
|
1525
|
+
status: 'projection_error',
|
|
1526
|
+
repo_id: dispatch.repo_id,
|
|
1527
|
+
turn_id: dispatch.turn_id,
|
|
1528
|
+
error: projection.error,
|
|
1529
|
+
};
|
|
1530
|
+
}
|
|
1531
|
+
}
|
|
1532
|
+
|
|
1459
1533
|
// 5. Sync plan state from coordinator (updates barriers, accepted repos, failures)
|
|
1460
1534
|
const synced = synchronizeCoordinatorPlanState(root, mission, launch.plan);
|
|
1461
1535
|
|
|
@@ -2285,3 +2359,5 @@ function formatTimestamp(value) {
|
|
|
2285
2359
|
function pad(value, width) {
|
|
2286
2360
|
return String(value).padEnd(width);
|
|
2287
2361
|
}
|
|
2362
|
+
|
|
2363
|
+
function noop() {}
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
import { appendFileSync, copyFileSync } from 'node:fs';
|
|
2
2
|
import { join } from 'node:path';
|
|
3
3
|
import { loadProjectContext, loadProjectState } from './config.js';
|
|
4
|
-
import { assignGovernedTurn, getActiveTurnCount } from './governed-state.js';
|
|
4
|
+
import { assignGovernedTurn, getActiveTurnCount, initializeGovernedRun } from './governed-state.js';
|
|
5
5
|
import { writeDispatchBundle } from './dispatch-bundle.js';
|
|
6
6
|
import { getDispatchTurnDir } from './turn-paths.js';
|
|
7
7
|
import { readBarriers, readCoordinatorHistory, recordCoordinatorDecision } from './coordinator-state.js';
|
|
@@ -253,6 +253,17 @@ export function dispatchCoordinatorTurn(workspacePath, state, config, assignment
|
|
|
253
253
|
return { ok: false, error: runtime.detail };
|
|
254
254
|
}
|
|
255
255
|
|
|
256
|
+
let repoState = runtime.state;
|
|
257
|
+
if (repoState.status === 'idle' || repoState.status === 'completed') {
|
|
258
|
+
const initResult = initializeGovernedRun(runtime.root, runtime.config, {
|
|
259
|
+
allow_terminal_restart: repoState.status === 'completed',
|
|
260
|
+
});
|
|
261
|
+
if (!initResult.ok) {
|
|
262
|
+
return { ok: false, error: initResult.error };
|
|
263
|
+
}
|
|
264
|
+
repoState = initResult.state;
|
|
265
|
+
}
|
|
266
|
+
|
|
256
267
|
const assignResult = assignGovernedTurn(runtime.root, runtime.config, assignment.role);
|
|
257
268
|
if (!assignResult.ok) {
|
|
258
269
|
return { ok: false, error: assignResult.error };
|
package/src/lib/mission-plans.js
CHANGED
|
@@ -12,7 +12,7 @@ import { join } from 'path';
|
|
|
12
12
|
import { loadChainReport } from './chain-reports.js';
|
|
13
13
|
import { writeDispatchBundle } from './dispatch-bundle.js';
|
|
14
14
|
import { loadProjectContext, loadProjectState } from './config.js';
|
|
15
|
-
import { reissueTurn } from './governed-state.js';
|
|
15
|
+
import { reactivateGovernedRun, reissueTurn } from './governed-state.js';
|
|
16
16
|
import { emitRunEvent } from './run-events.js';
|
|
17
17
|
import { readBarriers, readCoordinatorHistory, recordCoordinatorDecision } from './coordinator-state.js';
|
|
18
18
|
import { loadCoordinatorConfig } from './coordinator-config.js';
|
|
@@ -1074,7 +1074,19 @@ export function retryCoordinatorWorkstream(root, mission, planId, workstreamId,
|
|
|
1074
1074
|
return { ok: false, error: reissued.error };
|
|
1075
1075
|
}
|
|
1076
1076
|
|
|
1077
|
-
|
|
1077
|
+
let repoStateForBundle = reissued.state;
|
|
1078
|
+
if (reissued.state?.status === 'blocked' || reissued.state?.status === 'paused') {
|
|
1079
|
+
const reactivated = reactivateGovernedRun(repoTurn.root, reissued.state, {
|
|
1080
|
+
via: 'mission plan launch --retry',
|
|
1081
|
+
notificationConfig: repoTurn.config,
|
|
1082
|
+
});
|
|
1083
|
+
if (!reactivated.ok) {
|
|
1084
|
+
return { ok: false, error: `Turn reissued but blocked run could not be reactivated: ${reactivated.error}` };
|
|
1085
|
+
}
|
|
1086
|
+
repoStateForBundle = reactivated.state;
|
|
1087
|
+
}
|
|
1088
|
+
|
|
1089
|
+
const bundleResult = writeDispatchBundle(repoTurn.root, repoStateForBundle, repoTurn.config, {
|
|
1078
1090
|
turnId: reissued.newTurn.turn_id,
|
|
1079
1091
|
});
|
|
1080
1092
|
if (!bundleResult.ok) {
|