sneakoscope 0.7.69 → 0.7.74
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 +2 -2
- package/package.json +1 -1
- package/src/cli/install-helpers.mjs +25 -1
- package/src/cli/main.mjs +34 -47
- package/src/cli/maintenance-commands.mjs +61 -23
- package/src/core/fsx.mjs +1 -1
- package/src/core/hooks-runtime.mjs +1 -8
- package/src/core/init.mjs +65 -1
- package/src/core/pipeline.mjs +1 -1
- package/src/core/research.mjs +327 -37
- package/src/core/routes.mjs +31 -10
- package/src/core/team-live.mjs +13 -3
- package/src/core/tmux-ui.mjs +25 -5
package/README.md
CHANGED
|
@@ -192,7 +192,7 @@ sks qa-loop prepare "http://localhost:3000"
|
|
|
192
192
|
sks qa-loop run latest --max-cycles 2
|
|
193
193
|
sks goal create "persist this migration workflow"
|
|
194
194
|
sks research prepare "evaluate this approach"
|
|
195
|
-
sks research run latest --max-cycles 3
|
|
195
|
+
sks research run latest --max-cycles 3 --cycle-timeout-minutes 120
|
|
196
196
|
sks research status latest
|
|
197
197
|
sks db scan --json
|
|
198
198
|
sks wiki refresh
|
|
@@ -211,7 +211,7 @@ sks skill-dream run --json
|
|
|
211
211
|
sks code-structure scan --json
|
|
212
212
|
```
|
|
213
213
|
|
|
214
|
-
`sks research` prepares a genius-lens scout council, requires every scout to run at `xhigh`, records one literal `Eureka!` idea per scout, runs an evidence-bound debate,
|
|
214
|
+
`sks research` prepares a genius-lens scout council, requires every scout to run at `xhigh`, records one literal `Eureka!` idea per scout, runs an evidence-bound debate, and now creates `research-source-skill.md` as a route-local source collection skill before synthesis. Normal Research is intentionally allowed to take one or two hours when the problem needs it; `--mock` is only for selftests or dry harness checks, and a real run blocks with `research-blocker.json` instead of silently substituting mock output when the Codex execution path is unavailable. The source layer contract separates latest papers, official/government or leading-institution sources, standards/primary docs, current news such as BBC/CNN/GDELT-style sources, public discourse such as X/Reddit, developer/practitioner knowledge such as Stack Overflow/GitHub, and counterevidence/fact-checking; `source-ledger.json` must record layer coverage, source quality, blockers, citations, and cross-layer triangulation. Context7 is optional for `$Research` and only becomes relevant when the research topic specifically depends on package, API, framework, or SDK documentation. Research runs require `research-report.md`, `research-paper.md`, `genius-opinion-summary.md`, `research-source-skill.md`, `source-ledger.json`, `scout-ledger.json`, `debate-ledger.json`, `novelty-ledger.json`, `falsification-ledger.json`, and `research-gate.json` so they stay source-backed, adversarially checked, falsifiable, paper-ready, and clear about every scout lens opinion. `research status` reports source entries, source-layer coverage, triangulation checks, counterevidence, xhigh scout count, Eureka moments, debate exchanges, paper presence/sections, genius-opinion summary coverage, scout findings, and falsification cases alongside the gate.
|
|
215
215
|
|
|
216
216
|
`sks pipeline plan` shows the active route lane, kept/skipped stages, verification commands, and no-unrequested-fallback invariant. `sks proof-field scan` is the lightweight rubric for small changes; risky or broad signals return to the full Team/Honest path.
|
|
217
217
|
|
package/package.json
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "sneakoscope",
|
|
3
3
|
"displayName": "ㅅㅋㅅ",
|
|
4
|
-
"version": "0.7.
|
|
4
|
+
"version": "0.7.74",
|
|
5
5
|
"description": "Sneakoscope Codex: database-safe Codex CLI/App harness with Team, Goal, AutoResearch, TriWiki, and Honest Mode.",
|
|
6
6
|
"type": "module",
|
|
7
7
|
"homepage": "https://github.com/mandarange/Sneakoscope-Codex#readme",
|
|
@@ -361,6 +361,7 @@ export function normalizeCodexFastModeUiConfig(text = '') {
|
|
|
361
361
|
next = removeTomlTableKey(next, 'features', 'codex_hooks');
|
|
362
362
|
next = upsertTopLevelTomlString(next, 'model', 'gpt-5.5');
|
|
363
363
|
next = upsertTopLevelTomlString(next, 'service_tier', 'fast');
|
|
364
|
+
next = upsertTopLevelTomlBoolean(next, 'suppress_unstable_features_warning', true);
|
|
364
365
|
next = upsertTomlTableKey(next, 'features', 'hooks = true');
|
|
365
366
|
next = upsertTomlTableKey(next, 'features', 'multi_agent = true');
|
|
366
367
|
next = upsertTomlTableKey(next, 'features', 'fast_mode = true');
|
|
@@ -457,6 +458,21 @@ function upsertTopLevelTomlString(text, key, value) {
|
|
|
457
458
|
return lines.join('\n').replace(/^\n+/, '').replace(/\n{3,}/g, '\n\n');
|
|
458
459
|
}
|
|
459
460
|
|
|
461
|
+
function upsertTopLevelTomlBoolean(text, key, value) {
|
|
462
|
+
const line = `${key} = ${value ? 'true' : 'false'}`;
|
|
463
|
+
const lines = String(text || '').split('\n');
|
|
464
|
+
const firstTable = lines.findIndex((x) => /^\s*\[.+\]\s*$/.test(x));
|
|
465
|
+
const end = firstTable === -1 ? lines.length : firstTable;
|
|
466
|
+
for (let i = 0; i < end; i += 1) {
|
|
467
|
+
if (new RegExp(`^\\s*${escapeRegExp(key)}\\s*=`).test(lines[i])) {
|
|
468
|
+
lines[i] = line;
|
|
469
|
+
return lines.join('\n').replace(/\n{3,}/g, '\n\n');
|
|
470
|
+
}
|
|
471
|
+
}
|
|
472
|
+
lines.splice(end, 0, line);
|
|
473
|
+
return lines.join('\n').replace(/^\n+/, '').replace(/\n{3,}/g, '\n\n');
|
|
474
|
+
}
|
|
475
|
+
|
|
460
476
|
function upsertTomlTable(text, table, block) {
|
|
461
477
|
let lines = String(text || '').trimEnd().split('\n');
|
|
462
478
|
if (lines.length === 1 && lines[0] === '') lines = [];
|
|
@@ -929,15 +945,18 @@ export async function selftestCodexLb(tmp) {
|
|
|
929
945
|
const codexLbEnv = await safeReadText(path.join(codexLbHome, '.codex', 'sks-codex-lb.env'));
|
|
930
946
|
const codexLbAuth = await safeReadText(path.join(codexLbHome, '.codex', 'auth.json'));
|
|
931
947
|
if (!codexLbSetupJson.ok || codexLbSetupJson.base_url !== 'https://lb.example.test/backend-api/codex' || !codexLbConfig.includes('model_provider = "codex-lb"') || !codexLbConfig.includes('[model_providers.codex-lb]') || !codexLbEnv.includes("CODEX_LB_BASE_URL='https://lb.example.test/backend-api/codex'") || !codexLbEnv.includes("CODEX_LB_API_KEY='sk-test'") || !/(\"auth_mode\"\s*:\s*\"apikey\")/.test(codexLbAuth)) throw new Error('selftest: codex-lb setup');
|
|
948
|
+
if (!hasCodexUnstableFeatureWarningSuppression(codexLbConfig)) throw new Error('selftest: codex-lb setup did not suppress Codex unstable feature warning');
|
|
932
949
|
await initProject(codexLbHome, { installScope: 'global', force: true, repair: true });
|
|
933
950
|
const codexLbRepairSetupConfig = await safeReadText(path.join(codexLbHome, '.codex', 'config.toml'));
|
|
934
951
|
if (!codexLbRepairSetupConfig.includes('model_provider = "codex-lb"') || !codexLbRepairSetupConfig.includes('[model_providers.codex-lb]') || !codexLbRepairSetupConfig.includes('https://lb.example.test/backend-api/codex') || codexLbRepairSetupConfig.includes('sk-test')) throw new Error('selftest: init codex-lb');
|
|
952
|
+
if (!hasCodexUnstableFeatureWarningSuppression(codexLbRepairSetupConfig)) throw new Error('selftest: init codex-lb did not suppress Codex unstable feature warning');
|
|
935
953
|
await writeTextAtomic(path.join(codexLbHome, '.codex', 'config.toml'), `${codexLbConfig}\n[mcp_servers.supabase]\nurl = "https://mcp.supabase.com/mcp?project_ref=ref&read_only=true&features=database,docs"\n`);
|
|
936
954
|
const ptmp = path.join(tmp, 'codex-lb-project-config'), prevHome = process.env.HOME;
|
|
937
955
|
try { process.env.HOME = codexLbHome; await initProject(ptmp, { installScope: 'global' }); }
|
|
938
956
|
finally { if (prevHome === undefined) delete process.env.HOME; else process.env.HOME = prevHome; }
|
|
939
957
|
const pcfg = await safeReadText(path.join(ptmp, '.codex', 'config.toml'));
|
|
940
958
|
if (!pcfg.includes('model_provider = "codex-lb"') || !pcfg.includes('[model_providers.codex-lb]') || !pcfg.includes('[mcp_servers.supabase]') || !pcfg.includes('read_only=true')) throw new Error('selftest: project codex-lb');
|
|
959
|
+
if (!hasCodexUnstableFeatureWarningSuppression(pcfg)) throw new Error('selftest: project codex-lb config did not suppress Codex unstable feature warning');
|
|
941
960
|
await writeTextAtomic(path.join(codexLbHome, '.codex', 'auth.json'), '{"auth_mode":"browser"}\n');
|
|
942
961
|
const codexLbRepair = await runProcess(process.execPath, [path.join(packageRoot(), 'bin', 'sks.mjs'), 'auth', 'repair', '--json'], { cwd: tmp, env: codexLbEnvForSelftest, timeoutMs: 15000, maxOutputBytes: 64 * 1024 });
|
|
943
962
|
if (codexLbRepair.code !== 0) throw new Error(`selftest: codex-lb repair exited ${codexLbRepair.code}: ${codexLbRepair.stderr}`);
|
|
@@ -1014,7 +1033,7 @@ export async function selftestCodexLb(tmp) {
|
|
|
1014
1033
|
const codexLbDoctorJson = JSON.parse(codexLbDoctorRepair.stdout);
|
|
1015
1034
|
const codexLbDoctorAuth = await safeReadText(path.join(codexLbHome, '.codex', 'auth.json'));
|
|
1016
1035
|
const codexLbDoctorConfig = await safeReadText(path.join(codexLbHome, '.codex', 'config.toml'));
|
|
1017
|
-
if (!codexLbDoctorJson.repair?.codex_lb?.ok || !codexLbDoctorJson.repair.codex_lb.config_repaired || !codexLbDoctorJson.codex_lb?.ok || !codexLbDoctorAuth.includes('"auth_mode":"apikey"') || !codexLbDoctorAuth.includes('sk-test') || !codexLbDoctorConfig.includes('model_provider = "codex-lb"') || !codexLbDoctorConfig.includes('https://lb.example.test/backend-api/codex')) throw new Error('selftest: doctor codex-lb');
|
|
1036
|
+
if (!codexLbDoctorJson.repair?.codex_lb?.ok || !codexLbDoctorJson.repair.codex_lb.config_repaired || !codexLbDoctorJson.codex_lb?.ok || !codexLbDoctorAuth.includes('"auth_mode":"apikey"') || !codexLbDoctorAuth.includes('sk-test') || !codexLbDoctorConfig.includes('model_provider = "codex-lb"') || !codexLbDoctorConfig.includes('https://lb.example.test/backend-api/codex') || !hasCodexUnstableFeatureWarningSuppression(codexLbDoctorConfig)) throw new Error('selftest: doctor codex-lb');
|
|
1018
1037
|
const codexLbContext7Bin = path.join(tmp, 'codex-lb-context7-bin');
|
|
1019
1038
|
await ensureDir(codexLbContext7Bin);
|
|
1020
1039
|
await writeTextAtomic(path.join(codexLbContext7Bin, 'codex'), '#!/bin/sh\nif [ "$1" = "--version" ]; then echo "codex-cli 99.0.0"; exit 0; fi\nif [ "$CODEX_LB_API_KEY" ]; then echo "context7 leaked CODEX_LB_API_KEY" >&2; exit 77; fi\nif [ "$1" = "mcp" ] && [ "$2" = "list" ]; then echo ""; exit 0; fi\nif [ "$1" = "mcp" ] && [ "$2" = "add" ]; then echo "context7 added"; exit 0; fi\necho "unexpected codex $*" >&2\nexit 2\n');
|
|
@@ -1092,6 +1111,7 @@ export async function selftestCodexLb(tmp) {
|
|
|
1092
1111
|
const codexLbStatusText = await runProcess(process.execPath, [path.join(packageRoot(), 'bin', 'sks.mjs'), 'codex-lb', 'status'], { cwd: tmp, env: codexLbEnvForSelftest, timeoutMs: 15000, maxOutputBytes: 64 * 1024 });
|
|
1093
1112
|
if (!String(codexLbStatusText.stdout || '').includes('Repair auth: sks codex-lb repair')) throw new Error('selftest: codex-lb status did not advertise repair command');
|
|
1094
1113
|
if (!/^model = "gpt-5\.5"/m.test(codexLbConfig) || !codexLbConfig.includes('service_tier = "fast"') || !codexLbConfig.includes('hooks = true') || hasDeprecatedCodexHooksFeatureFlag(codexLbConfig) || !codexLbConfig.includes('multi_agent = true') || !codexLbConfig.includes('fast_mode = true') || !codexLbConfig.includes('fast_mode_ui = true') || !codexLbConfig.includes('codex_git_commit = true') || !codexLbConfig.includes('computer_use = true') || !codexLbConfig.includes('apps = true') || !codexLbConfig.includes('plugins = true') || !codexLbConfig.includes('[user.fast_mode]') || !codexLbConfig.includes('visible = true') || !codexLbConfig.includes('enabled = true') || !codexLbConfig.includes('default_profile = "sks-fast-high"') || !/\[profiles\.sks-fast-high\][\s\S]*?service_tier = "fast"/.test(codexLbConfig) || codexLbConfig.includes('fast_default_opt_out = true') || hasTopLevelCodexModeLock(codexLbConfig)) throw new Error('selftest: codex-lb setup did not preserve Codex App feature flags, Fast mode defaults, Codex Git commit generation, force GPT-5.5, or migrate the hooks feature flag');
|
|
1114
|
+
if (!hasCodexUnstableFeatureWarningSuppression(codexLbConfig)) throw new Error('selftest: codex-lb setup did not suppress Codex unstable feature warning');
|
|
1095
1115
|
const codexLbLaunch = codexLaunchCommand(tmp, 'codex', []);
|
|
1096
1116
|
if (!codexLbLaunch.includes('sks-codex-lb.env')) throw new Error('selftest: tmux launch command does not source codex-lb env file');
|
|
1097
1117
|
if (!codexLbLaunch.includes("'--model' 'gpt-5.5'")) throw new Error('selftest: tmux launch command without args did not force GPT-5.5');
|
|
@@ -1118,3 +1138,7 @@ function hasDeprecatedCodexHooksFeatureFlag(text = '') {
|
|
|
1118
1138
|
}
|
|
1119
1139
|
return lines.slice(start + 1, end).some((line) => /^\s*codex_hooks\s*=/.test(line));
|
|
1120
1140
|
}
|
|
1141
|
+
|
|
1142
|
+
function hasCodexUnstableFeatureWarningSuppression(text = '') {
|
|
1143
|
+
return /(^|\n)\s*suppress_unstable_features_warning\s*=\s*true\s*(?:#.*)?(?=\n|$)/.test(String(text || ''));
|
|
1144
|
+
}
|
package/src/cli/main.mjs
CHANGED
|
@@ -4,7 +4,7 @@ import fsp from 'node:fs/promises';
|
|
|
4
4
|
import readline from 'node:readline/promises';
|
|
5
5
|
import { stdin as input, stdout as output } from 'node:process';
|
|
6
6
|
import { projectRoot, readJson, writeJsonAtomic, writeTextAtomic, appendJsonlBounded, nowIso, exists, ensureDir, tmpdir, packageRoot, dirSize, formatBytes, which, runProcess, PACKAGE_VERSION, sksRoot, globalSksRoot, findProjectRoot, readStdin } from '../core/fsx.mjs';
|
|
7
|
-
import { initProject, installSkills, normalizeInstallScope, sksCommandPrefix } from '../core/init.mjs';
|
|
7
|
+
import { assertCodexWarningSuppressed as assertCodexWarn, hasDeprecatedCodexHooksFeatureFlag, hasTopLevelCodexModeLock, initProject, installSkills, missingGeneratedCodexAppFeatureFlags, normalizeInstallScope, sksCommandPrefix } from '../core/init.mjs';
|
|
8
8
|
import { buildCodexExecArgs, getCodexInfo, runCodexExec } from '../core/codex-adapter.mjs';
|
|
9
9
|
import { createMission, loadMission, findLatestMission, missionDir, setCurrent, stateFile } from '../core/mission.mjs';
|
|
10
10
|
import { buildQuestionSchema, writeQuestions } from '../core/questions.mjs';
|
|
@@ -22,7 +22,7 @@ import { bumpProjectVersion, installVersionGitHook, runVersionPreCommit, version
|
|
|
22
22
|
import { rustInfo } from '../core/rust-accelerator.mjs';
|
|
23
23
|
import { renderCartridge, validateCartridge, driftCartridge, snapshotCartridge } from '../core/gx-renderer.mjs';
|
|
24
24
|
import { defaultEvaluationScenario, runEvaluationBenchmark } from '../core/evaluation.mjs';
|
|
25
|
-
import {
|
|
25
|
+
import { evaluateResearchGate, writeMockResearchResult, writeResearchPlan } from '../core/research.mjs';
|
|
26
26
|
import {
|
|
27
27
|
PPT_AUDIENCE_STRATEGY_ARTIFACT,
|
|
28
28
|
PPT_CLEANUP_REPORT_ARTIFACT,
|
|
@@ -78,7 +78,7 @@ import { OPENCLAW_SKILL_NAME, installOpenClawSkill } from '../core/openclaw.mjs'
|
|
|
78
78
|
import { buildTmuxLaunchPlan, buildTmuxOpenArgs, codexLaunchCommand, createTmuxSession, defaultCodexLaunchArgs, isTmuxShellSession, runTmuxLaunchPlanSyntaxCheck, shouldAutoAttachTmux, sksAsciiLogo, tmuxReadiness, tmuxStatusKind, defaultTmuxSessionName, formatTmuxBanner, launchMadTmuxUi, launchTmuxTeamView, launchTmuxUi, platformTmuxInstallHint, reconcileTmuxTeamCockpit, runTmuxStatus, sanitizeTmuxSessionName, teamLaneStyle } from '../core/tmux-ui.mjs';
|
|
79
79
|
import { autoReviewProfileName, autoReviewStatus, autoReviewSummary, enableAutoReview, disableAutoReview, enableMadHighProfile, madHighProfileName } from '../core/auto-review.mjs';
|
|
80
80
|
import { context7Command } from './context7-command.mjs';
|
|
81
|
-
import { askPostinstallQuestion, checkContext7, checkRequiredSkills, codexLbStatus, configureCodexLb, ensureCodexCliTool, ensureGlobalCodexSkillsDuringInstall, ensureProjectContext7Config, ensureRelatedCliTools, ensureSksCommandDuringInstall, ensureTmuxCliTool, globalCodexSkillsRoot, maybePromptCodexLbSetupForLaunch, maybePromptCodexUpdateForLaunch, postinstall, postinstallBootstrapDecision, repairCodexLbAuth, selftestCodexLb, shouldAutoApproveInstall } from './install-helpers.mjs';
|
|
81
|
+
import { askPostinstallQuestion, checkContext7, checkRequiredSkills, codexLbStatus, configureCodexLb, ensureCodexCliTool, ensureGlobalCodexFastModeDuringInstall, ensureGlobalCodexSkillsDuringInstall, ensureProjectContext7Config, ensureRelatedCliTools, ensureSksCommandDuringInstall, ensureTmuxCliTool, globalCodexSkillsRoot, maybePromptCodexLbSetupForLaunch, maybePromptCodexUpdateForLaunch, postinstall, postinstallBootstrapDecision, repairCodexLbAuth, selftestCodexLb, shouldAutoApproveInstall } from './install-helpers.mjs';
|
|
82
82
|
import { buildTeamPlan, codeStructureCommand, dbCommand, defaultBeta, defaultVGraph, evalCommand, gcCommand, goalCommand, gxCommand, harnessCommand, hproofCommand, madHighCommand as runMadHighCommand, memoryCommand, migrateWikiContextPack, parseTeamCreateArgs, perfCommand, profileCommand, projectWikiClaims, proofFieldCommand, qaLoopCommand, quickstartCommand, researchCommand, skillDreamCommand, statsCommand, team, teamWorkflowMarkdown, validateArtifactsCommand, wikiCommand, wikiVoxelRowCount, writeWikiContextPack } from './maintenance-commands.mjs';
|
|
83
83
|
import { openClawCommand } from './openclaw-command.mjs';
|
|
84
84
|
|
|
@@ -211,7 +211,7 @@ Usage:
|
|
|
211
211
|
sks team attach-tmux [mission-id|latest]
|
|
212
212
|
sks team cleanup-tmux [mission-id|latest]
|
|
213
213
|
sks research prepare "topic" [--depth frontier]
|
|
214
|
-
sks research run <mission-id|latest> [--mock] [--max-cycles N]
|
|
214
|
+
sks research run <mission-id|latest> [--mock] [--max-cycles N] [--cycle-timeout-minutes N]
|
|
215
215
|
sks research status <mission-id|latest>
|
|
216
216
|
sks db policy
|
|
217
217
|
sks db scan [--migrations] [--json]
|
|
@@ -1659,6 +1659,7 @@ async function doctor(args) {
|
|
|
1659
1659
|
let conflictScan = await scanHarnessConflicts(root);
|
|
1660
1660
|
let repairApplied = false;
|
|
1661
1661
|
let globalSkillsRepair = null;
|
|
1662
|
+
let globalCodexConfigRepair = null;
|
|
1662
1663
|
let projectRepair = null;
|
|
1663
1664
|
let codexLbRepair = null;
|
|
1664
1665
|
const globalCommand = await globalSksCommand();
|
|
@@ -1666,6 +1667,7 @@ async function doctor(args) {
|
|
|
1666
1667
|
const existingManifest = await readJson(path.join(root, '.sneakoscope', 'manifest.json'), null);
|
|
1667
1668
|
const fixScope = requestedScope || normalizeInstallScope(existingManifest?.installation?.scope || 'global');
|
|
1668
1669
|
projectRepair = await initProject(root, { installScope: fixScope, globalCommand, localOnly: flag(args, '--local-only') || Boolean(existingManifest?.git?.local_only), force: true, repair: true });
|
|
1670
|
+
if (!flag(args, '--local-only')) globalCodexConfigRepair = await ensureGlobalCodexFastModeDuringInstall();
|
|
1669
1671
|
if (!flag(args, '--local-only')) globalSkillsRepair = await ensureGlobalCodexSkillsDuringInstall({ force: true });
|
|
1670
1672
|
codexLbRepair = await repairCodexLbAuth();
|
|
1671
1673
|
repairApplied = true;
|
|
@@ -1695,7 +1697,7 @@ async function doctor(args) {
|
|
|
1695
1697
|
const result = {
|
|
1696
1698
|
node: { ok: nodeOk, version: process.version }, root, codex, rust,
|
|
1697
1699
|
install,
|
|
1698
|
-
repair: { applied: repairApplied, project: projectRepair, global_skills: globalSkillsRepair, codex_lb: codexLbRepair, blocked_by_other_harness: flag(args, '--fix') && conflictScan.hard_block },
|
|
1700
|
+
repair: { applied: repairApplied, project: projectRepair, global_codex_config: globalCodexConfigRepair, global_skills: globalSkillsRepair, codex_lb: codexLbRepair, blocked_by_other_harness: flag(args, '--fix') && conflictScan.hard_block },
|
|
1699
1701
|
harness_conflicts: {
|
|
1700
1702
|
ok: conflictScan.ok,
|
|
1701
1703
|
hard_block: conflictScan.hard_block,
|
|
@@ -1729,6 +1731,7 @@ async function doctor(args) {
|
|
|
1729
1731
|
console.log(`Install: ${install.ok ? 'ok' : 'missing'} ${install.scope} (${install.command_prefix})`);
|
|
1730
1732
|
console.log(`Conflicts: ${result.harness_conflicts.hard_block ? 'blocked' : 'ok'} ${result.harness_conflicts.conflicts.length} finding(s)`);
|
|
1731
1733
|
if (repairApplied) console.log('Repair: regenerated SKS managed files from the installed package template');
|
|
1734
|
+
if (globalCodexConfigRepair) console.log(`Global Codex config: ${globalCodexConfigRepair.status} ${globalCodexConfigRepair.config_path || ''}`.trimEnd());
|
|
1732
1735
|
if (globalSkillsRepair) {
|
|
1733
1736
|
const removed = globalSkillsRepair.removed_stale_generated_skills || [];
|
|
1734
1737
|
const cleanup = removed.length ? ` removed stale generated skill shadow(s): ${removed.join(', ')}` : '';
|
|
@@ -1925,34 +1928,6 @@ async function safeReadText(file, fallback = '') {
|
|
|
1925
1928
|
try { return await fsp.readFile(file, 'utf8'); } catch { return fallback; }
|
|
1926
1929
|
}
|
|
1927
1930
|
|
|
1928
|
-
function hasTopLevelCodexModeLock(text = '') {
|
|
1929
|
-
const lines = String(text || '').split('\n');
|
|
1930
|
-
const firstTable = lines.findIndex((x) => /^\s*\[.+\]\s*$/.test(x));
|
|
1931
|
-
const top = (firstTable === -1 ? lines : lines.slice(0, firstTable)).join('\n');
|
|
1932
|
-
const model = top.match(/^model\s*=\s*"([^"]+)"/m)?.[1];
|
|
1933
|
-
return (Boolean(model) && model !== 'gpt-5.5') || /^model_reasoning_effort\s*=/m.test(top);
|
|
1934
|
-
}
|
|
1935
|
-
|
|
1936
|
-
function hasDeprecatedCodexHooksFeatureFlag(text = '') {
|
|
1937
|
-
const lines = String(text || '').split('\n');
|
|
1938
|
-
const start = lines.findIndex((line) => line.trim() === '[features]');
|
|
1939
|
-
if (start === -1) return false;
|
|
1940
|
-
let end = lines.length;
|
|
1941
|
-
for (let i = start + 1; i < lines.length; i += 1) {
|
|
1942
|
-
if (/^\s*\[.+\]\s*$/.test(lines[i])) {
|
|
1943
|
-
end = i;
|
|
1944
|
-
break;
|
|
1945
|
-
}
|
|
1946
|
-
}
|
|
1947
|
-
return lines.slice(start + 1, end).some((line) => /^\s*codex_hooks\s*=/.test(line));
|
|
1948
|
-
}
|
|
1949
|
-
|
|
1950
|
-
const REQUIRED_GENERATED_CODEX_APP_FEATURE_FLAGS = ['hooks', 'multi_agent', 'fast_mode', 'fast_mode_ui', 'codex_git_commit', 'computer_use', 'apps', 'plugins'];
|
|
1951
|
-
|
|
1952
|
-
function missingGeneratedCodexAppFeatureFlags(text = '') {
|
|
1953
|
-
return REQUIRED_GENERATED_CODEX_APP_FEATURE_FLAGS.filter((name) => !String(text || '').includes(`${name} = true`));
|
|
1954
|
-
}
|
|
1955
|
-
|
|
1956
1931
|
async function resolveMissionId(root, arg) { return (!arg || arg === 'latest') ? findLatestMission(root) : arg; }
|
|
1957
1932
|
function readMaxCycles(args, fallback) {
|
|
1958
1933
|
const i = args.indexOf('--max-cycles');
|
|
@@ -1963,7 +1938,7 @@ function readMaxCycles(args, fallback) {
|
|
|
1963
1938
|
|
|
1964
1939
|
function positionalArgs(args = []) {
|
|
1965
1940
|
const out = [];
|
|
1966
|
-
const valueFlags = new Set(['--format', '--iterations', '--out', '--baseline', '--candidate', '--install-scope', '--max-cycles', '--depth', '--scope', '--transport', '--query', '--topic', '--tokens', '--timeout-ms', '--sql', '--command', '--project-ref', '--agent', '--phase', '--message', '--role', '--max-anchors', '--lines', '--dir']);
|
|
1941
|
+
const valueFlags = new Set(['--format', '--iterations', '--out', '--baseline', '--candidate', '--install-scope', '--max-cycles', '--cycle-timeout-minutes', '--depth', '--scope', '--transport', '--query', '--topic', '--tokens', '--timeout-ms', '--sql', '--command', '--project-ref', '--agent', '--phase', '--message', '--role', '--max-anchors', '--lines', '--dir']);
|
|
1967
1942
|
for (let i = 0; i < args.length; i++) {
|
|
1968
1943
|
const arg = String(args[i]);
|
|
1969
1944
|
if (valueFlags.has(arg)) {
|
|
@@ -2118,6 +2093,8 @@ async function selftest() {
|
|
|
2118
2093
|
if (!doctorRepairJson.repair?.applied || doctorRepairJson.install?.scope !== 'project' || !doctorRepairJson.install?.ok || !doctorRepairJson.install?.source_project) throw new Error('selftest: doctor scope');
|
|
2119
2094
|
const repairedManifest = await readJson(path.join(repairTmp, '.sneakoscope', 'manifest.json'));
|
|
2120
2095
|
if (repairedManifest.installation?.scope !== 'project' || repairedManifest.installation?.hook_command_prefix !== 'node ./bin/sks.mjs') throw new Error('selftest: manifest scope');
|
|
2096
|
+
const repairedCodexConfig = await safeReadText(path.join(repairTmp, '.codex', 'config.toml'));
|
|
2097
|
+
assertCodexWarn(repairedCodexConfig, 'doctor project config');
|
|
2121
2098
|
const repairedTeamSkill = await safeReadText(path.join(repairTmp, '.agents', 'skills', 'team', 'SKILL.md'));
|
|
2122
2099
|
if (!repairedTeamSkill.includes('SKS Team orchestration') || repairedTeamSkill.includes('tampered')) throw new Error('selftest: doctor repair did not regenerate team skill');
|
|
2123
2100
|
if (await exists(path.join(repairTmp, '.agents', 'skills', 'agent-team', 'SKILL.md'))) throw new Error('selftest: doctor repair did not remove deprecated agent-team alias skill');
|
|
@@ -2153,6 +2130,9 @@ async function selftest() {
|
|
|
2153
2130
|
});
|
|
2154
2131
|
if (doctorGlobalRepair.code !== 0) throw new Error(`selftest: doctor --fix global skill repair exited ${doctorGlobalRepair.code}: ${doctorGlobalRepair.stderr}`);
|
|
2155
2132
|
const doctorGlobalRepairJson = JSON.parse(doctorGlobalRepair.stdout || '{}');
|
|
2133
|
+
const doctorGlobalCodexConfig = await safeReadText(path.join(doctorGlobalHome, '.codex', 'config.toml'));
|
|
2134
|
+
if (!doctorGlobalRepairJson.repair?.global_codex_config) throw new Error('selftest: doctor global config repair missing');
|
|
2135
|
+
assertCodexWarn(doctorGlobalCodexConfig, 'doctor global config');
|
|
2156
2136
|
for (const name of stalePluginSkillNames) {
|
|
2157
2137
|
if (await exists(path.join(doctorGlobalHome, '.agents', 'skills', name, 'SKILL.md'))) throw new Error(`selftest: doctor --fix did not remove global generated ${name} plugin shadow skill`);
|
|
2158
2138
|
}
|
|
@@ -2185,6 +2165,7 @@ async function selftest() {
|
|
|
2185
2165
|
if (!(await exists(path.join(postinstallSetupTmp, '.codex', 'hooks.json')))) throw new Error('selftest: postinstall did not create project hooks during automatic bootstrap');
|
|
2186
2166
|
const postinstallSetupConfig = await safeReadText(path.join(postinstallSetupTmp, '.codex', 'config.toml'));
|
|
2187
2167
|
if (missingGeneratedCodexAppFeatureFlags(postinstallSetupConfig).length || hasDeprecatedCodexHooksFeatureFlag(postinstallSetupConfig)) throw new Error('selftest: postinstall flags');
|
|
2168
|
+
assertCodexWarn(postinstallSetupConfig, 'postinstall project config');
|
|
2188
2169
|
if (!String(postinstallSetup.stdout || '').includes('Codex App global $ skills: installed')) throw new Error('selftest: postinstall did not report automatic global Codex App skills');
|
|
2189
2170
|
if (!String(postinstallSetup.stdout || '').includes('Removed stale generated skill shadow(s):')) throw new Error('selftest: postinstall did not report stale first-party plugin shadow cleanup');
|
|
2190
2171
|
const postinstallSetupManifest = await readJson(path.join(postinstallSetupTmp, '.sneakoscope', 'manifest.json'));
|
|
@@ -2221,6 +2202,7 @@ async function selftest() {
|
|
|
2221
2202
|
if (!(await exists(path.join(postinstallNoMarkerGlobalRoot, '.sneakoscope', 'manifest.json')))) throw new Error('selftest: no-marker postinstall did not bootstrap global runtime root');
|
|
2222
2203
|
const postinstallNoMarkerConfig = await safeReadText(path.join(postinstallNoMarkerGlobalRoot, '.codex', 'config.toml'));
|
|
2223
2204
|
if (missingGeneratedCodexAppFeatureFlags(postinstallNoMarkerConfig).length || hasDeprecatedCodexHooksFeatureFlag(postinstallNoMarkerConfig)) throw new Error('selftest: no-marker flags');
|
|
2205
|
+
assertCodexWarn(postinstallNoMarkerConfig, 'postinstall global runtime config');
|
|
2224
2206
|
if (await exists(path.join(postinstallNoMarkerCwd, '.sneakoscope'))) throw new Error('selftest: no-marker postinstall polluted install cwd');
|
|
2225
2207
|
if (await exists(path.join(postinstallNoMarkerGlobalRoot, '.gitignore'))) throw new Error('selftest: global runtime bootstrap without project git wrote shared .gitignore');
|
|
2226
2208
|
const bootstrapJsonTmp = tmpdir();
|
|
@@ -2647,19 +2629,21 @@ async function selftest() {
|
|
|
2647
2629
|
const hookResearchTeamResult = await runProcess(process.execPath, [hookBin, 'hook', 'user-prompt-submit'], { cwd: hookResearchMarkdownTmp, input: hookResearchTeamPayload, env: { SKS_DISABLE_UPDATE_CHECK: '1' }, timeoutMs: 15000, maxOutputBytes: 256 * 1024 });
|
|
2648
2630
|
if (hookResearchTeamResult.code !== 0) throw new Error(`selftest: active Team setup before markdown $Research hook exited ${hookResearchTeamResult.code}: ${hookResearchTeamResult.stderr}`);
|
|
2649
2631
|
const hookResearchTeamState = await readJson(stateFile(hookResearchMarkdownTmp), {});
|
|
2650
|
-
const hookResearchMarkdownPayload = JSON.stringify({ cwd: hookResearchMarkdownTmp, prompt: '[$research](
|
|
2632
|
+
const hookResearchMarkdownPayload = JSON.stringify({ cwd: hookResearchMarkdownTmp, prompt: '논문 [$research](x) 팀 커밋 푸쉬 연구' });
|
|
2651
2633
|
const hookResearchMarkdownResult = await runProcess(process.execPath, [hookBin, 'hook', 'user-prompt-submit'], { cwd: hookResearchMarkdownTmp, input: hookResearchMarkdownPayload, env: { SKS_DISABLE_UPDATE_CHECK: '1' }, timeoutMs: 15000, maxOutputBytes: 256 * 1024 });
|
|
2652
2634
|
if (hookResearchMarkdownResult.code !== 0) throw new Error(`selftest: markdown $Research hook exited ${hookResearchMarkdownResult.code}: ${hookResearchMarkdownResult.stderr}`);
|
|
2653
2635
|
const hookResearchMarkdownJson = JSON.parse(hookResearchMarkdownResult.stdout);
|
|
2654
2636
|
const hookResearchMarkdownContext = hookResearchMarkdownJson.hookSpecificOutput?.additionalContext || '';
|
|
2655
|
-
if (!hookResearchMarkdownContext.includes('$Research route prepared')) throw new Error('selftest: markdown
|
|
2656
|
-
if (hookResearchMarkdownContext.includes(`Active Team mission ${hookResearchTeamState.mission_id}`)) throw new Error('selftest:
|
|
2657
|
-
if (!String(hookResearchMarkdownJson.systemMessage || '').includes('Research route') || String(hookResearchMarkdownJson.systemMessage || '').includes('QA-LOOP route')) throw new Error('selftest:
|
|
2637
|
+
if (!hookResearchMarkdownContext.includes('$Research route prepared')) throw new Error('selftest: markdown research hook');
|
|
2638
|
+
if (hookResearchMarkdownContext.includes(`Active Team mission ${hookResearchTeamState.mission_id}`)) throw new Error('selftest: stale Team context');
|
|
2639
|
+
if (!String(hookResearchMarkdownJson.systemMessage || '').includes('Research route') || String(hookResearchMarkdownJson.systemMessage || '').includes('QA-LOOP route')) throw new Error('selftest: research hook message');
|
|
2658
2640
|
const hookResearchMarkdownState = await readJson(stateFile(hookResearchMarkdownTmp), {});
|
|
2659
|
-
if (hookResearchMarkdownState.mode !== 'RESEARCH' || hookResearchMarkdownState.route !== 'Research' || hookResearchMarkdownState.mission_id === hookResearchTeamState.mission_id || hookResearchMarkdownState.stop_gate !== 'research-gate.json' || !hookResearchMarkdownState.pipeline_plan_ready) throw new Error('selftest:
|
|
2641
|
+
if (hookResearchMarkdownState.mode !== 'RESEARCH' || hookResearchMarkdownState.route !== 'Research' || hookResearchMarkdownState.mission_id === hookResearchTeamState.mission_id || hookResearchMarkdownState.stop_gate !== 'research-gate.json' || !hookResearchMarkdownState.pipeline_plan_ready) throw new Error('selftest: research hook state');
|
|
2660
2642
|
const hookResearchMissionDir = missionDir(hookResearchMarkdownTmp, hookResearchMarkdownState.mission_id);
|
|
2661
|
-
if (!(await exists(path.join(hookResearchMissionDir, PIPELINE_PLAN_ARTIFACT)))) throw new Error('selftest:
|
|
2662
|
-
|
|
2643
|
+
if (!(await exists(path.join(hookResearchMissionDir, PIPELINE_PLAN_ARTIFACT)))) throw new Error('selftest: research hook plan');
|
|
2644
|
+
const rss = 'research-source-skill.md';
|
|
2645
|
+
const gos = 'genius-opinion-summary.md';
|
|
2646
|
+
for (const artifact of [rss, 'source-ledger.json', 'scout-ledger.json', 'debate-ledger.json', 'falsification-ledger.json']) {
|
|
2663
2647
|
if (!(await exists(path.join(hookResearchMissionDir, artifact)))) throw new Error(`selftest: hook research ${artifact}`);
|
|
2664
2648
|
}
|
|
2665
2649
|
const hookPayload = JSON.stringify({ cwd: hookGoalTmp, prompt: '$Goal 로그인 세션 만료 UX 개선' });
|
|
@@ -2999,6 +2983,7 @@ async function selftest() {
|
|
|
2999
2983
|
const codexConfigText = await safeReadText(path.join(tmp, '.codex', 'config.toml'));
|
|
3000
2984
|
const missingCodexConfigFlags = missingGeneratedCodexAppFeatureFlags(codexConfigText);
|
|
3001
2985
|
if (missingCodexConfigFlags.length || hasDeprecatedCodexHooksFeatureFlag(codexConfigText)) throw new Error(`selftest: generated Codex App feature flags missing or deprecated: ${missingCodexConfigFlags.join(', ')}`);
|
|
2986
|
+
assertCodexWarn(codexConfigText, 'generated Codex App config');
|
|
3002
2987
|
if (!hasContext7ConfigText(codexConfigText)) throw new Error('selftest: Context7 MCP not configured');
|
|
3003
2988
|
if (!codexConfigText.includes('[profiles.sks-task-low]') || !codexConfigText.includes('[profiles.sks-task-medium]') || !codexConfigText.includes('[profiles.sks-logic-high]') || !codexConfigText.includes('[profiles.sks-fast-high]') || !codexConfigText.includes('[profiles.sks-research-xhigh]') || !codexConfigText.includes('[profiles.sks-mad-high]')) throw new Error('selftest: GPT-5.5 reasoning profiles not configured');
|
|
3004
2989
|
if (!/\[profiles\.sks-mad-high\][\s\S]*?approval_policy = "never"[\s\S]*?sandbox_mode = "danger-full-access"/.test(codexConfigText)) throw new Error('selftest: generated sks-mad-high profile is not full access');
|
|
@@ -3010,6 +2995,7 @@ async function selftest() {
|
|
|
3010
2995
|
await initProject(preservedConfigTmp, {});
|
|
3011
2996
|
const preservedConfig = await safeReadText(path.join(preservedConfigTmp, '.codex', 'config.toml'));
|
|
3012
2997
|
if (!/^model = "gpt-5\.5"/m.test(preservedConfig) || !preservedConfig.includes('service_tier = "fast"') || !preservedConfig.includes('fast_mode = true') || !preservedConfig.includes('fast_mode_ui = true') || !preservedConfig.includes('[user.fast_mode]') || !preservedConfig.includes('visible = true') || !preservedConfig.includes('enabled = true') || !preservedConfig.includes('default_profile = "sks-fast-high"') || !/\[profiles\.sks-fast-high\][\s\S]*?service_tier = "fast"/.test(preservedConfig)) throw new Error('selftest: Codex config merge dropped or failed to enable Fast mode defaults and GPT-5.5');
|
|
2998
|
+
assertCodexWarn(preservedConfig, 'merged Codex config');
|
|
3013
2999
|
if (preservedConfig.includes('fast_default_opt_out = true') || !preservedConfig.includes('keep = true')) throw new Error('selftest: Codex config merge did not remove stale Fast opt-out notice while preserving other notice keys');
|
|
3014
3000
|
const missingPreservedFlags = missingGeneratedCodexAppFeatureFlags(preservedConfig);
|
|
3015
3001
|
if (missingPreservedFlags.length || hasDeprecatedCodexHooksFeatureFlag(preservedConfig) || !preservedConfig.includes('custom_preview = true') || !preservedConfig.includes('[profiles.sks-fast-high]')) throw new Error(`selftest: Codex config merge did not add required app feature flags, preserve existing feature flags, or remove deprecated codex_hooks: ${missingPreservedFlags.join(', ')}`);
|
|
@@ -3245,7 +3231,7 @@ async function selftest() {
|
|
|
3245
3231
|
if (teamPlan.agent_session_count !== 5) throw new Error('selftest: team default sessions not 5');
|
|
3246
3232
|
if (teamPlan.role_counts.executor !== 3 || teamPlan.role_counts.user !== 1 || teamPlan.role_counts.reviewer !== 5) throw new Error('selftest: team default role counts invalid');
|
|
3247
3233
|
const teamPlanFeatureFlags = teamPlan.codex_config_required?.features || {};
|
|
3248
|
-
const missingTeamPlanFeatureFlags =
|
|
3234
|
+
const missingTeamPlanFeatureFlags = missingGeneratedCodexAppFeatureFlags(teamPlanFeatureFlags);
|
|
3249
3235
|
if (missingTeamPlanFeatureFlags.length || teamPlanFeatureFlags.codex_hooks === true) throw new Error(`selftest: team plan Codex config missing required app flags or still uses deprecated codex_hooks: ${missingTeamPlanFeatureFlags.join(', ')}`);
|
|
3250
3236
|
if (!teamPlan.review_gate?.passed || teamPlan.review_gate.required_reviewer_lanes !== 5) throw new Error('selftest: team review policy gate did not pass default plan');
|
|
3251
3237
|
if (teamPlan.codex_config_required?.service_tier !== 'fast' || teamPlan.reasoning?.service_tier !== 'fast') throw new Error('selftest: team plan did not require Fast service tier');
|
|
@@ -3476,7 +3462,7 @@ async function selftest() {
|
|
|
3476
3462
|
if (!(await readTeamTranscriptTail(teamDir, 1)).join('\n').includes('selftest mapped options')) throw new Error('selftest: team transcript tail missing event');
|
|
3477
3463
|
const teamLane = await renderTeamAgentLane(teamDir, { missionId: teamId, agent: 'analysis_scout_1', lines: 4 });
|
|
3478
3464
|
if (!teamLane.includes('selftest mapped repo slice')) throw new Error('selftest: team agent lane missing event context');
|
|
3479
|
-
if (!teamLane.includes('##
|
|
3465
|
+
if (!teamLane.includes('## Codex Chat') || !teamLane.includes('+-- me [status]') || !teamLane.includes('selftest mapped repo slice') || teamLane.includes('## Global Tail')) throw new Error('selftest: chat lane');
|
|
3480
3466
|
const teamLaneCli = await runProcess(process.execPath, [hookBin, 'team', 'lane', teamId, '--agent', 'analysis_scout_1', '--lines', '4'], { cwd: tmp, env: { SKS_DISABLE_UPDATE_CHECK: '1' }, timeoutMs: 15000, maxOutputBytes: 64 * 1024 });
|
|
3481
3467
|
if (teamLaneCli.code !== 0 || !String(teamLaneCli.stdout || '').includes('SKS Team Agent Lane') || !String(teamLaneCli.stdout || '').includes('analysis_scout_1')) throw new Error('selftest: sks team lane CLI did not render an agent lane');
|
|
3482
3468
|
await writeTextAtomic(path.join(teamDir, 'team-analysis.md'), '- claim: analysis scout mapped route registry | source: src/core/routes.mjs | risk: high | confidence: supported\n');
|
|
@@ -3782,15 +3768,16 @@ async function selftest() {
|
|
|
3782
3768
|
if (wikiPruneDryRun.candidates < 1 || !wikiPruneDryRun.actions.some((action) => action.reason === 'low_wiki_trust')) throw new Error('selftest: wiki prune did not flag low-trust artifact');
|
|
3783
3769
|
const { dir: researchDir, mission: researchMission } = await createMission(tmp, { mode: 'research', prompt: '새로운 코드 리뷰 방법론 연구' });
|
|
3784
3770
|
const researchPlan = await writeResearchPlan(researchDir, researchMission.prompt, {});
|
|
3785
|
-
if (researchPlan.methodology !== 'genius-scout-council-frontier-discovery-loop' || researchPlan.web_research_policy?.mode !== '
|
|
3771
|
+
if (researchPlan.methodology !== 'genius-scout-council-frontier-discovery-loop' || researchPlan.web_research_policy?.mode !== 'layered_source_retrieval_and_triangulation') throw new Error('selftest: research plan contract');
|
|
3786
3772
|
const rArts = researchPlan.required_artifacts || [];
|
|
3787
|
-
for (const a of ['source-ledger.json', 'scout-ledger.json', 'debate-ledger.json', 'falsification-ledger.json']) if (!rArts.includes(a) || !(await exists(path.join(researchDir, a)))) throw new Error('selftest: research artifact');
|
|
3788
|
-
if (!rArts.includes('research-paper.md')) throw new Error('selftest: research paper');
|
|
3773
|
+
for (const a of [rss, 'source-ledger.json', 'scout-ledger.json', 'debate-ledger.json', 'falsification-ledger.json']) if (!rArts.includes(a) || !(await exists(path.join(researchDir, a)))) throw new Error('selftest: research artifact');
|
|
3774
|
+
if (!rArts.includes('research-paper.md') || !rArts.includes(gos)) throw new Error('selftest: research paper');
|
|
3789
3775
|
const initialResearchGate = await evaluateResearchGate(researchDir);
|
|
3790
3776
|
if (initialResearchGate.passed || ['web_search_pass_missing', 'eureka_missing', 'debate_exchanges_missing', 'research_paper_missing'].some((r) => !initialResearchGate.reasons.includes(r))) throw new Error('selftest: research gate');
|
|
3791
3777
|
const researchGate = await writeMockResearchResult(researchDir, researchPlan);
|
|
3792
3778
|
if (!researchGate.passed) throw new Error('selftest: mock research gate did not pass');
|
|
3793
|
-
|
|
3779
|
+
const rm = researchGate.metrics || {};
|
|
3780
|
+
if (['independent_scouts', 'xhigh_scouts', 'eureka_moments', 'debate_participants', 'genius_opinion_summaries'].some((m) => rm[m] < 5) || ['counterevidence_sources', 'falsification_cases', 'triangulation_checks'].some((m) => rm[m] < 1) || rm.paper_sections < 8 || rm.citation_coverage !== true || rm.source_layers_covered < 7) throw new Error('selftest: research metrics');
|
|
3794
3781
|
await writeJsonAtomic(path.join(dir, 'done-gate.json'), { passed: true, unsupported_critical_claims: 0, database_safety_violation: false, database_safety_reviewed: true, visual_drift: 'low', wiki_drift: 'low', tests_required: false });
|
|
3795
3782
|
const gate = await evaluateDoneGate(tmp, id);
|
|
3796
3783
|
if (!gate.passed) throw new Error('selftest: done gate');
|
|
@@ -8,14 +8,14 @@ import { buildQuestionSchema, writeQuestions } from '../core/questions.mjs';
|
|
|
8
8
|
import { sealContract } from '../core/decision-contract.mjs';
|
|
9
9
|
import { buildQaLoopQuestionSchema, buildQaLoopPrompt, evaluateQaGate, qaStatus, writeMockQaResult, writeQaLoopArtifacts } from '../core/qa-loop.mjs';
|
|
10
10
|
import { containsUserQuestion, noQuestionContinuationReason } from '../core/no-question-guard.mjs';
|
|
11
|
-
import { RESEARCH_PAPER_ARTIFACT, countResearchPaperSections, buildResearchPrompt, evaluateResearchGate, writeMockResearchResult, writeResearchPlan } from '../core/research.mjs';
|
|
11
|
+
import { RESEARCH_GENIUS_SUMMARY_ARTIFACT, RESEARCH_PAPER_ARTIFACT, RESEARCH_SOURCE_SKILL_ARTIFACT, countGeniusOpinionSummaries, countResearchPaperSections, buildResearchPrompt, evaluateResearchGate, writeMockResearchResult, writeResearchPlan } from '../core/research.mjs';
|
|
12
12
|
import { storageReport, enforceRetention, pruneWikiArtifacts } from '../core/retention.mjs';
|
|
13
13
|
import { evaluateDoneGate } from '../core/hproof.mjs';
|
|
14
14
|
import { renderCartridge, validateCartridge, driftCartridge, snapshotCartridge } from '../core/gx-renderer.mjs';
|
|
15
15
|
import { DEFAULT_EVAL_THRESHOLDS, compareEvaluationReports, runEvaluationBenchmark } from '../core/evaluation.mjs';
|
|
16
16
|
import { contextCapsule } from '../core/triwiki-attention.mjs';
|
|
17
17
|
import { rgbaKey, rgbaToWikiCoord, validateWikiCoordinateIndex } from '../core/wiki-coordinate.mjs';
|
|
18
|
-
import { ALLOWED_REASONING_EFFORTS, CODEX_COMPUTER_USE_ONLY_POLICY, DOLLAR_SKILL_NAMES, FROM_CHAT_IMG_CHECKLIST_ARTIFACT, FROM_CHAT_IMG_COVERAGE_ARTIFACT, FROM_CHAT_IMG_QA_LOOP_ARTIFACT, FROM_CHAT_IMG_SOURCE_INVENTORY_ARTIFACT, FROM_CHAT_IMG_TEMP_TRIWIKI_ARTIFACT, FROM_CHAT_IMG_TEMP_TRIWIKI_SESSIONS, FROM_CHAT_IMG_VISUAL_MAP_ARTIFACT, FROM_CHAT_IMG_WORK_ORDER_ARTIFACT, RECOMMENDED_SKILLS, ROUTES, hasFromChatImgSignal, reflectionRequiredForRoute, routePrompt, routeReasoning, routeRequiresSubagents, stackCurrentDocsPolicy, stripVisibleDecisionAnswerBlocks, triwikiContextTracking } from '../core/routes.mjs';
|
|
18
|
+
import { ALLOWED_REASONING_EFFORTS, CODEX_COMPUTER_USE_ONLY_POLICY, DOLLAR_SKILL_NAMES, FROM_CHAT_IMG_CHECKLIST_ARTIFACT, FROM_CHAT_IMG_COVERAGE_ARTIFACT, FROM_CHAT_IMG_QA_LOOP_ARTIFACT, FROM_CHAT_IMG_SOURCE_INVENTORY_ARTIFACT, FROM_CHAT_IMG_TEMP_TRIWIKI_ARTIFACT, FROM_CHAT_IMG_TEMP_TRIWIKI_SESSIONS, FROM_CHAT_IMG_VISUAL_MAP_ARTIFACT, FROM_CHAT_IMG_WORK_ORDER_ARTIFACT, RECOMMENDED_SKILLS, ROUTES, hasFromChatImgSignal, reflectionRequiredForRoute, routeNeedsContext7, routePrompt, routeReasoning, routeRequiresSubagents, stackCurrentDocsPolicy, stripVisibleDecisionAnswerBlocks, triwikiContextTracking } from '../core/routes.mjs';
|
|
19
19
|
import { TEAM_DECOMPOSITION_ARTIFACT, TEAM_GRAPH_ARTIFACT, TEAM_INBOX_DIR, TEAM_RUNTIME_TASKS_ARTIFACT, teamRuntimePlanMetadata, teamRuntimeRequiredArtifacts, writeTeamRuntimeArtifacts } from '../core/team-dag.mjs';
|
|
20
20
|
import { appendTeamEvent, formatAgentReasoning, formatRoleCounts, initTeamLive, normalizeTeamSpec, parseTeamSpecArgs, readTeamControl, readTeamDashboard, readTeamLive, readTeamTranscriptTail, renderTeamAgentLane, renderTeamCleanupSummary, renderTeamWatch, requestTeamSessionCleanup, teamCleanupRequested, teamReasoningPolicy } from '../core/team-live.mjs';
|
|
21
21
|
import { evaluateTeamReviewPolicyGate, MIN_TEAM_REVIEWER_LANES, MIN_TEAM_REVIEW_POLICY_TEXT, teamReviewPolicy } from '../core/team-review-policy.mjs';
|
|
@@ -42,6 +42,10 @@ const flag = (args, name) => args.includes(name);
|
|
|
42
42
|
const promptOf = (args) => args.filter((x) => !String(x).startsWith('--')).join(' ').trim();
|
|
43
43
|
const TEAM_SESSION_CLEANUP_ARTIFACT = 'team-session-cleanup.json';
|
|
44
44
|
const REPOSITORY_URL = 'https://github.com/mandarange/Sneakoscope-Codex.git';
|
|
45
|
+
const RESEARCH_DEFAULT_MAX_CYCLES = 3;
|
|
46
|
+
const RESEARCH_DEFAULT_CYCLE_TIMEOUT_MINUTES = 120;
|
|
47
|
+
const RESEARCH_MIN_CYCLE_TIMEOUT_MINUTES = 15;
|
|
48
|
+
const RESEARCH_MAX_CYCLE_TIMEOUT_MINUTES = 240;
|
|
45
49
|
|
|
46
50
|
async function resolveMissionId(root, arg) { return (!arg || arg === 'latest') ? findLatestMission(root) : arg; }
|
|
47
51
|
|
|
@@ -429,7 +433,7 @@ async function researchPrepare(args) {
|
|
|
429
433
|
if (!prompt) throw new Error('Missing research topic.');
|
|
430
434
|
const { id, dir } = await createMission(root, { mode: 'research', prompt });
|
|
431
435
|
const route = ROUTES.find((entry) => entry.id === 'Research') || routePrompt('$Research');
|
|
432
|
-
const context7Required =
|
|
436
|
+
const context7Required = routeNeedsContext7(route, prompt);
|
|
433
437
|
const reasoning = routeReasoning(route, prompt);
|
|
434
438
|
const plan = await writeResearchPlan(dir, prompt, { depth: readFlagValue(args, '--depth', 'frontier') });
|
|
435
439
|
const pipelinePlan = await writePipelinePlan(dir, { missionId: id, route, task: prompt, required: context7Required, ambiguity: { required: false, status: 'direct_research_cli' } });
|
|
@@ -457,7 +461,7 @@ async function researchPrepare(args) {
|
|
|
457
461
|
mode: route.mode,
|
|
458
462
|
phase: 'RESEARCH_PREPARED',
|
|
459
463
|
questions_allowed: false,
|
|
460
|
-
implementation_allowed:
|
|
464
|
+
implementation_allowed: false,
|
|
461
465
|
context7_required: context7Required,
|
|
462
466
|
context7_verified: false,
|
|
463
467
|
subagents_required: routeRequiresSubagents(route, prompt),
|
|
@@ -480,14 +484,16 @@ async function researchPrepare(args) {
|
|
|
480
484
|
console.log(`Plan: ${path.relative(root, path.join(dir, 'research-plan.md'))}`);
|
|
481
485
|
console.log(`Pipeline: ${path.relative(root, path.join(dir, PIPELINE_PLAN_ARTIFACT))}`);
|
|
482
486
|
console.log(`Paper: ${RESEARCH_PAPER_ARTIFACT}`);
|
|
487
|
+
console.log(`Genius summary: ${RESEARCH_GENIUS_SUMMARY_ARTIFACT}`);
|
|
488
|
+
console.log(`Source skill: ${RESEARCH_SOURCE_SKILL_ARTIFACT}`);
|
|
483
489
|
console.log('Ledgers: source-ledger.json, scout-ledger.json, debate-ledger.json, novelty-ledger.json, falsification-ledger.json');
|
|
484
|
-
console.log(`Run: sks research run ${id} --max-cycles
|
|
490
|
+
console.log(`Run: sks research run ${id} --max-cycles ${RESEARCH_DEFAULT_MAX_CYCLES} --cycle-timeout-minutes ${RESEARCH_DEFAULT_CYCLE_TIMEOUT_MINUTES}`);
|
|
485
491
|
}
|
|
486
492
|
|
|
487
493
|
async function researchRun(args) {
|
|
488
494
|
const root = await sksRoot();
|
|
489
495
|
const id = await resolveMissionId(root, args[0]);
|
|
490
|
-
if (!id) throw new Error('Usage: sks research run <mission-id|latest> [--mock] [--max-cycles N]');
|
|
496
|
+
if (!id) throw new Error('Usage: sks research run <mission-id|latest> [--mock] [--max-cycles N] [--cycle-timeout-minutes N]');
|
|
491
497
|
const { dir, mission } = await loadMission(root, id);
|
|
492
498
|
const planPath = path.join(dir, 'research-plan.json');
|
|
493
499
|
if (!(await exists(planPath))) await writeResearchPlan(dir, mission.prompt || '', {});
|
|
@@ -499,32 +505,46 @@ async function researchRun(args) {
|
|
|
499
505
|
process.exitCode = 2;
|
|
500
506
|
return;
|
|
501
507
|
}
|
|
502
|
-
const maxCycles = readMaxCycles(args,
|
|
508
|
+
const maxCycles = readMaxCycles(args, RESEARCH_DEFAULT_MAX_CYCLES);
|
|
509
|
+
const cycleTimeoutMinutes = readResearchCycleTimeoutMinutes(args);
|
|
510
|
+
const cycleTimeoutMs = cycleTimeoutMinutes * 60 * 1000;
|
|
503
511
|
const mock = flag(args, '--mock');
|
|
504
|
-
await setCurrent(root, { mission_id: id, mode: 'RESEARCH', phase: 'RESEARCH_RUNNING_NO_QUESTIONS', questions_allowed: false });
|
|
505
|
-
await appendJsonlBounded(path.join(dir, 'events.jsonl'), { ts: nowIso(), type: 'research.run.started', maxCycles, mock });
|
|
512
|
+
await setCurrent(root, { mission_id: id, mode: 'RESEARCH', phase: 'RESEARCH_RUNNING_NO_QUESTIONS', questions_allowed: false, implementation_allowed: false, research_real_run_required: !mock, research_cycle_timeout_minutes: cycleTimeoutMinutes });
|
|
513
|
+
await appendJsonlBounded(path.join(dir, 'events.jsonl'), { ts: nowIso(), type: 'research.run.started', maxCycles, mock, cycleTimeoutMinutes, real_run_required: !mock });
|
|
506
514
|
if (mock) {
|
|
507
515
|
const gate = await writeMockResearchResult(dir, plan);
|
|
508
|
-
await setCurrent(root, { mission_id: id, mode: 'RESEARCH', phase: gate.passed ? 'RESEARCH_DONE' : 'RESEARCH_PAUSED', questions_allowed: true });
|
|
516
|
+
await setCurrent(root, { mission_id: id, mode: 'RESEARCH', phase: gate.passed ? 'RESEARCH_DONE' : 'RESEARCH_PAUSED', questions_allowed: true, implementation_allowed: false });
|
|
509
517
|
console.log(`Mock research done: ${id}`);
|
|
510
518
|
console.log(`Gate: ${gate.passed ? 'passed' : 'blocked'}`);
|
|
511
519
|
return;
|
|
512
520
|
}
|
|
513
521
|
const codex = await getCodexInfo();
|
|
514
522
|
if (!codex.bin) {
|
|
515
|
-
|
|
516
|
-
|
|
517
|
-
|
|
518
|
-
|
|
523
|
+
const blocker = {
|
|
524
|
+
schema_version: 1,
|
|
525
|
+
mission_id: id,
|
|
526
|
+
ts: nowIso(),
|
|
527
|
+
phase: 'RESEARCH_BLOCKED_REAL_RUN_REQUIRED',
|
|
528
|
+
reason: 'Codex CLI not found; normal Research cannot fall back to mock output.',
|
|
529
|
+
required_action: 'Install/configure the Codex CLI or set SKS_CODEX_BIN to a valid executable, then rerun sks research run without --mock.',
|
|
530
|
+
mock_policy: '--mock is allowed only for selftests and dry harness checks.',
|
|
531
|
+
implementation_allowed: false
|
|
532
|
+
};
|
|
533
|
+
await writeJsonAtomic(path.join(dir, 'research-blocker.json'), blocker);
|
|
534
|
+
await appendJsonlBounded(path.join(dir, 'events.jsonl'), { ts: blocker.ts, type: 'research.blocked.real_run_required', reason: blocker.reason, blocker: 'research-blocker.json' });
|
|
535
|
+
await setCurrent(root, { mission_id: id, mode: 'RESEARCH', phase: 'RESEARCH_BLOCKED_REAL_RUN_REQUIRED', questions_allowed: true, implementation_allowed: false, research_real_run_required: true, blocker: 'research-blocker.json' });
|
|
536
|
+
console.error('Research cannot run real sources: Codex CLI not found.');
|
|
537
|
+
console.error('Mock fallback is disabled for normal Research. Use --mock only for selftests, or install/configure Codex CLI/SKS_CODEX_BIN.');
|
|
538
|
+
process.exitCode = 2;
|
|
519
539
|
return;
|
|
520
540
|
}
|
|
521
541
|
let last = '';
|
|
522
542
|
for (let cycle = 1; cycle <= maxCycles; cycle++) {
|
|
523
543
|
const cycleDir = path.join(dir, 'research', `cycle-${cycle}`);
|
|
524
544
|
const outputFile = path.join(cycleDir, 'final.md');
|
|
525
|
-
await appendJsonlBounded(path.join(dir, 'events.jsonl'), { ts: nowIso(), type: 'research.cycle.start', cycle });
|
|
545
|
+
await appendJsonlBounded(path.join(dir, 'events.jsonl'), { ts: nowIso(), type: 'research.cycle.start', cycle, timeoutMinutes: cycleTimeoutMinutes });
|
|
526
546
|
const prompt = buildResearchPrompt({ id, mission, plan, cycle, previous: last });
|
|
527
|
-
const result = await runCodexExec({ root, prompt, outputFile, json: true, profile: 'sks-research', logDir: cycleDir, timeoutMs:
|
|
547
|
+
const result = await runCodexExec({ root, prompt, outputFile, json: true, profile: 'sks-research', logDir: cycleDir, timeoutMs: cycleTimeoutMs });
|
|
528
548
|
await writeJsonAtomic(path.join(cycleDir, 'process.json'), { code: result.code, stdout_tail: result.stdout, stderr_tail: result.stderr, stdout_bytes: result.stdoutBytes, stderr_bytes: result.stderrBytes, truncated: result.truncated, timed_out: result.timedOut });
|
|
529
549
|
last = await safeReadText(outputFile, result.stdout || result.stderr || '');
|
|
530
550
|
if (containsUserQuestion(last)) {
|
|
@@ -534,7 +554,7 @@ async function researchRun(args) {
|
|
|
534
554
|
}
|
|
535
555
|
const gate = await evaluateResearchGate(dir);
|
|
536
556
|
if (gate.passed) {
|
|
537
|
-
await setCurrent(root, { mission_id: id, mode: 'RESEARCH', phase: 'RESEARCH_DONE', questions_allowed: true });
|
|
557
|
+
await setCurrent(root, { mission_id: id, mode: 'RESEARCH', phase: 'RESEARCH_DONE', questions_allowed: true, implementation_allowed: false });
|
|
538
558
|
await appendJsonlBounded(path.join(dir, 'events.jsonl'), { ts: nowIso(), type: 'research.done', cycle });
|
|
539
559
|
await enforceRetention(root).catch(() => {});
|
|
540
560
|
console.log(`Research done: ${id}`);
|
|
@@ -542,7 +562,7 @@ async function researchRun(args) {
|
|
|
542
562
|
}
|
|
543
563
|
await appendJsonlBounded(path.join(dir, 'events.jsonl'), { ts: nowIso(), type: 'research.cycle.continue', cycle, reasons: gate.reasons });
|
|
544
564
|
}
|
|
545
|
-
await setCurrent(root, { mission_id: id, mode: 'RESEARCH', phase: 'RESEARCH_PAUSED_MAX_CYCLES', questions_allowed: true });
|
|
565
|
+
await setCurrent(root, { mission_id: id, mode: 'RESEARCH', phase: 'RESEARCH_PAUSED_MAX_CYCLES', questions_allowed: true, implementation_allowed: false });
|
|
546
566
|
console.log(`Research paused after max cycles: ${id}`);
|
|
547
567
|
}
|
|
548
568
|
|
|
@@ -558,19 +578,29 @@ async function researchStatus(args) {
|
|
|
558
578
|
const scoutLedger = await readJson(path.join(dir, 'scout-ledger.json'), null);
|
|
559
579
|
const debateLedger = await readJson(path.join(dir, 'debate-ledger.json'), null);
|
|
560
580
|
const falsificationLedger = await readJson(path.join(dir, 'falsification-ledger.json'), null);
|
|
581
|
+
const sourceSkillText = await readText(path.join(dir, RESEARCH_SOURCE_SKILL_ARTIFACT), '');
|
|
582
|
+
const geniusSummaryText = await readText(path.join(dir, RESEARCH_GENIUS_SUMMARY_ARTIFACT), '');
|
|
561
583
|
const paperText = await readText(path.join(dir, RESEARCH_PAPER_ARTIFACT), '');
|
|
562
584
|
const scoutRows = Array.isArray(scoutLedger?.scouts) ? scoutLedger.scouts : [];
|
|
585
|
+
const sourceLayerRows = Array.isArray(sourceLedger?.source_layers) ? sourceLedger.source_layers : [];
|
|
586
|
+
const sourceLayersCovered = sourceLayerRows.filter((layer) => layer.status === 'covered' && ((Array.isArray(layer.source_ids) && layer.source_ids.length) || (Array.isArray(layer.counterevidence_ids) && layer.counterevidence_ids.length))).length;
|
|
563
587
|
console.log(JSON.stringify({
|
|
564
588
|
mission,
|
|
565
589
|
state,
|
|
566
590
|
gate,
|
|
567
591
|
novelty_entries: ledger?.entries?.length ?? null,
|
|
568
592
|
source_entries: sourceLedger?.sources?.length ?? null,
|
|
593
|
+
source_layers_required: sourceLayerRows.length || gate?.metrics?.source_layers_required || gate?.source_layers_required || null,
|
|
594
|
+
source_layers_covered: gate?.metrics?.source_layers_covered ?? gate?.source_layers_covered ?? (sourceLayerRows.length ? sourceLayersCovered : null),
|
|
595
|
+
triangulation_checks: sourceLedger?.triangulation?.cross_layer_checks?.length ?? gate?.metrics?.triangulation_checks ?? gate?.triangulation_checks ?? null,
|
|
596
|
+
genius_opinion_summaries: gate?.metrics?.genius_opinion_summaries ?? gate?.genius_opinion_summaries ?? (geniusSummaryText.trim() ? countGeniusOpinionSummaries(geniusSummaryText) : null),
|
|
569
597
|
counterevidence_sources: sourceLedger?.counterevidence_sources?.length ?? null,
|
|
570
598
|
xhigh_scouts: scoutRows.length ? scoutRows.filter((scout) => scout.effort === 'xhigh').length : null,
|
|
571
599
|
eureka_moments: scoutRows.length ? scoutRows.filter((scout) => scout.eureka?.exclamation === 'Eureka!' && String(scout.eureka?.idea || '').trim()).length : null,
|
|
572
600
|
scout_findings: scoutRows.length ? scoutRows.reduce((sum, scout) => sum + (Array.isArray(scout.findings) ? scout.findings.length : 0), 0) : null,
|
|
573
601
|
debate_exchanges: debateLedger?.exchanges?.length ?? null,
|
|
602
|
+
research_source_skill_present: Boolean(sourceSkillText.trim()),
|
|
603
|
+
genius_opinion_summary_present: Boolean(geniusSummaryText.trim()),
|
|
574
604
|
paper_present: Boolean(paperText.trim()),
|
|
575
605
|
paper_sections: countResearchPaperSections(paperText),
|
|
576
606
|
falsification_cases: falsificationLedger?.cases?.length ?? null
|
|
@@ -625,11 +655,19 @@ async function safeReadText(file, fallback = '') {
|
|
|
625
655
|
try { return await fsp.readFile(file, 'utf8'); } catch { return fallback; }
|
|
626
656
|
}
|
|
627
657
|
|
|
628
|
-
function
|
|
629
|
-
const i = args.indexOf(
|
|
658
|
+
function readBoundedIntegerFlag(args, name, fallback, min, max) {
|
|
659
|
+
const i = args.indexOf(name);
|
|
630
660
|
const raw = i >= 0 && args[i + 1] ? Number(args[i + 1]) : Number(fallback);
|
|
631
|
-
if (!Number.isFinite(raw)) return Math.max(
|
|
632
|
-
return Math.max(
|
|
661
|
+
if (!Number.isFinite(raw)) return Math.max(min, Number.parseInt(fallback, 10) || min);
|
|
662
|
+
return Math.max(min, Math.min(max, Math.floor(raw)));
|
|
663
|
+
}
|
|
664
|
+
|
|
665
|
+
function readMaxCycles(args, fallback) {
|
|
666
|
+
return readBoundedIntegerFlag(args, '--max-cycles', fallback, 1, 50);
|
|
667
|
+
}
|
|
668
|
+
|
|
669
|
+
function readResearchCycleTimeoutMinutes(args) {
|
|
670
|
+
return readBoundedIntegerFlag(args, '--cycle-timeout-minutes', RESEARCH_DEFAULT_CYCLE_TIMEOUT_MINUTES, RESEARCH_MIN_CYCLE_TIMEOUT_MINUTES, RESEARCH_MAX_CYCLE_TIMEOUT_MINUTES);
|
|
633
671
|
}
|
|
634
672
|
|
|
635
673
|
export async function goalCommand(sub, args) {
|
|
@@ -1580,7 +1618,7 @@ export async function statsCommand(args) {
|
|
|
1580
1618
|
|
|
1581
1619
|
function positionalArgs(args = []) {
|
|
1582
1620
|
const out = [];
|
|
1583
|
-
const valueFlags = new Set(['--format', '--iterations', '--out', '--baseline', '--candidate', '--install-scope', '--max-cycles', '--depth', '--scope', '--transport', '--query', '--topic', '--tokens', '--timeout-ms', '--sql', '--command', '--project-ref', '--agent', '--phase', '--message', '--role', '--max-anchors', '--lines', '--intent', '--changed', '--route', '--skills', '--prompt-signature']);
|
|
1621
|
+
const valueFlags = new Set(['--format', '--iterations', '--out', '--baseline', '--candidate', '--install-scope', '--max-cycles', '--cycle-timeout-minutes', '--depth', '--scope', '--transport', '--query', '--topic', '--tokens', '--timeout-ms', '--sql', '--command', '--project-ref', '--agent', '--phase', '--message', '--role', '--max-anchors', '--lines', '--intent', '--changed', '--route', '--skills', '--prompt-signature']);
|
|
1584
1622
|
for (let i = 0; i < args.length; i++) {
|
|
1585
1623
|
const arg = String(args[i]);
|
|
1586
1624
|
if (valueFlags.has(arg)) {
|
package/src/core/fsx.mjs
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
|
|
|
8
|
-
export const PACKAGE_VERSION = '0.7.
|
|
8
|
+
export const PACKAGE_VERSION = '0.7.74';
|
|
9
9
|
export const DEFAULT_PROCESS_TAIL_BYTES = 256 * 1024;
|
|
10
10
|
export const DEFAULT_PROCESS_TIMEOUT_MS = 30 * 60 * 1000;
|
|
11
11
|
|
|
@@ -7,7 +7,7 @@ import { checkHarnessModification, harnessGuardBlockReason } from './harness-gua
|
|
|
7
7
|
import { activeRouteContext, evaluateStop, prepareRoute, promptPipelineContext as routePipelineContext, recordContext7Evidence, recordSubagentEvidence, routePrompt } from './pipeline.mjs';
|
|
8
8
|
import { classifyToolError } from './evaluation.mjs';
|
|
9
9
|
import { REQUIRED_CODEX_MODEL, isForbiddenCodexModel } from './codex-model-guard.mjs';
|
|
10
|
-
import { stripVisibleDecisionAnswerBlocks } from './routes.mjs';
|
|
10
|
+
import { dollarCommand, stripVisibleDecisionAnswerBlocks } from './routes.mjs';
|
|
11
11
|
|
|
12
12
|
const TEAM_DIGEST_MAX_EVENTS = 4;
|
|
13
13
|
const TEAM_DIGEST_MESSAGE_CHARS = 180;
|
|
@@ -77,13 +77,6 @@ function toolFailed(payload = {}) {
|
|
|
77
77
|
return false;
|
|
78
78
|
}
|
|
79
79
|
|
|
80
|
-
function dollarCommand(prompt) {
|
|
81
|
-
const text = String(prompt || '').trim();
|
|
82
|
-
const match = text.match(/^\$([A-Za-z][A-Za-z0-9_-]*)(?:\s|:|$)/)
|
|
83
|
-
|| text.match(/^\[\$([A-Za-z][A-Za-z0-9_-]*)\]\([^)]+\)(?:\s|:|$)/);
|
|
84
|
-
return match ? match[1].toUpperCase() : null;
|
|
85
|
-
}
|
|
86
|
-
|
|
87
80
|
function looksLikeUpdateDecline(prompt) {
|
|
88
81
|
return /^(no|nope|skip|later|not now|don't|dont|아니|아니요|안해|안 함|나중에|건너뛰|스킵)/i.test(String(prompt || '').trim());
|
|
89
82
|
}
|