sneakoscope 2.0.2 → 2.0.5
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/README.md +12 -8
- package/crates/sks-core/Cargo.lock +1 -1
- package/crates/sks-core/Cargo.toml +1 -1
- package/crates/sks-core/src/main.rs +1 -1
- package/dist/.sks-build-stamp.json +4 -4
- package/dist/bin/sks.js +1 -1
- package/dist/build-manifest.json +86 -8
- package/dist/commands/doctor.js +14 -0
- package/dist/core/agents/agent-orchestrator.js +70 -4
- package/dist/core/agents/agent-patch-proof.js +5 -0
- package/dist/core/agents/agent-proof-evidence.js +61 -0
- package/dist/core/agents/agent-roster.js +35 -6
- package/dist/core/agents/agent-schema.js +1 -1
- package/dist/core/agents/native-worker-backend-router.js +31 -9
- package/dist/core/agents/ollama-worker-config.js +164 -15
- package/dist/core/codex/codex-0-137-compat.js +119 -0
- package/dist/core/codex-control/codex-control-proof.js +4 -1
- package/dist/core/codex-control/codex-fake-sdk-adapter.js +20 -0
- package/dist/core/codex-control/codex-output-schemas.js +5 -1
- package/dist/core/codex-control/codex-sdk-capability.js +1 -1
- package/dist/core/codex-control/codex-task-runner.js +329 -5
- package/dist/core/codex-control/gpt-final-arbiter.js +160 -0
- package/dist/core/codex-control/gpt-final-context-compressor.js +17 -0
- package/dist/core/codex-control/gpt-final-proof-pack.js +120 -0
- package/dist/core/codex-control/gpt-final-review-schema.js +71 -0
- package/dist/core/codex-control/python-codex-sdk-adapter.js +197 -0
- package/dist/core/codex-control/python-codex-sdk-event-translator.js +14 -0
- package/dist/core/commands/local-model-command.js +79 -18
- package/dist/core/commands/naruto-command.js +195 -12
- package/dist/core/commands/run-command.js +6 -2
- package/dist/core/doctor/doctor-readiness-matrix.js +34 -0
- package/dist/core/feature-fixtures.js +4 -0
- package/dist/core/fsx.js +1 -1
- package/dist/core/git-simple.js +143 -4
- package/dist/core/local-llm/local-collaboration-policy.js +93 -0
- package/dist/core/local-llm/local-llm-backpressure.js +20 -0
- package/dist/core/local-llm/local-llm-capability.js +29 -0
- package/dist/core/local-llm/local-llm-client.js +100 -0
- package/dist/core/local-llm/local-llm-config.js +20 -0
- package/dist/core/local-llm/local-llm-context-cache.js +21 -0
- package/dist/core/local-llm/local-llm-control-adapter.js +101 -0
- package/dist/core/local-llm/local-llm-json-repair.js +52 -0
- package/dist/core/local-llm/local-llm-metrics.js +42 -0
- package/dist/core/local-llm/local-llm-ollama-client.js +67 -0
- package/dist/core/local-llm/local-llm-openai-compatible-client.js +30 -0
- package/dist/core/local-llm/local-llm-prompt-cache.js +12 -0
- package/dist/core/local-llm/local-llm-scheduler.js +29 -0
- package/dist/core/local-llm/local-llm-schema-enforcer.js +15 -0
- package/dist/core/local-llm/local-llm-smoke.js +83 -0
- package/dist/core/local-llm/local-llm-warmup.js +20 -0
- package/dist/core/local-llm/local-worker-eligibility.js +27 -0
- package/dist/core/naruto/hardware-capacity-probe.js +36 -0
- package/dist/core/naruto/naruto-active-pool.js +118 -0
- package/dist/core/naruto/naruto-backpressure.js +13 -0
- package/dist/core/naruto/naruto-concurrency-governor.js +65 -0
- package/dist/core/naruto/naruto-finalizer.js +18 -0
- package/dist/core/naruto/naruto-generation-scheduler.js +18 -0
- package/dist/core/naruto/naruto-gpt-final-pack.js +49 -0
- package/dist/core/naruto/naruto-parallel-patch-apply.js +95 -0
- package/dist/core/naruto/naruto-patch-transaction-batch.js +42 -0
- package/dist/core/naruto/naruto-role-policy.js +107 -0
- package/dist/core/naruto/naruto-verification-dag.js +42 -0
- package/dist/core/naruto/naruto-verification-pool.js +18 -0
- package/dist/core/naruto/naruto-work-graph.js +198 -0
- package/dist/core/naruto/naruto-work-item.js +40 -0
- package/dist/core/naruto/naruto-work-stealing.js +11 -0
- package/dist/core/naruto/resource-pressure-monitor.js +32 -0
- package/dist/core/pipeline/final-gpt-patch-stage.js +31 -0
- package/dist/core/pipeline/final-gpt-review-stage.js +5 -0
- package/dist/core/pipeline/finalize-pipeline-result.js +58 -0
- package/dist/core/pipeline/gpt-final-required.js +12 -0
- package/dist/core/prompt/prompt-placeholder-guard.js +30 -0
- package/dist/core/router/capability-card.js +13 -0
- package/dist/core/router/route-cache.js +3 -0
- package/dist/core/router/ultra-router.js +2 -1
- package/dist/core/routes.js +4 -4
- package/dist/core/safety/mutation-guard.js +2 -0
- package/dist/core/update-check.js +60 -25
- package/dist/core/version.js +1 -1
- package/dist/core/zellij/zellij-lane-runtime.js +2 -2
- package/dist/core/zellij/zellij-naruto-dashboard.js +36 -0
- package/dist/core/zellij/zellij-worker-pane-manager.js +4 -4
- package/dist/scripts/blackbox-command-import-smoke.js +10 -1
- package/dist/scripts/check-package-boundary.js +12 -3
- package/dist/scripts/codex-0-137-compat-check.js +27 -0
- package/dist/scripts/codex-environment-scoped-approvals-check.js +10 -0
- package/dist/scripts/codex-plugin-list-json-check.js +8 -0
- package/dist/scripts/codex-sdk-team-naruto-agent-pipeline-check.js +2 -1
- package/dist/scripts/codex-thread-runtime-choice-check.js +10 -0
- package/dist/scripts/gpt-final-arbiter-check.js +63 -0
- package/dist/scripts/gpt-final-arbiter-performance-check.js +36 -0
- package/dist/scripts/local-collab-all-pipelines-final-gpt-check.js +21 -0
- package/dist/scripts/local-collab-gpt-final-availability-check.js +58 -0
- package/dist/scripts/local-collab-no-local-only-final-check.js +27 -0
- package/dist/scripts/local-collab-policy-check.js +17 -0
- package/dist/scripts/local-llm-all-pipelines-check.js +11 -0
- package/dist/scripts/local-llm-cache-performance-check.js +10 -0
- package/dist/scripts/local-llm-capability-check.js +14 -0
- package/dist/scripts/local-llm-smoke-check.js +23 -0
- package/dist/scripts/local-llm-structured-output-check.js +11 -0
- package/dist/scripts/local-llm-throughput-check.js +10 -0
- package/dist/scripts/local-llm-tool-call-repair-check.js +10 -0
- package/dist/scripts/local-llm-warmup-check.js +11 -0
- package/dist/scripts/naruto-active-pool-check.js +27 -0
- package/dist/scripts/naruto-concurrency-governor-check.js +52 -0
- package/dist/scripts/naruto-gpt-final-pack-check.js +34 -0
- package/dist/scripts/naruto-parallel-patch-apply-check.js +41 -0
- package/dist/scripts/naruto-real-local-gpt-final-smoke.js +16 -0
- package/dist/scripts/naruto-role-distribution-check.js +23 -0
- package/dist/scripts/naruto-shadow-clone-swarm-check.js +6 -0
- package/dist/scripts/naruto-verification-pool-check.js +36 -0
- package/dist/scripts/naruto-work-graph-check.js +24 -0
- package/dist/scripts/naruto-zellij-massive-ui-check.js +23 -0
- package/dist/scripts/prompt-placeholder-guard-check.js +33 -0
- package/dist/scripts/python-codex-sdk-all-pipelines-check.js +47 -0
- package/dist/scripts/python-codex-sdk-capability-check.js +75 -0
- package/dist/scripts/python-codex-sdk-sandbox-policy-check.js +10 -0
- package/dist/scripts/python-codex-sdk-stream-bridge-check.js +12 -0
- package/dist/scripts/release-parallel-check.js +1 -1
- package/dist/scripts/release-real-check.js +5 -0
- package/dist/scripts/zellij-worker-pane-manager-check.js +1 -1
- package/package.json +38 -4
- package/schemas/local-llm/local-collaboration-policy.schema.json +57 -0
- package/schemas/local-llm/local-model-config.schema.json +74 -0
- package/schemas/naruto/naruto-concurrency-governor.schema.json +21 -0
- package/schemas/naruto/naruto-work-graph.schema.json +22 -0
|
@@ -2,6 +2,7 @@ import os from 'node:os';
|
|
|
2
2
|
import { DEFAULT_AGENT_CONCURRENCY, DEFAULT_AGENT_COUNT, DEFAULT_NARUTO_CLONES, MAX_AGENT_COUNT, MAX_NARUTO_AGENT_COUNT, agentSessionId } from './agent-schema.js';
|
|
3
3
|
import { defaultAgentPersonas, validatePersonaUniqueness } from './agent-persona.js';
|
|
4
4
|
import { buildAgentEffortPolicy, decideAgentEffort, decideNarutoCloneEffort } from './agent-effort-policy.js';
|
|
5
|
+
import { mapNarutoRoleToAgentRole, narutoRoleAllowsWrite } from '../naruto/naruto-role-policy.js';
|
|
5
6
|
// $Naruto must never blindly spawn the full clone count at once, but the live
|
|
6
7
|
// CONCURRENCY ceiling is NOT a function of CPU cores. Each clone is a separate CLI
|
|
7
8
|
// worker process that spends ~all of its wall-clock awaiting the Codex API
|
|
@@ -156,28 +157,40 @@ export function buildNarutoCloneRoster(opts = {}) {
|
|
|
156
157
|
const basePool = pool.length ? pool : defaultAgentPersonas(DEFAULT_AGENT_COUNT);
|
|
157
158
|
const personas = [];
|
|
158
159
|
const roster = [];
|
|
160
|
+
const roleCycle = narutoRoleCycle(readonly);
|
|
159
161
|
for (let index = 0; index < cloneCount; index += 1) {
|
|
160
162
|
const base = basePool[index % basePool.length];
|
|
161
163
|
const cloneTag = 'clone-' + String(index + 1).padStart(3, '0');
|
|
162
164
|
const id = 'naruto_' + cloneTag.replace(/-/g, '_');
|
|
163
|
-
const
|
|
165
|
+
const narutoRole = roleCycle[index % roleCycle.length] || 'verifier';
|
|
166
|
+
const writeAllowed = !readonly && narutoRoleAllowsWrite(narutoRole);
|
|
167
|
+
const cloneReadonly = readonly || !writeAllowed;
|
|
168
|
+
const role = mapNarutoRoleToAgentRole(narutoRole);
|
|
169
|
+
const allowedTools = writeAllowed ? ['read', 'search', 'edit', 'test'] : narutoRole === 'verifier' ? ['read', 'search', 'test'] : ['read', 'search'];
|
|
164
170
|
// Dynamic per-clone effort like team mode, capped at low/medium and always fast.
|
|
165
|
-
const effort = decideNarutoCloneEffort({ persona: base, prompt: opts.prompt || '', agentId: id, readonly: cloneReadonly });
|
|
171
|
+
const effort = decideNarutoCloneEffort({ persona: { ...base, role, allowed_tools: allowedTools, read_only: cloneReadonly, write_policy: writeAllowed ? 'exclusive Naruto patch-envelope lease required' : 'read-only Naruto role' }, prompt: opts.prompt || '', agentId: id, readonly: cloneReadonly });
|
|
166
172
|
const persona = {
|
|
167
173
|
...base,
|
|
168
174
|
id,
|
|
169
175
|
stable_id: base.stable_id + '-' + cloneTag,
|
|
170
|
-
|
|
171
|
-
|
|
176
|
+
role,
|
|
177
|
+
naruto_role: narutoRole,
|
|
178
|
+
write_allowed: writeAllowed,
|
|
179
|
+
read_only: cloneReadonly,
|
|
180
|
+
allowed_tools: allowedTools,
|
|
181
|
+
write_policy: writeAllowed ? 'exclusive Naruto patch-envelope lease required' : 'read-only Naruto role',
|
|
182
|
+
prompt: 'SHADOW CLONE: ' + cloneTag + ' (Kage Bunshin of ' + base.stable_id + ')\nNARUTO ROLE: ' + narutoRole + '\n' + base.prompt
|
|
172
183
|
};
|
|
173
184
|
personas.push(persona);
|
|
174
185
|
roster.push({
|
|
175
186
|
id,
|
|
176
187
|
session_id: agentSessionId(id, index + 1),
|
|
177
188
|
persona_id: id,
|
|
178
|
-
role
|
|
189
|
+
role,
|
|
190
|
+
naruto_role: narutoRole,
|
|
191
|
+
write_allowed: writeAllowed,
|
|
179
192
|
index: index + 1,
|
|
180
|
-
write_policy: cloneReadonly ? 'read-only' :
|
|
193
|
+
write_policy: cloneReadonly ? 'read-only' : 'exclusive Naruto patch-envelope lease required',
|
|
181
194
|
status: 'pending',
|
|
182
195
|
reasoning_effort: effort.reasoning_effort,
|
|
183
196
|
model_reasoning_effort: effort.model_reasoning_effort,
|
|
@@ -208,4 +221,20 @@ export function buildNarutoCloneRoster(opts = {}) {
|
|
|
208
221
|
};
|
|
209
222
|
return { ...result, effort_policy: buildAgentEffortPolicy(result) };
|
|
210
223
|
}
|
|
224
|
+
function narutoRoleCycle(readonly) {
|
|
225
|
+
if (readonly)
|
|
226
|
+
return ['verifier', 'researcher', 'verifier', 'gpt_final_arbiter'];
|
|
227
|
+
return [
|
|
228
|
+
'implementer',
|
|
229
|
+
'modifier',
|
|
230
|
+
'test_writer',
|
|
231
|
+
'verifier',
|
|
232
|
+
'researcher',
|
|
233
|
+
'conflict_resolver',
|
|
234
|
+
'rollback_planner',
|
|
235
|
+
'integrator',
|
|
236
|
+
'modifier',
|
|
237
|
+
'test_writer'
|
|
238
|
+
];
|
|
239
|
+
}
|
|
211
240
|
//# sourceMappingURL=agent-roster.js.map
|
|
@@ -14,7 +14,7 @@ export const DEFAULT_AGENT_CONCURRENCY = 5;
|
|
|
14
14
|
// cap; every other roster/scheduler caller keeps MAX_AGENT_COUNT as the default.
|
|
15
15
|
export const MAX_NARUTO_AGENT_COUNT = 100;
|
|
16
16
|
export const DEFAULT_NARUTO_CLONES = 12;
|
|
17
|
-
export const AGENT_BACKENDS = ['fake', 'process', 'codex-sdk', 'zellij', 'ollama'];
|
|
17
|
+
export const AGENT_BACKENDS = ['fake', 'process', 'codex-sdk', 'zellij', 'ollama', 'local-llm'];
|
|
18
18
|
export function normalizeAgentBackend(input) {
|
|
19
19
|
const value = String(input || 'codex-sdk');
|
|
20
20
|
return AGENT_BACKENDS.includes(value) ? value : 'codex-sdk';
|
|
@@ -86,7 +86,8 @@ export async function runNativeWorkerBackendRouter(input) {
|
|
|
86
86
|
verification: { status: ollamaRun.status === 'done' ? 'passed' : 'failed', checks: [...(ollamaRun.verification?.checks || []), 'native-worker-backend-router', 'ollama-api-generate'] }
|
|
87
87
|
});
|
|
88
88
|
}
|
|
89
|
-
else if (backend === 'codex-sdk' || backend === 'zellij') {
|
|
89
|
+
else if (backend === 'codex-sdk' || backend === 'zellij' || backend === 'local-llm') {
|
|
90
|
+
const localPreferred = backend === 'local-llm';
|
|
90
91
|
const sdkTask = await runCodexTask({
|
|
91
92
|
route: String(input.intake.route || '$Agent'),
|
|
92
93
|
tier: 'worker',
|
|
@@ -111,6 +112,12 @@ export async function runNativeWorkerBackendRouter(input) {
|
|
|
111
112
|
user_confirmed_full_access: false,
|
|
112
113
|
mad_sks_authorized: input.intake.mad_sks_authorized === true || process.env.SKS_MAD_SKS_ACTIVE === '1'
|
|
113
114
|
},
|
|
115
|
+
backendPreference: localPreferred ? ['local-llm', 'codex-sdk'] : ['codex-sdk'],
|
|
116
|
+
allowLocalLlm: localPreferred,
|
|
117
|
+
...(localPreferred ? { localLlmPolicy: {
|
|
118
|
+
mode: 'local_preferred',
|
|
119
|
+
requiresGptFinal: true
|
|
120
|
+
} } : {}),
|
|
114
121
|
mutationLedgerRoot: path.join(root, input.workerDirRel),
|
|
115
122
|
zellijPaneId: await readZellijPaneId(root, input.workerDirRel),
|
|
116
123
|
reliabilityPolicy: {
|
|
@@ -121,12 +128,14 @@ export async function runNativeWorkerBackendRouter(input) {
|
|
|
121
128
|
outputLastMessagePath = sdkTask.workerResultPath;
|
|
122
129
|
const sdkWorkerResult = await readJson(sdkTask.workerResultPath, null);
|
|
123
130
|
patchEnvelopes = normalizeSdkPatchEnvelopes(sdkWorkerResult?.patch_envelopes || [], input, sdkTask.sdkThreadId);
|
|
124
|
-
proofLevel = sdkTask.ok ? (patchEnvelopes.length ? 'model_authored' : 'codex_sdk_thread_proven') : 'blocked';
|
|
131
|
+
proofLevel = sdkTask.ok ? (patchEnvelopes.length ? 'model_authored' : sdkTask.backend === 'local-llm' ? 'local_llm_worker_proven' : 'codex_sdk_thread_proven') : 'blocked';
|
|
125
132
|
const sdkReport = {
|
|
126
133
|
schema: 'sks.codex-sdk-worker-adapter.v1',
|
|
127
|
-
backend:
|
|
134
|
+
backend: sdkTask.backend,
|
|
135
|
+
backend_family: sdkTask.backend_family,
|
|
128
136
|
sdk_thread_id: sdkTask.sdkThreadId,
|
|
129
137
|
sdk_run_id: sdkTask.sdkRunId,
|
|
138
|
+
local_llm_proof_path: sdkTask.localLlmProofPath || null,
|
|
130
139
|
stream_event_count: sdkTask.streamEventCount,
|
|
131
140
|
structured_output_valid: sdkTask.structuredOutputValid,
|
|
132
141
|
worker_result_path: sdkTask.workerResultPath,
|
|
@@ -136,17 +145,30 @@ export async function runNativeWorkerBackendRouter(input) {
|
|
|
136
145
|
childReports = [sdkReport];
|
|
137
146
|
result = validateAgentWorkerResult({
|
|
138
147
|
...sdkWorkerResult,
|
|
139
|
-
backend: 'codex-sdk',
|
|
148
|
+
backend: sdkTask.backend === 'local-llm' ? 'local-llm' : 'codex-sdk',
|
|
140
149
|
patch_envelopes: patchEnvelopes,
|
|
141
150
|
codex_child_report: sdkReport,
|
|
142
151
|
codex_sdk_thread: sdkReport,
|
|
143
152
|
model_authored_patch_envelopes: patchEnvelopes.length > 0,
|
|
144
153
|
fixture_patch_envelopes: false,
|
|
145
|
-
artifacts: [...new Set([
|
|
154
|
+
artifacts: [...new Set([
|
|
155
|
+
...(sdkWorkerResult?.artifacts || []),
|
|
156
|
+
path.relative(root, sdkTask.workerResultPath),
|
|
157
|
+
path.join(input.workerDirRel, 'codex-control-proof.json'),
|
|
158
|
+
path.join(input.workerDirRel, 'codex-thread-registry.json'),
|
|
159
|
+
sdkTask.backend === 'local-llm' ? path.join(input.workerDirRel, 'local-llm-events.jsonl') : path.join(input.workerDirRel, 'codex-sdk-events.jsonl'),
|
|
160
|
+
...(sdkTask.localLlmProofPath ? [path.relative(root, sdkTask.localLlmProofPath)] : [])
|
|
161
|
+
])],
|
|
146
162
|
blockers: [...(sdkWorkerResult?.blockers || []), ...sdkTask.blockers],
|
|
147
163
|
verification: {
|
|
148
164
|
status: sdkTask.ok ? 'passed' : 'failed',
|
|
149
|
-
checks: [
|
|
165
|
+
checks: [
|
|
166
|
+
...(sdkWorkerResult?.verification?.checks || []),
|
|
167
|
+
sdkTask.backend === 'local-llm' ? 'local-llm-control-plane' : 'codex-sdk-control-plane',
|
|
168
|
+
sdkTask.backend === 'local-llm' ? 'local-llm-event-stream' : 'codex-sdk-event-stream',
|
|
169
|
+
sdkTask.backend === 'local-llm' ? 'local-llm-structured-output' : 'codex-sdk-structured-output',
|
|
170
|
+
...(sdkTask.backend === 'local-llm' ? ['gpt-final-required-before-acceptance'] : [])
|
|
171
|
+
]
|
|
150
172
|
}
|
|
151
173
|
});
|
|
152
174
|
}
|
|
@@ -221,13 +243,13 @@ async function maybeAutoSelectOllamaBackend(backend, input) {
|
|
|
221
243
|
model: input.intake?.ollama_model || null,
|
|
222
244
|
baseUrl: input.intake?.ollama_base_url || null
|
|
223
245
|
}).catch(() => null);
|
|
224
|
-
if (!config?.ok || config.enabled !== true)
|
|
246
|
+
if (!config?.ok || config.enabled !== true || config.status !== 'verified')
|
|
225
247
|
return backend;
|
|
226
248
|
const policy = classifyOllamaWorkerSlice(input.slice, { route: input.intake?.route, agent: input.agent });
|
|
227
|
-
return policy.ok ? '
|
|
249
|
+
return policy.ok ? 'local-llm' : backend;
|
|
228
250
|
}
|
|
229
251
|
function normalizeBackend(value) {
|
|
230
|
-
return value === 'fake' || value === 'process' || value === 'codex-sdk' || value === 'zellij' || value === 'ollama' ? value : null;
|
|
252
|
+
return value === 'fake' || value === 'process' || value === 'codex-sdk' || value === 'zellij' || value === 'ollama' || value === 'local-llm' ? value : null;
|
|
231
253
|
}
|
|
232
254
|
function envelopeOpts(input, source, childPid) {
|
|
233
255
|
return {
|
|
@@ -2,12 +2,16 @@ import os from 'node:os';
|
|
|
2
2
|
import path from 'node:path';
|
|
3
3
|
import { ensureDir, exists, nowIso, readJson, writeJsonAtomic } from '../fsx.js';
|
|
4
4
|
export const LOCAL_MODEL_CONFIG_SCHEMA = 'sks.local-model-config.v1';
|
|
5
|
+
export const LOCAL_MODEL_CONFIG_SCHEMA_V2 = 'sks.local-model-config.v2';
|
|
5
6
|
export const OLLAMA_WORKER_CONFIG_SCHEMA = 'sks.ollama-worker-config.v1';
|
|
6
7
|
export const DEFAULT_OLLAMA_CODER_MODEL = 'rafw007/qwen36-a3b-claude-coder:q4_K_M';
|
|
7
8
|
export const DEFAULT_OLLAMA_BASE_URL = 'http://127.0.0.1:11434';
|
|
9
|
+
export const DEFAULT_MLX_LM_MODEL = 'mlx-community/Qwen3.6-35B-A3B-4bit';
|
|
10
|
+
export const DEFAULT_MLX_LM_BASE_URL = 'http://127.0.0.1:8080';
|
|
8
11
|
export const DEFAULT_OLLAMA_KEEP_ALIVE = '30m';
|
|
9
12
|
export const DEFAULT_OLLAMA_TIMEOUT_MS = 120_000;
|
|
10
13
|
export const DEFAULT_OLLAMA_THINK = false;
|
|
14
|
+
export const LOCAL_LLM_SMOKE_TTL_MS = 24 * 60 * 60 * 1000;
|
|
11
15
|
export function localModelConfigPath() {
|
|
12
16
|
return process.env.SKS_LOCAL_MODEL_CONFIG
|
|
13
17
|
? path.resolve(process.env.SKS_LOCAL_MODEL_CONFIG)
|
|
@@ -30,28 +34,38 @@ export async function resolveOllamaWorkerConfig(input = {}) {
|
|
|
30
34
|
const explicitDisable = boolEnv(process.env.SKS_OLLAMA_WORKERS) === false;
|
|
31
35
|
const explicitEnable = boolEnv(process.env.SKS_OLLAMA_WORKERS) === true || input.ollamaEnabled === true || input.backend === 'ollama';
|
|
32
36
|
const enabled = explicitDisable ? false : explicitEnable || stored.enabled === true;
|
|
33
|
-
const
|
|
34
|
-
const
|
|
37
|
+
const explicitProvider = firstText(process.env.SKS_LOCAL_LLM_PROVIDER, input.provider, input.backend === 'ollama' ? 'ollama' : '');
|
|
38
|
+
const provider = explicitProvider ? normalizeProvider(explicitProvider) : normalizeProvider(stored.provider);
|
|
39
|
+
const storedMatchesProvider = stored.provider === provider;
|
|
40
|
+
const model = firstText(process.env.SKS_LOCAL_LLM_MODEL, process.env.SKS_OLLAMA_MODEL, input.model, storedMatchesProvider ? stored.model : '', defaultModelForProvider(provider));
|
|
41
|
+
const baseUrl = trimTrailingSlash(firstText(process.env.SKS_LOCAL_LLM_BASE_URL, process.env.SKS_OLLAMA_BASE_URL, input.baseUrl, storedMatchesProvider ? stored.base_url : '', defaultBaseUrlForProvider(provider)));
|
|
35
42
|
const keepAlive = firstText(process.env.SKS_OLLAMA_KEEP_ALIVE, input.keepAlive, stored.keep_alive, DEFAULT_OLLAMA_KEEP_ALIVE);
|
|
36
43
|
const timeoutMs = positiveNumber(process.env.SKS_OLLAMA_TIMEOUT_MS, input.timeoutMs, stored.timeout_ms, DEFAULT_OLLAMA_TIMEOUT_MS);
|
|
37
44
|
const temperature = finiteNumber(process.env.SKS_OLLAMA_TEMPERATURE, input.temperature, stored.temperature, 0.1);
|
|
38
45
|
const think = boolEnv(process.env.SKS_OLLAMA_THINK) ?? input.think ?? stored.think ?? DEFAULT_OLLAMA_THINK;
|
|
46
|
+
const status = enabled ? resolveEnabledStatus(stored) : 'disabled';
|
|
39
47
|
const blockers = [
|
|
40
48
|
...(enabled ? [] : ['ollama_workers_disabled']),
|
|
41
|
-
...(
|
|
42
|
-
...(!
|
|
49
|
+
...(enabled && status !== 'verified' ? [`local_llm_${status}`] : []),
|
|
50
|
+
...(!model ? ['local_model_missing'] : []),
|
|
51
|
+
...(!baseUrl ? ['local_model_base_url_missing'] : [])
|
|
43
52
|
];
|
|
44
53
|
return {
|
|
45
54
|
schema: OLLAMA_WORKER_CONFIG_SCHEMA,
|
|
46
55
|
ok: blockers.length === 0,
|
|
47
56
|
enabled,
|
|
48
|
-
|
|
57
|
+
status,
|
|
58
|
+
provider,
|
|
49
59
|
model,
|
|
50
60
|
base_url: baseUrl,
|
|
61
|
+
endpoint: baseUrl,
|
|
51
62
|
keep_alive: keepAlive,
|
|
52
63
|
timeout_ms: timeoutMs,
|
|
53
64
|
temperature,
|
|
54
65
|
think,
|
|
66
|
+
policy: stored.policy,
|
|
67
|
+
capability: stored.capability,
|
|
68
|
+
last_smoke: stored.last_smoke,
|
|
55
69
|
config_path: localModelConfigPath(),
|
|
56
70
|
explicit_disable: explicitDisable,
|
|
57
71
|
explicit_enable: explicitEnable,
|
|
@@ -59,25 +73,62 @@ export async function resolveOllamaWorkerConfig(input = {}) {
|
|
|
59
73
|
};
|
|
60
74
|
}
|
|
61
75
|
export function normalizeLocalModelConfig(raw = {}) {
|
|
76
|
+
const enabled = raw.enabled === true;
|
|
77
|
+
const lastSmoke = normalizeSmoke(raw.last_smoke || raw.lastSmoke || null);
|
|
78
|
+
const status = enabled ? normalizeStatus(raw.status, lastSmoke) : 'disabled';
|
|
79
|
+
const provider = normalizeProvider(raw.provider);
|
|
80
|
+
const baseUrl = trimTrailingSlash(firstText(raw.base_url, raw.baseUrl, raw.endpoint, defaultBaseUrlForProvider(provider)));
|
|
62
81
|
return {
|
|
63
|
-
schema:
|
|
82
|
+
schema: LOCAL_MODEL_CONFIG_SCHEMA_V2,
|
|
64
83
|
...(raw.generated_at ? { generated_at: String(raw.generated_at) } : { generated_at: nowIso() }),
|
|
65
84
|
...(raw.updated_at ? { updated_at: String(raw.updated_at) } : {}),
|
|
66
|
-
enabled
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
85
|
+
enabled,
|
|
86
|
+
status,
|
|
87
|
+
provider,
|
|
88
|
+
model: firstText(raw.model, defaultModelForProvider(provider)),
|
|
89
|
+
endpoint: baseUrl,
|
|
90
|
+
base_url: baseUrl,
|
|
70
91
|
keep_alive: firstText(raw.keep_alive, raw.keepAlive, DEFAULT_OLLAMA_KEEP_ALIVE),
|
|
71
92
|
timeout_ms: positiveNumber(raw.timeout_ms, raw.timeoutMs, DEFAULT_OLLAMA_TIMEOUT_MS),
|
|
72
93
|
temperature: finiteNumber(raw.temperature, 0.1),
|
|
73
94
|
think: typeof raw.think === 'boolean' ? raw.think : DEFAULT_OLLAMA_THINK,
|
|
74
|
-
policy:
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
}
|
|
95
|
+
policy: normalizePolicy(raw.policy),
|
|
96
|
+
capability: normalizeCapability(raw.capability),
|
|
97
|
+
last_smoke: lastSmoke,
|
|
98
|
+
blockers: Array.isArray(raw.blockers) ? raw.blockers.map(String) : status === 'blocked' ? ['local_llm_smoke_failed'] : []
|
|
79
99
|
};
|
|
80
100
|
}
|
|
101
|
+
export function applyLocalLlmSmokeResult(config, smoke) {
|
|
102
|
+
const blockers = Array.isArray(smoke.blockers) ? smoke.blockers.map(String) : [];
|
|
103
|
+
const status = smoke.skipped
|
|
104
|
+
? 'enabled_unverified'
|
|
105
|
+
: smoke.ok && smoke.schema_valid !== false
|
|
106
|
+
? 'verified'
|
|
107
|
+
: smoke.status === 'degraded'
|
|
108
|
+
? 'degraded'
|
|
109
|
+
: 'blocked';
|
|
110
|
+
return normalizeLocalModelConfig({
|
|
111
|
+
...config,
|
|
112
|
+
enabled: true,
|
|
113
|
+
status,
|
|
114
|
+
capability: {
|
|
115
|
+
...config.capability,
|
|
116
|
+
api_reachable: smoke.ok !== false,
|
|
117
|
+
model_installed: smoke.ok !== false
|
|
118
|
+
},
|
|
119
|
+
last_smoke: {
|
|
120
|
+
...smoke,
|
|
121
|
+
status
|
|
122
|
+
},
|
|
123
|
+
blockers
|
|
124
|
+
});
|
|
125
|
+
}
|
|
126
|
+
export function localModelSmokeFresh(smoke, now = Date.now()) {
|
|
127
|
+
if (!smoke?.ok || smoke.schema_valid === false || !smoke.ran_at)
|
|
128
|
+
return false;
|
|
129
|
+
const ranAt = Date.parse(smoke.ran_at);
|
|
130
|
+
return Number.isFinite(ranAt) && now - ranAt <= LOCAL_LLM_SMOKE_TTL_MS;
|
|
131
|
+
}
|
|
81
132
|
export function boolEnv(value) {
|
|
82
133
|
const text = String(value ?? '').trim().toLowerCase();
|
|
83
134
|
if (!text)
|
|
@@ -99,6 +150,104 @@ function firstText(...values) {
|
|
|
99
150
|
function trimTrailingSlash(value) {
|
|
100
151
|
return value.replace(/\/+$/, '');
|
|
101
152
|
}
|
|
153
|
+
export function normalizeProvider(...values) {
|
|
154
|
+
for (const value of values) {
|
|
155
|
+
const text = String(value ?? '').trim().toLowerCase();
|
|
156
|
+
if (!text)
|
|
157
|
+
continue;
|
|
158
|
+
if (['ollama'].includes(text))
|
|
159
|
+
return 'ollama';
|
|
160
|
+
if (['mlx', 'mlx-lm', 'mlx_lm', 'mlxlm'].includes(text))
|
|
161
|
+
return 'mlx-lm';
|
|
162
|
+
if (['openai-compatible', 'openai_compatible', 'openai', 'openai-compatible-local'].includes(text))
|
|
163
|
+
return 'openai-compatible';
|
|
164
|
+
}
|
|
165
|
+
return 'ollama';
|
|
166
|
+
}
|
|
167
|
+
export function defaultModelForProvider(provider) {
|
|
168
|
+
if (provider === 'mlx-lm')
|
|
169
|
+
return DEFAULT_MLX_LM_MODEL;
|
|
170
|
+
if (provider === 'openai-compatible')
|
|
171
|
+
return '';
|
|
172
|
+
return DEFAULT_OLLAMA_CODER_MODEL;
|
|
173
|
+
}
|
|
174
|
+
export function defaultBaseUrlForProvider(provider) {
|
|
175
|
+
if (provider === 'mlx-lm')
|
|
176
|
+
return DEFAULT_MLX_LM_BASE_URL;
|
|
177
|
+
if (provider === 'openai-compatible')
|
|
178
|
+
return '';
|
|
179
|
+
return DEFAULT_OLLAMA_BASE_URL;
|
|
180
|
+
}
|
|
181
|
+
function resolveEnabledStatus(config) {
|
|
182
|
+
if (config.status === 'verified' && !localModelSmokeFresh(config.last_smoke))
|
|
183
|
+
return 'enabled_unverified';
|
|
184
|
+
return config.status === 'disabled' ? 'enabled_unverified' : config.status;
|
|
185
|
+
}
|
|
186
|
+
function normalizeStatus(value, smoke) {
|
|
187
|
+
const text = String(value ?? '').trim();
|
|
188
|
+
const known = ['disabled', 'enabled_unverified', 'verified', 'degraded', 'blocked'];
|
|
189
|
+
if (known.includes(text)) {
|
|
190
|
+
if (text === 'verified' && !localModelSmokeFresh(smoke))
|
|
191
|
+
return 'enabled_unverified';
|
|
192
|
+
return text;
|
|
193
|
+
}
|
|
194
|
+
return localModelSmokeFresh(smoke) ? 'verified' : 'enabled_unverified';
|
|
195
|
+
}
|
|
196
|
+
function normalizePolicy(value) {
|
|
197
|
+
const defaultAllowed = ['simple_patch_envelope', 'read_only_collection', 'grep_like_qa', 'test_generation_draft'];
|
|
198
|
+
const allowed = [
|
|
199
|
+
...defaultAllowed,
|
|
200
|
+
...(Array.isArray(value?.allowed_task_classes) ? value.allowed_task_classes : []),
|
|
201
|
+
...(Array.isArray(value?.allowed_work) ? value.allowed_work : [])
|
|
202
|
+
];
|
|
203
|
+
const forbidden = Array.isArray(value?.forbidden_task_classes) ? value.forbidden_task_classes : [
|
|
204
|
+
'planning',
|
|
205
|
+
'strategy',
|
|
206
|
+
'final_review',
|
|
207
|
+
'verification_authority',
|
|
208
|
+
'safety_authority',
|
|
209
|
+
'integration_authority'
|
|
210
|
+
];
|
|
211
|
+
return {
|
|
212
|
+
role: 'worker_only',
|
|
213
|
+
allowed_task_classes: [...new Set(allowed.map(String))],
|
|
214
|
+
forbidden_task_classes: forbidden.map(String),
|
|
215
|
+
requires_gpt_final: value?.requires_gpt_final !== false
|
|
216
|
+
};
|
|
217
|
+
}
|
|
218
|
+
function normalizeCapability(value) {
|
|
219
|
+
return {
|
|
220
|
+
api_reachable: value?.api_reachable === true,
|
|
221
|
+
model_installed: value?.model_installed === true,
|
|
222
|
+
supports_streaming: value?.supports_streaming !== false,
|
|
223
|
+
supports_json_schema: value?.supports_json_schema === true,
|
|
224
|
+
supports_tools: value?.supports_tools === true,
|
|
225
|
+
supports_images: value?.supports_images === true,
|
|
226
|
+
context_window: positiveNumber(value?.context_window, 32768),
|
|
227
|
+
max_parallel_requests: Math.max(1, Math.min(16, positiveNumber(value?.max_parallel_requests, 4)))
|
|
228
|
+
};
|
|
229
|
+
}
|
|
230
|
+
function normalizeSmoke(value) {
|
|
231
|
+
if (!value || typeof value !== 'object')
|
|
232
|
+
return null;
|
|
233
|
+
return {
|
|
234
|
+
ok: value.ok === true,
|
|
235
|
+
...(value.skipped === true ? { skipped: true } : {}),
|
|
236
|
+
...(value.ran_at ? { ran_at: String(value.ran_at) } : {}),
|
|
237
|
+
...(value.prompt_hash ? { prompt_hash: String(value.prompt_hash) } : {}),
|
|
238
|
+
...(Number.isFinite(Number(value.latency_ms)) ? { latency_ms: Number(value.latency_ms) } : {}),
|
|
239
|
+
...(Number.isFinite(Number(value.tokens_per_second)) ? { tokens_per_second: Number(value.tokens_per_second) } : {}),
|
|
240
|
+
...(typeof value.schema_valid === 'boolean' ? { schema_valid: value.schema_valid } : {}),
|
|
241
|
+
...(value.result_path ? { result_path: String(value.result_path) } : {}),
|
|
242
|
+
...(normalizeKnownStatus(value.status) ? { status: normalizeKnownStatus(value.status) } : {}),
|
|
243
|
+
...(value.reason ? { reason: String(value.reason) } : {}),
|
|
244
|
+
...(Array.isArray(value.blockers) ? { blockers: value.blockers.map(String) } : {})
|
|
245
|
+
};
|
|
246
|
+
}
|
|
247
|
+
function normalizeKnownStatus(value) {
|
|
248
|
+
const text = String(value ?? '').trim();
|
|
249
|
+
return ['disabled', 'enabled_unverified', 'verified', 'degraded', 'blocked'].includes(text) ? text : undefined;
|
|
250
|
+
}
|
|
102
251
|
function positiveNumber(...values) {
|
|
103
252
|
for (const value of values) {
|
|
104
253
|
const n = Number(value);
|
|
@@ -0,0 +1,119 @@
|
|
|
1
|
+
import { runProcess, which } from '../fsx.js';
|
|
2
|
+
import { compareSemverLike, parseCodexVersionText } from '../codex-compat/codex-version-policy.js';
|
|
3
|
+
import { createHash } from 'node:crypto';
|
|
4
|
+
export const CODEX_0_137_BASELINE_TAG = 'rust-v0.137.0';
|
|
5
|
+
export const CODEX_0_137_VERSION = '0.137.0';
|
|
6
|
+
export const CODEX_0_137_SCHEMA = 'sks.codex-0.137-compat.v1';
|
|
7
|
+
export function codex0137Matrix(input = {}) {
|
|
8
|
+
const version = parseCodexVersionText(input.version) || input.version || null;
|
|
9
|
+
const available = input.available !== false && Boolean(version);
|
|
10
|
+
const meets = available && compareSemverLike(version, CODEX_0_137_VERSION) >= 0;
|
|
11
|
+
const pluginJsonDetected = looksLikeJson(input.pluginListText || '');
|
|
12
|
+
const runtimeDetected = /runtime|model|provider|thread/i.test(input.debugModelsText || '');
|
|
13
|
+
const approvalsDetected = /approval|environment|sandbox|profile|permission/i.test(input.doctorText || '');
|
|
14
|
+
const localOrBaseline = (detected) => detected ? 'detected' : meets ? 'release_baseline' : available ? 'blocked' : 'unavailable';
|
|
15
|
+
const capabilities = [
|
|
16
|
+
capability('plugin_list_json', 'P0', localOrBaseline(pluginJsonDetected), '`codex plugin list --json` parses as JSON or 0.137 baseline records it.'),
|
|
17
|
+
capability('thread_runtime_choice', 'P0', localOrBaseline(runtimeDetected), 'Thread/runtime choice is tracked by SKS per-thread proof and Codex runtime evidence.'),
|
|
18
|
+
capability('environment_scoped_approvals', 'P0', localOrBaseline(approvalsDetected), 'Approval proof carries environment identity and sandbox scope.'),
|
|
19
|
+
capability('managed_proxy_ca_bundle_child_commands', 'P1', meets ? 'release_baseline' : available ? 'blocked' : 'unavailable', 'Managed proxy CA bundle propagation is release-baseline plus SKS env proof.'),
|
|
20
|
+
capability('python_sdk_app_server_json_rpc', 'P0', meets ? 'release_baseline' : available ? 'blocked' : 'unavailable', 'Python SDK controls local app-server over JSON-RPC per current Codex manual.')
|
|
21
|
+
];
|
|
22
|
+
const below = available && version ? compareSemverLike(version, CODEX_0_137_VERSION) < 0 : false;
|
|
23
|
+
const blockers = [
|
|
24
|
+
...(input.requireReal && (!version || below) ? ['codex_0_137_required_but_not_detected'] : []),
|
|
25
|
+
...capabilities
|
|
26
|
+
.filter((row) => input.requireReal && row.priority === 'P0' && (row.status === 'blocked' || row.status === 'unavailable'))
|
|
27
|
+
.map((row) => `codex_0_137_capability_unavailable:${row.id}`)
|
|
28
|
+
];
|
|
29
|
+
return {
|
|
30
|
+
schema: CODEX_0_137_SCHEMA,
|
|
31
|
+
baseline: CODEX_0_137_BASELINE_TAG,
|
|
32
|
+
required_version: CODEX_0_137_VERSION,
|
|
33
|
+
release_evidence: {
|
|
34
|
+
upstream: 'openai/codex',
|
|
35
|
+
npm_package: '@openai/codex-sdk',
|
|
36
|
+
npm_version: '0.137.0',
|
|
37
|
+
manual_source: 'https://developers.openai.com/codex/codex-manual.md',
|
|
38
|
+
checked_topics: ['Codex SDK TypeScript', 'Codex SDK Python', 'CLI plugin/json/runtime/approval surfaces']
|
|
39
|
+
},
|
|
40
|
+
inherited_baselines: ['rust-v0.136.0', 'rust-v0.135.0', 'rust-v0.134.0'],
|
|
41
|
+
detected_version: version,
|
|
42
|
+
available,
|
|
43
|
+
require_real: input.requireReal === true,
|
|
44
|
+
capabilities,
|
|
45
|
+
plugin_list_json_supported: supported(capabilities, 'plugin_list_json'),
|
|
46
|
+
thread_runtime_choice_supported: supported(capabilities, 'thread_runtime_choice'),
|
|
47
|
+
environment_scoped_approvals_supported: supported(capabilities, 'environment_scoped_approvals'),
|
|
48
|
+
ok: blockers.length === 0,
|
|
49
|
+
warnings: !input.requireReal && (!version || below) ? [`Codex ${CODEX_0_137_BASELINE_TAG} not detected; release:check treats this as warning-only.`] : [],
|
|
50
|
+
blockers
|
|
51
|
+
};
|
|
52
|
+
}
|
|
53
|
+
export async function collectCodex0137LocalEvidence(opts = {}) {
|
|
54
|
+
const bin = opts.codexBin || await which('codex');
|
|
55
|
+
if (!bin) {
|
|
56
|
+
return {
|
|
57
|
+
available: false,
|
|
58
|
+
versionText: '',
|
|
59
|
+
pluginListText: '',
|
|
60
|
+
debugModelsText: '',
|
|
61
|
+
doctorText: '',
|
|
62
|
+
warnings: ['codex_binary_missing']
|
|
63
|
+
};
|
|
64
|
+
}
|
|
65
|
+
const run = async (args) => runProcess(bin, args, { timeoutMs: 10000, maxOutputBytes: 64 * 1024 }).catch((err) => ({
|
|
66
|
+
code: 1,
|
|
67
|
+
stdout: '',
|
|
68
|
+
stderr: err.message || String(err)
|
|
69
|
+
}));
|
|
70
|
+
const [version, pluginList, debugModels, doctor] = await Promise.all([
|
|
71
|
+
run(['--version']),
|
|
72
|
+
run(['plugin', 'list', '--json']),
|
|
73
|
+
run(['debug', 'models', '--bundled']),
|
|
74
|
+
run(['doctor', '--json'])
|
|
75
|
+
]);
|
|
76
|
+
return {
|
|
77
|
+
available: version.code === 0,
|
|
78
|
+
versionText: `${version.stdout || ''}${version.stderr || ''}`.trim(),
|
|
79
|
+
pluginListText: `${pluginList.stdout || ''}${pluginList.stderr || ''}`,
|
|
80
|
+
debugModelsText: summarizeCodexCommandOutput(`${debugModels.stdout || ''}${debugModels.stderr || ''}`, ['runtime', 'model', 'provider', 'thread']),
|
|
81
|
+
doctorText: summarizeCodexCommandOutput(`${doctor.stdout || ''}${doctor.stderr || ''}`, ['approval', 'environment', 'sandbox', 'profile', 'permission']),
|
|
82
|
+
warnings: [
|
|
83
|
+
...(pluginList.code === 0 ? [] : ['codex_plugin_list_json_unavailable']),
|
|
84
|
+
...(debugModels.code === 0 ? [] : ['codex_debug_models_unavailable']),
|
|
85
|
+
...(doctor.code === 0 ? [] : ['codex_doctor_json_unavailable'])
|
|
86
|
+
]
|
|
87
|
+
};
|
|
88
|
+
}
|
|
89
|
+
function capability(id, priority, status, detector) {
|
|
90
|
+
return { id, priority, status, detector, notes: [] };
|
|
91
|
+
}
|
|
92
|
+
function supported(capabilities, id) {
|
|
93
|
+
const status = capabilities.find((capability) => capability.id === id)?.status;
|
|
94
|
+
return status === 'detected' || status === 'release_baseline';
|
|
95
|
+
}
|
|
96
|
+
function looksLikeJson(value) {
|
|
97
|
+
const text = String(value || '').trim();
|
|
98
|
+
if (!text)
|
|
99
|
+
return false;
|
|
100
|
+
try {
|
|
101
|
+
JSON.parse(text);
|
|
102
|
+
return true;
|
|
103
|
+
}
|
|
104
|
+
catch {
|
|
105
|
+
return false;
|
|
106
|
+
}
|
|
107
|
+
}
|
|
108
|
+
function summarizeCodexCommandOutput(value, keywords) {
|
|
109
|
+
const text = String(value || '');
|
|
110
|
+
const lower = text.toLowerCase();
|
|
111
|
+
const matched = keywords.filter((keyword) => lower.includes(keyword.toLowerCase()));
|
|
112
|
+
return [
|
|
113
|
+
`raw_output_redacted=true`,
|
|
114
|
+
`bytes=${Buffer.byteLength(text, 'utf8')}`,
|
|
115
|
+
`sha256=${createHash('sha256').update(text).digest('hex')}`,
|
|
116
|
+
`matched_keywords=${matched.join(',') || 'none'}`
|
|
117
|
+
].join(' ');
|
|
118
|
+
}
|
|
119
|
+
//# sourceMappingURL=codex-0-137-compat.js.map
|
|
@@ -7,7 +7,8 @@ export async function writeCodexControlProof(root, input) {
|
|
|
7
7
|
schema: CODEX_CONTROL_PROOF_SCHEMA,
|
|
8
8
|
generated_at: nowIso(),
|
|
9
9
|
ok: input.result.ok === true,
|
|
10
|
-
backend:
|
|
10
|
+
backend: input.result.backend,
|
|
11
|
+
backend_family: input.result.backend_family,
|
|
11
12
|
route: input.task.route,
|
|
12
13
|
mission_id: input.task.missionId,
|
|
13
14
|
work_item_id: input.task.workItemId || null,
|
|
@@ -22,6 +23,8 @@ export async function writeCodexControlProof(root, input) {
|
|
|
22
23
|
output_schema_id: input.task.outputSchemaId,
|
|
23
24
|
worker_result_path: input.result.workerResultPath,
|
|
24
25
|
patch_envelope_path: input.result.patchEnvelopePath || null,
|
|
26
|
+
local_llm_proof_path: input.result.localLlmProofPath || null,
|
|
27
|
+
python_sdk_proof_path: input.result.pythonSdkProofPath || null,
|
|
25
28
|
sandbox: input.sandbox || null,
|
|
26
29
|
env: input.envProof || null,
|
|
27
30
|
config: input.config ? redactCodexSdkConfig(input.config) : null,
|
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
import { nowIso, randomId } from '../fsx.js';
|
|
2
|
+
import { GPT_FINAL_ARBITER_RESULT_SCHEMA_ID } from './gpt-final-review-schema.js';
|
|
2
3
|
export function fakeCodexSdkAllowed() {
|
|
3
4
|
return process.env.NODE_ENV === 'test'
|
|
4
5
|
|| process.env.SKS_CODEX_SDK_FAKE === '1'
|
|
@@ -25,6 +26,25 @@ export async function runFakeCodexSdkTask(input) {
|
|
|
25
26
|
};
|
|
26
27
|
}
|
|
27
28
|
function fakeStructuredOutput(input) {
|
|
29
|
+
if (input.outputSchemaId === GPT_FINAL_ARBITER_RESULT_SCHEMA_ID) {
|
|
30
|
+
const unsafe = /\b(truncate|delete all|drop table|credential)\b/i.test(input.prompt || '');
|
|
31
|
+
return {
|
|
32
|
+
schema: GPT_FINAL_ARBITER_RESULT_SCHEMA_ID,
|
|
33
|
+
status: unsafe ? 'rejected' : 'approved',
|
|
34
|
+
summary: unsafe
|
|
35
|
+
? 'Fake Codex SDK GPT final arbiter rejected an unsafe candidate for hermetic verification.'
|
|
36
|
+
: 'Fake Codex SDK GPT final arbiter approved the candidate for hermetic verification.',
|
|
37
|
+
gpt_review_findings: unsafe ? [{ severity: 'high', message: 'unsafe candidate rejected' }] : [],
|
|
38
|
+
accepted_patch_envelopes: unsafe ? [] : [],
|
|
39
|
+
modified_patch_envelopes: [],
|
|
40
|
+
rejected_patch_envelopes: unsafe ? [{ reason: 'unsafe candidate' }] : [],
|
|
41
|
+
required_followup_work: unsafe ? [{ blocker: 'unsafe_candidate_patch' }] : [],
|
|
42
|
+
verification_plan: ['schema validation', 'local collaboration final gate'],
|
|
43
|
+
rollback_notes: [],
|
|
44
|
+
blockers: unsafe ? ['unsafe_candidate_patch'] : [],
|
|
45
|
+
confidence: unsafe ? 'medium' : 'high'
|
|
46
|
+
};
|
|
47
|
+
}
|
|
28
48
|
return {
|
|
29
49
|
status: 'done',
|
|
30
50
|
summary: `Fake Codex SDK task completed for ${input.workItemId || input.route}.`,
|
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
import { CODEX_AGENT_WORKER_RESULT_SCHEMA_ID, codexAgentWorkerResultSchema } from './schemas/agent-worker-result.schema.js';
|
|
2
|
+
import { GPT_FINAL_ARBITER_RESULT_SCHEMA_ID, gptFinalArbiterResultSchema } from './gpt-final-review-schema.js';
|
|
2
3
|
export const CODEX_OUTPUT_SCHEMA_IDS = [
|
|
3
4
|
CODEX_AGENT_WORKER_RESULT_SCHEMA_ID,
|
|
4
5
|
'sks.patch-envelope-result.v1',
|
|
@@ -6,11 +7,14 @@ export const CODEX_OUTPUT_SCHEMA_IDS = [
|
|
|
6
7
|
'sks.research-digest.v1',
|
|
7
8
|
'sks.release-failure-analysis.v1',
|
|
8
9
|
'sks.ux-ppt-review-result.v1',
|
|
9
|
-
'sks.core-skill-heldout-validation.v1'
|
|
10
|
+
'sks.core-skill-heldout-validation.v1',
|
|
11
|
+
GPT_FINAL_ARBITER_RESULT_SCHEMA_ID
|
|
10
12
|
];
|
|
11
13
|
export function resolveCodexOutputSchema(schemaId, fallback) {
|
|
12
14
|
if (schemaId === CODEX_AGENT_WORKER_RESULT_SCHEMA_ID)
|
|
13
15
|
return codexAgentWorkerResultSchema;
|
|
16
|
+
if (schemaId === GPT_FINAL_ARBITER_RESULT_SCHEMA_ID)
|
|
17
|
+
return gptFinalArbiterResultSchema;
|
|
14
18
|
if (fallback && typeof fallback === 'object')
|
|
15
19
|
return fallback;
|
|
16
20
|
return {
|
|
@@ -37,7 +37,7 @@ export async function detectCodexSdkCapability(input = {}) {
|
|
|
37
37
|
node_compatible: nodeCompatible,
|
|
38
38
|
dynamic_import_ok: dynamicImportOk,
|
|
39
39
|
structured_output_fake_smoke: structuredFake,
|
|
40
|
-
setup_action: blockers.includes('codex_sdk_unavailable') ? 'npm install @openai/codex-sdk@0.
|
|
40
|
+
setup_action: blockers.includes('codex_sdk_unavailable') ? 'npm install @openai/codex-sdk@0.137.0' : null,
|
|
41
41
|
blockers
|
|
42
42
|
};
|
|
43
43
|
}
|