@intutic/sync-daemon 2.2.0 → 2.4.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 +23 -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 +1 -3
- package/dist/configReader.d.ts.map +1 -1
- package/dist/configReader.js +0 -2
- package/dist/configReader.js.map +1 -1
- package/dist/configWriter.d.ts +11 -12
- package/dist/configWriter.d.ts.map +1 -1
- package/dist/configWriter.js +35 -58
- package/dist/configWriter.js.map +1 -1
- package/dist/disconnect/harnesses.d.ts +0 -2
- package/dist/disconnect/harnesses.d.ts.map +1 -1
- package/dist/disconnect/harnesses.js +157 -80
- package/dist/disconnect/harnesses.js.map +1 -1
- package/dist/disconnect/index.d.ts.map +1 -1
- package/dist/disconnect/index.js +11 -1
- package/dist/disconnect/index.js.map +1 -1
- package/dist/disconnect/mcp.d.ts +21 -0
- package/dist/disconnect/mcp.d.ts.map +1 -1
- package/dist/disconnect/mcp.js +71 -0
- package/dist/disconnect/mcp.js.map +1 -1
- package/dist/disconnect/recognise.d.ts +2 -2
- package/dist/disconnect/recognise.d.ts.map +1 -1
- package/dist/disconnect/recognise.js +15 -5
- package/dist/disconnect/recognise.js.map +1 -1
- package/dist/harness/aiderConfigMerger.d.ts +28 -8
- package/dist/harness/aiderConfigMerger.d.ts.map +1 -1
- package/dist/harness/aiderConfigMerger.js +59 -11
- 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 +36 -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 -5
- package/dist/harness/claudeCodeHooks.d.ts.map +1 -1
- package/dist/harness/claudeCodeHooks.js +98 -24
- package/dist/harness/claudeCodeHooks.js.map +1 -1
- package/dist/harness/clineHooks.d.ts +0 -1
- package/dist/harness/clineHooks.d.ts.map +1 -1
- package/dist/harness/clineHooks.js +10 -4
- package/dist/harness/clineHooks.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 +12 -4
- package/dist/harness/codexHooks.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 +9 -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 +5 -5
- 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 +186 -69
- package/dist/harness/gateBody.d.ts.map +1 -1
- package/dist/harness/gateBody.js +820 -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 +7 -1
- 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 +14 -1
- package/dist/harness/gooseHooks.d.ts.map +1 -1
- package/dist/harness/gooseHooks.js +55 -29
- 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 +11 -6
- 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 +48 -17
- package/dist/harness/hermesHooks.js.map +1 -1
- package/dist/harness/mcpAutoWrite.d.ts +24 -9
- package/dist/harness/mcpAutoWrite.d.ts.map +1 -1
- package/dist/harness/mcpAutoWrite.js +218 -25
- 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 +9 -5
- 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 +7 -3
- 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 +5 -1
- package/dist/harness/openWebuiHooks.js.map +1 -1
- package/dist/harness/openclawHooks.d.ts +70 -18
- package/dist/harness/openclawHooks.d.ts.map +1 -1
- package/dist/harness/openclawHooks.js +213 -161
- package/dist/harness/openclawHooks.js.map +1 -1
- package/dist/harness/openhandsHooks.d.ts +24 -10
- package/dist/harness/openhandsHooks.d.ts.map +1 -1
- package/dist/harness/openhandsHooks.js +49 -48
- 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 -166
- 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 +0 -1
- package/dist/harness/windsurfHooks.d.ts.map +1 -1
- package/dist/harness/windsurfHooks.js +4 -1
- package/dist/harness/windsurfHooks.js.map +1 -1
- package/dist/index.d.ts +12 -8
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +11 -9
- 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 +34 -16
- package/dist/lib/decisionsDigest.d.ts.map +1 -1
- package/dist/lib/decisionsDigest.js +105 -46
- 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 +46 -10
- package/dist/lib/policySnapshot.d.ts.map +1 -1
- package/dist/lib/policySnapshot.js +136 -47
- 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 +17 -11
- package/dist/sessionReporter.d.ts.map +1 -1
- package/dist/sessionReporter.js +36 -23
- 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.map +1 -1
- package/dist/syncCycle.js +21 -9
- package/dist/syncCycle.js.map +1 -1
- package/dist/trajectoryMonitor.d.ts +1 -3
- package/dist/trajectoryMonitor.d.ts.map +1 -1
- package/dist/trajectoryMonitor.js +1 -4
- 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 +33 -7
- package/dist/watcher/settingsGuard.d.ts.map +1 -1
- package/dist/watcher/settingsGuard.js +373 -61
- 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 -33
- package/dist/harness/claudeDesktopHooks.d.ts.map +0 -1
- package/dist/harness/claudeDesktopHooks.js +0 -256
- 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 -250
- 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 -281
- package/dist/harness/rooCodeHooks.js.map +0 -1
|
@@ -1,164 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* gateKind.ts — classifies each harness by HOW its tool calls get governed,
|
|
3
|
-
* for `agentReporter.ts`'s `guardrails` facet.
|
|
4
|
-
*
|
|
5
|
-
* # Why this exists
|
|
6
|
-
*
|
|
7
|
-
* `collectAgentReport` used to report `hook_gate: true` unconditionally for
|
|
8
|
-
* every harness, with the comment "the daemon writes the hook gate for every
|
|
9
|
-
* harness it supports." That was already false for `langgraph` — which has
|
|
10
|
-
* no on-disk hook, only the SDK-side `intutic_clawde.gate` — and for `aider`,
|
|
11
|
-
* whose only NO_GATE-registered mechanism is a post-edit, /run-blind
|
|
12
|
-
* pre-commit hook (see `aiderConfigMerger.ts`'s NO_GATE row in
|
|
13
|
-
* `__tests__/harness/gateRegistry.ts`). Wave 1 adds eight more SDK-gated
|
|
14
|
-
* harnesses, which would have made the lie eight times larger without this
|
|
15
|
-
* fix.
|
|
16
|
-
*
|
|
17
|
-
* # What this does NOT do
|
|
18
|
-
*
|
|
19
|
-
* This does not verify that a user actually pip-installed `intutic-clawde`
|
|
20
|
-
* and wired `guard_tools`/`IntuticMiddleware`/etc. into their process — the
|
|
21
|
-
* daemon cannot observe another process's imports, the same way it cannot
|
|
22
|
-
* observe whether a written hook file was left executable. It only reports
|
|
23
|
-
* which MECHANISM this harness's governance model uses (a file the daemon
|
|
24
|
-
* itself writes and can verify existence of, vs. an SDK the harness's own
|
|
25
|
-
* process must import), matching the trust level `hook_gate: true` already
|
|
26
|
-
* carried for file-based harnesses before this fix.
|
|
27
|
-
*
|
|
28
|
-
* # Keeping this in sync with the gate registry
|
|
29
|
-
*
|
|
30
|
-
* `services/sync-daemon/__tests__/harness/gateRegistry.ts` (the GATES/NO_GATE
|
|
31
|
-
* completeness registry exercised by `generatedGateBehaviour.test.ts`) is the
|
|
32
|
-
* authoritative decision per harness, but it is a test-only module (it
|
|
33
|
-
* dynamically imports every writer's compiled `.js` from a temp build dir)
|
|
34
|
-
* and must not be imported from daemon runtime code. `gateKind.test.ts`
|
|
35
|
-
* cross-checks the two lists so this file cannot silently drift from that
|
|
36
|
-
* registry's own NO_GATE rows: `file: null` harnesses split between 'sdk'
|
|
37
|
-
* (LangGraph + the Python frameworks — the gate ships in intutic-clawde),
|
|
38
|
-
* 'delegated' (xirp — the gate is whichever wrapped harness's own gate is
|
|
39
|
-
* running), and 'bridge' (trueforge-server — the gate runs in a separate
|
|
40
|
-
* Intutic-operated service, not in any process this repo's writers touch);
|
|
41
|
-
* the one non-null row (aiderConfigMerger.ts, "no ... exists") is 'none'.
|
|
42
|
-
*
|
|
43
|
-
* @module
|
|
44
|
-
*/
|
|
45
|
-
import { type HarnessType as HarnessTypeT } from '@intutic/shared-types';
|
|
46
|
-
/**
|
|
47
|
-
* `'hook'` — the daemon writes an on-disk hook/config file this harness
|
|
48
|
-
* reads before running a tool call (the 19-harness majority).
|
|
49
|
-
* `'sdk'` — the blocking gate ships in `intutic-clawde`, imported into
|
|
50
|
-
* the harness's own Python process; no file for the daemon
|
|
51
|
-
* to write or verify (LangGraph + the eight Wave/Wave-2
|
|
52
|
-
* frameworks).
|
|
53
|
-
* `'none'` — no enforcement point exists today at all (aider: its only
|
|
54
|
-
* native hook is post-edit and /run-blind).
|
|
55
|
-
* `'delegated'` — this harness has no gate mechanism of its own because it
|
|
56
|
-
* does not run tools itself: it wraps OTHER harnesses that
|
|
57
|
-
* are already `'hook'`- or `'sdk'`-gated, and a tool call
|
|
58
|
-
* made inside it is governed by whichever wrapped harness's
|
|
59
|
-
* own gate is running (Xirp, Agentic Orchestrator, and — a
|
|
60
|
-
* different shape of the same idea — AgentCore Runtime,
|
|
61
|
-
* which HOSTS a customer's already-gated framework code
|
|
62
|
-
* rather than spawning it as a subprocess; see
|
|
63
|
-
* gateRegistry.ts's NO_GATE rows for the reasoning this
|
|
64
|
-
* precedent sets for future orchestrator/host-shaped
|
|
65
|
-
* harnesses).
|
|
66
|
-
* Every wrapped backend of the two orchestrators has a gate of
|
|
67
|
-
* its own (Agentic Orchestrator's `opencode` backend since
|
|
68
|
-
* TD-397 closed with `openCodeHooks.ts`), so `'delegated'`
|
|
69
|
-
* claims exactly what happens. Distinct from
|
|
70
|
-
* `'none'`: `'none'` means no enforcement point exists
|
|
71
|
-
* ANYWHERE for this harness's tool calls; `'delegated'`
|
|
72
|
-
* means one exists, just not one this harness's own row
|
|
73
|
-
* owns or can be credited for independently — reporting it
|
|
74
|
-
* as `'none'` would undercount real coverage, and reporting
|
|
75
|
-
* it as `'hook'` would overclaim a file this daemon never
|
|
76
|
-
* writes for the wrapping harness itself.
|
|
77
|
-
* `'bridge'` — the gate runs OUT OF PROCESS, in an Intutic-operated
|
|
78
|
-
* service reacting to the harness's own async
|
|
79
|
-
* approval/event API, rather than a file this daemon writes
|
|
80
|
-
* (`'hook'`), an SDK imported into the harness's own process
|
|
81
|
-
* (`'sdk'`), no enforcement point at all (`'none'`), or
|
|
82
|
-
* governance credited to a wrapped harness's own gate
|
|
83
|
-
* (`'delegated'`) — a fifth, genuinely different shape.
|
|
84
|
-
* TRUEFORGE_SERVER is the first and so far only member: a
|
|
85
|
-
* TrueForge standalone/hosted server is a separate process
|
|
86
|
-
* nobody embeds an Intutic gate into (unlike TRUEFORGE,
|
|
87
|
-
* embedded mode, which IS `'sdk'`-gated — see that row's own
|
|
88
|
-
* doc), so `services/trueforge-bridge` watches its turn/event
|
|
89
|
-
* stream externally and answers pending approvals over HTTP.
|
|
90
|
-
* Distinct from `'delegated'`: a delegated harness's tool
|
|
91
|
-
* calls are governed by ANOTHER HARNESS's own gate running
|
|
92
|
-
* in that other harness's process; a bridge-gated harness's
|
|
93
|
-
* tool calls are governed by a service this repo ships and
|
|
94
|
-
* operates, running in neither the harness's process nor any
|
|
95
|
-
* other harness's. Distinct from `'sdk'`: no code from this
|
|
96
|
-
* repo ever runs inside the governed harness's own process
|
|
97
|
-
* at all — the harness's only obligation is to expose the
|
|
98
|
-
* async approval API the bridge polls/subscribes to.
|
|
99
|
-
*/
|
|
100
|
-
export type GateKind = 'hook' | 'sdk' | 'none' | 'delegated' | 'bridge';
|
|
101
|
-
/**
|
|
102
|
-
* Harnesses whose blocking gate ships SDK-side, in the harness's own
|
|
103
|
-
* process — no on-disk hook/config file exists to point a gate at. Mirrors
|
|
104
|
-
* the `file: null` NO_GATE rows in `gateRegistry.ts`.
|
|
105
|
-
*
|
|
106
|
-
* MASTRA, VERCEL_AI_SDK (T2), EVE (A2), and TRUEFORGE (embedded-library mode
|
|
107
|
-
* only — see its own HarnessType doc) are the JS/TS-native members of this
|
|
108
|
-
* family: their blocking gate ships in `@intutic/gate/mastra`/
|
|
109
|
-
* `@intutic/gate/vercel`/`@intutic/gate/eve`/`@intutic/gate/trueforge`
|
|
110
|
-
* (packages/gate-js) rather than `intutic-clawde`, but the shape is
|
|
111
|
-
* identical — no on-disk hook/config file, tools run as plain callables in
|
|
112
|
-
* the harness's own process (for eve, as per-tool/per-connection `approval`
|
|
113
|
-
* policies attached in the agent directory's own TypeScript; for TrueForge,
|
|
114
|
-
* as `intuticApprovalResponder()` answering `tool.approval_required`
|
|
115
|
-
* pauses — see `@intutic/gate/trueforge`'s module doc for why that shape
|
|
116
|
-
* differs from mastra/vercel's single-callback pattern).
|
|
117
|
-
*
|
|
118
|
-
* AI_SDK_HARNESS and AI_SDK_WORKFLOW (A3) are the same `@intutic/gate`
|
|
119
|
-
* family with one nuance worth naming: the VETO still runs SDK-side in the
|
|
120
|
-
* caller's own Node.js process (an approval responder / a `needsApproval`
|
|
121
|
-
* function), which is what `'sdk'` classifies — but for AI_SDK_HARNESS the
|
|
122
|
-
* tool EXECUTION it vetoes happens server-side in a Vercel Sandbox microVM,
|
|
123
|
-
* and its built-in sandbox tools have no veto surface at all beyond
|
|
124
|
-
* `permissionMode` (defaults to 'allow-all'). `'sdk'` describes where the
|
|
125
|
-
* gate mechanism lives, not a claim that every tool the harness can run
|
|
126
|
-
* passes through it — see gateRegistry.ts's NO_GATE rows for the honest
|
|
127
|
-
* scope statement per harness.
|
|
128
|
-
*/
|
|
129
|
-
export declare const SDK_GATED_HARNESSES: ReadonlySet<HarnessTypeT>;
|
|
130
|
-
/**
|
|
131
|
-
* Harnesses with no enforcement point at all today. Mirrors the
|
|
132
|
-
* `aiderConfigMerger.ts` NO_GATE row: aider's only native hook
|
|
133
|
-
* (`--git-commit-verify`) is opt-in, post-edit, and blind to `/run`.
|
|
134
|
-
*/
|
|
135
|
-
export declare const NO_GATE_HARNESSES: ReadonlySet<HarnessTypeT>;
|
|
136
|
-
/**
|
|
137
|
-
* Harnesses that wrap OTHER already-gated harnesses instead of running tools
|
|
138
|
-
* themselves — no gate of their own, but not ungoverned either. Mirrors the
|
|
139
|
-
* `file: null` NO_GATE rows for `xirp` and `agentic-orchestrator` in
|
|
140
|
-
* `gateRegistry.ts`. Xirp was the first entry of this shape; Agentic
|
|
141
|
-
* Orchestrator (O3) is the second. O2 (QM) is expected to add a third, same
|
|
142
|
-
* "wraps other harnesses" shape, in a concurrent phase not yet merged as of
|
|
143
|
-
* this one.
|
|
144
|
-
*/
|
|
145
|
-
export declare const DELEGATED_GATE_HARNESSES: ReadonlySet<HarnessTypeT>;
|
|
146
|
-
/**
|
|
147
|
-
* Harnesses whose blocking gate runs OUT OF PROCESS, in an Intutic-operated
|
|
148
|
-
* service reacting to the harness's own async approval/event API — not a
|
|
149
|
-
* file this daemon writes, and not an SDK imported into the harness's own
|
|
150
|
-
* process (no process of ours runs inside the harness at all). Mirrors the
|
|
151
|
-
* `file: null` NO_GATE row for `trueforge-server` in `gateRegistry.ts`.
|
|
152
|
-
*
|
|
153
|
-
* TRUEFORGE_SERVER (B3) is the first member: a TrueForge standalone/hosted
|
|
154
|
-
* server has no in-process host to hang an SDK gate off of (contrast
|
|
155
|
-
* TRUEFORGE, embedded mode, in `SDK_GATED_HARNESSES` above) — instead
|
|
156
|
-
* `services/trueforge-bridge` watches its turn/event stream externally and
|
|
157
|
-
* answers pending `tool.approval_required` pauses over HTTP. See the
|
|
158
|
-
* `'bridge'` `GateKind` doc above for the full distinction from `'sdk'` and
|
|
159
|
-
* `'delegated'`.
|
|
160
|
-
*/
|
|
161
|
-
export declare const BRIDGE_GATED_HARNESSES: ReadonlySet<HarnessTypeT>;
|
|
162
|
-
/** How this harness's tool calls get gated. Defaults to `'hook'`. */
|
|
163
|
-
export declare function gateKindForHarness(type: HarnessTypeT): GateKind;
|
|
164
|
-
//# sourceMappingURL=gateKind.d.ts.map
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"file":"gateKind.d.ts","sourceRoot":"","sources":["../../src/harness/gateKind.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GA2CG;AAEH,OAAO,EAAe,KAAK,WAAW,IAAI,YAAY,EAAE,MAAM,uBAAuB,CAAA;AAErF;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GAqDG;AACH,MAAM,MAAM,QAAQ,GAAG,MAAM,GAAG,KAAK,GAAG,MAAM,GAAG,WAAW,GAAG,QAAQ,CAAA;AAEvE;;;;;;;;;;;;;;;;;;;;;;;;;;;GA2BG;AACH,eAAO,MAAM,mBAAmB,EAAE,WAAW,CAAC,YAAY,CAkBxD,CAAA;AAEF;;;;GAIG;AACH,eAAO,MAAM,iBAAiB,EAAE,WAAW,CAAC,YAAY,CAAgC,CAAA;AAExF;;;;;;;;GAQG;AACH,eAAO,MAAM,wBAAwB,EAAE,WAAW,CAAC,YAAY,CAO7D,CAAA;AAEF;;;;;;;;;;;;;;GAcG;AACH,eAAO,MAAM,sBAAsB,EAAE,WAAW,CAAC,YAAY,CAA2C,CAAA;AAExG,qEAAqE;AACrE,wBAAgB,kBAAkB,CAAC,IAAI,EAAE,YAAY,GAAG,QAAQ,CAM/D"}
|
package/dist/harness/gateKind.js
DELETED
|
@@ -1,144 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* gateKind.ts — classifies each harness by HOW its tool calls get governed,
|
|
3
|
-
* for `agentReporter.ts`'s `guardrails` facet.
|
|
4
|
-
*
|
|
5
|
-
* # Why this exists
|
|
6
|
-
*
|
|
7
|
-
* `collectAgentReport` used to report `hook_gate: true` unconditionally for
|
|
8
|
-
* every harness, with the comment "the daemon writes the hook gate for every
|
|
9
|
-
* harness it supports." That was already false for `langgraph` — which has
|
|
10
|
-
* no on-disk hook, only the SDK-side `intutic_clawde.gate` — and for `aider`,
|
|
11
|
-
* whose only NO_GATE-registered mechanism is a post-edit, /run-blind
|
|
12
|
-
* pre-commit hook (see `aiderConfigMerger.ts`'s NO_GATE row in
|
|
13
|
-
* `__tests__/harness/gateRegistry.ts`). Wave 1 adds eight more SDK-gated
|
|
14
|
-
* harnesses, which would have made the lie eight times larger without this
|
|
15
|
-
* fix.
|
|
16
|
-
*
|
|
17
|
-
* # What this does NOT do
|
|
18
|
-
*
|
|
19
|
-
* This does not verify that a user actually pip-installed `intutic-clawde`
|
|
20
|
-
* and wired `guard_tools`/`IntuticMiddleware`/etc. into their process — the
|
|
21
|
-
* daemon cannot observe another process's imports, the same way it cannot
|
|
22
|
-
* observe whether a written hook file was left executable. It only reports
|
|
23
|
-
* which MECHANISM this harness's governance model uses (a file the daemon
|
|
24
|
-
* itself writes and can verify existence of, vs. an SDK the harness's own
|
|
25
|
-
* process must import), matching the trust level `hook_gate: true` already
|
|
26
|
-
* carried for file-based harnesses before this fix.
|
|
27
|
-
*
|
|
28
|
-
* # Keeping this in sync with the gate registry
|
|
29
|
-
*
|
|
30
|
-
* `services/sync-daemon/__tests__/harness/gateRegistry.ts` (the GATES/NO_GATE
|
|
31
|
-
* completeness registry exercised by `generatedGateBehaviour.test.ts`) is the
|
|
32
|
-
* authoritative decision per harness, but it is a test-only module (it
|
|
33
|
-
* dynamically imports every writer's compiled `.js` from a temp build dir)
|
|
34
|
-
* and must not be imported from daemon runtime code. `gateKind.test.ts`
|
|
35
|
-
* cross-checks the two lists so this file cannot silently drift from that
|
|
36
|
-
* registry's own NO_GATE rows: `file: null` harnesses split between 'sdk'
|
|
37
|
-
* (LangGraph + the Python frameworks — the gate ships in intutic-clawde),
|
|
38
|
-
* 'delegated' (xirp — the gate is whichever wrapped harness's own gate is
|
|
39
|
-
* running), and 'bridge' (trueforge-server — the gate runs in a separate
|
|
40
|
-
* Intutic-operated service, not in any process this repo's writers touch);
|
|
41
|
-
* the one non-null row (aiderConfigMerger.ts, "no ... exists") is 'none'.
|
|
42
|
-
*
|
|
43
|
-
* @module
|
|
44
|
-
*/
|
|
45
|
-
import { HarnessType } from '@intutic/shared-types';
|
|
46
|
-
/**
|
|
47
|
-
* Harnesses whose blocking gate ships SDK-side, in the harness's own
|
|
48
|
-
* process — no on-disk hook/config file exists to point a gate at. Mirrors
|
|
49
|
-
* the `file: null` NO_GATE rows in `gateRegistry.ts`.
|
|
50
|
-
*
|
|
51
|
-
* MASTRA, VERCEL_AI_SDK (T2), EVE (A2), and TRUEFORGE (embedded-library mode
|
|
52
|
-
* only — see its own HarnessType doc) are the JS/TS-native members of this
|
|
53
|
-
* family: their blocking gate ships in `@intutic/gate/mastra`/
|
|
54
|
-
* `@intutic/gate/vercel`/`@intutic/gate/eve`/`@intutic/gate/trueforge`
|
|
55
|
-
* (packages/gate-js) rather than `intutic-clawde`, but the shape is
|
|
56
|
-
* identical — no on-disk hook/config file, tools run as plain callables in
|
|
57
|
-
* the harness's own process (for eve, as per-tool/per-connection `approval`
|
|
58
|
-
* policies attached in the agent directory's own TypeScript; for TrueForge,
|
|
59
|
-
* as `intuticApprovalResponder()` answering `tool.approval_required`
|
|
60
|
-
* pauses — see `@intutic/gate/trueforge`'s module doc for why that shape
|
|
61
|
-
* differs from mastra/vercel's single-callback pattern).
|
|
62
|
-
*
|
|
63
|
-
* AI_SDK_HARNESS and AI_SDK_WORKFLOW (A3) are the same `@intutic/gate`
|
|
64
|
-
* family with one nuance worth naming: the VETO still runs SDK-side in the
|
|
65
|
-
* caller's own Node.js process (an approval responder / a `needsApproval`
|
|
66
|
-
* function), which is what `'sdk'` classifies — but for AI_SDK_HARNESS the
|
|
67
|
-
* tool EXECUTION it vetoes happens server-side in a Vercel Sandbox microVM,
|
|
68
|
-
* and its built-in sandbox tools have no veto surface at all beyond
|
|
69
|
-
* `permissionMode` (defaults to 'allow-all'). `'sdk'` describes where the
|
|
70
|
-
* gate mechanism lives, not a claim that every tool the harness can run
|
|
71
|
-
* passes through it — see gateRegistry.ts's NO_GATE rows for the honest
|
|
72
|
-
* scope statement per harness.
|
|
73
|
-
*/
|
|
74
|
-
export const SDK_GATED_HARNESSES = new Set([
|
|
75
|
-
HarnessType.LANGGRAPH,
|
|
76
|
-
HarnessType.LANGCHAIN,
|
|
77
|
-
HarnessType.CREWAI,
|
|
78
|
-
HarnessType.AUTOGEN,
|
|
79
|
-
HarnessType.AG2,
|
|
80
|
-
HarnessType.GOOGLE_ADK,
|
|
81
|
-
HarnessType.OPENAI_AGENTS,
|
|
82
|
-
HarnessType.PYDANTIC_AI,
|
|
83
|
-
HarnessType.SMOLAGENTS,
|
|
84
|
-
HarnessType.STRANDS,
|
|
85
|
-
HarnessType.AGENT_FRAMEWORK,
|
|
86
|
-
HarnessType.MASTRA,
|
|
87
|
-
HarnessType.VERCEL_AI_SDK,
|
|
88
|
-
HarnessType.EVE,
|
|
89
|
-
HarnessType.TRUEFORGE,
|
|
90
|
-
HarnessType.AI_SDK_HARNESS,
|
|
91
|
-
HarnessType.AI_SDK_WORKFLOW,
|
|
92
|
-
]);
|
|
93
|
-
/**
|
|
94
|
-
* Harnesses with no enforcement point at all today. Mirrors the
|
|
95
|
-
* `aiderConfigMerger.ts` NO_GATE row: aider's only native hook
|
|
96
|
-
* (`--git-commit-verify`) is opt-in, post-edit, and blind to `/run`.
|
|
97
|
-
*/
|
|
98
|
-
export const NO_GATE_HARNESSES = new Set([HarnessType.AIDER]);
|
|
99
|
-
/**
|
|
100
|
-
* Harnesses that wrap OTHER already-gated harnesses instead of running tools
|
|
101
|
-
* themselves — no gate of their own, but not ungoverned either. Mirrors the
|
|
102
|
-
* `file: null` NO_GATE rows for `xirp` and `agentic-orchestrator` in
|
|
103
|
-
* `gateRegistry.ts`. Xirp was the first entry of this shape; Agentic
|
|
104
|
-
* Orchestrator (O3) is the second. O2 (QM) is expected to add a third, same
|
|
105
|
-
* "wraps other harnesses" shape, in a concurrent phase not yet merged as of
|
|
106
|
-
* this one.
|
|
107
|
-
*/
|
|
108
|
-
export const DELEGATED_GATE_HARNESSES = new Set([
|
|
109
|
-
HarnessType.XIRP,
|
|
110
|
-
HarnessType.AGENTIC_ORCHESTRATOR,
|
|
111
|
-
// B2: AWS Bedrock AgentCore Runtime — hosts a customer's own framework
|
|
112
|
-
// code unchanged; governed by whichever already-supported framework
|
|
113
|
-
// adapter that code uses. See tools/cli/src/harness/agentcore.ts.
|
|
114
|
-
HarnessType.AGENTCORE_RUNTIME,
|
|
115
|
-
]);
|
|
116
|
-
/**
|
|
117
|
-
* Harnesses whose blocking gate runs OUT OF PROCESS, in an Intutic-operated
|
|
118
|
-
* service reacting to the harness's own async approval/event API — not a
|
|
119
|
-
* file this daemon writes, and not an SDK imported into the harness's own
|
|
120
|
-
* process (no process of ours runs inside the harness at all). Mirrors the
|
|
121
|
-
* `file: null` NO_GATE row for `trueforge-server` in `gateRegistry.ts`.
|
|
122
|
-
*
|
|
123
|
-
* TRUEFORGE_SERVER (B3) is the first member: a TrueForge standalone/hosted
|
|
124
|
-
* server has no in-process host to hang an SDK gate off of (contrast
|
|
125
|
-
* TRUEFORGE, embedded mode, in `SDK_GATED_HARNESSES` above) — instead
|
|
126
|
-
* `services/trueforge-bridge` watches its turn/event stream externally and
|
|
127
|
-
* answers pending `tool.approval_required` pauses over HTTP. See the
|
|
128
|
-
* `'bridge'` `GateKind` doc above for the full distinction from `'sdk'` and
|
|
129
|
-
* `'delegated'`.
|
|
130
|
-
*/
|
|
131
|
-
export const BRIDGE_GATED_HARNESSES = new Set([HarnessType.TRUEFORGE_SERVER]);
|
|
132
|
-
/** How this harness's tool calls get gated. Defaults to `'hook'`. */
|
|
133
|
-
export function gateKindForHarness(type) {
|
|
134
|
-
if (SDK_GATED_HARNESSES.has(type))
|
|
135
|
-
return 'sdk';
|
|
136
|
-
if (NO_GATE_HARNESSES.has(type))
|
|
137
|
-
return 'none';
|
|
138
|
-
if (DELEGATED_GATE_HARNESSES.has(type))
|
|
139
|
-
return 'delegated';
|
|
140
|
-
if (BRIDGE_GATED_HARNESSES.has(type))
|
|
141
|
-
return 'bridge';
|
|
142
|
-
return 'hook';
|
|
143
|
-
}
|
|
144
|
-
//# sourceMappingURL=gateKind.js.map
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"file":"gateKind.js","sourceRoot":"","sources":["../../src/harness/gateKind.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GA2CG;AAEH,OAAO,EAAE,WAAW,EAAoC,MAAM,uBAAuB,CAAA;AA0DrF;;;;;;;;;;;;;;;;;;;;;;;;;;;GA2BG;AACH,MAAM,CAAC,MAAM,mBAAmB,GAA8B,IAAI,GAAG,CAAC;IACpE,WAAW,CAAC,SAAS;IACrB,WAAW,CAAC,SAAS;IACrB,WAAW,CAAC,MAAM;IAClB,WAAW,CAAC,OAAO;IACnB,WAAW,CAAC,GAAG;IACf,WAAW,CAAC,UAAU;IACtB,WAAW,CAAC,aAAa;IACzB,WAAW,CAAC,WAAW;IACvB,WAAW,CAAC,UAAU;IACtB,WAAW,CAAC,OAAO;IACnB,WAAW,CAAC,eAAe;IAC3B,WAAW,CAAC,MAAM;IAClB,WAAW,CAAC,aAAa;IACzB,WAAW,CAAC,GAAG;IACf,WAAW,CAAC,SAAS;IACrB,WAAW,CAAC,cAAc;IAC1B,WAAW,CAAC,eAAe;CAC5B,CAAC,CAAA;AAEF;;;;GAIG;AACH,MAAM,CAAC,MAAM,iBAAiB,GAA8B,IAAI,GAAG,CAAC,CAAC,WAAW,CAAC,KAAK,CAAC,CAAC,CAAA;AAExF;;;;;;;;GAQG;AACH,MAAM,CAAC,MAAM,wBAAwB,GAA8B,IAAI,GAAG,CAAC;IACzE,WAAW,CAAC,IAAI;IAChB,WAAW,CAAC,oBAAoB;IAChC,uEAAuE;IACvE,oEAAoE;IACpE,kEAAkE;IAClE,WAAW,CAAC,iBAAiB;CAC9B,CAAC,CAAA;AAEF;;;;;;;;;;;;;;GAcG;AACH,MAAM,CAAC,MAAM,sBAAsB,GAA8B,IAAI,GAAG,CAAC,CAAC,WAAW,CAAC,gBAAgB,CAAC,CAAC,CAAA;AAExG,qEAAqE;AACrE,MAAM,UAAU,kBAAkB,CAAC,IAAkB;IACnD,IAAI,mBAAmB,CAAC,GAAG,CAAC,IAAI,CAAC;QAAE,OAAO,KAAK,CAAA;IAC/C,IAAI,iBAAiB,CAAC,GAAG,CAAC,IAAI,CAAC;QAAE,OAAO,MAAM,CAAA;IAC9C,IAAI,wBAAwB,CAAC,GAAG,CAAC,IAAI,CAAC;QAAE,OAAO,WAAW,CAAA;IAC1D,IAAI,sBAAsB,CAAC,GAAG,CAAC,IAAI,CAAC;QAAE,OAAO,QAAQ,CAAA;IACrD,OAAO,MAAM,CAAA;AACf,CAAC"}
|
|
@@ -1,31 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* rooCodeHooks.ts — Roo Code (ZooCode) governance hook generator.
|
|
3
|
-
*
|
|
4
|
-
* Roo Code uses `.roorules` for SOP rules and `.roomodes` for custom modes.
|
|
5
|
-
* For hook interception, it reads hook config from `.roorules/hooks/hooks.json`.
|
|
6
|
-
* The hook result format uses stdout JSON: {"cancel": true, "errorMessage": "..."}
|
|
7
|
-
* rather than exit codes (unlike Claude Code's exit 2).
|
|
8
|
-
*
|
|
9
|
-
* Hook events are written via dual-path logEvent:
|
|
10
|
-
* Path B (reliable) — synchronous append to .intutic/events/roo-hook-events.jsonl
|
|
11
|
-
* Path A (telemetry) — fire-and-forget https.request to the control plane
|
|
12
|
-
*
|
|
13
|
-
* LLD #14 — Phase 3 cross-harness defence (Gap 3, WS-B)
|
|
14
|
-
* HLD §3.14 — Three-Tier Defense Cascade (Tier 1 Native Gating)
|
|
15
|
-
*
|
|
16
|
-
* @module
|
|
17
|
-
*/
|
|
18
|
-
/**
|
|
19
|
-
* Write the Roo Code governance hook configuration:
|
|
20
|
-
* 1. `.roorules` — SOP rules in Roo Code markdown format.
|
|
21
|
-
* 2. `.roorules/hooks/hooks.json` — hook config pointing to the Node.js gate.
|
|
22
|
-
* 3. `.intutic/hooks/roo-check.js` — Node.js dual-path hook (stdout cancel JSON).
|
|
23
|
-
*
|
|
24
|
-
* Safe to call repeatedly — uses atomic rename (`.intutic-tmp` → final path).
|
|
25
|
-
*
|
|
26
|
-
* @param workspaceRoot - Absolute workspace root path.
|
|
27
|
-
* @param proxyUrl - Intutic proxy URL written into the hook script header.
|
|
28
|
-
* @param workspaceId - Workspace ID embedded in every hook event payload.
|
|
29
|
-
*/
|
|
30
|
-
export declare function writeRooCodeHooks(workspaceRoot: string, proxyUrl: string, workspaceId?: string): Promise<void>;
|
|
31
|
-
//# sourceMappingURL=rooCodeHooks.d.ts.map
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"file":"rooCodeHooks.d.ts","sourceRoot":"","sources":["../../src/harness/rooCodeHooks.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;GAgBG;AAsNH;;;;;;;;;;;GAWG;AACH,wBAAsB,iBAAiB,CACrC,aAAa,EAAE,MAAM,EACrB,QAAQ,EAAE,MAAM,EAChB,WAAW,SAAK,GACf,OAAO,CAAC,IAAI,CAAC,CAiEf"}
|
|
@@ -1,281 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* rooCodeHooks.ts — Roo Code (ZooCode) governance hook generator.
|
|
3
|
-
*
|
|
4
|
-
* Roo Code uses `.roorules` for SOP rules and `.roomodes` for custom modes.
|
|
5
|
-
* For hook interception, it reads hook config from `.roorules/hooks/hooks.json`.
|
|
6
|
-
* The hook result format uses stdout JSON: {"cancel": true, "errorMessage": "..."}
|
|
7
|
-
* rather than exit codes (unlike Claude Code's exit 2).
|
|
8
|
-
*
|
|
9
|
-
* Hook events are written via dual-path logEvent:
|
|
10
|
-
* Path B (reliable) — synchronous append to .intutic/events/roo-hook-events.jsonl
|
|
11
|
-
* Path A (telemetry) — fire-and-forget https.request to the control plane
|
|
12
|
-
*
|
|
13
|
-
* LLD #14 — Phase 3 cross-harness defence (Gap 3, WS-B)
|
|
14
|
-
* HLD §3.14 — Three-Tier Defense Cascade (Tier 1 Native Gating)
|
|
15
|
-
*
|
|
16
|
-
* @module
|
|
17
|
-
*/
|
|
18
|
-
import * as fs from 'node:fs/promises';
|
|
19
|
-
import * as path from 'node:path';
|
|
20
|
-
import * as os from 'node:os';
|
|
21
|
-
import { createLogger } from '@intutic/logger';
|
|
22
|
-
import { keepOriginal, writeOwnedFile } from '../disconnect/originals.js';
|
|
23
|
-
import { newIso } from '@intutic/id';
|
|
24
|
-
import { emitJsGate, emitJsFailClosedPrelude } from './gateBody.js';
|
|
25
|
-
import { UNIVERSAL_PROTECTED_PATHS } from './protectedPaths.js';
|
|
26
|
-
const log = createLogger('sync-roo-code-hooks');
|
|
27
|
-
/** Governance-sensitive paths that the hook gate protects. */
|
|
28
|
-
// ─── Node.js hook script template ─────────────────────────────────────────────
|
|
29
|
-
function buildRooCheckScript(proxyUrl, workspaceRoot, workspaceId) {
|
|
30
|
-
const hookEventsLog = path.join(workspaceRoot, '.intutic', 'events', 'roo-hook-events.jsonl');
|
|
31
|
-
const runtimeEnvPath = path.join(os.homedir(), '.intutic', 'env', 'runtime.env');
|
|
32
|
-
return `/**
|
|
33
|
-
* Intutic Roo Code PreToolUse execution gate.
|
|
34
|
-
* Auto-generated by intutic sync-daemon. DO NOT EDIT.
|
|
35
|
-
*
|
|
36
|
-
* Block format: stdout JSON {"cancel": true, "errorMessage": "..."}
|
|
37
|
-
* Allow format: stdout JSON {"cancel": false}
|
|
38
|
-
*
|
|
39
|
-
* Dual-path logEvent:
|
|
40
|
-
* Path B — synchronous file append to roo-hook-events.jsonl
|
|
41
|
-
* Path A — fire-and-forget https.request to the control plane
|
|
42
|
-
*
|
|
43
|
-
* Proxy: ${proxyUrl}
|
|
44
|
-
* Generated: ${newIso()}
|
|
45
|
-
* Workspace: ${workspaceId}
|
|
46
|
-
*/
|
|
47
|
-
'use strict';
|
|
48
|
-
${emitJsFailClosedPrelude({ harness: 'roo-code', contract: 'stdout-cancel' })}
|
|
49
|
-
const path = require('path');
|
|
50
|
-
const os = require('os');
|
|
51
|
-
const fs = require('fs');
|
|
52
|
-
const https = require('https');
|
|
53
|
-
const crypto = require('crypto');
|
|
54
|
-
const url = require('url');
|
|
55
|
-
|
|
56
|
-
// ── Runtime env loader (Path A credentials) ──────────────────────────────────
|
|
57
|
-
const RUNTIME_ENV_PATH = ${JSON.stringify(runtimeEnvPath)};
|
|
58
|
-
|
|
59
|
-
function loadRuntimeEnv() {
|
|
60
|
-
const env = {
|
|
61
|
-
INTUTIC_HOST: 'https://api.intutic.ai',
|
|
62
|
-
INTUTIC_API_KEY: '',
|
|
63
|
-
INTUTIC_WORKSPACE_ID: ${JSON.stringify(workspaceId)},
|
|
64
|
-
};
|
|
65
|
-
try {
|
|
66
|
-
const raw = fs.readFileSync(RUNTIME_ENV_PATH, 'utf-8');
|
|
67
|
-
for (const line of raw.split('\\n')) {
|
|
68
|
-
const stripped = line.trim();
|
|
69
|
-
if (!stripped || stripped.startsWith('#') || !stripped.includes('=')) continue;
|
|
70
|
-
const eq = stripped.indexOf('=');
|
|
71
|
-
const k = stripped.slice(0, eq).trim();
|
|
72
|
-
const v = stripped.slice(eq + 1).trim();
|
|
73
|
-
env[k] = v;
|
|
74
|
-
}
|
|
75
|
-
} catch { /* file may not exist yet */ }
|
|
76
|
-
return env;
|
|
77
|
-
}
|
|
78
|
-
|
|
79
|
-
// ── Governance-sensitive paths ────────────────────────────────────────────────
|
|
80
|
-
${emitJsGate({ harness: "roo-code", contract: 'stdout-cancel' })}
|
|
81
|
-
|
|
82
|
-
// ── Dual-path logEvent ────────────────────────────────────────────────────────
|
|
83
|
-
const HOOK_EVENTS_LOG = ${JSON.stringify(hookEventsLog)};
|
|
84
|
-
|
|
85
|
-
let _intuticSessionId = '';
|
|
86
|
-
function logEvent(env, verdict, toolName, reason) {
|
|
87
|
-
const ts = new Date().toISOString();
|
|
88
|
-
const incidentId = crypto.createHash('sha1')
|
|
89
|
-
.update(ts + toolName + env.INTUTIC_WORKSPACE_ID)
|
|
90
|
-
.digest('hex').slice(0, 16);
|
|
91
|
-
|
|
92
|
-
const entry = {
|
|
93
|
-
// Passed through, not collapsed to two values: the advisory tier emits
|
|
94
|
-
// 'tool_flagged', and a ternary here silently recorded it as an allow.
|
|
95
|
-
event: verdict,
|
|
96
|
-
toolName,
|
|
97
|
-
reason: reason || '',
|
|
98
|
-
workspaceId: env.INTUTIC_WORKSPACE_ID,
|
|
99
|
-
harnessType: 'roo-code',
|
|
100
|
-
timestamp: ts,
|
|
101
|
-
incidentId,
|
|
102
|
-
...(_intuticSessionId ? { sessionId: _intuticSessionId } : {}),
|
|
103
|
-
};
|
|
104
|
-
|
|
105
|
-
// Path B — reliable file append
|
|
106
|
-
try {
|
|
107
|
-
const dir = path.dirname(HOOK_EVENTS_LOG);
|
|
108
|
-
if (!fs.existsSync(dir)) fs.mkdirSync(dir, { recursive: true });
|
|
109
|
-
fs.appendFileSync(HOOK_EVENTS_LOG, JSON.stringify(entry) + '\\n', { flag: 'a' });
|
|
110
|
-
} catch { /* never crash the hook */ }
|
|
111
|
-
|
|
112
|
-
// Path A — fire-and-forget https.request
|
|
113
|
-
if (env.INTUTIC_API_KEY) {
|
|
114
|
-
try {
|
|
115
|
-
const body = Buffer.from(JSON.stringify({ events: [entry] }));
|
|
116
|
-
const parsedUrl = url.parse(env.INTUTIC_HOST + '/api/v1/hook-events');
|
|
117
|
-
const req = https.request({
|
|
118
|
-
hostname: parsedUrl.hostname,
|
|
119
|
-
port: parsedUrl.port || 443,
|
|
120
|
-
path: parsedUrl.path,
|
|
121
|
-
method: 'POST',
|
|
122
|
-
headers: {
|
|
123
|
-
'Content-Type': 'application/json',
|
|
124
|
-
'Content-Length': body.length,
|
|
125
|
-
'Authorization': 'Bearer ' + env.INTUTIC_API_KEY,
|
|
126
|
-
},
|
|
127
|
-
});
|
|
128
|
-
req.on('error', () => {}); // fire-and-forget
|
|
129
|
-
req.write(body);
|
|
130
|
-
req.end();
|
|
131
|
-
} catch { /* never crash the hook */ }
|
|
132
|
-
}
|
|
133
|
-
}
|
|
134
|
-
|
|
135
|
-
function block(env, toolName, reason) {
|
|
136
|
-
logEvent(env, 'tool_blocked', toolName, reason);
|
|
137
|
-
process.stdout.write(JSON.stringify({ cancel: true, errorMessage: reason }));
|
|
138
|
-
process.exit(0);
|
|
139
|
-
}
|
|
140
|
-
|
|
141
|
-
function allow(env, toolName) {
|
|
142
|
-
logEvent(env, 'tool_allowed', toolName, '');
|
|
143
|
-
process.stdout.write(JSON.stringify({ cancel: false }));
|
|
144
|
-
process.exit(0);
|
|
145
|
-
}
|
|
146
|
-
|
|
147
|
-
// ── Main ──────────────────────────────────────────────────────────────────────
|
|
148
|
-
const runtimeEnv = loadRuntimeEnv();
|
|
149
|
-
let inputData = '';
|
|
150
|
-
process.stdin.on('data', (chunk) => { inputData += chunk; });
|
|
151
|
-
process.stdin.on('end', () => {
|
|
152
|
-
try {
|
|
153
|
-
const ctx = JSON.parse(inputData);
|
|
154
|
-
_intuticSessionId = ctx.session_id || ctx.sessionId || ctx.conversation_id || ctx.conversationId || ctx.task_id || ctx.taskId || '';
|
|
155
|
-
// An envelope carrying none of the tool fields extracts to empty strings,
|
|
156
|
-
// which match no rule — an allow. Refused instead, and the refusal CANCELS
|
|
157
|
-
// (this harness ignores exit codes); see intuticGuardEnvelope.
|
|
158
|
-
intuticGuardEnvelope(ctx, ['tool_name', 'toolName', 'tool_input', 'toolInput'],
|
|
159
|
-
(v, t, r) => logEvent(runtimeEnv, v, t, r));
|
|
160
|
-
const toolName = (ctx.tool_name || ctx.toolName || '').toLowerCase()
|
|
161
|
-
// Case preserved for the gate. A BLOCK: SOP compiles to a tool-name
|
|
162
|
-
// pattern the operator wrote as they see it (Bash, Write) and this
|
|
163
|
-
// harness lowercases the tool for its own matching. Handing the gate the
|
|
164
|
-
// lowercased form means every SOP tool rule silently matches nothing here
|
|
165
|
-
// while appearing active everywhere else.
|
|
166
|
-
const rawToolName = ctx.tool_name || ctx.toolName || '';
|
|
167
|
-
const toolInput = ctx.tool_input || ctx.toolInput || {};
|
|
168
|
-
const toolInputStr = JSON.stringify(toolInput);
|
|
169
|
-
|
|
170
|
-
// Roo Code ignores the exit code and reads a cancel object from stdout, so
|
|
171
|
-
// the shared gate is emitted with the stdout-cancel contract. Getting that
|
|
172
|
-
// wrong is invisible in review — the gate would exit 2 into a harness that
|
|
173
|
-
// never looks at it, and every call would proceed.
|
|
174
|
-
const targetPath = toolInput.path || toolInput.file_path || toolInput.filePath ||
|
|
175
|
-
toolInput.new_path || toolInput.target || toolInput.notebook_path || '';
|
|
176
|
-
const command = toolInput.command || toolInput.cmd || toolInput.script || '';
|
|
177
|
-
intuticGate(rawToolName, targetPath, command, (v, t, r) => logEvent(runtimeEnv, v, t, r), runtimeEnv.INTUTIC_WORKSPACE_ID, toolInput);
|
|
178
|
-
|
|
179
|
-
allow(runtimeEnv, toolName);
|
|
180
|
-
} catch (err) {
|
|
181
|
-
// Fail closed on parse error
|
|
182
|
-
const env = runtimeEnv;
|
|
183
|
-
logEvent(env, 'tool_blocked', 'unknown', String(err));
|
|
184
|
-
process.stdout.write(JSON.stringify({ cancel: true, errorMessage: '[Intutic] Hook error: ' + String(err) }));
|
|
185
|
-
process.exit(0);
|
|
186
|
-
}
|
|
187
|
-
});
|
|
188
|
-
`.trim();
|
|
189
|
-
}
|
|
190
|
-
// ─── .roorules SOP content ────────────────────────────────────────────────────
|
|
191
|
-
function buildRooRulesContent() {
|
|
192
|
-
return [
|
|
193
|
-
'# Intutic Governance Rules (auto-generated)',
|
|
194
|
-
'# DO NOT EDIT — managed by intutic sync daemon',
|
|
195
|
-
`# Last sync: ${newIso()}`,
|
|
196
|
-
'',
|
|
197
|
-
'## Roo Code SOP Governance',
|
|
198
|
-
'',
|
|
199
|
-
'This workspace is governed by Intutic SOP policies. All tool executions are',
|
|
200
|
-
'monitored and may be blocked according to the active SOPs configured in the',
|
|
201
|
-
'Intutic control plane.',
|
|
202
|
-
'',
|
|
203
|
-
'### Protected Paths',
|
|
204
|
-
'',
|
|
205
|
-
'The following paths must not be modified by any agent:',
|
|
206
|
-
'',
|
|
207
|
-
...UNIVERSAL_PROTECTED_PATHS.map((p) => `- \`${p}\``),
|
|
208
|
-
'',
|
|
209
|
-
'### Hook Events',
|
|
210
|
-
'',
|
|
211
|
-
'All tool executions are logged to `.intutic/events/roo-hook-events.jsonl`',
|
|
212
|
-
'and forwarded to the Intutic control plane on each sync cycle.',
|
|
213
|
-
'',
|
|
214
|
-
].join('\n');
|
|
215
|
-
}
|
|
216
|
-
// ─── Main export ──────────────────────────────────────────────────────────────
|
|
217
|
-
/**
|
|
218
|
-
* Write the Roo Code governance hook configuration:
|
|
219
|
-
* 1. `.roorules` — SOP rules in Roo Code markdown format.
|
|
220
|
-
* 2. `.roorules/hooks/hooks.json` — hook config pointing to the Node.js gate.
|
|
221
|
-
* 3. `.intutic/hooks/roo-check.js` — Node.js dual-path hook (stdout cancel JSON).
|
|
222
|
-
*
|
|
223
|
-
* Safe to call repeatedly — uses atomic rename (`.intutic-tmp` → final path).
|
|
224
|
-
*
|
|
225
|
-
* @param workspaceRoot - Absolute workspace root path.
|
|
226
|
-
* @param proxyUrl - Intutic proxy URL written into the hook script header.
|
|
227
|
-
* @param workspaceId - Workspace ID embedded in every hook event payload.
|
|
228
|
-
*/
|
|
229
|
-
export async function writeRooCodeHooks(workspaceRoot, proxyUrl, workspaceId = '') {
|
|
230
|
-
// ── 1. Ensure directories ──────────────────────────────────────────────────
|
|
231
|
-
const hookScriptDir = path.join(workspaceRoot, '.intutic', 'hooks');
|
|
232
|
-
const hookEventsDir = path.join(workspaceRoot, '.intutic', 'events');
|
|
233
|
-
const rooRulesDir = path.join(workspaceRoot, '.roorules');
|
|
234
|
-
const rooHooksDir = path.join(rooRulesDir, 'hooks');
|
|
235
|
-
await keepOriginal(path.join(rooHooksDir, 'hooks.json'), workspaceRoot);
|
|
236
|
-
await Promise.all([
|
|
237
|
-
fs.mkdir(hookScriptDir, { recursive: true }),
|
|
238
|
-
fs.mkdir(hookEventsDir, { recursive: true }),
|
|
239
|
-
fs.mkdir(rooHooksDir, { recursive: true }),
|
|
240
|
-
]);
|
|
241
|
-
// ── 2. Write hook script (atomic) ─────────────────────────────────────────
|
|
242
|
-
const hookScriptPath = path.join(hookScriptDir, 'roo-check.js');
|
|
243
|
-
const tmpScript = hookScriptPath + '.intutic-tmp';
|
|
244
|
-
await fs.writeFile(tmpScript, buildRooCheckScript(proxyUrl, workspaceRoot, workspaceId), 'utf-8');
|
|
245
|
-
await fs.rename(tmpScript, hookScriptPath);
|
|
246
|
-
log.info({ action: 'roo_hook_script_written', path: hookScriptPath }, 'Roo Code hook script written');
|
|
247
|
-
// ── 3. Write .roorules (atomic) ───────────────────────────────────────────
|
|
248
|
-
const rooRulesPath = path.join(workspaceRoot, '.roorules');
|
|
249
|
-
// .roorules is a file (not the directory used for hooks), so we check if
|
|
250
|
-
// it's already a directory and write a README inside it instead.
|
|
251
|
-
let rooRulesIsDir = false;
|
|
252
|
-
try {
|
|
253
|
-
const stat = await fs.stat(rooRulesPath);
|
|
254
|
-
rooRulesIsDir = stat.isDirectory();
|
|
255
|
-
}
|
|
256
|
-
catch {
|
|
257
|
-
// Doesn't exist — write as file
|
|
258
|
-
}
|
|
259
|
-
if (rooRulesIsDir) {
|
|
260
|
-
// Write README.md inside the directory
|
|
261
|
-
await writeOwnedFile(path.join(rooRulesPath, 'README.md'), workspaceRoot, buildRooRulesContent());
|
|
262
|
-
}
|
|
263
|
-
else {
|
|
264
|
-
await writeOwnedFile(rooRulesPath, workspaceRoot, buildRooRulesContent());
|
|
265
|
-
}
|
|
266
|
-
log.info({ action: 'roo_rules_written' }, 'Roo Code .roorules written');
|
|
267
|
-
// ── 4. Write .roorules/hooks/hooks.json (atomic) ──────────────────────────
|
|
268
|
-
const hooksJsonPath = path.join(rooHooksDir, 'hooks.json');
|
|
269
|
-
const hooksConfig = {
|
|
270
|
-
hooks: [
|
|
271
|
-
{
|
|
272
|
-
type: 'PreToolUse',
|
|
273
|
-
matcher: '.*',
|
|
274
|
-
command: `node ${hookScriptPath}`,
|
|
275
|
-
},
|
|
276
|
-
],
|
|
277
|
-
};
|
|
278
|
-
await writeOwnedFile(hooksJsonPath, workspaceRoot, JSON.stringify(hooksConfig, null, 2) + '\n');
|
|
279
|
-
log.info({ action: 'roo_hooks_json_written', path: hooksJsonPath }, 'Roo Code hooks.json written');
|
|
280
|
-
}
|
|
281
|
-
//# sourceMappingURL=rooCodeHooks.js.map
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"file":"rooCodeHooks.js","sourceRoot":"","sources":["../../src/harness/rooCodeHooks.ts"],"names":[],"mappings":"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"}
|