agent-transport-system 0.3.0 → 0.3.2

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
@@ -24,11 +24,11 @@ import wrapAnsi from "wrap-ansi";
24
24
  import { Box, Container, Editor, Key, ProcessTerminal, TUI, Text, getEditorKeybindings, matchesKey } from "@mariozechner/pi-tui";
25
25
 
26
26
  //#region package.json
27
- var version$1 = "0.3.0";
27
+ var version$1 = "0.3.2";
28
28
  var package_default = {
29
29
  name: "agent-transport-system",
30
30
  version: version$1,
31
- atsReleaseDate: "2026-03-12",
31
+ atsReleaseDate: "2026-04-16",
32
32
  description: "Agent Transport System CLI - https://ats.sh",
33
33
  license: "MIT",
34
34
  type: "module",
@@ -4319,7 +4319,7 @@ const daemonRouteObservationSummarySchema = strictObject({
4319
4319
  });
4320
4320
 
4321
4321
  //#endregion
4322
- //#region ../../packages/schemas/dist/entry-brief-LY-buFs9.js
4322
+ //#region ../../packages/schemas/dist/entry-brief-B2jV7t7E.js
4323
4323
  const providerConversationExternalImportCapabilitySchema = _enum(["unsupported", "by_ref_id"]);
4324
4324
  const providerConversationResumeCapabilitySchema = _enum(["unsupported", "by_ref_id"]);
4325
4325
  const providerConversationDiscoverCapabilitySchema = literal("unsupported");
@@ -6576,6 +6576,7 @@ const REPLY_READINESS_REASON_CODE_VALUES = [
6576
6576
  "runtime.adapter.unsupported",
6577
6577
  "controller.bootstrap.failed",
6578
6578
  "controller.gateway.unhealthy",
6579
+ "local_agents.none_needed",
6579
6580
  "route.offline",
6580
6581
  "route.not_registered"
6581
6582
  ];
@@ -11423,6 +11424,10 @@ function buildSkillsSharedNotes(context) {
11423
11424
  //#endregion
11424
11425
  //#region src/command-entry-checks/command-check-requirements.ts
11425
11426
  const SKIP_ALL_CHECKS = {
11427
+ disclaimer: {
11428
+ mode: "skip",
11429
+ stage: "before-entry"
11430
+ },
11426
11431
  cliUpdate: {
11427
11432
  mode: "skip",
11428
11433
  stage: "before-entry"
@@ -11452,6 +11457,10 @@ const OPTIONAL_CLI_UPDATE = { cliUpdate: {
11452
11457
  mode: "optional",
11453
11458
  stage: "before-entry"
11454
11459
  } };
11460
+ const REQUIRED_DISCLAIMER = { disclaimer: {
11461
+ mode: "required",
11462
+ stage: "before-entry"
11463
+ } };
11455
11464
  const AUTO_FIX_LOCAL_RUNTIME = { localRuntimeState: {
11456
11465
  mode: "auto-fix",
11457
11466
  stage: "before-entry"
@@ -11480,10 +11489,6 @@ const STATUS_ONLY_SELECTED_PROFILE = { selectedProfile: {
11480
11489
  mode: "status-only",
11481
11490
  stage: "status-only"
11482
11491
  } };
11483
- const OPTIONAL_SERVICE_AFTER_PROFILE = { service: {
11484
- mode: "optional",
11485
- stage: "after-profile"
11486
- } };
11487
11492
  const STATUS_ONLY_SERVICE = { service: {
11488
11493
  mode: "status-only",
11489
11494
  stage: "status-only"
@@ -11494,77 +11499,77 @@ function buildCommandChecks(...parts) {
11494
11499
  const COMMAND_CHECK_REQUIREMENTS = Object.freeze({
11495
11500
  root: buildCommandChecks(OPTIONAL_CLI_UPDATE),
11496
11501
  view: buildCommandChecks(OPTIONAL_CLI_UPDATE),
11497
- start: buildCommandChecks(OPTIONAL_CLI_UPDATE, AUTO_FIX_LOCAL_RUNTIME),
11498
- "repair.local-state": buildCommandChecks(),
11499
- uninstall: buildCommandChecks(),
11500
- reset: buildCommandChecks(),
11501
- upgrade: buildCommandChecks(),
11502
- "auth.menu": buildCommandChecks(),
11503
- "auth.status": buildCommandChecks(),
11504
- "auth.login": buildCommandChecks(),
11505
- "auth.logout": buildCommandChecks(),
11506
- "skills.menu": buildCommandChecks(OPTIONAL_CLI_UPDATE),
11507
- "skills.install": buildCommandChecks(OPTIONAL_CLI_UPDATE),
11508
- "skills.uninstall": buildCommandChecks(OPTIONAL_CLI_UPDATE),
11509
- "skills.list": buildCommandChecks(OPTIONAL_CLI_UPDATE),
11510
- "skills.check": buildCommandChecks(OPTIONAL_CLI_UPDATE),
11511
- "skills.update": buildCommandChecks(OPTIONAL_CLI_UPDATE),
11512
- "agents.menu": buildCommandChecks(OPTIONAL_CLI_UPDATE, AUTO_FIX_LOCAL_RUNTIME),
11513
- "agents.detect": buildCommandChecks(OPTIONAL_CLI_UPDATE, AUTO_FIX_LOCAL_RUNTIME),
11514
- "agents.list": buildCommandChecks(OPTIONAL_CLI_UPDATE, AUTO_FIX_LOCAL_RUNTIME),
11515
- "agents.manage": buildCommandChecks(OPTIONAL_CLI_UPDATE, AUTO_FIX_LOCAL_RUNTIME),
11516
- "agents.enable": buildCommandChecks(OPTIONAL_CLI_UPDATE, AUTO_FIX_LOCAL_RUNTIME),
11517
- "agents.repair": buildCommandChecks(OPTIONAL_CLI_UPDATE, AUTO_FIX_LOCAL_RUNTIME),
11518
- "agents.disable": buildCommandChecks(OPTIONAL_CLI_UPDATE, AUTO_FIX_LOCAL_RUNTIME),
11519
- "agents.show": buildCommandChecks(OPTIONAL_CLI_UPDATE, AUTO_FIX_LOCAL_RUNTIME),
11520
- "agents.custom.list": buildCommandChecks(OPTIONAL_CLI_UPDATE, AUTO_FIX_LOCAL_RUNTIME),
11521
- "agents.custom.add": buildCommandChecks(OPTIONAL_CLI_UPDATE, AUTO_FIX_LOCAL_RUNTIME),
11522
- "agents.custom.update": buildCommandChecks(OPTIONAL_CLI_UPDATE, AUTO_FIX_LOCAL_RUNTIME),
11523
- "agents.custom.remove": buildCommandChecks(OPTIONAL_CLI_UPDATE, AUTO_FIX_LOCAL_RUNTIME),
11524
- "agents.config": buildCommandChecks(OPTIONAL_CLI_UPDATE, AUTO_FIX_LOCAL_RUNTIME),
11525
- "service.menu": buildCommandChecks(OPTIONAL_CLI_UPDATE),
11526
- "service.run": buildCommandChecks(OPTIONAL_CLI_UPDATE, AUTO_FIX_LOCAL_RUNTIME, REQUIRED_SIGN_IN),
11527
- "service.status": buildCommandChecks(OPTIONAL_CLI_UPDATE),
11528
- "service.snapshot": buildCommandChecks(OPTIONAL_CLI_UPDATE),
11529
- "service.interrupt": buildCommandChecks(OPTIONAL_CLI_UPDATE, AUTO_FIX_LOCAL_RUNTIME),
11530
- "service.cancel": buildCommandChecks(OPTIONAL_CLI_UPDATE, AUTO_FIX_LOCAL_RUNTIME),
11531
- "service.trace": buildCommandChecks(OPTIONAL_CLI_UPDATE, REQUIRED_SIGN_IN, REQUIRED_SELECTED_PROFILE),
11532
- "service.install": buildCommandChecks(OPTIONAL_CLI_UPDATE),
11533
- "service.uninstall": buildCommandChecks(OPTIONAL_CLI_UPDATE),
11534
- "service.reinstall": buildCommandChecks(OPTIONAL_CLI_UPDATE),
11535
- "service.stop": buildCommandChecks(OPTIONAL_CLI_UPDATE),
11536
- "space.menu": buildCommandChecks(OPTIONAL_CLI_UPDATE, AUTO_FIX_LOCAL_RUNTIME, REQUIRED_SIGN_IN, CONFIRM_SELECTED_PROFILE),
11537
- "space.create": buildCommandChecks(OPTIONAL_CLI_UPDATE, AUTO_FIX_LOCAL_RUNTIME, REQUIRED_SIGN_IN, CONFIRM_SELECTED_PROFILE, OPTIONAL_SERVICE_AFTER_PROFILE),
11538
- "space.delete": buildCommandChecks(OPTIONAL_CLI_UPDATE, AUTO_FIX_LOCAL_RUNTIME, REQUIRED_SIGN_IN, CONFIRM_SELECTED_PROFILE),
11539
- "space.conversation.bind": buildCommandChecks(OPTIONAL_CLI_UPDATE, AUTO_FIX_LOCAL_RUNTIME, REQUIRED_SIGN_IN, CONFIRM_SELECTED_PROFILE),
11540
- "space.conversation.clear": buildCommandChecks(OPTIONAL_CLI_UPDATE, AUTO_FIX_LOCAL_RUNTIME, REQUIRED_SIGN_IN, CONFIRM_SELECTED_PROFILE),
11541
- "space.conversation.status": buildCommandChecks(OPTIONAL_CLI_UPDATE, AUTO_FIX_LOCAL_RUNTIME, REQUIRED_SIGN_IN, CONFIRM_SELECTED_PROFILE),
11542
- "space.join": buildCommandChecks(OPTIONAL_CLI_UPDATE, AUTO_FIX_LOCAL_RUNTIME, REQUIRED_SIGN_IN, CONFIRM_SELECTED_PROFILE, OPTIONAL_SERVICE_AFTER_PROFILE),
11543
- "space.add-members": buildCommandChecks(OPTIONAL_CLI_UPDATE, AUTO_FIX_LOCAL_RUNTIME, REQUIRED_SIGN_IN, CONFIRM_SELECTED_PROFILE),
11544
- "space.remove-members": buildCommandChecks(OPTIONAL_CLI_UPDATE, AUTO_FIX_LOCAL_RUNTIME, REQUIRED_SIGN_IN, CONFIRM_SELECTED_PROFILE),
11545
- "space.watch": buildCommandChecks(OPTIONAL_CLI_UPDATE, AUTO_FIX_LOCAL_RUNTIME, REQUIRED_SIGN_IN, CONFIRM_SELECTED_PROFILE, OPTIONAL_SERVICE_AFTER_PROFILE),
11546
- "space.send": buildCommandChecks(OPTIONAL_CLI_UPDATE, AUTO_FIX_LOCAL_RUNTIME, REQUIRED_SIGN_IN, CONFIRM_SELECTED_PROFILE),
11547
- "space.history": buildCommandChecks(OPTIONAL_CLI_UPDATE, AUTO_FIX_LOCAL_RUNTIME, REQUIRED_SIGN_IN, CONFIRM_SELECTED_PROFILE),
11548
- "space.contract": buildCommandChecks(OPTIONAL_CLI_UPDATE, AUTO_FIX_LOCAL_RUNTIME, REQUIRED_SIGN_IN, CONFIRM_SELECTED_PROFILE),
11549
- "space.contract.set": buildCommandChecks(OPTIONAL_CLI_UPDATE, AUTO_FIX_LOCAL_RUNTIME, REQUIRED_SIGN_IN, CONFIRM_SELECTED_PROFILE),
11550
- "space.status": buildCommandChecks(OPTIONAL_CLI_UPDATE, AUTO_FIX_LOCAL_RUNTIME, REQUIRED_SIGN_IN, CONFIRM_SELECTED_PROFILE),
11551
- "space.updates": buildCommandChecks(OPTIONAL_CLI_UPDATE, AUTO_FIX_LOCAL_RUNTIME, REQUIRED_SIGN_IN, CONFIRM_SELECTED_PROFILE),
11552
- "space.result": buildCommandChecks(OPTIONAL_CLI_UPDATE, AUTO_FIX_LOCAL_RUNTIME, REQUIRED_SIGN_IN, CONFIRM_SELECTED_PROFILE),
11553
- "space.list": buildCommandChecks(OPTIONAL_CLI_UPDATE, AUTO_FIX_LOCAL_RUNTIME, REQUIRED_SIGN_IN, CONFIRM_SELECTED_PROFILE),
11554
- "space.password": buildCommandChecks(OPTIONAL_CLI_UPDATE, AUTO_FIX_LOCAL_RUNTIME, REQUIRED_SIGN_IN, CONFIRM_SELECTED_PROFILE),
11555
- "space.guide": buildCommandChecks(OPTIONAL_CLI_UPDATE, AUTO_FIX_LOCAL_RUNTIME, REQUIRED_SIGN_IN, CONFIRM_SELECTED_PROFILE),
11556
- "space.guide.set": buildCommandChecks(OPTIONAL_CLI_UPDATE, AUTO_FIX_LOCAL_RUNTIME, REQUIRED_SIGN_IN, CONFIRM_SELECTED_PROFILE),
11557
- "space.guide.clear": buildCommandChecks(OPTIONAL_CLI_UPDATE, AUTO_FIX_LOCAL_RUNTIME, REQUIRED_SIGN_IN, CONFIRM_SELECTED_PROFILE),
11558
- doctor: buildCommandChecks(OPTIONAL_CLI_UPDATE, OPTIONAL_SIGN_IN, STATUS_ONLY_SERVICE),
11559
- whoami: buildCommandChecks(OPTIONAL_CLI_UPDATE),
11560
- "profile.menu": buildCommandChecks(OPTIONAL_CLI_UPDATE, AUTO_FIX_LOCAL_RUNTIME, REQUIRED_SIGN_IN),
11561
- "profile.create": buildCommandChecks(OPTIONAL_CLI_UPDATE, AUTO_FIX_LOCAL_RUNTIME, REQUIRED_SIGN_IN),
11562
- "profile.list": buildCommandChecks(OPTIONAL_CLI_UPDATE, REQUIRED_SIGN_IN),
11563
- "profile.ready": buildCommandChecks(OPTIONAL_CLI_UPDATE, STATUS_ONLY_SIGN_IN, STATUS_ONLY_SELECTED_PROFILE),
11564
- "profile.set": buildCommandChecks(OPTIONAL_CLI_UPDATE, AUTO_FIX_LOCAL_RUNTIME, REQUIRED_SIGN_IN),
11565
- "profile.show": buildCommandChecks(OPTIONAL_CLI_UPDATE, AUTO_FIX_LOCAL_RUNTIME, REQUIRED_SIGN_IN),
11566
- "profile.update": buildCommandChecks(OPTIONAL_CLI_UPDATE, AUTO_FIX_LOCAL_RUNTIME, REQUIRED_SIGN_IN),
11567
- "profile.delete": buildCommandChecks(OPTIONAL_CLI_UPDATE, AUTO_FIX_LOCAL_RUNTIME, REQUIRED_SIGN_IN)
11502
+ start: buildCommandChecks(REQUIRED_DISCLAIMER, OPTIONAL_CLI_UPDATE, AUTO_FIX_LOCAL_RUNTIME),
11503
+ "repair.local-state": buildCommandChecks(REQUIRED_DISCLAIMER),
11504
+ uninstall: buildCommandChecks(REQUIRED_DISCLAIMER),
11505
+ reset: buildCommandChecks(REQUIRED_DISCLAIMER),
11506
+ upgrade: buildCommandChecks(REQUIRED_DISCLAIMER),
11507
+ "auth.menu": buildCommandChecks(REQUIRED_DISCLAIMER),
11508
+ "auth.status": buildCommandChecks(REQUIRED_DISCLAIMER),
11509
+ "auth.login": buildCommandChecks(REQUIRED_DISCLAIMER),
11510
+ "auth.logout": buildCommandChecks(REQUIRED_DISCLAIMER),
11511
+ "skills.menu": buildCommandChecks(REQUIRED_DISCLAIMER, OPTIONAL_CLI_UPDATE),
11512
+ "skills.install": buildCommandChecks(REQUIRED_DISCLAIMER, OPTIONAL_CLI_UPDATE),
11513
+ "skills.uninstall": buildCommandChecks(REQUIRED_DISCLAIMER, OPTIONAL_CLI_UPDATE),
11514
+ "skills.list": buildCommandChecks(REQUIRED_DISCLAIMER, OPTIONAL_CLI_UPDATE),
11515
+ "skills.check": buildCommandChecks(REQUIRED_DISCLAIMER, OPTIONAL_CLI_UPDATE),
11516
+ "skills.update": buildCommandChecks(REQUIRED_DISCLAIMER, OPTIONAL_CLI_UPDATE),
11517
+ "agents.menu": buildCommandChecks(REQUIRED_DISCLAIMER, OPTIONAL_CLI_UPDATE, AUTO_FIX_LOCAL_RUNTIME),
11518
+ "agents.detect": buildCommandChecks(REQUIRED_DISCLAIMER, OPTIONAL_CLI_UPDATE, AUTO_FIX_LOCAL_RUNTIME),
11519
+ "agents.list": buildCommandChecks(REQUIRED_DISCLAIMER, OPTIONAL_CLI_UPDATE, AUTO_FIX_LOCAL_RUNTIME),
11520
+ "agents.manage": buildCommandChecks(REQUIRED_DISCLAIMER, OPTIONAL_CLI_UPDATE, AUTO_FIX_LOCAL_RUNTIME),
11521
+ "agents.enable": buildCommandChecks(REQUIRED_DISCLAIMER, OPTIONAL_CLI_UPDATE, AUTO_FIX_LOCAL_RUNTIME),
11522
+ "agents.repair": buildCommandChecks(REQUIRED_DISCLAIMER, OPTIONAL_CLI_UPDATE, AUTO_FIX_LOCAL_RUNTIME),
11523
+ "agents.disable": buildCommandChecks(REQUIRED_DISCLAIMER, OPTIONAL_CLI_UPDATE, AUTO_FIX_LOCAL_RUNTIME),
11524
+ "agents.show": buildCommandChecks(REQUIRED_DISCLAIMER, OPTIONAL_CLI_UPDATE, AUTO_FIX_LOCAL_RUNTIME),
11525
+ "agents.custom.list": buildCommandChecks(REQUIRED_DISCLAIMER, OPTIONAL_CLI_UPDATE, AUTO_FIX_LOCAL_RUNTIME),
11526
+ "agents.custom.add": buildCommandChecks(REQUIRED_DISCLAIMER, OPTIONAL_CLI_UPDATE, AUTO_FIX_LOCAL_RUNTIME),
11527
+ "agents.custom.update": buildCommandChecks(REQUIRED_DISCLAIMER, OPTIONAL_CLI_UPDATE, AUTO_FIX_LOCAL_RUNTIME),
11528
+ "agents.custom.remove": buildCommandChecks(REQUIRED_DISCLAIMER, OPTIONAL_CLI_UPDATE, AUTO_FIX_LOCAL_RUNTIME),
11529
+ "agents.config": buildCommandChecks(REQUIRED_DISCLAIMER, OPTIONAL_CLI_UPDATE, AUTO_FIX_LOCAL_RUNTIME),
11530
+ "service.menu": buildCommandChecks(REQUIRED_DISCLAIMER, OPTIONAL_CLI_UPDATE),
11531
+ "service.run": buildCommandChecks(REQUIRED_DISCLAIMER, OPTIONAL_CLI_UPDATE, AUTO_FIX_LOCAL_RUNTIME, REQUIRED_SIGN_IN),
11532
+ "service.status": buildCommandChecks(REQUIRED_DISCLAIMER, OPTIONAL_CLI_UPDATE),
11533
+ "service.snapshot": buildCommandChecks(REQUIRED_DISCLAIMER, OPTIONAL_CLI_UPDATE),
11534
+ "service.interrupt": buildCommandChecks(REQUIRED_DISCLAIMER, OPTIONAL_CLI_UPDATE, AUTO_FIX_LOCAL_RUNTIME),
11535
+ "service.cancel": buildCommandChecks(REQUIRED_DISCLAIMER, OPTIONAL_CLI_UPDATE, AUTO_FIX_LOCAL_RUNTIME),
11536
+ "service.trace": buildCommandChecks(REQUIRED_DISCLAIMER, OPTIONAL_CLI_UPDATE, REQUIRED_SIGN_IN, REQUIRED_SELECTED_PROFILE),
11537
+ "service.install": buildCommandChecks(REQUIRED_DISCLAIMER, OPTIONAL_CLI_UPDATE),
11538
+ "service.uninstall": buildCommandChecks(REQUIRED_DISCLAIMER, OPTIONAL_CLI_UPDATE),
11539
+ "service.reinstall": buildCommandChecks(REQUIRED_DISCLAIMER, OPTIONAL_CLI_UPDATE),
11540
+ "service.stop": buildCommandChecks(REQUIRED_DISCLAIMER, OPTIONAL_CLI_UPDATE),
11541
+ "space.menu": buildCommandChecks(REQUIRED_DISCLAIMER, OPTIONAL_CLI_UPDATE, AUTO_FIX_LOCAL_RUNTIME, REQUIRED_SIGN_IN, CONFIRM_SELECTED_PROFILE),
11542
+ "space.create": buildCommandChecks(REQUIRED_DISCLAIMER, OPTIONAL_CLI_UPDATE, AUTO_FIX_LOCAL_RUNTIME, REQUIRED_SIGN_IN, CONFIRM_SELECTED_PROFILE),
11543
+ "space.delete": buildCommandChecks(REQUIRED_DISCLAIMER, OPTIONAL_CLI_UPDATE, AUTO_FIX_LOCAL_RUNTIME, REQUIRED_SIGN_IN, CONFIRM_SELECTED_PROFILE),
11544
+ "space.conversation.bind": buildCommandChecks(REQUIRED_DISCLAIMER, OPTIONAL_CLI_UPDATE, AUTO_FIX_LOCAL_RUNTIME, REQUIRED_SIGN_IN, CONFIRM_SELECTED_PROFILE),
11545
+ "space.conversation.clear": buildCommandChecks(REQUIRED_DISCLAIMER, OPTIONAL_CLI_UPDATE, AUTO_FIX_LOCAL_RUNTIME, REQUIRED_SIGN_IN, CONFIRM_SELECTED_PROFILE),
11546
+ "space.conversation.status": buildCommandChecks(REQUIRED_DISCLAIMER, OPTIONAL_CLI_UPDATE, AUTO_FIX_LOCAL_RUNTIME, REQUIRED_SIGN_IN, CONFIRM_SELECTED_PROFILE),
11547
+ "space.join": buildCommandChecks(REQUIRED_DISCLAIMER, OPTIONAL_CLI_UPDATE, AUTO_FIX_LOCAL_RUNTIME, REQUIRED_SIGN_IN, CONFIRM_SELECTED_PROFILE),
11548
+ "space.add-members": buildCommandChecks(REQUIRED_DISCLAIMER, OPTIONAL_CLI_UPDATE, AUTO_FIX_LOCAL_RUNTIME, REQUIRED_SIGN_IN, CONFIRM_SELECTED_PROFILE),
11549
+ "space.remove-members": buildCommandChecks(REQUIRED_DISCLAIMER, OPTIONAL_CLI_UPDATE, AUTO_FIX_LOCAL_RUNTIME, REQUIRED_SIGN_IN, CONFIRM_SELECTED_PROFILE),
11550
+ "space.watch": buildCommandChecks(REQUIRED_DISCLAIMER, OPTIONAL_CLI_UPDATE, AUTO_FIX_LOCAL_RUNTIME, REQUIRED_SIGN_IN, CONFIRM_SELECTED_PROFILE),
11551
+ "space.send": buildCommandChecks(REQUIRED_DISCLAIMER, OPTIONAL_CLI_UPDATE, AUTO_FIX_LOCAL_RUNTIME, REQUIRED_SIGN_IN, CONFIRM_SELECTED_PROFILE),
11552
+ "space.history": buildCommandChecks(REQUIRED_DISCLAIMER, OPTIONAL_CLI_UPDATE, AUTO_FIX_LOCAL_RUNTIME, REQUIRED_SIGN_IN, CONFIRM_SELECTED_PROFILE),
11553
+ "space.contract": buildCommandChecks(REQUIRED_DISCLAIMER, OPTIONAL_CLI_UPDATE, AUTO_FIX_LOCAL_RUNTIME, REQUIRED_SIGN_IN, CONFIRM_SELECTED_PROFILE),
11554
+ "space.contract.set": buildCommandChecks(REQUIRED_DISCLAIMER, OPTIONAL_CLI_UPDATE, AUTO_FIX_LOCAL_RUNTIME, REQUIRED_SIGN_IN, CONFIRM_SELECTED_PROFILE),
11555
+ "space.status": buildCommandChecks(REQUIRED_DISCLAIMER, OPTIONAL_CLI_UPDATE, AUTO_FIX_LOCAL_RUNTIME, REQUIRED_SIGN_IN, CONFIRM_SELECTED_PROFILE),
11556
+ "space.updates": buildCommandChecks(REQUIRED_DISCLAIMER, OPTIONAL_CLI_UPDATE, AUTO_FIX_LOCAL_RUNTIME, REQUIRED_SIGN_IN, CONFIRM_SELECTED_PROFILE),
11557
+ "space.result": buildCommandChecks(REQUIRED_DISCLAIMER, OPTIONAL_CLI_UPDATE, AUTO_FIX_LOCAL_RUNTIME, REQUIRED_SIGN_IN, CONFIRM_SELECTED_PROFILE),
11558
+ "space.list": buildCommandChecks(REQUIRED_DISCLAIMER, OPTIONAL_CLI_UPDATE, AUTO_FIX_LOCAL_RUNTIME, REQUIRED_SIGN_IN, CONFIRM_SELECTED_PROFILE),
11559
+ "space.password": buildCommandChecks(REQUIRED_DISCLAIMER, OPTIONAL_CLI_UPDATE, AUTO_FIX_LOCAL_RUNTIME, REQUIRED_SIGN_IN, CONFIRM_SELECTED_PROFILE),
11560
+ "space.guide": buildCommandChecks(REQUIRED_DISCLAIMER, OPTIONAL_CLI_UPDATE, AUTO_FIX_LOCAL_RUNTIME, REQUIRED_SIGN_IN, CONFIRM_SELECTED_PROFILE),
11561
+ "space.guide.set": buildCommandChecks(REQUIRED_DISCLAIMER, OPTIONAL_CLI_UPDATE, AUTO_FIX_LOCAL_RUNTIME, REQUIRED_SIGN_IN, CONFIRM_SELECTED_PROFILE),
11562
+ "space.guide.clear": buildCommandChecks(REQUIRED_DISCLAIMER, OPTIONAL_CLI_UPDATE, AUTO_FIX_LOCAL_RUNTIME, REQUIRED_SIGN_IN, CONFIRM_SELECTED_PROFILE),
11563
+ doctor: buildCommandChecks(REQUIRED_DISCLAIMER, OPTIONAL_CLI_UPDATE, OPTIONAL_SIGN_IN, STATUS_ONLY_SERVICE),
11564
+ whoami: buildCommandChecks(REQUIRED_DISCLAIMER, OPTIONAL_CLI_UPDATE),
11565
+ "profile.menu": buildCommandChecks(REQUIRED_DISCLAIMER, OPTIONAL_CLI_UPDATE, AUTO_FIX_LOCAL_RUNTIME, REQUIRED_SIGN_IN),
11566
+ "profile.create": buildCommandChecks(REQUIRED_DISCLAIMER, OPTIONAL_CLI_UPDATE, AUTO_FIX_LOCAL_RUNTIME, REQUIRED_SIGN_IN),
11567
+ "profile.list": buildCommandChecks(REQUIRED_DISCLAIMER, OPTIONAL_CLI_UPDATE, REQUIRED_SIGN_IN),
11568
+ "profile.ready": buildCommandChecks(REQUIRED_DISCLAIMER, OPTIONAL_CLI_UPDATE, STATUS_ONLY_SIGN_IN, STATUS_ONLY_SELECTED_PROFILE),
11569
+ "profile.set": buildCommandChecks(REQUIRED_DISCLAIMER, OPTIONAL_CLI_UPDATE, AUTO_FIX_LOCAL_RUNTIME, REQUIRED_SIGN_IN),
11570
+ "profile.show": buildCommandChecks(REQUIRED_DISCLAIMER, OPTIONAL_CLI_UPDATE, AUTO_FIX_LOCAL_RUNTIME, REQUIRED_SIGN_IN),
11571
+ "profile.update": buildCommandChecks(REQUIRED_DISCLAIMER, OPTIONAL_CLI_UPDATE, AUTO_FIX_LOCAL_RUNTIME, REQUIRED_SIGN_IN),
11572
+ "profile.delete": buildCommandChecks(REQUIRED_DISCLAIMER, OPTIONAL_CLI_UPDATE, AUTO_FIX_LOCAL_RUNTIME, REQUIRED_SIGN_IN)
11568
11573
  });
11569
11574
  function getCommandCheckRequirements(entryName) {
11570
11575
  return COMMAND_CHECK_REQUIREMENTS[entryName];
@@ -13127,6 +13132,13 @@ function daemonServiceStableLauncherPath(pathInput = {}) {
13127
13132
  function daemonTokenPath(pathInput = {}) {
13128
13133
  return join(systemDir(pathInput), "daemon-token.json");
13129
13134
  }
13135
+ async function restoreDaemonInstallStateSnapshot(snapshot, pathInput = {}) {
13136
+ if (!snapshot) {
13137
+ await rm(daemonStatePath(pathInput), { force: true });
13138
+ return;
13139
+ }
13140
+ await writeDaemonInstallState(snapshot, pathInput);
13141
+ }
13130
13142
  function resolveDaemonControlPlaneEndpoint(pathInput = {}) {
13131
13143
  const runtimeRoot = daemonRuntimeRootPath(pathInput);
13132
13144
  const suffix = createHash("sha256").update(runtimeRoot).digest("hex").slice(0, 24);
@@ -13306,9 +13318,9 @@ function parseDaemonInstallState(raw) {
13306
13318
  function isDaemonInstallSource(value) {
13307
13319
  return value === "cli.auto_check" || value === "daemon.command.install";
13308
13320
  }
13309
- async function writeDaemonInstallState(state) {
13310
- const path = daemonStatePath();
13311
- await mkdir(systemDir(), { recursive: true });
13321
+ async function writeDaemonInstallState(state, pathInput = {}) {
13322
+ const path = daemonStatePath(pathInput);
13323
+ await mkdir(systemDir(pathInput), { recursive: true });
13312
13324
  const tempPath = `${path}.tmp-${process.pid}-${Date.now()}`;
13313
13325
  let shouldCleanupTemp = true;
13314
13326
  try {
@@ -14663,6 +14675,9 @@ async function getConfiguredDaemonDispatchLimits() {
14663
14675
  async function getConfiguredOnboardingConfig() {
14664
14676
  return (await readRuntimeConfig())?.onboarding ?? null;
14665
14677
  }
14678
+ async function getConfiguredOnboardingDisclaimerAcceptedAt() {
14679
+ return (await getConfiguredOnboardingConfig())?.disclaimerAcceptedAt ?? null;
14680
+ }
14666
14681
  async function getConfiguredStartV1OnboardingState() {
14667
14682
  return (await getConfiguredOnboardingConfig())?.startV1 ?? null;
14668
14683
  }
@@ -14727,10 +14742,23 @@ async function updateConfiguredDeviceRuntimeState(updater) {
14727
14742
  async function updateConfiguredStartV1OnboardingState(updater) {
14728
14743
  await withRuntimeConfigLock(async () => {
14729
14744
  const previous = await readRuntimeConfig();
14730
- const previousStartV1 = previous?.onboarding?.startV1 ?? null;
14745
+ const previousOnboarding = previous?.onboarding ?? null;
14746
+ const previousStartV1 = previousOnboarding?.startV1 ?? null;
14731
14747
  const nextStartV1 = updater(previousStartV1);
14732
14748
  if (JSON.stringify(previousStartV1) === JSON.stringify(nextStartV1)) return;
14733
- await writeRuntimeConfig(buildNextConfig(previous, { onboarding: nextStartV1 ? { startV1: nextStartV1 } : null }));
14749
+ await writeRuntimeConfig(buildNextConfig(previous, { onboarding: nextStartV1 || previousOnboarding?.disclaimerAcceptedAt ? {
14750
+ ...previousOnboarding?.disclaimerAcceptedAt ? { disclaimerAcceptedAt: previousOnboarding.disclaimerAcceptedAt } : {},
14751
+ ...nextStartV1 ? { startV1: nextStartV1 } : {}
14752
+ } : null }));
14753
+ });
14754
+ }
14755
+ async function updateConfiguredOnboardingConfig(updater) {
14756
+ await withRuntimeConfigLock(async () => {
14757
+ const previous = await readRuntimeConfig();
14758
+ const previousOnboarding = previous?.onboarding ?? null;
14759
+ const nextOnboarding = updater(previousOnboarding);
14760
+ if (JSON.stringify(previousOnboarding) === JSON.stringify(nextOnboarding)) return;
14761
+ await writeRuntimeConfig(buildNextConfig(previous, { onboarding: nextOnboarding }));
14734
14762
  });
14735
14763
  }
14736
14764
  function buildNextConfig(previous, overrides) {
@@ -14860,9 +14888,14 @@ function parseRuntimeDeviceRuntimeStateObject(value) {
14860
14888
  }
14861
14889
  function parseRuntimeOnboardingConfig(value) {
14862
14890
  if (!value || typeof value !== "object") return;
14863
- const startV1 = parseRuntimeStartV1OnboardingState(value.startV1);
14864
- if (!startV1) return;
14865
- return { startV1 };
14891
+ const obj = value;
14892
+ const disclaimerAcceptedAt = toTrimmedStringOrUndefined(obj.disclaimerAcceptedAt);
14893
+ const startV1 = parseRuntimeStartV1OnboardingState(obj.startV1);
14894
+ if (!(disclaimerAcceptedAt || startV1)) return;
14895
+ return {
14896
+ ...disclaimerAcceptedAt ? { disclaimerAcceptedAt } : {},
14897
+ ...startV1 ? { startV1 } : {}
14898
+ };
14866
14899
  }
14867
14900
  function parseRuntimeStartV1OnboardingState(value) {
14868
14901
  if (!value || typeof value !== "object") return;
@@ -16604,8 +16637,77 @@ function createPresenter(context) {
16604
16637
 
16605
16638
  //#endregion
16606
16639
  //#region src/system/daemon-install-attention.ts
16640
+ const DAEMON_INSTALL_DECISION_CHOICE = {
16641
+ install: "install",
16642
+ notNow: "not_now"
16643
+ };
16607
16644
  const DAEMON_INSTALL_COMMAND = "ats service install";
16608
16645
  const DAEMON_UPGRADE_COMMAND = "ats upgrade";
16646
+ const DAEMON_DECISION_RECOMMENDED_HINT = "Recommended";
16647
+ const DAEMON_INSTALL_DECISION_COPY = {
16648
+ missing: {
16649
+ question: "ATS Service is missing. Install it now?",
16650
+ explanation: "Without it, local agents on this device can't reply from Space.",
16651
+ primaryLabel: "Yes, install it for me",
16652
+ deferred: "ATS Service is still missing. Local agents on this device can't reply from Space until you install it later with `ats service install`.",
16653
+ failed: "ATS Service could not be installed. Local agents on this device still can't reply from Space. Run `ats service install`."
16654
+ },
16655
+ outdated: {
16656
+ question: "ATS Service needs an update. Fix it now?",
16657
+ explanation: "Until ATS fixes it, local agents on this device may not be able to reply from Space.",
16658
+ primaryLabel: "Yes, fix it for me",
16659
+ deferred: "ATS Service still needs an update. Local agents on this device may not be able to reply from Space until ATS fixes it. Run `ats service install` later.",
16660
+ failed: "ATS Service was not updated. Local agents on this device may not be able to reply from Space. Run `ats service install`."
16661
+ }
16662
+ };
16663
+ function buildDaemonInstallDecisionPromptConfig(input) {
16664
+ const copy = DAEMON_INSTALL_DECISION_COPY[input.kind];
16665
+ const detailLine = input.kind === "outdated" && typeof input.installedVersion === "string" && typeof input.expectedVersion === "string" ? `Installed v${input.installedVersion}, expected v${input.expectedVersion}.` : null;
16666
+ return {
16667
+ message: [
16668
+ copy.question,
16669
+ copy.explanation,
16670
+ detailLine
16671
+ ].filter((line) => typeof line === "string" && line.length > 0).join("\n"),
16672
+ initialValue: DAEMON_INSTALL_DECISION_CHOICE.install,
16673
+ options: [{
16674
+ value: DAEMON_INSTALL_DECISION_CHOICE.install,
16675
+ label: copy.primaryLabel,
16676
+ hint: DAEMON_DECISION_RECOMMENDED_HINT
16677
+ }, {
16678
+ value: DAEMON_INSTALL_DECISION_CHOICE.notNow,
16679
+ label: "Not now"
16680
+ }]
16681
+ };
16682
+ }
16683
+ async function promptForDaemonInstallDecision(input) {
16684
+ const choice = await select(buildDaemonInstallDecisionPromptConfig(input));
16685
+ if (isCancel(choice)) return "cancelled";
16686
+ return choice === DAEMON_INSTALL_DECISION_CHOICE.install ? DAEMON_INSTALL_DECISION_CHOICE.install : DAEMON_INSTALL_DECISION_CHOICE.notNow;
16687
+ }
16688
+ async function runDaemonInstallDecisionPrompt(input) {
16689
+ const decision = await promptForDaemonInstallDecision({
16690
+ kind: input.kind,
16691
+ installedVersion: input.installedVersion,
16692
+ expectedVersion: input.expectedVersion
16693
+ });
16694
+ if (decision === DAEMON_INSTALL_DECISION_CHOICE.install) try {
16695
+ await input.installService();
16696
+ return "install";
16697
+ } catch {
16698
+ await input.emitLine(formatDaemonInstallFailedMessage(input.kind));
16699
+ return "failed";
16700
+ }
16701
+ if (decision === "cancelled") return "cancelled";
16702
+ await input.emitLine(formatDaemonInstallDeferredMessage(input.kind));
16703
+ return decision;
16704
+ }
16705
+ function formatDaemonInstallDeferredMessage(kind) {
16706
+ return formatInlineAtsCliCommands(DAEMON_INSTALL_DECISION_COPY[kind].deferred);
16707
+ }
16708
+ function formatDaemonInstallFailedMessage(kind) {
16709
+ return formatInlineAtsCliCommands(DAEMON_INSTALL_DECISION_COPY[kind].failed);
16710
+ }
16609
16711
  function formatDaemonServiceMissingRecommendation() {
16610
16712
  return "ATS Service is not installed yet. Local agents on this device can't reply from Space until it is installed.";
16611
16713
  }
@@ -17432,6 +17534,17 @@ async function writeDaemonServiceLauncher() {
17432
17534
  mode: 448
17433
17535
  });
17434
17536
  }
17537
+ async function restoreDaemonActiveBundleStateSnapshot(snapshot, pathInput = {}) {
17538
+ if (!snapshot) {
17539
+ await rm(daemonServiceActiveBundlePath(pathInput), { force: true });
17540
+ return;
17541
+ }
17542
+ await mkdir(daemonRuntimeStateRootPath(pathInput), { recursive: true });
17543
+ await writeFile(daemonServiceActiveBundlePath(pathInput), `${JSON.stringify(snapshot, null, 2)}\n`, {
17544
+ encoding: "utf8",
17545
+ mode: 384
17546
+ });
17547
+ }
17435
17548
  async function cleanupInactiveDaemonServiceBundles(input = {}) {
17436
17549
  const [active, serviceContract, runtimeLease] = await Promise.all([
17437
17550
  readDaemonActiveBundleState(),
@@ -19694,6 +19807,12 @@ function getStepOrderIndex$1(stepId) {
19694
19807
  if (index === -1) throw new Error(`unknown start session step "${stepId}"`);
19695
19808
  return index;
19696
19809
  }
19810
+ function resolveServiceParticipationRequirement(input) {
19811
+ if (input.humanOnlyParticipation) return "not_needed";
19812
+ if (input.explicitSelectedLocalAgents) return input.selectedAgentProfilesHaveRunnableLocalSetup ? "offer_service_gate" : "agent_setup_needed";
19813
+ if (input.deviceLocalDemand === "keep_running") return "offer_service_gate";
19814
+ return "not_needed";
19815
+ }
19697
19816
  function buildDaemonRouteObservationPresentation$1(observation) {
19698
19817
  if (observation.status === "online") return {
19699
19818
  detail: "ATS server can see an active daemon route for this profile.",
@@ -19756,6 +19875,7 @@ function formatStartReplyReadinessReason(reasonCode) {
19756
19875
  case "runtime.adapter.unsupported": return "This agent setup can't reply from this device";
19757
19876
  case "controller.bootstrap.failed": return "ATS couldn't finish setting up OpenClaw on this device";
19758
19877
  case "controller.gateway.unhealthy": return "OpenClaw is not ready on this device yet";
19878
+ case "local_agents.none_needed": return "This device does not need ATS Service until you choose local agents to bring into a space";
19759
19879
  case "route.offline": return "ATS server can't currently see an active daemon route for this profile";
19760
19880
  case "route.not_registered": return "ATS is still setting up replies for this profile on this device";
19761
19881
  default: return reasonCode ? "ATS can't confirm reply status yet" : null;
@@ -26893,6 +27013,7 @@ function emitStructuredReconcileEvidence(eventName, payload) {
26893
27013
 
26894
27014
  //#endregion
26895
27015
  //#region src/system/device-runtime-state-sync.ts
27016
+ const NO_LOCAL_AGENTS_NEED_ATS_SERVICE_REASON = "No local agents on this device need ATS Service right now";
26896
27017
  async function syncDeviceRuntimeStateProjection(input) {
26897
27018
  const mode = input?.mode ?? "check";
26898
27019
  const currentControllers = await readCurrentControllers();
@@ -26932,7 +27053,7 @@ async function syncDeviceRuntimeStateProjection(input) {
26932
27053
  disabledControllerIds: [],
26933
27054
  repairRequiredControllerIds: [],
26934
27055
  repairedControllerIds: [],
26935
- reason: "no bound agent controller requires local projection"
27056
+ reason: NO_LOCAL_AGENTS_NEED_ATS_SERVICE_REASON
26936
27057
  });
26937
27058
  const gap = resolveProjectionGap({
26938
27059
  currentControllers,
@@ -28021,7 +28142,7 @@ function normalizeOptionalString$14(value) {
28021
28142
  }
28022
28143
  function resolveZeroLocalValueReason(projection) {
28023
28144
  if (!projection) return "no wakeable local agent routes are available";
28024
- if (projection.status === "ok" && projection.projectedControllerIds.length === 0) return "no bound agent controller currently needs local projection";
28145
+ if (projection.status === "ok" && projection.projectedControllerIds.length === 0) return NO_LOCAL_AGENTS_NEED_ATS_SERVICE_REASON;
28025
28146
  if (projection.status === "degraded") return "projected local controllers are not currently wakeable";
28026
28147
  return "no wakeable local agent routes are available";
28027
28148
  }
@@ -28454,13 +28575,17 @@ function resolveDeviceReplyReadinessFromRuntimeProjection(runtimeProjection) {
28454
28575
  return null;
28455
28576
  }
28456
28577
  function createRouteNotRegisteredReplyReadinessStatus(input) {
28578
+ const projectionWithoutLocalDemand = input.localDemand?.projection ?? input.runtimeProjection;
28457
28579
  return createDeviceReplyReadinessStatus({
28458
- reasonCode: "route.not_registered",
28459
- reasonText: normalizeReplyReadinessReason(input.localDemand?.reason ?? input.runtimeProjection?.reason) ?? "ATS is still setting up replies on this device",
28460
- replyReadiness: input.runtimeProjection?.status === "skipped" ? "unknown" : "blocked",
28580
+ reasonCode: input.runtimeProjection?.status === "skipped" || hasNoProjectedLocalControllers(projectionWithoutLocalDemand) ? "local_agents.none_needed" : "route.not_registered",
28581
+ reasonText: normalizeOptionalText$17(input.localDemand?.reason ?? input.runtimeProjection?.reason) ?? "ATS is still setting up replies on this device",
28582
+ replyReadiness: input.runtimeProjection?.status === "skipped" || hasNoProjectedLocalControllers(projectionWithoutLocalDemand) ? "unknown" : "blocked",
28461
28583
  wakeableRouteCount: 0
28462
28584
  });
28463
28585
  }
28586
+ function hasNoProjectedLocalControllers(projection) {
28587
+ return projection?.status === "ok" && projection.projectedControllerIds.length === 0;
28588
+ }
28464
28589
  function createDeviceReplyReadinessStatus(input) {
28465
28590
  return {
28466
28591
  reasonCodes: sortReplyReadinessReasonCodes(input.reasonCode ? [input.reasonCode] : []),
@@ -28513,6 +28638,7 @@ function describeReplyReadinessReason(reasonCode) {
28513
28638
  case "runtime.adapter.unsupported": return "the local agent controller setup is not supported";
28514
28639
  case "controller.bootstrap.failed": return "ATS couldn't finish setting up the local OpenClaw agent for this profile";
28515
28640
  case "controller.gateway.unhealthy": return "the local OpenClaw Gateway is not healthy on this device";
28641
+ case "local_agents.none_needed": return "no local agents on this device need ATS Service right now";
28516
28642
  case "route.offline": return "ATS server can't currently see an active daemon route for this profile";
28517
28643
  case "route.not_registered": return "ATS is still setting up replies for this profile on this device";
28518
28644
  default: return "ATS can't confirm local reply readiness yet";
@@ -29611,6 +29737,7 @@ async function runDaemonServiceFullAlignmentLocked(input) {
29611
29737
  });
29612
29738
  const inventoryBefore = input.state.inventoryBefore;
29613
29739
  if (!inventoryBefore) throw new Error("ATS could not resolve local service inventory for alignment.");
29740
+ const desiredState = resolveAlignmentDesiredState(inventoryBefore);
29614
29741
  const shouldRestoreBackground = resolveShouldRestoreBackground(inventoryBefore);
29615
29742
  const commandOptions = await resolveAlignmentCommandOptions({
29616
29743
  inventory: inventoryBefore,
@@ -29621,9 +29748,16 @@ async function runDaemonServiceFullAlignmentLocked(input) {
29621
29748
  });
29622
29749
  const serviceManager = await resolveAlignmentServiceManager(inventoryBefore) ?? null;
29623
29750
  if (!serviceManager) throw new Error("ATS could not resolve a system service manager for background service alignment.");
29751
+ const restorePoint = {
29752
+ desiredState,
29753
+ previousActiveBundle: inventoryBefore.activeBundle ?? null,
29754
+ previousContract: inventoryBefore.serviceContract ?? null,
29755
+ previousInstallState: inventoryBefore.installStatus.installed ? inventoryBefore.installStatus.state : null
29756
+ };
29624
29757
  const alignmentContext = {
29625
29758
  commandOptions,
29626
29759
  currentContract: inventoryBefore.serviceContract ?? null,
29760
+ desiredState,
29627
29761
  serviceManager,
29628
29762
  shouldRestoreBackground
29629
29763
  };
@@ -29646,8 +29780,7 @@ async function runDaemonServiceFullAlignmentLocked(input) {
29646
29780
  } catch (error) {
29647
29781
  try {
29648
29782
  await restoreDaemonServiceAfterFailedAlignment({
29649
- commandOptions: alignmentContext.commandOptions,
29650
- currentContract: alignmentContext.currentContract,
29783
+ restorePoint,
29651
29784
  serviceManager: alignmentContext.serviceManager,
29652
29785
  shouldRestoreBackground: alignmentContext.shouldRestoreBackground
29653
29786
  });
@@ -29689,7 +29822,7 @@ async function executeDaemonServiceFullAlignment(input) {
29689
29822
  commandOptions: input.alignmentContext.commandOptions,
29690
29823
  currentContract: input.alignmentContext.currentContract,
29691
29824
  daemonVersion: input.expectedVersion,
29692
- desiredState: input.alignmentContext.shouldRestoreBackground ? "background" : "stopped",
29825
+ desiredState: input.alignmentContext.desiredState,
29693
29826
  preparedBundle,
29694
29827
  serviceController: input.alignmentContext.serviceManager.controller
29695
29828
  });
@@ -29722,6 +29855,7 @@ async function executeDaemonServiceFullAlignment(input) {
29722
29855
  ...input.alignmentContext.shouldRestoreBackground ? { intendedMode: "background" } : {}
29723
29856
  });
29724
29857
  const verificationError = verifyAlignedInventory({
29858
+ desiredState: input.alignmentContext.desiredState,
29725
29859
  inventory: inventoryAfter,
29726
29860
  shouldRestoreBackground: input.alignmentContext.shouldRestoreBackground,
29727
29861
  target: input.target
@@ -29795,9 +29929,15 @@ async function resolveAlignmentCommandOptions(input) {
29795
29929
  ...typeof heartbeatMs === "number" ? { heartbeatMs } : {}
29796
29930
  };
29797
29931
  }
29932
+ function resolveAlignmentDesiredState(inventory) {
29933
+ if (inventory.serviceContract?.desiredState === "background") return "background";
29934
+ if (inventory.serviceContract?.desiredState === "stopped") return "stopped";
29935
+ if (inventory.serviceManagerStatus?.running) return "background";
29936
+ if (inventory.runtimeStatus.mode !== "background") return "stopped";
29937
+ return inventory.runtimeStatus.status === "running" || inventory.runtimeStatus.status === "stale" || inventory.runtimeStatus.managedBySystemService || inventory.runtimeStatus.autoStart ? "background" : "stopped";
29938
+ }
29798
29939
  function resolveShouldRestoreBackground(inventory) {
29799
- if (inventory.serviceContract?.desiredState === "background") return true;
29800
- if (inventory.serviceContract?.desiredState === "stopped") return false;
29940
+ if (resolveAlignmentDesiredState(inventory) !== "background") return false;
29801
29941
  if (inventory.serviceManagerStatus?.running) return true;
29802
29942
  if (inventory.runtimeStatus.mode !== "background") return false;
29803
29943
  return inventory.runtimeStatus.status === "running" || inventory.runtimeStatus.status === "stale" || inventory.runtimeStatus.managedBySystemService || inventory.runtimeStatus.autoStart;
@@ -29912,8 +30052,7 @@ function verifyAlignedInventoryContractState(input) {
29912
30052
  if (!contract) return "Alignment verification failed because the service contract is missing.";
29913
30053
  if (contract.daemonVersion !== input.target.daemonVersion) return `Alignment verification failed because service contract version is ${contract.daemonVersion} instead of ${input.target.daemonVersion}.`;
29914
30054
  if (input.inventory.activeBundle && contract.bundleId !== input.inventory.activeBundle.bundleId) return `Alignment verification failed because service contract bundle is ${contract.bundleId} instead of ${input.inventory.activeBundle.bundleId}.`;
29915
- const expectedDesiredState = input.shouldRestoreBackground ? "background" : "stopped";
29916
- if (contract.desiredState !== expectedDesiredState) return `Alignment verification failed because service contract desiredState is ${contract.desiredState} instead of ${expectedDesiredState}.`;
30055
+ if (contract.desiredState !== input.desiredState) return `Alignment verification failed because service contract desiredState is ${contract.desiredState} instead of ${input.desiredState}.`;
29917
30056
  return null;
29918
30057
  }
29919
30058
  function verifyAlignedInventoryBundleState(input) {
@@ -29946,16 +30085,28 @@ async function ensureAlignmentServiceContract(input) {
29946
30085
  ...input.currentContract?.lane ? { lane: input.currentContract.lane } : {}
29947
30086
  });
29948
30087
  }
29949
- async function restoreDaemonServiceAfterFailedAlignment(input) {
29950
- const activeBundle = await readDaemonActiveBundleState().catch(() => null);
29951
- if (activeBundle) await ensureAlignmentServiceContract({
29952
- commandOptions: input.commandOptions,
29953
- currentContract: input.currentContract,
29954
- daemonVersion: activeBundle.daemonVersion ?? input.currentContract?.daemonVersion ?? "unknown",
29955
- desiredState: input.shouldRestoreBackground ? "background" : "stopped",
29956
- preparedBundle: activeBundle,
29957
- serviceController: input.serviceManager.controller
30088
+ async function restoreAlignmentServiceContractSnapshot(snapshot) {
30089
+ if (!snapshot) {
30090
+ await clearDaemonServiceContract();
30091
+ return;
30092
+ }
30093
+ await upsertDaemonServiceContract({
30094
+ daemonVersion: snapshot.daemonVersion,
30095
+ bundleId: snapshot.bundleId,
30096
+ desiredState: snapshot.desiredState,
30097
+ serviceController: snapshot.serviceController,
30098
+ autoStart: snapshot.autoStart,
30099
+ gatewayUrl: snapshot.gatewayUrl,
30100
+ profileId: snapshot.profileId,
30101
+ deviceId: snapshot.deviceId,
30102
+ heartbeatMs: snapshot.heartbeatMs,
30103
+ lane: snapshot.lane
29958
30104
  });
30105
+ }
30106
+ async function restoreDaemonServiceAfterFailedAlignment(input) {
30107
+ await restoreDaemonInstallStateSnapshot(input.restorePoint.previousInstallState);
30108
+ await restoreDaemonActiveBundleStateSnapshot(input.restorePoint.previousActiveBundle);
30109
+ await restoreAlignmentServiceContractSnapshot(input.restorePoint.previousContract);
29959
30110
  await installSystemServiceRegistrationForAlignment({ serviceManager: input.serviceManager });
29960
30111
  if (input.shouldRestoreBackground) {
29961
30112
  await startDaemonSystemServiceWithAutoRepair({
@@ -30846,7 +30997,7 @@ const COMMAND_ENTRY_COPY_BY_NAME = Object.freeze({
30846
30997
  signInAction: "creating a space",
30847
30998
  profileAction: "creating a space",
30848
30999
  cliUpdateAction: "creating a space",
30849
- serviceAction: "creating a space"
31000
+ serviceAction: null
30850
31001
  },
30851
31002
  "space.delete": {
30852
31003
  signInAction: "deleting a space",
@@ -30876,7 +31027,7 @@ const COMMAND_ENTRY_COPY_BY_NAME = Object.freeze({
30876
31027
  signInAction: "joining a space",
30877
31028
  profileAction: "joining a space",
30878
31029
  cliUpdateAction: "joining a space",
30879
- serviceAction: "joining a space"
31030
+ serviceAction: null
30880
31031
  },
30881
31032
  "space.add-members": {
30882
31033
  signInAction: "adding members to a space",
@@ -30894,7 +31045,7 @@ const COMMAND_ENTRY_COPY_BY_NAME = Object.freeze({
30894
31045
  signInAction: "watching a space",
30895
31046
  profileAction: "watching a space",
30896
31047
  cliUpdateAction: "watching a space",
30897
- serviceAction: "watching a space"
31048
+ serviceAction: null
30898
31049
  },
30899
31050
  "space.send": {
30900
31051
  signInAction: "sending a message",
@@ -31303,6 +31454,82 @@ async function runCliUpdateCheck(input) {
31303
31454
  };
31304
31455
  }
31305
31456
 
31457
+ //#endregion
31458
+ //#region src/onboarding/disclaimer.ts
31459
+ const ATS_ACCEPT_DISCLAIMER_ENV = "ATS_ACCEPT_DISCLAIMER";
31460
+ const ATS_ONBOARDING_DISCLAIMER_ACCEPT = "__onboarding_disclaimer_accept__";
31461
+ const ATS_ONBOARDING_DISCLAIMER_DECLINE = "__onboarding_disclaimer_decline__";
31462
+ const ATS_ONBOARDING_DISCLAIMER_TEXT = "ATSD is experimental and changes rapidly. Breaking changes may occur. You are fully responsible for data security and sensitive information handling. Use ATSD only in trusted environments.";
31463
+ async function runOnboardingDisclaimerGate(input) {
31464
+ if (await getConfiguredOnboardingDisclaimerAcceptedAt().catch(() => null)) return true;
31465
+ if (shouldBypassDisclaimerWithEnv()) {
31466
+ await persistOnboardingDisclaimerAccepted();
31467
+ return true;
31468
+ }
31469
+ if (input.runtime.resolvedView === "agent") {
31470
+ input.presenter.data({
31471
+ code: input.codePrefix,
31472
+ payload: {
31473
+ acceptedAt: null,
31474
+ text: ATS_ONBOARDING_DISCLAIMER_TEXT,
31475
+ mode: "agent_notice",
31476
+ requiresConfirmation: false
31477
+ }
31478
+ });
31479
+ await persistOnboardingDisclaimerAccepted();
31480
+ return true;
31481
+ }
31482
+ note(`⚠️ ${ATS_ONBOARDING_DISCLAIMER_TEXT}`, "Disclaimer");
31483
+ if (!input.interactive) {
31484
+ input.presenter.line({
31485
+ code: `${input.codePrefix}.auto_continue`,
31486
+ text: "Non-interactive session: disclaimer displayed and ATS continues."
31487
+ });
31488
+ await persistOnboardingDisclaimerAccepted();
31489
+ return true;
31490
+ }
31491
+ const decision = await select({
31492
+ message: "Do you accept and continue?",
31493
+ options: [{
31494
+ value: ATS_ONBOARDING_DISCLAIMER_ACCEPT,
31495
+ label: "Yes, continue"
31496
+ }, {
31497
+ value: ATS_ONBOARDING_DISCLAIMER_DECLINE,
31498
+ label: "No, exit"
31499
+ }],
31500
+ initialValue: ATS_ONBOARDING_DISCLAIMER_ACCEPT
31501
+ });
31502
+ if (isCancel(decision) || decision === ATS_ONBOARDING_DISCLAIMER_DECLINE) {
31503
+ cancel("Cancelled.");
31504
+ return false;
31505
+ }
31506
+ await persistOnboardingDisclaimerAccepted();
31507
+ return true;
31508
+ }
31509
+ function shouldBypassDisclaimerWithEnv() {
31510
+ return String(process.env[ATS_ACCEPT_DISCLAIMER_ENV] ?? "").trim() === "1";
31511
+ }
31512
+ async function persistOnboardingDisclaimerAccepted() {
31513
+ const acceptedAt = (/* @__PURE__ */ new Date()).toISOString();
31514
+ await updateConfiguredOnboardingConfig((current) => ({
31515
+ ...current ?? {},
31516
+ disclaimerAcceptedAt: acceptedAt
31517
+ }));
31518
+ }
31519
+
31520
+ //#endregion
31521
+ //#region src/command-entry-checks/check-disclaimer.ts
31522
+ async function runDisclaimerCheck(input) {
31523
+ if (input.requirement.mode === "skip") return { status: "continue" };
31524
+ if (await runOnboardingDisclaimerGate({
31525
+ runtime: input.context.runtime,
31526
+ presenter: input.context.presenter,
31527
+ interactive: input.context.allowPrompt,
31528
+ codePrefix: "command.entry.disclaimer"
31529
+ })) return { status: "continue" };
31530
+ return { status: "cancelled" };
31531
+ }
31532
+
31306
31533
  //#endregion
31307
31534
  //#region src/config/storage-version.ts
31308
31535
  const STORAGE_VERSION_SCHEMA = "ats-local-state-v1";
@@ -37417,9 +37644,11 @@ const loadLedgerState = async (input) => {
37417
37644
  };
37418
37645
  const handleInvalidLedgerState = async (input) => {
37419
37646
  if (input.invalidStateStrategy !== "backup_and_reset") throw new Error(input.errorCode);
37420
- const emptyState = createEmptyLedgerState();
37647
+ const resetAtMs = Date.now();
37648
+ const emptyState = createEmptyLedgerState(resetAtMs);
37421
37649
  await persistLedgerStateAtomic({
37422
37650
  io: input.io,
37651
+ nowMs: resetAtMs,
37423
37652
  paths: input.paths,
37424
37653
  state: emptyState
37425
37654
  });
@@ -37428,7 +37657,7 @@ const handleInvalidLedgerState = async (input) => {
37428
37657
  const buildScopedStateTempFilePath = (input) => `${input.stateTempFilePath}.${Date.now()}.${Math.random().toString(36).slice(2, 10)}`;
37429
37658
  const persistLedgerStateAtomic = async (input) => {
37430
37659
  const normalizedState = compactLedgerState({
37431
- nowMs: Date.now(),
37660
+ nowMs: input.nowMs ?? Date.now(),
37432
37661
  state: cloneLedgerState$1(input.state)
37433
37662
  });
37434
37663
  await input.io.mkdir(input.paths.rootDirPath, { recursive: true });
@@ -46738,7 +46967,7 @@ function resolveServiceParticipationReadiness(input) {
46738
46967
  };
46739
46968
  }
46740
46969
  if (reasonCodes.includes("service.not_installed")) {
46741
- const nextSteps = formatAtsCliCommands(["ats service install", "ats service run --mode foreground"]);
46970
+ const nextSteps = formatAtsCliCommands(["ats service install", "ats service run"]);
46742
46971
  return {
46743
46972
  serviceReadiness: "not_installed",
46744
46973
  summaryText: "Not installed",
@@ -46749,7 +46978,7 @@ function resolveServiceParticipationReadiness(input) {
46749
46978
  };
46750
46979
  }
46751
46980
  if (reasonCodes.includes("service.not_running")) {
46752
- const nextSteps = [formatAtsCliCommand("ats service run --mode foreground")];
46981
+ const nextSteps = [formatAtsCliCommand("ats service run")];
46753
46982
  return {
46754
46983
  serviceReadiness: "not_running",
46755
46984
  summaryText: "Not running",
@@ -46767,6 +46996,21 @@ function resolveServiceParticipationReadiness(input) {
46767
46996
  nextSteps: [],
46768
46997
  runtimeHeadline
46769
46998
  };
46999
+ if (reasonCodes.includes("local_agents.none_needed")) {
47000
+ const nextSteps = formatAtsCliCommands([
47001
+ "ats start",
47002
+ "ats service install",
47003
+ "ats service run"
47004
+ ]);
47005
+ return {
47006
+ serviceReadiness: "unknown",
47007
+ summaryText: "Not needed yet",
47008
+ detailText: input.deviceReplyReadiness.reasonText,
47009
+ nextStepSummary: "If you later choose local agents to bring into a space, rerun `ats start` or enable ATS Service directly.",
47010
+ nextSteps,
47011
+ runtimeHeadline
47012
+ };
47013
+ }
46770
47014
  const nextSteps = [formatAtsCliCommand("ats service status")];
46771
47015
  return {
46772
47016
  serviceReadiness: "unknown",
@@ -50806,7 +51050,7 @@ function mapLocalControllerStatusReason(reason) {
50806
51050
  const projectedControllersDisabledReason = "one or more projected controllers are disabled locally";
50807
51051
  if (reason.includes("already exists")) return "setup is already prepared";
50808
51052
  if (reason.includes("authentication is not valid")) return "sign in required";
50809
- if (reason.includes("no bound agent controller requires local projection")) return "no local agent is configured for this device";
51053
+ if (reason.includes(NO_LOCAL_AGENTS_NEED_ATS_SERVICE_REASON)) return "no local agent is configured for this device";
50810
51054
  if (reason.includes(missingOrDisabledReason)) return "some local agents are missing or turned off";
50811
51055
  if (reason.includes("is missing for one or more bound controllers")) return "some local agents are missing";
50812
51056
  if (reason.includes(projectedControllersDisabledReason)) return "some local agent entries are disabled";
@@ -51012,6 +51256,177 @@ async function ensureDaemonVersionForCliStartup(input) {
51012
51256
  };
51013
51257
  }
51014
51258
 
51259
+ //#endregion
51260
+ //#region src/system/daemon-service-participation-gate.ts
51261
+ function shouldRequireDaemonServiceParticipation(localDemand) {
51262
+ return localDemand?.decision === "keep_running";
51263
+ }
51264
+ async function resolveDaemonServiceParticipationStatus(input) {
51265
+ const expectedVersion = input?.expectedVersion ?? resolveCurrentDaemonExpectedVersion();
51266
+ const inventory = await inspectDaemonServiceInventory({ expectedVersion });
51267
+ if (!(inventory.installStatus.installed && inventory.installStatus.state)) return {
51268
+ kind: "missing",
51269
+ expectedVersion
51270
+ };
51271
+ const installedVersion = inventory.installStatus.state.daemonVersion;
51272
+ if (installedVersion !== expectedVersion) return {
51273
+ kind: "outdated",
51274
+ expectedVersion,
51275
+ installedVersion
51276
+ };
51277
+ const repairAnomalies = inventory.anomalies.filter((anomaly) => DAEMON_SERVICE_REPAIR_ANOMALIES.has(anomaly.code));
51278
+ if (repairAnomalies.length > 0) return {
51279
+ kind: "needs_repair",
51280
+ expectedVersion,
51281
+ installedVersion,
51282
+ runtimeStatus: inventory.runtimeStatus.status,
51283
+ detail: repairAnomalies[0]?.message ?? "ATS found conflicting local service state on this device.",
51284
+ anomalyCodes: repairAnomalies.map((anomaly) => anomaly.code)
51285
+ };
51286
+ if (inventory.runtimeStatus.status !== "running") return {
51287
+ kind: "not_running",
51288
+ expectedVersion,
51289
+ installedVersion,
51290
+ runtimeStatus: inventory.runtimeStatus.status
51291
+ };
51292
+ return {
51293
+ kind: "ready",
51294
+ expectedVersion,
51295
+ installedVersion,
51296
+ runtimeStatus: inventory.runtimeStatus.status
51297
+ };
51298
+ }
51299
+ async function runDaemonServiceParticipationGate(input) {
51300
+ if (!shouldRunDaemonServiceParticipationGate(input)) return { status: "aligned" };
51301
+ const initialStatus = await resolveDaemonServiceParticipationStatus();
51302
+ if (initialStatus.kind === "ready") return { status: "aligned" };
51303
+ const codePrefix = resolveDaemonServiceParticipationCodePrefix(input.intent);
51304
+ const installStageResult = await runDaemonServiceParticipationInstallStage({
51305
+ input,
51306
+ codePrefix,
51307
+ status: initialStatus
51308
+ });
51309
+ if (installStageResult.result) return installStageResult.result;
51310
+ return await runDaemonServiceParticipationActionStage({
51311
+ input,
51312
+ codePrefix,
51313
+ status: installStageResult.status
51314
+ });
51315
+ }
51316
+ function shouldRunDaemonServiceParticipationGate(input) {
51317
+ return input.resolvedView === "human" && input.allowPrompt && (input.forcePrompt === true || shouldRequireDaemonServiceParticipation(input.localDemand));
51318
+ }
51319
+ async function runDaemonServiceParticipationInstallStage(input) {
51320
+ if (input.status.kind !== "missing" && input.status.kind !== "outdated") return { status: input.status };
51321
+ const installResult = await runDaemonInstallDecisionPrompt({
51322
+ kind: input.status.kind,
51323
+ ...input.status.kind === "outdated" ? { installedVersion: input.status.installedVersion } : {},
51324
+ expectedVersion: input.status.expectedVersion,
51325
+ emitLine: (text) => {
51326
+ input.input.presenter.line({
51327
+ code: `${input.codePrefix}.install`,
51328
+ text
51329
+ });
51330
+ },
51331
+ installService: async () => {
51332
+ await runDaemonInstall({ view: input.input.view }, { suppressHumanOutput: shouldSuppressHumanInstallOutput(input.input.intent) });
51333
+ }
51334
+ });
51335
+ if (installResult === "cancelled") return {
51336
+ status: input.status,
51337
+ result: { status: "cancelled" }
51338
+ };
51339
+ if (installResult === "failed") return {
51340
+ status: input.status,
51341
+ result: { status: "needs_attention" }
51342
+ };
51343
+ if (installResult === "not_now" || !shouldStartAfterDaemonInstall(input.input.intent)) return {
51344
+ status: input.status,
51345
+ result: { status: "declined" }
51346
+ };
51347
+ const refreshedStatus = await resolveDaemonServiceParticipationStatus();
51348
+ if (refreshedStatus.kind === "ready") return {
51349
+ status: refreshedStatus,
51350
+ result: { status: "aligned" }
51351
+ };
51352
+ if (refreshedStatus.kind === "missing" || refreshedStatus.kind === "outdated") {
51353
+ renderDaemonStartNeedsAttention({
51354
+ presenter: input.input.presenter,
51355
+ codePrefix: input.codePrefix,
51356
+ summary: refreshedStatus.kind === "missing" ? formatDaemonInstallFailedMessage("missing") : formatDaemonInstallFailedMessage("outdated")
51357
+ });
51358
+ return {
51359
+ status: refreshedStatus,
51360
+ result: { status: "needs_attention" }
51361
+ };
51362
+ }
51363
+ return { status: refreshedStatus };
51364
+ }
51365
+ async function runDaemonServiceParticipationActionStage(input) {
51366
+ if (input.status.kind === "needs_repair") return mapDaemonBackgroundStartResult(await runDaemonBackgroundStartForDecision({ view: input.input.view }), {
51367
+ presenter: input.input.presenter,
51368
+ codePrefix: input.codePrefix
51369
+ });
51370
+ if (input.status.kind !== "not_running") return { status: "aligned" };
51371
+ return mapDaemonBackgroundStartResult(await runDaemonStartDecisionPrompt({ startService: async () => await runDaemonBackgroundStartForDecision({ view: input.input.view }) }), {
51372
+ presenter: input.input.presenter,
51373
+ codePrefix: input.codePrefix
51374
+ });
51375
+ }
51376
+ function resolveDaemonServiceParticipationCodePrefix(intent) {
51377
+ switch (intent) {
51378
+ case "start": return "start.service";
51379
+ case "space_create_join": return "space.create_join.service";
51380
+ case "space_join": return "space.join.service";
51381
+ case "space_add_members": return "space.add_members.service";
51382
+ default: throw new Error(`Unknown daemon service participation intent: ${intent}`);
51383
+ }
51384
+ }
51385
+ function shouldStartAfterDaemonInstall(_intent) {
51386
+ return true;
51387
+ }
51388
+ function shouldSuppressHumanInstallOutput(intent) {
51389
+ return intent !== "start";
51390
+ }
51391
+ function mapDaemonBackgroundStartResult(result, input) {
51392
+ if (result === "started" || result === "deferred_with_card") return { status: "aligned" };
51393
+ if (result === "cancelled") return { status: "cancelled" };
51394
+ if (result === "not_now") {
51395
+ renderDaemonStartDeferred({
51396
+ presenter: input.presenter,
51397
+ codePrefix: input.codePrefix
51398
+ });
51399
+ return { status: "declined" };
51400
+ }
51401
+ if (result === "failed") {
51402
+ renderDaemonStartNeedsAttention({
51403
+ presenter: input.presenter,
51404
+ codePrefix: input.codePrefix,
51405
+ summary: "ATS could not finish starting ATS Service."
51406
+ });
51407
+ return { status: "needs_attention" };
51408
+ }
51409
+ return { status: "needs_attention" };
51410
+ }
51411
+ function renderDaemonStartDeferred(input) {
51412
+ const card = buildDaemonStartDeferredCard();
51413
+ renderInfoCard({
51414
+ presenter: input.presenter,
51415
+ title: card.title,
51416
+ codePrefix: `${input.codePrefix}.deferred`,
51417
+ rows: card.rows
51418
+ });
51419
+ }
51420
+ function renderDaemonStartNeedsAttention(input) {
51421
+ const card = buildDaemonStartNeedsAttentionCard({ summary: input.summary });
51422
+ renderInfoCard({
51423
+ presenter: input.presenter,
51424
+ title: card.title,
51425
+ codePrefix: `${input.codePrefix}.failed`,
51426
+ rows: card.rows
51427
+ });
51428
+ }
51429
+
51015
51430
  //#endregion
51016
51431
  //#region src/command-entry-checks/check-service.ts
51017
51432
  const FIX_SERVICE = "__command_entry_fix_service__";
@@ -51024,10 +51439,9 @@ async function runServiceCheck(input) {
51024
51439
  shouldAutoRepair: input.requirement.mode !== "status-only",
51025
51440
  view: input.context.runtime.resolvedView
51026
51441
  });
51027
- const shouldSuppressStandaloneHumanServiceCard = shouldDeferHumanServiceCardToJoinQuickCheck(input.context);
51028
51442
  if (serviceStatus.kind === "ready") return { status: "continue" };
51029
51443
  if (input.requirement.mode === "status-only") {
51030
- if (!shouldSuppressStandaloneHumanServiceCard) emitServiceStatus({
51444
+ emitServiceStatus({
51031
51445
  context: input.context,
51032
51446
  serviceStatus,
51033
51447
  blocking: false
@@ -51035,22 +51449,21 @@ async function runServiceCheck(input) {
51035
51449
  return { status: "continue" };
51036
51450
  }
51037
51451
  if (input.context.runtime.resolvedView !== "human" || !input.context.allowPrompt) {
51038
- if (!shouldSuppressStandaloneHumanServiceCard) emitServiceStatus({
51452
+ emitServiceStatus({
51039
51453
  context: input.context,
51040
51454
  serviceStatus,
51041
51455
  blocking: false
51042
51456
  });
51043
51457
  return { status: "continue" };
51044
51458
  }
51045
- if (serviceStatus.kind === "repair_blocked" || serviceStatus.kind === "repair_failed") {
51046
- if (!shouldSuppressStandaloneHumanServiceCard) emitServiceStatus({
51459
+ if (serviceStatus.kind === "needs_repair" || serviceStatus.kind === "repair_blocked" || serviceStatus.kind === "repair_failed") {
51460
+ emitServiceStatus({
51047
51461
  context: input.context,
51048
51462
  serviceStatus,
51049
51463
  blocking: false
51050
51464
  });
51051
51465
  return { status: "continue" };
51052
51466
  }
51053
- if (shouldSuppressStandaloneHumanServiceCard) return { status: "continue" };
51054
51467
  const copy = getCommandEntryCopy(input.context.entryName);
51055
51468
  showHumanCheckCard({
51056
51469
  presenter: input.context.presenter,
@@ -51085,12 +51498,21 @@ async function runServiceCheck(input) {
51085
51498
  if (!await fixServiceStatus(serviceStatus)) return { status: "continue" };
51086
51499
  return { status: "continue" };
51087
51500
  }
51088
- function shouldDeferHumanServiceCardToJoinQuickCheck(context) {
51089
- return context.entryName === "space.join" && context.runtime.resolvedView === "human";
51090
- }
51091
51501
  async function resolveServiceStatus(input) {
51092
51502
  const expectedVersion = resolveCurrentDaemonExpectedVersion();
51093
- const currentStatus = await readCurrentServiceStatus(expectedVersion);
51503
+ const currentStatus = await resolveDaemonServiceParticipationStatus({ expectedVersion }).catch((error) => {
51504
+ warnServiceCheckIssue("resolveDaemonServiceParticipationStatus failed", {
51505
+ error,
51506
+ expectedVersion,
51507
+ view: input.view
51508
+ });
51509
+ return {
51510
+ kind: "not_running",
51511
+ expectedVersion,
51512
+ installedVersion: null,
51513
+ runtimeStatus: null
51514
+ };
51515
+ });
51094
51516
  if (currentStatus.kind === "ready") return currentStatus;
51095
51517
  if (input.shouldAutoRepair) {
51096
51518
  const preflight = await runDaemonStartupPreflight({
@@ -51113,27 +51535,19 @@ async function resolveServiceStatus(input) {
51113
51535
  detail: buildStartupPreflightAttentionDetail(preflight)
51114
51536
  };
51115
51537
  }
51116
- return await readCurrentServiceStatus(expectedVersion);
51117
- }
51118
- async function readCurrentServiceStatus(expectedVersion) {
51119
- const daemonStatus = await getDaemonStatus();
51120
- if (!(daemonStatus.installed && daemonStatus.state)) return { kind: "missing" };
51121
- if (daemonStatus.state.daemonVersion !== expectedVersion) return {
51122
- kind: "outdated",
51123
- installedVersion: daemonStatus.state.daemonVersion
51124
- };
51125
- const runtime = await readCurrentDaemonRuntimeContext({ status: daemonStatus }).catch((error) => {
51126
- warnServiceCheckIssue("readCurrentDaemonRuntimeContext failed", {
51127
- daemonStatus,
51128
- error
51538
+ return await resolveDaemonServiceParticipationStatus({ expectedVersion }).catch((error) => {
51539
+ warnServiceCheckIssue("resolveDaemonServiceParticipationStatus failed", {
51540
+ error,
51541
+ expectedVersion,
51542
+ view: input.view
51129
51543
  });
51130
- return null;
51544
+ return {
51545
+ kind: "not_running",
51546
+ expectedVersion,
51547
+ installedVersion: null,
51548
+ runtimeStatus: null
51549
+ };
51131
51550
  });
51132
- if (!runtime || runtime.effectiveRuntimeStatus !== "running") return {
51133
- kind: "not_running",
51134
- runtimeStatus: runtime?.runtimeStatus ?? null
51135
- };
51136
- return { kind: "ready" };
51137
51551
  }
51138
51552
  async function fixServiceStatus(serviceStatus) {
51139
51553
  const expectedVersion = resolveCurrentDaemonExpectedVersion();
@@ -51154,6 +51568,7 @@ async function fixServiceStatus(serviceStatus) {
51154
51568
  return true;
51155
51569
  }
51156
51570
  if (serviceStatus.kind === "not_running") return await runDaemonBackgroundStartForDecision({ view: "human" }) === "started";
51571
+ if (serviceStatus.kind === "needs_repair") return await runDaemonBackgroundStartForDecision({ view: "human" }) === "started";
51157
51572
  if (serviceStatus.kind === "repair_blocked" || serviceStatus.kind === "repair_failed") {
51158
51573
  await runDaemonStatusSafely();
51159
51574
  return false;
@@ -51203,6 +51618,7 @@ function emitServiceStatus(input) {
51203
51618
  function buildServiceStatusText(serviceStatus) {
51204
51619
  if (serviceStatus.kind === "missing") return "ATS Service is not installed on this device.";
51205
51620
  if (serviceStatus.kind === "outdated") return `ATS Service is on ${serviceStatus.installedVersion}. ${resolveCurrentDaemonExpectedVersion()} is needed.`;
51621
+ if (serviceStatus.kind === "needs_repair") return "ATS Service needs cleanup before local agents on this device can reply from Space.";
51206
51622
  if (serviceStatus.kind === "repair_blocked") return "ATS Service needs manual repair before ATS can align it automatically.";
51207
51623
  if (serviceStatus.kind === "repair_failed") return "ATS tried to repair ATS Service automatically, but the service still needs attention.";
51208
51624
  return "ATS Service is installed but not running.";
@@ -51210,17 +51626,19 @@ function buildServiceStatusText(serviceStatus) {
51210
51626
  function buildServiceNextText(serviceStatus, action) {
51211
51627
  if (serviceStatus.kind === "missing") return action ? `Install ATS Service now, or continue ${action} without it.` : "Install ATS Service now, or continue without it.";
51212
51628
  if (serviceStatus.kind === "outdated") return "Update ATS Service now to match the current ATS CLI.";
51629
+ if (serviceStatus.kind === "needs_repair") return "Repair ATS Service now so local agents on this device can reply from Space again.";
51213
51630
  if (serviceStatus.kind === "repair_blocked" || serviceStatus.kind === "repair_failed") return "Run `ats service status` first. If ATS still says the service needs repair, run `ats service reinstall`.";
51214
51631
  return action ? `Start ATS Service now, or continue ${action} without it.` : "Start ATS Service now, or continue without it.";
51215
51632
  }
51216
51633
  function buildServiceFixLabel(serviceStatus) {
51217
51634
  if (serviceStatus.kind === "missing") return "Install ATS Service now (Recommended)";
51218
51635
  if (serviceStatus.kind === "outdated") return "Update ATS Service now (Recommended)";
51636
+ if (serviceStatus.kind === "needs_repair") return "Repair ATS Service now (Recommended)";
51219
51637
  if (serviceStatus.kind === "repair_blocked" || serviceStatus.kind === "repair_failed") return "Open ATS Service diagnostics (Recommended)";
51220
51638
  return "Start ATS Service now (Recommended)";
51221
51639
  }
51222
51640
  function buildServiceAgentNextSteps(serviceStatus) {
51223
- if (serviceStatus.kind === "repair_blocked" || serviceStatus.kind === "repair_failed") return formatAtsCliCommands(["ats service status", "ats service reinstall"]);
51641
+ if (serviceStatus.kind === "needs_repair" || serviceStatus.kind === "repair_blocked" || serviceStatus.kind === "repair_failed") return formatAtsCliCommands(["ats service status", "ats service reinstall"]);
51224
51642
  return formatAtsCliCommands(["ats service install", "ats service run"]);
51225
51643
  }
51226
51644
  function buildStartupPreflightAttentionDetail(preflight) {
@@ -54638,6 +55056,7 @@ async function runSkillsCheck(input) {
54638
55056
  //#endregion
54639
55057
  //#region src/command-entry-checks/run-command-entry-flow.ts
54640
55058
  const CHECK_RUNNERS = {
55059
+ disclaimer: runDisclaimerCheck,
54641
55060
  cliUpdate: runCliUpdateCheck,
54642
55061
  signIn: runSignInCheck,
54643
55062
  selectedProfile: runSelectedProfileCheck,
@@ -54652,6 +55071,7 @@ const CHECK_STAGES = [
54652
55071
  "status-only"
54653
55072
  ];
54654
55073
  const CHECK_ORDER = [
55074
+ "disclaimer",
54655
55075
  "cliUpdate",
54656
55076
  "localRuntimeState",
54657
55077
  "signIn",
@@ -60533,6 +60953,7 @@ function buildServiceReplyReadinessGuidance(input) {
60533
60953
  }
60534
60954
  function buildReplyReadinessGuidanceSummary(reasonCode) {
60535
60955
  switch (reasonCode) {
60956
+ case "local_agents.none_needed": return "This device does not need ATS Service until you choose local agents to bring into a space.";
60536
60957
  case "profile.inactive": return "Choose an active agent profile first.";
60537
60958
  case "profile.unbound": return "This profile is not connected to an agent yet.";
60538
60959
  case "controller.binding.disabled": return "This profile's agent is turned off.";
@@ -60561,6 +60982,11 @@ function buildReplyReadinessNextSteps(input) {
60561
60982
  controllerRef
60562
60983
  }) ?? controllerRef ?? "<agent-id>";
60563
60984
  switch (input.reasonCode) {
60985
+ case "local_agents.none_needed": return [
60986
+ formatAtsCliCommand("ats start"),
60987
+ formatAtsCliCommand("ats service install"),
60988
+ formatAtsCliCommand("ats service run")
60989
+ ];
60564
60990
  case "profile.inactive": return [formatAtsCliCommand("ats profile list"), formatAtsCliCommand("ats profile set <agent-profile-id>")];
60565
60991
  case "profile.unbound": return [formatAtsCliCommand(`ats profile update ${profileId} --controller-kind builtin --controller-ref ${controllerRef ?? "<controller-ref>"} --controller-enabled true`), formatAtsCliCommand(`ats space join <space-id> --profile ${profileId}`)];
60566
60992
  case "controller.binding.disabled": return [formatAtsCliCommand(`ats profile update ${profileId} --controller-enabled true`)];
@@ -62583,19 +63009,6 @@ function normalizeOptionalString$5(value) {
62583
63009
 
62584
63010
  //#endregion
62585
63011
  //#region src/start-foundation/selectors.ts
62586
- const BACKGROUND_HELPER_REASON_CODES = new Set([
62587
- "background_helper.not_installed",
62588
- "background_helper.installed_outdated",
62589
- "background_helper.not_running",
62590
- "background_helper.stale",
62591
- "background_helper.unknown"
62592
- ]);
62593
- function isCliStartBackgroundHelperReasonCode(reasonCode) {
62594
- return BACKGROUND_HELPER_REASON_CODES.has(reasonCode);
62595
- }
62596
- function isCliStartBackgroundHelperSignal(signal) {
62597
- return signal.actionId === "device_check" && isCliStartBackgroundHelperReasonCode(signal.reasonCode);
62598
- }
62599
63012
  function findCliStartSelectedProfileReadinessEntry(input) {
62600
63013
  return input.foundation.foundationState.readModel.profileReadiness.find((entry) => entry.profileId === input.profileId) ?? null;
62601
63014
  }
@@ -62605,8 +63018,13 @@ function readCliStartDeviceProjection(foundation) {
62605
63018
  function isCliStartNextActionBlocked(foundation) {
62606
63019
  return foundation.foundationState.decision.status === "blocking";
62607
63020
  }
62608
- function hasCliStartBackgroundHelperBlockingOrAttentionSignal(foundation) {
62609
- return [...foundation.foundationState.decision.blocking, ...foundation.foundationState.decision.attention].some(isCliStartBackgroundHelperSignal);
63021
+ function hasCliStartSelectedLocalAgents(foundation) {
63022
+ return foundation.foundationState.readModel.session.value?.agentSelection.mode === "selected_agents";
63023
+ }
63024
+ function readCliStartSelectedLocalAgentIds(foundation) {
63025
+ const agentSelection = foundation.foundationState.readModel.session.value?.agentSelection;
63026
+ if (!agentSelection || agentSelection.mode !== "selected_agents") return [];
63027
+ return agentSelection.selectedAgentIds;
62610
63028
  }
62611
63029
 
62612
63030
  //#endregion
@@ -62900,12 +63318,6 @@ function collectStartLocalBackgroundHelperReadiness() {
62900
63318
  async function resolveStartLocalBackgroundHelperDemand(input) {
62901
63319
  return await resolveDaemonLocalServiceDemand(input);
62902
63320
  }
62903
- async function runStartLocalBackgroundHelperInstall(input) {
62904
- await runDaemonInstall({ view: input.view });
62905
- }
62906
- async function runStartLocalBackgroundHelperBackgroundStart(input) {
62907
- return await runDaemonBackgroundStartForDecision({ view: input.view });
62908
- }
62909
63321
 
62910
63322
  //#endregion
62911
63323
  //#region src/start-local/skills-local.ts
@@ -62948,49 +63360,13 @@ async function collectStartLocalReadiness(input) {
62948
63360
 
62949
63361
  //#endregion
62950
63362
  //#region src/commands/start-auth-and-readiness.ts
62951
- const START_DISCLAIMER_ACCEPT = "__start_disclaimer_accept__";
62952
- const START_DISCLAIMER_DECLINE = "__start_disclaimer_decline__";
62953
- const START_V1_DISCLAIMER_VERSION = "start-v1-2026-03-02";
62954
- const START_V1_DISCLAIMER_TEXT = "ATSD is experimental and changes rapidly. Breaking changes may occur. You are fully responsible for data security and sensitive information handling. Use ATSD only in trusted environments.";
62955
63363
  async function runStartDisclaimerStep(input) {
62956
- if ((await getConfiguredStartV1OnboardingState().catch(() => null))?.disclaimerAcceptedVersion === START_V1_DISCLAIMER_VERSION) return true;
62957
- if (input.runtime.resolvedView === "agent") {
62958
- input.presenter.data({
62959
- code: "start.disclaimer",
62960
- payload: {
62961
- version: START_V1_DISCLAIMER_VERSION,
62962
- text: START_V1_DISCLAIMER_TEXT,
62963
- mode: "agent_notice",
62964
- requiresConfirmation: false
62965
- }
62966
- });
62967
- return true;
62968
- }
62969
- note(`⚠️ ${START_V1_DISCLAIMER_TEXT}`, "Disclaimer");
62970
- if (!input.interactive) {
62971
- input.presenter.line({
62972
- code: "start.disclaimer.auto_continue",
62973
- text: "Non-interactive session: disclaimer displayed and onboarding continues."
62974
- });
62975
- return true;
62976
- }
62977
- const decision = await select({
62978
- message: "Do you accept and continue?",
62979
- options: [{
62980
- value: START_DISCLAIMER_ACCEPT,
62981
- label: "Yes, continue"
62982
- }, {
62983
- value: START_DISCLAIMER_DECLINE,
62984
- label: "No, exit"
62985
- }],
62986
- initialValue: START_DISCLAIMER_ACCEPT
63364
+ return await runOnboardingDisclaimerGate({
63365
+ runtime: input.runtime,
63366
+ presenter: input.presenter,
63367
+ interactive: input.interactive,
63368
+ codePrefix: "start.disclaimer"
62987
63369
  });
62988
- if (isCancel(decision) || decision === START_DISCLAIMER_DECLINE) {
62989
- cancel("Cancelled.");
62990
- return false;
62991
- }
62992
- await persistStartDisclaimerAccepted();
62993
- return true;
62994
63370
  }
62995
63371
  async function markStartV1Completed() {
62996
63372
  const now = (/* @__PURE__ */ new Date()).toISOString();
@@ -63077,12 +63453,6 @@ function formatReadinessLine(input) {
63077
63453
  if (!input.detail) return line;
63078
63454
  return `${line} ${pc.dim(input.detail)}`;
63079
63455
  }
63080
- async function persistStartDisclaimerAccepted() {
63081
- await updateConfiguredStartV1OnboardingState((current) => ({
63082
- ...current ?? {},
63083
- disclaimerAcceptedVersion: START_V1_DISCLAIMER_VERSION
63084
- }));
63085
- }
63086
63456
  function evaluateStartAuthentication(input) {
63087
63457
  return evaluateCommandAuthentication({ gatewayUrl: input.gatewayUrl });
63088
63458
  }
@@ -64247,10 +64617,14 @@ function resolveStartBackgroundHelperShellState(input) {
64247
64617
  runtimeStatus: input.readiness.service.runtime?.status ?? "unknown"
64248
64618
  };
64249
64619
  }
64250
- function resolveStartServiceRecommendation(input) {
64251
- if (input.backgroundHelper.status === "installed_outdated") return "recommended";
64252
- if (input.backgroundHelper.status === "installed_current") return input.localDemand?.decision === "keep_running" ? "recommended" : "optional";
64253
- return hasCliStartBackgroundHelperBlockingOrAttentionSignal(input.foundation) ? "recommended" : "optional";
64620
+ function haveRunnableSelectedLocalAgentSetup(input) {
64621
+ const selectedAgentIds = readCliStartSelectedLocalAgentIds(input.foundation);
64622
+ if (selectedAgentIds.length === 0) return false;
64623
+ const candidateById = new Map(input.readiness.agents.candidates.map((candidate) => [candidate.agentId, candidate]));
64624
+ return selectedAgentIds.every((selectedAgentId) => {
64625
+ const candidate = candidateById.get(selectedAgentId);
64626
+ return candidate?.enabled === true && candidate.selectable === true;
64627
+ });
64254
64628
  }
64255
64629
  function emitStartServiceNonInteractiveLine(input) {
64256
64630
  const text = input.backgroundHelper.status === "installed_current" ? formatInlineAtsCliCommands("⚠️ ATS Service: Installed and up to date, but not currently running. Start it manually with `ats service run`.") : formatInlineAtsCliCommands(input.backgroundHelper.status === "not_installed" ? `⚠️ ATS Service: ${formatDaemonServiceMissingStatus()} ${formatDaemonServiceMissingAction()}` : `⚠️ ATS Service: ${formatDaemonServiceOutdatedStatus({
@@ -64262,78 +64636,47 @@ function emitStartServiceNonInteractiveLine(input) {
64262
64636
  text
64263
64637
  });
64264
64638
  }
64265
- function emitStartServiceSkippedLine(input) {
64266
- const text = resolveStartServiceSkippedText(input);
64267
- input.presenter.line({
64268
- code: "start.service.skipped",
64269
- text: formatInlineAtsCliCommands(text)
64639
+
64640
+ //#endregion
64641
+ //#region src/system/service-participation-policy.ts
64642
+ function resolveCliServiceParticipationRequirement(input) {
64643
+ return resolveServiceParticipationRequirement({
64644
+ intent: input.intent,
64645
+ humanOnlyParticipation: input.humanOnlyParticipation,
64646
+ explicitSelectedLocalAgents: input.explicitSelectedLocalAgents,
64647
+ selectedAgentProfilesHaveRunnableLocalSetup: input.selectedAgentProfilesHaveRunnableLocalSetup,
64648
+ deviceLocalDemand: mapDaemonLocalServiceDemandToSharedValue(input.localDemand)
64270
64649
  });
64271
64650
  }
64272
- function renderStartServiceDeferredCard(input) {
64273
- const card = buildDaemonStartDeferredCard();
64274
- renderInfoCard({
64275
- presenter: input.presenter,
64276
- title: card.title,
64277
- codePrefix: "start.service.deferred",
64278
- rows: card.rows
64279
- });
64651
+ function isExplicitLocalAgentProfile(profile) {
64652
+ return profile.profileKind === "agent";
64280
64653
  }
64281
- function renderStartServiceFailureCard(input) {
64282
- const card = buildDaemonStartNeedsAttentionCard({ summary: "ATS could not finish starting ATS Service." });
64283
- renderInfoCard({
64284
- presenter: input.presenter,
64285
- title: card.title,
64286
- codePrefix: "start.service.failed",
64287
- rows: card.rows
64654
+ function hasRunnableLocalAgentSetup(profile) {
64655
+ return hasRunnableLocalAgentParticipationCandidate({
64656
+ profileKind: profile.profileKind === "agent" ? "agent" : "human",
64657
+ controllerRef: profile.controllerBinding?.controllerRef,
64658
+ controllerEnabled: profile.controllerBinding?.controllerEnabled
64288
64659
  });
64289
64660
  }
64290
- async function promptForStartServiceInstallDecision(input) {
64291
- const actionLabel = input.kind === "outdated" ? "Update ATS Service now" : "Install ATS Service now";
64292
- const statusLine = input.kind === "outdated" ? formatDaemonServiceOutdatedStatus({
64293
- installedVersion: input.installedVersion,
64294
- expectedVersion: input.expectedVersion
64295
- }) : formatDaemonServiceMissingStatus();
64296
- const explanation = input.recommendation === "recommended" ? "ATS Service lets local agents on this device reply from Space, so we recommend it for this setup." : "You can keep going without ATS Service, but local agents on this device won't be able to reply from Space.";
64297
- const detailLine = input.kind === "outdated" && input.installedVersion ? `Installed v${input.installedVersion}, expected v${input.expectedVersion}.` : null;
64298
- const decision = await select({
64299
- message: [
64300
- input.kind === "outdated" ? "ATS Service needs an update. Fix it now?" : "ATS Service is missing. Install it now?",
64301
- explanation,
64302
- `Current status: ${statusLine}`,
64303
- detailLine
64304
- ].filter((line) => typeof line === "string" && line.length > 0).join("\n"),
64305
- initialValue: "fix_now",
64306
- options: [{
64307
- value: "fix_now",
64308
- label: actionLabel,
64309
- hint: input.recommendation === "recommended" ? "Recommended" : "Optional"
64310
- }, {
64311
- value: "not_now",
64312
- label: "Not now"
64313
- }]
64314
- });
64315
- if (isCancel(decision)) return "cancelled";
64316
- return decision === "fix_now" ? "fix_now" : "not_now";
64661
+ function hasAnyExplicitLocalAgentCandidates(candidates) {
64662
+ return candidates.some((candidate) => candidate.profileKind === "agent");
64317
64663
  }
64318
- function resolveStartServiceSkippedText(input) {
64319
- if (input.outcome === "failed_install") return input.backgroundHelper.status === "installed_outdated" ? "ATS Service was not updated. Local agents on this device may not be able to reply from Space. Run `ats service install`." : "ATS Service could not be installed. Local agents on this device still can't reply from Space. Run `ats service install`.";
64320
- return input.backgroundHelper.status === "installed_outdated" ? "ATS Service still needs an update. Local agents on this device may not be able to reply from Space until ATS fixes it. Run `ats service install` later." : "ATS Service is still missing. Local agents on this device can't reply from Space until you install it later with `ats service install`.";
64664
+ function haveAllExplicitLocalAgentCandidatesRunnable(candidates) {
64665
+ const agentCandidates = candidates.filter((candidate) => candidate.profileKind === "agent");
64666
+ if (agentCandidates.length === 0) return false;
64667
+ return agentCandidates.every((candidate) => hasRunnableLocalAgentParticipationCandidate(candidate));
64321
64668
  }
64322
- async function promptForStartServiceStartDecision(input) {
64323
- const decision = await select({
64324
- message: ["ATS Service is not running. Start it now?", input.recommendation === "recommended" ? "ATS Service lets local agents on this device reply from Space, so we recommend it for this setup." : "You can keep going without ATS Service, but local agents on this device won't be able to reply from Space."].join("\n"),
64325
- initialValue: "fix_now",
64326
- options: [{
64327
- value: "fix_now",
64328
- label: "Start ATS Service now",
64329
- hint: input.recommendation === "recommended" ? "Recommended" : "Optional"
64330
- }, {
64331
- value: "not_now",
64332
- label: "Not now"
64333
- }]
64334
- });
64335
- if (isCancel(decision)) return "cancelled";
64336
- return decision === "fix_now" ? "fix_now" : "not_now";
64669
+ function hasRunnableLocalAgentParticipationCandidate(candidate) {
64670
+ if (candidate.profileKind !== "agent") return false;
64671
+ return candidate.controllerEnabled === true && typeof candidate.controllerRef === "string" && candidate.controllerRef.trim().length > 0;
64672
+ }
64673
+ function mapDaemonLocalServiceDemandToSharedValue(localDemand) {
64674
+ switch (localDemand?.decision) {
64675
+ case "keep_running":
64676
+ case "allow_auto_stop":
64677
+ case "indeterminate": return localDemand.decision;
64678
+ default: return "indeterminate";
64679
+ }
64337
64680
  }
64338
64681
 
64339
64682
  //#endregion
@@ -64343,6 +64686,7 @@ async function runStartServiceStep(input) {
64343
64686
  foundation: input.foundation,
64344
64687
  readiness: input.readiness
64345
64688
  });
64689
+ const selectedLocalAgentsRequireService = hasCliStartSelectedLocalAgents(input.foundation);
64346
64690
  const payload = buildStartServicePayload({
64347
64691
  readiness: input.readiness,
64348
64692
  backgroundHelper
@@ -64354,15 +64698,7 @@ async function runStartServiceStep(input) {
64354
64698
  });
64355
64699
  return "continue";
64356
64700
  }
64357
- if (backgroundHelper.status === "installed_current") return await runInstalledCurrentStartServiceStep({
64358
- ...input,
64359
- backgroundHelper
64360
- });
64361
- const recommendation = resolveStartServiceRecommendation({
64362
- backgroundHelper,
64363
- foundation: input.foundation,
64364
- localDemand: null
64365
- });
64701
+ if (backgroundHelper.status === "installed_current" && backgroundHelper.runtimeStatus === "running") return "continue";
64366
64702
  if (!input.interactive) {
64367
64703
  emitStartServiceNonInteractiveLine({
64368
64704
  presenter: input.presenter,
@@ -64370,78 +64706,46 @@ async function runStartServiceStep(input) {
64370
64706
  });
64371
64707
  return "continue";
64372
64708
  }
64373
- const decision = await promptForStartServiceInstallDecision({
64374
- kind: backgroundHelper.status === "installed_outdated" ? "outdated" : "missing",
64375
- installedVersion: backgroundHelper.installedVersion ?? void 0,
64376
- expectedVersion: backgroundHelper.expectedVersion,
64377
- recommendation
64709
+ const localDemand = await resolveStartLocalBackgroundHelperDemand({ ownerUserId: input.ownerUserId }).catch(() => null);
64710
+ const currentProfileIsExplicitLocalAgent = isExplicitLocalAgentProfile(input.profile);
64711
+ const selectedLocalAgentsHaveRunnableLocalSetup = !selectedLocalAgentsRequireService || haveRunnableSelectedLocalAgentSetup({
64712
+ foundation: input.foundation,
64713
+ readiness: input.readiness
64378
64714
  });
64379
- if (decision === "cancelled") {
64380
- cancel("Cancelled.");
64381
- return "cancelled";
64382
- }
64383
- if (decision === "fix_now") {
64384
- try {
64385
- await runStartLocalBackgroundHelperInstall({ view: input.view });
64386
- } catch {
64387
- emitStartServiceSkippedLine({
64388
- presenter: input.presenter,
64389
- backgroundHelper,
64390
- outcome: "failed_install"
64391
- });
64392
- return "needs_attention";
64393
- }
64394
- return "continue";
64395
- }
64396
- emitStartServiceSkippedLine({
64397
- presenter: input.presenter,
64398
- backgroundHelper,
64399
- outcome: "not_fixed"
64715
+ const currentProfileHasRunnableLocalSetup = !currentProfileIsExplicitLocalAgent || hasRunnableLocalAgentSetup(input.profile);
64716
+ const serviceRequirement = resolveCliServiceParticipationRequirement({
64717
+ intent: "start",
64718
+ humanOnlyParticipation: !(selectedLocalAgentsRequireService || currentProfileIsExplicitLocalAgent) && localDemand?.decision !== "keep_running",
64719
+ explicitSelectedLocalAgents: selectedLocalAgentsRequireService || currentProfileIsExplicitLocalAgent,
64720
+ selectedAgentProfilesHaveRunnableLocalSetup: selectedLocalAgentsHaveRunnableLocalSetup && currentProfileHasRunnableLocalSetup,
64721
+ localDemand
64400
64722
  });
64401
- return "continue";
64402
- }
64403
- async function runInstalledCurrentStartServiceStep(input) {
64404
- const localDemand = input.runtime.resolvedView === "human" && input.interactive ? await resolveStartLocalBackgroundHelperDemand({ ownerUserId: input.ownerUserId }).catch(() => null) : null;
64405
- if (input.backgroundHelper.runtimeStatus === "running") return "continue";
64406
- if (localDemand && localDemand.decision !== "keep_running") return "continue";
64407
- if (!input.interactive) {
64408
- emitStartServiceNonInteractiveLine({
64409
- presenter: input.presenter,
64410
- backgroundHelper: input.backgroundHelper
64723
+ if (serviceRequirement === "not_needed") return "continue";
64724
+ if (serviceRequirement === "agent_setup_needed") {
64725
+ input.presenter.line({
64726
+ code: "start.service.agent_setup_needed",
64727
+ text: `Finish local agent setup before you bring this profile into a space. Use \`ats profile ready --profile ${input.profile.atsProfileId}\` or rerun \`ats start\`.`
64411
64728
  });
64412
64729
  return "continue";
64413
64730
  }
64414
- return await handleInstalledCurrentStartServiceDecision({
64731
+ const gateResult = await runDaemonServiceParticipationGate({
64732
+ intent: "start",
64415
64733
  presenter: input.presenter,
64416
- recommendation: resolveStartServiceRecommendation({
64417
- backgroundHelper: input.backgroundHelper,
64418
- foundation: input.foundation,
64419
- localDemand
64420
- }),
64734
+ resolvedView: input.runtime.resolvedView,
64735
+ allowPrompt: input.interactive,
64736
+ localDemand,
64737
+ forcePrompt: selectedLocalAgentsRequireService || currentProfileIsExplicitLocalAgent,
64421
64738
  view: input.view
64422
64739
  });
64423
- }
64424
- async function handleInstalledCurrentStartServiceDecision(input) {
64425
- const decision = await promptForStartServiceStartDecision({ recommendation: input.recommendation });
64426
- if (decision === "cancelled") {
64427
- cancel("Cancelled.");
64428
- return "cancelled";
64429
- }
64430
- if (decision === "not_now") {
64431
- renderStartServiceDeferredCard({ presenter: input.presenter });
64432
- return "continue";
64433
- }
64434
- const startResult = await runStartLocalBackgroundHelperBackgroundStart({ view: input.view }).catch(() => "failed");
64435
- if (startResult === "cancelled") {
64740
+ if (gateResult.status === "cancelled") {
64436
64741
  cancel("Cancelled.");
64437
64742
  return "cancelled";
64438
64743
  }
64439
- if (startResult === "deferred_with_card") return "continue";
64440
- if (startResult === "failed") {
64441
- renderStartServiceFailureCard({ presenter: input.presenter });
64442
- return "needs_attention";
64443
- }
64444
- if (startResult === "failed_with_card") return "needs_attention";
64744
+ if (gateResult.status === "needs_attention") return "needs_attention";
64745
+ if (gateResult.status === "declined") input.presenter.line({
64746
+ code: "start.service.declined",
64747
+ text: "ATS will continue without wakeable local agent participation on this device. To enable background replies later, rerun `ats start` or use `ats service install` and `ats service run`."
64748
+ });
64445
64749
  return "continue";
64446
64750
  }
64447
64751
 
@@ -71915,6 +72219,11 @@ async function emitSpaceCommandPreflight(input) {
71915
72219
  },
71916
72220
  serviceRepairGuidance: resolveSpacePreflightRepairGuidance({ serviceParticipationReadiness })
71917
72221
  };
72222
+ if (!shouldEmitSpaceCommandPreflightOutput(input.emitOutput)) return {
72223
+ deviceReplyReadiness: replyReadinessSnapshot.deviceReplyReadiness,
72224
+ payload,
72225
+ serviceParticipationReadiness
72226
+ };
71918
72227
  if (input.resolvedView === "human") {
71919
72228
  const authLabel = buildHumanPreflightAuthLabel(payload.auth);
71920
72229
  const agentReplyReadinessLabel = payload.agentReplyReadiness && input.profile.profileKind === "agent" && shouldSurfaceSenderReplyReadinessInPreflightHeadline(input.command) ? formatReplyReadinessSummary(payload.agentReplyReadiness) : null;
@@ -71926,7 +72235,8 @@ async function emitSpaceCommandPreflight(input) {
71926
72235
  profileName: payload.profile.profileName,
71927
72236
  agentReplyReadinessLabel,
71928
72237
  guidance: payload.serviceRepairGuidance,
71929
- serviceParticipationReadiness
72238
+ serviceParticipationReadiness,
72239
+ humanServiceGuidanceMode: input.humanServiceGuidanceMode ?? "show"
71930
72240
  });
71931
72241
  return {
71932
72242
  deviceReplyReadiness: replyReadinessSnapshot.deviceReplyReadiness,
@@ -71984,7 +72294,8 @@ function emitHumanSpaceCommandPreflight(input) {
71984
72294
  emitSpacePreflightRepairGuidance({
71985
72295
  presenter: input.presenter,
71986
72296
  command: input.command,
71987
- guidance: input.guidance
72297
+ guidance: input.guidance,
72298
+ mode: input.humanServiceGuidanceMode
71988
72299
  });
71989
72300
  return;
71990
72301
  }
@@ -71995,7 +72306,8 @@ function emitHumanSpaceCommandPreflight(input) {
71995
72306
  emitSpacePreflightRepairGuidance({
71996
72307
  presenter: input.presenter,
71997
72308
  command: input.command,
71998
- guidance: input.guidance
72309
+ guidance: input.guidance,
72310
+ mode: input.humanServiceGuidanceMode
71999
72311
  });
72000
72312
  }
72001
72313
  function shouldSurfaceSenderReplyReadinessInPreflightHeadline(command) {
@@ -72063,7 +72375,7 @@ function toSpacePreflightAgentReplyReadiness(input) {
72063
72375
  };
72064
72376
  }
72065
72377
  function emitSpacePreflightRepairGuidance(input) {
72066
- if (!input.guidance) return;
72378
+ if (!input.guidance || input.mode === "suppress") return;
72067
72379
  input.presenter.line({
72068
72380
  code: `space.${input.command}.preflight.next_step`,
72069
72381
  text: `Next step: ${input.guidance.summary}`,
@@ -72073,6 +72385,9 @@ function emitSpacePreflightRepairGuidance(input) {
72073
72385
  }
72074
72386
  });
72075
72387
  }
72388
+ function shouldEmitSpaceCommandPreflightOutput(emitOutput) {
72389
+ return emitOutput !== false;
72390
+ }
72076
72391
  function emitSpaceFailureNextStep(input) {
72077
72392
  const guidance = resolveSpaceFailureGuidance(input.error);
72078
72393
  if (!guidance) return;
@@ -79302,10 +79617,6 @@ function emitSpaceJoinLockBlocked(input) {
79302
79617
 
79303
79618
  //#endregion
79304
79619
  //#region src/space/join-entry.ts
79305
- const NOT_INSTALLED_DAEMON_STATUS = {
79306
- installed: false,
79307
- state: null
79308
- };
79309
79620
  const LOCAL_SERVICE_ONLY_JOIN_BLOCK_REASON_CODES = new Set([
79310
79621
  "service.drifted",
79311
79622
  "service.not_installed",
@@ -79413,99 +79724,67 @@ async function prepareSpaceJoinServiceState(input) {
79413
79724
  };
79414
79725
  }
79415
79726
  async function assertSpaceJoinTargetPreflightReady(input) {
79727
+ const serviceRequirement = input.resolvedView === "human" ? resolveCliServiceParticipationRequirement({
79728
+ intent: input.serviceIntent ?? "space_join",
79729
+ humanOnlyParticipation: !isExplicitLocalAgentProfile(input.atsProfile) && input.localDemand?.decision !== "keep_running",
79730
+ explicitSelectedLocalAgents: isExplicitLocalAgentProfile(input.atsProfile),
79731
+ selectedAgentProfilesHaveRunnableLocalSetup: hasRunnableLocalAgentSetup(input.atsProfile),
79732
+ localDemand: input.localDemand ?? null
79733
+ }) : "not_needed";
79734
+ if (serviceRequirement === "agent_setup_needed") {
79735
+ input.presenter.line({
79736
+ code: "space.join.agent_setup_needed",
79737
+ text: `Finish local agent setup before you bring this profile into a space. Use \`ats profile ready --profile ${input.atsProfile.atsProfileId}\` or rerun \`ats start\`.`
79738
+ });
79739
+ return "cancelled";
79740
+ }
79741
+ const shouldManageServiceParticipation = serviceRequirement === "offer_service_gate";
79416
79742
  const runPreflight = async () => await emitSpaceCommandPreflight({
79417
79743
  presenter: input.presenter,
79418
79744
  command: "join",
79419
79745
  profile: input.atsProfile,
79420
79746
  baseUrl: input.baseUrl,
79421
- resolvedView: input.resolvedView
79422
- });
79423
- let preflight = await runPreflight();
79424
- const blockingLocalServiceOutcome = await maybeHandleBlockingLocalServiceAttentionBeforeSpaceJoin({
79425
- localDemand: input.localDemand ?? null,
79426
- preflight,
79427
- presenter: input.presenter,
79428
79747
  resolvedView: input.resolvedView,
79429
- view: input.view
79748
+ humanServiceGuidanceMode: shouldManageServiceParticipation || serviceRequirement === "not_needed" ? "suppress" : "show"
79430
79749
  });
79431
- if (blockingLocalServiceOutcome === "recovered") preflight = await runPreflight();
79432
- if (preflight.deviceReplyReadiness.replyReadiness === "blocked") {
79433
- if (blockingLocalServiceOutcome === "continue_without_service" || blockingLocalServiceOutcome === "recovered" && isBlockingLocalServiceJoinAttention(preflight)) return;
79434
- if (await maybeOfferDaemonStartBeforeSpaceJoin({
79435
- localDemand: input.localDemand ?? null,
79750
+ let preflight = await runPreflight();
79751
+ if (shouldManageServiceParticipation) {
79752
+ const gateResult = await runDaemonServiceParticipationGate({
79753
+ intent: input.serviceIntent ?? "space_join",
79436
79754
  presenter: input.presenter,
79755
+ resolvedView: input.resolvedView,
79756
+ allowPrompt: input.allowPrompt ?? true,
79757
+ localDemand: input.localDemand ?? null,
79437
79758
  view: input.view
79438
- })) preflight = await runPreflight();
79439
- if (preflight.deviceReplyReadiness.replyReadiness === "blocked") throw new Error(preflight.deviceReplyReadiness.reasonText);
79440
- return;
79441
- }
79442
- if (blockingLocalServiceOutcome !== "not_applicable") return;
79443
- await maybeOfferDaemonStartBeforeSpaceJoin({
79444
- localDemand: input.localDemand ?? null,
79445
- presenter: input.presenter,
79446
- view: input.view
79447
- });
79448
- }
79449
- async function maybeHandleBlockingLocalServiceAttentionBeforeSpaceJoin(input) {
79450
- if (input.resolvedView !== "human" || !isBlockingLocalServiceJoinAttention(input.preflight)) return "not_applicable";
79451
- if (input.preflight.deviceReplyReadiness.reasonCodes.includes("service.drifted")) return await maybeOfferDaemonRepairBeforeSpaceJoin({
79452
- localDemand: input.localDemand ?? null,
79453
- view: input.view
79454
- }) ? "recovered" : "continue_without_service";
79455
- return await maybeOfferDaemonStartBeforeSpaceJoin({
79456
- localDemand: input.localDemand ?? null,
79457
- presenter: input.presenter,
79458
- view: input.view
79459
- }) ? "recovered" : "continue_without_service";
79460
- }
79461
- function isBlockingLocalServiceJoinAttention(preflight) {
79462
- const { deviceReplyReadiness } = preflight;
79463
- return deviceReplyReadiness.replyReadiness === "blocked" && deviceReplyReadiness.reasonCodes.length > 0 && deviceReplyReadiness.reasonCodes.every((reasonCode) => LOCAL_SERVICE_ONLY_JOIN_BLOCK_REASON_CODES.has(reasonCode));
79464
- }
79465
- async function maybeOfferDaemonRepairBeforeSpaceJoin(input) {
79466
- if (!isHumanInteractiveJoinRuntime(await resolveRuntimeContext({ view: input.view }))) return false;
79467
- if (input.localDemand?.decision !== "keep_running") return false;
79468
- return await runDaemonBackgroundStartForDecision({ view: input.view }) === "started";
79469
- }
79470
- async function maybeOfferDaemonStartBeforeSpaceJoin(input) {
79471
- const runtime = await resolveRuntimeContext({ view: input.view });
79472
- if (runtime.resolvedView !== "human" || !canUseInteractivePrompts(runtime)) return false;
79473
- if (input.localDemand?.decision !== "keep_running") return false;
79474
- const daemonStatus = await getDaemonStatus().catch(() => NOT_INSTALLED_DAEMON_STATUS);
79475
- if (!daemonStatus.installed) return false;
79476
- const runtimeState = await resolveDaemonRuntimeState(daemonStatus);
79477
- if (!runtimeState) return false;
79478
- const { serviceManagerStatus } = await resolveDaemonServiceManagerForRuntimeState({
79479
- runtimeState,
79480
- status: daemonStatus
79481
- });
79482
- if (!shouldOfferDaemonStartDecision(getEffectiveDaemonRuntimeStatus({
79483
- runtimeState,
79484
- serviceManagerStatus
79485
- }))) return false;
79486
- const decision = await runDaemonStartDecisionPrompt({ startService: async () => await runDaemonBackgroundStartForDecision({ view: input.view }) });
79487
- if (decision === "not_now") {
79488
- const card = buildDaemonStartDeferredCard();
79489
- renderInfoCard({
79759
+ });
79760
+ if (gateResult.status === "cancelled") return "cancelled";
79761
+ preflight = await emitSpaceCommandPreflight({
79490
79762
  presenter: input.presenter,
79491
- title: card.title,
79492
- codePrefix: "space.join.service.deferred",
79493
- rows: card.rows
79763
+ command: "join",
79764
+ profile: input.atsProfile,
79765
+ baseUrl: input.baseUrl,
79766
+ resolvedView: input.resolvedView,
79767
+ humanServiceGuidanceMode: "suppress",
79768
+ emitOutput: false
79769
+ });
79770
+ if (gateResult.status === "declined") input.presenter.line({
79771
+ code: "space.join.service.declined",
79772
+ text: "ATS will continue joining this space without wakeable local agent participation on this device. To enable background replies later, rerun `ats start` or use `ats service install` and `ats service run`."
79494
79773
  });
79495
- return false;
79496
79774
  }
79497
- if (decision === "cancelled" || decision === "deferred_with_card") return false;
79498
- if (decision === "failed") {
79499
- const card = buildDaemonStartNeedsAttentionCard({ summary: "ATS could not finish starting ATS Service." });
79500
- renderInfoCard({
79501
- presenter: input.presenter,
79502
- title: card.title,
79503
- codePrefix: "space.join.service.failed",
79504
- rows: card.rows
79775
+ if (preflight.deviceReplyReadiness.replyReadiness === "blocked" && (shouldManageServiceParticipation || serviceRequirement === "not_needed") && isBlockingLocalServiceJoinAttention(preflight)) {
79776
+ if (serviceRequirement === "not_needed") input.presenter.line({
79777
+ code: "space.join.service.not_needed",
79778
+ text: "ATS will keep joining this space without wakeable local agent participation on this device. If you decide to bring local agents later, rerun `ats start` or use `ats service install` and `ats service run`."
79505
79779
  });
79506
- return false;
79780
+ return "continue";
79507
79781
  }
79508
- return decision === "started";
79782
+ if (preflight.deviceReplyReadiness.replyReadiness === "blocked") throw new Error(preflight.deviceReplyReadiness.reasonText);
79783
+ return "continue";
79784
+ }
79785
+ function isBlockingLocalServiceJoinAttention(preflight) {
79786
+ const { deviceReplyReadiness } = preflight;
79787
+ return deviceReplyReadiness.replyReadiness === "blocked" && deviceReplyReadiness.reasonCodes.length > 0 && deviceReplyReadiness.reasonCodes.every((reasonCode) => LOCAL_SERVICE_ONLY_JOIN_BLOCK_REASON_CODES.has(reasonCode));
79509
79788
  }
79510
79789
  function buildJoinTargetMissingMessage(input) {
79511
79790
  return formatMessage(input.resolvedView, "space.target.missing", { command: "join" });
@@ -79857,7 +80136,8 @@ function normalizeSpaceJoinMembershipCandidate(value) {
79857
80136
  ownerProfileName: normalizeOptionalString$2(candidate.ownerProfileName),
79858
80137
  ownerUserId: normalizeOptionalString$2(candidate.ownerUserId),
79859
80138
  ownerName: normalizeOptionalString$2(candidate.ownerName),
79860
- controllerRef: normalizeOptionalString$2(candidate.controllerRef)
80139
+ controllerRef: normalizeOptionalString$2(candidate.controllerRef),
80140
+ controllerEnabled: candidate.controllerEnabled === true
79861
80141
  };
79862
80142
  }
79863
80143
  function resolveSpaceJoinMembershipProfileKind(value) {
@@ -79875,6 +80155,7 @@ function buildLocallyCreatedPendingSpaceJoinMembershipCandidate(profile) {
79875
80155
  ownerUserId: profile.ownerUserId,
79876
80156
  ownerName: typeof profile.ownerName === "string" ? profile.ownerName : null,
79877
80157
  controllerRef: profile.controllerBinding?.controllerRef ?? null,
80158
+ controllerEnabled: profile.controllerBinding?.controllerEnabled === true,
79878
80159
  alreadyJoined: false,
79879
80160
  needsActivation: false
79880
80161
  };
@@ -80086,6 +80367,12 @@ async function applySelectedSpaceAddMembersBeforeJoin(input, dependencies) {
80086
80367
  });
80087
80368
  }
80088
80369
  if (confirmedCandidates.candidates.length === 0) return "completed";
80370
+ if (await dependencies.prepareSpaceMemberCandidatesLocalParticipation({
80371
+ actorProfile: input.actorProfile,
80372
+ candidates: confirmedCandidates.candidates,
80373
+ presenter: input.presenter,
80374
+ resolvedView: input.resolvedView
80375
+ }) === "cancelled") return input.cancelResult;
80089
80376
  const result = await dependencies.applySpaceAddMembers({
80090
80377
  actorProfile: input.actorProfile,
80091
80378
  candidates: confirmedCandidates.candidates,
@@ -80614,6 +80901,7 @@ function buildPostCreateJoinRequest(input) {
80614
80901
  ...typeof input.postCreateJoin?.mentionAlias === "string" ? { mentionAlias: input.postCreateJoin.mentionAlias } : {},
80615
80902
  ...typeof input.postCreateJoin?.rawStream === "boolean" ? { rawStream: input.postCreateJoin.rawStream } : {},
80616
80903
  ...typeof input.postCreateJoin?.skipBringProfilesPrompt === "boolean" ? { skipBringProfilesPrompt: input.postCreateJoin.skipBringProfilesPrompt } : {},
80904
+ serviceIntent: input.postCreateJoin?.serviceIntent ?? "space_create_join",
80617
80905
  entryChecksHandled: true
80618
80906
  };
80619
80907
  }
@@ -80643,7 +80931,8 @@ async function runSpaceCreate(input) {
80643
80931
  command: "create",
80644
80932
  profile: atsProfile,
80645
80933
  baseUrl,
80646
- resolvedView: contextRuntime.resolvedView
80934
+ resolvedView: contextRuntime.resolvedView,
80935
+ humanServiceGuidanceMode: isHumanInteractiveSpaceRuntime(contextRuntime) ? "suppress" : "show"
80647
80936
  });
80648
80937
  await ensureSpaceCreateAuthReady({ baseUrl });
80649
80938
  const createInput = await resolveSpaceCreateInput({
@@ -81743,6 +82032,17 @@ async function runSpaceAddMembersForTarget(input) {
81743
82032
  selectedProfileIds
81744
82033
  });
81745
82034
  if (selectedCandidates.length === 0) return { status: "completed" };
82035
+ if (await ensureSpaceMemberCandidatesLocalParticipationReady({
82036
+ atsProfile: input.atsProfile,
82037
+ candidates: selectedCandidates,
82038
+ presenter: input.presenter,
82039
+ resolvedView: input.runtime.resolvedView,
82040
+ allowPrompt: input.allowPrompt,
82041
+ view: input.view
82042
+ }) === "cancelled") return input.allowBackToCaller ? {
82043
+ status: "repeat_target",
82044
+ authenticatedGatewayUrl
82045
+ } : { status: "cancelled" };
81746
82046
  const result = await applySpaceAddMembers({
81747
82047
  actorProfile: input.atsProfile,
81748
82048
  candidates: selectedCandidates,
@@ -81849,6 +82149,46 @@ async function runSpaceRemoveMembersForTarget(input) {
81849
82149
  function resolveSelectedSpaceMemberCandidates(input) {
81850
82150
  return input.selectedProfileIds.map((profileId) => input.candidates.find((candidate) => candidate.profileId === profileId)).filter((candidate) => Boolean(candidate));
81851
82151
  }
82152
+ async function ensureSpaceMemberCandidatesLocalParticipationReady(input) {
82153
+ const explicitSelectedLocalAgents = hasAnyExplicitLocalAgentCandidates(input.candidates);
82154
+ const serviceRequirement = resolveCliServiceParticipationRequirement({
82155
+ intent: "space_add_members",
82156
+ humanOnlyParticipation: !explicitSelectedLocalAgents,
82157
+ explicitSelectedLocalAgents,
82158
+ selectedAgentProfilesHaveRunnableLocalSetup: haveAllExplicitLocalAgentCandidatesRunnable(input.candidates),
82159
+ localDemand: input.resolvedView === "human" ? await resolveDaemonLocalServiceDemand({ ownerUserId: input.atsProfile.ownerUserId }).catch(() => null) : null
82160
+ });
82161
+ if (serviceRequirement === "not_needed") return "continue";
82162
+ if (serviceRequirement === "agent_setup_needed") {
82163
+ input.presenter.line({
82164
+ code: "space.add_members.agent_setup_needed",
82165
+ text: "At least one selected agent profile is not set up on this device yet. Finish agent setup first, then retry. Use `ats start` or `ats profile ready --profile <agent-profile-id>`."
82166
+ });
82167
+ return "cancelled";
82168
+ }
82169
+ if (!(input.resolvedView === "human" && input.allowPrompt)) {
82170
+ input.presenter.line({
82171
+ code: "space.add_members.service.deferred",
82172
+ text: "ATS will continue without wakeable local agent participation on this device. To enable background replies later, rerun `ats start` or use `ats service install` and `ats service run`."
82173
+ });
82174
+ return "continue";
82175
+ }
82176
+ const gateResult = await runDaemonServiceParticipationGate({
82177
+ intent: "space_add_members",
82178
+ presenter: input.presenter,
82179
+ resolvedView: input.resolvedView,
82180
+ allowPrompt: input.allowPrompt,
82181
+ localDemand: await resolveDaemonLocalServiceDemand({ ownerUserId: input.atsProfile.ownerUserId }).catch(() => null),
82182
+ forcePrompt: explicitSelectedLocalAgents,
82183
+ view: input.view
82184
+ });
82185
+ if (gateResult.status === "cancelled") return "cancelled";
82186
+ if (gateResult.status !== "aligned") input.presenter.line({
82187
+ code: "space.add_members.service.declined",
82188
+ text: "ATS will continue adding these profiles without wakeable local agent participation on this device. To enable background replies later, rerun `ats start` or use `ats service install` and `ats service run`."
82189
+ });
82190
+ return "continue";
82191
+ }
81852
82192
  async function runSpaceJoin(input) {
81853
82193
  let failureRuntime = null;
81854
82194
  let failurePresenter = null;
@@ -81889,7 +82229,8 @@ async function runSpaceJoin(input) {
81889
82229
  role: input.role,
81890
82230
  roleInstructions: input.roleInstructions,
81891
82231
  mentionAlias: input.mentionAlias,
81892
- rawStream: input.rawStream === true
82232
+ rawStream: input.rawStream === true,
82233
+ serviceIntent: input.serviceIntent ?? "space_join"
81893
82234
  });
81894
82235
  } catch (error) {
81895
82236
  const runtime = failureRuntime ?? await resolveRuntimeContext({
@@ -81936,7 +82277,8 @@ async function runSpaceJoinTargetLoop(input) {
81936
82277
  rawStream: input.rawStream,
81937
82278
  skipBringProfilesPrompt: input.skipBringProfilesPrompt,
81938
82279
  output: input.output,
81939
- view: input.view
82280
+ view: input.view,
82281
+ serviceIntent: "space_create_join"
81940
82282
  }
81941
82283
  });
81942
82284
  }
@@ -81948,14 +82290,16 @@ async function runSpaceJoinTargetLoop(input) {
81948
82290
  space: target.space
81949
82291
  }), "reading an existing local space config before join");
81950
82292
  const targetBaseUrl = input.hasExplicitBaseUrlInput ? input.baseUrl : target.baseUrl ?? input.baseUrl;
81951
- await assertSpaceJoinTargetPreflightReady({
82293
+ if (await assertSpaceJoinTargetPreflightReady({
81952
82294
  atsProfile: input.atsProfile,
81953
82295
  baseUrl: targetBaseUrl,
82296
+ allowPrompt: input.canPromptPreJoinActions,
81954
82297
  localDemand: input.localDemand,
81955
82298
  presenter: input.presenter,
81956
82299
  resolvedView: input.runtime.resolvedView,
82300
+ serviceIntent: input.serviceIntent,
81957
82301
  view: input.view
81958
- });
82302
+ }) === "cancelled") return "cancelled";
81959
82303
  authenticatedGatewayUrl = await ensureSpaceCommandGatewayAuthenticationOrCancel({
81960
82304
  runtime: input.runtime,
81961
82305
  gatewayUrl: targetBaseUrl,
@@ -81997,6 +82341,14 @@ async function runSpaceJoinTargetLoop(input) {
81997
82341
  resolveCreateProfileName,
81998
82342
  resolveAgentProfileSetup,
81999
82343
  persistAgentTransportSelection,
82344
+ prepareSpaceMemberCandidatesLocalParticipation: async ({ actorProfile, candidates, presenter, resolvedView }) => await ensureSpaceMemberCandidatesLocalParticipationReady({
82345
+ atsProfile: actorProfile,
82346
+ candidates,
82347
+ presenter,
82348
+ resolvedView,
82349
+ allowPrompt: input.canPromptPreJoinActions,
82350
+ view: input.view
82351
+ }),
82000
82352
  applySpaceAddMembers,
82001
82353
  persistSpaceAddMembersJoinNotices,
82002
82354
  emitSpaceAddMembersResult
@@ -83027,6 +83379,7 @@ async function resolveSpaceMemberCandidatesForAdd(input) {
83027
83379
  ownerUserId: profile.ownerUserId,
83028
83380
  ownerName: profile.ownerName,
83029
83381
  controllerRef: profile.controllerBinding?.controllerRef,
83382
+ controllerEnabled: profile.controllerBinding?.controllerEnabled,
83030
83383
  alreadyJoined: memberProfileIds.has(profile.atsProfileId),
83031
83384
  needsActivation: false
83032
83385
  };
@@ -87026,11 +87379,13 @@ async function runStart(input) {
87026
87379
  const presenter = createPresenter(runtime);
87027
87380
  const interactive = canUseInteractivePrompts(runtime);
87028
87381
  if (interactive && runtime.resolvedView === "human") intro(`\n${renderAtsHeader()}`);
87029
- if (!await runStartDisclaimerStep({
87030
- runtime,
87031
- presenter,
87032
- interactive
87033
- })) return;
87382
+ if (!input.entryChecksHandled) {
87383
+ if (!await runStartDisclaimerStep({
87384
+ runtime,
87385
+ presenter,
87386
+ interactive
87387
+ })) return;
87388
+ }
87034
87389
  if (input.startSession) {
87035
87390
  await runStartHandoff({
87036
87391
  ...input,
@@ -91667,7 +92022,8 @@ async function runStartFromCliOptions(opts) {
91667
92022
  baseUrl: opts.gatewayUrl,
91668
92023
  startSession: opts.startSession,
91669
92024
  skillsMode: opts.skillsMode,
91670
- view: opts.view
92025
+ view: opts.view,
92026
+ entryChecksHandled: true
91671
92027
  });
91672
92028
  }
91673
92029
  function withSpaceProfileOption(cmd) {