sneakoscope 3.1.5 → 3.1.7
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 +9 -37
- 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/cli/command-registry.js +1 -2
- package/dist/commands/codex-app.js +20 -12
- package/dist/commands/codex-native.js +84 -0
- package/dist/commands/doctor.js +90 -2
- package/dist/core/codex-app/codex-agent-role-sync.js +15 -5
- package/dist/core/codex-app/codex-app-execution-profile.js +38 -16
- package/dist/core/codex-app/codex-app-harness-matrix.js +4 -117
- package/dist/core/codex-app/codex-hook-lifecycle.js +4 -1
- package/dist/core/codex-app/codex-init-deep.js +66 -4
- package/dist/core/codex-app/codex-skill-sync.js +13 -8
- package/dist/core/codex-control/codex-0138-capability.js +5 -2
- package/dist/core/codex-native/codex-native-capability-cache.js +21 -0
- package/dist/core/codex-native/codex-native-feature-broker.js +250 -0
- package/dist/core/codex-native/codex-native-feature-matrix.js +31 -0
- package/dist/core/codex-native/codex-native-harness-compat.js +54 -0
- package/dist/core/{codex-app/lazycodex-interop-policy.js → codex-native/codex-native-interop-policy.js} +13 -15
- package/dist/core/codex-native/codex-native-invocation-router.js +112 -0
- package/dist/core/codex-native/codex-native-pattern-analysis.js +68 -0
- package/dist/core/codex-native/codex-native-reference-cache.js +98 -0
- package/dist/core/codex-native/codex-native-reference-evidence.js +2 -0
- package/dist/core/codex-native/codex-native-reference-source.js +149 -0
- package/dist/core/codex-native/codex-native-rename-map.js +25 -0
- package/dist/core/codex-native/codex-native-repair-transaction.js +150 -0
- package/dist/core/codex-plugins/codex-plugin-json.js +5 -2
- package/dist/core/commands/mad-sks-command.js +16 -0
- package/dist/core/feature-fixtures.js +2 -4
- package/dist/core/feature-registry.js +1 -1
- package/dist/core/fsx.js +1 -1
- package/dist/core/image/image-artifact-path-contract.js +18 -1
- package/dist/core/loops/loop-owner-inference.js +3 -0
- package/dist/core/loops/loop-planner.js +8 -2
- package/dist/core/loops/loop-worker-prompts.js +2 -0
- package/dist/core/loops/loop-worker-runtime.js +42 -7
- package/dist/core/qa-loop.js +24 -1
- package/dist/core/research.js +36 -3
- package/dist/core/routes.js +2 -3
- package/dist/core/version.js +1 -1
- package/dist/scripts/codex-native-runtime-e2e-fixture.js +75 -0
- package/dist/scripts/loop-worker-fixture-child.js +2 -1
- package/dist/scripts/sks-3-1-4-directive-check-lib.js +1 -30
- package/dist/scripts/sks-3-1-5-directive-check-lib.js +4 -33
- package/dist/scripts/sks-3-1-6-directive-check-lib.js +522 -0
- package/dist/scripts/sks-3-1-7-directive-check-lib.js +58 -0
- package/package.json +44 -13
- package/dist/cli/hermes-command.js +0 -99
- package/dist/cli/openclaw-command.js +0 -83
- package/dist/commands/hermes.js +0 -5
- package/dist/commands/openclaw.js +0 -3
- package/dist/core/codex-app/lazycodex-analysis.js +0 -72
- package/dist/core/codex-app/lazycodex-live-analyzer.js +0 -98
- package/dist/core/hermes.js +0 -192
- package/dist/core/openclaw.js +0 -171
|
@@ -0,0 +1,150 @@
|
|
|
1
|
+
import path from 'node:path';
|
|
2
|
+
import { buildCodexHookLifecycle } from '../codex-app/codex-hook-lifecycle.js';
|
|
3
|
+
import { runCodexInitDeep } from '../codex-app/codex-init-deep.js';
|
|
4
|
+
import { syncCodexAgentRoles } from '../codex-app/codex-agent-role-sync.js';
|
|
5
|
+
import { syncCodexSksSkills } from '../codex-app/codex-skill-sync.js';
|
|
6
|
+
import { nowIso, writeJsonAtomic } from '../fsx.js';
|
|
7
|
+
import { createRequestedScopeContract } from '../safety/requested-scope-contract.js';
|
|
8
|
+
import { evaluateMutation, mutationLedgerPath, recordMutation } from '../safety/mutation-ledger.js';
|
|
9
|
+
export async function repairCodexNativeManagedAssets(input) {
|
|
10
|
+
const root = path.resolve(input.root);
|
|
11
|
+
const requested = {
|
|
12
|
+
skills: input.repairSkills !== false,
|
|
13
|
+
agent_roles: input.repairAgentRoles !== false,
|
|
14
|
+
hooks: input.repairHooks !== false,
|
|
15
|
+
project_memory: input.repairProjectMemory !== false
|
|
16
|
+
};
|
|
17
|
+
const requestedAssets = Object.entries(requested)
|
|
18
|
+
.filter(([, enabled]) => enabled)
|
|
19
|
+
.map(([asset]) => asset);
|
|
20
|
+
if (input.yes !== true) {
|
|
21
|
+
const report = {
|
|
22
|
+
schema: 'sks.codex-native-repair-transaction.v1',
|
|
23
|
+
ok: false,
|
|
24
|
+
generated_at: nowIso(),
|
|
25
|
+
requested_by: input.requestedBy,
|
|
26
|
+
repaired: requestedAssets.map((asset) => ({
|
|
27
|
+
asset,
|
|
28
|
+
ok: false,
|
|
29
|
+
changed: false,
|
|
30
|
+
artifact_path: artifactPathFor(asset),
|
|
31
|
+
blockers: ['repair_transaction_requires_yes']
|
|
32
|
+
})),
|
|
33
|
+
confirmed: false,
|
|
34
|
+
mutation_ledger_path: null,
|
|
35
|
+
blockers: ['repair_transaction_requires_yes'],
|
|
36
|
+
warnings: []
|
|
37
|
+
};
|
|
38
|
+
await writeJsonAtomic(path.join(root, '.sneakoscope', 'reports', 'codex-native-repair-transaction.json'), report).catch(() => undefined);
|
|
39
|
+
return report;
|
|
40
|
+
}
|
|
41
|
+
const rows = [];
|
|
42
|
+
if (requested.skills) {
|
|
43
|
+
const report = await syncCodexSksSkills({ root, apply: true }).catch((err) => ({ ok: false, blockers: [messageOf(err)] }));
|
|
44
|
+
rows.push({
|
|
45
|
+
asset: 'skills',
|
|
46
|
+
ok: recordOk(report) !== false,
|
|
47
|
+
changed: listLength(report, 'created') > 0,
|
|
48
|
+
artifact_path: '.sneakoscope/reports/codex-skill-sync.json',
|
|
49
|
+
blockers: blockersOf(report)
|
|
50
|
+
});
|
|
51
|
+
}
|
|
52
|
+
if (requested.agent_roles) {
|
|
53
|
+
const report = await syncCodexAgentRoles({ root, apply: true }).catch((err) => ({ ok: false, blockers: [messageOf(err)] }));
|
|
54
|
+
rows.push({
|
|
55
|
+
asset: 'agent_roles',
|
|
56
|
+
ok: recordOk(report) !== false,
|
|
57
|
+
changed: listLength(report, 'created') > 0,
|
|
58
|
+
artifact_path: '.sneakoscope/reports/codex-agent-role-sync.json',
|
|
59
|
+
blockers: blockersOf(report)
|
|
60
|
+
});
|
|
61
|
+
}
|
|
62
|
+
if (requested.hooks) {
|
|
63
|
+
const report = await buildCodexHookLifecycle({ root, apply: true }).catch((err) => ({ ok: false, blockers: [messageOf(err)] }));
|
|
64
|
+
rows.push({
|
|
65
|
+
asset: 'hooks',
|
|
66
|
+
ok: recordOk(report) !== false,
|
|
67
|
+
changed: recordOk(report) !== false,
|
|
68
|
+
artifact_path: '.sneakoscope/reports/codex-hook-lifecycle.json',
|
|
69
|
+
blockers: blockersOf(report)
|
|
70
|
+
});
|
|
71
|
+
}
|
|
72
|
+
if (requested.project_memory) {
|
|
73
|
+
const report = await runCodexInitDeep({ root, apply: true, directoryLocal: true }).catch((err) => ({ ok: false, blockers: [messageOf(err)] }));
|
|
74
|
+
rows.push({
|
|
75
|
+
asset: 'project_memory',
|
|
76
|
+
ok: recordOk(report) !== false,
|
|
77
|
+
changed: listLength(report, 'directory_local_agents.created') + listLength(report, 'directory_local_agents.updated') > 0,
|
|
78
|
+
artifact_path: '.sneakoscope/reports/codex-init-deep.json',
|
|
79
|
+
blockers: blockersOf(report)
|
|
80
|
+
});
|
|
81
|
+
}
|
|
82
|
+
const blockers = rows.flatMap((row) => row.blockers);
|
|
83
|
+
const mutationWarnings = await recordRepairMutations(root, rows);
|
|
84
|
+
const report = {
|
|
85
|
+
schema: 'sks.codex-native-repair-transaction.v1',
|
|
86
|
+
ok: rows.every((row) => row.ok) && blockers.length === 0,
|
|
87
|
+
generated_at: nowIso(),
|
|
88
|
+
requested_by: input.requestedBy,
|
|
89
|
+
repaired: rows,
|
|
90
|
+
confirmed: true,
|
|
91
|
+
mutation_ledger_path: path.relative(root, mutationLedgerPath(root)).split(path.sep).join('/'),
|
|
92
|
+
blockers: [...new Set(blockers)],
|
|
93
|
+
warnings: mutationWarnings
|
|
94
|
+
};
|
|
95
|
+
await writeJsonAtomic(path.join(root, '.sneakoscope', 'reports', 'codex-native-repair-transaction.json'), report).catch(() => undefined);
|
|
96
|
+
return report;
|
|
97
|
+
}
|
|
98
|
+
async function recordRepairMutations(root, rows) {
|
|
99
|
+
const contract = createRequestedScopeContract({
|
|
100
|
+
route: 'codex-native-repair-transaction',
|
|
101
|
+
userRequest: 'Repair SKS-managed Codex Native assets after explicit --yes confirmation.',
|
|
102
|
+
projectRoot: root,
|
|
103
|
+
overrides: { global_codex_config: true }
|
|
104
|
+
});
|
|
105
|
+
const warnings = [];
|
|
106
|
+
for (const row of rows) {
|
|
107
|
+
const kind = row.asset === 'project_memory' ? 'file_write' : 'global_config_write';
|
|
108
|
+
const entry = evaluateMutation(contract, kind, {
|
|
109
|
+
target: row.asset,
|
|
110
|
+
confirmed: true,
|
|
111
|
+
applied: row.changed,
|
|
112
|
+
noOpReason: row.changed ? 'sks_managed_asset_transaction_recorded' : 'repair_noop'
|
|
113
|
+
});
|
|
114
|
+
await recordMutation(root, entry).catch((err) => warnings.push(`mutation_ledger_write_failed:${messageOf(err)}`));
|
|
115
|
+
}
|
|
116
|
+
return warnings;
|
|
117
|
+
}
|
|
118
|
+
function artifactPathFor(asset) {
|
|
119
|
+
return asset === 'skills'
|
|
120
|
+
? '.sneakoscope/reports/codex-skill-sync.json'
|
|
121
|
+
: asset === 'agent_roles'
|
|
122
|
+
? '.sneakoscope/reports/codex-agent-role-sync.json'
|
|
123
|
+
: asset === 'hooks'
|
|
124
|
+
? '.sneakoscope/reports/codex-hook-lifecycle.json'
|
|
125
|
+
: '.sneakoscope/reports/codex-init-deep.json';
|
|
126
|
+
}
|
|
127
|
+
function recordOk(value) {
|
|
128
|
+
return Boolean(value) && typeof value === 'object' && typeof value.ok === 'boolean'
|
|
129
|
+
? value.ok
|
|
130
|
+
: undefined;
|
|
131
|
+
}
|
|
132
|
+
function blockersOf(value) {
|
|
133
|
+
return Boolean(value) && typeof value === 'object' && Array.isArray(value.blockers)
|
|
134
|
+
? value.blockers.map((item) => String(item)).filter(Boolean)
|
|
135
|
+
: [];
|
|
136
|
+
}
|
|
137
|
+
function listLength(value, key) {
|
|
138
|
+
const parts = key.split('.');
|
|
139
|
+
let current = value;
|
|
140
|
+
for (const part of parts) {
|
|
141
|
+
if (!current || typeof current !== 'object')
|
|
142
|
+
return 0;
|
|
143
|
+
current = current[part];
|
|
144
|
+
}
|
|
145
|
+
return Array.isArray(current) ? current.length : 0;
|
|
146
|
+
}
|
|
147
|
+
function messageOf(err) {
|
|
148
|
+
return err instanceof Error ? err.message : String(err);
|
|
149
|
+
}
|
|
150
|
+
//# sourceMappingURL=codex-native-repair-transaction.js.map
|
|
@@ -33,7 +33,8 @@ export async function buildCodexPluginInventory() {
|
|
|
33
33
|
});
|
|
34
34
|
const blockers = [
|
|
35
35
|
...(capability.supports_plugin_json ? [] : ['codex_0_138_plugin_json_unavailable']),
|
|
36
|
-
...normalizeList(listJson?.blockers)
|
|
36
|
+
...normalizeList(listJson?.blockers),
|
|
37
|
+
...(process.env.SKS_CODEX_PLUGIN_JSON_FAKE_NO_MCP === '1' ? ['fixture_mcp_candidates_disabled'] : [])
|
|
37
38
|
];
|
|
38
39
|
return {
|
|
39
40
|
schema: 'sks.codex-plugin-inventory.v1',
|
|
@@ -169,7 +170,9 @@ function fakePluginDetail(pluginId) {
|
|
|
169
170
|
installed: true,
|
|
170
171
|
enabled: true,
|
|
171
172
|
default_prompts: ['Use the fixture plugin safely.'],
|
|
172
|
-
remote_mcp_servers:
|
|
173
|
+
remote_mcp_servers: process.env.SKS_CODEX_PLUGIN_JSON_FAKE_NO_MCP === '1'
|
|
174
|
+
? []
|
|
175
|
+
: [{ name: 'fixture-db-docs', url: 'https://mcp.example.test', auth_type: 'oauth' }],
|
|
173
176
|
unavailable_app_templates: ['fixture-desktop-template']
|
|
174
177
|
};
|
|
175
178
|
}
|
|
@@ -22,6 +22,7 @@ import { checkSksUpdateNotice } from '../update/update-notice.js';
|
|
|
22
22
|
import { createMadDbCapability, MAD_DB_ACK } from '../mad-db/mad-db-capability.js';
|
|
23
23
|
import { writeCodex0138CapabilityArtifacts } from '../codex-control/codex-0138-capability.js';
|
|
24
24
|
import { writeCodex0139CapabilityArtifacts } from '../codex-control/codex-0139-capability.js';
|
|
25
|
+
import { resolveCodexNativeInvocationPlan } from '../codex-native/codex-native-invocation-router.js';
|
|
25
26
|
import { repairZellijForSks } from '../zellij/zellij-self-heal.js';
|
|
26
27
|
export async function madHighCommand(args = [], deps = {}) {
|
|
27
28
|
const subcommand = firstSubcommand(args);
|
|
@@ -478,6 +479,14 @@ async function activateMadZellijPermissionState(cwd = process.cwd(), args = [])
|
|
|
478
479
|
const { id, dir } = await createMission(root, { mode: 'mad-sks', prompt: 'sks --mad Zellij scoped high-power maintenance session' });
|
|
479
480
|
await writeCodex0138CapabilityArtifacts(root, { missionId: id }).catch(() => null);
|
|
480
481
|
await writeCodex0139CapabilityArtifacts(root, { missionId: id }).catch(() => null);
|
|
482
|
+
const codexNativeInvocation = await resolveCodexNativeInvocationPlan({
|
|
483
|
+
root,
|
|
484
|
+
missionId: id,
|
|
485
|
+
route: '$MAD',
|
|
486
|
+
desiredCapability: 'hook-evidence'
|
|
487
|
+
}).catch(() => null);
|
|
488
|
+
if (codexNativeInvocation)
|
|
489
|
+
await writeJsonAtomic(path.join(dir, 'mad-codex-native-invocation.json'), codexNativeInvocation).catch(() => undefined);
|
|
481
490
|
const protectedCore = resolveProtectedCore({ packageRoot: packageRoot(), targetRoot: cwd });
|
|
482
491
|
// The interactive launch 'before' snapshot is only persisted (env + policy json)
|
|
483
492
|
// and is never compared against an 'after' snapshot during the session, so the
|
|
@@ -521,6 +530,13 @@ async function activateMadZellijPermissionState(cwd = process.cwd(), args = [])
|
|
|
521
530
|
protected_core_policy: protectedCorePolicyPath,
|
|
522
531
|
protected_core_before: protectedCoreBeforePath,
|
|
523
532
|
protected_core_digest: protectedCoreBefore.digest,
|
|
533
|
+
codex_native_invocation_plan: codexNativeInvocation ? {
|
|
534
|
+
selected_strategy: codexNativeInvocation.selected_strategy,
|
|
535
|
+
hook_evidence_policy: codexNativeInvocation.env.SKS_CODEX_NATIVE_HOOK_EVIDENCE_POLICY,
|
|
536
|
+
blockers: codexNativeInvocation.blockers,
|
|
537
|
+
warnings: codexNativeInvocation.warnings,
|
|
538
|
+
artifact_path: 'mad-codex-native-invocation.json'
|
|
539
|
+
} : null,
|
|
524
540
|
activated_by: 'sks --mad',
|
|
525
541
|
cwd: path.resolve(cwd || process.cwd())
|
|
526
542
|
};
|
|
@@ -64,8 +64,7 @@ const FIXTURES = Object.freeze({
|
|
|
64
64
|
'cli-bootstrap': fixture('mock', 'sks bootstrap --dry-run', [], 'pass'),
|
|
65
65
|
'cli-deps': fixture('mock', 'sks deps check --json', [], 'pass'),
|
|
66
66
|
'cli-auth': fixture('mock', 'sks auth status --json', [], 'pass'),
|
|
67
|
-
'cli-
|
|
68
|
-
'cli-hermes': fixture('mock', 'sks hermes status --json', [], 'pass'),
|
|
67
|
+
'cli-codex-native': fixture('mock', 'sks codex-native status --json', [], 'pass'),
|
|
69
68
|
'cli-zellij': fixture('mock', 'npm run zellij:capability --silent', [], 'pass'),
|
|
70
69
|
'cli-tmux': fixture('mock', 'removed runtime migration notice: sks tmux --json', [], 'pass'),
|
|
71
70
|
'cli-mad': fixture('mock', 'sks mad --help', [], 'pass'),
|
|
@@ -129,8 +128,7 @@ const STATIC_CONTRACT_FEATURES = new Set([
|
|
|
129
128
|
'cli-bootstrap',
|
|
130
129
|
'cli-deps',
|
|
131
130
|
'cli-auth',
|
|
132
|
-
'cli-
|
|
133
|
-
'cli-hermes',
|
|
131
|
+
'cli-codex-native',
|
|
134
132
|
'cli-zellij',
|
|
135
133
|
'cli-tmux',
|
|
136
134
|
'cli-mad',
|
|
@@ -813,7 +813,7 @@ function commandCategory(name) {
|
|
|
813
813
|
return 'proof-route';
|
|
814
814
|
if (['qa-loop', 'research', 'recallpulse', 'skill-dream', 'eval', 'perf'].includes(name))
|
|
815
815
|
return 'loop';
|
|
816
|
-
if (['codex', 'codex-app', 'codex-lb', 'auth', 'hooks', 'context7'
|
|
816
|
+
if (['codex', 'codex-app', 'codex-native', 'codex-lb', 'auth', 'hooks', 'context7'].includes(name))
|
|
817
817
|
return 'integration';
|
|
818
818
|
if (['db', 'guard', 'conflicts', 'harness', 'versioning'].includes(name))
|
|
819
819
|
return 'safety';
|
package/dist/core/fsx.js
CHANGED
|
@@ -5,7 +5,7 @@ import os from 'node:os';
|
|
|
5
5
|
import crypto from 'node:crypto';
|
|
6
6
|
import { spawn } from 'node:child_process';
|
|
7
7
|
import { fileURLToPath } from 'node:url';
|
|
8
|
-
export const PACKAGE_VERSION = '3.1.
|
|
8
|
+
export const PACKAGE_VERSION = '3.1.7';
|
|
9
9
|
export const DEFAULT_PROCESS_TAIL_BYTES = 256 * 1024;
|
|
10
10
|
export const DEFAULT_PROCESS_TIMEOUT_MS = 30 * 60 * 1000;
|
|
11
11
|
export function nowIso() {
|
|
@@ -2,9 +2,17 @@ import fs from 'node:fs/promises';
|
|
|
2
2
|
import path from 'node:path';
|
|
3
3
|
import { nowIso, writeJsonAtomic } from '../fsx.js';
|
|
4
4
|
import { imageDimensions } from '../wiki-image/image-hash.js';
|
|
5
|
+
import { resolveCodexNativeInvocationPlan } from '../codex-native/codex-native-invocation-router.js';
|
|
5
6
|
export async function buildImageArtifactPathContract(root, input) {
|
|
6
7
|
const images = [];
|
|
7
8
|
const blockers = [];
|
|
9
|
+
const invocationPlan = await resolveCodexNativeInvocationPlan({
|
|
10
|
+
root,
|
|
11
|
+
missionId: input.missionId,
|
|
12
|
+
route: '$Image',
|
|
13
|
+
desiredCapability: 'image-followup'
|
|
14
|
+
}).catch(() => null);
|
|
15
|
+
const followupStrategy = invocationPlan?.selected_strategy === 'codex-app-native' ? 'model-visible-path' : 'artifact-path';
|
|
8
16
|
for (const [index, image] of input.images.entries()) {
|
|
9
17
|
const filePath = path.resolve(root, image.filePath || '');
|
|
10
18
|
const exists = await fileExists(filePath);
|
|
@@ -23,8 +31,11 @@ export async function buildImageArtifactPathContract(root, input) {
|
|
|
23
31
|
width: dims?.width ?? null,
|
|
24
32
|
height: dims?.height ?? null,
|
|
25
33
|
model_visible_path: filePath,
|
|
34
|
+
codex_native_followup_strategy: followupStrategy,
|
|
26
35
|
followup_edit_hint: exists
|
|
27
|
-
?
|
|
36
|
+
? followupStrategy === 'model-visible-path'
|
|
37
|
+
? `Use this model-visible saved local path for follow-up image edits: ${filePath}`
|
|
38
|
+
: `Use this saved artifact path for follow-up image edits: ${filePath}`
|
|
28
39
|
: 'Image file path missing; do not run visual QA until a real saved file path exists.'
|
|
29
40
|
});
|
|
30
41
|
}
|
|
@@ -35,6 +46,12 @@ export async function buildImageArtifactPathContract(root, input) {
|
|
|
35
46
|
mission_id: input.missionId,
|
|
36
47
|
generated_at: nowIso(),
|
|
37
48
|
images,
|
|
49
|
+
codex_native_invocation_plan: invocationPlan ? {
|
|
50
|
+
selected_strategy: invocationPlan.selected_strategy,
|
|
51
|
+
proof_policy: invocationPlan.proof_policy,
|
|
52
|
+
blockers: invocationPlan.blockers,
|
|
53
|
+
warnings: invocationPlan.warnings
|
|
54
|
+
} : null,
|
|
38
55
|
blockers: [...new Set(blockers)]
|
|
39
56
|
};
|
|
40
57
|
}
|
|
@@ -39,6 +39,9 @@ export function detectOwnerScopeCollisions(scopes) {
|
|
|
39
39
|
}
|
|
40
40
|
return blockers;
|
|
41
41
|
}
|
|
42
|
+
export function memoryHintMayExpandOwnerScope() {
|
|
43
|
+
return false;
|
|
44
|
+
}
|
|
42
45
|
function inferPackageScripts(domainId) {
|
|
43
46
|
if (domainId === 'docs')
|
|
44
47
|
return ['docs:loop-runtime'];
|
|
@@ -56,7 +56,12 @@ export async function planLoopsFromRequest(input) {
|
|
|
56
56
|
const memoryHints = await readInitDeepMemoryHints(input.root, scopePathsForNodes(nodes)).catch(() => []);
|
|
57
57
|
const nodesWithMemory = nodes.map((node) => {
|
|
58
58
|
const hints = memoryHints.filter((hint) => hintAppliesToNode(hint, node)).slice(0, 5);
|
|
59
|
-
return
|
|
59
|
+
return {
|
|
60
|
+
...node,
|
|
61
|
+
...(hints.length ? { memory_hints: hints } : {}),
|
|
62
|
+
memory_hints_used: hints,
|
|
63
|
+
memory_did_not_expand_scope: true
|
|
64
|
+
};
|
|
60
65
|
});
|
|
61
66
|
const plan = {
|
|
62
67
|
schema: 'sks.loop-plan.v1',
|
|
@@ -94,7 +99,8 @@ export async function planLoopsFromRequest(input) {
|
|
|
94
99
|
plan.project_memory = {
|
|
95
100
|
source: projectMemory.path,
|
|
96
101
|
injected: true,
|
|
97
|
-
summary: projectMemory.text.split(/\r?\n/).filter((line) => /^##\s+/.test(line)).slice(0, 8)
|
|
102
|
+
summary: projectMemory.text.split(/\r?\n/).filter((line) => /^##\s+/.test(line)).slice(0, 8),
|
|
103
|
+
memory_did_not_expand_scope: true
|
|
98
104
|
};
|
|
99
105
|
}
|
|
100
106
|
const validation = validateLoopPlan(plan);
|
|
@@ -10,6 +10,7 @@ export function buildLoopMakerPrompt(input) {
|
|
|
10
10
|
`Owner directories: ${node.owner_scope.directories.join(', ') || '-'}`,
|
|
11
11
|
`Allowed mutation scope: ${ownerScopeText(node)}`,
|
|
12
12
|
'Do not mutate outside the owner scope.',
|
|
13
|
+
'Memory hints are guidance only; memory never grants write permission or expands owner scope.',
|
|
13
14
|
`Selected local gates: ${allGateIds(node.gates).join(', ') || '-'}`,
|
|
14
15
|
`Budget: ${JSON.stringify(node.budget)}`,
|
|
15
16
|
`Worktree path: ${input.worktreePath || '-'}`,
|
|
@@ -30,6 +31,7 @@ export function buildLoopCheckerPrompt(input) {
|
|
|
30
31
|
`Selected gates: ${allGateIds(node.gates).join(', ') || '-'}`,
|
|
31
32
|
`Risk: ${node.risk.level} (${node.risk.reasons.join(', ') || '-'})`,
|
|
32
33
|
'Reject unrequested side effects and owner-scope violations.',
|
|
34
|
+
'Memory hints are guidance only; memory never grants write permission or expands owner scope.',
|
|
33
35
|
'Write checker-findings.json with fresh_session, reviewed_maker_artifacts, side_effects_detected, and approved.',
|
|
34
36
|
'No synthetic pass is allowed for production proof.'
|
|
35
37
|
].join('\n');
|
|
@@ -7,6 +7,7 @@ import { computeLoopConcurrencyBudget, loopWorkerBudgetFor } from './loop-concur
|
|
|
7
7
|
import { decideLoopFixturePolicy, writeLoopFixturePolicyDecision } from './loop-fixture-policy.js';
|
|
8
8
|
import { buildLoopCheckerPrompt, buildLoopMakerPrompt } from './loop-worker-prompts.js';
|
|
9
9
|
import { resolveCodexAppExecutionProfile } from '../codex-app/codex-app-execution-profile.js';
|
|
10
|
+
import { resolveCodexNativeInvocationPlan } from '../codex-native/codex-native-invocation-router.js';
|
|
10
11
|
export async function runLoopMakerWorkers(input) {
|
|
11
12
|
return runLoopWorkers({ ...input, phase: 'maker' });
|
|
12
13
|
}
|
|
@@ -44,7 +45,13 @@ async function runLoopWorkerNative(input) {
|
|
|
44
45
|
: buildLoopCheckerPrompt({ plan: input.plan, node: input.node, makerArtifacts: input.makerArtifacts || [] });
|
|
45
46
|
const workerCount = effectiveLoopWorkerCount(input);
|
|
46
47
|
const executionProfile = await resolveCodexAppExecutionProfile({ root: input.root }).catch(() => null);
|
|
47
|
-
const
|
|
48
|
+
const invocationPlan = await resolveCodexNativeInvocationPlan({
|
|
49
|
+
root: input.root,
|
|
50
|
+
missionId: input.plan.mission_id,
|
|
51
|
+
route: '$Loop',
|
|
52
|
+
desiredCapability: 'agent-role'
|
|
53
|
+
}).catch(() => null);
|
|
54
|
+
const workGraph = buildLoopNarutoWorkGraph(input, workerCount, executionProfile, invocationPlan);
|
|
48
55
|
// Root-cause-1 fix: keep the ORCHESTRATOR root on the MAIN repo (input.root), not the
|
|
49
56
|
// loop worktree. All zellij/right-column/slot-telemetry state derives from the orchestrator
|
|
50
57
|
// root, so anchoring it on input.root makes the SLOTS snapshot land under
|
|
@@ -81,7 +88,10 @@ async function runLoopWorkerNative(input) {
|
|
|
81
88
|
SKS_LOOP_MAIN_ROOT: input.root,
|
|
82
89
|
SKS_LOOP_WORKER_BUDGET: String(workerCount),
|
|
83
90
|
SKS_CODEX_APP_EXECUTION_PROFILE: executionProfile?.mode || 'unknown',
|
|
84
|
-
SKS_CODEX_AGENT_ROLE_STRATEGY: executionProfile?.agent_role_strategy || 'message-role'
|
|
91
|
+
SKS_CODEX_AGENT_ROLE_STRATEGY: executionProfile?.agent_role_strategy || 'message-role',
|
|
92
|
+
SKS_CODEX_NATIVE_STRATEGY: invocationPlan?.selected_strategy || 'message-role-fallback',
|
|
93
|
+
SKS_CODEX_NATIVE_AGENT_ROLE_STRATEGY: invocationPlan?.env.SKS_CODEX_NATIVE_AGENT_ROLE_STRATEGY || executionProfile?.agent_role_strategy || 'message-role',
|
|
94
|
+
SKS_CODEX_NATIVE_FEATURE_MATRIX: invocationPlan?.feature_matrix_artifact || '.sneakoscope/reports/codex-native-feature-matrix.json'
|
|
85
95
|
},
|
|
86
96
|
...(input.worktree?.path ? {
|
|
87
97
|
worktree: {
|
|
@@ -99,9 +109,9 @@ async function runLoopWorkerNative(input) {
|
|
|
99
109
|
fallback_reason: null
|
|
100
110
|
} : null
|
|
101
111
|
});
|
|
102
|
-
return normalizeNativeResult(input, orchestrator, executionProfile);
|
|
112
|
+
return normalizeNativeResult(input, orchestrator, executionProfile, invocationPlan);
|
|
103
113
|
}
|
|
104
|
-
async function normalizeNativeResult(input, result, executionProfile) {
|
|
114
|
+
async function normalizeNativeResult(input, result, executionProfile, invocationPlan) {
|
|
105
115
|
const artifacts = collectArtifactPaths(result);
|
|
106
116
|
const changedFiles = stringArray(result?.changed_files || result?.proof?.changed_files || result?.results?.flatMap?.((row) => row?.changed_files || []));
|
|
107
117
|
const blockers = [
|
|
@@ -132,6 +142,9 @@ async function normalizeNativeResult(input, result, executionProfile) {
|
|
|
132
142
|
artifact_path: executionProfile.artifact_path,
|
|
133
143
|
agent_type_probe_artifact_path: executionProfile.agent_type_probe_artifact_path
|
|
134
144
|
}
|
|
145
|
+
} : {}),
|
|
146
|
+
...(invocationPlan ? {
|
|
147
|
+
codex_native_invocation_plan: compactInvocationPlan(invocationPlan)
|
|
135
148
|
} : {})
|
|
136
149
|
};
|
|
137
150
|
await writeJsonAtomic(proofPath, { ...normalized, native_result_summary: summarizeNativeResult(result), generated_at: nowIso() });
|
|
@@ -146,6 +159,12 @@ async function runLoopWorkerFixture(input) {
|
|
|
146
159
|
});
|
|
147
160
|
const dir = path.join(loopNodeRoot(input.root, input.plan.mission_id, input.node.loop_id), input.phase);
|
|
148
161
|
await ensureDir(dir);
|
|
162
|
+
const invocationPlan = await resolveCodexNativeInvocationPlan({
|
|
163
|
+
root: input.root,
|
|
164
|
+
missionId: input.plan.mission_id,
|
|
165
|
+
route: '$Loop',
|
|
166
|
+
desiredCapability: 'agent-role'
|
|
167
|
+
}).catch(() => null);
|
|
149
168
|
const resultPath = path.join(dir, 'worker-runtime-result.json');
|
|
150
169
|
const childInputPath = path.join(dir, 'worker-fixture-intake.json');
|
|
151
170
|
await writeJsonAtomic(childInputPath, {
|
|
@@ -157,7 +176,8 @@ async function runLoopWorkerFixture(input) {
|
|
|
157
176
|
worker_count: input.phase === 'maker' ? input.node.maker.worker_count : input.node.checker.worker_count,
|
|
158
177
|
result_path: resultPath,
|
|
159
178
|
owner_scope: input.node.owner_scope,
|
|
160
|
-
maker_artifacts: input.makerArtifacts || []
|
|
179
|
+
maker_artifacts: input.makerArtifacts || [],
|
|
180
|
+
codex_native_invocation_plan: invocationPlan ? compactInvocationPlan(invocationPlan) : null
|
|
161
181
|
});
|
|
162
182
|
const child = await runProcess(process.execPath, [fixtureChildEntrypoint(), childInputPath], {
|
|
163
183
|
cwd: input.root,
|
|
@@ -197,13 +217,14 @@ async function runLoopWorkerFixture(input) {
|
|
|
197
217
|
fixture_allowed_reason: fixturePolicy.allowed ? fixturePolicy.reason : null
|
|
198
218
|
};
|
|
199
219
|
}
|
|
200
|
-
function buildLoopNarutoWorkGraph(input, workerCount, executionProfile) {
|
|
220
|
+
function buildLoopNarutoWorkGraph(input, workerCount, executionProfile, invocationPlan) {
|
|
201
221
|
const profilePayload = executionProfile ? {
|
|
202
222
|
mode: executionProfile.mode,
|
|
203
223
|
agent_role_strategy: executionProfile.agent_role_strategy,
|
|
204
224
|
artifact_path: executionProfile.artifact_path,
|
|
205
225
|
agent_type_probe_artifact_path: executionProfile.agent_type_probe_artifact_path
|
|
206
226
|
} : undefined;
|
|
227
|
+
const invocationPayload = invocationPlan ? compactInvocationPlan(invocationPlan) : undefined;
|
|
207
228
|
const workItems = Array.from({ length: Math.max(1, workerCount) }, (_, index) => {
|
|
208
229
|
const id = `${input.node.loop_id}-${input.phase}-${index + 1}`;
|
|
209
230
|
const writeAllowed = input.phase === 'maker';
|
|
@@ -233,7 +254,8 @@ function buildLoopNarutoWorkGraph(input, workerCount, executionProfile) {
|
|
|
233
254
|
required: input.node.worktree.required,
|
|
234
255
|
allocation_required: false
|
|
235
256
|
},
|
|
236
|
-
...(profilePayload ? { codex_app_execution_profile: profilePayload } : {})
|
|
257
|
+
...(profilePayload ? { codex_app_execution_profile: profilePayload } : {}),
|
|
258
|
+
...(invocationPayload ? { codex_native_invocation_plan: invocationPayload } : {})
|
|
237
259
|
};
|
|
238
260
|
});
|
|
239
261
|
return {
|
|
@@ -255,10 +277,23 @@ function buildLoopNarutoWorkGraph(input, workerCount, executionProfile) {
|
|
|
255
277
|
fallback_reason: input.worktree?.path ? 'loop_worktree_already_allocated' : null
|
|
256
278
|
},
|
|
257
279
|
...(profilePayload ? { codex_app_execution_profile: profilePayload } : {}),
|
|
280
|
+
...(invocationPayload ? { codex_native_invocation_plan: invocationPayload } : {}),
|
|
258
281
|
blockers: [],
|
|
259
282
|
ok: true
|
|
260
283
|
};
|
|
261
284
|
}
|
|
285
|
+
function compactInvocationPlan(plan) {
|
|
286
|
+
return {
|
|
287
|
+
route: plan.route,
|
|
288
|
+
desired_capability: plan.desired_capability,
|
|
289
|
+
selected_strategy: plan.selected_strategy,
|
|
290
|
+
required_artifacts: plan.required_artifacts,
|
|
291
|
+
proof_policy: plan.proof_policy,
|
|
292
|
+
env: plan.env,
|
|
293
|
+
blockers: plan.blockers,
|
|
294
|
+
warnings: plan.warnings
|
|
295
|
+
};
|
|
296
|
+
}
|
|
262
297
|
function collectArtifactPaths(result) {
|
|
263
298
|
return stringArray([
|
|
264
299
|
result?.ledger_root,
|
package/dist/core/qa-loop.js
CHANGED
|
@@ -3,6 +3,7 @@ import { exists, nowIso, readJson, readText, writeJsonAtomic, writeTextAtomic, P
|
|
|
3
3
|
import { CODEX_WEB_VERIFICATION_EVIDENCE_SOURCE, CODEX_WEB_VERIFICATION_POLICY, evidenceMentionsForbiddenBrowserAutomation, evidenceMentionsForbiddenWebComputerUseEvidence } from './routes.js';
|
|
4
4
|
import { appendAgentLedgerEvent, initializeAgentCentralLedger } from './agents/agent-central-ledger.js';
|
|
5
5
|
import { resolveCodexAppExecutionProfile } from './codex-app/codex-app-execution-profile.js';
|
|
6
|
+
import { resolveCodexNativeInvocationPlan } from './codex-native/codex-native-invocation-router.js';
|
|
6
7
|
export const QA_LOOP_ROUTE = 'QALoop';
|
|
7
8
|
const QA_REPORT_SUFFIX = 'qa-report.md';
|
|
8
9
|
const UI_CHROME_EXTENSION_FIRST_ACK = 'use_codex_chrome_extension_first_no_computer_use_for_web_ui_or_mark_unverified';
|
|
@@ -325,6 +326,7 @@ export function defaultQaGate(contract = {}, opts = {}) {
|
|
|
325
326
|
codex_app_execution_profile_artifact: opts.executionProfile ? 'qa-loop/execution-profile.json' : null,
|
|
326
327
|
codex_app_hooks_approval_required: opts.executionProfile?.hooks_approval_required === true,
|
|
327
328
|
codex_app_agent_role_strategy: opts.executionProfile?.agent_role_strategy || null,
|
|
329
|
+
codex_native_invocation: opts.codexNativeInvocation || null,
|
|
328
330
|
api_e2e_required: apiRequired,
|
|
329
331
|
unsafe_external_side_effects: false,
|
|
330
332
|
corrective_loop_enabled: corrective,
|
|
@@ -345,19 +347,23 @@ export async function writeQaLoopArtifacts(dir, mission, contract) {
|
|
|
345
347
|
const reportFile = qaReportFilename();
|
|
346
348
|
const root = missionRootFromDir(dir);
|
|
347
349
|
const executionProfile = root ? await resolveCodexAppExecutionProfile({ root }).catch(() => null) : null;
|
|
350
|
+
const codexNativeInvocation = root ? await resolveQaCodexNativeInvocation(root, mission.id).catch(() => null) : null;
|
|
348
351
|
if (executionProfile)
|
|
349
352
|
await writeJsonAtomic(path.join(dir, 'qa-loop', 'execution-profile.json'), executionProfile).catch(() => undefined);
|
|
353
|
+
if (codexNativeInvocation)
|
|
354
|
+
await writeJsonAtomic(path.join(dir, 'qa-loop', 'codex-native-invocation.json'), codexNativeInvocation).catch(() => undefined);
|
|
350
355
|
await writeJsonAtomic(path.join(dir, 'qa-ledger.json'), {
|
|
351
356
|
schema_version: 1,
|
|
352
357
|
generated_at: nowIso(),
|
|
353
358
|
mission_id: mission.id,
|
|
354
359
|
qa_report_file: reportFile,
|
|
355
360
|
codex_app_execution_profile: executionProfile ? compactExecutionProfile(executionProfile) : null,
|
|
361
|
+
codex_native_invocation: codexNativeInvocation,
|
|
356
362
|
target: { scope: a.QA_SCOPE, environment: a.TARGET_ENVIRONMENT, base_url: a.TARGET_BASE_URL, api_base_url: a.API_BASE_URL },
|
|
357
363
|
safety: { mutation_policy: a.QA_MUTATION_POLICY, deployed_destructive_tests_allowed: 'never', credentials: 'temp_only_never_saved', ui_evidence: 'codex_chrome_extension_first_required_for_web_ui_e2e' },
|
|
358
364
|
checklist
|
|
359
365
|
});
|
|
360
|
-
await writeJsonAtomic(path.join(dir, 'qa-gate.json'), defaultQaGate(contract, { reportFile, executionProfile }));
|
|
366
|
+
await writeJsonAtomic(path.join(dir, 'qa-gate.json'), defaultQaGate(contract, { reportFile, executionProfile, codexNativeInvocation }));
|
|
361
367
|
await writeTextAtomic(path.join(dir, reportFile), qaReportTemplate(mission, contract, checklist));
|
|
362
368
|
return { checklist_count: checklist.length, report_file: reportFile };
|
|
363
369
|
}
|
|
@@ -446,6 +452,7 @@ export async function writeMockQaResult(dir, mission, contract) {
|
|
|
446
452
|
codex_app_execution_profile_artifact: previousGate.codex_app_execution_profile_artifact || null,
|
|
447
453
|
codex_app_hooks_approval_required: previousGate.codex_app_hooks_approval_required === true,
|
|
448
454
|
codex_app_agent_role_strategy: previousGate.codex_app_agent_role_strategy || null,
|
|
455
|
+
codex_native_invocation: previousGate.codex_native_invocation || null,
|
|
449
456
|
blockers: previousGate.blockers || [],
|
|
450
457
|
passed: !uiRequired,
|
|
451
458
|
qa_report_written: true,
|
|
@@ -467,6 +474,22 @@ export async function writeMockQaResult(dir, mission, contract) {
|
|
|
467
474
|
});
|
|
468
475
|
return evaluateQaGate(dir);
|
|
469
476
|
}
|
|
477
|
+
async function resolveQaCodexNativeInvocation(root, missionId) {
|
|
478
|
+
const [visualReview, hookEvidence, imageFollowup] = await Promise.all([
|
|
479
|
+
resolveCodexNativeInvocationPlan({ root, missionId, route: '$QA-LOOP', desiredCapability: 'visual-review' }),
|
|
480
|
+
resolveCodexNativeInvocationPlan({ root, missionId, route: '$QA-LOOP', desiredCapability: 'hook-evidence' }),
|
|
481
|
+
resolveCodexNativeInvocationPlan({ root, missionId, route: '$Image', desiredCapability: 'image-followup' })
|
|
482
|
+
]);
|
|
483
|
+
return {
|
|
484
|
+
visual_review: visualReview.selected_strategy,
|
|
485
|
+
visual_review_plan: visualReview,
|
|
486
|
+
hook_evidence_policy: hookEvidence.env.SKS_CODEX_NATIVE_HOOK_EVIDENCE_POLICY,
|
|
487
|
+
hook_evidence_plan: hookEvidence,
|
|
488
|
+
image_path_strategy: imageFollowup.selected_strategy === 'codex-app-native' ? 'model-visible-path' : 'artifact-path',
|
|
489
|
+
image_followup_plan: imageFollowup,
|
|
490
|
+
hook_derived_evidence_counted: hookEvidence.selected_strategy !== 'blocked'
|
|
491
|
+
};
|
|
492
|
+
}
|
|
470
493
|
export function buildQaLoopPrompt({ id, mission, contract, cycle, previous, reportFile, imagePathContract, appHandoff, executionProfile }) {
|
|
471
494
|
const report = reportFile && isQaReportFilename(reportFile) ? reportFile : 'the date/version-prefixed report named by qa-gate.json.qa_report_file';
|
|
472
495
|
const imageContractText = imagePathContract
|
package/dist/core/research.js
CHANGED
|
@@ -18,6 +18,7 @@ import { RESEARCH_WORK_GRAPH_ARTIFACT, writeResearchWorkGraph } from './research
|
|
|
18
18
|
import { researchPromptContractText, validateResearchPromptContract } from './research/research-prompt-contract.js';
|
|
19
19
|
import { buildRealisticResearchPaper, buildRealisticResearchReport } from './research/research-realistic-report.js';
|
|
20
20
|
import { resolveCodexAppExecutionProfile } from './codex-app/codex-app-execution-profile.js';
|
|
21
|
+
import { resolveCodexNativeInvocationPlan } from './codex-native/codex-native-invocation-router.js';
|
|
21
22
|
export const RESEARCH_PAPER_ARTIFACT = 'research-paper.md';
|
|
22
23
|
export const RESEARCH_SOURCE_SKILL_ARTIFACT = 'research-source-skill.md';
|
|
23
24
|
export const RESEARCH_GENIUS_SUMMARY_ARTIFACT = 'genius-opinion-summary.md';
|
|
@@ -254,6 +255,14 @@ export function createResearchPlan(prompt, opts = {}) {
|
|
|
254
255
|
const paperArtifact = researchPaperArtifactName(prompt, createdAt, opts);
|
|
255
256
|
const nativeAgentPlan = researchNativeAgentPlan(prompt, { paperArtifact, missionId: opts.missionId });
|
|
256
257
|
const executionProfile = opts.executionProfile || null;
|
|
258
|
+
const codexNativeInvocation = opts.codexNativeInvocation || null;
|
|
259
|
+
const sourceStrategy = codexNativeInvocation?.mcp_source?.selected_strategy === 'codex-app-native'
|
|
260
|
+
? 'mcp-plugin-candidates'
|
|
261
|
+
: codexNativeInvocation?.web_search?.selected_strategy === 'codex-cli-headless'
|
|
262
|
+
? 'web-sources'
|
|
263
|
+
: executionProfile?.plugin_mcp_inventory_ready
|
|
264
|
+
? 'mcp-plugin-candidates'
|
|
265
|
+
: 'local-files';
|
|
257
266
|
return {
|
|
258
267
|
schema_version: 1,
|
|
259
268
|
mission_id: opts.missionId || null,
|
|
@@ -265,6 +274,7 @@ export function createResearchPlan(prompt, opts = {}) {
|
|
|
265
274
|
quality_contract: DEFAULT_RESEARCH_QUALITY_CONTRACT,
|
|
266
275
|
native_agent_plan: nativeAgentPlan,
|
|
267
276
|
codex_app_execution_profile: executionProfile ? compactExecutionProfile(executionProfile) : null,
|
|
277
|
+
codex_native_invocation: codexNativeInvocation,
|
|
268
278
|
agent_sessions: nativeAgentPlan.personas,
|
|
269
279
|
agent_batches: nativeAgentPlan.batches,
|
|
270
280
|
autoresearch_cycle_policy: nativeAgentPlan.autoresearch_cycle_policy,
|
|
@@ -322,10 +332,11 @@ export function createResearchPlan(prompt, opts = {}) {
|
|
|
322
332
|
mode: 'layered_source_retrieval_and_triangulation',
|
|
323
333
|
requirement: 'Use every safely available public web/source route before synthesis, separated into source layers so the final claim is not dominated by one corpus or platform.',
|
|
324
334
|
source_tool_routing: {
|
|
325
|
-
mode:
|
|
335
|
+
mode: sourceStrategy,
|
|
326
336
|
plugin_mcp_inventory_ready: executionProfile?.plugin_mcp_inventory_ready === true,
|
|
327
337
|
execution_profile_artifact: executionProfile?.artifact_path || '.sneakoscope/reports/codex-app-execution-profile.json',
|
|
328
|
-
|
|
338
|
+
codex_native_invocation_artifact: codexNativeInvocation ? 'research/codex-native-invocation.json' : null,
|
|
339
|
+
rule: 'Prefer verified plugin/MCP candidates when available; otherwise record source-tool blockers instead of assuming live search coverage.'
|
|
329
340
|
},
|
|
330
341
|
query_sets: [
|
|
331
342
|
'first-principles and theory sources',
|
|
@@ -553,7 +564,9 @@ export function countGeniusOpinionSummaries(text = '') {
|
|
|
553
564
|
export async function writeResearchPlan(dir, prompt, opts = {}) {
|
|
554
565
|
const root = opts.root || missionRootFromDir(String(dir || ''));
|
|
555
566
|
const executionProfile = opts.executionProfile || (root ? await resolveCodexAppExecutionProfile({ root }).catch(() => null) : null);
|
|
556
|
-
const
|
|
567
|
+
const missionId = opts.missionId || path.basename(String(dir || ''));
|
|
568
|
+
const codexNativeInvocation = root ? await resolveResearchCodexNativeInvocation(root, missionId).catch(() => null) : null;
|
|
569
|
+
const plan = createResearchPlan(prompt, { ...opts, executionProfile, codexNativeInvocation });
|
|
557
570
|
const noveltyLedger = {
|
|
558
571
|
schema_version: 1,
|
|
559
572
|
entries: [],
|
|
@@ -571,6 +584,8 @@ export async function writeResearchPlan(dir, prompt, opts = {}) {
|
|
|
571
584
|
await writeJsonAtomic(path.join(dir, 'research-plan.json'), plan);
|
|
572
585
|
if (executionProfile)
|
|
573
586
|
await writeJsonAtomic(path.join(dir, 'research', 'execution-profile.json'), executionProfile).catch(() => undefined);
|
|
587
|
+
if (codexNativeInvocation)
|
|
588
|
+
await writeJsonAtomic(path.join(dir, 'research', 'codex-native-invocation.json'), codexNativeInvocation).catch(() => undefined);
|
|
574
589
|
await writeTextAtomic(path.join(dir, 'research-plan.md'), researchPlanMarkdown(plan));
|
|
575
590
|
await writeTextAtomic(path.join(dir, RESEARCH_SOURCE_SKILL_ARTIFACT), researchSourceSkillMarkdown(plan));
|
|
576
591
|
await writeResearchQualityContract(dir, plan.quality_contract);
|
|
@@ -593,6 +608,24 @@ export async function writeResearchPlan(dir, prompt, opts = {}) {
|
|
|
593
608
|
await appendJsonlBounded(path.join(dir, 'events.jsonl'), { ts: nowIso(), type: 'research.plan.created', depth: plan.depth });
|
|
594
609
|
return plan;
|
|
595
610
|
}
|
|
611
|
+
async function resolveResearchCodexNativeInvocation(root, missionId) {
|
|
612
|
+
const [pluginSource, mcpSource, webSearch] = await Promise.all([
|
|
613
|
+
resolveCodexNativeInvocationPlan({ root, missionId, route: '$Research', desiredCapability: 'plugin-source' }),
|
|
614
|
+
resolveCodexNativeInvocationPlan({ root, missionId, route: '$Research', desiredCapability: 'mcp-source' }),
|
|
615
|
+
resolveCodexNativeInvocationPlan({ root, missionId, route: '$Research', desiredCapability: 'web-search' })
|
|
616
|
+
]);
|
|
617
|
+
return {
|
|
618
|
+
plugin_source: pluginSource,
|
|
619
|
+
mcp_source: mcpSource,
|
|
620
|
+
web_search: webSearch,
|
|
621
|
+
selected_source_strategy: mcpSource.selected_strategy === 'codex-app-native'
|
|
622
|
+
? 'mcp-plugin-candidates'
|
|
623
|
+
: webSearch.selected_strategy === 'codex-cli-headless'
|
|
624
|
+
? 'web-sources'
|
|
625
|
+
: 'local-files',
|
|
626
|
+
hook_derived_source_evidence_allowed: false
|
|
627
|
+
};
|
|
628
|
+
}
|
|
596
629
|
function missionRootFromDir(dir) {
|
|
597
630
|
const normalized = path.resolve(String(dir || ''));
|
|
598
631
|
const marker = `${path.sep}.sneakoscope${path.sep}missions${path.sep}`;
|