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.
Files changed (126) hide show
  1. package/README.md +12 -8
  2. package/crates/sks-core/Cargo.lock +1 -1
  3. package/crates/sks-core/Cargo.toml +1 -1
  4. package/crates/sks-core/src/main.rs +1 -1
  5. package/dist/.sks-build-stamp.json +4 -4
  6. package/dist/bin/sks.js +1 -1
  7. package/dist/build-manifest.json +86 -8
  8. package/dist/commands/doctor.js +14 -0
  9. package/dist/core/agents/agent-orchestrator.js +70 -4
  10. package/dist/core/agents/agent-patch-proof.js +5 -0
  11. package/dist/core/agents/agent-proof-evidence.js +61 -0
  12. package/dist/core/agents/agent-roster.js +35 -6
  13. package/dist/core/agents/agent-schema.js +1 -1
  14. package/dist/core/agents/native-worker-backend-router.js +31 -9
  15. package/dist/core/agents/ollama-worker-config.js +164 -15
  16. package/dist/core/codex/codex-0-137-compat.js +119 -0
  17. package/dist/core/codex-control/codex-control-proof.js +4 -1
  18. package/dist/core/codex-control/codex-fake-sdk-adapter.js +20 -0
  19. package/dist/core/codex-control/codex-output-schemas.js +5 -1
  20. package/dist/core/codex-control/codex-sdk-capability.js +1 -1
  21. package/dist/core/codex-control/codex-task-runner.js +329 -5
  22. package/dist/core/codex-control/gpt-final-arbiter.js +160 -0
  23. package/dist/core/codex-control/gpt-final-context-compressor.js +17 -0
  24. package/dist/core/codex-control/gpt-final-proof-pack.js +120 -0
  25. package/dist/core/codex-control/gpt-final-review-schema.js +71 -0
  26. package/dist/core/codex-control/python-codex-sdk-adapter.js +197 -0
  27. package/dist/core/codex-control/python-codex-sdk-event-translator.js +14 -0
  28. package/dist/core/commands/local-model-command.js +79 -18
  29. package/dist/core/commands/naruto-command.js +195 -12
  30. package/dist/core/commands/run-command.js +6 -2
  31. package/dist/core/doctor/doctor-readiness-matrix.js +34 -0
  32. package/dist/core/feature-fixtures.js +4 -0
  33. package/dist/core/fsx.js +1 -1
  34. package/dist/core/git-simple.js +143 -4
  35. package/dist/core/local-llm/local-collaboration-policy.js +93 -0
  36. package/dist/core/local-llm/local-llm-backpressure.js +20 -0
  37. package/dist/core/local-llm/local-llm-capability.js +29 -0
  38. package/dist/core/local-llm/local-llm-client.js +100 -0
  39. package/dist/core/local-llm/local-llm-config.js +20 -0
  40. package/dist/core/local-llm/local-llm-context-cache.js +21 -0
  41. package/dist/core/local-llm/local-llm-control-adapter.js +101 -0
  42. package/dist/core/local-llm/local-llm-json-repair.js +52 -0
  43. package/dist/core/local-llm/local-llm-metrics.js +42 -0
  44. package/dist/core/local-llm/local-llm-ollama-client.js +67 -0
  45. package/dist/core/local-llm/local-llm-openai-compatible-client.js +30 -0
  46. package/dist/core/local-llm/local-llm-prompt-cache.js +12 -0
  47. package/dist/core/local-llm/local-llm-scheduler.js +29 -0
  48. package/dist/core/local-llm/local-llm-schema-enforcer.js +15 -0
  49. package/dist/core/local-llm/local-llm-smoke.js +83 -0
  50. package/dist/core/local-llm/local-llm-warmup.js +20 -0
  51. package/dist/core/local-llm/local-worker-eligibility.js +27 -0
  52. package/dist/core/naruto/hardware-capacity-probe.js +36 -0
  53. package/dist/core/naruto/naruto-active-pool.js +118 -0
  54. package/dist/core/naruto/naruto-backpressure.js +13 -0
  55. package/dist/core/naruto/naruto-concurrency-governor.js +65 -0
  56. package/dist/core/naruto/naruto-finalizer.js +18 -0
  57. package/dist/core/naruto/naruto-generation-scheduler.js +18 -0
  58. package/dist/core/naruto/naruto-gpt-final-pack.js +49 -0
  59. package/dist/core/naruto/naruto-parallel-patch-apply.js +95 -0
  60. package/dist/core/naruto/naruto-patch-transaction-batch.js +42 -0
  61. package/dist/core/naruto/naruto-role-policy.js +107 -0
  62. package/dist/core/naruto/naruto-verification-dag.js +42 -0
  63. package/dist/core/naruto/naruto-verification-pool.js +18 -0
  64. package/dist/core/naruto/naruto-work-graph.js +198 -0
  65. package/dist/core/naruto/naruto-work-item.js +40 -0
  66. package/dist/core/naruto/naruto-work-stealing.js +11 -0
  67. package/dist/core/naruto/resource-pressure-monitor.js +32 -0
  68. package/dist/core/pipeline/final-gpt-patch-stage.js +31 -0
  69. package/dist/core/pipeline/final-gpt-review-stage.js +5 -0
  70. package/dist/core/pipeline/finalize-pipeline-result.js +58 -0
  71. package/dist/core/pipeline/gpt-final-required.js +12 -0
  72. package/dist/core/prompt/prompt-placeholder-guard.js +30 -0
  73. package/dist/core/router/capability-card.js +13 -0
  74. package/dist/core/router/route-cache.js +3 -0
  75. package/dist/core/router/ultra-router.js +2 -1
  76. package/dist/core/routes.js +4 -4
  77. package/dist/core/safety/mutation-guard.js +2 -0
  78. package/dist/core/update-check.js +60 -25
  79. package/dist/core/version.js +1 -1
  80. package/dist/core/zellij/zellij-lane-runtime.js +2 -2
  81. package/dist/core/zellij/zellij-naruto-dashboard.js +36 -0
  82. package/dist/core/zellij/zellij-worker-pane-manager.js +4 -4
  83. package/dist/scripts/blackbox-command-import-smoke.js +10 -1
  84. package/dist/scripts/check-package-boundary.js +12 -3
  85. package/dist/scripts/codex-0-137-compat-check.js +27 -0
  86. package/dist/scripts/codex-environment-scoped-approvals-check.js +10 -0
  87. package/dist/scripts/codex-plugin-list-json-check.js +8 -0
  88. package/dist/scripts/codex-sdk-team-naruto-agent-pipeline-check.js +2 -1
  89. package/dist/scripts/codex-thread-runtime-choice-check.js +10 -0
  90. package/dist/scripts/gpt-final-arbiter-check.js +63 -0
  91. package/dist/scripts/gpt-final-arbiter-performance-check.js +36 -0
  92. package/dist/scripts/local-collab-all-pipelines-final-gpt-check.js +21 -0
  93. package/dist/scripts/local-collab-gpt-final-availability-check.js +58 -0
  94. package/dist/scripts/local-collab-no-local-only-final-check.js +27 -0
  95. package/dist/scripts/local-collab-policy-check.js +17 -0
  96. package/dist/scripts/local-llm-all-pipelines-check.js +11 -0
  97. package/dist/scripts/local-llm-cache-performance-check.js +10 -0
  98. package/dist/scripts/local-llm-capability-check.js +14 -0
  99. package/dist/scripts/local-llm-smoke-check.js +23 -0
  100. package/dist/scripts/local-llm-structured-output-check.js +11 -0
  101. package/dist/scripts/local-llm-throughput-check.js +10 -0
  102. package/dist/scripts/local-llm-tool-call-repair-check.js +10 -0
  103. package/dist/scripts/local-llm-warmup-check.js +11 -0
  104. package/dist/scripts/naruto-active-pool-check.js +27 -0
  105. package/dist/scripts/naruto-concurrency-governor-check.js +52 -0
  106. package/dist/scripts/naruto-gpt-final-pack-check.js +34 -0
  107. package/dist/scripts/naruto-parallel-patch-apply-check.js +41 -0
  108. package/dist/scripts/naruto-real-local-gpt-final-smoke.js +16 -0
  109. package/dist/scripts/naruto-role-distribution-check.js +23 -0
  110. package/dist/scripts/naruto-shadow-clone-swarm-check.js +6 -0
  111. package/dist/scripts/naruto-verification-pool-check.js +36 -0
  112. package/dist/scripts/naruto-work-graph-check.js +24 -0
  113. package/dist/scripts/naruto-zellij-massive-ui-check.js +23 -0
  114. package/dist/scripts/prompt-placeholder-guard-check.js +33 -0
  115. package/dist/scripts/python-codex-sdk-all-pipelines-check.js +47 -0
  116. package/dist/scripts/python-codex-sdk-capability-check.js +75 -0
  117. package/dist/scripts/python-codex-sdk-sandbox-policy-check.js +10 -0
  118. package/dist/scripts/python-codex-sdk-stream-bridge-check.js +12 -0
  119. package/dist/scripts/release-parallel-check.js +1 -1
  120. package/dist/scripts/release-real-check.js +5 -0
  121. package/dist/scripts/zellij-worker-pane-manager-check.js +1 -1
  122. package/package.json +38 -4
  123. package/schemas/local-llm/local-collaboration-policy.schema.json +57 -0
  124. package/schemas/local-llm/local-model-config.schema.json +74 -0
  125. package/schemas/naruto/naruto-concurrency-governor.schema.json +21 -0
  126. package/schemas/naruto/naruto-work-graph.schema.json +22 -0
@@ -1,5 +1,6 @@
1
+ import fs from 'node:fs';
1
2
  import path from 'node:path';
2
- import { appendJsonl, ensureDir, nowIso, writeJsonAtomic } from '../fsx.js';
3
+ import { appendJsonl, ensureDir, nowIso, packageRoot, writeJsonAtomic, writeTextAtomic } from '../fsx.js';
3
4
  import { validateJsonSchemaRecursive } from '../json-schema-validator.js';
4
5
  import { resolveCodexOutputSchema } from './codex-output-schemas.js';
5
6
  import { detectCodexSdkCapability } from './codex-sdk-capability.js';
@@ -12,6 +13,9 @@ import { recordCodexThread } from './codex-thread-registry.js';
12
13
  import { runWithCodexReliabilityShield } from './codex-reliability-shield.js';
13
14
  import { routeCodexTask } from '../router/ultra-router.js';
14
15
  import { writeUltraRouterProof } from '../router/router-proof.js';
16
+ import { readLocalModelConfig } from '../agents/ollama-worker-config.js';
17
+ import { runLocalLlmTask } from '../local-llm/local-llm-control-adapter.js';
18
+ import { detectPythonCodexSdkCapability, runPythonCodexSdkTask } from './python-codex-sdk-adapter.js';
15
19
  export async function runCodexTask(input) {
16
20
  const root = path.resolve(input.mutationLedgerRoot);
17
21
  await ensureDir(root);
@@ -19,13 +23,22 @@ export async function runCodexTask(input) {
19
23
  const routerDecision = routeCodexTask(input);
20
24
  const task = { ...input, tier: input.tier || routerDecision.tier, outputSchema: schema };
21
25
  await writeUltraRouterProof(root, { task, decision: routerDecision });
26
+ const selectedBackend = selectCodexControlBackend(task, routerDecision);
27
+ if (selectedBackend === 'local-llm')
28
+ return runLocalControlTask(root, task, schema, routerDecision);
29
+ if (selectedBackend === 'python-codex-sdk')
30
+ return runPythonControlTask(root, task, schema, routerDecision);
22
31
  const capability = await detectCodexSdkCapability();
23
32
  const sandbox = mapCodexSdkSandboxPolicy(task);
24
33
  const runtime = codexSdkRuntimePolicies(task);
34
+ const bundledCodex = resolveBundledCodexBinary();
35
+ if (bundledCodex && !runtime.env.env.SKS_PYTHON_CODEX_SDK_CODEX_BIN)
36
+ runtime.env.env.SKS_PYTHON_CODEX_SDK_CODEX_BIN = bundledCodex;
25
37
  if (runtime.env.env.HOME)
26
38
  await ensureDir(runtime.env.env.HOME);
27
39
  if (runtime.env.env.CODEX_HOME)
28
40
  await ensureDir(runtime.env.env.CODEX_HOME);
41
+ await ensurePythonCodexLbConfig(runtime.env.env, runtime.config);
29
42
  const fakeAllowed = fakeCodexSdkAllowed();
30
43
  const blockers = [
31
44
  ...(capability.ok || fakeAllowed ? [] : capability.blockers),
@@ -84,6 +97,7 @@ export async function runCodexTask(input) {
84
97
  const result = {
85
98
  ok: finalBlockers.length === 0,
86
99
  backend: 'codex-sdk',
100
+ backend_family: fakeAllowed ? 'fake' : 'remote-gpt',
87
101
  sdkThreadId: String(adapterResult?.sdkThreadId || ''),
88
102
  sdkRunId: adapterResult?.sdkRunId ? String(adapterResult.sdkRunId) : null,
89
103
  streamEventCount: events.length,
@@ -99,6 +113,8 @@ export async function runCodexTask(input) {
99
113
  translatedEventCount: translatedEvents.length
100
114
  };
101
115
  await recordCodexThread(root, {
116
+ backend: result.backend,
117
+ backend_family: result.backend_family,
102
118
  route: task.route,
103
119
  mission_id: task.missionId,
104
120
  work_item_id: task.workItemId || null,
@@ -126,7 +142,213 @@ export async function runCodexTask(input) {
126
142
  });
127
143
  return result;
128
144
  }
129
- function normalizeWorkerResult(value, input, blockers, structuredOutputValid) {
145
+ async function runPythonControlTask(root, task, schema, routerDecision) {
146
+ const capability = await detectPythonCodexSdkCapability();
147
+ const runtime = codexSdkRuntimePolicies(task);
148
+ if (runtime.env.env.HOME)
149
+ await ensureDir(runtime.env.env.HOME);
150
+ if (runtime.env.env.CODEX_HOME)
151
+ await ensureDir(runtime.env.env.CODEX_HOME);
152
+ const fakeAllowed = process.env.SKS_PYTHON_CODEX_SDK_FAKE === '1';
153
+ const adapterResult = capability.ok || fakeAllowed
154
+ ? await runPythonCodexSdkTask(task, { env: runtime.env.env, config: runtime.config })
155
+ : { ok: false, events: [], translatedEvents: [], finalResponse: '', threadId: '', turnId: '', blockers: capability.blockers, capability };
156
+ const events = Array.isArray(adapterResult.events) ? adapterResult.events : [];
157
+ const translatedEvents = Array.isArray(adapterResult.translatedEvents) ? adapterResult.translatedEvents : [];
158
+ for (const event of translatedEvents)
159
+ await appendJsonl(path.join(root, 'python-codex-sdk-events.jsonl'), event);
160
+ const structuredOutput = parseStructuredOutput(adapterResult.finalResponse || '');
161
+ const validation = structuredOutput ? validateJsonSchemaRecursive(structuredOutput, schema) : { ok: false, issues: ['structured_output_missing'] };
162
+ const finalBlockers = [
163
+ ...(adapterResult.blockers || []),
164
+ ...(events.length > 0 ? [] : ['python_codex_sdk_event_stream_missing']),
165
+ ...(validation.ok ? [] : ['python_codex_sdk_structured_output_invalid', ...validation.issues.map((issue) => `schema:${issue}`)])
166
+ ];
167
+ const workerResult = normalizeWorkerResult(structuredOutput, task, finalBlockers, validation.ok, 'python-codex-sdk');
168
+ const workerResultPath = path.join(root, 'python-codex-sdk-worker-result.json');
169
+ await writeJsonAtomic(workerResultPath, workerResult);
170
+ const patchEnvelopePath = Array.isArray(workerResult.patch_envelopes) && workerResult.patch_envelopes.length
171
+ ? path.join(root, 'python-codex-sdk-patch-envelope.json')
172
+ : null;
173
+ if (patchEnvelopePath) {
174
+ await writeJsonAtomic(patchEnvelopePath, {
175
+ schema: 'sks.python-codex-sdk-patch-envelope.v1',
176
+ generated_at: nowIso(),
177
+ ok: true,
178
+ envelope_count: workerResult.patch_envelopes.length,
179
+ envelopes: workerResult.patch_envelopes
180
+ });
181
+ }
182
+ const pythonSdkProofPath = path.join(root, 'python-codex-sdk-proof.json');
183
+ await writeJsonAtomic(pythonSdkProofPath, {
184
+ schema: 'sks.python-codex-sdk-proof.v1',
185
+ generated_at: nowIso(),
186
+ ok: finalBlockers.length === 0,
187
+ backend: 'python-codex-sdk',
188
+ backend_family: fakeAllowed ? 'fake' : 'python-sdk',
189
+ package_name: capability.package_name,
190
+ import_name: capability.import_name,
191
+ python_bin: capability.python_bin,
192
+ python_version: capability.python_version,
193
+ sandbox: task.sandboxPolicy,
194
+ thread_id: adapterResult.threadId || '',
195
+ turn_id: adapterResult.turnId || '',
196
+ stream_event_count: events.length,
197
+ structured_output_valid: validation.ok,
198
+ worker_result_path: workerResultPath,
199
+ blockers: finalBlockers
200
+ });
201
+ const result = {
202
+ ok: finalBlockers.length === 0,
203
+ backend: 'python-codex-sdk',
204
+ backend_family: fakeAllowed ? 'fake' : 'python-sdk',
205
+ sdkThreadId: String(adapterResult.threadId || ''),
206
+ sdkRunId: adapterResult.turnId ? String(adapterResult.turnId) : null,
207
+ streamEventCount: events.length,
208
+ structuredOutputValid: validation.ok,
209
+ workerResultPath,
210
+ patchEnvelopePath,
211
+ pythonSdkProofPath,
212
+ blockers: finalBlockers,
213
+ reliabilityShield: {},
214
+ ultraRouterDecision: routerDecision,
215
+ outputSchemaId: task.outputSchemaId,
216
+ finalResponse: adapterResult.finalResponse || '',
217
+ eventTypes: events.map((event) => String(event?.event || event?.type || 'unknown')),
218
+ translatedEventCount: translatedEvents.length
219
+ };
220
+ await recordCodexThread(root, {
221
+ backend: result.backend,
222
+ backend_family: result.backend_family,
223
+ route: task.route,
224
+ mission_id: task.missionId,
225
+ work_item_id: task.workItemId || null,
226
+ slot_id: task.slotId || null,
227
+ generation_index: task.generationIndex ?? null,
228
+ session_id: task.sessionId || null,
229
+ zellij_pane_id: task.zellijPaneId || null,
230
+ sdk_thread_id: result.sdkThreadId,
231
+ sdk_run_id: result.sdkRunId,
232
+ stream_event_count: result.streamEventCount,
233
+ output_schema_id: task.outputSchemaId,
234
+ structured_output_valid: result.structuredOutputValid,
235
+ worker_result_path: result.workerResultPath
236
+ });
237
+ await writeCodexControlProof(root, {
238
+ task,
239
+ result,
240
+ capability: capability,
241
+ sandbox: { ok: true, sandbox_policy: task.sandboxPolicy, python_sandbox: mapPythonSandbox(task.sandboxPolicy), blockers: [] },
242
+ envProof: { ...runtime.env.proof, python_bin: capability.python_bin, python_version: capability.python_version },
243
+ config: { ...runtime.config, backend: 'python-codex-sdk', package_name: capability.package_name, import_name: capability.import_name },
244
+ reliabilityShield: null,
245
+ routerDecision: routerDecision,
246
+ translatedEvents
247
+ });
248
+ return result;
249
+ }
250
+ async function runLocalControlTask(root, task, schema, routerDecision) {
251
+ const config = await readLocalModelConfig();
252
+ const adapterResult = await runLocalLlmTask(task, { config, outputSchema: schema });
253
+ for (const event of adapterResult.events || [])
254
+ await appendJsonl(path.join(root, 'local-llm-events.jsonl'), event);
255
+ const structuredOutput = adapterResult.structuredOutput;
256
+ const validation = structuredOutput ? validateJsonSchemaRecursive(structuredOutput, schema) : { ok: false, issues: ['structured_output_missing'] };
257
+ const finalBlockers = [
258
+ ...(adapterResult.blockers || []),
259
+ ...(Array.isArray(adapterResult.events) && adapterResult.events.length > 0 ? [] : ['local_llm_event_stream_missing']),
260
+ ...(validation.ok ? [] : ['local_llm_structured_output_invalid', ...validation.issues.map((issue) => `schema:${issue}`)])
261
+ ];
262
+ const workerResult = normalizeWorkerResult(structuredOutput, task, finalBlockers, validation.ok, 'local-llm');
263
+ const workerResultPath = path.join(root, 'local-llm-worker-result.json');
264
+ await writeJsonAtomic(workerResultPath, workerResult);
265
+ const patchEnvelopePath = Array.isArray(workerResult.patch_envelopes) && workerResult.patch_envelopes.length
266
+ ? path.join(root, 'local-llm-patch-envelope.json')
267
+ : null;
268
+ if (patchEnvelopePath) {
269
+ await writeJsonAtomic(patchEnvelopePath, {
270
+ schema: 'sks.local-llm-patch-envelope.v1',
271
+ generated_at: nowIso(),
272
+ ok: true,
273
+ envelope_count: workerResult.patch_envelopes.length,
274
+ envelopes: workerResult.patch_envelopes,
275
+ requires_gpt_final: true
276
+ });
277
+ }
278
+ const localLlmProofPath = path.join(root, 'local-llm-proof.json');
279
+ await writeJsonAtomic(localLlmProofPath, adapterResult.proof);
280
+ const result = {
281
+ ok: finalBlockers.length === 0,
282
+ backend: 'local-llm',
283
+ backend_family: 'local-llm',
284
+ sdkThreadId: '',
285
+ sdkRunId: null,
286
+ streamEventCount: adapterResult.events?.length || 0,
287
+ structuredOutputValid: validation.ok,
288
+ workerResultPath,
289
+ patchEnvelopePath,
290
+ localLlmProofPath,
291
+ blockers: finalBlockers,
292
+ reliabilityShield: {},
293
+ ultraRouterDecision: routerDecision,
294
+ outputSchemaId: task.outputSchemaId,
295
+ finalResponse: adapterResult.finalResponse || '',
296
+ eventTypes: (adapterResult.events || []).map((event) => String(event?.type || 'unknown')),
297
+ translatedEventCount: adapterResult.events?.length || 0,
298
+ localLlmRequestId: adapterResult.requestId
299
+ };
300
+ await recordCodexThread(root, {
301
+ backend: result.backend,
302
+ backend_family: result.backend_family,
303
+ route: task.route,
304
+ mission_id: task.missionId,
305
+ work_item_id: task.workItemId || null,
306
+ slot_id: task.slotId || null,
307
+ generation_index: task.generationIndex ?? null,
308
+ session_id: task.sessionId || null,
309
+ zellij_pane_id: task.zellijPaneId || null,
310
+ sdk_thread_id: null,
311
+ sdk_run_id: null,
312
+ local_llm_request_id: adapterResult.requestId,
313
+ stream_event_count: result.streamEventCount,
314
+ output_schema_id: task.outputSchemaId,
315
+ structured_output_valid: result.structuredOutputValid,
316
+ worker_result_path: result.workerResultPath
317
+ });
318
+ await writeCodexControlProof(root, {
319
+ task,
320
+ result,
321
+ capability: config.capability,
322
+ sandbox: { ok: task.sandboxPolicy !== 'full-access', sandbox_policy: task.sandboxPolicy, blockers: task.sandboxPolicy === 'full-access' ? ['local_llm_full_access_blocked'] : [] },
323
+ envProof: { provider: config.provider, endpoint: config.base_url },
324
+ config: { provider: config.provider, model: config.model, endpoint: config.base_url, status: config.status },
325
+ reliabilityShield: null,
326
+ routerDecision: routerDecision,
327
+ translatedEvents: adapterResult.events || []
328
+ });
329
+ return result;
330
+ }
331
+ function selectCodexControlBackend(input, routerDecision) {
332
+ const prefs = Array.isArray(input.backendPreference) ? input.backendPreference : [];
333
+ for (const pref of prefs) {
334
+ if (pref === 'python-codex-sdk')
335
+ return 'python-codex-sdk';
336
+ if (pref === 'codex-sdk')
337
+ return 'codex-sdk';
338
+ if (pref === 'local-llm' && input.tier === 'worker')
339
+ return 'local-llm';
340
+ }
341
+ if (input.localLlmPolicy?.mode === 'disabled')
342
+ return 'codex-sdk';
343
+ if (input.allowLocalLlm === true && input.tier === 'worker' && routerDecision?.selected_profile === 'local-llm-worker')
344
+ return 'local-llm';
345
+ if (input.allowLocalLlm === true && input.tier === 'worker' && input.localLlmPolicy?.mode === 'local_only')
346
+ return 'local-llm';
347
+ if (input.allowLocalLlm === true && input.tier === 'worker' && input.localLlmPolicy?.mode === 'local_preferred')
348
+ return 'local-llm';
349
+ return 'codex-sdk';
350
+ }
351
+ function normalizeWorkerResult(value, input, blockers, structuredOutputValid, backend = 'codex-sdk') {
130
352
  const status = blockers.length ? 'blocked' : normalizeStatus(value?.status);
131
353
  return {
132
354
  ...value,
@@ -135,9 +357,9 @@ function normalizeWorkerResult(value, input, blockers, structuredOutputValid) {
135
357
  session_id: String(value?.session_id || input.sessionId || input.workItemId || 'codex-sdk-session'),
136
358
  persona_id: String(value?.persona_id || value?.agent_id || input.slotId || 'codex-sdk-worker'),
137
359
  task_slice_id: String(value?.task_slice_id || input.workItemId || ''),
138
- backend: 'codex-sdk',
360
+ backend,
139
361
  status,
140
- summary: String(value?.summary || (blockers.length ? 'Codex SDK task blocked.' : 'Codex SDK task completed.')),
362
+ summary: String(value?.summary || (blockers.length ? `${backend} task blocked.` : `${backend} task completed.`)),
141
363
  findings: Array.isArray(value?.findings) ? value.findings : [],
142
364
  proposed_changes: Array.isArray(value?.proposed_changes) ? value.proposed_changes : [],
143
365
  changed_files: Array.isArray(value?.changed_files) ? value.changed_files : [],
@@ -145,7 +367,7 @@ function normalizeWorkerResult(value, input, blockers, structuredOutputValid) {
145
367
  artifacts: Array.isArray(value?.artifacts) ? value.artifacts : [],
146
368
  blockers,
147
369
  confidence: String(value?.confidence || (structuredOutputValid ? 'verified_partial' : 'blocked')),
148
- handoff_notes: String(value?.handoff_notes || 'Codex SDK Control Plane produced this worker result.'),
370
+ handoff_notes: String(value?.handoff_notes || `${backend} Control Plane produced this worker result.`),
149
371
  unverified: Array.isArray(value?.unverified) ? value.unverified : [],
150
372
  writes: Array.isArray(value?.writes) ? value.writes : [],
151
373
  patch_envelopes: Array.isArray(value?.patch_envelopes) ? value.patch_envelopes : [],
@@ -157,4 +379,106 @@ function normalizeWorkerResult(value, input, blockers, structuredOutputValid) {
157
379
  function normalizeStatus(value) {
158
380
  return value === 'failed' || value === 'blocked' || value === 'done' ? value : 'done';
159
381
  }
382
+ function mapPythonSandbox(value) {
383
+ if (value === 'workspace-write')
384
+ return 'workspace_write';
385
+ if (value === 'full-access')
386
+ return 'full_access';
387
+ return 'read_only';
388
+ }
389
+ function parseStructuredOutput(text) {
390
+ const trimmed = String(text || '').trim();
391
+ if (!trimmed)
392
+ return null;
393
+ try {
394
+ return JSON.parse(trimmed);
395
+ }
396
+ catch {
397
+ const start = trimmed.indexOf('{');
398
+ const end = trimmed.lastIndexOf('}');
399
+ if (start >= 0 && end > start) {
400
+ try {
401
+ return JSON.parse(trimmed.slice(start, end + 1));
402
+ }
403
+ catch { }
404
+ }
405
+ return null;
406
+ }
407
+ }
408
+ async function ensurePythonCodexLbConfig(env, config) {
409
+ const codexHome = env.CODEX_HOME;
410
+ const lbBaseUrl = normalizeCodexLbBaseUrl(env.CODEX_LB_BASE_URL);
411
+ if (!codexHome || !lbBaseUrl || !env.CODEX_LB_API_KEY)
412
+ return;
413
+ const model = String(config.model || env.SKS_CODEX_MODEL || env.CODEX_MODEL || 'gpt-5.5');
414
+ const text = [
415
+ `model = ${tomlQuote(model)}`,
416
+ 'model_provider = "codex-lb"',
417
+ 'service_tier = "fast"',
418
+ 'model_reasoning_effort = "minimal"',
419
+ 'approval_policy = "never"',
420
+ '',
421
+ '[model_providers.codex-lb]',
422
+ 'name = "OpenAI"',
423
+ `base_url = ${tomlQuote(lbBaseUrl)}`,
424
+ 'wire_api = "responses"',
425
+ 'env_key = "CODEX_LB_API_KEY"',
426
+ 'supports_websockets = true',
427
+ 'requires_openai_auth = false',
428
+ ''
429
+ ].join('\n');
430
+ await writeTextAtomic(path.join(codexHome, 'config.toml'), text);
431
+ }
432
+ function normalizeCodexLbBaseUrl(value) {
433
+ let host = String(value || '').trim();
434
+ if (!host)
435
+ return '';
436
+ if (!/^[a-z][a-z0-9+.-]*:\/\//i.test(host))
437
+ host = `https://${host}`;
438
+ host = host.replace(/\/+$/, '');
439
+ return /\/backend-api\/codex$/i.test(host) ? host : `${host}/backend-api/codex`;
440
+ }
441
+ function tomlQuote(value) {
442
+ return JSON.stringify(value);
443
+ }
444
+ function resolveBundledCodexBinary() {
445
+ const platform = process.platform;
446
+ const arch = process.arch;
447
+ const pkg = platform === 'darwin' && arch === 'arm64'
448
+ ? '@openai/codex-darwin-arm64'
449
+ : platform === 'darwin' && arch === 'x64'
450
+ ? '@openai/codex-darwin-x64'
451
+ : platform === 'linux' && arch === 'arm64'
452
+ ? '@openai/codex-linux-arm64'
453
+ : platform === 'linux' && arch === 'x64'
454
+ ? '@openai/codex-linux-x64'
455
+ : platform === 'win32' && arch === 'x64'
456
+ ? '@openai/codex-win32-x64'
457
+ : platform === 'win32' && arch === 'arm64'
458
+ ? '@openai/codex-win32-arm64'
459
+ : '';
460
+ if (!pkg)
461
+ return '';
462
+ const binary = platform === 'win32' ? 'codex.exe' : 'codex';
463
+ const candidates = [
464
+ path.join(packageRoot(), 'node_modules', pkg, 'vendor', nativeTargetTriple(platform, arch), 'bin', binary),
465
+ path.join(packageRoot(), 'node_modules', pkg, 'vendor', nativeTargetTriple(platform, arch), 'codex', binary)
466
+ ];
467
+ return candidates.find((candidate) => fs.existsSync(candidate)) || '';
468
+ }
469
+ function nativeTargetTriple(platform, arch) {
470
+ if (platform === 'darwin' && arch === 'arm64')
471
+ return 'aarch64-apple-darwin';
472
+ if (platform === 'darwin' && arch === 'x64')
473
+ return 'x86_64-apple-darwin';
474
+ if (platform === 'linux' && arch === 'arm64')
475
+ return 'aarch64-unknown-linux-musl';
476
+ if (platform === 'linux' && arch === 'x64')
477
+ return 'x86_64-unknown-linux-musl';
478
+ if (platform === 'win32' && arch === 'arm64')
479
+ return 'aarch64-pc-windows-msvc';
480
+ if (platform === 'win32' && arch === 'x64')
481
+ return 'x86_64-pc-windows-msvc';
482
+ return '';
483
+ }
160
484
  //# sourceMappingURL=codex-task-runner.js.map
@@ -0,0 +1,160 @@
1
+ import path from 'node:path';
2
+ import { nowIso, readJson, writeJsonAtomic } from '../fsx.js';
3
+ import { validateJsonSchemaRecursive } from '../json-schema-validator.js';
4
+ import { evaluateLocalCollaborationFinalGate, resolveLocalCollaborationPolicy } from '../local-llm/local-collaboration-policy.js';
5
+ import { runCodexTask } from './codex-control-plane.js';
6
+ import { GPT_FINAL_ARBITER_INPUT_SCHEMA, GPT_FINAL_ARBITER_RESULT_SCHEMA_ID, gptFinalArbiterResultSchema, normalizeGptFinalArbiterResult } from './gpt-final-review-schema.js';
7
+ import { compressGptFinalContext } from './gpt-final-context-compressor.js';
8
+ export const GPT_FINAL_ARBITER_RUN_SCHEMA = 'sks.gpt-final-arbiter-run.v1';
9
+ export async function runGptFinalArbiter(input, opts = {}) {
10
+ const started = Date.now();
11
+ const cwd = path.resolve(opts.cwd || process.cwd());
12
+ const root = path.resolve(opts.mutationLedgerRoot || path.join(cwd, '.sneakoscope', 'tmp', 'gpt-final-arbiter', safeName(input.mission_id || 'mission')));
13
+ const policy = resolveLocalCollaborationPolicy({ mode: input.local_mode });
14
+ const compressed = compressGptFinalContext(input);
15
+ if (policy.local_only_draft) {
16
+ return finalize(root, input, policy, compressed, blockedResult('needs_gpt_final_review', 'Local-only draft mode cannot produce final accepted proof.'), started, opts);
17
+ }
18
+ if (opts.forceUnavailable || process.env.SKS_GPT_FINAL_ARBITER_UNAVAILABLE === '1') {
19
+ return finalize(root, input, policy, compressed, blockedResult('gpt_final_arbiter_unavailable', 'GPT final arbiter backend is unavailable.'), started, opts);
20
+ }
21
+ let codexTask = null;
22
+ let parsed = null;
23
+ try {
24
+ codexTask = await runCodexTask({
25
+ route: String(input.route || '$Pipeline'),
26
+ tier: 'orchestrator',
27
+ missionId: String(input.mission_id || ''),
28
+ workItemId: 'gpt-final-arbiter',
29
+ slotId: 'gpt-final-arbiter',
30
+ generationIndex: 1,
31
+ sessionId: `gpt-final-${safeName(input.mission_id || 'mission')}`,
32
+ cwd,
33
+ prompt: buildArbiterPrompt(input, compressed),
34
+ inputFiles: [],
35
+ inputImages: [],
36
+ outputSchemaId: GPT_FINAL_ARBITER_RESULT_SCHEMA_ID,
37
+ outputSchema: gptFinalArbiterResultSchema,
38
+ sandboxPolicy: 'read-only',
39
+ requestedScopeContract: {
40
+ id: `gpt-final:${input.mission_id || 'mission'}`,
41
+ route: String(input.route || '$Pipeline'),
42
+ read_only: true,
43
+ allowed_paths: [],
44
+ write_paths: [],
45
+ user_confirmed_full_access: false,
46
+ mad_sks_authorized: process.env.SKS_MAD_SKS_ACTIVE === '1'
47
+ },
48
+ mutationLedgerRoot: root,
49
+ reliabilityPolicy: {
50
+ maxEmptyResultRetries: 1,
51
+ timeoutClass: 'standard'
52
+ }
53
+ });
54
+ parsed = parseFinalResponse(codexTask.finalResponse);
55
+ }
56
+ catch (error) {
57
+ return finalize(root, input, policy, compressed, blockedResult('gpt_final_arbiter_unavailable', error instanceof Error ? error.message : String(error)), started, opts);
58
+ }
59
+ const normalized = normalizeGptFinalArbiterResult(parsed);
60
+ const validation = validateJsonSchemaRecursive(normalized, gptFinalArbiterResultSchema);
61
+ const taskBlockers = Array.isArray(codexTask?.blockers) ? codexTask.blockers.map(String) : [];
62
+ const result = {
63
+ ...normalized,
64
+ blockers: [
65
+ ...normalized.blockers,
66
+ ...(codexTask?.ok === true ? [] : ['gpt_final_arbiter_unavailable']),
67
+ ...taskBlockers,
68
+ ...(validation.ok ? [] : ['gpt_final_result_schema_invalid', ...validation.issues.map((issue) => `schema:${issue}`)])
69
+ ]
70
+ };
71
+ return finalize(root, input, policy, compressed, result, started, opts, codexTask);
72
+ }
73
+ function finalize(root, input, policy, compressed, result, started, opts, codexTask) {
74
+ const latencyMs = Math.max(0, Date.now() - started);
75
+ const gate = evaluateLocalCollaborationFinalGate({
76
+ policy,
77
+ localParticipated: true,
78
+ gptFinalStatus: result.status,
79
+ gptFinalAvailable: !result.blockers.includes('gpt_final_arbiter_unavailable'),
80
+ gptFinalBackend: codexTask ? 'codex-sdk' : null,
81
+ applyPatches: false
82
+ });
83
+ const artifact = {
84
+ schema: GPT_FINAL_ARBITER_RUN_SCHEMA,
85
+ generated_at: nowIso(),
86
+ ok: gate.ok && result.blockers.length === 0,
87
+ input_schema: input.schema || GPT_FINAL_ARBITER_INPUT_SCHEMA,
88
+ route: input.route,
89
+ mission_id: input.mission_id,
90
+ local_mode: policy.mode,
91
+ backend: codexTask ? 'codex-sdk' : 'unavailable',
92
+ backend_family: codexTask ? 'remote-gpt' : 'none',
93
+ local_outputs_count: Array.isArray(input.local_outputs) ? input.local_outputs.length : 0,
94
+ proof_pack: compressed.proof_pack,
95
+ latency_budget: {
96
+ ...compressed.latency_budget,
97
+ latency_ms: latencyMs
98
+ },
99
+ result,
100
+ final_gate: gate,
101
+ codex_task: codexTask ? {
102
+ ok: codexTask.ok === true,
103
+ sdk_thread_id: codexTask.sdkThreadId || null,
104
+ sdk_run_id: codexTask.sdkRunId || null,
105
+ stream_event_count: codexTask.streamEventCount || 0,
106
+ structured_output_valid: codexTask.structuredOutputValid === true,
107
+ worker_result_path: codexTask.workerResultPath || null,
108
+ blockers: codexTask.blockers || []
109
+ } : null,
110
+ blockers: [
111
+ ...result.blockers,
112
+ ...gate.blockers,
113
+ ...compressed.blockers
114
+ ]
115
+ };
116
+ artifact.ok = artifact.blockers.length === 0 && (result.status === 'approved' || result.status === 'modified');
117
+ if (opts.writeArtifact !== false)
118
+ return writeArtifact(root, artifact);
119
+ return artifact;
120
+ }
121
+ async function writeArtifact(root, artifact) {
122
+ await writeJsonAtomic(path.join(root, 'gpt-final-arbiter.json'), artifact);
123
+ return artifact;
124
+ }
125
+ function blockedResult(blocker, summary) {
126
+ return normalizeGptFinalArbiterResult({
127
+ status: 'needs_more_work',
128
+ summary,
129
+ blockers: [blocker],
130
+ confidence: 'low',
131
+ required_followup_work: [{ blocker }]
132
+ });
133
+ }
134
+ function parseFinalResponse(value) {
135
+ if (typeof value !== 'string')
136
+ return value || {};
137
+ try {
138
+ return JSON.parse(value);
139
+ }
140
+ catch {
141
+ return {};
142
+ }
143
+ }
144
+ function buildArbiterPrompt(input, compressed) {
145
+ return [
146
+ 'You are the GPT Final Arbiter for an SKS local collaboration run.',
147
+ 'Local model outputs are drafts only. Review the proof pack, candidate diff, patch envelopes, verification results, side effects, mutation ledger, and rollback plan.',
148
+ 'Approve or modify only when the candidate is safe and supported. Reject unsafe local patches. Return only the requested structured JSON schema.',
149
+ JSON.stringify({
150
+ route: input.route,
151
+ mission_id: input.mission_id,
152
+ local_mode: input.local_mode,
153
+ proof_pack: compressed.proof_pack
154
+ })
155
+ ].join('\n');
156
+ }
157
+ function safeName(value) {
158
+ return String(value || 'unknown').replace(/[^a-zA-Z0-9_.-]+/g, '-').slice(0, 80);
159
+ }
160
+ //# sourceMappingURL=gpt-final-arbiter.js.map
@@ -0,0 +1,17 @@
1
+ import { buildGptFinalLatencyBudgetReport, buildGptFinalProofPack } from './gpt-final-proof-pack.js';
2
+ export function compressGptFinalContext(input, opts = {}) {
3
+ const proofPack = buildGptFinalProofPack(input, opts);
4
+ const latencyBudget = buildGptFinalLatencyBudgetReport({
5
+ workerCount: proofPack.worker_count,
6
+ tokenBudgetEstimate: proofPack.token_budget_estimate,
7
+ latencyMs: opts.latencyMs ?? null
8
+ });
9
+ return {
10
+ schema: 'sks.gpt-final-context-compressor.v1',
11
+ ok: latencyBudget.ok,
12
+ proof_pack: proofPack,
13
+ latency_budget: latencyBudget,
14
+ blockers: latencyBudget.ok ? [] : ['gpt_final_context_budget_exceeded']
15
+ };
16
+ }
17
+ //# sourceMappingURL=gpt-final-context-compressor.js.map