@phnx-labs/agents-cli 1.22.116 → 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 (254) hide show
  1. package/CHANGELOG.md +247 -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.d.ts +18 -0
  9. package/dist/commands/exec.js +104 -12
  10. package/dist/commands/repo.js +13 -49
  11. package/dist/commands/route.js +5 -5
  12. package/dist/commands/routines.js +39 -12
  13. package/dist/commands/sessions-render.d.ts +1 -1
  14. package/dist/commands/sessions-render.js +0 -1
  15. package/dist/commands/sessions.test-fixture.d.ts +0 -1
  16. package/dist/commands/sessions.test-fixture.js +0 -37
  17. package/dist/commands/setup-browser.d.ts +17 -12
  18. package/dist/commands/setup-browser.js +52 -87
  19. package/dist/commands/setup-preferences.d.ts +0 -17
  20. package/dist/commands/setup-preferences.js +18 -49
  21. package/dist/commands/setup-term.d.ts +4 -3
  22. package/dist/commands/setup-term.js +5 -4
  23. package/dist/commands/setup.js +11 -11
  24. package/dist/commands/ssh.js +10 -1
  25. package/dist/commands/sync.js +6 -44
  26. package/dist/commands/teams.js +0 -1
  27. package/dist/commands/update.d.ts +19 -0
  28. package/dist/commands/update.js +34 -6
  29. package/dist/commands/view.d.ts +12 -1
  30. package/dist/commands/view.js +47 -2
  31. package/dist/index.js +107 -26
  32. package/dist/lib/account-capabilities.js +0 -2
  33. package/dist/lib/account-catalog.d.ts +38 -0
  34. package/dist/lib/account-catalog.js +110 -26
  35. package/dist/lib/account-provider-registry.js +1 -1
  36. package/dist/lib/account-registry.d.ts +1 -1
  37. package/dist/lib/account-registry.js +36 -2
  38. package/dist/lib/accounting/rotate.js +22 -8
  39. package/dist/lib/accounting/usage-ingest.d.ts +6 -1
  40. package/dist/lib/accounting/usage-ingest.js +115 -45
  41. package/dist/lib/accounting/usage-sync.d.ts +134 -31
  42. package/dist/lib/accounting/usage-sync.js +227 -66
  43. package/dist/lib/accounting/usage.d.ts +16 -3
  44. package/dist/lib/accounting/usage.js +26 -8
  45. package/dist/lib/accounts/slots.d.ts +8 -0
  46. package/dist/lib/accounts/slots.js +17 -0
  47. package/dist/lib/add-dir.js +0 -1
  48. package/dist/lib/agent-cli-commands.js +0 -1
  49. package/dist/lib/agent-spec/agents.d.ts +6 -7
  50. package/dist/lib/agent-spec/agents.js +6 -60
  51. package/dist/lib/auth-health.d.ts +69 -1
  52. package/dist/lib/auth-health.js +133 -4
  53. package/dist/lib/browser/context.d.ts +82 -0
  54. package/dist/lib/browser/context.js +88 -0
  55. package/dist/lib/browser/paths.d.ts +25 -0
  56. package/dist/lib/browser/paths.js +51 -0
  57. package/dist/lib/browser/record.d.ts +38 -0
  58. package/dist/lib/browser/record.js +57 -0
  59. package/dist/lib/browser/sessions-list.js +1 -2
  60. package/dist/lib/browser-client.d.ts +158 -0
  61. package/dist/lib/browser-client.js +234 -0
  62. package/dist/lib/channels/owner-sink.d.ts +1 -18
  63. package/dist/lib/channels/owner-sink.js +29 -82
  64. package/dist/lib/channels/providers/rush.d.ts +7 -7
  65. package/dist/lib/channels/providers/rush.js +127 -57
  66. package/dist/lib/claude-statusline.d.ts +1 -1
  67. package/dist/lib/claude-statusline.js +15 -3
  68. package/dist/lib/commands.js +1 -1
  69. package/dist/lib/crabbox/lease.js +1 -1
  70. package/dist/lib/crabbox/runtimes.js +1 -3
  71. package/dist/lib/daemon/account-state-daemon-service.js +1 -0
  72. package/dist/lib/daemon/auth-sync-service.d.ts +21 -18
  73. package/dist/lib/daemon/auth-sync-service.js +37 -43
  74. package/dist/lib/daemon/daemon.d.ts +25 -14
  75. package/dist/lib/daemon/daemon.js +92 -154
  76. package/dist/lib/daemon/runner.d.ts +2 -2
  77. package/dist/lib/daemon/runner.js +32 -9
  78. package/dist/lib/daemon/self-update-service.d.ts +35 -34
  79. package/dist/lib/daemon/self-update-service.js +40 -36
  80. package/dist/lib/daemon/service.d.ts +12 -8
  81. package/dist/lib/daemon/supervisor.d.ts +65 -54
  82. package/dist/lib/daemon/supervisor.js +136 -171
  83. package/dist/lib/daemon/testdata/process-view-start.js +5 -2
  84. package/dist/lib/daemon/usage-sync-service.d.ts +21 -30
  85. package/dist/lib/daemon/usage-sync-service.js +46 -78
  86. package/dist/lib/daemon-health.d.ts +22 -5
  87. package/dist/lib/daemon-health.js +47 -3
  88. package/dist/lib/daemon-services.d.ts +1 -1
  89. package/dist/lib/daemon-services.js +0 -10
  90. package/dist/lib/daemon-ticks.js +4 -2
  91. package/dist/lib/devices/doctor-findings.js +8 -11
  92. package/dist/lib/devices/harness-inventory.js +14 -5
  93. package/dist/lib/exec-account-home.js +5 -0
  94. package/dist/lib/exec.js +0 -11
  95. package/dist/lib/feed/events.js +0 -1
  96. package/dist/lib/feed/tool-activity.js +5 -2
  97. package/dist/lib/fleet-shared-state.d.ts +45 -0
  98. package/dist/lib/fleet-shared-state.js +78 -9
  99. package/dist/lib/fs-atomic.js +25 -0
  100. package/dist/lib/harness-auth-capabilities.js +0 -1
  101. package/dist/lib/helper-versions.js +1 -1
  102. package/dist/lib/hooks/cache.d.ts +7 -0
  103. package/dist/lib/hooks/cache.js +27 -2
  104. package/dist/lib/hooks/install.js +7 -1
  105. package/dist/lib/hosts/ready.d.ts +38 -8
  106. package/dist/lib/hosts/ready.js +62 -14
  107. package/dist/lib/hosts/remote-cmd.js +1 -0
  108. package/dist/lib/installations/active-check.d.ts +27 -0
  109. package/dist/lib/installations/active-check.js +61 -0
  110. package/dist/lib/installations/migrate.js +6 -3
  111. package/dist/lib/installations/shims.js +9 -7
  112. package/dist/lib/installations/update.js +21 -15
  113. package/dist/lib/installations/versions.js +12 -1
  114. package/dist/lib/manifest.js +1 -1
  115. package/dist/lib/models.js +0 -2
  116. package/dist/lib/open-url.js +28 -54
  117. package/dist/lib/placement.d.ts +2 -0
  118. package/dist/lib/placement.js +5 -0
  119. package/dist/lib/profiles.js +0 -2
  120. package/dist/lib/refresh.js +1 -1
  121. package/dist/lib/routine-readiness.d.ts +35 -0
  122. package/dist/lib/routine-readiness.js +74 -16
  123. package/dist/lib/rules/compile.js +6 -0
  124. package/dist/lib/sandbox.js +0 -2
  125. package/dist/lib/secrets-policy.d.ts +89 -11
  126. package/dist/lib/secrets-policy.js +175 -28
  127. package/dist/lib/session/db.d.ts +1 -1
  128. package/dist/lib/session/db.js +65 -18
  129. package/dist/lib/session/discover.js +1 -283
  130. package/dist/lib/session/mirror.d.ts +4 -3
  131. package/dist/lib/session/mirror.js +4 -3
  132. package/dist/lib/session/parse.d.ts +0 -2
  133. package/dist/lib/session/parse.js +1 -138
  134. package/dist/lib/session/throughput.d.ts +1 -3
  135. package/dist/lib/session/throughput.js +0 -20
  136. package/dist/lib/session/types.d.ts +2 -2
  137. package/dist/lib/session/types.js +2 -2
  138. package/dist/lib/sessions-client.d.ts +60 -1
  139. package/dist/lib/sessions-client.js +227 -4
  140. package/dist/lib/setup-tool-status.js +8 -12
  141. package/dist/lib/signin-badge.d.ts +3 -2
  142. package/dist/lib/signin-badge.js +6 -3
  143. package/dist/lib/smart-launch.js +4 -1
  144. package/dist/lib/staleness/detectors/commands.d.ts +1 -1
  145. package/dist/lib/staleness/detectors/hooks.d.ts +1 -1
  146. package/dist/lib/staleness/detectors/mcp.d.ts +1 -1
  147. package/dist/lib/staleness/detectors/permissions.d.ts +1 -1
  148. package/dist/lib/staleness/detectors/plugins.d.ts +1 -1
  149. package/dist/lib/staleness/detectors/rules.d.ts +1 -1
  150. package/dist/lib/staleness/detectors/skills.d.ts +1 -1
  151. package/dist/lib/staleness/detectors/subagents.d.ts +1 -1
  152. package/dist/lib/staleness/detectors/workflows.d.ts +1 -1
  153. package/dist/lib/staleness/writers/commands.d.ts +1 -1
  154. package/dist/lib/staleness/writers/hooks.d.ts +1 -1
  155. package/dist/lib/staleness/writers/mcp.d.ts +1 -1
  156. package/dist/lib/staleness/writers/permissions.d.ts +1 -1
  157. package/dist/lib/staleness/writers/plugins.d.ts +1 -1
  158. package/dist/lib/staleness/writers/rules.d.ts +1 -1
  159. package/dist/lib/staleness/writers/skills.d.ts +1 -1
  160. package/dist/lib/staleness/writers/sources.js +5 -0
  161. package/dist/lib/staleness/writers/subagents.d.ts +1 -1
  162. package/dist/lib/staleness/writers/workflows.d.ts +1 -1
  163. package/dist/lib/state.d.ts +6 -4
  164. package/dist/lib/state.js +7 -17
  165. package/dist/lib/teams/parsers.d.ts +1 -1
  166. package/dist/lib/teams/parsers.js +1 -136
  167. package/dist/lib/teams/placement-probe.js +50 -15
  168. package/dist/lib/teams/scheduler.d.ts +7 -0
  169. package/dist/lib/types.d.ts +10 -1
  170. package/dist/lib/types.js +1 -1
  171. package/dist/lib/view-types.d.ts +32 -0
  172. package/dist/lib/watchdog/read.js +0 -1
  173. package/dist/lib/watchdog/rotate.js +1 -2
  174. package/dist/lib/watchdog/runner.js +2 -2
  175. package/dist/lib/watchdog/watchdog.d.ts +1 -1
  176. package/dist/lib/watchdog/watchdogTail.js +0 -31
  177. package/package.json +1 -1
  178. package/scripts/postinstall.js +8 -8
  179. package/dist/browser.d.ts +0 -2
  180. package/dist/browser.js +0 -17
  181. package/dist/commands/browser-picker.d.ts +0 -18
  182. package/dist/commands/browser-picker.js +0 -97
  183. package/dist/lib/browser/arc-discovery.d.ts +0 -52
  184. package/dist/lib/browser/arc-discovery.js +0 -197
  185. package/dist/lib/browser/arc-dom.d.ts +0 -14
  186. package/dist/lib/browser/arc-dom.js +0 -121
  187. package/dist/lib/browser/caller-identity.d.ts +0 -40
  188. package/dist/lib/browser/caller-identity.js +0 -175
  189. package/dist/lib/browser/cdp.d.ts +0 -51
  190. package/dist/lib/browser/cdp.js +0 -254
  191. package/dist/lib/browser/chrome.d.ts +0 -141
  192. package/dist/lib/browser/chrome.js +0 -704
  193. package/dist/lib/browser/chromium-discovery.d.ts +0 -28
  194. package/dist/lib/browser/chromium-discovery.js +0 -96
  195. package/dist/lib/browser/devices.d.ts +0 -34
  196. package/dist/lib/browser/devices.js +0 -61
  197. package/dist/lib/browser/domain-skills.d.ts +0 -71
  198. package/dist/lib/browser/domain-skills.js +0 -195
  199. package/dist/lib/browser/drivers/arc.d.ts +0 -40
  200. package/dist/lib/browser/drivers/arc.js +0 -276
  201. package/dist/lib/browser/drivers/firefox.d.ts +0 -99
  202. package/dist/lib/browser/drivers/firefox.js +0 -377
  203. package/dist/lib/browser/drivers/local.d.ts +0 -55
  204. package/dist/lib/browser/drivers/local.js +0 -266
  205. package/dist/lib/browser/drivers/ssh.d.ts +0 -120
  206. package/dist/lib/browser/drivers/ssh.js +0 -467
  207. package/dist/lib/browser/editor.d.ts +0 -3
  208. package/dist/lib/browser/editor.js +0 -50
  209. package/dist/lib/browser/ffmpeg.d.ts +0 -12
  210. package/dist/lib/browser/ffmpeg.js +0 -184
  211. package/dist/lib/browser/firefox-discovery.d.ts +0 -69
  212. package/dist/lib/browser/firefox-discovery.js +0 -162
  213. package/dist/lib/browser/har.d.ts +0 -85
  214. package/dist/lib/browser/har.js +0 -77
  215. package/dist/lib/browser/hygiene.d.ts +0 -97
  216. package/dist/lib/browser/hygiene.js +0 -153
  217. package/dist/lib/browser/index.d.ts +0 -5
  218. package/dist/lib/browser/index.js +0 -5
  219. package/dist/lib/browser/input.d.ts +0 -7
  220. package/dist/lib/browser/input.js +0 -92
  221. package/dist/lib/browser/ipc.d.ts +0 -204
  222. package/dist/lib/browser/ipc.js +0 -1453
  223. package/dist/lib/browser/login-detection.d.ts +0 -87
  224. package/dist/lib/browser/login-detection.js +0 -274
  225. package/dist/lib/browser/profiles.d.ts +0 -422
  226. package/dist/lib/browser/profiles.js +0 -1157
  227. package/dist/lib/browser/refs.d.ts +0 -89
  228. package/dist/lib/browser/refs.js +0 -191
  229. package/dist/lib/browser/registry.d.ts +0 -70
  230. package/dist/lib/browser/registry.js +0 -252
  231. package/dist/lib/browser/remote-control.d.ts +0 -59
  232. package/dist/lib/browser/remote-control.js +0 -85
  233. package/dist/lib/browser/resolve-target.d.ts +0 -66
  234. package/dist/lib/browser/resolve-target.js +0 -264
  235. package/dist/lib/browser/runtime-state.d.ts +0 -291
  236. package/dist/lib/browser/runtime-state.js +0 -584
  237. package/dist/lib/browser/secret-ref.d.ts +0 -10
  238. package/dist/lib/browser/secret-ref.js +0 -14
  239. package/dist/lib/browser/service.d.ts +0 -812
  240. package/dist/lib/browser/service.js +0 -4693
  241. package/dist/lib/browser/stream.d.ts +0 -17
  242. package/dist/lib/browser/stream.js +0 -72
  243. package/dist/lib/browser/task-index.d.ts +0 -78
  244. package/dist/lib/browser/task-index.js +0 -199
  245. package/dist/lib/browser/types.d.ts +0 -647
  246. package/dist/lib/browser/types.js +0 -61
  247. package/dist/lib/browser/upload.d.ts +0 -29
  248. package/dist/lib/browser/upload.js +0 -298
  249. package/dist/lib/daemon/browser-ipc-service.d.ts +0 -20
  250. package/dist/lib/daemon/browser-ipc-service.js +0 -46
  251. package/dist/lib/daemon/browser-task-reap-service.d.ts +0 -14
  252. package/dist/lib/daemon/browser-task-reap-service.js +0 -26
  253. package/dist/lib/fleet-shared-repo-sync.d.ts +0 -55
  254. 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 });
@@ -140,10 +135,11 @@ async function checkTool(row) {
140
135
  if (row.tool === 'secrets')
141
136
  return { ...row, checkedAtMs: Date.now(), detail: 'Installed. Secret access is checked when used; this check does not unlock secrets.' };
142
137
  // term is a headless PTY engine with no `status --json` health surface; it is
143
- // spawned on demand by the OAuth device-code driver (fleet login / auth mint).
138
+ // spawned on demand by the setup-token mint (`agents accounts add`/`login`,
139
+ // via auth-mint.ts → term-driver.ts, the sole term-client consumers).
144
140
  // Presence on PATH is the whole readiness signal — do not probe it.
145
141
  if (row.tool === 'term')
146
- return { ...row, readiness: 'ready', detail: 'Installed. Spawned on demand by fleet login and auth mint.', checkedAtMs: Date.now() };
142
+ return { ...row, readiness: 'ready', detail: 'Installed. Spawned on demand by `agents accounts add`/`login`.', checkedAtMs: Date.now() };
147
143
  try {
148
144
  const { command, prefix } = invocation(row.executable);
149
145
  const { stdout } = await probeCapture(command, [...prefix, 'status', '--json'], 8000, { acceptedExitCodes: [0, 1], maxOutputBytes: 256 * 1024 });
@@ -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. */