agent-transport-system 0.4.92 → 0.4.94

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/dist/ats.js CHANGED
@@ -27,7 +27,7 @@ import wrapAnsi from "wrap-ansi";
27
27
  import { Box, Container, Editor, Key, ProcessTerminal, TUI, Text, getEditorKeybindings, matchesKey } from "@mariozechner/pi-tui";
28
28
 
29
29
  //#region package.json
30
- var version = "0.4.92";
30
+ var version = "0.4.94";
31
31
  var package_default = {
32
32
  $schema: "https://www.schemastore.org/package.json",
33
33
  name: "agent-transport-system",
@@ -8304,7 +8304,7 @@ async function findDaemonDependencyPackageRoot(resolvedEntryPath) {
8304
8304
  throw new Error(`ATS could not determine the package root for background service dependency entry ${resolvedEntryPath}.`);
8305
8305
  }
8306
8306
  function buildDaemonServiceLauncherSource() {
8307
- return `import { readFile } from "node:fs/promises";
8307
+ return `import { access, readFile, readdir, stat, writeFile } from "node:fs/promises";
8308
8308
  import { join } from "node:path";
8309
8309
  import process from "node:process";
8310
8310
  import { spawn } from "node:child_process";
@@ -8329,6 +8329,18 @@ async function readJsonFile(path) {
8329
8329
  }
8330
8330
  }
8331
8331
 
8332
+ async function pathExists(path) {
8333
+ try {
8334
+ await access(path);
8335
+ return true;
8336
+ } catch (error) {
8337
+ if (error && typeof error === "object" && "code" in error && error.code === "ENOENT") {
8338
+ return false;
8339
+ }
8340
+ throw error;
8341
+ }
8342
+ }
8343
+
8332
8344
  function normalizeInstallState(parsed) {
8333
8345
  if (!parsed || parsed.schema !== INSTALL_STATE_SCHEMA || typeof parsed.daemonVersion !== "string" || parsed.daemonVersion.length === 0) {
8334
8346
  return null;
@@ -8372,6 +8384,94 @@ function normalizeBundleState(parsed) {
8372
8384
  return null;
8373
8385
  }
8374
8386
 
8387
+ function normalizeBundlePackageManifest(parsed) {
8388
+ if (!parsed || typeof parsed.version !== "string" || parsed.version.length === 0) {
8389
+ return null;
8390
+ }
8391
+ return {
8392
+ version: parsed.version,
8393
+ };
8394
+ }
8395
+
8396
+ function uniqueNonEmptyStrings(values) {
8397
+ return [...new Set(values.filter((value) => typeof value === "string" && value.length > 0))];
8398
+ }
8399
+
8400
+ async function findFallbackSelfContainedBundle(input) {
8401
+ const directories = await readdir(BUNDLE_ROOT_PATH, { withFileTypes: true }).catch((error) => {
8402
+ if (error && typeof error === "object" && "code" in error && error.code === "ENOENT") {
8403
+ return [];
8404
+ }
8405
+ throw error;
8406
+ });
8407
+ const entryScriptRelativePaths = uniqueNonEmptyStrings([
8408
+ input.preferredEntryScriptRelativePath,
8409
+ "dist/ats.js",
8410
+ "dist/ats-dev.js",
8411
+ ]);
8412
+ const candidates = [];
8413
+ for (const directory of directories) {
8414
+ if (!directory.isDirectory()) {
8415
+ continue;
8416
+ }
8417
+ const bundleId = directory.name;
8418
+ const bundlePath = join(BUNDLE_ROOT_PATH, bundleId);
8419
+ const manifest = normalizeBundlePackageManifest(
8420
+ await readJsonFile(join(bundlePath, "package.json"))
8421
+ );
8422
+ if (manifest?.version !== input.installState.daemonVersion) {
8423
+ continue;
8424
+ }
8425
+ for (const entryScriptRelativePath of entryScriptRelativePaths) {
8426
+ const entryPath = join(bundlePath, entryScriptRelativePath);
8427
+ if (!(await pathExists(entryPath))) {
8428
+ continue;
8429
+ }
8430
+ const entryStat = await stat(entryPath).catch(() => null);
8431
+ candidates.push({
8432
+ bundleId,
8433
+ entryPath,
8434
+ entryScriptRelativePath,
8435
+ mtimeMs: entryStat?.mtimeMs ?? 0,
8436
+ });
8437
+ break;
8438
+ }
8439
+ }
8440
+ candidates.sort((a, b) => {
8441
+ if (b.mtimeMs !== a.mtimeMs) {
8442
+ return b.mtimeMs - a.mtimeMs;
8443
+ }
8444
+ return b.bundleId.localeCompare(a.bundleId);
8445
+ });
8446
+ return candidates[0] ?? null;
8447
+ }
8448
+
8449
+ async function persistFallbackActiveBundleState(input) {
8450
+ const now = new Date().toISOString();
8451
+ const bundleRootPath = join(BUNDLE_ROOT_PATH, input.bundleId);
8452
+ const nextState = {
8453
+ v: 2,
8454
+ schema: ACTIVE_BUNDLE_SCHEMA,
8455
+ bundleId: input.bundleId,
8456
+ daemonVersion: input.installState.daemonVersion,
8457
+ entryScriptRelativePath: input.entryScriptRelativePath,
8458
+ bundleFormat: "self_contained_v1",
8459
+ sourceEntryScriptPath: input.entryPath,
8460
+ sourcePackageRootPath: bundleRootPath,
8461
+ sourceDistRootPath: join(bundleRootPath, "dist"),
8462
+ sourceDistFingerprint: null,
8463
+ sourceKind: "package_install",
8464
+ createdAt: input.previousBundleState?.createdAt ?? now,
8465
+ updatedAt: now,
8466
+ };
8467
+ await writeFile(ACTIVE_BUNDLE_PATH, \`\${JSON.stringify(nextState, null, 2)}\\n\`, {
8468
+ encoding: "utf8",
8469
+ mode: 0o600,
8470
+ }).catch((error) => {
8471
+ console.error(\`ATS Service launcher recovered a matching bundle but could not update active bundle state: \${String(error)}\`);
8472
+ });
8473
+ }
8474
+
8375
8475
  async function resolveEntryPath() {
8376
8476
  const installState = normalizeInstallState(await readJsonFile(INSTALL_STATE_PATH));
8377
8477
  const bundleState = normalizeBundleState(await readJsonFile(ACTIVE_BUNDLE_PATH));
@@ -8379,18 +8479,87 @@ async function resolveEntryPath() {
8379
8479
  throw new Error("ATS Service launcher could not read a valid install state.");
8380
8480
  }
8381
8481
  if (!bundleState) {
8382
- throw new Error("ATS Service launcher could not read a valid active bundle state.");
8482
+ const fallback = await findFallbackSelfContainedBundle({
8483
+ installState,
8484
+ preferredEntryScriptRelativePath: null,
8485
+ });
8486
+ if (!fallback) {
8487
+ throw new Error("ATS Service launcher could not read a valid active bundle state.");
8488
+ }
8489
+ await persistFallbackActiveBundleState({
8490
+ ...fallback,
8491
+ installState,
8492
+ previousBundleState: null,
8493
+ });
8494
+ console.error(\`ATS Service launcher recovered bundle \${fallback.bundleId} because active bundle state was missing or invalid.\`);
8495
+ return fallback.entryPath;
8383
8496
  }
8384
8497
  if (bundleState.bundleFormat !== "self_contained_v1") {
8385
- throw new Error(\`ATS Service launcher refused to start legacy bundle format \${String(bundleState.bundleFormat)}.\`);
8498
+ const fallback = await findFallbackSelfContainedBundle({
8499
+ installState,
8500
+ preferredEntryScriptRelativePath: bundleState.entryScriptRelativePath,
8501
+ });
8502
+ if (!fallback) {
8503
+ throw new Error(\`ATS Service launcher refused to start legacy bundle format \${String(bundleState.bundleFormat)}.\`);
8504
+ }
8505
+ await persistFallbackActiveBundleState({
8506
+ ...fallback,
8507
+ installState,
8508
+ previousBundleState: bundleState,
8509
+ });
8510
+ console.error(\`ATS Service launcher recovered bundle \${fallback.bundleId} because active bundle state used legacy bundle format \${String(bundleState.bundleFormat)}.\`);
8511
+ return fallback.entryPath;
8386
8512
  }
8387
8513
  if (bundleState.sourceKind === "workspace" && !ALLOW_WORKSPACE_BUNDLE_SOURCE) {
8388
- throw new Error("ATS Service launcher refused to start a repo-local workspace bundle in this lane. Repo-local service bundles are only allowed through \`ats-dev\`.");
8514
+ const fallback = await findFallbackSelfContainedBundle({
8515
+ installState,
8516
+ preferredEntryScriptRelativePath: bundleState.entryScriptRelativePath,
8517
+ });
8518
+ if (!fallback) {
8519
+ throw new Error("ATS Service launcher refused to start a repo-local workspace bundle in this lane. Repo-local service bundles are only allowed through \`ats-dev\`.");
8520
+ }
8521
+ await persistFallbackActiveBundleState({
8522
+ ...fallback,
8523
+ installState,
8524
+ previousBundleState: bundleState,
8525
+ });
8526
+ console.error(\`ATS Service launcher recovered bundle \${fallback.bundleId} because active bundle state pointed at a repo-local workspace bundle in this lane.\`);
8527
+ return fallback.entryPath;
8389
8528
  }
8390
8529
  if (bundleState.daemonVersion !== installState.daemonVersion) {
8391
- throw new Error(\`ATS Service launcher detected bundle version drift: bundle v\${String(bundleState.daemonVersion)} != install v\${String(installState.daemonVersion)}.\`);
8530
+ const fallback = await findFallbackSelfContainedBundle({
8531
+ installState,
8532
+ preferredEntryScriptRelativePath: bundleState.entryScriptRelativePath,
8533
+ });
8534
+ if (!fallback) {
8535
+ throw new Error(\`ATS Service launcher detected bundle version drift: bundle v\${String(bundleState.daemonVersion)} != install v\${String(installState.daemonVersion)}.\`);
8536
+ }
8537
+ await persistFallbackActiveBundleState({
8538
+ ...fallback,
8539
+ installState,
8540
+ previousBundleState: bundleState,
8541
+ });
8542
+ console.error(\`ATS Service launcher recovered bundle \${fallback.bundleId} because active bundle state had version drift.\`);
8543
+ return fallback.entryPath;
8544
+ }
8545
+ const entryPath = join(BUNDLE_ROOT_PATH, bundleState.bundleId, bundleState.entryScriptRelativePath);
8546
+ if (await pathExists(entryPath)) {
8547
+ return entryPath;
8392
8548
  }
8393
- return join(BUNDLE_ROOT_PATH, bundleState.bundleId, bundleState.entryScriptRelativePath);
8549
+ const fallback = await findFallbackSelfContainedBundle({
8550
+ installState,
8551
+ preferredEntryScriptRelativePath: bundleState.entryScriptRelativePath,
8552
+ });
8553
+ if (!fallback) {
8554
+ throw new Error(\`ATS Service launcher could not find active bundle entry \${entryPath}.\`);
8555
+ }
8556
+ await persistFallbackActiveBundleState({
8557
+ ...fallback,
8558
+ installState,
8559
+ previousBundleState: bundleState,
8560
+ });
8561
+ console.error(\`ATS Service launcher recovered bundle \${fallback.bundleId} because active bundle entry was missing.\`);
8562
+ return fallback.entryPath;
8394
8563
  }
8395
8564
 
8396
8565
  async function main() {
@@ -15688,8 +15857,9 @@ function resolvePrimaryAction(input) {
15688
15857
  if (!input.profile.active || input.profile.source === "unavailable") return "check_status";
15689
15858
  if (!input.binding.routable) return input.binding.source === "unavailable" || input.binding.source === "not_checked" ? "check_status" : "reconnect_binding";
15690
15859
  if (!input.service.executionCompatible || hasServiceUpdateRequiredReason(input.service.reasonCodes)) return "update_service";
15691
- if (!(input.localService.reachable || input.route.online)) return "run_prepare";
15692
15860
  if (hasServiceUpdateRequiredReason(input.runtime.reasonCodes)) return "update_service";
15861
+ if (hasBindingReconnectReason(input.localService.reasonCodes) || input.claim.executionBlocked && hasBindingReconnectReason(input.claim.reasonCodes)) return "reconnect_binding";
15862
+ if (!(input.localService.reachable || input.route.online)) return "run_prepare";
15693
15863
  if (!input.runtime.ready) return resolveRuntimeNotReadyPrimaryAction({
15694
15864
  localService: input.localService,
15695
15865
  runtime: input.runtime
@@ -15711,9 +15881,15 @@ function resolveBlockedClaimPrimaryAction(input) {
15711
15881
  ];
15712
15882
  if (hasServiceUpdateRequiredReason(claimAndRuntimeReasonCodes)) return "update_service";
15713
15883
  if (hasRuntimeCapabilityReportRefreshReason(claimAndRuntimeReasonCodes) && input.localService.refreshable) return "check_status";
15884
+ if (hasBindingReconnectReason(input.claim.reasonCodes)) return "reconnect_binding";
15714
15885
  if (hasSetupRepairReason(input.claim.reasonCodes)) return "run_prepare";
15715
15886
  return "run_prepare";
15716
15887
  }
15888
+ function hasBindingReconnectReason(reasonCodes) {
15889
+ return reasonCodes.some((reasonCode) => {
15890
+ return reasonCode === "claim_eligibility.binding_capability_mismatch" || reasonCode === "claim_eligibility.binding_owner_mismatch" || reasonCode === "claim_eligibility.binding_runtime_mismatch" || reasonCode === "claim_eligibility.owner_mismatch" || reasonCode === "claim_eligibility.runtime_id_mismatch" || reasonCode === "local_service.identity_mismatch";
15891
+ });
15892
+ }
15717
15893
  function resolveRuntimeNotReadyPrimaryAction(input) {
15718
15894
  if (hasRuntimeCapabilityReportRefreshReason(input.runtime.reasonCodes) && input.localService.refreshable) return "check_status";
15719
15895
  return "run_prepare";
@@ -52336,8 +52512,9 @@ async function stopRegisteredDaemonServiceManager(input) {
52336
52512
  runtimeStatus: input.ownerSnapshot.runtimeStatus
52337
52513
  });
52338
52514
  }
52515
+ let residualCleanupResult;
52339
52516
  try {
52340
- await stopResidualDaemonProcessesAfterManagerStop({
52517
+ residualCleanupResult = await stopResidualDaemonProcessesAfterManagerStop({
52341
52518
  force: input.force,
52342
52519
  mode: input.mode,
52343
52520
  runtimeStatus: input.ownerSnapshot.runtimeStatus
@@ -52350,7 +52527,7 @@ async function stopRegisteredDaemonServiceManager(input) {
52350
52527
  errorMessage: toErrorMessage$12(error)
52351
52528
  });
52352
52529
  }
52353
- const finalOwnerSnapshot = await readCurrentDaemonOwnerSnapshot();
52530
+ const finalOwnerSnapshot = residualCleanupResult.shouldRefresh ? await readCurrentDaemonOwnerSnapshot() : residualCleanupResult.ownerSnapshot;
52354
52531
  if (hasActiveDaemonOwners(finalOwnerSnapshot) || finalOwnerSnapshot.serviceManagerStatus?.enabled === true) return buildDaemonStopWarningResult({
52355
52532
  reason: "stop_failed",
52356
52533
  runtimeStatus: finalOwnerSnapshot.runtimeStatus,
@@ -52387,6 +52564,10 @@ async function stopResidualDaemonProcessesAfterManagerStop(input) {
52387
52564
  throw new Error(`Residual ATS-owned process pid=${String(processInfo.pid)} did not exit after ${signal}.`);
52388
52565
  }
52389
52566
  }
52567
+ return {
52568
+ ownerSnapshot: residualOwnerSnapshot,
52569
+ shouldRefresh: residualOwnerSnapshot.ownedProcesses.length > 0
52570
+ };
52390
52571
  }
52391
52572
  async function stopCurrentLaneDaemonProcess(input) {
52392
52573
  const daemonPid = input.runtimeStatus.pid;
@@ -54129,6 +54310,28 @@ async function resolveDaemonServiceParticipationStatus(input) {
54129
54310
  async function runDaemonServiceParticipationGate(input) {
54130
54311
  if (!shouldRunDaemonServiceParticipationGate(input)) return { status: "aligned" };
54131
54312
  const initialStatus = await resolveDaemonServiceParticipationStatus();
54313
+ if (isRunningAdvisoryOnlyServiceDrift(initialStatus)) {
54314
+ const accountIssue = await resolveDaemonServiceAccountAlignmentIssue(input);
54315
+ if (!accountIssue) return { status: "aligned" };
54316
+ const codePrefix = resolveDaemonServiceParticipationCodePrefix(input.intent);
54317
+ if (input.autoRepair === true) return await runDaemonServiceParticipationRefresh({
54318
+ input,
54319
+ codePrefix,
54320
+ expectedVersion: initialStatus.expectedVersion,
54321
+ accountIssue
54322
+ });
54323
+ renderDaemonStartNeedsAttention({
54324
+ presenter: input.presenter,
54325
+ codePrefix,
54326
+ summary: accountIssue.summary
54327
+ });
54328
+ input.presenter.line({
54329
+ code: `${codePrefix}.account_alignment_failed`,
54330
+ text: accountIssue.summary,
54331
+ data: { reasonCode: accountIssue.reasonCode }
54332
+ });
54333
+ return { status: "needs_attention" };
54334
+ }
54132
54335
  if (input.forceRefresh === true && initialStatus.kind === "ready") return await runDaemonServiceParticipationRefresh({
54133
54336
  input,
54134
54337
  codePrefix: resolveDaemonServiceParticipationCodePrefix(input.intent),
@@ -54169,6 +54372,9 @@ async function runDaemonServiceParticipationGate(input) {
54169
54372
  status: installStageResult.status
54170
54373
  });
54171
54374
  }
54375
+ function isRunningAdvisoryOnlyServiceDrift(status) {
54376
+ return status.kind === "needs_repair" && status.runtimeStatus === "running" && status.anomalyCodes.length > 0 && status.anomalyCodes.every((code) => DAEMON_SERVICE_WAKEABILITY_ADVISORY_ANOMALIES.has(code));
54377
+ }
54172
54378
  function shouldRunDaemonServiceParticipationGate(input) {
54173
54379
  return input.resolvedView === "human" && (input.allowPrompt || input.autoRepair === true) && (input.forcePrompt === true || shouldRequireDaemonServiceParticipation(input.localDemand));
54174
54380
  }
@@ -56939,6 +57145,12 @@ const TERMINAL_PREPARE_STEP_ORDER = [
56939
57145
  "report_runtime",
56940
57146
  "verify_setup"
56941
57147
  ];
57148
+ var PrepareSessionBlockedByCoreError = class extends Error {
57149
+ constructor(message) {
57150
+ super(message);
57151
+ this.name = "PrepareSessionBlockedByCoreError";
57152
+ }
57153
+ };
56942
57154
  async function runPrepareSessionExecutor(input) {
56943
57155
  let initial = await resolvePrepareSessionToken({
56944
57156
  gatewayUrl: input.gatewayUrl,
@@ -57099,11 +57311,18 @@ async function runTerminalStep(input) {
57099
57311
  stepId: input.stepId,
57100
57312
  status: "completed"
57101
57313
  });
57102
- return await input.stream.waitForSnapshot((snapshot) => {
57314
+ const resolvedSnapshot = await input.stream.waitForSnapshot((snapshot) => {
57103
57315
  const step = findStep(snapshot, input.stepId);
57104
- return snapshot.sessionRevision > stepCompleteRevision && step?.state === "complete";
57316
+ return snapshot.sessionRevision > stepCompleteRevision && (step?.state === "complete" || step?.state === "blocked" || snapshot.phase === "blocked" || snapshot.lifecycleStatus !== "active");
57105
57317
  });
57318
+ assertTerminalStepCompletedOrThrow({
57319
+ input: input.input,
57320
+ snapshot: resolvedSnapshot,
57321
+ stepId: input.stepId
57322
+ });
57323
+ return resolvedSnapshot;
57106
57324
  } catch (error) {
57325
+ if (error instanceof PrepareSessionBlockedByCoreError) throw error;
57107
57326
  if (error instanceof PrepareSessionRevisionConflictError) {
57108
57327
  const message = "This setup changed while Terminal was working. Run the same setup command again to continue from the latest step.";
57109
57328
  emitHumanStatus(input.input, "Setup changed", [message]);
@@ -57132,6 +57351,13 @@ async function runTerminalStep(input) {
57132
57351
  throw userFacingError;
57133
57352
  }
57134
57353
  }
57354
+ function assertTerminalStepCompletedOrThrow(input) {
57355
+ if (findStep(input.snapshot, input.stepId)?.state === "complete" && input.snapshot.phase !== "blocked") return;
57356
+ const blocker = input.snapshot.blockers.find((candidate) => candidate.stepId === input.stepId) ?? input.snapshot.blockers[0] ?? null;
57357
+ const message = blocker?.message ?? "ATS setup is blocked. Return to ATS Web for details.";
57358
+ emitHumanStatus(input.input, "Setup blocked", [message, ...blocker?.reasonCode ? [`Support code: ${blocker.reasonCode}`] : []]);
57359
+ throw new PrepareSessionBlockedByCoreError(message);
57360
+ }
57135
57361
  function resolveNextTerminalPrepareStep(snapshot) {
57136
57362
  if (snapshot.selectedLocalAgentIds.length === 0) return null;
57137
57363
  return TERMINAL_PREPARE_STEP_ORDER.find((stepId) => {