@openagentsy/hooks 0.0.0-stage → 0.2.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/LICENSE +674 -0
- package/README.md +32 -2
- package/dist/accountability.d.ts +29 -0
- package/dist/accountability.d.ts.map +1 -0
- package/dist/accountability.js +21 -0
- package/dist/accountability.js.map +1 -0
- package/dist/accountability.test.d.ts +2 -0
- package/dist/accountability.test.d.ts.map +1 -0
- package/dist/accountability.test.js +42 -0
- package/dist/accountability.test.js.map +1 -0
- package/dist/approvals.d.ts +110 -0
- package/dist/approvals.d.ts.map +1 -0
- package/dist/approvals.js +464 -0
- package/dist/approvals.js.map +1 -0
- package/dist/approvals.test.d.ts +2 -0
- package/dist/approvals.test.d.ts.map +1 -0
- package/dist/approvals.test.js +307 -0
- package/dist/approvals.test.js.map +1 -0
- package/dist/base-tool.d.ts +26 -0
- package/dist/base-tool.d.ts.map +1 -0
- package/dist/base-tool.js +37 -0
- package/dist/base-tool.js.map +1 -0
- package/dist/base-tool.test.d.ts +2 -0
- package/dist/base-tool.test.d.ts.map +1 -0
- package/dist/base-tool.test.js +34 -0
- package/dist/base-tool.test.js.map +1 -0
- package/dist/eval/index.d.ts +3 -0
- package/dist/eval/index.d.ts.map +1 -0
- package/dist/eval/index.js +3 -0
- package/dist/eval/index.js.map +1 -0
- package/dist/eval/runner.d.ts +179 -0
- package/dist/eval/runner.d.ts.map +1 -0
- package/dist/eval/runner.js +616 -0
- package/dist/eval/runner.js.map +1 -0
- package/dist/eval/runner.test.d.ts +2 -0
- package/dist/eval/runner.test.d.ts.map +1 -0
- package/dist/eval/runner.test.js +431 -0
- package/dist/eval/runner.test.js.map +1 -0
- package/dist/eval/types.d.ts +173 -0
- package/dist/eval/types.d.ts.map +1 -0
- package/dist/eval/types.js +104 -0
- package/dist/eval/types.js.map +1 -0
- package/dist/event.d.ts +59 -0
- package/dist/event.d.ts.map +1 -0
- package/dist/event.js +178 -0
- package/dist/event.js.map +1 -0
- package/dist/event.test.d.ts +2 -0
- package/dist/event.test.d.ts.map +1 -0
- package/dist/event.test.js +49 -0
- package/dist/event.test.js.map +1 -0
- package/dist/index.d.ts +30 -0
- package/dist/index.d.ts.map +1 -0
- package/dist/index.js +29 -0
- package/dist/index.js.map +1 -0
- package/dist/lessons/capture.d.ts +66 -0
- package/dist/lessons/capture.d.ts.map +1 -0
- package/dist/lessons/capture.js +166 -0
- package/dist/lessons/capture.js.map +1 -0
- package/dist/lessons/capture.test.d.ts +2 -0
- package/dist/lessons/capture.test.d.ts.map +1 -0
- package/dist/lessons/capture.test.js +200 -0
- package/dist/lessons/capture.test.js.map +1 -0
- package/dist/lessons/decide.d.ts +90 -0
- package/dist/lessons/decide.d.ts.map +1 -0
- package/dist/lessons/decide.js +334 -0
- package/dist/lessons/decide.js.map +1 -0
- package/dist/lessons/decide.test.d.ts +2 -0
- package/dist/lessons/decide.test.d.ts.map +1 -0
- package/dist/lessons/decide.test.js +527 -0
- package/dist/lessons/decide.test.js.map +1 -0
- package/dist/lessons/id.d.ts +5 -0
- package/dist/lessons/id.d.ts.map +1 -0
- package/dist/lessons/id.js +20 -0
- package/dist/lessons/id.js.map +1 -0
- package/dist/lessons/index.d.ts +5 -0
- package/dist/lessons/index.d.ts.map +1 -0
- package/dist/lessons/index.js +5 -0
- package/dist/lessons/index.js.map +1 -0
- package/dist/lessons/model.d.ts +163 -0
- package/dist/lessons/model.d.ts.map +1 -0
- package/dist/lessons/model.js +181 -0
- package/dist/lessons/model.js.map +1 -0
- package/dist/lessons/model.test.d.ts +2 -0
- package/dist/lessons/model.test.d.ts.map +1 -0
- package/dist/lessons/model.test.js +178 -0
- package/dist/lessons/model.test.js.map +1 -0
- package/dist/lessons/permissions.test.d.ts +2 -0
- package/dist/lessons/permissions.test.d.ts.map +1 -0
- package/dist/lessons/permissions.test.js +88 -0
- package/dist/lessons/permissions.test.js.map +1 -0
- package/dist/lessons/store.d.ts +74 -0
- package/dist/lessons/store.d.ts.map +1 -0
- package/dist/lessons/store.js +231 -0
- package/dist/lessons/store.js.map +1 -0
- package/dist/output-checks.d.ts +64 -0
- package/dist/output-checks.d.ts.map +1 -0
- package/dist/output-checks.js +248 -0
- package/dist/output-checks.js.map +1 -0
- package/dist/output-checks.test.d.ts +2 -0
- package/dist/output-checks.test.d.ts.map +1 -0
- package/dist/output-checks.test.js +58 -0
- package/dist/output-checks.test.js.map +1 -0
- package/dist/paths.d.ts +37 -0
- package/dist/paths.d.ts.map +1 -0
- package/dist/paths.js +70 -0
- package/dist/paths.js.map +1 -0
- package/dist/paths.test.d.ts +2 -0
- package/dist/paths.test.d.ts.map +1 -0
- package/dist/paths.test.js +36 -0
- package/dist/paths.test.js.map +1 -0
- package/dist/policies/agi-framing.d.ts +12 -0
- package/dist/policies/agi-framing.d.ts.map +1 -0
- package/dist/policies/agi-framing.js +33 -0
- package/dist/policies/agi-framing.js.map +1 -0
- package/dist/policies/anti-anthropomorphism.d.ts +13 -0
- package/dist/policies/anti-anthropomorphism.d.ts.map +1 -0
- package/dist/policies/anti-anthropomorphism.js +34 -0
- package/dist/policies/anti-anthropomorphism.js.map +1 -0
- package/dist/policies/anti-dependence.d.ts +12 -0
- package/dist/policies/anti-dependence.d.ts.map +1 -0
- package/dist/policies/anti-dependence.js +33 -0
- package/dist/policies/anti-dependence.js.map +1 -0
- package/dist/policies/anti-sycophancy.d.ts +13 -0
- package/dist/policies/anti-sycophancy.d.ts.map +1 -0
- package/dist/policies/anti-sycophancy.js +34 -0
- package/dist/policies/anti-sycophancy.js.map +1 -0
- package/dist/policies/dark-pattern.d.ts +14 -0
- package/dist/policies/dark-pattern.d.ts.map +1 -0
- package/dist/policies/dark-pattern.js +44 -0
- package/dist/policies/dark-pattern.js.map +1 -0
- package/dist/policies/destructive-needs-approval.d.ts +14 -0
- package/dist/policies/destructive-needs-approval.d.ts.map +1 -0
- package/dist/policies/destructive-needs-approval.js +31 -0
- package/dist/policies/destructive-needs-approval.js.map +1 -0
- package/dist/policies/discovery.d.ts +12 -0
- package/dist/policies/discovery.d.ts.map +1 -0
- package/dist/policies/discovery.js +130 -0
- package/dist/policies/discovery.js.map +1 -0
- package/dist/policies/high-risk.d.ts +25 -0
- package/dist/policies/high-risk.d.ts.map +1 -0
- package/dist/policies/high-risk.js +105 -0
- package/dist/policies/high-risk.js.map +1 -0
- package/dist/policies/human-only-decisions.d.ts +14 -0
- package/dist/policies/human-only-decisions.d.ts.map +1 -0
- package/dist/policies/human-only-decisions.js +33 -0
- package/dist/policies/human-only-decisions.js.map +1 -0
- package/dist/policies/index.d.ts +29 -0
- package/dist/policies/index.d.ts.map +1 -0
- package/dist/policies/index.js +82 -0
- package/dist/policies/index.js.map +1 -0
- package/dist/policies/no-skip-git-hooks.d.ts +14 -0
- package/dist/policies/no-skip-git-hooks.d.ts.map +1 -0
- package/dist/policies/no-skip-git-hooks.js +31 -0
- package/dist/policies/no-skip-git-hooks.js.map +1 -0
- package/dist/policies/package-manager.d.ts +12 -0
- package/dist/policies/package-manager.d.ts.map +1 -0
- package/dist/policies/package-manager.js +29 -0
- package/dist/policies/package-manager.js.map +1 -0
- package/dist/policies/patterns.d.ts +17 -0
- package/dist/policies/patterns.d.ts.map +1 -0
- package/dist/policies/patterns.js +30 -0
- package/dist/policies/patterns.js.map +1 -0
- package/dist/policies/policies.test.d.ts +2 -0
- package/dist/policies/policies.test.d.ts.map +1 -0
- package/dist/policies/policies.test.js +210 -0
- package/dist/policies/policies.test.js.map +1 -0
- package/dist/policies/privacy-transparency.d.ts +14 -0
- package/dist/policies/privacy-transparency.d.ts.map +1 -0
- package/dist/policies/privacy-transparency.js +50 -0
- package/dist/policies/privacy-transparency.js.map +1 -0
- package/dist/policies/professional-displacement.d.ts +15 -0
- package/dist/policies/professional-displacement.d.ts.map +1 -0
- package/dist/policies/professional-displacement.js +45 -0
- package/dist/policies/professional-displacement.js.map +1 -0
- package/dist/policies/protect-policy.d.ts +14 -0
- package/dist/policies/protect-policy.d.ts.map +1 -0
- package/dist/policies/protect-policy.js +35 -0
- package/dist/policies/protect-policy.js.map +1 -0
- package/dist/policies/scope-enforcement.d.ts +14 -0
- package/dist/policies/scope-enforcement.d.ts.map +1 -0
- package/dist/policies/scope-enforcement.js +34 -0
- package/dist/policies/scope-enforcement.js.map +1 -0
- package/dist/policies/secret-scan-writes.d.ts +13 -0
- package/dist/policies/secret-scan-writes.d.ts.map +1 -0
- package/dist/policies/secret-scan-writes.js +38 -0
- package/dist/policies/secret-scan-writes.js.map +1 -0
- package/dist/policies/selection.d.ts +20 -0
- package/dist/policies/selection.d.ts.map +1 -0
- package/dist/policies/selection.js +93 -0
- package/dist/policies/selection.js.map +1 -0
- package/dist/policies/structural-bias.d.ts +14 -0
- package/dist/policies/structural-bias.d.ts.map +1 -0
- package/dist/policies/structural-bias.js +41 -0
- package/dist/policies/structural-bias.js.map +1 -0
- package/dist/policies/third-party-impact.d.ts +13 -0
- package/dist/policies/third-party-impact.d.ts.map +1 -0
- package/dist/policies/third-party-impact.js +38 -0
- package/dist/policies/third-party-impact.js.map +1 -0
- package/dist/policies/tool-call.d.ts +38 -0
- package/dist/policies/tool-call.d.ts.map +1 -0
- package/dist/policies/tool-call.js +53 -0
- package/dist/policies/tool-call.js.map +1 -0
- package/dist/policies/truthfulness-uncertainty.d.ts +13 -0
- package/dist/policies/truthfulness-uncertainty.d.ts.map +1 -0
- package/dist/policies/truthfulness-uncertainty.js +45 -0
- package/dist/policies/truthfulness-uncertainty.js.map +1 -0
- package/dist/policies/workspace-writes-only.d.ts +25 -0
- package/dist/policies/workspace-writes-only.d.ts.map +1 -0
- package/dist/policies/workspace-writes-only.js +54 -0
- package/dist/policies/workspace-writes-only.js.map +1 -0
- package/dist/policy-engine.d.ts +69 -0
- package/dist/policy-engine.d.ts.map +1 -0
- package/dist/policy-engine.js +145 -0
- package/dist/policy-engine.js.map +1 -0
- package/dist/policy-registry.d.ts +62 -0
- package/dist/policy-registry.d.ts.map +1 -0
- package/dist/policy-registry.js +63 -0
- package/dist/policy-registry.js.map +1 -0
- package/dist/policy-registry.test.d.ts +2 -0
- package/dist/policy-registry.test.d.ts.map +1 -0
- package/dist/policy-registry.test.js +79 -0
- package/dist/policy-registry.test.js.map +1 -0
- package/dist/policy-release.d.ts +44 -0
- package/dist/policy-release.d.ts.map +1 -0
- package/dist/policy-release.js +65 -0
- package/dist/policy-release.js.map +1 -0
- package/dist/policy.d.ts +141 -0
- package/dist/policy.d.ts.map +1 -0
- package/dist/policy.js +22 -0
- package/dist/policy.js.map +1 -0
- package/dist/quality/audit.d.ts +56 -0
- package/dist/quality/audit.d.ts.map +1 -0
- package/dist/quality/audit.js +52 -0
- package/dist/quality/audit.js.map +1 -0
- package/dist/quality/findings.d.ts +34 -0
- package/dist/quality/findings.d.ts.map +1 -0
- package/dist/quality/findings.js +121 -0
- package/dist/quality/findings.js.map +1 -0
- package/dist/quality/findings.test.d.ts +2 -0
- package/dist/quality/findings.test.d.ts.map +1 -0
- package/dist/quality/findings.test.js +79 -0
- package/dist/quality/findings.test.js.map +1 -0
- package/dist/quality/index.d.ts +20 -0
- package/dist/quality/index.d.ts.map +1 -0
- package/dist/quality/index.js +20 -0
- package/dist/quality/index.js.map +1 -0
- package/dist/quality/rule.d.ts +43 -0
- package/dist/quality/rule.d.ts.map +1 -0
- package/dist/quality/rule.js +162 -0
- package/dist/quality/rule.js.map +1 -0
- package/dist/quality/rule.test.d.ts +2 -0
- package/dist/quality/rule.test.d.ts.map +1 -0
- package/dist/quality/rule.test.js +187 -0
- package/dist/quality/rule.test.js.map +1 -0
- package/dist/quality/tools.d.ts +30 -0
- package/dist/quality/tools.d.ts.map +1 -0
- package/dist/quality/tools.js +33 -0
- package/dist/quality/tools.js.map +1 -0
- package/dist/quality/tools.test.d.ts +2 -0
- package/dist/quality/tools.test.d.ts.map +1 -0
- package/dist/quality/tools.test.js +41 -0
- package/dist/quality/tools.test.js.map +1 -0
- package/dist/quality/waive.d.ts +37 -0
- package/dist/quality/waive.d.ts.map +1 -0
- package/dist/quality/waive.js +39 -0
- package/dist/quality/waive.js.map +1 -0
- package/dist/quality/waivers.d.ts +63 -0
- package/dist/quality/waivers.d.ts.map +1 -0
- package/dist/quality/waivers.js +132 -0
- package/dist/quality/waivers.js.map +1 -0
- package/dist/quality/waivers.test.d.ts +2 -0
- package/dist/quality/waivers.test.d.ts.map +1 -0
- package/dist/quality/waivers.test.js +138 -0
- package/dist/quality/waivers.test.js.map +1 -0
- package/dist/registry.d.ts +86 -0
- package/dist/registry.d.ts.map +1 -0
- package/dist/registry.js +92 -0
- package/dist/registry.js.map +1 -0
- package/dist/registry.test.d.ts +2 -0
- package/dist/registry.test.d.ts.map +1 -0
- package/dist/registry.test.js +117 -0
- package/dist/registry.test.js.map +1 -0
- package/dist/rules.d.ts +107 -0
- package/dist/rules.d.ts.map +1 -0
- package/dist/rules.js +632 -0
- package/dist/rules.js.map +1 -0
- package/dist/rules.test.d.ts +2 -0
- package/dist/rules.test.d.ts.map +1 -0
- package/dist/rules.test.js +488 -0
- package/dist/rules.test.js.map +1 -0
- package/dist/secrets.d.ts +17 -0
- package/dist/secrets.d.ts.map +1 -0
- package/dist/secrets.js +17 -0
- package/dist/secrets.js.map +1 -0
- package/dist/secrets.test.d.ts +2 -0
- package/dist/secrets.test.d.ts.map +1 -0
- package/dist/secrets.test.js +20 -0
- package/dist/secrets.test.js.map +1 -0
- package/dist/shell-env.d.ts +23 -0
- package/dist/shell-env.d.ts.map +1 -0
- package/dist/shell-env.js +34 -0
- package/dist/shell-env.js.map +1 -0
- package/dist/shell-env.test.d.ts +2 -0
- package/dist/shell-env.test.d.ts.map +1 -0
- package/dist/shell-env.test.js +45 -0
- package/dist/shell-env.test.js.map +1 -0
- package/package.json +49 -4
|
@@ -0,0 +1,162 @@
|
|
|
1
|
+
import { readFile } from 'node:fs/promises';
|
|
2
|
+
import { join } from 'node:path';
|
|
3
|
+
// The narrow paths are the point. The barrel of the quality package carries
|
|
4
|
+
// the comment rules, and the comment rules carry the TypeScript compiler.
|
|
5
|
+
import { check } from '@openagentsy/quality/check';
|
|
6
|
+
import { compilerFreeRegistry } from '@openagentsy/quality/compiler-free';
|
|
7
|
+
import { formatFindings, MAX_REPORTED_FINDINGS } from '@openagentsy/quality/report';
|
|
8
|
+
import { readPreToolCall } from '../event.js';
|
|
9
|
+
import { createFindingsStore } from './findings.js';
|
|
10
|
+
import { loadWaiverLookup } from './waivers.js';
|
|
11
|
+
/**
|
|
12
|
+
* The quality check, run after a write.
|
|
13
|
+
*
|
|
14
|
+
* The rule runs the static rules on the files a call touched, records what
|
|
15
|
+
* it found, and tells the agent what to fix. It never stops the call: the
|
|
16
|
+
* plan's blocking is on the task, not on the write, and a write that has
|
|
17
|
+
* already happened cannot be stopped anyway. What it says is capped, so a
|
|
18
|
+
* check cannot flood the context of the session that wrote the file.
|
|
19
|
+
*
|
|
20
|
+
* The rules it runs are the ones that need no compiler. The comment rules
|
|
21
|
+
* need a TypeScript parse to find the comments of a file, and this rule
|
|
22
|
+
* runs on every write, so the plugin would carry the compiler and pay for
|
|
23
|
+
* a parse per write. The comment lint still runs in the `oa_quality_check`
|
|
24
|
+
* tool and in the audit command, where a parse is paid for on demand.
|
|
25
|
+
*/
|
|
26
|
+
/** The answer of a rule that lets the call through. */
|
|
27
|
+
const CONTINUE = { continue: true };
|
|
28
|
+
/**
|
|
29
|
+
* The rule.
|
|
30
|
+
*
|
|
31
|
+
* The mode decides what the rule does: `during` runs the check after every
|
|
32
|
+
* write, and `after` does nothing here — the findings are reported by the
|
|
33
|
+
* audit command instead.
|
|
34
|
+
*/
|
|
35
|
+
export function qualityAfterWrite(options) {
|
|
36
|
+
const store = options.store ?? createFindingsStore(options.stateRoot);
|
|
37
|
+
const run = options.check ?? check;
|
|
38
|
+
return {
|
|
39
|
+
id: 'quality-after-write',
|
|
40
|
+
event: 'PostToolCall',
|
|
41
|
+
block: false,
|
|
42
|
+
handler: async (event) => {
|
|
43
|
+
if (options.mode !== 'during')
|
|
44
|
+
return CONTINUE;
|
|
45
|
+
const call = readPreToolCall(event);
|
|
46
|
+
if (call === undefined)
|
|
47
|
+
return CONTINUE;
|
|
48
|
+
const paths = writtenPaths(call.files);
|
|
49
|
+
if (paths.length === 0)
|
|
50
|
+
return CONTINUE;
|
|
51
|
+
const findings = await findingsOf(run, paths, options);
|
|
52
|
+
if (findings === undefined)
|
|
53
|
+
return CONTINUE;
|
|
54
|
+
await recordOf(store, options, call.sessionID, paths, findings);
|
|
55
|
+
const open = await openFindings(findings, options);
|
|
56
|
+
if (open.length === 0)
|
|
57
|
+
return CONTINUE;
|
|
58
|
+
return {
|
|
59
|
+
transform: {
|
|
60
|
+
appendToResult: formatFindings(open.slice(0, MAX_REPORTED_FINDINGS)),
|
|
61
|
+
},
|
|
62
|
+
};
|
|
63
|
+
},
|
|
64
|
+
};
|
|
65
|
+
}
|
|
66
|
+
/**
|
|
67
|
+
* The paths a call wrote.
|
|
68
|
+
*
|
|
69
|
+
* A file that was deleted is in the list as well: there is nothing to read
|
|
70
|
+
* of it, and the findings it had are moot, so the store drops them.
|
|
71
|
+
*/
|
|
72
|
+
function writtenPaths(files) {
|
|
73
|
+
const paths = [];
|
|
74
|
+
for (const file of files) {
|
|
75
|
+
if (file.path === undefined || file.path === '')
|
|
76
|
+
continue;
|
|
77
|
+
if (!paths.includes(file.path))
|
|
78
|
+
paths.push(file.path);
|
|
79
|
+
}
|
|
80
|
+
return paths;
|
|
81
|
+
}
|
|
82
|
+
/**
|
|
83
|
+
* The findings of the check, when the check ran.
|
|
84
|
+
*
|
|
85
|
+
* A rule that throws is a bug in the rule, and the rules have their own
|
|
86
|
+
* tests. The write has already happened, so a broken check must not turn it
|
|
87
|
+
* into an error: the failure is dropped and the agent sees nothing.
|
|
88
|
+
*/
|
|
89
|
+
async function findingsOf(run, paths, options) {
|
|
90
|
+
try {
|
|
91
|
+
const design = options.design ?? (await readDesignMd(options.workspace));
|
|
92
|
+
const ctx = design === undefined ? {} : { design };
|
|
93
|
+
return await run(paths, ctx, { root: options.workspace, registry: compilerFreeRegistry });
|
|
94
|
+
}
|
|
95
|
+
catch {
|
|
96
|
+
return undefined;
|
|
97
|
+
}
|
|
98
|
+
}
|
|
99
|
+
/**
|
|
100
|
+
* The findings that are open: not taste, and not waived.
|
|
101
|
+
*
|
|
102
|
+
* The waivers are read when there is something to filter: a call that found
|
|
103
|
+
* nothing does not read the decisions of the project. A waiver a person
|
|
104
|
+
* recorded covers a finding, and a covered finding is not open.
|
|
105
|
+
*/
|
|
106
|
+
async function openFindings(findings, options) {
|
|
107
|
+
if (findings.length === 0)
|
|
108
|
+
return [];
|
|
109
|
+
const waivers = options.waivers ?? (await loadWaiverLookup(options.stateRoot));
|
|
110
|
+
return findings.filter((finding) => finding.tier !== 'finish' && !waivers.covers(finding));
|
|
111
|
+
}
|
|
112
|
+
/**
|
|
113
|
+
* Record the findings, when there is a task to record them for.
|
|
114
|
+
*
|
|
115
|
+
* A session with no task records nothing: there is no task to complete, so
|
|
116
|
+
* there is nothing for the findings to stop. An unwritable state root must
|
|
117
|
+
* not fail the write either.
|
|
118
|
+
*/
|
|
119
|
+
async function recordOf(store, options, sessionID, paths, findings) {
|
|
120
|
+
const taskId = await taskIdOf(options, sessionID);
|
|
121
|
+
if (taskId === undefined)
|
|
122
|
+
return false;
|
|
123
|
+
try {
|
|
124
|
+
await store.record(taskId, paths, findings);
|
|
125
|
+
return true;
|
|
126
|
+
}
|
|
127
|
+
catch {
|
|
128
|
+
return false;
|
|
129
|
+
}
|
|
130
|
+
}
|
|
131
|
+
/** The task of a session, when there is one. */
|
|
132
|
+
async function taskIdOf(options, sessionID) {
|
|
133
|
+
if (options.taskId !== undefined)
|
|
134
|
+
return options.taskId;
|
|
135
|
+
if (options.taskIdForSession === undefined || sessionID === undefined)
|
|
136
|
+
return undefined;
|
|
137
|
+
return await options.taskIdForSession(sessionID);
|
|
138
|
+
}
|
|
139
|
+
/**
|
|
140
|
+
* The design file of a project, read as data.
|
|
141
|
+
*
|
|
142
|
+
* The file is optional, and it is never read as instructions: it can name
|
|
143
|
+
* a taste rule and turn it off, and nothing else.
|
|
144
|
+
*/
|
|
145
|
+
export async function readDesignMd(workspace) {
|
|
146
|
+
for (const name of ['DESIGN.md', 'design.md']) {
|
|
147
|
+
const text = await readDesign(join(workspace, name));
|
|
148
|
+
if (text !== undefined)
|
|
149
|
+
return text;
|
|
150
|
+
}
|
|
151
|
+
return undefined;
|
|
152
|
+
}
|
|
153
|
+
/** The text of a file, when it can be read. */
|
|
154
|
+
async function readDesign(path) {
|
|
155
|
+
try {
|
|
156
|
+
return await readFile(path, 'utf8');
|
|
157
|
+
}
|
|
158
|
+
catch {
|
|
159
|
+
return undefined;
|
|
160
|
+
}
|
|
161
|
+
}
|
|
162
|
+
//# sourceMappingURL=rule.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"rule.js","sourceRoot":"","sources":["../../src/quality/rule.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"rule.test.d.ts","sourceRoot":"","sources":["../../src/quality/rule.test.ts"],"names":[],"mappings":""}
|
|
@@ -0,0 +1,187 @@
|
|
|
1
|
+
import { mkdtemp, writeFile } from 'node:fs/promises';
|
|
2
|
+
import { tmpdir } from 'node:os';
|
|
3
|
+
import { join } from 'node:path';
|
|
4
|
+
import { describe, expect, it } from 'vitest';
|
|
5
|
+
import { createFindingsStore } from './findings.js';
|
|
6
|
+
import { qualityAfterWrite } from './rule.js';
|
|
7
|
+
import { createWaiverStore } from './waivers.js';
|
|
8
|
+
/** A workspace the test writes into. */
|
|
9
|
+
async function scratch() {
|
|
10
|
+
return await mkdtemp(join(tmpdir(), 'openagentsy-quality-rule-'));
|
|
11
|
+
}
|
|
12
|
+
/** A finding, for the check to report. */
|
|
13
|
+
function finding(over = {}) {
|
|
14
|
+
return {
|
|
15
|
+
ruleId: 'css/no-transition-all',
|
|
16
|
+
source: 'hallmark:floor-2',
|
|
17
|
+
tier: 'reflex',
|
|
18
|
+
file: 'src/app.css',
|
|
19
|
+
line: 12,
|
|
20
|
+
message: 'A transition of all moves every property.',
|
|
21
|
+
fix: 'Name the properties to transition instead of all',
|
|
22
|
+
...over,
|
|
23
|
+
};
|
|
24
|
+
}
|
|
25
|
+
/** The event a write arrives as. */
|
|
26
|
+
function written(files) {
|
|
27
|
+
return {
|
|
28
|
+
toolName: 'apply_patch',
|
|
29
|
+
args: { patchText: '' },
|
|
30
|
+
files: files.map((path) => ({ path })),
|
|
31
|
+
sessionID: 'ses-1',
|
|
32
|
+
};
|
|
33
|
+
}
|
|
34
|
+
/** The text the rule appended to the result. */
|
|
35
|
+
function appended(result) {
|
|
36
|
+
if (typeof result !== 'object' || result === null)
|
|
37
|
+
return undefined;
|
|
38
|
+
const transform = result.transform;
|
|
39
|
+
if (typeof transform !== 'object' || transform === null)
|
|
40
|
+
return undefined;
|
|
41
|
+
const text = transform.appendToResult;
|
|
42
|
+
return typeof text === 'string' ? text : undefined;
|
|
43
|
+
}
|
|
44
|
+
describe('the quality rule', () => {
|
|
45
|
+
it('checks the files the patch touched and not the others', async () => {
|
|
46
|
+
const root = await scratch();
|
|
47
|
+
await writeFile(join(root, 'app.css'), '.a { transition: all 0.2s; }\n', 'utf8');
|
|
48
|
+
await writeFile(join(root, 'other.css'), '.b { transition: all 0.2s; }\n', 'utf8');
|
|
49
|
+
const rule = qualityAfterWrite({ workspace: root, stateRoot: root, mode: 'during' });
|
|
50
|
+
const result = await rule.handler(written(['app.css']));
|
|
51
|
+
const text = appended(result) ?? '';
|
|
52
|
+
expect(text).toContain('app.css');
|
|
53
|
+
expect(text).not.toContain('other.css');
|
|
54
|
+
});
|
|
55
|
+
it('runs the rules that need no compiler, and not the comment rules', async () => {
|
|
56
|
+
const root = await scratch();
|
|
57
|
+
await writeFile(join(root, 'app.css'), '.a { transition: all 0.2s; }\n', 'utf8');
|
|
58
|
+
await writeFile(join(root, 'app.ts'), '// Step 1: trim the input\nexport const a = 1;\n', 'utf8');
|
|
59
|
+
const rule = qualityAfterWrite({ workspace: root, stateRoot: root, mode: 'during' });
|
|
60
|
+
const result = await rule.handler(written(['app.css', 'app.ts']));
|
|
61
|
+
const text = appended(result) ?? '';
|
|
62
|
+
expect(text).toContain('css/no-transition-all');
|
|
63
|
+
expect(text).not.toContain('comment/');
|
|
64
|
+
});
|
|
65
|
+
it('shows the agent at most eight lines', async () => {
|
|
66
|
+
const root = await scratch();
|
|
67
|
+
const many = Array.from({ length: 12 }, (_unused, index) => finding({ line: index + 1 }));
|
|
68
|
+
const rule = qualityAfterWrite({
|
|
69
|
+
workspace: root,
|
|
70
|
+
stateRoot: root,
|
|
71
|
+
mode: 'during',
|
|
72
|
+
check: async () => many,
|
|
73
|
+
});
|
|
74
|
+
const result = await rule.handler(written(['src/app.css']));
|
|
75
|
+
expect((appended(result) ?? '').split('\n')).toHaveLength(8);
|
|
76
|
+
});
|
|
77
|
+
it('says nothing about a taste finding', async () => {
|
|
78
|
+
const root = await scratch();
|
|
79
|
+
const rule = qualityAfterWrite({
|
|
80
|
+
workspace: root,
|
|
81
|
+
stateRoot: root,
|
|
82
|
+
mode: 'during',
|
|
83
|
+
check: async () => [finding({ tier: 'finish', ruleId: 'copy/em-dash' })],
|
|
84
|
+
});
|
|
85
|
+
const result = await rule.handler(written(['src/app.css']));
|
|
86
|
+
expect(result).toEqual({ continue: true });
|
|
87
|
+
});
|
|
88
|
+
it('says nothing about a finding a waiver covers', async () => {
|
|
89
|
+
const root = await scratch();
|
|
90
|
+
await createWaiverStore(root).add({
|
|
91
|
+
ruleId: 'css/no-transition-all',
|
|
92
|
+
path: 'src/app.css',
|
|
93
|
+
reason: 'The file is a fixture, not a page.',
|
|
94
|
+
decidedBy: 'human',
|
|
95
|
+
ts: '2026-10-27T10:00:00.000Z',
|
|
96
|
+
});
|
|
97
|
+
const rule = qualityAfterWrite({
|
|
98
|
+
workspace: root,
|
|
99
|
+
stateRoot: root,
|
|
100
|
+
mode: 'during',
|
|
101
|
+
check: async () => [finding()],
|
|
102
|
+
});
|
|
103
|
+
const result = await rule.handler(written(['src/app.css']));
|
|
104
|
+
expect(result).toEqual({ continue: true });
|
|
105
|
+
});
|
|
106
|
+
it('still says something about a finding no waiver covers', async () => {
|
|
107
|
+
const root = await scratch();
|
|
108
|
+
await createWaiverStore(root).add({
|
|
109
|
+
ruleId: 'css/no-transition-all',
|
|
110
|
+
path: 'src/other.css',
|
|
111
|
+
reason: 'The file is a fixture, not a page.',
|
|
112
|
+
decidedBy: 'human',
|
|
113
|
+
ts: '2026-10-27T10:00:00.000Z',
|
|
114
|
+
});
|
|
115
|
+
const rule = qualityAfterWrite({
|
|
116
|
+
workspace: root,
|
|
117
|
+
stateRoot: root,
|
|
118
|
+
mode: 'during',
|
|
119
|
+
check: async () => [finding()],
|
|
120
|
+
});
|
|
121
|
+
const result = await rule.handler(written(['src/app.css']));
|
|
122
|
+
expect(appended(result) ?? '').toContain('css/no-transition-all');
|
|
123
|
+
});
|
|
124
|
+
it('records a floor finding for the task of the session', async () => {
|
|
125
|
+
const root = await scratch();
|
|
126
|
+
const store = createFindingsStore(root);
|
|
127
|
+
const rule = qualityAfterWrite({
|
|
128
|
+
workspace: root,
|
|
129
|
+
stateRoot: root,
|
|
130
|
+
mode: 'during',
|
|
131
|
+
store,
|
|
132
|
+
taskIdForSession: () => 'oa-1',
|
|
133
|
+
check: async () => [finding({ tier: 'floor', ruleId: 'copy/unsourced-claim' })],
|
|
134
|
+
});
|
|
135
|
+
await rule.handler(written(['src/app.css']));
|
|
136
|
+
const floor = await store.openFloor('oa-1');
|
|
137
|
+
expect(floor).toHaveLength(1);
|
|
138
|
+
expect(floor[0]?.ruleId).toBe('copy/unsourced-claim');
|
|
139
|
+
});
|
|
140
|
+
it('records nothing when the session has no task', async () => {
|
|
141
|
+
const root = await scratch();
|
|
142
|
+
const store = createFindingsStore(root);
|
|
143
|
+
const rule = qualityAfterWrite({
|
|
144
|
+
workspace: root,
|
|
145
|
+
stateRoot: root,
|
|
146
|
+
mode: 'during',
|
|
147
|
+
store,
|
|
148
|
+
check: async () => [finding({ tier: 'floor' })],
|
|
149
|
+
});
|
|
150
|
+
await rule.handler(written(['src/app.css']));
|
|
151
|
+
expect(await store.forTask('oa-1')).toEqual([]);
|
|
152
|
+
});
|
|
153
|
+
it('does nothing in the after mode', async () => {
|
|
154
|
+
const root = await scratch();
|
|
155
|
+
const store = createFindingsStore(root);
|
|
156
|
+
const rule = qualityAfterWrite({
|
|
157
|
+
workspace: root,
|
|
158
|
+
stateRoot: root,
|
|
159
|
+
mode: 'after',
|
|
160
|
+
store,
|
|
161
|
+
taskId: 'oa-1',
|
|
162
|
+
check: async () => [finding({ tier: 'floor' })],
|
|
163
|
+
});
|
|
164
|
+
const result = await rule.handler(written(['src/app.css']));
|
|
165
|
+
expect(result).toEqual({ continue: true });
|
|
166
|
+
expect(await store.forTask('oa-1')).toEqual([]);
|
|
167
|
+
});
|
|
168
|
+
it('does not fail the write when the check throws', async () => {
|
|
169
|
+
const root = await scratch();
|
|
170
|
+
const rule = qualityAfterWrite({
|
|
171
|
+
workspace: root,
|
|
172
|
+
stateRoot: root,
|
|
173
|
+
mode: 'during',
|
|
174
|
+
check: async () => {
|
|
175
|
+
throw new Error('the rule is broken');
|
|
176
|
+
},
|
|
177
|
+
});
|
|
178
|
+
const result = await rule.handler(written(['src/app.css']));
|
|
179
|
+
expect(result).toEqual({ continue: true });
|
|
180
|
+
});
|
|
181
|
+
it('lets a call that touched no file through', async () => {
|
|
182
|
+
const root = await scratch();
|
|
183
|
+
const rule = qualityAfterWrite({ workspace: root, stateRoot: root, mode: 'during' });
|
|
184
|
+
expect(await rule.handler(written([]))).toEqual({ continue: true });
|
|
185
|
+
});
|
|
186
|
+
});
|
|
187
|
+
//# sourceMappingURL=rule.test.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"rule.test.js","sourceRoot":"","sources":["../../src/quality/rule.test.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,30 @@
|
|
|
1
|
+
import { z } from 'zod';
|
|
2
|
+
/**
|
|
3
|
+
* The tools a session reads the findings with.
|
|
4
|
+
*
|
|
5
|
+
* The rule shows the agent eight lines, so that a check cannot flood the
|
|
6
|
+
* context of the session that wrote the file. The tool returns all of
|
|
7
|
+
* them: the cap is for the context, not for the answer.
|
|
8
|
+
*
|
|
9
|
+
* The tool runs every rule, the comment rules included. A call to the tool
|
|
10
|
+
* is a call a session made on purpose, and the comment rules need a
|
|
11
|
+
* TypeScript parse to find the comments of a file. The parse is paid for
|
|
12
|
+
* on demand here, and it is not paid at all on the write path.
|
|
13
|
+
*/
|
|
14
|
+
/** A tool, in the shape the server registers it in. */
|
|
15
|
+
export interface QualityTool {
|
|
16
|
+
/** What the tool does, for the model that may call it. */
|
|
17
|
+
description: string;
|
|
18
|
+
/** The arguments the tool takes. */
|
|
19
|
+
args: z.ZodType;
|
|
20
|
+
/** What the tool answers. */
|
|
21
|
+
execute(args: unknown, context: {
|
|
22
|
+
sessionID: string;
|
|
23
|
+
agent: string;
|
|
24
|
+
}): Promise<unknown>;
|
|
25
|
+
}
|
|
26
|
+
/** The quality tools, bound to a project. */
|
|
27
|
+
export declare function qualityTools(options: {
|
|
28
|
+
workspace: string;
|
|
29
|
+
}): Record<string, QualityTool>;
|
|
30
|
+
//# sourceMappingURL=tools.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"tools.d.ts","sourceRoot":"","sources":["../../src/quality/tools.ts"],"names":[],"mappings":"AACA,OAAO,EAAE,CAAC,EAAE,MAAM,KAAK,CAAC;AAGxB;;;;;;;;;;;GAWG;AAEH,uDAAuD;AACvD,MAAM,WAAW,WAAW;IAC1B,0DAA0D;IAC1D,WAAW,EAAE,MAAM,CAAC;IACpB,oCAAoC;IACpC,IAAI,EAAE,CAAC,CAAC,OAAO,CAAC;IAChB,6BAA6B;IAC7B,OAAO,CAAC,IAAI,EAAE,OAAO,EAAE,OAAO,EAAE;QAAE,SAAS,EAAE,MAAM,CAAC;QAAC,KAAK,EAAE,MAAM,CAAA;KAAE,GAAG,OAAO,CAAC,OAAO,CAAC,CAAC;CACzF;AAED,6CAA6C;AAC7C,wBAAgB,YAAY,CAAC,OAAO,EAAE;IAAE,SAAS,EAAE,MAAM,CAAA;CAAE,GAAG,MAAM,CAAC,MAAM,EAAE,WAAW,CAAC,CAmBxF"}
|
|
@@ -0,0 +1,33 @@
|
|
|
1
|
+
import { allRegistry, check } from '@openagentsy/quality';
|
|
2
|
+
import { z } from 'zod';
|
|
3
|
+
import { readDesignMd } from './rule.js';
|
|
4
|
+
/** The quality tools, bound to a project. */
|
|
5
|
+
export function qualityTools(options) {
|
|
6
|
+
return {
|
|
7
|
+
oa_quality_check: {
|
|
8
|
+
description: 'Check files for the quality rules and return every finding. The paths are relative to the project root.',
|
|
9
|
+
args: z.object({
|
|
10
|
+
paths: z.array(z.string().min(1)).default([]),
|
|
11
|
+
}),
|
|
12
|
+
execute: async (args) => {
|
|
13
|
+
const design = await readDesignMd(options.workspace);
|
|
14
|
+
const ctx = design === undefined ? {} : { design };
|
|
15
|
+
const findings = await check(pathsOf(args), ctx, {
|
|
16
|
+
root: options.workspace,
|
|
17
|
+
registry: allRegistry,
|
|
18
|
+
});
|
|
19
|
+
return { findings };
|
|
20
|
+
},
|
|
21
|
+
},
|
|
22
|
+
};
|
|
23
|
+
}
|
|
24
|
+
/** The paths the call named. */
|
|
25
|
+
function pathsOf(args) {
|
|
26
|
+
if (typeof args !== 'object' || args === null)
|
|
27
|
+
return [];
|
|
28
|
+
const paths = args.paths;
|
|
29
|
+
if (!Array.isArray(paths))
|
|
30
|
+
return [];
|
|
31
|
+
return paths.filter((path) => typeof path === 'string' && path !== '');
|
|
32
|
+
}
|
|
33
|
+
//# sourceMappingURL=tools.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"tools.js","sourceRoot":"","sources":["../../src/quality/tools.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,WAAW,EAAE,KAAK,EAAgB,MAAM,sBAAsB,CAAC;AACxE,OAAO,EAAE,CAAC,EAAE,MAAM,KAAK,CAAC;AACxB,OAAO,EAAE,YAAY,EAAE,MAAM,WAAW,CAAC;AAyBzC,6CAA6C;AAC7C,MAAM,UAAU,YAAY,CAAC,OAA8B;IACzD,OAAO;QACL,gBAAgB,EAAE;YAChB,WAAW,EACT,yGAAyG;YAC3G,IAAI,EAAE,CAAC,CAAC,MAAM,CAAC;gBACb,KAAK,EAAE,CAAC,CAAC,KAAK,CAAC,CAAC,CAAC,MAAM,EAAE,CAAC,GAAG,CAAC,CAAC,CAAC,CAAC,CAAC,OAAO,CAAC,EAAE,CAAC;aAC9C,CAAC;YACF,OAAO,EAAE,KAAK,EAAE,IAAI,EAA+B,EAAE;gBACnD,MAAM,MAAM,GAAG,MAAM,YAAY,CAAC,OAAO,CAAC,SAAS,CAAC,CAAC;gBACrD,MAAM,GAAG,GAAG,MAAM,KAAK,SAAS,CAAC,CAAC,CAAC,EAAE,CAAC,CAAC,CAAC,EAAE,MAAM,EAAE,CAAC;gBACnD,MAAM,QAAQ,GAAG,MAAM,KAAK,CAAC,OAAO,CAAC,IAAI,CAAC,EAAE,GAAG,EAAE;oBAC/C,IAAI,EAAE,OAAO,CAAC,SAAS;oBACvB,QAAQ,EAAE,WAAW;iBACtB,CAAC,CAAC;gBACH,OAAO,EAAE,QAAQ,EAAE,CAAC;YACtB,CAAC;SACF;KACF,CAAC;AACJ,CAAC;AAED,gCAAgC;AAChC,SAAS,OAAO,CAAC,IAAa;IAC5B,IAAI,OAAO,IAAI,KAAK,QAAQ,IAAI,IAAI,KAAK,IAAI;QAAE,OAAO,EAAE,CAAC;IACzD,MAAM,KAAK,GAAI,IAA4B,CAAC,KAAK,CAAC;IAClD,IAAI,CAAC,KAAK,CAAC,OAAO,CAAC,KAAK,CAAC;QAAE,OAAO,EAAE,CAAC;IACrC,OAAO,KAAK,CAAC,MAAM,CAAC,CAAC,IAAI,EAAkB,EAAE,CAAC,OAAO,IAAI,KAAK,QAAQ,IAAI,IAAI,KAAK,EAAE,CAAC,CAAC;AACzF,CAAC"}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"tools.test.d.ts","sourceRoot":"","sources":["../../src/quality/tools.test.ts"],"names":[],"mappings":""}
|
|
@@ -0,0 +1,41 @@
|
|
|
1
|
+
import { mkdtemp, writeFile } from 'node:fs/promises';
|
|
2
|
+
import { tmpdir } from 'node:os';
|
|
3
|
+
import { join } from 'node:path';
|
|
4
|
+
import { describe, expect, it } from 'vitest';
|
|
5
|
+
import { qualityTools } from './tools.js';
|
|
6
|
+
/** A workspace the test writes into. */
|
|
7
|
+
async function scratch() {
|
|
8
|
+
return await mkdtemp(join(tmpdir(), 'openagentsy-quality-tools-'));
|
|
9
|
+
}
|
|
10
|
+
/** The findings of an answer of the tool. */
|
|
11
|
+
function findingsOf(result) {
|
|
12
|
+
if (typeof result !== 'object' || result === null)
|
|
13
|
+
return [];
|
|
14
|
+
const list = result.findings;
|
|
15
|
+
return Array.isArray(list) ? list : [];
|
|
16
|
+
}
|
|
17
|
+
describe('the quality tool', () => {
|
|
18
|
+
it('returns the findings of every rule, the comment rules included', async () => {
|
|
19
|
+
const root = await scratch();
|
|
20
|
+
await writeFile(join(root, 'app.ts'), '// Step 1: trim the input\nexport const a = 1;\n', 'utf8');
|
|
21
|
+
const tools = qualityTools({ workspace: root });
|
|
22
|
+
const tool = tools.oa_quality_check;
|
|
23
|
+
if (tool === undefined)
|
|
24
|
+
throw new Error('the quality tool is missing');
|
|
25
|
+
const result = await tool.execute({ paths: ['app.ts'] }, { sessionID: 'ses-1', agent: 'oa-builder' });
|
|
26
|
+
const findings = findingsOf(result);
|
|
27
|
+
expect(findings.some((finding) => finding.ruleId === 'comment/step-narration')).toBe(true);
|
|
28
|
+
});
|
|
29
|
+
it('returns the findings of the rules that need no compiler as well', async () => {
|
|
30
|
+
const root = await scratch();
|
|
31
|
+
await writeFile(join(root, 'app.css'), '.a { transition: all 0.2s; }\n', 'utf8');
|
|
32
|
+
const tools = qualityTools({ workspace: root });
|
|
33
|
+
const tool = tools.oa_quality_check;
|
|
34
|
+
if (tool === undefined)
|
|
35
|
+
throw new Error('the quality tool is missing');
|
|
36
|
+
const result = await tool.execute({ paths: ['app.css'] }, { sessionID: 'ses-1', agent: 'oa-builder' });
|
|
37
|
+
const findings = findingsOf(result);
|
|
38
|
+
expect(findings.some((finding) => finding.ruleId === 'css/no-transition-all')).toBe(true);
|
|
39
|
+
});
|
|
40
|
+
});
|
|
41
|
+
//# sourceMappingURL=tools.test.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"tools.test.js","sourceRoot":"","sources":["../../src/quality/tools.test.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,37 @@
|
|
|
1
|
+
import { type Waiver } from './waivers.js';
|
|
2
|
+
/**
|
|
3
|
+
* The recording of a waiver.
|
|
4
|
+
*
|
|
5
|
+
* A person records a waiver; an agent may only propose one. The command a
|
|
6
|
+
* person runs and the tool the server registers both call this function, so
|
|
7
|
+
* the decision has one implementation and one audit trail.
|
|
8
|
+
*
|
|
9
|
+
* The module is not exported from the package barrel: the whole rule set
|
|
10
|
+
* carries the TypeScript compiler, and the plugin must not pay for it. The
|
|
11
|
+
* callers reach it by its own path, `@openagentsy/hooks/quality/waive`.
|
|
12
|
+
*/
|
|
13
|
+
/** What a person decides. */
|
|
14
|
+
export interface WaiveInput {
|
|
15
|
+
/** Where the waivers live. */
|
|
16
|
+
stateRoot: string;
|
|
17
|
+
/** The rule the waiver is for. */
|
|
18
|
+
ruleId: string;
|
|
19
|
+
/** The path the waiver covers. */
|
|
20
|
+
path: string;
|
|
21
|
+
/** Why. Ten characters at least. */
|
|
22
|
+
reason: string;
|
|
23
|
+
/** Who decided. A person, never an agent. */
|
|
24
|
+
decidedBy: string;
|
|
25
|
+
/** When the decision was made. */
|
|
26
|
+
ts?: string | undefined;
|
|
27
|
+
}
|
|
28
|
+
/**
|
|
29
|
+
* Record a waiver.
|
|
30
|
+
*
|
|
31
|
+
* A rule that cannot be waived is refused: a floor rule is honesty, function
|
|
32
|
+
* or accessibility, and the registry refuses a waivable floor rule at load.
|
|
33
|
+
* A reason that is too short is refused too, because a waiver without a
|
|
34
|
+
* reason is a decision nobody can check.
|
|
35
|
+
*/
|
|
36
|
+
export declare function waive(input: WaiveInput): Promise<Waiver>;
|
|
37
|
+
//# sourceMappingURL=waive.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"waive.d.ts","sourceRoot":"","sources":["../../src/quality/waive.ts"],"names":[],"mappings":"AACA,OAAO,EAAqB,KAAK,MAAM,EAAgB,MAAM,cAAc,CAAC;AAE5E;;;;;;;;;;GAUG;AAEH,6BAA6B;AAC7B,MAAM,WAAW,UAAU;IACzB,8BAA8B;IAC9B,SAAS,EAAE,MAAM,CAAC;IAClB,kCAAkC;IAClC,MAAM,EAAE,MAAM,CAAC;IACf,kCAAkC;IAClC,IAAI,EAAE,MAAM,CAAC;IACb,oCAAoC;IACpC,MAAM,EAAE,MAAM,CAAC;IACf,6CAA6C;IAC7C,SAAS,EAAE,MAAM,CAAC;IAClB,kCAAkC;IAClC,EAAE,CAAC,EAAE,MAAM,GAAG,SAAS,CAAC;CACzB;AAED;;;;;;;GAOG;AACH,wBAAsB,KAAK,CAAC,KAAK,EAAE,UAAU,GAAG,OAAO,CAAC,MAAM,CAAC,CAuB9D"}
|
|
@@ -0,0 +1,39 @@
|
|
|
1
|
+
import { allRegistry } from '@openagentsy/quality';
|
|
2
|
+
import { createWaiverStore, WaiverSchema } from './waivers.js';
|
|
3
|
+
/**
|
|
4
|
+
* Record a waiver.
|
|
5
|
+
*
|
|
6
|
+
* A rule that cannot be waived is refused: a floor rule is honesty, function
|
|
7
|
+
* or accessibility, and the registry refuses a waivable floor rule at load.
|
|
8
|
+
* A reason that is too short is refused too, because a waiver without a
|
|
9
|
+
* reason is a decision nobody can check.
|
|
10
|
+
*/
|
|
11
|
+
export async function waive(input) {
|
|
12
|
+
const rule = allRegistry.rules().find((candidate) => candidate.id === input.ruleId);
|
|
13
|
+
if (rule === undefined) {
|
|
14
|
+
throw new Error(`no rule is named ${input.ruleId}`);
|
|
15
|
+
}
|
|
16
|
+
if (!rule.waivable) {
|
|
17
|
+
throw new Error(`the rule ${input.ruleId} cannot be waived`);
|
|
18
|
+
}
|
|
19
|
+
if (input.reason.trim().length < 10) {
|
|
20
|
+
throw new Error('a waiver needs a reason of ten characters at least');
|
|
21
|
+
}
|
|
22
|
+
const waiver = WaiverSchema.safeParse({
|
|
23
|
+
ruleId: input.ruleId,
|
|
24
|
+
path: input.path,
|
|
25
|
+
reason: input.reason,
|
|
26
|
+
decidedBy: input.decidedBy,
|
|
27
|
+
ts: input.ts ?? new Date().toISOString(),
|
|
28
|
+
});
|
|
29
|
+
if (!waiver.success) {
|
|
30
|
+
throw new Error(`the waiver is not valid: ${issuesOf(waiver.error)}`);
|
|
31
|
+
}
|
|
32
|
+
await createWaiverStore(input.stateRoot).add(waiver.data);
|
|
33
|
+
return waiver.data;
|
|
34
|
+
}
|
|
35
|
+
/** What is wrong with a waiver, in one line. */
|
|
36
|
+
function issuesOf(error) {
|
|
37
|
+
return error.issues.map((issue) => `${issue.path.join('.')}: ${issue.message}`).join('; ');
|
|
38
|
+
}
|
|
39
|
+
//# sourceMappingURL=waive.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"waive.js","sourceRoot":"","sources":["../../src/quality/waive.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,WAAW,EAAE,MAAM,sBAAsB,CAAC;AACnD,OAAO,EAAE,iBAAiB,EAAe,YAAY,EAAE,MAAM,cAAc,CAAC;AA8B5E;;;;;;;GAOG;AACH,MAAM,CAAC,KAAK,UAAU,KAAK,CAAC,KAAiB;IAC3C,MAAM,IAAI,GAAG,WAAW,CAAC,KAAK,EAAE,CAAC,IAAI,CAAC,CAAC,SAAS,EAAE,EAAE,CAAC,SAAS,CAAC,EAAE,KAAK,KAAK,CAAC,MAAM,CAAC,CAAC;IACpF,IAAI,IAAI,KAAK,SAAS,EAAE,CAAC;QACvB,MAAM,IAAI,KAAK,CAAC,oBAAoB,KAAK,CAAC,MAAM,EAAE,CAAC,CAAC;IACtD,CAAC;IACD,IAAI,CAAC,IAAI,CAAC,QAAQ,EAAE,CAAC;QACnB,MAAM,IAAI,KAAK,CAAC,YAAY,KAAK,CAAC,MAAM,mBAAmB,CAAC,CAAC;IAC/D,CAAC;IACD,IAAI,KAAK,CAAC,MAAM,CAAC,IAAI,EAAE,CAAC,MAAM,GAAG,EAAE,EAAE,CAAC;QACpC,MAAM,IAAI,KAAK,CAAC,oDAAoD,CAAC,CAAC;IACxE,CAAC;IACD,MAAM,MAAM,GAAG,YAAY,CAAC,SAAS,CAAC;QACpC,MAAM,EAAE,KAAK,CAAC,MAAM;QACpB,IAAI,EAAE,KAAK,CAAC,IAAI;QAChB,MAAM,EAAE,KAAK,CAAC,MAAM;QACpB,SAAS,EAAE,KAAK,CAAC,SAAS;QAC1B,EAAE,EAAE,KAAK,CAAC,EAAE,IAAI,IAAI,IAAI,EAAE,CAAC,WAAW,EAAE;KACzC,CAAC,CAAC;IACH,IAAI,CAAC,MAAM,CAAC,OAAO,EAAE,CAAC;QACpB,MAAM,IAAI,KAAK,CAAC,4BAA4B,QAAQ,CAAC,MAAM,CAAC,KAAK,CAAC,EAAE,CAAC,CAAC;IACxE,CAAC;IACD,MAAM,iBAAiB,CAAC,KAAK,CAAC,SAAS,CAAC,CAAC,GAAG,CAAC,MAAM,CAAC,IAAI,CAAC,CAAC;IAC1D,OAAO,MAAM,CAAC,IAAI,CAAC;AACrB,CAAC;AAED,gDAAgD;AAChD,SAAS,QAAQ,CAAC,KAAsE;IACtF,OAAO,KAAK,CAAC,MAAM,CAAC,GAAG,CAAC,CAAC,KAAK,EAAE,EAAE,CAAC,GAAG,KAAK,CAAC,IAAI,CAAC,IAAI,CAAC,GAAG,CAAC,KAAK,KAAK,CAAC,OAAO,EAAE,CAAC,CAAC,IAAI,CAAC,IAAI,CAAC,CAAC;AAC7F,CAAC"}
|