@polygraph/codex-plugin 0.4.52 → 0.5.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.
|
@@ -8,6 +8,13 @@ const HOOK_LOG_MAX_BYTES = 5 * 1024 * 1024;
|
|
|
8
8
|
const AGENT_TYPES = new Set(['claude', 'codex', 'opencode', 'cursor']);
|
|
9
9
|
const COMMAND_HOOK_TOOL = /^mcp__(?:plugin_polygraph_)?polygraph[-_]mcp__/;
|
|
10
10
|
const OPENCODE_TOOL = /^polygraph(?:(?:-|_)mcp)?_/;
|
|
11
|
+
// Cursor reports MCP tools as `MCP:<tool>` with no server namespace, so the
|
|
12
|
+
// shim filters on the claim-worthy tool names to avoid spawning the CLI on
|
|
13
|
+
// every unrelated MCP call. This list is an optimization mirror of
|
|
14
|
+
// PARENT_CLAIM_POLICIES in the Polygraph CLI (parent-session-claim-evidence);
|
|
15
|
+
// classification authority stays in the CLI.
|
|
16
|
+
const CURSOR_MCP_CLAIM_TOOL =
|
|
17
|
+
/^MCP:(?:add_repo|allow_agent|archive_session|associate_pr|create_pr|deny_agent|git_fetch|link_reference|mark_pr_ready|pack_and_copy|push_branch|spawn_agent|start_session|stop_agent|update_session|upload_artifact)$/;
|
|
11
18
|
|
|
12
19
|
function nonEmptyString(value) {
|
|
13
20
|
return typeof value === 'string' && value.trim() ? value : undefined;
|
|
@@ -30,6 +37,7 @@ export function buildLinkAgentSessionArgs({
|
|
|
30
37
|
transcriptPath,
|
|
31
38
|
pid,
|
|
32
39
|
source,
|
|
40
|
+
hookOperation,
|
|
33
41
|
}) {
|
|
34
42
|
const session = nonEmptyString(polygraphSessionId);
|
|
35
43
|
const harnessSession = nonEmptyString(agentSessionId);
|
|
@@ -52,8 +60,21 @@ export function buildLinkAgentSessionArgs({
|
|
|
52
60
|
args.push('--pid', String(pid));
|
|
53
61
|
}
|
|
54
62
|
|
|
63
|
+
// Cursor post-tool evidence rides the hook payload (the transcript stores
|
|
64
|
+
// no tool results); forwarded verbatim, classified by the CLI. The
|
|
65
|
+
// operation travels on STDIN, never argv: toolInput can carry an entire
|
|
66
|
+
// upload_artifact document and Linux caps one argv string at 128KB, so an
|
|
67
|
+
// inline argument would kill the spawn with E2BIG and silently lose the
|
|
68
|
+
// evidence. The flag tells the CLI to read stdin; older strict CLIs
|
|
69
|
+
// reject it, which is why publication is gated on the Ocean deployment.
|
|
70
|
+
let input;
|
|
71
|
+
if (hookOperation && typeof hookOperation === 'object') {
|
|
72
|
+
args.push('--hook-operation-stdin');
|
|
73
|
+
input = JSON.stringify(hookOperation);
|
|
74
|
+
}
|
|
75
|
+
|
|
55
76
|
args.push('--source', claimSource);
|
|
56
|
-
return args;
|
|
77
|
+
return { args, input };
|
|
57
78
|
}
|
|
58
79
|
|
|
59
80
|
/**
|
|
@@ -70,7 +91,7 @@ function nodeRuntime() {
|
|
|
70
91
|
export function linkAgentSession(claim, spawn = spawnSync, env = process.env) {
|
|
71
92
|
if (isManagedChildEnvironment(env)) return false;
|
|
72
93
|
|
|
73
|
-
const args = buildLinkAgentSessionArgs(claim);
|
|
94
|
+
const { args, input } = buildLinkAgentSessionArgs(claim);
|
|
74
95
|
const command = nonEmptyString(env?.POLYGRAPH_CLI) ?? 'polygraph';
|
|
75
96
|
const commandEnv = nonEmptyString(claim.polygraphSessionId) ? env : { ...env };
|
|
76
97
|
if (commandEnv !== env) {
|
|
@@ -81,7 +102,8 @@ export function linkAgentSession(claim, spawn = spawnSync, env = process.env) {
|
|
|
81
102
|
const spawnOptions = {
|
|
82
103
|
encoding: 'utf8',
|
|
83
104
|
env: commandEnv,
|
|
84
|
-
stdio: ['ignore', 'ignore', 'pipe'],
|
|
105
|
+
stdio: [input === undefined ? 'ignore' : 'pipe', 'ignore', 'pipe'],
|
|
106
|
+
...(input === undefined ? {} : { input }),
|
|
85
107
|
};
|
|
86
108
|
|
|
87
109
|
let result = spawn(command, args, spawnOptions);
|
|
@@ -148,6 +170,24 @@ export function buildCommandHookLink(payload, agentType, env = process.env) {
|
|
|
148
170
|
return isPolygraphMcpToolName(payload.tool_name) ? common : undefined;
|
|
149
171
|
}
|
|
150
172
|
|
|
173
|
+
// Cursor's camelCase postToolUse: the payload carries the whole operation
|
|
174
|
+
// (tool_name `MCP:<tool>`, tool_input, tool_output) and is forwarded as
|
|
175
|
+
// evidence because the cursor transcript stores no tool results and lags
|
|
176
|
+
// the hook. The CLI classifies the operation; this filter only avoids
|
|
177
|
+
// spawning the CLI for unrelated tools.
|
|
178
|
+
if (payload.hook_event_name === 'postToolUse') {
|
|
179
|
+
const toolName = nonEmptyString(payload.tool_name);
|
|
180
|
+
if (!toolName || !CURSOR_MCP_CLAIM_TOOL.test(toolName)) return undefined;
|
|
181
|
+
return {
|
|
182
|
+
...common,
|
|
183
|
+
hookOperation: {
|
|
184
|
+
toolName,
|
|
185
|
+
toolInput: payload.tool_input,
|
|
186
|
+
toolOutput: payload.tool_output,
|
|
187
|
+
},
|
|
188
|
+
};
|
|
189
|
+
}
|
|
190
|
+
|
|
151
191
|
return undefined;
|
|
152
192
|
}
|
|
153
193
|
|
package/package.json
CHANGED
|
@@ -40,7 +40,7 @@ For each id, launch one background poller subagent whose entire job is to block
|
|
|
40
40
|
|
|
41
41
|
- **Claude Code** — a background `Task` with `subagent_type: "polygraph:polygraph-delegate-subagent"`, `run_in_background: true`, and description `Delegate to <repo>`. Fall back to the bare agent name only if the namespaced form is not found.
|
|
42
42
|
- **OpenCode** — invoke `@polygraph-delegate-subagent`.
|
|
43
|
-
- **Codex** — launch `agent_type: "polygraph-delegate-subagent"` via Codex's own `spawn_agent`, and collect it with `wait_agent
|
|
43
|
+
- **Codex** — launch `agent_type: "polygraph-delegate-subagent"` via Codex's own `spawn_agent`, and collect it with `wait_agent`, passing a long `timeout_ms` (five minutes or more): `wait_agent` returns as soon as the poller stops, so a short timeout only adds wake-ups that burn tokens and fill the user-visible transcript with waiting noise.
|
|
44
44
|
- **Cursor** — a background `Task` with `subagent_type: "polygraph-delegate-subagent"`, `run_in_background: true`, and description `Delegate to <repo>`. Collect it with `Await`.
|
|
45
45
|
|
|
46
46
|
A collect step that returns while the poller subagent is still running has not failed. It has only reached the end of its collection window. Collect the same background-task id again, as many times as it takes for the poller subagent to stop. A poller that runs for several minutes is ordinary, and it is never a reason to take the wait back into the main conversation.
|