session-orchestrator 5.2.0 → 5.3.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/.agents/skills/architecture/SKILL.md +3 -1
- package/.agents/skills/autopilot/SKILL.md +5 -1
- package/.agents/skills/autopilot/agents/openai.yaml +5 -0
- package/.agents/skills/bootstrap/SKILL.md +5 -1
- package/.agents/skills/bootstrap/agents/openai.yaml +5 -0
- package/.agents/skills/brainstorm/SKILL.md +5 -1
- package/.agents/skills/brainstorm/agents/openai.yaml +5 -0
- package/.agents/skills/claude-md-drift-check/SKILL.md +3 -1
- package/.agents/skills/close/SKILL.md +5 -1
- package/.agents/skills/close/agents/openai.yaml +5 -0
- package/.agents/skills/convergence-monitoring/SKILL.md +4 -2
- package/.agents/skills/debug/SKILL.md +5 -1
- package/.agents/skills/debug/agents/openai.yaml +5 -0
- package/.agents/skills/discovery/SKILL.md +5 -1
- package/.agents/skills/discovery/agents/openai.yaml +5 -0
- package/.agents/skills/dispatcher/SKILL.md +5 -1
- package/.agents/skills/dispatcher/agents/openai.yaml +5 -0
- package/.agents/skills/docs-orchestrator/SKILL.md +3 -1
- package/.agents/skills/ecosystem-health/SKILL.md +3 -1
- package/.agents/skills/eli5/SKILL.md +5 -1
- package/.agents/skills/eli5/agents/openai.yaml +5 -0
- package/.agents/skills/eval/SKILL.md +6 -2
- package/.agents/skills/eval/agents/openai.yaml +5 -0
- package/.agents/skills/evolve/SKILL.md +6 -2
- package/.agents/skills/evolve/agents/openai.yaml +5 -0
- package/.agents/skills/frontmatter-guard/SKILL.md +3 -1
- package/.agents/skills/gitlab-ops/SKILL.md +3 -1
- package/.agents/skills/gitlab-portfolio/SKILL.md +3 -1
- package/.agents/skills/go/SKILL.md +5 -1
- package/.agents/skills/go/agents/openai.yaml +5 -0
- package/.agents/skills/grill/SKILL.md +5 -1
- package/.agents/skills/grill/agents/openai.yaml +5 -0
- package/.agents/skills/harness-audit/SKILL.md +5 -1
- package/.agents/skills/harness-audit/agents/openai.yaml +5 -0
- package/.agents/skills/hook-development/SKILL.md +3 -1
- package/.agents/skills/mcp-builder/SKILL.md +3 -1
- package/.agents/skills/memory-cleanup/SKILL.md +5 -1
- package/.agents/skills/memory-cleanup/agents/openai.yaml +5 -0
- package/.agents/skills/mode-selector/SKILL.md +3 -1
- package/.agents/skills/npm-publish/SKILL.md +4 -2
- package/.agents/skills/peekaboo-driver/SKILL.md +3 -1
- package/.agents/skills/persona-panel/SKILL.md +5 -1
- package/.agents/skills/persona-panel/agents/openai.yaml +5 -0
- package/.agents/skills/plan/SKILL.md +5 -1
- package/.agents/skills/plan/agents/openai.yaml +5 -0
- package/.agents/skills/playwright-driver/SKILL.md +3 -1
- package/.agents/skills/portfolio/SKILL.md +5 -1
- package/.agents/skills/portfolio/agents/openai.yaml +5 -0
- package/.agents/skills/quality-gates/SKILL.md +3 -1
- package/.agents/skills/reconcile/SKILL.md +5 -1
- package/.agents/skills/reconcile/agents/openai.yaml +5 -0
- package/.agents/skills/release/SKILL.md +5 -1
- package/.agents/skills/release/agents/openai.yaml +5 -0
- package/.agents/skills/remote-offload/SKILL.md +3 -1
- package/.agents/skills/repo-audit/SKILL.md +5 -1
- package/.agents/skills/repo-audit/agents/openai.yaml +5 -0
- package/.agents/skills/session/SKILL.md +21 -0
- package/.agents/skills/session/agents/openai.yaml +5 -0
- package/.agents/skills/session-end/SKILL.md +3 -1
- package/.agents/skills/session-plan/SKILL.md +3 -1
- package/.agents/skills/session-start/SKILL.md +3 -1
- package/.agents/skills/spinout/SKILL.md +5 -1
- package/.agents/skills/spinout/agents/openai.yaml +5 -0
- package/.agents/skills/sunset-review/SKILL.md +5 -1
- package/.agents/skills/sunset-review/agents/openai.yaml +5 -0
- package/.agents/skills/templates-ack/SKILL.md +21 -0
- package/.agents/skills/templates-ack/agents/openai.yaml +5 -0
- package/.agents/skills/test/SKILL.md +5 -1
- package/.agents/skills/test/agents/openai.yaml +5 -0
- package/.agents/skills/test-runner/SKILL.md +3 -1
- package/.agents/skills/tmux-layout/SKILL.md +3 -1
- package/.agents/skills/using-orchestrator/SKILL.md +3 -1
- package/.agents/skills/ux-grill/SKILL.md +5 -1
- package/.agents/skills/ux-grill/agents/openai.yaml +5 -0
- package/.agents/skills/vault-mirror/SKILL.md +3 -1
- package/.agents/skills/vault-sync/SKILL.md +3 -1
- package/.agents/skills/wave-executor/SKILL.md +3 -1
- package/.agents/skills/write-executable-plan/SKILL.md +3 -1
- package/.claude-plugin/marketplace.json +1 -1
- package/.claude-plugin/plugin.json +1 -1
- package/.codex-plugin/plugin.json +4 -4
- package/.codex-plugin/skills/convergence-monitoring/SKILL.md +1 -3
- package/.codex-plugin/skills/eval/SKILL.md +1 -1
- package/.codex-plugin/skills/evolve/SKILL.md +1 -1
- package/.codex-plugin/skills/npm-publish/SKILL.md +1 -3
- package/.codex-plugin/skills/session/SKILL.md +1 -1
- package/.cursor/commands/eval.md +1 -1
- package/.cursor/commands/session.md +1 -1
- package/.cursor/rules/000-session-orchestrator.mdc +0 -2
- package/.cursor/rules/050-plan.mdc +1 -1
- package/.cursor/skills/convergence-monitoring/SKILL.md +1 -0
- package/.cursor/skills/eval/SKILL.md +1 -1
- package/.cursor/skills/npm-publish/SKILL.md +1 -0
- package/.cursor-plugin/plugin.json +1 -1
- package/.orchestrator/policy/blocked-commands.json +12 -3
- package/AGENTS.md +3 -2
- package/CHANGELOG.md +136 -0
- package/README.md +9 -9
- package/SECURITY.md +12 -0
- package/agents/dialectic-deriver.md +13 -10
- package/agents/eval-judge.md +67 -45
- package/agents/skill-applied-judge.md +34 -19
- package/commands/session.md +7 -3
- package/docs/baseline.md +12 -6
- package/docs/codex-setup.md +14 -2
- package/docs/components.md +7 -5
- package/docs/events-schema.md +56 -9
- package/docs/rule-authoring.md +58 -6
- package/docs/session-config-reference.md +100 -7
- package/docs/session-config-template.md +31 -2
- package/docs/telemetry.md +2 -0
- package/hooks/_lib/hook-import-set.json +85 -8
- package/hooks/_lib/subagent-transcript.mjs +582 -31
- package/hooks/config-protection.mjs +11 -3
- package/hooks/cwd-change-restore.mjs +11 -3
- package/hooks/enforce-commands.mjs +70 -23
- package/hooks/enforce-scope.mjs +143 -33
- package/hooks/hooks-codex.json +1 -1
- package/hooks/hooks.json +1 -1
- package/hooks/loop-guard.mjs +11 -3
- package/hooks/on-session-end.mjs +58 -23
- package/hooks/on-session-start.mjs +48 -11
- package/hooks/on-stop.mjs +168 -22
- package/hooks/operator-steer.mjs +11 -3
- package/hooks/post-bash-issue-budget-refund.mjs +18 -8
- package/hooks/post-bash-write-verify.mjs +3 -2
- package/hooks/post-edit-import-probe.mjs +17 -9
- package/hooks/post-edit-validate.mjs +13 -5
- package/hooks/post-subagent-discovery-validator.mjs +98 -13
- package/hooks/post-tool-batch-wave-signal.mjs +200 -38
- package/hooks/post-tool-failure-corrective-context.mjs +11 -5
- package/hooks/post-tooluse-frontend-slop.mjs +10 -4
- package/hooks/pre-auq-clarity.mjs +15 -2
- package/hooks/pre-bash-destructive-guard.mjs +80 -9
- package/hooks/pre-bash-issue-budget.mjs +16 -11
- package/hooks/pre-bash-memory-propose-audit.mjs +86 -54
- package/hooks/pre-bash-sessions-ledger-guard.mjs +391 -20
- package/hooks/pre-bash-staging-fence.mjs +335 -31
- package/hooks/pre-bash-templates-first.mjs +19 -14
- package/hooks/pre-task-scope-disjoint.mjs +233 -2
- package/hooks/subagent-telemetry.mjs +15 -19
- package/hooks/wave-scope-commit-guard.mjs +197 -100
- package/monitors/monitors.json +1 -1
- package/output-styles/wave-summary.md +1 -1
- package/package.json +1 -1
- package/pi/prompts/eval.md +1 -1
- package/pi/prompts/session.md +1 -1
- package/rules/README.md +1 -1
- package/rules/opt-in-domain/prompt-caching.md +1 -1
- package/rules/opt-in-stack/backend-data.md +1 -1
- package/rules/opt-in-stack/backend.md +3 -3
- package/rules/opt-in-stack/frontend.md +1 -1
- package/rules/opt-in-stack/security-web.md +3 -3
- package/rules/opt-in-stack/swift.md +1 -1
- package/scripts/autopilot.mjs +23 -2
- package/scripts/backfill-abandoned-sessions.mjs +117 -15
- package/scripts/check-sessions-integrity.mjs +300 -0
- package/scripts/dialectic-deriver.mjs +50 -13
- package/scripts/emit-session.mjs +75 -29
- package/scripts/eval-session.mjs +65 -3
- package/scripts/generate-agents-skills.mjs +102 -29
- package/scripts/generate-cursor-adapter.mjs +61 -16
- package/scripts/lib/agent-status.mjs +2 -31
- package/scripts/lib/auq/clarity.mjs +10 -2
- package/scripts/lib/auq/parse.mjs +12 -31
- package/scripts/lib/auq/schema.mjs +56 -41
- package/scripts/lib/auto-dialectic.mjs +304 -15
- package/scripts/lib/autopilot/flags.mjs +12 -1
- package/scripts/lib/autopilot/kill-switches.mjs +6 -3
- package/scripts/lib/autopilot/loop.mjs +14 -1
- package/scripts/lib/autopilot/stall-sampler.mjs +80 -23
- package/scripts/lib/ci-status-banner.mjs +376 -16
- package/scripts/lib/command-blocker.mjs +275 -28
- package/scripts/lib/config/dialectic.mjs +12 -3
- package/scripts/lib/config/gate.mjs +74 -0
- package/scripts/lib/config/reaper.mjs +162 -0
- package/scripts/lib/config.mjs +14 -0
- package/scripts/lib/convergence-monitor.mjs +74 -11
- package/scripts/lib/ecosystem-health.mjs +11 -0
- package/scripts/lib/eval/engine.mjs +421 -53
- package/scripts/lib/eval/judge.mjs +463 -40
- package/scripts/lib/eval/schema.mjs +10 -1
- package/scripts/lib/events-rotation.mjs +221 -25
- package/scripts/lib/events-schema.mjs +114 -0
- package/scripts/lib/events.mjs +524 -5
- package/scripts/lib/frontmatter-guard.mjs +21 -10
- package/scripts/lib/gates/gate-baseline.mjs +27 -2
- package/scripts/lib/gates/gate-full.mjs +28 -3
- package/scripts/lib/gates/gate-helpers.mjs +243 -21
- package/scripts/lib/gates/gate-incremental.mjs +28 -3
- package/scripts/lib/gates/gate-per-file.mjs +27 -2
- package/scripts/lib/gitlab-portfolio/markdown-writer.mjs +6 -1
- package/scripts/lib/instruction-budget-guard.mjs +146 -4
- package/scripts/lib/io.mjs +42 -8
- package/scripts/lib/issue-close-strip-labels.mjs +207 -49
- package/scripts/lib/js-mask.mjs +197 -0
- package/scripts/lib/learnings/evolve-telemetry.mjs +11 -7
- package/scripts/lib/maintenance-due-banner.mjs +53 -88
- package/scripts/lib/orphan-reaper.mjs +1588 -0
- package/scripts/lib/peer-cards/merger.mjs +48 -10
- package/scripts/lib/peer-cards/reader.mjs +78 -2
- package/scripts/lib/process-group.mjs +899 -0
- package/scripts/lib/quality-gate.mjs +107 -28
- package/scripts/lib/reconcile/backlog.mjs +368 -0
- package/scripts/lib/reconcile/engine.mjs +55 -188
- package/scripts/lib/reconcile/rule-expiry-sweep.mjs +302 -60
- package/scripts/lib/reconcile/sanitize.mjs +69 -3
- package/scripts/lib/reconcile-nudge-banner.mjs +138 -45
- package/scripts/lib/resource-probe/parsers.mjs +31 -0
- package/scripts/lib/rule-loader.mjs +41 -12
- package/scripts/lib/scope-echo.mjs +39 -2
- package/scripts/lib/scope-gate.mjs +605 -1
- package/scripts/lib/session-close-backfill.mjs +33 -6
- package/scripts/lib/session-id.mjs +9 -20
- package/scripts/lib/session-invocation.mjs +20 -0
- package/scripts/lib/session-schema/constants.mjs +30 -2
- package/scripts/lib/session-schema/normalizer.mjs +56 -4
- package/scripts/lib/session-schema.mjs +8 -3
- package/scripts/lib/session-start-probes.mjs +95 -10
- package/scripts/lib/sessions-canonical.mjs +23 -0
- package/scripts/lib/sessions-integrity-banner.mjs +7 -1
- package/scripts/lib/sessions-staleness-banner.mjs +193 -51
- package/scripts/lib/skill-evidence-window.mjs +891 -0
- package/scripts/lib/skill-evolution/candidate-intake.mjs +133 -12
- package/scripts/lib/skill-evolution/engine.mjs +18 -9
- package/scripts/lib/skill-judge.mjs +45 -3
- package/scripts/lib/tail-window.mjs +56 -0
- package/scripts/lib/telemetry/schema.mjs +30 -0
- package/scripts/lib/telemetry/sync.mjs +61 -6
- package/scripts/lib/telemetry-flush-health-banner.mjs +4 -22
- package/scripts/lib/test-runner/issue-reconcile.mjs +48 -16
- package/scripts/lib/tmux-layout/telemetry-stats.mjs +72 -13
- package/scripts/lib/user-invocable-skills.mjs +23 -3
- package/scripts/lib/ux-grill/reconcile.mjs +48 -22
- package/scripts/lib/validate/check-agents-skills.mjs +26 -15
- package/scripts/lib/validate/check-cursor-adapter.mjs +1 -0
- package/scripts/lib/validate/check-entry-guard.mjs +13 -50
- package/scripts/lib/validate/check-hook-entry-guards.mjs +636 -0
- package/scripts/lib/validate/check-pi-prompts.mjs +1 -0
- package/scripts/lib/validate/check-rules.mjs +7 -5
- package/scripts/lib/validate/check-skill-links.mjs +9 -1
- package/scripts/lib/validate/check-skill-script-paths.mjs +239 -27
- package/scripts/lib/validate/check-test-git-config-target.mjs +24 -34
- package/scripts/lib/validate/check-untracked-test-deps.mjs +7 -102
- package/scripts/lib/validate/check-unwired-features.mjs +130 -27
- package/scripts/lib/validate/check-validator-registration.mjs +34 -10
- package/scripts/lib/validate/confidential-names.mjs +10 -0
- package/scripts/lib/validate-vendored-rules.mjs +4 -3
- package/scripts/lib/vault-mirror/namespace.mjs +46 -8
- package/scripts/lib/vault-mirror/process.mjs +10 -3
- package/scripts/lib/vault-mirror/render-sessions.mjs +12 -2
- package/scripts/lib/vault-status/narrative-mirror.mjs +31 -7
- package/scripts/lib/vault-yaml.mjs +118 -0
- package/scripts/lib/worktree/lifecycle.mjs +153 -1
- package/scripts/release-session-lock.mjs +305 -0
- package/scripts/release.mjs +30 -5
- package/scripts/resolve-session-invocation.mjs +59 -0
- package/scripts/run-quality-gate.mjs +156 -17
- package/scripts/sweep-expired-rules.mjs +14 -3
- package/scripts/validate-plugin.mjs +12 -0
- package/scripts/validate-wave-scope.mjs +32 -105
- package/scripts/vault-mirror.mjs +9 -1
- package/skills/_shared/platform-tools.md +23 -11
- package/skills/autopilot/SKILL.md +22 -7
- package/skills/claude-md-drift-check/SKILL.md +1 -1
- package/skills/convergence-monitoring/README.md +8 -1
- package/skills/convergence-monitoring/SIGNALS.md +50 -6
- package/skills/convergence-monitoring/SKILL.md +15 -6
- package/skills/eval/SKILL.md +39 -24
- package/skills/eval/rubric-v1.md +1 -0
- package/skills/eval/rubric-v2.md +457 -0
- package/skills/evolve/SKILL.md +1 -1
- package/skills/evolve/references/evolve-dialectic-mode.md +42 -25
- package/skills/gitlab-ops/SKILL.md +3 -2
- package/skills/npm-publish/SKILL.md +1 -1
- package/skills/reconcile/SKILL.md +11 -0
- package/skills/session-end/SKILL.md +13 -16
- package/skills/session-end/discovery-scan.md +1 -1
- package/skills/session-end/phase-3-6-tail.md +55 -9
- package/skills/session-end/references/phase-5-issue-cleanup.md +9 -14
- package/skills/session-end/session-metrics-write.md +10 -0
- package/skills/session-plan/SKILL.md +17 -5
- package/skills/session-plan/references/session-plan-task-classification.md +2 -2
- package/skills/session-start/references/phase-4-ssot-environment-check.md +2 -1
- package/skills/ux-grill/SKILL.md +1 -1
- package/skills/wave-executor/SKILL.md +8 -4
- package/skills/wave-executor/circuit-breaker.md +2 -0
- package/skills/wave-executor/references/wave-executor-state-init.md +5 -3
- package/skills/wave-executor/references/wave-loop-dispatch.md +2 -1
- package/.codex-plugin/skills/convergence-monitoring/agents/openai.yaml +0 -5
- package/.codex-plugin/skills/npm-publish/agents/openai.yaml +0 -5
- package/.cursor/commands/convergence-monitoring.md +0 -13
- package/.cursor/commands/npm-publish.md +0 -13
- package/pi/prompts/convergence-monitoring.md +0 -11
- package/pi/prompts/npm-publish.md +0 -11
|
@@ -29,6 +29,12 @@
|
|
|
29
29
|
* generic pattern path — its `pattern: ">"` would FP-match nearly
|
|
30
30
|
* every redirect. Unresolved targets (variable/substitution) warn
|
|
31
31
|
* on stderr (fail-visible) and never block.
|
|
32
|
+
* rule.type === 'path-delete' (#1401): decided by the DELETE/RENAME
|
|
33
|
+
* operand via firstDeniedDeleteTarget (same target-denylist globs,
|
|
34
|
+
* same compiler), likewise shadowing the generic pattern path. The
|
|
35
|
+
* redirect class answers "what does this command WRITE"; nothing
|
|
36
|
+
* answered "what does it REMOVE", so `rm -f <ledger>` was allowed
|
|
37
|
+
* while `> <ledger>` was denied — 53,896 lines of telemetry lost.
|
|
32
38
|
* G6 no match → exit 0
|
|
33
39
|
*
|
|
34
40
|
* Telemetry (Epic #803 process-safety dimension): best-effort
|
|
@@ -46,8 +52,7 @@ import crypto from 'node:crypto';
|
|
|
46
52
|
import { pathToFileURL } from 'node:url';
|
|
47
53
|
|
|
48
54
|
import { shouldRunHook } from './_lib/profile-gate.mjs';
|
|
49
|
-
|
|
50
|
-
if (!shouldRunHook('pre-bash-destructive-guard')) process.exit(0);
|
|
55
|
+
import { isMainModule } from '../scripts/lib/is-main-module.mjs';
|
|
51
56
|
|
|
52
57
|
// ---------------------------------------------------------------------------
|
|
53
58
|
// #992 — late-bound repo dependencies
|
|
@@ -179,6 +184,8 @@ async function bootstrap() {
|
|
|
179
184
|
'redirectSpanEnd',
|
|
180
185
|
'resolveSegmentVerb',
|
|
181
186
|
'splitChainSegments',
|
|
187
|
+
'extractDeleteTargets',
|
|
188
|
+
'firstDeniedDeleteTarget',
|
|
182
189
|
],
|
|
183
190
|
},
|
|
184
191
|
},
|
|
@@ -877,6 +884,61 @@ async function main() {
|
|
|
877
884
|
continue; // unreachable (blockCommand never returns) — kept for clarity
|
|
878
885
|
}
|
|
879
886
|
|
|
887
|
+
// #1401 — path-delete rules are decided by the DELETE/RENAME operand via
|
|
888
|
+
// firstDeniedDeleteTarget, never by the generic pattern path below (same
|
|
889
|
+
// full-shadow contract as redirect-truncate above: this rule's `pattern` is
|
|
890
|
+
// a human-readable label, and routing it through commandMatchesBlocked
|
|
891
|
+
// would match either nothing or every `rm`).
|
|
892
|
+
//
|
|
893
|
+
// Why a second surface at all: the redirect rule answers "what does this
|
|
894
|
+
// command WRITE". Nothing answered "what does it REMOVE" — so `rm -f
|
|
895
|
+
// .orchestrator/metrics/events.jsonl.1` was ALLOWED while `> ` on the same
|
|
896
|
+
// file was DENIED (measured 2026-09-19, #1401: 53,896 ledger lines lost).
|
|
897
|
+
if (rule.type === 'path-delete') {
|
|
898
|
+
const collected = blocker.extractDeleteTargets(command);
|
|
899
|
+
const cutOff = collected.filter(
|
|
900
|
+
(e) => e.unresolved && (e.reason === 'budget-exhausted' || e.reason === 'depth-exceeded')
|
|
901
|
+
);
|
|
902
|
+
if (cutOff.length > 0) {
|
|
903
|
+
// A recursion cap that drops payloads silently is a bypass, not a cap
|
|
904
|
+
// (#988 T2) — surface it on the VISIBLE channel (#995).
|
|
905
|
+
//
|
|
906
|
+
// Deliberately NARROWER than the redirect branch above, which also
|
|
907
|
+
// surfaces `variable/substitution` operands: `rm -rf "$WORK"` is the
|
|
908
|
+
// ORDINARY spelling of a temp cleanup, so notifying on it would fire on
|
|
909
|
+
// a large share of benign commands and train the operator to ignore the
|
|
910
|
+
// channel (host-resources.md § HR-101 — a signal may only warn if it is
|
|
911
|
+
// rare). A cut-off marker, by contrast, needs 32+ payload segments or
|
|
912
|
+
// 3 levels of nesting: pathological by construction. Unresolved
|
|
913
|
+
// operands stay fail-visible on stderr below; neither class ever blocks.
|
|
914
|
+
const reasons = [...new Set(cutOff.map((e) => e.reason))].join(', ');
|
|
915
|
+
const msg = `pre-bash-destructive-guard: delete-target traversal cut off (${reasons}) — not matched (fail-visible)`;
|
|
916
|
+
process.stderr.write(`⚠ ${msg}\n`);
|
|
917
|
+
notices.push(msg);
|
|
918
|
+
}
|
|
919
|
+
const unresolvedOperands = collected.filter(
|
|
920
|
+
(e) => e.unresolved && e.reason === 'variable/substitution'
|
|
921
|
+
);
|
|
922
|
+
if (unresolvedOperands.length > 0) {
|
|
923
|
+
process.stderr.write(
|
|
924
|
+
`ℹ pre-bash-destructive-guard: ${unresolvedOperands.length} delete target(s) carry a variable/substitution — not matched (rule: ${id})\n`
|
|
925
|
+
);
|
|
926
|
+
}
|
|
927
|
+
const hit = blocker.firstDeniedDeleteTarget(rule, collected, { repoRoot: projectDir });
|
|
928
|
+
if (!hit) continue;
|
|
929
|
+
if (severity !== 'block') {
|
|
930
|
+
const msg = `pre-bash-destructive-guard: protected delete target matched (rule: ${id}) — ${rationale}`;
|
|
931
|
+
process.stderr.write(`⚠ ${msg}\n`);
|
|
932
|
+
notices.push(msg);
|
|
933
|
+
continue;
|
|
934
|
+
}
|
|
935
|
+
// Reason stays short (stdout-budget): verb + target, never the command.
|
|
936
|
+
// The policy `rationale` is long by design (it carries the measurement
|
|
937
|
+
// trail); blockCommand puts only its first line in the systemMessage.
|
|
938
|
+
await blockCommand(`${hit.verb} ${hit.target}`, id, rationale, command, sessionId);
|
|
939
|
+
continue; // unreachable (blockCommand never returns) — kept for clarity
|
|
940
|
+
}
|
|
941
|
+
|
|
880
942
|
// The rm-rf-destructive rule also fires for recursive+force rm flag variants
|
|
881
943
|
// the literal "rm -rf" pattern misses (`rm -r -f`, `rm -fr`) — #641 gap closure.
|
|
882
944
|
const matched = id === 'rm-rf-destructive'
|
|
@@ -984,10 +1046,19 @@ try {
|
|
|
984
1046
|
process.exit(0); // fail-open, but no longer fail-silent
|
|
985
1047
|
}
|
|
986
1048
|
|
|
987
|
-
//
|
|
988
|
-
|
|
989
|
-
|
|
990
|
-
|
|
991
|
-
|
|
992
|
-
|
|
993
|
-
|
|
1049
|
+
// Entry guard (#1393): run only as the node script the harness execs — a bare
|
|
1050
|
+
// `import()` must run no handler and must not exit the importing process.
|
|
1051
|
+
// Placed strictly AFTER all decision logic: it gates whether main() is invoked,
|
|
1052
|
+
// never what main() decides, so no deny path can be short-circuited by it.
|
|
1053
|
+
if (isMainModule(import.meta.url)) {
|
|
1054
|
+
// #211: exit 0 immediately (silent allow) when this hook is disabled via profile/env
|
|
1055
|
+
if (!shouldRunHook('pre-bash-destructive-guard')) process.exit(0);
|
|
1056
|
+
|
|
1057
|
+
// Top-level error handler — never let exit 1 leak
|
|
1058
|
+
main().catch((e) => {
|
|
1059
|
+
process.stderr.write(
|
|
1060
|
+
`⚠ pre-bash-destructive-guard: internal error — ${e?.message || e}\n`
|
|
1061
|
+
);
|
|
1062
|
+
process.exit(0); // fail-open on internal errors to avoid blocking legitimate work
|
|
1063
|
+
});
|
|
1064
|
+
}
|
|
@@ -67,13 +67,10 @@ import {
|
|
|
67
67
|
} from '../scripts/lib/issue-budget.mjs';
|
|
68
68
|
|
|
69
69
|
import { shouldRunHook } from './_lib/profile-gate.mjs';
|
|
70
|
+
import { isMainModule } from '../scripts/lib/is-main-module.mjs';
|
|
70
71
|
import { existsSync } from 'node:fs';
|
|
71
72
|
import path from 'node:path';
|
|
72
73
|
|
|
73
|
-
// Opt-out per session via SO_DISABLED_HOOKS=pre-bash-issue-budget; the
|
|
74
|
-
// "minimal"/"off" profiles disable it like every other non-core hook.
|
|
75
|
-
if (!shouldRunHook('pre-bash-issue-budget')) process.exit(0);
|
|
76
|
-
|
|
77
74
|
// ---------------------------------------------------------------------------
|
|
78
75
|
// Helpers
|
|
79
76
|
// ---------------------------------------------------------------------------
|
|
@@ -500,10 +497,18 @@ async function main() {
|
|
|
500
497
|
return emitAllow();
|
|
501
498
|
}
|
|
502
499
|
|
|
503
|
-
//
|
|
504
|
-
|
|
505
|
-
|
|
506
|
-
|
|
507
|
-
|
|
508
|
-
process.exit(0);
|
|
509
|
-
|
|
500
|
+
// Entry guard (#1393): run only as the node script the harness execs — a bare
|
|
501
|
+
// `import()` must run no handler and must not exit the importing process.
|
|
502
|
+
if (isMainModule(import.meta.url)) {
|
|
503
|
+
// Opt-out per session via SO_DISABLED_HOOKS=pre-bash-issue-budget; the
|
|
504
|
+
// "minimal"/"off" profiles disable it like every other non-core hook.
|
|
505
|
+
if (!shouldRunHook('pre-bash-issue-budget')) process.exit(0);
|
|
506
|
+
|
|
507
|
+
// Top-level error handler — fail open, same posture as the sibling hooks.
|
|
508
|
+
main().catch((e) => {
|
|
509
|
+
process.stderr.write(
|
|
510
|
+
`⚠ pre-bash-issue-budget: internal error — ${e?.message || e}\n`,
|
|
511
|
+
);
|
|
512
|
+
process.exit(0);
|
|
513
|
+
});
|
|
514
|
+
}
|
|
@@ -13,7 +13,8 @@
|
|
|
13
13
|
* G3 — regex match /\bnode\b.*\bmemory-propose\.mjs\b/i (no match → exit 0)
|
|
14
14
|
* G4 — resolve session_id (from stdin payload or .orchestrator/current-session.json)
|
|
15
15
|
* G5 — resolve wave (from the active wave-scope.json `wave` field, default 0)
|
|
16
|
-
* G6 —
|
|
16
|
+
* G6 — derive a NON-REVERSIBLE argv summary: sha256 command_hash + known flag
|
|
17
|
+
* NAMES + raw length. The command text itself never leaves this process.
|
|
17
18
|
* G7 — append event JSON line to .orchestrator/metrics/events.jsonl
|
|
18
19
|
* Always: exit 0 (never block)
|
|
19
20
|
*
|
|
@@ -22,6 +23,7 @@
|
|
|
22
23
|
*/
|
|
23
24
|
|
|
24
25
|
import { readStdin, emitAllow } from '../scripts/lib/io.mjs';
|
|
26
|
+
import crypto from 'node:crypto';
|
|
25
27
|
import { existsSync } from 'node:fs';
|
|
26
28
|
import { readFile } from 'node:fs/promises';
|
|
27
29
|
import path from 'node:path';
|
|
@@ -29,8 +31,7 @@ import { emitEvent } from '../scripts/lib/events.mjs';
|
|
|
29
31
|
import { findScopeFile } from '../scripts/lib/hardening.mjs';
|
|
30
32
|
|
|
31
33
|
import { shouldRunHook } from './_lib/profile-gate.mjs';
|
|
32
|
-
|
|
33
|
-
if (!shouldRunHook('pre-bash-memory-propose-audit')) process.exit(0);
|
|
34
|
+
import { isMainModule } from '../scripts/lib/is-main-module.mjs';
|
|
34
35
|
|
|
35
36
|
// ---------------------------------------------------------------------------
|
|
36
37
|
// Constants
|
|
@@ -48,59 +49,78 @@ if (!shouldRunHook('pre-bash-memory-propose-audit')) process.exit(0);
|
|
|
48
49
|
const MEMORY_PROPOSE_REGEX = /\bnode\b.*\bmemory-propose\.mjs\b/i;
|
|
49
50
|
|
|
50
51
|
/**
|
|
51
|
-
*
|
|
52
|
-
*
|
|
52
|
+
* The flag NAMES `scripts/memory-propose.mjs` accepts (its `parseArgs` options
|
|
53
|
+
* block, plus `--help`). CLOSED LIST BY DESIGN: `flags_present` is built by
|
|
54
|
+
* intersecting the command with this constant, never by scraping `--\S+` tokens
|
|
55
|
+
* out of the command. A scrape would re-open the leak this file just closed —
|
|
56
|
+
* `--insight --my-secret-value` would publish the value as if it were a flag.
|
|
53
57
|
*
|
|
54
|
-
*
|
|
55
|
-
*
|
|
56
|
-
*
|
|
57
|
-
* --insight unquoted → --insight [REDACTED]
|
|
58
|
+
* A flag the CLI does not know is simply not reported. That is the correct
|
|
59
|
+
* trade: the field exists to say WHICH of the audited CLI's switches were used,
|
|
60
|
+
* not to mirror the command line.
|
|
58
61
|
*/
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
// Issue #546: `\S+` would match an opening quote `"` or `'` (non-whitespace),
|
|
70
|
-
// and on malformed inputs (e.g. unbalanced or shell-already-unescaped values)
|
|
71
|
-
// could partial-match the value, leaving the tail unredacted. The negative
|
|
72
|
-
// lookahead `(?!["'])` forces `\S+` to be tried only on values that do not
|
|
73
|
-
// start with a quote, preserving the quoted-alt as the sole path for quoted
|
|
74
|
-
// values and preventing tail-leaks on malformed inputs.
|
|
75
|
-
const SENSITIVE_FLAGS_REGEX = /--(?:insight|subject|evidence|content|reason)(?:=("(?:[^"\\]|\\.)*"|'(?:[^'\\]|\\.)*'|(?!["'])\S+)|\s+("(?:[^"\\]|\\.)*"|'(?:[^'\\]|\\.)*'|(?!["'])\S+))/g;
|
|
62
|
+
const KNOWN_FLAGS = Object.freeze([
|
|
63
|
+
'type',
|
|
64
|
+
'subject',
|
|
65
|
+
'insight',
|
|
66
|
+
'evidence',
|
|
67
|
+
'confidence',
|
|
68
|
+
'dry-run',
|
|
69
|
+
'file-paths',
|
|
70
|
+
'help',
|
|
71
|
+
]);
|
|
76
72
|
|
|
77
73
|
// ---------------------------------------------------------------------------
|
|
78
74
|
// Helpers
|
|
79
75
|
// ---------------------------------------------------------------------------
|
|
80
76
|
|
|
81
77
|
/**
|
|
82
|
-
*
|
|
83
|
-
*
|
|
78
|
+
* sha256(command), truncated to 16 hex characters.
|
|
79
|
+
*
|
|
80
|
+
* WORD-FOR-WORD the `hashCommand()` of `hooks/enforce-commands.mjs:69` (itself
|
|
81
|
+
* taken from `hooks/pre-bash-destructive-guard.mjs:253`, which mirrors
|
|
82
|
+
* `loop-guard.mjs` `hashArgs()`). Deliberately duplicated rather than shared:
|
|
83
|
+
* a PreToolUse hook must not gain another loadable module that can fail at
|
|
84
|
+
* start — three lines are cheaper than a load error on the hot path. Verified
|
|
85
|
+
* before duplicating that no helper is EXPORTED at any of those sites.
|
|
86
|
+
*
|
|
87
|
+
* WHY THE HASH IS HERE (#1415, 2026-09-21): until today this hook wrote a
|
|
88
|
+
* merely flag-redacted `argv_truncated` (512 chars of command text) into
|
|
89
|
+
* `orchestrator.memory.propose_invoked` — a TRACKED `events.jsonl` line and,
|
|
90
|
+
* with the webhook configured, a network payload. Redaction covered only the
|
|
91
|
+
* five `--insight/--subject/--evidence/--content/--reason` values and said so
|
|
92
|
+
* in its own caveat: `$VAR`, `$(cat secret)` and any value written elsewhere
|
|
93
|
+
* on the line passed through. This was the THIRD and last open site of the
|
|
94
|
+
* class closed by `8f15f77b` (enforce-commands) and #1404 (staging-fence).
|
|
95
|
+
*
|
|
96
|
+
* The hash keeps the event COUNTABLE and GROUPABLE — repeat invocations of the
|
|
97
|
+
* same command still collapse to one key — which is the only property any
|
|
98
|
+
* consumer needed. Measured 2026-09-21 (`rg argv_truncated` over `scripts/
|
|
99
|
+
* hooks/ skills/ docs/`): ZERO production readers, the field's only consumers
|
|
100
|
+
* were this hook's own tests.
|
|
84
101
|
*
|
|
85
102
|
* @param {string} command
|
|
86
103
|
* @returns {string}
|
|
87
104
|
*/
|
|
88
|
-
function
|
|
89
|
-
return command.
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
|
|
105
|
+
function hashCommand(command) {
|
|
106
|
+
return crypto.createHash('sha256').update(command).digest('hex').slice(0, 16);
|
|
107
|
+
}
|
|
108
|
+
|
|
109
|
+
/**
|
|
110
|
+
* The subset of KNOWN_FLAGS that appears in the command, in KNOWN_FLAGS order.
|
|
111
|
+
*
|
|
112
|
+
* Names only — a value can never reach this array, because the array is built
|
|
113
|
+
* from the constant above and the command is only ever TESTED against it. Both
|
|
114
|
+
* spellings count (`--insight=x` and `--insight x`); the trailing boundary
|
|
115
|
+
* `(?![\w-])` keeps `--insightful` from reporting `insight`.
|
|
116
|
+
*
|
|
117
|
+
* @param {string} command
|
|
118
|
+
* @returns {string[]}
|
|
119
|
+
*/
|
|
120
|
+
function flagsPresent(command) {
|
|
121
|
+
return KNOWN_FLAGS.filter((flag) =>
|
|
122
|
+
new RegExp(`--${flag}(?![\\w-])`).test(command),
|
|
123
|
+
);
|
|
104
124
|
}
|
|
105
125
|
|
|
106
126
|
/**
|
|
@@ -180,8 +200,11 @@ async function main() {
|
|
|
180
200
|
// G5 — resolve wave
|
|
181
201
|
const wave = await resolveWave(projectDir);
|
|
182
202
|
|
|
183
|
-
// G6 —
|
|
184
|
-
|
|
203
|
+
// G6 — derive the non-reversible argv summary (#1415). Nothing downstream
|
|
204
|
+
// ever sees the command text: hash for grouping, known flag NAMES for
|
|
205
|
+
// shape, raw length for size — no operands, no paths, no values.
|
|
206
|
+
const commandHash = hashCommand(command);
|
|
207
|
+
const flags = flagsPresent(command);
|
|
185
208
|
|
|
186
209
|
// G7 — emit canonical event via emitEvent (single emission path: schema + webhook,
|
|
187
210
|
// replacing the local hand-rolled appendFileSync bypass).
|
|
@@ -193,7 +216,9 @@ async function main() {
|
|
|
193
216
|
await emitEvent('orchestrator.memory.propose_invoked', {
|
|
194
217
|
session_id: sessionId,
|
|
195
218
|
wave,
|
|
196
|
-
|
|
219
|
+
command_hash: commandHash,
|
|
220
|
+
flags_present: flags,
|
|
221
|
+
argv_length: command.length,
|
|
197
222
|
cwd: process.cwd(),
|
|
198
223
|
exit_code: null,
|
|
199
224
|
});
|
|
@@ -203,10 +228,17 @@ async function main() {
|
|
|
203
228
|
return emitAllow();
|
|
204
229
|
}
|
|
205
230
|
|
|
206
|
-
//
|
|
207
|
-
|
|
208
|
-
|
|
209
|
-
|
|
210
|
-
);
|
|
211
|
-
|
|
212
|
-
|
|
231
|
+
// Entry guard (#1393): run only as the node script the harness execs — a bare
|
|
232
|
+
// `import()` must run no handler and must not exit the importing process.
|
|
233
|
+
if (isMainModule(import.meta.url)) {
|
|
234
|
+
// exit 0 immediately when this hook is disabled via profile/env
|
|
235
|
+
if (!shouldRunHook('pre-bash-memory-propose-audit')) process.exit(0);
|
|
236
|
+
|
|
237
|
+
// Top-level error handler — never let exit 1 leak
|
|
238
|
+
main().catch((e) => {
|
|
239
|
+
process.stderr.write(
|
|
240
|
+
`⚠ pre-bash-memory-propose-audit: internal error — ${e?.message || e}\n`,
|
|
241
|
+
);
|
|
242
|
+
process.exit(0); // fail-open on internal errors
|
|
243
|
+
});
|
|
244
|
+
}
|