@probelabs/probe 0.6.0-rc331 → 0.6.0-rc334
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/bin/binaries/{probe-v0.6.0-rc331-aarch64-apple-darwin.tar.gz → probe-v0.6.0-rc334-aarch64-apple-darwin.tar.gz} +0 -0
- package/bin/binaries/{probe-v0.6.0-rc331-aarch64-unknown-linux-musl.tar.gz → probe-v0.6.0-rc334-aarch64-unknown-linux-musl.tar.gz} +0 -0
- package/bin/binaries/{probe-v0.6.0-rc331-x86_64-apple-darwin.tar.gz → probe-v0.6.0-rc334-x86_64-apple-darwin.tar.gz} +0 -0
- package/bin/binaries/{probe-v0.6.0-rc331-x86_64-pc-windows-msvc.zip → probe-v0.6.0-rc334-x86_64-pc-windows-msvc.zip} +0 -0
- package/bin/binaries/{probe-v0.6.0-rc331-x86_64-unknown-linux-musl.tar.gz → probe-v0.6.0-rc334-x86_64-unknown-linux-musl.tar.gz} +0 -0
- package/build/agent/ProbeAgent.d.ts +105 -4
- package/build/agent/ProbeAgent.js +209 -12
- package/build/agent/bashExecutor.js +36 -101
- package/build/agent/engines/codex.js +367 -88
- package/build/agent/engines/governed-answer-failure.js +152 -0
- package/build/agent/engines/governed-codex-profile.js +198 -0
- package/build/agent/governance/acknowledgedJsonlChannel.js +328 -0
- package/build/agent/governance/atomicTerminalReceipt.js +188 -0
- package/build/agent/governance/index.d.ts +130 -0
- package/build/agent/governance/index.js +8 -0
- package/build/agent/mcp/built-in-server.js +152 -53
- package/build/agent/mcp/index.d.ts +65 -0
- package/build/agent/mcp/index.js +6 -1
- package/build/agent/probeTool.js +1 -1
- package/build/agent/processSupervisor.js +351 -0
- package/build/agent/tools.js +14 -8
- package/build/index.js +2 -0
- package/build/utils/provider.js +9 -3
- package/cjs/agent/ProbeAgent.cjs +13463 -12187
- package/cjs/index.cjs +75974 -74139
- package/index.d.ts +149 -4
- package/package.json +6 -2
- package/src/agent/ProbeAgent.d.ts +105 -4
- package/src/agent/ProbeAgent.js +209 -12
- package/src/agent/bashExecutor.js +36 -101
- package/src/agent/engines/codex.js +367 -88
- package/src/agent/engines/governed-answer-failure.js +152 -0
- package/src/agent/engines/governed-codex-profile.js +198 -0
- package/src/agent/governance/acknowledgedJsonlChannel.js +328 -0
- package/src/agent/governance/atomicTerminalReceipt.js +188 -0
- package/src/agent/governance/index.d.ts +130 -0
- package/src/agent/governance/index.js +8 -0
- package/src/agent/mcp/built-in-server.js +152 -53
- package/src/agent/mcp/index.d.ts +65 -0
- package/src/agent/mcp/index.js +6 -1
- package/src/agent/probeTool.js +1 -1
- package/src/agent/processSupervisor.js +351 -0
- package/src/agent/tools.js +14 -8
- package/src/index.js +2 -0
- package/src/utils/provider.js +9 -3
|
@@ -4,16 +4,239 @@
|
|
|
4
4
|
*/
|
|
5
5
|
|
|
6
6
|
import { spawn } from 'child_process';
|
|
7
|
-
import { randomBytes } from 'crypto';
|
|
7
|
+
import { createHash, randomBytes } from 'crypto';
|
|
8
8
|
import { createInterface } from 'readline';
|
|
9
9
|
import { BuiltInMCPServer } from '../mcp/built-in-server.js';
|
|
10
|
+
import { governSpawnedProcess } from '../processSupervisor.js';
|
|
10
11
|
import { Session } from '../shared/Session.js';
|
|
12
|
+
import { attestGovernedCodexSession, buildGovernedCodexInitialToolArgs, validateGovernedCodexProfile } from './governed-codex-profile.js';
|
|
13
|
+
import { governedAnswerFailure, normalizeGovernedAnswerFailure } from './governed-answer-failure.js';
|
|
14
|
+
|
|
15
|
+
const GOVERNED_NATIVE_EVENT_LIMIT = 256;
|
|
16
|
+
const GOVERNED_SAFE_ID = /^[A-Za-z0-9._:-]{1,128}$/;
|
|
17
|
+
const GOVERNED_SAFE_KIND = /^[A-Za-z0-9._:-]{1,64}$/;
|
|
18
|
+
const GOVERNED_CODEX_NATIVE_CALLS = new Map([['exec', 'exec']]);
|
|
19
|
+
const GOVERNED_PROBE_MCP_CALLS = new Map([
|
|
20
|
+
['mcp__probe__search', 'search'], ['mcp__probe__extract', 'extract'], ['mcp__probe__listFiles', 'listFiles'],
|
|
21
|
+
]);
|
|
22
|
+
|
|
23
|
+
function governedRawItemInvalid() { throw governedAnswerFailure('native_event_grammar', 'raw_item_predicate'); }
|
|
24
|
+
function governedLiveEnvelopeInvalid(subreason, correlationOperand = null) {
|
|
25
|
+
throw governedAnswerFailure('native_event_grammar', 'live_envelope_session', subreason, correlationOperand);
|
|
26
|
+
}
|
|
27
|
+
function governedExactObject(value, keys, invalid = governedRawItemInvalid) {
|
|
28
|
+
if (value === null || typeof value !== 'object' || Array.isArray(value)) invalid();
|
|
29
|
+
const proto = Object.getPrototypeOf(value);
|
|
30
|
+
if (proto !== Object.prototype && proto !== null) invalid();
|
|
31
|
+
const actual = Reflect.ownKeys(value).filter((key) => Object.prototype.propertyIsEnumerable.call(value, key));
|
|
32
|
+
if (actual.some((key) => typeof key !== 'string') || actual.length !== keys.length || keys.some((key) => !actual.includes(key))) invalid();
|
|
33
|
+
for (const key of keys) if (!Object.prototype.hasOwnProperty.call(Object.getOwnPropertyDescriptor(value, key), 'value')) invalid();
|
|
34
|
+
return value;
|
|
35
|
+
}
|
|
36
|
+
function governedSafeId(value) { if (typeof value !== 'string' || !GOVERNED_SAFE_ID.test(value)) governedRawItemInvalid(); }
|
|
37
|
+
function governedProbeMcpCallCount(evidence) {
|
|
38
|
+
const invalid = () => { throw new TypeError('Invalid attester input'); };
|
|
39
|
+
const snapshot = governedExactObject(evidence, ['admitted', 'closed', 'overflow'], invalid);
|
|
40
|
+
if (!Object.isFrozen(snapshot) || !Number.isInteger(snapshot.admitted) || snapshot.admitted < 0 ||
|
|
41
|
+
snapshot.admitted > GOVERNED_NATIVE_EVENT_LIMIT || !Number.isInteger(snapshot.closed) ||
|
|
42
|
+
snapshot.closed < 0 || snapshot.closed > GOVERNED_NATIVE_EVENT_LIMIT ||
|
|
43
|
+
typeof snapshot.overflow !== 'boolean' || snapshot.admitted !== snapshot.closed || snapshot.overflow) invalid();
|
|
44
|
+
return snapshot.closed;
|
|
45
|
+
}
|
|
46
|
+
function governedPassthrough(value, message = false) {
|
|
47
|
+
const keys = Object.keys(value ?? {}).sort().join(',');
|
|
48
|
+
const legacy = keys === 'turn_id';
|
|
49
|
+
const current = keys === (message ? 'content_item_kinds,create_time,turn_id' : 'create_time,turn_id');
|
|
50
|
+
if (!legacy && !current) governedRawItemInvalid();
|
|
51
|
+
governedSafeId(value.turn_id);
|
|
52
|
+
if (current) {
|
|
53
|
+
if (typeof value.create_time !== 'number' || !Number.isFinite(value.create_time) ||
|
|
54
|
+
value.create_time < 0 || value.create_time > Number.MAX_SAFE_INTEGER) governedRawItemInvalid();
|
|
55
|
+
if (message) {
|
|
56
|
+
if (!Array.isArray(value.content_item_kinds) || value.content_item_kinds.length > 16) governedRawItemInvalid();
|
|
57
|
+
for (const kind of value.content_item_kinds) if (typeof kind !== 'string' || !GOVERNED_SAFE_KIND.test(kind)) governedRawItemInvalid();
|
|
58
|
+
}
|
|
59
|
+
}
|
|
60
|
+
}
|
|
61
|
+
function validateGovernedRawMessage(item) {
|
|
62
|
+
const assistant = item.role === 'assistant';
|
|
63
|
+
const keys = assistant
|
|
64
|
+
? ['type', 'id', 'role', 'content', 'phase', 'internal_chat_message_metadata_passthrough']
|
|
65
|
+
: Object.prototype.hasOwnProperty.call(item ?? {}, 'id')
|
|
66
|
+
? ['type', 'id', 'role', 'content', 'internal_chat_message_metadata_passthrough']
|
|
67
|
+
: ['type', 'role', 'content', 'internal_chat_message_metadata_passthrough'];
|
|
68
|
+
governedExactObject(item, keys);
|
|
69
|
+
if (item.type !== 'message' || !['developer', 'user', 'assistant'].includes(item.role)) governedRawItemInvalid();
|
|
70
|
+
if (Object.prototype.hasOwnProperty.call(item, 'id')) governedSafeId(item.id);
|
|
71
|
+
if (assistant && !['commentary', 'final_answer'].includes(item.phase)) governedRawItemInvalid();
|
|
72
|
+
if (!Array.isArray(item.content) || item.content.length < 1 || item.content.length > 64) governedRawItemInvalid();
|
|
73
|
+
for (const part of item.content) {
|
|
74
|
+
governedExactObject(part, ['type', 'text']);
|
|
75
|
+
const allowed = assistant ? part.type === 'output_text' : part.type === 'input_text';
|
|
76
|
+
if (!allowed || typeof part.text !== 'string' || Buffer.byteLength(part.text, 'utf8') > 131072) governedRawItemInvalid();
|
|
77
|
+
}
|
|
78
|
+
governedPassthrough(item.internal_chat_message_metadata_passthrough, true);
|
|
79
|
+
}
|
|
80
|
+
function validateGovernedRawReasoning(item) {
|
|
81
|
+
governedExactObject(item, ['type', 'id', 'summary', 'encrypted_content', 'internal_chat_message_metadata_passthrough']);
|
|
82
|
+
if (item.type !== 'reasoning') governedRawItemInvalid(); governedSafeId(item.id);
|
|
83
|
+
if (!Array.isArray(item.summary) || item.summary.length !== 0 || typeof item.encrypted_content !== 'string' || Buffer.byteLength(item.encrypted_content, 'utf8') > 1048576) governedRawItemInvalid();
|
|
84
|
+
governedPassthrough(item.internal_chat_message_metadata_passthrough);
|
|
85
|
+
}
|
|
86
|
+
function createGovernedNativeCollector(profile) {
|
|
87
|
+
let sessionEvent = null, requestId = null, threadId = null, nativeCallCount = 0;
|
|
88
|
+
let relevantEventCount = 0, totalCallCount = 0, rawResponseItemCount = 0, assistantMessageCount = 0, finalAnswerCount = 0;
|
|
89
|
+
const rawIds = new Set(), callOrigins = new Map(), outputIds = new Set();
|
|
90
|
+
function observe(event) {
|
|
91
|
+
const type = event?.params?.msg?.type;
|
|
92
|
+
if (type === 'session_configured') {
|
|
93
|
+
if (sessionEvent) governedLiveEnvelopeInvalid('session_sequence');
|
|
94
|
+
sessionEvent = event;
|
|
95
|
+
requestId = event.params?._meta?.requestId;
|
|
96
|
+
threadId = event.params?._meta?.threadId;
|
|
97
|
+
return;
|
|
98
|
+
}
|
|
99
|
+
if (profile.version !== 'probe.governed-codex-profile/v2' || type !== 'raw_response_item') return;
|
|
100
|
+
if (!sessionEvent) governedLiveEnvelopeInvalid('session_sequence');
|
|
101
|
+
governedExactObject(event, ['jsonrpc', 'method', 'params'], () => governedLiveEnvelopeInvalid('envelope_shape'));
|
|
102
|
+
if (event.jsonrpc !== '2.0' || event.method !== 'codex/event') governedLiveEnvelopeInvalid('envelope_shape');
|
|
103
|
+
const params = governedExactObject(event.params, ['_meta', 'msg', 'id'], () => governedLiveEnvelopeInvalid('envelope_shape'));
|
|
104
|
+
const meta = governedExactObject(params._meta, ['requestId', 'threadId'], () => governedLiveEnvelopeInvalid('envelope_shape'));
|
|
105
|
+
if (meta.requestId !== requestId) governedLiveEnvelopeInvalid('correlation');
|
|
106
|
+
if (meta.threadId !== threadId) governedLiveEnvelopeInvalid('correlation', 'thread_id');
|
|
107
|
+
if (typeof params.id !== 'string') governedLiveEnvelopeInvalid('envelope_shape');
|
|
108
|
+
const msg = governedExactObject(params.msg, ['type', 'item'], () => governedLiveEnvelopeInvalid('envelope_shape'));
|
|
109
|
+
if (msg.type !== 'raw_response_item') governedLiveEnvelopeInvalid('envelope_shape');
|
|
110
|
+
const item = msg.item;
|
|
111
|
+
if (++rawResponseItemCount > GOVERNED_NATIVE_EVENT_LIMIT) governedRawItemInvalid();
|
|
112
|
+
if (item?.type === 'message') {
|
|
113
|
+
validateGovernedRawMessage(item);
|
|
114
|
+
if (Object.prototype.hasOwnProperty.call(item, 'id')) {
|
|
115
|
+
if (rawIds.has(item.id)) governedRawItemInvalid(); rawIds.add(item.id);
|
|
116
|
+
}
|
|
117
|
+
if (item.role === 'assistant') { assistantMessageCount++; if (item.phase === 'final_answer') finalAnswerCount++; }
|
|
118
|
+
return;
|
|
119
|
+
}
|
|
120
|
+
if (item?.type === 'reasoning') {
|
|
121
|
+
validateGovernedRawReasoning(item);
|
|
122
|
+
if (rawIds.has(item.id)) governedRawItemInvalid(); rawIds.add(item.id);
|
|
123
|
+
return;
|
|
124
|
+
}
|
|
125
|
+
if (item?.type === 'custom_tool_call') {
|
|
126
|
+
governedExactObject(item, ['type', 'id', 'status', 'call_id', 'name', 'input', 'internal_chat_message_metadata_passthrough']);
|
|
127
|
+
governedSafeId(item.id); governedSafeId(item.call_id);
|
|
128
|
+
if (rawIds.has(item.id) || callOrigins.has(item.call_id)) governedRawItemInvalid();
|
|
129
|
+
const nativeName = GOVERNED_CODEX_NATIVE_CALLS.get(item.name);
|
|
130
|
+
const probeMcpName = GOVERNED_PROBE_MCP_CALLS.get(item.name);
|
|
131
|
+
if ((nativeName !== undefined) === (probeMcpName !== undefined)) governedRawItemInvalid();
|
|
132
|
+
if (nativeName !== undefined && !profile.codexNativeTools.includes(nativeName)) governedRawItemInvalid();
|
|
133
|
+
if (probeMcpName !== undefined && !profile.probeMcpTools.includes(probeMcpName)) governedRawItemInvalid();
|
|
134
|
+
if (item.status !== 'completed' || typeof item.input !== 'string' || Buffer.byteLength(item.input, 'utf8') > 131072) governedRawItemInvalid();
|
|
135
|
+
governedPassthrough(item.internal_chat_message_metadata_passthrough);
|
|
136
|
+
if (++relevantEventCount > GOVERNED_NATIVE_EVENT_LIMIT || ++totalCallCount > GOVERNED_NATIVE_EVENT_LIMIT) governedRawItemInvalid();
|
|
137
|
+
const origin = nativeName !== undefined ? 'codex-native' : 'probe-mcp';
|
|
138
|
+
rawIds.add(item.id); callOrigins.set(item.call_id, origin);
|
|
139
|
+
if (origin === 'codex-native') nativeCallCount++;
|
|
140
|
+
return;
|
|
141
|
+
}
|
|
142
|
+
if (item?.type === 'custom_tool_call_output') {
|
|
143
|
+
const hasId = Object.prototype.hasOwnProperty.call(item, 'id');
|
|
144
|
+
governedExactObject(item, hasId
|
|
145
|
+
? ['type', 'id', 'call_id', 'output', 'internal_chat_message_metadata_passthrough']
|
|
146
|
+
: ['type', 'call_id', 'output', 'internal_chat_message_metadata_passthrough']);
|
|
147
|
+
if (hasId) { governedSafeId(item.id); if (rawIds.has(item.id)) governedRawItemInvalid(); }
|
|
148
|
+
governedSafeId(item.call_id);
|
|
149
|
+
if (!callOrigins.has(item.call_id) || outputIds.has(item.call_id) || !Array.isArray(item.output) || item.output.length > 64) governedRawItemInvalid();
|
|
150
|
+
for (const part of item.output) {
|
|
151
|
+
governedExactObject(part, ['type', 'text']);
|
|
152
|
+
if (part.type !== 'input_text' || typeof part.text !== 'string' || Buffer.byteLength(part.text, 'utf8') > 1048576) governedRawItemInvalid();
|
|
153
|
+
}
|
|
154
|
+
governedPassthrough(item.internal_chat_message_metadata_passthrough);
|
|
155
|
+
if (++relevantEventCount > GOVERNED_NATIVE_EVENT_LIMIT) governedRawItemInvalid();
|
|
156
|
+
if (hasId) rawIds.add(item.id);
|
|
157
|
+
outputIds.add(item.call_id);
|
|
158
|
+
return;
|
|
159
|
+
}
|
|
160
|
+
governedRawItemInvalid();
|
|
161
|
+
}
|
|
162
|
+
function evidence() {
|
|
163
|
+
if (!sessionEvent) governedLiveEnvelopeInvalid('session_sequence');
|
|
164
|
+
if (assistantMessageCount > 0 && finalAnswerCount !== 1) governedRawItemInvalid();
|
|
165
|
+
const tools = nativeCallCount === 0 ? [] : [{ name: 'exec', status: 'completed', count: nativeCallCount }];
|
|
166
|
+
return { sessionEvent, capabilities: { nativeTools: { total: nativeCallCount, tools } } };
|
|
167
|
+
}
|
|
168
|
+
return { observe, evidence };
|
|
169
|
+
}
|
|
170
|
+
|
|
171
|
+
function externalReceipt(attestation, capabilityCounts) {
|
|
172
|
+
if (attestation.version === 'probe.governed-codex-attestation/v3') return externalV3Receipt(attestation, {}, capabilityCounts);
|
|
173
|
+
return {
|
|
174
|
+
version: attestation.version, profileId: attestation.profileId,
|
|
175
|
+
requested: { profileDigest: attestation.requested.profileDigest, cwdDigest: attestation.requested.cwdDigest, probeToolsDigest: attestation.requested.probeToolsDigest, model: attestation.requested.model, reasoningEffort: attestation.requested.reasoningEffort, sandbox: attestation.requested.sandbox, approvalPolicy: attestation.requested.approvalPolicy },
|
|
176
|
+
observed: { source: attestation.observed.source, model: attestation.observed.model, modelProviderId: attestation.observed.modelProviderId, reasoningEffort: attestation.observed.reasoningEffort, approvalPolicy: attestation.observed.approvalPolicy, cwdDigest: attestation.observed.cwdDigest, permissionProfileDigest: attestation.observed.permissionProfileDigest, filesystem: attestation.observed.filesystem, network: attestation.observed.network },
|
|
177
|
+
evidence: { eventCount: 1 }, usage: { status: 'unavailable' },
|
|
178
|
+
};
|
|
179
|
+
}
|
|
180
|
+
|
|
181
|
+
function externalV3Receipt(attestation, extra, capabilityCounts) {
|
|
182
|
+
return {
|
|
183
|
+
version: attestation.version, profileId: attestation.profileId,
|
|
184
|
+
requested: { profileDigest: attestation.requested.profileDigest, cwdDigest: attestation.requested.cwdDigest,
|
|
185
|
+
probeMcpToolsDigest: attestation.requested.probeMcpToolsDigest, codexNativeToolsDigest: attestation.requested.codexNativeToolsDigest,
|
|
186
|
+
probeMcpTools: [...attestation.requested.probeMcpTools], codexNativeTools: [...attestation.requested.codexNativeTools],
|
|
187
|
+
model: attestation.requested.model, reasoningEffort: attestation.requested.reasoningEffort,
|
|
188
|
+
sandbox: attestation.requested.sandbox, approvalPolicy: attestation.requested.approvalPolicy },
|
|
189
|
+
observed: { source: attestation.observed.source, model: attestation.observed.model,
|
|
190
|
+
modelProviderId: attestation.observed.modelProviderId, reasoningEffort: attestation.observed.reasoningEffort,
|
|
191
|
+
approvalPolicy: attestation.observed.approvalPolicy, cwdDigest: attestation.observed.cwdDigest,
|
|
192
|
+
permissionProfileDigest: attestation.observed.permissionProfileDigest, filesystem: attestation.observed.filesystem,
|
|
193
|
+
network: attestation.observed.network, nativeTools: { total: attestation.observed.nativeTools.total,
|
|
194
|
+
tools: attestation.observed.nativeTools.tools.map((item) => ({ ...item })) } },
|
|
195
|
+
...extra, evidence: { sessionEventCount: 1, nativeCallCount: capabilityCounts.nativeCallCount,
|
|
196
|
+
probeMcpCallCount: capabilityCounts.probeMcpCallCount }, usage: { status: 'unavailable' },
|
|
197
|
+
};
|
|
198
|
+
}
|
|
199
|
+
|
|
200
|
+
export function governedCodexDispatch(prompt) {
|
|
201
|
+
const promptBytes = Buffer.byteLength(prompt, 'utf8');
|
|
202
|
+
const byteLength = Buffer.alloc(8);
|
|
203
|
+
byteLength.writeBigUInt64BE(BigInt(promptBytes));
|
|
204
|
+
const promptDigest = `sha256:${createHash('sha256')
|
|
205
|
+
.update('probe.governed-codex-dispatch/prompt/v1', 'utf8')
|
|
206
|
+
.update(Buffer.from([0])).update(byteLength).update(prompt, 'utf8').digest('hex')}`;
|
|
207
|
+
return Object.freeze({ source: 'probe-host-tools-call', tool: 'codex', promptDigest, promptBytes });
|
|
208
|
+
}
|
|
209
|
+
|
|
210
|
+
export function composeCodexInitialPrompt({ systemPrompt, customPrompt, prompt }) {
|
|
211
|
+
const fullPrompt = combinePrompts(systemPrompt, customPrompt);
|
|
212
|
+
return fullPrompt ? `${fullPrompt}\n\n${prompt}` : prompt;
|
|
213
|
+
}
|
|
214
|
+
|
|
215
|
+
export function previewGovernedCodexInitialDispatch(input) {
|
|
216
|
+
return governedCodexDispatch(composeCodexInitialPrompt(input));
|
|
217
|
+
}
|
|
218
|
+
|
|
219
|
+
function externalBoundReceipt(internal, dispatch, invocationDigest, capabilityCounts) {
|
|
220
|
+
if (internal.version === 'probe.governed-codex-attestation/v3') return externalV3Receipt(internal, {
|
|
221
|
+
executionContext: { source: 'caller', invocationDigest },
|
|
222
|
+
dispatch: { source: dispatch.source, tool: dispatch.tool, promptDigest: dispatch.promptDigest, promptBytes: dispatch.promptBytes },
|
|
223
|
+
}, capabilityCounts);
|
|
224
|
+
return {
|
|
225
|
+
version: 'probe.governed-codex-attestation/v2', profileId: 'luna-xhigh-readonly-v1',
|
|
226
|
+
requested: { profileDigest: internal.requested.profileDigest, cwdDigest: internal.requested.cwdDigest, probeToolsDigest: internal.requested.probeToolsDigest, model: internal.requested.model, reasoningEffort: internal.requested.reasoningEffort, sandbox: internal.requested.sandbox, approvalPolicy: internal.requested.approvalPolicy },
|
|
227
|
+
observed: { source: internal.observed.source, model: internal.observed.model, modelProviderId: internal.observed.modelProviderId, reasoningEffort: internal.observed.reasoningEffort, approvalPolicy: internal.observed.approvalPolicy, cwdDigest: internal.observed.cwdDigest, permissionProfileDigest: internal.observed.permissionProfileDigest, filesystem: internal.observed.filesystem, network: internal.observed.network },
|
|
228
|
+
executionContext: { source: 'caller', invocationDigest },
|
|
229
|
+
dispatch: { source: dispatch.source, tool: dispatch.tool, promptDigest: dispatch.promptDigest, promptBytes: dispatch.promptBytes },
|
|
230
|
+
evidence: { eventCount: 1 }, usage: { status: 'unavailable' },
|
|
231
|
+
};
|
|
232
|
+
}
|
|
11
233
|
|
|
12
234
|
/**
|
|
13
235
|
* Codex Engine using MCP Server with event streaming
|
|
14
236
|
*/
|
|
15
237
|
export async function createCodexEngine(options = {}) {
|
|
16
238
|
const { agent, systemPrompt, customPrompt, debug, sessionId, allowedTools, model } = options;
|
|
239
|
+
const governedProfile = options.governedCodexProfile === undefined ? null : validateGovernedCodexProfile(options.governedCodexProfile);
|
|
17
240
|
|
|
18
241
|
const session = new Session(
|
|
19
242
|
sessionId || randomBytes(8).toString('hex'),
|
|
@@ -29,7 +252,9 @@ export async function createCodexEngine(options = {}) {
|
|
|
29
252
|
mcpServer = new BuiltInMCPServer(agent, {
|
|
30
253
|
port: 0,
|
|
31
254
|
host: '127.0.0.1',
|
|
32
|
-
debug: debug
|
|
255
|
+
debug: debug,
|
|
256
|
+
...(governedProfile?.version === 'probe.governed-codex-profile/v2'
|
|
257
|
+
? { governedProfileVersion: governedProfile.version } : {})
|
|
33
258
|
});
|
|
34
259
|
|
|
35
260
|
const { host, port } = await mcpServer.start();
|
|
@@ -48,13 +273,21 @@ export async function createCodexEngine(options = {}) {
|
|
|
48
273
|
}
|
|
49
274
|
|
|
50
275
|
const codexProcess = spawn('codex', ['mcp-server'], {
|
|
51
|
-
stdio: ['pipe', 'pipe', 'pipe']
|
|
276
|
+
stdio: ['pipe', 'pipe', 'pipe'],
|
|
277
|
+
detached: true
|
|
278
|
+
});
|
|
279
|
+
const governedProcess = governSpawnedProcess(codexProcess, {
|
|
280
|
+
captureStdout: false,
|
|
281
|
+
signalScope: 'process-group',
|
|
282
|
+
stdoutByteCap: 0
|
|
52
283
|
});
|
|
53
284
|
|
|
54
285
|
// Setup JSON-RPC communication
|
|
55
286
|
let requestId = 0;
|
|
56
287
|
const pendingRequests = new Map();
|
|
57
288
|
const eventHandlers = new Map();
|
|
289
|
+
const governedEvidenceHandlers = new Map();
|
|
290
|
+
let governedQueryStarted = false, closePromise = null;
|
|
58
291
|
|
|
59
292
|
// Read stdout line by line
|
|
60
293
|
const stdoutReader = createInterface({
|
|
@@ -74,8 +307,9 @@ export async function createCodexEngine(options = {}) {
|
|
|
74
307
|
|
|
75
308
|
// Handle responses to our requests
|
|
76
309
|
if (message.id !== undefined && pendingRequests.has(message.id)) {
|
|
77
|
-
const { resolve, reject } = pendingRequests.get(message.id);
|
|
310
|
+
const { resolve, reject, timer } = pendingRequests.get(message.id);
|
|
78
311
|
pendingRequests.delete(message.id);
|
|
312
|
+
clearTimeout(timer);
|
|
79
313
|
|
|
80
314
|
if (message.error) {
|
|
81
315
|
reject(new Error(message.error.message || JSON.stringify(message.error)));
|
|
@@ -87,6 +321,9 @@ export async function createCodexEngine(options = {}) {
|
|
|
87
321
|
// Handle notifications (codex/event)
|
|
88
322
|
if (message.method === 'codex/event' && message.params) {
|
|
89
323
|
const requestId = message.params._meta?.requestId;
|
|
324
|
+
if (requestId !== undefined && governedEvidenceHandlers.has(requestId)) {
|
|
325
|
+
governedEvidenceHandlers.get(requestId)(message);
|
|
326
|
+
}
|
|
90
327
|
if (requestId !== undefined && eventHandlers.has(requestId)) {
|
|
91
328
|
eventHandlers.get(requestId)(message.params);
|
|
92
329
|
}
|
|
@@ -98,6 +335,8 @@ export async function createCodexEngine(options = {}) {
|
|
|
98
335
|
}
|
|
99
336
|
});
|
|
100
337
|
|
|
338
|
+
codexProcess.once('error', (error) => rejectPending(error)); codexProcess.once('close', () => rejectPending(new Error('Codex process closed')));
|
|
339
|
+
|
|
101
340
|
// Handle stderr
|
|
102
341
|
if (debug) {
|
|
103
342
|
codexProcess.stderr.on('data', (data) => {
|
|
@@ -108,6 +347,7 @@ export async function createCodexEngine(options = {}) {
|
|
|
108
347
|
// Send JSON-RPC request
|
|
109
348
|
function sendRequest(method, params = {}) {
|
|
110
349
|
return new Promise((resolve, reject) => {
|
|
350
|
+
if (closePromise) return reject(new Error('Codex engine is closed'));
|
|
111
351
|
const id = ++requestId;
|
|
112
352
|
const request = {
|
|
113
353
|
jsonrpc: '2.0',
|
|
@@ -116,37 +356,48 @@ export async function createCodexEngine(options = {}) {
|
|
|
116
356
|
params
|
|
117
357
|
};
|
|
118
358
|
|
|
119
|
-
pendingRequests.set(id, { resolve, reject });
|
|
120
|
-
|
|
121
359
|
// Timeout after 10 minutes
|
|
122
|
-
setTimeout(() => {
|
|
360
|
+
const timer = setTimeout(() => {
|
|
123
361
|
if (pendingRequests.has(id)) {
|
|
124
362
|
pendingRequests.delete(id);
|
|
125
363
|
reject(new Error(`Request ${method} timed out after 10 minutes`));
|
|
126
364
|
}
|
|
127
365
|
}, 600000);
|
|
366
|
+
pendingRequests.set(id, { resolve, reject, timer });
|
|
128
367
|
|
|
129
368
|
codexProcess.stdin.write(JSON.stringify(request) + '\n');
|
|
130
369
|
});
|
|
131
370
|
}
|
|
132
371
|
|
|
372
|
+
function rejectPending(error) { for (const { reject, timer } of pendingRequests.values()) { clearTimeout(timer); reject(error); } pendingRequests.clear(); }
|
|
373
|
+
|
|
374
|
+
async function cleanup(reason = new Error('Codex engine closed')) {
|
|
375
|
+
if (closePromise) return closePromise;
|
|
376
|
+
closePromise = (async () => {
|
|
377
|
+
rejectPending(reason); eventHandlers.clear(); governedEvidenceHandlers.clear();
|
|
378
|
+
stdoutReader.close(); codexProcess.stdin.destroy();
|
|
379
|
+
const receipt = await governedProcess.terminate('codex_engine_closed');
|
|
380
|
+
const processCleanupFailed = receipt.classification === 'cleanup_timeout' ||
|
|
381
|
+
!receipt.barriers.close || !receipt.barriers.stdoutEOF || !receipt.barriers.stderrEOF;
|
|
382
|
+
if (mcpServer) await mcpServer.stop();
|
|
383
|
+
if (processCleanupFailed) throw new Error('Codex process cleanup failed');
|
|
384
|
+
})();
|
|
385
|
+
return closePromise;
|
|
386
|
+
}
|
|
387
|
+
|
|
133
388
|
// Initialize MCP connection
|
|
134
|
-
|
|
135
|
-
|
|
136
|
-
|
|
137
|
-
|
|
138
|
-
|
|
139
|
-
|
|
140
|
-
}
|
|
141
|
-
});
|
|
389
|
+
try {
|
|
390
|
+
await sendRequest('initialize', {
|
|
391
|
+
protocolVersion: '2024-11-05', capabilities: { tools: {} },
|
|
392
|
+
clientInfo: { name: 'probe-codex-client', version: '1.0.0' }
|
|
393
|
+
});
|
|
394
|
+
} catch (error) { await cleanup(error); throw error; }
|
|
142
395
|
|
|
143
396
|
if (debug) {
|
|
144
397
|
console.log('[DEBUG] Connected to Codex MCP server');
|
|
145
398
|
console.log('[DEBUG] Session:', session.id);
|
|
146
399
|
}
|
|
147
400
|
|
|
148
|
-
const fullPrompt = combinePrompts(systemPrompt, customPrompt, agent);
|
|
149
|
-
|
|
150
401
|
return {
|
|
151
402
|
sessionId: session.id,
|
|
152
403
|
session,
|
|
@@ -157,15 +408,24 @@ export async function createCodexEngine(options = {}) {
|
|
|
157
408
|
async *query(prompt, opts = {}) {
|
|
158
409
|
// Build prompt
|
|
159
410
|
let finalPrompt = prompt;
|
|
160
|
-
if (!session.conversationId
|
|
161
|
-
finalPrompt =
|
|
411
|
+
if (!session.conversationId) {
|
|
412
|
+
finalPrompt = composeCodexInitialPrompt({ systemPrompt, customPrompt, prompt });
|
|
162
413
|
}
|
|
163
414
|
|
|
164
415
|
const isFollowUp = session.conversationId !== null;
|
|
165
416
|
const toolName = isFollowUp ? 'codex-reply' : 'codex';
|
|
417
|
+
let abortHandler, queryError = null;
|
|
166
418
|
|
|
167
|
-
|
|
168
|
-
|
|
419
|
+
try {
|
|
420
|
+
const hasInvocationDigest = Object.prototype.hasOwnProperty.call(opts,
|
|
421
|
+
'invocationDigest');
|
|
422
|
+
const invocationDigest = hasInvocationDigest ? opts.invocationDigest : undefined;
|
|
423
|
+
if (hasInvocationDigest && (typeof invocationDigest !== 'string' || !/^sha256:[0-9a-f]{64}$/.test(invocationDigest))) {
|
|
424
|
+
throw new TypeError('answerGoverned invocationDigest must match sha256:<64 lowercase hexadecimal digits>');
|
|
425
|
+
}
|
|
426
|
+
|
|
427
|
+
// Build arguments
|
|
428
|
+
let toolArgs = { prompt: finalPrompt };
|
|
169
429
|
|
|
170
430
|
if (isFollowUp) {
|
|
171
431
|
toolArgs.conversationId = session.conversationId;
|
|
@@ -173,10 +433,14 @@ export async function createCodexEngine(options = {}) {
|
|
|
173
433
|
console.log(`[DEBUG] Follow-up with conversationId: ${session.conversationId}`);
|
|
174
434
|
}
|
|
175
435
|
} else {
|
|
176
|
-
if (
|
|
436
|
+
if (governedProfile) {
|
|
437
|
+
if (governedQueryStarted) throw new Error('Governed Codex engine permits one initial query');
|
|
438
|
+
governedQueryStarted = true;
|
|
439
|
+
toolArgs = buildGovernedCodexInitialToolArgs({ profile: governedProfile, prompt: finalPrompt, mcp: { name: mcpServerName, url: mcpServerUrl } });
|
|
440
|
+
} else if (model) {
|
|
177
441
|
toolArgs.model = model;
|
|
178
442
|
}
|
|
179
|
-
if (mcpServerUrl && mcpServerName) {
|
|
443
|
+
if (!governedProfile && mcpServerUrl && mcpServerName) {
|
|
180
444
|
toolArgs.config = {
|
|
181
445
|
mcp_servers: {
|
|
182
446
|
[mcpServerName]: { url: mcpServerUrl }
|
|
@@ -188,53 +452,91 @@ export async function createCodexEngine(options = {}) {
|
|
|
188
452
|
}
|
|
189
453
|
}
|
|
190
454
|
|
|
191
|
-
try {
|
|
192
455
|
const reqId = requestId + 1;
|
|
193
456
|
let fullResponse = '';
|
|
194
457
|
let gotSessionId = false;
|
|
458
|
+
const collector = governedProfile ? createGovernedNativeCollector(governedProfile) : null;
|
|
459
|
+
let evidenceFailure = null;
|
|
460
|
+
if (governedProfile) governedEvidenceHandlers.set(reqId, (event) => {
|
|
461
|
+
try { collector.observe(event); } catch (error) { evidenceFailure ??= normalizeGovernedAnswerFailure(error, 'native_event_grammar'); }
|
|
462
|
+
});
|
|
463
|
+
if (opts.abortSignal) {
|
|
464
|
+
if (opts.abortSignal.aborted) throw new Error('Codex query cancelled');
|
|
465
|
+
abortHandler = () => { void cleanup(new Error('Codex query cancelled')).catch(() => {}); };
|
|
466
|
+
opts.abortSignal.addEventListener('abort', abortHandler, { once: true });
|
|
467
|
+
}
|
|
195
468
|
|
|
196
469
|
// Register event handler for this request
|
|
197
|
-
|
|
198
|
-
|
|
199
|
-
const msg = eventParams.msg;
|
|
200
|
-
|
|
201
|
-
// Extract session_id from session_configured event
|
|
202
|
-
if (msg.type === 'session_configured' && msg.session_id && !gotSessionId) {
|
|
203
|
-
session.setConversationId(msg.session_id);
|
|
204
|
-
gotSessionId = true;
|
|
205
|
-
}
|
|
470
|
+
eventHandlers.set(reqId, (eventParams) => {
|
|
471
|
+
const msg = eventParams.msg;
|
|
206
472
|
|
|
207
|
-
|
|
208
|
-
|
|
209
|
-
|
|
210
|
-
|
|
211
|
-
|
|
212
|
-
|
|
213
|
-
|
|
214
|
-
|
|
473
|
+
// Extract session_id from session_configured event
|
|
474
|
+
if (!governedProfile && msg.type === 'session_configured' && msg.session_id && !gotSessionId) {
|
|
475
|
+
session.setConversationId(msg.session_id);
|
|
476
|
+
gotSessionId = true;
|
|
477
|
+
}
|
|
478
|
+
|
|
479
|
+
// Collect agent messages
|
|
480
|
+
if (msg.type === 'raw_response_item' && msg.item?.role === 'assistant') {
|
|
481
|
+
const content = msg.item.content;
|
|
482
|
+
if (Array.isArray(content)) {
|
|
483
|
+
for (const part of content) {
|
|
484
|
+
if (part.type === 'text' && part.text) {
|
|
485
|
+
fullResponse += part.text;
|
|
215
486
|
}
|
|
216
487
|
}
|
|
217
488
|
}
|
|
218
|
-
}
|
|
219
|
-
|
|
220
|
-
// Mark as resolved when we're done
|
|
221
|
-
setTimeout(() => {
|
|
222
|
-
eventHandlers.delete(reqId);
|
|
223
|
-
resolve();
|
|
224
|
-
}, 600000); // 10 min timeout
|
|
489
|
+
}
|
|
225
490
|
});
|
|
226
491
|
|
|
227
492
|
// Call the tool
|
|
493
|
+
const dispatch = governedProfile ? governedCodexDispatch(toolArgs.prompt) : null;
|
|
228
494
|
const resultPromise = sendRequest('tools/call', {
|
|
229
495
|
name: toolName,
|
|
230
496
|
arguments: toolArgs
|
|
231
497
|
});
|
|
232
498
|
|
|
233
499
|
// Wait for result
|
|
234
|
-
|
|
500
|
+
let result;
|
|
501
|
+
try { result = await resultPromise; }
|
|
502
|
+
catch (error) {
|
|
503
|
+
throw governedProfile
|
|
504
|
+
? governedAnswerFailure(evidenceFailure ? 'unknown' : 'provider_engine')
|
|
505
|
+
: error;
|
|
506
|
+
}
|
|
235
507
|
|
|
236
508
|
// Clean up event handler
|
|
237
509
|
eventHandlers.delete(reqId);
|
|
510
|
+
governedEvidenceHandlers.delete(reqId);
|
|
511
|
+
let attestation = null;
|
|
512
|
+
if (governedProfile) {
|
|
513
|
+
if (evidenceFailure) throw evidenceFailure;
|
|
514
|
+
let collected, internal, probeMcpCallCount;
|
|
515
|
+
try {
|
|
516
|
+
collected = collector.evidence();
|
|
517
|
+
} catch (error) {
|
|
518
|
+
throw normalizeGovernedAnswerFailure(error, 'native_event_grammar', 'live_envelope_session');
|
|
519
|
+
}
|
|
520
|
+
try {
|
|
521
|
+
internal = attestGovernedCodexSession({ profile: governedProfile,
|
|
522
|
+
events: governedProfile.version === 'probe.governed-codex-profile/v2'
|
|
523
|
+
? [collected.sessionEvent, collected.capabilities.nativeTools] : [collected.sessionEvent] });
|
|
524
|
+
probeMcpCallCount = governedProfile.version === 'probe.governed-codex-profile/v2'
|
|
525
|
+
? governedProbeMcpCallCount(mcpServer?.getGovernedCallEvidence()) : 0;
|
|
526
|
+
} catch (error) {
|
|
527
|
+
throw normalizeGovernedAnswerFailure(error, 'native_event_grammar', 'live_envelope_session', 'attestation');
|
|
528
|
+
}
|
|
529
|
+
attestation = hasInvocationDigest
|
|
530
|
+
? externalBoundReceipt(internal, dispatch, invocationDigest, {
|
|
531
|
+
nativeCallCount: collected.capabilities.nativeTools.total,
|
|
532
|
+
probeMcpCallCount,
|
|
533
|
+
})
|
|
534
|
+
: externalReceipt(internal, {
|
|
535
|
+
nativeCallCount: collected.capabilities.nativeTools.total,
|
|
536
|
+
probeMcpCallCount,
|
|
537
|
+
});
|
|
538
|
+
session.setConversationId(collected.sessionEvent.params.msg.session_id);
|
|
539
|
+
}
|
|
238
540
|
|
|
239
541
|
// Parse result
|
|
240
542
|
if (result && result.content && Array.isArray(result.content)) {
|
|
@@ -257,11 +559,16 @@ export async function createCodexEngine(options = {}) {
|
|
|
257
559
|
};
|
|
258
560
|
}
|
|
259
561
|
|
|
562
|
+
if (governedProfile?.version === 'probe.governed-codex-profile/v2') {
|
|
563
|
+
yield { type: 'toolBatch', total: attestation.observed.nativeTools.total,
|
|
564
|
+
tools: attestation.observed.nativeTools.tools.map((item) => ({ ...item })) };
|
|
565
|
+
}
|
|
566
|
+
|
|
260
567
|
session.incrementMessageCount();
|
|
261
568
|
|
|
262
569
|
yield {
|
|
263
570
|
type: 'metadata',
|
|
264
|
-
data: {
|
|
571
|
+
data: governedProfile ? { attestation } : {
|
|
265
572
|
sessionId: session.id,
|
|
266
573
|
conversationId: session.conversationId,
|
|
267
574
|
messageCount: session.messageCount
|
|
@@ -269,6 +576,8 @@ export async function createCodexEngine(options = {}) {
|
|
|
269
576
|
};
|
|
270
577
|
|
|
271
578
|
} catch (error) {
|
|
579
|
+
if (governedProfile) error = normalizeGovernedAnswerFailure(error, 'unknown');
|
|
580
|
+
queryError = error;
|
|
272
581
|
if (debug) {
|
|
273
582
|
console.error('[DEBUG] Codex query error:', error);
|
|
274
583
|
}
|
|
@@ -276,6 +585,12 @@ export async function createCodexEngine(options = {}) {
|
|
|
276
585
|
type: 'error',
|
|
277
586
|
error: error
|
|
278
587
|
};
|
|
588
|
+
} finally {
|
|
589
|
+
if (opts.abortSignal && abortHandler) opts.abortSignal.removeEventListener('abort', abortHandler);
|
|
590
|
+
if (governedProfile) {
|
|
591
|
+
try { await cleanup(); }
|
|
592
|
+
catch (cleanupError) { if (!queryError) throw cleanupError; }
|
|
593
|
+
}
|
|
279
594
|
}
|
|
280
595
|
},
|
|
281
596
|
|
|
@@ -290,43 +605,7 @@ export async function createCodexEngine(options = {}) {
|
|
|
290
605
|
* Clean up resources
|
|
291
606
|
*/
|
|
292
607
|
async close() {
|
|
293
|
-
|
|
294
|
-
// Close readline interface first to remove event listeners
|
|
295
|
-
if (stdoutReader) {
|
|
296
|
-
stdoutReader.close();
|
|
297
|
-
if (debug) {
|
|
298
|
-
console.log('[DEBUG] Closed stdout reader');
|
|
299
|
-
}
|
|
300
|
-
}
|
|
301
|
-
|
|
302
|
-
// Clear all pending requests and event handlers
|
|
303
|
-
pendingRequests.clear();
|
|
304
|
-
eventHandlers.clear();
|
|
305
|
-
|
|
306
|
-
// Kill Codex process
|
|
307
|
-
if (codexProcess && !codexProcess.killed) {
|
|
308
|
-
codexProcess.kill();
|
|
309
|
-
if (debug) {
|
|
310
|
-
console.log('[DEBUG] Killed Codex MCP server process');
|
|
311
|
-
}
|
|
312
|
-
}
|
|
313
|
-
|
|
314
|
-
// Stop Probe MCP server
|
|
315
|
-
if (mcpServer) {
|
|
316
|
-
await mcpServer.stop();
|
|
317
|
-
if (debug) {
|
|
318
|
-
console.log('[DEBUG] Stopped Probe MCP server');
|
|
319
|
-
}
|
|
320
|
-
}
|
|
321
|
-
|
|
322
|
-
if (debug) {
|
|
323
|
-
console.log('[DEBUG] Engine closed, session:', session.id);
|
|
324
|
-
}
|
|
325
|
-
} catch (error) {
|
|
326
|
-
if (debug) {
|
|
327
|
-
console.error('[DEBUG] Error during cleanup:', error.message);
|
|
328
|
-
}
|
|
329
|
-
}
|
|
608
|
+
await cleanup();
|
|
330
609
|
}
|
|
331
610
|
};
|
|
332
611
|
}
|
|
@@ -334,7 +613,7 @@ export async function createCodexEngine(options = {}) {
|
|
|
334
613
|
/**
|
|
335
614
|
* Combine prompts intelligently
|
|
336
615
|
*/
|
|
337
|
-
function combinePrompts(systemPrompt, customPrompt
|
|
616
|
+
function combinePrompts(systemPrompt, customPrompt) {
|
|
338
617
|
if (!systemPrompt && customPrompt) {
|
|
339
618
|
return customPrompt;
|
|
340
619
|
}
|