@intutic/sync-daemon 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 (288) hide show
  1. package/README.md +1 -1
  2. package/dist/agentReporter.d.ts +24 -11
  3. package/dist/agentReporter.d.ts.map +1 -1
  4. package/dist/agentReporter.js +20 -13
  5. package/dist/agentReporter.js.map +1 -1
  6. package/dist/configReader.d.ts +45 -25
  7. package/dist/configReader.d.ts.map +1 -1
  8. package/dist/configReader.js +70 -46
  9. package/dist/configReader.js.map +1 -1
  10. package/dist/configWriter.d.ts +32 -45
  11. package/dist/configWriter.d.ts.map +1 -1
  12. package/dist/configWriter.js +52 -686
  13. package/dist/configWriter.js.map +1 -1
  14. package/dist/disconnect/harnesses.d.ts +29 -0
  15. package/dist/disconnect/harnesses.d.ts.map +1 -0
  16. package/dist/disconnect/harnesses.js +805 -0
  17. package/dist/disconnect/harnesses.js.map +1 -0
  18. package/dist/disconnect/index.d.ts +25 -0
  19. package/dist/disconnect/index.d.ts.map +1 -0
  20. package/dist/disconnect/index.js +92 -0
  21. package/dist/disconnect/index.js.map +1 -0
  22. package/dist/disconnect/mcp.d.ts +81 -0
  23. package/dist/disconnect/mcp.d.ts.map +1 -0
  24. package/dist/disconnect/mcp.js +354 -0
  25. package/dist/disconnect/mcp.js.map +1 -0
  26. package/dist/disconnect/originals.d.ts +88 -0
  27. package/dist/disconnect/originals.d.ts.map +1 -0
  28. package/dist/disconnect/originals.js +257 -0
  29. package/dist/disconnect/originals.js.map +1 -0
  30. package/dist/disconnect/plan.d.ts +175 -0
  31. package/dist/disconnect/plan.d.ts.map +1 -0
  32. package/dist/disconnect/plan.js +580 -0
  33. package/dist/disconnect/plan.js.map +1 -0
  34. package/dist/disconnect/recognise.d.ts +43 -0
  35. package/dist/disconnect/recognise.d.ts.map +1 -0
  36. package/dist/disconnect/recognise.js +108 -0
  37. package/dist/disconnect/recognise.js.map +1 -0
  38. package/dist/disconnect/tomlLines.d.ts +39 -0
  39. package/dist/disconnect/tomlLines.d.ts.map +1 -0
  40. package/dist/disconnect/tomlLines.js +105 -0
  41. package/dist/disconnect/tomlLines.js.map +1 -0
  42. package/dist/harness/aiderConfigMerger.d.ts +29 -8
  43. package/dist/harness/aiderConfigMerger.d.ts.map +1 -1
  44. package/dist/harness/aiderConfigMerger.js +63 -12
  45. package/dist/harness/aiderConfigMerger.js.map +1 -1
  46. package/dist/harness/antigravityCliHooks.d.ts +61 -0
  47. package/dist/harness/antigravityCliHooks.d.ts.map +1 -0
  48. package/dist/harness/antigravityCliHooks.js +207 -0
  49. package/dist/harness/antigravityCliHooks.js.map +1 -0
  50. package/dist/harness/antigravityHooks.d.ts +12 -2
  51. package/dist/harness/antigravityHooks.d.ts.map +1 -1
  52. package/dist/harness/antigravityHooks.js +38 -16
  53. package/dist/harness/antigravityHooks.js.map +1 -1
  54. package/dist/harness/antigravityProducts.d.ts +40 -0
  55. package/dist/harness/antigravityProducts.d.ts.map +1 -0
  56. package/dist/harness/antigravityProducts.js +109 -0
  57. package/dist/harness/antigravityProducts.js.map +1 -0
  58. package/dist/harness/claudeAgentsMd.d.ts +29 -0
  59. package/dist/harness/claudeAgentsMd.d.ts.map +1 -0
  60. package/dist/harness/claudeAgentsMd.js +80 -0
  61. package/dist/harness/claudeAgentsMd.js.map +1 -0
  62. package/dist/harness/claudeCodeHooks.d.ts +6 -6
  63. package/dist/harness/claudeCodeHooks.d.ts.map +1 -1
  64. package/dist/harness/claudeCodeHooks.js +115 -28
  65. package/dist/harness/claudeCodeHooks.js.map +1 -1
  66. package/dist/harness/claudeProjectApproval.d.ts +53 -0
  67. package/dist/harness/claudeProjectApproval.d.ts.map +1 -0
  68. package/dist/harness/claudeProjectApproval.js +159 -0
  69. package/dist/harness/claudeProjectApproval.js.map +1 -0
  70. package/dist/harness/clineHooks.d.ts +0 -1
  71. package/dist/harness/clineHooks.d.ts.map +1 -1
  72. package/dist/harness/clineHooks.js +13 -4
  73. package/dist/harness/clineHooks.js.map +1 -1
  74. package/dist/harness/codexConfigMerger.d.ts.map +1 -1
  75. package/dist/harness/codexConfigMerger.js +4 -0
  76. package/dist/harness/codexConfigMerger.js.map +1 -1
  77. package/dist/harness/codexHooks.d.ts +2 -0
  78. package/dist/harness/codexHooks.d.ts.map +1 -1
  79. package/dist/harness/codexHooks.js +15 -4
  80. package/dist/harness/codexHooks.js.map +1 -1
  81. package/dist/harness/continueConfigMerger.d.ts.map +1 -1
  82. package/dist/harness/continueConfigMerger.js +4 -0
  83. package/dist/harness/continueConfigMerger.js.map +1 -1
  84. package/dist/harness/cursorHooks.d.ts +5 -2
  85. package/dist/harness/cursorHooks.d.ts.map +1 -1
  86. package/dist/harness/cursorHooks.js +12 -3
  87. package/dist/harness/cursorHooks.js.map +1 -1
  88. package/dist/harness/dshHooks.d.ts +5 -5
  89. package/dist/harness/dshHooks.d.ts.map +1 -1
  90. package/dist/harness/dshHooks.js +12 -5
  91. package/dist/harness/dshHooks.js.map +1 -1
  92. package/dist/harness/gateArtifacts.d.ts +35 -0
  93. package/dist/harness/gateArtifacts.d.ts.map +1 -0
  94. package/dist/harness/gateArtifacts.js +88 -0
  95. package/dist/harness/gateArtifacts.js.map +1 -0
  96. package/dist/harness/gateBody.d.ts +195 -69
  97. package/dist/harness/gateBody.d.ts.map +1 -1
  98. package/dist/harness/gateBody.js +847 -182
  99. package/dist/harness/gateBody.js.map +1 -1
  100. package/dist/harness/gateSightings.d.ts +47 -0
  101. package/dist/harness/gateSightings.d.ts.map +1 -0
  102. package/dist/harness/gateSightings.js +159 -0
  103. package/dist/harness/gateSightings.js.map +1 -0
  104. package/dist/harness/githubCopilotHooks.d.ts.map +1 -1
  105. package/dist/harness/githubCopilotHooks.js +11 -10
  106. package/dist/harness/githubCopilotHooks.js.map +1 -1
  107. package/dist/harness/gooseHardener.d.ts +0 -1
  108. package/dist/harness/gooseHardener.d.ts.map +1 -1
  109. package/dist/harness/gooseHardener.js +1 -1
  110. package/dist/harness/gooseHardener.js.map +1 -1
  111. package/dist/harness/gooseHooks.d.ts +29 -1
  112. package/dist/harness/gooseHooks.d.ts.map +1 -1
  113. package/dist/harness/gooseHooks.js +107 -69
  114. package/dist/harness/gooseHooks.js.map +1 -1
  115. package/dist/harness/grokHooks.d.ts +2 -2
  116. package/dist/harness/grokHooks.d.ts.map +1 -1
  117. package/dist/harness/grokHooks.js +20 -18
  118. package/dist/harness/grokHooks.js.map +1 -1
  119. package/dist/harness/hermesHooks.d.ts +18 -8
  120. package/dist/harness/hermesHooks.d.ts.map +1 -1
  121. package/dist/harness/hermesHooks.js +55 -19
  122. package/dist/harness/hermesHooks.js.map +1 -1
  123. package/dist/harness/holdRedaction.d.ts +4 -1
  124. package/dist/harness/holdRedaction.d.ts.map +1 -1
  125. package/dist/harness/holdRedaction.js +32 -8
  126. package/dist/harness/holdRedaction.js.map +1 -1
  127. package/dist/harness/jetbrainsXmlConfig.d.ts +20 -0
  128. package/dist/harness/jetbrainsXmlConfig.d.ts.map +1 -1
  129. package/dist/harness/jetbrainsXmlConfig.js +39 -18
  130. package/dist/harness/jetbrainsXmlConfig.js.map +1 -1
  131. package/dist/harness/mcpAutoWrite.d.ts +62 -25
  132. package/dist/harness/mcpAutoWrite.d.ts.map +1 -1
  133. package/dist/harness/mcpAutoWrite.js +406 -95
  134. package/dist/harness/mcpAutoWrite.js.map +1 -1
  135. package/dist/harness/museHooks.d.ts +3 -3
  136. package/dist/harness/museHooks.d.ts.map +1 -1
  137. package/dist/harness/museHooks.js +15 -7
  138. package/dist/harness/museHooks.js.map +1 -1
  139. package/dist/harness/n8nHooks.d.ts +0 -1
  140. package/dist/harness/n8nHooks.d.ts.map +1 -1
  141. package/dist/harness/n8nHooks.js +4 -1
  142. package/dist/harness/n8nHooks.js.map +1 -1
  143. package/dist/harness/openCodeHooks.d.ts +2 -2
  144. package/dist/harness/openCodeHooks.d.ts.map +1 -1
  145. package/dist/harness/openCodeHooks.js +9 -8
  146. package/dist/harness/openCodeHooks.js.map +1 -1
  147. package/dist/harness/openWebuiHooks.d.ts +0 -1
  148. package/dist/harness/openWebuiHooks.d.ts.map +1 -1
  149. package/dist/harness/openWebuiHooks.js +11 -3
  150. package/dist/harness/openWebuiHooks.js.map +1 -1
  151. package/dist/harness/openclawHooks.d.ts +75 -18
  152. package/dist/harness/openclawHooks.d.ts.map +1 -1
  153. package/dist/harness/openclawHooks.js +214 -160
  154. package/dist/harness/openclawHooks.js.map +1 -1
  155. package/dist/harness/openhandsHooks.d.ts +37 -11
  156. package/dist/harness/openhandsHooks.d.ts.map +1 -1
  157. package/dist/harness/openhandsHooks.js +148 -97
  158. package/dist/harness/openhandsHooks.js.map +1 -1
  159. package/dist/harness/piHooks.d.ts +52 -17
  160. package/dist/harness/piHooks.d.ts.map +1 -1
  161. package/dist/harness/piHooks.js +177 -163
  162. package/dist/harness/piHooks.js.map +1 -1
  163. package/dist/harness/protectedPaths.d.ts +107 -25
  164. package/dist/harness/protectedPaths.d.ts.map +1 -1
  165. package/dist/harness/protectedPaths.js +342 -48
  166. package/dist/harness/protectedPaths.js.map +1 -1
  167. package/dist/harness/rulesSection.d.ts +62 -0
  168. package/dist/harness/rulesSection.d.ts.map +1 -0
  169. package/dist/harness/rulesSection.js +145 -0
  170. package/dist/harness/rulesSection.js.map +1 -0
  171. package/dist/harness/windsurfHooks.d.ts +18 -3
  172. package/dist/harness/windsurfHooks.d.ts.map +1 -1
  173. package/dist/harness/windsurfHooks.js +41 -8
  174. package/dist/harness/windsurfHooks.js.map +1 -1
  175. package/dist/harness/windsurfJetBrainsProxy.d.ts +1 -1
  176. package/dist/harness/windsurfJetBrainsProxy.d.ts.map +1 -1
  177. package/dist/harness/windsurfJetBrainsProxy.js +5 -1
  178. package/dist/harness/windsurfJetBrainsProxy.js.map +1 -1
  179. package/dist/index.d.ts +21 -20
  180. package/dist/index.d.ts.map +1 -1
  181. package/dist/index.js +19 -19
  182. package/dist/index.js.map +1 -1
  183. package/dist/inventory.d.ts +53 -0
  184. package/dist/inventory.d.ts.map +1 -0
  185. package/dist/inventory.js +133 -0
  186. package/dist/inventory.js.map +1 -0
  187. package/dist/lib/complianceProbes.d.ts +2 -1
  188. package/dist/lib/complianceProbes.d.ts.map +1 -1
  189. package/dist/lib/complianceProbes.js +1 -1
  190. package/dist/lib/complianceProbes.js.map +1 -1
  191. package/dist/lib/decisionsDigest.d.ts +35 -17
  192. package/dist/lib/decisionsDigest.d.ts.map +1 -1
  193. package/dist/lib/decisionsDigest.js +106 -47
  194. package/dist/lib/decisionsDigest.js.map +1 -1
  195. package/dist/lib/egressPolicy.d.ts +2 -2
  196. package/dist/lib/egressPolicy.js +2 -2
  197. package/dist/lib/mcpRegistryPy.d.ts +11 -0
  198. package/dist/lib/mcpRegistryPy.d.ts.map +1 -0
  199. package/dist/lib/mcpRegistryPy.js +173 -0
  200. package/dist/lib/mcpRegistryPy.js.map +1 -0
  201. package/dist/lib/phrasesPy.d.ts +11 -0
  202. package/dist/lib/phrasesPy.d.ts.map +1 -0
  203. package/dist/lib/phrasesPy.js +351 -0
  204. package/dist/lib/phrasesPy.js.map +1 -0
  205. package/dist/lib/policySnapshot.d.ts +63 -9
  206. package/dist/lib/policySnapshot.d.ts.map +1 -1
  207. package/dist/lib/policySnapshot.js +269 -45
  208. package/dist/lib/policySnapshot.js.map +1 -1
  209. package/dist/lib/processPoller.d.ts.map +1 -1
  210. package/dist/lib/processPoller.js +9 -2
  211. package/dist/lib/processPoller.js.map +1 -1
  212. package/dist/lib/regexLinearity.d.ts +43 -0
  213. package/dist/lib/regexLinearity.d.ts.map +1 -0
  214. package/dist/lib/regexLinearity.js +275 -0
  215. package/dist/lib/regexLinearity.js.map +1 -0
  216. package/dist/lib/runtimeEnv.d.ts +1 -3
  217. package/dist/lib/runtimeEnv.d.ts.map +1 -1
  218. package/dist/lib/runtimeEnv.js +0 -2
  219. package/dist/lib/runtimeEnv.js.map +1 -1
  220. package/dist/lib/sequencePy.d.ts +10 -0
  221. package/dist/lib/sequencePy.d.ts.map +1 -0
  222. package/dist/lib/sequencePy.js +102 -0
  223. package/dist/lib/sequencePy.js.map +1 -0
  224. package/dist/sessionReporter.d.ts +18 -12
  225. package/dist/sessionReporter.d.ts.map +1 -1
  226. package/dist/sessionReporter.js +37 -24
  227. package/dist/sessionReporter.js.map +1 -1
  228. package/dist/skillWriter.d.ts +23 -5
  229. package/dist/skillWriter.d.ts.map +1 -1
  230. package/dist/skillWriter.js +159 -16
  231. package/dist/skillWriter.js.map +1 -1
  232. package/dist/syncCycle.d.ts +142 -0
  233. package/dist/syncCycle.d.ts.map +1 -0
  234. package/dist/syncCycle.js +327 -0
  235. package/dist/syncCycle.js.map +1 -0
  236. package/dist/trajectoryMonitor.d.ts +2 -4
  237. package/dist/trajectoryMonitor.d.ts.map +1 -1
  238. package/dist/trajectoryMonitor.js +2 -5
  239. package/dist/trajectoryMonitor.js.map +1 -1
  240. package/dist/watcher/driftWatcher.d.ts +0 -1
  241. package/dist/watcher/driftWatcher.d.ts.map +1 -1
  242. package/dist/watcher/driftWatcher.js +1 -2
  243. package/dist/watcher/driftWatcher.js.map +1 -1
  244. package/dist/watcher/settingsGuard.d.ts +36 -7
  245. package/dist/watcher/settingsGuard.d.ts.map +1 -1
  246. package/dist/watcher/settingsGuard.js +397 -60
  247. package/dist/watcher/settingsGuard.js.map +1 -1
  248. package/dist/watcher/vscodeHookSettings.d.ts +30 -0
  249. package/dist/watcher/vscodeHookSettings.d.ts.map +1 -0
  250. package/dist/watcher/vscodeHookSettings.js +170 -0
  251. package/dist/watcher/vscodeHookSettings.js.map +1 -0
  252. package/dist/wsClient.d.ts +0 -1
  253. package/dist/wsClient.d.ts.map +1 -1
  254. package/dist/wsClient.js +0 -1
  255. package/dist/wsClient.js.map +1 -1
  256. package/package.json +5 -5
  257. package/dist/harness/claudeDesktopHooks.d.ts +0 -30
  258. package/dist/harness/claudeDesktopHooks.d.ts.map +0 -1
  259. package/dist/harness/claudeDesktopHooks.js +0 -254
  260. package/dist/harness/claudeDesktopHooks.js.map +0 -1
  261. package/dist/harness/continueHooks.d.ts +0 -63
  262. package/dist/harness/continueHooks.d.ts.map +0 -1
  263. package/dist/harness/continueHooks.js +0 -247
  264. package/dist/harness/continueHooks.js.map +0 -1
  265. package/dist/harness/gateKind.d.ts +0 -164
  266. package/dist/harness/gateKind.d.ts.map +0 -1
  267. package/dist/harness/gateKind.js +0 -144
  268. package/dist/harness/gateKind.js.map +0 -1
  269. package/dist/harness/rooCodeHooks.d.ts +0 -31
  270. package/dist/harness/rooCodeHooks.d.ts.map +0 -1
  271. package/dist/harness/rooCodeHooks.js +0 -286
  272. package/dist/harness/rooCodeHooks.js.map +0 -1
  273. package/dist/hashReporter.d.ts +0 -33
  274. package/dist/hashReporter.d.ts.map +0 -1
  275. package/dist/hashReporter.js +0 -69
  276. package/dist/hashReporter.js.map +0 -1
  277. package/dist/integrityStore.d.ts +0 -35
  278. package/dist/integrityStore.d.ts.map +0 -1
  279. package/dist/integrityStore.js +0 -71
  280. package/dist/integrityStore.js.map +0 -1
  281. package/dist/lib/gitWorktrees.d.ts +0 -140
  282. package/dist/lib/gitWorktrees.d.ts.map +0 -1
  283. package/dist/lib/gitWorktrees.js +0 -191
  284. package/dist/lib/gitWorktrees.js.map +0 -1
  285. package/dist/syncLoop.d.ts +0 -172
  286. package/dist/syncLoop.d.ts.map +0 -1
  287. package/dist/syncLoop.js +0 -878
  288. package/dist/syncLoop.js.map +0 -1
@@ -6,10 +6,11 @@
6
6
  * capture and the Jira/Linear/GitHub task-context cascade) had no production
7
7
  * caller: sessions only ever existed as synthetic `ssp_` rows minted
8
8
  * server-side. The daemon knows everything the route wants — workspace root,
9
- * harness, git branch and commit — so it reports them once per harness per
10
- * daemon run when the harness is first detected.
9
+ * harness, git repository, branch and commit — so it reports them when the
10
+ * harness is first detected, and again whenever the repository, branch or
11
+ * HEAD commit changes.
11
12
  *
12
- * WHICH row it reports them onto (TD-231, Wave 5.6): when a local proxy is
13
+ * WHICH row it reports them onto (Wave 5.6): when a local proxy is
13
14
  * reachable, the caller passes that process's `proxyInstanceId` and the
14
15
  * control plane puts the context on the proxy's OWN row — the
15
16
  * `ssp_<ws>_<harness>_<instance>` session it derives for that process's
@@ -26,17 +27,20 @@
26
27
  * (PATCH /api/v1/sessions/:id/end).
27
28
  *
28
29
  * Dedupe lives here (module state keyed workspace+harness+instance), so
29
- * callers — `startSyncLoop`'s iteration as well as the CLI's inline connect
30
- * loop — can call `startHarnessSession` every iteration and only the first
31
- * one POSTs. Git context is captured once per key, not on a branch switch.
32
- * Everything is best-effort: a dead control plane costs a warning, never the
33
- * sync loop.
30
+ * callers (`reportHarnessAgents`, every `intutic connect` cycle) can call
31
+ * `startHarnessSession` every iteration. Git is read on every call and the
32
+ * session is POSTed again only when it changed: the control plane copies the
33
+ * session's git context onto each trace as it records it, which is what makes
34
+ * cost per branch and per commit possible, so a stale commit would file a
35
+ * call under the wrong one. A re-POST updates the same row. Everything is
36
+ * best-effort: a dead control plane costs a warning, never the sync loop.
34
37
  *
35
38
  * @module
36
39
  */
37
40
  import { execFile } from 'node:child_process';
38
41
  import { promisify } from 'node:util';
39
42
  import { newIso } from '@intutic/id';
43
+ import { normalizeGitRemote } from '@intutic/shared-types';
40
44
  const execFileP = promisify(execFile);
41
45
  /**
42
46
  * sessionIds this daemon run opened AND owns the lifecycle of — the `ses_`
@@ -48,8 +52,12 @@ const openSessions = new Map();
48
52
  * `${workspaceId}:${harness}:${proxyInstanceId}`. Dedupe only — never ended.
49
53
  */
50
54
  const registeredProxyRows = new Map();
51
- /** Keys with a POST in flight or already attempted (success or not) — one try per run. */
52
- const attempted = new Set();
55
+ /**
56
+ * The git context last POSTed (or attempted, success or not) per key. One try
57
+ * per key and git state per run: a failing control plane is not retried every
58
+ * cycle, but a branch switch or a new commit is reported.
59
+ */
60
+ const attempted = new Map();
53
61
  /** Best-effort git context from the workspace root. */
54
62
  export async function readGitInfo(workspaceRoot) {
55
63
  const git = async (...args) => {
@@ -62,28 +70,31 @@ export async function readGitInfo(workspaceRoot) {
62
70
  return undefined;
63
71
  }
64
72
  };
65
- const [branchName, commitHash, commitMessage] = await Promise.all([
73
+ const [remote, branchName, commitHash, commitMessage] = await Promise.all([
74
+ git('remote', 'get-url', 'origin'),
66
75
  git('rev-parse', '--abbrev-ref', 'HEAD'),
67
76
  git('rev-parse', 'HEAD'),
68
77
  git('log', '-1', '--format=%s'),
69
78
  ]);
70
- return { branchName, commitHash, commitMessage };
79
+ // Normalised here, before it is held anywhere: a remote can embed a token.
80
+ const repoUrl = remote ? (normalizeGitRemote(remote) ?? undefined) : undefined;
81
+ return { repoUrl, branchName, commitHash, commitMessage };
71
82
  }
72
83
  /**
73
- * Report a harness session once per run — onto the local proxy's own row when
74
- * `proxyInstanceId` is given (see the module doc), else as a new `ses_` row.
75
- * Returns the sessionId when one is known (new or previously created).
84
+ * Report a harness session — onto the local proxy's own row when
85
+ * `proxyInstanceId` is given (see the module doc), else as a `ses_` row — and
86
+ * report it again when its git context changes. Returns the sessionId when one
87
+ * is known (new or previously created).
76
88
  */
77
89
  export async function startHarnessSession(opts) {
78
90
  const key = `${opts.workspaceId}:${opts.harnessType}:${opts.proxyInstanceId ?? ''}`;
79
91
  const existing = opts.proxyInstanceId ? registeredProxyRows.get(key) : openSessions.get(key);
80
- if (existing)
81
- return existing;
82
- if (attempted.has(key))
83
- return null;
84
- attempted.add(key);
92
+ const git = await readGitInfo(opts.workspaceRoot);
93
+ const gitState = JSON.stringify([git.repoUrl, git.branchName, git.commitHash]);
94
+ if (attempted.get(key) === gitState)
95
+ return existing ?? null;
96
+ attempted.set(key, gitState);
85
97
  try {
86
- const git = await readGitInfo(opts.workspaceRoot);
87
98
  const res = await fetch(`${opts.controlPlaneUrl}/api/v1/sessions`, {
88
99
  method: 'POST',
89
100
  headers: { 'Content-Type': 'application/json', Authorization: `Bearer ${opts.apiKey}` },
@@ -92,6 +103,8 @@ export async function startHarnessSession(opts) {
92
103
  harnessType: opts.harnessType,
93
104
  ...(opts.agentRole ? { agentRole: opts.agentRole } : {}),
94
105
  ...(opts.proxyInstanceId ? { proxyInstanceId: opts.proxyInstanceId } : {}),
106
+ // A `ses_` row this run already opened is updated, not opened again.
107
+ ...(existing && !opts.proxyInstanceId ? { sessionId: existing } : {}),
95
108
  ...git,
96
109
  reportedAt: newIso(),
97
110
  }),
@@ -99,7 +112,7 @@ export async function startHarnessSession(opts) {
99
112
  });
100
113
  if (!res.ok) {
101
114
  console.warn(`[sync-daemon] startHarnessSession failed for ${opts.harnessType}: ${res.status}`);
102
- return null;
115
+ return existing ?? null;
103
116
  }
104
117
  const body = (await res.json());
105
118
  if (body.sessionId) {
@@ -107,11 +120,11 @@ export async function startHarnessSession(opts) {
107
120
  (opts.proxyInstanceId ? registeredProxyRows : openSessions).set(key, body.sessionId);
108
121
  return body.sessionId;
109
122
  }
110
- return null;
123
+ return existing ?? null;
111
124
  }
112
125
  catch (err) {
113
126
  console.warn(`[sync-daemon] startHarnessSession error for ${opts.harnessType}:`, err instanceof Error ? err.message : err);
114
- return null;
127
+ return existing ?? null;
115
128
  }
116
129
  }
117
130
  /**
@@ -1 +1 @@
1
- {"version":3,"file":"sessionReporter.js","sourceRoot":"","sources":["../src/sessionReporter.ts"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"sessionReporter.js","sourceRoot":"","sources":["../src/sessionReporter.ts"],"names":[],"mappings":"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"}
@@ -5,17 +5,35 @@
5
5
  * written only when absent (write-if-missing), never overwrite user edits,
6
6
  * and are deliberately not tracked by the drift watcher / IntegrityStore.
7
7
  *
8
- * The skill content is embedded as a constant because the daemon runs
9
- * outside the monorepo. The canonical source is
10
- * `.agents/skills/intutic-rule-author/SKILL.md` in the open-core repo; a
11
- * test asserts the two stay identical.
8
+ * Two skills ship: the rule-author skill, which teaches an agent to extend
9
+ * governance, and the Kitkat skill, which teaches it to operate under it —
10
+ * what each refusal code means, what a hold is and who approves it. Every
11
+ * connected agent meets refusals, so Kitkat is written alongside the rule
12
+ * author rather than left as a manual download.
13
+ *
14
+ * Each is written as a file Intutic owns (disconnect/originals.ts):
15
+ * `intutic disconnect` deletes it again, unless it was edited since, and
16
+ * never touches a copy that was already there.
17
+ *
18
+ * The skill content is embedded as constants because the daemon runs outside
19
+ * the monorepo. The canonical sources are `.agents/skills/<name>/SKILL.md` in
20
+ * the open-core repo; a test asserts each pair stays identical.
12
21
  *
13
22
  * @module
14
23
  */
15
24
  /** Workspace-relative path the rule-author skill is written to. */
16
25
  export declare const RULE_AUTHOR_SKILL_PATH: string;
17
26
  /** Embedded copy of .agents/skills/intutic-rule-author/SKILL.md (canonical). */
18
- export declare const RULE_AUTHOR_SKILL = "---\nname: intutic-rule-author\ndescription: Convert natural-language business rules into compiled WASM governance policies enforced by the local Intutic proxy \u2014 author, compile, dry-run, and install rules without a control plane.\n---\n\n# \uD83D\uDCDC Rule Author \u2014 Business Rules as Enforced WASM Policies\n\nUse this skill whenever the user states a rule that AI agent traffic must obey \u2014 \"block any agent call that touches the prod database\", \"kill requests once the session budget drops below $1\", \"never allow shell tool calls containing rm -rf\". You convert the rule into an AssemblyScript policy, compile it to WebAssembly, dry-run it against mock contexts, and install it into the local Intutic proxy. The proxy hot-loads it within ~5 seconds and enforces it on every intercepted LLM request.\n\n**The other way you get here is `/fix`.** That proxy command scores the workspace's governance posture and, when it finds no custom policy, recommends: *\"Add a WASM rule under `~/.intutic/wasm` to codify a custom guardrail.\"* When that recommendation is the prompt, the rule to author is the gap `/fix` found \u2014 ask the user which specific behaviour they want blocked rather than writing a generic rule, then run `/fix` again after installing to confirm the posture score moved. If it did not, the rule is not loading: check `intutic policy list-local` and the sandbox constraints in \u00A72, since a rule that violates them is silently disabled.\n\n## 1. The contract your rule sees\n\nThe proxy calls `evaluate(offset, len)` with a JSON-serialized `RequestContext`:\n\n```jsonc\n{\n \"session_id\": \"sess_x\", \"workspace_id\": \"ws_x\", \"virtual_key_prefix\": \"vk_x\",\n \"model\": \"claude-3-5-sonnet\",\n \"tools\": [{ \"name\": \"bash\", \"description\": \"run shell commands\" }],\n \"tool_calls\": [{ \"id\": \"t1\", \"name\": \"bash\", \"arguments\": {} }],\n \"estimated_input_tokens\": 1200,\n \"budget_remaining_usd\": 4.25,\n \"risk_tier\": \"Low\",\n \"dlp_findings\": [{ \"category\": \"secret\", \"pattern_name\": \"aws_access_key\", \"action\": \"redact\", \"offset\": 0, \"length\": 20 }],\n \"tool_sequence\": [\"read_file\", \"bash\"]\n}\n```\n\n`risk_tier` is one of `Low | Medium | High | Critical`. Return an `i32` verdict: `0` = ALLOW, `1` = BLOCK, `3` = REASK (refuse this attempt, tell the agent why, let it retry). `2` is deprecated \u2014 the guest never receives the request body, so redaction was never expressible; the proxy maps it to a block. Prefer `3` over `1` for any finding that is a pattern match, because pattern matches produce false positives.\n\n## 2. Author\n\nStart from the `@intutic/wasm-sdk` template (`packages/wasm-sdk/` in the open-core repo). Add one function per business rule and call it from `runRules()` in `assembly/index.ts`.\n\nSandbox constraints \u2014 violating them silently disables the rule (the proxy **fails open**):\n\n- 5 ms wall-clock budget, 1,000,000 fuel, 16 MB memory.\n- Keep logic simple: no unbounded loops over `arguments`, no recursion, no I/O (none exists in the sandbox).\n- Host imports are limited to `env.log_info`, `env.trace`, `env.abort` and `env.read_referenced_file` (reads the bytes of a file the host already resolved before the sandbox existed \u2014 not a filesystem import, and it cannot be used to browse). A rule importing anything else cannot link, so it is refused at `intutic policy install` and refused again when the proxy loads it.\n- **`Math.random()` is unavailable.** AssemblyScript compiles it to an `env.seed` import the proxy does not provide. This list previously named `env.seed`, and a rule using randomness installed clean and then failed to link on every request \u2014 enforcing nothing, silently. Beyond the missing import, a verdict that samples is not a verdict: the same request would get different answers, and the audit trail would not explain either.\n\n## 3. Compile\n\n```bash\nintutic policy compile --src assembly/index.ts --out build/rule.wasm\n```\n\nRequires AssemblyScript in the project: `pnpm add -D assemblyscript assemblyscript-json`.\n\n## 4. Dry-run \u2014 required before install\n\nAuthor BOTH mock contexts and verify both outcomes before installing:\n\n```bash\nintutic policy test --wasm build/rule.wasm --mock should-block.json\nintutic policy test --wasm build/rule.wasm --mock should-allow.json\n```\n\nThe first must report BLOCK, the second ALLOW. A rule that blocks everything is as broken as one that blocks nothing.\n\n## 5. Install\n\n```bash\nintutic policy install --wasm build/rule.wasm --name block-prod-db --priority 50\nintutic policy list-local\n```\n\nRules install to `~/.intutic/wasm/` as `NN_name.wasm` (lower `NN` runs first; default 100). The proxy picks up changes within ~5 s on the next request \u2014 no restart needed.\n\n## 6. Guardrails\n\n- **Never embed secrets** (API keys, tokens, passwords) in rule sources or mock contexts \u2014 `intutic skill audit` flags them.\n- **Local rules cannot weaken central rules.** Verdicts merge most-restrictive-wins: a KILL from any rule \u2014 local or centrally synced \u2014 ends the request. \"Allow-override\" rules are no-ops by design; do not author them.\n- Keep rule sources (`assembly/*.ts`, mock JSON files) in version control alongside the workspace.\n";
27
+ export declare const RULE_AUTHOR_SKILL = "---\nname: intutic-rule-author\ndescription: Convert natural-language business rules into compiled WASM governance policies enforced by the local Intutic proxy \u2014 author, compile, dry-run, and install rules without a control plane.\n---\n\n# \uD83D\uDCDC Rule Author \u2014 Business Rules as Enforced WASM Policies\n\nUse this skill whenever the user states a rule that AI agent traffic must obey \u2014 \"block any agent call that touches the prod database\", \"kill requests once the session budget drops below $1\", \"never allow shell tool calls containing rm -rf\". You convert the rule into an AssemblyScript policy, compile it to WebAssembly, dry-run it against mock contexts, and install it into the local Intutic proxy. The proxy hot-loads it within ~5 seconds and enforces it on every intercepted LLM request.\n\n**The other way you get here is `/fix`.** That proxy command scores the workspace's governance posture and, when it finds no custom policy, recommends: *\"Add a WASM rule under `~/.intutic/wasm` to codify a custom guardrail.\"* When that recommendation is the prompt, the rule to author is the gap `/fix` found \u2014 ask the user which specific behaviour they want blocked rather than writing a generic rule, then run `/fix` again after installing to confirm the posture score moved. If it did not, the rule is not loading: check `intutic policy list-local` and the sandbox constraints in \u00A72, since a rule that violates them is silently disabled.\n\n## 1. The contract your rule sees\n\nThe proxy calls `evaluate(offset, len)` with a JSON-serialized `RequestContext`:\n\n```jsonc\n{\n \"session_id\": \"sess_x\", \"workspace_id\": \"ws_x\", \"virtual_key_prefix\": \"vk_x\",\n \"model\": \"claude-3-5-sonnet\",\n \"tools\": [{ \"name\": \"bash\", \"description\": \"run shell commands\" }],\n \"tool_calls\": [{ \"id\": \"t1\", \"name\": \"bash\", \"arguments\": {} }],\n \"estimated_input_tokens\": 1200,\n \"budget_remaining_usd\": 4.25,\n \"risk_tier\": \"Low\",\n \"dlp_findings\": [{ \"category\": \"secret\", \"pattern_name\": \"aws_access_key\", \"action\": \"redact\", \"offset\": 0, \"length\": 20 }],\n \"tool_sequence\": [\"read_file\", \"bash\"]\n}\n```\n\n`risk_tier` is one of `Low | Medium | High | Critical`. Return an `i32` verdict: `0` = ALLOW, `1` = BLOCK, `3` = REASK (refuse this attempt, tell the agent why, let it retry). `2` is deprecated \u2014 the guest never receives the request body, so redaction was never expressible; the proxy maps it to a block. Prefer `3` over `1` for any finding that is a pattern match, because pattern matches produce false positives.\n\n## 2. Author\n\nStart from the `@intutic/wasm-sdk` template (`packages/wasm-sdk/` in the open-core repo). Add one function per business rule and call it from `runRules()` in `assembly/index.ts`.\n\nSandbox constraints \u2014 a rule that breaks one reaches no verdict, and every call it could not judge is refused with `GOVERNANCE_UNAVAILABLE`, whatever the proxy's fail setting:\n\n- 1,000,000 fuel (instructions), the limit to write against; a 1 s wall-clock backstop; 16 MB memory.\n- Keep logic simple: no unbounded loops over `arguments`, no recursion, no I/O (none exists in the sandbox).\n- Host imports are limited to `env.log_info`, `env.trace`, `env.abort` and `env.read_referenced_file` (reads the bytes of a file the host already resolved before the sandbox existed \u2014 not a filesystem import, and it cannot be used to browse). A rule importing anything else cannot link, so it is refused at `intutic policy install` and refused again when the proxy loads it.\n- **`Math.random()` is unavailable.** AssemblyScript compiles it to an `env.seed` import the proxy does not provide. This list previously named `env.seed`, and a rule using randomness installed clean and then failed to link on every request \u2014 enforcing nothing, silently. Beyond the missing import, a verdict that samples is not a verdict: the same request would get different answers, and the audit trail would not explain either.\n\n## 3. Compile\n\n```bash\nintutic policy compile --src assembly/index.ts --out build/rule.wasm\n```\n\nRequires AssemblyScript in the project: `pnpm add -D assemblyscript assemblyscript-json`.\n\n## 4. Dry-run \u2014 required before install\n\nAuthor BOTH mock contexts and verify both outcomes before installing:\n\n```bash\nintutic policy test --wasm build/rule.wasm --mock should-block.json\nintutic policy test --wasm build/rule.wasm --mock should-allow.json\n```\n\nThe first must report BLOCK, the second ALLOW. A rule that blocks everything is as broken as one that blocks nothing.\n\n## 5. Install\n\n```bash\nintutic policy install --wasm build/rule.wasm --name block-prod-db --priority 50\nintutic policy list-local\n```\n\nRules install to `~/.intutic/wasm/` as `NN_name.wasm` (lower `NN` runs first; default 100). The proxy picks up changes within ~5 s on the next request \u2014 no restart needed.\n\n## 6. Guardrails\n\n- **Never embed secrets** (API keys, tokens, passwords) in rule sources or mock contexts \u2014 `intutic skill audit` flags them.\n- **Local rules cannot weaken central rules.** Verdicts merge most-restrictive-wins: a KILL from any rule \u2014 local or centrally synced \u2014 ends the request. \"Allow-override\" rules are no-ops by design; do not author them.\n- Keep rule sources (`assembly/*.ts`, mock JSON files) in version control alongside the workspace.\n";
28
+ /** Workspace-relative path the Kitkat skill is written to. */
29
+ export declare const KITKAT_SKILL_PATH: string;
30
+ /** Embedded copy of .agents/skills/intutic-governance-kitkat/SKILL.md (canonical). */
31
+ export declare const KITKAT_SKILL = "---\nname: intutic-governance-kitkat\ndescription: Guide and execute Intutic governed developer workflows, ticket cost attributions, pre-flight token predictions, and safety policy interceptions using the Kitkat persona.\n---\n\n# \uD83D\uDC31 Kitkat \u2014 Intutic Agentic Governance Skill\n\nHello! I am **Kitkat**, your friendly agentic control plane assistant for the Intutic Governance Engine. I ensure that your AI agentic workflows are safe, structured, FinOps-optimized, and compliant with both global enterprise policies (Notion/Confluence) and local rules.\n\n---\n\n## \uD83D\uDEE0\uFE0F CLI Reference Catalog\n\nUse the `intutic` CLI commands to manage the daemon, inspect execution traces, or manage governed task loops in your workspace:\n\n### 1. Connection & Session Setup\n- **Authenticate:** `intutic login [--control-plane-url <url>]` (authenticates with your Intutic control plane).\n- **Initialize Workspace:** `intutic init` (detects the developer harnesses in the workspace, like Claude Code or Cursor, and records them for sync).\n- **Start Connection:** `intutic connect` (boots the local interceptor proxy, writes each recorded harness's rules and hooks, keeps them in sync with the control plane, and writes this skill and the rule-author skill into `.agents/skills/` when they are missing).\n- **Stop Governing:** `intutic disconnect [--harness <id>] [--dry-run] [--keep-login]` (undoes `connect` on this machine: restores every harness config it changed, removes its hooks, services and the skills it wrote, and logs out). Run it only when the user asks; `--dry-run` shows what would change.\n- **Offline Spend Sync:** The sync daemon automatically reconciles offline query consumption logs (`traces-*.jsonl`) and local budgets back to the control plane on reconnect.\n\n### 2. Traces & Auditing\n- **Check Budget:** `intutic budget` (displays overall monthly/daily spend limits, local spend caps, and active task loops).\n- **List Sessions:** `intutic traces list`\n- **Inspect Session Trace:** `intutic traces inspect <trace_id>`\n- **Push SOP Rules:** `intutic sops push <name>` (promotes local SOP rules to central repository).\n\n### 3. Governed Task Loops\n- **Start Task Loop:** `intutic loop start --name <name> [--budget <USD>] [--sops <indices_or_names>] [--auto-judge]`\n- **Execute Wrapped Agent:** `intutic loop exec --name <name> [--budget <USD>] [--sops <indices_or_names>] [--auto-judge] -- <command>`\n- **Complete/Kill Loop:** `intutic loop complete <loopRunId>` / `intutic loop kill <loopRunId>`\n- **List Active Loops:** `intutic loop list`\n\n---\n\n## \uD83D\uDD0C Interception & Command Prepends\n\nWhen interacting with LLM providers through the local proxy, prepend requests with `@intutic` (or `/intutic`) to control the active governance session:\n\n### 1. Cost & Task Attribution\n- **Initialize Picker:** `@intutic initialize`\n - *Description:* Pulls open tickets from linked task providers (Jira, Linear, GitHub Issues) and recommends ticket candidates for the session.\n- **Lock Session & Scope Rules:** `@intutic start [<ticket_id>] [--sops=<names_or_indices>] [--auto-judge]`\n - *Description:* Attributes token costs to a ticket (optional) and scopes active local SOP directories for governance.\n - *Examples:*\n * `@intutic start 1 --sops=3 --auto-judge`\n * `@intutic start --sops=security-rules --auto-judge`\n\n### 2. FinOps & Token Projections\n- **Predict Costs:** `@intutic predict <prompt>`\n - *Description:* Estimates input/output tokens and cost breakdown before submitting the request to the upstream model.\n- **Optimize Context:** `@intutic recommend`\n - *Description:* Provides recommendations on prompt adjustments and context reduction strategies to minimize token waste.\n\n### 3. Policy Compliance & E2E Judging\n- **Verify Prompt:** `@intutic verify <prompt>` (or `@intutic check <prompt>`)\n - *Description:* Audits the prompt pre-flight against active SOPs and guidelines, returning pass/fail verdicts and listing any violations before submitting requests to the LLM.\n- **Review Prompt:** `@intutic review <prompt>`\n - *Description:* Runs a deep heuristic prompt quality evaluation to grade clarity, specificity, and actionability.\n- **Judge Response:** `@intutic judge <prompt>`\n - *Description:* Submits the prompt to the upstream LLM and evaluates the response stream in parallel using LLM-as-a-judge compliance checkers.\n - *Note:* If no prompt is provided (e.g. `@intutic judge`), returns usage instructions.\n- **Inspect Status:** `@intutic status` (displays active session stats, average compliance, and spent budget).\n- **Inspect Budget:** `@intutic budget` (displays daily monitored limits and workspace progress).\n\n### 4. Posture & Prompt Enhancement\n- **Enhance & Diagnose:** `/fix <prompt>` (aliases: `@fix`, `/intutic-fix`, `@intutic fix`)\n - *Description:* Rewrites the prompt and scores the workspace's governance posture against OWASP categories from what the proxy can see locally \u2014 DLP settings, WASM rules, the SOPs on disk for the caller's role, skills and MCP servers. Returns the enhanced prompt plus concrete recommendations for whatever is missing.\n - *Note:* Blocking by default; it can be configured to pass the enhanced prompt through to the model instead. The recommendations are the handoff points into the rest of the toolchain \u2014 \"add a WASM rule\" is the `intutic-rule-author` skill's job, \"write a role SOP\" and \"enable DLP\" are handled here.\n- **Draw Guardrails:** `/draw` (aliases: `@draw`, `/intutic-draw`, `@intutic draw`)\n - *Description:* Renders a Mermaid diagram plus text summary of the agent's trajectory and the guardrails currently around it. Always blocking \u2014 a visualization has nothing to forward upstream.\n\n---\n\n## \uD83D\uDEA6 When a Call Is Refused or Held\n\nIntutic names every refusal with a code: an SDK raises it (`ClawdeBlockedError.code`, `IntuticGateRefusal.code`), the MCP proxy puts it in `error.data.code`, a hook gate that answers in JSON (Cline, Grok Build, Antigravity) puts it in `code` beside `ruleId`, and the proxy names a withheld answer in its `x-intutic-refusal` header. A refusal you read as text, starting `[Intutic]` or `[Intutic Governance]`, is the same refusal: the call it names did not run (unless it says the result was withheld). Find the code below and act on it.\n\n### Held: tell the user, then wait\nA hold rule wants a person to see this call before it runs. Codes: `HELD` (with a hold id, `hold_\u2026`), a hook gate's `[Intutic Governance] HELD:` message, `LOOP_RUN_PENDING_REVIEW` (the loop run is paused for review), and the proxy's `policy_held` (a Rego or WASM rule held the request; the error names the hold id). `SERVER_HELD` is the MCP registry's version: the server changed its tools in a risky way and waits for an owner or admin on the MCP Servers page.\n- Tell the user what was held and quote the hold id.\n- Only an **Owner, Admin or Engineering Manager** can decide it: `intutic decision approve <holdId>` or `intutic decision reject <holdId>`, or the Slack card. A developer cannot approve their own hold.\n- Do not retry while it is pending. After approval, the identical call (same tool, same arguments) passes only if the workspace's review-hold bypass (`reviewHoldBypassEnabled`) is on, and only for a short window (10 minutes by default); otherwise approval records the decision and a retry is held again. The `intutic_hold_status` MCP tool says which.\n- A hold with no hold id could not be recorded, so there is nothing to approve: tell the user.\n\n### Revise: change the approach\n`policy_reask` (proxy) and `REASK` (MCP proxy) refuse this attempt with a reason. Do something different; repeating it becomes a block (`policy_denied`, `REASK_EXHAUSTED`).\n\n### Blocked: do not retry the same call\nContinue without it, or tell the user what would have to change and who can change it.\n- **Policy:** `policy_denied`, `TOOL_DENIED`, `SOP_RULE`, `SNAPSHOT`, `HOOK_GATE`, `ANOMALY`, `WASM_RULE`, `REASK_EXHAUSTED`, `LOOP_RUN_TERMINATED`, `model_not_allowed`.\n- **Protected by the hook gate:** `BUILT_IN_RULE`: a governance bypass, a write to Intutic's own config or a skill directory, or a secret in written content. Do not look for another route to the same change; tell the user.\n- **Identity:** `SSO_GROUP`: the user's SSO groups do not clear this tool (rule `sso_group.high_risk.<tool>` or `sso_group.require_obo.<tool>`). An admin grants the group; you cannot work around it.\n- **Data:** `dlp_policy_violation` and `DLP` (a secret, destructive command or PII value in the input: remove it), `INJECTION` (the input carries a prompt-injection pattern), `SQL_GUARD` (destructive SQL against a database not on the SQL allowlist: use an allowlisted database, named explicitly on the command line).\n- **MCP servers:** `SERVER_BLOCKED`, `SERVER_NOT_APPROVED`, `TOOL_DISABLED`, `SERVER_NOT_ALLOWED`, `TOOL_NOT_ALLOWED`, `TOOL_DEFINITIONS_CHANGED`. An owner or admin decides on the MCP Servers page; the `intutic_mcp_registry_status` MCP tool shows what the registry says.\n- **Spend:** `BUDGET_EXCEEDED`, `OVERAGE_HARD_CAP_EXCEEDED`, `COST_GATE_EXCEEDED` (a smaller request may pass). An MCP `BUDGET_EXCEEDED` lasts until its `resetAt`; `intutic_mcp_budget_remaining` shows what is left.\n- **Images:** `E_UNPINNED_LATEST`, `E_UNPINNED_TAG`, `E_UNKNOWN_REGISTRY`, `E_UNKNOWN_IMAGE`, `E_DIGEST_MISMATCH`, `E_MANIFEST_UNPARSEABLE`: deploy an image pinned to a digest the image allowlist approves.\n- **Gate setup:** `WORKFLOW_SANDBOX`, `NO_GATE`: the gate is wired wrongly in the agent's code; fix the code, not the call. `UNREADABLE_CALL`: the hook could not read the tool call from its harness, which is a setup fault to report, not something to retry.\n- **Custom rule could not decide:** `GOVERNANCE_UNAVAILABLE` whose reason names a custom rule (`Custom rule <id> reached no verdict (<cause>)`, rule id `wasm:<id>`; cause `deadline`, `budget`, `error`, `result` or `quarantined`). The rule cannot decide this input, so the same call is refused again however often it is retried. Do not retry it; tell the user, who can fix or remove the rule.\n- **Size:** `COMMAND_TOO_LARGE`: the command is over 256 KiB, or the tool's arguments over 1 MiB, more than any gate evaluates. Split the work: smaller commands, or a file written in parts. A hook gate that could not finish deciding in time says `GATE_DEADLINE` in its reason; the call did not run, and the same call will be refused again, so tell the user.\n\n### Not checked: retry once, later\n`GOVERNANCE_UNAVAILABLE` (unless it names a custom rule: see above), `REGISTRY_UNAVAILABLE`, `BUDGET_UNAVAILABLE`, `TOFU_UNAVAILABLE`, `RESPONSE_UNPARSEABLE`, `GATE_CRASHED` (the hook gate failed while deciding), `POLICY_SNAPSHOT_UNVERIFIED` (the policy snapshot on this machine failed its integrity check, so no MCP call runs until the sync daemon restores it), and `HOOK_GATE` when its reason says the control plane was unreachable: governance could not check the call, so it did not run. One later retry is fine; if it fails again, tell the user.\n\n### Ran, but the result was withheld\n`RESULT_WITHHELD_DLP`, `RESULT_WITHHELD_INJECTION` (MCP proxy) and `OUTPUT_DLP` (proxy): the tool or model ran, and its output was not delivered. Do not run it again blindly, since it may already have had its effect; tell the user.\n\n---\n\n## \uD83D\uDEE1\uFE0F Policy & Hook Verification\n\n1. **Pre-Tool Interception Hooks:**\n Harness configurations (e.g. `.claude/settings.json`, `.cursor/hooks.json`) are automatically updated by `intutic connect`. They register execution hooks that block unauthorized tools or unsafe commands before execution.\n2. **Local Guidelines & Scoping:**\n Define local, developer-specific rules inside subdirectories of the `.intutic/sops/` directory (e.g. `.intutic/sops/my-rules/rules.md`). Initialize them using `@intutic initialize` and select active local SOPs using `@intutic start <ticket> --sops=<indices_or_names>`. These rules are enforced locally in the developer console to protect developer privacy, while global/corporate rules log incidents back to the control plane.\n3. **Offline Sync & Promotion:**\n When offline, prompt telemetry is stored locally in sharded files `~/.intutic/logs/traces-YYYY-MM-DD.jsonl` and budgets are enforced against sharded daily files `~/.intutic/logs/local-spend-YYYY-MM-DD.jsonl`. The sync daemon uploads them automatically on connect via `/api/v1/traces/sync-back` and cleans up the local files. Rules can be promoted workspace-wide via `intutic sops push <name>`.\n";
32
+ /** Every skill connect writes, by workspace-relative path. */
33
+ export declare const BUNDLED_SKILLS: ReadonlyArray<{
34
+ path: string;
35
+ content: string;
36
+ }>;
19
37
  /**
20
38
  * Write bundled skills into the workspace, skipping any that already exist.
21
39
  * Returns the paths written (empty when everything was already present).
@@ -1 +1 @@
1
- {"version":3,"file":"skillWriter.d.ts","sourceRoot":"","sources":["../src/skillWriter.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;GAaG;AAKH,mEAAmE;AACnE,eAAO,MAAM,sBAAsB,QAKlC,CAAA;AAED,gFAAgF;AAChF,eAAO,MAAM,iBAAiB,0sKA2E7B,CAAA;AAED;;;GAGG;AACH,wBAAsB,kBAAkB,CAAC,aAAa,EAAE,MAAM,GAAG,OAAO,CAAC,MAAM,EAAE,CAAC,CAejF"}
1
+ {"version":3,"file":"skillWriter.d.ts","sourceRoot":"","sources":["../src/skillWriter.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;GAsBG;AAMH,mEAAmE;AACnE,eAAO,MAAM,sBAAsB,QAKlC,CAAA;AAED,gFAAgF;AAChF,eAAO,MAAM,iBAAiB,40KA2E7B,CAAA;AAED,8DAA8D;AAC9D,eAAO,MAAM,iBAAiB,QAA+E,CAAA;AAE7G,sFAAsF;AACtF,eAAO,MAAM,YAAY,w2YAsHxB,CAAA;AAED,8DAA8D;AAC9D,eAAO,MAAM,cAAc,EAAE,aAAa,CAAC;IAAE,IAAI,EAAE,MAAM,CAAC;IAAC,OAAO,EAAE,MAAM,CAAA;CAAE,CAG3E,CAAA;AAED;;;GAGG;AACH,wBAAsB,kBAAkB,CAAC,aAAa,EAAE,MAAM,GAAG,OAAO,CAAC,MAAM,EAAE,CAAC,CAmBjF"}
@@ -5,15 +5,25 @@
5
5
  * written only when absent (write-if-missing), never overwrite user edits,
6
6
  * and are deliberately not tracked by the drift watcher / IntegrityStore.
7
7
  *
8
- * The skill content is embedded as a constant because the daemon runs
9
- * outside the monorepo. The canonical source is
10
- * `.agents/skills/intutic-rule-author/SKILL.md` in the open-core repo; a
11
- * test asserts the two stay identical.
8
+ * Two skills ship: the rule-author skill, which teaches an agent to extend
9
+ * governance, and the Kitkat skill, which teaches it to operate under it —
10
+ * what each refusal code means, what a hold is and who approves it. Every
11
+ * connected agent meets refusals, so Kitkat is written alongside the rule
12
+ * author rather than left as a manual download.
13
+ *
14
+ * Each is written as a file Intutic owns (disconnect/originals.ts):
15
+ * `intutic disconnect` deletes it again, unless it was edited since, and
16
+ * never touches a copy that was already there.
17
+ *
18
+ * The skill content is embedded as constants because the daemon runs outside
19
+ * the monorepo. The canonical sources are `.agents/skills/<name>/SKILL.md` in
20
+ * the open-core repo; a test asserts each pair stays identical.
12
21
  *
13
22
  * @module
14
23
  */
15
24
  import * as node_fs from 'node:fs/promises';
16
25
  import * as node_path from 'node:path';
26
+ import { keepOriginal, noteWritten } from './disconnect/originals.js';
17
27
  /** Workspace-relative path the rule-author skill is written to. */
18
28
  export const RULE_AUTHOR_SKILL_PATH = node_path.join('.agents', 'skills', 'intutic-rule-author', 'SKILL.md');
19
29
  /** Embedded copy of .agents/skills/intutic-rule-author/SKILL.md (canonical). */
@@ -52,9 +62,9 @@ The proxy calls \`evaluate(offset, len)\` with a JSON-serialized \`RequestContex
52
62
 
53
63
  Start from the \`@intutic/wasm-sdk\` template (\`packages/wasm-sdk/\` in the open-core repo). Add one function per business rule and call it from \`runRules()\` in \`assembly/index.ts\`.
54
64
 
55
- Sandbox constraints — violating them silently disables the rule (the proxy **fails open**):
65
+ Sandbox constraints — a rule that breaks one reaches no verdict, and every call it could not judge is refused with \`GOVERNANCE_UNAVAILABLE\`, whatever the proxy's fail setting:
56
66
 
57
- - 5 ms wall-clock budget, 1,000,000 fuel, 16 MB memory.
67
+ - 1,000,000 fuel (instructions), the limit to write against; a 1 s wall-clock backstop; 16 MB memory.
58
68
  - Keep logic simple: no unbounded loops over \`arguments\`, no recursion, no I/O (none exists in the sandbox).
59
69
  - Host imports are limited to \`env.log_info\`, \`env.trace\`, \`env.abort\` and \`env.read_referenced_file\` (reads the bytes of a file the host already resolved before the sandbox existed — not a filesystem import, and it cannot be used to browse). A rule importing anything else cannot link, so it is refused at \`intutic policy install\` and refused again when the proxy loads it.
60
70
  - **\`Math.random()\` is unavailable.** AssemblyScript compiles it to an \`env.seed\` import the proxy does not provide. This list previously named \`env.seed\`, and a rule using randomness installed clean and then failed to link on every request — enforcing nothing, silently. Beyond the missing import, a verdict that samples is not a verdict: the same request would get different answers, and the audit trail would not explain either.
@@ -93,23 +103,156 @@ Rules install to \`~/.intutic/wasm/\` as \`NN_name.wasm\` (lower \`NN\` runs fir
93
103
  - **Local rules cannot weaken central rules.** Verdicts merge most-restrictive-wins: a KILL from any rule — local or centrally synced — ends the request. "Allow-override" rules are no-ops by design; do not author them.
94
104
  - Keep rule sources (\`assembly/*.ts\`, mock JSON files) in version control alongside the workspace.
95
105
  `;
106
+ /** Workspace-relative path the Kitkat skill is written to. */
107
+ export const KITKAT_SKILL_PATH = node_path.join('.agents', 'skills', 'intutic-governance-kitkat', 'SKILL.md');
108
+ /** Embedded copy of .agents/skills/intutic-governance-kitkat/SKILL.md (canonical). */
109
+ export const KITKAT_SKILL = `---
110
+ name: intutic-governance-kitkat
111
+ description: Guide and execute Intutic governed developer workflows, ticket cost attributions, pre-flight token predictions, and safety policy interceptions using the Kitkat persona.
112
+ ---
113
+
114
+ # 🐱 Kitkat — Intutic Agentic Governance Skill
115
+
116
+ Hello! I am **Kitkat**, your friendly agentic control plane assistant for the Intutic Governance Engine. I ensure that your AI agentic workflows are safe, structured, FinOps-optimized, and compliant with both global enterprise policies (Notion/Confluence) and local rules.
117
+
118
+ ---
119
+
120
+ ## 🛠️ CLI Reference Catalog
121
+
122
+ Use the \`intutic\` CLI commands to manage the daemon, inspect execution traces, or manage governed task loops in your workspace:
123
+
124
+ ### 1. Connection & Session Setup
125
+ - **Authenticate:** \`intutic login [--control-plane-url <url>]\` (authenticates with your Intutic control plane).
126
+ - **Initialize Workspace:** \`intutic init\` (detects the developer harnesses in the workspace, like Claude Code or Cursor, and records them for sync).
127
+ - **Start Connection:** \`intutic connect\` (boots the local interceptor proxy, writes each recorded harness's rules and hooks, keeps them in sync with the control plane, and writes this skill and the rule-author skill into \`.agents/skills/\` when they are missing).
128
+ - **Stop Governing:** \`intutic disconnect [--harness <id>] [--dry-run] [--keep-login]\` (undoes \`connect\` on this machine: restores every harness config it changed, removes its hooks, services and the skills it wrote, and logs out). Run it only when the user asks; \`--dry-run\` shows what would change.
129
+ - **Offline Spend Sync:** The sync daemon automatically reconciles offline query consumption logs (\`traces-*.jsonl\`) and local budgets back to the control plane on reconnect.
130
+
131
+ ### 2. Traces & Auditing
132
+ - **Check Budget:** \`intutic budget\` (displays overall monthly/daily spend limits, local spend caps, and active task loops).
133
+ - **List Sessions:** \`intutic traces list\`
134
+ - **Inspect Session Trace:** \`intutic traces inspect <trace_id>\`
135
+ - **Push SOP Rules:** \`intutic sops push <name>\` (promotes local SOP rules to central repository).
136
+
137
+ ### 3. Governed Task Loops
138
+ - **Start Task Loop:** \`intutic loop start --name <name> [--budget <USD>] [--sops <indices_or_names>] [--auto-judge]\`
139
+ - **Execute Wrapped Agent:** \`intutic loop exec --name <name> [--budget <USD>] [--sops <indices_or_names>] [--auto-judge] -- <command>\`
140
+ - **Complete/Kill Loop:** \`intutic loop complete <loopRunId>\` / \`intutic loop kill <loopRunId>\`
141
+ - **List Active Loops:** \`intutic loop list\`
142
+
143
+ ---
144
+
145
+ ## 🔌 Interception & Command Prepends
146
+
147
+ When interacting with LLM providers through the local proxy, prepend requests with \`@intutic\` (or \`/intutic\`) to control the active governance session:
148
+
149
+ ### 1. Cost & Task Attribution
150
+ - **Initialize Picker:** \`@intutic initialize\`
151
+ - *Description:* Pulls open tickets from linked task providers (Jira, Linear, GitHub Issues) and recommends ticket candidates for the session.
152
+ - **Lock Session & Scope Rules:** \`@intutic start [<ticket_id>] [--sops=<names_or_indices>] [--auto-judge]\`
153
+ - *Description:* Attributes token costs to a ticket (optional) and scopes active local SOP directories for governance.
154
+ - *Examples:*
155
+ * \`@intutic start 1 --sops=3 --auto-judge\`
156
+ * \`@intutic start --sops=security-rules --auto-judge\`
157
+
158
+ ### 2. FinOps & Token Projections
159
+ - **Predict Costs:** \`@intutic predict <prompt>\`
160
+ - *Description:* Estimates input/output tokens and cost breakdown before submitting the request to the upstream model.
161
+ - **Optimize Context:** \`@intutic recommend\`
162
+ - *Description:* Provides recommendations on prompt adjustments and context reduction strategies to minimize token waste.
163
+
164
+ ### 3. Policy Compliance & E2E Judging
165
+ - **Verify Prompt:** \`@intutic verify <prompt>\` (or \`@intutic check <prompt>\`)
166
+ - *Description:* Audits the prompt pre-flight against active SOPs and guidelines, returning pass/fail verdicts and listing any violations before submitting requests to the LLM.
167
+ - **Review Prompt:** \`@intutic review <prompt>\`
168
+ - *Description:* Runs a deep heuristic prompt quality evaluation to grade clarity, specificity, and actionability.
169
+ - **Judge Response:** \`@intutic judge <prompt>\`
170
+ - *Description:* Submits the prompt to the upstream LLM and evaluates the response stream in parallel using LLM-as-a-judge compliance checkers.
171
+ - *Note:* If no prompt is provided (e.g. \`@intutic judge\`), returns usage instructions.
172
+ - **Inspect Status:** \`@intutic status\` (displays active session stats, average compliance, and spent budget).
173
+ - **Inspect Budget:** \`@intutic budget\` (displays daily monitored limits and workspace progress).
174
+
175
+ ### 4. Posture & Prompt Enhancement
176
+ - **Enhance & Diagnose:** \`/fix <prompt>\` (aliases: \`@fix\`, \`/intutic-fix\`, \`@intutic fix\`)
177
+ - *Description:* Rewrites the prompt and scores the workspace's governance posture against OWASP categories from what the proxy can see locally — DLP settings, WASM rules, the SOPs on disk for the caller's role, skills and MCP servers. Returns the enhanced prompt plus concrete recommendations for whatever is missing.
178
+ - *Note:* Blocking by default; it can be configured to pass the enhanced prompt through to the model instead. The recommendations are the handoff points into the rest of the toolchain — "add a WASM rule" is the \`intutic-rule-author\` skill's job, "write a role SOP" and "enable DLP" are handled here.
179
+ - **Draw Guardrails:** \`/draw\` (aliases: \`@draw\`, \`/intutic-draw\`, \`@intutic draw\`)
180
+ - *Description:* Renders a Mermaid diagram plus text summary of the agent's trajectory and the guardrails currently around it. Always blocking — a visualization has nothing to forward upstream.
181
+
182
+ ---
183
+
184
+ ## 🚦 When a Call Is Refused or Held
185
+
186
+ Intutic names every refusal with a code: an SDK raises it (\`ClawdeBlockedError.code\`, \`IntuticGateRefusal.code\`), the MCP proxy puts it in \`error.data.code\`, a hook gate that answers in JSON (Cline, Grok Build, Antigravity) puts it in \`code\` beside \`ruleId\`, and the proxy names a withheld answer in its \`x-intutic-refusal\` header. A refusal you read as text, starting \`[Intutic]\` or \`[Intutic Governance]\`, is the same refusal: the call it names did not run (unless it says the result was withheld). Find the code below and act on it.
187
+
188
+ ### Held: tell the user, then wait
189
+ A hold rule wants a person to see this call before it runs. Codes: \`HELD\` (with a hold id, \`hold_…\`), a hook gate's \`[Intutic Governance] HELD:\` message, \`LOOP_RUN_PENDING_REVIEW\` (the loop run is paused for review), and the proxy's \`policy_held\` (a Rego or WASM rule held the request; the error names the hold id). \`SERVER_HELD\` is the MCP registry's version: the server changed its tools in a risky way and waits for an owner or admin on the MCP Servers page.
190
+ - Tell the user what was held and quote the hold id.
191
+ - Only an **Owner, Admin or Engineering Manager** can decide it: \`intutic decision approve <holdId>\` or \`intutic decision reject <holdId>\`, or the Slack card. A developer cannot approve their own hold.
192
+ - Do not retry while it is pending. After approval, the identical call (same tool, same arguments) passes only if the workspace's review-hold bypass (\`reviewHoldBypassEnabled\`) is on, and only for a short window (10 minutes by default); otherwise approval records the decision and a retry is held again. The \`intutic_hold_status\` MCP tool says which.
193
+ - A hold with no hold id could not be recorded, so there is nothing to approve: tell the user.
194
+
195
+ ### Revise: change the approach
196
+ \`policy_reask\` (proxy) and \`REASK\` (MCP proxy) refuse this attempt with a reason. Do something different; repeating it becomes a block (\`policy_denied\`, \`REASK_EXHAUSTED\`).
197
+
198
+ ### Blocked: do not retry the same call
199
+ Continue without it, or tell the user what would have to change and who can change it.
200
+ - **Policy:** \`policy_denied\`, \`TOOL_DENIED\`, \`SOP_RULE\`, \`SNAPSHOT\`, \`HOOK_GATE\`, \`ANOMALY\`, \`WASM_RULE\`, \`REASK_EXHAUSTED\`, \`LOOP_RUN_TERMINATED\`, \`model_not_allowed\`.
201
+ - **Protected by the hook gate:** \`BUILT_IN_RULE\`: a governance bypass, a write to Intutic's own config or a skill directory, or a secret in written content. Do not look for another route to the same change; tell the user.
202
+ - **Identity:** \`SSO_GROUP\`: the user's SSO groups do not clear this tool (rule \`sso_group.high_risk.<tool>\` or \`sso_group.require_obo.<tool>\`). An admin grants the group; you cannot work around it.
203
+ - **Data:** \`dlp_policy_violation\` and \`DLP\` (a secret, destructive command or PII value in the input: remove it), \`INJECTION\` (the input carries a prompt-injection pattern), \`SQL_GUARD\` (destructive SQL against a database not on the SQL allowlist: use an allowlisted database, named explicitly on the command line).
204
+ - **MCP servers:** \`SERVER_BLOCKED\`, \`SERVER_NOT_APPROVED\`, \`TOOL_DISABLED\`, \`SERVER_NOT_ALLOWED\`, \`TOOL_NOT_ALLOWED\`, \`TOOL_DEFINITIONS_CHANGED\`. An owner or admin decides on the MCP Servers page; the \`intutic_mcp_registry_status\` MCP tool shows what the registry says.
205
+ - **Spend:** \`BUDGET_EXCEEDED\`, \`OVERAGE_HARD_CAP_EXCEEDED\`, \`COST_GATE_EXCEEDED\` (a smaller request may pass). An MCP \`BUDGET_EXCEEDED\` lasts until its \`resetAt\`; \`intutic_mcp_budget_remaining\` shows what is left.
206
+ - **Images:** \`E_UNPINNED_LATEST\`, \`E_UNPINNED_TAG\`, \`E_UNKNOWN_REGISTRY\`, \`E_UNKNOWN_IMAGE\`, \`E_DIGEST_MISMATCH\`, \`E_MANIFEST_UNPARSEABLE\`: deploy an image pinned to a digest the image allowlist approves.
207
+ - **Gate setup:** \`WORKFLOW_SANDBOX\`, \`NO_GATE\`: the gate is wired wrongly in the agent's code; fix the code, not the call. \`UNREADABLE_CALL\`: the hook could not read the tool call from its harness, which is a setup fault to report, not something to retry.
208
+ - **Custom rule could not decide:** \`GOVERNANCE_UNAVAILABLE\` whose reason names a custom rule (\`Custom rule <id> reached no verdict (<cause>)\`, rule id \`wasm:<id>\`; cause \`deadline\`, \`budget\`, \`error\`, \`result\` or \`quarantined\`). The rule cannot decide this input, so the same call is refused again however often it is retried. Do not retry it; tell the user, who can fix or remove the rule.
209
+ - **Size:** \`COMMAND_TOO_LARGE\`: the command is over 256 KiB, or the tool's arguments over 1 MiB, more than any gate evaluates. Split the work: smaller commands, or a file written in parts. A hook gate that could not finish deciding in time says \`GATE_DEADLINE\` in its reason; the call did not run, and the same call will be refused again, so tell the user.
210
+
211
+ ### Not checked: retry once, later
212
+ \`GOVERNANCE_UNAVAILABLE\` (unless it names a custom rule: see above), \`REGISTRY_UNAVAILABLE\`, \`BUDGET_UNAVAILABLE\`, \`TOFU_UNAVAILABLE\`, \`RESPONSE_UNPARSEABLE\`, \`GATE_CRASHED\` (the hook gate failed while deciding), \`POLICY_SNAPSHOT_UNVERIFIED\` (the policy snapshot on this machine failed its integrity check, so no MCP call runs until the sync daemon restores it), and \`HOOK_GATE\` when its reason says the control plane was unreachable: governance could not check the call, so it did not run. One later retry is fine; if it fails again, tell the user.
213
+
214
+ ### Ran, but the result was withheld
215
+ \`RESULT_WITHHELD_DLP\`, \`RESULT_WITHHELD_INJECTION\` (MCP proxy) and \`OUTPUT_DLP\` (proxy): the tool or model ran, and its output was not delivered. Do not run it again blindly, since it may already have had its effect; tell the user.
216
+
217
+ ---
218
+
219
+ ## 🛡️ Policy & Hook Verification
220
+
221
+ 1. **Pre-Tool Interception Hooks:**
222
+ Harness configurations (e.g. \`.claude/settings.json\`, \`.cursor/hooks.json\`) are automatically updated by \`intutic connect\`. They register execution hooks that block unauthorized tools or unsafe commands before execution.
223
+ 2. **Local Guidelines & Scoping:**
224
+ Define local, developer-specific rules inside subdirectories of the \`.intutic/sops/\` directory (e.g. \`.intutic/sops/my-rules/rules.md\`). Initialize them using \`@intutic initialize\` and select active local SOPs using \`@intutic start <ticket> --sops=<indices_or_names>\`. These rules are enforced locally in the developer console to protect developer privacy, while global/corporate rules log incidents back to the control plane.
225
+ 3. **Offline Sync & Promotion:**
226
+ When offline, prompt telemetry is stored locally in sharded files \`~/.intutic/logs/traces-YYYY-MM-DD.jsonl\` and budgets are enforced against sharded daily files \`~/.intutic/logs/local-spend-YYYY-MM-DD.jsonl\`. The sync daemon uploads them automatically on connect via \`/api/v1/traces/sync-back\` and cleans up the local files. Rules can be promoted workspace-wide via \`intutic sops push <name>\`.
227
+ `;
228
+ /** Every skill connect writes, by workspace-relative path. */
229
+ export const BUNDLED_SKILLS = [
230
+ { path: RULE_AUTHOR_SKILL_PATH, content: RULE_AUTHOR_SKILL },
231
+ { path: KITKAT_SKILL_PATH, content: KITKAT_SKILL },
232
+ ];
96
233
  /**
97
234
  * Write bundled skills into the workspace, skipping any that already exist.
98
235
  * Returns the paths written (empty when everything was already present).
99
236
  */
100
237
  export async function writeBundledSkills(workspaceRoot) {
101
238
  const written = [];
102
- const dest = node_path.join(workspaceRoot, RULE_AUTHOR_SKILL_PATH);
103
- try {
104
- await node_fs.access(dest);
105
- return written; // present — never overwrite user edits
106
- }
107
- catch {
108
- // absent → write below
239
+ for (const skill of BUNDLED_SKILLS) {
240
+ const dest = node_path.join(workspaceRoot, skill.path);
241
+ try {
242
+ await node_fs.access(dest);
243
+ continue; // present — never overwrite user edits
244
+ }
245
+ catch {
246
+ // absent → write below
247
+ }
248
+ // Recorded first, so disconnect knows Intutic created it (and the
249
+ // directories it needed) and can tell a later edit apart.
250
+ await keepOriginal(dest, workspaceRoot);
251
+ await node_fs.mkdir(node_path.dirname(dest), { recursive: true });
252
+ await node_fs.writeFile(dest, skill.content, 'utf-8');
253
+ await noteWritten(dest, workspaceRoot, skill.content);
254
+ written.push(dest);
109
255
  }
110
- await node_fs.mkdir(node_path.dirname(dest), { recursive: true });
111
- await node_fs.writeFile(dest, RULE_AUTHOR_SKILL, 'utf-8');
112
- written.push(dest);
113
256
  return written;
114
257
  }
115
258
  //# sourceMappingURL=skillWriter.js.map
@@ -1 +1 @@
1
- {"version":3,"file":"skillWriter.js","sourceRoot":"","sources":["../src/skillWriter.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;GAaG;AAEH,OAAO,KAAK,OAAO,MAAM,kBAAkB,CAAA;AAC3C,OAAO,KAAK,SAAS,MAAM,WAAW,CAAA;AAEtC,mEAAmE;AACnE,MAAM,CAAC,MAAM,sBAAsB,GAAG,SAAS,CAAC,IAAI,CAClD,SAAS,EACT,QAAQ,EACR,qBAAqB,EACrB,UAAU,CACX,CAAA;AAED,gFAAgF;AAChF,MAAM,CAAC,MAAM,iBAAiB,GAAG;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;CA2EhC,CAAA;AAED;;;GAGG;AACH,MAAM,CAAC,KAAK,UAAU,kBAAkB,CAAC,aAAqB;IAC5D,MAAM,OAAO,GAAa,EAAE,CAAA;IAC5B,MAAM,IAAI,GAAG,SAAS,CAAC,IAAI,CAAC,aAAa,EAAE,sBAAsB,CAAC,CAAA;IAElE,IAAI,CAAC;QACH,MAAM,OAAO,CAAC,MAAM,CAAC,IAAI,CAAC,CAAA;QAC1B,OAAO,OAAO,CAAA,CAAC,uCAAuC;IACxD,CAAC;IAAC,MAAM,CAAC;QACP,uBAAuB;IACzB,CAAC;IAED,MAAM,OAAO,CAAC,KAAK,CAAC,SAAS,CAAC,OAAO,CAAC,IAAI,CAAC,EAAE,EAAE,SAAS,EAAE,IAAI,EAAE,CAAC,CAAA;IACjE,MAAM,OAAO,CAAC,SAAS,CAAC,IAAI,EAAE,iBAAiB,EAAE,OAAO,CAAC,CAAA;IACzD,OAAO,CAAC,IAAI,CAAC,IAAI,CAAC,CAAA;IAClB,OAAO,OAAO,CAAA;AAChB,CAAC"}
1
+ {"version":3,"file":"skillWriter.js","sourceRoot":"","sources":["../src/skillWriter.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;GAsBG;AAEH,OAAO,KAAK,OAAO,MAAM,kBAAkB,CAAA;AAC3C,OAAO,KAAK,SAAS,MAAM,WAAW,CAAA;AACtC,OAAO,EAAE,YAAY,EAAE,WAAW,EAAE,MAAM,2BAA2B,CAAA;AAErE,mEAAmE;AACnE,MAAM,CAAC,MAAM,sBAAsB,GAAG,SAAS,CAAC,IAAI,CAClD,SAAS,EACT,QAAQ,EACR,qBAAqB,EACrB,UAAU,CACX,CAAA;AAED,gFAAgF;AAChF,MAAM,CAAC,MAAM,iBAAiB,GAAG;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;CA2EhC,CAAA;AAED,8DAA8D;AAC9D,MAAM,CAAC,MAAM,iBAAiB,GAAG,SAAS,CAAC,IAAI,CAAC,SAAS,EAAE,QAAQ,EAAE,2BAA2B,EAAE,UAAU,CAAC,CAAA;AAE7G,sFAAsF;AACtF,MAAM,CAAC,MAAM,YAAY,GAAG;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;CAsH3B,CAAA;AAED,8DAA8D;AAC9D,MAAM,CAAC,MAAM,cAAc,GAAqD;IAC9E,EAAE,IAAI,EAAE,sBAAsB,EAAE,OAAO,EAAE,iBAAiB,EAAE;IAC5D,EAAE,IAAI,EAAE,iBAAiB,EAAE,OAAO,EAAE,YAAY,EAAE;CACnD,CAAA;AAED;;;GAGG;AACH,MAAM,CAAC,KAAK,UAAU,kBAAkB,CAAC,aAAqB;IAC5D,MAAM,OAAO,GAAa,EAAE,CAAA;IAC5B,KAAK,MAAM,KAAK,IAAI,cAAc,EAAE,CAAC;QACnC,MAAM,IAAI,GAAG,SAAS,CAAC,IAAI,CAAC,aAAa,EAAE,KAAK,CAAC,IAAI,CAAC,CAAA;QACtD,IAAI,CAAC;YACH,MAAM,OAAO,CAAC,MAAM,CAAC,IAAI,CAAC,CAAA;YAC1B,SAAQ,CAAC,uCAAuC;QAClD,CAAC;QAAC,MAAM,CAAC;YACP,uBAAuB;QACzB,CAAC;QACD,kEAAkE;QAClE,0DAA0D;QAC1D,MAAM,YAAY,CAAC,IAAI,EAAE,aAAa,CAAC,CAAA;QACvC,MAAM,OAAO,CAAC,KAAK,CAAC,SAAS,CAAC,OAAO,CAAC,IAAI,CAAC,EAAE,EAAE,SAAS,EAAE,IAAI,EAAE,CAAC,CAAA;QACjE,MAAM,OAAO,CAAC,SAAS,CAAC,IAAI,EAAE,KAAK,CAAC,OAAO,EAAE,OAAO,CAAC,CAAA;QACrD,MAAM,WAAW,CAAC,IAAI,EAAE,aAAa,EAAE,KAAK,CAAC,OAAO,CAAC,CAAA;QACrD,OAAO,CAAC,IAAI,CAAC,IAAI,CAAC,CAAA;IACpB,CAAC;IACD,OAAO,OAAO,CAAA;AAChB,CAAC"}
@@ -0,0 +1,142 @@
1
+ /**
2
+ * syncCycle.ts — the per-cycle work `intutic connect` runs against the
3
+ * control plane.
4
+ *
5
+ * `intutic connect` (tools/cli/src/commands/connect.ts) owns the loop: it
6
+ * polls the control plane, listens for pushed config over the WebSocket, and
7
+ * on each cycle calls the helpers below. They live here, not in the CLI, so
8
+ * they can be tested without starting a daemon and so the gate-side caches
9
+ * are refreshed by exactly one implementation.
10
+ *
11
+ * This module used to also hold a second, complete loop (`startSyncLoop`)
12
+ * that nothing started; the service has always run `connect`. The behaviours
13
+ * only that loop had and the docs promise (SkillOpt edits, skill findings,
14
+ * the decisions log, the bundled rule-author skill) are the helpers below and
15
+ * run from `connect` now, as does its config capture (configReader.ts), which
16
+ * uploads file content only when the workspace turned that on.
17
+ *
18
+ * @module
19
+ */
20
+ import type { HarnessType, SyncConfigPayload, SyncSopEntry } from '@intutic/shared-types';
21
+ import type { ConfigEditApplyOutcome } from './configWriter.js';
22
+ import type { GovernanceCoverageInputs } from './configReader.js';
23
+ import { type AgentReport } from './agentReporter.js';
24
+ /**
25
+ * Turns a non-clean `scanSkillContent` result — already computed as part of
26
+ * `collectAgentReport`'s `facets.skills` — into a `skill_flagged` hook event.
27
+ *
28
+ * Deliberately reuses the report the daemon already built for this cycle
29
+ * rather than re-scanning: `agentReporter.ts`'s `collectSkills` runs
30
+ * `scanSkillContent` on every `.agents/skills/**\/SKILL.md` as part of
31
+ * building `facets.skills` already, so this only turns a result the daemon
32
+ * already has into a visible signal — it does not add a second scan pass.
33
+ *
34
+ * Appends directly to `.intutic/events/hook-events.jsonl`, the SAME file
35
+ * every emitted PreToolUse gate's `log_event`/`logEvent` writes to — not a
36
+ * new event pipeline. `drainHookEvents` (`claudeCodeHooks.ts`) picks this
37
+ * line up on its next pass exactly like a gate-written one, because it only
38
+ * ever reads the file; it does not care who appended a given line.
39
+ *
40
+ * Report-only, per `scanSkillContent`'s own doc comment on the unmeasured
41
+ * false-positive rate against real, benign skill markdown: this NEVER
42
+ * blocks, mutates, or removes a skill. `hookEvents.ts`'s
43
+ * `HookEventSchema.event` enum (control plane) counts `skill_flagged`
44
+ * exactly like `tool_flagged` — advisory telemetry, not an incident.
45
+ *
46
+ * A failure to append is caught and warned, never thrown: a governance
47
+ * telemetry write must not be able to abort the sync cycle it rides in on.
48
+ */
49
+ export declare function emitSkillFlaggedEvents(opts: {
50
+ workspaceRoot: string;
51
+ workspaceId: string;
52
+ harnessType: HarnessType;
53
+ skills: AgentReport['facets']['skills'];
54
+ /** Skills already emitted in this cycle, keyed `source:name`, mutated in
55
+ * place. A skill is not harness-specific, and the caller invokes this
56
+ * once per harness in the workspace — without a shared set, a workspace
57
+ * with two active harnesses would double-emit every flagged skill. */
58
+ alreadyEmitted: Set<string>;
59
+ }): void;
60
+ /** The credentials every gate-side refresh takes. */
61
+ export interface GateCacheRefreshOptions {
62
+ controlPlaneUrl: string;
63
+ apiKey: string;
64
+ workspaceId: string;
65
+ /** See {@link PolicySnapshotOptions.localHoldTokens}. */
66
+ localHoldTokens?: readonly string[];
67
+ }
68
+ /**
69
+ * The `review_before:` tokens the snapshot compiles into hold rules: from the
70
+ * synced SOPs' front matter and JSON blocks, the workspace settings, and the
71
+ * local `.intutic/sops`. The same parse the Claude Code writer runs for its
72
+ * pattern blacklist, so the two cannot disagree about what is held.
73
+ */
74
+ export declare function localHoldTokensFor(workspaceRoot: string, sops: SyncSopEntry[], settings: Record<string, unknown> | undefined, harnesses?: HarnessType[]): Promise<string[]>;
75
+ /**
76
+ * Refresh the three gate-side caches every hook reads: the policy snapshot
77
+ * (`~/.intutic/hooks/policy-snapshot.rules`), the approved review-hold
78
+ * bypasses and the central egress policy. Called on EVERY sync cycle, never
79
+ * only when the config version moved: policy changes without the version
80
+ * changing, and before this helper existed `connect` refreshed once at
81
+ * startup and never again, so a guardrail promoted mid-session reached a
82
+ * connected machine only on restart. None of the three throw; the return
83
+ * says which of them landed.
84
+ */
85
+ export declare function refreshGateCaches(opts: GateCacheRefreshOptions): Promise<{
86
+ snapshot: boolean;
87
+ bypasses: boolean;
88
+ egress: boolean;
89
+ }>;
90
+ /** Where the ids of SkillOpt suggestions that fully landed are remembered. */
91
+ export declare const APPLIED_SUGGESTIONS_RELATIVE_PATH: string;
92
+ /**
93
+ * Apply the SkillOpt config edits the control plane sent with this config,
94
+ * and ack each outcome back (`POST /api/v1/skillopt/:id/apply-result`). A
95
+ * suggestion queued for auto-apply reaches `applied` only through that ack.
96
+ *
97
+ * Only ids whose every operation landed are remembered in
98
+ * `.intutic/applied-suggestions.json`, so a failed edit (a fuzzy-match miss,
99
+ * a refused write) is retried next cycle rather than forgotten. With
100
+ * `reapplyAll` every edit is applied again regardless: the caller has just
101
+ * rewritten the harness config files from the SOPs, which drops every overlay
102
+ * an earlier cycle applied. ADD is idempotent, so re-applying an edit that is
103
+ * still present changes nothing.
104
+ *
105
+ * Never throws: a failed apply or ack must not abort the sync cycle. A
106
+ * dropped ack only means the suggestion is synced and acked again next cycle.
107
+ */
108
+ export declare function applySkillOptEdits(opts: {
109
+ workspaceRoot: string;
110
+ controlPlaneUrl: string;
111
+ apiKey: string;
112
+ appliedEdits: SyncConfigPayload['appliedEdits'];
113
+ bypassEnforcementTier?: string;
114
+ reapplyAll: boolean;
115
+ }): Promise<ConfigEditApplyOutcome[]>;
116
+ /**
117
+ * Register each harness as an agent with its facets, report one session per
118
+ * harness, and turn this cycle's skill-scan findings into `skill_flagged`
119
+ * events. Returns each harness's governance-coverage inputs, derived from the
120
+ * same facets, for the config capture.
121
+ *
122
+ * One harness failing must not stop the others, so failures are collected
123
+ * and returned rather than thrown.
124
+ */
125
+ export declare function reportHarnessAgents(opts: {
126
+ controlPlaneUrl: string;
127
+ apiKey: string;
128
+ workspaceId: string;
129
+ workspaceRoot: string;
130
+ harnesses: readonly HarnessType[];
131
+ allowLocalVaults?: boolean;
132
+ /** The local proxy's instance id: the session context lands on its row. */
133
+ proxyInstanceId?: string | null;
134
+ }): Promise<{
135
+ governanceInputs: Partial<Record<HarnessType, GovernanceCoverageInputs>>;
136
+ failures: Array<{
137
+ harness: HarnessType;
138
+ error: string;
139
+ }>;
140
+ }>;
141
+ export declare function syncOfflineTraces(controlPlaneUrl: string, apiKey: string): Promise<void>;
142
+ //# sourceMappingURL=syncCycle.d.ts.map