@dzhechkov/harness-core 0.8.10 → 0.8.20
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/.dz-manifest.json +490 -170
- package/LICENSE +13 -0
- package/README.md +420 -6
- package/dist/agents-policy.d.ts +15 -1
- package/dist/agents-policy.d.ts.map +1 -1
- package/dist/agents-policy.js +27 -1
- package/dist/agents-policy.js.map +1 -1
- package/dist/amendment-trace.d.ts +72 -0
- package/dist/amendment-trace.d.ts.map +1 -1
- package/dist/amendment-trace.js +385 -17
- package/dist/amendment-trace.js.map +1 -1
- package/dist/backlog-public.d.ts +153 -0
- package/dist/backlog-public.d.ts.map +1 -0
- package/dist/backlog-public.js +415 -0
- package/dist/backlog-public.js.map +1 -0
- package/dist/backlog-transitions.d.ts +48 -0
- package/dist/backlog-transitions.d.ts.map +1 -0
- package/dist/backlog-transitions.js +64 -0
- package/dist/backlog-transitions.js.map +1 -0
- package/dist/backlog.d.ts.map +1 -1
- package/dist/backlog.js +13 -0
- package/dist/backlog.js.map +1 -1
- package/dist/claim-check.d.ts.map +1 -1
- package/dist/claim-check.js +24 -2
- package/dist/claim-check.js.map +1 -1
- package/dist/claude-hooks-assets.d.ts +93 -0
- package/dist/claude-hooks-assets.d.ts.map +1 -0
- package/dist/claude-hooks-assets.js +208 -0
- package/dist/claude-hooks-assets.js.map +1 -0
- package/dist/clean-room-smoke.d.ts +83 -0
- package/dist/clean-room-smoke.d.ts.map +1 -0
- package/dist/clean-room-smoke.js +138 -0
- package/dist/clean-room-smoke.js.map +1 -0
- package/dist/cmd-usage.d.ts.map +1 -1
- package/dist/cmd-usage.js +36 -6
- package/dist/cmd-usage.js.map +1 -1
- package/dist/codex-hooks-assets.d.ts +25 -7
- package/dist/codex-hooks-assets.d.ts.map +1 -1
- package/dist/codex-hooks-assets.js +138 -30
- package/dist/codex-hooks-assets.js.map +1 -1
- package/dist/codex-hooks.d.ts +21 -1
- package/dist/codex-hooks.d.ts.map +1 -1
- package/dist/codex-hooks.js +21 -1
- package/dist/codex-hooks.js.map +1 -1
- package/dist/codex-invoke.d.ts +73 -0
- package/dist/codex-invoke.d.ts.map +1 -0
- package/dist/codex-invoke.js +80 -0
- package/dist/codex-invoke.js.map +1 -0
- package/dist/course-staleness.d.ts +19 -0
- package/dist/course-staleness.d.ts.map +1 -0
- package/dist/course-staleness.js +95 -0
- package/dist/course-staleness.js.map +1 -0
- package/dist/destructive-guard-hook.d.ts +40 -0
- package/dist/destructive-guard-hook.d.ts.map +1 -0
- package/dist/destructive-guard-hook.js +109 -0
- package/dist/destructive-guard-hook.js.map +1 -0
- package/dist/destructive-guard.d.ts +27 -0
- package/dist/destructive-guard.d.ts.map +1 -0
- package/dist/destructive-guard.js +2808 -0
- package/dist/destructive-guard.js.map +1 -0
- package/dist/discrimination-gate.d.ts +88 -3
- package/dist/discrimination-gate.d.ts.map +1 -1
- package/dist/discrimination-gate.js +173 -16
- package/dist/discrimination-gate.js.map +1 -1
- package/dist/event-chain.d.ts +30 -0
- package/dist/event-chain.d.ts.map +1 -1
- package/dist/event-chain.js +24 -0
- package/dist/event-chain.js.map +1 -1
- package/dist/feature-adr-routing.d.ts +95 -1
- package/dist/feature-adr-routing.d.ts.map +1 -1
- package/dist/feature-adr-routing.js +193 -27
- package/dist/feature-adr-routing.js.map +1 -1
- package/dist/guard.d.ts +98 -0
- package/dist/guard.d.ts.map +1 -1
- package/dist/guard.js +308 -0
- package/dist/guard.js.map +1 -1
- package/dist/harness-core-location.d.ts +18 -0
- package/dist/harness-core-location.d.ts.map +1 -0
- package/dist/harness-core-location.js +42 -0
- package/dist/harness-core-location.js.map +1 -0
- package/dist/index.d.ts +32 -11
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +32 -7
- package/dist/index.js.map +1 -1
- package/dist/lead-shift-gate.d.ts +95 -0
- package/dist/lead-shift-gate.d.ts.map +1 -0
- package/dist/lead-shift-gate.js +100 -0
- package/dist/lead-shift-gate.js.map +1 -0
- package/dist/ledger-backfill.d.ts +11 -1
- package/dist/ledger-backfill.d.ts.map +1 -1
- package/dist/ledger-backfill.js +19 -0
- package/dist/ledger-backfill.js.map +1 -1
- package/dist/loop-blobs.generated.d.ts +1 -1
- package/dist/loop-blobs.generated.d.ts.map +1 -1
- package/dist/loop-blobs.generated.js +12 -3
- package/dist/loop-blobs.generated.js.map +1 -1
- package/dist/loop-lint.d.ts.map +1 -1
- package/dist/loop-lint.js +56 -7
- package/dist/loop-lint.js.map +1 -1
- package/dist/loop-plan-graph.d.ts +1 -3
- package/dist/loop-plan-graph.d.ts.map +1 -1
- package/dist/loop-plan-graph.js +70 -1
- package/dist/loop-plan-graph.js.map +1 -1
- package/dist/loop-trace.d.ts.map +1 -1
- package/dist/loop-trace.js +16 -2
- package/dist/loop-trace.js.map +1 -1
- package/dist/managed-hooks.d.ts +5 -6
- package/dist/managed-hooks.d.ts.map +1 -1
- package/dist/managed-hooks.js +2 -2
- package/dist/managed-hooks.js.map +1 -1
- package/dist/model-recommender.d.ts.map +1 -1
- package/dist/model-recommender.js +14 -3
- package/dist/model-recommender.js.map +1 -1
- package/dist/mutation-gate.d.ts +39 -36
- package/dist/mutation-gate.d.ts.map +1 -1
- package/dist/mutation-gate.js +111 -5
- package/dist/mutation-gate.js.map +1 -1
- package/dist/operations.d.ts.map +1 -1
- package/dist/operations.js +90 -5
- package/dist/operations.js.map +1 -1
- package/dist/patterns.d.ts +23 -0
- package/dist/patterns.d.ts.map +1 -1
- package/dist/patterns.js +10 -0
- package/dist/patterns.js.map +1 -1
- package/dist/plugin.d.ts.map +1 -1
- package/dist/plugin.js +27 -5
- package/dist/plugin.js.map +1 -1
- package/dist/publish.d.ts +11 -0
- package/dist/publish.d.ts.map +1 -1
- package/dist/publish.js +16 -2
- package/dist/publish.js.map +1 -1
- package/dist/recommend.d.ts +4 -5
- package/dist/recommend.d.ts.map +1 -1
- package/dist/recommend.js +110 -45
- package/dist/recommend.js.map +1 -1
- package/dist/registry.d.ts +32 -1
- package/dist/registry.d.ts.map +1 -1
- package/dist/registry.js +168 -11
- package/dist/registry.js.map +1 -1
- package/dist/run-records.d.ts +3 -0
- package/dist/run-records.d.ts.map +1 -1
- package/dist/run-records.js +18 -0
- package/dist/run-records.js.map +1 -1
- package/dist/score.d.ts +95 -0
- package/dist/score.d.ts.map +1 -1
- package/dist/score.js +289 -6
- package/dist/score.js.map +1 -1
- package/dist/session-retro.d.ts +121 -2
- package/dist/session-retro.d.ts.map +1 -1
- package/dist/session-retro.js +454 -11
- package/dist/session-retro.js.map +1 -1
- package/dist/setup.d.ts +28 -0
- package/dist/setup.d.ts.map +1 -1
- package/dist/setup.js +223 -6
- package/dist/setup.js.map +1 -1
- package/dist/sign.d.ts.map +1 -1
- package/dist/sign.js +18 -1
- package/dist/sign.js.map +1 -1
- package/dist/skill-selection.d.ts +72 -0
- package/dist/skill-selection.d.ts.map +1 -0
- package/dist/skill-selection.js +76 -0
- package/dist/skill-selection.js.map +1 -0
- package/dist/skills-verify.d.ts +34 -1
- package/dist/skills-verify.d.ts.map +1 -1
- package/dist/skills-verify.js +82 -0
- package/dist/skills-verify.js.map +1 -1
- package/dist/stage-line.d.ts +68 -0
- package/dist/stage-line.d.ts.map +1 -0
- package/dist/stage-line.js +129 -0
- package/dist/stage-line.js.map +1 -0
- package/dist/statusline.d.ts +99 -0
- package/dist/statusline.d.ts.map +1 -1
- package/dist/statusline.js +310 -44
- package/dist/statusline.js.map +1 -1
- package/dist/stem.d.ts +12 -0
- package/dist/stem.d.ts.map +1 -0
- package/dist/stem.js +89 -0
- package/dist/stem.js.map +1 -0
- package/dist/store-counts.d.ts +26 -0
- package/dist/store-counts.d.ts.map +1 -0
- package/dist/store-counts.js +125 -0
- package/dist/store-counts.js.map +1 -0
- package/dist/store-guard.d.ts +106 -0
- package/dist/store-guard.d.ts.map +1 -0
- package/dist/store-guard.js +294 -0
- package/dist/store-guard.js.map +1 -0
- package/dist/swarm-brief.d.ts +95 -0
- package/dist/swarm-brief.d.ts.map +1 -0
- package/dist/swarm-brief.js +660 -0
- package/dist/swarm-brief.js.map +1 -0
- package/dist/telemetry-vocabulary.d.ts +7 -0
- package/dist/telemetry-vocabulary.d.ts.map +1 -1
- package/dist/telemetry-vocabulary.js +29 -0
- package/dist/telemetry-vocabulary.js.map +1 -1
- package/dist/trace-bundle.d.ts +8 -0
- package/dist/trace-bundle.d.ts.map +1 -1
- package/dist/trace-bundle.js +11 -0
- package/dist/trace-bundle.js.map +1 -1
- package/package.json +10 -9
- package/sbom.json +1033 -233
- package/src/agents-policy.ts +46 -2
- package/src/amendment-trace.ts +441 -19
- package/src/backlog-public.ts +503 -0
- package/src/backlog-transitions.ts +77 -0
- package/src/backlog.ts +12 -0
- package/src/claim-check.ts +25 -2
- package/src/claude-hooks-assets.ts +227 -0
- package/src/clean-room-smoke.ts +195 -0
- package/src/cmd-usage.ts +29 -5
- package/src/codex-hooks-assets.ts +140 -30
- package/src/codex-hooks.ts +21 -1
- package/src/codex-invoke.ts +138 -0
- package/src/course-staleness.ts +125 -0
- package/src/destructive-guard-hook.ts +151 -0
- package/src/destructive-guard.ts +3027 -0
- package/src/discrimination-gate.ts +262 -19
- package/src/event-chain.ts +41 -0
- package/src/feature-adr-routing.ts +220 -22
- package/src/guard.ts +358 -0
- package/src/harness-core-location.ts +44 -0
- package/src/index.ts +121 -7
- package/src/lead-shift-gate.ts +145 -0
- package/src/ledger-backfill.ts +20 -1
- package/src/loop-blobs.generated.ts +12 -3
- package/src/loop-lint.ts +52 -7
- package/src/loop-plan-graph.ts +66 -1
- package/src/loop-trace.ts +13 -1
- package/src/managed-hooks.ts +5 -6
- package/src/model-recommender.ts +14 -3
- package/src/mutation-gate.ts +165 -5
- package/src/operations.ts +83 -5
- package/src/patterns.ts +33 -0
- package/src/plugin.ts +27 -5
- package/src/publish.ts +27 -2
- package/src/recommend.ts +116 -46
- package/src/registry.ts +147 -13
- package/src/run-records.ts +23 -0
- package/src/score.ts +377 -7
- package/src/session-retro.ts +466 -11
- package/src/setup.ts +246 -9
- package/src/sign.ts +18 -1
- package/src/skill-selection.ts +111 -0
- package/src/skills-verify.ts +99 -1
- package/src/stage-line.ts +151 -0
- package/src/statusline.ts +396 -47
- package/src/stem.ts +87 -0
- package/src/store-counts.ts +154 -0
- package/src/store-guard.ts +388 -0
- package/src/swarm-brief.ts +661 -0
- package/src/telemetry-vocabulary.ts +36 -0
- package/src/trace-bundle.ts +10 -0
|
@@ -0,0 +1,151 @@
|
|
|
1
|
+
// destructive-guard-hook — the CONSUMER half of the destructive-command guard
|
|
2
|
+
// (feature `destructive-command-guard`, ADR-001, plan task T8).
|
|
3
|
+
//
|
|
4
|
+
// WHY THIS FILE EXISTS AT ALL. The classifier (`destructive-guard.ts`) already returned a verdict,
|
|
5
|
+
// and nothing on either host read it: the export existed, the consumer did not, so
|
|
6
|
+
// `rm -rf .agentic-qe` still executed on both runtimes. A verdict nobody consumes is a document,
|
|
7
|
+
// not a guard. This module is that consumer — and it is ONE module rather than two hook bodies,
|
|
8
|
+
// because the two hosts must give the SAME answer in the SAME words. A refusal text duplicated in
|
|
9
|
+
// two shell-spawned strings drifts silently the first time either is edited.
|
|
10
|
+
//
|
|
11
|
+
// PURE BY CONTRACT, exactly like the classifier it wraps: a payload in, an exit code and a piece of
|
|
12
|
+
// text out. No filesystem access, no subprocess, no environment. The hook bodies that call it read
|
|
13
|
+
// stdin, hand the parsed object over, print what comes back and exit with the code — nothing else.
|
|
14
|
+
//
|
|
15
|
+
// FAIL-OPEN ON OUR OWN FAILURE. Every branch that is not a decided `refuse` exits 0. An unparseable
|
|
16
|
+
// payload, a classifier that throws, a tool that is not a shell — none of them is evidence of a
|
|
17
|
+
// violation, and a guard that blocks on its own confusion is a guard the user switches off. This is
|
|
18
|
+
// the same polarity the sibling guards ship with (`claim-check-hook.cjs`, `dz-codex-veto.cjs`).
|
|
19
|
+
|
|
20
|
+
import { classifyDestructive } from './destructive-guard.js';
|
|
21
|
+
import type { DestructiveVerdict } from './destructive-guard.js';
|
|
22
|
+
|
|
23
|
+
/** Prefix of a BLOCKING message. Must appear verbatim for a block to be attributed to this guard. */
|
|
24
|
+
export const DESTRUCTIVE_REFUSE_MARKER = 'DZ-DESTRUCTIVE:';
|
|
25
|
+
|
|
26
|
+
/** Prefix of a non-blocking message: an `undecidable` verdict, or our own failure to read. */
|
|
27
|
+
export const DESTRUCTIVE_WARN_MARKER = 'DZ-DESTRUCTIVE-WARN:';
|
|
28
|
+
|
|
29
|
+
/**
|
|
30
|
+
* Which host contract the payload follows. The two differ in ONE measured respect, so the
|
|
31
|
+
* difference is a parameter rather than a guess:
|
|
32
|
+
*
|
|
33
|
+
* - `claude` — Claude Code sends `tool_name` for every tool and the registered matcher is `^Bash$`.
|
|
34
|
+
* A payload whose `tool_name` is not a shell tool is skipped WITHOUT looking at any command field,
|
|
35
|
+
* which is what the host contract promises and what the test pins.
|
|
36
|
+
* - `codex` — AM-8 of `crossrt-2-codex-hooks`: key on the PRESENCE of `tool_input.command`, never on
|
|
37
|
+
* `tool_name` equality. Codex's shell tool has been observed under more than one name
|
|
38
|
+
* (`Bash|shell|local_shell` is the matcher the emitted registry entry already carries), so a
|
|
39
|
+
* tool_name test there would silently disarm the guard the day the runtime renames its tool.
|
|
40
|
+
*/
|
|
41
|
+
export type DestructiveHookHost = 'claude' | 'codex';
|
|
42
|
+
|
|
43
|
+
/** Tool names that ARE a shell on the Claude host. */
|
|
44
|
+
const SHELL_TOOL_NAMES: ReadonlySet<string> = new Set(['Bash', 'bash', 'shell', 'local_shell']);
|
|
45
|
+
|
|
46
|
+
export interface DestructiveHookDecision {
|
|
47
|
+
/** 2 = block the call (the host contract on BOTH runtimes). 0 = let it through. */
|
|
48
|
+
readonly exitCode: 0 | 2;
|
|
49
|
+
/** Exactly what the hook body writes to stderr. Empty string ⇒ write nothing at all. */
|
|
50
|
+
readonly stderr: string;
|
|
51
|
+
/**
|
|
52
|
+
* What happened, for tests and for the helper's own notes log:
|
|
53
|
+
* `refuse` / `allow` / `undecidable` are the classifier's three outcomes;
|
|
54
|
+
* `skipped` = not a shell command, the classifier was never consulted;
|
|
55
|
+
* `unreadable` = the payload or the classifier failed us, and we passed rather than guessed.
|
|
56
|
+
*/
|
|
57
|
+
readonly outcome: 'refuse' | 'allow' | 'undecidable' | 'skipped' | 'unreadable';
|
|
58
|
+
/** The rule id from a refusal, so a note can record WHICH rule fired without the command line. */
|
|
59
|
+
readonly rule: string | null;
|
|
60
|
+
}
|
|
61
|
+
|
|
62
|
+
/**
|
|
63
|
+
* Render the limits block from THE VERDICT's own list — never from a copy kept here.
|
|
64
|
+
*
|
|
65
|
+
* ADR-001 obliges the guard to NAME what it does not decide, in its own text, on every verdict it
|
|
66
|
+
* speaks about: a guard silent about its limits reads as a guarantee that recursive deletion is
|
|
67
|
+
* covered, and it is not. Re-listing them in this file would be a second copy free to drift from
|
|
68
|
+
* the classifier's; `verdict.limits` is the single source.
|
|
69
|
+
*/
|
|
70
|
+
function renderLimits(limits: readonly string[]): string {
|
|
71
|
+
const head = 'Чего этот страж НЕ решает (печатается при каждом вердикте, чтобы он не читался как гарантия):';
|
|
72
|
+
return [head, ...limits.map((l) => ` - ${l}`)].join('\n');
|
|
73
|
+
}
|
|
74
|
+
|
|
75
|
+
function renderRefusal(verdict: DestructiveVerdict): string {
|
|
76
|
+
return [
|
|
77
|
+
`${DESTRUCTIVE_REFUSE_MARKER} ОТКАЗ — удаление по буквальному пути в защищаемое хранилище.`,
|
|
78
|
+
'',
|
|
79
|
+
`Путь: ${verdict.path ?? '(не назван)'}`,
|
|
80
|
+
`Правило: ${verdict.rule ?? '(не названо)'}`,
|
|
81
|
+
`Причина: ${verdict.reason}`,
|
|
82
|
+
'',
|
|
83
|
+
renderLimits(verdict.limits),
|
|
84
|
+
'',
|
|
85
|
+
'Санкционированное удаление защищаемого хранилища выполняет владелец вручную, вне сеанса агента.',
|
|
86
|
+
'',
|
|
87
|
+
].join('\n');
|
|
88
|
+
}
|
|
89
|
+
|
|
90
|
+
function renderUndecidable(verdict: DestructiveVerdict): string {
|
|
91
|
+
return [
|
|
92
|
+
`${DESTRUCTIVE_WARN_MARKER} команду не удалось разобрать (${verdict.reason}) — ПРОПУЩЕНА без отказа: «проверить не удалось» никогда не выдаётся за «запрещено» (AC-10).`,
|
|
93
|
+
renderLimits(verdict.limits),
|
|
94
|
+
'',
|
|
95
|
+
].join('\n');
|
|
96
|
+
}
|
|
97
|
+
|
|
98
|
+
function unreadable(message: string): DestructiveHookDecision {
|
|
99
|
+
return {
|
|
100
|
+
exitCode: 0,
|
|
101
|
+
// One line, and no limits block: there is no verdict here to quote limits FROM, and inventing
|
|
102
|
+
// one would be the same lie in the other direction.
|
|
103
|
+
stderr: `${DESTRUCTIVE_WARN_MARKER} ${message} — пропуск без отказа; отсутствие вердикта нарушением не является.\n`,
|
|
104
|
+
outcome: 'unreadable',
|
|
105
|
+
rule: null,
|
|
106
|
+
};
|
|
107
|
+
}
|
|
108
|
+
|
|
109
|
+
const SKIP: DestructiveHookDecision = { exitCode: 0, stderr: '', outcome: 'skipped', rule: null };
|
|
110
|
+
const ALLOW: DestructiveHookDecision = { exitCode: 0, stderr: '', outcome: 'allow', rule: null };
|
|
111
|
+
|
|
112
|
+
/**
|
|
113
|
+
* Turn one hook payload into an exit code and a piece of text. The whole policy, both hosts.
|
|
114
|
+
*
|
|
115
|
+
* @param payload the parsed stdin object, or `null`/anything else when it did not parse.
|
|
116
|
+
* @param host which host contract the payload follows — see {@link DestructiveHookHost}.
|
|
117
|
+
*/
|
|
118
|
+
export function decideDestructiveHook(payload: unknown, host: DestructiveHookHost): DestructiveHookDecision {
|
|
119
|
+
if (payload === null || typeof payload !== 'object') {
|
|
120
|
+
return unreadable('полезная нагрузка хука не прочиталась');
|
|
121
|
+
}
|
|
122
|
+
|
|
123
|
+
const p = payload as { tool_name?: unknown; tool_input?: unknown };
|
|
124
|
+
|
|
125
|
+
if (host === 'claude') {
|
|
126
|
+
// The Claude contract: decide from the tool NAME first, and on a non-shell tool return before
|
|
127
|
+
// any command field is even looked at.
|
|
128
|
+
const toolName = typeof p.tool_name === 'string' ? p.tool_name : '';
|
|
129
|
+
if (!SHELL_TOOL_NAMES.has(toolName)) return SKIP;
|
|
130
|
+
}
|
|
131
|
+
|
|
132
|
+
const input = p.tool_input;
|
|
133
|
+
const command = input !== null && typeof input === 'object' ? (input as { command?: unknown }).command : undefined;
|
|
134
|
+
if (typeof command !== 'string' || command === '') return SKIP;
|
|
135
|
+
|
|
136
|
+
let verdict: DestructiveVerdict;
|
|
137
|
+
try {
|
|
138
|
+
verdict = classifyDestructive(command);
|
|
139
|
+
} catch (err) {
|
|
140
|
+
// Our own failure. Never a refusal — see the FAIL-OPEN note at the top of the file.
|
|
141
|
+
return unreadable(`классификатор не смог вынести вердикт (${String((err as Error)?.message ?? err)})`);
|
|
142
|
+
}
|
|
143
|
+
|
|
144
|
+
if (verdict.outcome === 'refuse') {
|
|
145
|
+
return { exitCode: 2, stderr: renderRefusal(verdict), outcome: 'refuse', rule: verdict.rule };
|
|
146
|
+
}
|
|
147
|
+
if (verdict.outcome === 'undecidable') {
|
|
148
|
+
return { exitCode: 0, stderr: renderUndecidable(verdict), outcome: 'undecidable', rule: null };
|
|
149
|
+
}
|
|
150
|
+
return ALLOW;
|
|
151
|
+
}
|