sneakoscope 4.6.4 → 4.7.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/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 +73 -6
- package/dist/commands/codex-app.js +34 -3
- package/dist/commands/doctor.js +6 -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/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-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 +9 -9
- 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/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
|
@@ -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
|
}
|
|
@@ -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
|
|
@@ -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) {
|
|
@@ -154,7 +154,7 @@ export function buildAllFeaturesSelftest(registry, opts = {}) {
|
|
|
154
154
|
checkRow('native_agent_intake_contract_present', registry.features.some((feature) => feature.id === 'route-native-agent-intake'), ['route-native-agent-intake']),
|
|
155
155
|
checkRow('cli_agent_fixture_pass', registry.features.some((feature) => feature.id === 'cli-agent' && feature.fixture?.status === 'pass' && feature.fixture.expected_artifacts?.some((artifact) => expectedArtifactPath(artifact).includes('agent-proof-evidence'))), ['cli-agent']),
|
|
156
156
|
checkRow('agent_proof_evidence_contract_present', registry.features.some((feature) => feature.id === 'proof-agent-evidence'), ['proof-agent-evidence']),
|
|
157
|
-
checkRow('agent_lease_policy_present', registry.features.some((feature) => feature.id === 'route-native-agent-intake' && /
|
|
157
|
+
checkRow('agent_lease_policy_present', registry.features.some((feature) => feature.id === 'route-native-agent-intake' && /bounded workspace-write/i.test(JSON.stringify(feature.contract || {})) && /lease/i.test(JSON.stringify(feature.contract || {}))), ['route-native-agent-intake']),
|
|
158
158
|
checkRow('fixture_pass_threshold', (fixturesSummary.counts.pass || 0) >= 90, [`pass=${fixturesSummary.counts.pass || 0}`]),
|
|
159
159
|
checkRow('fixture_not_required_ceiling', (fixturesSummary.counts.not_required || 0) <= 16, [`not_required=${fixturesSummary.counts.not_required || 0}`]),
|
|
160
160
|
checkRow('fixture_mock_blocked_zero', (fixturesSummary.counts.blocked || 0) === 0, [`blocked=${fixturesSummary.counts.blocked || 0}`]),
|
|
@@ -628,7 +628,7 @@ function nativeAgentIntakeFeature() {
|
|
|
628
628
|
aliases: ['sks team "task" [executor:5 reviewer:6 user:1]'],
|
|
629
629
|
category: 'proof-route',
|
|
630
630
|
maturity: 'stable',
|
|
631
|
-
intent: 'Default
|
|
631
|
+
intent: 'Default bounded workspace-write native multi-session agent intake before serious route implementation.',
|
|
632
632
|
voxel_triwiki_integration: 'native agent findings are TriWiki-ready and can require image voxel evidence for visual routes',
|
|
633
633
|
completion_proof_integration: 'Completion Proof evidence.agents records agent_count, route, leases, no-overlap proof, cleanup, proof graph, and dynamic effort policy',
|
|
634
634
|
known_gaps: ['real speedup claims require runtime timing/eval evidence; mock/static timing is not enough'],
|
|
@@ -636,7 +636,7 @@ function nativeAgentIntakeFeature() {
|
|
|
636
636
|
input: 'serious route mission, route collaboration fixture, or explicit sks agent run',
|
|
637
637
|
output: 'agents/agent-central-ledger.json, agents/agent-task-board.json, agents/agent-leases.json, agents/agent-no-overlap-proof.json, agents/agent-session-cleanup.json, agents/agent-proof-evidence.json, agents/agent-effort-policy.json',
|
|
638
638
|
state: 'mission-local native agent artifacts',
|
|
639
|
-
safety: '
|
|
639
|
+
safety: 'bounded workspace-write analysis agents; central leases prevent overlapping write scopes; parent owns integration',
|
|
640
640
|
proof: 'evidence.agents required for serious native route proof',
|
|
641
641
|
voxel: 'visual agent records image voxel requirements without satisfying visual evidence by itself',
|
|
642
642
|
tests: 'unit, integration, e2e route fixtures, native release gate scripts',
|
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 = '4.
|
|
8
|
+
export const PACKAGE_VERSION = '4.7.0';
|
|
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() {
|
|
@@ -125,8 +125,9 @@ export function classifyHarnessPayload(root, payload = {}, policy = {}) {
|
|
|
125
125
|
const command = extractCommand(payload);
|
|
126
126
|
const writeIntent = hasWriteIntent(toolName, command, hay);
|
|
127
127
|
const maintenance = classifyMaintenanceCommand(command || hay);
|
|
128
|
-
const
|
|
129
|
-
const
|
|
128
|
+
const writeTargets = extractWriteTargets(root, payload, strings, command);
|
|
129
|
+
const protectedMatches = findProtectedMatches(root, writeTargets.length ? writeTargets : strings, policy);
|
|
130
|
+
const packageEdit = writeIntent && packageManifestEditDetected(writeTargets, hay);
|
|
130
131
|
const block = maintenance.block || packageEdit || (writeIntent && protectedMatches.length > 0);
|
|
131
132
|
const reasons = [];
|
|
132
133
|
if (maintenance.block)
|
|
@@ -135,7 +136,7 @@ export function classifyHarnessPayload(root, payload = {}, policy = {}) {
|
|
|
135
136
|
reasons.push('package_manifest_sneakoscope_edit_blocked');
|
|
136
137
|
if (writeIntent && protectedMatches.length)
|
|
137
138
|
reasons.push('protected_harness_path_write_blocked');
|
|
138
|
-
return { block, reasons: [...new Set(reasons)], matches: protectedMatches, writeIntent, toolName, command };
|
|
139
|
+
return { block, reasons: [...new Set(reasons)], matches: protectedMatches, writeIntent, toolName, command, writeTargets };
|
|
139
140
|
}
|
|
140
141
|
export async function collectHarnessFingerprints(root) {
|
|
141
142
|
const out = {};
|
|
@@ -196,6 +197,91 @@ function collectPayloadStrings(obj, out = [], depth = 0) {
|
|
|
196
197
|
}
|
|
197
198
|
return out;
|
|
198
199
|
}
|
|
200
|
+
function extractWriteTargets(root, payload = {}, strings = [], command = '') {
|
|
201
|
+
const targets = new Set();
|
|
202
|
+
for (const value of collectPathFieldStrings(payload))
|
|
203
|
+
addWriteTarget(root, targets, value);
|
|
204
|
+
for (const text of [command, ...strings]) {
|
|
205
|
+
const s = String(text || '');
|
|
206
|
+
for (const match of s.matchAll(/^\*\*\*\s+(?:Update|Add|Delete)\s+File:\s+(.+)$/gmi))
|
|
207
|
+
addWriteTarget(root, targets, match[1]);
|
|
208
|
+
for (const match of s.matchAll(/^\*\*\*\s+Move to:\s+(.+)$/gmi))
|
|
209
|
+
addWriteTarget(root, targets, match[1]);
|
|
210
|
+
for (const match of s.matchAll(/>{1,2}\s*(['"]?)([^\s;&|'"`]+)\1/g))
|
|
211
|
+
addWriteTarget(root, targets, match[2]);
|
|
212
|
+
collectShellCommandTargets(root, targets, s);
|
|
213
|
+
}
|
|
214
|
+
return [...targets].sort();
|
|
215
|
+
}
|
|
216
|
+
function collectPathFieldStrings(obj, out = [], depth = 0) {
|
|
217
|
+
if (depth > 8 || obj == null)
|
|
218
|
+
return out;
|
|
219
|
+
if (Array.isArray(obj)) {
|
|
220
|
+
for (const value of obj)
|
|
221
|
+
collectPathFieldStrings(value, out, depth + 1);
|
|
222
|
+
return out;
|
|
223
|
+
}
|
|
224
|
+
if (typeof obj !== 'object')
|
|
225
|
+
return out;
|
|
226
|
+
for (const [key, value] of Object.entries(obj)) {
|
|
227
|
+
const normalized = String(key || '').toLowerCase();
|
|
228
|
+
if (typeof value === 'string' && /^(?:path|file|filename|target|destination|dest|to|from|cwd|workdir|file_path|target_path|output_path|artifact_path)$/.test(normalized))
|
|
229
|
+
out.push(value);
|
|
230
|
+
collectPathFieldStrings(value, out, depth + 1);
|
|
231
|
+
}
|
|
232
|
+
return out;
|
|
233
|
+
}
|
|
234
|
+
function collectShellCommandTargets(root, targets, text) {
|
|
235
|
+
const words = shellWords(text);
|
|
236
|
+
for (let i = 0; i < words.length; i += 1) {
|
|
237
|
+
const word = words[i] || '';
|
|
238
|
+
if (!/^(?:rm|rmdir|mv|cp|touch|mkdir|chmod|chown)$/.test(word))
|
|
239
|
+
continue;
|
|
240
|
+
for (let j = i + 1; j < words.length; j += 1) {
|
|
241
|
+
const candidate = words[j] || '';
|
|
242
|
+
if (!candidate || candidate.startsWith('-'))
|
|
243
|
+
continue;
|
|
244
|
+
if (/^(?:&&|\|\||;|\|)$/.test(candidate))
|
|
245
|
+
break;
|
|
246
|
+
if (/^(?:rm|rmdir|mv|cp|touch|mkdir|chmod|chown|git|npm|node|python\d?)$/.test(candidate) && j > i + 1)
|
|
247
|
+
break;
|
|
248
|
+
addWriteTarget(root, targets, candidate);
|
|
249
|
+
}
|
|
250
|
+
}
|
|
251
|
+
}
|
|
252
|
+
function shellWords(text) {
|
|
253
|
+
return [...String(text || '').matchAll(/"([^"]*)"|'([^']*)'|([^\s]+)/g)].map((match) => match[1] || match[2] || match[3]).filter(Boolean);
|
|
254
|
+
}
|
|
255
|
+
function addWriteTarget(root, targets, value) {
|
|
256
|
+
const normalized = normalizeTargetPath(root, value);
|
|
257
|
+
if (normalized)
|
|
258
|
+
targets.add(normalized);
|
|
259
|
+
}
|
|
260
|
+
function normalizeTargetPath(root, value) {
|
|
261
|
+
let s = String(value || '').trim();
|
|
262
|
+
if (!s)
|
|
263
|
+
return '';
|
|
264
|
+
s = s.replace(/^['"`<]+|['"`>,]+$/g, '');
|
|
265
|
+
if (!s || /^\$[\w{]/.test(s) || /^[A-Z_][A-Z0-9_]*=/.test(s))
|
|
266
|
+
return '';
|
|
267
|
+
if (/^(?:--?|&&|\|\||;|\|)$/.test(s))
|
|
268
|
+
return '';
|
|
269
|
+
s = s.replace(/\\/g, '/');
|
|
270
|
+
if (s.startsWith('a/') || s.startsWith('b/'))
|
|
271
|
+
s = s.slice(2);
|
|
272
|
+
if (s.startsWith('./'))
|
|
273
|
+
s = s.slice(2);
|
|
274
|
+
const rootNorm = String(root || '').replace(/\\/g, '/').replace(/\/$/, '');
|
|
275
|
+
if (rootNorm && s.startsWith(`${rootNorm}/`))
|
|
276
|
+
s = s.slice(rootNorm.length + 1);
|
|
277
|
+
return s.replace(/\/$/, '');
|
|
278
|
+
}
|
|
279
|
+
function packageManifestEditDetected(writeTargets, hay) {
|
|
280
|
+
const hasPackageTarget = writeTargets.length > 0
|
|
281
|
+
? writeTargets.some((target) => /(^|\/)(?:package\.json|package-lock\.json|pnpm-lock\.yaml|yarn\.lock)$/.test(target))
|
|
282
|
+
: /\b(package\.json|package-lock\.json|pnpm-lock\.yaml|yarn\.lock)\b/i.test(hay);
|
|
283
|
+
return hasPackageTarget && /\bsneakoscope\b/i.test(hay);
|
|
284
|
+
}
|
|
199
285
|
function hasWriteIntent(toolName, command, hay) {
|
|
200
286
|
if (/\b(apply_patch|edit|write|create|delete|remove|rename|str_replace|file_write|fs_write)\b/i.test(toolName))
|
|
201
287
|
return true;
|