@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.
- package/README.md +1 -1
- package/dist/agentReporter.d.ts +24 -11
- package/dist/agentReporter.d.ts.map +1 -1
- package/dist/agentReporter.js +20 -13
- package/dist/agentReporter.js.map +1 -1
- package/dist/configReader.d.ts +45 -25
- package/dist/configReader.d.ts.map +1 -1
- package/dist/configReader.js +70 -46
- package/dist/configReader.js.map +1 -1
- package/dist/configWriter.d.ts +32 -45
- package/dist/configWriter.d.ts.map +1 -1
- package/dist/configWriter.js +52 -686
- package/dist/configWriter.js.map +1 -1
- package/dist/disconnect/harnesses.d.ts +29 -0
- package/dist/disconnect/harnesses.d.ts.map +1 -0
- package/dist/disconnect/harnesses.js +805 -0
- package/dist/disconnect/harnesses.js.map +1 -0
- package/dist/disconnect/index.d.ts +25 -0
- package/dist/disconnect/index.d.ts.map +1 -0
- package/dist/disconnect/index.js +92 -0
- package/dist/disconnect/index.js.map +1 -0
- package/dist/disconnect/mcp.d.ts +81 -0
- package/dist/disconnect/mcp.d.ts.map +1 -0
- package/dist/disconnect/mcp.js +354 -0
- package/dist/disconnect/mcp.js.map +1 -0
- package/dist/disconnect/originals.d.ts +88 -0
- package/dist/disconnect/originals.d.ts.map +1 -0
- package/dist/disconnect/originals.js +257 -0
- package/dist/disconnect/originals.js.map +1 -0
- package/dist/disconnect/plan.d.ts +175 -0
- package/dist/disconnect/plan.d.ts.map +1 -0
- package/dist/disconnect/plan.js +580 -0
- package/dist/disconnect/plan.js.map +1 -0
- package/dist/disconnect/recognise.d.ts +43 -0
- package/dist/disconnect/recognise.d.ts.map +1 -0
- package/dist/disconnect/recognise.js +108 -0
- package/dist/disconnect/recognise.js.map +1 -0
- package/dist/disconnect/tomlLines.d.ts +39 -0
- package/dist/disconnect/tomlLines.d.ts.map +1 -0
- package/dist/disconnect/tomlLines.js +105 -0
- package/dist/disconnect/tomlLines.js.map +1 -0
- package/dist/harness/aiderConfigMerger.d.ts +29 -8
- package/dist/harness/aiderConfigMerger.d.ts.map +1 -1
- package/dist/harness/aiderConfigMerger.js +63 -12
- package/dist/harness/aiderConfigMerger.js.map +1 -1
- package/dist/harness/antigravityCliHooks.d.ts +61 -0
- package/dist/harness/antigravityCliHooks.d.ts.map +1 -0
- package/dist/harness/antigravityCliHooks.js +207 -0
- package/dist/harness/antigravityCliHooks.js.map +1 -0
- package/dist/harness/antigravityHooks.d.ts +12 -2
- package/dist/harness/antigravityHooks.d.ts.map +1 -1
- package/dist/harness/antigravityHooks.js +38 -16
- package/dist/harness/antigravityHooks.js.map +1 -1
- package/dist/harness/antigravityProducts.d.ts +40 -0
- package/dist/harness/antigravityProducts.d.ts.map +1 -0
- package/dist/harness/antigravityProducts.js +109 -0
- package/dist/harness/antigravityProducts.js.map +1 -0
- package/dist/harness/claudeAgentsMd.d.ts +29 -0
- package/dist/harness/claudeAgentsMd.d.ts.map +1 -0
- package/dist/harness/claudeAgentsMd.js +80 -0
- package/dist/harness/claudeAgentsMd.js.map +1 -0
- package/dist/harness/claudeCodeHooks.d.ts +6 -6
- package/dist/harness/claudeCodeHooks.d.ts.map +1 -1
- package/dist/harness/claudeCodeHooks.js +115 -28
- package/dist/harness/claudeCodeHooks.js.map +1 -1
- package/dist/harness/claudeProjectApproval.d.ts +53 -0
- package/dist/harness/claudeProjectApproval.d.ts.map +1 -0
- package/dist/harness/claudeProjectApproval.js +159 -0
- package/dist/harness/claudeProjectApproval.js.map +1 -0
- package/dist/harness/clineHooks.d.ts +0 -1
- package/dist/harness/clineHooks.d.ts.map +1 -1
- package/dist/harness/clineHooks.js +13 -4
- package/dist/harness/clineHooks.js.map +1 -1
- package/dist/harness/codexConfigMerger.d.ts.map +1 -1
- package/dist/harness/codexConfigMerger.js +4 -0
- package/dist/harness/codexConfigMerger.js.map +1 -1
- package/dist/harness/codexHooks.d.ts +2 -0
- package/dist/harness/codexHooks.d.ts.map +1 -1
- package/dist/harness/codexHooks.js +15 -4
- package/dist/harness/codexHooks.js.map +1 -1
- package/dist/harness/continueConfigMerger.d.ts.map +1 -1
- package/dist/harness/continueConfigMerger.js +4 -0
- package/dist/harness/continueConfigMerger.js.map +1 -1
- package/dist/harness/cursorHooks.d.ts +5 -2
- package/dist/harness/cursorHooks.d.ts.map +1 -1
- package/dist/harness/cursorHooks.js +12 -3
- package/dist/harness/cursorHooks.js.map +1 -1
- package/dist/harness/dshHooks.d.ts +5 -5
- package/dist/harness/dshHooks.d.ts.map +1 -1
- package/dist/harness/dshHooks.js +12 -5
- package/dist/harness/dshHooks.js.map +1 -1
- package/dist/harness/gateArtifacts.d.ts +35 -0
- package/dist/harness/gateArtifacts.d.ts.map +1 -0
- package/dist/harness/gateArtifacts.js +88 -0
- package/dist/harness/gateArtifacts.js.map +1 -0
- package/dist/harness/gateBody.d.ts +195 -69
- package/dist/harness/gateBody.d.ts.map +1 -1
- package/dist/harness/gateBody.js +847 -182
- package/dist/harness/gateBody.js.map +1 -1
- package/dist/harness/gateSightings.d.ts +47 -0
- package/dist/harness/gateSightings.d.ts.map +1 -0
- package/dist/harness/gateSightings.js +159 -0
- package/dist/harness/gateSightings.js.map +1 -0
- package/dist/harness/githubCopilotHooks.d.ts.map +1 -1
- package/dist/harness/githubCopilotHooks.js +11 -10
- package/dist/harness/githubCopilotHooks.js.map +1 -1
- package/dist/harness/gooseHardener.d.ts +0 -1
- package/dist/harness/gooseHardener.d.ts.map +1 -1
- package/dist/harness/gooseHardener.js +1 -1
- package/dist/harness/gooseHardener.js.map +1 -1
- package/dist/harness/gooseHooks.d.ts +29 -1
- package/dist/harness/gooseHooks.d.ts.map +1 -1
- package/dist/harness/gooseHooks.js +107 -69
- package/dist/harness/gooseHooks.js.map +1 -1
- package/dist/harness/grokHooks.d.ts +2 -2
- package/dist/harness/grokHooks.d.ts.map +1 -1
- package/dist/harness/grokHooks.js +20 -18
- package/dist/harness/grokHooks.js.map +1 -1
- package/dist/harness/hermesHooks.d.ts +18 -8
- package/dist/harness/hermesHooks.d.ts.map +1 -1
- package/dist/harness/hermesHooks.js +55 -19
- package/dist/harness/hermesHooks.js.map +1 -1
- package/dist/harness/holdRedaction.d.ts +4 -1
- package/dist/harness/holdRedaction.d.ts.map +1 -1
- package/dist/harness/holdRedaction.js +32 -8
- package/dist/harness/holdRedaction.js.map +1 -1
- package/dist/harness/jetbrainsXmlConfig.d.ts +20 -0
- package/dist/harness/jetbrainsXmlConfig.d.ts.map +1 -1
- package/dist/harness/jetbrainsXmlConfig.js +39 -18
- package/dist/harness/jetbrainsXmlConfig.js.map +1 -1
- package/dist/harness/mcpAutoWrite.d.ts +62 -25
- package/dist/harness/mcpAutoWrite.d.ts.map +1 -1
- package/dist/harness/mcpAutoWrite.js +406 -95
- package/dist/harness/mcpAutoWrite.js.map +1 -1
- package/dist/harness/museHooks.d.ts +3 -3
- package/dist/harness/museHooks.d.ts.map +1 -1
- package/dist/harness/museHooks.js +15 -7
- package/dist/harness/museHooks.js.map +1 -1
- package/dist/harness/n8nHooks.d.ts +0 -1
- package/dist/harness/n8nHooks.d.ts.map +1 -1
- package/dist/harness/n8nHooks.js +4 -1
- package/dist/harness/n8nHooks.js.map +1 -1
- package/dist/harness/openCodeHooks.d.ts +2 -2
- package/dist/harness/openCodeHooks.d.ts.map +1 -1
- package/dist/harness/openCodeHooks.js +9 -8
- package/dist/harness/openCodeHooks.js.map +1 -1
- package/dist/harness/openWebuiHooks.d.ts +0 -1
- package/dist/harness/openWebuiHooks.d.ts.map +1 -1
- package/dist/harness/openWebuiHooks.js +11 -3
- package/dist/harness/openWebuiHooks.js.map +1 -1
- package/dist/harness/openclawHooks.d.ts +75 -18
- package/dist/harness/openclawHooks.d.ts.map +1 -1
- package/dist/harness/openclawHooks.js +214 -160
- package/dist/harness/openclawHooks.js.map +1 -1
- package/dist/harness/openhandsHooks.d.ts +37 -11
- package/dist/harness/openhandsHooks.d.ts.map +1 -1
- package/dist/harness/openhandsHooks.js +148 -97
- package/dist/harness/openhandsHooks.js.map +1 -1
- package/dist/harness/piHooks.d.ts +52 -17
- package/dist/harness/piHooks.d.ts.map +1 -1
- package/dist/harness/piHooks.js +177 -163
- package/dist/harness/piHooks.js.map +1 -1
- package/dist/harness/protectedPaths.d.ts +107 -25
- package/dist/harness/protectedPaths.d.ts.map +1 -1
- package/dist/harness/protectedPaths.js +342 -48
- package/dist/harness/protectedPaths.js.map +1 -1
- package/dist/harness/rulesSection.d.ts +62 -0
- package/dist/harness/rulesSection.d.ts.map +1 -0
- package/dist/harness/rulesSection.js +145 -0
- package/dist/harness/rulesSection.js.map +1 -0
- package/dist/harness/windsurfHooks.d.ts +18 -3
- package/dist/harness/windsurfHooks.d.ts.map +1 -1
- package/dist/harness/windsurfHooks.js +41 -8
- package/dist/harness/windsurfHooks.js.map +1 -1
- package/dist/harness/windsurfJetBrainsProxy.d.ts +1 -1
- package/dist/harness/windsurfJetBrainsProxy.d.ts.map +1 -1
- package/dist/harness/windsurfJetBrainsProxy.js +5 -1
- package/dist/harness/windsurfJetBrainsProxy.js.map +1 -1
- package/dist/index.d.ts +21 -20
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +19 -19
- package/dist/index.js.map +1 -1
- package/dist/inventory.d.ts +53 -0
- package/dist/inventory.d.ts.map +1 -0
- package/dist/inventory.js +133 -0
- package/dist/inventory.js.map +1 -0
- package/dist/lib/complianceProbes.d.ts +2 -1
- package/dist/lib/complianceProbes.d.ts.map +1 -1
- package/dist/lib/complianceProbes.js +1 -1
- package/dist/lib/complianceProbes.js.map +1 -1
- package/dist/lib/decisionsDigest.d.ts +35 -17
- package/dist/lib/decisionsDigest.d.ts.map +1 -1
- package/dist/lib/decisionsDigest.js +106 -47
- package/dist/lib/decisionsDigest.js.map +1 -1
- package/dist/lib/egressPolicy.d.ts +2 -2
- package/dist/lib/egressPolicy.js +2 -2
- package/dist/lib/mcpRegistryPy.d.ts +11 -0
- package/dist/lib/mcpRegistryPy.d.ts.map +1 -0
- package/dist/lib/mcpRegistryPy.js +173 -0
- package/dist/lib/mcpRegistryPy.js.map +1 -0
- package/dist/lib/phrasesPy.d.ts +11 -0
- package/dist/lib/phrasesPy.d.ts.map +1 -0
- package/dist/lib/phrasesPy.js +351 -0
- package/dist/lib/phrasesPy.js.map +1 -0
- package/dist/lib/policySnapshot.d.ts +63 -9
- package/dist/lib/policySnapshot.d.ts.map +1 -1
- package/dist/lib/policySnapshot.js +269 -45
- package/dist/lib/policySnapshot.js.map +1 -1
- package/dist/lib/processPoller.d.ts.map +1 -1
- package/dist/lib/processPoller.js +9 -2
- package/dist/lib/processPoller.js.map +1 -1
- package/dist/lib/regexLinearity.d.ts +43 -0
- package/dist/lib/regexLinearity.d.ts.map +1 -0
- package/dist/lib/regexLinearity.js +275 -0
- package/dist/lib/regexLinearity.js.map +1 -0
- package/dist/lib/runtimeEnv.d.ts +1 -3
- package/dist/lib/runtimeEnv.d.ts.map +1 -1
- package/dist/lib/runtimeEnv.js +0 -2
- package/dist/lib/runtimeEnv.js.map +1 -1
- package/dist/lib/sequencePy.d.ts +10 -0
- package/dist/lib/sequencePy.d.ts.map +1 -0
- package/dist/lib/sequencePy.js +102 -0
- package/dist/lib/sequencePy.js.map +1 -0
- package/dist/sessionReporter.d.ts +18 -12
- package/dist/sessionReporter.d.ts.map +1 -1
- package/dist/sessionReporter.js +37 -24
- package/dist/sessionReporter.js.map +1 -1
- package/dist/skillWriter.d.ts +23 -5
- package/dist/skillWriter.d.ts.map +1 -1
- package/dist/skillWriter.js +159 -16
- package/dist/skillWriter.js.map +1 -1
- package/dist/syncCycle.d.ts +142 -0
- package/dist/syncCycle.d.ts.map +1 -0
- package/dist/syncCycle.js +327 -0
- package/dist/syncCycle.js.map +1 -0
- package/dist/trajectoryMonitor.d.ts +2 -4
- package/dist/trajectoryMonitor.d.ts.map +1 -1
- package/dist/trajectoryMonitor.js +2 -5
- package/dist/trajectoryMonitor.js.map +1 -1
- package/dist/watcher/driftWatcher.d.ts +0 -1
- package/dist/watcher/driftWatcher.d.ts.map +1 -1
- package/dist/watcher/driftWatcher.js +1 -2
- package/dist/watcher/driftWatcher.js.map +1 -1
- package/dist/watcher/settingsGuard.d.ts +36 -7
- package/dist/watcher/settingsGuard.d.ts.map +1 -1
- package/dist/watcher/settingsGuard.js +397 -60
- package/dist/watcher/settingsGuard.js.map +1 -1
- package/dist/watcher/vscodeHookSettings.d.ts +30 -0
- package/dist/watcher/vscodeHookSettings.d.ts.map +1 -0
- package/dist/watcher/vscodeHookSettings.js +170 -0
- package/dist/watcher/vscodeHookSettings.js.map +1 -0
- package/dist/wsClient.d.ts +0 -1
- package/dist/wsClient.d.ts.map +1 -1
- package/dist/wsClient.js +0 -1
- package/dist/wsClient.js.map +1 -1
- package/package.json +5 -5
- package/dist/harness/claudeDesktopHooks.d.ts +0 -30
- package/dist/harness/claudeDesktopHooks.d.ts.map +0 -1
- package/dist/harness/claudeDesktopHooks.js +0 -254
- package/dist/harness/claudeDesktopHooks.js.map +0 -1
- package/dist/harness/continueHooks.d.ts +0 -63
- package/dist/harness/continueHooks.d.ts.map +0 -1
- package/dist/harness/continueHooks.js +0 -247
- package/dist/harness/continueHooks.js.map +0 -1
- package/dist/harness/gateKind.d.ts +0 -164
- package/dist/harness/gateKind.d.ts.map +0 -1
- package/dist/harness/gateKind.js +0 -144
- package/dist/harness/gateKind.js.map +0 -1
- package/dist/harness/rooCodeHooks.d.ts +0 -31
- package/dist/harness/rooCodeHooks.d.ts.map +0 -1
- package/dist/harness/rooCodeHooks.js +0 -286
- package/dist/harness/rooCodeHooks.js.map +0 -1
- package/dist/hashReporter.d.ts +0 -33
- package/dist/hashReporter.d.ts.map +0 -1
- package/dist/hashReporter.js +0 -69
- package/dist/hashReporter.js.map +0 -1
- package/dist/integrityStore.d.ts +0 -35
- package/dist/integrityStore.d.ts.map +0 -1
- package/dist/integrityStore.js +0 -71
- package/dist/integrityStore.js.map +0 -1
- package/dist/lib/gitWorktrees.d.ts +0 -140
- package/dist/lib/gitWorktrees.d.ts.map +0 -1
- package/dist/lib/gitWorktrees.js +0 -191
- package/dist/lib/gitWorktrees.js.map +0 -1
- package/dist/syncLoop.d.ts +0 -172
- package/dist/syncLoop.d.ts.map +0 -1
- package/dist/syncLoop.js +0 -878
- package/dist/syncLoop.js.map +0 -1
package/dist/configWriter.js
CHANGED
|
@@ -1,13 +1,9 @@
|
|
|
1
1
|
/**
|
|
2
|
-
* configWriter.ts —
|
|
2
|
+
* configWriter.ts — the harness rules-file map, local SOP loading, SkillOpt
|
|
3
|
+
* config edits and the atomic write they share.
|
|
3
4
|
*
|
|
4
|
-
*
|
|
5
|
-
*
|
|
6
|
-
* resolves paths, formats content, and performs atomic writes
|
|
7
|
-
* (write-to-tmp → rename) to prevent partial/corrupt files.
|
|
8
|
-
*
|
|
9
|
-
* HLD §3.14 — Real-Time State Mirroring
|
|
10
|
-
* LLD #8 — Sync Daemon / CLI
|
|
5
|
+
* The harness config files themselves are written by the CLI's adapters
|
|
6
|
+
* (tools/cli/src/harness), which `intutic connect` runs.
|
|
11
7
|
*
|
|
12
8
|
* @module
|
|
13
9
|
*/
|
|
@@ -15,15 +11,14 @@ import * as node_fs from 'node:fs/promises';
|
|
|
15
11
|
import * as node_path from 'node:path';
|
|
16
12
|
import { execFile as _execFile } from 'node:child_process';
|
|
17
13
|
import { promisify } from 'node:util';
|
|
18
|
-
import {
|
|
19
|
-
import { proxyHost } from '@intutic/shared-types';
|
|
14
|
+
import { HarnessType as HarnessTypes, rulesFileOf } from '@intutic/shared-types';
|
|
20
15
|
const execFile = promisify(_execFile);
|
|
21
16
|
/**
|
|
22
|
-
* Clear the macOS user-immutable flag
|
|
23
|
-
*
|
|
24
|
-
*
|
|
17
|
+
* Clear the macOS user-immutable flag so the daemon can rewrite a file it
|
|
18
|
+
* write-protected (the workspace's `bypassEnforcementTier: 'immutable'`).
|
|
19
|
+
* No-op on other platforms or if the file does not exist yet.
|
|
25
20
|
*/
|
|
26
|
-
async function clearImmutable(filePath) {
|
|
21
|
+
export async function clearImmutable(filePath) {
|
|
27
22
|
if (process.platform !== 'darwin')
|
|
28
23
|
return;
|
|
29
24
|
try {
|
|
@@ -32,12 +27,11 @@ async function clearImmutable(filePath) {
|
|
|
32
27
|
catch { /* file may not exist yet */ }
|
|
33
28
|
}
|
|
34
29
|
/**
|
|
35
|
-
* Set the macOS user-immutable flag
|
|
36
|
-
*
|
|
37
|
-
*
|
|
38
|
-
* No-op on non-macOS platforms. (WS-5 Q3)
|
|
30
|
+
* Set the macOS user-immutable flag (`chflags uchg`), so a hand edit of a
|
|
31
|
+
* managed file fails with EPERM. The flag needs no root to set or clear; it
|
|
32
|
+
* stops casual editing, not a determined user. No-op on other platforms.
|
|
39
33
|
*/
|
|
40
|
-
async function setImmutable(filePath) {
|
|
34
|
+
export async function setImmutable(filePath) {
|
|
41
35
|
if (process.platform !== 'darwin')
|
|
42
36
|
return;
|
|
43
37
|
try {
|
|
@@ -45,139 +39,42 @@ async function setImmutable(filePath) {
|
|
|
45
39
|
}
|
|
46
40
|
catch { /* non-fatal */ }
|
|
47
41
|
}
|
|
48
|
-
import { writeAntigravityHooks } from './harness/antigravityHooks.js';
|
|
49
|
-
import { writeClaudeDesktopHooks } from './harness/claudeDesktopHooks.js';
|
|
50
|
-
import { writeRooCodeHooks } from './harness/rooCodeHooks.js';
|
|
51
|
-
import { writeContinueHooks } from './harness/continueHooks.js';
|
|
52
|
-
import { writeOpenWebuiHooks } from './harness/openWebuiHooks.js';
|
|
53
|
-
import { writeN8nHooks } from './harness/n8nHooks.js';
|
|
54
|
-
import { writeHermesHooks } from './harness/hermesHooks.js';
|
|
55
|
-
import { writeOpenclawHooks } from './harness/openclawHooks.js';
|
|
56
|
-
import { writePiHooks } from './harness/piHooks.js';
|
|
57
|
-
import { writeCodexHooks } from './harness/codexHooks.js';
|
|
58
|
-
import { writeGithubCopilotHooks } from './harness/githubCopilotHooks.js';
|
|
59
|
-
import { writeMuseHooks } from './harness/museHooks.js';
|
|
60
|
-
import { writeOpenCodeHooks } from './harness/openCodeHooks.js';
|
|
61
|
-
import { writeGrokHooks } from './harness/grokHooks.js';
|
|
62
42
|
// ─── Harness config file mapping ─────────────────────────────────────
|
|
43
|
+
/** Every adapter that writes `.env.intutic`: Codex and the SDK-gated frameworks. */
|
|
44
|
+
export const ENV_INTUTIC_WRITERS = [
|
|
45
|
+
'codex',
|
|
46
|
+
'langgraph',
|
|
47
|
+
'langchain',
|
|
48
|
+
'crewai',
|
|
49
|
+
'autogen',
|
|
50
|
+
'ag2',
|
|
51
|
+
'google-adk',
|
|
52
|
+
'openai-agents',
|
|
53
|
+
'pydantic-ai',
|
|
54
|
+
'smolagents',
|
|
55
|
+
'strands',
|
|
56
|
+
'agent-framework',
|
|
57
|
+
'mastra',
|
|
58
|
+
'vercel-ai-sdk',
|
|
59
|
+
'eve',
|
|
60
|
+
'trueforge',
|
|
61
|
+
'ai-sdk-harness',
|
|
62
|
+
'ai-sdk-workflow',
|
|
63
|
+
];
|
|
63
64
|
/**
|
|
64
|
-
*
|
|
65
|
-
*
|
|
66
|
-
*
|
|
65
|
+
* The workspace file captured, watched for drift and edited by SkillOpt for
|
|
66
|
+
* each harness: its rules file (`rulesFileOf`, from `HARNESS_RULES_FILES` in
|
|
67
|
+
* `@intutic/shared-types`), else `.env.intutic` for the SDK-gated frameworks,
|
|
68
|
+
* which carries their proxy variables. An empty string means nothing in the
|
|
69
|
+
* workspace: the harness's rule sets go elsewhere (OpenClaw's agent
|
|
70
|
+
* workspace) or nowhere (see the map's `none` entries).
|
|
67
71
|
*/
|
|
68
|
-
export const HARNESS_FILES =
|
|
69
|
-
cursor: '.cursorrules',
|
|
70
|
-
'claude-code': 'CLAUDE.md',
|
|
71
|
-
antigravity: '.gemini/settings.json',
|
|
72
|
-
windsurf: '.windsurfrules',
|
|
73
|
-
aider: '.aider.conf.yml',
|
|
74
|
-
openhands: 'config.toml',
|
|
75
|
-
codex: '.env.intutic',
|
|
76
|
-
n8n: '.intutic/n8n/governance-workflow.json',
|
|
77
|
-
openclaw: '.openclaw/openclaw.json',
|
|
78
|
-
hermes: '.hermes/config.yaml',
|
|
79
|
-
pi: '.pi/hooks.json',
|
|
80
|
-
'github-copilot': '.github/copilot-instructions.md',
|
|
81
|
-
cline: '.clinerules/intutic-governance.md',
|
|
82
|
-
'roo-code': '.roorules',
|
|
83
|
-
continue: '.continue/config.json',
|
|
84
|
-
'claude-desktop': 'claude_desktop_config.json',
|
|
85
|
-
goose: '.agents/plugins/intutic-governance/hooks/hooks.json',
|
|
86
|
-
'open-webui': '.open-webui/intutic-governance-filter.py',
|
|
87
|
-
langgraph: '.env.intutic',
|
|
88
|
-
// Muse Code reads AGENTS.md (falling back to CLAUDE.md) — see muse.ts (CLI)
|
|
89
|
-
// and museHooks.ts (the hook writer invoked below, same split codex uses).
|
|
90
|
-
'muse-code': 'AGENTS.md',
|
|
91
|
-
// Grok Build's native rules file — same cross-tool convention Codex/Amp
|
|
92
|
-
// read. Formatted by `formatMarkdown`, the same `---`-separated formatter
|
|
93
|
-
// Cursor/Claude Code/Windsurf/GitHub Copilot already share below.
|
|
94
|
-
grok: 'AGENTS.md',
|
|
95
|
-
// OpenCode reads AGENTS.md (CLAUDE.md fallback) — the same markdown rules
|
|
96
|
-
// file Muse/Grok get. The gate is the plugin `writeOpenCodeHooks` writes
|
|
97
|
-
// below, not this file.
|
|
98
|
-
opencode: 'AGENTS.md',
|
|
99
|
-
// dsh has no workspace-relative rules file (governance lives entirely
|
|
100
|
-
// under $DSH_HOME) — empty, same as goose/openhands below: this writer's
|
|
101
|
-
// per-harness chain deliberately does not call writeDshHooks either (see
|
|
102
|
-
// the "goose and openhands are deliberately absent here" comment above);
|
|
103
|
-
// dshHooks.ts is invoked by tools/cli/src/harness/dsh.ts's own
|
|
104
|
-
// writeConfig() and re-run on tamper by settingsGuard.ts, the same
|
|
105
|
-
// two-path coverage goose/openhands rely on instead of this loop.
|
|
106
|
-
dsh: '',
|
|
107
|
-
// Xirp writes no config of its own (orchestrates other already-gated
|
|
108
|
-
// harnesses — see gateRegistry.ts's NO_GATE row and tools/cli/src/harness/
|
|
109
|
-
// xirp.ts). Empty filename => the loop below reports it `(deferred)`,
|
|
110
|
-
// same as n8n before it gained a writer.
|
|
111
|
-
xirp: '',
|
|
112
|
-
// Agentic Orchestrator writes no config of its own either — same
|
|
113
|
-
// "delegated" shape as Xirp (orchestrates already-gated Claude Code, Codex
|
|
114
|
-
// and OpenCode — see gateRegistry.ts's NO_GATE row and
|
|
115
|
-
// tools/cli/src/harness/agenticOrchestrator.ts).
|
|
116
|
-
'agentic-orchestrator': '',
|
|
117
|
-
// Wave 1 SDK-gated frameworks — same rationale as langgraph: no on-disk
|
|
118
|
-
// hook/config file exists to gate tool calls, so each writes .env.intutic
|
|
119
|
-
// (proxy base-URL vars + an SDK-gate pointer comment). See formatContent's
|
|
120
|
-
// switch below and SDK_GATED_FRAMEWORKS.
|
|
121
|
-
langchain: '.env.intutic',
|
|
122
|
-
crewai: '.env.intutic',
|
|
123
|
-
autogen: '.env.intutic',
|
|
124
|
-
ag2: '.env.intutic',
|
|
125
|
-
'google-adk': '.env.intutic',
|
|
126
|
-
'openai-agents': '.env.intutic',
|
|
127
|
-
'pydantic-ai': '.env.intutic',
|
|
128
|
-
smolagents: '.env.intutic',
|
|
129
|
-
// A4: AWS Strands Agents — same Python SDK-gated rationale as the Wave 1
|
|
130
|
-
// family above (gate ships in intutic_clawde.gate.adapters.strands).
|
|
131
|
-
strands: '.env.intutic',
|
|
132
|
-
// TD-375: Microsoft Agent Framework — same Python SDK-gated rationale
|
|
133
|
-
// (gate ships in intutic_clawde.gate.adapters.agent_framework).
|
|
134
|
-
'agent-framework': '.env.intutic',
|
|
135
|
-
// T2: JS/TS SDK-gated frameworks — same rationale as the Wave 1 Python
|
|
136
|
-
// family above, but the blocking gate ships in @intutic/gate
|
|
137
|
-
// (packages/gate-js) rather than intutic-clawde. See formatContent's
|
|
138
|
-
// switch below and JS_SDK_GATED_FRAMEWORKS.
|
|
139
|
-
mastra: '.env.intutic',
|
|
140
|
-
'vercel-ai-sdk': '.env.intutic',
|
|
141
|
-
// eve (Vercel, PREVIEW) — same JS/TS SDK-gated family: the gate is
|
|
142
|
-
// @intutic/gate/eve's per-tool/per-connection approval policies, attached
|
|
143
|
-
// in the developer's own agent/ directory; this file only carries the
|
|
144
|
-
// proxy vars + pointer comment. See JS_SDK_GATED_FRAMEWORKS below.
|
|
145
|
-
eve: '.env.intutic',
|
|
146
|
-
// TrueForge (embedded-library mode only — see its HarnessType doc) — same
|
|
147
|
-
// JS/TS SDK-gated family, gate ships in @intutic/gate/trueforge. See
|
|
148
|
-
// formatContent's switch below and JS_SDK_GATED_FRAMEWORKS.
|
|
149
|
-
trueforge: '.env.intutic',
|
|
150
|
-
// A3: Vercel platform-agent runtimes — same @intutic/gate family as the T2
|
|
151
|
-
// rows above. For ai-sdk-harness the env vars are weaker still: tool
|
|
152
|
-
// execution is server-side in Vercel Sandbox microVMs the local proxy
|
|
153
|
-
// never sees (the generated comment says so — see JS_SDK_GATED_FRAMEWORKS'
|
|
154
|
-
// preamble override below and @intutic/gate/harness's module doc).
|
|
155
|
-
'ai-sdk-harness': '.env.intutic',
|
|
156
|
-
'ai-sdk-workflow': '.env.intutic',
|
|
157
|
-
// B2: AWS Bedrock AgentCore Runtime — hosts the customer's own framework
|
|
158
|
-
// code unchanged, so it writes no config of its own; the real tool-call
|
|
159
|
-
// gate belongs to whichever already-supported framework adapter that code
|
|
160
|
-
// uses (Strands, LangGraph, ...). Same 'delegated'/empty-filename shape as
|
|
161
|
-
// xirp/agentic-orchestrator above — see tools/cli/src/harness/agentcore.ts
|
|
162
|
-
// and gateKind.ts's DELEGATED_GATE_HARNESSES.
|
|
163
|
-
'agentcore-runtime': '',
|
|
164
|
-
// B3: TrueForge, standalone/hosted server (see its HarnessType doc) —
|
|
165
|
-
// writes no config of its own: it is not detected by intutic init at all
|
|
166
|
-
// (an operator-configured deployment, not a repo dependency this daemon's
|
|
167
|
-
// scanner can find), and its gate is entirely out-of-process in
|
|
168
|
-
// services/trueforge-bridge, not a file this daemon writes. Empty filename
|
|
169
|
-
// => the loop below reports it `(deferred)`, same shape as
|
|
170
|
-
// xirp/agentic-orchestrator/agentcore-runtime above, though for a
|
|
171
|
-
// genuinely different reason — those are 'delegated' (governed by a
|
|
172
|
-
// wrapped harness's own gate); this is 'bridge' (governed by a service
|
|
173
|
-
// this repo ships and operates). See gateKind.ts's BRIDGE_GATED_HARNESSES.
|
|
174
|
-
'trueforge-server': '',
|
|
175
|
-
};
|
|
72
|
+
export const HARNESS_FILES = Object.fromEntries(Object.values(HarnessTypes).map((h) => [h, rulesFileOf(h) ?? (ENV_INTUTIC_WRITERS.includes(h) ? '.env.intutic' : '')]));
|
|
176
73
|
/**
|
|
177
74
|
* The workspace's local SOPs (`.intutic/sops/<dir>/*.md`, narrowed by
|
|
178
|
-
* `session-context.json`'s `activeLocalSops` when present).
|
|
179
|
-
*
|
|
180
|
-
* `review_before:` tokens into hold rules
|
|
75
|
+
* `session-context.json`'s `activeLocalSops` when present). Read by the
|
|
76
|
+
* gate-cache refresh, which compiles their
|
|
77
|
+
* `review_before:` tokens into hold rules. A missing
|
|
181
78
|
* directory is an empty list.
|
|
182
79
|
*/
|
|
183
80
|
export async function loadLocalSopEntries(workspaceRoot, harnesses) {
|
|
@@ -221,215 +118,6 @@ export async function loadLocalSopEntries(workspaceRoot, harnesses) {
|
|
|
221
118
|
}
|
|
222
119
|
return localSopEntries;
|
|
223
120
|
}
|
|
224
|
-
/**
|
|
225
|
-
* Write SOP content to all targeted harness config files.
|
|
226
|
-
*
|
|
227
|
-
* For each harness type, resolves the config file path, formats the
|
|
228
|
-
* content according to the harness-specific format, and performs an
|
|
229
|
-
* atomic write (tmp file → rename).
|
|
230
|
-
*
|
|
231
|
-
* @param workspaceRoot - Absolute path to the workspace root.
|
|
232
|
-
* @param sops - SOPs to write, each specifying which harnesses to target.
|
|
233
|
-
* @param proxyUrl - Intutic proxy URL for LLM API redirect.
|
|
234
|
-
* @param harnesses - Harness types detected in the workspace.
|
|
235
|
-
* @returns WriteResult with written and skipped file paths.
|
|
236
|
-
*/
|
|
237
|
-
export async function writeConfigFiles(workspaceRoot, sops, proxyUrl, harnesses, workspaceId = '',
|
|
238
|
-
/** WS-5 Q3: 'rewrite' (default) | 'immutable' | 'alert-only' */
|
|
239
|
-
bypassEnforcementTier) {
|
|
240
|
-
const filesWritten = [];
|
|
241
|
-
const filesSkipped = [];
|
|
242
|
-
// Bundled agent skills — write-if-missing, not drift-enforced
|
|
243
|
-
try {
|
|
244
|
-
const { writeBundledSkills } = await import('./skillWriter.js');
|
|
245
|
-
await writeBundledSkills(workspaceRoot);
|
|
246
|
-
}
|
|
247
|
-
catch (e) {
|
|
248
|
-
console.warn('[sync-daemon] writeBundledSkills failed (non-fatal):', e);
|
|
249
|
-
}
|
|
250
|
-
const localSopEntries = await loadLocalSopEntries(workspaceRoot, harnesses);
|
|
251
|
-
const combinedSops = [...sops, ...localSopEntries];
|
|
252
|
-
for (const harness of harnesses) {
|
|
253
|
-
const filename = HARNESS_FILES[harness];
|
|
254
|
-
// Phase 3 proprietary harnesses — no config file path needed (handled below)
|
|
255
|
-
if (harness === 'hermes') {
|
|
256
|
-
try {
|
|
257
|
-
await writeHermesHooks(workspaceRoot, proxyUrl, workspaceId);
|
|
258
|
-
}
|
|
259
|
-
catch (e) {
|
|
260
|
-
console.warn('[sync-daemon] writeHermesHooks failed (non-fatal):', e);
|
|
261
|
-
}
|
|
262
|
-
continue;
|
|
263
|
-
}
|
|
264
|
-
if (harness === 'openclaw') {
|
|
265
|
-
try {
|
|
266
|
-
await writeOpenclawHooks(workspaceRoot, proxyUrl, workspaceId);
|
|
267
|
-
}
|
|
268
|
-
catch (e) {
|
|
269
|
-
console.warn('[sync-daemon] writeOpenclawHooks failed (non-fatal):', e);
|
|
270
|
-
}
|
|
271
|
-
continue;
|
|
272
|
-
}
|
|
273
|
-
if (harness === 'pi') {
|
|
274
|
-
try {
|
|
275
|
-
await writePiHooks(workspaceRoot, proxyUrl, workspaceId);
|
|
276
|
-
}
|
|
277
|
-
catch (e) {
|
|
278
|
-
console.warn('[sync-daemon] writePiHooks failed (non-fatal):', e);
|
|
279
|
-
}
|
|
280
|
-
continue;
|
|
281
|
-
}
|
|
282
|
-
// cursor is deliberately absent here, for the same reason goose and
|
|
283
|
-
// openhands are below.
|
|
284
|
-
//
|
|
285
|
-
// `cursorHooksJson` was a second writer of `.cursor/hooks.json`, and the two
|
|
286
|
-
// disagreed about the file's schema: `cursorHooks` writes an object per
|
|
287
|
-
// event carrying `failClosed: true`, while `cursorHooksJson.mergeHooks`
|
|
288
|
-
// tested `Array.isArray(existing[event])`, found false, and REPLACED it with
|
|
289
|
-
// a bare `{command}` array. So it silently dropped fail-closed and repointed
|
|
290
|
-
// the user-level shell and MCP gates at `~/.intutic/hooks/cursor-check.js`,
|
|
291
|
-
// a path no writer produces. `cursorHooks` already registers a superset of
|
|
292
|
-
// its events at both levels, so it is deleted rather than repaired.
|
|
293
|
-
// goose and openhands are deliberately absent here.
|
|
294
|
-
//
|
|
295
|
-
// They each used to get a *second* gate from this call site —
|
|
296
|
-
// gooseHooksWriter.ts and openhandsHooksWriter.ts — installed alongside the
|
|
297
|
-
// one their own writer produces. The two had contradictory models: the
|
|
298
|
-
// survivors enforce locally and fail closed, these POSTed every tool call to
|
|
299
|
-
// the control plane and failed open on any error or timeout. For openhands
|
|
300
|
-
// both wrote `.openhands/hooks.json`, so whichever ran last silently won.
|
|
301
|
-
//
|
|
302
|
-
// Deleted rather than deprecated: two modules exporting the same symbol for
|
|
303
|
-
// one harness *is* the defect. And they are not replaced by calls to the
|
|
304
|
-
// survivors here — a second call site is how this started.
|
|
305
|
-
// Phase 2 deferred or unknown harness
|
|
306
|
-
if (!filename) {
|
|
307
|
-
filesSkipped.push(`[${harness}] (deferred)`);
|
|
308
|
-
continue;
|
|
309
|
-
}
|
|
310
|
-
const configPath = node_path.join(workspaceRoot, filename);
|
|
311
|
-
// Filter SOPs that target this harness
|
|
312
|
-
const targetedSops = combinedSops.filter((sop) => sop.harnessTargets.includes(harness));
|
|
313
|
-
if (targetedSops.length === 0) {
|
|
314
|
-
filesSkipped.push(configPath);
|
|
315
|
-
continue;
|
|
316
|
-
}
|
|
317
|
-
const content = formatContent(harness, targetedSops, proxyUrl);
|
|
318
|
-
try {
|
|
319
|
-
await atomicWrite(configPath, content, bypassEnforcementTier);
|
|
320
|
-
filesWritten.push(configPath);
|
|
321
|
-
// For 'antigravity', also write the governance hook script and settings.json merge.
|
|
322
|
-
// This is separate from the SOP content write above (which writes customInstructions).
|
|
323
|
-
if (harness === 'antigravity') {
|
|
324
|
-
try {
|
|
325
|
-
await writeAntigravityHooks(workspaceRoot, proxyUrl, workspaceId);
|
|
326
|
-
}
|
|
327
|
-
catch (hookErr) {
|
|
328
|
-
// Non-fatal — SOP content was written, only the hook script failed
|
|
329
|
-
console.warn('[sync-daemon] writeAntigravityHooks failed (non-fatal):', hookErr);
|
|
330
|
-
}
|
|
331
|
-
}
|
|
332
|
-
if (harness === 'claude-desktop') {
|
|
333
|
-
try {
|
|
334
|
-
await writeClaudeDesktopHooks(workspaceRoot, proxyUrl, workspaceId);
|
|
335
|
-
}
|
|
336
|
-
catch (e) {
|
|
337
|
-
console.warn('[sync-daemon] writeClaudeDesktopHooks failed (non-fatal):', e);
|
|
338
|
-
}
|
|
339
|
-
}
|
|
340
|
-
if (harness === 'roo-code') {
|
|
341
|
-
try {
|
|
342
|
-
await writeRooCodeHooks(workspaceRoot, proxyUrl, workspaceId);
|
|
343
|
-
}
|
|
344
|
-
catch (e) {
|
|
345
|
-
console.warn('[sync-daemon] writeRooCodeHooks failed (non-fatal):', e);
|
|
346
|
-
}
|
|
347
|
-
}
|
|
348
|
-
if (harness === 'continue') {
|
|
349
|
-
try {
|
|
350
|
-
await writeContinueHooks(workspaceRoot, proxyUrl, workspaceId);
|
|
351
|
-
}
|
|
352
|
-
catch (e) {
|
|
353
|
-
console.warn('[sync-daemon] writeContinueHooks failed (non-fatal):', e);
|
|
354
|
-
}
|
|
355
|
-
}
|
|
356
|
-
if (harness === 'open-webui') {
|
|
357
|
-
try {
|
|
358
|
-
await writeOpenWebuiHooks(workspaceRoot, proxyUrl, workspaceId);
|
|
359
|
-
}
|
|
360
|
-
catch (e) {
|
|
361
|
-
console.warn('[sync-daemon] writeOpenWebuiHooks failed (non-fatal):', e);
|
|
362
|
-
}
|
|
363
|
-
}
|
|
364
|
-
if (harness === 'n8n') {
|
|
365
|
-
try {
|
|
366
|
-
await writeN8nHooks(workspaceRoot, proxyUrl, workspaceId);
|
|
367
|
-
}
|
|
368
|
-
catch (e) {
|
|
369
|
-
console.warn('[sync-daemon] writeN8nHooks failed (non-fatal):', e);
|
|
370
|
-
}
|
|
371
|
-
}
|
|
372
|
-
// The .env.intutic proxy routing above governs LLM egress only; the gate
|
|
373
|
-
// is what refuses tool calls. Written alongside, same as claude-desktop.
|
|
374
|
-
if (harness === 'codex') {
|
|
375
|
-
try {
|
|
376
|
-
await writeCodexHooks(workspaceRoot, proxyUrl, workspaceId);
|
|
377
|
-
}
|
|
378
|
-
catch (e) {
|
|
379
|
-
console.warn('[sync-daemon] writeCodexHooks failed (non-fatal):', e);
|
|
380
|
-
}
|
|
381
|
-
}
|
|
382
|
-
// Preview mechanism (VS Code agent hooks) — the writer's header says so,
|
|
383
|
-
// and the gate fails closed if the stdin shape shifts.
|
|
384
|
-
if (harness === 'github-copilot') {
|
|
385
|
-
try {
|
|
386
|
-
await writeGithubCopilotHooks(workspaceRoot, proxyUrl, workspaceId);
|
|
387
|
-
}
|
|
388
|
-
catch (e) {
|
|
389
|
-
console.warn('[sync-daemon] writeGithubCopilotHooks failed (non-fatal):', e);
|
|
390
|
-
}
|
|
391
|
-
}
|
|
392
|
-
// Same split as codex/claude-desktop: AGENTS.md above carries the rules
|
|
393
|
-
// text, this installs the PreToolUse/PermissionRequest gate (project +
|
|
394
|
-
// managed tiers) and the managed_hooks_path merge into settings.json.
|
|
395
|
-
if (harness === 'muse-code') {
|
|
396
|
-
try {
|
|
397
|
-
await writeMuseHooks(workspaceRoot, proxyUrl, workspaceId);
|
|
398
|
-
}
|
|
399
|
-
catch (e) {
|
|
400
|
-
console.warn('[sync-daemon] writeMuseHooks failed (non-fatal):', e);
|
|
401
|
-
}
|
|
402
|
-
}
|
|
403
|
-
// AGENTS.md above carries the rules text; this writes the plugin
|
|
404
|
-
// OpenCode loads into its own process, which is the gate (TD-397).
|
|
405
|
-
if (harness === 'opencode') {
|
|
406
|
-
try {
|
|
407
|
-
await writeOpenCodeHooks(workspaceRoot, proxyUrl, workspaceId);
|
|
408
|
-
}
|
|
409
|
-
catch (e) {
|
|
410
|
-
console.warn('[sync-daemon] writeOpenCodeHooks failed (non-fatal):', e);
|
|
411
|
-
}
|
|
412
|
-
}
|
|
413
|
-
// AGENTS.md above governs prompt-level rules only; the gate is what
|
|
414
|
-
// refuses tool calls, and the config.toml model base_url merge is what
|
|
415
|
-
// routes LLM egress. Written alongside, same as codex/claude-desktop.
|
|
416
|
-
if (harness === 'grok') {
|
|
417
|
-
try {
|
|
418
|
-
await writeGrokHooks(workspaceRoot, proxyUrl, workspaceId);
|
|
419
|
-
}
|
|
420
|
-
catch (e) {
|
|
421
|
-
console.warn('[sync-daemon] writeGrokHooks failed (non-fatal):', e);
|
|
422
|
-
}
|
|
423
|
-
}
|
|
424
|
-
}
|
|
425
|
-
catch (err) {
|
|
426
|
-
console.warn(`[sync-daemon] writeConfigFiles failed for ${filename}:`, err);
|
|
427
|
-
// Don't crash the loop — report as skipped
|
|
428
|
-
filesSkipped.push(configPath);
|
|
429
|
-
}
|
|
430
|
-
}
|
|
431
|
-
return { filesWritten, filesSkipped };
|
|
432
|
-
}
|
|
433
121
|
/**
|
|
434
122
|
* Finds a match for a target block in content, tolerating line ending and whitespace variations.
|
|
435
123
|
*/
|
|
@@ -464,17 +152,19 @@ function findFuzzyMatch(content, target) {
|
|
|
464
152
|
/**
|
|
465
153
|
* Apply a list of custom config edits (ADD, DELETE, REPLACE) to a workspace rule file.
|
|
466
154
|
*
|
|
467
|
-
* Returns one `ConfigEditApplyOutcome` per input suggestion
|
|
155
|
+
* Returns one `ConfigEditApplyOutcome` per input suggestion instead
|
|
468
156
|
* of `void` — every operation's real fate (fuzzy-match miss on DELETE/
|
|
469
157
|
* REPLACE, an unresolvable target file, or the final `atomicWrite` itself
|
|
470
158
|
* failing) is recorded here rather than only `console.warn`'d, so the caller
|
|
471
|
-
* (
|
|
159
|
+
* (`applySkillOptEdits`, syncCycle.ts) can ack the daemon's actual outcome back to control-plane
|
|
472
160
|
* instead of the daemon-side write being invisible to it.
|
|
473
161
|
*/
|
|
474
162
|
export async function applyConfigEdits(workspaceRoot, appliedEdits, bypassEnforcementTier) {
|
|
475
163
|
const outcomes = [];
|
|
476
164
|
for (const applied of appliedEdits) {
|
|
477
|
-
|
|
165
|
+
// The harness's rules file only: an edit is instruction text, and a file
|
|
166
|
+
// the product does not read as instructions (`.env.intutic`) is no place for it.
|
|
167
|
+
const filename = rulesFileOf(applied.harnessType);
|
|
478
168
|
const editsList = typeof applied.edits === 'string'
|
|
479
169
|
? JSON.parse(applied.edits)
|
|
480
170
|
: applied.edits;
|
|
@@ -489,7 +179,7 @@ export async function applyConfigEdits(workspaceRoot, appliedEdits, bypassEnforc
|
|
|
489
179
|
index,
|
|
490
180
|
operation: edit.operation,
|
|
491
181
|
applied: false,
|
|
492
|
-
reason: `
|
|
182
|
+
reason: `Harness "${applied.harnessType}" has no rules file in the workspace`,
|
|
493
183
|
})),
|
|
494
184
|
});
|
|
495
185
|
continue;
|
|
@@ -590,328 +280,6 @@ export async function applyConfigEdits(workspaceRoot, appliedEdits, bypassEnforc
|
|
|
590
280
|
}
|
|
591
281
|
return outcomes;
|
|
592
282
|
}
|
|
593
|
-
// ─── Formatters ──────────────────────────────────────────────────────
|
|
594
|
-
/** Standard file header injected at the top of every auto-generated file. */
|
|
595
|
-
function fileHeader() {
|
|
596
|
-
return [
|
|
597
|
-
'# Intutic Governance Rules (auto-generated)',
|
|
598
|
-
'# DO NOT EDIT — managed by intutic sync daemon',
|
|
599
|
-
`# Last sync: ${newIso()}`,
|
|
600
|
-
'',
|
|
601
|
-
'',
|
|
602
|
-
].join('\n');
|
|
603
|
-
}
|
|
604
|
-
/**
|
|
605
|
-
* Format SOP content for a specific harness type.
|
|
606
|
-
*
|
|
607
|
-
* Each harness has a distinct format:
|
|
608
|
-
* - Cursor / Claude Code / Windsurf → Markdown with `---` separators
|
|
609
|
-
* - Antigravity → JSON with `customInstructions` field
|
|
610
|
-
* - Aider → YAML with `extra-instructions` field
|
|
611
|
-
* - OpenHands → TOML with `[intutic]` section
|
|
612
|
-
* - Codex → `.env.intutic` with proxy URL vars
|
|
613
|
-
*/
|
|
614
|
-
function formatContent(harness, sops, proxyUrl) {
|
|
615
|
-
switch (harness) {
|
|
616
|
-
case 'cursor':
|
|
617
|
-
case 'claude-code':
|
|
618
|
-
case 'windsurf':
|
|
619
|
-
case 'github-copilot':
|
|
620
|
-
case 'grok':
|
|
621
|
-
return formatMarkdown(sops);
|
|
622
|
-
case 'antigravity':
|
|
623
|
-
return formatAntigravity(sops);
|
|
624
|
-
case 'aider':
|
|
625
|
-
return formatAider(sops);
|
|
626
|
-
case 'openhands':
|
|
627
|
-
return formatOpenHands(sops);
|
|
628
|
-
case 'codex':
|
|
629
|
-
return formatCodex(sops, proxyUrl);
|
|
630
|
-
case 'langgraph':
|
|
631
|
-
return formatLanggraph(sops, proxyUrl);
|
|
632
|
-
case 'muse-code':
|
|
633
|
-
case 'opencode':
|
|
634
|
-
// AGENTS.md — same markdown shape as CLAUDE.md/.cursorrules/.windsurfrules.
|
|
635
|
-
return formatMarkdown(sops);
|
|
636
|
-
case 'langchain':
|
|
637
|
-
case 'crewai':
|
|
638
|
-
case 'autogen':
|
|
639
|
-
case 'ag2':
|
|
640
|
-
case 'google-adk':
|
|
641
|
-
case 'openai-agents':
|
|
642
|
-
case 'pydantic-ai':
|
|
643
|
-
case 'smolagents':
|
|
644
|
-
case 'strands':
|
|
645
|
-
case 'agent-framework':
|
|
646
|
-
return formatSdkGatedEnv(sops, proxyUrl, SDK_GATED_FRAMEWORKS[harness]);
|
|
647
|
-
case 'mastra':
|
|
648
|
-
case 'vercel-ai-sdk':
|
|
649
|
-
case 'eve':
|
|
650
|
-
case 'trueforge':
|
|
651
|
-
case 'ai-sdk-harness':
|
|
652
|
-
case 'ai-sdk-workflow':
|
|
653
|
-
return formatJsSdkGatedEnv(sops, proxyUrl, JS_SDK_GATED_FRAMEWORKS[harness]);
|
|
654
|
-
default:
|
|
655
|
-
return formatMarkdown(sops);
|
|
656
|
-
}
|
|
657
|
-
}
|
|
658
|
-
/** Cursor / Claude Code / Windsurf: Markdown with `---` separators. */
|
|
659
|
-
function formatMarkdown(sops) {
|
|
660
|
-
const header = fileHeader();
|
|
661
|
-
const sections = sops.map((sop) => {
|
|
662
|
-
// Hybrid approach: full SOP content + sop:// pointer comment for traceability
|
|
663
|
-
const ref = sop.sopRef ? `\n${sop.sopRef}` : '';
|
|
664
|
-
return [`## ${sop.title}`, '', sop.content + ref].join('\n');
|
|
665
|
-
});
|
|
666
|
-
return header + sections.join('\n\n---\n\n') + '\n';
|
|
667
|
-
}
|
|
668
|
-
/** Antigravity: JSON with `customInstructions` field. */
|
|
669
|
-
function formatAntigravity(sops) {
|
|
670
|
-
const combinedContent = sops
|
|
671
|
-
.map((sop) => `## ${sop.title}\n\n${sop.content}`)
|
|
672
|
-
.join('\n\n---\n\n');
|
|
673
|
-
const payload = {
|
|
674
|
-
_comment: 'Intutic Governance Rules (auto-generated) — DO NOT EDIT',
|
|
675
|
-
_lastSync: newIso(),
|
|
676
|
-
customInstructions: combinedContent,
|
|
677
|
-
};
|
|
678
|
-
return JSON.stringify(payload, null, 2) + '\n';
|
|
679
|
-
}
|
|
680
|
-
/** Aider: YAML with `extra-instructions` field. */
|
|
681
|
-
function formatAider(sops) {
|
|
682
|
-
const header = fileHeader();
|
|
683
|
-
const combinedContent = sops
|
|
684
|
-
.map((sop) => `## ${sop.title}\n\n${sop.content}`)
|
|
685
|
-
.join('\n\n---\n\n');
|
|
686
|
-
// YAML multiline block scalar using `|`
|
|
687
|
-
const indented = combinedContent
|
|
688
|
-
.split('\n')
|
|
689
|
-
.map((line) => ` ${line}`)
|
|
690
|
-
.join('\n');
|
|
691
|
-
return header + `extra-instructions: |\n${indented}\n`;
|
|
692
|
-
}
|
|
693
|
-
/** OpenHands: TOML with `[intutic]` section. */
|
|
694
|
-
function formatOpenHands(sops) {
|
|
695
|
-
const header = fileHeader();
|
|
696
|
-
const combinedContent = sops
|
|
697
|
-
.map((sop) => `## ${sop.title}\n\n${sop.content}`)
|
|
698
|
-
.join('\n\n---\n\n');
|
|
699
|
-
// TOML multiline basic string uses triple quotes
|
|
700
|
-
const escaped = combinedContent
|
|
701
|
-
.replace(/\\/g, '\\\\')
|
|
702
|
-
.replace(/"""/g, '\\"\\"\\"');
|
|
703
|
-
return (header +
|
|
704
|
-
`[intutic]\n` +
|
|
705
|
-
`last_sync = "${newIso()}"\n` +
|
|
706
|
-
`governance_rules = """\n${escaped}\n"""\n`);
|
|
707
|
-
}
|
|
708
|
-
/** Codex: `.env.intutic` with proxy URL vars. */
|
|
709
|
-
function formatCodex(sops, proxyUrl) {
|
|
710
|
-
const header = fileHeader();
|
|
711
|
-
const sopIds = sops.map((sop) => sop.sopId).join(',');
|
|
712
|
-
return (header +
|
|
713
|
-
`INTUTIC_PROXY_URL=${proxyHost(proxyUrl)}\n` +
|
|
714
|
-
`INTUTIC_SOP_IDS=${sopIds}\n` +
|
|
715
|
-
`INTUTIC_LAST_SYNC=${newIso()}\n`);
|
|
716
|
-
}
|
|
717
|
-
/**
|
|
718
|
-
* LangGraph: `.env.intutic` with proxy URL vars, plus a pointer at the
|
|
719
|
-
* SDK-side tool gate. LangGraph tools are plain Python callables in the
|
|
720
|
-
* agent's own process — there is no config or hook file this daemon could
|
|
721
|
-
* write a tool-call gate into, so the env vars govern LLM egress only and
|
|
722
|
-
* the blocking gate ships in `intutic-clawde` (`intutic_clawde.gate`).
|
|
723
|
-
*/
|
|
724
|
-
function formatLanggraph(sops, proxyUrl) {
|
|
725
|
-
return (formatCodex(sops, proxyUrl) +
|
|
726
|
-
'\n' +
|
|
727
|
-
'# These env vars govern LLM egress only. LangGraph tools run in your own\n' +
|
|
728
|
-
'# Python process, where no config or hook file can gate them — the\n' +
|
|
729
|
-
'# blocking tool gate ships SDK-side:\n' +
|
|
730
|
-
'# pip install intutic-clawde\n' +
|
|
731
|
-
'# from intutic_clawde.gate import guard_tools\n');
|
|
732
|
-
}
|
|
733
|
-
/**
|
|
734
|
-
* Wave 1: seven more frameworks in LangGraph's family — tools are plain
|
|
735
|
-
* Python callables/objects in the agent's own process, so the daemon has no
|
|
736
|
-
* on-disk hook/config file to gate a call with, and every one of these
|
|
737
|
-
* writes the same `.env.intutic` shape `formatLanggraph` does (proxy URL
|
|
738
|
-
* vars + a comment pointing at the SDK-side gate). Declared as data here,
|
|
739
|
-
* rather than as seven more hand-copied `formatXxx` functions, so the shape
|
|
740
|
-
* cannot drift per-framework the way `gateBody.ts`'s module doc warns
|
|
741
|
-
* hand-copied gate logic does.
|
|
742
|
-
*
|
|
743
|
-
* `pipExtra` is omitted for the three frameworks with no dedicated
|
|
744
|
-
* `intutic_clawde.gate.adapters.*` module yet (autogen, ag2, pydantic-ai,
|
|
745
|
-
* smolagents ship in a later wave — see `gateRegistry.ts`'s NO_GATE rows) —
|
|
746
|
-
* their tools are governed today through the framework-agnostic
|
|
747
|
-
* `@guard`/`guard_tools` helpers, which need no optional import.
|
|
748
|
-
*/
|
|
749
|
-
const SDK_GATED_FRAMEWORKS = {
|
|
750
|
-
langchain: {
|
|
751
|
-
label: 'LangChain',
|
|
752
|
-
pipExtra: 'langchain',
|
|
753
|
-
importLine: 'from intutic_clawde.gate.adapters.langchain import IntuticMiddleware',
|
|
754
|
-
docsSlug: 'langchain',
|
|
755
|
-
},
|
|
756
|
-
crewai: {
|
|
757
|
-
label: 'CrewAI',
|
|
758
|
-
pipExtra: 'crewai',
|
|
759
|
-
importLine: 'from intutic_clawde.gate.adapters.crewai import install',
|
|
760
|
-
docsSlug: 'crewai',
|
|
761
|
-
},
|
|
762
|
-
autogen: {
|
|
763
|
-
label: 'AutoGen',
|
|
764
|
-
importLine: 'from intutic_clawde.gate import guard, guard_tools',
|
|
765
|
-
docsSlug: 'autogen',
|
|
766
|
-
},
|
|
767
|
-
ag2: {
|
|
768
|
-
label: 'AG2',
|
|
769
|
-
importLine: 'from intutic_clawde.gate import guard, guard_tools',
|
|
770
|
-
docsSlug: 'ag2',
|
|
771
|
-
},
|
|
772
|
-
'google-adk': {
|
|
773
|
-
label: 'Google ADK',
|
|
774
|
-
pipExtra: 'google-adk',
|
|
775
|
-
importLine: 'from intutic_clawde.gate.adapters.google_adk import IntuticPlugin',
|
|
776
|
-
docsSlug: 'google-adk',
|
|
777
|
-
},
|
|
778
|
-
'openai-agents': {
|
|
779
|
-
label: 'OpenAI Agents SDK',
|
|
780
|
-
pipExtra: 'openai-agents',
|
|
781
|
-
importLine: 'from intutic_clawde.gate.adapters.openai_agents import intutic_tool_guardrail',
|
|
782
|
-
docsSlug: 'openai-agents',
|
|
783
|
-
},
|
|
784
|
-
'pydantic-ai': {
|
|
785
|
-
label: 'Pydantic AI',
|
|
786
|
-
importLine: 'from intutic_clawde.gate import guard, guard_tools',
|
|
787
|
-
docsSlug: 'pydantic-ai',
|
|
788
|
-
},
|
|
789
|
-
smolagents: {
|
|
790
|
-
label: 'smolagents',
|
|
791
|
-
importLine: 'from intutic_clawde.gate import guard, guard_tools',
|
|
792
|
-
docsSlug: 'smolagents',
|
|
793
|
-
},
|
|
794
|
-
// A4: AWS Strands Agents — dedicated adapter module from day one
|
|
795
|
-
// (intutic_clawde.gate.adapters.strands), unlike the four `pipExtra`-less
|
|
796
|
-
// rows above that started on the framework-agnostic helpers.
|
|
797
|
-
strands: {
|
|
798
|
-
label: 'Strands Agents',
|
|
799
|
-
pipExtra: 'strands',
|
|
800
|
-
importLine: 'from intutic_clawde.gate.adapters.strands import IntuticHookProvider',
|
|
801
|
-
docsSlug: 'strands',
|
|
802
|
-
},
|
|
803
|
-
// TD-375: Microsoft Agent Framework — dedicated adapter module from day one.
|
|
804
|
-
'agent-framework': {
|
|
805
|
-
label: 'Microsoft Agent Framework',
|
|
806
|
-
pipExtra: 'agent-framework',
|
|
807
|
-
importLine: 'from intutic_clawde.gate.adapters.agent_framework import IntuticFunctionMiddleware',
|
|
808
|
-
docsSlug: 'microsoft-agent-framework',
|
|
809
|
-
},
|
|
810
|
-
};
|
|
811
|
-
/** Shared formatter for every Wave 1 SDK-gated framework — see SDK_GATED_FRAMEWORKS. */
|
|
812
|
-
function formatSdkGatedEnv(sops, proxyUrl, info) {
|
|
813
|
-
const pipInstall = info.pipExtra ? `intutic-clawde[${info.pipExtra}]` : 'intutic-clawde';
|
|
814
|
-
return (formatCodex(sops, proxyUrl) +
|
|
815
|
-
'\n' +
|
|
816
|
-
`# These env vars govern LLM egress only. ${info.label} tools run in your own\n` +
|
|
817
|
-
'# Python process, where no config or hook file can gate them — the\n' +
|
|
818
|
-
'# blocking tool gate ships SDK-side:\n' +
|
|
819
|
-
`# pip install ${pipInstall}\n` +
|
|
820
|
-
`# ${info.importLine}\n` +
|
|
821
|
-
`# See https://docs.intutic.ai/integrations/${info.docsSlug}\n`);
|
|
822
|
-
}
|
|
823
|
-
/**
|
|
824
|
-
* T2: two more frameworks in LangGraph's family — tools are plain
|
|
825
|
-
* objects/callables in the agent's own Node.js process, so the daemon has no
|
|
826
|
-
* on-disk hook/config file to gate a call with, and both write the same
|
|
827
|
-
* `.env.intutic` shape the Python family does. JS/TS-native, so the pointer
|
|
828
|
-
* comment reads `npm install`/`import { ... } from '@intutic/gate/<subpath>'`
|
|
829
|
-
* rather than `pip install`/`from intutic_clawde...` — see
|
|
830
|
-
* `formatJsSdkGatedEnv`.
|
|
831
|
-
*/
|
|
832
|
-
const JS_SDK_GATED_FRAMEWORKS = {
|
|
833
|
-
mastra: {
|
|
834
|
-
label: 'Mastra',
|
|
835
|
-
importLine: "import { intuticHooks } from '@intutic/gate/mastra'",
|
|
836
|
-
usageSummary: "new Agent({ ..., hooks: intuticHooks() }) — Mastra's beforeToolCall veto point. NOTE: " +
|
|
837
|
-
'per-call `hooks` on .generate()/.stream() OVERRIDE this, not merge with it.',
|
|
838
|
-
docsSlug: 'mastra',
|
|
839
|
-
},
|
|
840
|
-
'vercel-ai-sdk': {
|
|
841
|
-
label: 'Vercel AI SDK',
|
|
842
|
-
importLine: "import { intuticToolApproval, withIntuticProxy } from '@intutic/gate/vercel'",
|
|
843
|
-
usageSummary: 'generateText({ ..., toolApproval: intuticToolApproval() }) — the toolApproval veto ' +
|
|
844
|
-
'point. NOTE: this framework has NO env-var LLM-egress routing; the vars above do not ' +
|
|
845
|
-
'route it — use withIntuticProxy(createOpenAI)(...) or equivalent in code.',
|
|
846
|
-
docsSlug: 'vercel-ai-sdk',
|
|
847
|
-
},
|
|
848
|
-
eve: {
|
|
849
|
-
label: 'eve',
|
|
850
|
-
importLine: "import { intuticApproval, intuticAuditHooks } from '@intutic/gate/eve'",
|
|
851
|
-
usageSummary: 'defineTool({ ..., approval: intuticApproval() }) per tool (and intuticConnectionApproval() ' +
|
|
852
|
-
"per MCP/OpenAPI connection) — eve has no agent-level default approval field. NOTE: eve's " +
|
|
853
|
-
'default AI Gateway model routing is NOT proxy-governable; the vars above only reach a ' +
|
|
854
|
-
'direct-provider model built in code via withIntuticProxy(...). PREVIEW product.',
|
|
855
|
-
docsSlug: 'eve',
|
|
856
|
-
},
|
|
857
|
-
trueforge: {
|
|
858
|
-
label: 'TrueForge (embedded)',
|
|
859
|
-
importLine: "import { intuticApprovalResponder } from '@intutic/gate/trueforge'",
|
|
860
|
-
usageSummary: 'intuticApprovalResponder() answers tool.approval_required pauses with a real ' +
|
|
861
|
-
'Gate.guard() verdict, producing user.tool_approval items for your next ' +
|
|
862
|
-
'session.createTurn() call — TrueForge has no synchronous approval callback to hang a ' +
|
|
863
|
-
"function off (confirmed against a real install); see that module's doc. Covers ONLY " +
|
|
864
|
-
'the embedded-library deployment mode — TrueForge run as its own server is governed ' +
|
|
865
|
-
'by the TrueForge bridge service instead (see the server-mode docs).',
|
|
866
|
-
docsSlug: 'trueforge',
|
|
867
|
-
},
|
|
868
|
-
'ai-sdk-harness': {
|
|
869
|
-
label: 'AI SDK Harness',
|
|
870
|
-
importLine: "import { intuticApprovalResponder, intuticStaticApprovals, recommendedHarnessSettings } from '@intutic/gate/harness'",
|
|
871
|
-
usageSummary: 'toolApproval: intuticStaticApprovals(tools) routes every custom tool through the approval ' +
|
|
872
|
-
'flow; answer pauses with intuticApprovalResponder(). NOTE: built-in sandbox tools ignore ' +
|
|
873
|
-
"toolApproval entirely — set permissionMode (defaults to 'allow-all') via " +
|
|
874
|
-
'recommendedHarnessSettings(); sandbox egress never crosses this proxy — set a sandbox ' +
|
|
875
|
-
'networkPolicy.',
|
|
876
|
-
docsSlug: 'ai-sdk-harness',
|
|
877
|
-
envPreamble: [
|
|
878
|
-
'These env vars govern LLM egress from THIS process only. AI SDK Harness',
|
|
879
|
-
'agents execute their tools server-side in Vercel Sandbox microVMs — that',
|
|
880
|
-
'traffic never crosses this proxy, and no config or hook file can gate the',
|
|
881
|
-
'sandbox from here. The blocking tool gate (custom host-executed tools',
|
|
882
|
-
'only) ships SDK-side:',
|
|
883
|
-
],
|
|
884
|
-
},
|
|
885
|
-
'ai-sdk-workflow': {
|
|
886
|
-
label: 'AI SDK Workflow',
|
|
887
|
-
importLine: "import { intuticApprovalStep, withIntuticApproval } from '@intutic/gate/workflow'",
|
|
888
|
-
usageSummary: 'new WorkflowAgent({ ..., tools: withIntuticApproval(tools, { step: intuticGate }) }), where ' +
|
|
889
|
-
"intuticGate is your own \"use step\" function returning intuticApprovalStep(toolName, input, { gate }) " +
|
|
890
|
-
'(needsApproval runs in the workflow sandbox, which has no Node.js). Attaches an async ' +
|
|
891
|
-
'needsApproval per tool (WorkflowAgent itself has no approval option). BLOCK throws a ' +
|
|
892
|
-
'FatalError-compatible refusal so the durable runtime aborts instead of retry-looping ' +
|
|
893
|
-
"the denial; ALLOW resolves false (or true with { onAllow: 'human' }).",
|
|
894
|
-
docsSlug: 'ai-sdk-workflow',
|
|
895
|
-
},
|
|
896
|
-
};
|
|
897
|
-
/** Shared formatter for every T2 JS/TS SDK-gated framework — see
|
|
898
|
-
* JS_SDK_GATED_FRAMEWORKS. Same env-var block `formatSdkGatedEnv` writes,
|
|
899
|
-
* but "npm install"/an ESM import line/"Node.js process" in the pointer
|
|
900
|
-
* comment, not "pip install"/"Python process". */
|
|
901
|
-
function formatJsSdkGatedEnv(sops, proxyUrl, info) {
|
|
902
|
-
const preamble = info.envPreamble ?? [
|
|
903
|
-
`These env vars govern LLM egress only. ${info.label} tools run in your own`,
|
|
904
|
-
'Node.js process, where no config or hook file can gate them — the',
|
|
905
|
-
'blocking tool gate ships SDK-side:',
|
|
906
|
-
];
|
|
907
|
-
return (formatCodex(sops, proxyUrl) +
|
|
908
|
-
'\n' +
|
|
909
|
-
preamble.map((line) => `# ${line}\n`).join('') +
|
|
910
|
-
`# npm install @intutic/gate\n` +
|
|
911
|
-
`# ${info.importLine}\n` +
|
|
912
|
-
`# ${info.usageSummary}\n` +
|
|
913
|
-
`# See https://docs.intutic.ai/integrations/${info.docsSlug}\n`);
|
|
914
|
-
}
|
|
915
283
|
// ─── Atomic file write ───────────────────────────────────────────────
|
|
916
284
|
/**
|
|
917
285
|
* Write content to a file atomically.
|
|
@@ -920,11 +288,9 @@ function formatJsSdkGatedEnv(sops, proxyUrl, info) {
|
|
|
920
288
|
* This prevents partial/corrupt files if the process is interrupted mid-write.
|
|
921
289
|
*
|
|
922
290
|
* If bypassEnforcementTier === 'immutable', clears the macOS user-immutable
|
|
923
|
-
* flag before writing and re-sets it after
|
|
291
|
+
* flag before writing and re-sets it after.
|
|
924
292
|
*
|
|
925
|
-
* Exported for
|
|
926
|
-
* `lib/decisionsDigest.ts`) that want the same write-tmp-then-rename
|
|
927
|
-
* atomicity without duplicating it.
|
|
293
|
+
* Exported for `lib/decisionsDigest.ts`, which regenerates whole files too.
|
|
928
294
|
*/
|
|
929
295
|
export async function atomicWrite(filePath, content, bypassEnforcementTier) {
|
|
930
296
|
const dir = node_path.dirname(filePath);
|