agent-transport-system 0.7.5 → 0.7.6

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.5";
30
+ var version = "0.7.6";
31
31
  var package_default = {
32
32
  $schema: "https://www.schemastore.org/package.json",
33
33
  name: "agent-transport-system",
@@ -2510,6 +2510,14 @@ const SPACE_ADD_MEMBERS_PROGRESS_PHASES = [
2510
2510
  }
2511
2511
  ];
2512
2512
  const SPACE_ADD_MEMBERS_PROGRESS_OVERVIEW_STAGES = SPACE_ADD_MEMBERS_PROGRESS_PHASES.map((phase) => `${phase.stage}/${phase.phase}`);
2513
+ const SPACE_ADD_MEMBERS_AGENT_OVERVIEW_STAGES = [
2514
+ "check_arguments",
2515
+ "authenticate",
2516
+ "check_profile",
2517
+ "resolve_target",
2518
+ "add_members/write_membership",
2519
+ "record_join_notices/write_join_notices"
2520
+ ];
2513
2521
  function resolveSpaceAddMembersProgressPhase(input) {
2514
2522
  const contract = SPACE_ADD_MEMBERS_PROGRESS_PHASES.find((phase) => phase.stage === input.stage && phase.phase === input.phase);
2515
2523
  if (!contract) throw new Error(`unknown space add-members progress phase: ${input.stage}/${input.phase}`);
@@ -3273,7 +3281,7 @@ const COMMAND_ENTRY_CONTRACTS = {
3273
3281
  },
3274
3282
  notes: ["space is single-select; members support one or many selections.", "already-joined profiles are skipped automatically."],
3275
3283
  pattern: getSpaceMemberSelectionPatternContract("add-members"),
3276
- stages: SPACE_ADD_MEMBERS_PROGRESS_OVERVIEW_STAGES,
3284
+ stages: [...SPACE_ADD_MEMBERS_AGENT_OVERVIEW_STAGES],
3277
3285
  summary: "Use `ats space add-members --profile <profile-id> <space-id> --member <profile-id...> --view agent` with explicit acting profile, Space ID, and member selection."
3278
3286
  },
3279
3287
  buildHints: (context) => getSpaceMemberSelectionPatternContract("add-members").buildHints?.({
@@ -60440,13 +60448,7 @@ function renderProfileSummaryCard(input) {
60440
60448
  });
60441
60449
  }
60442
60450
  function buildAgentCreateNextSteps(profile) {
60443
- if (profile.profileKind === "agent") return [
60444
- formatAtsCliCommand(`ats setup --profile ${profile.atsProfileId} --local-agent <local-agent-id> --view agent`),
60445
- formatAtsCliCommand(`ats setup --profile ${profile.atsProfileId} --view agent`),
60446
- formatAtsCliCommand(`ats service status --profile ${profile.atsProfileId} --view agent`),
60447
- formatAtsCliCommand(`ats profiles show ${profile.atsProfileId} --view agent`),
60448
- formatAtsCliCommand(`ats whoami --profile ${profile.atsProfileId} --view agent`)
60449
- ];
60451
+ if (profile.profileKind === "agent") return [formatAtsCliCommand(`ats space add-members <space-id> --member ${profile.atsProfileId} --profile <human-profile-id> --view agent`)];
60450
60452
  return [
60451
60453
  formatAtsCliCommand("ats profiles list --view agent"),
60452
60454
  formatAtsCliCommand(`ats profiles set ${profile.atsProfileId} --view agent`),
@@ -60460,28 +60462,24 @@ function buildAgentCreateWakeReadinessData(profile) {
60460
60462
  return {
60461
60463
  identityCreated: true,
60462
60464
  wakeReadinessVerified: false,
60463
- wakeReadinessNote: "This command created the Agent Profile identity. It did not verify that this profile can be woken from a Space or reply from this computer."
60465
+ wakeReadinessNote: "Profile creation creates the Space identity. Add it to a Space when the user wants this teammate to participate."
60464
60466
  };
60465
60467
  }
60466
60468
  function buildProfileCreateWakeReadinessRows(input) {
60467
60469
  if (input.state !== "created" || input.profile.profileKind !== "agent") return [];
60468
60470
  return [{
60469
- label: "Wake availability",
60470
- value: "Not verified by profile creation"
60471
+ label: "Space identity",
60472
+ value: "Created"
60471
60473
  }, {
60472
60474
  label: "Next",
60473
- value: `Run ${formatAtsCliCommand(`ats setup --profile ${input.profile.atsProfileId} --local-agent <local-agent-id>`)} before expecting this Agent Profile to reply from a Space.`
60475
+ value: "Add this profile to a Space when the user wants this teammate to participate."
60474
60476
  }];
60475
60477
  }
60476
60478
  function buildAgentUpdateNextSteps(profile) {
60479
+ if (profile.profileKind === "agent") return [formatAtsCliCommand(`ats profiles show ${profile.atsProfileId} --view agent`), formatAtsCliCommand(`ats space add-members <space-id> --member ${profile.atsProfileId} --profile <human-profile-id> --view agent`)];
60477
60480
  return [
60478
60481
  formatAtsCliCommand(`ats profiles show ${profile.atsProfileId} --view agent`),
60479
60482
  formatAtsCliCommand(`ats profiles update ${profile.atsProfileId} --name <new-name> --view agent`),
60480
- formatAtsCliCommand(`ats setup --profile ${profile.atsProfileId} --local-agent <local-agent-id> --view agent`),
60481
- formatAtsCliCommand(`ats setup --profile ${profile.atsProfileId} --view agent`),
60482
- formatAtsCliCommand(`ats profiles update ${profile.atsProfileId} --workspace-mode ats-managed --view agent`),
60483
- formatAtsCliCommand(`ats profiles update ${profile.atsProfileId} --workspace-mode custom --workspace-path /absolute/path --view agent`),
60484
- formatAtsCliCommand(`ats whoami --profile ${profile.atsProfileId} --view agent`),
60485
60483
  formatAtsCliCommand("ats profiles list --view agent")
60486
60484
  ];
60487
60485
  }
@@ -69476,7 +69474,7 @@ const STAGES_BY_COMMAND = {
69476
69474
  "resolve_target",
69477
69475
  "read_or_update_guide"
69478
69476
  ],
69479
- add_members: SPACE_ADD_MEMBERS_PROGRESS_OVERVIEW_STAGES,
69477
+ add_members: SPACE_ADD_MEMBERS_AGENT_OVERVIEW_STAGES,
69480
69478
  remove_members: [
69481
69479
  "check_arguments",
69482
69480
  "authenticate",
@@ -83782,13 +83780,13 @@ function handleBlockedSpaceJoinLocalParticipation(input) {
83782
83780
  if (input.assessmentState === "not_needed") {
83783
83781
  input.presenter.line({
83784
83782
  code: "space.join.service.not_needed",
83785
- text: formatInlineAtsCliCommands("ATS will keep joining this space without local Wake participation on this device. Space membership is separate from whether a local agent can reply from this computer. If you later want local agents from this device to reply, run `ats service status` and follow its recovery guidance.")
83783
+ text: formatInlineAtsCliCommands("ATS will join this space. Local agent participation on this device can be checked later with `ats service status`.")
83786
83784
  });
83787
83785
  return "continue";
83788
83786
  }
83789
83787
  input.presenter.line({
83790
83788
  code: "space.join.service.pending",
83791
- text: formatInlineAtsCliCommands("ATS will keep joining this space without local Wake participation on this device. Space membership is separate from whether a local agent can reply from this computer. Run `ats service status` to inspect local participation before expecting background replies.")
83789
+ text: formatInlineAtsCliCommands("ATS will join this space. Local agent participation on this device can be checked separately with `ats service status`.")
83792
83790
  });
83793
83791
  return "continue";
83794
83792
  }
@@ -83801,10 +83799,10 @@ function buildSpaceJoinMembershipOnlyMessage(input) {
83801
83799
  const profileId = input.atsProfile.atsProfileId;
83802
83800
  const reasonCodes = input.reasonCodes;
83803
83801
  const statusCommand = formatAtsCliCommand(`ats service status --profile ${profileId}`);
83804
- if (reasonCodes.includes("profile.unbound")) return `${profileName} will join this space, but it is not connected to a local agent on this device yet. It can appear here now, but it will not wake or reply from this device until you choose a local agent in ATS Web.`;
83805
- if (reasonCodes.some((reasonCode) => LOCAL_SERVICE_ONLY_JOIN_BLOCK_REASON_CODES.has(reasonCode)) || reasonCodes.includes("dispatch.storage_not_ready") || reasonCodes.includes("service.gateway_chain_unhealthy") || reasonCodes.includes("route.offline") || reasonCodes.includes("route.not_registered")) return formatInlineAtsCliCommands(`${profileName} will join this space, but it will not wake or reply from this device yet. Space membership is separate from local Wake participation. Use \`${statusCommand}\` to check this device; if local participation is unhealthy, follow the status or repair guidance before expecting background replies.`);
83802
+ if (reasonCodes.includes("profile.unbound")) return `${profileName} will join this space. Choose its local agent in ATS Web when you want it to handle work from this device.`;
83803
+ if (reasonCodes.some((reasonCode) => LOCAL_SERVICE_ONLY_JOIN_BLOCK_REASON_CODES.has(reasonCode)) || reasonCodes.includes("dispatch.storage_not_ready") || reasonCodes.includes("service.gateway_chain_unhealthy") || reasonCodes.includes("route.offline") || reasonCodes.includes("route.not_registered")) return formatInlineAtsCliCommands(`${profileName} will join this space. ATS has not confirmed its local reply path on this device yet. Use \`${statusCommand}\` to check this device.`);
83806
83804
  if (reasonCodes.includes("controller.launch.needs_repair") || reasonCodes.includes("controller.launch.choice_required") || reasonCodes.includes("workspace.missing") || reasonCodes.includes("workspace.invalid")) return formatInlineAtsCliCommands(`${profileName} will join this space, but its local agent setup on this device needs attention. Use \`ats agents list\` to find the local agent, then run \`ats agents repair --agent <agent-id>\`.`);
83807
- return `${profileName} will join this space, but local setup on this device is still incomplete. It can appear here now, but it will not wake or reply from this device until you check Local Agents in ATS Web.`;
83805
+ return `${profileName} will join this space. Check Local Agents in ATS Web when you want it to handle work from this device.`;
83808
83806
  }
83809
83807
  function buildJoinTargetMissingMessage(input) {
83810
83808
  return formatMessage(input.resolvedView, "space.target.missing", { command: "join" });
@@ -85054,6 +85052,8 @@ const SPACE_MEMBER_PROFILE_RECONNECT_REASON_CODES = new Set([
85054
85052
  "local_service.identity_mismatch"
85055
85053
  ]);
85056
85054
  const SPACE_MEMBER_SERVICE_STATUS_REASON_CODES = new Set([
85055
+ "agent_profile_binding.local_service_not_connected",
85056
+ "agent_profile_binding.execution_readiness_pending",
85057
85057
  "route.offline",
85058
85058
  "route.not_registered",
85059
85059
  "service.gateway_chain_unhealthy",
@@ -85617,6 +85617,11 @@ function toAuthEndpoint(baseUrl, pathname) {
85617
85617
  function buildSpaceWebUrl(baseUrl, spaceId) {
85618
85618
  const url = new URL(baseUrl);
85619
85619
  if (isLoopbackHostname(url.hostname) && url.port === "8080") url.port = "3000";
85620
+ if (url.hostname === "gateway.ats.sh") {
85621
+ url.hostname = "ats.sh";
85622
+ url.port = "";
85623
+ url.protocol = "https:";
85624
+ }
85620
85625
  url.pathname = `/app/spaces/${encodeURIComponent(spaceId)}`;
85621
85626
  url.search = "";
85622
85627
  url.hash = "";
@@ -86129,7 +86134,7 @@ async function runSpaceAddMembers(input) {
86129
86134
  resolvedView: runtime.resolvedView,
86130
86135
  interactive
86131
86136
  });
86132
- await emitSpaceCommandPreflight({
86137
+ if (shouldRunSpaceAddMembersDiagnosticPreflight(input, explicitMemberIds)) await emitSpaceCommandPreflight({
86133
86138
  presenter,
86134
86139
  command: "add_members",
86135
86140
  profile: atsProfile,
@@ -86321,6 +86326,12 @@ async function runSpaceLeave(input) {
86321
86326
  throw error;
86322
86327
  }
86323
86328
  }
86329
+ function shouldRunSpaceAddMembersDiagnosticPreflight(input, explicitMemberIds) {
86330
+ return input.details === true || input.all === true || explicitMemberIds.length === 0;
86331
+ }
86332
+ function shouldUseFastExplicitSpaceAddMembers(input) {
86333
+ return input.details !== true && input.explicitMemberIds.length > 0;
86334
+ }
86324
86335
  async function runSpaceAddMembersForTarget(input) {
86325
86336
  const targetBaseUrl = input.hasExplicitBaseUrlInput ? input.baseUrl : input.target.baseUrl ?? input.baseUrl;
86326
86337
  const authenticatedGatewayUrl = await ensureSpaceCommandGatewayAuthenticationOrCancel({
@@ -86338,6 +86349,7 @@ async function runSpaceAddMembersForTarget(input) {
86338
86349
  };
86339
86350
  const explicitMemberIds = normalizeRequestedMemberIds(input.explicitMembers);
86340
86351
  const progress = input.details || explicitMemberIds.length === 0 ? addMembersProgress : void 0;
86352
+ const includeLocalParticipation = progress !== void 0 || explicitMemberIds.length === 0;
86341
86353
  const candidates = await resolveSpaceMemberCandidatesForAdd({
86342
86354
  currentProfile: input.atsProfile,
86343
86355
  currentOwnerUserId: input.atsProfile.ownerUserId,
@@ -86345,7 +86357,12 @@ async function runSpaceAddMembersForTarget(input) {
86345
86357
  baseUrl: targetBaseUrl,
86346
86358
  password: input.password,
86347
86359
  explicitMemberIds,
86348
- progress
86360
+ progress,
86361
+ fastExplicitMembers: shouldUseFastExplicitSpaceAddMembers({
86362
+ details: input.details,
86363
+ explicitMemberIds
86364
+ }),
86365
+ includeLocalParticipation
86349
86366
  });
86350
86367
  if (candidates.length === 0 && explicitMemberIds.length === 0) {
86351
86368
  input.presenter.line({
@@ -86377,15 +86394,10 @@ async function runSpaceAddMembersForTarget(input) {
86377
86394
  selectedProfileIds
86378
86395
  });
86379
86396
  if (selectedCandidates.length === 0) return { status: "completed" };
86380
- emitSpaceAddMembersProgressIfRequested(progress, {
86381
- stage: "check_local_wake_readiness",
86382
- phase: "start",
86383
- summary: "Checking whether selected agent profiles can wake from this computer. Members can still be added if local Wake setup needs attention.",
86384
- profileIds: selectedCandidates.map((candidate) => candidate.profileId)
86385
- });
86386
- const servicePreparationResult = await ensureSpaceMemberCandidatesLocalParticipationReady({
86397
+ const servicePreparationResult = await prepareSpaceAddMembersLocalParticipationIfRequested({
86387
86398
  atsProfile: input.atsProfile,
86388
86399
  candidates: selectedCandidates,
86400
+ enabled: includeLocalParticipation,
86389
86401
  progress,
86390
86402
  presenter: input.presenter,
86391
86403
  resolvedView: input.runtime.resolvedView,
@@ -86455,6 +86467,28 @@ async function runSpaceAddMembersForTarget(input) {
86455
86467
  });
86456
86468
  return { status: "completed" };
86457
86469
  }
86470
+ async function prepareSpaceAddMembersLocalParticipationIfRequested(input) {
86471
+ if (!input.enabled) return {
86472
+ status: "continue",
86473
+ candidates: input.candidates
86474
+ };
86475
+ emitSpaceAddMembersProgressIfRequested(input.progress, {
86476
+ stage: "check_local_wake_readiness",
86477
+ phase: "start",
86478
+ summary: "Checking whether selected agent profiles can handle work from this computer. Members can still be added if local setup needs attention.",
86479
+ profileIds: input.candidates.map((candidate) => candidate.profileId)
86480
+ });
86481
+ return await ensureSpaceMemberCandidatesLocalParticipationReady({
86482
+ atsProfile: input.atsProfile,
86483
+ candidates: input.candidates,
86484
+ progress: input.progress,
86485
+ presenter: input.presenter,
86486
+ resolvedView: input.resolvedView,
86487
+ allowPrompt: input.allowPrompt,
86488
+ spaceId: input.spaceId,
86489
+ view: input.view
86490
+ });
86491
+ }
86458
86492
  function emitSpaceAddMembersProgressIfRequested(progress, input) {
86459
86493
  if (!progress) return;
86460
86494
  emitAgentSpaceAddMembersProgress({
@@ -86503,13 +86537,13 @@ function buildSpaceAddMembersLocalWakePlanSummary(plan) {
86503
86537
  if (assessment.profileKind !== "agent") continue;
86504
86538
  counts.set(assessment.localParticipationState, (counts.get(assessment.localParticipationState) ?? 0) + 1);
86505
86539
  }
86506
- return `Local Wake plan: ${[
86507
- formatLocalWakePlanCount(counts.get("ready") ?? 0, "available for local Wake"),
86540
+ return `Local agent plan: ${[
86541
+ formatLocalWakePlanCount(counts.get("ready") ?? 0, "can handle work from this device"),
86508
86542
  formatLocalWakePlanCount(counts.get("needs_agent_prepare") ?? 0, "need agent preparation"),
86509
86543
  formatLocalWakePlanCount(counts.get("needs_ats_service") ?? 0, "need ATS Service"),
86510
86544
  formatLocalWakePlanCount(counts.get("membership_only") ?? 0, "will be added as members only"),
86511
- formatLocalWakePlanCount(counts.get("not_needed") ?? 0, "do not need local Wake setup")
86512
- ].filter((part) => Boolean(part)).join(", ")}. Adding members is separate from making them available for Wake on this computer.`;
86545
+ formatLocalWakePlanCount(counts.get("not_needed") ?? 0, "do not need local agent setup")
86546
+ ].filter((part) => Boolean(part)).join(", ")}. Adding members is separate from local agent participation on this computer.`;
86513
86547
  }
86514
86548
  function formatLocalWakePlanCount(count, label) {
86515
86549
  return count > 0 ? `${String(count)} ${label}` : null;
@@ -86672,7 +86706,7 @@ async function ensureSpaceMemberCandidatesLocalParticipationReady(input) {
86672
86706
  candidates: preparedCandidates.map(toActionParticipationCandidateFromSpaceMemberCandidate)
86673
86707
  }).shouldOfferServiceGate) input.presenter.line({
86674
86708
  code: input.resolvedView === "human" && input.allowPrompt ? "space.add_members.service.declined" : "space.add_members.service.deferred",
86675
- text: formatInlineAtsCliCommands("ATS will continue adding these profiles without local Wake participation on this device. Space membership is separate from whether a local agent can reply from this computer. Run `ats service status` to inspect local participation before expecting background replies.")
86709
+ text: formatInlineAtsCliCommands("ATS will add these profiles to the Space. Local agent participation can be checked separately with `ats service status`.")
86676
86710
  });
86677
86711
  return {
86678
86712
  status: "continue",
@@ -86746,17 +86780,18 @@ function emitSpaceMemberJoinOnlyWarnings(input) {
86746
86780
  }
86747
86781
  function buildSpaceMemberJoinOnlyWarning(candidate) {
86748
86782
  if (candidate.profileKind !== "agent") return null;
86783
+ if (candidate.localParticipationChecked !== true) return null;
86749
86784
  if (isSpaceMemberCandidateBackgroundReplyReady(candidate)) return null;
86750
86785
  const profileLabel = resolveSpaceMemberTerminalProfileLabel(candidate.profileName);
86751
- if (isSpaceMemberCandidateRepairableLocalParticipationCandidate(candidate)) return `${profileLabel} can join this space now, but ${resolveSpaceMemberTerminalLocalTargetLabel({
86786
+ if (isSpaceMemberCandidateRepairableLocalParticipationCandidate(candidate)) return `${profileLabel} can join this space now. ${resolveSpaceMemberTerminalLocalTargetLabel({
86752
86787
  displayName: candidate.localTargetDisplayName,
86753
86788
  agentId: candidate.localTargetAgentId
86754
- })} still needs local setup on this device before it can wake or reply in the background. Use \`${buildSpaceMemberRepairCommand({
86789
+ })} still needs local setup on this device before it can handle work from the Space. Use \`${buildSpaceMemberRepairCommand({
86755
86790
  agentId: candidate.localTargetAgentId,
86756
86791
  profileId: candidate.profileId
86757
- })}\` before expecting background replies.`;
86792
+ })}\` to repair the local agent.`;
86758
86793
  const nextAction = resolveSpaceMemberJoinOnlyNextAction(candidate);
86759
- return `${profileLabel} can join this space now, but it will not wake or reply from this device yet. ${resolveSpaceMemberJoinOnlyReason(candidate)} ${formatSpaceMemberRecoveryNextAction(nextAction)}`;
86794
+ return `${profileLabel} can join this space now. ATS has not confirmed its local reply path on this device yet. ${resolveSpaceMemberJoinOnlyReason(candidate)} ${formatSpaceMemberRecoveryNextAction(nextAction)}`;
86760
86795
  }
86761
86796
  function resolveSpaceMemberJoinOnlyNextAction(candidate) {
86762
86797
  if (candidate.localParticipationReadiness === "not_running") return {
@@ -86834,6 +86869,7 @@ async function refreshSpaceMemberCandidatesLocalReadiness(candidates, progress,
86834
86869
  async function enrichSpaceMemberCandidateLocalParticipationReadiness(candidate, progress, options = {}) {
86835
86870
  if (candidate.profileKind !== "agent" || !candidate.atsProfile || candidate.controllerEnabled !== true || typeof candidate.controllerRef !== "string" || candidate.controllerRef.trim().length === 0) return {
86836
86871
  ...candidate,
86872
+ localParticipationChecked: true,
86837
86873
  localParticipationReadiness: null,
86838
86874
  localParticipationReasonCodes: [],
86839
86875
  localParticipationReasonText: null
@@ -86872,6 +86908,7 @@ async function enrichSpaceMemberCandidateLocalParticipationReadiness(candidate,
86872
86908
  }
86873
86909
  if (!snapshot) return {
86874
86910
  ...candidate,
86911
+ localParticipationChecked: true,
86875
86912
  localParticipationReadiness: null,
86876
86913
  localParticipationReasonCodes: [],
86877
86914
  localParticipationReasonText: null
@@ -86883,6 +86920,7 @@ async function enrichSpaceMemberCandidateLocalParticipationReadiness(candidate,
86883
86920
  });
86884
86921
  return {
86885
86922
  ...candidate,
86923
+ localParticipationChecked: true,
86886
86924
  localParticipationReadiness: readinessSummary.serviceReadiness,
86887
86925
  localParticipationReasonCodes: [...snapshot.deviceReplyReadiness.reasonCodes],
86888
86926
  localParticipationReasonText: snapshot.deviceReplyReadiness.reasonText
@@ -88159,6 +88197,9 @@ function mapSpaceLeaveFailureToGuideError(input) {
88159
88197
  return input.error instanceof Error ? input.error : new Error(String(input.error));
88160
88198
  }
88161
88199
  async function resolveSpaceMemberCandidatesForAdd(input) {
88200
+ const explicitMemberIds = normalizeRequestedMemberIds(input.explicitMemberIds);
88201
+ const includeLocalParticipation = input.includeLocalParticipation !== false;
88202
+ if (input.fastExplicitMembers === true && explicitMemberIds.length > 0) return explicitMemberIds.map(buildFastExplicitSpaceMemberCandidate);
88162
88203
  if (input.progress) emitAgentSpaceAddMembersProgress({
88163
88204
  ...input.progress,
88164
88205
  stage: "resolve_candidates",
@@ -88166,23 +88207,19 @@ async function resolveSpaceMemberCandidatesForAdd(input) {
88166
88207
  summary: "Reading active local profile identities for this Space."
88167
88208
  });
88168
88209
  const profiles = await listAtsProfiles();
88169
- if (input.progress) emitAgentSpaceAddMembersProgress({
88210
+ if (input.progress && includeLocalParticipation) emitAgentSpaceAddMembersProgress({
88170
88211
  ...input.progress,
88171
88212
  stage: "resolve_candidates",
88172
88213
  phase: "read_local_targets",
88173
88214
  summary: "Reading local agent controller state for candidate profiles before selection."
88174
88215
  });
88175
- const localTargetStates = input.progress ? await runSpaceAddMembersProgressHeartbeat({
88176
- ...input.progress,
88177
- stage: "resolve_candidates",
88178
- phase: "read_local_targets_wait",
88179
- summary: "Still reading local agent controller state for candidate profiles before selection.",
88180
- operation: async () => await listAgentTargetStates().catch(() => [])
88181
- }) : await listAgentTargetStates().catch(() => []);
88216
+ const localTargetStates = await listSpaceAddMembersLocalTargetStatesIfRequested({
88217
+ includeLocalParticipation,
88218
+ progress: input.progress
88219
+ });
88182
88220
  const localTargetStateById = new Map(localTargetStates.map((state) => [state.agentId, state]));
88183
88221
  const ownerProfileNameByOwnerUserId = buildOwnerProfileNameByOwnerUserId(profiles);
88184
88222
  const candidateProfiles = profiles.filter((profile) => profile.status === "active" && isSpaceMembershipProfileKind(profile.profileKind) && profile.atsProfileId !== input.currentProfile.atsProfileId).filter((profile) => {
88185
- const explicitMemberIds = input.explicitMemberIds ?? [];
88186
88223
  if (explicitMemberIds.length > 0 && !explicitMemberIds.includes(profile.atsProfileId)) return false;
88187
88224
  return typeof input.currentOwnerUserId !== "string" || input.currentOwnerUserId.trim().length === 0 || profile.ownerUserId === input.currentOwnerUserId;
88188
88225
  });
@@ -88199,29 +88236,58 @@ async function resolveSpaceMemberCandidatesForAdd(input) {
88199
88236
  ...input.password === void 0 ? {} : { spacePassword: input.password }
88200
88237
  });
88201
88238
  const memberProfileIds = new Set([...membersSnapshot.humans, ...membersSnapshot.agents].map((member) => member.profileId));
88202
- return await refreshSpaceMemberCandidatesLocalReadiness((await Promise.all(candidateProfiles.map((profile) => buildSpaceMemberCandidateFromProfile({
88239
+ const candidates = (await Promise.all(candidateProfiles.map((profile) => buildSpaceMemberCandidateFromProfile({
88203
88240
  baseUrl: input.baseUrl,
88204
88241
  profile,
88205
88242
  localTargetStateById,
88206
88243
  ownerProfileName: ownerProfileNameByOwnerUserId.get(profile.ownerUserId) ?? void 0,
88207
88244
  alreadyJoined: memberProfileIds.has(profile.atsProfileId),
88208
- progress: input.progress
88245
+ progress: input.progress,
88246
+ includeLocalParticipation
88209
88247
  })))).sort((left, right) => {
88210
88248
  const byName = left.profileName.localeCompare(right.profileName);
88211
88249
  if (byName !== 0) return byName;
88212
88250
  return left.profileId.localeCompare(right.profileId);
88213
- }), input.progress, { stage: "resolve_candidates" });
88251
+ });
88252
+ if (!includeLocalParticipation) return candidates;
88253
+ return await refreshSpaceMemberCandidatesLocalReadiness(candidates, input.progress, { stage: "resolve_candidates" });
88214
88254
  }
88215
- async function buildSpaceMemberCandidateFromProfile(input) {
88216
- if (input.progress) emitAgentSpaceAddMembersProgress({
88255
+ function buildFastExplicitSpaceMemberCandidate(profileId) {
88256
+ return {
88257
+ profileId,
88258
+ profileName: profileId,
88259
+ profileKind: profileId.startsWith("agt_") ? "agent" : "human",
88260
+ targetMentionLabel: null,
88261
+ localTargetAgentId: null,
88262
+ localTargetDisplayName: null,
88263
+ localTargetLaunchStatus: null,
88264
+ localTargetSelectable: false,
88265
+ localParticipationReadiness: null,
88266
+ localParticipationChecked: false,
88267
+ localParticipationReasonCodes: [],
88268
+ localParticipationReasonText: null,
88269
+ alreadyJoined: false,
88270
+ needsActivation: false
88271
+ };
88272
+ }
88273
+ async function listSpaceAddMembersLocalTargetStatesIfRequested(input) {
88274
+ if (!input.includeLocalParticipation) return [];
88275
+ if (!input.progress) return await listAgentTargetStates().catch(() => []);
88276
+ return await runSpaceAddMembersProgressHeartbeat({
88217
88277
  ...input.progress,
88218
88278
  stage: "resolve_candidates",
88219
- phase: "read_runtime_capability",
88220
- summary: `Reading runtime capability projection for ${resolveSpaceMemberTerminalProfileLabel(input.profile.profileName)}.`,
88221
- profileId: input.profile.atsProfileId,
88222
- profileIds: [input.profile.atsProfileId]
88279
+ phase: "read_local_targets_wait",
88280
+ summary: "Still reading local agent controller state for candidate profiles before selection.",
88281
+ operation: async () => await listAgentTargetStates().catch(() => [])
88223
88282
  });
88224
- const runtimeCapability = input.progress ? await runSpaceAddMembersProgressHeartbeat({
88283
+ }
88284
+ async function resolveSpaceMemberCandidateRuntimeCapabilityIfRequested(input) {
88285
+ if (!input.includeLocalParticipation) return null;
88286
+ if (!input.progress) return await resolveAgentProfilePrimaryRuntimeCapability({
88287
+ baseUrl: input.baseUrl,
88288
+ profile: input.profile
88289
+ }).catch(() => null);
88290
+ return await runSpaceAddMembersProgressHeartbeat({
88225
88291
  ...input.progress,
88226
88292
  stage: "resolve_candidates",
88227
88293
  phase: "read_runtime_capability_wait",
@@ -88232,10 +88298,24 @@ async function buildSpaceMemberCandidateFromProfile(input) {
88232
88298
  baseUrl: input.baseUrl,
88233
88299
  profile: input.profile
88234
88300
  }).catch(() => null)
88235
- }) : await resolveAgentProfilePrimaryRuntimeCapability({
88301
+ });
88302
+ }
88303
+ async function buildSpaceMemberCandidateFromProfile(input) {
88304
+ const includeLocalParticipation = input.includeLocalParticipation !== false;
88305
+ if (input.progress && includeLocalParticipation) emitAgentSpaceAddMembersProgress({
88306
+ ...input.progress,
88307
+ stage: "resolve_candidates",
88308
+ phase: "read_runtime_capability",
88309
+ summary: `Reading runtime capability projection for ${resolveSpaceMemberTerminalProfileLabel(input.profile.profileName)}.`,
88310
+ profileId: input.profile.atsProfileId,
88311
+ profileIds: [input.profile.atsProfileId]
88312
+ });
88313
+ const runtimeCapability = await resolveSpaceMemberCandidateRuntimeCapabilityIfRequested({
88236
88314
  baseUrl: input.baseUrl,
88237
- profile: input.profile
88238
- }).catch(() => null);
88315
+ includeLocalParticipation,
88316
+ profile: input.profile,
88317
+ progress: input.progress
88318
+ });
88239
88319
  const controllerRef = runtimeCapability?.capabilityRef ?? void 0;
88240
88320
  const controllerEnabled = runtimeCapability?.bindingState === "enabled_primary" && runtimeCapability.routable;
88241
88321
  const localTargetAgentId = resolveSpaceMemberCandidateLocalTargetAgentId({
@@ -88260,6 +88340,7 @@ async function buildSpaceMemberCandidateFromProfile(input) {
88260
88340
  localTargetLaunchStatus: localTargetState?.launchStatus ?? null,
88261
88341
  localTargetSelectable: localTargetState?.selectable ?? false,
88262
88342
  localParticipationReadiness: null,
88343
+ localParticipationChecked: false,
88263
88344
  localParticipationReasonCodes: [],
88264
88345
  localParticipationReasonText: null,
88265
88346
  alreadyJoined: input.alreadyJoined,
@@ -88588,14 +88669,19 @@ async function applySpaceAddMembers(input) {
88588
88669
  }
88589
88670
  const addedProfileIds = new Set(response.addedMemberIds);
88590
88671
  const alreadyMemberProfileIds = new Set(response.alreadyMemberIds);
88672
+ const responseItemByProfileId = new Map(response.items.map((item) => [item.profileId, item]));
88591
88673
  const lastJoinedAt = (/* @__PURE__ */ new Date()).toISOString();
88592
88674
  const spaceName = normalizeOptionalString$1(input.spaceName);
88593
88675
  for (const candidate of selectedCandidates) {
88676
+ const nextCandidate = hydrateSpaceMemberCandidateFromResponseItem({
88677
+ candidate,
88678
+ item: responseItemByProfileId.get(candidate.profileId)
88679
+ });
88594
88680
  if (addedProfileIds.has(candidate.profileId)) {
88595
- result.added.push(candidate);
88681
+ result.added.push(nextCandidate);
88596
88682
  continue;
88597
88683
  }
88598
- if (alreadyMemberProfileIds.has(candidate.profileId)) result.alreadyJoined.push(candidate);
88684
+ if (alreadyMemberProfileIds.has(candidate.profileId)) result.alreadyJoined.push(nextCandidate);
88599
88685
  }
88600
88686
  for (const candidate of result.added) try {
88601
88687
  await upsertProfileSpaceHistory({
@@ -88622,6 +88708,27 @@ async function applySpaceAddMembers(input) {
88622
88708
  }
88623
88709
  return result;
88624
88710
  }
88711
+ function hydrateSpaceMemberCandidateFromResponseItem(input) {
88712
+ if (!input.item) return input.candidate;
88713
+ return {
88714
+ ...input.candidate,
88715
+ profileName: input.item.profileName,
88716
+ profileKind: input.item.profileKind,
88717
+ ownerUserId: normalizeOptionalString$1(input.item.ownerUserId) ?? void 0,
88718
+ ownerName: normalizeOptionalString$1(input.item.ownerName) ?? void 0,
88719
+ targetMentionLabel: resolvePrimaryMentionLabelFromSpaceMemberItem(input.item),
88720
+ ...input.item.profileKind === "agent" ? { controllerRef: normalizeOptionalString$1(input.item.controllerRef) ?? input.candidate.controllerRef } : {}
88721
+ };
88722
+ }
88723
+ function resolvePrimaryMentionLabelFromSpaceMemberItem(item) {
88724
+ const mentionAlias = normalizeOptionalString$1(item.mentionAlias);
88725
+ if (mentionAlias) return mentionAlias;
88726
+ return resolveSingleSpaceMentionLabels({
88727
+ mentionAlias,
88728
+ profileId: item.profileId,
88729
+ profileName: item.profileName
88730
+ }).primaryMentionLabel;
88731
+ }
88625
88732
  async function waitForAddedMembersWakeProjection(input) {
88626
88733
  const expectedWakeableProfileIds = resolveExpectedWakeProjectionProfileIds(input.result);
88627
88734
  if (expectedWakeableProfileIds.length === 0) return;
@@ -88728,6 +88835,7 @@ function emitSpaceAddMembersResult(input) {
88728
88835
  }
88729
88836
  function buildSpaceMemberJoinedFollowUp(candidate) {
88730
88837
  if (candidate.profileKind !== "agent") return null;
88838
+ if (candidate.localParticipationChecked !== true) return null;
88731
88839
  if (isSpaceMemberCandidateRepairableLocalParticipationCandidate(candidate)) {
88732
88840
  const repairCommand = buildSpaceMemberRepairCommand({
88733
88841
  agentId: candidate.localTargetAgentId,
@@ -88741,7 +88849,7 @@ function buildSpaceMemberJoinedFollowUp(candidate) {
88741
88849
  return {
88742
88850
  profileId: candidate.profileId,
88743
88851
  reasonCodes: candidate.localParticipationReasonCodes ?? [],
88744
- text: `${profileLabel} joined this space, but ${agentLabel} still needs repair on this device before it can wake or reply in the background. Use \`${repairCommand}\` before expecting background replies.`
88852
+ text: `${profileLabel} joined this space. ${agentLabel} still needs repair on this device before it can handle work from the Space. Use \`${repairCommand}\` to repair the local agent.`
88745
88853
  };
88746
88854
  }
88747
88855
  if (!isSpaceMemberCandidateRunnableLocalParticipationCandidate(candidate)) {
@@ -88750,7 +88858,7 @@ function buildSpaceMemberJoinedFollowUp(candidate) {
88750
88858
  return {
88751
88859
  profileId: candidate.profileId,
88752
88860
  reasonCodes: candidate.localParticipationReasonCodes ?? [],
88753
- text: `${profileLabel} joined this space, but it is not connected to a local agent on this device yet. It can appear here now, but it will not wake or reply from this device until you choose a local agent. ${formatSpaceMemberRecoveryNextAction(nextAction)}`
88861
+ text: `${profileLabel} joined this space. ATS has not confirmed its local reply path on this device yet. ${formatSpaceMemberRecoveryNextAction(nextAction)}`
88754
88862
  };
88755
88863
  }
88756
88864
  return null;
@@ -88985,8 +89093,8 @@ async function persistSpaceAddMembersJoinNotices(input) {
88985
89093
  failedCount: 0
88986
89094
  };
88987
89095
  const failures = [];
88988
- let targetMentionLabelByProfileId;
88989
- try {
89096
+ let targetMentionLabelByProfileId = buildKnownMentionLabelByProfileId(input.addedMembers);
89097
+ if (!hasMentionLabelsForAllMembers(input.addedMembers, targetMentionLabelByProfileId)) try {
88990
89098
  targetMentionLabelByProfileId = buildSpacePrimaryMentionLabelByProfileId(await createCliSpaceApi(input.baseUrl).getMembersSnapshot({
88991
89099
  spaceId: input.space,
88992
89100
  requestContext: buildAtsRequestContextFromProfile({ atsProfile: input.actorProfile }),
@@ -89032,6 +89140,17 @@ async function persistSpaceAddMembersJoinNotices(input) {
89032
89140
  ...failures[0] ? { firstFailure: toStructuredGatewayFailure(failures[0].error) } : {}
89033
89141
  };
89034
89142
  }
89143
+ function buildKnownMentionLabelByProfileId(members) {
89144
+ const labelsByProfileId = /* @__PURE__ */ new Map();
89145
+ for (const member of members) {
89146
+ const targetMentionLabel = normalizeOptionalString$1(member.targetMentionLabel);
89147
+ if (targetMentionLabel) labelsByProfileId.set(member.profileId, targetMentionLabel);
89148
+ }
89149
+ return labelsByProfileId;
89150
+ }
89151
+ function hasMentionLabelsForAllMembers(members, labelsByProfileId) {
89152
+ return members.every((member) => labelsByProfileId.has(member.profileId));
89153
+ }
89035
89154
  async function postSpaceAddMemberJoinNotice(input) {
89036
89155
  const requestContext = buildAtsRequestContextFromProfile({ atsProfile: input.actorProfile });
89037
89156
  await createCliSpaceApi(input.baseUrl).postNormalMessage({
@@ -89217,8 +89336,9 @@ function resolveSpaceMemberCandidateStatusLabel(candidate) {
89217
89336
  const TRAILING_PERIOD_RE = /\.$/u;
89218
89337
  function resolveSpaceMemberCandidateStatusBadges(candidate) {
89219
89338
  if (candidate.profileKind !== "agent") return [];
89339
+ if (candidate.localParticipationChecked !== true) return [];
89220
89340
  if (isSpaceMemberCandidateBackgroundReplyReady(candidate)) return ["Local setup connected"];
89221
- if (isSpaceMemberCandidateRepairableLocalParticipationCandidate(candidate)) return ["Needs repair before background replies"];
89341
+ if (isSpaceMemberCandidateRepairableLocalParticipationCandidate(candidate)) return ["Needs repair"];
89222
89342
  return [resolveSpaceMemberJoinOnlyReason(candidate).replace(TRAILING_PERIOD_RE, ""), "Joins only for now"];
89223
89343
  }
89224
89344
  function isSpaceMemberCandidateBackgroundReplyReady(candidate) {
@@ -89242,7 +89362,7 @@ function resolveSpaceMemberProfileConnectionAction(candidate) {
89242
89362
  kind: "instruction"
89243
89363
  };
89244
89364
  if (candidate.controllerEnabled !== true || typeof candidate.controllerRef !== "string" || candidate.controllerRef.trim().length === 0 || reasonCodes.some((reasonCode) => SPACE_MEMBER_PROFILE_CONNECTION_REASON_CODES.has(reasonCode))) return {
89245
- instruction: "Open this Agent in ATS Web and choose the local agent that should handle Wake work.",
89365
+ instruction: "Open this Agent in ATS Web and choose the local agent that should handle work from the Space.",
89246
89366
  kind: "instruction"
89247
89367
  };
89248
89368
  if (reasonCodes.includes("controller.binding.disabled")) return {
@@ -89255,9 +89375,9 @@ function hasSpaceMemberReasonCode(candidate, reasonCodes) {
89255
89375
  return (candidate.localParticipationReasonCodes ?? []).some((reasonCode) => reasonCodes.has(reasonCode));
89256
89376
  }
89257
89377
  function formatSpaceMemberRecoveryNextAction(action) {
89258
- if (action.kind === "instruction") return `${action.instruction} Do this before expecting background replies.`;
89259
- if (action.command === "ats agents list") return formatInlineAtsCliCommands("Use `ats agents list` to find the local agent that needs repair before expecting background replies.");
89260
- return `Use \`${formatAtsCliCommand(action.command)}\` before expecting background replies.`;
89378
+ if (action.kind === "instruction") return action.instruction;
89379
+ if (action.command === "ats agents list") return formatInlineAtsCliCommands("Use `ats agents list` to find the local agent that needs repair.");
89380
+ return `Use \`${formatAtsCliCommand(action.command)}\` to check the local reply path.`;
89261
89381
  }
89262
89382
  function resolveSpaceMemberTerminalProfileLabel(profileName) {
89263
89383
  return sanitizeSingleLinePromptText(normalizeOptionalString$1(profileName)) || "This agent";