@intutic/sync-daemon 1.10.0 → 2.0.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 (200) hide show
  1. package/README.md +2 -2
  2. package/dist/agentReporter.d.ts +205 -0
  3. package/dist/agentReporter.d.ts.map +1 -0
  4. package/dist/agentReporter.js +394 -0
  5. package/dist/agentReporter.js.map +1 -0
  6. package/dist/configReader.d.ts +39 -2
  7. package/dist/configReader.d.ts.map +1 -1
  8. package/dist/configReader.js +56 -2
  9. package/dist/configReader.js.map +1 -1
  10. package/dist/configWriter.d.ts +51 -1
  11. package/dist/configWriter.d.ts.map +1 -1
  12. package/dist/configWriter.js +490 -63
  13. package/dist/configWriter.js.map +1 -1
  14. package/dist/harness/antigravityHooks.d.ts.map +1 -1
  15. package/dist/harness/antigravityHooks.js +33 -55
  16. package/dist/harness/antigravityHooks.js.map +1 -1
  17. package/dist/harness/claudeCodeHooks.d.ts +47 -13
  18. package/dist/harness/claudeCodeHooks.d.ts.map +1 -1
  19. package/dist/harness/claudeCodeHooks.js +316 -54
  20. package/dist/harness/claudeCodeHooks.js.map +1 -1
  21. package/dist/harness/claudeDesktopHooks.d.ts.map +1 -1
  22. package/dist/harness/claudeDesktopHooks.js +35 -48
  23. package/dist/harness/claudeDesktopHooks.js.map +1 -1
  24. package/dist/harness/clineHooks.d.ts.map +1 -1
  25. package/dist/harness/clineHooks.js +42 -34
  26. package/dist/harness/clineHooks.js.map +1 -1
  27. package/dist/harness/codexHooks.d.ts +36 -0
  28. package/dist/harness/codexHooks.d.ts.map +1 -0
  29. package/dist/harness/codexHooks.js +227 -0
  30. package/dist/harness/codexHooks.js.map +1 -0
  31. package/dist/harness/continueHooks.d.ts +45 -5
  32. package/dist/harness/continueHooks.d.ts.map +1 -1
  33. package/dist/harness/continueHooks.js +225 -5
  34. package/dist/harness/continueHooks.js.map +1 -1
  35. package/dist/harness/cursorHooks.d.ts +31 -0
  36. package/dist/harness/cursorHooks.d.ts.map +1 -1
  37. package/dist/harness/cursorHooks.js +78 -49
  38. package/dist/harness/cursorHooks.js.map +1 -1
  39. package/dist/harness/dshHooks.d.ts +198 -0
  40. package/dist/harness/dshHooks.d.ts.map +1 -0
  41. package/dist/harness/dshHooks.js +569 -0
  42. package/dist/harness/dshHooks.js.map +1 -0
  43. package/dist/harness/gateBody.d.ts +422 -0
  44. package/dist/harness/gateBody.d.ts.map +1 -0
  45. package/dist/harness/gateBody.js +1816 -0
  46. package/dist/harness/gateBody.js.map +1 -0
  47. package/dist/harness/gateKind.d.ts +164 -0
  48. package/dist/harness/gateKind.d.ts.map +1 -0
  49. package/dist/harness/gateKind.js +143 -0
  50. package/dist/harness/gateKind.js.map +1 -0
  51. package/dist/harness/githubCopilotHooks.d.ts +54 -0
  52. package/dist/harness/githubCopilotHooks.d.ts.map +1 -0
  53. package/dist/harness/githubCopilotHooks.js +218 -0
  54. package/dist/harness/githubCopilotHooks.js.map +1 -0
  55. package/dist/harness/gooseHooks.d.ts.map +1 -1
  56. package/dist/harness/gooseHooks.js +67 -37
  57. package/dist/harness/gooseHooks.js.map +1 -1
  58. package/dist/harness/grokHooks.d.ts +77 -0
  59. package/dist/harness/grokHooks.d.ts.map +1 -0
  60. package/dist/harness/grokHooks.js +350 -0
  61. package/dist/harness/grokHooks.js.map +1 -0
  62. package/dist/harness/hermesHooks.d.ts.map +1 -1
  63. package/dist/harness/hermesHooks.js +26 -55
  64. package/dist/harness/hermesHooks.js.map +1 -1
  65. package/dist/harness/holdRedaction.d.ts +69 -0
  66. package/dist/harness/holdRedaction.d.ts.map +1 -0
  67. package/dist/harness/holdRedaction.js +211 -0
  68. package/dist/harness/holdRedaction.js.map +1 -0
  69. package/dist/harness/jetbrainsXmlConfig.d.ts +51 -0
  70. package/dist/harness/jetbrainsXmlConfig.d.ts.map +1 -0
  71. package/dist/harness/jetbrainsXmlConfig.js +151 -0
  72. package/dist/harness/jetbrainsXmlConfig.js.map +1 -0
  73. package/dist/harness/mcpAutoWrite.d.ts +100 -3
  74. package/dist/harness/mcpAutoWrite.d.ts.map +1 -1
  75. package/dist/harness/mcpAutoWrite.js +712 -90
  76. package/dist/harness/mcpAutoWrite.js.map +1 -1
  77. package/dist/harness/museHooks.d.ts +53 -0
  78. package/dist/harness/museHooks.d.ts.map +1 -0
  79. package/dist/harness/museHooks.js +273 -0
  80. package/dist/harness/museHooks.js.map +1 -0
  81. package/dist/harness/n8nHooks.d.ts +25 -9
  82. package/dist/harness/n8nHooks.d.ts.map +1 -1
  83. package/dist/harness/n8nHooks.js +174 -9
  84. package/dist/harness/n8nHooks.js.map +1 -1
  85. package/dist/harness/openCodeHooks.d.ts +93 -0
  86. package/dist/harness/openCodeHooks.d.ts.map +1 -0
  87. package/dist/harness/openCodeHooks.js +248 -0
  88. package/dist/harness/openCodeHooks.js.map +1 -0
  89. package/dist/harness/openWebuiHooks.d.ts.map +1 -1
  90. package/dist/harness/openWebuiHooks.js +56 -11
  91. package/dist/harness/openWebuiHooks.js.map +1 -1
  92. package/dist/harness/openclawHooks.d.ts.map +1 -1
  93. package/dist/harness/openclawHooks.js +30 -54
  94. package/dist/harness/openclawHooks.js.map +1 -1
  95. package/dist/harness/openhandsHooks.d.ts.map +1 -1
  96. package/dist/harness/openhandsHooks.js +35 -41
  97. package/dist/harness/openhandsHooks.js.map +1 -1
  98. package/dist/harness/piHooks.d.ts.map +1 -1
  99. package/dist/harness/piHooks.js +31 -58
  100. package/dist/harness/piHooks.js.map +1 -1
  101. package/dist/harness/protectedPaths.d.ts +353 -0
  102. package/dist/harness/protectedPaths.d.ts.map +1 -0
  103. package/dist/harness/protectedPaths.js +805 -0
  104. package/dist/harness/protectedPaths.js.map +1 -0
  105. package/dist/harness/rooCodeHooks.d.ts.map +1 -1
  106. package/dist/harness/rooCodeHooks.js +34 -48
  107. package/dist/harness/rooCodeHooks.js.map +1 -1
  108. package/dist/harness/windsurfHooks.d.ts +72 -0
  109. package/dist/harness/windsurfHooks.d.ts.map +1 -1
  110. package/dist/harness/windsurfHooks.js +172 -58
  111. package/dist/harness/windsurfHooks.js.map +1 -1
  112. package/dist/harness/windsurfJetBrainsProxy.d.ts +78 -0
  113. package/dist/harness/windsurfJetBrainsProxy.d.ts.map +1 -0
  114. package/dist/harness/windsurfJetBrainsProxy.js +159 -0
  115. package/dist/harness/windsurfJetBrainsProxy.js.map +1 -0
  116. package/dist/hashReporter.js +1 -1
  117. package/dist/hashReporter.js.map +1 -1
  118. package/dist/index.d.ts +12 -6
  119. package/dist/index.d.ts.map +1 -1
  120. package/dist/index.js +14 -6
  121. package/dist/index.js.map +1 -1
  122. package/dist/integrityStore.d.ts +0 -8
  123. package/dist/integrityStore.d.ts.map +1 -1
  124. package/dist/integrityStore.js +6 -29
  125. package/dist/integrityStore.js.map +1 -1
  126. package/dist/lib/approvedBypasses.d.ts +76 -0
  127. package/dist/lib/approvedBypasses.d.ts.map +1 -0
  128. package/dist/lib/approvedBypasses.js +140 -0
  129. package/dist/lib/approvedBypasses.js.map +1 -0
  130. package/dist/lib/complianceProbes.d.ts.map +1 -1
  131. package/dist/lib/complianceProbes.js +5 -0
  132. package/dist/lib/complianceProbes.js.map +1 -1
  133. package/dist/lib/decisionsDigest.d.ts +99 -0
  134. package/dist/lib/decisionsDigest.d.ts.map +1 -0
  135. package/dist/lib/decisionsDigest.js +199 -0
  136. package/dist/lib/decisionsDigest.js.map +1 -0
  137. package/dist/lib/egressPolicy.d.ts +69 -0
  138. package/dist/lib/egressPolicy.d.ts.map +1 -0
  139. package/dist/lib/egressPolicy.js +126 -0
  140. package/dist/lib/egressPolicy.js.map +1 -0
  141. package/dist/lib/gitWorktrees.d.ts +140 -0
  142. package/dist/lib/gitWorktrees.d.ts.map +1 -0
  143. package/dist/lib/gitWorktrees.js +191 -0
  144. package/dist/lib/gitWorktrees.js.map +1 -0
  145. package/dist/lib/policySnapshot.d.ts +224 -0
  146. package/dist/lib/policySnapshot.d.ts.map +1 -0
  147. package/dist/lib/policySnapshot.js +637 -0
  148. package/dist/lib/policySnapshot.js.map +1 -0
  149. package/dist/lib/processPoller.d.ts +44 -0
  150. package/dist/lib/processPoller.d.ts.map +1 -1
  151. package/dist/lib/processPoller.js +176 -1
  152. package/dist/lib/processPoller.js.map +1 -1
  153. package/dist/lib/runtimeEnv.d.ts +8 -0
  154. package/dist/lib/runtimeEnv.d.ts.map +1 -1
  155. package/dist/lib/runtimeEnv.js +3 -0
  156. package/dist/lib/runtimeEnv.js.map +1 -1
  157. package/dist/localTrajectoryHeuristics.d.ts +51 -0
  158. package/dist/localTrajectoryHeuristics.d.ts.map +1 -0
  159. package/dist/localTrajectoryHeuristics.js +90 -0
  160. package/dist/localTrajectoryHeuristics.js.map +1 -0
  161. package/dist/sessionReporter.d.ts +70 -0
  162. package/dist/sessionReporter.d.ts.map +1 -0
  163. package/dist/sessionReporter.js +140 -0
  164. package/dist/sessionReporter.js.map +1 -0
  165. package/dist/skillWriter.d.ts +1 -1
  166. package/dist/skillWriter.d.ts.map +1 -1
  167. package/dist/skillWriter.js +5 -2
  168. package/dist/skillWriter.js.map +1 -1
  169. package/dist/syncLoop.d.ts +105 -3
  170. package/dist/syncLoop.d.ts.map +1 -1
  171. package/dist/syncLoop.js +397 -65
  172. package/dist/syncLoop.js.map +1 -1
  173. package/dist/trajectoryMonitor.d.ts +2 -0
  174. package/dist/trajectoryMonitor.d.ts.map +1 -1
  175. package/dist/trajectoryMonitor.js +57 -0
  176. package/dist/trajectoryMonitor.js.map +1 -1
  177. package/dist/watcher/driftWatcher.d.ts.map +1 -1
  178. package/dist/watcher/driftWatcher.js +12 -3
  179. package/dist/watcher/driftWatcher.js.map +1 -1
  180. package/dist/watcher/settingsGuard.d.ts +23 -0
  181. package/dist/watcher/settingsGuard.d.ts.map +1 -1
  182. package/dist/watcher/settingsGuard.js +202 -17
  183. package/dist/watcher/settingsGuard.js.map +1 -1
  184. package/package.json +37 -6
  185. package/dist/brainIndexer.d.ts +0 -51
  186. package/dist/brainIndexer.d.ts.map +0 -1
  187. package/dist/brainIndexer.js +0 -148
  188. package/dist/brainIndexer.js.map +0 -1
  189. package/dist/harness/cursorHooksJson.d.ts +0 -29
  190. package/dist/harness/cursorHooksJson.d.ts.map +0 -1
  191. package/dist/harness/cursorHooksJson.js +0 -130
  192. package/dist/harness/cursorHooksJson.js.map +0 -1
  193. package/dist/harness/gooseHooksWriter.d.ts +0 -27
  194. package/dist/harness/gooseHooksWriter.d.ts.map +0 -1
  195. package/dist/harness/gooseHooksWriter.js +0 -147
  196. package/dist/harness/gooseHooksWriter.js.map +0 -1
  197. package/dist/harness/openhandsHooksWriter.d.ts +0 -23
  198. package/dist/harness/openhandsHooksWriter.d.ts.map +0 -1
  199. package/dist/harness/openhandsHooksWriter.js +0 -146
  200. package/dist/harness/openhandsHooksWriter.js.map +0 -1
@@ -20,17 +20,9 @@ import * as path from 'node:path';
20
20
  import * as os from 'node:os';
21
21
  import { createLogger } from '@intutic/logger';
22
22
  import { newIso } from '@intutic/id';
23
+ import { emitJsGate, emitJsFailClosedPrelude } from './gateBody.js';
23
24
  const log = createLogger('sync-claude-desktop-hooks');
24
25
  /** Governance-sensitive paths that the hook gate protects (mirrors claudeCodeHooks). */
25
- const PROTECTED_PATHS = [
26
- path.join(os.homedir(), '.claude', 'settings.json'),
27
- path.join(os.homedir(), '.claude', 'settings.local.json'),
28
- '.claude/settings.json',
29
- '.claude/settings.local.json',
30
- '.intutic/hooks',
31
- '.intutic/integrity.json',
32
- '.intutic/context_integrity.json',
33
- ];
34
26
  /** Resolve the OS-specific claude_desktop_config.json path. */
35
27
  function resolveClaudeDesktopConfigPath() {
36
28
  switch (process.platform) {
@@ -60,6 +52,7 @@ function buildClaudeDesktopCheckScript(proxyUrl, workspaceRoot, workspaceId) {
60
52
  * Workspace: ${workspaceId}
61
53
  */
62
54
  'use strict';
55
+ ${emitJsFailClosedPrelude({ harness: 'claude-desktop', contract: 'exit2' })}
63
56
  const path = require('path');
64
57
  const os = require('os');
65
58
  const fs = require('fs');
@@ -91,11 +84,12 @@ function loadRuntimeEnv() {
91
84
  }
92
85
 
93
86
  // ── Governance-sensitive paths ────────────────────────────────────────────────
94
- const PROTECTED_PATHS = ${JSON.stringify(PROTECTED_PATHS)};
87
+ ${emitJsGate({ harness: "claude-desktop", contract: 'exit2' })}
95
88
 
96
89
  // ── Dual-path logEvent ────────────────────────────────────────────────────────
97
90
  const HOOK_EVENTS_LOG = ${JSON.stringify(hookEventsLog)};
98
91
 
92
+ let _intuticSessionId = '';
99
93
  function logEvent(env, verdict, toolName, reason) {
100
94
  const ts = new Date().toISOString();
101
95
  const incidentId = crypto.createHash('sha1')
@@ -103,13 +97,16 @@ function logEvent(env, verdict, toolName, reason) {
103
97
  .digest('hex').slice(0, 16);
104
98
 
105
99
  const entry = {
106
- event: verdict === 'blocked' ? 'tool_blocked' : 'tool_allowed',
100
+ // Passed through, not collapsed to two values: the advisory tier emits
101
+ // 'tool_flagged', and a ternary here silently recorded it as an allow.
102
+ event: verdict,
107
103
  toolName,
108
104
  reason: reason || '',
109
105
  workspaceId: env.INTUTIC_WORKSPACE_ID,
110
106
  harnessType: 'claude-desktop',
111
107
  timestamp: ts,
112
108
  incidentId,
109
+ ...(_intuticSessionId ? { sessionId: _intuticSessionId } : {}),
113
110
  };
114
111
 
115
112
  // Path B — reliable file append
@@ -149,49 +146,36 @@ process.stdin.on('data', (chunk) => { inputData += chunk; });
149
146
  process.stdin.on('end', () => {
150
147
  try {
151
148
  const ctx = JSON.parse(inputData);
152
- const toolName = (ctx.tool_name || ctx.toolName || '').toLowerCase();
149
+ _intuticSessionId = ctx.session_id || ctx.sessionId || ctx.conversation_id || ctx.conversationId || ctx.task_id || ctx.taskId || '';
150
+ // An envelope carrying none of the tool fields extracts to empty strings,
151
+ // which match no rule — an allow. Refused instead; see the guard itself.
152
+ intuticGuardEnvelope(ctx, ['tool_name', 'toolName', 'tool_input', 'toolInput'],
153
+ (v, t, r) => logEvent(runtimeEnv, v, t, r));
154
+ const toolName = (ctx.tool_name || ctx.toolName || '').toLowerCase()
155
+ // Case preserved for the gate. A BLOCK: SOP compiles to a tool-name
156
+ // pattern the operator wrote as they see it (Bash, Write) and this
157
+ // harness lowercases the tool for its own matching. Handing the gate the
158
+ // lowercased form means every SOP tool rule silently matches nothing here
159
+ // while appearing active everywhere else.
160
+ const rawToolName = ctx.tool_name || ctx.toolName || '';
153
161
  const toolInput = ctx.tool_input || ctx.toolInput || {};
154
162
  const toolInputStr = JSON.stringify(toolInput);
155
163
 
156
- // 1. Protected-path guard
157
- if (['edit', 'write', 'multiedit'].includes(toolName)) {
158
- const targetPath = toolInput.path || toolInput.file_path ||
159
- toolInput.new_path || toolInput.target || '';
160
- for (const p of PROTECTED_PATHS) {
161
- if (targetPath.includes(p) || String(targetPath).startsWith(p)) {
162
- const reason = 'Attempt to modify governance-protected path: "' + targetPath + '". ' +
163
- 'To change governance policy, update your SOP via the Intutic control plane.';
164
- console.error('[Intutic Guardrail] BLOCKED: ' + reason);
165
- logEvent(runtimeEnv, 'blocked', toolName, reason);
166
- process.exit(2);
167
- }
168
- }
169
- }
170
-
171
- // 2. Bash/shell command bypass guard
172
- if (toolName === 'bash') {
173
- const cmd = toolInput.command || toolInput.cmd || '';
174
- const BYPASS_PATTERNS = [
175
- 'chflags nouchg',
176
- 'chattr -i',
177
- 'rm -rf .intutic',
178
- 'CLAUDE_DESKTOP_HOOKS=',
179
- ];
180
- for (const pattern of BYPASS_PATTERNS) {
181
- if (cmd.includes(pattern)) {
182
- const reason = 'Shell command matches governance bypass pattern: "' + pattern + '"';
183
- console.error('[Intutic Guardrail] BLOCKED: ' + reason);
184
- logEvent(runtimeEnv, 'blocked', toolName, reason);
185
- process.exit(2);
186
- }
187
- }
188
- }
164
+ // Both guards used to be gated on the tool name — paths only for
165
+ // edit/write/multiedit, commands only for a tool literally called "bash".
166
+ // A harness that names its shell tool anything else (run_command, terminal,
167
+ // execute) walked past both. The shared gate keys on the *arguments*, so a
168
+ // tool name nobody anticipated is no longer a bypass.
169
+ const targetPath = toolInput.path || toolInput.file_path || toolInput.filePath ||
170
+ toolInput.new_path || toolInput.target || toolInput.notebook_path || '';
171
+ const command = toolInput.command || toolInput.cmd || toolInput.script || '';
172
+ intuticGate(rawToolName, targetPath, command, (v, t, r) => logEvent(runtimeEnv, v, t, r), runtimeEnv.INTUTIC_WORKSPACE_ID, toolInput);
189
173
 
190
- logEvent(runtimeEnv, 'allowed', toolName, '');
174
+ logEvent(runtimeEnv, 'tool_allowed', toolName, '');
191
175
  process.exit(0);
192
176
  } catch (err) {
193
177
  console.error('[Intutic Guardrail] Hook error (blocking for safety):', err);
194
- logEvent(runtimeEnv, 'blocked', 'unknown', String(err));
178
+ logEvent(runtimeEnv, 'tool_blocked', 'unknown', String(err));
195
179
  process.exit(2);
196
180
  }
197
181
  });
@@ -238,7 +222,10 @@ export async function writeClaudeDesktopHooks(workspaceRoot, proxyUrl, workspace
238
222
  const existingHooks = existingConfig.hooks ?? {};
239
223
  const existingPreToolUse = existingHooks.PreToolUse ?? [];
240
224
  // De-duplicate: remove any existing Intutic entry for Bash then re-add
241
- const HOOK_MATCHERS = ['Bash', 'Edit', 'Write', 'MultiEdit'];
225
+ // M3: 'mcp__.*' routes Claude Desktop's mcp__<server>__<tool> calls into
226
+ // this same gate script — see the identical note in claudeCodeHooks.ts,
227
+ // which shares this harness's PreToolUse contract exactly.
228
+ const HOOK_MATCHERS = ['Bash', 'Edit', 'Write', 'MultiEdit', 'mcp__.*'];
242
229
  const intuticCmd = `node ${hookScriptPath}`;
243
230
  const filteredPTU = existingPreToolUse.filter((entry) => {
244
231
  if (typeof entry !== 'object' || entry === null)
@@ -1 +1 @@
1
- {"version":3,"file":"claudeDesktopHooks.js","sourceRoot":"","sources":["../../src/harness/claudeDesktopHooks.ts"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"claudeDesktopHooks.js","sourceRoot":"","sources":["../../src/harness/claudeDesktopHooks.ts"],"names":[],"mappings":"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"}
@@ -1 +1 @@
1
- {"version":3,"file":"clineHooks.d.ts","sourceRoot":"","sources":["../../src/harness/clineHooks.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;GAcG;AAoCH;;;;;GAKG;AACH,wBAAsB,eAAe,CACnC,aAAa,EAAE,MAAM,EACrB,QAAQ,EAAE,MAAM,EAChB,WAAW,SAAK,GACf,OAAO,CAAC,IAAI,CAAC,CA6Hf"}
1
+ {"version":3,"file":"clineHooks.d.ts","sourceRoot":"","sources":["../../src/harness/clineHooks.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;GAcG;AAsBH;;;;;GAKG;AACH,wBAAsB,eAAe,CACnC,aAAa,EAAE,MAAM,EACrB,QAAQ,EAAE,MAAM,EAChB,WAAW,SAAK,GACf,OAAO,CAAC,IAAI,CAAC,CAkJf"}
@@ -17,6 +17,7 @@ import * as fs from 'node:fs/promises';
17
17
  import * as path from 'node:path';
18
18
  import { createLogger } from '@intutic/logger';
19
19
  import { newIso } from '@intutic/id';
20
+ import { emitJsGate, emitJsFailClosedPrelude, REVIEW_REQUESTS_BASENAME } from './gateBody.js';
20
21
  const log = createLogger('sync-cline-hooks');
21
22
  /** Tools Cline can invoke that require governance interception. */
22
23
  const GOVERNED_TOOLS = [
@@ -29,20 +30,6 @@ const GOVERNED_TOOLS = [
29
30
  'rename_file',
30
31
  'read_file', // watch for exfiltration patterns
31
32
  ];
32
- /**
33
- * Governance-sensitive paths that must never be agent-modified.
34
- * Mirrors the protected path list in claudeCodeHooks.ts.
35
- */
36
- const PROTECTED_PATH_FRAGMENTS = [
37
- '.cline/hooks',
38
- '.intutic/hooks',
39
- '.intutic/integrity.json',
40
- '.claude/settings.json',
41
- '.cursor/hooks.json',
42
- '.codeium/windsurf/hooks.json',
43
- '.agents/plugins/intutic-governance',
44
- '.openhands/hooks.json',
45
- ];
46
33
  /**
47
34
  * Write the Intutic governance hook into .cline/hooks/.
48
35
  *
@@ -79,6 +66,7 @@ export async function writeClineHooks(workspaceRoot, proxyUrl, workspaceId = '')
79
66
  * Proxy: ${proxyUrl}
80
67
  * Generated: ${newIso()}
81
68
  */
69
+ ${emitJsFailClosedPrelude({ harness: 'cline', contract: 'stdout-cancel' })}
82
70
  const path = require('path');
83
71
  const os = require('os');
84
72
  const fs = require('fs');
@@ -100,12 +88,25 @@ try {
100
88
 
101
89
  const HOOK_EVENTS_LOG = path.join(os.homedir(), '.intutic', 'events', 'cline-hook-events.jsonl');
102
90
 
91
+ let _intuticSessionId = '';
103
92
  function logEvent(verdict, toolName, reason) {
104
93
  try {
105
94
  const ts = new Date().toISOString();
106
95
  const incidentId = crypto.createHash('sha1').update(ts + toolName + _intuticWsId).digest('hex').slice(0, 16);
107
- const entry = JSON.stringify({ event: verdict === 'blocked' ? 'tool_blocked' : 'tool_allowed', toolName, reason: reason || '', workspaceId: _intuticWsId, harnessType: 'cline', timestamp: ts, incidentId }) + '\\n';
108
- try { fs.appendFileSync(HOOK_EVENTS_LOG, entry, { flag: 'a' }); } catch {}
96
+ const entry = JSON.stringify({ // Passed through, not collapsed to two values: the advisory tier emits
97
+ // 'tool_flagged', and a ternary here silently recorded it as an allow.
98
+ event: verdict, toolName, reason: reason || '', workspaceId: _intuticWsId, harnessType: 'cline', timestamp: ts, incidentId, ...(_intuticSessionId ? { sessionId: _intuticSessionId } : {}) }) + '\\n';
99
+ // mkdir first. This was a bare appendFileSync inside a swallowing catch, so
100
+ // on any machine where nothing else had created ~/.intutic/events the append
101
+ // threw ENOENT and was discarded — every Cline audit line, including blocks,
102
+ // silently dropped. The other writers create this directory from the
103
+ // TypeScript side; Cline's writer only creates .cline/hooks, so its log
104
+ // existed only by luck.
105
+ try {
106
+ const _dir = path.dirname(HOOK_EVENTS_LOG);
107
+ if (!fs.existsSync(_dir)) fs.mkdirSync(_dir, { recursive: true });
108
+ fs.appendFileSync(HOOK_EVENTS_LOG, entry, { flag: 'a' });
109
+ } catch {}
109
110
  if (_intuticKey) {
110
111
  try {
111
112
  const body = JSON.stringify({ events: [JSON.parse(entry)] });
@@ -119,39 +120,46 @@ function logEvent(verdict, toolName, reason) {
119
120
  } catch {}
120
121
  }
121
122
 
122
- const PROTECTED_PATHS = ${JSON.stringify(PROTECTED_PATH_FRAGMENTS)};
123
+ ${emitJsGate({ harness: 'cline', contract: 'stdout-cancel', reviewRequestFile: path.join(workspaceRoot, '.intutic', 'events', REVIEW_REQUESTS_BASENAME) })}
123
124
 
124
125
  let raw = '';
125
126
  process.stdin.on('data', (c) => { raw += c; });
126
127
  process.stdin.on('end', () => {
127
128
  try {
128
129
  const ctx = JSON.parse(raw);
129
- const tool = (ctx.tool_name || '').toLowerCase();
130
+ _intuticSessionId = ctx.session_id || ctx.sessionId || ctx.conversation_id || ctx.conversationId || ctx.task_id || ctx.taskId || '';
131
+ // An envelope carrying none of the tool fields extracts to empty strings,
132
+ // which match no rule — an allow. Refused instead, and the refusal CANCELS
133
+ // (this harness ignores exit codes); see intuticGuardEnvelope.
134
+ intuticGuardEnvelope(ctx, ['tool_name', 'toolName', 'tool_input', 'toolInput'], logEvent);
135
+ const tool = (ctx.tool_name || '').toLowerCase()
136
+ // Case preserved for the gate. A BLOCK: SOP compiles to a tool-name
137
+ // pattern the operator wrote as they see it (Bash, Write) and this
138
+ // harness lowercases the tool for its own matching. Handing the gate the
139
+ // lowercased form means every SOP tool rule silently matches nothing here
140
+ // while appearing active everywhere else.
141
+ const rawToolName = ctx.tool_name || '';
130
142
  const input = ctx.tool_input || {};
131
143
  const inputStr = JSON.stringify(input);
132
144
 
133
- // Protected-path guard: block any write/edit/delete targeting governance files
134
- const targetPath = input.path || input.file_path || input.target || input.source || '';
135
- if (['write_to_file','str_replace_based_edit_tool','replace_in_file','create_file','delete_file','rename_file'].includes(tool)) {
136
- for (const p of PROTECTED_PATHS) {
137
- if (String(targetPath).includes(p)) {
138
- logEvent('blocked', tool, 'Blocked: attempt to modify governance-protected path "' + targetPath + '"');
139
- process.stdout.write(JSON.stringify({
140
- cancel: true,
141
- errorMessage: '[Intutic Governance] Blocked: attempt to modify governance-protected path "' + targetPath + '". Update policy via the Intutic control plane.',
142
- }));
143
- process.exit(0);
144
- }
145
- }
146
- }
145
+ // This gate had two defects that hid each other. Its path list was
146
+ // hand-rolled as PROTECTED_PATH_FRAGMENTS with four of the twelve universal
147
+ // entries missing, and harnessProtectedPaths.test.ts keyed its coverage on
148
+ // finding a constant named PROTECTED_PATHS — so not having one read as "not
149
+ // a harness" rather than "unguarded". It also had no command guard at all,
150
+ // and ran only for six named file tools.
151
+ const targetPath = input.path || input.file_path || input.filePath ||
152
+ input.target || input.source || input.notebook_path || '';
153
+ const command = input.command || input.cmd || input.script || '';
154
+ intuticGate(rawToolName, targetPath, command, logEvent, _intuticWsId, input);
147
155
 
148
156
  // Allow all other tool calls
149
- logEvent('allowed', tool || 'unknown', '');
157
+ logEvent('tool_allowed', tool || 'unknown', '');
150
158
  process.stdout.write(JSON.stringify({ cancel: false }));
151
159
  process.exit(0);
152
160
  } catch (err) {
153
161
  // Fail CLOSED — hook parse error blocks the tool call
154
- logEvent('blocked', 'unknown', String(err));
162
+ logEvent('tool_blocked', 'unknown', String(err));
155
163
  process.stdout.write(JSON.stringify({
156
164
  cancel: true,
157
165
  errorMessage: '[Intutic Governance] Hook error (fail-closed): ' + String(err),
@@ -1 +1 @@
1
- {"version":3,"file":"clineHooks.js","sourceRoot":"","sources":["../../src/harness/clineHooks.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;GAcG;AAEH,OAAO,KAAK,EAAE,MAAM,kBAAkB,CAAA;AACtC,OAAO,KAAK,IAAI,MAAM,WAAW,CAAA;AACjC,OAAO,EAAE,YAAY,EAAE,MAAM,iBAAiB,CAAA;AAC9C,OAAO,EAAE,MAAM,EAAE,MAAM,aAAa,CAAA;AAEpC,MAAM,GAAG,GAAG,YAAY,CAAC,kBAAkB,CAAC,CAAA;AAE5C,mEAAmE;AACnE,MAAM,cAAc,GAAG;IACrB,iBAAiB;IACjB,eAAe;IACf,6BAA6B;IAC7B,iBAAiB;IACjB,aAAa;IACb,aAAa;IACb,aAAa;IACb,WAAW,EAAG,kCAAkC;CACjD,CAAA;AAED;;;GAGG;AACH,MAAM,wBAAwB,GAAG;IAC/B,cAAc;IACd,gBAAgB;IAChB,yBAAyB;IACzB,uBAAuB;IACvB,oBAAoB;IACpB,8BAA8B;IAC9B,oCAAoC;IACpC,uBAAuB;CACxB,CAAA;AAED;;;;;GAKG;AACH,MAAM,CAAC,KAAK,UAAU,eAAe,CACnC,aAAqB,EACrB,QAAgB,EAChB,WAAW,GAAG,EAAE;IAEhB,MAAM,QAAQ,GAAG,IAAI,CAAC,IAAI,CAAC,aAAa,EAAE,QAAQ,EAAE,OAAO,CAAC,CAAA;IAC5D,MAAM,aAAa,GAAG,IAAI,CAAC,IAAI,CAAC,QAAQ,EAAE,YAAY,CAAC,CAAA;IACvD,MAAM,eAAe,GAAG,IAAI,CAAC,IAAI,CAAC,QAAQ,EAAE,kBAAkB,CAAC,CAAA;IAE/D,MAAM,EAAE,CAAC,KAAK,CAAC,QAAQ,EAAE,EAAE,SAAS,EAAE,IAAI,EAAE,CAAC,CAAA;IAE7C,uEAAuE;IACvE,6EAA6E;IAC7E,sDAAsD;IACtD,MAAM,WAAW,GAAG;QAClB,QAAQ,EAAE,yDAAyD;QACnE,SAAS,EAAE,MAAM,EAAE;QACnB,KAAK,EAAE,cAAc,CAAC,GAAG,CAAC,CAAC,IAAI,EAAE,EAAE,CAAC,CAAC;YACnC,IAAI,EAAE,YAAY;YAClB,OAAO,EAAE,IAAI,IAAI,GAAG;YACpB,OAAO,EAAE,QAAQ,eAAe,EAAE;SACnC,CAAC,CAAC;KACJ,CAAA;IAED,MAAM,OAAO,GAAG,aAAa,GAAG,cAAc,CAAA;IAC9C,MAAM,EAAE,CAAC,SAAS,CAAC,OAAO,EAAE,IAAI,CAAC,SAAS,CAAC,WAAW,EAAE,IAAI,EAAE,CAAC,CAAC,GAAG,IAAI,EAAE,OAAO,CAAC,CAAA;IACjF,MAAM,EAAE,CAAC,MAAM,CAAC,OAAO,EAAE,aAAa,CAAC,CAAA;IAEvC,wEAAwE;IACxE,wDAAwD;IACxD,uFAAuF;IACvF,MAAM,WAAW,GAAG;;;;YAIV,QAAQ;gBACJ,MAAM,EAAE;;;;;;;;;;gFAUwD,IAAI,CAAC,SAAS,CAAC,WAAW,CAAC;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;0BAgCjF,IAAI,CAAC,SAAS,CAAC,wBAAwB,CAAC;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;CAwCjE,CAAA;IAEC,MAAM,SAAS,GAAG,eAAe,GAAG,cAAc,CAAA;IAClD,MAAM,EAAE,CAAC,SAAS,CAAC,SAAS,EAAE,WAAW,EAAE,OAAO,CAAC,CAAA;IACnD,MAAM,EAAE,CAAC,MAAM,CAAC,SAAS,EAAE,eAAe,CAAC,CAAA;IAC3C,MAAM,EAAE,CAAC,KAAK,CAAC,eAAe,EAAE,KAAK,CAAC,CAAA;IAEtC,GAAG,CAAC,IAAI,CACN,EAAE,MAAM,EAAE,qBAAqB,EAAE,QAAQ,EAAE,SAAS,EAAE,cAAc,CAAC,MAAM,EAAE,EAC7E,gCAAgC,CACjC,CAAA;AACH,CAAC"}
1
+ {"version":3,"file":"clineHooks.js","sourceRoot":"","sources":["../../src/harness/clineHooks.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;GAcG;AAEH,OAAO,KAAK,EAAE,MAAM,kBAAkB,CAAA;AACtC,OAAO,KAAK,IAAI,MAAM,WAAW,CAAA;AACjC,OAAO,EAAE,YAAY,EAAE,MAAM,iBAAiB,CAAA;AAC9C,OAAO,EAAE,MAAM,EAAE,MAAM,aAAa,CAAA;AACpC,OAAO,EAAE,UAAU,EAAE,uBAAuB,EAAE,wBAAwB,EAAE,MAAM,eAAe,CAAA;AAE7F,MAAM,GAAG,GAAG,YAAY,CAAC,kBAAkB,CAAC,CAAA;AAE5C,mEAAmE;AACnE,MAAM,cAAc,GAAG;IACrB,iBAAiB;IACjB,eAAe;IACf,6BAA6B;IAC7B,iBAAiB;IACjB,aAAa;IACb,aAAa;IACb,aAAa;IACb,WAAW,EAAG,kCAAkC;CACjD,CAAA;AAED;;;;;GAKG;AACH,MAAM,CAAC,KAAK,UAAU,eAAe,CACnC,aAAqB,EACrB,QAAgB,EAChB,WAAW,GAAG,EAAE;IAEhB,MAAM,QAAQ,GAAG,IAAI,CAAC,IAAI,CAAC,aAAa,EAAE,QAAQ,EAAE,OAAO,CAAC,CAAA;IAC5D,MAAM,aAAa,GAAG,IAAI,CAAC,IAAI,CAAC,QAAQ,EAAE,YAAY,CAAC,CAAA;IACvD,MAAM,eAAe,GAAG,IAAI,CAAC,IAAI,CAAC,QAAQ,EAAE,kBAAkB,CAAC,CAAA;IAE/D,MAAM,EAAE,CAAC,KAAK,CAAC,QAAQ,EAAE,EAAE,SAAS,EAAE,IAAI,EAAE,CAAC,CAAA;IAE7C,uEAAuE;IACvE,6EAA6E;IAC7E,sDAAsD;IACtD,MAAM,WAAW,GAAG;QAClB,QAAQ,EAAE,yDAAyD;QACnE,SAAS,EAAE,MAAM,EAAE;QACnB,KAAK,EAAE,cAAc,CAAC,GAAG,CAAC,CAAC,IAAI,EAAE,EAAE,CAAC,CAAC;YACnC,IAAI,EAAE,YAAY;YAClB,OAAO,EAAE,IAAI,IAAI,GAAG;YACpB,OAAO,EAAE,QAAQ,eAAe,EAAE;SACnC,CAAC,CAAC;KACJ,CAAA;IAED,MAAM,OAAO,GAAG,aAAa,GAAG,cAAc,CAAA;IAC9C,MAAM,EAAE,CAAC,SAAS,CAAC,OAAO,EAAE,IAAI,CAAC,SAAS,CAAC,WAAW,EAAE,IAAI,EAAE,CAAC,CAAC,GAAG,IAAI,EAAE,OAAO,CAAC,CAAA;IACjF,MAAM,EAAE,CAAC,MAAM,CAAC,OAAO,EAAE,aAAa,CAAC,CAAA;IAEvC,wEAAwE;IACxE,wDAAwD;IACxD,uFAAuF;IACvF,MAAM,WAAW,GAAG;;;;YAIV,QAAQ;gBACJ,MAAM,EAAE;;EAEtB,uBAAuB,CAAC,EAAE,OAAO,EAAE,OAAO,EAAE,QAAQ,EAAE,eAAe,EAAE,CAAC;;;;;;;;;gFASM,IAAI,CAAC,SAAS,CAAC,WAAW,CAAC;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;EA6CzG,UAAU,CAAC,EAAE,OAAO,EAAE,OAAO,EAAE,QAAQ,EAAE,eAAe,EAAE,iBAAiB,EAAE,IAAI,CAAC,IAAI,CAAC,aAAa,EAAE,UAAU,EAAE,QAAQ,EAAE,wBAAwB,CAAC,EAAE,CAAC;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;CA+CzJ,CAAA;IAEC,MAAM,SAAS,GAAG,eAAe,GAAG,cAAc,CAAA;IAClD,MAAM,EAAE,CAAC,SAAS,CAAC,SAAS,EAAE,WAAW,EAAE,OAAO,CAAC,CAAA;IACnD,MAAM,EAAE,CAAC,MAAM,CAAC,SAAS,EAAE,eAAe,CAAC,CAAA;IAC3C,MAAM,EAAE,CAAC,KAAK,CAAC,eAAe,EAAE,KAAK,CAAC,CAAA;IAEtC,GAAG,CAAC,IAAI,CACN,EAAE,MAAM,EAAE,qBAAqB,EAAE,QAAQ,EAAE,SAAS,EAAE,cAAc,CAAC,MAAM,EAAE,EAC7E,gCAAgC,CACjC,CAAA;AACH,CAAC"}
@@ -0,0 +1,36 @@
1
+ /**
2
+ * codexHooks.ts — Codex CLI PreToolUse hook injection.
3
+ *
4
+ * Codex CLI executes PreToolUse hooks registered in `hooks.json`:
5
+ * - User-level: ~/.codex/hooks.json
6
+ * - Project-level: <workspaceRoot>/.codex/hooks.json
7
+ *
8
+ * Each PreToolUse entry carries a `matcher` regex over tool names; the hook
9
+ * command receives JSON on stdin — {tool_name, tool_use_id, tool_input}, with
10
+ * a shell command at tool_input.command — and refuses with **exit code 2**
11
+ * (stderr becomes the reason shown to the agent). That is the same stdin shape
12
+ * and exit-2 contract the shared JS gate already speaks, so the gate body is a
13
+ * drop-in; this writer's whole job is emitting the gate file and registering
14
+ * it in both hooks.json files without destroying anything the user put there.
15
+ *
16
+ * (Codex also accepts a stdout `{"hookSpecificOutput":{"permissionDecision":
17
+ * "deny"}}` object; exit 2 is used here because it is the contract every other
18
+ * exit-2 harness shares and the one `generatedGateBehaviour.test.ts` drives.)
19
+ *
20
+ * Historically codex was governed by `.env.intutic` proxy routing only — an
21
+ * egress control, not a tool gate. This closes that gap.
22
+ *
23
+ * @module
24
+ */
25
+ /**
26
+ * Write the Codex gate script and register it at user and project level.
27
+ *
28
+ * Safe to call repeatedly — uses atomic rename (`.intutic-tmp` → final path)
29
+ * and command-keyed de-duplication in both hooks.json files.
30
+ *
31
+ * @param workspaceRoot - Absolute workspace root path.
32
+ * @param proxyUrl - Intutic proxy URL written into the gate header.
33
+ * @param workspaceId - Workspace ID embedded in every hook event payload.
34
+ */
35
+ export declare function writeCodexHooks(workspaceRoot: string, proxyUrl: string, workspaceId?: string): Promise<void>;
36
+ //# sourceMappingURL=codexHooks.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"codexHooks.d.ts","sourceRoot":"","sources":["../../src/harness/codexHooks.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;;GAuBG;AAwLH;;;;;;;;;GASG;AACH,wBAAsB,eAAe,CACnC,aAAa,EAAE,MAAM,EACrB,QAAQ,EAAE,MAAM,EAChB,WAAW,SAAK,GACf,OAAO,CAAC,IAAI,CAAC,CAwBf"}
@@ -0,0 +1,227 @@
1
+ /**
2
+ * codexHooks.ts — Codex CLI PreToolUse hook injection.
3
+ *
4
+ * Codex CLI executes PreToolUse hooks registered in `hooks.json`:
5
+ * - User-level: ~/.codex/hooks.json
6
+ * - Project-level: <workspaceRoot>/.codex/hooks.json
7
+ *
8
+ * Each PreToolUse entry carries a `matcher` regex over tool names; the hook
9
+ * command receives JSON on stdin — {tool_name, tool_use_id, tool_input}, with
10
+ * a shell command at tool_input.command — and refuses with **exit code 2**
11
+ * (stderr becomes the reason shown to the agent). That is the same stdin shape
12
+ * and exit-2 contract the shared JS gate already speaks, so the gate body is a
13
+ * drop-in; this writer's whole job is emitting the gate file and registering
14
+ * it in both hooks.json files without destroying anything the user put there.
15
+ *
16
+ * (Codex also accepts a stdout `{"hookSpecificOutput":{"permissionDecision":
17
+ * "deny"}}` object; exit 2 is used here because it is the contract every other
18
+ * exit-2 harness shares and the one `generatedGateBehaviour.test.ts` drives.)
19
+ *
20
+ * Historically codex was governed by `.env.intutic` proxy routing only — an
21
+ * egress control, not a tool gate. This closes that gap.
22
+ *
23
+ * @module
24
+ */
25
+ import * as fs from 'node:fs/promises';
26
+ import * as path from 'node:path';
27
+ import * as os from 'node:os';
28
+ import { createLogger } from '@intutic/logger';
29
+ import { newIso } from '@intutic/id';
30
+ import { emitJsGate, emitJsFailClosedPrelude } from './gateBody.js';
31
+ const log = createLogger('sync-codex-hooks');
32
+ /**
33
+ * The pre-tool gate script — receives Codex's PreToolUse JSON on stdin,
34
+ * exits 0 to allow or 2 to block.
35
+ */
36
+ function buildHookScript(proxyUrl, workspaceRoot, workspaceId) {
37
+ const hookEventsLog = path.join(workspaceRoot, '.intutic', 'events', 'hook-events.jsonl');
38
+ return `#!/usr/bin/env node
39
+ /**
40
+ * Intutic Codex CLI governance gate.
41
+ * Auto-generated by intutic sync-daemon. DO NOT EDIT.
42
+ * Proxy: ${proxyUrl}
43
+ * Generated: ${newIso()}
44
+ */
45
+ ${emitJsFailClosedPrelude({ harness: 'codex', contract: 'exit2' })}
46
+ const path = require('path');
47
+ const os = require('os');
48
+ const fs = require('fs');
49
+ const crypto = require('crypto');
50
+ const https = require('https');
51
+
52
+ // Runtime credentials
53
+ const _runtimeEnvPath = path.join(os.homedir(), '.intutic', 'env', 'runtime.env');
54
+ let _intuticHost = 'https://api.intutic.ai', _intuticKey = '', _intuticWsId = ${JSON.stringify(workspaceId)};
55
+ try {
56
+ fs.readFileSync(_runtimeEnvPath, 'utf-8').split('\\n').forEach(line => {
57
+ const eq = line.indexOf('='); if (eq < 0) return;
58
+ const k = line.slice(0, eq).trim(), v = line.slice(eq + 1).trim();
59
+ if (k === 'INTUTIC_HOST' && v) _intuticHost = v;
60
+ if (k === 'INTUTIC_API_KEY' && v) _intuticKey = v;
61
+ if (k === 'INTUTIC_WORKSPACE_ID' && v) _intuticWsId = v;
62
+ });
63
+ } catch {}
64
+
65
+ ${emitJsGate({ harness: 'codex', contract: 'exit2' })}
66
+
67
+ let _intuticSessionId = '';
68
+ function logEvent(verdict, toolName, reason) {
69
+ try {
70
+ const ts = new Date().toISOString();
71
+ const incidentId = crypto.createHash('sha1').update(ts + toolName + _intuticWsId).digest('hex').slice(0, 16);
72
+ const entry = JSON.stringify({
73
+ // Passed through, not collapsed to two values: the advisory tier emits
74
+ // 'tool_flagged', and a ternary here silently recorded it as an allow.
75
+ event: verdict,
76
+ toolName, reason: reason || '',
77
+ workspaceId: _intuticWsId,
78
+ harnessType: 'codex',
79
+ timestamp: ts,
80
+ incidentId,
81
+ ...(_intuticSessionId ? { sessionId: _intuticSessionId } : {}),
82
+ }) + '\\n';
83
+ fs.appendFileSync(${JSON.stringify(hookEventsLog)}, entry, { flag: 'a' });
84
+ if (_intuticKey) {
85
+ try {
86
+ const body = JSON.stringify({ events: [JSON.parse(entry)] });
87
+ const urlObj = new URL('/api/v1/hook-events', _intuticHost);
88
+ const mod = urlObj.protocol === 'https:' ? https : require('http');
89
+ const req = mod.request({ hostname: urlObj.hostname, port: urlObj.port || 443, path: urlObj.pathname, method: 'POST', headers: { 'Content-Type': 'application/json', 'Content-Length': Buffer.byteLength(body), 'Authorization': 'Bearer ' + _intuticKey } });
90
+ req.on('error', () => { /* fire-and-forget */ });
91
+ req.write(body); req.end();
92
+ } catch { /* never crash the hook */ }
93
+ }
94
+ } catch { /* never crash the hook */ }
95
+ }
96
+
97
+ let raw = '';
98
+ process.stdin.on('data', (c) => { raw += c; });
99
+ process.stdin.on('end', () => {
100
+ try {
101
+ const ctx = JSON.parse(raw);
102
+ _intuticSessionId = ctx.session_id || ctx.sessionId || '';
103
+ // Verified during the v5 audit: this writer did NOT have the envelope
104
+ // guard its registry note implied it inherited — a payload with neither a
105
+ // tool name nor a tool_input defaulted to toolName 'tool' with empty
106
+ // fields, matched no rule, and was allowed. Refused now, uniformly.
107
+ intuticGuardEnvelope(ctx, ['tool_name', 'toolName', 'tool_input', 'toolInput'], logEvent);
108
+ const input = ctx.tool_input || ctx.toolInput || {};
109
+ const toolName = ctx.tool_name || ctx.toolName || 'tool';
110
+
111
+ // Every spelling any harness uses, matching the bash extractor. A field
112
+ // name this list misses is a path guard that silently does not run — and
113
+ // the caller controls which spelling it sends.
114
+ const targetPath = input.path || input.file_path || input.filePath || input.file ||
115
+ input.target || input.notebook_path || '';
116
+ const command = input.command || input.cmd || input.script || '';
117
+
118
+ intuticGate(toolName, targetPath, command, logEvent, _intuticWsId, input);
119
+
120
+ // Allow
121
+ logEvent('tool_allowed', toolName, '');
122
+ process.exit(0);
123
+ } catch (err) {
124
+ // Fail CLOSED
125
+ const errMsg = String(err);
126
+ process.stderr.write('[Intutic Governance] Hook error (fail-closed): ' + errMsg + '\\n');
127
+ logEvent('tool_blocked', 'unknown', errMsg);
128
+ process.exit(2);
129
+ }
130
+ });
131
+ `;
132
+ }
133
+ /**
134
+ * Merge the Intutic PreToolUse entry into a Codex hooks.json, preserving
135
+ * everything the user registered themselves.
136
+ *
137
+ * De-duplication is by command (any entry whose hooks all invoke this gate
138
+ * script is ours and is replaced), the same convention claudeDesktopHooks
139
+ * uses — so repeated syncs are idempotent and never stack duplicate entries.
140
+ */
141
+ async function mergeCodexHooksJson(configPath, hookScriptPath) {
142
+ let existing = {};
143
+ try {
144
+ existing = JSON.parse(await fs.readFile(configPath, 'utf-8'));
145
+ }
146
+ catch {
147
+ // File doesn't exist or is malformed — start fresh
148
+ }
149
+ const existingHooks = existing.hooks ?? {};
150
+ const existingPreToolUse = Array.isArray(existingHooks.PreToolUse)
151
+ ? existingHooks.PreToolUse
152
+ : [];
153
+ const intuticCmd = `node "${hookScriptPath}"`;
154
+ const filtered = existingPreToolUse.filter((entry) => {
155
+ if (typeof entry !== 'object' || entry === null)
156
+ return true;
157
+ const inner = entry.hooks ?? [];
158
+ return !(inner.length > 0 &&
159
+ inner.every((h) => typeof h === 'object' && h !== null && h.command === intuticCmd));
160
+ });
161
+ // Provenance lives in `description` and NOWHERE else: Codex parses
162
+ // hooks.json with a strict schema ("expected `description` or `hooks`") and
163
+ // REJECTS the entire file over an unknown top-level key. The first version
164
+ // of this writer stamped `_intutic_comment`/`_intutic_last_sync` here, and a
165
+ // live smoke against codex-cli 0.147.0 showed the gate silently never
166
+ // loading — "warning: failed to parse hooks config … unknown field
167
+ // `_intutic_comment`" — while every emitted-script test stayed green. Only
168
+ // fields the consumer's schema names may appear at this level, and the two
169
+ // legacy keys are actively deleted so a file the old writer touched heals
170
+ // on the next sync instead of staying rejected forever.
171
+ delete existing['_intutic_comment'];
172
+ delete existing['_intutic_last_sync'];
173
+ const merged = {
174
+ ...existing,
175
+ description: 'Intutic governance PreToolUse hook (auto-managed; your own hooks are preserved). ' +
176
+ `Last sync: ${newIso()}`,
177
+ hooks: {
178
+ ...existingHooks,
179
+ PreToolUse: [
180
+ ...filtered,
181
+ {
182
+ // One catch-all matcher rather than a tool list: the gate itself
183
+ // decides from the arguments, so a tool name nobody anticipated is
184
+ // still evaluated rather than silently unmatched.
185
+ matcher: '.*',
186
+ hooks: [{ type: 'command', command: intuticCmd }],
187
+ },
188
+ ],
189
+ },
190
+ };
191
+ const tmp = configPath + '.intutic-tmp';
192
+ await fs.writeFile(tmp, JSON.stringify(merged, null, 2) + '\n', 'utf-8');
193
+ await fs.rename(tmp, configPath);
194
+ }
195
+ /**
196
+ * Write the Codex gate script and register it at user and project level.
197
+ *
198
+ * Safe to call repeatedly — uses atomic rename (`.intutic-tmp` → final path)
199
+ * and command-keyed de-duplication in both hooks.json files.
200
+ *
201
+ * @param workspaceRoot - Absolute workspace root path.
202
+ * @param proxyUrl - Intutic proxy URL written into the gate header.
203
+ * @param workspaceId - Workspace ID embedded in every hook event payload.
204
+ */
205
+ export async function writeCodexHooks(workspaceRoot, proxyUrl, workspaceId = '') {
206
+ const hookScriptDir = path.join(workspaceRoot, '.intutic', 'hooks');
207
+ await fs.mkdir(hookScriptDir, { recursive: true });
208
+ await fs.mkdir(path.join(workspaceRoot, '.intutic', 'events'), { recursive: true });
209
+ // Named after its writer — see the note in claudeCodeHooks.ts about two
210
+ // writers overwriting one `pre-tool-check.js`.
211
+ const hookScriptPath = path.join(hookScriptDir, 'codex-check.js');
212
+ const tmpScript = hookScriptPath + '.intutic-tmp';
213
+ await fs.writeFile(tmpScript, buildHookScript(proxyUrl, workspaceRoot, workspaceId), 'utf-8');
214
+ await fs.rename(tmpScript, hookScriptPath);
215
+ await fs.chmod(hookScriptPath, 0o755);
216
+ // 1. Project-level: <root>/.codex/hooks.json
217
+ const projectCodexDir = path.join(workspaceRoot, '.codex');
218
+ await fs.mkdir(projectCodexDir, { recursive: true });
219
+ await mergeCodexHooksJson(path.join(projectCodexDir, 'hooks.json'), hookScriptPath);
220
+ log.info({ action: 'codex_hooks_written', level: 'project', path: projectCodexDir }, 'Codex project-level hooks written');
221
+ // 2. User-level: ~/.codex/hooks.json
222
+ const userCodexDir = path.join(os.homedir(), '.codex');
223
+ await fs.mkdir(userCodexDir, { recursive: true });
224
+ await mergeCodexHooksJson(path.join(userCodexDir, 'hooks.json'), hookScriptPath);
225
+ log.info({ action: 'codex_hooks_written', level: 'user', path: userCodexDir }, 'Codex user-level hooks written');
226
+ }
227
+ //# sourceMappingURL=codexHooks.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"codexHooks.js","sourceRoot":"","sources":["../../src/harness/codexHooks.ts"],"names":[],"mappings":"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"}