@vibe-commit/capture 0.1.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/LICENSE +21 -0
- package/README.md +66 -0
- package/dist/agents/claude_code.d.ts +11 -0
- package/dist/agents/claude_code.js +162 -0
- package/dist/agents/claude_code.js.map +1 -0
- package/dist/agents/codex_cli.d.ts +15 -0
- package/dist/agents/codex_cli.js +67 -0
- package/dist/agents/codex_cli.js.map +1 -0
- package/dist/agents/cursor.d.ts +21 -0
- package/dist/agents/cursor.js +89 -0
- package/dist/agents/cursor.js.map +1 -0
- package/dist/agents/registry.d.ts +184 -0
- package/dist/agents/registry.js +212 -0
- package/dist/agents/registry.js.map +1 -0
- package/dist/agents/types.d.ts +279 -0
- package/dist/agents/types.js +67 -0
- package/dist/agents/types.js.map +1 -0
- package/dist/commands/connect.d.ts +36 -0
- package/dist/commands/connect.js +499 -0
- package/dist/commands/connect.js.map +1 -0
- package/dist/commands/context.d.ts +45 -0
- package/dist/commands/context.js +14 -0
- package/dist/commands/context.js.map +1 -0
- package/dist/commands/report.d.ts +105 -0
- package/dist/commands/report.js +430 -0
- package/dist/commands/report.js.map +1 -0
- package/dist/commands/status.d.ts +29 -0
- package/dist/commands/status.js +236 -0
- package/dist/commands/status.js.map +1 -0
- package/dist/commands/why.d.ts +349 -0
- package/dist/commands/why.js +937 -0
- package/dist/commands/why.js.map +1 -0
- package/dist/consent.d.ts +59 -0
- package/dist/consent.js +111 -0
- package/dist/consent.js.map +1 -0
- package/dist/copy/claims.d.ts +79 -0
- package/dist/copy/claims.js +343 -0
- package/dist/copy/claims.js.map +1 -0
- package/dist/copy/grades.d.ts +117 -0
- package/dist/copy/grades.js +96 -0
- package/dist/copy/grades.js.map +1 -0
- package/dist/copy/index.d.ts +12 -0
- package/dist/copy/index.js +13 -0
- package/dist/copy/index.js.map +1 -0
- package/dist/copy/strings.d.ts +1328 -0
- package/dist/copy/strings.js +1243 -0
- package/dist/copy/strings.js.map +1 -0
- package/dist/copy/time.d.ts +27 -0
- package/dist/copy/time.js +43 -0
- package/dist/copy/time.js.map +1 -0
- package/dist/credential.d.ts +211 -0
- package/dist/credential.js +298 -0
- package/dist/credential.js.map +1 -0
- package/dist/exit.d.ts +59 -0
- package/dist/exit.js +60 -0
- package/dist/exit.js.map +1 -0
- package/dist/git.d.ts +299 -0
- package/dist/git.js +653 -0
- package/dist/git.js.map +1 -0
- package/dist/hooks/entry.d.ts +277 -0
- package/dist/hooks/entry.js +1092 -0
- package/dist/hooks/entry.js.map +1 -0
- package/dist/hooks/post_commit.d.ts +59 -0
- package/dist/hooks/post_commit.js +160 -0
- package/dist/hooks/post_commit.js.map +1 -0
- package/dist/hooks/post_rewrite.d.ts +59 -0
- package/dist/hooks/post_rewrite.js +161 -0
- package/dist/hooks/post_rewrite.js.map +1 -0
- package/dist/hooks/session_end.d.ts +101 -0
- package/dist/hooks/session_end.js +120 -0
- package/dist/hooks/session_end.js.map +1 -0
- package/dist/index.d.ts +34 -0
- package/dist/index.js +360 -0
- package/dist/index.js.map +1 -0
- package/dist/install.d.ts +153 -0
- package/dist/install.js +284 -0
- package/dist/install.js.map +1 -0
- package/dist/json.d.ts +100 -0
- package/dist/json.js +94 -0
- package/dist/json.js.map +1 -0
- package/dist/oauth/discovery.d.ts +96 -0
- package/dist/oauth/discovery.js +163 -0
- package/dist/oauth/discovery.js.map +1 -0
- package/dist/oauth/lock.d.ts +100 -0
- package/dist/oauth/lock.js +287 -0
- package/dist/oauth/lock.js.map +1 -0
- package/dist/oauth/loopback.d.ts +85 -0
- package/dist/oauth/loopback.js +124 -0
- package/dist/oauth/loopback.js.map +1 -0
- package/dist/oauth/pkce.d.ts +58 -0
- package/dist/oauth/pkce.js +77 -0
- package/dist/oauth/pkce.js.map +1 -0
- package/dist/oauth/read.d.ts +120 -0
- package/dist/oauth/read.js +144 -0
- package/dist/oauth/read.js.map +1 -0
- package/dist/oauth/session.d.ts +80 -0
- package/dist/oauth/session.js +112 -0
- package/dist/oauth/session.js.map +1 -0
- package/dist/oauth/signin.d.ts +178 -0
- package/dist/oauth/signin.js +225 -0
- package/dist/oauth/signin.js.map +1 -0
- package/dist/oauth/token.d.ts +139 -0
- package/dist/oauth/token.js +183 -0
- package/dist/oauth/token.js.map +1 -0
- package/dist/paths.d.ts +366 -0
- package/dist/paths.js +460 -0
- package/dist/paths.js.map +1 -0
- package/dist/policy.d.ts +195 -0
- package/dist/policy.js +244 -0
- package/dist/policy.js.map +1 -0
- package/dist/post.d.ts +302 -0
- package/dist/post.js +384 -0
- package/dist/post.js.map +1 -0
- package/dist/project.d.ts +99 -0
- package/dist/project.js +192 -0
- package/dist/project.js.map +1 -0
- package/dist/redact.d.ts +175 -0
- package/dist/redact.js +700 -0
- package/dist/redact.js.map +1 -0
- package/dist/runtime.d.ts +30 -0
- package/dist/runtime.js +37 -0
- package/dist/runtime.js.map +1 -0
- package/dist/spawn_budget.d.ts +101 -0
- package/dist/spawn_budget.js +124 -0
- package/dist/spawn_budget.js.map +1 -0
- package/dist/spool.d.ts +390 -0
- package/dist/spool.js +556 -0
- package/dist/spool.js.map +1 -0
- package/dist/state.d.ts +109 -0
- package/dist/state.js +217 -0
- package/dist/state.js.map +1 -0
- package/dist/system_message.d.ts +101 -0
- package/dist/system_message.js +141 -0
- package/dist/system_message.js.map +1 -0
- package/dist/term.d.ts +112 -0
- package/dist/term.js +134 -0
- package/dist/term.js.map +1 -0
- package/dist/version.d.ts +16 -0
- package/dist/version.js +17 -0
- package/dist/version.js.map +1 -0
- package/package.json +50 -0
package/LICENSE
ADDED
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
MIT License
|
|
2
|
+
|
|
3
|
+
Copyright (c) 2026 VibeCommit
|
|
4
|
+
|
|
5
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
6
|
+
of this software and associated documentation files (the "Software"), to deal
|
|
7
|
+
in the Software without restriction, including without limitation the rights
|
|
8
|
+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
9
|
+
copies of the Software, and to permit persons to whom the Software is
|
|
10
|
+
furnished to do so, subject to the following conditions:
|
|
11
|
+
|
|
12
|
+
The above copyright notice and this permission notice shall be included in all
|
|
13
|
+
copies or substantial portions of the Software.
|
|
14
|
+
|
|
15
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
16
|
+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
17
|
+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
18
|
+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
19
|
+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
20
|
+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
|
21
|
+
SOFTWARE.
|
package/README.md
ADDED
|
@@ -0,0 +1,66 @@
|
|
|
1
|
+
# @vibe-commit/capture
|
|
2
|
+
|
|
3
|
+
The VibeCommit capture client. Records Claude Code sessions against the commits they produced.
|
|
4
|
+
|
|
5
|
+
```
|
|
6
|
+
npm install -g @vibe-commit/capture
|
|
7
|
+
vibecommit connect
|
|
8
|
+
```
|
|
9
|
+
|
|
10
|
+
MIT licensed. This is the client that runs on your machine.
|
|
11
|
+
See [what we upload](https://vibecommit.ai/docs/what-we-upload).
|
|
12
|
+
|
|
13
|
+
<!--
|
|
14
|
+
The sentence that used to sit here claimed this client was open source so you could read
|
|
15
|
+
exactly what leaves it. That is FALSE as of D64 (2026-08-09): the repo is private, with the
|
|
16
|
+
MIT licence intact. The claim is BLOCKED by `docs/claims-register.md`, and this README ships
|
|
17
|
+
inside the npm tarball, so it would have carried the claim to the registry on first publish.
|
|
18
|
+
It is DELETED here rather than replaced — `CR-111` owns the replacement trust argument, and
|
|
19
|
+
the approved direction points at `Vibe-Commit/claude-plugin`, which vendors the exact binary.
|
|
20
|
+
"Inspect the build" is WEAKER than "read the source" and must not be dressed up as it.
|
|
21
|
+
-->
|
|
22
|
+
|
|
23
|
+
|
|
24
|
+
## Commands
|
|
25
|
+
|
|
26
|
+
| Command | What it does |
|
|
27
|
+
|---|---|
|
|
28
|
+
| `vibecommit connect` | Connect this repo and capture the current session. |
|
|
29
|
+
| `vibecommit status` | Show whether capture is on for this repo and when it last succeeded. |
|
|
30
|
+
| `vibecommit off` | Stop capturing for this repo. |
|
|
31
|
+
| `vibecommit why <file>:<line>` | Show the conversation turns recorded against the commit that last changed this line. |
|
|
32
|
+
| `vibecommit report --since <when>` | Summarise capture coverage over a time window. |
|
|
33
|
+
|
|
34
|
+
## Status
|
|
35
|
+
|
|
36
|
+
**Implemented**, not a scaffold. All five commands above have real bodies — OAuth sign-in
|
|
37
|
+
and credential storage, git inspection, redaction, and the Claude Code hook contract — backed
|
|
38
|
+
by an extensive test suite. Beyond the five interactive commands, this binary also answers a
|
|
39
|
+
hidden `hook` invocation (dispatched outside `--help`, since no one types it) for Claude Code's
|
|
40
|
+
`Stop`, `PreCompact`, and `SessionEnd` events; that hook mode is what
|
|
41
|
+
[`Vibe-Commit/claude-plugin`](https://github.com/Vibe-Commit/claude-plugin) vendors and wires up
|
|
42
|
+
by default when you install the Claude Code plugin instead of this package directly.
|
|
43
|
+
|
|
44
|
+
What is established, and what future changes must not undo:
|
|
45
|
+
|
|
46
|
+
- **All user-facing copy lives in `src/copy/`.** No command body contains an inline user-facing
|
|
47
|
+
string literal. A claims gate can only check copy it can find.
|
|
48
|
+
- **One commit-header renderer**, in `src/copy/grades.ts`, which takes the edge grade. Evidence
|
|
49
|
+
qualifiers are adjacent to the claim by construction rather than by review.
|
|
50
|
+
- **The exit-code contract is split** (`src/exit.ts`): hook invocations always exit 0 and stay
|
|
51
|
+
silent; interactive verbs get normal CLI semantics.
|
|
52
|
+
|
|
53
|
+
## Development
|
|
54
|
+
|
|
55
|
+
```
|
|
56
|
+
npm install
|
|
57
|
+
npm test # node --test --import tsx
|
|
58
|
+
npm run typecheck
|
|
59
|
+
npm run build
|
|
60
|
+
```
|
|
61
|
+
|
|
62
|
+
Node >= 22.15.0.
|
|
63
|
+
|
|
64
|
+
## License
|
|
65
|
+
|
|
66
|
+
MIT — see [LICENSE](./LICENSE).
|
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* The `claude-code` dialect — `CR-183`.
|
|
3
|
+
*
|
|
4
|
+
* ⛔ **NOTHING ABOUT CLAUDE CODE'S BEHAVIOUR CHANGES HERE.** Every value below is
|
|
5
|
+
* the value the client already used, moved to where a second agent can be
|
|
6
|
+
* described beside it. The one function member, `locateCurrentTranscript`, is
|
|
7
|
+
* `commands/connect.ts`'s own `findCurrentTranscript` verbatim except for the
|
|
8
|
+
* confinement call, which now runs against the registry's union.
|
|
9
|
+
*/
|
|
10
|
+
import type { TranscriptDialect } from "./types.js";
|
|
11
|
+
export declare const CLAUDE_CODE: TranscriptDialect;
|
|
@@ -0,0 +1,162 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* The `claude-code` dialect — `CR-183`.
|
|
3
|
+
*
|
|
4
|
+
* ⛔ **NOTHING ABOUT CLAUDE CODE'S BEHAVIOUR CHANGES HERE.** Every value below is
|
|
5
|
+
* the value the client already used, moved to where a second agent can be
|
|
6
|
+
* described beside it. The one function member, `locateCurrentTranscript`, is
|
|
7
|
+
* `commands/connect.ts`'s own `findCurrentTranscript` verbatim except for the
|
|
8
|
+
* confinement call, which now runs against the registry's union.
|
|
9
|
+
*/
|
|
10
|
+
import { readdirSync, statSync } from "node:fs";
|
|
11
|
+
import { basename, join } from "node:path";
|
|
12
|
+
import { isInside, isTranscriptFile, transcriptRoot } from "../paths.js";
|
|
13
|
+
/**
|
|
14
|
+
* The client-side backstop, per event.
|
|
15
|
+
*
|
|
16
|
+
* ⛔ **This is `hooks/entry.ts`'s `DEFAULT_HOOK_BUDGET_MS`, and the constant does
|
|
17
|
+
* not move.** It is restated as a literal here for one reason: `entry.ts`
|
|
18
|
+
* imports this registry, so importing the constant back would close an import
|
|
19
|
+
* cycle whose failure mode is a temporal-dead-zone `undefined` at module load —
|
|
20
|
+
* a budget of `NaN`, silently. `test/agent-registry.test.ts` asserts the two
|
|
21
|
+
* agree, which is the coupling the import would have provided.
|
|
22
|
+
*/
|
|
23
|
+
const CLAUDE_CLIENT_BUDGET_MS = 5_000;
|
|
24
|
+
/**
|
|
25
|
+
* What `claude-plugin/hooks/hooks.json` registers, in MILLISECONDS.
|
|
26
|
+
*
|
|
27
|
+
* ⚠ **That file is hand-written in another repository and nothing generates it
|
|
28
|
+
* from here**, so these three numbers are a MIRROR, not a source. They are
|
|
29
|
+
* pinned as literals so the `clientBudgetMs < registeredTimeoutMs` relation can
|
|
30
|
+
* be asserted at all — see `test/agent-registry.test.ts`, which also states what
|
|
31
|
+
* the assertion cannot see: a drift in the file itself.
|
|
32
|
+
*
|
|
33
|
+
* Its own timeouts are in SECONDS (8, 8, 10). Every value is above the client
|
|
34
|
+
* budget above, which is the relation that lets the client's watchdog fire
|
|
35
|
+
* first; below that line the agent kills the process and the exit-0 contract
|
|
36
|
+
* goes with it.
|
|
37
|
+
*/
|
|
38
|
+
const CLAUDE_REGISTERED_STOP_MS = 8_000;
|
|
39
|
+
const CLAUDE_REGISTERED_PRECOMPACT_MS = 8_000;
|
|
40
|
+
const CLAUDE_REGISTERED_SESSION_END_MS = 10_000;
|
|
41
|
+
/**
|
|
42
|
+
* The transcript for the session running right now.
|
|
43
|
+
*
|
|
44
|
+
* A hook is HANDED `transcript_path` on stdin. `connect` is typed by a human and
|
|
45
|
+
* gets nothing, so it has to find one: Claude Code stores transcripts at
|
|
46
|
+
* `<transcriptRoot>/<encoded-project-path>/<session-id>.jsonl`, where the
|
|
47
|
+
* encoding replaces `/` and `.` with `-`. That was derived by comparing every
|
|
48
|
+
* directory on this machine against the `cwd` its transcripts record — 15 of 17
|
|
49
|
+
* matched exactly, and both misses were sessions whose `cwd` MOVED mid-session,
|
|
50
|
+
* not a different encoding.
|
|
51
|
+
*
|
|
52
|
+
* Which is also why this looks up the directory for THIS project rather than
|
|
53
|
+
* taking the newest transcript anywhere: a session that started in another
|
|
54
|
+
* project and `cd`'d here lives under that project's directory, and uploading it
|
|
55
|
+
* would attach one project's session to another's repo — the same confusion
|
|
56
|
+
* `CR-017d`'s key exists to prevent.
|
|
57
|
+
*
|
|
58
|
+
* **Confined.** The result is gated, because this is a reader of transcript
|
|
59
|
+
* bytes and `/cso` finding 1 is about exactly this class of read. A candidate
|
|
60
|
+
* that resolves outside the root is not returned.
|
|
61
|
+
*
|
|
62
|
+
* ⛔ **Against THIS DIALECT'S OWN ROOT, and not against the registry union —
|
|
63
|
+
* which is a deliberate difference from the two hook call sites.** The union
|
|
64
|
+
* exists for a path that ARRIVES from outside, where which agent produced it is
|
|
65
|
+
* not known. This function BUILDS its candidates by joining its own root, so
|
|
66
|
+
* every other root in the registry is unreachable here by construction and
|
|
67
|
+
* admitting them would be a widening that buys nothing. (It is also what keeps
|
|
68
|
+
* this module out of an import cycle with the registry, whose failure mode is a
|
|
69
|
+
* temporal-dead-zone `undefined` at module load rather than an error.)
|
|
70
|
+
*/
|
|
71
|
+
function locateCurrentTranscript(home, env, projectKey) {
|
|
72
|
+
const root = transcriptRoot(home, env);
|
|
73
|
+
const dir = join(root, projectKey.replace(/[/.]/g, "-"));
|
|
74
|
+
let entries;
|
|
75
|
+
try {
|
|
76
|
+
entries = readdirSync(dir);
|
|
77
|
+
}
|
|
78
|
+
catch {
|
|
79
|
+
return null;
|
|
80
|
+
}
|
|
81
|
+
let best = null;
|
|
82
|
+
for (const entry of entries) {
|
|
83
|
+
if (!entry.endsWith(".jsonl"))
|
|
84
|
+
continue;
|
|
85
|
+
const path = join(dir, entry);
|
|
86
|
+
if (!isInside(root, path))
|
|
87
|
+
continue;
|
|
88
|
+
let stat;
|
|
89
|
+
try {
|
|
90
|
+
stat = statSync(path);
|
|
91
|
+
}
|
|
92
|
+
catch {
|
|
93
|
+
continue;
|
|
94
|
+
}
|
|
95
|
+
if (!stat.isFile() || stat.size === 0)
|
|
96
|
+
continue;
|
|
97
|
+
if (best === null || stat.mtimeMs > best.mtime) {
|
|
98
|
+
best = {
|
|
99
|
+
path,
|
|
100
|
+
sessionId: basename(entry, ".jsonl"),
|
|
101
|
+
size: stat.size,
|
|
102
|
+
mtime: stat.mtimeMs,
|
|
103
|
+
};
|
|
104
|
+
}
|
|
105
|
+
}
|
|
106
|
+
return best === null ? null : { path: best.path, sessionId: best.sessionId, size: best.size };
|
|
107
|
+
}
|
|
108
|
+
/**
|
|
109
|
+
* ⛔ **MEASURED (`M1`), 2026-08-21, BOTH ARMS.**
|
|
110
|
+
*
|
|
111
|
+
* Claude Code exports `CLAUDE_CODE_SESSION_ID` into the Bash tool's environment
|
|
112
|
+
* and git passes it through to `post-commit`:
|
|
113
|
+
*
|
|
114
|
+
* - agent commit → the uuid, **byte-identical to the transcript filename**;
|
|
115
|
+
* - human commit, the variable stripped → `<UNSET>`, so `null` here;
|
|
116
|
+
* - commit from a linked worktree → **the same uuid**.
|
|
117
|
+
*
|
|
118
|
+
* That third row is why this is worth a member: the value survives the worktree
|
|
119
|
+
* boundary that the mtime heuristic in `spool.ts` cannot see across.
|
|
120
|
+
*
|
|
121
|
+
* ⚠ **`null` on empty or whitespace, not `""`.** An exported-but-empty variable
|
|
122
|
+
* is the shell's idea of unset, and a `""` session id would key a spool bucket
|
|
123
|
+
* every unset process shares.
|
|
124
|
+
*/
|
|
125
|
+
function claudeSessionIdFromEnv(env) {
|
|
126
|
+
const raw = env.CLAUDE_CODE_SESSION_ID;
|
|
127
|
+
if (raw === undefined)
|
|
128
|
+
return null;
|
|
129
|
+
const value = raw.trim();
|
|
130
|
+
return value === "" ? null : value;
|
|
131
|
+
}
|
|
132
|
+
export const CLAUDE_CODE = {
|
|
133
|
+
id: "claude-code",
|
|
134
|
+
transport: "ndjson",
|
|
135
|
+
transcriptRoot,
|
|
136
|
+
// ⛔ `CR-193`, D177 §2 — the lane's `.jsonl` gate, and NOTHING MORE under this
|
|
137
|
+
// root. `<projects>/<encoded-project-path>/` holds `<session-id>.jsonl` and a
|
|
138
|
+
// `subagents/` directory, so every file here is already a transcript; there is
|
|
139
|
+
// no interior shape to require, unlike Cursor's.
|
|
140
|
+
admitsFile: isTranscriptFile,
|
|
141
|
+
events: {
|
|
142
|
+
Stop: {
|
|
143
|
+
registeredTimeoutMs: CLAUDE_REGISTERED_STOP_MS,
|
|
144
|
+
clientBudgetMs: CLAUDE_CLIENT_BUDGET_MS,
|
|
145
|
+
},
|
|
146
|
+
PreCompact: {
|
|
147
|
+
registeredTimeoutMs: CLAUDE_REGISTERED_PRECOMPACT_MS,
|
|
148
|
+
clientBudgetMs: CLAUDE_CLIENT_BUDGET_MS,
|
|
149
|
+
},
|
|
150
|
+
SessionEnd: {
|
|
151
|
+
registeredTimeoutMs: CLAUDE_REGISTERED_SESSION_END_MS,
|
|
152
|
+
clientBudgetMs: CLAUDE_CLIENT_BUDGET_MS,
|
|
153
|
+
},
|
|
154
|
+
},
|
|
155
|
+
// `<session>/subagents/agent-*.jsonl`, globbed — the paths are never handed
|
|
156
|
+
// over. `CR-124`, D76.
|
|
157
|
+
delegatedTranscripts: "discover",
|
|
158
|
+
redaction: "claude-code",
|
|
159
|
+
sessionIdFromEnv: claudeSessionIdFromEnv,
|
|
160
|
+
locateCurrentTranscript,
|
|
161
|
+
};
|
|
162
|
+
//# sourceMappingURL=claude_code.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"claude_code.js","sourceRoot":"","sources":["../../src/agents/claude_code.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* The `codex-cli` dialect — `CR-183`, D164.
|
|
3
|
+
*
|
|
4
|
+
* ⚠ **DATA, described here; the readers land in later tasks of this wave.** What
|
|
5
|
+
* this entry does today is put Codex's transcript root into the confinement
|
|
6
|
+
* union and give `connect` a truthful answer about having no locator. The
|
|
7
|
+
* scrubber it names is `CR-185`'s, and the delegated-transcript mechanism it
|
|
8
|
+
* declares has no reader at all yet (see `DelegatedTranscripts`).
|
|
9
|
+
*
|
|
10
|
+
* ⛔ **Every number and mechanism below is CARRIED FROM THE TASK BRIEF, not
|
|
11
|
+
* measured by this task.** They are recorded as the brief stated them so a later
|
|
12
|
+
* reader can check them against the product rather than against this file.
|
|
13
|
+
*/
|
|
14
|
+
import type { TranscriptDialect } from "./types.js";
|
|
15
|
+
export declare const CODEX_CLI: TranscriptDialect;
|
|
@@ -0,0 +1,67 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* The `codex-cli` dialect — `CR-183`, D164.
|
|
3
|
+
*
|
|
4
|
+
* ⚠ **DATA, described here; the readers land in later tasks of this wave.** What
|
|
5
|
+
* this entry does today is put Codex's transcript root into the confinement
|
|
6
|
+
* union and give `connect` a truthful answer about having no locator. The
|
|
7
|
+
* scrubber it names is `CR-185`'s, and the delegated-transcript mechanism it
|
|
8
|
+
* declares has no reader at all yet (see `DelegatedTranscripts`).
|
|
9
|
+
*
|
|
10
|
+
* ⛔ **Every number and mechanism below is CARRIED FROM THE TASK BRIEF, not
|
|
11
|
+
* measured by this task.** They are recorded as the brief stated them so a later
|
|
12
|
+
* reader can check them against the product rather than against this file.
|
|
13
|
+
*/
|
|
14
|
+
import { codexTranscriptRoot, isTranscriptFile } from "../paths.js";
|
|
15
|
+
/**
|
|
16
|
+
* `SessionEnd` is the tight one — 3 s registered, 2.2 s client.
|
|
17
|
+
*
|
|
18
|
+
* That cap is why `CR-182`'s gap-stamp reserve exists at all: at 2200 ms a slow
|
|
19
|
+
* send routinely consumed the whole budget and the honest hole went unrecorded,
|
|
20
|
+
* which is the one outcome D56 §D8 says this client may not produce.
|
|
21
|
+
*/
|
|
22
|
+
const CODEX_SESSION_END_REGISTERED_MS = 3_000;
|
|
23
|
+
const CODEX_SESSION_END_CLIENT_MS = 2_200;
|
|
24
|
+
/** `Stop` and `PreCompact` are registered far wider — 30 s, client 8 s. */
|
|
25
|
+
const CODEX_TURN_REGISTERED_MS = 30_000;
|
|
26
|
+
const CODEX_TURN_CLIENT_MS = 8_000;
|
|
27
|
+
export const CODEX_CLI = {
|
|
28
|
+
id: "codex-cli",
|
|
29
|
+
transport: "ndjson",
|
|
30
|
+
transcriptRoot: codexTranscriptRoot,
|
|
31
|
+
// ⛔ `CR-193`, D177 §2 — the lane's `.jsonl` gate. `~/.codex/sessions` is
|
|
32
|
+
// date-nested rollout transcripts and nothing else, so there is no interior
|
|
33
|
+
// shape to require here either. ⚠ CARRIED FROM THE BRIEF like the numbers
|
|
34
|
+
// above: this task did not re-walk a Codex tree to confirm it.
|
|
35
|
+
admitsFile: isTranscriptFile,
|
|
36
|
+
events: {
|
|
37
|
+
Stop: {
|
|
38
|
+
registeredTimeoutMs: CODEX_TURN_REGISTERED_MS,
|
|
39
|
+
clientBudgetMs: CODEX_TURN_CLIENT_MS,
|
|
40
|
+
},
|
|
41
|
+
PreCompact: {
|
|
42
|
+
registeredTimeoutMs: CODEX_TURN_REGISTERED_MS,
|
|
43
|
+
clientBudgetMs: CODEX_TURN_CLIENT_MS,
|
|
44
|
+
},
|
|
45
|
+
SessionEnd: {
|
|
46
|
+
registeredTimeoutMs: CODEX_SESSION_END_REGISTERED_MS,
|
|
47
|
+
clientBudgetMs: CODEX_SESSION_END_CLIENT_MS,
|
|
48
|
+
},
|
|
49
|
+
},
|
|
50
|
+
// The delegated path ARRIVES on stdin as `agent_transcript_path` — there is no
|
|
51
|
+
// directory to glob. Nothing parses it yet; the discriminator is here so the
|
|
52
|
+
// Claude glob cannot run against a layout that has no `subagents/`.
|
|
53
|
+
delegatedTranscripts: "announce",
|
|
54
|
+
redaction: "codex-cli",
|
|
55
|
+
// ⛔ **UNMEASURED.** `null` here does NOT mean "Codex CLI has no session id in
|
|
56
|
+
// its environment" — it means nobody has run the measurement `M1` ran for
|
|
57
|
+
// Claude Code (agent commit, human commit, worktree commit) against Codex, so
|
|
58
|
+
// this client has no variable it can honestly name. ⚠ Read it as the absence
|
|
59
|
+
// of a measurement, not as a fact about the agent. Measuring it is a change to
|
|
60
|
+
// this line and nothing else.
|
|
61
|
+
sessionIdFromEnv: () => null,
|
|
62
|
+
// ⛔ NO `locateCurrentTranscript`, and the omission is the decision. Finding
|
|
63
|
+
// the repository would mean reading a `cwd` out of the transcript's own
|
|
64
|
+
// records — ANALYSIS, which D60 §D6 forbids this client. `connect` therefore
|
|
65
|
+
// skips the test-capture beat rather than guessing.
|
|
66
|
+
};
|
|
67
|
+
//# sourceMappingURL=codex_cli.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"codex_cli.js","sourceRoot":"","sources":["../../src/agents/codex_cli.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;GAYG;AAEH,OAAO,EAAE,mBAAmB,EAAE,gBAAgB,EAAE,MAAM,aAAa,CAAC;AAGpE;;;;;;GAMG;AACH,MAAM,+BAA+B,GAAG,KAAK,CAAC;AAC9C,MAAM,2BAA2B,GAAG,KAAK,CAAC;AAE1C,2EAA2E;AAC3E,MAAM,wBAAwB,GAAG,MAAM,CAAC;AACxC,MAAM,oBAAoB,GAAG,KAAK,CAAC;AAEnC,MAAM,CAAC,MAAM,SAAS,GAAsB;IAC1C,EAAE,EAAE,WAAW;IACf,SAAS,EAAE,QAAQ;IACnB,cAAc,EAAE,mBAAmB;IACnC,yEAAyE;IACzE,4EAA4E;IAC5E,0EAA0E;IAC1E,+DAA+D;IAC/D,UAAU,EAAE,gBAAgB;IAC5B,MAAM,EAAE;QACN,IAAI,EAAE;YACJ,mBAAmB,EAAE,wBAAwB;YAC7C,cAAc,EAAE,oBAAoB;SACrC;QACD,UAAU,EAAE;YACV,mBAAmB,EAAE,wBAAwB;YAC7C,cAAc,EAAE,oBAAoB;SACrC;QACD,UAAU,EAAE;YACV,mBAAmB,EAAE,+BAA+B;YACpD,cAAc,EAAE,2BAA2B;SAC5C;KACF;IACD,+EAA+E;IAC/E,6EAA6E;IAC7E,oEAAoE;IACpE,oBAAoB,EAAE,UAAU;IAChC,SAAS,EAAE,WAAW;IACtB,8EAA8E;IAC9E,0EAA0E;IAC1E,8EAA8E;IAC9E,6EAA6E;IAC7E,+EAA+E;IAC/E,8BAA8B;IAC9B,gBAAgB,EAAE,GAAG,EAAE,CAAC,IAAI;IAC5B,4EAA4E;IAC5E,wEAAwE;IACxE,6EAA6E;IAC7E,oDAAoD;CACrD,CAAC"}
|
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* The `cursor` dialect — `CR-193`, D177.
|
|
3
|
+
*
|
|
4
|
+
* ⛔ **THE ID IS `cursor`. NEVER `cursor-ide`, never `cursor-mcp`** (D177 §1).
|
|
5
|
+
* The server's client-name alias map already folds both of those spellings onto
|
|
6
|
+
* `cursor`, and a cell over there asserts that every producible id is one of
|
|
7
|
+
* that map's TARGETS — so a different spelling here reds a test in another
|
|
8
|
+
* repository, on day one, for a reason nothing on this side would report.
|
|
9
|
+
* ⚠ Cited from D177 §1 rather than read: this task has no access to that tree.
|
|
10
|
+
*
|
|
11
|
+
* ⛔ **`transport: "ndjson"` DOES NOT CHANGE, and the lock-out doing its job is
|
|
12
|
+
* it NOT FIRING** (D177 §1). D164 §2 installed that literal type precisely so
|
|
13
|
+
* *"an Aider or **Cursor** dialect cannot be registered without editing the
|
|
14
|
+
* interface"* — and Cursor turns out to meet D164 §1's membership rule exactly:
|
|
15
|
+
* it appends NDJSON to a file and hands a hook its path.
|
|
16
|
+
*
|
|
17
|
+
* ⚠ **What is DATA here and what is MEASURED is not the same set**, and the
|
|
18
|
+
* difference is marked on each member below rather than left to a reader.
|
|
19
|
+
*/
|
|
20
|
+
import type { TranscriptDialect } from "./types.js";
|
|
21
|
+
export declare const CURSOR: TranscriptDialect;
|
|
@@ -0,0 +1,89 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* The `cursor` dialect — `CR-193`, D177.
|
|
3
|
+
*
|
|
4
|
+
* ⛔ **THE ID IS `cursor`. NEVER `cursor-ide`, never `cursor-mcp`** (D177 §1).
|
|
5
|
+
* The server's client-name alias map already folds both of those spellings onto
|
|
6
|
+
* `cursor`, and a cell over there asserts that every producible id is one of
|
|
7
|
+
* that map's TARGETS — so a different spelling here reds a test in another
|
|
8
|
+
* repository, on day one, for a reason nothing on this side would report.
|
|
9
|
+
* ⚠ Cited from D177 §1 rather than read: this task has no access to that tree.
|
|
10
|
+
*
|
|
11
|
+
* ⛔ **`transport: "ndjson"` DOES NOT CHANGE, and the lock-out doing its job is
|
|
12
|
+
* it NOT FIRING** (D177 §1). D164 §2 installed that literal type precisely so
|
|
13
|
+
* *"an Aider or **Cursor** dialect cannot be registered without editing the
|
|
14
|
+
* interface"* — and Cursor turns out to meet D164 §1's membership rule exactly:
|
|
15
|
+
* it appends NDJSON to a file and hands a hook its path.
|
|
16
|
+
*
|
|
17
|
+
* ⚠ **What is DATA here and what is MEASURED is not the same set**, and the
|
|
18
|
+
* difference is marked on each member below rather than left to a reader.
|
|
19
|
+
*/
|
|
20
|
+
import { cursorTranscriptRoot, isCursorTranscript } from "../paths.js";
|
|
21
|
+
/**
|
|
22
|
+
* ⛔ **UNMEASURED, AND ADOPTED AS A FLOOR RATHER THAN GUESSED.**
|
|
23
|
+
*
|
|
24
|
+
* The plan's `D5` — the exact `hook_event_name` strings Cursor sends, and the
|
|
25
|
+
* timeouts its `~/.cursor/hooks.json` registers — is **founder-blocked and
|
|
26
|
+
* gates `CR-195`**, the unit that writes that file. Nothing installs a Cursor
|
|
27
|
+
* hook today, so no Cursor hook has ever fired and these numbers are currently
|
|
28
|
+
* unreachable in production.
|
|
29
|
+
*
|
|
30
|
+
* ⚠ **The direction of safety is what picks them.** Too WIDE and the agent
|
|
31
|
+
* kills the process before this client's watchdog fires — which loses the
|
|
32
|
+
* exit-0/silent-stdout contract, the one outcome DESIGN.md §13.7 forbids. Too
|
|
33
|
+
* TIGHT and an invocation sends less and the next one picks it up. So the
|
|
34
|
+
* registry's tightest existing pair is taken as a FLOOR: it holds whatever
|
|
35
|
+
* Cursor turns out to register, down to Codex's 3 s cap.
|
|
36
|
+
*
|
|
37
|
+
* ⛔ **This is a refusal to guess, not a measurement of Cursor**, and `CR-195`
|
|
38
|
+
* owns the real numbers once `D5` comes back.
|
|
39
|
+
*/
|
|
40
|
+
const CURSOR_REGISTERED_MS = 3_000;
|
|
41
|
+
const CURSOR_CLIENT_MS = 2_200;
|
|
42
|
+
export const CURSOR = {
|
|
43
|
+
id: "cursor",
|
|
44
|
+
transport: "ndjson",
|
|
45
|
+
// ⛔ `projects`, one level ABOVE the root D177 §2 names, because `<munged>` is
|
|
46
|
+
// the workspace fsPath and is derivable from neither `home` nor `env`. The
|
|
47
|
+
// measurement that rules out deriving it from the project key, and what that
|
|
48
|
+
// costs, are in `cursorTranscriptRoot`'s own docblock.
|
|
49
|
+
transcriptRoot: cursorTranscriptRoot,
|
|
50
|
+
// ⛔ THE GATE D177 §2 REQUIRES TO SHIP IN THIS UNIT — `.jsonl`, plus the
|
|
51
|
+
// `<workspace>/agent-transcripts/` shape that puts §2's boundary back.
|
|
52
|
+
admitsFile: isCursorTranscript,
|
|
53
|
+
events: {
|
|
54
|
+
Stop: {
|
|
55
|
+
registeredTimeoutMs: CURSOR_REGISTERED_MS,
|
|
56
|
+
clientBudgetMs: CURSOR_CLIENT_MS,
|
|
57
|
+
},
|
|
58
|
+
PreCompact: {
|
|
59
|
+
registeredTimeoutMs: CURSOR_REGISTERED_MS,
|
|
60
|
+
clientBudgetMs: CURSOR_CLIENT_MS,
|
|
61
|
+
},
|
|
62
|
+
SessionEnd: {
|
|
63
|
+
registeredTimeoutMs: CURSOR_REGISTERED_MS,
|
|
64
|
+
clientBudgetMs: CURSOR_CLIENT_MS,
|
|
65
|
+
},
|
|
66
|
+
},
|
|
67
|
+
// ⛔ DECLARED EXPLICITLY, and it is load-bearing now in a way it was not
|
|
68
|
+
// before (D177 §2, §9). `subagentsDir()` would compute
|
|
69
|
+
// `<root>/<cid>/<cid>/subagents` for a Cursor transcript — ONE LEVEL TOO DEEP,
|
|
70
|
+
// since the real layout is `<root>/<parentId>/subagents/<childId>.jsonl` — and
|
|
71
|
+
// that wrong directory is inert only because `announce` short-circuits before
|
|
72
|
+
// it. ⚠ `CR-204` made the selection ROOT-derived rather than flag-derived, so
|
|
73
|
+
// a wrong declaration here is no longer a line nobody reaches.
|
|
74
|
+
delegatedTranscripts: "announce",
|
|
75
|
+
redaction: "cursor",
|
|
76
|
+
// ⛔ **UNMEASURED.** `null` here does NOT mean "Cursor has no session id in its
|
|
77
|
+
// environment" — it means nobody has run the measurement `M1` ran for Claude
|
|
78
|
+
// Code (agent commit, human commit, worktree commit) against Cursor, so this
|
|
79
|
+
// client has no variable it can honestly name. ⚠ Read it as the absence of a
|
|
80
|
+
// measurement, not as a fact about the agent. Measuring it is a change to this
|
|
81
|
+
// line and nothing else.
|
|
82
|
+
sessionIdFromEnv: () => null,
|
|
83
|
+
// ⛔ NO `locateCurrentTranscript`, and the omission is the decision — the same
|
|
84
|
+
// one Codex made. Finding the conversation for THIS project would mean munging
|
|
85
|
+
// a workspace path this client is never told, or reading a `cwd` out of the
|
|
86
|
+
// transcript's own records, which is ANALYSIS (D60 §D6). `connect` skips the
|
|
87
|
+
// test-capture beat rather than guessing.
|
|
88
|
+
};
|
|
89
|
+
//# sourceMappingURL=cursor.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"cursor.js","sourceRoot":"","sources":["../../src/agents/cursor.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;GAkBG;AAEH,OAAO,EAAE,oBAAoB,EAAE,kBAAkB,EAAE,MAAM,aAAa,CAAC;AAGvE;;;;;;;;;;;;;;;;;;GAkBG;AACH,MAAM,oBAAoB,GAAG,KAAK,CAAC;AACnC,MAAM,gBAAgB,GAAG,KAAK,CAAC;AAE/B,MAAM,CAAC,MAAM,MAAM,GAAsB;IACvC,EAAE,EAAE,QAAQ;IACZ,SAAS,EAAE,QAAQ;IACnB,8EAA8E;IAC9E,2EAA2E;IAC3E,6EAA6E;IAC7E,uDAAuD;IACvD,cAAc,EAAE,oBAAoB;IACpC,wEAAwE;IACxE,uEAAuE;IACvE,UAAU,EAAE,kBAAkB;IAC9B,MAAM,EAAE;QACN,IAAI,EAAE;YACJ,mBAAmB,EAAE,oBAAoB;YACzC,cAAc,EAAE,gBAAgB;SACjC;QACD,UAAU,EAAE;YACV,mBAAmB,EAAE,oBAAoB;YACzC,cAAc,EAAE,gBAAgB;SACjC;QACD,UAAU,EAAE;YACV,mBAAmB,EAAE,oBAAoB;YACzC,cAAc,EAAE,gBAAgB;SACjC;KACF;IACD,wEAAwE;IACxE,uDAAuD;IACvD,+EAA+E;IAC/E,+EAA+E;IAC/E,8EAA8E;IAC9E,8EAA8E;IAC9E,+DAA+D;IAC/D,oBAAoB,EAAE,UAAU;IAChC,SAAS,EAAE,QAAQ;IACnB,+EAA+E;IAC/E,6EAA6E;IAC7E,6EAA6E;IAC7E,6EAA6E;IAC7E,+EAA+E;IAC/E,yBAAyB;IACzB,gBAAgB,EAAE,GAAG,EAAE,CAAC,IAAI;IAC5B,8EAA8E;IAC9E,+EAA+E;IAC/E,4EAA4E;IAC5E,6EAA6E;IAC7E,0CAA0C;CAC3C,CAAC"}
|
|
@@ -0,0 +1,184 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* The registry — `CR-183`, D164.
|
|
3
|
+
*
|
|
4
|
+
* The one place that knows which dialects exist. Two things are derived from it
|
|
5
|
+
* and from nothing else: which adapter a `--agent=` value selects, and the set
|
|
6
|
+
* of roots the confinement check runs over.
|
|
7
|
+
*
|
|
8
|
+
* ## ⛔ Why `--agent=` is the selector, and why two alternatives are SECURITY
|
|
9
|
+
* rejections rather than taste
|
|
10
|
+
*
|
|
11
|
+
* - **an environment variable** — rejected because Codex exports
|
|
12
|
+
* `CLAUDE_PLUGIN_ROOT` itself, so the obvious variable is set by the wrong
|
|
13
|
+
* agent and says the wrong thing.
|
|
14
|
+
* - **`hook_event_name`** — rejected because both members of this lane fire
|
|
15
|
+
* the same three events. It discriminates nothing.
|
|
16
|
+
* - ⛔ **the `transcript_path` prefix, as the PRIMARY selector** — rejected
|
|
17
|
+
* because that is *the attacker-influenced input `isInside` exists to
|
|
18
|
+
* constrain.* A path that CLAIMS to sit under a root may not be what
|
|
19
|
+
* decides which root we will read from.
|
|
20
|
+
*
|
|
21
|
+
* ⚠ **`CR-185` picking the SCRUBBER by the containing root is not a
|
|
22
|
+
* contradiction — read D164 §5 before you think it is.** Choosing a STRICTER
|
|
23
|
+
* scrubber because a path sits outside every known root is fail-closed;
|
|
24
|
+
* choosing a WIDER root because a path claims to be there is not. The
|
|
25
|
+
* confinement boundary is computed from this registry either way, so a wrong
|
|
26
|
+
* SELECTION can pick a suboptimal redaction profile and can never widen the
|
|
27
|
+
* boundary.
|
|
28
|
+
*/
|
|
29
|
+
import { type AgentId, type SelectedAgentId, type TranscriptDialect } from "./types.js";
|
|
30
|
+
/**
|
|
31
|
+
* Every dialect, in registration order.
|
|
32
|
+
*
|
|
33
|
+
* ⛔ **The ids are D164 §1's, spelled exactly.** `test/agent-registry.test.ts`
|
|
34
|
+
* pins them against the literal list, because the four-way cross-repo spelling
|
|
35
|
+
* fails silently in the direction that matters: emit `codex_cli` and the server
|
|
36
|
+
* does not recognise it, the default extractor runs, and the row lands `unknown`
|
|
37
|
+
* permanently with no error anywhere.
|
|
38
|
+
*/
|
|
39
|
+
export declare const DIALECTS: readonly TranscriptDialect[];
|
|
40
|
+
/**
|
|
41
|
+
* What an invocation with no `--agent=` is.
|
|
42
|
+
*
|
|
43
|
+
* Claude Code, because that is what every install shipped before this flag
|
|
44
|
+
* existed — the `.claude-plugin` bundle's command carries no `--agent=` and
|
|
45
|
+
* must keep working unchanged.
|
|
46
|
+
*/
|
|
47
|
+
export declare const DEFAULT_AGENT_ID: AgentId;
|
|
48
|
+
/**
|
|
49
|
+
* Read the adapter id off argv.
|
|
50
|
+
*
|
|
51
|
+
* ⛔ **CALLED FROM `index.ts`, beside `invocationMode`, and NOWHERE ELSE.** The
|
|
52
|
+
* hook path never sees argv: `runHook` takes a `HookEnvironment`, and the id
|
|
53
|
+
* reaches it as a field on that object — the same seam that already injects
|
|
54
|
+
* `home` and `nodeVersion` so the whole path can be driven by a test. ⛔ And
|
|
55
|
+
* `parseHookInput` must NOT select the adapter: stdin is the input the
|
|
56
|
+
* confinement boundary exists to constrain, and a selector read from there is
|
|
57
|
+
* the rejection above with an extra step.
|
|
58
|
+
*
|
|
59
|
+
* ⚠ **Resolving an id is NOT entering hook mode**, and the difference is the
|
|
60
|
+
* trap this wave is most likely to ship. `invocationMode` returns `"hook"` only
|
|
61
|
+
* when `argv[0] === "hook"` (or a legacy env var is set), and Codex sets
|
|
62
|
+
* neither — so a registered command whose FIRST argument is this flag runs the
|
|
63
|
+
* binary INTERACTIVELY, inside a hook. ⛔ **The registered command must lead
|
|
64
|
+
* with the `hook` verb.** `test/agent-registry.test.ts` holds both arms.
|
|
65
|
+
*
|
|
66
|
+
* Returns the LITERAL it was given. It is deliberately not narrowed to
|
|
67
|
+
* `AgentId`: an id we do not recognise still has to travel, and the honest thing
|
|
68
|
+
* to carry is what the operator wrote.
|
|
69
|
+
*/
|
|
70
|
+
export declare function resolveAgentId(argv: readonly string[]): string;
|
|
71
|
+
/**
|
|
72
|
+
* The dialect an id selects.
|
|
73
|
+
*
|
|
74
|
+
* ⛔ **AN UNKNOWN ID FALLS BACK; IT NEVER STOPS CAPTURE (plan §D7, founder
|
|
75
|
+
* ruling).** The client still reads, still redacts and still sends. Refusing was
|
|
76
|
+
* considered and rejected: a misconfigured hook that captures NOTHING is a worse
|
|
77
|
+
* outcome than one that captures conservatively, and the server has an honest
|
|
78
|
+
* label (`unknown`, D164 §1) for a producer it does not recognise — so nothing
|
|
79
|
+
* downstream has to guess.
|
|
80
|
+
*
|
|
81
|
+
* ⚠ **The fallback is a BEHAVIOUR choice only.** The literal the operator gave
|
|
82
|
+
* is not rewritten by this function; callers that put the agent on the wire read
|
|
83
|
+
* the id, not the dialect.
|
|
84
|
+
*/
|
|
85
|
+
export declare function dialectFor(agentId: string | undefined): TranscriptDialect;
|
|
86
|
+
/**
|
|
87
|
+
* Every transcript root, deduped — ⛔ **the confinement boundary, computed from
|
|
88
|
+
* the REGISTRY and never from input.**
|
|
89
|
+
*
|
|
90
|
+
* A union rather than "the selected dialect's root" on purpose. The selection
|
|
91
|
+
* comes from a flag an install may have got wrong; the boundary must not. With
|
|
92
|
+
* the union, a wrong selection costs a suboptimal redaction profile (D164 §5)
|
|
93
|
+
* and cannot make a file readable that was not readable before.
|
|
94
|
+
*
|
|
95
|
+
* Deduped because two dialects can be pointed at one directory by environment —
|
|
96
|
+
* `CODEX_HOME` and `CLAUDE_CONFIG_DIR` are both operator-set — and a duplicated
|
|
97
|
+
* root would make `isInsideAny` do the same `realpath` pair twice for no reason.
|
|
98
|
+
*/
|
|
99
|
+
export declare function transcriptRoots(home: string, env: NodeJS.ProcessEnv): readonly string[];
|
|
100
|
+
/**
|
|
101
|
+
* ⛔ **MAY THIS PATH BE READ AS A TRANSCRIPT? — confinement, THEN the file
|
|
102
|
+
* gate (`CR-193`, D177 §2).**
|
|
103
|
+
*
|
|
104
|
+
* Two different questions, asked in an order that cannot be swapped:
|
|
105
|
+
*
|
|
106
|
+
* 1. `isInsideAny(transcriptRoots(...))` — *may this path be read at all.*
|
|
107
|
+
* Registry-computed, never derived from input, byte-unchanged from
|
|
108
|
+
* `CR-183`. `/cso` finding 1.
|
|
109
|
+
* 2. the containing dialect's `admitsFile` — *is this file a transcript.* A
|
|
110
|
+
* root can be exactly right and still hold files that are not transcripts;
|
|
111
|
+
* there are 112 of them under Cursor's.
|
|
112
|
+
*
|
|
113
|
+
* ⛔ **STEP 2 CAN ONLY REFUSE.** It runs over a path step 1 already admitted, so
|
|
114
|
+
* every outcome is a narrowing — D177 §7's direction test, which is what makes
|
|
115
|
+
* this the accepted shape rather than the rejected `transcript_path`-as-boundary
|
|
116
|
+
* one. The boundary is still the registry's.
|
|
117
|
+
*
|
|
118
|
+
* ⚠ **The `unknown` arm keeps step 1's answer, deliberately.** A path inside two
|
|
119
|
+
* roots is admitted by the boundary and is `unknown` to `agentForTranscript`;
|
|
120
|
+
* `dialectFor` then falls back to Claude, whose gate is the plain `.jsonl` test.
|
|
121
|
+
* Refusing on `unknown` here would be a THIRD boundary rule, and it would refuse
|
|
122
|
+
* captures D177 §7 decided to keep (the header is omitted, the bytes still go).
|
|
123
|
+
*
|
|
124
|
+
* ⚠ Two root resolutions rather than one — `isInsideAny` resolves, then
|
|
125
|
+
* `agentForTranscript` resolves again. Measured against the alternative, which
|
|
126
|
+
* is folding the two into one pass by editing `agentForTranscript`: that
|
|
127
|
+
* function is the wire's producer oracle and `test/wire-agent.test.ts` gates it,
|
|
128
|
+
* so a handful of `realpath` calls inside a multi-second budget is the cheaper
|
|
129
|
+
* side of that trade.
|
|
130
|
+
*/
|
|
131
|
+
export declare function admitsTranscript(home: string, env: NodeJS.ProcessEnv, transcriptPath: string): boolean;
|
|
132
|
+
/**
|
|
133
|
+
* ⛔ **WHICH AGENT PRODUCED THIS TRANSCRIPT — the containing registry root's,
|
|
134
|
+
* NEVER the `--agent=` flag's (`CR-204`, D177 §7).**
|
|
135
|
+
*
|
|
136
|
+
* ## Why the flag cannot be the answer
|
|
137
|
+
*
|
|
138
|
+
* `DEFAULT_AGENT_ID` is `claude-code` and **no install passes the flag** — the
|
|
139
|
+
* `.claude-plugin` bundle's command is `node "…/bin/index.js" hook`, no flag, as
|
|
140
|
+
* the docblock above records. So a flag-derived wire value is `claude-code` for
|
|
141
|
+
* every path that did not remember to set it: a FALSE PROVENANCE CLAIM,
|
|
142
|
+
* permanent, in the artifact whose entire worth is faithfulness. The wire is not
|
|
143
|
+
* a place to be wrong in a plausible direction
|
|
144
|
+
* (`project_plausible_answer_hazard_class`).
|
|
145
|
+
*
|
|
146
|
+
* ## ⛔ This is `CR-185`'s shape, not `CR-183`'s rejected one
|
|
147
|
+
*
|
|
148
|
+
* `rulesForTranscript` in `redact.ts` is the same three lines with `RULES` in
|
|
149
|
+
* place of `id`, and its own docblock carries the distinction verbatim:
|
|
150
|
+
*
|
|
151
|
+
* - choosing a **stricter scrubber** — or a LESS specific producer — because a
|
|
152
|
+
* path sits outside every known root is **fail-closed**;
|
|
153
|
+
* - choosing a **wider root** because a path *claims* to be there is not.
|
|
154
|
+
*
|
|
155
|
+
* ⛔ **The confinement boundary is untouched and still runs FIRST.** This is a
|
|
156
|
+
* LOOKUP AGAINST an already-computed boundary: `transcriptRoots` decides what
|
|
157
|
+
* may be read, `isInsideAny` refuses everything else, and only then does this
|
|
158
|
+
* ask which of the roots that already admitted the file names its producer. It
|
|
159
|
+
* can never widen anything, and its arity is 3 — ⛔ **the flag is not a
|
|
160
|
+
* parameter of it at all.**
|
|
161
|
+
*
|
|
162
|
+
* ## Zero or two → `unknown`, ⛔ which the wire step turns into an OMISSION
|
|
163
|
+
*
|
|
164
|
+
* Exactly one match names the agent. Two dialects can be pointed at one
|
|
165
|
+
* directory by environment (`CLAUDE_CONFIG_DIR` and `CODEX_HOME` are both
|
|
166
|
+
* operator-set and both sides resolve through `realpath`), and a path inside two
|
|
167
|
+
* roots is a path whose producer is not known. `unknown` is D164 §3's honest
|
|
168
|
+
* label for exactly that.
|
|
169
|
+
*
|
|
170
|
+
* ⛔ **It is not, however, what goes on the wire.** `buildIngestHeaders` OMITS
|
|
171
|
+
* `x-agent` for this answer, because the server treats an absent header and a
|
|
172
|
+
* present `unknown` as different facts: absent is *"nothing to say"* and leaves
|
|
173
|
+
* a stream's pinned agent alone, while a present `unknown` resolves and
|
|
174
|
+
* DISAGREES with any non-`unknown` pin — which drops the buffered tail and sheds
|
|
175
|
+
* the in-flight turn. Selecting `unknown` here is therefore a decision to say
|
|
176
|
+
* nothing, not a value to assert. `post.ts` carries the full argument.
|
|
177
|
+
*
|
|
178
|
+
* ⚠ **The zero-match arm has NO CLIENT CALLER on the hook path**, said plainly
|
|
179
|
+
* rather than left to be assumed (`feedback_unexercisable_branch_not_verified`):
|
|
180
|
+
* `isInsideAny(transcriptRoots(...))` refuses everything outside the union
|
|
181
|
+
* before a delta is ever built. It is a second independent line, and
|
|
182
|
+
* `test/agent-registry.test.ts` exercises it by calling this function directly.
|
|
183
|
+
*/
|
|
184
|
+
export declare function agentForTranscript(home: string, env: NodeJS.ProcessEnv, transcriptPath: string): SelectedAgentId;
|