@intutic/cli 2.1.0 → 2.3.0

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 (368) hide show
  1. package/README.md +8 -8
  2. package/dist/cli.d.ts +0 -1
  3. package/dist/cli.d.ts.map +1 -1
  4. package/dist/cli.js +492 -21
  5. package/dist/cli.js.map +1 -1
  6. package/dist/commands/apiCommand.d.ts +40 -0
  7. package/dist/commands/apiCommand.d.ts.map +1 -0
  8. package/dist/commands/apiCommand.js +88 -0
  9. package/dist/commands/apiCommand.js.map +1 -0
  10. package/dist/commands/budget.d.ts +4 -0
  11. package/dist/commands/budget.d.ts.map +1 -1
  12. package/dist/commands/budget.js +8 -0
  13. package/dist/commands/budget.js.map +1 -1
  14. package/dist/commands/compliance.d.ts +122 -0
  15. package/dist/commands/compliance.d.ts.map +1 -0
  16. package/dist/commands/compliance.js +278 -0
  17. package/dist/commands/compliance.js.map +1 -0
  18. package/dist/commands/connect.d.ts +26 -1
  19. package/dist/commands/connect.d.ts.map +1 -1
  20. package/dist/commands/connect.js +222 -86
  21. package/dist/commands/connect.js.map +1 -1
  22. package/dist/commands/credentials.d.ts +1 -1
  23. package/dist/commands/credentials.js +1 -1
  24. package/dist/commands/decision.d.ts +4 -4
  25. package/dist/commands/decision.js +4 -4
  26. package/dist/commands/disconnect.d.ts +29 -0
  27. package/dist/commands/disconnect.d.ts.map +1 -0
  28. package/dist/commands/disconnect.js +266 -0
  29. package/dist/commands/disconnect.js.map +1 -0
  30. package/dist/commands/doctor.d.ts +0 -1
  31. package/dist/commands/doctor.d.ts.map +1 -1
  32. package/dist/commands/doctor.js +0 -1
  33. package/dist/commands/doctor.js.map +1 -1
  34. package/dist/commands/enforce.d.ts +1 -1
  35. package/dist/commands/enforce.js +1 -1
  36. package/dist/commands/enterprise.d.ts +3 -2
  37. package/dist/commands/enterprise.d.ts.map +1 -1
  38. package/dist/commands/enterprise.js +11 -3
  39. package/dist/commands/enterprise.js.map +1 -1
  40. package/dist/commands/env.d.ts +1 -1
  41. package/dist/commands/env.d.ts.map +1 -1
  42. package/dist/commands/env.js +3 -8
  43. package/dist/commands/env.js.map +1 -1
  44. package/dist/commands/exec.d.ts +2 -3
  45. package/dist/commands/exec.d.ts.map +1 -1
  46. package/dist/commands/exec.js +4 -5
  47. package/dist/commands/exec.js.map +1 -1
  48. package/dist/commands/gateLiveness.d.ts +15 -0
  49. package/dist/commands/gateLiveness.d.ts.map +1 -0
  50. package/dist/commands/gateLiveness.js +34 -0
  51. package/dist/commands/gateLiveness.js.map +1 -0
  52. package/dist/commands/gateway.d.ts +16 -4
  53. package/dist/commands/gateway.d.ts.map +1 -1
  54. package/dist/commands/gateway.js +47 -6
  55. package/dist/commands/gateway.js.map +1 -1
  56. package/dist/commands/github.d.ts +20 -0
  57. package/dist/commands/github.d.ts.map +1 -0
  58. package/dist/commands/github.js +41 -0
  59. package/dist/commands/github.js.map +1 -0
  60. package/dist/commands/guardrails.d.ts +28 -2
  61. package/dist/commands/guardrails.d.ts.map +1 -1
  62. package/dist/commands/guardrails.js +233 -24
  63. package/dist/commands/guardrails.js.map +1 -1
  64. package/dist/commands/init.d.ts +1 -2
  65. package/dist/commands/init.d.ts.map +1 -1
  66. package/dist/commands/init.js +3 -2
  67. package/dist/commands/init.js.map +1 -1
  68. package/dist/commands/install-daemon.d.ts +1 -1
  69. package/dist/commands/install-daemon.js +2 -2
  70. package/dist/commands/integrity.d.ts +10 -2
  71. package/dist/commands/integrity.d.ts.map +1 -1
  72. package/dist/commands/integrity.js +10 -3
  73. package/dist/commands/integrity.js.map +1 -1
  74. package/dist/commands/inventory.d.ts +60 -0
  75. package/dist/commands/inventory.d.ts.map +1 -0
  76. package/dist/commands/inventory.js +141 -0
  77. package/dist/commands/inventory.js.map +1 -0
  78. package/dist/commands/judge.d.ts +1 -1
  79. package/dist/commands/judge.js +2 -2
  80. package/dist/commands/judge.js.map +1 -1
  81. package/dist/commands/login.d.ts +0 -1
  82. package/dist/commands/login.d.ts.map +1 -1
  83. package/dist/commands/login.js +0 -1
  84. package/dist/commands/login.js.map +1 -1
  85. package/dist/commands/logout.d.ts +0 -1
  86. package/dist/commands/logout.d.ts.map +1 -1
  87. package/dist/commands/logout.js +0 -1
  88. package/dist/commands/logout.js.map +1 -1
  89. package/dist/commands/mcp.d.ts +31 -0
  90. package/dist/commands/mcp.d.ts.map +1 -0
  91. package/dist/commands/mcp.js +65 -0
  92. package/dist/commands/mcp.js.map +1 -0
  93. package/dist/commands/notifications.d.ts +54 -0
  94. package/dist/commands/notifications.d.ts.map +1 -0
  95. package/dist/commands/notifications.js +151 -0
  96. package/dist/commands/notifications.js.map +1 -0
  97. package/dist/commands/org.d.ts +3 -3
  98. package/dist/commands/org.js +3 -3
  99. package/dist/commands/policy.d.ts +6 -1
  100. package/dist/commands/policy.d.ts.map +1 -1
  101. package/dist/commands/policy.js +77 -37
  102. package/dist/commands/policy.js.map +1 -1
  103. package/dist/commands/predict-cost.d.ts +1 -2
  104. package/dist/commands/predict-cost.d.ts.map +1 -1
  105. package/dist/commands/predict-cost.js +1 -4
  106. package/dist/commands/predict-cost.js.map +1 -1
  107. package/dist/commands/rollback.d.ts.map +1 -1
  108. package/dist/commands/rollback.js +3 -1
  109. package/dist/commands/rollback.js.map +1 -1
  110. package/dist/commands/routing.d.ts +1 -1
  111. package/dist/commands/routing.js +1 -1
  112. package/dist/commands/rules.d.ts +74 -0
  113. package/dist/commands/rules.d.ts.map +1 -0
  114. package/dist/commands/rules.js +283 -0
  115. package/dist/commands/rules.js.map +1 -0
  116. package/dist/commands/settings.d.ts +43 -0
  117. package/dist/commands/settings.d.ts.map +1 -0
  118. package/dist/commands/settings.js +108 -0
  119. package/dist/commands/settings.js.map +1 -0
  120. package/dist/commands/setup.d.ts +1 -1
  121. package/dist/commands/setup.js +1 -1
  122. package/dist/commands/siem.d.ts +53 -0
  123. package/dist/commands/siem.d.ts.map +1 -0
  124. package/dist/commands/siem.js +132 -0
  125. package/dist/commands/siem.js.map +1 -0
  126. package/dist/commands/skill.d.ts +5 -5
  127. package/dist/commands/skill.js +7 -7
  128. package/dist/commands/skill.js.map +1 -1
  129. package/dist/commands/status.d.ts +0 -1
  130. package/dist/commands/status.d.ts.map +1 -1
  131. package/dist/commands/status.js +2 -3
  132. package/dist/commands/status.js.map +1 -1
  133. package/dist/commands/syncContext.d.ts +0 -1
  134. package/dist/commands/syncContext.d.ts.map +1 -1
  135. package/dist/commands/syncContext.js +0 -1
  136. package/dist/commands/syncContext.js.map +1 -1
  137. package/dist/commands/team.d.ts +2 -2
  138. package/dist/commands/team.js +2 -2
  139. package/dist/commands/traces.d.ts +0 -3
  140. package/dist/commands/traces.d.ts.map +1 -1
  141. package/dist/commands/traces.js +0 -3
  142. package/dist/commands/traces.js.map +1 -1
  143. package/dist/commands/usage.d.ts +41 -0
  144. package/dist/commands/usage.d.ts.map +1 -0
  145. package/dist/commands/usage.js +129 -0
  146. package/dist/commands/usage.js.map +1 -0
  147. package/dist/commands/whoami.d.ts +0 -1
  148. package/dist/commands/whoami.d.ts.map +1 -1
  149. package/dist/commands/whoami.js +0 -1
  150. package/dist/commands/whoami.js.map +1 -1
  151. package/dist/config/keychain.d.ts +0 -1
  152. package/dist/config/keychain.d.ts.map +1 -1
  153. package/dist/config/keychain.js +0 -1
  154. package/dist/config/keychain.js.map +1 -1
  155. package/dist/config/paths.d.ts +0 -1
  156. package/dist/config/paths.d.ts.map +1 -1
  157. package/dist/config/paths.js +0 -1
  158. package/dist/config/paths.js.map +1 -1
  159. package/dist/config/store.d.ts +0 -1
  160. package/dist/config/store.d.ts.map +1 -1
  161. package/dist/config/store.js +0 -1
  162. package/dist/config/store.js.map +1 -1
  163. package/dist/harness/ag2.d.ts +1 -1
  164. package/dist/harness/ag2.js +1 -1
  165. package/dist/harness/agentFramework.d.ts +1 -1
  166. package/dist/harness/agentFramework.js +1 -1
  167. package/dist/harness/agenticOrchestrator.d.ts +4 -6
  168. package/dist/harness/agenticOrchestrator.d.ts.map +1 -1
  169. package/dist/harness/agenticOrchestrator.js +5 -7
  170. package/dist/harness/agenticOrchestrator.js.map +1 -1
  171. package/dist/harness/agentsMd.d.ts +24 -0
  172. package/dist/harness/agentsMd.d.ts.map +1 -0
  173. package/dist/harness/agentsMd.js +28 -0
  174. package/dist/harness/agentsMd.js.map +1 -0
  175. package/dist/harness/aider.d.ts +3 -1
  176. package/dist/harness/aider.d.ts.map +1 -1
  177. package/dist/harness/aider.js +9 -7
  178. package/dist/harness/aider.js.map +1 -1
  179. package/dist/harness/antigravity.d.ts +23 -5
  180. package/dist/harness/antigravity.d.ts.map +1 -1
  181. package/dist/harness/antigravity.js +45 -54
  182. package/dist/harness/antigravity.js.map +1 -1
  183. package/dist/harness/autogen.d.ts +1 -1
  184. package/dist/harness/autogen.js +2 -2
  185. package/dist/harness/autogen.js.map +1 -1
  186. package/dist/harness/claudeCode.d.ts +22 -1
  187. package/dist/harness/claudeCode.d.ts.map +1 -1
  188. package/dist/harness/claudeCode.js +47 -3
  189. package/dist/harness/claudeCode.js.map +1 -1
  190. package/dist/harness/cline.d.ts.map +1 -1
  191. package/dist/harness/cline.js +16 -24
  192. package/dist/harness/cline.js.map +1 -1
  193. package/dist/harness/codex.d.ts +6 -1
  194. package/dist/harness/codex.d.ts.map +1 -1
  195. package/dist/harness/codex.js +26 -28
  196. package/dist/harness/codex.js.map +1 -1
  197. package/dist/harness/continue.d.ts +10 -4
  198. package/dist/harness/continue.d.ts.map +1 -1
  199. package/dist/harness/continue.js +22 -22
  200. package/dist/harness/continue.js.map +1 -1
  201. package/dist/harness/cursor.d.ts +5 -2
  202. package/dist/harness/cursor.d.ts.map +1 -1
  203. package/dist/harness/cursor.js +19 -27
  204. package/dist/harness/cursor.js.map +1 -1
  205. package/dist/harness/detector.d.ts +0 -1
  206. package/dist/harness/detector.d.ts.map +1 -1
  207. package/dist/harness/detector.js +0 -1
  208. package/dist/harness/detector.js.map +1 -1
  209. package/dist/harness/dsh.d.ts +5 -3
  210. package/dist/harness/dsh.d.ts.map +1 -1
  211. package/dist/harness/dsh.js +17 -29
  212. package/dist/harness/dsh.js.map +1 -1
  213. package/dist/harness/eve.d.ts +3 -4
  214. package/dist/harness/eve.d.ts.map +1 -1
  215. package/dist/harness/eve.js +3 -4
  216. package/dist/harness/eve.js.map +1 -1
  217. package/dist/harness/githubCopilot.d.ts +4 -1
  218. package/dist/harness/githubCopilot.d.ts.map +1 -1
  219. package/dist/harness/githubCopilot.js +13 -24
  220. package/dist/harness/githubCopilot.js.map +1 -1
  221. package/dist/harness/goose.d.ts +7 -3
  222. package/dist/harness/goose.d.ts.map +1 -1
  223. package/dist/harness/goose.js +18 -21
  224. package/dist/harness/goose.js.map +1 -1
  225. package/dist/harness/grok.d.ts +2 -2
  226. package/dist/harness/grok.d.ts.map +1 -1
  227. package/dist/harness/grok.js +15 -28
  228. package/dist/harness/grok.js.map +1 -1
  229. package/dist/harness/hermes.d.ts +6 -0
  230. package/dist/harness/hermes.d.ts.map +1 -1
  231. package/dist/harness/hermes.js +17 -16
  232. package/dist/harness/hermes.js.map +1 -1
  233. package/dist/harness/jsSdkGatedAdapter.d.ts.map +1 -1
  234. package/dist/harness/jsSdkGatedAdapter.js +4 -6
  235. package/dist/harness/jsSdkGatedAdapter.js.map +1 -1
  236. package/dist/harness/langgraph.d.ts.map +1 -1
  237. package/dist/harness/langgraph.js +4 -6
  238. package/dist/harness/langgraph.js.map +1 -1
  239. package/dist/harness/muse.d.ts +3 -7
  240. package/dist/harness/muse.d.ts.map +1 -1
  241. package/dist/harness/muse.js +17 -39
  242. package/dist/harness/muse.js.map +1 -1
  243. package/dist/harness/n8n.d.ts +13 -7
  244. package/dist/harness/n8n.d.ts.map +1 -1
  245. package/dist/harness/n8n.js +21 -127
  246. package/dist/harness/n8n.js.map +1 -1
  247. package/dist/harness/openWebUI.d.ts.map +1 -1
  248. package/dist/harness/openWebUI.js +5 -4
  249. package/dist/harness/openWebUI.js.map +1 -1
  250. package/dist/harness/openaiAgents.d.ts +1 -1
  251. package/dist/harness/openaiAgents.d.ts.map +1 -1
  252. package/dist/harness/openaiAgents.js +6 -3
  253. package/dist/harness/openaiAgents.js.map +1 -1
  254. package/dist/harness/openclaw.d.ts +8 -0
  255. package/dist/harness/openclaw.d.ts.map +1 -1
  256. package/dist/harness/openclaw.js +27 -12
  257. package/dist/harness/openclaw.js.map +1 -1
  258. package/dist/harness/opencode.d.ts +4 -5
  259. package/dist/harness/opencode.d.ts.map +1 -1
  260. package/dist/harness/opencode.js +17 -34
  261. package/dist/harness/opencode.js.map +1 -1
  262. package/dist/harness/openhands.d.ts +9 -3
  263. package/dist/harness/openhands.d.ts.map +1 -1
  264. package/dist/harness/openhands.js +43 -25
  265. package/dist/harness/openhands.js.map +1 -1
  266. package/dist/harness/pi.d.ts +7 -2
  267. package/dist/harness/pi.d.ts.map +1 -1
  268. package/dist/harness/pi.js +19 -18
  269. package/dist/harness/pi.js.map +1 -1
  270. package/dist/harness/rooCode.d.ts +12 -5
  271. package/dist/harness/rooCode.d.ts.map +1 -1
  272. package/dist/harness/rooCode.js +25 -43
  273. package/dist/harness/rooCode.js.map +1 -1
  274. package/dist/harness/rulesFiles.d.ts +53 -0
  275. package/dist/harness/rulesFiles.d.ts.map +1 -0
  276. package/dist/harness/rulesFiles.js +106 -0
  277. package/dist/harness/rulesFiles.js.map +1 -0
  278. package/dist/harness/sdkGatedAdapter.d.ts.map +1 -1
  279. package/dist/harness/sdkGatedAdapter.js +4 -6
  280. package/dist/harness/sdkGatedAdapter.js.map +1 -1
  281. package/dist/harness/smolagents.d.ts +2 -2
  282. package/dist/harness/smolagents.js +3 -3
  283. package/dist/harness/smolagents.js.map +1 -1
  284. package/dist/harness/types.d.ts +20 -6
  285. package/dist/harness/types.d.ts.map +1 -1
  286. package/dist/harness/types.js +1 -95
  287. package/dist/harness/types.js.map +1 -1
  288. package/dist/harness/windsurf.d.ts +7 -2
  289. package/dist/harness/windsurf.d.ts.map +1 -1
  290. package/dist/harness/windsurf.js +22 -29
  291. package/dist/harness/windsurf.js.map +1 -1
  292. package/dist/harness/xirp.d.ts +6 -9
  293. package/dist/harness/xirp.d.ts.map +1 -1
  294. package/dist/harness/xirp.js +7 -10
  295. package/dist/harness/xirp.js.map +1 -1
  296. package/dist/lib/api.d.ts +27 -1
  297. package/dist/lib/api.d.ts.map +1 -1
  298. package/dist/lib/api.js +55 -18
  299. package/dist/lib/api.js.map +1 -1
  300. package/dist/lib/ciscoScanner.d.ts +1 -1
  301. package/dist/lib/ciscoScanner.js +1 -1
  302. package/dist/lib/decisionsLogRefresh.d.ts.map +1 -1
  303. package/dist/lib/decisionsLogRefresh.js +6 -7
  304. package/dist/lib/decisionsLogRefresh.js.map +1 -1
  305. package/dist/lib/deviceReport.d.ts +15 -0
  306. package/dist/lib/deviceReport.d.ts.map +1 -1
  307. package/dist/lib/deviceReport.js +30 -0
  308. package/dist/lib/deviceReport.js.map +1 -1
  309. package/dist/lib/envInjector.d.ts +16 -6
  310. package/dist/lib/envInjector.d.ts.map +1 -1
  311. package/dist/lib/envInjector.js +99 -88
  312. package/dist/lib/envInjector.js.map +1 -1
  313. package/dist/lib/gitHooks.d.ts +0 -1
  314. package/dist/lib/gitHooks.d.ts.map +1 -1
  315. package/dist/lib/gitHooks.js +8 -9
  316. package/dist/lib/gitHooks.js.map +1 -1
  317. package/dist/lib/gracefulShutdown.d.ts +1 -1
  318. package/dist/lib/gracefulShutdown.js +1 -1
  319. package/dist/lib/hash.d.ts +0 -1
  320. package/dist/lib/hash.d.ts.map +1 -1
  321. package/dist/lib/hash.js +0 -1
  322. package/dist/lib/hash.js.map +1 -1
  323. package/dist/lib/inventory.d.ts +47 -0
  324. package/dist/lib/inventory.d.ts.map +1 -0
  325. package/dist/lib/inventory.js +106 -0
  326. package/dist/lib/inventory.js.map +1 -0
  327. package/dist/lib/logger.d.ts +0 -1
  328. package/dist/lib/logger.d.ts.map +1 -1
  329. package/dist/lib/logger.js +0 -1
  330. package/dist/lib/logger.js.map +1 -1
  331. package/dist/lib/mdmManifest.d.ts +30 -2
  332. package/dist/lib/mdmManifest.d.ts.map +1 -1
  333. package/dist/lib/mdmManifest.js +50 -2
  334. package/dist/lib/mdmManifest.js.map +1 -1
  335. package/dist/lib/onboarding.d.ts +0 -1
  336. package/dist/lib/onboarding.d.ts.map +1 -1
  337. package/dist/lib/onboarding.js +2 -3
  338. package/dist/lib/onboarding.js.map +1 -1
  339. package/dist/lib/otelPropagation.d.ts +1 -1
  340. package/dist/lib/otelPropagation.js +1 -1
  341. package/dist/lib/policySnapshot.d.ts +3 -1
  342. package/dist/lib/policySnapshot.d.ts.map +1 -1
  343. package/dist/lib/policySnapshot.js +15 -5
  344. package/dist/lib/policySnapshot.js.map +1 -1
  345. package/dist/lib/process.d.ts +5 -1
  346. package/dist/lib/process.d.ts.map +1 -1
  347. package/dist/lib/process.js +13 -4
  348. package/dist/lib/process.js.map +1 -1
  349. package/dist/lib/providerProbe.d.ts +2 -2
  350. package/dist/lib/providerProbe.js +2 -2
  351. package/dist/lib/sandbox/firecracker.d.ts +3 -3
  352. package/dist/lib/sandbox/firecracker.js +3 -3
  353. package/dist/lib/sandbox/index.d.ts +2 -2
  354. package/dist/lib/sandbox/index.js +2 -2
  355. package/dist/lib/sandbox/oci.d.ts +1 -1
  356. package/dist/lib/sandbox/oci.js +1 -1
  357. package/dist/lib/sandbox/types.d.ts +1 -1
  358. package/dist/lib/sandbox/types.js +1 -1
  359. package/dist/lib/sopFrontMatter.d.ts +1 -1
  360. package/dist/lib/sopFrontMatter.js +1 -1
  361. package/package.json +6 -5
  362. package/resources/sandbox/Dockerfile +1 -1
  363. package/resources/sandbox/entrypoint.sh +4 -4
  364. package/resources/sandbox/firecracker-launch.sh +1 -1
  365. package/dist/harness/base.d.ts +0 -20
  366. package/dist/harness/base.d.ts.map +0 -1
  367. package/dist/harness/base.js +0 -73
  368. package/dist/harness/base.js.map +0 -1
@@ -5,7 +5,6 @@
5
5
  * a real-time filesystem watcher for configuration drift detection,
6
6
  * and a 30-second HTTP polling loop as a secondary fallback.
7
7
  *
8
- * LLD #14 — connect.ts
9
8
  * HLD §3.14 — Real-Time State Mirroring
10
9
  *
11
10
  * @module
@@ -25,10 +24,12 @@ import { getAdapter } from '../harness/detector.js';
25
24
  import { printOnboardingGuide } from '../lib/onboarding.js';
26
25
  import { writeEnforcementState } from '../lib/enforcementState.js';
27
26
  import { reportDeviceState } from '../lib/deviceReport.js';
27
+ import { reportMachineInventory, shouldReportInventoryThisIteration } from '../lib/inventory.js';
28
28
  import { parseChecksums, verifyChecksum } from '../lib/binaryChecksum.js';
29
29
  import { newIso } from '@intutic/id';
30
+ import { HarnessType, rulesFileOf } from '@intutic/shared-types';
30
31
  import pc from 'picocolors';
31
- import { SyncWsClient, startWatcher, updatePreToolUseHooks, injectMcpServer, guardSettingsFile, warnIfDshCoverageGap, writeRuntimeEnv, refreshPolicySnapshot, refreshGateCaches, localHoldTokensFor, runComplianceProbes, drainHookEvents, drainReviewRequests, REVIEW_REQUESTS_LOG, syncOfflineTraces, TrajectoryMonitor, collectAgentReport, reportAgent, fetchLocalProxyInstanceId, startHarnessSession, endAllOpenSessions, } from '@intutic/sync-daemon';
32
+ import { SyncWsClient, startWatcher, updatePreToolUseHooks, injectMcpServer, noteProxyUrl, guardPolicySnapshot, guardSettingsFile, isGuardedPath, warnIfDshCoverageGap, writeRuntimeEnv, refreshPolicySnapshot, refreshGateCaches, localHoldTokensFor, runComplianceProbes, drainHookEvents, drainReviewRequests, REVIEW_REQUESTS_LOG, syncOfflineTraces, TrajectoryMonitor, fetchLocalProxyInstanceId, endAllOpenSessions, applySkillOptEdits, reportHarnessAgents, captureAndUpload, shouldCaptureThisIteration, refreshDecisionsDigest, retireClaudeMdDigest, claudeCodeReadsAgentsMd, writeBundledSkills, clearImmutable, setImmutable, } from '@intutic/sync-daemon';
32
33
  import { watch } from 'chokidar';
33
34
  // Named, not default: under `module: Node16` TypeScript resolves ioredis's
34
35
  // default export to the module namespace rather than the class, so `new
@@ -61,20 +62,57 @@ function isPortInUse(port) {
61
62
  });
62
63
  }
63
64
  /**
64
- * Is this a path the tamper guard should inspect?
65
+ * Applies a synced configuration to each configured harness.
65
66
  *
66
- * Deliberately broad: `guardSettingsFile` already branches per harness, so the only
67
- * job here is to stop filtering out paths it knows how to handle.
67
+ * The gate is installed whatever rule sets the workspace has: it enforces the
68
+ * built-in protections, the destructive-command tier, group rules and holds,
69
+ * none of which need a rule set. Gating it on rule sets left a workspace with
70
+ * none, or with none targeting a harness, with that harness ungoverned. The
71
+ * rules file is written only when a rule set targets the harness, or on a
72
+ * forced sync.
73
+ *
74
+ * Harnesses that read the same file (Codex, Grok Build, OpenCode, Muse Code
75
+ * and others all read `AGENTS.md`) each get every rule set aimed at any of
76
+ * them that is configured here: one section, the same for every writer, so
77
+ * the last writer no longer replaces the others' rules.
78
+ *
79
+ * Claude Code reads its own `.claude/rules/` file at every launch, and also
80
+ * the workspace's `AGENTS.md` when there is no `CLAUDE.md` on the path (or
81
+ * the user chose `claude-md-and-agents-md`; see claudeAgentsMd.ts). When it
82
+ * does, a rule set aimed at both Claude Code and an `AGENTS.md` reader
83
+ * reaches it through `AGENTS.md`, so its own file leaves that rule set out
84
+ * rather than load it twice.
85
+ *
86
+ * @returns how many rule sets were written into rules files.
68
87
  */
69
- function isGovernedConfigPath(changedPath, filename) {
70
- if (filename === 'settings.json' || filename === 'settings.local.json')
71
- return true;
72
- if (filename === 'hooks.json')
73
- return true;
74
- // Goose's immutable governance plugin.
75
- if (changedPath.includes('intutic-governance'))
76
- return true;
77
- return false;
88
+ export async function writeHarnessConfigs(harnesses, workspaceRoot, sops, proxyUrl, force) {
89
+ let written = 0;
90
+ const writtenFiles = new Set();
91
+ const agentsMdReaders = harnesses.filter((h) => rulesFileOf(h) === 'AGENTS.md');
92
+ const claudeFromAgentsMd = harnesses.includes(HarnessType.CLAUDE_CODE) && agentsMdReaders.length > 0 && (await claudeCodeReadsAgentsMd(workspaceRoot));
93
+ for (const harnessType of harnesses) {
94
+ const adapter = getAdapter(harnessType);
95
+ if (!adapter)
96
+ continue;
97
+ await adapter.installGate?.(workspaceRoot, proxyUrl);
98
+ const file = rulesFileOf(harnessType);
99
+ const readers = file === null ? [harnessType] : harnesses.filter((h) => rulesFileOf(h) === file);
100
+ const aimed = sops.filter((sop) => sop.harnessTargets.some((t) => readers.includes(t)));
101
+ const viaAgentsMd = harnessType === HarnessType.CLAUDE_CODE && claudeFromAgentsMd;
102
+ const targetSops = viaAgentsMd ? aimed.filter((sop) => !sop.harnessTargets.some((t) => agentsMdReaders.includes(t))) : aimed;
103
+ // Claude Code's own file is rewritten even when every rule set aimed at
104
+ // it now comes through AGENTS.md, so it stops carrying them.
105
+ if (aimed.length === 0 && !force)
106
+ continue;
107
+ if (!(await adapter.writeConfig(workspaceRoot, targetSops, proxyUrl)))
108
+ continue;
109
+ // A shared file counts once, however many of its readers wrote it.
110
+ if (file === null || !writtenFiles.has(file))
111
+ written += targetSops.length;
112
+ if (file !== null)
113
+ writtenFiles.add(file);
114
+ }
115
+ return written;
78
116
  }
79
117
  /**
80
118
  * Asset names MUST match publish.yml's build-rust-proxy matrix
@@ -262,7 +300,7 @@ export async function runConnect(opts) {
262
300
  // 2. AbortController for clean shutdown
263
301
  let proxyProc = null;
264
302
  const ac = new AbortController();
265
- // TD-484: this handler used to abort the loop and hope. The process stayed
303
+ // This handler used to abort the loop and hope. The process stayed
266
304
  // alive for hours. `createShutdownHandler` adds the exit deadline and the
267
305
  // second-signal exit; `terminateChild` escalates to SIGKILL.
268
306
  const shutdown = createShutdownHandler({
@@ -453,24 +491,57 @@ export async function runConnect(opts) {
453
491
  // when the config version moved: policy changes without the config version
454
492
  // changing, and a guardrail promoted, a hold approved or an egress mode
455
493
  // flipped mid-session used to reach a connected machine only on restart —
456
- // this function was called once, at startup (TD-488). Parity with the
457
- // daemon's own loop (`syncLoop.ts`'s Steps 0b–0d). None of these throw.
494
+ // this function was called once, at startup. None of these throw.
458
495
  //
459
496
  // The snapshot also carries this workspace's `review_before:` tokens as hold
460
497
  // rules (synced SOPs, settings and local `.intutic/sops`), so every gate —
461
- // not only the Claude Code hook — holds on them (TD-474 item 4).
462
- async function refreshGateCachesForConnect() {
498
+ // not only the Claude Code hook — holds on them.
499
+ //
500
+ // First, the snapshot's self-heal: a snapshot changed while the daemon was
501
+ // stopped is put back to the last verified copy, and reported, before the
502
+ // refresh replaces it. With `checkSnapshot` false the caller is that check.
503
+ async function refreshGateCachesForConnect(checkSnapshot = true) {
504
+ if (checkSnapshot)
505
+ await guardPolicySnapshot(safeConfig.workspaceRoot, async () => { });
463
506
  const localHoldTokens = await localHoldTokensFor(safeConfig.workspaceRoot, lastCachedConfig?.sops ?? [], lastCachedConfig?.settings, safeConfig.harnesses);
464
507
  const landed = await refreshGateCaches({ controlPlaneUrl, apiKey: safeCreds.apiKey, workspaceId: safeCreds.workspaceId, localHoldTokens });
465
508
  if (!landed.snapshot)
466
509
  log.dim('Policy snapshot refresh failed (will retry next sync); built-in protections are unaffected.');
467
510
  }
511
+ // The rules files the adapters write, absolute. `configFileName` is
512
+ // relative to the workspace for most harnesses and absolute for the few
513
+ // that keep their config in the home directory.
514
+ const rulesFilePaths = () => safeConfig.harnesses
515
+ .map((h) => getAdapter(h)?.configFileName)
516
+ .filter((f) => Boolean(f))
517
+ .map((f) => node_path.resolve(safeConfig.workspaceRoot, f));
518
+ // Whether Claude Code read the workspace's AGENTS.md at the last sync. A
519
+ // CLAUDE.md added or removed (or the user's Project instructions setting
520
+ // changed) moves rule sets between AGENTS.md and Claude Code's own file, so
521
+ // a change rewrites the rules files as a new config would.
522
+ let lastClaudeFromAgentsMd;
468
523
  // 3. Define configuration applier function
469
524
  async function applySyncConfig(syncConfig, force = false) {
470
525
  let sopsWritten = 0;
471
526
  lastCachedConfig = syncConfig;
472
527
  await refreshGateCachesForConnect();
473
- if (syncConfig.configVersion > localConfigVersion || force) {
528
+ const claudeFromAgentsMd = safeConfig.harnesses.includes(HarnessType.CLAUDE_CODE)
529
+ ? await claudeCodeReadsAgentsMd(safeConfig.workspaceRoot)
530
+ : false;
531
+ const claudeDeliveryMoved = lastClaudeFromAgentsMd !== undefined && claudeFromAgentsMd !== lastClaudeFromAgentsMd;
532
+ lastClaudeFromAgentsMd = claudeFromAgentsMd;
533
+ // Write-protect (`bypassEnforcementTier: 'immutable'`, macOS): the rules
534
+ // files carry the user-immutable flag between cycles, so it comes off
535
+ // before anything below writes them and goes back on afterwards. It also
536
+ // comes off whenever the config moved, so a workspace that switched away
537
+ // from write-protect is not left with files nothing can rewrite.
538
+ const writeProtect = syncConfig.settings?.bypassEnforcementTier === 'immutable';
539
+ const configMoved = syncConfig.configVersion > localConfigVersion || force || claudeDeliveryMoved;
540
+ if (writeProtect || configMoved) {
541
+ for (const file of rulesFilePaths())
542
+ await clearImmutable(file);
543
+ }
544
+ if (configMoved) {
474
545
  log.info(`Applying configuration v${syncConfig.configVersion}...`);
475
546
  // Load and compile local SOP entries
476
547
  const localSopEntries = [];
@@ -524,19 +595,11 @@ export async function runConnect(opts) {
524
595
  }
525
596
  }
526
597
  const combinedSops = [...syncConfig.sops, ...localSopEntries];
527
- // a. Write configs for all active harnesses
528
- for (const harnessType of safeConfig.harnesses) {
529
- const adapter = getAdapter(harnessType);
530
- if (!adapter)
531
- continue;
532
- const targetSops = combinedSops.filter((sop) => sop.harnessTargets.includes(harnessType));
533
- if (targetSops.length === 0 && !force)
534
- continue;
535
- const written = await adapter.writeConfig(safeConfig.workspaceRoot, targetSops, syncConfig.proxyUrl);
536
- if (written) {
537
- sopsWritten += targetSops.length;
538
- }
539
- }
598
+ // a. Write configs for all active harnesses. The proxy URL is recorded
599
+ // first: `intutic disconnect` recognises the base-URL settings it is
600
+ // written into by it.
601
+ await noteProxyUrl(syncConfig.proxyUrl);
602
+ sopsWritten += await writeHarnessConfigs(safeConfig.harnesses, safeConfig.workspaceRoot, combinedSops, syncConfig.proxyUrl, force);
540
603
  // b. Invalidate/update Claude Code hooks and settings
541
604
  if (safeConfig.harnesses.includes('claude-code')) {
542
605
  try {
@@ -546,19 +609,54 @@ export async function runConnect(opts) {
546
609
  log.warn(`Failed to update Claude Code hooks: ${err instanceof Error ? err.message : String(err)}`);
547
610
  }
548
611
  }
549
- // c. Inject + proxy-wrap MCP servers across all supported harnesses
550
- try {
551
- await injectMcpServer(safeConfig.workspaceRoot, safeCreds.workspaceId);
552
- }
553
- catch (err) {
554
- log.warn(`Failed to inject MCP server configs: ${err instanceof Error ? err.message : String(err)}`);
555
- }
556
612
  localConfigVersion = syncConfig.configVersion;
557
613
  // Mirror workspace settings locally so spawn-time policies (like
558
614
  // allowLocalMemoryVaults) apply on the next connect without a fetch.
559
615
  safeConfig.settings = syncConfig.settings;
560
616
  saveConfig({ ...safeConfig, configVersion: localConfigVersion, settings: syncConfig.settings });
561
617
  }
618
+ // c. Inject + proxy-wrap MCP servers across all supported harnesses, on
619
+ // every cycle rather than only when the config moved: a server a user adds
620
+ // to a harness config after `connect` started is wrapped on the next cycle.
621
+ // The writes are write-if-changed, so an unchanged config costs no write.
622
+ try {
623
+ await injectMcpServer(safeConfig.workspaceRoot, safeCreds.workspaceId, { skip: safeConfig.disconnectedHarnesses });
624
+ }
625
+ catch (err) {
626
+ log.warn(`Failed to inject MCP server configs: ${err instanceof Error ? err.message : String(err)}`);
627
+ }
628
+ // d. SkillOpt edits the control plane queued for this workspace, acked
629
+ // back so a suggestion reaches `applied` only once it is on disk. All of
630
+ // them again when the rules files were just rewritten, which drops every
631
+ // overlay. Before the integrity hashes below, so the drift watcher does
632
+ // not take the edit for tampering.
633
+ await applySkillOptEdits({
634
+ workspaceRoot: safeConfig.workspaceRoot,
635
+ controlPlaneUrl,
636
+ apiKey: safeCreds.apiKey,
637
+ appliedEdits: syncConfig.appliedEdits,
638
+ bypassEnforcementTier: syncConfig.settings?.bypassEnforcementTier,
639
+ reapplyAll: configMoved,
640
+ });
641
+ // e. The governed decisions log, opt-in (`decisionsLogEnabled`, off by
642
+ // default), into each harness's instructions file. Also before the hashes:
643
+ // it writes into shared files such as AGENTS.md, whose rules section they
644
+ // cover. The section earlier versions put in CLAUDE.md comes out whether
645
+ // or not the log is on.
646
+ await retireClaudeMdDigest(safeConfig.workspaceRoot);
647
+ if (syncConfig.settings?.decisionsLogEnabled) {
648
+ await refreshDecisionsDigest({
649
+ controlPlaneUrl,
650
+ apiKey: safeCreds.apiKey,
651
+ workspaceId: safeCreds.workspaceId,
652
+ workspaceRoot: safeConfig.workspaceRoot,
653
+ harnesses: safeConfig.harnesses,
654
+ });
655
+ }
656
+ if (writeProtect) {
657
+ for (const file of rulesFilePaths())
658
+ await setImmutable(file);
659
+ }
562
660
  // c. Compute file hashes + update integrity store
563
661
  const fileHashes = [];
564
662
  const canonicalHashes = {};
@@ -695,7 +793,7 @@ export async function runConnect(opts) {
695
793
  proxyStatus = 'unhealthy';
696
794
  if (ac.signal.aborted) {
697
795
  // Shutting down: the proxy is gone because we stopped it. Re-spawning
698
- // it here would leave an orphan holding the port (TD-484).
796
+ // it here would leave an orphan holding the port.
699
797
  proxyStatus = 'stopped';
700
798
  }
701
799
  else if (proxyProc) {
@@ -762,7 +860,7 @@ export async function runConnect(opts) {
762
860
  catch (err) {
763
861
  log.warn(`Failed to send daemon heartbeat: ${err instanceof Error ? err.message : String(err)}`);
764
862
  }
765
- // g. Enforcement device visibility (post-strip gap #2, LLD #63
863
+ // g. Enforcement device visibility (post-strip gap #2, egress
766
864
  // hardening) — the "interval" half of "continuous compliance": this
767
865
  // loop already runs unprivileged, as the real user, on a timer, and
768
866
  // already just computed sslTrustStatus above for its own DR-healing
@@ -797,7 +895,7 @@ export async function runConnect(opts) {
797
895
  mcpProxyFailBehavior: syncConfig.settings?.mcpProxyFailBehavior,
798
896
  mcpProxyMode: syncConfig.settings?.mcpProxyMode,
799
897
  bypassEnforcementTier: syncConfig.settings?.bypassEnforcementTier,
800
- // Shared by the MCP proxies for their session window (Wave 5.3, TD-437) — only when it is running.
898
+ // Shared by the MCP proxies for their session window — only when it is running.
801
899
  valkeyUrl: valkeyResult.running ? valkeyUrl : undefined,
802
900
  });
803
901
  }
@@ -828,7 +926,7 @@ export async function runConnect(opts) {
828
926
  log.warn(`Compliance probes failed (non-fatal): ${err instanceof Error ? err.message : String(err)}`);
829
927
  }
830
928
  };
831
- // Drains BOTH logs, matching `services/sync-daemon/src/syncLoop.ts`.
929
+ // Drains BOTH logs.
832
930
  //
833
931
  // This drained hook events only. `intutic connect` runs the daemon in-process,
834
932
  // so under the CLI runtime a `review_before` hold still blocked the tool
@@ -899,7 +997,7 @@ export async function runConnect(opts) {
899
997
  controlPlaneUrl,
900
998
  apiKey: safeCreds.apiKey,
901
999
  workspaceId: safeCreds.workspaceId,
902
- // Shared by the MCP proxies for their session window (Wave 5.3, TD-437) — only when it is running.
1000
+ // Shared by the MCP proxies for their session window — only when it is running.
903
1001
  valkeyUrl: valkeyResult.running ? valkeyUrl : undefined,
904
1002
  });
905
1003
  }
@@ -923,6 +1021,14 @@ export async function runConnect(opts) {
923
1021
  'will not apply until the next successful sync. Built-in protections are unaffected.');
924
1022
  }
925
1023
  await refreshGateCachesForConnect();
1024
+ // The bundled rule-author agent skill, written only when absent so a local
1025
+ // edit is never overwritten.
1026
+ try {
1027
+ await writeBundledSkills(safeConfig.workspaceRoot);
1028
+ }
1029
+ catch (err) {
1030
+ log.dim(`Could not write the bundled agent skill: ${err instanceof Error ? err.message : String(err)}`);
1031
+ }
926
1032
  // Sync offline traces back to PostgreSQL on startup
927
1033
  try {
928
1034
  await syncOfflineTraces(controlPlaneUrl, safeCreds.apiKey);
@@ -1016,7 +1122,7 @@ export async function runConnect(opts) {
1016
1122
  const drainSafetyTimer = setInterval(runDrain, 60_000);
1017
1123
  // Run initial compliance check on startup
1018
1124
  await runProbes();
1019
- // dsh coverage-gap visibility (TD-370): once at startup, not every poll
1125
+ // dsh coverage-gap visibility: once at startup, not every poll
1020
1126
  // tick — the gap only changes state on the user's first `dsh --profile
1021
1127
  // <name>` run, which the filesystem watcher below reacts to immediately
1022
1128
  // via settingsGuard.ts's isDshProfilesRoot `addDir` handling. Only worth
@@ -1062,20 +1168,17 @@ export async function runConnect(opts) {
1062
1168
  }
1063
1169
  // B. Tamper detection for every governed config, not just Claude Code's.
1064
1170
  //
1065
- // This used to filter on `filename === 'settings.json'`, which silently disabled
1066
- // five of the six branches inside guardSettingsFile: the Cursor, Windsurf, Cline
1067
- // and OpenHands restores all key off `hooks.json`, and the Goose
1068
- // governance-plugin override incident keys off a path containing
1069
- // `intutic-governance`. None of those basenames could ever reach the guard, so a
1070
- // tampered hooks file on any harness but Claude Code was a no-op — and a tampered
1071
- // Windsurf/VS Code/Gemini settings file passed the basename test only to fall
1072
- // through and log `settings_intact`.
1073
- //
1074
- // Route anything on the governed list to the guard and let it decide.
1075
- if (isGovernedConfigPath(changedPath, filename)) {
1171
+ // Routed on the guard's own watch list. A filter on file names here, first
1172
+ // `settings.json` and later `settings.json`/`hooks.json`/`intutic-governance`,
1173
+ // kept the guard from ever seeing the paths it restores under other names:
1174
+ // a gate script, Cline's PreToolUse, OpenClaw's openclaw.json, Muse's
1175
+ // managed-hooks file and every dsh profile patch.
1176
+ if (isGuardedPath(changedPath, safeConfig.workspaceRoot)) {
1076
1177
  try {
1077
1178
  const sops = lastCachedConfig?.sops ?? [];
1078
- const tampered = await guardSettingsFile(changedPath, safeConfig.workspaceRoot, sops);
1179
+ // The proxy URL too: the restored gate scripts carry it, and an empty
1180
+ // one would be written into them.
1181
+ const tampered = await guardSettingsFile(changedPath, safeConfig.workspaceRoot, sops, lastCachedConfig?.proxyUrl ?? '', undefined, new Set(safeConfig.disconnectedHarnesses ?? []), () => refreshGateCachesForConnect(false));
1079
1182
  if (tampered) {
1080
1183
  log.warn(`[Security] Governance settings tamper detected and restored: ${changedPath}`);
1081
1184
  wsClient.send({
@@ -1092,8 +1195,9 @@ export async function runConnect(opts) {
1092
1195
  }
1093
1196
  return;
1094
1197
  }
1095
- // B. Handle governed harness file drift detection
1096
- const matchingHarness = safeConfig.harnesses.find((h) => getAdapter(h)?.configFileName === filename);
1198
+ // B. Handle governed harness file drift detection. By path, not
1199
+ // basename: rules files sit in directories (`.cursor/rules/…`).
1200
+ const matchingHarness = safeConfig.harnesses.find((h) => getAdapter(h)?.configFileName === relativePath.split(node_path.sep).join('/'));
1097
1201
  if (!matchingHarness)
1098
1202
  return;
1099
1203
  const adapter = getAdapter(matchingHarness);
@@ -1105,6 +1209,20 @@ export async function runConnect(opts) {
1105
1209
  return;
1106
1210
  const canonical = integrity.files[adapter.configFileName] ?? '';
1107
1211
  if (currentHash !== canonical) {
1212
+ // Record only (`bypassEnforcementTier: 'alert-only'`): the edit stays,
1213
+ // and the drift report below is the record.
1214
+ const tier = (lastCachedConfig?.settings ?? safeConfig.settings)?.bypassEnforcementTier;
1215
+ if (tier === 'alert-only') {
1216
+ log.warn(`Governed config file "${filename}" was edited by hand; recording the drift and leaving the edit in place.`);
1217
+ wsClient.send({
1218
+ type: 'drift_report',
1219
+ harnessType: adapter.type,
1220
+ filePath: adapter.configFileName,
1221
+ localHash: currentHash || '',
1222
+ canonicalHash: canonical,
1223
+ });
1224
+ return;
1225
+ }
1108
1226
  log.warn(`Governed config file "${filename}" modification detected! Reverting to approved baseline...`);
1109
1227
  // Backup drifted version first
1110
1228
  try {
@@ -1138,6 +1256,7 @@ export async function runConnect(opts) {
1138
1256
  }
1139
1257
  });
1140
1258
  // 6. Secondary fallback HTTP poll loop
1259
+ let pollIteration = 0;
1141
1260
  while (!ac.signal.aborted) {
1142
1261
  try {
1143
1262
  const syncConfig = await client.fetchConfig(safeCreds.workspaceId);
@@ -1154,40 +1273,56 @@ export async function runConnect(opts) {
1154
1273
  log.dim(`Offline trace sync failed (will retry next poll): ${err instanceof Error ? err.message : String(err)}`);
1155
1274
  }
1156
1275
  // Register agents + report one session per harness (the reporter
1157
- // dedupes per run and per proxy process). connect's inline loop bypassed
1158
- // both, leaving the dashboard graph empty for the primary user path.
1159
- // The local proxy's instance id is read once per iteration: with it the
1160
- // harness's git/task context lands on the proxy's own session row, the
1161
- // one its traces are filed under (TD-231, Wave 5.6).
1276
+ // dedupes per run and per proxy process), and turn this cycle's skill
1277
+ // scan findings into `skill_flagged` events. The local proxy's instance
1278
+ // id is read once per iteration: with it the harness's git/task context
1279
+ // lands on the proxy's own session row, the one its traces are filed
1280
+ // under.
1162
1281
  const proxyInstanceId = safeConfig.harnesses.length > 0 ? await fetchLocalProxyInstanceId() : null;
1163
- for (const harnessType of safeConfig.harnesses) {
1282
+ const { governanceInputs, failures } = await reportHarnessAgents({
1283
+ controlPlaneUrl,
1284
+ apiKey: safeCreds.apiKey,
1285
+ workspaceId: safeCreds.workspaceId,
1286
+ workspaceRoot: safeConfig.workspaceRoot,
1287
+ harnesses: safeConfig.harnesses,
1288
+ allowLocalVaults: syncConfig.settings?.allowLocalMemoryVaults,
1289
+ proxyInstanceId,
1290
+ });
1291
+ for (const { harness, error } of failures) {
1292
+ log.dim(`Agent report/session for harness '${harness}' failed: ${error}`);
1293
+ }
1294
+ // Every few minutes, the machine's AI inventory for the org-wide view:
1295
+ // every harness the detection rules find, connected or not, with its
1296
+ // gate state, and the MCP servers and skill bundles on the machine.
1297
+ if (shouldReportInventoryThisIteration(pollIteration)) {
1298
+ const inventory = await reportMachineInventory({
1299
+ controlPlaneUrl,
1300
+ apiKey: safeCreds.apiKey,
1301
+ workspaceRoot: safeConfig.workspaceRoot,
1302
+ configured: safeConfig.harnesses,
1303
+ disconnected: safeConfig.disconnectedHarnesses,
1304
+ cliVersion: cliPkgVersion,
1305
+ });
1306
+ if (!inventory.reported)
1307
+ log.dim(`AI inventory report not sent (will retry): ${inventory.reason}`);
1308
+ }
1309
+ // Every Nth poll, capture the rules files that changed for the config
1310
+ // history. Content goes only when this poll's settings have
1311
+ // `configBodyUpload` on; otherwise path, hash, size and time.
1312
+ if (shouldCaptureThisIteration(pollIteration)) {
1164
1313
  try {
1165
- const report = await collectAgentReport({
1166
- workspaceRoot: process.cwd(),
1167
- harnessType,
1168
- configSynced: true,
1169
- dlpEnabled: true,
1170
- policyEnforced: true,
1171
- allowLocalVaults: syncConfig.settings?.allowLocalMemoryVaults,
1172
- });
1173
- await reportAgent(controlPlaneUrl, safeCreds.apiKey, safeCreds.workspaceId, report);
1174
- await startHarnessSession({
1314
+ await captureAndUpload({
1175
1315
  controlPlaneUrl,
1176
1316
  apiKey: safeCreds.apiKey,
1177
1317
  workspaceId: safeCreds.workspaceId,
1178
- harnessType,
1179
- workspaceRoot: process.cwd(),
1180
- ...(proxyInstanceId ? { proxyInstanceId } : {}),
1318
+ workspaceRoot: safeConfig.workspaceRoot,
1319
+ harnesses: safeConfig.harnesses,
1320
+ includeContent: syncConfig.settings?.configBodyUpload === true,
1321
+ governanceInputs,
1181
1322
  });
1182
1323
  }
1183
1324
  catch (err) {
1184
- // Per-harness isolation is deliberate: one harness failing to register
1185
- // must not stop the others or abort the poll iteration. Silence was
1186
- // not deliberate — fetchConfig already succeeded above, so a failure
1187
- // here means the control plane accepted the config read and rejected
1188
- // the agent report, which is exactly the "dashboard graph is empty"
1189
- // symptom this loop was added to fix. Report it so it is diagnosable.
1190
- log.dim(`Agent report/session for harness '${harnessType}' failed: ${err instanceof Error ? err.message : String(err)}`);
1325
+ log.dim(`Config capture failed (will retry): ${err instanceof Error ? err.message : String(err)}`);
1191
1326
  }
1192
1327
  }
1193
1328
  // Run compliance probes on each iteration
@@ -1197,6 +1332,7 @@ export async function runConnect(opts) {
1197
1332
  log.error(`Sync iteration failed: ${err instanceof Error ? err.message : String(err)}`);
1198
1333
  log.dim(`Retrying in ${pollInterval / 1000}s...`);
1199
1334
  }
1335
+ pollIteration++;
1200
1336
  // Sleep until next interval (AbortSignal-aware)
1201
1337
  await new Promise((resolve) => {
1202
1338
  const timer = setTimeout(resolve, pollInterval);