agent-transport-system 0.7.3 → 0.7.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/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.7.3";
30
+ var version = "0.7.5";
31
31
  var package_default = {
32
32
  $schema: "https://www.schemastore.org/package.json",
33
33
  name: "agent-transport-system",
@@ -2492,6 +2492,14 @@ const SPACE_ADD_MEMBERS_PROGRESS_PHASES = [
2492
2492
  factSource: "space_membership_api_write",
2493
2493
  summary: "Adding selected profiles to the Space."
2494
2494
  },
2495
+ {
2496
+ stage: "add_members",
2497
+ phase: "confirm_membership_projection",
2498
+ owner: "space",
2499
+ waitingFor: "Space membership and Wake availability projection",
2500
+ factSource: "space_membership_projection",
2501
+ summary: "Confirming the Space can see newly added wakeable members before returning."
2502
+ },
2495
2503
  {
2496
2504
  stage: "record_join_notices",
2497
2505
  phase: "write_join_notices",
@@ -28706,11 +28714,11 @@ async function readSystemLayoutWithRetry(path) {
28706
28714
  }
28707
28715
  const parsed = raw ? parseSystemLayout(raw) : null;
28708
28716
  if (parsed) return parsed;
28709
- if (attempt < SYSTEM_LAYOUT_READ_RETRY_COUNT - 1) await delay(SYSTEM_LAYOUT_READ_RETRY_DELAY_MS);
28717
+ if (attempt < SYSTEM_LAYOUT_READ_RETRY_COUNT - 1) await delay$1(SYSTEM_LAYOUT_READ_RETRY_DELAY_MS);
28710
28718
  }
28711
28719
  return null;
28712
28720
  }
28713
- function delay(ms) {
28721
+ function delay$1(ms) {
28714
28722
  return new Promise((resolve) => {
28715
28723
  setTimeout(resolve, ms);
28716
28724
  });
@@ -64447,8 +64455,10 @@ async function writeConnectedComputerEnrollmentToServiceContract(input) {
64447
64455
  const PREPARE_TOKEN_HEADER = "x-ats-prepare-token";
64448
64456
  const PREPARE_SESSION_MAX_RECONNECT_ATTEMPTS = 3;
64449
64457
  const PREPARE_SESSION_RECONNECT_DELAY_MS = 1e3;
64450
- const ROUTE_CATALOG_SYNC_MAX_ATTEMPTS$1 = 8;
64451
- const ROUTE_CATALOG_SYNC_RETRY_DELAY_MS$1 = 750;
64458
+ const REPORT_RUNTIME_CONNECTION_MAX_ATTEMPTS = 12;
64459
+ const REPORT_RUNTIME_CONNECTION_RETRY_DELAY_MS = 1e3;
64460
+ const ROUTE_CATALOG_SYNC_MAX_ATTEMPTS$1 = 12;
64461
+ const ROUTE_CATALOG_SYNC_RETRY_DELAY_MS$1 = 1e3;
64452
64462
  const TERMINAL_PREPARE_STEP_ORDER = [
64453
64463
  "prepare_selected_agents",
64454
64464
  "refresh_service",
@@ -64882,6 +64892,17 @@ async function enforcePrepareSessionTerminalAuth(input) {
64882
64892
  throw new Error(result.message);
64883
64893
  }
64884
64894
  async function connectSelectedLocalAgentsToAts(input) {
64895
+ let lastTransientError = null;
64896
+ for (let attempt = 1; attempt <= REPORT_RUNTIME_CONNECTION_MAX_ATTEMPTS; attempt++) try {
64897
+ return await connectSelectedLocalAgentsToAtsOnce(input);
64898
+ } catch (error) {
64899
+ if (!isTransientSelectedAgentConnectionError(error) || attempt >= REPORT_RUNTIME_CONNECTION_MAX_ATTEMPTS) throw error;
64900
+ lastTransientError = error;
64901
+ await sleep$2(REPORT_RUNTIME_CONNECTION_RETRY_DELAY_MS);
64902
+ }
64903
+ throw lastTransientError ?? /* @__PURE__ */ new Error("ATS could not connect local agents.");
64904
+ }
64905
+ async function connectSelectedLocalAgentsToAtsOnce(input) {
64885
64906
  const runtimeIdentity = await syncLocalSetupCurrentDeviceTargetFromServiceContract({ gatewayUrl: input.gatewayUrl });
64886
64907
  const readiness = await collectStartLocalReadiness({ auth: {
64887
64908
  status: "valid",
@@ -64907,6 +64928,10 @@ async function connectSelectedLocalAgentsToAts(input) {
64907
64928
  localReadinessSnapshot: buildPrepareLocalReadinessSnapshot(readiness)
64908
64929
  };
64909
64930
  }
64931
+ function isTransientSelectedAgentConnectionError(error) {
64932
+ const rawMessage = toErrorMessage$10(error).toLowerCase();
64933
+ return rawMessage.includes("service has not finished starting") || rawMessage.includes("ats service is not running") || rawMessage.includes("service.not_running") || rawMessage.includes("service_contract_unavailable") || rawMessage.includes("runtime.reporting.write_failed");
64934
+ }
64910
64935
  async function verifySelectedLocalAgentsConnectedToAts(input) {
64911
64936
  const runtimeIdentity = await readTerminalRuntimeIdentityFromServiceContract();
64912
64937
  const readiness = await collectStartLocalReadiness({ auth: {
@@ -65001,7 +65026,6 @@ function formatTerminalStepFailureMessage(input) {
65001
65026
  const detail = formatTerminalStepFailureDetail(input.rawMessage);
65002
65027
  return `ATS Service did not report a running local connection during setup. Copy a fresh setup command from ATS Web and run setup again to continue from this step. If it fails again, run \`${formatAtsCliCommand("ats service start --mode foreground")}\` to see live startup logs, then copy a fresh setup command from ATS Web and run setup again.${detail ? ` Detail: ${detail}` : ""}`;
65003
65028
  }
65004
- if (input.stepId === "report_runtime" && input.rawMessage.toLowerCase().includes("service")) return `ATS Service stopped before ATS could connect selected agents to ATS. Start ATS Service with \`${formatAtsCliCommand("ats service start")}\`, then copy a fresh setup command from ATS Web and run setup again.`;
65005
65029
  return input.rawMessage;
65006
65030
  }
65007
65031
  function formatTerminalStepFailureDetail(rawMessage) {
@@ -68206,7 +68230,14 @@ function isAgentProfile(profile) {
68206
68230
  //#region src/commands/setup.ts
68207
68231
  const ROUTE_CATALOG_SYNC_MAX_ATTEMPTS = 8;
68208
68232
  const ROUTE_CATALOG_SYNC_RETRY_DELAY_MS = 750;
68233
+ const SETUP_COMMAND_LOCK_PROFILE = "setup";
68234
+ const SETUP_COMMAND_LOCK_KEY = "setup-command";
68209
68235
  async function runSetup(input) {
68236
+ return await withSetupCommandLock(input, async () => {
68237
+ await runSetupUnlocked(input);
68238
+ });
68239
+ }
68240
+ async function runSetupUnlocked(input) {
68210
68241
  if (shouldUseSetupLocalAgentConnection(input)) {
68211
68242
  const setupResult = await runSetupLocalAgentConnection(input);
68212
68243
  if (setupResult.status !== "completed") return;
@@ -68310,6 +68341,33 @@ async function runSetup(input) {
68310
68341
  view: runtime.resolvedView
68311
68342
  });
68312
68343
  }
68344
+ async function withSetupCommandLock(input, run) {
68345
+ try {
68346
+ return await runWithHeldLock({
68347
+ lock: await acquireLock({
68348
+ atsProfileId: SETUP_COMMAND_LOCK_PROFILE,
68349
+ key: SETUP_COMMAND_LOCK_KEY,
68350
+ meta: {
68351
+ command: "ats setup",
68352
+ path: shouldUseSetupLocalAgentConnection(input) ? "delegated_or_profile_setup" : "local_setup",
68353
+ view: input.view ?? null
68354
+ }
68355
+ }),
68356
+ run,
68357
+ releaseContext: {
68358
+ atsProfileId: SETUP_COMMAND_LOCK_PROFILE,
68359
+ component: "setup_command",
68360
+ lockKey: SETUP_COMMAND_LOCK_KEY
68361
+ }
68362
+ });
68363
+ } catch (error) {
68364
+ if (isAtsLockError(error)) throw new Error(buildSetupCommandLockMessage(error));
68365
+ throw error;
68366
+ }
68367
+ }
68368
+ function buildSetupCommandLockMessage(error) {
68369
+ return `Another ATS setup is already running on this computer. Wait for it to finish, then run setup again.${error.metaRaw ? ` Existing setup: ${error.metaRaw}` : ""}`;
68370
+ }
68313
68371
  function shouldUseSetupLocalAgentConnection(input) {
68314
68372
  return Boolean(normalizeOptionalString$5(input.startSession) ?? normalizeOptionalString$5(input.humanProfile) ?? normalizeOptionalString$5(input.profile) ?? (input.agent && input.agent.length > 0 ? "agent" : null));
68315
68373
  }
@@ -84915,6 +84973,8 @@ const TRAILING_SLASHES_RE = /\/+$/u;
84915
84973
  const SPACE_CREATE_AUTH_CHECK_TIMEOUT_MS = 8e3;
84916
84974
  const SPACE_CREATE_AUTH_CHECK_LOOPBACK_TIMEOUT_MS = 1500;
84917
84975
  const SPACE_LOADING_SPINNER_MESSAGE = "Loading spaces...";
84976
+ const SPACE_ADD_MEMBERS_PROJECTION_WAIT_MAX_ATTEMPTS = 3;
84977
+ const SPACE_ADD_MEMBERS_PROJECTION_WAIT_DELAY_MS = 500;
84918
84978
  function requireReadableSpaceConfigList(result, operation) {
84919
84979
  if (result.kind === "corrupt") throw toSpaceConfigListIntegrityError({
84920
84980
  operation,
@@ -85117,6 +85177,7 @@ async function runSpaceCreate(input) {
85117
85177
  guide: typeof parsed.meta?.guide === "string" ? parsed.meta.guide : createInput.value.guide
85118
85178
  });
85119
85179
  emitSpaceCreateSuccessOutput({
85180
+ baseUrl,
85120
85181
  presenter: contextPresenter,
85121
85182
  parsed,
85122
85183
  outputMode,
@@ -85135,6 +85196,7 @@ async function runSpaceCreate(input) {
85135
85196
  const joinNow = isHumanInteractiveSpaceRuntime(contextRuntime) ? Boolean(createInput.value.joinInput) : await resolveJoinAfterCreate(createInput.value.joinInput, contextInteractive);
85136
85197
  if (contextRuntime.resolvedView === "agent") {
85137
85198
  emitSpaceCreateAgentFollowUp({
85199
+ baseUrl,
85138
85200
  presenter: contextPresenter,
85139
85201
  profileId: atsProfile.atsProfileId,
85140
85202
  spaceId: parsed.spaceId,
@@ -85170,12 +85232,14 @@ async function runSpaceCreate(input) {
85170
85232
  }
85171
85233
  }
85172
85234
  function emitSpaceCreateAgentFollowUp(input) {
85235
+ const spaceUrl = buildSpaceWebUrl(input.baseUrl, input.spaceId);
85173
85236
  const historyCommand = formatAtsCliCommand(`ats space history ${input.spaceId} --profile ${input.profileId} --kind text --brief --limit 20 --view agent`);
85174
85237
  const sendCommand = formatAtsCliCommand(`ats space send ${input.spaceId} --profile ${input.profileId} "hello" --view agent`);
85175
85238
  input.presenter.data({
85176
85239
  code: "space.create.next_commands",
85177
85240
  payload: {
85178
85241
  spaceId: input.spaceId,
85242
+ spaceUrl,
85179
85243
  joinRequested: input.joinRequested,
85180
85244
  joinedByCreate: true,
85181
85245
  note: "Agent View create returns after creating the Space. Use explicit follow-up commands instead of opening a long-running join session.",
@@ -85262,6 +85326,9 @@ function renderHumanSpaceCreateCard(input) {
85262
85326
  const rows = [{
85263
85327
  label: "🆔 Space ID",
85264
85328
  value: input.spaceId
85329
+ }, {
85330
+ label: "🔗 Space URL",
85331
+ value: input.spaceUrl
85265
85332
  }];
85266
85333
  if (input.passwordProtected) {
85267
85334
  rows.push({
@@ -85547,6 +85614,14 @@ function toAuthEndpoint(baseUrl, pathname) {
85547
85614
  url.hash = "";
85548
85615
  return url.toString();
85549
85616
  }
85617
+ function buildSpaceWebUrl(baseUrl, spaceId) {
85618
+ const url = new URL(baseUrl);
85619
+ if (isLoopbackHostname(url.hostname) && url.port === "8080") url.port = "3000";
85620
+ url.pathname = `/app/spaces/${encodeURIComponent(spaceId)}`;
85621
+ url.search = "";
85622
+ url.hash = "";
85623
+ return url.toString();
85624
+ }
85550
85625
  function resolveSpaceCreateAuthCheckTimeoutMs(endpoint) {
85551
85626
  const url = parseUrlOrNull(endpoint);
85552
85627
  if (url && isLoopbackHostname(url.hostname)) return SPACE_CREATE_AUTH_CHECK_LOOPBACK_TIMEOUT_MS;
@@ -85563,10 +85638,14 @@ function isLoopbackHostname(hostname) {
85563
85638
  return hostname === "localhost" || hostname === "127.0.0.1" || hostname === "::1";
85564
85639
  }
85565
85640
  function emitSpaceCreateSuccessOutput(input) {
85641
+ const spaceUrl = buildSpaceWebUrl(input.baseUrl, input.parsed.spaceId);
85566
85642
  if (input.outputMode === "ndjson") {
85567
85643
  input.presenter.data({
85568
85644
  code: "space.create.response",
85569
- payload: input.parsed
85645
+ payload: {
85646
+ ...input.parsed,
85647
+ spaceUrl
85648
+ }
85570
85649
  });
85571
85650
  return;
85572
85651
  }
@@ -85574,6 +85653,7 @@ function emitSpaceCreateSuccessOutput(input) {
85574
85653
  renderHumanSpaceCreateCard({
85575
85654
  presenter: input.presenter,
85576
85655
  spaceId: input.parsed.spaceId,
85656
+ spaceUrl,
85577
85657
  passwordProtected: input.passwordProtected
85578
85658
  });
85579
85659
  return;
@@ -85583,6 +85663,14 @@ function emitSpaceCreateSuccessOutput(input) {
85583
85663
  text: formatMessage(input.resolvedView, "space.create.created", { spaceId: input.parsed.spaceId }),
85584
85664
  data: { spaceId: input.parsed.spaceId }
85585
85665
  });
85666
+ input.presenter.line({
85667
+ code: "space.create.url",
85668
+ text: `Space URL: ${spaceUrl}`,
85669
+ data: {
85670
+ spaceId: input.parsed.spaceId,
85671
+ spaceUrl
85672
+ }
85673
+ });
85586
85674
  if (!input.passwordProtected) return;
85587
85675
  input.presenter.line({
85588
85676
  code: "space.password.warning",
@@ -86324,6 +86412,14 @@ async function runSpaceAddMembersForTarget(input) {
86324
86412
  baseUrl: targetBaseUrl,
86325
86413
  password: input.password
86326
86414
  });
86415
+ await waitForAddedMembersWakeProjection({
86416
+ actorProfile: input.atsProfile,
86417
+ baseUrl: targetBaseUrl,
86418
+ password: input.password,
86419
+ progress,
86420
+ result,
86421
+ space: input.target.space
86422
+ });
86327
86423
  emitSpaceAddMembersProgressIfRequested(progress, {
86328
86424
  stage: "record_join_notices",
86329
86425
  phase: "write_join_notices",
@@ -88526,6 +88622,42 @@ async function applySpaceAddMembers(input) {
88526
88622
  }
88527
88623
  return result;
88528
88624
  }
88625
+ async function waitForAddedMembersWakeProjection(input) {
88626
+ const expectedWakeableProfileIds = resolveExpectedWakeProjectionProfileIds(input.result);
88627
+ if (expectedWakeableProfileIds.length === 0) return;
88628
+ emitSpaceAddMembersProgressIfRequested(input.progress, {
88629
+ stage: "add_members",
88630
+ phase: "confirm_membership_projection",
88631
+ summary: "Confirming the Space can see newly added wakeable members before returning.",
88632
+ profileIds: expectedWakeableProfileIds
88633
+ });
88634
+ const expected = new Set(expectedWakeableProfileIds);
88635
+ for (let attempt = 0; attempt < SPACE_ADD_MEMBERS_PROJECTION_WAIT_MAX_ATTEMPTS; attempt += 1) {
88636
+ if (attempt > 0) await delay(SPACE_ADD_MEMBERS_PROJECTION_WAIT_DELAY_MS);
88637
+ let membersSnapshot;
88638
+ try {
88639
+ membersSnapshot = await createCliSpaceApi(input.baseUrl).getMembersSnapshot({
88640
+ spaceId: input.space,
88641
+ requestContext: buildAtsRequestContextFromProfile({ atsProfile: input.actorProfile }),
88642
+ ...input.password === void 0 ? {} : { spacePassword: input.password }
88643
+ });
88644
+ } catch {
88645
+ return;
88646
+ }
88647
+ if (hasExpectedWakeProjection(membersSnapshot, expected)) return;
88648
+ }
88649
+ }
88650
+ function resolveExpectedWakeProjectionProfileIds(result) {
88651
+ return result.added.filter((candidate) => candidate.profileKind === "agent" && candidate.localParticipationReadiness === "ready").map((candidate) => candidate.profileId);
88652
+ }
88653
+ function hasExpectedWakeProjection(membersSnapshot, expectedProfileIds) {
88654
+ const wakeableProfileIds = new Set(membersSnapshot.agents.filter((member) => member.status === "active" && member.publicReplyPresence?.status === "ready").map((member) => member.profileId));
88655
+ for (const profileId of expectedProfileIds) if (!wakeableProfileIds.has(profileId)) return false;
88656
+ return true;
88657
+ }
88658
+ async function delay(ms) {
88659
+ await new Promise((resolve) => setTimeout(resolve, ms));
88660
+ }
88529
88661
  function emitSpaceAddMembersResult(input) {
88530
88662
  const localParticipationFollowUps = input.result.added.map((candidate) => buildSpaceMemberJoinedFollowUp(candidate)).filter((followUp) => followUp !== null);
88531
88663
  if (input.resolvedView === "agent") {