@intutic/sync-daemon 2.2.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 +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
package/dist/harness/piHooks.js
CHANGED
|
@@ -1,19 +1,45 @@
|
|
|
1
1
|
/**
|
|
2
|
-
* piHooks.ts — Pi (earendil-works/pi)
|
|
2
|
+
* piHooks.ts — Pi (earendil-works/pi) extension gate and provider routing.
|
|
3
3
|
*
|
|
4
|
-
* Pi
|
|
5
|
-
*
|
|
6
|
-
*
|
|
4
|
+
* Pi has no hook file. Its hooks are extensions: TypeScript or JavaScript
|
|
5
|
+
* modules it loads into its own process from `~/.pi/agent/extensions/` (and a
|
|
6
|
+
* trusted project's `.pi/extensions/`), each a default-exported factory that
|
|
7
|
+
* receives the `ExtensionAPI`. Earlier versions of this writer registered a
|
|
8
|
+
* Claude-Code-style `PreToolUse` hook in `~/.pi/hooks.json`, a file Pi never
|
|
9
|
+
* reads, and pointed providers at the proxy in `~/.pi/models.json`, where Pi
|
|
10
|
+
* does not look either: both live in the agent directory, `~/.pi/agent`.
|
|
11
|
+
* Sources, at earendil-works/pi 6fb2e7815167e6b19006fc526d1a5d0f5f998787:
|
|
7
12
|
*
|
|
8
|
-
*
|
|
9
|
-
*
|
|
10
|
-
*
|
|
11
|
-
*
|
|
12
|
-
*
|
|
13
|
+
* - discovery: `packages/coding-agent/src/core/extensions/loader.ts`
|
|
14
|
+
* (`discoverExtensionsInDir`: direct `*.ts`/`*.js` files, or `<dir>/index.*`;
|
|
15
|
+
* loaded with jiti, `{ default: true }`, and the default export must be a
|
|
16
|
+
* function), `packages/coding-agent/docs/configuration.md` (the agent
|
|
17
|
+
* directory and `models.json`);
|
|
18
|
+
* - the hook: `pi.on("tool_call", handler)`, with
|
|
19
|
+
* `event = { type: "tool_call", toolName, toolCallId, input }`, refused by
|
|
20
|
+
* returning `{ block: true, reason }` (`core/extensions/types.ts`,
|
|
21
|
+
* `ToolCallEventResult`). The agent loop turns the reason into the error
|
|
22
|
+
* result the model reads (`packages/agent/src/agent-loop.ts`).
|
|
23
|
+
* - failure: "A `tool_call` handler failure blocks the tool as a fail-safe"
|
|
24
|
+
* (`docs/extensions.md`); `ExtensionRunner.emitToolCall` awaits each
|
|
25
|
+
* handler with no timeout (`core/extensions/runner.ts`), so a handler that
|
|
26
|
+
* hangs hangs Pi. The gate body therefore runs under its own deadline
|
|
27
|
+
* (`gateDeadlineMs('pi')`), from the start of each call, and refuses past it.
|
|
13
28
|
*
|
|
14
|
-
*
|
|
15
|
-
*
|
|
16
|
-
*
|
|
29
|
+
* The extension embeds the shared `emitJsGate` body (`'throw'` contract) and
|
|
30
|
+
* returns Pi's block result for the refusal it throws; any other fault is a
|
|
31
|
+
* block too. Pi names MCP tools `mcp__<server>__<tool>` already
|
|
32
|
+
* (`docs/mcp.md`), and calls a tool makes through `ctx.executeTool()` pass
|
|
33
|
+
* through `tool_call` handlers like the model's own (`docs/extensions.md`).
|
|
34
|
+
*
|
|
35
|
+
* The file is ESM with `createRequire`, the shape the OpenCode plugin uses:
|
|
36
|
+
* Node (Pi needs 22.19 or later) detects the module syntax, and jiti
|
|
37
|
+
* transforms the file where native loading fails and in Pi's compiled binary
|
|
38
|
+
* (`core/extensions/jiti-loader.ts`, `loader.ts`).
|
|
39
|
+
*
|
|
40
|
+
* Only the default agent directory is written. A user who moves it with
|
|
41
|
+
* `PI_CODING_AGENT_DIR` copies the extension there (the Pi integration page
|
|
42
|
+
* says so).
|
|
17
43
|
*
|
|
18
44
|
* @module
|
|
19
45
|
*/
|
|
@@ -21,127 +47,149 @@ import * as fs from 'node:fs/promises';
|
|
|
21
47
|
import * as path from 'node:path';
|
|
22
48
|
import * as os from 'node:os';
|
|
23
49
|
import { createLogger } from '@intutic/logger';
|
|
24
|
-
import { keepOriginal } from '../disconnect/originals.js';
|
|
50
|
+
import { keepOriginal, writeOwnedFile } from '../disconnect/originals.js';
|
|
25
51
|
import { newIso } from '@intutic/id';
|
|
26
|
-
import {
|
|
52
|
+
import { emitJsGate, REVIEW_REQUESTS_BASENAME } from './gateBody.js';
|
|
27
53
|
import { readJsonObjectForMerge } from './jsonMergeTarget.js';
|
|
28
54
|
import { anthropicBaseUrl, openaiBaseUrl } from '@intutic/shared-types';
|
|
29
55
|
const log = createLogger('sync-pi-hooks');
|
|
30
|
-
/**
|
|
31
|
-
const
|
|
32
|
-
/**
|
|
33
|
-
const
|
|
34
|
-
|
|
35
|
-
|
|
56
|
+
/** Pi's default agent directory, relative to the home directory. */
|
|
57
|
+
export const PI_AGENT_DIR = path.join('.pi', 'agent');
|
|
58
|
+
/** The extension file, relative to the home directory. */
|
|
59
|
+
export const PI_EXTENSION_FILE = path.join(PI_AGENT_DIR, 'extensions', 'intutic-governance.js');
|
|
60
|
+
/** The first line of the extension's header, which disconnect recognises it by. */
|
|
61
|
+
export const PI_EXTENSION_MARKER = 'Intutic Pi governance extension.';
|
|
62
|
+
// ─── The extension ───────────────────────────────────────────────────────────
|
|
63
|
+
export function buildPiExtension(proxyUrl, workspaceRoot, workspaceId) {
|
|
36
64
|
const hookEventsLog = path.join(workspaceRoot, '.intutic', 'events', 'hook-events.jsonl');
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
INTUTIC_WORKSPACE_ID="\${INTUTIC_WORKSPACE_ID:-${workspaceId}}"
|
|
65
|
+
return `/**
|
|
66
|
+
* ${PI_EXTENSION_MARKER}
|
|
67
|
+
* Auto-generated by intutic sync-daemon. DO NOT EDIT.
|
|
68
|
+
* Proxy: ${proxyUrl}
|
|
69
|
+
* Generated: ${newIso()}
|
|
70
|
+
*
|
|
71
|
+
* A tool_call handler Pi loads from its extensions directory. Refuses a tool
|
|
72
|
+
* call by returning { block: true, reason }. See
|
|
73
|
+
* services/sync-daemon/src/harness/piHooks.ts.
|
|
74
|
+
*/
|
|
75
|
+
import { createRequire } from 'node:module';
|
|
76
|
+
const require = createRequire(import.meta.url);
|
|
77
|
+
const path = require('path');
|
|
78
|
+
const os = require('os');
|
|
79
|
+
const fs = require('fs');
|
|
80
|
+
const crypto = require('crypto');
|
|
81
|
+
const https = require('https');
|
|
55
82
|
|
|
56
|
-
|
|
57
|
-
|
|
83
|
+
// Runtime credentials, read when Pi loads the extension.
|
|
84
|
+
const _runtimeEnvPath = path.join(os.homedir(), '.intutic', 'env', 'runtime.env');
|
|
85
|
+
let _intuticHost = 'https://api.intutic.ai', _intuticKey = '', _intuticWsId = ${JSON.stringify(workspaceId)};
|
|
86
|
+
try {
|
|
87
|
+
fs.readFileSync(_runtimeEnvPath, 'utf-8').split('\\n').forEach(line => {
|
|
88
|
+
const eq = line.indexOf('='); if (eq < 0) return;
|
|
89
|
+
const k = line.slice(0, eq).trim(), v = line.slice(eq + 1).trim();
|
|
90
|
+
if (k === 'INTUTIC_HOST' && v) _intuticHost = v;
|
|
91
|
+
if (k === 'INTUTIC_API_KEY' && v) _intuticKey = v;
|
|
92
|
+
if (k === 'INTUTIC_WORKSPACE_ID' && v) _intuticWsId = v;
|
|
93
|
+
});
|
|
94
|
+
} catch {}
|
|
58
95
|
|
|
59
|
-
|
|
96
|
+
${emitJsGate({ harness: 'pi', contract: 'throw', reviewRequestFile: path.join(workspaceRoot, '.intutic', 'events', REVIEW_REQUESTS_BASENAME) })}
|
|
60
97
|
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
curl -s -o /dev/null --max-time 3 -X POST \\
|
|
95
|
-
-H "Content-Type: application/json" \\
|
|
96
|
-
-H "Authorization: Bearer \$INTUTIC_API_KEY" \\
|
|
97
|
-
-d "{\\"events\\":[\$entry]}" \\
|
|
98
|
-
"\${INTUTIC_HOST}/api/v1/hook-events" & disown 2>/dev/null || true
|
|
99
|
-
fi
|
|
98
|
+
let _intuticSessionId = '';
|
|
99
|
+
function logEvent(verdict, toolName, reason) {
|
|
100
|
+
try {
|
|
101
|
+
const ts = new Date().toISOString();
|
|
102
|
+
const incidentId = crypto.createHash('sha1').update(ts + toolName + _intuticWsId).digest('hex').slice(0, 16);
|
|
103
|
+
// The event's id: random, made once here, and resent with the line it is
|
|
104
|
+
// written into, so the control plane processes the event once.
|
|
105
|
+
const eventId = crypto.randomBytes(16).toString('hex');
|
|
106
|
+
const entry = JSON.stringify({
|
|
107
|
+
event: verdict,
|
|
108
|
+
toolName, reason: reason || '',
|
|
109
|
+
workspaceId: _intuticWsId,
|
|
110
|
+
harnessType: 'pi',
|
|
111
|
+
timestamp: ts,
|
|
112
|
+
incidentId,
|
|
113
|
+
eventId,
|
|
114
|
+
...(_intuticSessionId ? { sessionId: _intuticSessionId } : {}),
|
|
115
|
+
}) + '\\n';
|
|
116
|
+
const logPath = ${JSON.stringify(hookEventsLog)};
|
|
117
|
+
try { fs.mkdirSync(path.dirname(logPath), { recursive: true }); } catch {}
|
|
118
|
+
fs.appendFileSync(logPath, entry, { flag: 'a' });
|
|
119
|
+
if (_intuticKey) {
|
|
120
|
+
try {
|
|
121
|
+
const body = JSON.stringify({ events: [JSON.parse(entry)] });
|
|
122
|
+
const urlObj = new URL('/api/v1/hook-events', _intuticHost);
|
|
123
|
+
const isHttps = urlObj.protocol === 'https:';
|
|
124
|
+
const mod = isHttps ? https : require('http');
|
|
125
|
+
const req = mod.request({ hostname: urlObj.hostname, port: urlObj.port || (isHttps ? 443 : 80), path: urlObj.pathname, method: 'POST', headers: { 'Content-Type': 'application/json', 'Content-Length': Buffer.byteLength(body), 'Authorization': 'Bearer ' + _intuticKey } });
|
|
126
|
+
req.on('error', () => { /* fire-and-forget */ });
|
|
127
|
+
req.write(body); req.end();
|
|
128
|
+
} catch { /* never crash the hook */ }
|
|
129
|
+
}
|
|
130
|
+
} catch { /* never crash the hook */ }
|
|
100
131
|
}
|
|
101
132
|
|
|
102
|
-
${emitShellGate({ harness: "pi" })}
|
|
103
|
-
log_event "tool_allowed" "$TOOL_NAME" ""
|
|
104
|
-
exit 0
|
|
105
|
-
`;
|
|
106
|
-
}
|
|
107
|
-
// ─── Pi hooks.json merge ──────────────────────────────────────────────────────
|
|
108
133
|
/**
|
|
109
|
-
*
|
|
110
|
-
*
|
|
111
|
-
*
|
|
134
|
+
* One tool call. Returns to allow; throws '[Intutic Governance] …' to refuse.
|
|
135
|
+
* Input that is not an object is refused outright: the gate reads paths and
|
|
136
|
+
* commands out of it, and a shape it cannot read is not one it can allow.
|
|
112
137
|
*/
|
|
113
|
-
function
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
]
|
|
138
|
+
function intuticEvaluate(event, ctx) {
|
|
139
|
+
_intuticSessionId = '';
|
|
140
|
+
try { _intuticSessionId = String(ctx.sessionManager.getSessionId() || ''); } catch (e) {}
|
|
141
|
+
const tool = String((event && event.toolName) || 'tool');
|
|
142
|
+
const input = event && event.input;
|
|
143
|
+
if (!input || typeof input !== 'object' || Array.isArray(input)) {
|
|
144
|
+
const reason = '[Intutic Governance] BLOCKED: tool "' + tool + '" was called with input the gate cannot read (' +
|
|
145
|
+
(input === null ? 'null' : typeof input) + '); refusing rather than allowing a call it cannot evaluate.';
|
|
146
|
+
try { logEvent('tool_blocked', tool, reason); } catch (e) {}
|
|
147
|
+
throw new Error(reason);
|
|
148
|
+
}
|
|
149
|
+
const target = input.path || input.file_path || input.filePath || input.target || '';
|
|
150
|
+
const command = input.command || input.cmd || input.script || '';
|
|
151
|
+
intuticGate(tool, target, command, logEvent, _intuticWsId, input, _intuticSessionId);
|
|
152
|
+
logEvent('tool_allowed', tool, '');
|
|
120
153
|
}
|
|
154
|
+
|
|
121
155
|
/**
|
|
122
|
-
*
|
|
123
|
-
*
|
|
124
|
-
*
|
|
156
|
+
* Fail closed: a fault inside the gate is a refusal, never an allow. The gate
|
|
157
|
+
* body reports a refusal on stderr for the hook processes; inside Pi that
|
|
158
|
+
* line would land in the middle of the terminal UI, and the reason reaches
|
|
159
|
+
* the user in the tool result anyway, so stderr is muted for the call. The
|
|
160
|
+
* evaluation is synchronous, so nothing else logs in between.
|
|
125
161
|
*/
|
|
126
|
-
function
|
|
127
|
-
|
|
128
|
-
|
|
129
|
-
|
|
130
|
-
|
|
131
|
-
|
|
132
|
-
|
|
133
|
-
|
|
134
|
-
|
|
135
|
-
|
|
136
|
-
|
|
162
|
+
function toolCall(event, ctx) {
|
|
163
|
+
const consoleError = console.error;
|
|
164
|
+
console.error = () => {};
|
|
165
|
+
try {
|
|
166
|
+
intuticEvaluate(event, ctx);
|
|
167
|
+
return undefined;
|
|
168
|
+
} catch (err) {
|
|
169
|
+
let reason = String((err && err.message) || err);
|
|
170
|
+
if (reason.indexOf('[Intutic Governance]') !== 0) {
|
|
171
|
+
reason = '[Intutic Governance] BLOCKED: gate fault (failing closed): ' + reason;
|
|
172
|
+
try { logEvent('tool_blocked', String((event && event.toolName) || 'unknown'), reason); } catch (e) {}
|
|
173
|
+
}
|
|
174
|
+
return { block: true, reason };
|
|
175
|
+
} finally {
|
|
176
|
+
console.error = consoleError;
|
|
177
|
+
}
|
|
137
178
|
}
|
|
138
|
-
|
|
179
|
+
|
|
180
|
+
export default function intuticGovernance(pi) {
|
|
181
|
+
pi.on('tool_call', toolCall);
|
|
182
|
+
}
|
|
183
|
+
`;
|
|
184
|
+
}
|
|
185
|
+
// ─── <agent-dir>/models.json ─────────────────────────────────────────────────
|
|
139
186
|
/**
|
|
140
187
|
* Merge Pi models.json — point the anthropic and openai providers at the
|
|
141
188
|
* proxy, each with the base URL its SDK expects (the Anthropic SDK appends
|
|
142
189
|
* `/v1/messages`, the OpenAI one `/chat/completions`). Google is left alone:
|
|
143
190
|
* the proxy does not serve the Gemini API, so routing it there would break it.
|
|
144
|
-
* Every other provider and key is kept.
|
|
191
|
+
* Every other provider and key is kept. A provider entry may carry `baseUrl`
|
|
192
|
+
* alone (`ProviderConfigSchema` in `packages/coding-agent/src/core/model-config.ts`).
|
|
145
193
|
*/
|
|
146
194
|
export function mergePiModels(existing, proxyUrl) {
|
|
147
195
|
const providers = existing.providers ?? {};
|
|
@@ -163,70 +211,33 @@ export function mergePiModels(existing, proxyUrl) {
|
|
|
163
211
|
}
|
|
164
212
|
// ─── Main export ──────────────────────────────────────────────────────────────
|
|
165
213
|
/**
|
|
166
|
-
* Write the Pi governance
|
|
167
|
-
*
|
|
214
|
+
* Write the Pi governance extension into `~/.pi/agent/extensions/` and merge
|
|
215
|
+
* the proxy routing into `~/.pi/agent/models.json`.
|
|
168
216
|
*
|
|
169
|
-
* Safe to call repeatedly
|
|
170
|
-
*
|
|
217
|
+
* Safe to call repeatedly: atomic renames and a fixed file name, so repeated
|
|
218
|
+
* syncs replace rather than stack. Pi discovers the extension by scanning the
|
|
219
|
+
* directory; nothing registers it.
|
|
171
220
|
*
|
|
172
221
|
* @param workspaceRoot - Absolute workspace root path.
|
|
173
222
|
* @param proxyUrl - Intutic proxy URL for LLM provider routing.
|
|
174
223
|
* @param workspaceId - Workspace ID embedded in every hook event payload.
|
|
175
224
|
*/
|
|
176
225
|
export async function writePiHooks(workspaceRoot, proxyUrl, workspaceId = '') {
|
|
177
|
-
|
|
178
|
-
const
|
|
179
|
-
|
|
180
|
-
|
|
181
|
-
const piDir = path.join(os.homedir(), '.pi');
|
|
182
|
-
await keepOriginal(PI_HOOKS_CONFIG, workspaceRoot);
|
|
183
|
-
await keepOriginal(PI_MODELS_CONFIG, workspaceRoot);
|
|
184
|
-
await Promise.all([
|
|
185
|
-
fs.mkdir(globalHookDir, { recursive: true }),
|
|
186
|
-
fs.mkdir(hookEventsDir, { recursive: true }),
|
|
187
|
-
fs.mkdir(envDir, { recursive: true }),
|
|
188
|
-
fs.mkdir(piDir, { recursive: true }),
|
|
189
|
-
]);
|
|
190
|
-
// ── 2. Write bash hook script ──────────────────────────────────────────────
|
|
191
|
-
const hookScriptPath = path.join(globalHookDir, 'pi-check.sh');
|
|
192
|
-
const tmpScript = hookScriptPath + '.intutic-tmp';
|
|
193
|
-
await fs.writeFile(tmpScript, buildPiCheckScript(proxyUrl, workspaceRoot, workspaceId), 'utf-8');
|
|
194
|
-
await fs.rename(tmpScript, hookScriptPath);
|
|
195
|
-
await fs.chmod(hookScriptPath, 0o755);
|
|
196
|
-
log.info({ action: 'pi_hook_script_written', path: hookScriptPath }, 'Pi hook script written');
|
|
197
|
-
// ── 3. Write/merge ~/.pi/hooks.json ───────────────────────────────────────
|
|
226
|
+
await fs.mkdir(path.join(workspaceRoot, '.intutic', 'events'), { recursive: true });
|
|
227
|
+
const extensionPath = path.join(os.homedir(), PI_EXTENSION_FILE);
|
|
228
|
+
await writeOwnedFile(extensionPath, workspaceRoot, buildPiExtension(proxyUrl, workspaceRoot, workspaceId));
|
|
229
|
+
log.info({ action: 'pi_extension_written', path: extensionPath }, 'Pi governance extension written');
|
|
198
230
|
// A file that is not a plain JSON object is left untouched (and reported)
|
|
199
231
|
// rather than replaced with only the Intutic entries.
|
|
200
|
-
const
|
|
201
|
-
|
|
202
|
-
const mergedHooks = mergePiHooks(existingHooks, hookScriptPath);
|
|
203
|
-
const tmpHooks = PI_HOOKS_CONFIG + '.intutic-tmp';
|
|
204
|
-
await fs.writeFile(tmpHooks, JSON.stringify(mergedHooks, null, 2) + '\n', 'utf-8');
|
|
205
|
-
await fs.rename(tmpHooks, PI_HOOKS_CONFIG);
|
|
206
|
-
}
|
|
207
|
-
log.info({ action: 'pi_hooks_written', path: PI_HOOKS_CONFIG }, 'Pi hooks.json written');
|
|
208
|
-
// ── 4. Write/merge ~/.pi/models.json ──────────────────────────────────────
|
|
209
|
-
const existingModels = await readJsonObjectForMerge(PI_MODELS_CONFIG);
|
|
232
|
+
const modelsPath = path.join(os.homedir(), PI_AGENT_DIR, 'models.json');
|
|
233
|
+
const existingModels = await readJsonObjectForMerge(modelsPath);
|
|
210
234
|
if (existingModels !== null) {
|
|
211
|
-
|
|
212
|
-
|
|
213
|
-
|
|
214
|
-
await fs.
|
|
235
|
+
await keepOriginal(modelsPath, workspaceRoot);
|
|
236
|
+
await fs.mkdir(path.dirname(modelsPath), { recursive: true });
|
|
237
|
+
const tmp = modelsPath + '.intutic-tmp';
|
|
238
|
+
await fs.writeFile(tmp, JSON.stringify(mergePiModels(existingModels, proxyUrl), null, 2) + '\n', 'utf-8');
|
|
239
|
+
await fs.rename(tmp, modelsPath);
|
|
240
|
+
log.info({ action: 'pi_models_written', path: modelsPath }, 'Pi models.json written');
|
|
215
241
|
}
|
|
216
|
-
log.info({ action: 'pi_models_written', path: PI_MODELS_CONFIG }, 'Pi models.json written');
|
|
217
|
-
// ── 5. Write env snippet ───────────────────────────────────────────────────
|
|
218
|
-
const envSnippet = [
|
|
219
|
-
`# Intutic pi governance env`,
|
|
220
|
-
`INTUTIC_PI_HOOK=~/.intutic/hooks/pi-check.sh`,
|
|
221
|
-
`INTUTIC_PI_HOOKS_CONFIG=~/.pi/hooks.json`,
|
|
222
|
-
`INTUTIC_PI_MODELS_CONFIG=~/.pi/models.json`,
|
|
223
|
-
`OPENAI_BASE_URL=${proxyUrl}`,
|
|
224
|
-
`ANTHROPIC_BASE_URL=${proxyUrl}`,
|
|
225
|
-
].join('\n') + '\n';
|
|
226
|
-
const envFilePath = path.join(envDir, 'pi.env');
|
|
227
|
-
const tmpEnv = envFilePath + '.intutic-tmp';
|
|
228
|
-
await fs.writeFile(tmpEnv, envSnippet, 'utf-8');
|
|
229
|
-
await fs.rename(tmpEnv, envFilePath);
|
|
230
|
-
log.info({ action: 'pi_env_written', path: envFilePath }, 'Pi env snippet written');
|
|
231
242
|
}
|
|
232
243
|
//# sourceMappingURL=piHooks.js.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"piHooks.js","sourceRoot":"","sources":["../../src/harness/piHooks.ts"],"names":[],"mappings":"AAAA
|
|
1
|
+
{"version":3,"file":"piHooks.js","sourceRoot":"","sources":["../../src/harness/piHooks.ts"],"names":[],"mappings":"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"}
|