sneakoscope 4.6.5 → 4.7.1
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 +18 -4
- 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/bin/sks.js +1 -1
- package/dist/cli/install-helpers.js +80 -15
- package/dist/commands/codex-app.js +34 -3
- package/dist/commands/doctor.js +1 -1
- package/dist/core/agents/agent-effort-policy.js +110 -5
- package/dist/core/agents/agent-orchestrator.js +27 -3
- package/dist/core/agents/agent-plan.js +2 -2
- package/dist/core/agents/agent-roster.js +8 -0
- package/dist/core/agents/agent-worker-slot.js +4 -0
- package/dist/core/codex/agent-config-file-repair.js +4 -4
- package/dist/core/codex-app/codex-app-fast-ui-repair.js +4 -2
- package/dist/core/codex-app/codex-app-ui-state-snapshot.js +14 -3
- package/dist/core/codex-app/glm-model-profile.js +1 -1
- package/dist/core/codex-app/glm-profile-installer.js +111 -7
- package/dist/core/codex-app/glm-profile-schema.js +21 -2
- package/dist/core/codex-app.js +2 -0
- package/dist/core/codex-control/codex-sdk-config-policy.js +2 -1
- package/dist/core/codex-model-guard.js +3 -1
- package/dist/core/commands/fast-mode-command.js +5 -2
- package/dist/core/commands/glm-command.js +2 -1
- package/dist/core/commands/mad-sks-command.js +24 -0
- package/dist/core/doctor/doctor-codex-startup-repair.js +111 -9
- package/dist/core/feature-registry.js +3 -3
- package/dist/core/fsx.js +1 -1
- package/dist/core/harness-guard.js +89 -3
- package/dist/core/init.js +13 -16
- package/dist/core/managed-assets/managed-assets-manifest.js +9 -8
- package/dist/core/pipeline-internals/runtime-core.js +3 -3
- package/dist/core/providers/glm/glm-52-profile.js +46 -0
- package/dist/core/providers/glm/glm-52-request.js +11 -2
- package/dist/core/providers/glm/glm-direct-run.js +1 -0
- package/dist/core/providers/glm/glm-mad-launch.js +3 -2
- package/dist/core/providers/glm/glm-profile-resolver.js +69 -4
- package/dist/core/providers/glm/naruto/glm-naruto-command.js +2 -1
- package/dist/core/team-live.js +2 -2
- package/dist/core/version.js +1 -1
- package/dist/scripts/agent-role-config-repair-check.js +5 -3
- package/dist/scripts/codex-native-reference-cache-check.js +8 -1
- package/dist/scripts/doctor-codex-startup-repair-check.js +21 -0
- package/dist/scripts/doctor-fixes-codex-app-fast-ui-check.js +6 -2
- package/dist/scripts/naruto-shadow-clone-swarm-check.js +11 -3
- package/dist/scripts/release-readiness-report.js +1 -1
- package/dist/scripts/sks-3-1-6-directive-check-lib.js +11 -0
- package/package.json +1 -1
|
@@ -112,8 +112,12 @@ export function buildAgentRoster(opts = {}) {
|
|
|
112
112
|
index: index + 1,
|
|
113
113
|
write_policy: persona.write_policy,
|
|
114
114
|
status: 'pending',
|
|
115
|
+
model: effort.model,
|
|
115
116
|
reasoning_effort: effort.reasoning_effort,
|
|
116
117
|
model_reasoning_effort: effort.model_reasoning_effort,
|
|
118
|
+
model_tier: effort.model_tier,
|
|
119
|
+
model_profile: effort.model_profile,
|
|
120
|
+
model_selection_reason: effort.model_selection_reason,
|
|
117
121
|
reasoning_profile: effort.reasoning_profile,
|
|
118
122
|
service_tier: effort.service_tier,
|
|
119
123
|
reasoning_reason: effort.reason,
|
|
@@ -192,8 +196,12 @@ export function buildNarutoCloneRoster(opts = {}) {
|
|
|
192
196
|
index: index + 1,
|
|
193
197
|
write_policy: cloneReadonly ? 'read-only' : 'exclusive Naruto patch-envelope lease required',
|
|
194
198
|
status: 'pending',
|
|
199
|
+
model: effort.model,
|
|
195
200
|
reasoning_effort: effort.reasoning_effort,
|
|
196
201
|
model_reasoning_effort: effort.model_reasoning_effort,
|
|
202
|
+
model_tier: effort.model_tier,
|
|
203
|
+
model_profile: effort.model_profile,
|
|
204
|
+
model_selection_reason: effort.model_selection_reason,
|
|
197
205
|
reasoning_profile: effort.reasoning_profile,
|
|
198
206
|
service_tier: 'fast',
|
|
199
207
|
fast_mode: true,
|
|
@@ -17,7 +17,11 @@ export function createAgentWorkerSlots(roster, targetActiveSlots) {
|
|
|
17
17
|
persona_id: String(persona.persona_id || persona.id || `agent_${index + 1}`),
|
|
18
18
|
role: String(persona.role || 'verifier'),
|
|
19
19
|
write_policy: String(persona.write_policy || 'read-only'),
|
|
20
|
+
model: persona.model || null,
|
|
20
21
|
reasoning_effort: persona.reasoning_effort || persona.model_reasoning_effort || null,
|
|
22
|
+
model_reasoning_effort: persona.model_reasoning_effort || null,
|
|
23
|
+
model_tier: persona.model_tier || null,
|
|
24
|
+
model_profile: persona.model_profile || null,
|
|
21
25
|
reasoning_profile: persona.reasoning_profile || null,
|
|
22
26
|
service_tier: persona.service_tier || 'fast',
|
|
23
27
|
fast_mode: persona.fast_mode !== false
|
|
@@ -175,15 +175,15 @@ function minimalManagedConfigToml() {
|
|
|
175
175
|
'[mcp_servers.context7]',
|
|
176
176
|
'url = "https://mcp.context7.com/mcp"',
|
|
177
177
|
'',
|
|
178
|
-
agentConfigBlock('native_agent', '
|
|
178
|
+
agentConfigBlock('native_agent', 'SKS native agent with bounded write capability.', './agents/native-agent-intake.toml', ['Analysis', 'Mapper']),
|
|
179
179
|
'',
|
|
180
|
-
agentConfigBlock('team_consensus', 'SKS planning/debate agent.', './agents/team-consensus.toml', ['Consensus', 'Atlas']),
|
|
180
|
+
agentConfigBlock('team_consensus', 'SKS planning/debate agent with bounded write capability.', './agents/team-consensus.toml', ['Consensus', 'Atlas']),
|
|
181
181
|
'',
|
|
182
182
|
agentConfigBlock('implementation_worker', 'SKS bounded implementation worker.', './agents/implementation-worker.toml', ['Builder', 'Mason']),
|
|
183
183
|
'',
|
|
184
|
-
agentConfigBlock('db_safety_reviewer', '
|
|
184
|
+
agentConfigBlock('db_safety_reviewer', 'DB safety reviewer with bounded write capability.', './agents/db-safety-reviewer.toml', ['Sentinel', 'Ledger']),
|
|
185
185
|
'',
|
|
186
|
-
agentConfigBlock('qa_reviewer', '
|
|
186
|
+
agentConfigBlock('qa_reviewer', 'QA reviewer with bounded write capability.', './agents/qa-reviewer.toml', ['Verifier', 'Reviewer']),
|
|
187
187
|
''
|
|
188
188
|
].join('\n');
|
|
189
189
|
}
|
|
@@ -4,7 +4,8 @@ import { ensureDir, nowIso, readText, sha256, writeJsonAtomic, writeTextAtomic }
|
|
|
4
4
|
import { codexHome, scanProjectLocalForbiddenKeys, snapshotCodexAppUiState } from './codex-app-ui-state-snapshot.js';
|
|
5
5
|
import { assertCodexAppUiMutationAllowed } from './codex-app-ui-clobber-guard.js';
|
|
6
6
|
export const CODEX_APP_FAST_UI_REPAIR_SCHEMA = 'sks.codex-app-fast-ui-repair.v1';
|
|
7
|
-
const FAST_UI_TOP_LEVEL_RE = /^\s*service_tier\s*=/;
|
|
7
|
+
const FAST_UI_TOP_LEVEL_RE = /^\s*(?:model|model_reasoning_effort|service_tier)\s*=/;
|
|
8
|
+
const CODEX_APP_MODE_LOCK_TOP_LEVEL_RE = /^\s*(?:model|model_reasoning_effort)\s*=/;
|
|
8
9
|
const FAST_UI_FEATURE_LINE_RE = /^\s*fast_mode\s*=/;
|
|
9
10
|
const FAST_UI_USER_TABLE_LINE_RE = /^\s*(enabled|visible|locked|hidden|disabled)\s*=/;
|
|
10
11
|
const SKS_CAUSED_RE = /(?:SKS|Sneakoscope|codex-lb|sks-mad|sks fast)/i;
|
|
@@ -125,10 +126,11 @@ function stripProjectLocalForbiddenKeys(text) {
|
|
|
125
126
|
}
|
|
126
127
|
function stripSksCausedHostOwnedLines(text) {
|
|
127
128
|
return stripMatchingLines(text, (line, table, previous, next) => {
|
|
129
|
+
const isCodexAppModeLock = !table && CODEX_APP_MODE_LOCK_TOP_LEVEL_RE.test(line);
|
|
128
130
|
const isFastUiLine = FAST_UI_TOP_LEVEL_RE.test(line)
|
|
129
131
|
|| (table === 'features' && FAST_UI_FEATURE_LINE_RE.test(line))
|
|
130
132
|
|| (table === 'user.fast_mode' && FAST_UI_USER_TABLE_LINE_RE.test(line));
|
|
131
|
-
return isFastUiLine && (SKS_CAUSED_RE.test(line) || SKS_CAUSED_RE.test(previous) || SKS_CAUSED_RE.test(next));
|
|
133
|
+
return isCodexAppModeLock || (isFastUiLine && (SKS_CAUSED_RE.test(line) || SKS_CAUSED_RE.test(previous) || SKS_CAUSED_RE.test(next)));
|
|
132
134
|
});
|
|
133
135
|
}
|
|
134
136
|
function stripMatchingLines(text, shouldRemove) {
|
|
@@ -4,6 +4,8 @@ import path from 'node:path';
|
|
|
4
4
|
import { exists, nowIso, readText, sha256, writeJsonAtomic } from '../fsx.js';
|
|
5
5
|
export const CODEX_APP_UI_STATE_SNAPSHOT_SCHEMA = 'sks.codex-app-ui-state-snapshot.v1';
|
|
6
6
|
export const PROJECT_LOCAL_FORBIDDEN_CODEX_KEYS = [
|
|
7
|
+
'model',
|
|
8
|
+
'model_reasoning_effort',
|
|
7
9
|
'openai_base_url',
|
|
8
10
|
'chatgpt_base_url',
|
|
9
11
|
'apps_mcp_product_sku',
|
|
@@ -15,7 +17,7 @@ export const PROJECT_LOCAL_FORBIDDEN_CODEX_KEYS = [
|
|
|
15
17
|
'experimental_realtime_ws_base_url',
|
|
16
18
|
'otel'
|
|
17
19
|
];
|
|
18
|
-
const HOST_OWNED_KEY_RE = /^(?:openai_base_url|chatgpt_base_url|apps_mcp_product_sku|model_provider|model_providers(?:\.|$)|notify|profile|profiles(?:\.|$)|experimental_realtime_ws_base_url|otel(?:\.|$)|features\.fast_mode|service_tier|user\.fast_mode(?:\.|$))/;
|
|
20
|
+
const HOST_OWNED_KEY_RE = /^(?:model|model_reasoning_effort|openai_base_url|chatgpt_base_url|apps_mcp_product_sku|model_provider|model_providers(?:\.|$)|notify|profile|profiles(?:\.|$)|experimental_realtime_ws_base_url|otel(?:\.|$)|features\.fast_mode|service_tier|user\.fast_mode(?:\.|$))/;
|
|
19
21
|
const SECRET_KEY_RE = /(?:key|token|secret|password|credential|cookie|authorization|bearer|refresh|access)/i;
|
|
20
22
|
export function codexHome(input) {
|
|
21
23
|
return path.resolve(String(input?.codexHome || process.env.CODEX_HOME || path.join(os.homedir(), '.codex')));
|
|
@@ -35,11 +37,16 @@ export async function snapshotCodexAppUiState(root = process.cwd(), input = {})
|
|
|
35
37
|
const providerSignals = hostOwnedSignals
|
|
36
38
|
.filter((signal) => signal.provider_related)
|
|
37
39
|
.map((signal) => `${signal.key_path}=${signal.value_preview}`);
|
|
38
|
-
const
|
|
40
|
+
const baseConfigHostOwnedSignals = files
|
|
41
|
+
.filter((file) => !isProfileConfigSnapshotPath(file.path))
|
|
42
|
+
.flatMap((file) => file.signals.filter((signal) => signal.host_owned));
|
|
43
|
+
const fastSelectorLocked = baseConfigHostOwnedSignals.some((signal) => {
|
|
39
44
|
if (signal.key_path === 'features.fast_mode' && signal.value_preview === 'false')
|
|
40
45
|
return true;
|
|
41
46
|
if (signal.key_path.startsWith('user.fast_mode') && /hidden|fixed|disabled|false/i.test(signal.value_preview))
|
|
42
47
|
return true;
|
|
48
|
+
if (signal.key_path === 'model' || signal.key_path === 'model_reasoning_effort')
|
|
49
|
+
return true;
|
|
43
50
|
if (signal.key_path === 'service_tier' && signal.sks_related)
|
|
44
51
|
return true;
|
|
45
52
|
return false;
|
|
@@ -120,7 +127,7 @@ export function scanTomlSignals(text) {
|
|
|
120
127
|
value_hash: sha256(value),
|
|
121
128
|
line: index + 1,
|
|
122
129
|
host_owned: HOST_OWNED_KEY_RE.test(keyPath),
|
|
123
|
-
fast_ui_related: /(?:fast_mode|service_tier)/i.test(keyPath) || /(?:fast|priority|default)/i.test(value),
|
|
130
|
+
fast_ui_related: /(?:fast_mode|service_tier|model_reasoning_effort|^model$)/i.test(keyPath) || /(?:fast|priority|default)/i.test(value),
|
|
124
131
|
provider_related: /(?:provider|base_url|auth|profile|openai|chatgpt|codex-lb)/i.test(lowerPath),
|
|
125
132
|
sks_related: /(?:sks|sneakoscope|codex-lb)/i.test(lineText)
|
|
126
133
|
});
|
|
@@ -176,6 +183,10 @@ async function profileConfigFiles(home) {
|
|
|
176
183
|
return [];
|
|
177
184
|
}
|
|
178
185
|
}
|
|
186
|
+
function isProfileConfigSnapshotPath(file) {
|
|
187
|
+
const base = path.basename(file);
|
|
188
|
+
return /^profile-.+\.config\.toml$/.test(base) || /^[A-Za-z0-9_-]+\.config\.toml$/.test(base);
|
|
189
|
+
}
|
|
179
190
|
async function readAuthMetadata(file) {
|
|
180
191
|
const text = await readText(file, null);
|
|
181
192
|
if (text == null)
|
|
@@ -1,2 +1,2 @@
|
|
|
1
|
-
export { GLM_CODEX_APP_PROFILE_ID, GLM_CODEX_APP_PROFILE_LABEL, buildGlmCodexAppModelProfile } from '../providers/glm/glm-52-profile.js';
|
|
1
|
+
export { GLM_CODEX_APP_PROFILE_ID, GLM_CODEX_APP_PROFILE_LABEL, GLM_CODEX_CONFIG_PROFILE_ID, GLM_CODEX_CONFIG_PROVIDER_ID, GLM_CODEX_CONFIG_REASONING_PROFILES, GLM_CODEX_SELECTABLE_REASONING_EFFORTS, buildGlmCodexAppModelProfile } from '../providers/glm/glm-52-profile.js';
|
|
2
2
|
//# sourceMappingURL=glm-model-profile.js.map
|
|
@@ -1,30 +1,49 @@
|
|
|
1
1
|
import path from 'node:path';
|
|
2
|
-
import {
|
|
3
|
-
import {
|
|
2
|
+
import { codexLbConfigPath, ensureGlobalCodexAppGlmProfile } from '../../cli/install-helpers.js';
|
|
3
|
+
import { readJson, readText, writeJsonAtomic, nowIso } from '../fsx.js';
|
|
4
|
+
import { GLM_CODEX_CONFIG_PROFILE_ID, GLM_CODEX_CONFIG_PROVIDER_ID, GLM_CODEX_CONFIG_REASONING_PROFILES, buildGlmCodexAppModelProfile } from './glm-model-profile.js';
|
|
4
5
|
import { validateGlmCodexAppModelProfile } from './glm-profile-schema.js';
|
|
5
6
|
import { resolveOpenRouterApiKey } from '../providers/openrouter/openrouter-secret-store.js';
|
|
7
|
+
import { GLM_52_OPENROUTER_MODEL } from '../providers/glm/glm-52-settings.js';
|
|
6
8
|
export async function installCodexAppGlmProfile(input) {
|
|
7
9
|
const root = path.resolve(input.root);
|
|
8
10
|
const profile = buildGlmCodexAppModelProfile();
|
|
9
11
|
const profilePath = path.join(root, '.sneakoscope', 'codex-app', 'glm-model-profile.json');
|
|
10
12
|
const reportPath = path.join(root, '.sneakoscope', 'reports', 'codex-app-glm-profile.json');
|
|
11
13
|
const key = await resolveOpenRouterApiKey({ env: input.env || process.env });
|
|
14
|
+
const home = input.home || input.env?.HOME;
|
|
15
|
+
const configWrite = input.apply === false
|
|
16
|
+
? null
|
|
17
|
+
: await ensureGlobalCodexAppGlmProfile({ home, configPath: input.configPath });
|
|
18
|
+
const configStatus = input.apply === false
|
|
19
|
+
? await previewCodexAppGlmConfigStatus({ home, configPath: input.configPath })
|
|
20
|
+
: await inspectCodexAppGlmConfig({ home, configPath: input.configPath });
|
|
21
|
+
const configWriteBlockers = configWrite?.ok === false
|
|
22
|
+
? [`glm_codex_app_config_${configWrite.status || 'failed'}`]
|
|
23
|
+
: [];
|
|
24
|
+
const blockers = input.apply === false ? [] : [...configWriteBlockers, ...configStatus.blockers];
|
|
12
25
|
const warnings = [
|
|
13
26
|
...key.warnings,
|
|
14
|
-
...(key.key ? [] : ['openrouter_key_missing_until_sks_--mad_--glm_--repair'])
|
|
27
|
+
...(key.key ? [] : ['openrouter_key_missing_until_sks_--mad_--glm_--repair']),
|
|
28
|
+
...(input.apply === false ? ['codex_desktop_glm_config_not_written_apply_false'] : [])
|
|
15
29
|
];
|
|
16
30
|
if (input.apply !== false)
|
|
17
31
|
await writeJsonAtomic(profilePath, profile);
|
|
18
32
|
const result = {
|
|
19
33
|
schema: 'sks.codex-app-glm-profile-result.v1',
|
|
20
34
|
generated_at: nowIso(),
|
|
21
|
-
ok:
|
|
22
|
-
status: input.apply === false ? 'valid' : 'installed',
|
|
35
|
+
ok: blockers.length === 0,
|
|
36
|
+
status: blockers.length === 0 ? input.apply === false ? 'valid' : 'installed' : 'blocked',
|
|
23
37
|
profile,
|
|
24
38
|
profile_path: '.sneakoscope/codex-app/glm-model-profile.json',
|
|
25
39
|
report_path: '.sneakoscope/reports/codex-app-glm-profile.json',
|
|
40
|
+
config_path: configStatus.config_path,
|
|
41
|
+
codex_config_profile: GLM_CODEX_CONFIG_PROFILE_ID,
|
|
42
|
+
codex_reasoning_profiles: GLM_CODEX_CONFIG_REASONING_PROFILES.map((item) => item.id),
|
|
43
|
+
config_write: configWrite,
|
|
44
|
+
config_status: configStatus,
|
|
26
45
|
openrouter_key_source: key.source,
|
|
27
|
-
blockers
|
|
46
|
+
blockers,
|
|
28
47
|
warnings
|
|
29
48
|
};
|
|
30
49
|
await writeJsonAtomic(reportPath, result).catch(() => undefined);
|
|
@@ -38,7 +57,9 @@ export async function doctorCodexAppGlmProfile(input) {
|
|
|
38
57
|
const validation = validateGlmCodexAppModelProfile(existing);
|
|
39
58
|
const key = await resolveOpenRouterApiKey({ env: input.env || process.env });
|
|
40
59
|
const profile = validation.profile || buildGlmCodexAppModelProfile();
|
|
41
|
-
const
|
|
60
|
+
const home = input.home || input.env?.HOME;
|
|
61
|
+
const configStatus = await inspectCodexAppGlmConfig({ home, configPath: input.configPath });
|
|
62
|
+
const blockers = [...validation.blockers, ...configStatus.blockers];
|
|
42
63
|
const warnings = [
|
|
43
64
|
...key.warnings,
|
|
44
65
|
...(key.key ? [] : ['openrouter_key_missing_until_sks_--mad_--glm_--repair'])
|
|
@@ -51,6 +72,11 @@ export async function doctorCodexAppGlmProfile(input) {
|
|
|
51
72
|
profile,
|
|
52
73
|
profile_path: '.sneakoscope/codex-app/glm-model-profile.json',
|
|
53
74
|
report_path: '.sneakoscope/reports/codex-app-glm-profile.json',
|
|
75
|
+
config_path: configStatus.config_path,
|
|
76
|
+
codex_config_profile: GLM_CODEX_CONFIG_PROFILE_ID,
|
|
77
|
+
codex_reasoning_profiles: GLM_CODEX_CONFIG_REASONING_PROFILES.map((item) => item.id),
|
|
78
|
+
config_write: null,
|
|
79
|
+
config_status: configStatus,
|
|
54
80
|
openrouter_key_source: key.source,
|
|
55
81
|
blockers,
|
|
56
82
|
warnings
|
|
@@ -58,4 +84,82 @@ export async function doctorCodexAppGlmProfile(input) {
|
|
|
58
84
|
await writeJsonAtomic(reportPath, result).catch(() => undefined);
|
|
59
85
|
return result;
|
|
60
86
|
}
|
|
87
|
+
async function previewCodexAppGlmConfigStatus(input) {
|
|
88
|
+
const configPath = input.configPath || codexLbConfigPath(input.home);
|
|
89
|
+
return {
|
|
90
|
+
schema: 'sks.codex-app-glm-config-status.v1',
|
|
91
|
+
ok: true,
|
|
92
|
+
config_path: configPath,
|
|
93
|
+
provider_present: false,
|
|
94
|
+
profiles_present: [],
|
|
95
|
+
profiles_missing: [],
|
|
96
|
+
blockers: []
|
|
97
|
+
};
|
|
98
|
+
}
|
|
99
|
+
async function inspectCodexAppGlmConfig(input) {
|
|
100
|
+
const configPath = input.configPath || codexLbConfigPath(input.home);
|
|
101
|
+
const text = await readText(configPath, '').catch(() => '');
|
|
102
|
+
const providerBody = tomlTableBody(text, `model_providers.${GLM_CODEX_CONFIG_PROVIDER_ID}`);
|
|
103
|
+
const providerPresent = Boolean(providerBody);
|
|
104
|
+
const blockers = [];
|
|
105
|
+
if (!providerPresent) {
|
|
106
|
+
blockers.push('glm_codex_app_config_missing_openrouter_provider');
|
|
107
|
+
}
|
|
108
|
+
else {
|
|
109
|
+
if (!hasTomlString(providerBody, 'base_url', 'https://openrouter.ai/api/v1'))
|
|
110
|
+
blockers.push('glm_codex_app_config_invalid_openrouter_base_url');
|
|
111
|
+
if (!hasTomlString(providerBody, 'wire_api', 'responses'))
|
|
112
|
+
blockers.push('glm_codex_app_config_invalid_openrouter_wire_api');
|
|
113
|
+
if (!hasTomlString(providerBody, 'env_key', 'OPENROUTER_API_KEY'))
|
|
114
|
+
blockers.push('glm_codex_app_config_invalid_openrouter_env_key');
|
|
115
|
+
}
|
|
116
|
+
const profilesPresent = [];
|
|
117
|
+
const profilesMissing = [];
|
|
118
|
+
for (const profile of GLM_CODEX_CONFIG_REASONING_PROFILES) {
|
|
119
|
+
const body = tomlTableBody(text, `profiles.${profile.id}`);
|
|
120
|
+
if (!body) {
|
|
121
|
+
profilesMissing.push(profile.id);
|
|
122
|
+
blockers.push(`glm_codex_app_config_missing_profile:${profile.id}`);
|
|
123
|
+
continue;
|
|
124
|
+
}
|
|
125
|
+
profilesPresent.push(profile.id);
|
|
126
|
+
if (!hasTomlString(body, 'model_provider', GLM_CODEX_CONFIG_PROVIDER_ID))
|
|
127
|
+
blockers.push(`glm_codex_app_config_invalid_profile_provider:${profile.id}`);
|
|
128
|
+
if (!hasTomlString(body, 'model', GLM_52_OPENROUTER_MODEL))
|
|
129
|
+
blockers.push(`glm_codex_app_config_invalid_profile_model:${profile.id}`);
|
|
130
|
+
if (!hasTomlString(body, 'model_reasoning_effort', profile.reasoning_effort))
|
|
131
|
+
blockers.push(`glm_codex_app_config_invalid_profile_reasoning:${profile.id}`);
|
|
132
|
+
}
|
|
133
|
+
return {
|
|
134
|
+
schema: 'sks.codex-app-glm-config-status.v1',
|
|
135
|
+
ok: blockers.length === 0,
|
|
136
|
+
config_path: configPath,
|
|
137
|
+
provider_present: providerPresent,
|
|
138
|
+
profiles_present: profilesPresent,
|
|
139
|
+
profiles_missing: profilesMissing,
|
|
140
|
+
blockers
|
|
141
|
+
};
|
|
142
|
+
}
|
|
143
|
+
function tomlTableBody(text, table) {
|
|
144
|
+
const header = `[${table}]`;
|
|
145
|
+
const lines = String(text || '').split('\n');
|
|
146
|
+
const start = lines.findIndex((line) => line.trim() === header);
|
|
147
|
+
if (start === -1)
|
|
148
|
+
return '';
|
|
149
|
+
const body = [];
|
|
150
|
+
for (let index = start + 1; index < lines.length; index += 1) {
|
|
151
|
+
const line = lines[index];
|
|
152
|
+
if (/^\s*\[[^\]]+\]\s*$/.test(line || ''))
|
|
153
|
+
break;
|
|
154
|
+
body.push(line || '');
|
|
155
|
+
}
|
|
156
|
+
return body.join('\n');
|
|
157
|
+
}
|
|
158
|
+
function hasTomlString(text, key, value) {
|
|
159
|
+
const pattern = new RegExp(`^\\s*${escapeRegExp(key)}\\s*=\\s*"${escapeRegExp(value)}"\\s*(?:#.*)?$`, 'm');
|
|
160
|
+
return pattern.test(text);
|
|
161
|
+
}
|
|
162
|
+
function escapeRegExp(value) {
|
|
163
|
+
return String(value).replace(/[.*+?^${}()|[\]\\]/g, '\\$&');
|
|
164
|
+
}
|
|
61
165
|
//# sourceMappingURL=glm-profile-installer.js.map
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { GLM_CODEX_APP_PROFILE_ID, buildGlmCodexAppModelProfile } from './glm-model-profile.js';
|
|
1
|
+
import { GLM_CODEX_APP_PROFILE_ID, GLM_CODEX_CONFIG_PROFILE_ID, GLM_CODEX_CONFIG_PROVIDER_ID, GLM_CODEX_CONFIG_REASONING_PROFILES, GLM_CODEX_SELECTABLE_REASONING_EFFORTS, buildGlmCodexAppModelProfile } from './glm-model-profile.js';
|
|
2
2
|
import { GLM_52_OPENROUTER_MODEL, GLM_MAD_MODE } from '../providers/glm/glm-52-settings.js';
|
|
3
3
|
export function validateGlmCodexAppModelProfile(value) {
|
|
4
4
|
if (!value || typeof value !== 'object' || Array.isArray(value)) {
|
|
@@ -11,12 +11,16 @@ export function validateGlmCodexAppModelProfile(value) {
|
|
|
11
11
|
profile.id === GLM_CODEX_APP_PROFILE_ID ? null : 'glm_codex_app_profile_invalid_id',
|
|
12
12
|
profile.provider === 'openrouter' ? null : 'glm_codex_app_profile_invalid_provider',
|
|
13
13
|
profile.model === GLM_52_OPENROUTER_MODEL ? null : 'glm_codex_app_profile_invalid_model',
|
|
14
|
+
profile.codexConfigProvider === GLM_CODEX_CONFIG_PROVIDER_ID ? null : 'glm_codex_app_profile_invalid_codex_config_provider',
|
|
15
|
+
profile.codexConfigProfile === GLM_CODEX_CONFIG_PROFILE_ID ? null : 'glm_codex_app_profile_invalid_codex_config_profile',
|
|
16
|
+
hasExpectedReasoningEfforts(profile.supportedReasoningEfforts) ? null : 'glm_codex_app_profile_invalid_reasoning_efforts',
|
|
17
|
+
hasExpectedReasoningProfiles(profile.reasoningProfiles) ? null : 'glm_codex_app_profile_invalid_reasoning_profiles',
|
|
14
18
|
profile.mode === GLM_MAD_MODE ? null : 'glm_codex_app_profile_invalid_mode',
|
|
15
19
|
profile.strictModelLock === true ? null : 'glm_codex_app_profile_not_strict',
|
|
16
20
|
profile.gptFallbackAllowed === false ? null : 'glm_codex_app_profile_allows_gpt_fallback',
|
|
17
21
|
profile.defaultProfile === 'speed' ? null : 'glm_codex_app_profile_default_not_speed',
|
|
18
22
|
profile.defaultSettings?.tool_choice === 'none' ? null : 'glm_codex_app_profile_default_tools_not_omitted',
|
|
19
|
-
profile.defaultSettings?.provider_require_parameters ===
|
|
23
|
+
profile.defaultSettings?.provider_require_parameters === false ? null : 'glm_codex_app_profile_default_requires_parameters',
|
|
20
24
|
profile.defaultSettings?.provider_allow_fallbacks === false ? null : 'glm_codex_app_profile_allows_provider_fallback'
|
|
21
25
|
].filter((item) => Boolean(item));
|
|
22
26
|
return {
|
|
@@ -25,4 +29,19 @@ export function validateGlmCodexAppModelProfile(value) {
|
|
|
25
29
|
profile: blockers.length === 0 ? profile : null
|
|
26
30
|
};
|
|
27
31
|
}
|
|
32
|
+
function hasExpectedReasoningEfforts(value) {
|
|
33
|
+
if (!Array.isArray(value)) {
|
|
34
|
+
return false;
|
|
35
|
+
}
|
|
36
|
+
return GLM_CODEX_SELECTABLE_REASONING_EFFORTS.every((effort) => value.includes(effort));
|
|
37
|
+
}
|
|
38
|
+
function hasExpectedReasoningProfiles(value) {
|
|
39
|
+
if (!Array.isArray(value)) {
|
|
40
|
+
return false;
|
|
41
|
+
}
|
|
42
|
+
return GLM_CODEX_CONFIG_REASONING_PROFILES.every((expected) => value.some((item) => item &&
|
|
43
|
+
typeof item === 'object' &&
|
|
44
|
+
item.id === expected.id &&
|
|
45
|
+
item.reasoning_effort === expected.reasoning_effort));
|
|
46
|
+
}
|
|
28
47
|
//# sourceMappingURL=glm-profile-schema.js.map
|
package/dist/core/codex-app.js
CHANGED
|
@@ -798,6 +798,8 @@ async function codexFastModeConfigStatus(opts = {}) {
|
|
|
798
798
|
if (!config.text)
|
|
799
799
|
continue;
|
|
800
800
|
const topLevel = topLevelToml(config.text);
|
|
801
|
+
if (/(^|\n)\s*model\s*=/.test(topLevel))
|
|
802
|
+
blockers.push(`${config.scope}:top_level_model`);
|
|
801
803
|
if (/(^|\n)\s*model_reasoning_effort\s*=/.test(topLevel))
|
|
802
804
|
blockers.push(`${config.scope}:top_level_model_reasoning_effort`);
|
|
803
805
|
if (/(^|\n)\s*fast_default_opt_out\s*=\s*true\s*(?:#.*)?(?=\n|$)/.test(tomlTable(config.text, 'notice')))
|
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
export function buildCodexSdkConfig(input) {
|
|
2
|
+
const model = String(input.model || process.env.SKS_CODEX_MODEL || process.env.CODEX_MODEL || 'gpt-5.5');
|
|
2
3
|
const config = {
|
|
3
|
-
model
|
|
4
|
+
model,
|
|
4
5
|
service_tier: 'fast',
|
|
5
6
|
model_reasoning_effort: String(input.modelReasoningEffort || input.reasoningEffort || process.env.SKS_CODEX_REASONING || process.env.CODEX_MODEL_REASONING_EFFORT || 'medium'),
|
|
6
7
|
mcp_servers: {},
|
|
@@ -1,4 +1,6 @@
|
|
|
1
1
|
export const REQUIRED_CODEX_MODEL = 'gpt-5.5';
|
|
2
|
+
export const GPT54_MINI_CODEX_MODEL = 'gpt-5.4-mini';
|
|
3
|
+
export const SUPPORTED_CODEX_MODELS = [REQUIRED_CODEX_MODEL, GPT54_MINI_CODEX_MODEL];
|
|
2
4
|
const MODEL_VALUE_FLAGS = new Set(['--model', '-m']);
|
|
3
5
|
const CONFIG_VALUE_FLAGS = new Set(['-c', '--config']);
|
|
4
6
|
function isModelConfigOverride(value = '') {
|
|
@@ -43,6 +45,6 @@ export function forceGpt55CodexConfigArgs(args = []) {
|
|
|
43
45
|
}
|
|
44
46
|
export function isForbiddenCodexModel(value = '') {
|
|
45
47
|
const model = String(value || '').trim().toLowerCase();
|
|
46
|
-
return /^gpt-5\./.test(model) && model
|
|
48
|
+
return /^gpt-5\./.test(model) && !SUPPORTED_CODEX_MODELS.includes(model);
|
|
47
49
|
}
|
|
48
50
|
//# sourceMappingURL=codex-model-guard.js.map
|
|
@@ -21,8 +21,11 @@ export async function fastModeCommand(args = []) {
|
|
|
21
21
|
preference = null;
|
|
22
22
|
}
|
|
23
23
|
const policy = resolveFastModePolicy({ root });
|
|
24
|
-
const codexFastModeRepair = action === 'on'
|
|
25
|
-
? await ensureGlobalCodexFastModeDuringInstall({
|
|
24
|
+
const codexFastModeRepair = action === 'on' || action === 'off'
|
|
25
|
+
? await ensureGlobalCodexFastModeDuringInstall({
|
|
26
|
+
forceFastMode: action === 'on',
|
|
27
|
+
forceFastModeOff: action === 'off'
|
|
28
|
+
})
|
|
26
29
|
: null;
|
|
27
30
|
const result = {
|
|
28
31
|
schema: FAST_MODE_COMMAND_SCHEMA,
|
|
@@ -5,6 +5,7 @@ import { runGlmDirectSpeedRun } from '../providers/glm/glm-direct-run.js';
|
|
|
5
5
|
import { runGlmReadinessAndExit } from '../providers/glm/glm-readiness.js';
|
|
6
6
|
import { runGlmInteractiveLaunch } from '../providers/glm/glm-interactive-launch.js';
|
|
7
7
|
import { glmNarutoCommand } from '../providers/glm/naruto/glm-naruto-command.js';
|
|
8
|
+
import { stripGlmSlashModelArgs } from '../providers/glm/glm-profile-resolver.js';
|
|
8
9
|
export async function glmCommand(args = []) {
|
|
9
10
|
if (flag(args, '--naruto') || positionalArgs(args)[0] === 'naruto') {
|
|
10
11
|
const narutoArgs = args.filter((a) => a !== '--naruto' && a !== 'naruto');
|
|
@@ -56,7 +57,7 @@ export async function glmCommand(args = []) {
|
|
|
56
57
|
return result;
|
|
57
58
|
}
|
|
58
59
|
function extractGlmTask(args) {
|
|
59
|
-
const positional = positionalArgs(args).map(String);
|
|
60
|
+
const positional = positionalArgs(stripGlmSlashModelArgs(args)).map(String);
|
|
60
61
|
if (positional[0] === 'run')
|
|
61
62
|
return positional.slice(1).join(' ').trim() || null;
|
|
62
63
|
if (positional[0] === 'session')
|
|
@@ -332,6 +332,30 @@ function isMadGlmLaunch(args = [], deps = {}) {
|
|
|
332
332
|
async function prepareMadGlmLaunchRuntime(madLaunch, deps = {}) {
|
|
333
333
|
const keyResolution = await resolveMadGlmLaunchKey(process.env);
|
|
334
334
|
const profile = buildMadGlmLaunchProfileNoWrite(deps?.glmArgs || []);
|
|
335
|
+
if (profile.blockers.length) {
|
|
336
|
+
const blocked = {
|
|
337
|
+
schema: 'sks.glm-mad-launch.v1',
|
|
338
|
+
ok: false,
|
|
339
|
+
status: 'blocked',
|
|
340
|
+
mission_id: madLaunch.mission_id,
|
|
341
|
+
provider: profile.provider,
|
|
342
|
+
model: profile.model,
|
|
343
|
+
glm_profile: profile.glm_profile,
|
|
344
|
+
glm_mode: profile.glm_mode,
|
|
345
|
+
model_reasoning_effort: profile.model_reasoning_effort,
|
|
346
|
+
gpt_fallback_allowed: false,
|
|
347
|
+
blockers: profile.blockers,
|
|
348
|
+
warnings: []
|
|
349
|
+
};
|
|
350
|
+
await writeJsonAtomic(path.join(madLaunch.dir, 'mad-glm-launch.json'), blocked);
|
|
351
|
+
await appendJsonlBounded(path.join(madLaunch.dir, 'events.jsonl'), {
|
|
352
|
+
ts: nowIso(),
|
|
353
|
+
type: 'mad_sks.glm_launch_blocked',
|
|
354
|
+
blockers: profile.blockers
|
|
355
|
+
});
|
|
356
|
+
console.error(`SKS GLM MAD launch blocked: ${profile.blockers.join(', ')}`);
|
|
357
|
+
return blocked;
|
|
358
|
+
}
|
|
335
359
|
if (!keyResolution.key) {
|
|
336
360
|
const blocked = {
|
|
337
361
|
schema: 'sks.glm-mad-launch.v1',
|
|
@@ -5,12 +5,12 @@ import { REQUIRED_CODEX_MODEL } from '../codex-model-guard.js';
|
|
|
5
5
|
import { ensureDir, exists, nowIso, readText, writeJsonAtomic, writeTextAtomic } from '../fsx.js';
|
|
6
6
|
export const DOCTOR_CODEX_STARTUP_REPAIR_SCHEMA = 'sks.doctor-codex-startup-repair.v1';
|
|
7
7
|
const AGENT_ROLE_FILES = new Map([
|
|
8
|
-
['analysis_scout', { file: 'analysis-scout.toml', description: '
|
|
9
|
-
['native_agent', { file: 'native-agent-intake.toml', description: '
|
|
10
|
-
['team_consensus', { file: 'team-consensus.toml', description: 'SKS planning/debate agent.', sandbox: '
|
|
8
|
+
['analysis_scout', { file: 'analysis-scout.toml', description: 'SKS scout with bounded write capability.', sandbox: 'workspace-write', nicknames: ['Scout', 'Mapper'] }],
|
|
9
|
+
['native_agent', { file: 'native-agent-intake.toml', description: 'SKS native agent with bounded write capability.', sandbox: 'workspace-write', nicknames: ['Analysis', 'Mapper'] }],
|
|
10
|
+
['team_consensus', { file: 'team-consensus.toml', description: 'SKS planning/debate agent with bounded write capability.', sandbox: 'workspace-write', nicknames: ['Consensus', 'Atlas'] }],
|
|
11
11
|
['implementation_worker', { file: 'implementation-worker.toml', description: 'SKS bounded implementation worker.', sandbox: 'workspace-write', nicknames: ['Builder', 'Mason'] }],
|
|
12
|
-
['db_safety_reviewer', { file: 'db-safety-reviewer.toml', description: '
|
|
13
|
-
['qa_reviewer', { file: 'qa-reviewer.toml', description: '
|
|
12
|
+
['db_safety_reviewer', { file: 'db-safety-reviewer.toml', description: 'DB safety reviewer with bounded write capability.', sandbox: 'workspace-write', nicknames: ['Sentinel', 'Ledger'] }],
|
|
13
|
+
['qa_reviewer', { file: 'qa-reviewer.toml', description: 'QA reviewer with bounded write capability.', sandbox: 'workspace-write', nicknames: ['Verifier', 'Reviewer'] }]
|
|
14
14
|
]);
|
|
15
15
|
const DIRECTIVE_ROLE_FILES = [
|
|
16
16
|
'sks-explorer.toml',
|
|
@@ -42,7 +42,8 @@ export async function runDoctorCodexStartupRepair(input) {
|
|
|
42
42
|
...configs.flatMap((entry) => [
|
|
43
43
|
...entry.agent_config_files_repaired.map((file) => `${entry.scope} agent config_file now points at ${file}`),
|
|
44
44
|
...(entry.mcp_blocks_repaired || []).map((server) => `${entry.scope} MCP block repaired: ${server}`),
|
|
45
|
-
...entry.stale_mcp_blocks_removed.map((server) => `${entry.scope} stale MCP block removed: ${server}`)
|
|
45
|
+
...entry.stale_mcp_blocks_removed.map((server) => `${entry.scope} stale MCP block removed: ${server}`),
|
|
46
|
+
...entry.duplicate_toml_blocks_removed.map((header) => `${entry.scope} duplicate TOML table removed: ${header}`)
|
|
46
47
|
])
|
|
47
48
|
];
|
|
48
49
|
const manualActions = [
|
|
@@ -84,7 +85,8 @@ async function inspectOrRepairConfig(candidate, fix, nodeReplCommandCandidates,
|
|
|
84
85
|
mcp_blocks_repaired: [],
|
|
85
86
|
optional_mcp_blocks_ignored: [],
|
|
86
87
|
blockers: [],
|
|
87
|
-
warnings: candidate.scope === 'global' ? ['codex_home_config_missing_optional'] : []
|
|
88
|
+
warnings: candidate.scope === 'global' ? ['codex_home_config_missing_optional'] : [],
|
|
89
|
+
duplicate_toml_blocks_removed: []
|
|
88
90
|
};
|
|
89
91
|
}
|
|
90
92
|
let next = text;
|
|
@@ -92,13 +94,29 @@ async function inspectOrRepairConfig(candidate, fix, nodeReplCommandCandidates,
|
|
|
92
94
|
const staleMcpBlocksRemoved = [];
|
|
93
95
|
const mcpBlocksRepaired = [];
|
|
94
96
|
const optionalMcpBlocksIgnored = [];
|
|
97
|
+
const duplicateTomlBlocksRemoved = [];
|
|
95
98
|
const blockers = [];
|
|
96
99
|
const warnings = [];
|
|
100
|
+
const duplicateRepair = inspectOrRepairDuplicateTomlBlocks(next, candidate, fix);
|
|
101
|
+
next = duplicateRepair.text;
|
|
102
|
+
duplicateTomlBlocksRemoved.push(...duplicateRepair.removed);
|
|
103
|
+
warnings.push(...duplicateRepair.warnings);
|
|
97
104
|
for (const [tableName, role] of AGENT_ROLE_FILES) {
|
|
98
105
|
const target = path.join(candidate.agentDir, role.file);
|
|
99
|
-
|
|
106
|
+
let table = tomlBlock(next, `agents.${tableName}`);
|
|
100
107
|
if (!table)
|
|
101
108
|
continue;
|
|
109
|
+
const currentDescription = stringValue(table.text, 'description');
|
|
110
|
+
if (currentDescription !== role.description) {
|
|
111
|
+
if (!fix)
|
|
112
|
+
warnings.push(`agent_description_stale:${tableName}`);
|
|
113
|
+
else {
|
|
114
|
+
next = replaceOrInsertKey(next, table, 'description', `"${escapeToml(role.description)}"`);
|
|
115
|
+
table = tomlBlock(next, `agents.${tableName}`);
|
|
116
|
+
if (!table)
|
|
117
|
+
continue;
|
|
118
|
+
}
|
|
119
|
+
}
|
|
102
120
|
const current = stringValue(table.text, 'config_file');
|
|
103
121
|
const targetExists = await exists(target);
|
|
104
122
|
const currentValid = Boolean(current && path.isAbsolute(current) && await exists(current));
|
|
@@ -146,9 +164,87 @@ async function inspectOrRepairConfig(candidate, fix, nodeReplCommandCandidates,
|
|
|
146
164
|
mcp_blocks_repaired: mcpBlocksRepaired,
|
|
147
165
|
optional_mcp_blocks_ignored: optionalMcpBlocksIgnored,
|
|
148
166
|
blockers,
|
|
149
|
-
warnings
|
|
167
|
+
warnings,
|
|
168
|
+
duplicate_toml_blocks_removed: duplicateTomlBlocksRemoved
|
|
169
|
+
};
|
|
170
|
+
}
|
|
171
|
+
function inspectOrRepairDuplicateTomlBlocks(text, candidate, fix) {
|
|
172
|
+
const blocks = tomlBlocks(text);
|
|
173
|
+
const groups = new Map();
|
|
174
|
+
for (const block of blocks) {
|
|
175
|
+
const list = groups.get(block.header) || [];
|
|
176
|
+
list.push(block);
|
|
177
|
+
groups.set(block.header, list);
|
|
178
|
+
}
|
|
179
|
+
const warnings = [];
|
|
180
|
+
const removals = [];
|
|
181
|
+
for (const [header, rows] of groups) {
|
|
182
|
+
if (rows.length < 2)
|
|
183
|
+
continue;
|
|
184
|
+
warnings.push(`duplicate_toml_table:${header}`);
|
|
185
|
+
const keep = selectDuplicateTomlBlockToKeep(header, rows, candidate);
|
|
186
|
+
for (let index = 0; index < rows.length; index += 1) {
|
|
187
|
+
if (index !== keep)
|
|
188
|
+
removals.push(rows[index]);
|
|
189
|
+
}
|
|
190
|
+
}
|
|
191
|
+
if (!removals.length || !fix)
|
|
192
|
+
return { text, warnings, removed: [] };
|
|
193
|
+
return {
|
|
194
|
+
text: removeBlocks(text, removals),
|
|
195
|
+
warnings,
|
|
196
|
+
removed: removals.map((block) => block.header)
|
|
150
197
|
};
|
|
151
198
|
}
|
|
199
|
+
function tomlBlocks(text) {
|
|
200
|
+
const source = String(text || '');
|
|
201
|
+
const matches = [...source.matchAll(/(^|\n)\s*\[([^\]]+)\]\s*(?:#.*)?(?:\n|$)/g)];
|
|
202
|
+
return matches.map((match, index) => {
|
|
203
|
+
const start = Number(match.index || 0) + (match[1] ? 1 : 0);
|
|
204
|
+
const next = matches[index + 1];
|
|
205
|
+
const end = next ? Number(next.index || 0) + (next[1] ? 1 : 0) : source.length;
|
|
206
|
+
return {
|
|
207
|
+
header: String(match[2] || '').trim(),
|
|
208
|
+
start,
|
|
209
|
+
end,
|
|
210
|
+
text: source.slice(start, end)
|
|
211
|
+
};
|
|
212
|
+
});
|
|
213
|
+
}
|
|
214
|
+
function selectDuplicateTomlBlockToKeep(header, rows, candidate) {
|
|
215
|
+
const agentName = header.startsWith('agents.') ? header.slice('agents.'.length) : '';
|
|
216
|
+
const role = agentName ? AGENT_ROLE_FILES.get(agentName) : undefined;
|
|
217
|
+
if (role) {
|
|
218
|
+
const target = path.join(candidate.agentDir, role.file);
|
|
219
|
+
return maxIndexBy(rows, (block, index) => {
|
|
220
|
+
const configFile = stringValue(block.text, 'config_file');
|
|
221
|
+
const description = stringValue(block.text, 'description');
|
|
222
|
+
return ((configFile === target ? 100 : 0) +
|
|
223
|
+
(configFile && path.isAbsolute(configFile) ? 20 : 0) +
|
|
224
|
+
(description === role.description ? 30 : 0) +
|
|
225
|
+
assignmentCount(block.text) -
|
|
226
|
+
index / 1000);
|
|
227
|
+
});
|
|
228
|
+
}
|
|
229
|
+
if (header.startsWith('mcp_servers.'))
|
|
230
|
+
return 0;
|
|
231
|
+
return maxIndexBy(rows, (block, index) => assignmentCount(block.text) - index / 1000);
|
|
232
|
+
}
|
|
233
|
+
function maxIndexBy(rows, score) {
|
|
234
|
+
let best = 0;
|
|
235
|
+
let bestScore = Number.NEGATIVE_INFINITY;
|
|
236
|
+
rows.forEach((row, index) => {
|
|
237
|
+
const value = score(row, index);
|
|
238
|
+
if (value > bestScore) {
|
|
239
|
+
best = index;
|
|
240
|
+
bestScore = value;
|
|
241
|
+
}
|
|
242
|
+
});
|
|
243
|
+
return best;
|
|
244
|
+
}
|
|
245
|
+
function assignmentCount(text) {
|
|
246
|
+
return String(text || '').split(/\r?\n/).filter((line) => /^\s*[A-Za-z0-9_.-]+\s*=/.test(line)).length;
|
|
247
|
+
}
|
|
152
248
|
async function inspectOrRepairNodeRepl(text, fix, extraCandidates, includeDefaultCandidates) {
|
|
153
249
|
const server = 'node_repl';
|
|
154
250
|
const table = tomlBlock(text, `mcp_servers.${server}`);
|
|
@@ -223,6 +319,12 @@ async function repairAgentRoleFiles(root, codexHome) {
|
|
|
223
319
|
await ensureDir(dir);
|
|
224
320
|
await writeTextAtomic(file, roleConfigToml(name, role.description, role.sandbox));
|
|
225
321
|
created.push(file);
|
|
322
|
+
continue;
|
|
323
|
+
}
|
|
324
|
+
if (!text.includes(`sandbox_mode = "${role.sandbox}"`) || text.includes('Do not edit files.')) {
|
|
325
|
+
await backupConfig(file, text, 'role-write-capable');
|
|
326
|
+
await writeTextAtomic(file, roleConfigToml(name, role.description, role.sandbox));
|
|
327
|
+
sanitized.push(file);
|
|
226
328
|
}
|
|
227
329
|
}
|
|
228
330
|
for (const file of DIRECTIVE_ROLE_FILES) {
|