@openagentsy/hooks 0.0.0-stage → 0.2.1
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
package/README.md
CHANGED
|
@@ -1,3 +1,33 @@
|
|
|
1
|
-
#
|
|
1
|
+
# @openagentsy/hooks
|
|
2
2
|
|
|
3
|
-
|
|
3
|
+
The deterministic guardrails: the rule registry, the default rules, the output
|
|
4
|
+
checks, the shell environment policy, the redaction wrapper and the approval
|
|
5
|
+
gate. It is a library. The adapter (the OpenCode plugin) calls it; OpenCode
|
|
6
|
+
never loads it.
|
|
7
|
+
|
|
8
|
+
## What it guards
|
|
9
|
+
|
|
10
|
+
- **Git hooks cannot be skipped.** `--no-verify`, `git commit -n` and
|
|
11
|
+
`HUSKY=0` are refused.
|
|
12
|
+
- **The package manager is Bun.** `pnpm`, `yarn`, `npx` and `npm install` are
|
|
13
|
+
refused.
|
|
14
|
+
- **Writes stay in the workspace** and in the OpenAgentsy worktrees.
|
|
15
|
+
- **Policy files are a person's**: `roles.yaml`, the model lock,
|
|
16
|
+
`opencode.json`, the build config, the lockfile, the hooks, the workflows,
|
|
17
|
+
the templates, the lessons and the waivers.
|
|
18
|
+
- **Decisions are a person's**: a lesson, a waiver and a memory promotion are
|
|
19
|
+
decided by a person, never by an agent.
|
|
20
|
+
- **Secrets do not get written.** A credential in the content is refused.
|
|
21
|
+
- **Destructive commands need an approval**: `rm -rf`, a forced push, a hard
|
|
22
|
+
reset and a dropped table. The escape hatch is `/oa approve <callID>`, and
|
|
23
|
+
a grant passes once.
|
|
24
|
+
|
|
25
|
+
The human-only guarantee rides the permission map (`roles.yaml`), not this
|
|
26
|
+
package: the map denies the three decision tools to every agent role, and
|
|
27
|
+
this package is the second layer. The output checks flag, they never block.
|
|
28
|
+
|
|
29
|
+
## The known gap
|
|
30
|
+
|
|
31
|
+
The entropy guard in the redaction helper is strictly `> 4.0` bits per
|
|
32
|
+
character, so a pure-hex value of 32 characters is never flagged. The helper
|
|
33
|
+
is core's, and 2f owns the fix; this package does not widen it.
|
|
@@ -0,0 +1,29 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* The counts the Accountability block shows.
|
|
3
|
+
*
|
|
4
|
+
* The block itself is the sidebar's (plan §7.4) and it is phase 2e's. What
|
|
5
|
+
* lives here is the data the block renders: the count of the waivers a
|
|
6
|
+
* person recorded, and the line the block shows for it. A block that has to
|
|
7
|
+
* reach into a file to count a thing is a block that has to know where the
|
|
8
|
+
* file is, so the reading is done here and the block only renders.
|
|
9
|
+
*/
|
|
10
|
+
/** The counts. */
|
|
11
|
+
export type AccountabilityCounts = {
|
|
12
|
+
/** The waivers a person recorded. */
|
|
13
|
+
waivers: number;
|
|
14
|
+
};
|
|
15
|
+
/**
|
|
16
|
+
* The counts, read from the state root.
|
|
17
|
+
*
|
|
18
|
+
* A state root with no waivers file holds no waivers, and that is a count
|
|
19
|
+
* of zero and not a failure.
|
|
20
|
+
*/
|
|
21
|
+
export declare function accountabilityCounts(root: string): Promise<AccountabilityCounts>;
|
|
22
|
+
/**
|
|
23
|
+
* The line the block shows.
|
|
24
|
+
*
|
|
25
|
+
* The plan's example reads "2 waivers", so the count comes first and the
|
|
26
|
+
* word follows it.
|
|
27
|
+
*/
|
|
28
|
+
export declare function formatAccountability(counts: AccountabilityCounts): string;
|
|
29
|
+
//# sourceMappingURL=accountability.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"accountability.d.ts","sourceRoot":"","sources":["../src/accountability.ts"],"names":[],"mappings":"AAEA;;;;;;;;GAQG;AAEH,kBAAkB;AAClB,MAAM,MAAM,oBAAoB,GAAG;IACjC,qCAAqC;IACrC,OAAO,EAAE,MAAM,CAAC;CACjB,CAAC;AAEF;;;;;GAKG;AACH,wBAAsB,oBAAoB,CAAC,IAAI,EAAE,MAAM,GAAG,OAAO,CAAC,oBAAoB,CAAC,CAEtF;AAED;;;;;GAKG;AACH,wBAAgB,oBAAoB,CAAC,MAAM,EAAE,oBAAoB,GAAG,MAAM,CAGzE"}
|
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
import { createWaiverStore } from './quality/waivers.js';
|
|
2
|
+
/**
|
|
3
|
+
* The counts, read from the state root.
|
|
4
|
+
*
|
|
5
|
+
* A state root with no waivers file holds no waivers, and that is a count
|
|
6
|
+
* of zero and not a failure.
|
|
7
|
+
*/
|
|
8
|
+
export async function accountabilityCounts(root) {
|
|
9
|
+
return { waivers: await createWaiverStore(root).count() };
|
|
10
|
+
}
|
|
11
|
+
/**
|
|
12
|
+
* The line the block shows.
|
|
13
|
+
*
|
|
14
|
+
* The plan's example reads "2 waivers", so the count comes first and the
|
|
15
|
+
* word follows it.
|
|
16
|
+
*/
|
|
17
|
+
export function formatAccountability(counts) {
|
|
18
|
+
const waivers = counts.waivers;
|
|
19
|
+
return `${waivers} ${waivers === 1 ? 'waiver' : 'waivers'}`;
|
|
20
|
+
}
|
|
21
|
+
//# sourceMappingURL=accountability.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"accountability.js","sourceRoot":"","sources":["../src/accountability.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,iBAAiB,EAAE,MAAM,sBAAsB,CAAC;AAkBzD;;;;;GAKG;AACH,MAAM,CAAC,KAAK,UAAU,oBAAoB,CAAC,IAAY;IACrD,OAAO,EAAE,OAAO,EAAE,MAAM,iBAAiB,CAAC,IAAI,CAAC,CAAC,KAAK,EAAE,EAAE,CAAC;AAC5D,CAAC;AAED;;;;;GAKG;AACH,MAAM,UAAU,oBAAoB,CAAC,MAA4B;IAC/D,MAAM,OAAO,GAAG,MAAM,CAAC,OAAO,CAAC;IAC/B,OAAO,GAAG,OAAO,IAAI,OAAO,KAAK,CAAC,CAAC,CAAC,CAAC,QAAQ,CAAC,CAAC,CAAC,SAAS,EAAE,CAAC;AAC9D,CAAC"}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"accountability.test.d.ts","sourceRoot":"","sources":["../src/accountability.test.ts"],"names":[],"mappings":""}
|
|
@@ -0,0 +1,42 @@
|
|
|
1
|
+
import { mkdtemp } 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 { accountabilityCounts, formatAccountability } from './accountability.js';
|
|
6
|
+
import { createWaiverStore } from './quality/waivers.js';
|
|
7
|
+
/** A state root the test reads. */
|
|
8
|
+
async function scratch() {
|
|
9
|
+
return await mkdtemp(join(tmpdir(), 'openagentsy-accountability-'));
|
|
10
|
+
}
|
|
11
|
+
/** A waiver, as a person recorded it. */
|
|
12
|
+
function waiver(over = {}) {
|
|
13
|
+
return {
|
|
14
|
+
ruleId: 'css/no-transition-all',
|
|
15
|
+
path: 'src/app.css',
|
|
16
|
+
reason: 'The file is a fixture, not a page.',
|
|
17
|
+
decidedBy: 'human',
|
|
18
|
+
ts: '2026-10-27T10:00:00.000Z',
|
|
19
|
+
...over,
|
|
20
|
+
};
|
|
21
|
+
}
|
|
22
|
+
describe('the accountability counts', () => {
|
|
23
|
+
it('counts no waiver in a project that has none', async () => {
|
|
24
|
+
const root = await scratch();
|
|
25
|
+
const counts = await accountabilityCounts(root);
|
|
26
|
+
expect(counts.waivers).toBe(0);
|
|
27
|
+
expect(formatAccountability(counts)).toBe('0 waivers');
|
|
28
|
+
});
|
|
29
|
+
it('counts the waivers a person recorded', async () => {
|
|
30
|
+
const root = await scratch();
|
|
31
|
+
const store = createWaiverStore(root);
|
|
32
|
+
await store.add(waiver());
|
|
33
|
+
await store.add(waiver({ path: 'src/other.css' }));
|
|
34
|
+
const counts = await accountabilityCounts(root);
|
|
35
|
+
expect(counts.waivers).toBe(2);
|
|
36
|
+
expect(formatAccountability(counts)).toBe('2 waivers');
|
|
37
|
+
});
|
|
38
|
+
it('says one waiver in the singular', () => {
|
|
39
|
+
expect(formatAccountability({ waivers: 1 })).toBe('1 waiver');
|
|
40
|
+
});
|
|
41
|
+
});
|
|
42
|
+
//# sourceMappingURL=accountability.test.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"accountability.test.js","sourceRoot":"","sources":["../src/accountability.test.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,OAAO,EAAE,MAAM,kBAAkB,CAAC;AAC3C,OAAO,EAAE,MAAM,EAAE,MAAM,SAAS,CAAC;AACjC,OAAO,EAAE,IAAI,EAAE,MAAM,WAAW,CAAC;AACjC,OAAO,EAAE,QAAQ,EAAE,MAAM,EAAE,EAAE,EAAE,MAAM,QAAQ,CAAC;AAC9C,OAAO,EAAE,oBAAoB,EAAE,oBAAoB,EAAE,MAAM,qBAAqB,CAAC;AACjF,OAAO,EAAE,iBAAiB,EAAe,MAAM,sBAAsB,CAAC;AAEtE,mCAAmC;AACnC,KAAK,UAAU,OAAO;IACpB,OAAO,MAAM,OAAO,CAAC,IAAI,CAAC,MAAM,EAAE,EAAE,6BAA6B,CAAC,CAAC,CAAC;AACtE,CAAC;AAED,yCAAyC;AACzC,SAAS,MAAM,CAAC,OAAwB,EAAE;IACxC,OAAO;QACL,MAAM,EAAE,uBAAuB;QAC/B,IAAI,EAAE,aAAa;QACnB,MAAM,EAAE,oCAAoC;QAC5C,SAAS,EAAE,OAAO;QAClB,EAAE,EAAE,0BAA0B;QAC9B,GAAG,IAAI;KACR,CAAC;AACJ,CAAC;AAED,QAAQ,CAAC,2BAA2B,EAAE,GAAG,EAAE;IACzC,EAAE,CAAC,6CAA6C,EAAE,KAAK,IAAI,EAAE;QAC3D,MAAM,IAAI,GAAG,MAAM,OAAO,EAAE,CAAC;QAC7B,MAAM,MAAM,GAAG,MAAM,oBAAoB,CAAC,IAAI,CAAC,CAAC;QAChD,MAAM,CAAC,MAAM,CAAC,OAAO,CAAC,CAAC,IAAI,CAAC,CAAC,CAAC,CAAC;QAC/B,MAAM,CAAC,oBAAoB,CAAC,MAAM,CAAC,CAAC,CAAC,IAAI,CAAC,WAAW,CAAC,CAAC;IACzD,CAAC,CAAC,CAAC;IAEH,EAAE,CAAC,sCAAsC,EAAE,KAAK,IAAI,EAAE;QACpD,MAAM,IAAI,GAAG,MAAM,OAAO,EAAE,CAAC;QAC7B,MAAM,KAAK,GAAG,iBAAiB,CAAC,IAAI,CAAC,CAAC;QACtC,MAAM,KAAK,CAAC,GAAG,CAAC,MAAM,EAAE,CAAC,CAAC;QAC1B,MAAM,KAAK,CAAC,GAAG,CAAC,MAAM,CAAC,EAAE,IAAI,EAAE,eAAe,EAAE,CAAC,CAAC,CAAC;QACnD,MAAM,MAAM,GAAG,MAAM,oBAAoB,CAAC,IAAI,CAAC,CAAC;QAChD,MAAM,CAAC,MAAM,CAAC,OAAO,CAAC,CAAC,IAAI,CAAC,CAAC,CAAC,CAAC;QAC/B,MAAM,CAAC,oBAAoB,CAAC,MAAM,CAAC,CAAC,CAAC,IAAI,CAAC,WAAW,CAAC,CAAC;IACzD,CAAC,CAAC,CAAC;IAEH,EAAE,CAAC,iCAAiC,EAAE,GAAG,EAAE;QACzC,MAAM,CAAC,oBAAoB,CAAC,EAAE,OAAO,EAAE,CAAC,EAAE,CAAC,CAAC,CAAC,IAAI,CAAC,UAAU,CAAC,CAAC;IAChE,CAAC,CAAC,CAAC;AACL,CAAC,CAAC,CAAC"}
|
|
@@ -0,0 +1,110 @@
|
|
|
1
|
+
import type { HandlerResult } from './registry.js';
|
|
2
|
+
/**
|
|
3
|
+
* The approval gate.
|
|
4
|
+
*
|
|
5
|
+
* A destructive command needs a person. OpenCode's own permission prompt
|
|
6
|
+
* cannot be answered from inside a plugin, so an approval is a grant on
|
|
7
|
+
* disk: a person runs `/oa approve <callID>`, the grant is written down,
|
|
8
|
+
* and the call it names goes through once.
|
|
9
|
+
*/
|
|
10
|
+
/** One grant. */
|
|
11
|
+
export interface ApprovalEntry {
|
|
12
|
+
/** The call the grant covers. */
|
|
13
|
+
callID: string;
|
|
14
|
+
/** When the grant stops being valid, in milliseconds since the epoch. */
|
|
15
|
+
expiresAt: number;
|
|
16
|
+
}
|
|
17
|
+
/** How long a grant lives when no other time is given. */
|
|
18
|
+
export declare const DEFAULT_APPROVAL_TTL_MS: number;
|
|
19
|
+
/**
|
|
20
|
+
* The timings of the store.
|
|
21
|
+
*
|
|
22
|
+
* They are the defaults of a store; a test can shorten them.
|
|
23
|
+
*/
|
|
24
|
+
export interface ApprovalTiming {
|
|
25
|
+
/** How long a writer waits for the lock. */
|
|
26
|
+
timeoutMs?: number;
|
|
27
|
+
/** How old a lock must be before it is presumed abandoned. */
|
|
28
|
+
staleMs?: number;
|
|
29
|
+
/** How often the lock is refreshed while the action runs. */
|
|
30
|
+
heartbeatMs?: number;
|
|
31
|
+
}
|
|
32
|
+
/** The timings a store runs with. */
|
|
33
|
+
export type ResolvedTiming = Required<ApprovalTiming>;
|
|
34
|
+
/**
|
|
35
|
+
* Where the grants live.
|
|
36
|
+
*
|
|
37
|
+
* The file sits in the state root next to the board, so every session of
|
|
38
|
+
* the project reads the same grants. The path is a protected one: the file
|
|
39
|
+
* is written by a person and by the gate, never by an agent.
|
|
40
|
+
*/
|
|
41
|
+
export declare function approvalsPath(stateRoot: string): string;
|
|
42
|
+
/** The grants, and the gate that spends them. */
|
|
43
|
+
export interface ApprovalStore {
|
|
44
|
+
/** Where the grants live. */
|
|
45
|
+
readonly path: string;
|
|
46
|
+
/** Whether a grant covers the call now. */
|
|
47
|
+
isApproved(callID: string, now: number): Promise<boolean>;
|
|
48
|
+
/** Grant the call, unless it is granted already. */
|
|
49
|
+
approve(callID: string, now: number, ttlMs?: number): Promise<ApprovalGrant>;
|
|
50
|
+
/** Spend the grant: it passes once and is gone, and the use is audited. */
|
|
51
|
+
consumeApproval(callID: string, now: number, actor?: string): Promise<boolean>;
|
|
52
|
+
/** Take a grant back: the call is not approved after it. */
|
|
53
|
+
revokeApproval(callID: string, now: number): Promise<boolean>;
|
|
54
|
+
}
|
|
55
|
+
/**
|
|
56
|
+
* A grant, and whether this call wrote it.
|
|
57
|
+
*
|
|
58
|
+
* The store holds the lock while it decides, so it knows whether the grant
|
|
59
|
+
* was there already. The caller needs that: a grant that was written is
|
|
60
|
+
* audited, and a grant that was found is not.
|
|
61
|
+
*/
|
|
62
|
+
export interface ApprovalGrant {
|
|
63
|
+
/** The grant that is on file now. */
|
|
64
|
+
entry: ApprovalEntry;
|
|
65
|
+
/** Whether this call wrote it, as opposed to finding it there. */
|
|
66
|
+
created: boolean;
|
|
67
|
+
}
|
|
68
|
+
/**
|
|
69
|
+
* The lock that serialises a claim.
|
|
70
|
+
*
|
|
71
|
+
* Two calls that spend the same grant at the same time must not both pass,
|
|
72
|
+
* so the read and the write of the store are one step under a lock file
|
|
73
|
+
* that is created exclusively. The second caller waits and then finds the
|
|
74
|
+
* grant gone.
|
|
75
|
+
*/
|
|
76
|
+
export declare function withLock<T>(path: string, timing: ResolvedTiming, action: () => Promise<T>): Promise<T>;
|
|
77
|
+
/**
|
|
78
|
+
* The lock is given back.
|
|
79
|
+
*
|
|
80
|
+
* The lock is moved to a private path with a rename, which is one atomic
|
|
81
|
+
* step, and the token is read there: a lock that was taken away from this
|
|
82
|
+
* writer and given to another one is not removed by mistake, and the lock
|
|
83
|
+
* of the other writer is put back.
|
|
84
|
+
*/
|
|
85
|
+
export declare function releaseLock(lockPath: string, token: string): Promise<void>;
|
|
86
|
+
/**
|
|
87
|
+
* A lock that was moved aside goes back where it was.
|
|
88
|
+
*
|
|
89
|
+
* It is not put over a lock that is held now: the writer that holds that
|
|
90
|
+
* one would lose it. The link is one atomic step, so a lock that is taken
|
|
91
|
+
* in the window between the move and the restore is not lost either.
|
|
92
|
+
*/
|
|
93
|
+
export declare function restoreLock(privatePath: string, lockPath: string): Promise<void>;
|
|
94
|
+
/**
|
|
95
|
+
* The store of grants for one project.
|
|
96
|
+
*/
|
|
97
|
+
export declare function createApprovalStore(stateRoot: string, timing?: ApprovalTiming): ApprovalStore;
|
|
98
|
+
/**
|
|
99
|
+
* The rule handler that asks for an approval.
|
|
100
|
+
*
|
|
101
|
+
* A command that matches needs a grant. A pattern is enough for a single
|
|
102
|
+
* shape, and a matcher is enough for the shapes a pattern cannot say: the
|
|
103
|
+
* flags of `rm` in any order, for example.
|
|
104
|
+
*
|
|
105
|
+
* A store that cannot be read passes nothing: the command is refused, and
|
|
106
|
+
* the reason names the failure. A refusal is the safe answer, and the
|
|
107
|
+
* failure is not swallowed: it is in the reason.
|
|
108
|
+
*/
|
|
109
|
+
export declare function approvalGate(matcher: RegExp | ((command: string) => boolean), store: ApprovalStore, now?: () => number): (event: unknown) => Promise<HandlerResult>;
|
|
110
|
+
//# sourceMappingURL=approvals.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"approvals.d.ts","sourceRoot":"","sources":["../src/approvals.ts"],"names":[],"mappings":"AAOA,OAAO,KAAK,EAAE,aAAa,EAAE,MAAM,eAAe,CAAC;AAEnD;;;;;;;GAOG;AAEH,iBAAiB;AACjB,MAAM,WAAW,aAAa;IAC5B,iCAAiC;IACjC,MAAM,EAAE,MAAM,CAAC;IACf,yEAAyE;IACzE,SAAS,EAAE,MAAM,CAAC;CACnB;AAED,0DAA0D;AAC1D,eAAO,MAAM,uBAAuB,QAAiB,CAAC;AA8CtD;;;;GAIG;AACH,MAAM,WAAW,cAAc;IAC7B,4CAA4C;IAC5C,SAAS,CAAC,EAAE,MAAM,CAAC;IACnB,8DAA8D;IAC9D,OAAO,CAAC,EAAE,MAAM,CAAC;IACjB,6DAA6D;IAC7D,WAAW,CAAC,EAAE,MAAM,CAAC;CACtB;AAED,qCAAqC;AACrC,MAAM,MAAM,cAAc,GAAG,QAAQ,CAAC,cAAc,CAAC,CAAC;AAEtD;;;;;;GAMG;AACH,wBAAgB,aAAa,CAAC,SAAS,EAAE,MAAM,GAAG,MAAM,CAEvD;AAED,iDAAiD;AACjD,MAAM,WAAW,aAAa;IAC5B,6BAA6B;IAC7B,QAAQ,CAAC,IAAI,EAAE,MAAM,CAAC;IACtB,2CAA2C;IAC3C,UAAU,CAAC,MAAM,EAAE,MAAM,EAAE,GAAG,EAAE,MAAM,GAAG,OAAO,CAAC,OAAO,CAAC,CAAC;IAC1D,oDAAoD;IACpD,OAAO,CAAC,MAAM,EAAE,MAAM,EAAE,GAAG,EAAE,MAAM,EAAE,KAAK,CAAC,EAAE,MAAM,GAAG,OAAO,CAAC,aAAa,CAAC,CAAC;IAC7E,2EAA2E;IAC3E,eAAe,CAAC,MAAM,EAAE,MAAM,EAAE,GAAG,EAAE,MAAM,EAAE,KAAK,CAAC,EAAE,MAAM,GAAG,OAAO,CAAC,OAAO,CAAC,CAAC;IAC/E,4DAA4D;IAC5D,cAAc,CAAC,MAAM,EAAE,MAAM,EAAE,GAAG,EAAE,MAAM,GAAG,OAAO,CAAC,OAAO,CAAC,CAAC;CAC/D;AAED;;;;;;GAMG;AACH,MAAM,WAAW,aAAa;IAC5B,qCAAqC;IACrC,KAAK,EAAE,aAAa,CAAC;IACrB,kEAAkE;IAClE,OAAO,EAAE,OAAO,CAAC;CAClB;AA4BD;;;;;;;GAOG;AACH,wBAAsB,QAAQ,CAAC,CAAC,EAC9B,IAAI,EAAE,MAAM,EACZ,MAAM,EAAE,cAAc,EACtB,MAAM,EAAE,MAAM,OAAO,CAAC,CAAC,CAAC,GACvB,OAAO,CAAC,CAAC,CAAC,CAgBZ;AAsDD;;;;;;;GAOG;AACH,wBAAsB,WAAW,CAAC,QAAQ,EAAE,MAAM,EAAE,KAAK,EAAE,MAAM,GAAG,OAAO,CAAC,IAAI,CAAC,CAqBhF;AAED;;;;;;GAMG;AACH,wBAAsB,WAAW,CAAC,WAAW,EAAE,MAAM,EAAE,QAAQ,EAAE,MAAM,GAAG,OAAO,CAAC,IAAI,CAAC,CAUtF;AAoHD;;GAEG;AACH,wBAAgB,mBAAmB,CAAC,SAAS,EAAE,MAAM,EAAE,MAAM,GAAE,cAAmB,GAAG,aAAa,CAqFjG;AASD;;;;;;;;;;GAUG;AACH,wBAAgB,YAAY,CAC1B,OAAO,EAAE,MAAM,GAAG,CAAC,CAAC,OAAO,EAAE,MAAM,KAAK,OAAO,CAAC,EAChD,KAAK,EAAE,aAAa,EACpB,GAAG,GAAE,MAAM,MAAiB,GAC3B,CAAC,KAAK,EAAE,OAAO,KAAK,OAAO,CAAC,aAAa,CAAC,CA4B5C"}
|