sneakoscope 3.1.3 → 3.1.5
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +1 -1
- package/crates/sks-core/Cargo.lock +1 -1
- package/crates/sks-core/Cargo.toml +1 -1
- package/crates/sks-core/src/main.rs +1 -1
- package/dist/.sks-build-stamp.json +4 -4
- package/dist/bin/sks.js +1 -1
- package/dist/cli/install-helpers.js +56 -4
- package/dist/commands/codex-app.js +45 -1
- package/dist/commands/codex-lb.js +12 -9
- package/dist/commands/doctor.js +44 -1
- package/dist/core/codex-app/codex-agent-role-sync.js +119 -0
- package/dist/core/codex-app/codex-agent-type-probe.js +202 -0
- package/dist/core/codex-app/codex-app-execution-profile.js +39 -0
- package/dist/core/codex-app/codex-app-fast-ui-repair.js +7 -4
- package/dist/core/codex-app/codex-app-harness-matrix.js +127 -0
- package/dist/core/codex-app/codex-app-types.js +21 -0
- package/dist/core/codex-app/codex-hook-approval-probe.js +188 -0
- package/dist/core/codex-app/codex-hook-lifecycle.js +61 -0
- package/dist/core/codex-app/codex-init-deep.js +180 -0
- package/dist/core/codex-app/codex-skill-sync.js +164 -0
- package/dist/core/codex-app/lazycodex-analysis.js +72 -0
- package/dist/core/codex-app/lazycodex-interop-policy.js +60 -0
- package/dist/core/codex-app/lazycodex-live-analyzer.js +98 -0
- package/dist/core/commands/loop-command.js +11 -0
- package/dist/core/commands/mad-sks-command.js +113 -17
- package/dist/core/commands/qa-loop-command.js +3 -2
- package/dist/core/commands/research-command.js +2 -2
- package/dist/core/doctor/doctor-readiness-matrix.js +7 -0
- package/dist/core/doctor/doctor-zellij-repair.js +40 -0
- package/dist/core/feature-fixtures.js +1 -0
- package/dist/core/feature-registry.js +4 -1
- package/dist/core/fsx.js +1 -1
- package/dist/core/hooks-runtime.js +13 -0
- package/dist/core/init.js +4 -1
- package/dist/core/loops/loop-continuation-enforcer.js +40 -0
- package/dist/core/loops/loop-planner.js +29 -3
- package/dist/core/loops/loop-worker-runtime.js +27 -7
- package/dist/core/naruto/naruto-loop-worker-router.js +11 -2
- package/dist/core/qa-loop.js +39 -4
- package/dist/core/research/research-cycle-runner.js +1 -0
- package/dist/core/research/research-stage-runner.js +9 -2
- package/dist/core/research.js +35 -1
- package/dist/core/version.js +1 -1
- package/dist/core/zellij/homebrew-policy.js +44 -0
- package/dist/core/zellij/zellij-capability.js +32 -3
- package/dist/core/zellij/zellij-self-heal-types.js +45 -0
- package/dist/core/zellij/zellij-self-heal.js +414 -0
- package/dist/core/zellij/zellij-update.js +39 -6
- package/dist/scripts/sks-3-1-4-directive-check-lib.js +241 -0
- package/dist/scripts/sks-3-1-5-directive-check-lib.js +347 -0
- package/package.json +52 -2
|
@@ -1,7 +1,9 @@
|
|
|
1
|
-
export function routeNarutoLoopWorker(node, role) {
|
|
1
|
+
export function routeNarutoLoopWorker(node, role, profile) {
|
|
2
2
|
const domain = node.loop_id.replace(/^loop-/, '');
|
|
3
3
|
const roles = roleLabels(domain);
|
|
4
4
|
const gates = [...node.gates.triage, ...node.gates.local, ...node.gates.checker, ...node.gates.integration, ...node.gates.final];
|
|
5
|
+
const roleName = role === 'maker' ? roles.maker : roles.checker;
|
|
6
|
+
const strategy = profile?.agent_role_strategy || 'message-role';
|
|
5
7
|
return {
|
|
6
8
|
schema: 'sks.naruto-loop-worker-route.v1',
|
|
7
9
|
loop_id: node.loop_id,
|
|
@@ -9,7 +11,10 @@ export function routeNarutoLoopWorker(node, role) {
|
|
|
9
11
|
checker_role: roles.checker,
|
|
10
12
|
prompt: [
|
|
11
13
|
`loop purpose: ${node.purpose}`,
|
|
12
|
-
`role: ${
|
|
14
|
+
`role: ${roleName}`,
|
|
15
|
+
`agent role strategy: ${strategy}`,
|
|
16
|
+
`agent_type: ${strategy === 'agent_type' ? roleName.replace(/\s+/g, '-') : '-'}`,
|
|
17
|
+
`message role prefix: ${strategy === 'message-role' ? `Role: ${roleName}.` : '-'}`,
|
|
13
18
|
`owner files: ${node.owner_scope.files.join(', ') || '-'}`,
|
|
14
19
|
`owner directories: ${node.owner_scope.directories.join(', ') || '-'}`,
|
|
15
20
|
`gates: ${gates.join(', ') || '-'}`,
|
|
@@ -21,6 +26,10 @@ export function routeNarutoLoopWorker(node, role) {
|
|
|
21
26
|
allowed_files: node.owner_scope.files,
|
|
22
27
|
allowed_directories: node.owner_scope.directories,
|
|
23
28
|
gates,
|
|
29
|
+
agent_role_strategy: strategy,
|
|
30
|
+
agent_type: strategy === 'agent_type' ? roleName.replace(/\s+/g, '-') : null,
|
|
31
|
+
message_role_prefix: strategy === 'message-role' ? `Role: ${roleName}.` : null,
|
|
32
|
+
execution_profile_artifact: profile?.artifact_path || null,
|
|
24
33
|
mutation_outside_owner_scope_allowed: false
|
|
25
34
|
};
|
|
26
35
|
}
|
package/dist/core/qa-loop.js
CHANGED
|
@@ -2,6 +2,7 @@ import path from 'node:path';
|
|
|
2
2
|
import { exists, nowIso, readJson, readText, writeJsonAtomic, writeTextAtomic, PACKAGE_VERSION } from './fsx.js';
|
|
3
3
|
import { CODEX_WEB_VERIFICATION_EVIDENCE_SOURCE, CODEX_WEB_VERIFICATION_POLICY, evidenceMentionsForbiddenBrowserAutomation, evidenceMentionsForbiddenWebComputerUseEvidence } from './routes.js';
|
|
4
4
|
import { appendAgentLedgerEvent, initializeAgentCentralLedger } from './agents/agent-central-ledger.js';
|
|
5
|
+
import { resolveCodexAppExecutionProfile } from './codex-app/codex-app-execution-profile.js';
|
|
5
6
|
export const QA_LOOP_ROUTE = 'QALoop';
|
|
6
7
|
const QA_REPORT_SUFFIX = 'qa-report.md';
|
|
7
8
|
const UI_CHROME_EXTENSION_FIRST_ACK = 'use_codex_chrome_extension_first_no_computer_use_for_web_ui_or_mark_unverified';
|
|
@@ -320,6 +321,10 @@ export function defaultQaGate(contract = {}, opts = {}) {
|
|
|
320
321
|
image_artifact_path_contract_present: false,
|
|
321
322
|
image_artifact_path_contract_artifact: null,
|
|
322
323
|
image_artifact_path_contract_blockers: [],
|
|
324
|
+
codex_app_execution_profile: opts.executionProfile ? compactExecutionProfile(opts.executionProfile) : null,
|
|
325
|
+
codex_app_execution_profile_artifact: opts.executionProfile ? 'qa-loop/execution-profile.json' : null,
|
|
326
|
+
codex_app_hooks_approval_required: opts.executionProfile?.hooks_approval_required === true,
|
|
327
|
+
codex_app_agent_role_strategy: opts.executionProfile?.agent_role_strategy || null,
|
|
323
328
|
api_e2e_required: apiRequired,
|
|
324
329
|
unsafe_external_side_effects: false,
|
|
325
330
|
corrective_loop_enabled: corrective,
|
|
@@ -338,16 +343,21 @@ export async function writeQaLoopArtifacts(dir, mission, contract) {
|
|
|
338
343
|
const a = contract.answers || {};
|
|
339
344
|
const checklist = qaChecklist(a);
|
|
340
345
|
const reportFile = qaReportFilename();
|
|
346
|
+
const root = missionRootFromDir(dir);
|
|
347
|
+
const executionProfile = root ? await resolveCodexAppExecutionProfile({ root }).catch(() => null) : null;
|
|
348
|
+
if (executionProfile)
|
|
349
|
+
await writeJsonAtomic(path.join(dir, 'qa-loop', 'execution-profile.json'), executionProfile).catch(() => undefined);
|
|
341
350
|
await writeJsonAtomic(path.join(dir, 'qa-ledger.json'), {
|
|
342
351
|
schema_version: 1,
|
|
343
352
|
generated_at: nowIso(),
|
|
344
353
|
mission_id: mission.id,
|
|
345
354
|
qa_report_file: reportFile,
|
|
355
|
+
codex_app_execution_profile: executionProfile ? compactExecutionProfile(executionProfile) : null,
|
|
346
356
|
target: { scope: a.QA_SCOPE, environment: a.TARGET_ENVIRONMENT, base_url: a.TARGET_BASE_URL, api_base_url: a.API_BASE_URL },
|
|
347
357
|
safety: { mutation_policy: a.QA_MUTATION_POLICY, deployed_destructive_tests_allowed: 'never', credentials: 'temp_only_never_saved', ui_evidence: 'codex_chrome_extension_first_required_for_web_ui_e2e' },
|
|
348
358
|
checklist
|
|
349
359
|
});
|
|
350
|
-
await writeJsonAtomic(path.join(dir, 'qa-gate.json'), defaultQaGate(contract, { reportFile }));
|
|
360
|
+
await writeJsonAtomic(path.join(dir, 'qa-gate.json'), defaultQaGate(contract, { reportFile, executionProfile }));
|
|
351
361
|
await writeTextAtomic(path.join(dir, reportFile), qaReportTemplate(mission, contract, checklist));
|
|
352
362
|
return { checklist_count: checklist.length, report_file: reportFile };
|
|
353
363
|
}
|
|
@@ -432,6 +442,10 @@ export async function writeMockQaResult(dir, mission, contract) {
|
|
|
432
442
|
image_artifact_path_contract_present: previousGate.image_artifact_path_contract_present === true,
|
|
433
443
|
image_artifact_path_contract_artifact: previousGate.image_artifact_path_contract_artifact || null,
|
|
434
444
|
image_artifact_path_contract_blockers: previousGate.image_artifact_path_contract_blockers || [],
|
|
445
|
+
codex_app_execution_profile: previousGate.codex_app_execution_profile || null,
|
|
446
|
+
codex_app_execution_profile_artifact: previousGate.codex_app_execution_profile_artifact || null,
|
|
447
|
+
codex_app_hooks_approval_required: previousGate.codex_app_hooks_approval_required === true,
|
|
448
|
+
codex_app_agent_role_strategy: previousGate.codex_app_agent_role_strategy || null,
|
|
435
449
|
blockers: previousGate.blockers || [],
|
|
436
450
|
passed: !uiRequired,
|
|
437
451
|
qa_report_written: true,
|
|
@@ -453,7 +467,7 @@ export async function writeMockQaResult(dir, mission, contract) {
|
|
|
453
467
|
});
|
|
454
468
|
return evaluateQaGate(dir);
|
|
455
469
|
}
|
|
456
|
-
export function buildQaLoopPrompt({ id, mission, contract, cycle, previous, reportFile, imagePathContract, appHandoff }) {
|
|
470
|
+
export function buildQaLoopPrompt({ id, mission, contract, cycle, previous, reportFile, imagePathContract, appHandoff, executionProfile }) {
|
|
457
471
|
const report = reportFile && isQaReportFilename(reportFile) ? reportFile : 'the date/version-prefixed report named by qa-gate.json.qa_report_file';
|
|
458
472
|
const imageContractText = imagePathContract
|
|
459
473
|
? `\nIMAGE PATH CONTRACT:\n${JSON.stringify(imagePathContract, null, 2)}\nUse model_visible_path values for follow-up image edits; do not invent generated image paths.\n`
|
|
@@ -461,6 +475,9 @@ export function buildQaLoopPrompt({ id, mission, contract, cycle, previous, repo
|
|
|
461
475
|
const appHandoffText = appHandoff
|
|
462
476
|
? `\nCODEX DESKTOP /app HANDOFF:\n${JSON.stringify(appHandoff, null, 2)}\nThis is desktop-app review status only and is not web UI evidence.\n`
|
|
463
477
|
: '';
|
|
478
|
+
const executionProfileText = executionProfile
|
|
479
|
+
? `\nCODEX APP EXECUTION PROFILE:\n${JSON.stringify(compactExecutionProfile(executionProfile), null, 2)}\nUse this routing profile for agent role strategy and app/headless assumptions.\n`
|
|
480
|
+
: '';
|
|
464
481
|
return `SKS QA-LOOP
|
|
465
482
|
MISSION: ${id}
|
|
466
483
|
TASK: ${mission.prompt}
|
|
@@ -473,7 +490,7 @@ GATE: passed=false while unresolved_findings or unresolved_fixable_findings > 0,
|
|
|
473
490
|
ARTIFACTS: update qa-ledger.json, ${report}, qa-gate.json, and qa-loop/cycle-${cycle}/.
|
|
474
491
|
CONTRACT:
|
|
475
492
|
${JSON.stringify(contract, null, 2)}
|
|
476
|
-
${imageContractText}${appHandoffText}
|
|
493
|
+
${imageContractText}${appHandoffText}${executionProfileText}
|
|
477
494
|
Previous tail:
|
|
478
495
|
${String(previous || '').slice(-2500)}
|
|
479
496
|
`;
|
|
@@ -486,7 +503,8 @@ export async function qaStatus(dir) {
|
|
|
486
503
|
const imagePathContract = await readJson(path.join(dir, 'qa-loop', 'image-artifact-path-contract.json'), null);
|
|
487
504
|
const reportFile = qaReportFileFromGate(gate?.gate || gate || {}) || ledger?.qa_report_file || null;
|
|
488
505
|
const report = reportFile && isQaReportFilename(reportFile) ? await readText(path.join(dir, reportFile), '') : '';
|
|
489
|
-
|
|
506
|
+
const executionProfile = await readJson(path.join(dir, 'qa-loop', 'execution-profile.json'), null);
|
|
507
|
+
return { gate, checklist_count: ledger?.checklist?.length ?? null, report_file: reportFile, report_written: Boolean(report.trim()), desktop_app_handoff: appHandoff, desktop_app_confirmation: appConfirmation, desktop_review_complete: appConfirmation?.verdict === 'pass', image_path_contract: imagePathContract, codex_app_execution_profile: executionProfile };
|
|
490
508
|
}
|
|
491
509
|
function qaChecklist(a) {
|
|
492
510
|
const cases = [
|
|
@@ -504,6 +522,23 @@ function qaChecklist(a) {
|
|
|
504
522
|
cases.push(['report.evidence', 'Record pass/fail/blocked/skipped with evidence.'], ['report.corrective_loop', 'Record fixes, rechecks, unresolved findings, deferred blockers.'], ['report.honest', 'Run Honest Mode.']);
|
|
505
523
|
return cases.map(([id, title]) => ({ id, title, status: 'pending', evidence: [] }));
|
|
506
524
|
}
|
|
525
|
+
function missionRootFromDir(dir) {
|
|
526
|
+
const normalized = path.resolve(String(dir || ''));
|
|
527
|
+
const marker = `${path.sep}.sneakoscope${path.sep}missions${path.sep}`;
|
|
528
|
+
const idx = normalized.indexOf(marker);
|
|
529
|
+
return idx > 0 ? normalized.slice(0, idx) : null;
|
|
530
|
+
}
|
|
531
|
+
function compactExecutionProfile(profile) {
|
|
532
|
+
return profile ? {
|
|
533
|
+
mode: profile.mode || 'unknown',
|
|
534
|
+
agent_role_strategy: profile.agent_role_strategy || 'message-role',
|
|
535
|
+
hooks_approval_required: profile.hooks_approval_required === true,
|
|
536
|
+
hook_approval_state: profile.hook_approval_state || 'unknown',
|
|
537
|
+
app_handoff_ready: profile.app_handoff_ready === true,
|
|
538
|
+
plugin_mcp_inventory_ready: profile.plugin_mcp_inventory_ready === true,
|
|
539
|
+
artifact_path: profile.artifact_path || '.sneakoscope/reports/codex-app-execution-profile.json'
|
|
540
|
+
} : null;
|
|
541
|
+
}
|
|
507
542
|
function qaReportTemplate(mission, contract, checklist) {
|
|
508
543
|
const a = contract.answers || {};
|
|
509
544
|
return `# QA-LOOP Report\n\nMission: ${mission.id}\nTarget: ${a.TARGET_BASE_URL || 'unset'}\nScope: ${a.QA_SCOPE || 'unset'}\nEnvironment: ${a.TARGET_ENVIRONMENT || 'unset'}\n\n## Safety\n\n- Deployed destructive tests: never\n- Credentials: temp-only, never saved\n- UI evidence: ${CODEX_WEB_VERIFICATION_POLICY}\n\n## Checklist\n\n${checklist.map((item) => `- [ ] ${item.id}: ${item.title}`).join('\n')}\n\n## Findings\n\nTBD\n\n## Corrections And Rechecks\n\nTBD\n\n## Honest Mode\n\nTBD\n`;
|
|
@@ -63,6 +63,7 @@ export async function runResearchCycle(inputOrDir, legacyGraph = null, legacyOpt
|
|
|
63
63
|
blockers: [...new Set(blockers)],
|
|
64
64
|
stages: stageResults.map((stage) => stage.stage_id),
|
|
65
65
|
stage_results: stageResults,
|
|
66
|
+
codex_app_execution_profile: input.plan?.codex_app_execution_profile || null,
|
|
66
67
|
parallelism: {
|
|
67
68
|
max_parallel_stages: maxParallel,
|
|
68
69
|
max_observed_parallel: maxObservedParallel,
|
|
@@ -91,7 +91,7 @@ async function runSourceShardStage(input, startedAt) {
|
|
|
91
91
|
const validation = validateResearchSourceShardOutput(output);
|
|
92
92
|
await writeJsonAtomic(path.join(input.dir, artifact), output);
|
|
93
93
|
await writeTextAtomic(path.join(input.dir, 'research', `cycle-${input.cycle}`, 'source-notes', `${layer.id}.md`), `# Source shard: ${layer.label}\n\n${output.sources.map((source) => `- ${source.id}: ${source.title}`).join('\n')}\n`);
|
|
94
|
-
return baseResult(input, startedAt, 'source_shard', validation.ok ? 'passed' : 'blocked', [artifact, `research/cycle-${input.cycle}/source-notes/${layer.id}.md`], validation.blockers, { layer_id: layer.id, source_count: output.sources.length });
|
|
94
|
+
return baseResult(input, startedAt, 'source_shard', validation.ok ? 'passed' : 'blocked', [artifact, `research/cycle-${input.cycle}/source-notes/${layer.id}.md`], validation.blockers, { layer_id: layer.id, source_count: output.sources.length, source_tool_route: researchSourceToolRoute(input.plan) });
|
|
95
95
|
}
|
|
96
96
|
const codex = await runResearchCodexStage({
|
|
97
97
|
root: input.root,
|
|
@@ -333,9 +333,16 @@ function baseResult(input, startedAt, stageKind, status, outputArtifacts, blocke
|
|
|
333
333
|
backend: input.backend,
|
|
334
334
|
worker_result_path: typeof metrics.worker_result_path === 'string' ? metrics.worker_result_path : null,
|
|
335
335
|
blockers: [...new Set(blockers.map(String).filter(Boolean))],
|
|
336
|
-
metrics
|
|
336
|
+
metrics: {
|
|
337
|
+
...metrics,
|
|
338
|
+
codex_app_execution_profile: input.plan?.codex_app_execution_profile || null,
|
|
339
|
+
source_tool_route: metrics.source_tool_route || researchSourceToolRoute(input.plan)
|
|
340
|
+
}
|
|
337
341
|
};
|
|
338
342
|
}
|
|
343
|
+
function researchSourceToolRoute(plan) {
|
|
344
|
+
return plan?.web_research_policy?.source_tool_routing?.mode || (plan?.codex_app_execution_profile?.plugin_mcp_inventory_ready ? 'plugin-mcp-inventory-first' : 'codex-cli-or-web-fallback');
|
|
345
|
+
}
|
|
339
346
|
async function buildResearchGateSeed(dir, plan) {
|
|
340
347
|
const sourceLedger = await readJson(path.join(dir, 'source-ledger.json'), null);
|
|
341
348
|
const claimMatrix = await readJson(path.join(dir, 'claim-evidence-matrix.json'), null);
|
package/dist/core/research.js
CHANGED
|
@@ -17,6 +17,7 @@ import { writeResearchHandoffArtifacts } from './research/research-handoff.js';
|
|
|
17
17
|
import { RESEARCH_WORK_GRAPH_ARTIFACT, writeResearchWorkGraph } from './research/research-work-graph.js';
|
|
18
18
|
import { researchPromptContractText, validateResearchPromptContract } from './research/research-prompt-contract.js';
|
|
19
19
|
import { buildRealisticResearchPaper, buildRealisticResearchReport } from './research/research-realistic-report.js';
|
|
20
|
+
import { resolveCodexAppExecutionProfile } from './codex-app/codex-app-execution-profile.js';
|
|
20
21
|
export const RESEARCH_PAPER_ARTIFACT = 'research-paper.md';
|
|
21
22
|
export const RESEARCH_SOURCE_SKILL_ARTIFACT = 'research-source-skill.md';
|
|
22
23
|
export const RESEARCH_GENIUS_SUMMARY_ARTIFACT = 'genius-opinion-summary.md';
|
|
@@ -252,6 +253,7 @@ export function createResearchPlan(prompt, opts = {}) {
|
|
|
252
253
|
const createdAt = nowIso();
|
|
253
254
|
const paperArtifact = researchPaperArtifactName(prompt, createdAt, opts);
|
|
254
255
|
const nativeAgentPlan = researchNativeAgentPlan(prompt, { paperArtifact, missionId: opts.missionId });
|
|
256
|
+
const executionProfile = opts.executionProfile || null;
|
|
255
257
|
return {
|
|
256
258
|
schema_version: 1,
|
|
257
259
|
mission_id: opts.missionId || null,
|
|
@@ -262,6 +264,7 @@ export function createResearchPlan(prompt, opts = {}) {
|
|
|
262
264
|
paper_artifact: paperArtifact,
|
|
263
265
|
quality_contract: DEFAULT_RESEARCH_QUALITY_CONTRACT,
|
|
264
266
|
native_agent_plan: nativeAgentPlan,
|
|
267
|
+
codex_app_execution_profile: executionProfile ? compactExecutionProfile(executionProfile) : null,
|
|
265
268
|
agent_sessions: nativeAgentPlan.personas,
|
|
266
269
|
agent_batches: nativeAgentPlan.batches,
|
|
267
270
|
autoresearch_cycle_policy: nativeAgentPlan.autoresearch_cycle_policy,
|
|
@@ -318,6 +321,12 @@ export function createResearchPlan(prompt, opts = {}) {
|
|
|
318
321
|
web_research_policy: {
|
|
319
322
|
mode: 'layered_source_retrieval_and_triangulation',
|
|
320
323
|
requirement: 'Use every safely available public web/source route before synthesis, separated into source layers so the final claim is not dominated by one corpus or platform.',
|
|
324
|
+
source_tool_routing: {
|
|
325
|
+
mode: executionProfile?.plugin_mcp_inventory_ready ? 'plugin-mcp-inventory-first' : 'codex-cli-or-web-fallback',
|
|
326
|
+
plugin_mcp_inventory_ready: executionProfile?.plugin_mcp_inventory_ready === true,
|
|
327
|
+
execution_profile_artifact: executionProfile?.artifact_path || '.sneakoscope/reports/codex-app-execution-profile.json',
|
|
328
|
+
rule: 'Prefer verified plugin/MCP inventory when available; otherwise record source-tool blockers instead of assuming live search coverage.'
|
|
329
|
+
},
|
|
321
330
|
query_sets: [
|
|
322
331
|
'first-principles and theory sources',
|
|
323
332
|
'plain-language explanations and empirical examples',
|
|
@@ -417,6 +426,9 @@ export function researchPlanMarkdown(plan) {
|
|
|
417
426
|
lines.push(`Depth: ${plan.depth}`);
|
|
418
427
|
lines.push(`Methodology: ${plan.methodology}`);
|
|
419
428
|
lines.push(`Research paper: ${researchPaperArtifactForPlan(plan)}`);
|
|
429
|
+
if (plan.codex_app_execution_profile) {
|
|
430
|
+
lines.push(`Execution profile: ${plan.codex_app_execution_profile.mode}; agent role strategy ${plan.codex_app_execution_profile.agent_role_strategy}`);
|
|
431
|
+
}
|
|
420
432
|
if (plan.execution_policy) {
|
|
421
433
|
lines.push(`Execution: ${plan.execution_policy.normal_run}; default cycle timeout ${plan.execution_policy.default_cycle_timeout_minutes} minutes`);
|
|
422
434
|
if (plan.execution_policy.default_max_cycles)
|
|
@@ -468,6 +480,8 @@ export function researchPlanMarkdown(plan) {
|
|
|
468
480
|
lines.push('## Web Research Policy');
|
|
469
481
|
lines.push(`Mode: ${plan.web_research_policy.mode}`);
|
|
470
482
|
lines.push(`Requirement: ${plan.web_research_policy.requirement}`);
|
|
483
|
+
if (plan.web_research_policy.source_tool_routing)
|
|
484
|
+
lines.push(`Source tool routing: ${plan.web_research_policy.source_tool_routing.mode}`);
|
|
471
485
|
for (const querySet of plan.web_research_policy.query_sets || [])
|
|
472
486
|
lines.push(`- query set: ${querySet}`);
|
|
473
487
|
if (plan.web_research_policy.skill_creator?.artifact)
|
|
@@ -537,7 +551,9 @@ export function countGeniusOpinionSummaries(text = '') {
|
|
|
537
551
|
}).length;
|
|
538
552
|
}
|
|
539
553
|
export async function writeResearchPlan(dir, prompt, opts = {}) {
|
|
540
|
-
const
|
|
554
|
+
const root = opts.root || missionRootFromDir(String(dir || ''));
|
|
555
|
+
const executionProfile = opts.executionProfile || (root ? await resolveCodexAppExecutionProfile({ root }).catch(() => null) : null);
|
|
556
|
+
const plan = createResearchPlan(prompt, { ...opts, executionProfile });
|
|
541
557
|
const noveltyLedger = {
|
|
542
558
|
schema_version: 1,
|
|
543
559
|
entries: [],
|
|
@@ -553,6 +569,8 @@ export async function writeResearchPlan(dir, prompt, opts = {}) {
|
|
|
553
569
|
const experimentPlan = defaultExperimentPlan(plan);
|
|
554
570
|
const replicationPack = defaultReplicationPack(plan);
|
|
555
571
|
await writeJsonAtomic(path.join(dir, 'research-plan.json'), plan);
|
|
572
|
+
if (executionProfile)
|
|
573
|
+
await writeJsonAtomic(path.join(dir, 'research', 'execution-profile.json'), executionProfile).catch(() => undefined);
|
|
556
574
|
await writeTextAtomic(path.join(dir, 'research-plan.md'), researchPlanMarkdown(plan));
|
|
557
575
|
await writeTextAtomic(path.join(dir, RESEARCH_SOURCE_SKILL_ARTIFACT), researchSourceSkillMarkdown(plan));
|
|
558
576
|
await writeResearchQualityContract(dir, plan.quality_contract);
|
|
@@ -575,6 +593,22 @@ export async function writeResearchPlan(dir, prompt, opts = {}) {
|
|
|
575
593
|
await appendJsonlBounded(path.join(dir, 'events.jsonl'), { ts: nowIso(), type: 'research.plan.created', depth: plan.depth });
|
|
576
594
|
return plan;
|
|
577
595
|
}
|
|
596
|
+
function missionRootFromDir(dir) {
|
|
597
|
+
const normalized = path.resolve(String(dir || ''));
|
|
598
|
+
const marker = `${path.sep}.sneakoscope${path.sep}missions${path.sep}`;
|
|
599
|
+
const idx = normalized.indexOf(marker);
|
|
600
|
+
return idx > 0 ? normalized.slice(0, idx) : null;
|
|
601
|
+
}
|
|
602
|
+
function compactExecutionProfile(profile) {
|
|
603
|
+
return profile ? {
|
|
604
|
+
mode: profile.mode || 'unknown',
|
|
605
|
+
agent_role_strategy: profile.agent_role_strategy || 'message-role',
|
|
606
|
+
hooks_approval_required: profile.hooks_approval_required === true,
|
|
607
|
+
hook_approval_state: profile.hook_approval_state || 'unknown',
|
|
608
|
+
plugin_mcp_inventory_ready: profile.plugin_mcp_inventory_ready === true,
|
|
609
|
+
artifact_path: profile.artifact_path || '.sneakoscope/reports/codex-app-execution-profile.json'
|
|
610
|
+
} : null;
|
|
611
|
+
}
|
|
578
612
|
export async function writeResearchNativeAgentLedger(dir, plan, opts = {}) {
|
|
579
613
|
const missionId = opts.missionId || plan?.mission_id;
|
|
580
614
|
if (!missionId)
|
package/dist/core/version.js
CHANGED
|
@@ -1,2 +1,2 @@
|
|
|
1
|
-
export const PACKAGE_VERSION = '3.1.
|
|
1
|
+
export const PACKAGE_VERSION = '3.1.5';
|
|
2
2
|
//# sourceMappingURL=version.js.map
|
|
@@ -0,0 +1,44 @@
|
|
|
1
|
+
import readline from 'node:readline';
|
|
2
|
+
export const HOMEBREW_INSTALL_COMMAND = '/bin/bash -c "$(curl -fsSL https://raw.githubusercontent.com/Homebrew/install/HEAD/install.sh)"';
|
|
3
|
+
export function resolveHomebrewInstallPolicy(input = {}) {
|
|
4
|
+
const args = (input.args || []).map(String);
|
|
5
|
+
const env = input.env || process.env;
|
|
6
|
+
const hasFlag = input.installHomebrew === true || args.includes('--install-homebrew');
|
|
7
|
+
const hasYes = input.autoApprove === true || args.includes('--yes') || args.includes('-y');
|
|
8
|
+
const envAllowed = env.SKS_ALLOW_HOMEBREW_INSTALL === '1';
|
|
9
|
+
const interactiveAccepted = input.interactiveAccepted === true;
|
|
10
|
+
const allowed = envAllowed || interactiveAccepted || (hasFlag && hasYes);
|
|
11
|
+
const source = envAllowed ? 'env'
|
|
12
|
+
: interactiveAccepted ? 'interactive_confirmed'
|
|
13
|
+
: hasFlag && hasYes ? 'yes_install_homebrew'
|
|
14
|
+
: hasFlag ? 'flag'
|
|
15
|
+
: 'not_allowed';
|
|
16
|
+
return {
|
|
17
|
+
schema: 'sks.homebrew-policy.v1',
|
|
18
|
+
allowed,
|
|
19
|
+
source,
|
|
20
|
+
install_command: HOMEBREW_INSTALL_COMMAND,
|
|
21
|
+
blockers: allowed ? [] : ['homebrew_install_requires_explicit_approval']
|
|
22
|
+
};
|
|
23
|
+
}
|
|
24
|
+
export function homebrewMissingDoctorMessage() {
|
|
25
|
+
return [
|
|
26
|
+
'Zellij repair: Homebrew missing. Run:',
|
|
27
|
+
' sks doctor --fix --install-homebrew --yes',
|
|
28
|
+
'or install Homebrew manually, then:',
|
|
29
|
+
' sks doctor --fix --yes'
|
|
30
|
+
].join('\n');
|
|
31
|
+
}
|
|
32
|
+
export async function askHomebrewInstallAllowed(question = 'Homebrew is missing. Install Homebrew now? [y/N] ') {
|
|
33
|
+
if (!(process.stdin.isTTY && process.stdout.isTTY))
|
|
34
|
+
return false;
|
|
35
|
+
const rl = readline.createInterface({ input: process.stdin, output: process.stdout });
|
|
36
|
+
try {
|
|
37
|
+
const answer = await new Promise((resolve) => rl.question(question, resolve));
|
|
38
|
+
return /^(y|yes|예|네|응)$/i.test(String(answer || '').trim());
|
|
39
|
+
}
|
|
40
|
+
finally {
|
|
41
|
+
rl.close();
|
|
42
|
+
}
|
|
43
|
+
}
|
|
44
|
+
//# sourceMappingURL=homebrew-policy.js.map
|
|
@@ -30,7 +30,10 @@ export function resolveZellijStackedPaneCapability(input = {}) {
|
|
|
30
30
|
};
|
|
31
31
|
}
|
|
32
32
|
export async function checkZellijStackedPaneCapability(opts = {}) {
|
|
33
|
-
const
|
|
33
|
+
const runOpts = { optional: true, timeoutMs: 5000 };
|
|
34
|
+
if (opts.env !== undefined)
|
|
35
|
+
runOpts.env = opts.env;
|
|
36
|
+
const versionRun = await runZellij(['--version'], runOpts);
|
|
34
37
|
const versionText = `${versionRun.stdout_tail}\n${versionRun.stderr_tail}`.trim();
|
|
35
38
|
const report = resolveZellijStackedPaneCapability({
|
|
36
39
|
ok: versionRun.ok,
|
|
@@ -45,8 +48,34 @@ export async function checkZellijStackedPaneCapability(opts = {}) {
|
|
|
45
48
|
return report;
|
|
46
49
|
}
|
|
47
50
|
export async function checkZellijCapability(opts = {}) {
|
|
48
|
-
const
|
|
49
|
-
const
|
|
51
|
+
const env = opts.env || process.env;
|
|
52
|
+
const requireZellij = opts.require === true || env.SKS_REQUIRE_ZELLIJ === '1';
|
|
53
|
+
if (env.SKS_ZELLIJ_CAPABILITY_FAKE_STATUS) {
|
|
54
|
+
const fakeStatus = String(env.SKS_ZELLIJ_CAPABILITY_FAKE_STATUS);
|
|
55
|
+
const status = fakeStatus === 'ok' || fakeStatus === 'missing' || fakeStatus === 'too_old' || fakeStatus === 'blocked' ? fakeStatus : 'blocked';
|
|
56
|
+
const report = {
|
|
57
|
+
schema: ZELLIJ_CAPABILITY_SCHEMA,
|
|
58
|
+
generated_at: nowIso(),
|
|
59
|
+
ok: status === 'ok',
|
|
60
|
+
status,
|
|
61
|
+
integration_optional: !requireZellij,
|
|
62
|
+
require_zellij: requireZellij,
|
|
63
|
+
min_version: ZELLIJ_MIN_VERSION,
|
|
64
|
+
version: status === 'missing' ? null : String(env.SKS_ZELLIJ_CAPABILITY_FAKE_VERSION || '0.40.0'),
|
|
65
|
+
bin: 'zellij',
|
|
66
|
+
command: ['zellij', '--version'],
|
|
67
|
+
docs_evidence: [],
|
|
68
|
+
blockers: requireZellij && status !== 'ok' ? [`zellij_${status}_required`] : [],
|
|
69
|
+
warnings: !requireZellij && status !== 'ok' ? [`zellij_${status}_optional_integration`] : [],
|
|
70
|
+
operator_actions: status === 'ok' ? [] : ['Install Zellij. On macOS: `brew install zellij`.']
|
|
71
|
+
};
|
|
72
|
+
if (opts.writeReport !== false) {
|
|
73
|
+
const root = opts.root || process.cwd();
|
|
74
|
+
await writeJsonAtomic(path.join(root, '.sneakoscope', 'reports', 'zellij-capability.json'), report);
|
|
75
|
+
}
|
|
76
|
+
return report;
|
|
77
|
+
}
|
|
78
|
+
const versionRun = await runZellij(['--version'], { optional: !requireZellij, timeoutMs: 5000, env });
|
|
50
79
|
const versionText = `${versionRun.stdout_tail}\n${versionRun.stderr_tail}`.trim();
|
|
51
80
|
const version = parseZellijVersionText(versionText);
|
|
52
81
|
const missing = !versionRun.ok && versionRun.blockers.includes('zellij_missing');
|
|
@@ -0,0 +1,45 @@
|
|
|
1
|
+
import { nowIso } from '../fsx.js';
|
|
2
|
+
export function isZellijSelfHealResult(value) {
|
|
3
|
+
if (!value || typeof value !== 'object' || Array.isArray(value))
|
|
4
|
+
return false;
|
|
5
|
+
const row = value;
|
|
6
|
+
return row.schema === 'sks.zellij-self-heal.v1'
|
|
7
|
+
&& typeof row.ok === 'boolean'
|
|
8
|
+
&& typeof row.requested_by === 'string'
|
|
9
|
+
&& typeof row.strategy === 'string'
|
|
10
|
+
&& row.before !== null
|
|
11
|
+
&& typeof row.before === 'object'
|
|
12
|
+
&& row.after !== null
|
|
13
|
+
&& typeof row.after === 'object'
|
|
14
|
+
&& Array.isArray(row.blockers)
|
|
15
|
+
&& Array.isArray(row.warnings);
|
|
16
|
+
}
|
|
17
|
+
export function normalizeZellijSelfHealResult(value) {
|
|
18
|
+
if (isZellijSelfHealResult(value)) {
|
|
19
|
+
return {
|
|
20
|
+
...value,
|
|
21
|
+
dry_run: value.dry_run === true,
|
|
22
|
+
planned_mutations: Array.isArray(value.planned_mutations) ? value.planned_mutations : []
|
|
23
|
+
};
|
|
24
|
+
}
|
|
25
|
+
return {
|
|
26
|
+
schema: 'sks.zellij-self-heal.v1',
|
|
27
|
+
ok: false,
|
|
28
|
+
requested_by: 'setup',
|
|
29
|
+
fix_requested: false,
|
|
30
|
+
auto_approved: false,
|
|
31
|
+
install_homebrew_allowed: false,
|
|
32
|
+
dry_run: false,
|
|
33
|
+
planned_mutations: [],
|
|
34
|
+
before: { status: 'unknown', version: null, bin: null },
|
|
35
|
+
latest_version: null,
|
|
36
|
+
strategy: 'failed',
|
|
37
|
+
command: null,
|
|
38
|
+
after: { status: 'unknown', version: null, bin: null },
|
|
39
|
+
mutation_guard_artifact: null,
|
|
40
|
+
homebrew: { present: false, bin: null, install_attempted: false, install_allowed: false },
|
|
41
|
+
blockers: ['invalid_zellij_self_heal_result'],
|
|
42
|
+
warnings: [`normalized_at:${nowIso()}`]
|
|
43
|
+
};
|
|
44
|
+
}
|
|
45
|
+
//# sourceMappingURL=zellij-self-heal-types.js.map
|