overlord-cli 0.2608031719.0 → 0.2608061150.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/connectors/HARNESS-MATRIX.md +8 -5
- package/dist/connectors/README.md +3 -1
- package/dist/connectors/VERSION +1 -1
- package/dist/connectors/adapters/antigravity/plugin.json +1 -1
- package/dist/connectors/adapters/claude/.claude-plugin/plugin.json +1 -1
- package/dist/connectors/adapters/claude/CAPABILITIES.md +10 -4
- package/dist/connectors/adapters/claude/conformance-manifest.yaml +1 -1
- package/dist/connectors/adapters/claude/harness-capabilities.yaml +46 -3
- package/dist/connectors/adapters/codex/.codex-plugin/plugin.json +1 -1
- package/dist/connectors/adapters/codex/CAPABILITIES.md +7 -2
- package/dist/connectors/adapters/codex/conformance-manifest.yaml +1 -1
- package/dist/connectors/adapters/codex/harness-capabilities.yaml +32 -1
- package/dist/connectors/adapters/cursor/.cursor-plugin/plugin.json +1 -1
- package/dist/connectors/adapters/cursor/CAPABILITIES.md +9 -5
- package/dist/connectors/adapters/cursor/README.md +3 -2
- package/dist/connectors/adapters/cursor/codec/cursor.codec.yaml +71 -0
- package/dist/connectors/adapters/cursor/conformance-manifest.yaml +2 -1
- package/dist/connectors/adapters/cursor/fixtures/normalize-before-submit-prompt.json +31 -0
- package/dist/connectors/adapters/cursor/fixtures/normalize-post-tool-use-write.json +41 -0
- package/dist/connectors/adapters/cursor/fixtures/normalize-pre-tool-use-shell.json +40 -0
- package/dist/connectors/adapters/cursor/harness-capabilities.yaml +36 -9
- package/dist/connectors/core/README.md +3 -1
- package/dist/connectors/core/scripts/overlord-mcp.mjs +1 -1
- package/dist/index.js +289 -131
- package/dist/index.js.map +2 -2
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -182,7 +182,7 @@ export OVERLORD_BACKEND_URL="https://overlord.example.com"
|
|
|
182
182
|
export OVERLORD_USER_TOKEN="out_xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx"
|
|
183
183
|
|
|
184
184
|
# 4. Agent connector — write the local agent connector config (non-interactive).
|
|
185
|
-
ovld agent-setup claude # one of: claude | codex | cursor | pi | all
|
|
185
|
+
ovld agent-setup claude # one of: claude | codex | cursor | pi | antigravity | opencode | all
|
|
186
186
|
```
|
|
187
187
|
|
|
188
188
|
Then verify the backend is reachable and authenticated:
|
|
@@ -15,7 +15,7 @@ Legend: ✅ supported (fixture-proven) · ⛔ unsupported (harness cannot) ·
|
|
|
15
15
|
| Adapter | Harness | Verified version | Shape | Tier | Binding | Decision hold |
|
|
16
16
|
| --- | --- | --- | --- | --- | --- | --- |
|
|
17
17
|
| [`antigravity`](adapters/antigravity/CAPABILITIES.md) | Antigravity CLI | — | `callback` | 0 (Unsupported) | ❓ unverified | ❓ unverified |
|
|
18
|
-
| [`claude`](adapters/claude/CAPABILITIES.md) | Claude Code | `2.1.
|
|
18
|
+
| [`claude`](adapters/claude/CAPABILITIES.md) | Claude Code | `2.1.221` | `callback` | 3 (Conversational) | ✅ supported | ✅ supported |
|
|
19
19
|
| [`codex`](adapters/codex/CAPABILITIES.md) | Codex CLI | `0.146.0` | `callback` | 3 (Conversational) | ✅ supported | ✅ supported |
|
|
20
20
|
| [`cursor`](adapters/cursor/CAPABILITIES.md) | Cursor Agent CLI | `2026.07.23-e383d2b` | `callback` | 0 (Unsupported) | ❓ unverified | ✅ supported |
|
|
21
21
|
| [`opencode`](adapters/opencode/CAPABILITIES.md) | OpenCode | — | `controlPlane` | 3 (Conversational) | ✅ supported | ✅ supported |
|
|
@@ -25,10 +25,10 @@ Legend: ✅ supported (fixture-proven) · ⛔ unsupported (harness cannot) ·
|
|
|
25
25
|
|
|
26
26
|
| Capability | `antigravity` | `claude` | `codex` | `cursor` | `opencode` | `pi` |
|
|
27
27
|
| --- | --- | --- | --- | --- | --- | --- |
|
|
28
|
-
| `observe.prompt` | ❓ unverified | ✅ supported | ✅ supported |
|
|
29
|
-
| `observe.toolCall` | ❓ unverified | 🚧 not-implemented | ✅ supported |
|
|
30
|
-
| `observe.toolResult` | ❓ unverified | ✅ supported | ✅ supported |
|
|
31
|
-
| `observe.fileEdit` | ❓ unverified | ✅ supported | ✅ supported |
|
|
28
|
+
| `observe.prompt` | ❓ unverified | ✅ supported | ✅ supported | ✅ supported | 🚧 not-implemented | ✅ supported |
|
|
29
|
+
| `observe.toolCall` | ❓ unverified | 🚧 not-implemented | ✅ supported | ✅ supported | 🚧 not-implemented | ✅ supported |
|
|
30
|
+
| `observe.toolResult` | ❓ unverified | ✅ supported | ✅ supported | ✅ supported | ✅ supported | 🚧 not-implemented |
|
|
31
|
+
| `observe.fileEdit` | ❓ unverified | ✅ supported | ✅ supported | ✅ supported | ✅ supported | 🚧 not-implemented |
|
|
32
32
|
| `observe.sessionLifecycle` | ❓ unverified | ✅ supported | ✅ supported | 🚧 not-implemented | ✅ supported | ✅ supported |
|
|
33
33
|
| `decide.shell` | ❓ unverified | ✅ supported | ✅ supported | ✅ supported | ✅ supported | ✅ supported |
|
|
34
34
|
| `decide.mcp` | ❓ unverified | ✅ supported | ✅ supported | ✅ supported | ✅ supported | ✅ supported |
|
|
@@ -52,6 +52,9 @@ Legend: ✅ supported (fixture-proven) · ⛔ unsupported (harness cannot) ·
|
|
|
52
52
|
| `claude` | `shipped-stop-hook-inert` | medium | verified | required | `agent-session-phase-3` |
|
|
53
53
|
| `claude` | `unbound-session-side-effects` | medium | verified | required | `agent-session-phase-4` |
|
|
54
54
|
| `claude` | `fork-and-subagent-identity-unknown` | medium | unverified | required | `taxonomy-7.6` |
|
|
55
|
+
| `claude` | `subagent-stop-unregistered` | medium | verified | required | `coo:585` |
|
|
56
|
+
| `claude` | `subagent-commits-escape-delivery-delta` | high | verified | required | `coo:585` |
|
|
57
|
+
| `codex` | `prompt-and-agent-hook-handlers-inert` | low | verified | accepted | — |
|
|
55
58
|
| `codex` | `hooks-version-and-platform-gate` | medium | verified | required | `agent-session-phase-4` |
|
|
56
59
|
| `cursor` | `reads-claude-hook-config` | high | unverified | required | `taxonomy-7.1` |
|
|
57
60
|
| `cursor` | `allowlist-precedence` | medium | unverified | required | `taxonomy-7.8` |
|
|
@@ -24,7 +24,7 @@ OpenCode, Antigravity, and others). A connector is what lets an agent speak the
|
|
|
24
24
|
Install or refresh a connector with:
|
|
25
25
|
|
|
26
26
|
```bash
|
|
27
|
-
ovld agent-setup <agent> # claude | codex | cursor | pi | all
|
|
27
|
+
ovld agent-setup <agent> # claude | codex | cursor | pi | antigravity | opencode | all
|
|
28
28
|
ovld doctor # verify managed files and permissions
|
|
29
29
|
```
|
|
30
30
|
|
|
@@ -38,6 +38,8 @@ connector contract version changes.
|
|
|
38
38
|
- [Codex](adapters/codex/README.md): Codex plugin with hooks, MCP bridge, and permission warmup.
|
|
39
39
|
- [Cursor](adapters/cursor/README.md): Cursor plugin with slash commands, hooks, rules, and MCP bridge.
|
|
40
40
|
- [PI](adapters/pi/README.md): PI Agent skill and extension with follow-up capture and native-session resume.
|
|
41
|
+
- [Antigravity](adapters/antigravity/README.md): Antigravity plugin exposing the local mission workflow to Antigravity CLI/IDE sessions.
|
|
42
|
+
- [OpenCode](adapters/opencode/README.md): control-plane connector (Shape C) driven by `ovld agent-session sidecar` — no hook scripts.
|
|
41
43
|
|
|
42
44
|
### Extension point
|
|
43
45
|
|
package/dist/connectors/VERSION
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
0.3.
|
|
1
|
+
0.3.18
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"$schema": "https://antigravity.google/schemas/v1/plugin.json",
|
|
3
3
|
"name": "overlord",
|
|
4
|
-
"version": "0.3.
|
|
4
|
+
"version": "0.3.18",
|
|
5
5
|
"description": "Chat-driven access to Overlord mission protocol workflows with hook-driven activity capture.",
|
|
6
6
|
"author": {
|
|
7
7
|
"name": "Cooperativ",
|
|
@@ -4,9 +4,9 @@
|
|
|
4
4
|
|
|
5
5
|
**Adapter** `claude` · **Codec** `claude` · **Integration shape** `callback` · **Capability tier** 3 (Conversational)
|
|
6
6
|
|
|
7
|
-
**Harness version verified** `2.1.
|
|
7
|
+
**Harness version verified** `2.1.221` · **range** `>=2.1.0` · **scheme** `semver`
|
|
8
8
|
|
|
9
|
-
**Descriptor digest** `
|
|
9
|
+
**Descriptor digest** `a90745bb59252df29006cf632a030108e68fde84f956910e02cc842833e6f8d6`
|
|
10
10
|
|
|
11
11
|
> The tier is derived from passing fixtures, never authored. `unsupported` means the harness
|
|
12
12
|
> cannot do it — do not attempt it. `not-implemented` means it is buildable and unbuilt: that is
|
|
@@ -83,14 +83,20 @@ Per-command `timeout` in seconds with no documented ceiling. Omitting `decision`
|
|
|
83
83
|
| `shipped-stop-hook-inert` | medium | verified | required | `agent-session-phase-3` |
|
|
84
84
|
| `unbound-session-side-effects` | medium | verified | required | `agent-session-phase-4` |
|
|
85
85
|
| `fork-and-subagent-identity-unknown` | medium | unverified | required | `taxonomy-7.6` |
|
|
86
|
+
| `subagent-stop-unregistered` | medium | verified | required | `coo:585` |
|
|
87
|
+
| `subagent-commits-escape-delivery-delta` | high | verified | required | `coo:585` |
|
|
86
88
|
|
|
87
89
|
**`shipped-permission-hook-inert`** — The installed PermissionRequest hook calls a `ovld protocol permission-request` subcommand that does not exist, backgrounds the call with `) & disown`, and is gated on a MISSION_ID environment variable launched sessions do not always set. It records nothing and, being backgrounded with stdout discarded, is structurally incapable of returning a decision. The shipped conformance `permissionHook` flag therefore overstates what happens today.
|
|
88
90
|
|
|
89
|
-
**`shipped-stop-hook-inert`** — The installed Stop hook calls `ovld protocol hook-event --hook-type Stop`, which the CLI rejects, and then parses a `deliveryStatus` field nothing produces. Its mission-link footer still works.
|
|
91
|
+
**`shipped-stop-hook-inert`** — The installed Stop hook calls `ovld protocol hook-event --hook-type Stop`, which the CLI rejects, and then parses a `deliveryStatus` field nothing produces. Its mission-link footer still works. Confirmed again at 2.1.221 from a live agent-pod session: every entry in ~/.ovld/logs/stop-hook.log takes the `no launch mission id` branch and returns before the delivery check, because the hook gates on a `MISSION_ID` environment variable the pod does not set. The PostToolUse hook works in the same session because it resolves the mission from the per-cwd active-session manifest instead; the Stop hook should resolve it the same way.
|
|
90
92
|
|
|
91
93
|
**`unbound-session-side-effects`** — NARROWED, NOT CLOSED. The new agent-session registrations gate in bash on OVERLORD_SESSION_CHANNEL_ID before spawning anything, so they are completely silent in an unbound session (proved by fixtures/agent-session-hook-unbound.json). The LEGACY PostToolUse hook still spawns `ovld protocol record-touched` and appends to ~/.ovld/logs in every session on the machine, because its scope gate lives inside the CLI rather than before the spawn (proved by fixtures/unbound-session.json). It stays installed on purpose: it is what feeds touched-file change attribution at deliver time, and removing it before the normalized path has demonstrated equivalent attribution would trade a privacy nit for a data-loss bug. Closing this means retiring the legacy hook, which is a migration, not a patch.
|
|
92
94
|
|
|
93
|
-
**`fork-and-subagent-identity-unknown`** — Whether an in-process fork mints a new native session id, and whether subagent tool calls carry the parent's id, are both unverified. Guessing either routes a session's requests to the wrong mission.
|
|
95
|
+
**`fork-and-subagent-identity-unknown`** — Whether an in-process fork mints a new native session id, and whether subagent tool calls carry the parent's id, are both unverified. Guessing either routes a session's requests to the wrong mission. Partially narrowed at 2.1.221: the binary carries distinct `SubagentStart` and `SubagentStop` hook events, converts an agent-frontmatter `Stop` registration into `SubagentStop` ("subagents trigger SubagentStop"), and records `parent_session_id` and `agent_type` as separate fields from `session_id`. That the harness distinguishes the two identities is now established; which value lands in a subagent hook payload's `session_id` is still not, and that is the part binding depends on. Read it from a real subagent payload before changing any binding code.
|
|
96
|
+
|
|
97
|
+
**`subagent-stop-unregistered`** — The adapter registers `Stop` and no subagent-lifecycle event. Claude Code 2.1.221 fires `SubagentStop` — not `Stop` — when a subagent finishes, and subagents now run in the background past the parent's turn boundary, so the shipped Stop registration is guaranteed not to run for work a subagent completes after the parent stopped. Nothing is lost today because that registration is already inert (see `shipped-stop-hook-inert`), which is exactly why the fix is to repair the Stop path first and only then decide whether the repaired body should also ride `SubagentStop`. Registering the current inert script on a second event would add a spawn per subagent completion and buy nothing.
|
|
98
|
+
|
|
99
|
+
**`subagent-commits-escape-delivery-delta`** — Delivery change attribution is computed from `git status --porcelain` against the attach baseline (cli/src/vcs.ts `readChangedFiles` / `filterRunAttributableChanges`); nothing in that path consults HEAD or the commit graph. A background subagent that commits, merges, or opens a PR for its own work therefore removes those files from the worktree delta, and they vanish from the delivery report entirely — not flagged, not skipped, silently absent. The PostToolUse touched-files log is unaffected (its key is `sha256(abspath(cwd) + "\0" + MISSION_ID)`, so a subagent sharing the cwd writes to the same log), which means the evidence that the edit happened survives while the delta that decides coverage does not. Closing this means teaching the delta about commits made since the baseline, which is a CLI change with branch and worktree semantics to settle, not a hook edit.
|
|
94
100
|
|
|
95
101
|
## Native decision shape
|
|
96
102
|
|
|
@@ -15,7 +15,7 @@ connector:
|
|
|
15
15
|
harnessCapabilities:
|
|
16
16
|
path: connectors/adapters/claude/harness-capabilities.yaml
|
|
17
17
|
schemaVersion: 1
|
|
18
|
-
digest: "
|
|
18
|
+
digest: "a90745bb59252df29006cf632a030108e68fde84f956910e02cc842833e6f8d6"
|
|
19
19
|
capabilities:
|
|
20
20
|
- followUpHook
|
|
21
21
|
- permissionHook
|
|
@@ -14,7 +14,7 @@ decisionCodecSpec: codec/claude.decision-codec.yaml
|
|
|
14
14
|
|
|
15
15
|
harness:
|
|
16
16
|
name: Claude Code
|
|
17
|
-
verifiedVersion: '2.1.
|
|
17
|
+
verifiedVersion: '2.1.221'
|
|
18
18
|
versionRange: '>=2.1.0'
|
|
19
19
|
versionScheme: semver
|
|
20
20
|
|
|
@@ -184,7 +184,12 @@ hazards:
|
|
|
184
184
|
summary: >-
|
|
185
185
|
The installed Stop hook calls `ovld protocol hook-event --hook-type Stop`, which the CLI
|
|
186
186
|
rejects, and then parses a `deliveryStatus` field nothing produces. Its mission-link
|
|
187
|
-
footer still works.
|
|
187
|
+
footer still works. Confirmed again at 2.1.221 from a live agent-pod session: every entry
|
|
188
|
+
in ~/.ovld/logs/stop-hook.log takes the `no launch mission id` branch and returns before
|
|
189
|
+
the delivery check, because the hook gates on a `MISSION_ID` environment variable the pod
|
|
190
|
+
does not set. The PostToolUse hook works in the same session because it resolves the
|
|
191
|
+
mission from the per-cwd active-session manifest instead; the Stop hook should resolve it
|
|
192
|
+
the same way.
|
|
188
193
|
verification: verified
|
|
189
194
|
mitigation: required
|
|
190
195
|
trackedAs: agent-session-phase-3
|
|
@@ -210,10 +215,48 @@ hazards:
|
|
|
210
215
|
summary: >-
|
|
211
216
|
Whether an in-process fork mints a new native session id, and whether subagent tool
|
|
212
217
|
calls carry the parent's id, are both unverified. Guessing either routes a session's
|
|
213
|
-
requests to the wrong mission.
|
|
218
|
+
requests to the wrong mission. Partially narrowed at 2.1.221: the binary carries
|
|
219
|
+
distinct `SubagentStart` and `SubagentStop` hook events, converts an agent-frontmatter
|
|
220
|
+
`Stop` registration into `SubagentStop` ("subagents trigger SubagentStop"), and records
|
|
221
|
+
`parent_session_id` and `agent_type` as separate fields from `session_id`. That the
|
|
222
|
+
harness distinguishes the two identities is now established; which value lands in a
|
|
223
|
+
subagent hook payload's `session_id` is still not, and that is the part binding depends
|
|
224
|
+
on. Read it from a real subagent payload before changing any binding code.
|
|
214
225
|
verification: unverified
|
|
215
226
|
mitigation: required
|
|
216
227
|
trackedAs: taxonomy-7.6
|
|
228
|
+
- id: subagent-stop-unregistered
|
|
229
|
+
severity: medium
|
|
230
|
+
summary: >-
|
|
231
|
+
The adapter registers `Stop` and no subagent-lifecycle event. Claude Code 2.1.221 fires
|
|
232
|
+
`SubagentStop` — not `Stop` — when a subagent finishes, and subagents now run in the
|
|
233
|
+
background past the parent's turn boundary, so the shipped Stop registration is
|
|
234
|
+
guaranteed not to run for work a subagent completes after the parent stopped. Nothing is
|
|
235
|
+
lost today because that registration is already inert (see `shipped-stop-hook-inert`),
|
|
236
|
+
which is exactly why the fix is to repair the Stop path first and only then decide
|
|
237
|
+
whether the repaired body should also ride `SubagentStop`. Registering the current
|
|
238
|
+
inert script on a second event would add a spawn per subagent completion and buy
|
|
239
|
+
nothing.
|
|
240
|
+
verification: verified
|
|
241
|
+
mitigation: required
|
|
242
|
+
trackedAs: coo:585
|
|
243
|
+
- id: subagent-commits-escape-delivery-delta
|
|
244
|
+
severity: high
|
|
245
|
+
summary: >-
|
|
246
|
+
Delivery change attribution is computed from `git status --porcelain` against the attach
|
|
247
|
+
baseline (cli/src/vcs.ts `readChangedFiles` / `filterRunAttributableChanges`); nothing in
|
|
248
|
+
that path consults HEAD or the commit graph. A background subagent that commits, merges,
|
|
249
|
+
or opens a PR for its own work therefore removes those files from the worktree delta, and
|
|
250
|
+
they vanish from the delivery report entirely — not flagged, not skipped, silently
|
|
251
|
+
absent. The PostToolUse touched-files log is unaffected (its key is
|
|
252
|
+
`sha256(abspath(cwd) + "\0" + MISSION_ID)`, so a subagent sharing the cwd writes to the
|
|
253
|
+
same log), which means the evidence that the edit happened survives while the delta that
|
|
254
|
+
decides coverage does not. Closing this means teaching the delta about commits made since
|
|
255
|
+
the baseline, which is a CLI change with branch and worktree semantics to settle, not a
|
|
256
|
+
hook edit.
|
|
257
|
+
verification: verified
|
|
258
|
+
mitigation: required
|
|
259
|
+
trackedAs: coo:585
|
|
217
260
|
|
|
218
261
|
decisionShape:
|
|
219
262
|
codec: claude
|
|
@@ -6,7 +6,7 @@
|
|
|
6
6
|
|
|
7
7
|
**Harness version verified** `0.146.0` · **range** `>=0.124.0` · **scheme** `semver`
|
|
8
8
|
|
|
9
|
-
**Descriptor digest** `
|
|
9
|
+
**Descriptor digest** `ad12a2a5006966b0d00f074290a6d8d2396c23343305fb8504d6d489313dacc7`
|
|
10
10
|
|
|
11
11
|
> The tier is derived from passing fixtures, never authored. `unsupported` means the harness
|
|
12
12
|
> cannot do it — do not attempt it. `not-implemented` means it is buildable and unbuilt: that is
|
|
@@ -64,6 +64,8 @@ Per-hook timeouts in seconds; the documentation gives a 600-second default. The
|
|
|
64
64
|
|
|
65
65
|
### Capability notes
|
|
66
66
|
|
|
67
|
+
- **`observe.toolCall`** — PreToolUse fires for MCP tool calls, not only shell and patch tools. Re-verified against the installed 0.146.0 binary and the matching upstream tag: the upstream suite `hooks_mcp.rs` (under codex-rs/core/tests/suite) proves a PreToolUse hook can both deny and rewrite an MCP call before it executes, and the binary carries a distinct "Tool call blocked by PreToolUse hook: {}. Tool: {}" message alongside the shell-only one. `tool_name` is the full MCP identifier (`mcp__<server>__<tool>`) and the matcher matches that string, so a matcher written for a bare tool name will silently never fire.
|
|
68
|
+
- **`observe.toolResult`** — Also covers MCP tool results, on the same 0.146.0 evidence as `observe.toolCall`. What PostToolUse cannot do is rewrite one: the binary rejects a returned `updatedMCPToolOutput` as unsupported. Observation of an MCP result is therefore reliable; mutation of it is not.
|
|
67
69
|
- **`decide.universal`** — Codex PermissionRequest covers Bash, apply_patch/Edit/Write, and MCP tool names. The codec emits only documented allow/deny behavior and deliberately never emits reserved `updatedInput`, `updatedPermissions`, or `interrupt` fields.
|
|
68
70
|
- **`answer.structuredQuestion`** — `codex app-server` exposes ToolRequestUserInput with per-option label/description and an autoResolutionMs — an ACP-shaped structured question in everything but name. It is unreachable until the single-subscriber question is settled.
|
|
69
71
|
- **`inject.midTurn`** — ThreadInjectItems/TurnSteer exist on the experimental app-server surface only. The binary carries "expected exactly one client subscribed to the thread, found {}", so if the TUI is that one client Overlord cannot also subscribe and this path is closed.
|
|
@@ -74,12 +76,15 @@ Per-hook timeouts in seconds; the documentation gives a 600-second default. The
|
|
|
74
76
|
| --- | --- | --- | --- | --- |
|
|
75
77
|
| `two-integration-surfaces-could-disagree` | high | verified | implemented | fixture `fixtures/agent-session-hooks.json` |
|
|
76
78
|
| `shipped-permission-hook-inert` | high | verified | implemented | fixture `fixtures/agent-session-hooks.json` |
|
|
79
|
+
| `prompt-and-agent-hook-handlers-inert` | low | verified | accepted | — |
|
|
77
80
|
| `hooks-version-and-platform-gate` | medium | verified | required | `agent-session-phase-4` |
|
|
78
81
|
|
|
79
82
|
**`two-integration-surfaces-could-disagree`** — Hooks and `codex app-server` can both answer a permission. A session driven by both would race in a way no revision CAS can settle, so exactly one surface must be chosen and documented before either is built.
|
|
80
83
|
|
|
81
84
|
**`shipped-permission-hook-inert`** — The former PermissionRequest hook called a nonexistent protocol command in the background. The managed registration now invokes the fixed request runtime directly.
|
|
82
85
|
|
|
86
|
+
**`prompt-and-agent-hook-handlers-inert`** — Codex 0.146.0 accepts three hook handler types — `command`, `prompt`, and `agent` — but only `command` runs. Hook discovery parses `prompt` and `agent` and then skips them with a "not supported yet" warning, so a manifest that used either would install cleanly, list in `hooks`, and never execute. The connector must keep emitting `command` handlers only, and must not read the presence of these variants in the schema as availability. `command` is a shell command line run through `$SHELL -lc` (`/bin/sh` when SHELL is unset) — it is not restricted to Bash and not restricted to shell scripting, since the command may invoke any interpreter.
|
|
87
|
+
|
|
83
88
|
**`hooks-version-and-platform-gate`** — Hooks were experimental behind `features.codex_hooks` before v0.124. The connector requires Codex >=0.124; installations outside that range must be treated as unavailable.
|
|
84
89
|
|
|
85
90
|
## Native decision shape
|
|
@@ -90,7 +95,7 @@ Per-hook timeouts in seconds; the documentation gives a 600-second default. The
|
|
|
90
95
|
- **Deny fixture** `fixtures/decision-codec.json`
|
|
91
96
|
- **Defer fixture** `fixtures/decision-codec.json`
|
|
92
97
|
|
|
93
|
-
PermissionRequest accepts the documented hookSpecificOutput decision.behavior allow/deny shape. `ask` remains a deliberate no-output defer so Codex shows its own approval prompt. The adapter never emits updatedInput, updatedPermissions, or interrupt.
|
|
98
|
+
PermissionRequest accepts the documented hookSpecificOutput decision.behavior allow/deny shape. `ask` remains a deliberate no-output defer so Codex shows its own approval prompt. The adapter never emits updatedInput, updatedPermissions, or interrupt. The reserved list is not symmetric across events at 0.146.0: PermissionRequest rejects `updatedInput` as unsupported, while PreToolUse accepts it together with `permissionDecision: "allow"` and will rewrite the call. Keeping `updatedInput` on `neverSend` stays a deliberate policy choice — silently editing a tool call a user approved is not a decision Overlord should make — and not an inference about what the harness can do.
|
|
94
99
|
|
|
95
100
|
## Unbound-session negative test
|
|
96
101
|
|
|
@@ -15,7 +15,7 @@ connector:
|
|
|
15
15
|
harnessCapabilities:
|
|
16
16
|
path: connectors/adapters/codex/harness-capabilities.yaml
|
|
17
17
|
schemaVersion: 1
|
|
18
|
-
digest: "
|
|
18
|
+
digest: "ad12a2a5006966b0d00f074290a6d8d2396c23343305fb8504d6d489313dacc7"
|
|
19
19
|
capabilities:
|
|
20
20
|
- followUpHook
|
|
21
21
|
- permissionHook
|
|
@@ -52,10 +52,23 @@ capabilities:
|
|
|
52
52
|
status: supported
|
|
53
53
|
native: PreToolUse
|
|
54
54
|
fixtures: [fixtures/normalize-pre-tool-use.json]
|
|
55
|
+
notes: >-
|
|
56
|
+
PreToolUse fires for MCP tool calls, not only shell and patch tools. Re-verified against
|
|
57
|
+
the installed 0.146.0 binary and the matching upstream tag: the upstream suite
|
|
58
|
+
`hooks_mcp.rs` (under codex-rs/core/tests/suite) proves a PreToolUse hook can both deny
|
|
59
|
+
and rewrite an MCP call before it executes, and the binary carries a distinct
|
|
60
|
+
"Tool call blocked by PreToolUse hook: {}.
|
|
61
|
+
Tool: {}" message alongside the shell-only one. `tool_name` is the full MCP identifier
|
|
62
|
+
(`mcp__<server>__<tool>`) and the matcher matches that string, so a matcher written for a
|
|
63
|
+
bare tool name will silently never fire.
|
|
55
64
|
observe.toolResult:
|
|
56
65
|
status: supported
|
|
57
66
|
native: PostToolUse
|
|
58
67
|
fixtures: [fixtures/normalize-post-tool-use.json]
|
|
68
|
+
notes: >-
|
|
69
|
+
Also covers MCP tool results, on the same 0.146.0 evidence as `observe.toolCall`. What
|
|
70
|
+
PostToolUse cannot do is rewrite one: the binary rejects a returned `updatedMCPToolOutput`
|
|
71
|
+
as unsupported. Observation of an MCP result is therefore reliable; mutation of it is not.
|
|
59
72
|
observe.fileEdit:
|
|
60
73
|
status: supported
|
|
61
74
|
native: PostToolUse
|
|
@@ -144,6 +157,19 @@ hazards:
|
|
|
144
157
|
verification: verified
|
|
145
158
|
mitigation: implemented
|
|
146
159
|
fixture: fixtures/agent-session-hooks.json
|
|
160
|
+
- id: prompt-and-agent-hook-handlers-inert
|
|
161
|
+
severity: low
|
|
162
|
+
summary: >-
|
|
163
|
+
Codex 0.146.0 accepts three hook handler types — `command`, `prompt`, and `agent` — but
|
|
164
|
+
only `command` runs. Hook discovery parses `prompt` and `agent` and then skips them with
|
|
165
|
+
a "not supported yet" warning, so a manifest that used either would install cleanly,
|
|
166
|
+
list in `hooks`, and never execute. The connector must keep emitting `command` handlers
|
|
167
|
+
only, and must not read the presence of these variants in the schema as availability.
|
|
168
|
+
`command` is a shell command line run through `$SHELL -lc` (`/bin/sh` when SHELL is
|
|
169
|
+
unset) — it is not restricted to Bash and not restricted to shell scripting, since the
|
|
170
|
+
command may invoke any interpreter.
|
|
171
|
+
verification: verified
|
|
172
|
+
mitigation: accepted
|
|
147
173
|
- id: hooks-version-and-platform-gate
|
|
148
174
|
severity: medium
|
|
149
175
|
summary: >-
|
|
@@ -162,7 +188,12 @@ decisionShape:
|
|
|
162
188
|
notes: >-
|
|
163
189
|
PermissionRequest accepts the documented hookSpecificOutput decision.behavior allow/deny
|
|
164
190
|
shape. `ask` remains a deliberate no-output defer so Codex shows its own approval prompt.
|
|
165
|
-
The adapter never emits updatedInput, updatedPermissions, or interrupt.
|
|
191
|
+
The adapter never emits updatedInput, updatedPermissions, or interrupt. The reserved list
|
|
192
|
+
is not symmetric across events at 0.146.0: PermissionRequest rejects `updatedInput` as
|
|
193
|
+
unsupported, while PreToolUse accepts it together with `permissionDecision: "allow"` and
|
|
194
|
+
will rewrite the call. Keeping `updatedInput` on `neverSend` stays a deliberate policy
|
|
195
|
+
choice — silently editing a tool call a user approved is not a decision Overlord should
|
|
196
|
+
make — and not an inference about what the harness can do.
|
|
166
197
|
|
|
167
198
|
unboundSessionFixture: fixtures/unbound-session.json
|
|
168
199
|
|
|
@@ -6,7 +6,7 @@
|
|
|
6
6
|
|
|
7
7
|
**Harness version verified** `2026.07.23-e383d2b` · **range** `>=2026.07.01` · **scheme** `calendar`
|
|
8
8
|
|
|
9
|
-
**Descriptor digest** `
|
|
9
|
+
**Descriptor digest** `4f6c4bb7a8c3637a6fc3ddd31e0ea98c8fd354badbb61117c3263561ec17ff74`
|
|
10
10
|
|
|
11
11
|
> The tier is derived from passing fixtures, never authored. `unsupported` means the harness
|
|
12
12
|
> cannot do it — do not attempt it. `not-implemented` means it is buildable and unbuilt: that is
|
|
@@ -24,6 +24,7 @@
|
|
|
24
24
|
| Evidence | tracked as `agent-session-verify-cursor-binding` |
|
|
25
25
|
|
|
26
26
|
Hook input carries `conversation_id` and an optional `session_id`. Which of the two is stable across a resumed conversation, and whether either appears in the environment of a tool subprocess, were not established. Binding on the wrong one silently misattributes a session, so this must be settled by fixture before the adapter binds on it.
|
|
27
|
+
This is now the *only* thing holding Cursor at tier 0. Four `observe.*` capabilities and three `decide.*` capabilities are fixture-proven, and `inject.turnBoundary` with them — enough for tier 3 on every axis the tier derivation reads except this one. Recording a real Cursor hook payload across a resume, and flipping this node, is therefore a single-session piece of work with a three-tier payoff, not an incremental improvement. Note that the shipped prompt hook and `codec/cursor.codec.yaml` both already treat `conversation_id` as the primary identity; if the recorded evidence says `session_id` is the stable one, both must change together or one session splits into two.
|
|
27
28
|
|
|
28
29
|
The native session id is a **correlation alias only**. Authorization and mission scope come
|
|
29
30
|
from the verified channel/session credential; neither the working directory nor an unverified
|
|
@@ -45,10 +46,10 @@ The 60-second per-script default is an order of magnitude below any window sized
|
|
|
45
46
|
|
|
46
47
|
| Capability | Status | Native | Evidence |
|
|
47
48
|
| --- | --- | --- | --- |
|
|
48
|
-
| `observe.prompt` |
|
|
49
|
-
| `observe.toolCall` |
|
|
50
|
-
| `observe.toolResult` |
|
|
51
|
-
| `observe.fileEdit` |
|
|
49
|
+
| `observe.prompt` | ✅ supported | `beforeSubmitPrompt` | fixtures: `fixtures/normalize-before-submit-prompt.json` |
|
|
50
|
+
| `observe.toolCall` | ✅ supported | `preToolUse` | fixtures: `fixtures/normalize-pre-tool-use-shell.json` |
|
|
51
|
+
| `observe.toolResult` | ✅ supported | `postToolUse` | fixtures: `fixtures/normalize-post-tool-use-write.json` |
|
|
52
|
+
| `observe.fileEdit` | ✅ supported | `postToolUse` | fixtures: `fixtures/normalize-post-tool-use-write.json` |
|
|
52
53
|
| `observe.sessionLifecycle` | 🚧 not-implemented | `sessionStart` | tracked as `agent-session-phase-1` |
|
|
53
54
|
| `decide.shell` | ✅ supported | `beforeShellExecution` | fixtures: `fixtures/permission-request.json`, `fixtures/decision-codec.json` |
|
|
54
55
|
| `decide.mcp` | ✅ supported | `beforeMCPExecution` | fixtures: `fixtures/permission-request.json`, `fixtures/decision-codec.json` |
|
|
@@ -65,6 +66,9 @@ The 60-second per-script default is an order of magnitude below any window sized
|
|
|
65
66
|
|
|
66
67
|
### Capability notes
|
|
67
68
|
|
|
69
|
+
- **`observe.toolCall`** — preToolUse carries both an observational and a decision registration, rendered as two different action scripts (`event` and `request`). The observational one cannot allow, deny, or delay a call — the action is fixed at install time, so neither registration can become the other.
|
|
70
|
+
- **`observe.fileEdit`** — Carried on postToolUse rather than on Cursor's dedicated `afterFileEdit`, for the same reason Claude carries it on PostToolUse: one registration then covers every mutation, including the ones a shell command makes, and the split into `file.edited` happens where the normalized tool name is already known. `afterFileEdit` remains available if a future need appears for edit-level granularity the tool-level event cannot express.
|
|
71
|
+
- **`observe.sessionLifecycle`** — Cursor fires the event, but no shipped code in this repo has ever read one of its payloads, so the field carrying the equivalent of Claude's `source` is unknown. A codec rule on a guessed path would not fail loudly — an unresolved path yields a card with no detail — so this stays unbuilt until a real payload is recorded, rather than being claimed on an assumed field name.
|
|
68
72
|
- **`inject.turnBoundary`** — `stop` returns `followup_message`, a first-class continuation channel. It can only ever report Queued(turn-boundary), never Delivered, and the UI must say so.
|
|
69
73
|
- **`inject.nextTurn`** — Cursor's followup_message is scheduled for the next turn; the same path covers turn-boundary and next-turn naming.
|
|
70
74
|
- **`terminal.statusSurface`** — Whether Cursor exposes an in-TUI status surface equivalent to Claude's statusMessage was not established.
|
|
@@ -29,7 +29,7 @@ From an OpenOverlord checkout:
|
|
|
29
29
|
ovld agent-setup cursor
|
|
30
30
|
```
|
|
31
31
|
|
|
32
|
-
This copies managed plugin files to `~/.cursor/plugins/local/overlord`, merges Overlord's prompt, permission, tool-result, and stop hooks into `~/.cursor/hooks.json`, and adds a `Shell(ovld protocol:*)` permission allow rule to `~/.cursor/settings.json` when missing. Existing hooks and settings are preserved.
|
|
32
|
+
This copies managed plugin files to `~/.cursor/plugins/local/overlord`, merges Overlord's prompt, permission, tool-call, tool-result, agent-session observation, and stop hooks into `~/.cursor/hooks.json`, and adds a `Shell(ovld protocol:*)` permission allow rule to `~/.cursor/settings.json` when missing. Existing hooks and settings are preserved.
|
|
33
33
|
|
|
34
34
|
Re-run safely any time the connector contract version changes:
|
|
35
35
|
|
|
@@ -82,6 +82,7 @@ This connector is intentionally reviewable against the four connector layers in
|
|
|
82
82
|
Cursor keeps its native authority.
|
|
83
83
|
- `hooks/overlord-post-tool-use.sh` — records `postToolUse` file edits and shell-mediated changes for exact per-session delivery attribution.
|
|
84
84
|
- `hooks/overlord-stop.sh` — checks pending delivery through `ovld protocol hook-event`; when needed, it asks Cursor to continue once so the agent can finish delivery.
|
|
85
|
+
- `scripts/agent-session-event.sh` — **generated**, like the MCP shim: rendered from `connectors/core/scripts/agent-session-hook.sh` with the adapter key and the fixed `event` action substituted at setup time. Registered on `beforeSubmitPrompt`, `preToolUse`, and `postToolUse`, it publishes normalized activity events on the session channel through `codec/cursor.codec.yaml`. It is a separate registration from the protocol hooks above rather than a line added to them, because those speak the older `ovld protocol` surface, and because the scope gate that makes an unbound session cost nothing has to be the script's own first line. `sessionStart` is deliberately unregistered: no recorded Cursor payload establishes its field names, and a codec rule on a guessed path degrades silently rather than failing.
|
|
85
86
|
- `rules/overlord-local.mdc` — always-on workflow rules for Cursor sessions.
|
|
86
87
|
- `mcp.json` + `scripts/overlord-mcp.mjs` — MCP bridge to common `ovld protocol` operations. The shim is **generated**: it is rendered from `connectors/core/scripts/overlord-mcp.mjs` at setup time with the adapter key substituted, and there is no copy in this directory to edit.
|
|
87
88
|
- `conformance-manifest.yaml` — connector conformance declaration for the Overlord contract.
|
|
@@ -96,4 +97,4 @@ The shared Connector Core source lives at `connectors/core/overlord-mission`. `o
|
|
|
96
97
|
|
|
97
98
|
See the [adapter capability matrix](../../README.md#adapter-capability-matrix) for how this compares across adapters.
|
|
98
99
|
|
|
99
|
-
Cursor's generic `preToolUse` hook is intentionally not used for permission capture. Cursor currently documents that `ask` is not enforced for `preToolUse`; the narrower shell and MCP hooks expose the native permission-decision schema without changing the user's policy. Cursor also documents hook execution as fail-open by default, so Overlord telemetry failures do not strand the agent.
|
|
100
|
+
Cursor's generic `preToolUse` hook is intentionally not used for *permission capture* — it is registered for observation only, which is a different action script and cannot allow, deny, or delay a call. Cursor currently documents that `ask` is not enforced for `preToolUse`; the narrower shell and MCP hooks expose the native permission-decision schema without changing the user's policy. Cursor also documents hook execution as fail-open by default, so Overlord telemetry failures do not strand the agent.
|
|
@@ -0,0 +1,71 @@
|
|
|
1
|
+
# Cursor Agent CLI — agent-session event codec.
|
|
2
|
+
#
|
|
3
|
+
# Cursor already had a *decision* codec (`cursor.decision-codec.yaml`) and no event codec, which
|
|
4
|
+
# is the whole reason every `observe.*` capability read `not-implemented`: the adapter could hold
|
|
5
|
+
# and answer a permission request, but nothing it saw ever reached the activity feed.
|
|
6
|
+
#
|
|
7
|
+
# Field names here are not guessed. Each one is already load-bearing somewhere in the shipped
|
|
8
|
+
# adapter, which is the only evidence standard that survives not being able to run the harness:
|
|
9
|
+
#
|
|
10
|
+
# `hook_event_name` / `event` — both declared in cursor.decision-codec.yaml
|
|
11
|
+
# `cwd` — same
|
|
12
|
+
# `tool_name`, `tool_input` — same (preToolUse), and `tool_input.file_path` is what the
|
|
13
|
+
# shipped postToolUse hook feeds to `ovld protocol record-touched`
|
|
14
|
+
# `prompt` (then `message`) — read by hooks/overlord-user-prompt-submit.sh
|
|
15
|
+
# `conversation_id` — read by the same hook, and persisted as the native session alias
|
|
16
|
+
#
|
|
17
|
+
# `sessionStart` is deliberately absent. Cursor has the event, but no shipped code in this repo
|
|
18
|
+
# has ever read one of its payloads, so its `source`-equivalent field name is unknown. Declaring
|
|
19
|
+
# a rule on a guessed field would not fail loudly — an unresolved path just produces a card with
|
|
20
|
+
# no detail — so `observe.sessionLifecycle` stays `not-implemented` until a real payload is read.
|
|
21
|
+
codecVersion: 1
|
|
22
|
+
adapter: cursor
|
|
23
|
+
|
|
24
|
+
# Cursor is the adapter that forced the plural form: `hook_event_name` is its Claude-compatible
|
|
25
|
+
# name and `event` its own. Order matters only in that the first non-empty one wins; both are
|
|
26
|
+
# treated as the same authority.
|
|
27
|
+
eventNamePaths:
|
|
28
|
+
- hook_event_name
|
|
29
|
+
- event
|
|
30
|
+
|
|
31
|
+
# Correlation only. `conversation_id` is the id the shipped prompt hook already persists as this
|
|
32
|
+
# session's external alias, so using anything else here would split one conversation across two
|
|
33
|
+
# identities. Neither value authorizes anything — the channel credential does.
|
|
34
|
+
nativeSessionPaths:
|
|
35
|
+
- conversation_id
|
|
36
|
+
- session_id
|
|
37
|
+
|
|
38
|
+
# Used solely to relativize file paths, so a card can say `src/app.ts` rather than disclose
|
|
39
|
+
# someone's home directory layout.
|
|
40
|
+
projectRootPaths:
|
|
41
|
+
- cwd
|
|
42
|
+
|
|
43
|
+
events:
|
|
44
|
+
# Cursor's composer prompt. Same deliberate exception as every other adapter: prompt text is
|
|
45
|
+
# the one raw user content that flows, because showing follow-up prompts is what the activity
|
|
46
|
+
# feed is for. Secret redaction still runs over it.
|
|
47
|
+
- native: beforeSubmitPrompt
|
|
48
|
+
kind: prompt.submitted
|
|
49
|
+
origin: user
|
|
50
|
+
promptPath: prompt
|
|
51
|
+
|
|
52
|
+
# Fires before the call runs. This rule is observational only — the decision path for the same
|
|
53
|
+
# event lives in cursor.decision-codec.yaml and is reached through a different action script,
|
|
54
|
+
# so nothing here can allow, deny, or delay a tool call.
|
|
55
|
+
- native: preToolUse
|
|
56
|
+
kind: tool.called
|
|
57
|
+
origin: agent
|
|
58
|
+
toolPath: tool_name
|
|
59
|
+
inputPath: tool_input
|
|
60
|
+
callIdPath: call_id
|
|
61
|
+
|
|
62
|
+
# Fires after the tool has already run, which makes it observational by construction. A
|
|
63
|
+
# file-mutating tool becomes `file.edited` instead, so the feed can render a change timeline
|
|
64
|
+
# without the reader having to know which Cursor tool names mutate files.
|
|
65
|
+
- native: postToolUse
|
|
66
|
+
kind: tool.completed
|
|
67
|
+
origin: agent
|
|
68
|
+
toolPath: tool_name
|
|
69
|
+
inputPath: tool_input
|
|
70
|
+
callIdPath: call_id
|
|
71
|
+
fileEditKind: file.edited
|
|
@@ -15,7 +15,7 @@ connector:
|
|
|
15
15
|
harnessCapabilities:
|
|
16
16
|
path: connectors/adapters/cursor/harness-capabilities.yaml
|
|
17
17
|
schemaVersion: 1
|
|
18
|
-
digest: "
|
|
18
|
+
digest: "4f6c4bb7a8c3637a6fc3ddd31e0ea98c8fd354badbb61117c3263561ec17ff74"
|
|
19
19
|
capabilities:
|
|
20
20
|
- followUpHook
|
|
21
21
|
- stopHook
|
|
@@ -47,6 +47,7 @@ connector:
|
|
|
47
47
|
- "hooks/overlord-permission-request.sh"
|
|
48
48
|
- "hooks/overlord-post-tool-use.sh"
|
|
49
49
|
- "hooks/overlord-stop.sh"
|
|
50
|
+
- "scripts/agent-session-event.sh"
|
|
50
51
|
- "mcp.json"
|
|
51
52
|
- "prompt-wrapper.md"
|
|
52
53
|
- "rules/overlord-local.mdc"
|
|
@@ -0,0 +1,31 @@
|
|
|
1
|
+
{
|
|
2
|
+
"fixtureVersion": 1,
|
|
3
|
+
"kind": "normalized-event",
|
|
4
|
+
"description": "A Cursor beforeSubmitPrompt body. Proves observe.prompt: the composer prompt becomes prompt.submitted correlated to conversation_id, and the credential pasted into the prompt is redacted before the text is persisted. It also pins the `hook_event_name` spelling of the event name.",
|
|
5
|
+
"codec": "codec/cursor.codec.yaml",
|
|
6
|
+
"occurredAt": "2026-01-01T00:00:00.000Z",
|
|
7
|
+
"payload": {
|
|
8
|
+
"conversation_id": "conv_7f3a91c2",
|
|
9
|
+
"hook_event_name": "beforeSubmitPrompt",
|
|
10
|
+
"cwd": "/repo",
|
|
11
|
+
"prompt": "Re-run the migration against staging, my token is out_live_9f3ab21c7d"
|
|
12
|
+
},
|
|
13
|
+
"expect": {
|
|
14
|
+
"eventId": "ev_735b2ab46b295c9b6fbde04faa4ce9cf",
|
|
15
|
+
"producerSequence": null,
|
|
16
|
+
"occurredAt": "2026-01-01T00:00:00.000Z",
|
|
17
|
+
"kind": "prompt.submitted",
|
|
18
|
+
"severity": "info",
|
|
19
|
+
"nativeEvent": "beforeSubmitPrompt",
|
|
20
|
+
"nativeTurnId": null,
|
|
21
|
+
"nativeCallId": null,
|
|
22
|
+
"subagentId": null,
|
|
23
|
+
"correlationId": "conv_7f3a91c2",
|
|
24
|
+
"origin": "user",
|
|
25
|
+
"summary": "Re-run the migration against staging, my token is [redacted]",
|
|
26
|
+
"payload": {
|
|
27
|
+
"promptText": "Re-run the migration against staging, my token is [redacted]"
|
|
28
|
+
}
|
|
29
|
+
},
|
|
30
|
+
"mustNotContain": ["out_live_9f3ab21c7d"]
|
|
31
|
+
}
|
|
@@ -0,0 +1,41 @@
|
|
|
1
|
+
{
|
|
2
|
+
"fixtureVersion": 1,
|
|
3
|
+
"kind": "normalized-event",
|
|
4
|
+
"description": "A Cursor postToolUse body for a Write call. Proves observe.toolResult and observe.fileEdit: the event becomes file.edited with a repo-relative path, and the file contents — deliberately carrying credentials — appear nowhere in the envelope. It is also the fixture that pins the `event` spelling of the event name, which is why the codec declares eventNamePaths rather than a single path.",
|
|
5
|
+
"codec": "codec/cursor.codec.yaml",
|
|
6
|
+
"occurredAt": "2026-01-01T00:00:00.000Z",
|
|
7
|
+
"payload": {
|
|
8
|
+
"conversation_id": "conv_7f3a91c2",
|
|
9
|
+
"event": "postToolUse",
|
|
10
|
+
"cwd": "/repo",
|
|
11
|
+
"tool_name": "Write",
|
|
12
|
+
"call_id": "call_2b81",
|
|
13
|
+
"tool_input": {
|
|
14
|
+
"file_path": "/repo/config/secrets.ts",
|
|
15
|
+
"content": "export const AWS_KEY = 'AKIAIOSFODNN7EXAMPLE';\nexport const DB = 'postgres://user:hunter2@db/app';\n"
|
|
16
|
+
}
|
|
17
|
+
},
|
|
18
|
+
"expect": {
|
|
19
|
+
"eventId": "ev_005e149f289472f0bb9c94aea6c5cfa0",
|
|
20
|
+
"producerSequence": null,
|
|
21
|
+
"occurredAt": "2026-01-01T00:00:00.000Z",
|
|
22
|
+
"kind": "file.edited",
|
|
23
|
+
"severity": "info",
|
|
24
|
+
"nativeEvent": "postToolUse",
|
|
25
|
+
"nativeTurnId": null,
|
|
26
|
+
"nativeCallId": "call_2b81",
|
|
27
|
+
"subagentId": null,
|
|
28
|
+
"correlationId": "conv_7f3a91c2",
|
|
29
|
+
"origin": "agent",
|
|
30
|
+
"summary": "Write a file: config/secrets.ts: 99 bytes",
|
|
31
|
+
"payload": {
|
|
32
|
+
"tool": "write",
|
|
33
|
+
"action": "Write a file",
|
|
34
|
+
"subject": "config/secrets.ts",
|
|
35
|
+
"detail": "99 bytes",
|
|
36
|
+
"paths": ["config/secrets.ts"],
|
|
37
|
+
"formatterVersion": 1
|
|
38
|
+
}
|
|
39
|
+
},
|
|
40
|
+
"mustNotContain": ["AKIAIOSFODNN7EXAMPLE", "hunter2", "postgres://", "/repo/config"]
|
|
41
|
+
}
|
|
@@ -0,0 +1,40 @@
|
|
|
1
|
+
{
|
|
2
|
+
"fixtureVersion": 1,
|
|
3
|
+
"kind": "normalized-event",
|
|
4
|
+
"description": "A Cursor preToolUse body for a Shell call. Proves observe.toolCall: the event becomes tool.called with the destructive risk marker derived on the machine, and the raw command survives only as a bounded detail. This is the observational path for preToolUse; the decision path for the same native event is proven separately by fixtures/decision-codec.json and reached through a different action script.",
|
|
5
|
+
"codec": "codec/cursor.codec.yaml",
|
|
6
|
+
"occurredAt": "2026-01-01T00:00:00.000Z",
|
|
7
|
+
"payload": {
|
|
8
|
+
"conversation_id": "conv_7f3a91c2",
|
|
9
|
+
"hook_event_name": "preToolUse",
|
|
10
|
+
"cwd": "/repo",
|
|
11
|
+
"tool_name": "Shell",
|
|
12
|
+
"call_id": "call_44c0",
|
|
13
|
+
"tool_input": {
|
|
14
|
+
"command": "rm -rf ./build && yarn build"
|
|
15
|
+
}
|
|
16
|
+
},
|
|
17
|
+
"expect": {
|
|
18
|
+
"eventId": "ev_ad3ad02aaa00e3b1da140996b421f7cb",
|
|
19
|
+
"producerSequence": null,
|
|
20
|
+
"occurredAt": "2026-01-01T00:00:00.000Z",
|
|
21
|
+
"kind": "tool.called",
|
|
22
|
+
"severity": "info",
|
|
23
|
+
"nativeEvent": "preToolUse",
|
|
24
|
+
"nativeTurnId": null,
|
|
25
|
+
"nativeCallId": "call_44c0",
|
|
26
|
+
"subagentId": null,
|
|
27
|
+
"correlationId": "conv_7f3a91c2",
|
|
28
|
+
"origin": "agent",
|
|
29
|
+
"summary": "Run a shell command: rm -rf ./build && yarn build [destructive]",
|
|
30
|
+
"payload": {
|
|
31
|
+
"tool": "shell",
|
|
32
|
+
"action": "Run a shell command",
|
|
33
|
+
"subject": "rm",
|
|
34
|
+
"detail": "rm -rf ./build && yarn build",
|
|
35
|
+
"riskMarkers": ["destructive"],
|
|
36
|
+
"formatterVersion": 1
|
|
37
|
+
}
|
|
38
|
+
},
|
|
39
|
+
"mustNotContain": ["/repo/build"]
|
|
40
|
+
}
|