sneakoscope 8.7.0 → 9.0.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +1 -1
- package/crates/sks-core/Cargo.lock +1 -1
- package/crates/sks-core/Cargo.toml +1 -1
- package/dist/config/skills-manifest.json +1 -1
- package/dist/core/agents/agent-janitor.js +7 -0
- package/dist/core/agents/agent-orchestrator.js +2 -2
- package/dist/core/agents/agent-recursion-guard.js +18 -4
- package/dist/core/agents/native-cli-worker-runtime.js +83 -56
- package/dist/core/align/align-context-index.js +68 -0
- package/dist/core/align/align-route.js +2 -0
- package/dist/core/align/code-navigation-align.js +42 -42
- package/dist/core/codex-lb/desktop-bridge/http-forward.js +76 -7
- package/dist/core/codex-lb/desktop-bridge/server.js +2 -1
- package/dist/core/commands/search-command.js +4 -1
- package/dist/core/commands/triwiki-atlas-command.js +4 -4
- package/dist/core/commands/triwiki-graph-command.js +2 -2
- package/dist/core/commands/wiki-command.js +2 -2
- package/dist/core/fsx.js +1 -1
- package/dist/core/hooks-runtime/context-graph-freshness-preflight.js +2 -2
- package/dist/core/hooks-runtime/official-subagent-lifecycle.js +8 -1
- package/dist/core/init.js +4 -0
- package/dist/core/naruto/context-graph-advisor-pairs.js +29 -0
- package/dist/core/naruto/context-graph-advisor-scope.js +77 -139
- package/dist/core/naruto/context-graph-advisor.js +0 -0
- package/dist/core/release/gate-affected-globs.js +93 -0
- package/dist/core/release/gate-manifest.js +2 -88
- package/dist/core/runtime/task-profile.js +3 -0
- package/dist/core/runtime/verification-budget.js +25 -0
- package/dist/core/search/context-projection.js +130 -0
- package/dist/core/search/context.js +0 -0
- package/dist/core/subagents/official-subagent-preparation.js +12 -3
- package/dist/core/subagents/official-subagent-runner.js +7 -0
- package/dist/core/subagents/triwiki-attention.js +2 -1
- package/dist/core/triwiki/context-graph/compiler/cache-key.js +11 -2
- package/dist/core/triwiki/context-graph/compiler/fragment-manifest-schema.js +163 -0
- package/dist/core/triwiki/context-graph/compiler/fragment-manifest-store.js +41 -0
- package/dist/core/triwiki/context-graph/compiler/fragment-manifest.js +108 -0
- package/dist/core/triwiki/context-graph/compiler/fragment-merge.js +195 -0
- package/dist/core/triwiki/context-graph/compiler/fragment-plan.js +137 -0
- package/dist/core/triwiki/context-graph/compiler/fragment-store.js +82 -0
- package/dist/core/triwiki/context-graph/compiler/incremental-build.js +161 -0
- package/dist/core/triwiki/context-graph/compiler/incremental-extract.js +94 -0
- package/dist/core/triwiki/context-graph/compiler/k-way-merge.js +92 -0
- package/dist/core/triwiki/context-graph/compiler/publish-index.js +98 -0
- package/dist/core/triwiki/context-graph/compiler/source-fragment.js +74 -0
- package/dist/core/triwiki/context-graph/contracts.js +2 -0
- package/dist/core/triwiki/context-graph/extractors/evidence/claims.js +4 -1
- package/dist/core/triwiki/context-graph/extractors/evidence/redaction.js +53 -0
- package/dist/core/triwiki/context-graph/projections/anchors.js +23 -21
- package/dist/core/triwiki/context-graph/projections/attention.js +30 -36
- package/dist/core/triwiki/context-graph/projections/code-pack-entry.js +92 -0
- package/dist/core/triwiki/context-graph/projections/code-pack-workspace.js +35 -44
- package/dist/core/triwiki/context-graph/projections/code-pack.js +21 -101
- package/dist/core/triwiki/context-graph/projections/graph-facts.js +103 -0
- package/dist/core/triwiki/context-graph/projections/index.js +3 -0
- package/dist/core/triwiki/context-graph/projections/module-view.js +46 -89
- package/dist/core/triwiki/context-graph/projections/node-summary.js +18 -32
- package/dist/core/triwiki/context-graph/projections/projection-candidate.js +47 -0
- package/dist/core/triwiki/context-graph/query/cache.js +191 -0
- package/dist/core/triwiki/context-graph/query/changed-path-seeds.js +29 -0
- package/dist/core/triwiki/context-graph/query/hydrate-chain.js +105 -0
- package/dist/core/triwiki/context-graph/query/hydrate-verify.js +83 -0
- package/dist/core/triwiki/context-graph/query/hydrate.js +0 -0
- package/dist/core/triwiki/context-graph/query/index.js +6 -0
- package/dist/core/triwiki/context-graph/query/kernel-candidates.js +128 -0
- package/dist/core/triwiki/context-graph/query/kernel-frontier.js +141 -0
- package/dist/core/triwiki/context-graph/query/kernel-fuse.js +240 -0
- package/dist/core/triwiki/context-graph/query/kernel-lanes.js +149 -0
- package/dist/core/triwiki/context-graph/query/kernel-plan.js +178 -0
- package/dist/core/triwiki/context-graph/query/kernel-safety.js +94 -0
- package/dist/core/triwiki/context-graph/query/kernel-select.js +154 -0
- package/dist/core/triwiki/context-graph/query/kernel-traverse.js +194 -0
- package/dist/core/triwiki/context-graph/query/kernel-types.js +63 -0
- package/dist/core/triwiki/context-graph/query/kernel.js +69 -0
- package/dist/core/triwiki/context-graph/query/name-anchors.js +20 -0
- package/dist/core/triwiki/context-graph/query/ranking-config.js +56 -0
- package/dist/core/triwiki/context-graph/query/walk.js +136 -0
- package/dist/core/triwiki/context-graph/query/workspace.js +110 -0
- package/dist/core/triwiki/context-graph/runtime-index/format-contract.js +111 -0
- package/dist/core/triwiki/context-graph/runtime-index/format-header.js +148 -0
- package/dist/core/triwiki/context-graph/runtime-index/format-primitives.js +57 -0
- package/dist/core/triwiki/context-graph/runtime-index/format-sections.js +57 -0
- package/dist/core/triwiki/context-graph/runtime-index/format.js +4 -0
- package/dist/core/triwiki/context-graph/runtime-index/lexicon-bm25.js +80 -0
- package/dist/core/triwiki/context-graph/runtime-index/lexicon-builder.js +194 -0
- package/dist/core/triwiki/context-graph/runtime-index/lexicon-contract.js +72 -0
- package/dist/core/triwiki/context-graph/runtime-index/lexicon-text.js +124 -0
- package/dist/core/triwiki/context-graph/runtime-index/lexicon-tokenizer.js +237 -0
- package/dist/core/triwiki/context-graph/runtime-index/lexicon.js +5 -0
- package/dist/core/triwiki/context-graph/runtime-index/reader-cursor.js +132 -0
- package/dist/core/triwiki/context-graph/runtime-index/reader-errors.js +39 -0
- package/dist/core/triwiki/context-graph/runtime-index/reader-hydrate.js +129 -0
- package/dist/core/triwiki/context-graph/runtime-index/reader-layout.js +104 -0
- package/dist/core/triwiki/context-graph/runtime-index/reader-lookup.js +90 -0
- package/dist/core/triwiki/context-graph/runtime-index/reader-names.js +57 -0
- package/dist/core/triwiki/context-graph/runtime-index/reader-types.js +2 -0
- package/dist/core/triwiki/context-graph/runtime-index/reader-validate.js +198 -0
- package/dist/core/triwiki/context-graph/runtime-index/reader.js +146 -0
- package/dist/core/triwiki/context-graph/runtime-index/writer-assemble.js +49 -0
- package/dist/core/triwiki/context-graph/runtime-index/writer-contract.js +112 -0
- package/dist/core/triwiki/context-graph/runtime-index/writer-lexical.js +139 -0
- package/dist/core/triwiki/context-graph/runtime-index/writer-tables.js +158 -0
- package/dist/core/triwiki/context-graph/runtime-index/writer-types.js +1 -0
- package/dist/core/triwiki/context-graph/runtime-index/writer.js +217 -0
- package/dist/core/triwiki/context-graph/store/generation-commit.js +171 -0
- package/dist/core/triwiki/context-graph/store/generation-errors.js +60 -0
- package/dist/core/triwiki/context-graph/store/generation-io.js +45 -0
- package/dist/core/triwiki/context-graph/store/generation-layout.js +42 -0
- package/dist/core/triwiki/context-graph/store/generation-pointer.js +131 -0
- package/dist/core/triwiki/context-graph/store/generation-recovery.js +89 -0
- package/dist/core/triwiki/context-graph/store/generation-resolve.js +24 -0
- package/dist/core/triwiki/context-graph/store/generation-retention.js +57 -0
- package/dist/core/triwiki/context-graph/store/generation-store.js +9 -0
- package/dist/core/triwiki/context-graph/store/generation-verify.js +48 -0
- package/dist/core/triwiki/context-graph/store/index-freshness.js +107 -0
- package/dist/core/triwiki/context-graph/store/operation-journal-recovery.js +37 -0
- package/dist/core/triwiki/context-graph/store/operation-journal-schema.js +225 -0
- package/dist/core/triwiki/context-graph/store/operation-journal.js +32 -0
- package/dist/core/triwiki/context-graph/store/snapshot-store.js +16 -8
- package/dist/core/verification/context-graph-affected.js +139 -98
- package/dist/core/verification/machine-feedback.js +19 -3
- package/dist/core/verification/verification-worker-pool.js +3 -1
- package/dist/core/version.js +1 -1
- package/dist/scripts/architecture-map-check.js +1 -1
- package/dist/scripts/context-graph-v2-check.js +327 -0
- package/package.json +2 -2
- package/release-gates.v2.json +128 -0
package/README.md
CHANGED
|
@@ -22,7 +22,7 @@ Proof-first orchestration for Codex CLI, ChatGPT Desktop, AI coding agents, mult
|
|
|
22
22
|
Sneakoscope Codex (`sks`) is an open-source trust layer for Codex CLI and ChatGPT Desktop. It coordinates bounded AI coding agents, records machine-verifiable evidence, preserves project memory, and blocks release claims that are not supported by current tests or artifacts. Search visibility outcomes are measured separately; SKS does not promise rankings or traffic.
|
|
23
23
|
<!-- END SKS SEARCH VISIBILITY MARKETING -->
|
|
24
24
|
|
|
25
|
-
This README documents package **SKS
|
|
25
|
+
This README documents package **SKS 9.0.0** — its own identity, read from `package.json` and subject to release-gate verification, not advice about what to install.
|
|
26
26
|
|
|
27
27
|
Use the official latest stable SKS and Codex CLI releases. The Codex compatibility SSOT is always the **current latest stable** host; capability probes measure what that host can actually do. Product docs do not crown a fixed `0.x.y` string as SSOT (release pins and schema directories are measured artifacts for the current package, not a permanent product version claim). Menu Bar / Center induce updates to the latest stable build. Run `sks update-check` for what is installed and read the capability report for what is supported. Install SSOT is npm `sneakoscope@latest`; PATH `sks` and Menu Bar stamped generation must match that version or gates fail. It resolves managed SKS skills from the authoritative global install, preserves a runnable Naruto child slot when `max_threads=2`, and keeps Menu Bar repair transactional so stamped generations remain verifiable. Naruto uses stable opt-in multi-agent V2 when the host exposes it (Codex official multi-agent wrap-only; SKS does not reimplement a parallel runtime). Local code search is mode-separated (`sks search files|text|structure|symbol|context`); `context` is answered by the compiled TriWiki Context Graph (`context-graph.json` is exhaustive authority; `context-pack.json` and managed `AGENTS.md` are bounded projections) — see [docs/architecture/context-graph.md](https://github.com/mandarange/Sneakoscope-Codex/blob/main/docs/architecture/context-graph.md) and [docs/PRODUCT-CONTRACT.md](https://github.com/mandarange/Sneakoscope-Codex/blob/main/docs/PRODUCT-CONTRACT.md). See [CHANGELOG.md](https://github.com/mandarange/Sneakoscope-Codex/blob/main/CHANGELOG.md).
|
|
28
28
|
|
|
@@ -3,6 +3,7 @@ import path from 'node:path';
|
|
|
3
3
|
import { exists, nowIso, readJson, writeJsonAtomic } from '../fsx.js';
|
|
4
4
|
import { normalizeAgentSessionRows } from './agent-session-rows.js';
|
|
5
5
|
import { resolveOwnedNamespacePath } from './agent-namespace-safety.js';
|
|
6
|
+
import { runAgentCleanupExecutor } from './agent-cleanup-executor.js';
|
|
6
7
|
export async function runAgentJanitor(input) {
|
|
7
8
|
const staleMs = input.staleMs ?? 30 * 60 * 1000;
|
|
8
9
|
const agentRoot = path.join(input.missionDir, 'agents');
|
|
@@ -39,6 +40,11 @@ export async function runAgentJanitor(input) {
|
|
|
39
40
|
const orphanTempDirs = await scopedExistingPaths(Array.isArray(namespace?.orphan_temp_dirs) ? namespace.orphan_temp_dirs : [], projectHash, namespace?.temp_dir ? [namespace.temp_dir] : []);
|
|
40
41
|
const staleLocks = await scopedStaleLockPaths(namespace?.lock_dir ? [namespace.lock_dir] : [], projectHash, staleMs);
|
|
41
42
|
const cleaned = [];
|
|
43
|
+
const reaped = input.reapOrphanProcesses
|
|
44
|
+
? await runAgentCleanupExecutor({ missionDir: input.missionDir, missionId: input.missionId ?? null, action: 'cleanup', apply: true, staleMs })
|
|
45
|
+
.then((report) => (report?.actions || []).filter((row) => row?.kind === 'terminate_process' && row?.status === 'applied').length)
|
|
46
|
+
.catch(() => 0)
|
|
47
|
+
: 0;
|
|
42
48
|
if (input.cleanup) {
|
|
43
49
|
for (const dir of orphanGenerationDirs) {
|
|
44
50
|
await fsp.rm(path.join(agentRoot, dir), { recursive: true, force: true }).catch(() => { });
|
|
@@ -69,6 +75,7 @@ export async function runAgentJanitor(input) {
|
|
|
69
75
|
slot_generation_cleanup: cleaned.filter((entry) => entry.includes(`${path.sep}sessions${path.sep}`)),
|
|
70
76
|
orphan_temp_dirs: orphanTempDirs,
|
|
71
77
|
stale_locks: staleLocks,
|
|
78
|
+
reaped_orphan_processes: reaped,
|
|
72
79
|
cleaned,
|
|
73
80
|
blockers,
|
|
74
81
|
};
|
|
@@ -216,7 +216,7 @@ export async function runNativeAgentOrchestrator(opts = {}) {
|
|
|
216
216
|
partition = applyNarutoWorkGraphToPartition(partition, opts.narutoWorkGraph, roster, targetActiveSlots, prompt);
|
|
217
217
|
augmentVerificationRollbackDagForNaruto(strategyCompiled.verification_rollback_dag, partition.slices);
|
|
218
218
|
}
|
|
219
|
-
await runAgentJanitor({ missionDir: dir, missionId, projectHash: namespace.root_hash });
|
|
219
|
+
const preSpawnSweep = await runAgentJanitor({ missionDir: dir, missionId, projectHash: namespace.root_hash, reapOrphanProcesses: true });
|
|
220
220
|
const ledgerRoot = await initializeAgentCentralLedger(dir, { missionId, roster, partition, route, prompt, dynamicScheduler: true });
|
|
221
221
|
const triwikiContext = await loadTriWikiRuntimeContext(root);
|
|
222
222
|
await writeTriWikiContextArtifact(ledgerRoot, triwikiContext);
|
|
@@ -617,7 +617,7 @@ export async function runNativeAgentOrchestrator(opts = {}) {
|
|
|
617
617
|
backend,
|
|
618
618
|
ledger_root: path.relative(root, ledgerRoot),
|
|
619
619
|
roster,
|
|
620
|
-
partition: { ok: partition.ok, slice_count: partition.slices.length, lease_count: partition.leases.length, blockers: partition.blockers },
|
|
620
|
+
partition: { ok: partition.ok, slice_count: partition.slices.length, lease_count: partition.leases.length, blockers: partition.blockers, pre_spawn_reaped: preSpawnSweep.reaped_orphan_processes },
|
|
621
621
|
task_graph: partition.task_graph?.route_work_count_summary || null,
|
|
622
622
|
requested_work_items: partition.task_graph?.desired_work_items || desiredWorkItemCount,
|
|
623
623
|
actual_total_work_items: partition.task_graph?.total_work_items || partition.slices.length,
|
|
@@ -86,8 +86,9 @@ export function agentWorkerHookRecursionDecision(state = {}, payload = {}, comma
|
|
|
86
86
|
reason: `Agent command recursion guard blocked nested SKS route command in Codex PreToolUse hook: ${guard.violations.join(', ')}`
|
|
87
87
|
};
|
|
88
88
|
}
|
|
89
|
-
export function agentWorkerHookContext(state = {}, payload = {}) {
|
|
90
|
-
const
|
|
89
|
+
export function agentWorkerHookContext(state = {}, payload = {}, env = process.env) {
|
|
90
|
+
const declared = {
|
|
91
|
+
...(env || {}),
|
|
91
92
|
...(payload.env || {}),
|
|
92
93
|
...(payload.tool_input?.env || {}),
|
|
93
94
|
...(payload.toolInput?.env || {}),
|
|
@@ -95,11 +96,24 @@ export function agentWorkerHookContext(state = {}, payload = {}) {
|
|
|
95
96
|
...(payload.tool?.input?.env || {})
|
|
96
97
|
};
|
|
97
98
|
void state;
|
|
98
|
-
return Boolean(String(
|
|
99
|
-
|| String(
|
|
99
|
+
return Boolean(String(declared.SKS_AGENT_WORKER || '') === '1'
|
|
100
|
+
|| String(declared.SKS_DISABLE_ROUTE_RECURSION || '') === '1'
|
|
101
|
+
|| agentGenerationDepth(declared) > 0
|
|
100
102
|
|| payload.agent_worker === true
|
|
101
103
|
|| payload.agentWorker === true);
|
|
102
104
|
}
|
|
105
|
+
export function agentGenerationDepth(env = process.env) {
|
|
106
|
+
const raw = Number.parseInt(String(env[AGENT_GENERATION_DEPTH_ENV] || ''), 10);
|
|
107
|
+
return Number.isFinite(raw) && raw > 0 ? Math.min(raw, MAX_AGENT_GENERATION_DEPTH * 4) : 0;
|
|
108
|
+
}
|
|
109
|
+
export const AGENT_GENERATION_DEPTH_ENV = 'SKS_AGENT_GENERATION_DEPTH';
|
|
110
|
+
export const MAX_AGENT_GENERATION_DEPTH = 1;
|
|
111
|
+
export function nextAgentGenerationEnv(env = process.env) {
|
|
112
|
+
return { [AGENT_GENERATION_DEPTH_ENV]: String(agentGenerationDepth(env) + 1) };
|
|
113
|
+
}
|
|
114
|
+
export function agentGenerationDepthExceeded(env = process.env) {
|
|
115
|
+
return agentGenerationDepth(env) > MAX_AGENT_GENERATION_DEPTH;
|
|
116
|
+
}
|
|
103
117
|
export function assertNoAgentRecursion(text) {
|
|
104
118
|
const result = scanAgentTextForRecursion(text);
|
|
105
119
|
if (!result.ok)
|
|
@@ -1,13 +1,15 @@
|
|
|
1
1
|
import fs from 'node:fs';
|
|
2
2
|
import { spawn } from 'node:child_process';
|
|
3
3
|
import path from 'node:path';
|
|
4
|
-
import { ensureDir, exists, nowIso, packageRoot, readJson, writeJsonAtomic } from '../fsx.js';
|
|
4
|
+
import { ensureDir, exists, nowIso, packageRoot, readJson, registerDetachedProcessGroup, terminateProcessTree, writeJsonAtomic } from '../fsx.js';
|
|
5
5
|
import { fastModeEnv } from './fast-mode-policy.js';
|
|
6
6
|
import { validateAgentWorkerResult } from './agent-worker-pipeline.js';
|
|
7
7
|
import { appendParallelRuntimeEvent } from './parallel-runtime-proof.js';
|
|
8
8
|
import { appendAgentMessage } from './agent-message-bus.js';
|
|
9
9
|
import { markLoopWorkerInterrupted, registerLoopActiveWorker } from '../loops/loop-interrupt-registry.js';
|
|
10
10
|
export const NATIVE_CLI_WORKER_RUNTIME_SCHEMA = 'sks.native-cli-worker-runtime.v3';
|
|
11
|
+
export const NATIVE_CLI_WORKER_DEFAULT_TIMEOUT_MS = 60 * 60 * 1000;
|
|
12
|
+
export const NATIVE_CLI_WORKER_TIMEOUT_BLOCKER = 'native_cli_worker_timeout';
|
|
11
13
|
export function createNativeCliWorkerRuntimeRecorder(root, input) {
|
|
12
14
|
return new NativeCliWorkerRuntimeRecorder(root, input);
|
|
13
15
|
}
|
|
@@ -63,7 +65,7 @@ class NativeCliWorkerRuntimeRecorder {
|
|
|
63
65
|
recursion_guard_env: true
|
|
64
66
|
};
|
|
65
67
|
await writeJsonAtomic(path.join(this.root, intakeRel), intake);
|
|
66
|
-
const workerEntrypoint = await resolveWorkerEntrypointPath();
|
|
68
|
+
const workerEntrypoint = this.input.workerEntrypointPath || await resolveWorkerEntrypointPath();
|
|
67
69
|
const args = [workerEntrypoint, '--intake', path.join(this.root, intakeRel), '--json'];
|
|
68
70
|
const record = {
|
|
69
71
|
schema: 'sks.native-cli-worker-session-record.v2',
|
|
@@ -117,9 +119,16 @@ class NativeCliWorkerRuntimeRecorder {
|
|
|
117
119
|
SKS_AGENT_SLOT_ID: String(ctx.agent.slot_id || ''),
|
|
118
120
|
SKS_AGENT_GENERATION_INDEX: String(ctx.agent.generation_index || 1)
|
|
119
121
|
},
|
|
120
|
-
stdio: ['ignore', 'pipe', 'pipe']
|
|
122
|
+
stdio: ['ignore', 'pipe', 'pipe'],
|
|
123
|
+
detached: process.platform !== 'win32'
|
|
124
|
+
});
|
|
125
|
+
child.stdout?.pipe(stdout);
|
|
126
|
+
child.stderr?.pipe(stderr);
|
|
127
|
+
const unregisterProcessGroup = registerDetachedProcessGroup(child);
|
|
128
|
+
const supervisor = superviseWorkerChild(child, {
|
|
129
|
+
timeoutMs: resolveWorkerTimeoutMs(ctx.opts),
|
|
130
|
+
...(ctx.opts?.signal ? { signal: ctx.opts.signal } : {})
|
|
121
131
|
});
|
|
122
|
-
const removeAbortListener = terminateChildOnAbort(child, ctx.opts?.signal);
|
|
123
132
|
const exitPromise = new Promise((resolve) => {
|
|
124
133
|
child.once('close', (code, signal) => resolve({ code, signal }));
|
|
125
134
|
child.once('error', () => resolve({ code: 1, signal: null }));
|
|
@@ -130,48 +139,57 @@ class NativeCliWorkerRuntimeRecorder {
|
|
|
130
139
|
if (child.pid)
|
|
131
140
|
this.active.add(child.pid);
|
|
132
141
|
this.maxObserved = Math.max(this.maxObserved, this.active.size);
|
|
133
|
-
|
|
134
|
-
|
|
135
|
-
|
|
136
|
-
|
|
137
|
-
|
|
138
|
-
|
|
139
|
-
|
|
140
|
-
|
|
141
|
-
|
|
142
|
-
|
|
143
|
-
|
|
144
|
-
|
|
145
|
-
|
|
146
|
-
|
|
147
|
-
|
|
148
|
-
|
|
149
|
-
|
|
150
|
-
|
|
151
|
-
|
|
152
|
-
|
|
153
|
-
|
|
154
|
-
|
|
155
|
-
|
|
156
|
-
|
|
157
|
-
|
|
158
|
-
|
|
159
|
-
|
|
160
|
-
|
|
161
|
-
|
|
162
|
-
|
|
163
|
-
|
|
164
|
-
|
|
142
|
+
let exit;
|
|
143
|
+
let loopHandle = null;
|
|
144
|
+
try {
|
|
145
|
+
await this.record(record);
|
|
146
|
+
loopHandle = await registerLoopWorkerHandle({
|
|
147
|
+
root: ctx.opts.projectRoot || this.input.projectRoot || ctx.opts.cwd || packageRoot(),
|
|
148
|
+
env: ctx.opts.env || {},
|
|
149
|
+
agentId: String(ctx.agent.id || ctx.agent.session_id || 'agent'),
|
|
150
|
+
sessionId: ctx.agent.session_id || null,
|
|
151
|
+
pid: child.pid || null
|
|
152
|
+
});
|
|
153
|
+
await appendParallelRuntimeEvent(this.root, this.input.missionId, {
|
|
154
|
+
event_type: 'worker_process_spawned',
|
|
155
|
+
slot_id: ctx.agent.slot_id || ctx.agent.id || null,
|
|
156
|
+
generation_index: ctx.agent.generation_index || null,
|
|
157
|
+
session_id: ctx.agent.session_id || null,
|
|
158
|
+
pid: child.pid || null,
|
|
159
|
+
backend: this.input.backend,
|
|
160
|
+
placement: 'process',
|
|
161
|
+
worktree_id: worktree?.id || null
|
|
162
|
+
}).catch(() => undefined);
|
|
163
|
+
await this.lifecycle(ctx, {
|
|
164
|
+
eventType: 'worker_spawned',
|
|
165
|
+
status: 'launching',
|
|
166
|
+
artifacts: [intakeRel, heartbeatRel, resultRel, stdoutRel, stderrRel],
|
|
167
|
+
logTail: `pid=${child.pid || 'unknown'}`
|
|
168
|
+
});
|
|
169
|
+
exit = await exitPromise;
|
|
170
|
+
}
|
|
171
|
+
finally {
|
|
172
|
+
supervisor.dispose();
|
|
173
|
+
await supervisor.terminate();
|
|
174
|
+
unregisterProcessGroup();
|
|
175
|
+
stdout.end();
|
|
176
|
+
stderr.end();
|
|
177
|
+
if (child.pid)
|
|
178
|
+
this.active.delete(child.pid);
|
|
179
|
+
}
|
|
165
180
|
record.closed_at = nowIso();
|
|
166
181
|
record.exit_code = exit.code;
|
|
167
182
|
record.signal = exit.signal;
|
|
168
183
|
record.status = exit.code === 0 ? 'closed' : 'failed';
|
|
184
|
+
const timeoutBlockers = supervisor.timedOut ? [NATIVE_CLI_WORKER_TIMEOUT_BLOCKER] : [];
|
|
185
|
+
if (supervisor.timedOut)
|
|
186
|
+
record.status = 'failed';
|
|
169
187
|
if (loopHandle) {
|
|
170
188
|
await markLoopWorkerInterrupted(ctx.opts.projectRoot || this.input.projectRoot || ctx.opts.cwd || packageRoot(), loopHandle.mission_id, loopHandle.worker_id, record.status === 'closed' ? 'completed' : 'failed').catch(() => undefined);
|
|
171
189
|
}
|
|
172
190
|
const parsed = await readJson(path.join(this.root, resultRel), null).catch(() => null);
|
|
173
191
|
if (!parsed) {
|
|
174
|
-
record.blockers = ['native_cli_worker_result_missing'];
|
|
192
|
+
record.blockers = [...timeoutBlockers, 'native_cli_worker_result_missing'];
|
|
175
193
|
await this.lifecycle(ctx, {
|
|
176
194
|
eventType: 'worker_failed',
|
|
177
195
|
status: 'failed',
|
|
@@ -199,6 +217,8 @@ class NativeCliWorkerRuntimeRecorder {
|
|
|
199
217
|
}
|
|
200
218
|
const result = validateAgentWorkerResult({
|
|
201
219
|
...parsed,
|
|
220
|
+
...(timeoutBlockers.length ? { status: 'failed' } : {}),
|
|
221
|
+
blockers: [...timeoutBlockers, ...(Array.isArray(parsed.blockers) ? parsed.blockers : [])],
|
|
202
222
|
artifacts: [...new Set([...(Array.isArray(parsed.artifacts) ? parsed.artifacts : []), stdoutRel, stderrRel])]
|
|
203
223
|
});
|
|
204
224
|
record.status = result.status === 'done' ? 'closed' : result.status;
|
|
@@ -293,30 +313,37 @@ class NativeCliWorkerRuntimeRecorder {
|
|
|
293
313
|
};
|
|
294
314
|
}
|
|
295
315
|
}
|
|
296
|
-
function
|
|
297
|
-
|
|
298
|
-
|
|
299
|
-
let hardKillTimer = null;
|
|
316
|
+
function superviseWorkerChild(child, input) {
|
|
317
|
+
let timedOut = false;
|
|
318
|
+
let teardown = null;
|
|
300
319
|
const terminate = () => {
|
|
301
320
|
if (child.exitCode !== null || child.signalCode !== null)
|
|
302
|
-
return;
|
|
303
|
-
child.
|
|
304
|
-
|
|
305
|
-
if (child.exitCode === null && child.signalCode === null)
|
|
306
|
-
child.kill('SIGKILL');
|
|
307
|
-
}, 1500);
|
|
308
|
-
hardKillTimer.unref?.();
|
|
321
|
+
return Promise.resolve();
|
|
322
|
+
teardown = teardown || terminateProcessTree(child.pid, child);
|
|
323
|
+
return teardown;
|
|
309
324
|
};
|
|
310
|
-
|
|
311
|
-
|
|
312
|
-
|
|
313
|
-
|
|
314
|
-
|
|
315
|
-
|
|
316
|
-
|
|
317
|
-
|
|
325
|
+
const onAbort = () => { void terminate(); };
|
|
326
|
+
const timer = setTimeout(() => { timedOut = true; void terminate(); }, input.timeoutMs);
|
|
327
|
+
timer.unref?.();
|
|
328
|
+
if (input.signal) {
|
|
329
|
+
if (input.signal.aborted)
|
|
330
|
+
void terminate();
|
|
331
|
+
else
|
|
332
|
+
input.signal.addEventListener('abort', onAbort, { once: true });
|
|
333
|
+
}
|
|
334
|
+
return {
|
|
335
|
+
get timedOut() { return timedOut; },
|
|
336
|
+
terminate,
|
|
337
|
+
dispose() {
|
|
338
|
+
clearTimeout(timer);
|
|
339
|
+
input.signal?.removeEventListener('abort', onAbort);
|
|
340
|
+
}
|
|
318
341
|
};
|
|
319
342
|
}
|
|
343
|
+
function resolveWorkerTimeoutMs(opts) {
|
|
344
|
+
const candidate = Number(opts?.workerTimeoutMs ?? opts?.timeoutMs);
|
|
345
|
+
return Number.isFinite(candidate) && candidate > 0 ? candidate : NATIVE_CLI_WORKER_DEFAULT_TIMEOUT_MS;
|
|
346
|
+
}
|
|
320
347
|
async function resolveWorkerEntrypointPath() {
|
|
321
348
|
const distEntrypoint = path.join(packageRoot(), 'dist', 'core', 'agents', 'native-cli-worker-entry.js');
|
|
322
349
|
if (await exists(distEntrypoint))
|
|
@@ -0,0 +1,68 @@
|
|
|
1
|
+
import path from 'node:path';
|
|
2
|
+
import { readSourceHashes } from '../triwiki/context-graph/compiler/freshness.js';
|
|
3
|
+
import { computeSourceInventoryFingerprint } from '../triwiki/context-graph/compiler/fragment-manifest.js';
|
|
4
|
+
import { publishContextIndexGeneration } from '../triwiki/context-graph/compiler/publish-index.js';
|
|
5
|
+
import { openWorkspaceContextIndex } from '../triwiki/context-graph/query/index.js';
|
|
6
|
+
import { ContextIndexStoreError } from '../triwiki/context-graph/store/generation-errors.js';
|
|
7
|
+
import { ContextIndexWriterError } from '../triwiki/context-graph/runtime-index/writer.js';
|
|
8
|
+
import { ContextIndexFormatError } from '../triwiki/context-graph/runtime-index/format.js';
|
|
9
|
+
import { projectCodePackFromGraph } from '../triwiki/code-pack.js';
|
|
10
|
+
export const ALIGN_PENDING_ROOT_DIR = 'pending';
|
|
11
|
+
export const ALIGN_CONTEXT_INDEX_BLOCKER = 'code_navigation_context_index_blocked';
|
|
12
|
+
export function alignPendingRoot(stageRoot) {
|
|
13
|
+
return path.join(stageRoot, ALIGN_PENDING_ROOT_DIR);
|
|
14
|
+
}
|
|
15
|
+
export function alignPendingWiki(stageRoot) {
|
|
16
|
+
return path.join(alignPendingRoot(stageRoot), '.sneakoscope', 'wiki');
|
|
17
|
+
}
|
|
18
|
+
export function alignSourceFingerprint(inputHashes) {
|
|
19
|
+
return computeSourceInventoryFingerprint(new Map(Object.entries(inputHashes)));
|
|
20
|
+
}
|
|
21
|
+
function alignBlocker(error) {
|
|
22
|
+
if (error instanceof ContextIndexStoreError) {
|
|
23
|
+
return new Error(`${ALIGN_CONTEXT_INDEX_BLOCKER}:${error.publicCode}:${error.code}`);
|
|
24
|
+
}
|
|
25
|
+
if (error instanceof ContextIndexWriterError || error instanceof ContextIndexFormatError) {
|
|
26
|
+
return new Error(`${ALIGN_CONTEXT_INDEX_BLOCKER}:${error.publicCode}:${error.code}`);
|
|
27
|
+
}
|
|
28
|
+
return error instanceof Error ? error : new Error(String(error));
|
|
29
|
+
}
|
|
30
|
+
export async function publishAlignContextIndex(input) {
|
|
31
|
+
try {
|
|
32
|
+
return await publishContextIndexGeneration({
|
|
33
|
+
root: input.pendingRoot,
|
|
34
|
+
snapshot: input.snapshot,
|
|
35
|
+
sourceFingerprint: input.sourceFingerprint,
|
|
36
|
+
fragmentManifestHash: input.fragmentManifestHash ?? null,
|
|
37
|
+
...(input.lintErrors === undefined ? {} : { lintErrors: input.lintErrors }),
|
|
38
|
+
...(input.lintWarnings === undefined ? {} : { lintWarnings: input.lintWarnings })
|
|
39
|
+
});
|
|
40
|
+
}
|
|
41
|
+
catch (error) {
|
|
42
|
+
throw alignBlocker(error);
|
|
43
|
+
}
|
|
44
|
+
}
|
|
45
|
+
function citedPaths(pack) {
|
|
46
|
+
return [...new Set(pack.entries.flatMap((entry) => entry.citations.map((citation) => citation.path)))].sort();
|
|
47
|
+
}
|
|
48
|
+
export async function projectAlignCodePack(input) {
|
|
49
|
+
let reader;
|
|
50
|
+
try {
|
|
51
|
+
const handle = await openWorkspaceContextIndex(input.pendingRoot, {
|
|
52
|
+
cache: null,
|
|
53
|
+
expectedSourceFingerprint: input.sourceFingerprint
|
|
54
|
+
});
|
|
55
|
+
reader = handle.reader;
|
|
56
|
+
}
|
|
57
|
+
catch (error) {
|
|
58
|
+
throw alignBlocker(error);
|
|
59
|
+
}
|
|
60
|
+
const options = {
|
|
61
|
+
generatedAt: input.generatedAt,
|
|
62
|
+
gitHeadSha: input.gitHeadSha,
|
|
63
|
+
snapshotFreshness: 'fresh'
|
|
64
|
+
};
|
|
65
|
+
const first = projectCodePackFromGraph(input.root, reader, options);
|
|
66
|
+
const observed = await readSourceHashes(input.root, citedPaths(first.pack));
|
|
67
|
+
return projectCodePackFromGraph(input.root, reader, { ...options, observedSourceHashes: observed }).pack;
|
|
68
|
+
}
|
|
@@ -20,6 +20,8 @@ export const ALIGN_SOURCE_POLICY = Object.freeze({
|
|
|
20
20
|
export const ALIGN_OUTPUT_ARTIFACTS = Object.freeze([
|
|
21
21
|
'.sneakoscope/wiki/context-graph.json',
|
|
22
22
|
'.sneakoscope/wiki/context-graph.meta.json',
|
|
23
|
+
'.sneakoscope/wiki/context-graph/current.json',
|
|
24
|
+
'.sneakoscope/wiki/context-graph/context-graph.meta.json',
|
|
23
25
|
'.sneakoscope/wiki/code-navigation-manifest.json',
|
|
24
26
|
'.sneakoscope/wiki/code-pack.json',
|
|
25
27
|
'.sneakoscope/wiki/context-pack.json',
|
|
@@ -8,17 +8,17 @@ import { createRequestedScopeContract } from '../safety/requested-scope-contract
|
|
|
8
8
|
import { projectTriwikiToAgentsMdTransactional } from '../triwiki/agents-md-projector.js';
|
|
9
9
|
import { buildCodeNavigationContextPack, validateCodeNavigationContextPack } from '../triwiki/code-navigation-context-pack.js';
|
|
10
10
|
import { CODE_NAVIGATION_FATAL_SKIP_REASONS, CODE_NAVIGATION_LIMITS } from '../triwiki/code-navigation-policy.js';
|
|
11
|
-
import { isCodePackProjectionBoundToSnapshot,
|
|
11
|
+
import { isCodePackProjectionBoundToSnapshot, validateCodePack } from '../triwiki/code-pack.js';
|
|
12
12
|
import { validateContextGraphSnapshot } from '../triwiki/context-graph/contracts.js';
|
|
13
13
|
import { compileContextGraph } from '../triwiki/context-graph/compiler/index.js';
|
|
14
|
-
import { readSourceHashes } from '../triwiki/context-graph/compiler/freshness.js';
|
|
15
14
|
import { ARCHITECTURE_MAP_EXTRACTOR_IDS, architectureMapGraphExtractors } from '../triwiki/context-graph/extractors/index.js';
|
|
16
15
|
import { computeContextGraphCacheKey } from '../triwiki/context-graph/compiler/cache-key.js';
|
|
17
16
|
import { walkCodeInventory } from '../triwiki/context-graph/extractors/code/inventory.js';
|
|
18
17
|
import { codeInventoryInputHashes } from '../triwiki/code-navigation-policy.js';
|
|
19
|
-
import { clearContextGraphSnapshotCache } from '../triwiki/context-graph/query/
|
|
18
|
+
import { clearContextGraphSnapshotCache, clearWorkspaceContextIndex } from '../triwiki/context-graph/query/index.js';
|
|
20
19
|
import { withTriWikiStateLock } from '../triwiki/triwiki-cleanup.js';
|
|
21
20
|
import { publishArchitectureMapToStage } from '../triwiki/context-graph/store/architecture-map-store.js';
|
|
21
|
+
import { alignPendingRoot, alignPendingWiki, alignSourceFingerprint, projectAlignCodePack, publishAlignContextIndex } from './align-context-index.js';
|
|
22
22
|
import { ALIGN_GATE_ARTIFACT, ALIGN_LEDGER_ARTIFACT, ALIGN_OUTPUT_ARTIFACTS, ALIGN_PLAN_ARTIFACT, ALIGN_STAGING_ROOT_REL, buildInitialAlignLedger, refreshAlignGate } from './align-route.js';
|
|
23
23
|
const WIKI_PREFIX = '.sneakoscope/wiki/';
|
|
24
24
|
function isArchitectureMapOptionalSkip(skip) {
|
|
@@ -65,9 +65,6 @@ function countsBy(values, key) {
|
|
|
65
65
|
function difference(left, right) {
|
|
66
66
|
return left.filter((value) => !right.has(value)).sort();
|
|
67
67
|
}
|
|
68
|
-
function citedPaths(pack) {
|
|
69
|
-
return [...new Set(pack.entries.flatMap((entry) => entry.citations.map((citation) => citation.path)))].sort();
|
|
70
|
-
}
|
|
71
68
|
function alignGuard(root) {
|
|
72
69
|
const contract = createRequestedScopeContract({
|
|
73
70
|
route: '$sks-align',
|
|
@@ -127,20 +124,6 @@ async function writeEvidence(root, dir, ledger, missionId) {
|
|
|
127
124
|
await writeJsonAtomic(path.join(dir, ALIGN_LEDGER_ARTIFACT), ledger);
|
|
128
125
|
return (await refreshAlignGate(dir, missionId, root)).gate;
|
|
129
126
|
}
|
|
130
|
-
async function buildPack(root, snapshot, meta, generatedAt) {
|
|
131
|
-
const first = projectCodePackFromGraph(root, snapshot, {
|
|
132
|
-
generatedAt,
|
|
133
|
-
gitHeadSha: meta.cacheKeyParts.head,
|
|
134
|
-
snapshotFreshness: 'fresh'
|
|
135
|
-
});
|
|
136
|
-
const observed = await readSourceHashes(root, citedPaths(first.pack));
|
|
137
|
-
return projectCodePackFromGraph(root, snapshot, {
|
|
138
|
-
generatedAt,
|
|
139
|
-
gitHeadSha: meta.cacheKeyParts.head,
|
|
140
|
-
snapshotFreshness: 'fresh',
|
|
141
|
-
observedSourceHashes: observed
|
|
142
|
-
}).pack;
|
|
143
|
-
}
|
|
144
127
|
function buildManifest(generatedAt, inventory, inventoryDigest, snapshot) {
|
|
145
128
|
return {
|
|
146
129
|
schema: 'sks.code-navigation-manifest.v1',
|
|
@@ -276,29 +259,9 @@ async function runLocked(root, dir, missionId, plan, ledger, beforeFinalSourceCa
|
|
|
276
259
|
if (!graphValidation.ok || meta.cacheKeyParts.sourcePolicy !== 'workspace') {
|
|
277
260
|
throw new Error('code_navigation_graph_validation_failed');
|
|
278
261
|
}
|
|
279
|
-
const pack = await buildPack(root, snapshot, meta, generatedAt);
|
|
280
|
-
const packValidation = await validateCodePack(pack, root);
|
|
281
|
-
ledger.validation.code_pack = packValidation.ok
|
|
282
|
-
&& pack.source_file_count === inventoryPaths.length
|
|
283
|
-
&& isCodePackProjectionBoundToSnapshot(snapshot.snapshotHash, pack);
|
|
284
|
-
if (!ledger.validation.code_pack)
|
|
285
|
-
throw new Error(`code_navigation_code_pack_invalid:${packValidation.issues.join('|')}`);
|
|
286
|
-
const contextPack = buildCodeNavigationContextPack({
|
|
287
|
-
root,
|
|
288
|
-
codePack: pack,
|
|
289
|
-
snapshotHash: snapshot.snapshotHash,
|
|
290
|
-
fileCount: inventoryPaths.length,
|
|
291
|
-
symbolCount: ledger.graph.symbol_nodes,
|
|
292
|
-
edgeCount: snapshot.edgeCount,
|
|
293
|
-
extractorRevisions: snapshot.extractors.map(({ id, revision }) => ({ id, revision }))
|
|
294
|
-
});
|
|
295
|
-
const contextValidation = validateCodeNavigationContextPack(contextPack, root);
|
|
296
|
-
ledger.validation.context_pack = contextValidation.ok;
|
|
297
|
-
if (!contextValidation.ok)
|
|
298
|
-
throw new Error('code_navigation_context_pack_invalid');
|
|
299
|
-
const manifest = buildManifest(generatedAt, inventory, codeInventoryDigest, snapshot);
|
|
300
262
|
const stageRoot = path.join(stageBase, `${missionId}-${randomUUID().slice(0, 8)}`);
|
|
301
|
-
const
|
|
263
|
+
const pendingRoot = alignPendingRoot(stageRoot);
|
|
264
|
+
const stageWiki = alignPendingWiki(stageRoot);
|
|
302
265
|
const previousRoot = path.join(stageRoot, 'previous');
|
|
303
266
|
const activeWiki = path.join(root, '.sneakoscope', 'wiki');
|
|
304
267
|
const movedPrior = [];
|
|
@@ -307,6 +270,41 @@ async function runLocked(root, dir, missionId, plan, ledger, beforeFinalSourceCa
|
|
|
307
270
|
let commitStarted = false;
|
|
308
271
|
try {
|
|
309
272
|
await ensureDir(stageWiki);
|
|
273
|
+
const sourceFingerprint = alignSourceFingerprint(inputHashes);
|
|
274
|
+
const published = await publishAlignContextIndex({
|
|
275
|
+
pendingRoot,
|
|
276
|
+
snapshot,
|
|
277
|
+
sourceFingerprint,
|
|
278
|
+
fragmentManifestHash: null,
|
|
279
|
+
lintWarnings: compiled.issues.length
|
|
280
|
+
});
|
|
281
|
+
const pack = await projectAlignCodePack({
|
|
282
|
+
root,
|
|
283
|
+
pendingRoot,
|
|
284
|
+
gitHeadSha: meta.cacheKeyParts.head,
|
|
285
|
+
generatedAt,
|
|
286
|
+
sourceFingerprint: published.sourceFingerprint
|
|
287
|
+
});
|
|
288
|
+
const packValidation = await validateCodePack(pack, root);
|
|
289
|
+
ledger.validation.code_pack = packValidation.ok
|
|
290
|
+
&& pack.source_file_count === inventoryPaths.length
|
|
291
|
+
&& isCodePackProjectionBoundToSnapshot(snapshot.snapshotHash, pack);
|
|
292
|
+
if (!ledger.validation.code_pack)
|
|
293
|
+
throw new Error(`code_navigation_code_pack_invalid:${packValidation.issues.join('|')}`);
|
|
294
|
+
const contextPack = buildCodeNavigationContextPack({
|
|
295
|
+
root,
|
|
296
|
+
codePack: pack,
|
|
297
|
+
snapshotHash: snapshot.snapshotHash,
|
|
298
|
+
fileCount: inventoryPaths.length,
|
|
299
|
+
symbolCount: ledger.graph.symbol_nodes,
|
|
300
|
+
edgeCount: snapshot.edgeCount,
|
|
301
|
+
extractorRevisions: snapshot.extractors.map(({ id, revision }) => ({ id, revision }))
|
|
302
|
+
});
|
|
303
|
+
const contextValidation = validateCodeNavigationContextPack(contextPack, root);
|
|
304
|
+
ledger.validation.context_pack = contextValidation.ok;
|
|
305
|
+
if (!contextValidation.ok)
|
|
306
|
+
throw new Error('code_navigation_context_pack_invalid');
|
|
307
|
+
const manifest = buildManifest(generatedAt, inventory, codeInventoryDigest, snapshot);
|
|
310
308
|
await writeJsonAtomic(path.join(stageWiki, 'context-graph.json'), snapshot);
|
|
311
309
|
await writeJsonAtomic(path.join(stageWiki, 'context-graph.meta.json'), meta);
|
|
312
310
|
await writeJsonAtomic(path.join(stageWiki, 'code-navigation-manifest.json'), manifest);
|
|
@@ -333,6 +331,7 @@ async function runLocked(root, dir, missionId, plan, ledger, beforeFinalSourceCa
|
|
|
333
331
|
await guardedRename(guard, stageWiki, activeWiki);
|
|
334
332
|
promoted = true;
|
|
335
333
|
clearContextGraphSnapshotCache();
|
|
334
|
+
clearWorkspaceContextIndex(root);
|
|
336
335
|
ledger.publication.transactional_directory_replaced = true;
|
|
337
336
|
ledger.publication.active_artifacts = [...ALIGN_OUTPUT_ARTIFACTS];
|
|
338
337
|
projection = await projectTriwikiToAgentsMdTransactional(root);
|
|
@@ -401,6 +400,7 @@ async function runLocked(root, dir, missionId, plan, ledger, beforeFinalSourceCa
|
|
|
401
400
|
await ensureDir(path.dirname(stageWiki));
|
|
402
401
|
await guardedRename(guard, activeWiki, stageWiki);
|
|
403
402
|
clearContextGraphSnapshotCache();
|
|
403
|
+
clearWorkspaceContextIndex(root);
|
|
404
404
|
}
|
|
405
405
|
catch (rollbackError) {
|
|
406
406
|
rollbackFailures.push(`wiki:${String(rollbackError)}`);
|