@phnx-labs/agents-cli 1.22.117 → 1.22.118

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.
Files changed (248) hide show
  1. package/CHANGELOG.md +235 -0
  2. package/dist/bootstrap.js +4 -11
  3. package/dist/commands/browser.d.ts +52 -70
  4. package/dist/commands/browser.js +192 -3562
  5. package/dist/commands/daemon.d.ts +39 -2
  6. package/dist/commands/daemon.js +72 -73
  7. package/dist/commands/events.js +1 -1
  8. package/dist/commands/exec.js +53 -1
  9. package/dist/commands/repo.js +13 -49
  10. package/dist/commands/route.js +5 -5
  11. package/dist/commands/routines.js +39 -12
  12. package/dist/commands/sessions-render.d.ts +1 -1
  13. package/dist/commands/sessions-render.js +0 -1
  14. package/dist/commands/sessions.test-fixture.d.ts +0 -1
  15. package/dist/commands/sessions.test-fixture.js +0 -37
  16. package/dist/commands/setup-browser.d.ts +17 -12
  17. package/dist/commands/setup-browser.js +52 -87
  18. package/dist/commands/setup-preferences.d.ts +0 -17
  19. package/dist/commands/setup-preferences.js +18 -49
  20. package/dist/commands/setup.js +9 -9
  21. package/dist/commands/ssh.js +10 -1
  22. package/dist/commands/sync.js +6 -44
  23. package/dist/commands/teams.js +0 -1
  24. package/dist/commands/update.d.ts +19 -0
  25. package/dist/commands/update.js +34 -6
  26. package/dist/commands/view.d.ts +12 -1
  27. package/dist/commands/view.js +47 -2
  28. package/dist/index.js +107 -26
  29. package/dist/lib/account-capabilities.js +0 -2
  30. package/dist/lib/account-catalog.d.ts +38 -0
  31. package/dist/lib/account-catalog.js +110 -26
  32. package/dist/lib/account-provider-registry.js +1 -1
  33. package/dist/lib/account-registry.d.ts +1 -1
  34. package/dist/lib/account-registry.js +36 -2
  35. package/dist/lib/accounting/rotate.js +22 -8
  36. package/dist/lib/accounting/usage-ingest.d.ts +6 -1
  37. package/dist/lib/accounting/usage-ingest.js +115 -45
  38. package/dist/lib/accounting/usage-sync.d.ts +134 -31
  39. package/dist/lib/accounting/usage-sync.js +227 -66
  40. package/dist/lib/accounting/usage.d.ts +16 -3
  41. package/dist/lib/accounting/usage.js +26 -8
  42. package/dist/lib/accounts/slots.d.ts +8 -0
  43. package/dist/lib/accounts/slots.js +17 -0
  44. package/dist/lib/add-dir.js +0 -1
  45. package/dist/lib/agent-cli-commands.js +0 -1
  46. package/dist/lib/agent-spec/agents.d.ts +6 -7
  47. package/dist/lib/agent-spec/agents.js +6 -60
  48. package/dist/lib/auth-health.d.ts +69 -1
  49. package/dist/lib/auth-health.js +133 -4
  50. package/dist/lib/browser/context.d.ts +82 -0
  51. package/dist/lib/browser/context.js +88 -0
  52. package/dist/lib/browser/paths.d.ts +25 -0
  53. package/dist/lib/browser/paths.js +51 -0
  54. package/dist/lib/browser/record.d.ts +38 -0
  55. package/dist/lib/browser/record.js +57 -0
  56. package/dist/lib/browser/sessions-list.js +1 -2
  57. package/dist/lib/browser-client.d.ts +158 -0
  58. package/dist/lib/browser-client.js +234 -0
  59. package/dist/lib/channels/owner-sink.d.ts +1 -18
  60. package/dist/lib/channels/owner-sink.js +29 -82
  61. package/dist/lib/channels/providers/rush.d.ts +7 -7
  62. package/dist/lib/channels/providers/rush.js +127 -57
  63. package/dist/lib/claude-statusline.d.ts +1 -1
  64. package/dist/lib/claude-statusline.js +15 -3
  65. package/dist/lib/commands.js +1 -1
  66. package/dist/lib/crabbox/lease.js +1 -1
  67. package/dist/lib/crabbox/runtimes.js +1 -3
  68. package/dist/lib/daemon/account-state-daemon-service.js +1 -0
  69. package/dist/lib/daemon/auth-sync-service.d.ts +21 -18
  70. package/dist/lib/daemon/auth-sync-service.js +37 -43
  71. package/dist/lib/daemon/daemon.d.ts +25 -14
  72. package/dist/lib/daemon/daemon.js +92 -154
  73. package/dist/lib/daemon/runner.d.ts +2 -2
  74. package/dist/lib/daemon/runner.js +32 -9
  75. package/dist/lib/daemon/self-update-service.d.ts +35 -34
  76. package/dist/lib/daemon/self-update-service.js +40 -36
  77. package/dist/lib/daemon/service.d.ts +12 -8
  78. package/dist/lib/daemon/supervisor.d.ts +65 -54
  79. package/dist/lib/daemon/supervisor.js +136 -171
  80. package/dist/lib/daemon/testdata/process-view-start.js +5 -2
  81. package/dist/lib/daemon/usage-sync-service.d.ts +21 -30
  82. package/dist/lib/daemon/usage-sync-service.js +46 -78
  83. package/dist/lib/daemon-health.d.ts +22 -5
  84. package/dist/lib/daemon-health.js +47 -3
  85. package/dist/lib/daemon-services.d.ts +1 -1
  86. package/dist/lib/daemon-services.js +0 -10
  87. package/dist/lib/daemon-ticks.js +4 -2
  88. package/dist/lib/devices/doctor-findings.js +8 -11
  89. package/dist/lib/devices/harness-inventory.js +14 -5
  90. package/dist/lib/exec-account-home.js +5 -0
  91. package/dist/lib/exec.js +0 -11
  92. package/dist/lib/feed/events.js +0 -1
  93. package/dist/lib/feed/tool-activity.js +5 -2
  94. package/dist/lib/fleet-shared-state.d.ts +45 -0
  95. package/dist/lib/fleet-shared-state.js +78 -9
  96. package/dist/lib/fs-atomic.js +25 -0
  97. package/dist/lib/harness-auth-capabilities.js +0 -1
  98. package/dist/lib/helper-versions.js +1 -1
  99. package/dist/lib/hooks/cache.d.ts +7 -0
  100. package/dist/lib/hooks/cache.js +27 -2
  101. package/dist/lib/hooks/install.js +7 -1
  102. package/dist/lib/hosts/ready.d.ts +38 -8
  103. package/dist/lib/hosts/ready.js +62 -14
  104. package/dist/lib/installations/active-check.d.ts +27 -0
  105. package/dist/lib/installations/active-check.js +61 -0
  106. package/dist/lib/installations/migrate.js +6 -3
  107. package/dist/lib/installations/shims.js +9 -7
  108. package/dist/lib/installations/update.js +21 -15
  109. package/dist/lib/installations/versions.js +12 -1
  110. package/dist/lib/manifest.js +1 -1
  111. package/dist/lib/models.js +0 -2
  112. package/dist/lib/open-url.js +28 -54
  113. package/dist/lib/profiles.js +0 -2
  114. package/dist/lib/refresh.js +1 -1
  115. package/dist/lib/routine-readiness.d.ts +35 -0
  116. package/dist/lib/routine-readiness.js +74 -16
  117. package/dist/lib/rules/compile.js +6 -0
  118. package/dist/lib/sandbox.js +0 -2
  119. package/dist/lib/secrets-policy.d.ts +89 -11
  120. package/dist/lib/secrets-policy.js +175 -28
  121. package/dist/lib/session/db.d.ts +1 -1
  122. package/dist/lib/session/db.js +65 -18
  123. package/dist/lib/session/discover.js +1 -283
  124. package/dist/lib/session/mirror.d.ts +4 -3
  125. package/dist/lib/session/mirror.js +4 -3
  126. package/dist/lib/session/parse.d.ts +0 -2
  127. package/dist/lib/session/parse.js +1 -138
  128. package/dist/lib/session/throughput.d.ts +1 -3
  129. package/dist/lib/session/throughput.js +0 -20
  130. package/dist/lib/session/types.d.ts +2 -2
  131. package/dist/lib/session/types.js +2 -2
  132. package/dist/lib/sessions-client.d.ts +60 -1
  133. package/dist/lib/sessions-client.js +227 -4
  134. package/dist/lib/setup-tool-status.js +5 -10
  135. package/dist/lib/signin-badge.d.ts +3 -2
  136. package/dist/lib/signin-badge.js +6 -3
  137. package/dist/lib/smart-launch.js +4 -1
  138. package/dist/lib/staleness/detectors/commands.d.ts +1 -1
  139. package/dist/lib/staleness/detectors/hooks.d.ts +1 -1
  140. package/dist/lib/staleness/detectors/mcp.d.ts +1 -1
  141. package/dist/lib/staleness/detectors/permissions.d.ts +1 -1
  142. package/dist/lib/staleness/detectors/plugins.d.ts +1 -1
  143. package/dist/lib/staleness/detectors/rules.d.ts +1 -1
  144. package/dist/lib/staleness/detectors/skills.d.ts +1 -1
  145. package/dist/lib/staleness/detectors/subagents.d.ts +1 -1
  146. package/dist/lib/staleness/detectors/workflows.d.ts +1 -1
  147. package/dist/lib/staleness/writers/commands.d.ts +1 -1
  148. package/dist/lib/staleness/writers/hooks.d.ts +1 -1
  149. package/dist/lib/staleness/writers/mcp.d.ts +1 -1
  150. package/dist/lib/staleness/writers/permissions.d.ts +1 -1
  151. package/dist/lib/staleness/writers/plugins.d.ts +1 -1
  152. package/dist/lib/staleness/writers/rules.d.ts +1 -1
  153. package/dist/lib/staleness/writers/skills.d.ts +1 -1
  154. package/dist/lib/staleness/writers/sources.js +5 -0
  155. package/dist/lib/staleness/writers/subagents.d.ts +1 -1
  156. package/dist/lib/staleness/writers/workflows.d.ts +1 -1
  157. package/dist/lib/state.d.ts +6 -4
  158. package/dist/lib/state.js +7 -17
  159. package/dist/lib/teams/parsers.d.ts +1 -1
  160. package/dist/lib/teams/parsers.js +1 -136
  161. package/dist/lib/teams/placement-probe.js +50 -15
  162. package/dist/lib/teams/scheduler.d.ts +7 -0
  163. package/dist/lib/types.d.ts +10 -1
  164. package/dist/lib/types.js +1 -1
  165. package/dist/lib/view-types.d.ts +32 -0
  166. package/dist/lib/watchdog/read.js +0 -1
  167. package/dist/lib/watchdog/rotate.js +1 -2
  168. package/dist/lib/watchdog/runner.js +2 -2
  169. package/dist/lib/watchdog/watchdog.d.ts +1 -1
  170. package/dist/lib/watchdog/watchdogTail.js +0 -31
  171. package/package.json +1 -1
  172. package/scripts/postinstall.js +8 -8
  173. package/dist/browser.d.ts +0 -2
  174. package/dist/browser.js +0 -17
  175. package/dist/commands/browser-picker.d.ts +0 -18
  176. package/dist/commands/browser-picker.js +0 -97
  177. package/dist/lib/browser/arc-discovery.d.ts +0 -52
  178. package/dist/lib/browser/arc-discovery.js +0 -197
  179. package/dist/lib/browser/arc-dom.d.ts +0 -14
  180. package/dist/lib/browser/arc-dom.js +0 -121
  181. package/dist/lib/browser/caller-identity.d.ts +0 -40
  182. package/dist/lib/browser/caller-identity.js +0 -175
  183. package/dist/lib/browser/cdp.d.ts +0 -51
  184. package/dist/lib/browser/cdp.js +0 -254
  185. package/dist/lib/browser/chrome.d.ts +0 -141
  186. package/dist/lib/browser/chrome.js +0 -704
  187. package/dist/lib/browser/chromium-discovery.d.ts +0 -28
  188. package/dist/lib/browser/chromium-discovery.js +0 -96
  189. package/dist/lib/browser/devices.d.ts +0 -34
  190. package/dist/lib/browser/devices.js +0 -61
  191. package/dist/lib/browser/domain-skills.d.ts +0 -71
  192. package/dist/lib/browser/domain-skills.js +0 -195
  193. package/dist/lib/browser/drivers/arc.d.ts +0 -40
  194. package/dist/lib/browser/drivers/arc.js +0 -276
  195. package/dist/lib/browser/drivers/firefox.d.ts +0 -99
  196. package/dist/lib/browser/drivers/firefox.js +0 -377
  197. package/dist/lib/browser/drivers/local.d.ts +0 -55
  198. package/dist/lib/browser/drivers/local.js +0 -266
  199. package/dist/lib/browser/drivers/ssh.d.ts +0 -120
  200. package/dist/lib/browser/drivers/ssh.js +0 -467
  201. package/dist/lib/browser/editor.d.ts +0 -3
  202. package/dist/lib/browser/editor.js +0 -50
  203. package/dist/lib/browser/ffmpeg.d.ts +0 -12
  204. package/dist/lib/browser/ffmpeg.js +0 -184
  205. package/dist/lib/browser/firefox-discovery.d.ts +0 -69
  206. package/dist/lib/browser/firefox-discovery.js +0 -162
  207. package/dist/lib/browser/har.d.ts +0 -85
  208. package/dist/lib/browser/har.js +0 -77
  209. package/dist/lib/browser/hygiene.d.ts +0 -97
  210. package/dist/lib/browser/hygiene.js +0 -153
  211. package/dist/lib/browser/index.d.ts +0 -5
  212. package/dist/lib/browser/index.js +0 -5
  213. package/dist/lib/browser/input.d.ts +0 -7
  214. package/dist/lib/browser/input.js +0 -92
  215. package/dist/lib/browser/ipc.d.ts +0 -204
  216. package/dist/lib/browser/ipc.js +0 -1453
  217. package/dist/lib/browser/login-detection.d.ts +0 -87
  218. package/dist/lib/browser/login-detection.js +0 -274
  219. package/dist/lib/browser/profiles.d.ts +0 -422
  220. package/dist/lib/browser/profiles.js +0 -1157
  221. package/dist/lib/browser/refs.d.ts +0 -89
  222. package/dist/lib/browser/refs.js +0 -191
  223. package/dist/lib/browser/registry.d.ts +0 -70
  224. package/dist/lib/browser/registry.js +0 -252
  225. package/dist/lib/browser/remote-control.d.ts +0 -59
  226. package/dist/lib/browser/remote-control.js +0 -85
  227. package/dist/lib/browser/resolve-target.d.ts +0 -66
  228. package/dist/lib/browser/resolve-target.js +0 -264
  229. package/dist/lib/browser/runtime-state.d.ts +0 -291
  230. package/dist/lib/browser/runtime-state.js +0 -584
  231. package/dist/lib/browser/secret-ref.d.ts +0 -10
  232. package/dist/lib/browser/secret-ref.js +0 -14
  233. package/dist/lib/browser/service.d.ts +0 -812
  234. package/dist/lib/browser/service.js +0 -4693
  235. package/dist/lib/browser/stream.d.ts +0 -17
  236. package/dist/lib/browser/stream.js +0 -72
  237. package/dist/lib/browser/task-index.d.ts +0 -78
  238. package/dist/lib/browser/task-index.js +0 -199
  239. package/dist/lib/browser/types.d.ts +0 -647
  240. package/dist/lib/browser/types.js +0 -61
  241. package/dist/lib/browser/upload.d.ts +0 -29
  242. package/dist/lib/browser/upload.js +0 -298
  243. package/dist/lib/daemon/browser-ipc-service.d.ts +0 -20
  244. package/dist/lib/daemon/browser-ipc-service.js +0 -46
  245. package/dist/lib/daemon/browser-task-reap-service.d.ts +0 -14
  246. package/dist/lib/daemon/browser-task-reap-service.js +0 -26
  247. package/dist/lib/fleet-shared-repo-sync.d.ts +0 -55
  248. package/dist/lib/fleet-shared-repo-sync.js +0 -490
@@ -7,7 +7,66 @@ export declare function invocation(bin: string): {
7
7
  command: string;
8
8
  prefix: string[];
9
9
  };
10
- export declare function isReadQuery(args: string[]): boolean;
10
+ /** True if any 0.2.0-only filter flag is present — one case that needs the version probe. */
11
+ export declare function usesFilterFlags(args: string[]): boolean;
12
+ /**
13
+ * True if the 0.2.1 point-to-one remote read flag `--host <value>` (or `--host=value`)
14
+ * is present — the other case that needs the version probe, gated on its own floor.
15
+ */
16
+ export declare function usesHostFlag(args: string[]): boolean;
17
+ /**
18
+ * Whether the resolved `sessions` binary is at or above `minVersion`. A probe
19
+ * failure or an unparseable version reads as unsupported — the safe direction
20
+ * (in-repo engine handles it), never a mis-route to an old binary.
21
+ */
22
+ export declare function sessionsBinSupports(bin: string, minVersion: string): boolean;
23
+ /** Whether the resolved `sessions` binary is new enough for the 0.2.0 filters. */
24
+ export declare function sessionsBinSupportsFilters(bin: string): boolean;
25
+ /** Whether the resolved `sessions` binary is new enough for the 0.2.1 `--host` flag. */
26
+ export declare function sessionsBinSupportsHost(bin: string): boolean;
27
+ export declare function isReadQuery(args: string[], opts?: {
28
+ filters?: boolean;
29
+ host?: boolean;
30
+ }): boolean;
31
+ /**
32
+ * Plan the rewrite of a `sessions` READ query that names EXACTLY ONE device via
33
+ * `--device <name>` (and no explicit `--host`) into the standalone's
34
+ * point-to-one remote read: the caller resolves the device to an ssh target and
35
+ * appends `--host ssh://<target>` to `readArgs`, forwarding it to the LOCAL
36
+ * standalone (which owns the ssh hop). This is the local-orchestration collapse
37
+ * (secrets-cli model): `sessions` owns the remote read, replacing the in-repo
38
+ * `--device` peer fan-out — WHEN it is safe (the caller gates on the LOCAL
39
+ * standalone supporting `--host`, >=0.2.1, and falls through to the in-repo
40
+ * fan-out if the PEER lacks the standalone).
41
+ *
42
+ * `sessions --host` is point-to-one, so ONLY a single unambiguous device may
43
+ * collapse; a multi-device query MUST stay on the in-repo fan-out, which parses
44
+ * commander's variadic `--device` correctly. Returns null (→ in-repo path,
45
+ * unchanged) when the query is not a single-device read:
46
+ * - no `--device`/`-D`, or a missing/flag-shaped value;
47
+ * - `--device`/`-D` appears more than once (`--device box --device mac-mini`);
48
+ * - the value is a fan-out sentinel `all`/`fleet` (case-insensitive);
49
+ * - a bare token immediately follows the value (`--device box mac-mini`) —
50
+ * commander's variadic would take it as a second device, so this is
51
+ * multi-device too;
52
+ * - an explicit `--host` is already on the argv — it wins, matching `secrets`'
53
+ * `rewriteDeviceToHost`, so we never add a second `--host`;
54
+ * - the remaining query (with `--device` stripped) is not a read — a lifecycle
55
+ * `--device` (resume/watch/inject/focus/…) keeps its in-repo/`runOnPeer`
56
+ * behavior exactly.
57
+ *
58
+ * Pure and argv-only: fleet resolution (device → ssh target, via
59
+ * `resolveRemoteDevice`) and the version gate live in the caller so this is unit
60
+ * testable with real inputs. `filters` mirrors the caller's version gate for the
61
+ * 0.2.0 filter flags (the `--device` token is not a filter flag, so it is
62
+ * computed identically on the original argv and on `readArgs`).
63
+ */
64
+ export declare function planDeviceHostRead(args: string[], opts?: {
65
+ filters?: boolean;
66
+ }): {
67
+ device: string;
68
+ readArgs: string[];
69
+ } | null;
11
70
  /** Test seam: drop the memoized bin so PATH fixtures can re-resolve. */
12
71
  export declare function _resetSessionsClientForTest(): void;
13
72
  export declare const SESSIONS_INSTALL_HINT = "npm i -g @phnx-labs/sessions-cli";
@@ -12,8 +12,29 @@
12
12
  * `agents sessions`, and resolving it would recurse (the 1.22.85 secrets
13
13
  * fork bomb with the names swapped — agi-cli#3532).
14
14
  */
15
+ import { spawnSync } from 'node:child_process';
15
16
  import { findInPath } from './agent-spec/agents.js';
17
+ import { compareVersions } from './agent-spec/primitives.js';
18
+ import { stripRoutingFlags } from './hosts/remote-cmd.js';
16
19
  const INSTALL_HINT = 'npm i -g @phnx-labs/sessions-cli';
20
+ /**
21
+ * First `sessions` release that implements the metadata filters and sort
22
+ * (`-p/--project`, `--since`, `--until`, `--sort`, the `@version` agent suffix,
23
+ * and the harness shorthands). An older binary would treat these as FTS tokens,
24
+ * so a box with `sessions` < this floor keeps them on the in-repo engine (which
25
+ * implements the same filters) rather than mis-routing to a binary that can't.
26
+ */
27
+ const SESSIONS_FILTERS_MIN_VERSION = '0.2.0';
28
+ /**
29
+ * First `sessions` release that implements the point-to-one remote read flag
30
+ * `--host <target>` (SSH to ONE box, run `sessions … --local`, stream JSON back).
31
+ * An older binary would treat `--host` as an FTS token, so a box with `sessions`
32
+ * below this floor keeps a `--host` query on the in-repo engine (which resolves
33
+ * `--device` against the fleet) rather than mis-routing it. Kept separate from the
34
+ * filter floor above even though 0.2.1 ≥ 0.2.0 — a 0.2.0 binary supports the
35
+ * filters but NOT `--host`, so the two gates are checked independently.
36
+ */
37
+ const SESSIONS_HOST_MIN_VERSION = '0.2.1';
17
38
  export class SessionsClientError extends Error {
18
39
  code;
19
40
  constructor(code, message) {
@@ -52,6 +73,83 @@ const READ_FLAGS = new Set([
52
73
  '--local',
53
74
  '--no-interactive',
54
75
  ]);
76
+ /**
77
+ * Boolean harness-shorthand flags added in 0.2.0 (`--claude` = `--agent claude`).
78
+ * Recognized as read flags only when the resolved binary supports filters.
79
+ */
80
+ const FILTER_BOOL_FLAGS = new Set([
81
+ '--claude',
82
+ '--codex',
83
+ '--kimi',
84
+ '--antigravity',
85
+ '--grok',
86
+ '--opencode',
87
+ ]);
88
+ /**
89
+ * Value-taking filter flags added in 0.2.0 (each consumes the next argv token,
90
+ * or is written `--flag=value`). `--agent`/`--limit` are NOT here — they are the
91
+ * 0.1.x base set handled unconditionally below.
92
+ */
93
+ const FILTER_VALUE_FLAGS = new Set([
94
+ '--project',
95
+ '--since',
96
+ '--until',
97
+ '--sort',
98
+ ]);
99
+ /** True if any 0.2.0-only filter flag is present — one case that needs the version probe. */
100
+ export function usesFilterFlags(args) {
101
+ return args.some((arg) => {
102
+ if (arg === '-p' || arg === '-a')
103
+ return true;
104
+ if (FILTER_BOOL_FLAGS.has(arg))
105
+ return true;
106
+ for (const vf of FILTER_VALUE_FLAGS) {
107
+ if (arg === vf || arg.startsWith(`${vf}=`))
108
+ return true;
109
+ }
110
+ return false;
111
+ });
112
+ }
113
+ /**
114
+ * True if the 0.2.1 point-to-one remote read flag `--host <value>` (or `--host=value`)
115
+ * is present — the other case that needs the version probe, gated on its own floor.
116
+ */
117
+ export function usesHostFlag(args) {
118
+ return args.some((arg) => arg === '--host' || arg.startsWith('--host='));
119
+ }
120
+ let cachedProbedVersion;
121
+ /**
122
+ * Probe `sessions --version` ONCE per process (cached), returning the parsed
123
+ * version string, or `null` when the probe fails or the output is unparseable.
124
+ * The single cache is what keeps the filter and host gates to one spawn between
125
+ * them, however many `sessionsBinSupports*` calls a query makes.
126
+ */
127
+ function probeSessionsVersion(bin) {
128
+ if (cachedProbedVersion !== undefined)
129
+ return cachedProbedVersion;
130
+ const { command, prefix } = invocation(bin);
131
+ const res = spawnSync(command, [...prefix, '--version'], { encoding: 'utf8', timeout: 3000 });
132
+ const version = (res.stdout ?? '').trim();
133
+ cachedProbedVersion = !res.error && /^\d+\.\d+\.\d+/.test(version) ? version : null;
134
+ return cachedProbedVersion;
135
+ }
136
+ /**
137
+ * Whether the resolved `sessions` binary is at or above `minVersion`. A probe
138
+ * failure or an unparseable version reads as unsupported — the safe direction
139
+ * (in-repo engine handles it), never a mis-route to an old binary.
140
+ */
141
+ export function sessionsBinSupports(bin, minVersion) {
142
+ const version = probeSessionsVersion(bin);
143
+ return version !== null && compareVersions(version, minVersion) >= 0;
144
+ }
145
+ /** Whether the resolved `sessions` binary is new enough for the 0.2.0 filters. */
146
+ export function sessionsBinSupportsFilters(bin) {
147
+ return sessionsBinSupports(bin, SESSIONS_FILTERS_MIN_VERSION);
148
+ }
149
+ /** Whether the resolved `sessions` binary is new enough for the 0.2.1 `--host` flag. */
150
+ export function sessionsBinSupportsHost(bin) {
151
+ return sessionsBinSupports(bin, SESSIONS_HOST_MIN_VERSION);
152
+ }
55
153
  const ENGINE_VERBS = new Set([
56
154
  'resume',
57
155
  'detach',
@@ -76,19 +174,45 @@ const ENGINE_VERBS = new Set([
76
174
  'migrations',
77
175
  'export',
78
176
  ]);
79
- export function isReadQuery(args) {
177
+ export function isReadQuery(args, opts = {}) {
80
178
  if (args.length === 0)
81
179
  return false;
180
+ // Base (0.1.x) value flags, always recognized; the 0.2.0 filter value flags
181
+ // join them only when the binary supports filters, and the 0.2.1 `--host`
182
+ // value flag only when the binary supports it (its own, higher floor).
183
+ const valueFlags = new Set(['--limit', '--agent']);
184
+ const boolFlags = new Set(READ_FLAGS);
185
+ if (opts.filters) {
186
+ for (const vf of FILTER_VALUE_FLAGS)
187
+ valueFlags.add(vf);
188
+ for (const bf of FILTER_BOOL_FLAGS)
189
+ boolFlags.add(bf);
190
+ }
191
+ if (opts.host)
192
+ valueFlags.add('--host');
82
193
  for (let i = 0; i < args.length; i++) {
83
194
  const arg = args[i];
84
- if (arg === '--limit' || arg === '--agent') {
195
+ // `-a` (agent) and `-p` (project) are 0.2.0 short flags that take a value.
196
+ if (opts.filters && (arg === '-a' || arg === '-p')) {
85
197
  i += 1;
86
198
  continue;
87
199
  }
88
- if (arg.startsWith('--limit=') || arg.startsWith('--agent='))
200
+ let matchedValue = false;
201
+ for (const vf of valueFlags) {
202
+ if (arg === vf) {
203
+ i += 1;
204
+ matchedValue = true;
205
+ break;
206
+ }
207
+ if (arg.startsWith(`${vf}=`)) {
208
+ matchedValue = true;
209
+ break;
210
+ }
211
+ }
212
+ if (matchedValue)
89
213
  continue;
90
214
  if (arg.startsWith('-')) {
91
- if (!READ_FLAGS.has(arg))
215
+ if (!boolFlags.has(arg))
92
216
  return false;
93
217
  continue;
94
218
  }
@@ -97,8 +221,107 @@ export function isReadQuery(args) {
97
221
  }
98
222
  return true;
99
223
  }
224
+ /**
225
+ * Scan an argv for the `sessions` `--device`/`-D` flag, which commander defines
226
+ * as VARIADIC (`-D, --device <target...>`) — so it can appear more than once and
227
+ * a space-form value greedily consumes following bare tokens as extra devices.
228
+ * Returns how many times it occurs, the FIRST value, and the index of the last
229
+ * argv token that value occupies (the flag token itself for the `=`/glued forms,
230
+ * the following value token for the space form). Pure; no imports.
231
+ */
232
+ function scanDeviceFlag(args) {
233
+ let count = 0;
234
+ let value;
235
+ let valueEndIndex;
236
+ for (let i = 0; i < args.length; i++) {
237
+ const a = args[i];
238
+ let matched = false;
239
+ let v;
240
+ let endIdx = i;
241
+ if (a === '--device' || a === '-D') {
242
+ matched = true;
243
+ v = args[i + 1];
244
+ endIdx = i + 1;
245
+ }
246
+ else if (a.startsWith('--device=')) {
247
+ matched = true;
248
+ v = a.slice('--device='.length);
249
+ }
250
+ else if (a.startsWith('-D=')) {
251
+ matched = true;
252
+ v = a.slice(3);
253
+ }
254
+ else if (/^-D.+/.test(a)) {
255
+ matched = true;
256
+ v = a.slice(2);
257
+ }
258
+ if (matched) {
259
+ count += 1;
260
+ if (count === 1) {
261
+ value = v;
262
+ valueEndIndex = endIdx;
263
+ }
264
+ }
265
+ }
266
+ return { count, value, valueEndIndex };
267
+ }
268
+ /**
269
+ * Plan the rewrite of a `sessions` READ query that names EXACTLY ONE device via
270
+ * `--device <name>` (and no explicit `--host`) into the standalone's
271
+ * point-to-one remote read: the caller resolves the device to an ssh target and
272
+ * appends `--host ssh://<target>` to `readArgs`, forwarding it to the LOCAL
273
+ * standalone (which owns the ssh hop). This is the local-orchestration collapse
274
+ * (secrets-cli model): `sessions` owns the remote read, replacing the in-repo
275
+ * `--device` peer fan-out — WHEN it is safe (the caller gates on the LOCAL
276
+ * standalone supporting `--host`, >=0.2.1, and falls through to the in-repo
277
+ * fan-out if the PEER lacks the standalone).
278
+ *
279
+ * `sessions --host` is point-to-one, so ONLY a single unambiguous device may
280
+ * collapse; a multi-device query MUST stay on the in-repo fan-out, which parses
281
+ * commander's variadic `--device` correctly. Returns null (→ in-repo path,
282
+ * unchanged) when the query is not a single-device read:
283
+ * - no `--device`/`-D`, or a missing/flag-shaped value;
284
+ * - `--device`/`-D` appears more than once (`--device box --device mac-mini`);
285
+ * - the value is a fan-out sentinel `all`/`fleet` (case-insensitive);
286
+ * - a bare token immediately follows the value (`--device box mac-mini`) —
287
+ * commander's variadic would take it as a second device, so this is
288
+ * multi-device too;
289
+ * - an explicit `--host` is already on the argv — it wins, matching `secrets`'
290
+ * `rewriteDeviceToHost`, so we never add a second `--host`;
291
+ * - the remaining query (with `--device` stripped) is not a read — a lifecycle
292
+ * `--device` (resume/watch/inject/focus/…) keeps its in-repo/`runOnPeer`
293
+ * behavior exactly.
294
+ *
295
+ * Pure and argv-only: fleet resolution (device → ssh target, via
296
+ * `resolveRemoteDevice`) and the version gate live in the caller so this is unit
297
+ * testable with real inputs. `filters` mirrors the caller's version gate for the
298
+ * 0.2.0 filter flags (the `--device` token is not a filter flag, so it is
299
+ * computed identically on the original argv and on `readArgs`).
300
+ */
301
+ export function planDeviceHostRead(args, opts = {}) {
302
+ const { count, value, valueEndIndex } = scanDeviceFlag(args);
303
+ // Exactly one occurrence, with a real (non-flag) value — else fall through.
304
+ if (count !== 1)
305
+ return null;
306
+ if (value === undefined || value === '' || value.startsWith('-'))
307
+ return null;
308
+ if (usesHostFlag(args))
309
+ return null;
310
+ const lower = value.toLowerCase();
311
+ if (lower === 'all' || lower === 'fleet')
312
+ return null;
313
+ // A bare token after the value is a second variadic device (multi-device).
314
+ const next = valueEndIndex !== undefined ? args[valueEndIndex + 1] : undefined;
315
+ if (next !== undefined && !next.startsWith('-'))
316
+ return null;
317
+ const readArgs = stripRoutingFlags(args, [{ long: 'device', short: 'D', takesValue: true }]);
318
+ if (!isReadQuery(readArgs, { filters: opts.filters }))
319
+ return null;
320
+ return { device: value, readArgs };
321
+ }
100
322
  /** Test seam: drop the memoized bin so PATH fixtures can re-resolve. */
101
323
  export function _resetSessionsClientForTest() {
102
324
  cachedBin = undefined;
325
+ cachedProbedVersion = undefined;
103
326
  }
104
327
  export const SESSIONS_INSTALL_HINT = INSTALL_HINT;
@@ -5,7 +5,7 @@ import { getCacheDir, getDeviceMetaPath, getHelpersDir, getUserAgentsDir } from
5
5
  import { atomicWriteJsonSync, withFileLockAsync } from './fs-atomic.js';
6
6
  import { probeCapture } from './probe.js';
7
7
  import { invocation, isStandaloneComputer } from './computer-client.js';
8
- import { getSocketPath as browserSocketPath } from './browser/ipc.js';
8
+ import { isStandaloneBrowser } from './browser-client.js';
9
9
  export const SETUP_TOOLS = ['browser', 'computer', 'secrets', 'term'];
10
10
  export function toolSetupCacheDir(options = {}) {
11
11
  return path.join(options.cacheDir ?? getCacheDir(), 'setup-tools');
@@ -15,8 +15,9 @@ function cachePath(tool, options) {
15
15
  }
16
16
  function setupInputs(tool) {
17
17
  const inputs = [path.join(getUserAgentsDir(), 'agents.yaml'), getDeviceMetaPath()];
18
+ // browser-cli's IPC socket (integration contract §4). computer's is the helper socket.
18
19
  if (tool === 'browser')
19
- inputs.push(browserSocketPath());
20
+ inputs.push(path.join(getHelpersDir(), 'browser', 'browser.sock'));
20
21
  if (tool === 'computer')
21
22
  inputs.push(process.env.COMPUTER_HELPER_SOCKET || path.join(getHelpersDir(), 'computer.sock'));
22
23
  return inputs;
@@ -39,14 +40,8 @@ function binaryMetadata(tool) {
39
40
  const accept = (candidate) => {
40
41
  if (tool === 'computer')
41
42
  return isStandaloneComputer(candidate);
42
- if (tool === 'browser') {
43
- try {
44
- return !fs.realpathSync(candidate).endsWith(path.join('dist', 'browser.js'));
45
- }
46
- catch {
47
- return false;
48
- }
49
- }
43
+ if (tool === 'browser')
44
+ return isStandaloneBrowser(candidate);
50
45
  return true;
51
46
  };
52
47
  let executable = explicit ? (accept(explicit) ? explicit : null) : findInPath(tool, { accept });
@@ -37,8 +37,9 @@ export declare function loginSubcommand(agent: AgentId): string | null;
37
37
  * personal device; this box is provisioned from the durable credential).
38
38
  * - Unnamed legacy homes use the same version-targeted command shape as
39
39
  * doctor, so the hint never logs a different/default home in by accident.
40
- * - `unverified` emits nothing: the probe could not confirm state (e.g. a
41
- * worker whose token lacks the usage scope), so there is nothing to repair.
40
+ * - `unverified` / `no_evidence` emit nothing: the probe could not confirm state
41
+ * (codex/grok have no probe endpoint; a worker's token lacks the usage scope),
42
+ * so there is nothing to repair.
42
43
  */
43
44
  export declare function fixFor(input: {
44
45
  agent: AgentId;
@@ -66,12 +66,15 @@ export function loginSubcommand(agent) {
66
66
  * personal device; this box is provisioned from the durable credential).
67
67
  * - Unnamed legacy homes use the same version-targeted command shape as
68
68
  * doctor, so the hint never logs a different/default home in by accident.
69
- * - `unverified` emits nothing: the probe could not confirm state (e.g. a
70
- * worker whose token lacks the usage scope), so there is nothing to repair.
69
+ * - `unverified` / `no_evidence` emit nothing: the probe could not confirm state
70
+ * (codex/grok have no probe endpoint; a worker's token lacks the usage scope),
71
+ * so there is nothing to repair.
71
72
  */
72
73
  export function fixFor(input) {
73
74
  const { agent, verdict } = input;
74
- if (verdict === 'live' || verdict === 'rate_limited' || verdict === 'unverified' || verdict === 'ready')
75
+ // `no_evidence` (credential present, nothing probed/run here yet) is benign like
76
+ // `unverified` — there is nothing to repair (PHNX-4116).
77
+ if (verdict === 'live' || verdict === 'rate_limited' || verdict === 'unverified' || verdict === 'no_evidence' || verdict === 'ready')
75
78
  return null;
76
79
  if (input.provisioning === 'per-device' || verdict === 'per-device') {
77
80
  return loginHint(agent);
@@ -98,7 +98,10 @@ export function formatNoHealthyDeviceError(pool, signals, agent) {
98
98
  reason = signal.headroom === 'loaded'
99
99
  ? 'overloaded'
100
100
  : signal.installed !== true || signal.signedIn !== true
101
- ? 'no ready harness account'
101
+ // Surface the box's OWN reason (`runReady.reason`, e.g. "all
102
+ // signed_out") when it gave one, rather than a bare "no ready harness
103
+ // account" that leaves the operator guessing (PHNX-4116).
104
+ ? `no ready harness account${signal.reason ? ` (${signal.reason})` : ''}`
102
105
  : 'ineligible';
103
106
  }
104
107
  else if (signal?.timedOut) {
@@ -1,2 +1,2 @@
1
1
  import type { ResourceDetector } from './types.js';
2
- export declare const commandsDetectors: Partial<Record<"claude" | "codex" | "gemini" | "cursor" | "opencode" | "openclaw" | "copilot" | "amp" | "goose" | "antigravity" | "grok" | "kimi" | "droid" | "hermes" | "muse" | "warp", ResourceDetector>>;
2
+ export declare const commandsDetectors: Partial<Record<"claude" | "codex" | "cursor" | "opencode" | "openclaw" | "copilot" | "amp" | "goose" | "antigravity" | "grok" | "kimi" | "droid" | "hermes" | "muse" | "warp", ResourceDetector>>;
@@ -1,2 +1,2 @@
1
1
  import type { ResourceDetector } from './types.js';
2
- export declare const hooksDetectors: Partial<Record<"claude" | "codex" | "gemini" | "cursor" | "opencode" | "openclaw" | "copilot" | "amp" | "goose" | "antigravity" | "grok" | "kimi" | "droid" | "hermes" | "muse" | "warp", ResourceDetector>>;
2
+ export declare const hooksDetectors: Partial<Record<"claude" | "codex" | "cursor" | "opencode" | "openclaw" | "copilot" | "amp" | "goose" | "antigravity" | "grok" | "kimi" | "droid" | "hermes" | "muse" | "warp", ResourceDetector>>;
@@ -1,2 +1,2 @@
1
1
  import type { ResourceDetector } from './types.js';
2
- export declare const mcpDetectors: Partial<Record<"claude" | "codex" | "gemini" | "cursor" | "opencode" | "openclaw" | "copilot" | "amp" | "goose" | "antigravity" | "grok" | "kimi" | "droid" | "hermes" | "muse" | "warp", ResourceDetector>>;
2
+ export declare const mcpDetectors: Partial<Record<"claude" | "codex" | "cursor" | "opencode" | "openclaw" | "copilot" | "amp" | "goose" | "antigravity" | "grok" | "kimi" | "droid" | "hermes" | "muse" | "warp", ResourceDetector>>;
@@ -1,2 +1,2 @@
1
1
  import type { ResourceDetector } from './types.js';
2
- export declare const permissionsDetectors: Partial<Record<"claude" | "codex" | "gemini" | "cursor" | "opencode" | "openclaw" | "copilot" | "amp" | "goose" | "antigravity" | "grok" | "kimi" | "droid" | "hermes" | "muse" | "warp", ResourceDetector>>;
2
+ export declare const permissionsDetectors: Partial<Record<"claude" | "codex" | "cursor" | "opencode" | "openclaw" | "copilot" | "amp" | "goose" | "antigravity" | "grok" | "kimi" | "droid" | "hermes" | "muse" | "warp", ResourceDetector>>;
@@ -1,2 +1,2 @@
1
1
  import type { ResourceDetector } from './types.js';
2
- export declare const pluginsDetectors: Partial<Record<"claude" | "codex" | "gemini" | "cursor" | "opencode" | "openclaw" | "copilot" | "amp" | "goose" | "antigravity" | "grok" | "kimi" | "droid" | "hermes" | "muse" | "warp", ResourceDetector>>;
2
+ export declare const pluginsDetectors: Partial<Record<"claude" | "codex" | "cursor" | "opencode" | "openclaw" | "copilot" | "amp" | "goose" | "antigravity" | "grok" | "kimi" | "droid" | "hermes" | "muse" | "warp", ResourceDetector>>;
@@ -1,2 +1,2 @@
1
1
  import type { ResourceDetector } from './types.js';
2
- export declare const rulesDetectors: Partial<Record<"claude" | "codex" | "gemini" | "cursor" | "opencode" | "openclaw" | "copilot" | "amp" | "goose" | "antigravity" | "grok" | "kimi" | "droid" | "hermes" | "muse" | "warp", ResourceDetector>>;
2
+ export declare const rulesDetectors: Partial<Record<"claude" | "codex" | "cursor" | "opencode" | "openclaw" | "copilot" | "amp" | "goose" | "antigravity" | "grok" | "kimi" | "droid" | "hermes" | "muse" | "warp", ResourceDetector>>;
@@ -1,4 +1,4 @@
1
1
  import type { ResourceDetector } from './types.js';
2
2
  /** Exported for unit tests (RUSH-2320 #2). Production callers: list() below. */
3
3
  export declare function skillDirsMatch(src: string, dest: string): boolean;
4
- export declare const skillsDetectors: Partial<Record<"claude" | "codex" | "gemini" | "cursor" | "opencode" | "openclaw" | "copilot" | "amp" | "goose" | "antigravity" | "grok" | "kimi" | "droid" | "hermes" | "muse" | "warp", ResourceDetector>>;
4
+ export declare const skillsDetectors: Partial<Record<"claude" | "codex" | "cursor" | "opencode" | "openclaw" | "copilot" | "amp" | "goose" | "antigravity" | "grok" | "kimi" | "droid" | "hermes" | "muse" | "warp", ResourceDetector>>;
@@ -1,2 +1,2 @@
1
1
  import type { ResourceDetector } from './types.js';
2
- export declare const subagentsDetectors: Partial<Record<"claude" | "codex" | "gemini" | "cursor" | "opencode" | "openclaw" | "copilot" | "amp" | "goose" | "antigravity" | "grok" | "kimi" | "droid" | "hermes" | "muse" | "warp", ResourceDetector>>;
2
+ export declare const subagentsDetectors: Partial<Record<"claude" | "codex" | "cursor" | "opencode" | "openclaw" | "copilot" | "amp" | "goose" | "antigravity" | "grok" | "kimi" | "droid" | "hermes" | "muse" | "warp", ResourceDetector>>;
@@ -1,2 +1,2 @@
1
1
  import type { ResourceDetector } from './types.js';
2
- export declare const workflowsDetectors: Partial<Record<"claude" | "codex" | "gemini" | "cursor" | "opencode" | "openclaw" | "copilot" | "amp" | "goose" | "antigravity" | "grok" | "kimi" | "droid" | "hermes" | "muse" | "warp", ResourceDetector>>;
2
+ export declare const workflowsDetectors: Partial<Record<"claude" | "codex" | "cursor" | "opencode" | "openclaw" | "copilot" | "amp" | "goose" | "antigravity" | "grok" | "kimi" | "droid" | "hermes" | "muse" | "warp", ResourceDetector>>;
@@ -1,2 +1,2 @@
1
1
  import type { ResourceWriter } from './types.js';
2
- export declare const commandsWriters: Partial<Record<"claude" | "codex" | "gemini" | "cursor" | "opencode" | "openclaw" | "copilot" | "amp" | "goose" | "antigravity" | "grok" | "kimi" | "droid" | "hermes" | "muse" | "warp", ResourceWriter<string[]>>>;
2
+ export declare const commandsWriters: Partial<Record<"claude" | "codex" | "cursor" | "opencode" | "openclaw" | "copilot" | "amp" | "goose" | "antigravity" | "grok" | "kimi" | "droid" | "hermes" | "muse" | "warp", ResourceWriter<string[]>>>;
@@ -1,2 +1,2 @@
1
1
  import type { ResourceWriter } from './types.js';
2
- export declare const hooksWriters: Partial<Record<"claude" | "codex" | "gemini" | "cursor" | "opencode" | "openclaw" | "copilot" | "amp" | "goose" | "antigravity" | "grok" | "kimi" | "droid" | "hermes" | "muse" | "warp", ResourceWriter<string[]>>>;
2
+ export declare const hooksWriters: Partial<Record<"claude" | "codex" | "cursor" | "opencode" | "openclaw" | "copilot" | "amp" | "goose" | "antigravity" | "grok" | "kimi" | "droid" | "hermes" | "muse" | "warp", ResourceWriter<string[]>>>;
@@ -1,2 +1,2 @@
1
1
  import type { ResourceWriter } from './types.js';
2
- export declare const mcpWriters: Partial<Record<"claude" | "codex" | "gemini" | "cursor" | "opencode" | "openclaw" | "copilot" | "amp" | "goose" | "antigravity" | "grok" | "kimi" | "droid" | "hermes" | "muse" | "warp", ResourceWriter<string[]>>>;
2
+ export declare const mcpWriters: Partial<Record<"claude" | "codex" | "cursor" | "opencode" | "openclaw" | "copilot" | "amp" | "goose" | "antigravity" | "grok" | "kimi" | "droid" | "hermes" | "muse" | "warp", ResourceWriter<string[]>>>;
@@ -1,2 +1,2 @@
1
1
  import type { ResourceWriter } from './types.js';
2
- export declare const permissionsWriters: Partial<Record<"claude" | "codex" | "gemini" | "cursor" | "opencode" | "openclaw" | "copilot" | "amp" | "goose" | "antigravity" | "grok" | "kimi" | "droid" | "hermes" | "muse" | "warp", ResourceWriter<string[]>>>;
2
+ export declare const permissionsWriters: Partial<Record<"claude" | "codex" | "cursor" | "opencode" | "openclaw" | "copilot" | "amp" | "goose" | "antigravity" | "grok" | "kimi" | "droid" | "hermes" | "muse" | "warp", ResourceWriter<string[]>>>;
@@ -1,2 +1,2 @@
1
1
  import type { ResourceWriter } from './types.js';
2
- export declare const pluginsWriters: Partial<Record<"claude" | "codex" | "gemini" | "cursor" | "opencode" | "openclaw" | "copilot" | "amp" | "goose" | "antigravity" | "grok" | "kimi" | "droid" | "hermes" | "muse" | "warp", ResourceWriter<string[]>>>;
2
+ export declare const pluginsWriters: Partial<Record<"claude" | "codex" | "cursor" | "opencode" | "openclaw" | "copilot" | "amp" | "goose" | "antigravity" | "grok" | "kimi" | "droid" | "hermes" | "muse" | "warp", ResourceWriter<string[]>>>;
@@ -3,4 +3,4 @@ export interface RulesSelection {
3
3
  /** Preset name to compose. Empty string falls through to `"default"` in the composer. */
4
4
  preset: string;
5
5
  }
6
- export declare const rulesWriters: Partial<Record<"claude" | "codex" | "gemini" | "cursor" | "opencode" | "openclaw" | "copilot" | "amp" | "goose" | "antigravity" | "grok" | "kimi" | "droid" | "hermes" | "muse" | "warp", ResourceWriter<RulesSelection>>>;
6
+ export declare const rulesWriters: Partial<Record<"claude" | "codex" | "cursor" | "opencode" | "openclaw" | "copilot" | "amp" | "goose" | "antigravity" | "grok" | "kimi" | "droid" | "hermes" | "muse" | "warp", ResourceWriter<RulesSelection>>>;
@@ -1,2 +1,2 @@
1
1
  import type { ResourceWriter } from './types.js';
2
- export declare const skillsWriters: Partial<Record<"claude" | "codex" | "gemini" | "cursor" | "opencode" | "openclaw" | "copilot" | "amp" | "goose" | "antigravity" | "grok" | "kimi" | "droid" | "hermes" | "muse" | "warp", ResourceWriter<string[]>>>;
2
+ export declare const skillsWriters: Partial<Record<"claude" | "codex" | "cursor" | "opencode" | "openclaw" | "copilot" | "amp" | "goose" | "antigravity" | "grok" | "kimi" | "droid" | "hermes" | "muse" | "warp", ResourceWriter<string[]>>>;
@@ -11,6 +11,7 @@ import * as fs from 'fs';
11
11
  import * as path from 'path';
12
12
  import { getUserAgentsDir, getAgentsDir, getEnabledExtraRepos, getCommandsDir, getSkillsDir, getHooksDir } from '../../state.js';
13
13
  import { isSafeSegmentName, safeJoin } from '../../paths.js';
14
+ import { AGENTS } from '../../agents.js';
14
15
  /** Trusted source bases for content-like kinds. Project layer excluded. */
15
16
  function trustedSourceBases() {
16
17
  return [
@@ -57,6 +58,10 @@ function pluginSupportsAgent(manifest, agent) {
57
58
  /** Every `plugins/<plugin>/skills` dir across trusted source bases, filtered to an optional plugin/agent scope. */
58
59
  export function pluginSkillDirs(options = {}) {
59
60
  const dirs = [];
61
+ // A harness that loads plugin skills natively gets no flattened copy and no
62
+ // plugin credit for a top-level skills/<name>: that copy is an orphan there.
63
+ if (options.agent && AGENTS[options.agent]?.nativePluginSkills)
64
+ return dirs;
60
65
  for (const base of trustedSourceBases()) {
61
66
  const pluginsDir = path.join(base.dir, 'plugins');
62
67
  let entries;
@@ -1,2 +1,2 @@
1
1
  import type { ResourceWriter } from './types.js';
2
- export declare const subagentsWriters: Partial<Record<"claude" | "codex" | "gemini" | "cursor" | "opencode" | "openclaw" | "copilot" | "amp" | "goose" | "antigravity" | "grok" | "kimi" | "droid" | "hermes" | "muse" | "warp", ResourceWriter<string[]>>>;
2
+ export declare const subagentsWriters: Partial<Record<"claude" | "codex" | "cursor" | "opencode" | "openclaw" | "copilot" | "amp" | "goose" | "antigravity" | "grok" | "kimi" | "droid" | "hermes" | "muse" | "warp", ResourceWriter<string[]>>>;
@@ -1,2 +1,2 @@
1
1
  import type { ResourceWriter } from './types.js';
2
- export declare const workflowsWriters: Partial<Record<"claude" | "codex" | "gemini" | "cursor" | "opencode" | "openclaw" | "copilot" | "amp" | "goose" | "antigravity" | "grok" | "kimi" | "droid" | "hermes" | "muse" | "warp", ResourceWriter<string[]>>>;
2
+ export declare const workflowsWriters: Partial<Record<"claude" | "codex" | "cursor" | "opencode" | "openclaw" | "copilot" | "amp" | "goose" | "antigravity" | "grok" | "kimi" | "droid" | "hermes" | "muse" | "warp", ResourceWriter<string[]>>>;
@@ -427,8 +427,11 @@ export declare function withMetaLock<T>(fn: () => T): T;
427
427
  * never run inside the short, non-heartbeated lockfile window; the user repo
428
428
  * already pushes.
429
429
  *
430
- * Scoped tightly: only called when the central bytes actually changed, and never
431
- * from the daemon (see {@link isDaemonProcess}). A commit failure fails open —
430
+ * Scoped tightly: only called when the central bytes actually changed — from a
431
+ * CLI command and from the daemon alike, since no daemon tick commits the user
432
+ * repo any more (the shared-state exchange moved to SSH, PHNX-4116), so a
433
+ * daemon-side central write would otherwise sit dirty until the next CLI
434
+ * central edit. A commit failure fails open —
432
435
  * a concurrent daemon holding `index.lock`, a mid-rebase repo — leaving
433
436
  * agents.yaml dirty; the next successful central write commits it, and until
434
437
  * then a pull that would collide refuses rather than losing data. A config
@@ -489,8 +492,7 @@ export declare function updateMeta(updates: Partial<Meta> | ((meta: Meta) => Met
489
492
  * A CLI command that actually moved the fleet-shared central agents.yaml commits
490
493
  * it so the tree is never left dirty on that file at rest — the window that
491
494
  * trips `dirtyTreeRefusal` and wedges pulls fleet-wide (PHNX-3968). Gated on a
492
- * real byte change and never run in the daemon, whose publish tick owns central
493
- * commits. See {@link commitCentralConfig}.
495
+ * real byte change. See {@link commitCentralConfig}.
494
496
  */
495
497
  export declare function commitCentralConfigAfterWrite(centralChanged: boolean): void;
496
498
  /** Derive a filesystem-safe local clone path for a package source URL. */
package/dist/lib/state.js CHANGED
@@ -864,17 +864,6 @@ function writeIfChanged(filePath, content) {
864
864
  atomicWriteFileSync(filePath, content);
865
865
  return true;
866
866
  }
867
- /**
868
- * True in the always-on daemon process (launched as `agents __daemon-run`,
869
- * see cli/src/index.ts). The daemon owns central git commits through
870
- * fleet-shared-repo-sync's publish tick, so a central write from inside the
871
- * daemon must NOT also commit here — that would race the publisher's own
872
- * add/commit/rebase/push on the same index. Every ordinary CLI invocation
873
- * returns false and commits its own central edit synchronously.
874
- */
875
- function isDaemonProcess() {
876
- return process.argv[2] === '__daemon-run';
877
- }
878
867
  /**
879
868
  * Commit the central `agents.yaml` in the user repo, synchronously, so a CLI
880
869
  * config mutation never leaves the working tree dirty on that one shared-line
@@ -892,8 +881,11 @@ function isDaemonProcess() {
892
881
  * never run inside the short, non-heartbeated lockfile window; the user repo
893
882
  * already pushes.
894
883
  *
895
- * Scoped tightly: only called when the central bytes actually changed, and never
896
- * from the daemon (see {@link isDaemonProcess}). A commit failure fails open —
884
+ * Scoped tightly: only called when the central bytes actually changed — from a
885
+ * CLI command and from the daemon alike, since no daemon tick commits the user
886
+ * repo any more (the shared-state exchange moved to SSH, PHNX-4116), so a
887
+ * daemon-side central write would otherwise sit dirty until the next CLI
888
+ * central edit. A commit failure fails open —
897
889
  * a concurrent daemon holding `index.lock`, a mid-rebase repo — leaving
898
890
  * agents.yaml dirty; the next successful central write commits it, and until
899
891
  * then a pull that would collide refuses rather than losing data. A config
@@ -1685,13 +1677,11 @@ export function updateMeta(updates) {
1685
1677
  * A CLI command that actually moved the fleet-shared central agents.yaml commits
1686
1678
  * it so the tree is never left dirty on that file at rest — the window that
1687
1679
  * trips `dirtyTreeRefusal` and wedges pulls fleet-wide (PHNX-3968). Gated on a
1688
- * real byte change and never run in the daemon, whose publish tick owns central
1689
- * commits. See {@link commitCentralConfig}.
1680
+ * real byte change. See {@link commitCentralConfig}.
1690
1681
  */
1691
1682
  export function commitCentralConfigAfterWrite(centralChanged) {
1692
- if (centralChanged && !isDaemonProcess()) {
1683
+ if (centralChanged)
1693
1684
  commitCentralConfig(USER_AGENTS_DIR);
1694
- }
1695
1685
  }
1696
1686
  /** Derive a filesystem-safe local clone path for a package source URL. */
1697
1687
  export function getPackageLocalPath(source) {