@primitive.ai/prim 0.1.0-alpha.66 → 0.1.0-alpha.68
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/dist/chunk-I5BHJQH2.js +329 -0
- package/dist/chunk-K3AYW6VZ.js +300 -0
- package/dist/{chunk-CPX3BUOC.js → chunk-RTLFE6EZ.js} +6 -2
- package/dist/{chunk-KHZNYEUV.js → chunk-U6L2RU67.js} +1 -1
- package/dist/{chunk-HIMWM526.js → chunk-UN5O5SYG.js} +1 -1
- package/dist/chunk-XGADQPCI.js +26 -0
- package/dist/{chunk-DR66XM5L.js → chunk-XPVNH3J6.js} +1 -1
- package/dist/{chunk-WS3MCNFD.js → chunk-XUAP572Z.js} +1 -1
- package/dist/daemon/server.js +121 -26
- package/dist/hooks/post-commit.js +2 -2
- package/dist/hooks/post-rewrite.js +2 -2
- package/dist/hooks/post-tool-use.js +51 -21
- package/dist/hooks/pre-commit.js +2 -2
- package/dist/hooks/pre-tool-use.js +64 -25
- package/dist/hooks/prim-hook.js +67 -5
- package/dist/hooks/session-end.js +3 -3
- package/dist/hooks/session-start.js +28 -20
- package/dist/index.js +617 -107
- package/dist/statusline-main.js +16 -12
- package/package.json +1 -1
- package/dist/chunk-CZ24W5QU.js +0 -123
- package/dist/chunk-JQFN6LBO.js +0 -110
|
@@ -13,10 +13,15 @@ import {
|
|
|
13
13
|
readHookMode,
|
|
14
14
|
scrub
|
|
15
15
|
} from "../chunk-G5D3FF3D.js";
|
|
16
|
+
import {
|
|
17
|
+
appendCodexContext,
|
|
18
|
+
hasVisibleCodexMessage,
|
|
19
|
+
prepareCodexContext
|
|
20
|
+
} from "../chunk-I5BHJQH2.js";
|
|
16
21
|
import {
|
|
17
22
|
packageVersion,
|
|
18
23
|
warmBinCache
|
|
19
|
-
} from "../chunk-
|
|
24
|
+
} from "../chunk-K3AYW6VZ.js";
|
|
20
25
|
import {
|
|
21
26
|
canonicalGitRoot,
|
|
22
27
|
canonicalRepositoryPath,
|
|
@@ -25,11 +30,12 @@ import {
|
|
|
25
30
|
} from "../chunk-TDRXHBEX.js";
|
|
26
31
|
import {
|
|
27
32
|
getClient
|
|
28
|
-
} from "../chunk-
|
|
33
|
+
} from "../chunk-RTLFE6EZ.js";
|
|
29
34
|
import {
|
|
30
35
|
normalizeEnvelope,
|
|
31
36
|
parseAgent
|
|
32
37
|
} from "../chunk-TCDUH7AN.js";
|
|
38
|
+
import "../chunk-UTKQTZHL.js";
|
|
33
39
|
|
|
34
40
|
// src/hooks/preflight-v3.ts
|
|
35
41
|
import { Buffer as Buffer2 } from "buffer";
|
|
@@ -147,9 +153,19 @@ async function readStdin() {
|
|
|
147
153
|
});
|
|
148
154
|
}
|
|
149
155
|
var agent = parseAgent(process.argv);
|
|
150
|
-
function emit(output) {
|
|
151
|
-
|
|
152
|
-
|
|
156
|
+
async function emit(output) {
|
|
157
|
+
return await new Promise((resolve) => {
|
|
158
|
+
try {
|
|
159
|
+
process.stdout.write(`${JSON.stringify(output)}
|
|
160
|
+
`, (error) => resolve(!error));
|
|
161
|
+
} catch {
|
|
162
|
+
resolve(false);
|
|
163
|
+
}
|
|
164
|
+
});
|
|
165
|
+
}
|
|
166
|
+
async function emitWithAcknowledgment(output, acknowledge) {
|
|
167
|
+
const handedOff = await emit(output);
|
|
168
|
+
await acknowledge?.(handedOff);
|
|
153
169
|
}
|
|
154
170
|
function failOpen() {
|
|
155
171
|
if (agent === "hermes") return failOpenHermes();
|
|
@@ -159,16 +175,28 @@ function invocationId(envelope) {
|
|
|
159
175
|
const value = agent === "hermes" ? envelope.extra?.tool_call_id : envelope.tool_use_id;
|
|
160
176
|
return typeof value === "string" && value.length > 0 ? value : void 0;
|
|
161
177
|
}
|
|
162
|
-
function emitUnverified(message) {
|
|
178
|
+
async function emitUnverified(message, envelope) {
|
|
163
179
|
const result = unverifiedResult(message);
|
|
164
180
|
if (agent === "hermes") {
|
|
165
181
|
process.stderr.write(`[primitive] ${message}
|
|
166
182
|
`);
|
|
167
|
-
emit(failOpenHermes());
|
|
183
|
+
await emit(failOpenHermes());
|
|
168
184
|
} else {
|
|
169
|
-
|
|
170
|
-
|
|
171
|
-
|
|
185
|
+
let output = agent === "codex" ? buildCodexOutput("allow", [result]) : buildHookOutput("allow", [result]);
|
|
186
|
+
if (agent === "codex" && typeof envelope?.session_id === "string" && envelope.session_id) {
|
|
187
|
+
try {
|
|
188
|
+
const context = await prepareCodexContext({
|
|
189
|
+
cwd: envelope.cwd ?? process.cwd(),
|
|
190
|
+
sessionId: envelope.session_id,
|
|
191
|
+
includeDigest: false
|
|
192
|
+
});
|
|
193
|
+
output = appendCodexContext(output, context.context);
|
|
194
|
+
await emitWithAcknowledgment(output, context.acknowledge);
|
|
195
|
+
return;
|
|
196
|
+
} catch {
|
|
197
|
+
}
|
|
198
|
+
}
|
|
199
|
+
await emit(output);
|
|
172
200
|
}
|
|
173
201
|
}
|
|
174
202
|
async function main() {
|
|
@@ -177,7 +205,7 @@ async function main() {
|
|
|
177
205
|
try {
|
|
178
206
|
raw = await readStdin();
|
|
179
207
|
} catch {
|
|
180
|
-
emit(failOpen());
|
|
208
|
+
await emit(failOpen());
|
|
181
209
|
return;
|
|
182
210
|
}
|
|
183
211
|
let envelope;
|
|
@@ -187,17 +215,17 @@ async function main() {
|
|
|
187
215
|
agent
|
|
188
216
|
);
|
|
189
217
|
} catch {
|
|
190
|
-
emit(failOpen());
|
|
218
|
+
await emit(failOpen());
|
|
191
219
|
return;
|
|
192
220
|
}
|
|
193
221
|
if (envelope.hook_event_name !== "PreToolUse") {
|
|
194
|
-
emit(failOpen());
|
|
222
|
+
await emit(failOpen());
|
|
195
223
|
return;
|
|
196
224
|
}
|
|
197
225
|
const env = process.env;
|
|
198
226
|
const mode = readHookMode(env);
|
|
199
227
|
if (mode === "off") {
|
|
200
|
-
emit(failOpen());
|
|
228
|
+
await emit(failOpen());
|
|
201
229
|
return;
|
|
202
230
|
}
|
|
203
231
|
const toolName = typeof envelope.tool_name === "string" ? envelope.tool_name : "";
|
|
@@ -209,22 +237,25 @@ async function main() {
|
|
|
209
237
|
cwd
|
|
210
238
|
});
|
|
211
239
|
if (targets.mutation === "none") {
|
|
212
|
-
emit(failOpen());
|
|
240
|
+
await emit(failOpen());
|
|
213
241
|
return;
|
|
214
242
|
}
|
|
215
243
|
if (!isRepoActive(cwd)) {
|
|
216
|
-
emit(failOpen());
|
|
244
|
+
await emit(failOpen());
|
|
217
245
|
return;
|
|
218
246
|
}
|
|
219
247
|
if (targets.paths.length === 0) {
|
|
220
|
-
emitUnverified(
|
|
248
|
+
await emitUnverified(
|
|
249
|
+
"mutation targets could not be determined; enforcement not verified",
|
|
250
|
+
envelope
|
|
251
|
+
);
|
|
221
252
|
return;
|
|
222
253
|
}
|
|
223
254
|
const binding = repoSyncId(cwd);
|
|
224
255
|
const sessionId = envelope.session_id;
|
|
225
256
|
const callId = invocationId(envelope);
|
|
226
257
|
if (!binding || typeof sessionId !== "string" || !sessionId || !callId) {
|
|
227
|
-
emitUnverified("repository binding or tool invocation identity is unavailable");
|
|
258
|
+
await emitUnverified("repository binding or tool invocation identity is unavailable", envelope);
|
|
228
259
|
return;
|
|
229
260
|
}
|
|
230
261
|
const request = {
|
|
@@ -243,12 +274,12 @@ async function main() {
|
|
|
243
274
|
try {
|
|
244
275
|
const response = await requestPreflight(request);
|
|
245
276
|
if (!response) {
|
|
246
|
-
emitUnverified("enforcement service returned an incompatible response");
|
|
277
|
+
await emitUnverified("enforcement service returned an incompatible response", envelope);
|
|
247
278
|
return;
|
|
248
279
|
}
|
|
249
280
|
result = resultForPreflight(response);
|
|
250
281
|
} catch {
|
|
251
|
-
emitUnverified("enforcement service unavailable; change was not verified");
|
|
282
|
+
await emitUnverified("enforcement service unavailable; change was not verified", envelope);
|
|
252
283
|
return;
|
|
253
284
|
}
|
|
254
285
|
const aggregate = demoteForMode(
|
|
@@ -259,10 +290,18 @@ async function main() {
|
|
|
259
290
|
process.stderr.write(`[primitive] ${result.reason || result.unavailable}
|
|
260
291
|
`);
|
|
261
292
|
}
|
|
262
|
-
|
|
263
|
-
|
|
264
|
-
|
|
293
|
+
let output = agent === "hermes" ? buildHermesOutput(aggregate, [result]) : agent === "codex" ? buildCodexOutput(aggregate, [result]) : buildHookOutput(aggregate, [result]);
|
|
294
|
+
if (agent === "codex" && hasVisibleCodexMessage(output) && typeof sessionId === "string" && sessionId.length > 0) {
|
|
295
|
+
try {
|
|
296
|
+
const context = await prepareCodexContext({ cwd, sessionId, includeDigest: false });
|
|
297
|
+
output = appendCodexContext(output, context.context);
|
|
298
|
+
await emitWithAcknowledgment(output, context.acknowledge);
|
|
299
|
+
return;
|
|
300
|
+
} catch {
|
|
301
|
+
}
|
|
302
|
+
}
|
|
303
|
+
await emit(output);
|
|
265
304
|
}
|
|
266
|
-
main().catch(() => {
|
|
267
|
-
emit(failOpen());
|
|
305
|
+
main().catch(async () => {
|
|
306
|
+
await emit(failOpen());
|
|
268
307
|
});
|
package/dist/hooks/prim-hook.js
CHANGED
|
@@ -8,7 +8,7 @@ import {
|
|
|
8
8
|
acknowledgeDecisionFeedback,
|
|
9
9
|
leaseDecisionFeedback,
|
|
10
10
|
renderFeedback
|
|
11
|
-
} from "../chunk-
|
|
11
|
+
} from "../chunk-U6L2RU67.js";
|
|
12
12
|
import {
|
|
13
13
|
enrichHookPayloadWithFileRefs,
|
|
14
14
|
preserveHookFileMetadata
|
|
@@ -21,32 +21,88 @@ import {
|
|
|
21
21
|
import {
|
|
22
22
|
appendMove,
|
|
23
23
|
resolveOrg
|
|
24
|
-
} from "../chunk-
|
|
24
|
+
} from "../chunk-UN5O5SYG.js";
|
|
25
25
|
import {
|
|
26
26
|
getOrCreateWorkspaceId
|
|
27
27
|
} from "../chunk-UVIWZWM6.js";
|
|
28
28
|
import {
|
|
29
29
|
scrubFromCwd
|
|
30
30
|
} from "../chunk-G5D3FF3D.js";
|
|
31
|
+
import {
|
|
32
|
+
prepareCodexContext
|
|
33
|
+
} from "../chunk-I5BHJQH2.js";
|
|
31
34
|
import {
|
|
32
35
|
warmBinCache
|
|
33
|
-
} from "../chunk-
|
|
36
|
+
} from "../chunk-K3AYW6VZ.js";
|
|
34
37
|
import {
|
|
35
38
|
isRepoActiveForCapture,
|
|
36
39
|
repoSyncId,
|
|
37
40
|
resolveRepositoryContext
|
|
38
41
|
} from "../chunk-TDRXHBEX.js";
|
|
39
|
-
import "../chunk-
|
|
42
|
+
import "../chunk-RTLFE6EZ.js";
|
|
40
43
|
import {
|
|
41
44
|
normalizeEnvelope,
|
|
42
45
|
parseAgent
|
|
43
46
|
} from "../chunk-TCDUH7AN.js";
|
|
47
|
+
import "../chunk-UTKQTZHL.js";
|
|
44
48
|
|
|
45
49
|
// src/hooks/prim-hook.ts
|
|
46
50
|
import { spawn } from "child_process";
|
|
47
51
|
import { readFileSync } from "fs";
|
|
48
52
|
import { dirname, join } from "path";
|
|
49
53
|
import { fileURLToPath } from "url";
|
|
54
|
+
|
|
55
|
+
// src/hooks/codex-message-context.ts
|
|
56
|
+
var defaultDeps = { prepare: prepareCodexContext };
|
|
57
|
+
var STOP_BACKSTOP_INSTRUCTION = "A new Primitive Decision arrived during this turn. Incorporate it into the active task and proactively tell the user about it before finishing.";
|
|
58
|
+
async function processCodexMessageContext(envelope, deps = defaultDeps) {
|
|
59
|
+
const event = envelope.hook_event_name;
|
|
60
|
+
if (event !== "UserPromptSubmit" && event !== "Stop") return { output: {} };
|
|
61
|
+
if (typeof envelope.session_id !== "string" || envelope.session_id.length === 0) {
|
|
62
|
+
return { output: {} };
|
|
63
|
+
}
|
|
64
|
+
if (event === "Stop" && envelope.stop_hook_active === true) return { output: {} };
|
|
65
|
+
let prepared;
|
|
66
|
+
try {
|
|
67
|
+
prepared = await deps.prepare({
|
|
68
|
+
cwd: typeof envelope.cwd === "string" ? envelope.cwd : process.cwd(),
|
|
69
|
+
sessionId: envelope.session_id
|
|
70
|
+
});
|
|
71
|
+
} catch {
|
|
72
|
+
return { output: {} };
|
|
73
|
+
}
|
|
74
|
+
if (event === "UserPromptSubmit") {
|
|
75
|
+
return {
|
|
76
|
+
output: prepared.context ? {
|
|
77
|
+
hookSpecificOutput: {
|
|
78
|
+
hookEventName: "UserPromptSubmit",
|
|
79
|
+
additionalContext: prepared.context
|
|
80
|
+
}
|
|
81
|
+
} : {},
|
|
82
|
+
// Even an empty prompt response is a successful handoff. Committing it
|
|
83
|
+
// advances verified feed state and report dedup without inventing output.
|
|
84
|
+
acknowledge: async () => {
|
|
85
|
+
await prepared.acknowledge(true);
|
|
86
|
+
}
|
|
87
|
+
};
|
|
88
|
+
}
|
|
89
|
+
if (!prepared.decisionDigest) {
|
|
90
|
+
return { output: {} };
|
|
91
|
+
}
|
|
92
|
+
return {
|
|
93
|
+
output: {
|
|
94
|
+
decision: "block",
|
|
95
|
+
reason: `${prepared.context ?? prepared.decisionDigest}
|
|
96
|
+
|
|
97
|
+
${STOP_BACKSTOP_INSTRUCTION}`
|
|
98
|
+
},
|
|
99
|
+
acknowledge: async () => {
|
|
100
|
+
await prepared.acknowledge(true);
|
|
101
|
+
}
|
|
102
|
+
};
|
|
103
|
+
}
|
|
104
|
+
|
|
105
|
+
// src/hooks/prim-hook.ts
|
|
50
106
|
var here = dirname(fileURLToPath(import.meta.url));
|
|
51
107
|
var outputAttempted = false;
|
|
52
108
|
function emitOutput(output, acknowledge) {
|
|
@@ -90,8 +146,9 @@ async function main() {
|
|
|
90
146
|
}
|
|
91
147
|
const cwd = parsed.cwd ?? process.cwd();
|
|
92
148
|
const isClaudeStop = agent === "claude_code" && parsed.hook_event_name === "Stop";
|
|
149
|
+
const isCodexContextEvent = agent === "codex" && (parsed.hook_event_name === "UserPromptSubmit" || parsed.hook_event_name === "Stop");
|
|
93
150
|
if (!isRepoActiveForCapture(cwd)) {
|
|
94
|
-
if (isClaudeStop) await emitOutput(buildHookOutput({}));
|
|
151
|
+
if (isClaudeStop || isCodexContextEvent) await emitOutput(buildHookOutput({}));
|
|
95
152
|
return;
|
|
96
153
|
}
|
|
97
154
|
const identity = getOrCreateWorkspaceId(cwd);
|
|
@@ -123,6 +180,11 @@ async function main() {
|
|
|
123
180
|
} catch (error) {
|
|
124
181
|
debug("capture", error);
|
|
125
182
|
}
|
|
183
|
+
if (isCodexContextEvent) {
|
|
184
|
+
const result = await processCodexMessageContext(parsed);
|
|
185
|
+
await emitOutput(result.output, result.acknowledge);
|
|
186
|
+
return;
|
|
187
|
+
}
|
|
126
188
|
if (!isClaudeStop) return;
|
|
127
189
|
const sessionId = typeof parsed.session_id === "string" ? parsed.session_id : "";
|
|
128
190
|
if (!workspaceId || !sessionId) {
|
|
@@ -1,11 +1,11 @@
|
|
|
1
1
|
#!/usr/bin/env node
|
|
2
|
-
import {
|
|
3
|
-
daemonRequest
|
|
4
|
-
} from "../chunk-UTKQTZHL.js";
|
|
5
2
|
import {
|
|
6
3
|
normalizeEnvelope,
|
|
7
4
|
parseAgent
|
|
8
5
|
} from "../chunk-TCDUH7AN.js";
|
|
6
|
+
import {
|
|
7
|
+
daemonRequest
|
|
8
|
+
} from "../chunk-UTKQTZHL.js";
|
|
9
9
|
|
|
10
10
|
// src/hooks/session-end.ts
|
|
11
11
|
var STDIN_TIMEOUT_MS = 1e3;
|
|
@@ -5,7 +5,7 @@ import {
|
|
|
5
5
|
ensureEffectivePostRewriteHook,
|
|
6
6
|
hasUsableCodexGuidance,
|
|
7
7
|
refreshClaudePlugins
|
|
8
|
-
} from "../chunk-
|
|
8
|
+
} from "../chunk-XPVNH3J6.js";
|
|
9
9
|
import {
|
|
10
10
|
buildHookOutput,
|
|
11
11
|
handoffHookOutput
|
|
@@ -15,14 +15,17 @@ import {
|
|
|
15
15
|
acknowledgeDecisionFeedback,
|
|
16
16
|
leaseDecisionFeedback,
|
|
17
17
|
renderFeedback
|
|
18
|
-
} from "../chunk-
|
|
18
|
+
} from "../chunk-U6L2RU67.js";
|
|
19
19
|
import {
|
|
20
20
|
getOrCreateWorkspaceId
|
|
21
21
|
} from "../chunk-UVIWZWM6.js";
|
|
22
|
+
import {
|
|
23
|
+
prepareCodexContext
|
|
24
|
+
} from "../chunk-I5BHJQH2.js";
|
|
22
25
|
import {
|
|
23
26
|
binFile,
|
|
24
27
|
warmBinCache
|
|
25
|
-
} from "../chunk-
|
|
28
|
+
} from "../chunk-K3AYW6VZ.js";
|
|
26
29
|
import {
|
|
27
30
|
gitToplevel,
|
|
28
31
|
isRepoActiveForCapture,
|
|
@@ -30,16 +33,15 @@ import {
|
|
|
30
33
|
setRepoActive
|
|
31
34
|
} from "../chunk-TDRXHBEX.js";
|
|
32
35
|
import {
|
|
33
|
-
getSiteUrl,
|
|
34
36
|
isSessionEnded
|
|
35
|
-
} from "../chunk-
|
|
36
|
-
import {
|
|
37
|
-
daemonRequest
|
|
38
|
-
} from "../chunk-UTKQTZHL.js";
|
|
37
|
+
} from "../chunk-RTLFE6EZ.js";
|
|
39
38
|
import {
|
|
40
39
|
normalizeEnvelope,
|
|
41
40
|
parseAgent
|
|
42
41
|
} from "../chunk-TCDUH7AN.js";
|
|
42
|
+
import {
|
|
43
|
+
daemonRequest
|
|
44
|
+
} from "../chunk-UTKQTZHL.js";
|
|
43
45
|
|
|
44
46
|
// src/daemon/self-heal.ts
|
|
45
47
|
import { spawn } from "child_process";
|
|
@@ -68,7 +70,6 @@ function kickDaemonEnsure(options = {}) {
|
|
|
68
70
|
// src/hooks/reauth-notice.ts
|
|
69
71
|
var REAUTH_NOTICE = "[prim] authentication ended \u2014 run `prim auth login` to resume decision capture";
|
|
70
72
|
function reauthNoticeFields(agent) {
|
|
71
|
-
if (agent === "codex") return { additionalContext: REAUTH_NOTICE };
|
|
72
73
|
if (agent === "claude_code") return { systemMessage: REAUTH_NOTICE };
|
|
73
74
|
return;
|
|
74
75
|
}
|
|
@@ -156,7 +157,7 @@ async function processSessionStart(raw, agent, feedbackSignal) {
|
|
|
156
157
|
} catch {
|
|
157
158
|
}
|
|
158
159
|
}
|
|
159
|
-
if (isSessionEnded()) {
|
|
160
|
+
if (isSessionEnded() && agent !== "codex") {
|
|
160
161
|
const notice = reauthNoticeFields(agent);
|
|
161
162
|
if (notice) {
|
|
162
163
|
return {
|
|
@@ -165,14 +166,16 @@ async function processSessionStart(raw, agent, feedbackSignal) {
|
|
|
165
166
|
}
|
|
166
167
|
}
|
|
167
168
|
if (agent === "codex") {
|
|
168
|
-
|
|
169
|
-
|
|
170
|
-
|
|
171
|
-
|
|
172
|
-
|
|
173
|
-
|
|
174
|
-
|
|
175
|
-
|
|
169
|
+
let context;
|
|
170
|
+
try {
|
|
171
|
+
context = await prepareCodexContext({
|
|
172
|
+
cwd,
|
|
173
|
+
sessionId: envelope.session_id,
|
|
174
|
+
startup: true,
|
|
175
|
+
includeDigest: false
|
|
176
|
+
});
|
|
177
|
+
} catch {
|
|
178
|
+
}
|
|
176
179
|
projectRoot = await activeProjectRoot(cwd);
|
|
177
180
|
active = projectRoot !== null;
|
|
178
181
|
let proactive = false;
|
|
@@ -180,8 +183,13 @@ async function processSessionStart(raw, agent, feedbackSignal) {
|
|
|
180
183
|
proactive = projectRoot !== null && active && hasUsableCodexGuidance(projectRoot);
|
|
181
184
|
} catch {
|
|
182
185
|
}
|
|
183
|
-
const additionalContext = [proactive ? CODEX_PRIM_REMINDER : void 0,
|
|
184
|
-
return {
|
|
186
|
+
const additionalContext = [proactive ? CODEX_PRIM_REMINDER : void 0, context?.context].filter((value) => value !== void 0).join("\n\n");
|
|
187
|
+
return {
|
|
188
|
+
output: buildHookOutput({ additionalContext }),
|
|
189
|
+
acknowledge: async () => {
|
|
190
|
+
await context?.acknowledge(true);
|
|
191
|
+
}
|
|
192
|
+
};
|
|
185
193
|
}
|
|
186
194
|
if (agent === "claude_code") {
|
|
187
195
|
const additionalContext = active && skillState.installed > 0 ? PRIM_SKILL_REMINDER : void 0;
|