@principles/codex-adapter 0.2.9 → 0.4.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.
|
@@ -77,9 +77,11 @@ export function decodeCodexInput(raw) {
|
|
|
77
77
|
if (kind === 'before_tool_call' || kind === 'after_tool_call') {
|
|
78
78
|
const toolName = requiredString(raw, 'tool_name');
|
|
79
79
|
const toolInput = requiredUnknown(raw, 'tool_input');
|
|
80
|
-
|
|
80
|
+
// PRI-750: Codex `tool_use_id` is required and host-authoritative — keep it
|
|
81
|
+
// on the shared context so the receipt chain can bind tools to turns.
|
|
82
|
+
const toolCallId = requiredString(raw, 'tool_use_id');
|
|
81
83
|
const toolOutput = kind === 'after_tool_call' ? requiredUnknown(raw, 'tool_response') : undefined;
|
|
82
|
-
context = { ...common, toolName, toolInput, ...(kind === 'after_tool_call' ? { toolOutput } : {}) };
|
|
84
|
+
context = { ...common, toolName, toolCallId, toolInput, ...(kind === 'after_tool_call' ? { toolOutput } : {}) };
|
|
83
85
|
rawPayload = { toolInput: { toolName, params: toolInput } };
|
|
84
86
|
}
|
|
85
87
|
else if (kind === 'before_prompt_build') {
|
package/dist/pd-hook.d.ts
CHANGED
|
@@ -5,5 +5,6 @@ export interface PdHookResult {
|
|
|
5
5
|
exitCode: number;
|
|
6
6
|
stderr: string[];
|
|
7
7
|
}
|
|
8
|
+
export declare function annotateContextWarnings(warnings: readonly string[]): string[];
|
|
8
9
|
export declare function processHookInvocation(rawStdin: string, _env?: EnvMap, cwd?: string): Promise<PdHookResult>;
|
|
9
10
|
export {};
|
package/dist/pd-hook.js
CHANGED
|
@@ -1,7 +1,9 @@
|
|
|
1
1
|
#!/usr/bin/env node
|
|
2
2
|
import { readFileSync } from 'node:fs';
|
|
3
|
+
import path from 'node:path';
|
|
3
4
|
import process from 'node:process';
|
|
4
5
|
import { pathToFileURL } from 'node:url';
|
|
6
|
+
import { appendEventLogLine, redactTelemetryString } from '@principles/core/runtime-v2';
|
|
5
7
|
import { createProductionHostRuntime, loadPdConfigForPlugin, resolveNearestPdWorkspace } from '@principles/host-runtime';
|
|
6
8
|
import { CODEX_TOOL_SEMANTICS } from './tool-semantics.js';
|
|
7
9
|
import { computeFeatureFlagsFromConfig } from '@principles/core/runtime-v2';
|
|
@@ -9,6 +11,40 @@ import { CodexHooksHostAdapter } from './host-adapter.js';
|
|
|
9
11
|
import { CodexDecoderError, CodexEncoderError } from './codec/index.js';
|
|
10
12
|
import { ingestCodexConversation } from './ingestion/ingestion.js';
|
|
11
13
|
import { runGovernanceAdmission } from './ingestion/admission.js';
|
|
14
|
+
/**
|
|
15
|
+
* PRI-750: event emitter for the Codex subprocess model. Writes the same
|
|
16
|
+
* `events_<date>.jsonl` line shape as the OpenClaw EventLog via the core
|
|
17
|
+
* writer, so the Codex host path stays independent of the OpenClaw plugin
|
|
18
|
+
* (codex-adapter must not depend on principles-disciple — bundle guard).
|
|
19
|
+
*/
|
|
20
|
+
function codexEventEmitter(stateDir) {
|
|
21
|
+
return {
|
|
22
|
+
recordRuntimeV2ActivationsInjected(data) {
|
|
23
|
+
appendEventLogLine(stateDir, {
|
|
24
|
+
ts: new Date().toISOString(),
|
|
25
|
+
type: 'runtime_v2_prompt_activations_injected',
|
|
26
|
+
category: 'injected',
|
|
27
|
+
sessionId: data.sessionId,
|
|
28
|
+
data,
|
|
29
|
+
});
|
|
30
|
+
},
|
|
31
|
+
recordToolCall(sessionId, data) {
|
|
32
|
+
// rc-8: tool events are telemetry — redact every string field before
|
|
33
|
+
// persisting (same policy as the OpenClaw EventLog redactEventData).
|
|
34
|
+
const redacted = {};
|
|
35
|
+
for (const [key, value] of Object.entries(data)) {
|
|
36
|
+
redacted[key] = typeof value === 'string' ? redactTelemetryString(value) : value;
|
|
37
|
+
}
|
|
38
|
+
appendEventLogLine(stateDir, {
|
|
39
|
+
ts: new Date().toISOString(),
|
|
40
|
+
type: 'tool_call',
|
|
41
|
+
category: data.error || (data.exitCode !== undefined && data.exitCode !== 0) ? 'failure' : 'success',
|
|
42
|
+
sessionId,
|
|
43
|
+
data: redacted,
|
|
44
|
+
});
|
|
45
|
+
},
|
|
46
|
+
};
|
|
47
|
+
}
|
|
12
48
|
const MAX_DIAGNOSTIC = 500;
|
|
13
49
|
function diagnostic(reason, nextAction) {
|
|
14
50
|
const boundedReason = reason.replace(/\s+/g, ' ').trim().slice(0, MAX_DIAGNOSTIC);
|
|
@@ -18,6 +54,26 @@ function diagnostic(reason, nextAction) {
|
|
|
18
54
|
function errorMessage(error) {
|
|
19
55
|
return error instanceof Error ? error.message.slice(0, MAX_DIAGNOSTIC) : 'unknown_error';
|
|
20
56
|
}
|
|
57
|
+
/**
|
|
58
|
+
* PRI-780 Codex capability declaration (structured UNSUPPORTED — suspension
|
|
59
|
+
* semantics, revised after Codex review round 2 P1): the Codex host has no
|
|
60
|
+
* runtime context provider, and the "unavailable → allow" contract is
|
|
61
|
+
* generation-time prompt discipline, NOT a runtime-enforced invariant. Handing
|
|
62
|
+
* v2 rules a truthy unavailable-posture context would let a persisted rule
|
|
63
|
+
* evaluate context-blind and DENY tool calls that were previously suspended —
|
|
64
|
+
* a silent governance behavior change. Codex therefore passes NO context
|
|
65
|
+
* provider: the shared gate skips v2 rules with its structured
|
|
66
|
+
* `rule_context_v2_unavailable` warning, and this hook annotates that warning
|
|
67
|
+
* with the explicit host-unsupported reason before it reaches Codex stderr
|
|
68
|
+
* (see annotateContextWarnings — ticket option B: 明确 unsupported + 结构化
|
|
69
|
+
* warning, never a silent skip).
|
|
70
|
+
*/
|
|
71
|
+
const CODEX_CONTEXT_UNSUPPORTED_NOTE = 'codex_runtime_context_unsupported: the Codex host provides no runtime context provider; v2 rules stay suspended on this host';
|
|
72
|
+
export function annotateContextWarnings(warnings) {
|
|
73
|
+
return warnings.map((warning) => warning.startsWith('rule_context_v2_unavailable')
|
|
74
|
+
? `${warning}; ${CODEX_CONTEXT_UNSUPPORTED_NOTE}`
|
|
75
|
+
: warning);
|
|
76
|
+
}
|
|
21
77
|
// Bounded governance-observation ingestion (Codex Governance Closure Slice
|
|
22
78
|
// A) followed by the Slice B signal-admission pass (SPEC §12/§13): detection
|
|
23
79
|
// → canonical pain → evidence promotion → one pending Diagnostician task.
|
|
@@ -118,8 +174,20 @@ export async function processHookInvocation(rawStdin, _env = process.env, cwd =
|
|
|
118
174
|
const ingestionDiagnostics = ingestionEnabled
|
|
119
175
|
? await runConversationIngestion({ rawPayload: parsed, kind: event.kind, workspaceDir: resolution.workspaceDir, env: _env })
|
|
120
176
|
: [];
|
|
121
|
-
|
|
122
|
-
|
|
177
|
+
// PRI-750: emit shared-path injection/tool events with the host's natural
|
|
178
|
+
// turn/tool ids (turn_id → runId, tool_use_id → toolCallId) through the core
|
|
179
|
+
// event-JSONL writer (same events_*.jsonl format as the OpenClaw EventLog;
|
|
180
|
+
// the Codex adapter stays independent of the OpenClaw plugin). The line
|
|
181
|
+
// writer appends synchronously — no flush/dispose needed for the subprocess.
|
|
182
|
+
const result = await createProductionHostRuntime({
|
|
183
|
+
hostKind: 'codex',
|
|
184
|
+
toolSemantics: CODEX_TOOL_SEMANTICS,
|
|
185
|
+
events: codexEventEmitter(path.join(resolution.workspaceDir, '.state')),
|
|
186
|
+
// PRI-780 (revised after Codex review round 2 P1): NO context provider —
|
|
187
|
+
// v2 rules stay SUSPENDED on Codex (never loaded context-blind). See
|
|
188
|
+
// annotateContextWarnings for the structured unsupported declaration.
|
|
189
|
+
}).dispatch(event);
|
|
190
|
+
const stderr = [...annotateContextWarnings(result.warnings ?? []).slice(0, 16).map((warning) => diagnostic(warning, 'Inspect PD Workspace state and retry; the hook failed open.')), ...ingestionDiagnostics];
|
|
123
191
|
return { stdout: adapter.encodeOutput(result, event.kind), exitCode: 0, stderr };
|
|
124
192
|
}
|
|
125
193
|
catch (error) {
|
|
@@ -213,6 +213,9 @@ export async function runCodexWorkspaceWorkerCycle(options) {
|
|
|
213
213
|
logger,
|
|
214
214
|
emitEvent,
|
|
215
215
|
toolSemantics: CODEX_TOOL_SEMANTICS,
|
|
216
|
+
// PRI-741: artificer generation gets the Codex host projection
|
|
217
|
+
// (Bash/apply_patch — evidence-bound, intentionally sparse).
|
|
218
|
+
hostKinds: ['codex'],
|
|
216
219
|
// No hostToolCatalog: PD has not declared a Codex tool catalog; a wrong
|
|
217
220
|
// (OpenClaw) catalog would be worse than none (PRI-630 follow-up).
|
|
218
221
|
});
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@principles/codex-adapter",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.4.0",
|
|
4
4
|
"description": "Codex CLI host adapter for Principles Disciple — implements HostAdapter interface for OpenAI Codex CLI's stdin/stdout JSON hook model (ADR-0020).",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"main": "./dist/index.js",
|