@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,307 @@
|
|
|
1
|
+
import { mkdir, mkdtemp, readdir, readFile, stat, utimes, writeFile } from 'node:fs/promises';
|
|
2
|
+
import { tmpdir } from 'node:os';
|
|
3
|
+
import { join } from 'node:path';
|
|
4
|
+
import { beforeEach, describe, expect, it } from 'vitest';
|
|
5
|
+
import { approvalGate, approvalsPath, createApprovalStore, releaseLock, restoreLock, withLock, } from './approvals.js';
|
|
6
|
+
/** The reason a result carries, when it stops the call. */
|
|
7
|
+
function reasonOf(result) {
|
|
8
|
+
return 'reason' in result ? result.reason : undefined;
|
|
9
|
+
}
|
|
10
|
+
/** Whether a result lets the call through. */
|
|
11
|
+
function continues(result) {
|
|
12
|
+
return 'continue' in result ? result.continue : true;
|
|
13
|
+
}
|
|
14
|
+
/** A pause. */
|
|
15
|
+
function sleep(ms) {
|
|
16
|
+
return new Promise((resolve) => {
|
|
17
|
+
setTimeout(resolve, ms);
|
|
18
|
+
});
|
|
19
|
+
}
|
|
20
|
+
let root;
|
|
21
|
+
beforeEach(async () => {
|
|
22
|
+
root = await mkdtemp(join(tmpdir(), 'oa-approvals-'));
|
|
23
|
+
});
|
|
24
|
+
describe('the approval store', () => {
|
|
25
|
+
it('writes the grants under the state root and makes the directory', async () => {
|
|
26
|
+
const store = createApprovalStore(root);
|
|
27
|
+
expect(store.path).toBe(approvalsPath(root));
|
|
28
|
+
await store.approve('call-1', 1000, 60_000);
|
|
29
|
+
const raw = JSON.parse(await readFile(store.path, 'utf8'));
|
|
30
|
+
expect(raw).toEqual([{ callID: 'call-1', expiresAt: 61_000 }]);
|
|
31
|
+
});
|
|
32
|
+
it('passes a granted call exactly once', async () => {
|
|
33
|
+
const store = createApprovalStore(root);
|
|
34
|
+
await store.approve('call-1', 1000, 60_000);
|
|
35
|
+
expect(await store.isApproved('call-1', 2000)).toBe(true);
|
|
36
|
+
expect(await store.consumeApproval('call-1', 2000)).toBe(true);
|
|
37
|
+
expect(await store.consumeApproval('call-1', 2000)).toBe(false);
|
|
38
|
+
expect(await store.isApproved('call-1', 2000)).toBe(false);
|
|
39
|
+
});
|
|
40
|
+
it('never passes an expired grant', async () => {
|
|
41
|
+
const store = createApprovalStore(root);
|
|
42
|
+
await store.approve('call-1', 1000, 500);
|
|
43
|
+
expect(await store.isApproved('call-1', 2000)).toBe(false);
|
|
44
|
+
expect(await store.consumeApproval('call-1', 2000)).toBe(false);
|
|
45
|
+
});
|
|
46
|
+
it('prunes the grants that have run out', async () => {
|
|
47
|
+
const store = createApprovalStore(root);
|
|
48
|
+
await store.approve('old', 1000, 500);
|
|
49
|
+
await store.approve('new', 1000, 60_000);
|
|
50
|
+
expect(await store.isApproved('new', 2000)).toBe(true);
|
|
51
|
+
const raw = JSON.parse(await readFile(store.path, 'utf8'));
|
|
52
|
+
expect(raw).toEqual([{ callID: 'new', expiresAt: 61_000 }]);
|
|
53
|
+
});
|
|
54
|
+
it('holds no grant for a call nobody approved', async () => {
|
|
55
|
+
const store = createApprovalStore(root);
|
|
56
|
+
expect(await store.isApproved('call-1', 2000)).toBe(false);
|
|
57
|
+
});
|
|
58
|
+
it('spends a grant once, even when two callers race', async () => {
|
|
59
|
+
const store = createApprovalStore(root);
|
|
60
|
+
await store.approve('dup', 1000, 60_000);
|
|
61
|
+
const results = await Promise.all([
|
|
62
|
+
store.consumeApproval('dup', 2000),
|
|
63
|
+
store.consumeApproval('dup', 2000),
|
|
64
|
+
]);
|
|
65
|
+
expect(results.filter(Boolean)).toHaveLength(1);
|
|
66
|
+
});
|
|
67
|
+
it('keeps the other grants when one is added', async () => {
|
|
68
|
+
const store = createApprovalStore(root);
|
|
69
|
+
await store.approve('first', 1000, 60_000);
|
|
70
|
+
await store.approve('second', 1000, 60_000);
|
|
71
|
+
const raw = JSON.parse(await readFile(store.path, 'utf8'));
|
|
72
|
+
expect(raw).toHaveLength(2);
|
|
73
|
+
});
|
|
74
|
+
it('does not take a lock that a live writer holds', async () => {
|
|
75
|
+
const store = createApprovalStore(root);
|
|
76
|
+
await mkdir(join(root, '.openagentsy'), { recursive: true });
|
|
77
|
+
const lockPath = `${store.path}.lock`;
|
|
78
|
+
await writeFile(lockPath, 'held', 'utf8');
|
|
79
|
+
await expect(store.approve('call-1', 1000, 60_000)).rejects.toThrow('locked by another writer');
|
|
80
|
+
expect(await readFile(lockPath, 'utf8')).toBe('held');
|
|
81
|
+
});
|
|
82
|
+
it('lets one writer take a stale lock over and refuses the other', async () => {
|
|
83
|
+
const store = createApprovalStore(root, { timeoutMs: 50, staleMs: 200, heartbeatMs: 50 });
|
|
84
|
+
await mkdir(join(root, '.openagentsy'), { recursive: true });
|
|
85
|
+
const lockPath = `${store.path}.lock`;
|
|
86
|
+
await writeFile(lockPath, 'held', 'utf8');
|
|
87
|
+
const old = new Date(Date.now() - 60_000);
|
|
88
|
+
await utimes(lockPath, old, old);
|
|
89
|
+
const results = await Promise.allSettled([
|
|
90
|
+
store.approve('first', 1000, 60_000),
|
|
91
|
+
store.approve('second', 1000, 60_000),
|
|
92
|
+
]);
|
|
93
|
+
expect(results.filter((result) => result.status === 'fulfilled')).toHaveLength(1);
|
|
94
|
+
expect(results.filter((result) => result.status === 'rejected')).toHaveLength(1);
|
|
95
|
+
});
|
|
96
|
+
it('takes a stale lock over even when a reclaim file was left behind', async () => {
|
|
97
|
+
const store = createApprovalStore(root, { timeoutMs: 50, staleMs: 200, heartbeatMs: 50 });
|
|
98
|
+
await mkdir(join(root, '.openagentsy'), { recursive: true });
|
|
99
|
+
const lockPath = `${store.path}.lock`;
|
|
100
|
+
await writeFile(lockPath, 'held', 'utf8');
|
|
101
|
+
const old = new Date(Date.now() - 60_000);
|
|
102
|
+
await utimes(lockPath, old, old);
|
|
103
|
+
const reclaimPath = `${lockPath}.reclaim`;
|
|
104
|
+
await writeFile(reclaimPath, 'orphan', 'utf8');
|
|
105
|
+
await utimes(reclaimPath, old, old);
|
|
106
|
+
const grant = await store.approve('call-1', 1000, 60_000);
|
|
107
|
+
expect(grant.entry.callID).toBe('call-1');
|
|
108
|
+
expect(grant.created).toBe(true);
|
|
109
|
+
});
|
|
110
|
+
it('takes a stale lock over even when a stale file was left behind', async () => {
|
|
111
|
+
const store = createApprovalStore(root, { timeoutMs: 50, staleMs: 200, heartbeatMs: 50 });
|
|
112
|
+
await mkdir(join(root, '.openagentsy'), { recursive: true });
|
|
113
|
+
const lockPath = `${store.path}.lock`;
|
|
114
|
+
await writeFile(lockPath, 'held', 'utf8');
|
|
115
|
+
const old = new Date(Date.now() - 60_000);
|
|
116
|
+
await utimes(lockPath, old, old);
|
|
117
|
+
const stalePath = `${lockPath}.stale`;
|
|
118
|
+
await writeFile(stalePath, 'old', 'utf8');
|
|
119
|
+
await utimes(stalePath, old, old);
|
|
120
|
+
const grant = await store.approve('call-1', 1000, 60_000);
|
|
121
|
+
expect(grant.entry.callID).toBe('call-1');
|
|
122
|
+
expect(grant.created).toBe(true);
|
|
123
|
+
});
|
|
124
|
+
it('refreshes the lock while the action runs', async () => {
|
|
125
|
+
const path = approvalsPath(root);
|
|
126
|
+
await mkdir(join(root, '.openagentsy'), { recursive: true });
|
|
127
|
+
const timing = { timeoutMs: 50, staleMs: 200, heartbeatMs: 50 };
|
|
128
|
+
await withLock(path, timing, async () => {
|
|
129
|
+
await sleep(150);
|
|
130
|
+
const stats = await stat(`${path}.lock`);
|
|
131
|
+
expect(Date.now() - stats.mtimeMs).toBeLessThan(100);
|
|
132
|
+
});
|
|
133
|
+
});
|
|
134
|
+
it('leaves no lock behind', async () => {
|
|
135
|
+
const store = createApprovalStore(root);
|
|
136
|
+
await store.approve('call-1', 1000, 60_000);
|
|
137
|
+
const files = await readdir(join(root, '.openagentsy'));
|
|
138
|
+
expect(files.filter((name) => name.startsWith('approvals.json.lock'))).toEqual([]);
|
|
139
|
+
});
|
|
140
|
+
it('takes over a lock that was left behind', async () => {
|
|
141
|
+
const store = createApprovalStore(root);
|
|
142
|
+
await mkdir(join(root, '.openagentsy'), { recursive: true });
|
|
143
|
+
const lockPath = `${store.path}.lock`;
|
|
144
|
+
await writeFile(lockPath, '', 'utf8');
|
|
145
|
+
const old = new Date(Date.now() - 60_000);
|
|
146
|
+
await utimes(lockPath, old, old);
|
|
147
|
+
const grant = await store.approve('call-1', 1000, 60_000);
|
|
148
|
+
expect(grant.entry.callID).toBe('call-1');
|
|
149
|
+
expect(grant.created).toBe(true);
|
|
150
|
+
});
|
|
151
|
+
it('refuses to grant on a store it cannot read', async () => {
|
|
152
|
+
const store = createApprovalStore(root);
|
|
153
|
+
await mkdir(join(root, '.openagentsy'), { recursive: true });
|
|
154
|
+
await writeFile(store.path, '{not json', 'utf8');
|
|
155
|
+
await expect(store.approve('call-1', 1000, 60_000)).rejects.toThrow();
|
|
156
|
+
});
|
|
157
|
+
it('keeps the grant that is already there', async () => {
|
|
158
|
+
const store = createApprovalStore(root);
|
|
159
|
+
await store.approve('call-1', 1000, 60_000);
|
|
160
|
+
const grant = await store.approve('call-1', 2000, 60_000);
|
|
161
|
+
expect(grant.created).toBe(false);
|
|
162
|
+
expect(grant.entry.expiresAt).toBe(61_000);
|
|
163
|
+
const raw = JSON.parse(await readFile(store.path, 'utf8'));
|
|
164
|
+
expect(raw).toEqual([{ callID: 'call-1', expiresAt: 61_000 }]);
|
|
165
|
+
});
|
|
166
|
+
it('takes a grant back', async () => {
|
|
167
|
+
const store = createApprovalStore(root);
|
|
168
|
+
await store.approve('call-1', 1000, 60_000);
|
|
169
|
+
expect(await store.revokeApproval('call-1', 2000)).toBe(true);
|
|
170
|
+
expect(await store.revokeApproval('call-1', 2000)).toBe(false);
|
|
171
|
+
const raw = JSON.parse(await readFile(store.path, 'utf8'));
|
|
172
|
+
expect(raw).toEqual([]);
|
|
173
|
+
});
|
|
174
|
+
it('refuses a grant that would not run out at a moment a date can name', async () => {
|
|
175
|
+
const store = createApprovalStore(root);
|
|
176
|
+
await expect(store.approve('call-1', 1000, 1e60)).rejects.toThrow('a moment a date can name');
|
|
177
|
+
await expect(readFile(store.path, 'utf8')).rejects.toThrow();
|
|
178
|
+
});
|
|
179
|
+
it('puts the grant back when the trail of the spend cannot be written', async () => {
|
|
180
|
+
const store = createApprovalStore(root);
|
|
181
|
+
await store.approve('call-1', 1000, 60_000);
|
|
182
|
+
// The audit log is a directory, so nothing can be written to it.
|
|
183
|
+
await mkdir(join(root, '.openagentsy', 'audit.jsonl'), { recursive: true });
|
|
184
|
+
await expect(store.consumeApproval('call-1', 2000, 'builder')).rejects.toThrow();
|
|
185
|
+
const raw = JSON.parse(await readFile(store.path, 'utf8'));
|
|
186
|
+
expect(raw).toEqual([{ callID: 'call-1', expiresAt: 61_000 }]);
|
|
187
|
+
});
|
|
188
|
+
it('does not rewrite a store it cannot read', async () => {
|
|
189
|
+
const store = createApprovalStore(root);
|
|
190
|
+
await mkdir(join(root, '.openagentsy'), { recursive: true });
|
|
191
|
+
await writeFile(store.path, '{not json', 'utf8');
|
|
192
|
+
await expect(store.approve('call-1', 1000, 60_000)).rejects.toThrow();
|
|
193
|
+
expect(await readFile(store.path, 'utf8')).toBe('{not json');
|
|
194
|
+
});
|
|
195
|
+
});
|
|
196
|
+
describe('the approval gate', () => {
|
|
197
|
+
it('stops a destructive command and names the escape hatch', async () => {
|
|
198
|
+
const store = createApprovalStore(root);
|
|
199
|
+
const gate = approvalGate(/rm\s+-rf/, store);
|
|
200
|
+
const result = await gate({
|
|
201
|
+
toolName: 'bash',
|
|
202
|
+
args: {},
|
|
203
|
+
files: [],
|
|
204
|
+
command: 'rm -rf /',
|
|
205
|
+
callID: 'call-7',
|
|
206
|
+
});
|
|
207
|
+
expect(continues(result)).toBe(false);
|
|
208
|
+
expect(reasonOf(result)).toContain('/oa approve <callID>');
|
|
209
|
+
expect(reasonOf(result)).toContain('call-7');
|
|
210
|
+
});
|
|
211
|
+
it('lets an approved call through once', async () => {
|
|
212
|
+
const store = createApprovalStore(root);
|
|
213
|
+
await store.approve('call-7', Date.now(), 60_000);
|
|
214
|
+
const gate = approvalGate(/rm\s+-rf/, store);
|
|
215
|
+
const first = await gate({
|
|
216
|
+
toolName: 'bash',
|
|
217
|
+
args: {},
|
|
218
|
+
files: [],
|
|
219
|
+
command: 'rm -rf /',
|
|
220
|
+
callID: 'call-7',
|
|
221
|
+
});
|
|
222
|
+
expect(continues(first)).toBe(true);
|
|
223
|
+
const second = await gate({
|
|
224
|
+
toolName: 'bash',
|
|
225
|
+
args: {},
|
|
226
|
+
files: [],
|
|
227
|
+
command: 'rm -rf /',
|
|
228
|
+
callID: 'call-7',
|
|
229
|
+
});
|
|
230
|
+
expect(continues(second)).toBe(false);
|
|
231
|
+
});
|
|
232
|
+
it('writes the trail of the spend when the gate lets a call through', async () => {
|
|
233
|
+
const store = createApprovalStore(root);
|
|
234
|
+
await store.approve('call-7', Date.now(), 60_000);
|
|
235
|
+
const gate = approvalGate(/rm\s+-rf/, store);
|
|
236
|
+
const result = await gate({
|
|
237
|
+
toolName: 'bash',
|
|
238
|
+
args: {},
|
|
239
|
+
files: [],
|
|
240
|
+
command: 'rm -rf /',
|
|
241
|
+
callID: 'call-7',
|
|
242
|
+
role: 'builder',
|
|
243
|
+
});
|
|
244
|
+
expect(continues(result)).toBe(true);
|
|
245
|
+
const raw = await readFile(join(root, '.openagentsy', 'audit.jsonl'), 'utf8');
|
|
246
|
+
const entry = JSON.parse(raw.trim());
|
|
247
|
+
expect(entry).toMatchObject({
|
|
248
|
+
policyId: 'oa.approve.used',
|
|
249
|
+
policyVersion: 'v1',
|
|
250
|
+
actor: 'builder',
|
|
251
|
+
action: 'approve_use',
|
|
252
|
+
resource: 'call-7',
|
|
253
|
+
effect: 'allow',
|
|
254
|
+
});
|
|
255
|
+
});
|
|
256
|
+
it('lets a command that does not match through', async () => {
|
|
257
|
+
const store = createApprovalStore(root);
|
|
258
|
+
const gate = approvalGate(/rm\s+-rf/, store);
|
|
259
|
+
const result = await gate({ toolName: 'bash', args: {}, files: [], command: 'ls -la' });
|
|
260
|
+
expect(continues(result)).toBe(true);
|
|
261
|
+
});
|
|
262
|
+
it('refuses when the store cannot be read', async () => {
|
|
263
|
+
const store = createApprovalStore(root);
|
|
264
|
+
await mkdir(join(root, '.openagentsy'), { recursive: true });
|
|
265
|
+
await writeFile(store.path, '{not json', 'utf8');
|
|
266
|
+
const gate = approvalGate(/rm\s+-rf/, store);
|
|
267
|
+
const result = await gate({
|
|
268
|
+
toolName: 'bash',
|
|
269
|
+
args: {},
|
|
270
|
+
files: [],
|
|
271
|
+
command: 'rm -rf /',
|
|
272
|
+
callID: 'call-7',
|
|
273
|
+
});
|
|
274
|
+
expect(continues(result)).toBe(false);
|
|
275
|
+
expect(reasonOf(result)).toContain('approval store');
|
|
276
|
+
});
|
|
277
|
+
});
|
|
278
|
+
describe('the release of a lock', () => {
|
|
279
|
+
it('does not put a foreign lock over a lock that is held now', async () => {
|
|
280
|
+
const path = approvalsPath(root);
|
|
281
|
+
await mkdir(join(root, '.openagentsy'), { recursive: true });
|
|
282
|
+
const lockPath = `${path}.lock`;
|
|
283
|
+
const privatePath = `${lockPath}.release.ours`;
|
|
284
|
+
await writeFile(privatePath, 'foreign', 'utf8');
|
|
285
|
+
await writeFile(lockPath, 'new-holder', 'utf8');
|
|
286
|
+
await restoreLock(privatePath, lockPath);
|
|
287
|
+
expect(await readFile(lockPath, 'utf8')).toBe('new-holder');
|
|
288
|
+
});
|
|
289
|
+
it('puts a foreign lock back when the path is free', async () => {
|
|
290
|
+
const path = approvalsPath(root);
|
|
291
|
+
await mkdir(join(root, '.openagentsy'), { recursive: true });
|
|
292
|
+
const lockPath = `${path}.lock`;
|
|
293
|
+
const privatePath = `${lockPath}.release.ours`;
|
|
294
|
+
await writeFile(privatePath, 'foreign', 'utf8');
|
|
295
|
+
await restoreLock(privatePath, lockPath);
|
|
296
|
+
expect(await readFile(lockPath, 'utf8')).toBe('foreign');
|
|
297
|
+
});
|
|
298
|
+
it('does not remove a lock that is not the writer’s', async () => {
|
|
299
|
+
const path = approvalsPath(root);
|
|
300
|
+
await mkdir(join(root, '.openagentsy'), { recursive: true });
|
|
301
|
+
const lockPath = `${path}.lock`;
|
|
302
|
+
await writeFile(lockPath, 'foreign', 'utf8');
|
|
303
|
+
await releaseLock(lockPath, 'ours');
|
|
304
|
+
expect(await readFile(lockPath, 'utf8')).toBe('foreign');
|
|
305
|
+
});
|
|
306
|
+
});
|
|
307
|
+
//# sourceMappingURL=approvals.test.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"approvals.test.js","sourceRoot":"","sources":["../src/approvals.test.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,26 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* The base name of a tool.
|
|
3
|
+
*
|
|
4
|
+
* A host hands a tool name over as `<server>_<tool>`: the memory server's
|
|
5
|
+
* `oa_memory_decide` arrives as `openagentsy-memory_oa_memory_decide`. The
|
|
6
|
+
* base name is the tool itself, and the rules are written against the base
|
|
7
|
+
* name, so it has to be the same whichever host called.
|
|
8
|
+
*
|
|
9
|
+
* The prefix is not stripped by a pattern. A pattern that strips
|
|
10
|
+
* `^[a-z0-9-]+_` in front of `oa_` leaves every `board_*` name untouched,
|
|
11
|
+
* which is the hole the vocabulary closes: the name is resolved by the
|
|
12
|
+
* longest suffix the registered vocabulary knows.
|
|
13
|
+
*/
|
|
14
|
+
export declare function baseToolName(toolName: string, vocabulary: readonly string[]): string;
|
|
15
|
+
/**
|
|
16
|
+
* Every tool name the workspace knows.
|
|
17
|
+
*
|
|
18
|
+
* The board, the task store and the memory slice; whatever else a package
|
|
19
|
+
* exports. The list is the vocabulary a tool name is resolved against.
|
|
20
|
+
*/
|
|
21
|
+
export declare const toolVocabulary: readonly string[];
|
|
22
|
+
/**
|
|
23
|
+
* The base name of a tool, against the workspace vocabulary.
|
|
24
|
+
*/
|
|
25
|
+
export declare function baseTool(toolName: string): string;
|
|
26
|
+
//# sourceMappingURL=base-tool.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"base-tool.d.ts","sourceRoot":"","sources":["../src/base-tool.ts"],"names":[],"mappings":"AAEA;;;;;;;;;;;;GAYG;AACH,wBAAgB,YAAY,CAAC,QAAQ,EAAE,MAAM,EAAE,UAAU,EAAE,SAAS,MAAM,EAAE,GAAG,MAAM,CAIpF;AAED;;;;;GAKG;AACH,eAAO,MAAM,cAAc,EAAE,SAAS,MAAM,EAI3C,CAAC;AAEF;;GAEG;AACH,wBAAgB,QAAQ,CAAC,QAAQ,EAAE,MAAM,GAAG,MAAM,CAEjD"}
|
|
@@ -0,0 +1,37 @@
|
|
|
1
|
+
import { boardToolNames, memoryToolNames, taskToolNames } from '@openagentsy/core';
|
|
2
|
+
/**
|
|
3
|
+
* The base name of a tool.
|
|
4
|
+
*
|
|
5
|
+
* A host hands a tool name over as `<server>_<tool>`: the memory server's
|
|
6
|
+
* `oa_memory_decide` arrives as `openagentsy-memory_oa_memory_decide`. The
|
|
7
|
+
* base name is the tool itself, and the rules are written against the base
|
|
8
|
+
* name, so it has to be the same whichever host called.
|
|
9
|
+
*
|
|
10
|
+
* The prefix is not stripped by a pattern. A pattern that strips
|
|
11
|
+
* `^[a-z0-9-]+_` in front of `oa_` leaves every `board_*` name untouched,
|
|
12
|
+
* which is the hole the vocabulary closes: the name is resolved by the
|
|
13
|
+
* longest suffix the registered vocabulary knows.
|
|
14
|
+
*/
|
|
15
|
+
export function baseToolName(toolName, vocabulary) {
|
|
16
|
+
const longest = [...vocabulary].sort((a, b) => b.length - a.length);
|
|
17
|
+
const found = longest.find((entry) => toolName === entry || toolName.endsWith(`_${entry}`));
|
|
18
|
+
return found ?? toolName;
|
|
19
|
+
}
|
|
20
|
+
/**
|
|
21
|
+
* Every tool name the workspace knows.
|
|
22
|
+
*
|
|
23
|
+
* The board, the task store and the memory slice; whatever else a package
|
|
24
|
+
* exports. The list is the vocabulary a tool name is resolved against.
|
|
25
|
+
*/
|
|
26
|
+
export const toolVocabulary = [
|
|
27
|
+
...boardToolNames,
|
|
28
|
+
...taskToolNames,
|
|
29
|
+
...memoryToolNames,
|
|
30
|
+
];
|
|
31
|
+
/**
|
|
32
|
+
* The base name of a tool, against the workspace vocabulary.
|
|
33
|
+
*/
|
|
34
|
+
export function baseTool(toolName) {
|
|
35
|
+
return baseToolName(toolName, toolVocabulary);
|
|
36
|
+
}
|
|
37
|
+
//# sourceMappingURL=base-tool.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"base-tool.js","sourceRoot":"","sources":["../src/base-tool.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,cAAc,EAAE,eAAe,EAAE,aAAa,EAAE,MAAM,mBAAmB,CAAC;AAEnF;;;;;;;;;;;;GAYG;AACH,MAAM,UAAU,YAAY,CAAC,QAAgB,EAAE,UAA6B;IAC1E,MAAM,OAAO,GAAG,CAAC,GAAG,UAAU,CAAC,CAAC,IAAI,CAAC,CAAC,CAAC,EAAE,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,MAAM,GAAG,CAAC,CAAC,MAAM,CAAC,CAAC;IACpE,MAAM,KAAK,GAAG,OAAO,CAAC,IAAI,CAAC,CAAC,KAAK,EAAE,EAAE,CAAC,QAAQ,KAAK,KAAK,IAAI,QAAQ,CAAC,QAAQ,CAAC,IAAI,KAAK,EAAE,CAAC,CAAC,CAAC;IAC5F,OAAO,KAAK,IAAI,QAAQ,CAAC;AAC3B,CAAC;AAED;;;;;GAKG;AACH,MAAM,CAAC,MAAM,cAAc,GAAsB;IAC/C,GAAG,cAAc;IACjB,GAAG,aAAa;IAChB,GAAG,eAAe;CACnB,CAAC;AAEF;;GAEG;AACH,MAAM,UAAU,QAAQ,CAAC,QAAgB;IACvC,OAAO,YAAY,CAAC,QAAQ,EAAE,cAAc,CAAC,CAAC;AAChD,CAAC"}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"base-tool.test.d.ts","sourceRoot":"","sources":["../src/base-tool.test.ts"],"names":[],"mappings":""}
|
|
@@ -0,0 +1,34 @@
|
|
|
1
|
+
import { describe, expect, it } from 'vitest';
|
|
2
|
+
import { baseTool, baseToolName, toolVocabulary } from './base-tool.js';
|
|
3
|
+
/**
|
|
4
|
+
* The pattern revision 1 used to strip a server prefix.
|
|
5
|
+
*
|
|
6
|
+
* It looked ahead for `oa_`, so it left every `board_*` name alone. The
|
|
7
|
+
* test below pins that the old pattern would not have resolved the name.
|
|
8
|
+
*/
|
|
9
|
+
const OLD_PREFIX = /^[a-z0-9-]+_(?=oa_)/i;
|
|
10
|
+
describe('the base tool name', () => {
|
|
11
|
+
it('resolves a board tool the old prefix pattern left alone', () => {
|
|
12
|
+
const name = 'openagentsy-board_board_record';
|
|
13
|
+
expect(OLD_PREFIX.test(name)).toBe(false);
|
|
14
|
+
expect(baseToolName(name, toolVocabulary)).toBe('board_record');
|
|
15
|
+
});
|
|
16
|
+
it('keeps a tool that is already its base name', () => {
|
|
17
|
+
expect(baseToolName('oa_task_list', toolVocabulary)).toBe('oa_task_list');
|
|
18
|
+
expect(baseTool('oa_task_list')).toBe('oa_task_list');
|
|
19
|
+
});
|
|
20
|
+
it('returns an unknown tool as it is', () => {
|
|
21
|
+
expect(baseToolName('a_tool_nobody_registers', toolVocabulary)).toBe('a_tool_nobody_registers');
|
|
22
|
+
});
|
|
23
|
+
it('takes the longest suffix the vocabulary knows', () => {
|
|
24
|
+
expect(baseToolName('openagentsy-board_oa_task_list', toolVocabulary)).toBe('oa_task_list');
|
|
25
|
+
expect(baseToolName('openagentsy-memory_oa_memory_decide', toolVocabulary)).toBe('oa_memory_decide');
|
|
26
|
+
expect(baseToolName('openagentsy-board_board_lease_grant', toolVocabulary)).toBe('board_lease_grant');
|
|
27
|
+
});
|
|
28
|
+
it('knows the board, the task and the memory tools', () => {
|
|
29
|
+
expect(toolVocabulary).toContain('board_record');
|
|
30
|
+
expect(toolVocabulary).toContain('oa_task_list');
|
|
31
|
+
expect(toolVocabulary).toContain('oa_memory_decide');
|
|
32
|
+
});
|
|
33
|
+
});
|
|
34
|
+
//# sourceMappingURL=base-tool.test.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"base-tool.test.js","sourceRoot":"","sources":["../src/base-tool.test.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,QAAQ,EAAE,MAAM,EAAE,EAAE,EAAE,MAAM,QAAQ,CAAC;AAC9C,OAAO,EAAE,QAAQ,EAAE,YAAY,EAAE,cAAc,EAAE,MAAM,gBAAgB,CAAC;AAExE;;;;;GAKG;AACH,MAAM,UAAU,GAAG,sBAAsB,CAAC;AAE1C,QAAQ,CAAC,oBAAoB,EAAE,GAAG,EAAE;IAClC,EAAE,CAAC,yDAAyD,EAAE,GAAG,EAAE;QACjE,MAAM,IAAI,GAAG,gCAAgC,CAAC;QAC9C,MAAM,CAAC,UAAU,CAAC,IAAI,CAAC,IAAI,CAAC,CAAC,CAAC,IAAI,CAAC,KAAK,CAAC,CAAC;QAC1C,MAAM,CAAC,YAAY,CAAC,IAAI,EAAE,cAAc,CAAC,CAAC,CAAC,IAAI,CAAC,cAAc,CAAC,CAAC;IAClE,CAAC,CAAC,CAAC;IAEH,EAAE,CAAC,4CAA4C,EAAE,GAAG,EAAE;QACpD,MAAM,CAAC,YAAY,CAAC,cAAc,EAAE,cAAc,CAAC,CAAC,CAAC,IAAI,CAAC,cAAc,CAAC,CAAC;QAC1E,MAAM,CAAC,QAAQ,CAAC,cAAc,CAAC,CAAC,CAAC,IAAI,CAAC,cAAc,CAAC,CAAC;IACxD,CAAC,CAAC,CAAC;IAEH,EAAE,CAAC,kCAAkC,EAAE,GAAG,EAAE;QAC1C,MAAM,CAAC,YAAY,CAAC,yBAAyB,EAAE,cAAc,CAAC,CAAC,CAAC,IAAI,CAAC,yBAAyB,CAAC,CAAC;IAClG,CAAC,CAAC,CAAC;IAEH,EAAE,CAAC,+CAA+C,EAAE,GAAG,EAAE;QACvD,MAAM,CAAC,YAAY,CAAC,gCAAgC,EAAE,cAAc,CAAC,CAAC,CAAC,IAAI,CAAC,cAAc,CAAC,CAAC;QAC5F,MAAM,CAAC,YAAY,CAAC,qCAAqC,EAAE,cAAc,CAAC,CAAC,CAAC,IAAI,CAC9E,kBAAkB,CACnB,CAAC;QACF,MAAM,CAAC,YAAY,CAAC,qCAAqC,EAAE,cAAc,CAAC,CAAC,CAAC,IAAI,CAC9E,mBAAmB,CACpB,CAAC;IACJ,CAAC,CAAC,CAAC;IAEH,EAAE,CAAC,gDAAgD,EAAE,GAAG,EAAE;QACxD,MAAM,CAAC,cAAc,CAAC,CAAC,SAAS,CAAC,cAAc,CAAC,CAAC;QACjD,MAAM,CAAC,cAAc,CAAC,CAAC,SAAS,CAAC,cAAc,CAAC,CAAC;QACjD,MAAM,CAAC,cAAc,CAAC,CAAC,SAAS,CAAC,kBAAkB,CAAC,CAAC;IACvD,CAAC,CAAC,CAAC;AACL,CAAC,CAAC,CAAC"}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/eval/index.ts"],"names":[],"mappings":"AAAA,cAAc,aAAa,CAAC;AAC5B,cAAc,YAAY,CAAC"}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.js","sourceRoot":"","sources":["../../src/eval/index.ts"],"names":[],"mappings":"AAAA,cAAc,aAAa,CAAC;AAC5B,cAAc,YAAY,CAAC"}
|
|
@@ -0,0 +1,179 @@
|
|
|
1
|
+
import { EvalArtifact, type EvalAssertions, type EvalResult, type EvalScenario } from './types.js';
|
|
2
|
+
/** The environment a proof gets: the allowlist, and nothing else. */
|
|
3
|
+
export declare function childEnv(env?: NodeJS.ProcessEnv): Record<string, string>;
|
|
4
|
+
/**
|
|
5
|
+
* The command, checked.
|
|
6
|
+
*
|
|
7
|
+
* The schema already holds an array, and this says why: a string is a shell
|
|
8
|
+
* line, and a shell line is a program the runner does not choose.
|
|
9
|
+
*/
|
|
10
|
+
export declare function assertArgv(command: unknown): asserts command is string[];
|
|
11
|
+
/** The hash of a command, so a result can name it without carrying it. */
|
|
12
|
+
export declare function hashOf(text: string): string;
|
|
13
|
+
/** The hash of an argv. */
|
|
14
|
+
export declare function commandHash(command: readonly string[]): string;
|
|
15
|
+
/** What a run produced. */
|
|
16
|
+
export type RunOutcome = {
|
|
17
|
+
exitCode: number | null;
|
|
18
|
+
timedOut: boolean;
|
|
19
|
+
stdout: string;
|
|
20
|
+
stderr: string;
|
|
21
|
+
};
|
|
22
|
+
/**
|
|
23
|
+
* Run one command.
|
|
24
|
+
*
|
|
25
|
+
* The spawn has no shell, the environment is the allowlist, and the timeout
|
|
26
|
+
* kills the group. The output is kept in memory for the assertions and is
|
|
27
|
+
* never written anywhere.
|
|
28
|
+
*/
|
|
29
|
+
export declare function runCommand(command: string[], cwd: string, timeoutMs: number): Promise<RunOutcome>;
|
|
30
|
+
/**
|
|
31
|
+
* The verdicts of one scenario.
|
|
32
|
+
*
|
|
33
|
+
* A scenario that only names an exit code does not care what was printed, so
|
|
34
|
+
* only the named assertions are recorded. The spawn itself is always one:
|
|
35
|
+
* a command that could not start has not passed.
|
|
36
|
+
*/
|
|
37
|
+
export declare function assertionsOf(scenario: EvalScenario, outcome: RunOutcome): EvalAssertions;
|
|
38
|
+
/** Whether every assertion held. */
|
|
39
|
+
export declare function allPassed(assertions: EvalAssertions): boolean;
|
|
40
|
+
/**
|
|
41
|
+
* The directory the results live in.
|
|
42
|
+
*/
|
|
43
|
+
export declare function evalResultsDir(stateRoot: string): string;
|
|
44
|
+
/**
|
|
45
|
+
* The path a result is written to.
|
|
46
|
+
*
|
|
47
|
+
* The lesson id names the file, so it is checked before it names anything:
|
|
48
|
+
* an id that is really a path would write the result outside the directory
|
|
49
|
+
* the results belong in.
|
|
50
|
+
*/
|
|
51
|
+
export declare function evalResultPath(stateRoot: string, lessonId: string, at: Date): string;
|
|
52
|
+
/**
|
|
53
|
+
* Write a result.
|
|
54
|
+
*
|
|
55
|
+
* The mode is 0600, because a result says what a proof did in a project and
|
|
56
|
+
* nobody else has a reason to read it. The file is opened with
|
|
57
|
+
* O_NOFOLLOW, so a symlink at the path is not followed, and with O_EXCL, so
|
|
58
|
+
* an existing file is never replaced: a result is evidence and evidence is
|
|
59
|
+
* not overwritten.
|
|
60
|
+
*/
|
|
61
|
+
export declare function writeEvalResult(path: string, result: EvalResult, stateRoot: string): Promise<string>;
|
|
62
|
+
/**
|
|
63
|
+
* Check that the verdicts agree with the assertions.
|
|
64
|
+
*
|
|
65
|
+
* The result is recomputed from the assertions and compared with what was
|
|
66
|
+
* recorded. A result that disagrees with its own evidence is not written,
|
|
67
|
+
* because a proof nobody can reconcile is not a proof.
|
|
68
|
+
*/
|
|
69
|
+
export declare function assertConsistent(result: EvalResult): void;
|
|
70
|
+
/** What a run needs. */
|
|
71
|
+
export type EvalRunOptions = {
|
|
72
|
+
/** The artifact file. */
|
|
73
|
+
artifactPath: string;
|
|
74
|
+
/** The root a working directory may sit in. */
|
|
75
|
+
workspace: string;
|
|
76
|
+
/**
|
|
77
|
+
* The other roots a working directory may sit in.
|
|
78
|
+
*
|
|
79
|
+
* An OpenAgentsy worktree of the same project is one, and so is a
|
|
80
|
+
* temporary worktree the runner made itself.
|
|
81
|
+
*/
|
|
82
|
+
allowedRoots?: string[];
|
|
83
|
+
/** The directory a relative working directory resolves against. */
|
|
84
|
+
base?: string;
|
|
85
|
+
/** Where the result is written. The workspace by default. */
|
|
86
|
+
stateRoot?: string;
|
|
87
|
+
/** The lesson the run belongs to. The artifact's own lesson by default. */
|
|
88
|
+
lessonId?: string;
|
|
89
|
+
/** The clock, so a test can pin the moment. */
|
|
90
|
+
now?: () => Date;
|
|
91
|
+
};
|
|
92
|
+
/**
|
|
93
|
+
* The roots a working directory may sit in.
|
|
94
|
+
*
|
|
95
|
+
* The workspace, the OpenAgentsy worktrees of the same project, and whatever
|
|
96
|
+
* the caller adds.
|
|
97
|
+
*/
|
|
98
|
+
export declare function allowedRoots(options: EvalRunOptions): string[];
|
|
99
|
+
/**
|
|
100
|
+
* The working directory of a scenario, checked.
|
|
101
|
+
*
|
|
102
|
+
* A relative one resolves against the base and has to land inside one of the
|
|
103
|
+
* roots. An absolute one has to land inside one of them as well, so a proof
|
|
104
|
+
* cannot ask for `/etc` or for a directory that only looks close.
|
|
105
|
+
*
|
|
106
|
+
* The check is made on the real paths, because a link inside the workspace
|
|
107
|
+
* can point out of it: a scenario that names a link would otherwise run
|
|
108
|
+
* somewhere the workspace does not cover.
|
|
109
|
+
*/
|
|
110
|
+
export declare function resolveScenarioCwd(scenario: EvalScenario, base: string, roots: string[]): Promise<string>;
|
|
111
|
+
/**
|
|
112
|
+
* The artifact, read and checked.
|
|
113
|
+
*
|
|
114
|
+
* The schema is the contract: an artifact that does not match it is not a
|
|
115
|
+
* proof, and the issues say what is wrong with it.
|
|
116
|
+
*/
|
|
117
|
+
export declare function readArtifact(path: string): Promise<EvalArtifact>;
|
|
118
|
+
/**
|
|
119
|
+
* Run an artifact and write the result.
|
|
120
|
+
*
|
|
121
|
+
* The result is written whatever the verdicts are: a failing run is the
|
|
122
|
+
* evidence that a lesson is not real, and it is worth keeping.
|
|
123
|
+
*/
|
|
124
|
+
export declare function runEvalArtifact(options: EvalRunOptions): Promise<EvalResult>;
|
|
125
|
+
/**
|
|
126
|
+
* The argv of a git command, with the runner's own settings in front of it.
|
|
127
|
+
*
|
|
128
|
+
* A git option only counts before the subcommand it belongs to, so the
|
|
129
|
+
* settings come first.
|
|
130
|
+
*/
|
|
131
|
+
export declare function gitCommand(args: readonly string[]): string[];
|
|
132
|
+
/**
|
|
133
|
+
* A temporary worktree, and what to do in it.
|
|
134
|
+
*
|
|
135
|
+
* The worktree is where the candidate change is applied: the baseline
|
|
136
|
+
* scenario has to fail on the current owner and pass with the change, and
|
|
137
|
+
* the change lives in a commit, so the commit is checked out apart from the
|
|
138
|
+
* tree that is being worked in.
|
|
139
|
+
*
|
|
140
|
+
* The directory is made before the worktree is added, so the cleanup has to
|
|
141
|
+
* run whether the add worked or not: a directory left behind holds a
|
|
142
|
+
* checkout of the project.
|
|
143
|
+
*/
|
|
144
|
+
export declare function withTemporaryWorktree<T>(options: {
|
|
145
|
+
repo: string;
|
|
146
|
+
ref: string;
|
|
147
|
+
run: (worktree: string) => Promise<T>;
|
|
148
|
+
}): Promise<T>;
|
|
149
|
+
/** What the reproduction found. */
|
|
150
|
+
export type BaselineOutcome = {
|
|
151
|
+
/** Whether the baseline scenario failed on the current owner. */
|
|
152
|
+
baseline_failed: boolean;
|
|
153
|
+
/** Whether the same scenario passed with the change applied. */
|
|
154
|
+
with_change_passed: boolean;
|
|
155
|
+
/** Whether the proof holds. */
|
|
156
|
+
passed: boolean;
|
|
157
|
+
/** Why the baseline could not be reproduced, when it could not. */
|
|
158
|
+
reason?: string;
|
|
159
|
+
/** The run on the current owner, before the change. */
|
|
160
|
+
before?: EvalResult;
|
|
161
|
+
/** The run with the change applied, in the temporary worktree. */
|
|
162
|
+
after?: EvalResult;
|
|
163
|
+
};
|
|
164
|
+
/**
|
|
165
|
+
* Reproduce the baseline.
|
|
166
|
+
*
|
|
167
|
+
* The baseline scenario has to fail on the current owner and pass with the
|
|
168
|
+
* change applied in a temporary worktree. A baseline that passes on the
|
|
169
|
+
* current owner proves nothing: the behaviour was already there.
|
|
170
|
+
*/
|
|
171
|
+
export declare function reproduceBaseline(options: {
|
|
172
|
+
artifactPath: string;
|
|
173
|
+
workspace: string;
|
|
174
|
+
repo: string;
|
|
175
|
+
changeRef: string;
|
|
176
|
+
lessonId?: string;
|
|
177
|
+
now?: () => Date;
|
|
178
|
+
}): Promise<BaselineOutcome>;
|
|
179
|
+
//# sourceMappingURL=runner.d.ts.map
|