@skill-harness/adapters 0.16.0 → 0.18.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/dist/index.d.ts +0 -28
- package/dist/index.js +0 -28
- package/dist/pi.js +21 -133
- package/package.json +2 -3
- package/dist/archive-access.d.ts +0 -58
- package/dist/archive-access.js +0 -61
- package/dist/archive-checkpoint.d.ts +0 -47
- package/dist/archive-checkpoint.js +0 -116
- package/dist/archive-facts.d.ts +0 -19
- package/dist/archive-facts.js +0 -20
- package/dist/archive-observer.d.ts +0 -21
- package/dist/archive-observer.js +0 -79
- package/dist/archive-policy.d.ts +0 -107
- package/dist/archive-policy.js +0 -135
- package/dist/archive-read-capability.d.ts +0 -49
- package/dist/archive-read-capability.js +0 -45
- package/dist/archive-retention-policy.d.ts +0 -52
- package/dist/archive-retention-policy.js +0 -49
- package/dist/archived-work.d.ts +0 -45
- package/dist/archived-work.js +0 -78
- package/dist/blind-intervention.d.ts +0 -35
- package/dist/blind-intervention.js +0 -218
- package/dist/codex-diagnostic-model.d.ts +0 -14
- package/dist/codex-diagnostic-model.js +0 -39
- package/dist/codex-host-observer.d.ts +0 -111
- package/dist/codex-host-observer.js +0 -309
- package/dist/codex-https.d.ts +0 -7
- package/dist/codex-https.js +0 -221
- package/dist/codex-model-profile.d.ts +0 -38
- package/dist/codex-model-profile.js +0 -54
- package/dist/codex-producer-ipc.d.ts +0 -55
- package/dist/codex-producer-ipc.js +0 -67
- package/dist/codex-sdk-transport.d.ts +0 -50
- package/dist/codex-sdk-transport.js +0 -294
- package/dist/codex-subscription.d.ts +0 -114
- package/dist/codex-subscription.js +0 -268
- package/dist/evidence-archive.d.ts +0 -65
- package/dist/evidence-archive.js +0 -225
- package/dist/execution-projection-schema.d.ts +0 -10
- package/dist/execution-projection-schema.js +0 -30
- package/dist/execution-retention-archive.d.ts +0 -69
- package/dist/execution-retention-archive.js +0 -154
- package/dist/generated/retention-v2-contract.d.ts +0 -72
- package/dist/generated/retention-v2-contract.js +0 -105
- package/dist/generated/retention-v2-json.d.ts +0 -24
- package/dist/generated/retention-v2-json.js +0 -268
- package/dist/generated/retention-v2-native-json.d.ts +0 -2
- package/dist/generated/retention-v2-native-json.js +0 -33
- package/dist/generated/retention-v2-native.d.ts +0 -58
- package/dist/generated/retention-v2-native.js +0 -155
- package/dist/generated/retention-v2-pin.d.ts +0 -2
- package/dist/generated/retention-v2-pin.js +0 -4
- package/dist/generated/retention-v2-types.d.ts +0 -49
- package/dist/generated/retention-v2-types.js +0 -2
- package/dist/generated/retention-v2-work-types.d.ts +0 -260
- package/dist/generated/retention-v2-work-types.js +0 -13
- package/dist/generated/work-v4/execution-id.d.ts +0 -5
- package/dist/generated/work-v4/execution-id.js +0 -14
- package/dist/generated/work-v4/json.d.ts +0 -24
- package/dist/generated/work-v4/json.js +0 -268
- package/dist/generated/work-v4/occurrences.d.ts +0 -226
- package/dist/generated/work-v4/occurrences.js +0 -116
- package/dist/generated/work-v4/pin.d.ts +0 -1
- package/dist/generated/work-v4/pin.js +0 -3
- package/dist/generated/work-v4/projection.d.ts +0 -294
- package/dist/generated/work-v4/projection.js +0 -238
- package/dist/generated/work-v4/reader.d.ts +0 -24
- package/dist/generated/work-v4/reader.js +0 -109
- package/dist/generated/work-v4/snapshot.d.ts +0 -28
- package/dist/generated/work-v4/snapshot.js +0 -268
- package/dist/generated/work-v4/types.d.ts +0 -260
- package/dist/generated/work-v4/types.js +0 -13
- package/dist/generated/work-v4/validation.d.ts +0 -13
- package/dist/generated/work-v4/validation.js +0 -227
- package/dist/installed-review-session.d.ts +0 -28
- package/dist/installed-review-session.js +0 -166
- package/dist/intervention-run.d.ts +0 -403
- package/dist/intervention-run.js +0 -169
- package/dist/learning-case.d.ts +0 -12
- package/dist/learning-case.js +0 -15
- package/dist/learning-journal.d.ts +0 -18
- package/dist/learning-journal.js +0 -256
- package/dist/learning-lifecycle.d.ts +0 -28
- package/dist/learning-lifecycle.js +0 -66
- package/dist/learning-trust-setup.d.ts +0 -64
- package/dist/learning-trust-setup.js +0 -55
- package/dist/learning-workspace.d.ts +0 -930
- package/dist/learning-workspace.js +0 -663
- package/dist/principal-payload-port.d.ts +0 -25
- package/dist/principal-payload-port.js +0 -40
- package/dist/producer-product.d.ts +0 -70
- package/dist/producer-product.js +0 -186
- package/dist/producer-review.d.ts +0 -53
- package/dist/producer-review.js +0 -207
- package/dist/prompt-capture-extension.d.ts +0 -10
- package/dist/prompt-capture-extension.js +0 -26
- package/dist/prompt-provenance.d.ts +0 -22
- package/dist/prompt-provenance.js +0 -146
- package/dist/reviewed-archive-export.d.ts +0 -42
- package/dist/reviewed-archive-export.js +0 -78
- package/dist/trust-lifecycle.d.ts +0 -152
- package/dist/trust-lifecycle.js +0 -209
- package/dist/weekly-investigation.d.ts +0 -237
- package/dist/weekly-investigation.js +0 -330
- package/dist/work-candidates.d.ts +0 -29
- package/dist/work-candidates.js +0 -78
- package/dist/work-case-archive.d.ts +0 -25
- package/dist/work-case-archive.js +0 -63
- package/dist/work-case-review.d.ts +0 -45
- package/dist/work-case-review.js +0 -220
- package/dist/work-signal-cases.d.ts +0 -15
- package/dist/work-signal-cases.js +0 -54
- package/dist/work-signal-observation.d.ts +0 -23
- package/dist/work-signal-observation.js +0 -89
|
@@ -1,54 +0,0 @@
|
|
|
1
|
-
import { spawn } from 'node:child_process';
|
|
2
|
-
import { createHash } from 'node:crypto';
|
|
3
|
-
import { lstatSync, readFileSync, realpathSync, readlinkSync } from 'node:fs';
|
|
4
|
-
/** Separate from linux-bwrap-digest-v1: this proves only a fixed IPC probe workload.
|
|
5
|
-
* It MUST NOT be used to run arbitrary model/extension code. A model-bearing profile
|
|
6
|
-
* additionally needs aggregate host/descendant memory/CPU/egress limits and token
|
|
7
|
-
* semantics; these are deliberately reported unqualified rather than bypassed. */
|
|
8
|
-
export const CODEX_LOCAL_PROFILE = Object.freeze({ id: 'linux-bwrap-codex-ipc-probe-v1', network: 'unshared-no-egress', credentials: 'none', cpuSeconds: 2, nofile: 64, heapMiB: 32, wallMs: 3000, outputBytes: 4096, arbitraryCode: false, modelExecutionQualified: false });
|
|
9
|
-
const CODE = `const fs=require('node:fs');let rootReadOnly=false;try{fs.writeFileSync('/usr/.codex-profile-write-probe','denied',{flag:'wx'});}catch(e){rootReadOnly=e.code==='EROFS';}const id=process.argv[1];process.stdout.write(JSON.stringify({frame:JSON.stringify({id,sequence:1})+'\\n',netNamespace:fs.readlinkSync('/proc/self/ns/net'),envKeys:Object.keys(process.env),pwd:process.env.PWD,rootReadOnly}));`;
|
|
10
|
-
export async function probeLocalCodexProfile(id) {
|
|
11
|
-
if (typeof id !== 'string' || !/^[a-zA-Z0-9_-]{1,64}$/.test(id))
|
|
12
|
-
throw Error('bounded invocation identity required');
|
|
13
|
-
if (process.platform !== 'linux')
|
|
14
|
-
throw Error('Linux namespaces required; no fallback');
|
|
15
|
-
const parentNet = readlinkSync('/proc/self/ns/net'), node = realpathSync(process.execPath), fingerprints = {};
|
|
16
|
-
for (const p of [node, '/usr/bin/bwrap', '/usr/bin/prlimit']) {
|
|
17
|
-
const s = lstatSync(p);
|
|
18
|
-
if (!s.isFile() || (s.mode & 0o022) || s.size > 256 * 1024 * 1024)
|
|
19
|
-
throw Error('untrusted runtime executable');
|
|
20
|
-
fingerprints[p] = createHash('sha256').update(readFileSync(p)).digest('hex');
|
|
21
|
-
}
|
|
22
|
-
const args = ['--unshare-all', '--die-with-parent', '--new-session', '--cap-drop', 'ALL', '--ro-bind', '/usr', '/usr', '--ro-bind', '/lib', '/lib', '--ro-bind', '/lib64', '/lib64', '--dir', '/runtime', '--ro-bind', node, '/runtime/node', '--proc', '/proc', '--dev', '/dev', '--clearenv', '--chdir', '/', '--', '/usr/bin/prlimit', '--cpu=2:2', '--nofile=64:64', '--', '/runtime/node', '--max-old-space-size=32', '-e', CODE, id];
|
|
23
|
-
const proof = await new Promise((resolve, reject) => {
|
|
24
|
-
const child = spawn('/usr/bin/bwrap', args, { env: {}, stdio: ['ignore', 'pipe', 'pipe'] });
|
|
25
|
-
let bytes = 0, failed;
|
|
26
|
-
const chunks = [];
|
|
27
|
-
// The original ChildProcess handle is retained. No discovery/PID/title cancellation.
|
|
28
|
-
const fail = (e) => { failed ??= e; child.kill('SIGKILL'); };
|
|
29
|
-
const timer = setTimeout(() => fail(Error('local profile deadline')), CODEX_LOCAL_PROFILE.wallMs);
|
|
30
|
-
child.stdout.on('data', (b) => { bytes += b.length; if (bytes > CODEX_LOCAL_PROFILE.outputBytes)
|
|
31
|
-
fail(Error('local profile output bound'));
|
|
32
|
-
else
|
|
33
|
-
chunks.push(Buffer.from(b)); });
|
|
34
|
-
child.stderr.on('data', (b) => { bytes += b.length; if (bytes > CODEX_LOCAL_PROFILE.outputBytes)
|
|
35
|
-
fail(Error('local profile output bound')); });
|
|
36
|
-
child.once('error', e => { failed = e; });
|
|
37
|
-
child.once('close', (code, signal) => { clearTimeout(timer); if (failed) {
|
|
38
|
-
reject(failed);
|
|
39
|
-
return;
|
|
40
|
-
} if (code !== 0 || signal) {
|
|
41
|
-
reject(Error('local namespace probe failed; prerequisite NOT qualified'));
|
|
42
|
-
return;
|
|
43
|
-
} try {
|
|
44
|
-
resolve(JSON.parse(Buffer.concat(chunks).toString('utf8')));
|
|
45
|
-
}
|
|
46
|
-
catch {
|
|
47
|
-
reject(Error('malformed profile proof'));
|
|
48
|
-
} });
|
|
49
|
-
});
|
|
50
|
-
if (proof.frame !== JSON.stringify({ id, sequence: 1 }) + '\n' || proof.rootReadOnly !== true || !Array.isArray(proof.envKeys) || proof.envKeys.join() !== 'PWD' || proof.pwd !== '/' || (typeof proof.netNamespace !== 'string' || proof.netNamespace === parentNet))
|
|
51
|
-
throw Error('local profile invariant failed: ' + JSON.stringify(proof));
|
|
52
|
-
return { ...proof, profile: CODEX_LOCAL_PROFILE, fingerprints, modelExecutionQualified: false, remaining: ['aggregate model/host/descendant resource qualification', 'host-only subscription credential and egress boundary', 'provider output-token cap semantics'] };
|
|
53
|
-
}
|
|
54
|
-
//# sourceMappingURL=codex-model-profile.js.map
|
|
@@ -1,55 +0,0 @@
|
|
|
1
|
-
import type { Readable } from 'node:stream';
|
|
2
|
-
import type { CodexSdkBinding, CodexWireTransport } from './codex-sdk-transport.js';
|
|
3
|
-
export interface CodexProducerBinding {
|
|
4
|
-
version: string;
|
|
5
|
-
budgetDigest: string;
|
|
6
|
-
orderId: string;
|
|
7
|
-
experimentId: string;
|
|
8
|
-
executionId: string;
|
|
9
|
-
charterSha256: string;
|
|
10
|
-
invocationId: string;
|
|
11
|
-
}
|
|
12
|
-
/** These are trusted producer-module capabilities, not caller-provided source attestation.
|
|
13
|
-
* The launcher must pin the actual producer module; this adapter never loads credentials. */
|
|
14
|
-
export interface CodexProducerApi {
|
|
15
|
-
createProducerIpcHost(input: {
|
|
16
|
-
owner: unknown;
|
|
17
|
-
binding: CodexProducerBinding;
|
|
18
|
-
exchange: (frames: Readable, context: {
|
|
19
|
-
binding: CodexProducerBinding;
|
|
20
|
-
signal: AbortSignal;
|
|
21
|
-
}) => Promise<{
|
|
22
|
-
claimRef: string;
|
|
23
|
-
responseRef: string;
|
|
24
|
-
}>;
|
|
25
|
-
}): unknown;
|
|
26
|
-
startProducerIpc(input: {
|
|
27
|
-
owner: unknown;
|
|
28
|
-
permit: unknown;
|
|
29
|
-
binding: CodexProducerBinding;
|
|
30
|
-
host: unknown;
|
|
31
|
-
signal: AbortSignal;
|
|
32
|
-
timeoutMs: number;
|
|
33
|
-
}): Promise<{
|
|
34
|
-
completion: Promise<any>;
|
|
35
|
-
result: Promise<any>;
|
|
36
|
-
inspect(): any;
|
|
37
|
-
[key: string]: unknown;
|
|
38
|
-
}>;
|
|
39
|
-
}
|
|
40
|
-
export declare function startCodexProducerIpc(input: {
|
|
41
|
-
path: string;
|
|
42
|
-
binding: CodexProducerBinding;
|
|
43
|
-
sdk: CodexSdkBinding;
|
|
44
|
-
transport: CodexWireTransport;
|
|
45
|
-
producer: CodexProducerApi;
|
|
46
|
-
owner: unknown;
|
|
47
|
-
permit: unknown;
|
|
48
|
-
signal: AbortSignal;
|
|
49
|
-
timeoutMs: number;
|
|
50
|
-
}): Promise<Readonly<{
|
|
51
|
-
completion: Promise<any>;
|
|
52
|
-
result: Promise<any>;
|
|
53
|
-
inspect(): any;
|
|
54
|
-
}>>;
|
|
55
|
-
export declare function validateCodexProducerBinding(raw: CodexProducerBinding): CodexProducerBinding;
|
|
@@ -1,67 +0,0 @@
|
|
|
1
|
-
import { createHash } from 'node:crypto';
|
|
2
|
-
import { learningCopy, learningHash, learningJournal } from './learning-journal.js';
|
|
3
|
-
import { openLocalCodexHost, inspectCodexRoleSeparation } from './codex-host-observer.js';
|
|
4
|
-
export async function startCodexProducerIpc(input) {
|
|
5
|
-
const binding = validateCodexProducerBinding(input.binding);
|
|
6
|
-
return startValidatedProducer(input, binding);
|
|
7
|
-
}
|
|
8
|
-
export function validateCodexProducerBinding(raw) {
|
|
9
|
-
const binding = learningCopy(raw), keys = ['version', 'budgetDigest', 'orderId', 'experimentId', 'executionId', 'charterSha256', 'invocationId'];
|
|
10
|
-
if (!binding || Object.keys(binding).sort().join() !== keys.sort().join() || binding.version !== 'producer-ipc-v1' || ![binding.budgetDigest, binding.charterSha256].every(h => /^[a-f0-9]{64}$/.test(h)) || ![binding.orderId, binding.experimentId, binding.executionId, binding.invocationId].every(v => typeof v === 'string' && /^[a-zA-Z0-9:_-]{1,128}$/.test(v)))
|
|
11
|
-
throw Error('closed producer source binding required');
|
|
12
|
-
return binding;
|
|
13
|
-
}
|
|
14
|
-
async function startValidatedProducer(input, binding) {
|
|
15
|
-
const store = learningJournal(input.path), rows = store.read(), first = rows[0].value, host = openLocalCodexHost(input.path);
|
|
16
|
-
const invocation = first.spec.invocations.find((i) => i.id === binding.invocationId), reservation = first.subscription, model = input.sdk.model;
|
|
17
|
-
if (!reservation || reservation.charterSha256 !== binding.charterSha256 || input.transport.kind !== (reservation.mode === 'fixture' ? 'fixture-http' : 'subscription-http') || !first.rolePolicy || inspectCodexRoleSeparation(first.rolePolicy).state === 'BLOCKED' || !invocation || model.id !== invocation.model || model.provider !== 'openai-codex' || model.api !== 'openai-codex-responses' || model.baseUrl !== 'https://chatgpt.com/backend-api' || model.headers && Object.keys(model.headers).length || typeof input.sdk.stream !== 'function')
|
|
18
|
-
throw Error('unbound producer/host charter or SDK policy');
|
|
19
|
-
if (!(input.signal instanceof AbortSignal) || !Number.isSafeInteger(input.timeoutMs) || input.timeoutMs < 50 || input.timeoutMs > 30000)
|
|
20
|
-
throw Error('bounded original source signal/deadline required');
|
|
21
|
-
if (host.inspect().aborted || rows.some(e => e.value.id === binding.invocationId && ['claim', 'producer-ipc-bound'].includes(String(e.value.type))))
|
|
22
|
-
throw Error('source already bound/claimed or aborted; no retry');
|
|
23
|
-
const bindingHash = learningHash(binding), append = (value) => store.append(store.read().at(-1).id, value);
|
|
24
|
-
let invoked = false, references = null;
|
|
25
|
-
const port = input.producer.createProducerIpcHost({ owner: input.owner, binding, exchange: async (frames, context) => {
|
|
26
|
-
if (invoked) {
|
|
27
|
-
frames.destroy();
|
|
28
|
-
throw Error('repeated producer exchange refused');
|
|
29
|
-
}
|
|
30
|
-
invoked = true;
|
|
31
|
-
if (learningHash(context.binding) !== bindingHash || !(context.signal instanceof AbortSignal)) {
|
|
32
|
-
frames.destroy();
|
|
33
|
-
throw Error('original source context mismatch');
|
|
34
|
-
}
|
|
35
|
-
const observation = await host.exchangeSubscriptionSdk(frames, input.sdk, input.transport, binding.charterSha256, context.signal);
|
|
36
|
-
const records = store.read(), claim = records.find(e => e.value.type === 'claim' && e.value.id === observation.id), response = records.find(e => e.value.type === 'observation' && e.value.id === observation.id);
|
|
37
|
-
if (!claim || !response)
|
|
38
|
-
throw Error('host observation references missing');
|
|
39
|
-
references = { claimRef: claim.id, responseRef: response.id };
|
|
40
|
-
return { ...references };
|
|
41
|
-
} });
|
|
42
|
-
append({ type: 'producer-ipc-bound', id: binding.invocationId, bindingHash, binding, liveQualified: false });
|
|
43
|
-
let run;
|
|
44
|
-
try {
|
|
45
|
-
run = await input.producer.startProducerIpc({ owner: input.owner, permit: input.permit, binding, host: port, signal: input.signal, timeoutMs: input.timeoutMs });
|
|
46
|
-
}
|
|
47
|
-
catch {
|
|
48
|
-
append({ type: 'abort', reason: 'producer source start failed; no retry', id: binding.invocationId });
|
|
49
|
-
throw Error('producer source start failed');
|
|
50
|
-
}
|
|
51
|
-
const completion = run.completion.then(snapshot => {
|
|
52
|
-
const frameSha256 = createHash('sha256').update(JSON.stringify({ id: binding.invocationId, sequence: 1 }) + '\n').digest('hex');
|
|
53
|
-
const acknowledged = invoked && references !== null && learningHash(snapshot.binding) === bindingHash && snapshot.outcome === 'completed' && snapshot.settlement === 'acknowledged' && snapshot.childState === 'settled' && snapshot.hostState === 'acknowledged' && snapshot.frameSha256 === frameSha256 && snapshot.acceptance === 'not-assessed' && learningHash(snapshot.references) === learningHash(references);
|
|
54
|
-
if (acknowledged)
|
|
55
|
-
append({ type: 'producer-ipc-settled', id: binding.invocationId, bindingHash, references, frameSha256, liveQualified: false, acceptance: 'not-assessed' });
|
|
56
|
-
else {
|
|
57
|
-
append({ type: 'abort', reason: 'producer source did not acknowledge completed ownership', id: binding.invocationId });
|
|
58
|
-
if (snapshot.outcome === 'completed')
|
|
59
|
-
throw Error('producer source completion mismatch');
|
|
60
|
-
}
|
|
61
|
-
return snapshot;
|
|
62
|
-
}, () => { append({ type: 'abort', reason: 'producer source completion rejected', id: binding.invocationId }); throw Error('producer source completion rejected'); });
|
|
63
|
-
// Preserve original bounded result/child/readiness/inspect handles. Only original completion
|
|
64
|
-
// can unlock host eligibility; an observer timeout never substitutes for that completion.
|
|
65
|
-
return Object.freeze({ ...run, completion });
|
|
66
|
-
}
|
|
67
|
-
//# sourceMappingURL=codex-producer-ipc.js.map
|
|
@@ -1,50 +0,0 @@
|
|
|
1
|
-
import { PassThrough, Writable } from 'node:stream';
|
|
2
|
-
/** Trusted installed SDK dependency, NOT an extension/caller capability. No auth loader or
|
|
3
|
-
* network implementation is imported here. The sole credential is deliberately invalid.
|
|
4
|
-
* Inert execution additionally requires the documented network-unshared, no-home mount. */
|
|
5
|
-
export interface CodexSdkBinding {
|
|
6
|
-
model: {
|
|
7
|
-
id: string;
|
|
8
|
-
provider: string;
|
|
9
|
-
api: string;
|
|
10
|
-
baseUrl: string;
|
|
11
|
-
[key: string]: unknown;
|
|
12
|
-
};
|
|
13
|
-
stream: (model: any, context: any, options: any) => {
|
|
14
|
-
result(): Promise<any>;
|
|
15
|
-
};
|
|
16
|
-
}
|
|
17
|
-
/** Public result is final text only; reasoning metadata never leaves the private validator. */
|
|
18
|
-
export declare function validateCodexTextSse(bytes: Buffer, model: string): {
|
|
19
|
-
text: string;
|
|
20
|
-
responseId: string;
|
|
21
|
-
};
|
|
22
|
-
export interface CodexWire {
|
|
23
|
-
destination: string;
|
|
24
|
-
method: 'POST';
|
|
25
|
-
body: Buffer;
|
|
26
|
-
encoding: string | null;
|
|
27
|
-
}
|
|
28
|
-
export interface CodexWireTransport {
|
|
29
|
-
kind: 'fixture-http' | 'subscription-http';
|
|
30
|
-
exchange(wire: CodexWire, signal: AbortSignal, record: (value: Record<string, unknown>) => void): Promise<Response>;
|
|
31
|
-
}
|
|
32
|
-
export declare function inertCodexSdkStreams(binding: CodexSdkBinding, fixture: () => Promise<Response>, record: (value: Record<string, unknown>) => void): {
|
|
33
|
-
transport: Writable;
|
|
34
|
-
response: PassThrough;
|
|
35
|
-
};
|
|
36
|
-
export declare function boundCodexSdkStreams(binding: CodexSdkBinding, transport: CodexWireTransport, record: (value: Record<string, unknown>) => void, caps: {
|
|
37
|
-
requestBytes: number;
|
|
38
|
-
responseBytes: number;
|
|
39
|
-
}): {
|
|
40
|
-
transport: Writable;
|
|
41
|
-
response: PassThrough;
|
|
42
|
-
};
|
|
43
|
-
/** Separate review request profile; historical exact-output transport retains its 4096-byte cap. */
|
|
44
|
-
export declare function boundCodexReviewSdkStreams(binding: CodexSdkBinding, transport: CodexWireTransport, record: (value: Record<string, unknown>) => void, caps: {
|
|
45
|
-
requestBytes: number;
|
|
46
|
-
responseBytes: number;
|
|
47
|
-
}): {
|
|
48
|
-
transport: Writable;
|
|
49
|
-
response: PassThrough;
|
|
50
|
-
};
|
|
@@ -1,294 +0,0 @@
|
|
|
1
|
-
import { createHash } from 'node:crypto';
|
|
2
|
-
import { PassThrough, Writable } from 'node:stream';
|
|
3
|
-
import { isDeepStrictEqual } from 'node:util';
|
|
4
|
-
import * as zlib from 'node:zlib';
|
|
5
|
-
const destination = 'https://chatgpt.com/backend-api/codex/responses';
|
|
6
|
-
const hash = (b) => createHash('sha256').update(b).digest('hex');
|
|
7
|
-
const fixtureCredential = 'fixture.' + Buffer.from(JSON.stringify({ 'https://api.openai.com/auth': { chatgpt_account_id: 'fixture-no-account' } })).toString('base64url') + '.invalid';
|
|
8
|
-
/** Public result is final text only; reasoning metadata never leaves the private validator. */
|
|
9
|
-
export function validateCodexTextSse(bytes, model) {
|
|
10
|
-
const v = validateResponseProfile(bytes, model);
|
|
11
|
-
return { text: v.text, responseId: v.responseId };
|
|
12
|
-
}
|
|
13
|
-
function profileKeys(v, required, optional = []) { if (!v || typeof v !== 'object' || Array.isArray(v) || required.some(k => !Object.hasOwn(v, k)) || Object.keys(v).some(k => ![...required, ...optional].includes(k)))
|
|
14
|
-
throw Error('unsupported reasoning profile fields'); }
|
|
15
|
-
function reasoningShape(item) { profileKeys(item, ['id', 'type', 'summary', 'content', 'encrypted_content']); if (item.type !== 'reasoning' || typeof item.id !== 'string' || !item.id || item.id.length > 128 || !isDeepStrictEqual(item.content, []) || typeof item.encrypted_content !== 'string' || !item.encrypted_content || Buffer.byteLength(item.encrypted_content) > 8192)
|
|
16
|
-
throw Error('unsupported reasoning item'); }
|
|
17
|
-
function textPart(part) { profileKeys(part, ['type', 'text'], ['annotations', 'logprobs']); if (part.type !== 'output_text' || typeof part.text !== 'string' || ['annotations', 'logprobs'].some(k => Object.hasOwn(part, k) && !isDeepStrictEqual(part[k], [])))
|
|
18
|
-
throw Error('unsupported final text part'); }
|
|
19
|
-
function validateResponseProfile(bytes, model) {
|
|
20
|
-
const text = new TextDecoder('utf8', { fatal: true }).decode(bytes).replace(/\r\n/g, '\n');
|
|
21
|
-
if (!text.endsWith('\n\n'))
|
|
22
|
-
throw Error('truncated SSE framing');
|
|
23
|
-
let sequence = 0, phase = 0, responseId = '', itemId = '', output = '', completedItem;
|
|
24
|
-
let partOpen = false, partDone = false, textDone = false, progress = false;
|
|
25
|
-
let reasoningId = '', summary = '', summaryPhase = 0, summaryBytes = 0, reasoningItem, answerIndex = 0;
|
|
26
|
-
const summaries = []; // At most16 sequential parts;4096 aggregate text bytes.
|
|
27
|
-
for (const block of text.slice(0, -2).split('\n\n')) {
|
|
28
|
-
const lines = block.split('\n').filter(l => l && !l.startsWith(':'));
|
|
29
|
-
if (!lines.length)
|
|
30
|
-
continue;
|
|
31
|
-
const types = lines.filter(l => l.startsWith('event:')).map(l => l.slice(6).trim()), data = lines.filter(l => l.startsWith('data:')).map(l => l.slice(5).replace(/^ /, ''));
|
|
32
|
-
if (types.length > 1 || !data.length || types.length + data.length !== lines.length)
|
|
33
|
-
throw Error('unsupported SSE framing');
|
|
34
|
-
const e = JSON.parse(data.join('\n'));
|
|
35
|
-
if (types.length && e.type !== types[0] || e.sequence_number !== sequence++)
|
|
36
|
-
throw Error('SSE sequence or event mismatch');
|
|
37
|
-
if (reasoningItem && e.type !== 'response.completed') {
|
|
38
|
-
const fields = { 'response.output_item.added': ['output_index', 'item'], 'response.output_item.done': ['output_index', 'item'], 'response.content_part.added': ['output_index', 'content_index', 'item_id', 'part'], 'response.content_part.done': ['output_index', 'content_index', 'item_id', 'part'], 'response.output_text.delta': ['output_index', 'content_index', 'item_id', 'delta'], 'response.output_text.done': ['output_index', 'content_index', 'item_id', 'text'] };
|
|
39
|
-
if (!fields[e.type])
|
|
40
|
-
throw Error('unsupported reasoning/final event');
|
|
41
|
-
profileKeys(e, ['type', 'sequence_number', ...fields[e.type]], e.type === 'response.output_text.delta' ? ['obfuscation', 'logprobs'] : e.type === 'response.output_text.done' ? ['logprobs'] : []);
|
|
42
|
-
if (Object.hasOwn(e, 'logprobs') && !isDeepStrictEqual(e.logprobs, []) || Object.hasOwn(e, 'obfuscation') && (typeof e.obfuscation !== 'string' || Buffer.byteLength(e.obfuscation) > 4096))
|
|
43
|
-
throw Error('unsupported final metadata');
|
|
44
|
-
if (e.item) {
|
|
45
|
-
profileKeys(e.item, ['id', 'type', 'role', 'status', 'phase', 'content']);
|
|
46
|
-
if (e.item.phase !== 'final_answer')
|
|
47
|
-
throw Error('unsupported final phase');
|
|
48
|
-
if (e.type === 'response.output_item.done') {
|
|
49
|
-
if (!Array.isArray(e.item.content) || e.item.content.length !== 1)
|
|
50
|
-
throw Error('final text cardinality');
|
|
51
|
-
textPart(e.item.content[0]);
|
|
52
|
-
}
|
|
53
|
-
}
|
|
54
|
-
if (e.part)
|
|
55
|
-
textPart(e.part);
|
|
56
|
-
}
|
|
57
|
-
if (e.type === 'response.created' && phase === 0) {
|
|
58
|
-
responseId = e.response?.id;
|
|
59
|
-
if (typeof responseId !== 'string' || !responseId || responseId.length > 128 || e.response.status !== 'in_progress')
|
|
60
|
-
throw Error('response identity missing');
|
|
61
|
-
phase = 1;
|
|
62
|
-
}
|
|
63
|
-
else if (e.type === 'response.in_progress' && phase === 1 && !progress && !reasoningId) {
|
|
64
|
-
if (e.response?.id !== responseId || e.response.status !== 'in_progress')
|
|
65
|
-
throw Error('progress correlation');
|
|
66
|
-
progress = true;
|
|
67
|
-
}
|
|
68
|
-
else if (e.type === 'response.output_item.added' && phase === 1 && e.item?.type === 'reasoning' && !reasoningId) {
|
|
69
|
-
profileKeys(e, ['type', 'sequence_number', 'output_index', 'item']);
|
|
70
|
-
reasoningShape(e.item);
|
|
71
|
-
if (e.output_index !== 0 || !isDeepStrictEqual(e.item.summary, []))
|
|
72
|
-
throw Error('reasoning start mismatch');
|
|
73
|
-
reasoningId = e.item.id;
|
|
74
|
-
phase = 5;
|
|
75
|
-
}
|
|
76
|
-
else if (phase === 5) {
|
|
77
|
-
if (e.type === 'response.output_item.done') {
|
|
78
|
-
profileKeys(e, ['type', 'sequence_number', 'output_index', 'item']);
|
|
79
|
-
reasoningShape(e.item);
|
|
80
|
-
if (e.output_index !== 0 || e.item.id !== reasoningId || summaryPhase !== 0 || !isDeepStrictEqual(e.item.summary, summaries))
|
|
81
|
-
throw Error('reasoning completion mismatch');
|
|
82
|
-
reasoningItem = e.item;
|
|
83
|
-
answerIndex = 1;
|
|
84
|
-
phase = 1;
|
|
85
|
-
}
|
|
86
|
-
else {
|
|
87
|
-
const kind = e.type, field = kind === 'response.reasoning_summary_text.delta' ? 'delta' : kind === 'response.reasoning_summary_text.done' ? 'text' : 'part';
|
|
88
|
-
profileKeys(e, ['type', 'sequence_number', 'output_index', 'item_id', 'summary_index', field], field === 'delta' ? ['obfuscation'] : []);
|
|
89
|
-
if (e.output_index !== 0 || e.item_id !== reasoningId || e.summary_index !== summaries.length || Object.hasOwn(e, 'obfuscation') && (typeof e.obfuscation !== 'string' || Buffer.byteLength(e.obfuscation) > 4096))
|
|
90
|
-
throw Error('reasoning summary correlation');
|
|
91
|
-
if (field === 'part') {
|
|
92
|
-
profileKeys(e.part, ['type', 'text']);
|
|
93
|
-
if (e.part.type !== 'summary_text' || typeof e.part.text !== 'string')
|
|
94
|
-
throw Error('reasoning summary part');
|
|
95
|
-
}
|
|
96
|
-
if (kind === 'response.reasoning_summary_part.added' && summaryPhase === 0 && e.part.text === '' && summaries.length < 16)
|
|
97
|
-
summaryPhase = 1;
|
|
98
|
-
else if (kind === 'response.reasoning_summary_text.delta' && summaryPhase === 1 && typeof e.delta === 'string') {
|
|
99
|
-
summary += e.delta;
|
|
100
|
-
if (summaryBytes + Buffer.byteLength(summary) > 4096)
|
|
101
|
-
throw Error('reasoning summary byte bound');
|
|
102
|
-
}
|
|
103
|
-
else if (kind === 'response.reasoning_summary_text.done' && summaryPhase === 1 && e.text === summary)
|
|
104
|
-
summaryPhase = 2;
|
|
105
|
-
else if (kind === 'response.reasoning_summary_part.done' && summaryPhase === 2 && e.part.text === summary) {
|
|
106
|
-
summaries.push({ type: 'summary_text', text: summary });
|
|
107
|
-
summaryBytes += Buffer.byteLength(summary);
|
|
108
|
-
summary = '';
|
|
109
|
-
summaryPhase = 0;
|
|
110
|
-
}
|
|
111
|
-
else
|
|
112
|
-
throw Error('unsupported or reordered reasoning event');
|
|
113
|
-
}
|
|
114
|
-
}
|
|
115
|
-
else if (e.type === 'response.output_item.added' && phase === 1) {
|
|
116
|
-
itemId = e.item?.id;
|
|
117
|
-
if (typeof itemId !== 'string' || !itemId || itemId === reasoningId || itemId.length > 128 || e.output_index !== answerIndex || e.item.type !== 'message' || e.item.role !== 'assistant' || e.item.status !== 'in_progress' || !isDeepStrictEqual(e.item.content, []))
|
|
118
|
-
throw Error('unsupported output item');
|
|
119
|
-
phase = 2;
|
|
120
|
-
}
|
|
121
|
-
else if (e.type === 'response.content_part.added' && phase === 2 && !partOpen && !textDone) {
|
|
122
|
-
if (e.item_id !== itemId || e.output_index !== answerIndex || e.content_index !== 0 || e.part?.type !== 'output_text' || e.part.text !== '')
|
|
123
|
-
throw Error('content part correlation');
|
|
124
|
-
partOpen = true;
|
|
125
|
-
}
|
|
126
|
-
else if (e.type === 'response.output_text.done' && phase === 2 && !textDone) {
|
|
127
|
-
if (e.item_id !== itemId || e.output_index !== answerIndex || e.content_index !== 0 || e.text !== output)
|
|
128
|
-
throw Error('text completion mismatch');
|
|
129
|
-
textDone = true;
|
|
130
|
-
}
|
|
131
|
-
else if (e.type === 'response.content_part.done' && phase === 2 && partOpen && !partDone && textDone) {
|
|
132
|
-
if (e.item_id !== itemId || e.output_index !== answerIndex || e.content_index !== 0 || e.part?.type !== 'output_text' || e.part.text !== output)
|
|
133
|
-
throw Error('content part completion mismatch');
|
|
134
|
-
partDone = true;
|
|
135
|
-
}
|
|
136
|
-
else if (e.type === 'response.output_text.delta' && phase === 2 && !textDone) {
|
|
137
|
-
if (e.item_id !== itemId || e.output_index !== answerIndex || e.content_index !== 0 || typeof e.delta !== 'string')
|
|
138
|
-
throw Error('delta correlation');
|
|
139
|
-
output += e.delta;
|
|
140
|
-
}
|
|
141
|
-
else if (e.type === 'response.output_item.done' && phase === 2) {
|
|
142
|
-
if (reasoningItem && !(partOpen && partDone && textDone) || partOpen && !partDone || e.output_index !== answerIndex || e.item?.id !== itemId || e.item.type !== 'message' || e.item.role !== 'assistant' || e.item.status !== 'completed' || !Array.isArray(e.item.content) || e.item.content.length !== 1 || e.item.content[0].type !== 'output_text' || e.item.content[0].text !== output)
|
|
143
|
-
throw Error('completed item mismatch');
|
|
144
|
-
completedItem = e.item;
|
|
145
|
-
phase = 3;
|
|
146
|
-
}
|
|
147
|
-
else if (e.type === 'response.completed' && phase === 3) {
|
|
148
|
-
// Compact terminals omit duplicated items, not their preceding completion evidence.
|
|
149
|
-
const terminal = e.response?.output, compact = Array.isArray(terminal) && terminal.length === 0 && partOpen && partDone && textDone;
|
|
150
|
-
if (e.response?.id !== responseId || e.response.model !== model || e.response.status !== 'completed' || !Array.isArray(terminal) || (!compact && !isDeepStrictEqual(terminal, reasoningItem ? [reasoningItem, completedItem] : [completedItem])))
|
|
151
|
-
throw Error('completed response mismatch');
|
|
152
|
-
phase = 4;
|
|
153
|
-
}
|
|
154
|
-
else
|
|
155
|
-
throw Error('unsupported or reordered SSE event');
|
|
156
|
-
}
|
|
157
|
-
if (phase !== 4)
|
|
158
|
-
throw Error('truncated SSE response');
|
|
159
|
-
return { text: output, responseId, reasoning: reasoningItem ? { item: reasoningItem, sdkText: summaries.map(p => p.text).join('\n\n') || '\n\n'.repeat(summaries.length) } : undefined };
|
|
160
|
-
}
|
|
161
|
-
export function inertCodexSdkStreams(binding, fixture, record) {
|
|
162
|
-
return sdkStreams(binding, async () => fixture(), record, 'inert-sdk-fetch');
|
|
163
|
-
}
|
|
164
|
-
export function boundCodexSdkStreams(binding, transport, record, caps) {
|
|
165
|
-
if (!['fixture-http', 'subscription-http'].includes(transport.kind) || ![caps.requestBytes, caps.responseBytes].every(n => Number.isSafeInteger(n) && n > 0) || caps.requestBytes > 4096 || caps.responseBytes > 16384)
|
|
166
|
-
throw Error('unsupported SDK transport binding');
|
|
167
|
-
return sdkStreams(binding, transport.exchange, record, transport.kind, caps);
|
|
168
|
-
}
|
|
169
|
-
/** Separate review request profile; historical exact-output transport retains its 4096-byte cap. */
|
|
170
|
-
export function boundCodexReviewSdkStreams(binding, transport, record, caps) {
|
|
171
|
-
if (!['fixture-http', 'subscription-http'].includes(transport.kind) || ![caps.requestBytes, caps.responseBytes].every(n => Number.isSafeInteger(n) && n > 0) || caps.requestBytes > 65536 || caps.responseBytes > 262144)
|
|
172
|
-
throw Error('unsupported review SDK transport binding');
|
|
173
|
-
return sdkStreams(binding, transport.exchange, record, transport.kind, caps);
|
|
174
|
-
}
|
|
175
|
-
function sdkStreams(binding, send, record, mode, caps = { requestBytes: 4096, responseBytes: 16384 }) {
|
|
176
|
-
const response = new PassThrough(), controller = new AbortController();
|
|
177
|
-
let started = false;
|
|
178
|
-
const transport = new Writable({ autoDestroy: false, write(chunk, _encoding, done) {
|
|
179
|
-
void (async () => {
|
|
180
|
-
if (started)
|
|
181
|
-
throw Error('SDK transport single use; no retry');
|
|
182
|
-
started = true;
|
|
183
|
-
const expected = JSON.parse(Buffer.from(chunk).toString('utf8')), model = binding.model;
|
|
184
|
-
if (model.id !== expected.model || model.provider !== 'openai-codex' || model.api !== 'openai-codex-responses' || model.baseUrl !== 'https://chatgpt.com/backend-api')
|
|
185
|
-
throw Error('SDK model binding mismatch');
|
|
186
|
-
let fetches = 0, expectedOutput, profile;
|
|
187
|
-
const result = await binding.stream(model, { systemPrompt: expected.instructions, messages: [{ role: 'user', content: expected.input[0].content[0].text, timestamp: 0 }] }, { apiKey: fixtureCredential, transport: 'sse', maxRetries: 0, cacheRetention: 'none', reasoningEffort: expected.reasoning.effort, reasoningSummary: 'auto', textVerbosity: 'low', toolChoice: 'none', timeoutMs: 30000, signal: controller.signal,
|
|
188
|
-
onPayload: (payload) => { const normalized = { ...payload, parallel_tool_calls: false }; if (!isDeepStrictEqual(JSON.parse(JSON.stringify(normalized)), expected))
|
|
189
|
-
throw Error('SDK request settings tampered'); return normalized; },
|
|
190
|
-
fetch: async (url, init) => {
|
|
191
|
-
if (++fetches !== 1)
|
|
192
|
-
throw Error('SDK retry refused');
|
|
193
|
-
if (url !== destination || init.method !== 'POST' || init.redirect && init.redirect !== 'error' || controller.signal.aborted)
|
|
194
|
-
throw Error('SDK destination/redirect refused');
|
|
195
|
-
const wire = typeof init.body === 'string' ? Buffer.from(init.body) : init.body instanceof Uint8Array ? Buffer.from(init.body) : null;
|
|
196
|
-
if (!wire || wire.length > (mode === 'inert-sdk-fetch' ? 16384 : caps.requestBytes))
|
|
197
|
-
throw Error('SDK wire byte bound');
|
|
198
|
-
const encoding = new Headers(init.headers).get('content-encoding');
|
|
199
|
-
if (encoding !== null && encoding !== 'zstd')
|
|
200
|
-
throw Error('unsupported request encoding');
|
|
201
|
-
if (encoding === 'zstd' && typeof zlib.zstdDecompressSync !== 'function')
|
|
202
|
-
throw Error('zstd decoding unavailable');
|
|
203
|
-
const serialized = encoding === 'zstd' ? Buffer.from(zlib.zstdDecompressSync(wire, { maxOutputLength: caps.requestBytes })) : wire;
|
|
204
|
-
if (serialized.length > caps.requestBytes || !isDeepStrictEqual(JSON.parse(new TextDecoder('utf8', { fatal: true }).decode(serialized)), expected))
|
|
205
|
-
throw Error('SDK final request tampered');
|
|
206
|
-
record({ type: 'sdk-wire-observed', requestSha256: hash(chunk), serializedSha256: hash(serialized), serializedBase64: serialized.toString('base64'), wireBase64: wire.toString('base64'), wireSha256: hash(wire), encoding, destination, method: 'POST', authenticationHeadersRetained: false, transport: mode, networkCalls: mode === 'subscription-http' ? null : 0, liveQualified: false });
|
|
207
|
-
const reply = await send({ destination, method: 'POST', body: wire, encoding }, controller.signal, value => { if (!['http-request-started', 'http-response-observed', 'http-request-failed'].includes(String(value.type)))
|
|
208
|
-
throw Error('unbound transport observation'); record({ ...value, transport: mode, liveQualified: false }); });
|
|
209
|
-
if (controller.signal.aborted)
|
|
210
|
-
throw Error('SDK response deadline');
|
|
211
|
-
if (reply.redirected || reply.status >= 300 && reply.status < 400)
|
|
212
|
-
throw Error('fixture redirects refused');
|
|
213
|
-
if (!reply.body)
|
|
214
|
-
throw Error('missing response body');
|
|
215
|
-
const reader = reply.body.getReader(), parts = [];
|
|
216
|
-
let size = 0, cancellationError;
|
|
217
|
-
const abort = () => { void reader.cancel().catch(error => { cancellationError = error; }); };
|
|
218
|
-
controller.signal.addEventListener('abort', abort, { once: true });
|
|
219
|
-
try {
|
|
220
|
-
for (;;) {
|
|
221
|
-
if (controller.signal.aborted)
|
|
222
|
-
throw Error('SDK response deadline');
|
|
223
|
-
const next = await reader.read();
|
|
224
|
-
if (next.done)
|
|
225
|
-
break;
|
|
226
|
-
size += next.value.length;
|
|
227
|
-
if (size > caps.responseBytes)
|
|
228
|
-
throw Error('SDK response byte bound');
|
|
229
|
-
parts.push(Buffer.from(next.value));
|
|
230
|
-
}
|
|
231
|
-
}
|
|
232
|
-
finally {
|
|
233
|
-
controller.signal.removeEventListener('abort', abort);
|
|
234
|
-
await reader.cancel();
|
|
235
|
-
reader.releaseLock();
|
|
236
|
-
}
|
|
237
|
-
if (cancellationError || controller.signal.aborted)
|
|
238
|
-
throw Error('SDK response cancellation failed or deadline expired');
|
|
239
|
-
const raw = Buffer.concat(parts);
|
|
240
|
-
record({ type: 'sdk-response-observed', status: reply.status, responseBase64: raw.toString('base64'), responseSha256: hash(raw), liveQualified: false });
|
|
241
|
-
let decoderBody = raw;
|
|
242
|
-
const decoderHeaders = new Headers(reply.headers);
|
|
243
|
-
if (reply.ok) {
|
|
244
|
-
const contentType = reply.headers.get('content-type'), mediaType = contentType === null || contentType === '' ? 'missing' : contentType.startsWith('text/event-stream') ? 'event-stream' : 'other';
|
|
245
|
-
let category = 'media-type-invalid';
|
|
246
|
-
try {
|
|
247
|
-
if (mediaType === 'other' || mediaType === 'missing' && reply.status !== 200)
|
|
248
|
-
throw Error('unsupported response content type');
|
|
249
|
-
category = 'SSE-validation-failed';
|
|
250
|
-
const validated = validateResponseProfile(raw, expected.model);
|
|
251
|
-
profile = validated;
|
|
252
|
-
expectedOutput = validated.text;
|
|
253
|
-
decoderBody = Buffer.from(new TextDecoder('utf8', { fatal: true }).decode(raw).replace(/\r\n/g, '\n'));
|
|
254
|
-
// Only HTTP200 + validated, correlated model-bound text may supply a missing
|
|
255
|
-
// decoder media type. Preserve reply.headers; this is not an upstream header claim.
|
|
256
|
-
if (mediaType === 'missing')
|
|
257
|
-
decoderHeaders.set('content-type', 'text/event-stream');
|
|
258
|
-
category = 'validation-record-failed';
|
|
259
|
-
record({ type: 'sdk-response-validated', responseId: validated.responseId, decoderInputSha256: hash(decoderBody), decoderNormalization: 'SSE CRLF to LF; original response retained', originalMediaType: mediaType, decoderMediaType: 'event-stream', mediaTypeDerived: mediaType === 'missing', profile: validated.reasoning ? 'reasoning-plus-text' : 'text-only', liveQualified: false });
|
|
260
|
-
}
|
|
261
|
-
catch (error) {
|
|
262
|
-
// Diagnostic persistence is best effort only; it must never replace the initiating
|
|
263
|
-
// exception or turn a refused response into success. No raw header/body/error text.
|
|
264
|
-
try {
|
|
265
|
-
record({ type: 'sdk-response-failed', category, mediaType, liveQualified: false });
|
|
266
|
-
}
|
|
267
|
-
catch { /* Original error remains authoritative even when this recorder also fails. */ }
|
|
268
|
-
throw error;
|
|
269
|
-
}
|
|
270
|
-
}
|
|
271
|
-
return new Response(decoderBody, { status: reply.status, headers: decoderHeaders });
|
|
272
|
-
} }).result();
|
|
273
|
-
let contentMatches = false;
|
|
274
|
-
if (profile?.reasoning) {
|
|
275
|
-
const blocks = result.content, thinking = blocks?.[0];
|
|
276
|
-
if (blocks?.length === 2 && thinking.type === 'thinking' && thinking.thinking === profile.reasoning.sdkText && typeof thinking.thinkingSignature === 'string' && Buffer.byteLength(thinking.thinkingSignature) <= 16384 && blocks[1].type === 'text' && blocks[1].text === expectedOutput && result.responseId === profile.responseId) {
|
|
277
|
-
try {
|
|
278
|
-
contentMatches = isDeepStrictEqual(JSON.parse(thinking.thinkingSignature), profile.reasoning.item);
|
|
279
|
-
}
|
|
280
|
-
catch { /* Malformed private SDK signature is a mismatch, never logged or decoded as ciphertext. */ }
|
|
281
|
-
}
|
|
282
|
-
}
|
|
283
|
-
else
|
|
284
|
-
contentMatches = result.content?.length === 1 && result.content[0].type === 'text' && result.content[0].text === expectedOutput;
|
|
285
|
-
if (controller.signal.aborted || fetches !== 1 || result.stopReason !== 'stop' || result.model !== expected.model || result.provider !== 'openai-codex' || result.api !== 'openai-codex-responses' || !contentMatches)
|
|
286
|
-
throw Error(mode === 'inert-sdk-fetch' ? 'SDK response decoding failed: ' + String(result.errorMessage ?? 'decoded output mismatch').slice(0, 512) : 'subscription-exchange-failed');
|
|
287
|
-
record({ type: 'sdk-decoded', outputSha256: hash(expectedOutput), model: result.model, provider: result.provider, api: result.api, scoredContent: 'final-text-only', thinkingMetadataVerified: !!profile?.reasoning, providerInternalFacts: null, transport: mode, liveQualified: false });
|
|
288
|
-
response.end(expectedOutput);
|
|
289
|
-
done();
|
|
290
|
-
})().catch(e => done(e instanceof Error ? e : Error(String(e))));
|
|
291
|
-
}, destroy(error, done) { controller.abort(); response.destroy(); done(error); } });
|
|
292
|
-
return { transport, response };
|
|
293
|
-
}
|
|
294
|
-
//# sourceMappingURL=codex-sdk-transport.js.map
|