ruvnet-brain 4.7.19 → 4.7.21
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/bin/install.mjs +494 -97
- package/kb/download-retry.mjs +93 -21
- package/kb/forge-guard-injection.mjs +277 -0
- package/kb/forge-update.mjs +166 -38
- package/kb/lifecycle-evidence-retention.mjs +151 -14
- package/kb/model-requirements.mjs +4 -1
- package/kb/refresh-run.mjs +175 -27
- package/kb/verify-citation.mjs +15 -2
- package/package.json +3 -1
- package/plugin/.claude-plugin/plugin.json +1 -1
- package/plugin/.codex-plugin/plugin.json +1 -1
- package/plugin/mcp/managed-cli-interface.mjs +16 -8
- package/plugin/scripts/adr-currency-gate.mjs +2 -1
- package/plugin/scripts/advisory-contract.mjs +21 -0
- package/plugin/scripts/advocacy-route.mjs +5 -4
- package/plugin/scripts/agentdb-recall.mjs +15 -5
- package/plugin/scripts/automatic-update.mjs +9 -1
- package/plugin/scripts/brain-confirmation.mjs +2 -1
- package/plugin/scripts/brain-footprint.mjs +72 -11
- package/plugin/scripts/capability-registry.mjs +1 -1
- package/plugin/scripts/capacity-aware-parallel-work.mjs +2 -1
- package/plugin/scripts/codex-overnight-notice.mjs +33 -0
- package/plugin/scripts/completion-claim-evidence.mjs +56 -12
- package/plugin/scripts/continuation-gate.mjs +28 -9
- package/plugin/scripts/decision-gate.mjs +13 -63
- package/plugin/scripts/decision-outcomes.mjs +36 -0
- package/plugin/scripts/decision-recall-gate.mjs +40 -5
- package/plugin/scripts/decision-record.mjs +8 -3
- package/plugin/scripts/degradation-watch.mjs +2 -1
- package/plugin/scripts/developer-update-lock.mjs +172 -33
- package/plugin/scripts/developer-update-owner.mjs +8 -5
- package/plugin/scripts/developer-update-policy.mjs +4 -1
- package/plugin/scripts/developer-update.mjs +155 -53
- package/plugin/scripts/doc-currency.mjs +2 -16
- package/plugin/scripts/duplicate-gate.mjs +2 -1
- package/plugin/scripts/entrypoint.mjs +34 -0
- package/plugin/scripts/footprint-io.mjs +135 -8
- package/plugin/scripts/grok-hook-adapter.mjs +2 -1
- package/plugin/scripts/ground-ruvnet.sh +6 -3
- package/plugin/scripts/grounding-answer.mjs +2 -1
- package/plugin/scripts/grounding-code-projection.mjs +2 -1
- package/plugin/scripts/grounding-substance.mjs +2 -1
- package/plugin/scripts/grounding-turn-gate.mjs +2 -2
- package/plugin/scripts/grounding-turn-mark.mjs +2 -2
- package/plugin/scripts/hook-context-budget.mjs +2 -1
- package/plugin/scripts/hook-input.mjs +2 -2
- package/plugin/scripts/hook-shim.mjs +9 -2
- package/plugin/scripts/host-update.mjs +29 -5
- package/plugin/scripts/identifier-preflight.mjs +2 -1
- package/plugin/scripts/kb-copy-proof.mjs +57 -9
- package/plugin/scripts/learn-capture.mjs +2 -1
- package/plugin/scripts/learning-store.mjs +62 -16
- package/plugin/scripts/lesson-bridge.mjs +2 -2
- package/plugin/scripts/lesson-promote.mjs +83 -22
- package/plugin/scripts/md-stamp.mjs +2 -1
- package/plugin/scripts/native-turn-transcript.mjs +98 -6
- package/plugin/scripts/nightly-scheduler.mjs +10 -5
- package/plugin/scripts/package-recommender.mjs +2 -2
- package/plugin/scripts/project-store-resolver.mjs +19 -0
- package/plugin/scripts/routing-outcome-capture.mjs +2 -1
- package/plugin/scripts/session-snapshot-hook.mjs +24 -4
- package/plugin/scripts/session-start-core.mjs +8 -16
- package/plugin/scripts/session-start-health.mjs +97 -10
- package/plugin/scripts/session-start-issue-alert.mjs +105 -0
- package/plugin/scripts/session-start-update-plane.mjs +15 -1
- package/plugin/scripts/signal-watch.mjs +2 -1
- package/plugin/scripts/spend-guard.mjs +2 -1
- package/plugin/scripts/substitution-wall.mjs +2 -1
- package/plugin/scripts/turn-outcome-capture.mjs +7 -3
- package/plugin/scripts/unprompted-runtime.mjs +20 -3
- package/plugin/scripts/update-apply.mjs +186 -15
- package/plugin/skills/release-proof/scripts/release-proof.mjs +2 -1
- package/plugin/skills/ruvnet-brain/PLAYBOOK.md +2 -2
- package/plugin/skills/ruvnet-brain/SKILL.md +3 -2
- package/scripts/adr-072-completion.mjs +2 -1
- package/scripts/agentdb-context.mjs +2 -1
- package/scripts/agentdb-fleet-doctor.mjs +30 -8
- package/scripts/approved-runtime.mjs +2 -1
- package/scripts/architecture-review-lock.mjs +2 -1
- package/scripts/ascii-drift.mjs +2 -1
- package/scripts/brain-latency-50.mjs +2 -1
- package/scripts/brain-novice-50.mjs +2 -16
- package/scripts/brain-score.mjs +13 -2
- package/scripts/build-bundle.mjs +2 -1
- package/scripts/build-concepts.mjs +2 -1
- package/scripts/candidate-ci-receipt.mjs +2 -1
- package/scripts/candidate-host-evidence.mjs +2 -1
- package/scripts/card-from-source.mjs +2 -1
- package/scripts/ci/warm-brain-models.mjs +2 -1
- package/scripts/claims-verify.mjs +7 -3
- package/scripts/claude-controlled-terminal.mjs +2 -1
- package/scripts/claude-terminal-mod.mjs +2 -1
- package/scripts/code-release-corpus.mjs +2 -1
- package/scripts/codex-hook-trust-reconcile.mjs +67 -1
- package/scripts/codex-hook-trust.mjs +40 -1
- package/scripts/codex-hook-wrapper-history.mjs +2 -1
- package/scripts/codex-host-execution-proof.mjs +2 -1
- package/scripts/completion-claim-replay.mjs +2 -1
- package/scripts/console-engine.mjs +8 -3
- package/scripts/corpus-canary.mjs +2 -1
- package/scripts/corpus-candidate.mjs +2 -2
- package/scripts/corpus-coverage-sidecar.mjs +2 -1
- package/scripts/corpus-dispatch-decision.mjs +2 -1
- package/scripts/corpus-dispatch-receipt.mjs +2 -1
- package/scripts/corpus-next-seed.mjs +2 -1
- package/scripts/corpus-qa.mjs +2 -1
- package/scripts/corpus-reconcile.mjs +2 -2
- package/scripts/corpus-watchdog.mjs +2 -1
- package/scripts/count-chunks.mjs +2 -1
- package/scripts/customer-state-matrix.mjs +2 -1
- package/scripts/derive-passage-content-map.mjs +2 -1
- package/scripts/development-push-check.mjs +2 -1
- package/scripts/dispatch-receipt.mjs +2 -1
- package/scripts/doc-currency.mjs +2 -1
- package/scripts/dream-issue-gate.mjs +2 -1
- package/scripts/dual-host-deliberation.mjs +2 -16
- package/scripts/dual-host-suggest.mjs +2 -16
- package/scripts/eval-brain.mjs +7 -3
- package/scripts/execution-policy.mjs +2 -1
- package/scripts/execution-preflight.mjs +2 -1
- package/scripts/falsify.mjs +2 -1
- package/scripts/fix-metaharness-memretrieve.mjs +2 -1
- package/scripts/fix-workstream.mjs +2 -1
- package/scripts/full-hints.mjs +2 -1
- package/scripts/full-suite-gate.mjs +2 -1
- package/scripts/grok-host.mjs +68 -0
- package/scripts/hook-qualify-core.mjs +3 -1
- package/scripts/hook-qualify.mjs +2 -1
- package/scripts/hook-retirement-check.mjs +2 -1
- package/scripts/host-registry.mjs +2 -1
- package/scripts/independent-review-receipt.mjs +2 -1
- package/scripts/ingest-new-repos.mjs +2 -1
- package/scripts/ingest-repo.mjs +12 -2
- package/scripts/integration-evidence.mjs +2 -1
- package/scripts/issue-fix.mjs +7 -24
- package/scripts/issue-watch.mjs +164 -47
- package/scripts/job-heartbeat.sh +145 -13
- package/scripts/key-canary.mjs +9 -3
- package/scripts/latency-to-surface.mjs +2 -1
- package/scripts/learning-replay-cli.mjs +97 -19
- package/scripts/learning-replay-execution.mjs +2 -0
- package/scripts/learning-replay-fixture.mjs +3 -0
- package/scripts/learning-replay-proof.mjs +14 -13
- package/scripts/learning-replay.mjs +3 -3
- package/scripts/learnings.mjs +2 -1
- package/scripts/lesson-ratify.mjs +88 -40
- package/scripts/loop-checkpoint.mjs +2 -1
- package/scripts/metaharness-receipts.mjs +2 -1
- package/scripts/model-catalog.mjs +2 -1
- package/scripts/model-currency-evidence.mjs +6 -4
- package/scripts/model-currency.mjs +33 -3
- package/scripts/model-native-catalog.mjs +2 -1
- package/scripts/model-router-agent-hook.mjs +2 -1
- package/scripts/model-router-dispatch.mjs +2 -1
- package/scripts/model-router-engine.mjs +2 -1
- package/scripts/model-router-outcome.mjs +2 -1
- package/scripts/model-router-setup.mjs +2 -1
- package/scripts/model-routing-eval.mjs +2 -1
- package/scripts/model-routing-gateway.mjs +2 -1
- package/scripts/model-routing-launchers.mjs +4 -1
- package/scripts/model-terminal-gateway.mjs +2 -1
- package/scripts/model-terminal-launchers.mjs +2 -1
- package/scripts/model-weekly-analyst.mjs +2 -1
- package/scripts/model-weekly-cycle.mjs +2 -1
- package/scripts/model-weekly-qualification.mjs +2 -1
- package/scripts/nightly-gists.sh +30 -21
- package/scripts/nightly-two-run-proof.mjs +2 -1
- package/scripts/nightly-watchdog.mjs +158 -47
- package/scripts/no-silent-substitution.mjs +2 -1
- package/scripts/notify.sh +11 -6
- package/scripts/ntfy-inbox.mjs +549 -0
- package/scripts/onboarding-console.mjs +47 -8
- package/scripts/oracle/abstain-threshold-sweep.mjs +2 -1
- package/scripts/oracle/abstain-trace.mjs +2 -1
- package/scripts/oracle/doc2query-generate.mjs +2 -1
- package/scripts/oracle/doc2query-reach.mjs +2 -1
- package/scripts/oracle/judge-train.mjs +2 -1
- package/scripts/oracle/need-set-split.mjs +2 -1
- package/scripts/oracle/produce-questions.mjs +2 -16
- package/scripts/oracle/repo-recall.mjs +2 -2
- package/scripts/oracle/retrieval-accuracy.mjs +2 -2
- package/scripts/oracle/sona-query-adapter-eval.mjs +2 -1
- package/scripts/oracle/source-tree.mjs +2 -15
- package/scripts/oracle/source-units.mjs +2 -16
- package/scripts/oracle/spike-run.mjs +2 -16
- package/scripts/oracle/validate-labels.mjs +2 -16
- package/scripts/org-repo-count.mjs +2 -1
- package/scripts/package-cards.mjs +2 -1
- package/scripts/prepublication-evidence.mjs +2 -1
- package/scripts/private-overlay.mjs +2 -1
- package/scripts/product-integrity-contract.mjs +2 -1
- package/scripts/protected-release-invocation.mjs +2 -1
- package/scripts/public-verification-abandon.mjs +2 -1
- package/scripts/public-verification-aggregate.mjs +2 -1
- package/scripts/public-verification-finalizer.mjs +2 -1
- package/scripts/public-verification-inputs.mjs +2 -1
- package/scripts/public-verification-lane.mjs +2 -1
- package/scripts/publication-receipt.mjs +2 -1
- package/scripts/published-surface-probe.mjs +2 -1
- package/scripts/qe/card-lane-gate.mjs +2 -16
- package/scripts/qe/session-start-gate.mjs +2 -16
- package/scripts/qe/ux-suite.mjs +2 -1
- package/scripts/qualified-candidate-check.mjs +2 -1
- package/scripts/rebuild-gists-from-receipts.mjs +2 -1
- package/scripts/recommendation-eval.mjs +2 -1
- package/scripts/recommendation-floor.mjs +2 -1
- package/scripts/recommendation-judge-score.mjs +2 -1
- package/scripts/recommendation-real-host-score.mjs +2 -1
- package/scripts/recommendation-real-host.mjs +2 -1
- package/scripts/refresh-capability-only-store.mjs +2 -1
- package/scripts/refresh-model-catalog.mjs +2 -1
- package/scripts/rehearse-corpus-pipeline.mjs +2 -1
- package/scripts/release-authority.mjs +2 -1
- package/scripts/release-evidence-aggregate.mjs +2 -1
- package/scripts/release-payload.mjs +2 -1
- package/scripts/release-projection.mjs +2 -1
- package/scripts/release-proof.mjs +2 -1
- package/scripts/release-qualification-contract.mjs +71 -26
- package/scripts/release-qualification.mjs +8 -1
- package/scripts/release-vector.mjs +2 -1
- package/scripts/restore-local-ingests.mjs +2 -1
- package/scripts/retrieval-canary.mjs +2 -1
- package/scripts/route-cheap.mjs +2 -1
- package/scripts/route-gold-rank.mjs +2 -1
- package/scripts/route-index-memory.mjs +2 -1
- package/scripts/route-latency-warm.mjs +2 -1
- package/scripts/router-utilization.mjs +2 -1
- package/scripts/routing-flywheel.mjs +2 -1
- package/scripts/run-operational-benchmark.mjs +2 -1
- package/scripts/run-operational-benchmark.v3.mjs +2 -1
- package/scripts/rvf-index-audit.mjs +2 -16
- package/scripts/seal-gist-receipt.mjs +2 -1
- package/scripts/security-guidance-codex-compat.mjs +2 -1
- package/scripts/selfcheck.mjs +2 -1
- package/scripts/signal-watch.mjs +2 -1
- package/scripts/source-coverage.mjs +2 -1
- package/scripts/stabilization-receipt.mjs +2 -1
- package/scripts/stamp-sweep.mjs +2 -1
- package/scripts/status-honesty.mjs +2 -1
- package/scripts/sync-commands.mjs +2 -2
- package/scripts/top100-benchmark.mjs +2 -1
- package/scripts/top100-corpus.mjs +2 -1
- package/scripts/user-model-hook-install.mjs +2 -1
- package/scripts/user-model-prompt-hook.mjs +2 -1
- package/scripts/verify-bundle.mjs +3 -1
- package/scripts/verify-model-catalog.mjs +2 -1
- package/scripts/version.mjs +2 -1
- package/scripts/wired-check.mjs +11 -5
- package/scripts/worktree-integrity.mjs +2 -2
|
@@ -26,6 +26,7 @@ import { storeRoot } from '../kb/store-root.mjs';
|
|
|
26
26
|
import { validateCoverageDirectory, sha256File } from './coverage-integrity.mjs';
|
|
27
27
|
import { verifyPayload, verifyPayloadMembers, payloadIdFor } from './release-payload.mjs';
|
|
28
28
|
import { extractZip } from '../kb/zip-extract.mjs';
|
|
29
|
+
import { isDirectInvocation } from '../plugin/scripts/entrypoint.mjs';
|
|
29
30
|
|
|
30
31
|
const ROOT = path.resolve(path.dirname(fileURLToPath(import.meta.url)), '..');
|
|
31
32
|
|
|
@@ -206,6 +207,9 @@ export function readCoverageClaims(readme) {
|
|
|
206
207
|
export const COVERAGE_REGEN_CMD = 'npm run test:cov';
|
|
207
208
|
|
|
208
209
|
const DIR_PRUNE = new Set(['node_modules', '.git', 'coverage', 'dist', 'clones']);
|
|
210
|
+
// Root-relative trees that are never coverage sources but can be huge or busy: agent worktrees (whole
|
|
211
|
+
// checkouts), job logs, the QE fleet's state and local caches.
|
|
212
|
+
const ROOT_PRUNE = new Set(['.claude/worktrees', 'logs', '.agentic-qe', '.cache']);
|
|
209
213
|
|
|
210
214
|
/** Minimal glob → RegExp for the shapes coverage.include/exclude actually use (`**`, `*`, `?`). */
|
|
211
215
|
function globToRe(glob) {
|
|
@@ -250,9 +254,9 @@ export async function coverageSourceSet(vitestFile = path.join(ROOT, 'vitest.con
|
|
|
250
254
|
try { ents = fs.readdirSync(dir, { withFileTypes: true }); } catch { return; }
|
|
251
255
|
for (const ent of ents) {
|
|
252
256
|
const abs = path.join(dir, ent.name);
|
|
253
|
-
if (ent.isDirectory()) { if (!DIR_PRUNE.has(ent.name)) walk(abs); continue; }
|
|
254
|
-
if (!ent.isFile()) continue;
|
|
255
257
|
const rel = path.relative(root, abs).split(path.sep).join('/');
|
|
258
|
+
if (ent.isDirectory()) { if (!DIR_PRUNE.has(ent.name) && !ROOT_PRUNE.has(rel)) walk(abs); continue; }
|
|
259
|
+
if (!ent.isFile()) continue;
|
|
256
260
|
if (incl.some((r) => r.test(rel)) && !exclude.some((r) => r.test(rel))) files.push(abs);
|
|
257
261
|
}
|
|
258
262
|
};
|
|
@@ -903,6 +907,6 @@ async function main() {
|
|
|
903
907
|
if (verdict === 'UNKNOWN' && process.argv.includes('--strict')) process.exitCode = 4;
|
|
904
908
|
}
|
|
905
909
|
|
|
906
|
-
if (
|
|
910
|
+
if (isDirectInvocation(import.meta.url)) {
|
|
907
911
|
await main();
|
|
908
912
|
}
|
|
@@ -14,6 +14,7 @@ import { decideNativeTurn, appendGatewayReceipt } from './model-routing-gateway.
|
|
|
14
14
|
import { subscriptionEnvironment, assertSubscriptionAuth, validateDispatchDecision } from './model-router-dispatch.mjs';
|
|
15
15
|
import { validateClaudeTerminalSettings } from './model-terminal-launchers.mjs';
|
|
16
16
|
import { runManagedPrompt } from './model-managed-prompt.mjs';
|
|
17
|
+
import { isDirectInvocation } from '../plugin/scripts/entrypoint.mjs';
|
|
17
18
|
|
|
18
19
|
const REFUSED = 'Controlled Claude turn refused; no fallback.';
|
|
19
20
|
const uuid = value => /^[a-f0-9]{8}-[a-f0-9]{4}-[1-5][a-f0-9]{3}-[89ab][a-f0-9]{3}-[a-f0-9]{12}$/i.test(value || '');
|
|
@@ -386,7 +387,7 @@ export async function launchControlledClaudeTerminal({ binary, args = [], input
|
|
|
386
387
|
} finally { controller.abort(); terminal.close(); for (const name of ['SIGINT', 'SIGTERM', 'SIGHUP']) signalSource.removeListener(name, cancel); }
|
|
387
388
|
}
|
|
388
389
|
|
|
389
|
-
if (
|
|
390
|
+
if (isDirectInvocation(import.meta.url)) {
|
|
390
391
|
const args = process.argv.slice(2);
|
|
391
392
|
if (args[0] !== '--real-binary' || !args[1] || args[2] !== '--') {
|
|
392
393
|
process.stderr.write('Expected --real-binary /absolute/native/claude -- [--resume UUID]\n'); process.exitCode = 1;
|
|
@@ -6,6 +6,7 @@ import crypto from 'node:crypto';
|
|
|
6
6
|
import { spawnSync } from 'node:child_process';
|
|
7
7
|
import { fileURLToPath, pathToFileURL } from 'node:url';
|
|
8
8
|
import { classify } from '../config/model-router/policy.default.mjs';
|
|
9
|
+
import { isDirectInvocation } from '../plugin/scripts/entrypoint.mjs';
|
|
9
10
|
const ROOT = path.resolve(path.dirname(fileURLToPath(import.meta.url)), '..');
|
|
10
11
|
export const MOD_SOURCE = path.join(ROOT, 'config/model-router/claude-terminal-mod');
|
|
11
12
|
export function terminalModDigest(pluginRoot) {
|
|
@@ -84,6 +85,6 @@ async function main() {
|
|
|
84
85
|
else if (mode === '--prepare') process.stdout.write(JSON.stringify(prepareClaudeTerminalMod(request)) + '\n');
|
|
85
86
|
else throw new Error('Expected --prepare, --decision, --ready, or --health');
|
|
86
87
|
}
|
|
87
|
-
if (
|
|
88
|
+
if (isDirectInvocation(import.meta.url)) main().catch(() => {
|
|
88
89
|
process.stderr.write('claude-terminal-mod: routing or activation unavailable; no fallback\n'); process.exitCode = 1;
|
|
89
90
|
});
|
|
@@ -34,6 +34,7 @@ import { spawnSync } from 'node:child_process';
|
|
|
34
34
|
import { fileURLToPath } from 'node:url';
|
|
35
35
|
import { verifyCoverageSidecar } from './corpus-coverage-sidecar.mjs';
|
|
36
36
|
import { resolveNextCorpusSeed } from './corpus-next-seed.mjs';
|
|
37
|
+
import { isDirectInvocation } from '../plugin/scripts/entrypoint.mjs';
|
|
37
38
|
|
|
38
39
|
const ROOT = path.resolve(path.dirname(fileURLToPath(import.meta.url)), '..');
|
|
39
40
|
const CORPUS_TAG = /^corpus-sha256-([0-9a-f]{64})$/;
|
|
@@ -266,7 +267,7 @@ export function main(argv = process.argv.slice(2)) {
|
|
|
266
267
|
|
|
267
268
|
function isMain() {
|
|
268
269
|
try {
|
|
269
|
-
return
|
|
270
|
+
return isDirectInvocation(import.meta.url);
|
|
270
271
|
} catch {
|
|
271
272
|
return false;
|
|
272
273
|
}
|
|
@@ -7,6 +7,7 @@ import { fileURLToPath } from 'node:url';
|
|
|
7
7
|
import { spawn, spawnSync } from 'node:child_process';
|
|
8
8
|
import zlib from 'node:zlib';
|
|
9
9
|
import { codexHookIdentities, codexPluginContentHash } from './codex-hook-trust.mjs';
|
|
10
|
+
import { isDirectInvocation } from '../plugin/scripts/entrypoint.mjs';
|
|
10
11
|
|
|
11
12
|
/**
|
|
12
13
|
* sha256 of every regular file under package/plugin/ in a verified npm tarball, read in memory (ustar with pax `path`
|
|
@@ -335,8 +336,73 @@ export async function reconcileVerifiedCodexHookTrust({ releaseIdentity, install
|
|
|
335
336
|
} finally { await client?.close(); }
|
|
336
337
|
}
|
|
337
338
|
|
|
339
|
+
/**
|
|
340
|
+
* Refresh a RUNNING shared Codex app-server (the managed daemon the TUI attaches to) after the Brain changed its
|
|
341
|
+
* plugin bytes or hook trust from another process (#492). Without this, that long-lived server keeps the plugin
|
|
342
|
+
* declarations it parsed earlier — codex-rs/core-plugins/src/manager.rs LoadedPluginsCache::get() only drops an
|
|
343
|
+
* entry when the active plugin ROOT changes, not when files change in place — and its loaded sessions keep their
|
|
344
|
+
* old hook runtimes. Measured 2026-10-25, codex-cli 0.164.0, isolated CODEX_HOME: after an in-place change the
|
|
345
|
+
* daemon's hooks/list still showed the old SessionEnd declaration while a fresh server showed the new one.
|
|
346
|
+
*
|
|
347
|
+
* The refresh is Codex's own: config/batchWrite with no edits and reloadUserConfig. With no edits nothing is
|
|
348
|
+
* persisted (config_manager_service.rs apply_edits writes only when config_edits is non-empty); batch_write then
|
|
349
|
+
* clears the plugin and skill caches (config_processor.rs handle_config_mutation) and reloads every loaded thread
|
|
350
|
+
* (reload_user_config). The expectedVersion check makes a concurrent config writer fail it safely. It never grants
|
|
351
|
+
* an approval, starts or stops a server, or touches a thread. No server listening = nothing stale to refresh.
|
|
352
|
+
* Transport: WebSocket over $CODEX_HOME/app-server-control/app-server-control.sock, a symlink to the physical
|
|
353
|
+
* socket (codex-rs/app-server-transport/src/transport/{mod.rs,unix_socket.rs}).
|
|
354
|
+
*/
|
|
355
|
+
export async function refreshSharedCodexAppServer({ codexHome, timeoutMs = 8000, connect } = {}) {
|
|
356
|
+
if (typeof codexHome !== 'string' || !path.isAbsolute(codexHome)) return { state: 'blocked', changed: false, reason: 'Absolute Codex home required' };
|
|
357
|
+
const rendezvous = path.join(codexHome, 'app-server-control', 'app-server-control.sock');
|
|
358
|
+
let socketPath;
|
|
359
|
+
try { socketPath = fs.realpathSync(rendezvous); if (!fs.statSync(socketPath).isSocket()) throw new Error('not a socket'); }
|
|
360
|
+
catch { return { state: 'no-shared-server', changed: false }; }
|
|
361
|
+
// ONE total deadline, started BEFORE connecting: a socket that accepts but never completes the WebSocket handshake
|
|
362
|
+
// must not hang the installer (cross-vendor review F). On timeout the connecting socket is terminated.
|
|
363
|
+
let ws; let timer; let timedOut = false;
|
|
364
|
+
const deadline = new Promise((_, reject) => { timer = setTimeout(() => { timedOut = true; reject(new Error('Shared Codex app-server refresh timed out')); }, timeoutMs); });
|
|
365
|
+
deadline.catch(() => {});
|
|
366
|
+
try {
|
|
367
|
+
const run = (async () => {
|
|
368
|
+
ws = connect ? await connect(socketPath) : await (async () => {
|
|
369
|
+
const { default: WebSocket } = await import('ws');
|
|
370
|
+
const socket = new WebSocket(`ws+unix://${socketPath}:/`);
|
|
371
|
+
ws = socket; // reachable by the timeout's teardown while the handshake is still pending
|
|
372
|
+
await new Promise((resolve, reject) => { socket.once('open', resolve); socket.once('error', reject); });
|
|
373
|
+
return socket;
|
|
374
|
+
})();
|
|
375
|
+
if (timedOut) { try { if (ws.terminate) ws.terminate(); else ws.close(); } catch { /* already gone */ } return; }
|
|
376
|
+
const pending = new Map(); let nextId = 1;
|
|
377
|
+
ws.on('message', (data) => {
|
|
378
|
+
let row; try { row = JSON.parse(String(data)); } catch { return; }
|
|
379
|
+
const item = pending.get(row.id); if (!item) return;
|
|
380
|
+
pending.delete(row.id);
|
|
381
|
+
// Native error text can quote private config values; report only which call was refused.
|
|
382
|
+
if (row.error) item.reject(new Error(`Shared Codex app-server rejected ${item.method}`)); else item.resolve(row.result);
|
|
383
|
+
});
|
|
384
|
+
const rpc = (method, params) => new Promise((resolve, reject) => {
|
|
385
|
+
const id = nextId++; pending.set(id, { resolve, reject, method }); ws.send(JSON.stringify({ id, method, params }));
|
|
386
|
+
});
|
|
387
|
+
await rpc('initialize', { clientInfo: { name: 'rnb_host_refresh', version: '1' }, capabilities: { experimentalApi: true } });
|
|
388
|
+
ws.send(JSON.stringify({ method: 'initialized' }));
|
|
389
|
+
const read = await rpc('config/read', { includeLayers: true });
|
|
390
|
+
const users = (read?.layers || []).filter((r) => r.name?.type === 'user');
|
|
391
|
+
if (users.length !== 1 || typeof users[0].version !== 'string' || typeof users[0].name.file !== 'string') {
|
|
392
|
+
throw new Error('Shared Codex app-server user config version unavailable');
|
|
393
|
+
}
|
|
394
|
+
await rpc('config/batchWrite', { filePath: users[0].name.file, expectedVersion: users[0].version, reloadUserConfig: true, edits: [] });
|
|
395
|
+
})();
|
|
396
|
+
run.catch(() => {}); // a late failure after the deadline is already reported
|
|
397
|
+
await Promise.race([run, deadline]);
|
|
398
|
+
return { state: 'refreshed', changed: false, approvalsGranted: 0 };
|
|
399
|
+
} catch (error) {
|
|
400
|
+
return { state: 'failed', changed: false, reason: error.message };
|
|
401
|
+
} finally { clearTimeout(timer); try { if (timedOut && ws?.terminate) ws.terminate(); else ws?.close(); } catch { /* already closed */ } }
|
|
402
|
+
}
|
|
403
|
+
|
|
338
404
|
// Explicit CLI only; importing the helper never starts a process or reads stdin.
|
|
339
|
-
if (
|
|
405
|
+
if (isDirectInvocation(import.meta.url)
|
|
340
406
|
&& process.argv[2] === '--reconcile-installed') {
|
|
341
407
|
const started = Date.now(); let emitted = false; let reconcileStarted = false;
|
|
342
408
|
const emit = (receipt) => { if (!emitted) { emitted = true; process.stdout.write(JSON.stringify(receipt) + '\n'); } };
|
|
@@ -78,17 +78,56 @@ export function codexHookIdentities(hooksFile, keySource = 'ruvnet-brain@ruvnet-
|
|
|
78
78
|
* OLD file's hash at the same key ('modified') or that did not exist before ('untrusted'). A hook the user
|
|
79
79
|
* had already trusted at the old hash stops running until reviewed; that is the whole point of the list.
|
|
80
80
|
*/
|
|
81
|
-
export function codexTrustChanges(prevHooksFile, nextHooksFile, keySource) {
|
|
81
|
+
export function codexTrustChanges(prevHooksFile, nextHooksFile, keySource, { contentChanged = false } = {}) {
|
|
82
82
|
const prev = codexHookIdentities(prevHooksFile, keySource);
|
|
83
83
|
const next = codexHookIdentities(nextHooksFile, keySource);
|
|
84
84
|
const changes = [];
|
|
85
85
|
for (const [key, hash] of next) {
|
|
86
86
|
if (!prev.has(key)) changes.push({ key, status: 'untrusted' });
|
|
87
87
|
else if (prev.get(key) !== hash) changes.push({ key, status: 'modified' });
|
|
88
|
+
// Codex 0.164+: an unchanged declaration is still "modified" once the plugin's bytes change, because the approval
|
|
89
|
+
// also pins trusted_content_hash (plugin_trust.rs matches_hash; legacy approvals pin a baseline the same way).
|
|
90
|
+
else if (contentChanged) changes.push({ key, status: 'modified', reason: 'plugin-content' });
|
|
88
91
|
}
|
|
89
92
|
return changes;
|
|
90
93
|
}
|
|
91
94
|
|
|
95
|
+
/**
|
|
96
|
+
* Does this Codex approve plugin hooks by content as well as declaration? True from codex-cli 0.164.0, where
|
|
97
|
+
* discovery.rs reports currentHash "<declaration>/<content>" and plugin_trust.rs checks trusted_content_hash.
|
|
98
|
+
* Unknown or unparseable versions answer null so a caller can fail toward asking for review.
|
|
99
|
+
*/
|
|
100
|
+
export function codexUsesContentTrust(versionText) {
|
|
101
|
+
const m = /(\d+)\.(\d+)\.(\d+)/.exec(String(versionText || ''));
|
|
102
|
+
if (!m) return null;
|
|
103
|
+
const [major, minor] = [Number(m[1]), Number(m[2])];
|
|
104
|
+
return major > 0 || minor >= 164;
|
|
105
|
+
}
|
|
106
|
+
|
|
107
|
+
// Codex writes this under the installed plugin itself (codex-rs/core-plugins/src/command_migration.rs); a candidate
|
|
108
|
+
// release never carries it, so it says nothing about whether the release's bytes changed.
|
|
109
|
+
const CODEX_GENERATED_PREFIX = '.codex-plugin/migrated-command-skills/';
|
|
110
|
+
|
|
111
|
+
/**
|
|
112
|
+
* Would Codex see different plugin contents after replacing `installedRoot` with `candidateRoot`? Compares exactly
|
|
113
|
+
* the files codexPluginContentHash() fingerprints (Codex's own generated migrations aside). Anything unreadable
|
|
114
|
+
* counts as changed: the answer is used to warn, so it fails toward asking the user to review.
|
|
115
|
+
*/
|
|
116
|
+
export function codexPluginContentChanged(installedRoot, candidateRoot, options = {}) {
|
|
117
|
+
if (!installedRoot || !candidateRoot) return true;
|
|
118
|
+
const files = (root) => {
|
|
119
|
+
const out = new Map();
|
|
120
|
+
codexPluginContentHash(root, { ...options, onFile: (rel, bytes) => {
|
|
121
|
+
if (!rel.startsWith(CODEX_GENERATED_PREFIX)) out.set(rel, crypto.createHash('sha256').update(bytes).digest('hex'));
|
|
122
|
+
} });
|
|
123
|
+
return out;
|
|
124
|
+
};
|
|
125
|
+
try {
|
|
126
|
+
const a = files(installedRoot); const b = files(candidateRoot);
|
|
127
|
+
return a.size !== b.size || [...b].some(([rel, h]) => a.get(rel) !== h);
|
|
128
|
+
} catch { return true; }
|
|
129
|
+
}
|
|
130
|
+
|
|
92
131
|
/** The exact instruction for a user whose Brain hooks Codex is holding back. */
|
|
93
132
|
export const CODEX_TRUST_ACTION = 'Start Codex (interactive, in any project). At the "Hooks need review" prompt choose'
|
|
94
133
|
+ ' "Trust all and continue" — or type /hooks and trust each RuvNet Brain hook. Until then Codex skips them'
|
|
@@ -21,6 +21,7 @@ import fs from 'node:fs';
|
|
|
21
21
|
import path from 'node:path';
|
|
22
22
|
import { execFileSync } from 'node:child_process';
|
|
23
23
|
import { fileURLToPath } from 'node:url';
|
|
24
|
+
import { isDirectInvocation } from '../plugin/scripts/entrypoint.mjs';
|
|
24
25
|
|
|
25
26
|
const ROOT = path.resolve(path.dirname(fileURLToPath(import.meta.url)), '..');
|
|
26
27
|
export const HISTORY_FILE = path.join('data', 'codex-hook-wrapper-history.json');
|
|
@@ -122,6 +123,6 @@ export function main(argv = process.argv.slice(2)) {
|
|
|
122
123
|
return 2;
|
|
123
124
|
}
|
|
124
125
|
|
|
125
|
-
if (
|
|
126
|
+
if (isDirectInvocation(import.meta.url)) {
|
|
126
127
|
process.exitCode = main();
|
|
127
128
|
}
|
|
@@ -10,6 +10,7 @@ import { probeFreshCodexDeclarations, classifyFreshCodexDeclarations } from './c
|
|
|
10
10
|
import { runtimeSnapshot } from './model-routing-launchers.mjs';
|
|
11
11
|
import { maintenanceStatus } from '../plugin/scripts/development-maintenance.mjs';
|
|
12
12
|
import { proofFile, proofDigest, verifyReleasedHostRuntime, assertHostRuntimeUnchanged } from './codex-host-proof-runtime.mjs';
|
|
13
|
+
import { isDirectInvocation } from '../plugin/scripts/entrypoint.mjs';
|
|
13
14
|
|
|
14
15
|
const OWNER = 'ruvnet-brain@ruvnet-brain';
|
|
15
16
|
// The diagnostic verifies the canonical ordinary-work route, never selects a separate model.
|
|
@@ -241,6 +242,6 @@ export async function executionProofMain(args, { run = produceOwnedCodexExecutio
|
|
|
241
242
|
return result.ok ? 0 : 1;
|
|
242
243
|
} catch (error) { output(`${JSON.stringify(failure(error.message))}\n${usage}`); return 2; }
|
|
243
244
|
}
|
|
244
|
-
if (
|
|
245
|
+
if (isDirectInvocation(import.meta.url)) {
|
|
245
246
|
process.exitCode = await executionProofMain(process.argv.slice(2));
|
|
246
247
|
}
|
|
@@ -20,6 +20,7 @@ import os from 'node:os';
|
|
|
20
20
|
import path from 'node:path';
|
|
21
21
|
import { fileURLToPath } from 'node:url';
|
|
22
22
|
import { auditCompletionClaims, claudeTurnEvents, extractCommitments } from '../plugin/scripts/completion-claim-evidence.mjs';
|
|
23
|
+
import { isDirectInvocation } from '../plugin/scripts/entrypoint.mjs';
|
|
23
24
|
|
|
24
25
|
const argv = process.argv.slice(2);
|
|
25
26
|
const opt = (flag, fallback) => { const i = argv.indexOf(flag); return i >= 0 && argv[i + 1] ? argv[i + 1] : fallback; };
|
|
@@ -110,5 +111,5 @@ function main() {
|
|
|
110
111
|
console.log(JSON.stringify(summary, null, 2));
|
|
111
112
|
}
|
|
112
113
|
|
|
113
|
-
const isMain = (
|
|
114
|
+
const isMain = isDirectInvocation(import.meta.url);
|
|
114
115
|
if (isMain) main();
|
|
@@ -102,9 +102,14 @@ export function buildHealthRecommendations({ memory = null, learning = null } =
|
|
|
102
102
|
|
|
103
103
|
// 3. A learner that has not trained in days. Installed-but-dormant is a DEFECT, not a neutral
|
|
104
104
|
// state — the entire point of shipping a learning system is that it runs.
|
|
105
|
+
// #377: "quiet" means observations have WAITED more than two days for a distillation, measured
|
|
106
|
+
// from `ruflo memory distill status`. An unknown distillation state, or a learner that is caught
|
|
107
|
+
// up (nothing recorded since its last distillation), never shows this card.
|
|
105
108
|
const STALE_TRAIN_SECONDS = 60 * 60 * 24 * 2; // two days
|
|
106
|
-
const age =
|
|
107
|
-
|
|
109
|
+
const age = Number(learning?.lastTrainSeconds);
|
|
110
|
+
const waited = learning?.oldestPendingAt ? (Date.now() - Date.parse(learning.oldestPendingAt)) / 1000 : NaN;
|
|
111
|
+
if (learning?.enabled !== false && learning?.statusKnown && learning.distillKnown === true
|
|
112
|
+
&& learning.pendingObservations > 0 && Number.isFinite(waited) && waited > STALE_TRAIN_SECONDS) {
|
|
108
113
|
recs.push(makeRecommendation({
|
|
109
114
|
id: 'learning:train',
|
|
110
115
|
scope: learning?.scope === 'user' ? 'user' : 'project',
|
|
@@ -115,7 +120,7 @@ export function buildHealthRecommendations({ memory = null, learning = null } =
|
|
|
115
120
|
plainImpact:
|
|
116
121
|
'Runs one local training cycle so recent work becomes patterns it can reuse next time. '
|
|
117
122
|
+ 'Uses the same AgentDB, retains a checked local snapshot, and verifies pattern counts separately from recorded observations.',
|
|
118
|
-
evidence: [{ observed: `last distillation ${
|
|
123
|
+
evidence: [{ observed: `last distillation ${Number.isFinite(age) ? (age / 86400).toFixed(1) + " days ago" : "not recorded"}; ${learning.pendingObservations} of ${learning?.observations ?? learning?.trajectories ?? 0} observations waiting, oldest ${(waited / 86400).toFixed(1)} days${learning?.learningDb ? ` in ${learning.learningDb}` : ''}` }],
|
|
119
124
|
cost: { time: 'under a minute', risk: 'local structural patterns; snapshot retained; automatic exact restore unavailable' },
|
|
120
125
|
change: { human: 'run one training cycle so captured work becomes reusable patterns' },
|
|
121
126
|
undo: { human: 'retain the WAL-safe snapshot for supervised offline recovery; automatic exact restore is unavailable' },
|
|
@@ -48,6 +48,7 @@ import { CANARY_VERDICT_KIND, CORPUS_TAG_PATTERN, REQUIRED_CANARY_CHECKS } from
|
|
|
48
48
|
import { inventoryFootprint } from '../plugin/scripts/brain-footprint.mjs';
|
|
49
49
|
import { eligibleRepositoryStanding } from '../plugin/scripts/coverage-integrity.mjs';
|
|
50
50
|
import { TRANSIENT_HTTP, retryTransientAsync } from './transient-gh.mjs';
|
|
51
|
+
import { isDirectInvocation } from '../plugin/scripts/entrypoint.mjs';
|
|
51
52
|
|
|
52
53
|
export const PUBLIC_API = 'https://api.github.com';
|
|
53
54
|
export const FRESHNESS_LIMIT_MS = 48 * 3_600_000;
|
|
@@ -579,6 +580,6 @@ async function main(argv = process.argv.slice(2)) {
|
|
|
579
580
|
return record.verdict === 'PASS' ? 0 : 1;
|
|
580
581
|
}
|
|
581
582
|
|
|
582
|
-
if (
|
|
583
|
+
if (isDirectInvocation(import.meta.url)) {
|
|
583
584
|
main().then((code) => { process.exitCode = code; });
|
|
584
585
|
}
|
|
@@ -34,6 +34,7 @@ import { canonicalJson, digest, fileIdentity, sha256File, validateGistAggregateR
|
|
|
34
34
|
import { auditCorpusStores, isPassingState } from './rvf-index-audit.mjs';
|
|
35
35
|
import { readDiagnosticAccuracyReport } from './oracle/retrieval-accuracy.mjs';
|
|
36
36
|
import { loadFixture, readRecallReport } from './oracle/repo-recall.mjs';
|
|
37
|
+
import { isDirectInvocation } from '../plugin/scripts/entrypoint.mjs';
|
|
37
38
|
|
|
38
39
|
const HERE = path.dirname(fileURLToPath(import.meta.url));
|
|
39
40
|
const REQUIRED_SUFFIXES = [
|
|
@@ -571,8 +572,7 @@ async function main() {
|
|
|
571
572
|
// with no archive and no receipt on disk. Same defect, same fix as plugin/scripts/hook-input.mjs:518.
|
|
572
573
|
function isMain() {
|
|
573
574
|
try {
|
|
574
|
-
|
|
575
|
-
return fs.realpathSync(process.argv[1]) === fs.realpathSync(fileURLToPath(import.meta.url));
|
|
575
|
+
return isDirectInvocation(import.meta.url);
|
|
576
576
|
} catch {
|
|
577
577
|
return false;
|
|
578
578
|
}
|
|
@@ -26,6 +26,7 @@ import fs from 'node:fs';
|
|
|
26
26
|
import path from 'node:path';
|
|
27
27
|
import { fileURLToPath } from 'node:url';
|
|
28
28
|
import { eligibleRepositoryStanding, validateCoverageLedger } from '../plugin/scripts/coverage-integrity.mjs';
|
|
29
|
+
import { isDirectInvocation } from '../plugin/scripts/entrypoint.mjs';
|
|
29
30
|
|
|
30
31
|
export const COVERAGE_ASSET = 'CORPUS-COVERAGE.json';
|
|
31
32
|
export const COVERAGE_RECEIPT_ASSET = 'coverage-receipt.json';
|
|
@@ -195,7 +196,7 @@ export function main(argv = process.argv.slice(2), { stdout = process.stdout, st
|
|
|
195
196
|
|
|
196
197
|
function isMain() {
|
|
197
198
|
try {
|
|
198
|
-
return
|
|
199
|
+
return isDirectInvocation(import.meta.url);
|
|
199
200
|
} catch {
|
|
200
201
|
return false;
|
|
201
202
|
}
|
|
@@ -34,6 +34,7 @@
|
|
|
34
34
|
import fs from 'node:fs';
|
|
35
35
|
import path from 'node:path';
|
|
36
36
|
import { pathToFileURL } from 'node:url';
|
|
37
|
+
import { isDirectInvocation } from '../plugin/scripts/entrypoint.mjs';
|
|
37
38
|
|
|
38
39
|
export const MIN_PIPELINE_VERSION = '4.3.38'; // sync-version-ignore: a fixed pipeline floor, not the current version
|
|
39
40
|
/** The dispatcher's second schedule entry; corpus-nightly-dispatch.yml's `on.schedule` must carry exactly this line. */
|
|
@@ -188,6 +189,6 @@ export async function main(argv = process.argv.slice(2), { stdout = process.stdo
|
|
|
188
189
|
return decision.verdict === 'fail' ? 1 : 0;
|
|
189
190
|
}
|
|
190
191
|
|
|
191
|
-
if (
|
|
192
|
+
if (isDirectInvocation(import.meta.url)) {
|
|
192
193
|
main().then((code) => { process.exitCode = code; });
|
|
193
194
|
}
|
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
// Correlation is not authorization: protected-release still enforces its own release authority.
|
|
2
2
|
import fs from 'node:fs';
|
|
3
3
|
import { pathToFileURL } from 'node:url';
|
|
4
|
+
import { isDirectInvocation } from '../plugin/scripts/entrypoint.mjs';
|
|
4
5
|
|
|
5
6
|
export function selectCorpusDispatch(runs, { sha, dispatchId, notBefore }) {
|
|
6
7
|
if (!/^[a-f0-9]{40}$/.test(sha || '') || !/^corpus-\d+-\d+$/.test(dispatchId || '')
|
|
@@ -12,7 +13,7 @@ export function selectCorpusDispatch(runs, { sha, dispatchId, notBefore }) {
|
|
|
12
13
|
return matches[0] || null;
|
|
13
14
|
}
|
|
14
15
|
|
|
15
|
-
if (
|
|
16
|
+
if (isDirectInvocation(import.meta.url)) {
|
|
16
17
|
const response = JSON.parse(fs.readFileSync(process.argv[2], 'utf8'));
|
|
17
18
|
const run = selectCorpusDispatch(response.workflow_runs || [], {
|
|
18
19
|
sha: process.env.CANDIDATE_SHA, dispatchId: process.env.CORPUS_DISPATCH_ID,
|
|
@@ -62,6 +62,7 @@ import { spawnSync } from 'node:child_process';
|
|
|
62
62
|
import { fileURLToPath, pathToFileURL } from 'node:url';
|
|
63
63
|
import { COVERAGE_ASSET, COVERAGE_RECEIPT_ASSET, bindCoverageToReceipt, verifyCoverageSidecar } from './corpus-coverage-sidecar.mjs';
|
|
64
64
|
import { retryTransientResult } from './transient-gh.mjs';
|
|
65
|
+
import { isDirectInvocation } from '../plugin/scripts/entrypoint.mjs';
|
|
65
66
|
|
|
66
67
|
const ROOT = path.resolve(path.dirname(fileURLToPath(import.meta.url)), '..');
|
|
67
68
|
const HEX64 = /^[0-9a-f]{64}$/;
|
|
@@ -423,7 +424,7 @@ export async function main(argv = process.argv.slice(2), { run = defaultRun, std
|
|
|
423
424
|
// temp directories, and the CLI then exits 0 having done nothing.
|
|
424
425
|
function isMain() {
|
|
425
426
|
try {
|
|
426
|
-
return
|
|
427
|
+
return isDirectInvocation(import.meta.url);
|
|
427
428
|
} catch {
|
|
428
429
|
return false;
|
|
429
430
|
}
|
package/scripts/corpus-qa.mjs
CHANGED
|
@@ -50,6 +50,7 @@ import path from 'node:path';
|
|
|
50
50
|
import readline from 'node:readline';
|
|
51
51
|
import { fileURLToPath } from 'node:url';
|
|
52
52
|
import { FULL_HINTS } from './full-hints.mjs';
|
|
53
|
+
import { isDirectInvocation } from '../plugin/scripts/entrypoint.mjs';
|
|
53
54
|
|
|
54
55
|
const ROOT = path.resolve(path.dirname(fileURLToPath(import.meta.url)), '..');
|
|
55
56
|
const KB = path.join(ROOT, 'kb');
|
|
@@ -245,7 +246,7 @@ export function discoverStores(dir) {
|
|
|
245
246
|
}
|
|
246
247
|
|
|
247
248
|
// ---------------- CLI ----------------
|
|
248
|
-
if (import.meta.url
|
|
249
|
+
if (isDirectInvocation(import.meta.url)) {
|
|
249
250
|
const arg = (f, d) => { const i = process.argv.indexOf(f); return i >= 0 && process.argv[i + 1] ? process.argv[i + 1] : d; };
|
|
250
251
|
const has = (f) => process.argv.includes(f);
|
|
251
252
|
const DIR = path.resolve(arg('--dir', KB));
|
|
@@ -26,6 +26,7 @@ import { captureGistSources } from './gist-receipts.mjs';
|
|
|
26
26
|
import { projectSourceStore, RUNTIME_LEDGER_KIND } from './rvf-generation.mjs';
|
|
27
27
|
import { compareKnowledgeInputs, fromCoverage as knowledgeFromCoverage, fromSeed as knowledgeFromSeed } from './knowledge-input-digest.mjs';
|
|
28
28
|
import { defaultBaseDelayMs } from './transient-gh.mjs';
|
|
29
|
+
import { isDirectInvocation } from '../plugin/scripts/entrypoint.mjs';
|
|
29
30
|
|
|
30
31
|
export { rebuildCorpusAggregates };
|
|
31
32
|
|
|
@@ -1306,8 +1307,7 @@ export async function main(argv = process.argv.slice(2), {
|
|
|
1306
1307
|
// with no archive and no receipt on disk. Same defect, same fix as plugin/scripts/hook-input.mjs:518.
|
|
1307
1308
|
function isMain() {
|
|
1308
1309
|
try {
|
|
1309
|
-
|
|
1310
|
-
return fs.realpathSync(process.argv[1]) === fs.realpathSync(fileURLToPath(import.meta.url));
|
|
1310
|
+
return isDirectInvocation(import.meta.url);
|
|
1311
1311
|
} catch {
|
|
1312
1312
|
return false;
|
|
1313
1313
|
}
|
|
@@ -48,6 +48,7 @@ import { AGGREGATE_ASSET, SIGNING_PUBLIC_KEY_FILE } from './approved-runtime.mjs
|
|
|
48
48
|
import { COVERAGE_ASSET, COVERAGE_RECEIPT_ASSET, verifyCoverageSidecar } from './corpus-coverage-sidecar.mjs';
|
|
49
49
|
import { parseCorpusGeneration } from './corpus-promotion.mjs';
|
|
50
50
|
import { retryTransientResult } from './transient-gh.mjs';
|
|
51
|
+
import { isDirectInvocation } from '../plugin/scripts/entrypoint.mjs';
|
|
51
52
|
|
|
52
53
|
const ROOT = path.resolve(path.dirname(fileURLToPath(import.meta.url)), '..');
|
|
53
54
|
export const RED = 'RED';
|
|
@@ -496,6 +497,6 @@ async function main(argv = process.argv.slice(2)) {
|
|
|
496
497
|
return result.verdict === RED ? 1 : 0;
|
|
497
498
|
}
|
|
498
499
|
|
|
499
|
-
if (
|
|
500
|
+
if (isDirectInvocation(import.meta.url)) {
|
|
500
501
|
main().then((code) => { process.exitCode = code; });
|
|
501
502
|
}
|
package/scripts/count-chunks.mjs
CHANGED
|
@@ -23,6 +23,7 @@
|
|
|
23
23
|
import path from 'node:path';
|
|
24
24
|
import { fileURLToPath } from 'node:url';
|
|
25
25
|
import { applyFix } from './claims-verify.mjs';
|
|
26
|
+
import { isDirectInvocation } from '../plugin/scripts/entrypoint.mjs';
|
|
26
27
|
|
|
27
28
|
const CHECK = process.argv.includes('--check');
|
|
28
29
|
|
|
@@ -49,6 +50,6 @@ async function main() {
|
|
|
49
50
|
console.log(`\n[count-chunks] done — ${want} chunks is the live count; ${drifted.length} surface(s) rewritten.`);
|
|
50
51
|
}
|
|
51
52
|
|
|
52
|
-
if (
|
|
53
|
+
if (isDirectInvocation(import.meta.url)) {
|
|
53
54
|
await main();
|
|
54
55
|
}
|
|
@@ -34,6 +34,7 @@ import { customerEnv, customerManifestUrl, installCustomer, installedModules } f
|
|
|
34
34
|
import { addPrivateStore, resolveRuntime } from './customer-seams.mjs';
|
|
35
35
|
export { resolveRuntime };
|
|
36
36
|
import { doctorSmokeArgs } from './installed-brain-health.mjs';
|
|
37
|
+
import { isDirectInvocation } from '../plugin/scripts/entrypoint.mjs';
|
|
37
38
|
|
|
38
39
|
export const REPO = 'stuinfla/ruvnet-brain';
|
|
39
40
|
const readJson = (file) => JSON.parse(fs.readFileSync(file, 'utf8'));
|
|
@@ -358,6 +359,6 @@ async function main(argv = process.argv.slice(2)) {
|
|
|
358
359
|
return 0;
|
|
359
360
|
}
|
|
360
361
|
|
|
361
|
-
if (
|
|
362
|
+
if (isDirectInvocation(import.meta.url)) {
|
|
362
363
|
main().then((code) => { process.exitCode = code; }, (error) => { console.error(error.stack || error.message); process.exitCode = 1; });
|
|
363
364
|
}
|
|
@@ -16,6 +16,7 @@ import { spawnSync } from 'node:child_process';
|
|
|
16
16
|
import { fileURLToPath } from 'node:url';
|
|
17
17
|
import { digest } from './coverage-integrity.mjs';
|
|
18
18
|
import { CONTENT_MAP_FILE, CONTENT_MAP_KIND, passageContentDigest } from './retrieval-passage-identity.mjs';
|
|
19
|
+
import { isDirectInvocation } from '../plugin/scripts/entrypoint.mjs';
|
|
19
20
|
|
|
20
21
|
const ROOT = path.resolve(path.dirname(fileURLToPath(import.meta.url)), '..');
|
|
21
22
|
const sha256File = (file) => crypto.createHash('sha256').update(fs.readFileSync(file)).digest('hex');
|
|
@@ -68,4 +69,4 @@ function main(argv) {
|
|
|
68
69
|
console.log(JSON.stringify({ out, resolved: Object.keys(map.entries).length, unresolved: map.unresolved.length }));
|
|
69
70
|
}
|
|
70
71
|
|
|
71
|
-
if (
|
|
72
|
+
if (isDirectInvocation(import.meta.url)) main(process.argv.slice(2));
|
|
@@ -5,6 +5,7 @@ import fs from 'node:fs';
|
|
|
5
5
|
import { execFileSync } from 'node:child_process';
|
|
6
6
|
import path from 'node:path';
|
|
7
7
|
import { fileURLToPath } from 'node:url';
|
|
8
|
+
import { isDirectInvocation } from '../plugin/scripts/entrypoint.mjs';
|
|
8
9
|
|
|
9
10
|
export function inspectPush(root, input) {
|
|
10
11
|
const checked = [];
|
|
@@ -27,7 +28,7 @@ export function inspectPush(root, input) {
|
|
|
27
28
|
return { ok: true, checked, scope: 'unpublished-commit-secret-scan' };
|
|
28
29
|
}
|
|
29
30
|
|
|
30
|
-
if (
|
|
31
|
+
if (isDirectInvocation(import.meta.url)) {
|
|
31
32
|
try {
|
|
32
33
|
const root = execFileSync('git', ['rev-parse', '--show-toplevel'], { encoding: 'utf8' }).trim();
|
|
33
34
|
console.log(JSON.stringify(inspectPush(root, fs.readFileSync(0, 'utf8'))));
|
|
@@ -26,6 +26,7 @@ import fs from 'node:fs';
|
|
|
26
26
|
import path from 'node:path';
|
|
27
27
|
import { pathToFileURL } from 'node:url';
|
|
28
28
|
import { CLAUDE_MODEL_IDS, CLAUDE_TIERS, PRICING, estTokens, estimateCosts, receiptLine, receiptsPath, priceOf } from './route-cheap.mjs';
|
|
29
|
+
import { isDirectInvocation } from '../plugin/scripts/entrypoint.mjs';
|
|
29
30
|
|
|
30
31
|
// Default input share when only a MEASURED TOTAL is known. A subagent's tokens are dominated by input
|
|
31
32
|
// (it re-reads files and tool output on every turn); its final report is small. 0.9 is an assumption,
|
|
@@ -109,4 +110,4 @@ function main() {
|
|
|
109
110
|
console.log(receiptLine(receipt.model, { cost: receipt.est_cost, frontier: receipt.est_frontier_cost, saved: receipt.saved, ref: receipt.inherited }));
|
|
110
111
|
}
|
|
111
112
|
|
|
112
|
-
if (
|
|
113
|
+
if (isDirectInvocation(import.meta.url)) main();
|
package/scripts/doc-currency.mjs
CHANGED
|
@@ -3,7 +3,8 @@
|
|
|
3
3
|
import fs from 'node:fs';
|
|
4
4
|
import { fileURLToPath } from 'node:url';
|
|
5
5
|
import { main } from '../plugin/scripts/doc-currency.mjs';
|
|
6
|
+
import { isDirectInvocation } from '../plugin/scripts/entrypoint.mjs';
|
|
6
7
|
export * from '../plugin/scripts/doc-currency.mjs';
|
|
7
8
|
let direct = false;
|
|
8
|
-
try { direct =
|
|
9
|
+
try { direct = isDirectInvocation(import.meta.url); } catch {}
|
|
9
10
|
if (direct) process.exit(main());
|
|
@@ -5,6 +5,7 @@
|
|
|
5
5
|
// the caller supplies the finding and the current open issues, and receives a disposition before
|
|
6
6
|
// invoking any GitHub write operation.
|
|
7
7
|
|
|
8
|
+
import { isDirectInvocation } from '../plugin/scripts/entrypoint.mjs';
|
|
8
9
|
const HEX_SHA = /^[0-9a-f]{7,64}$/i;
|
|
9
10
|
const ISSUE_MARKER = 'dream-fingerprint:';
|
|
10
11
|
|
|
@@ -98,7 +99,7 @@ export function buildIssueBody(finding, decision = assessFinding(finding)) {
|
|
|
98
99
|
// Adapter contract for the Dream Machine runner. Input is JSON on stdin:
|
|
99
100
|
// { "finding": { ... }, "openIssues": [{ "number", "title", "body" }] }
|
|
100
101
|
// Output is the disposition JSON. This command intentionally cannot create or mutate GitHub data.
|
|
101
|
-
if (
|
|
102
|
+
if (isDirectInvocation(import.meta.url)) {
|
|
102
103
|
let input = '';
|
|
103
104
|
process.stdin.setEncoding('utf8');
|
|
104
105
|
process.stdin.on('data', (chunk) => { input += chunk; });
|
|
@@ -6,6 +6,7 @@ import { spawn } from 'node:child_process';
|
|
|
6
6
|
import { fileURLToPath } from 'node:url';
|
|
7
7
|
import { probeSubscriptionHosts, subscriptionOnlyEnv } from './subscription-hosts.mjs';
|
|
8
8
|
import { DUAL_HOST_MODEL_IDS } from './review-model-defaults.mjs';
|
|
9
|
+
import { isDirectInvocation } from '../plugin/scripts/entrypoint.mjs';
|
|
9
10
|
|
|
10
11
|
const HOSTS = Object.freeze(['claude-code', 'codex']);
|
|
11
12
|
// Top subscription models verified on the native hosts on 2026-09-10.
|
|
@@ -322,21 +323,6 @@ export async function main(argv = process.argv.slice(2), {
|
|
|
322
323
|
}
|
|
323
324
|
}
|
|
324
325
|
|
|
325
|
-
|
|
326
|
-
// NOT follow symlinks, while import.meta.url IS symlink-resolved by Node. Through a symlink (npm bin
|
|
327
|
-
// shims, wrapper scripts, and every os.tmpdir() path on macOS) the two sides disagree, so main()
|
|
328
|
-
// never runs -- and because nothing throws, the process exits 0. A silent exit 0 is indistinguishable
|
|
329
|
-
// from "ran, found nothing", which is how prepareCorpusCandidate once reported SUCCESS with no
|
|
330
|
-
// archive on disk. Reproduced live 2026-07-27; pinned by tests/unit/entrypoint-symlink.test.mjs.
|
|
331
|
-
function isDirectInvocation() {
|
|
332
|
-
try {
|
|
333
|
-
if (!process.argv[1]) return false;
|
|
334
|
-
return fs.realpathSync(process.argv[1]) === fs.realpathSync(fileURLToPath(import.meta.url));
|
|
335
|
-
} catch {
|
|
336
|
-
return false;
|
|
337
|
-
}
|
|
338
|
-
}
|
|
339
|
-
|
|
340
|
-
if (isDirectInvocation()) {
|
|
326
|
+
if (isDirectInvocation(import.meta.url)) {
|
|
341
327
|
process.exitCode = await main();
|
|
342
328
|
}
|