@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
|
@@ -0,0 +1,163 @@
|
|
|
1
|
+
import type { EvalResult } from '../eval/types.js';
|
|
2
|
+
export type { EvalResult };
|
|
3
|
+
/**
|
|
4
|
+
* The lesson model.
|
|
5
|
+
*
|
|
6
|
+
* A lesson is a claim about behaviour that a person has not decided on yet.
|
|
7
|
+
* It starts as a candidate and it only becomes a fact through states, and
|
|
8
|
+
* every state is a gate:
|
|
9
|
+
*
|
|
10
|
+
* - a candidate cannot leave without a start packet that says what should
|
|
11
|
+
* happen differently, what would show it, and who owns the change;
|
|
12
|
+
* - a validated lesson needs a passing eval result, because a claim with no
|
|
13
|
+
* proof is an opinion;
|
|
14
|
+
* - an applied lesson needs an owner and a change reference, because a change
|
|
15
|
+
* nobody owns is a change nobody can roll back.
|
|
16
|
+
*/
|
|
17
|
+
/** Every state a lesson can be in. */
|
|
18
|
+
export declare const LESSON_STATES: readonly ["candidate", "validated", "applied", "rejected", "superseded", "rolled_back", "no_delta", "open_question"];
|
|
19
|
+
export type LessonState = (typeof LESSON_STATES)[number];
|
|
20
|
+
/**
|
|
21
|
+
* Who holds the change.
|
|
22
|
+
*
|
|
23
|
+
* The owner is the narrowest thing that can hold the lesson, in this order:
|
|
24
|
+
* a test or a validator, the owning skill or role file, the project
|
|
25
|
+
* instructions, a memory note. The last two are the ones that cannot be
|
|
26
|
+
* enforced in code, so they need a reason.
|
|
27
|
+
*/
|
|
28
|
+
export type Owner = `test:${string}` | `validator:${string}` | `skill:${string}` | `role:${string}` | 'instructions' | 'memory';
|
|
29
|
+
/**
|
|
30
|
+
* The packet that says what a lesson asks for.
|
|
31
|
+
*
|
|
32
|
+
* It is what the person reads before deciding, so every field is a question
|
|
33
|
+
* that has an answer: what should happen differently, which task would show
|
|
34
|
+
* it, what is the evidence, who owns the change, which files may change and
|
|
35
|
+
* where the proof is.
|
|
36
|
+
*/
|
|
37
|
+
export interface StartPacket {
|
|
38
|
+
/** What the agent should do differently next time. */
|
|
39
|
+
futureBehavior: string;
|
|
40
|
+
/** One concrete scenario that should now succeed. */
|
|
41
|
+
representativeTask: string;
|
|
42
|
+
/** The ledger waste ids, the session ids, the failing test, the correction. */
|
|
43
|
+
evidence: string[];
|
|
44
|
+
/** Exactly one durable owner. */
|
|
45
|
+
owner: Owner;
|
|
46
|
+
/** Why code or a skill cannot hold the lesson. Needed for a weak owner. */
|
|
47
|
+
ownerJustification?: string;
|
|
48
|
+
/** The files the change may touch. */
|
|
49
|
+
writeBoundary: string[];
|
|
50
|
+
/** The path to an eval artifact, or to an evals/ suite. */
|
|
51
|
+
proof: string;
|
|
52
|
+
}
|
|
53
|
+
/** One decision, and the state it moved the lesson to. */
|
|
54
|
+
export interface Transition {
|
|
55
|
+
from: LessonState;
|
|
56
|
+
to: LessonState;
|
|
57
|
+
/** Who decided. */
|
|
58
|
+
decidedBy: string;
|
|
59
|
+
/** Why. */
|
|
60
|
+
reason: string;
|
|
61
|
+
/** ISO 8601. */
|
|
62
|
+
ts: string;
|
|
63
|
+
/** The commit or the change the decision names. */
|
|
64
|
+
changeRef?: string;
|
|
65
|
+
/** Whether the eval result passed, when one was part of the decision. */
|
|
66
|
+
evalPassed?: boolean;
|
|
67
|
+
}
|
|
68
|
+
/**
|
|
69
|
+
* A lesson.
|
|
70
|
+
*
|
|
71
|
+
* The summary says what happened, the occurrences say how often, and the
|
|
72
|
+
* history says who decided what. A count is not evidence: a lesson that
|
|
73
|
+
* happened often is still a claim.
|
|
74
|
+
*/
|
|
75
|
+
export interface Lesson {
|
|
76
|
+
id: string;
|
|
77
|
+
state: LessonState;
|
|
78
|
+
summary: string;
|
|
79
|
+
occurrences: number;
|
|
80
|
+
packet: Partial<StartPacket>;
|
|
81
|
+
history: Transition[];
|
|
82
|
+
/** The waste kind the lesson came from. */
|
|
83
|
+
kind: string;
|
|
84
|
+
/** The detail of the waste, normalised into the shape it was compared by. */
|
|
85
|
+
detail: string;
|
|
86
|
+
/** The sessions the waste was seen in. */
|
|
87
|
+
sessions: string[];
|
|
88
|
+
/** ISO 8601. */
|
|
89
|
+
created_at: string;
|
|
90
|
+
/** ISO 8601. */
|
|
91
|
+
updated_at: string;
|
|
92
|
+
}
|
|
93
|
+
/**
|
|
94
|
+
* A decision about a lesson.
|
|
95
|
+
*
|
|
96
|
+
* The decision is the person's, and it says who made it: an agent may
|
|
97
|
+
* propose, but the map keeps it from deciding.
|
|
98
|
+
*/
|
|
99
|
+
export interface Decision {
|
|
100
|
+
decidedBy: string;
|
|
101
|
+
reason: string;
|
|
102
|
+
/** The proof, when the decision rests on one. */
|
|
103
|
+
evalResult?: EvalResult;
|
|
104
|
+
/** The change the decision names, when it applies one. */
|
|
105
|
+
changeRef?: string;
|
|
106
|
+
}
|
|
107
|
+
/**
|
|
108
|
+
* The error the lesson model raises.
|
|
109
|
+
*
|
|
110
|
+
* It carries a hint, like every other error here: the message says what
|
|
111
|
+
* happened, the hint says what to do next.
|
|
112
|
+
*/
|
|
113
|
+
export declare class LessonError extends Error {
|
|
114
|
+
readonly hint: string;
|
|
115
|
+
constructor(message: string, hint: string);
|
|
116
|
+
}
|
|
117
|
+
/**
|
|
118
|
+
* Whether a value names an owner.
|
|
119
|
+
*/
|
|
120
|
+
export declare function isOwner(value: unknown): value is Owner;
|
|
121
|
+
/**
|
|
122
|
+
* Whether the owner is one that cannot hold the lesson in code.
|
|
123
|
+
*/
|
|
124
|
+
export declare function isWeakOwner(owner: Owner): boolean;
|
|
125
|
+
/**
|
|
126
|
+
* Whether the packet says everything it has to say.
|
|
127
|
+
*
|
|
128
|
+
* A weak owner needs its reason: an instruction or a memory note holds a
|
|
129
|
+
* lesson that code cannot, and the person has to read why.
|
|
130
|
+
*/
|
|
131
|
+
export declare function packetComplete(packet: Partial<StartPacket>): boolean;
|
|
132
|
+
/**
|
|
133
|
+
* The states a lesson may move to from where it is.
|
|
134
|
+
*/
|
|
135
|
+
export declare function nextStates(state: LessonState): LessonState[];
|
|
136
|
+
/**
|
|
137
|
+
* Move a lesson to another state.
|
|
138
|
+
*
|
|
139
|
+
* The move throws in four cases, and only in these four:
|
|
140
|
+
*
|
|
141
|
+
* - the move is not one the lifecycle allows;
|
|
142
|
+
* - the move is not a rejection while the start packet is incomplete, so a
|
|
143
|
+
* candidate cannot leave without saying what it asks for;
|
|
144
|
+
* - the move validates without a passing eval result;
|
|
145
|
+
* - the move applies without an owner and a change reference.
|
|
146
|
+
*/
|
|
147
|
+
export declare function transition(lesson: Lesson, to: LessonState, decision: Decision, now?: () => Date): Lesson;
|
|
148
|
+
/**
|
|
149
|
+
* A text as the dedup sees it: lowercased, whitespace collapsed, and
|
|
150
|
+
* without the punctuation that carries no meaning.
|
|
151
|
+
*
|
|
152
|
+
* The summary of a lesson and the detail of a waste are both compared this
|
|
153
|
+
* way, so "Read the file." and "read the file" are one lesson.
|
|
154
|
+
*/
|
|
155
|
+
export declare function normalizeText(text: string): string;
|
|
156
|
+
/**
|
|
157
|
+
* Whether a proof points at an evals/ suite rather than at an artifact.
|
|
158
|
+
*
|
|
159
|
+
* A suite runs through the safety suite's own runner, so it is judged by
|
|
160
|
+
* three runs instead of one.
|
|
161
|
+
*/
|
|
162
|
+
export declare function isSuiteProof(proof: string): boolean;
|
|
163
|
+
//# sourceMappingURL=model.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"model.d.ts","sourceRoot":"","sources":["../../src/lessons/model.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,181 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* The lesson model.
|
|
3
|
+
*
|
|
4
|
+
* A lesson is a claim about behaviour that a person has not decided on yet.
|
|
5
|
+
* It starts as a candidate and it only becomes a fact through states, and
|
|
6
|
+
* every state is a gate:
|
|
7
|
+
*
|
|
8
|
+
* - a candidate cannot leave without a start packet that says what should
|
|
9
|
+
* happen differently, what would show it, and who owns the change;
|
|
10
|
+
* - a validated lesson needs a passing eval result, because a claim with no
|
|
11
|
+
* proof is an opinion;
|
|
12
|
+
* - an applied lesson needs an owner and a change reference, because a change
|
|
13
|
+
* nobody owns is a change nobody can roll back.
|
|
14
|
+
*/
|
|
15
|
+
/** Every state a lesson can be in. */
|
|
16
|
+
export const LESSON_STATES = [
|
|
17
|
+
'candidate',
|
|
18
|
+
'validated',
|
|
19
|
+
'applied',
|
|
20
|
+
'rejected',
|
|
21
|
+
'superseded',
|
|
22
|
+
'rolled_back',
|
|
23
|
+
'no_delta',
|
|
24
|
+
'open_question',
|
|
25
|
+
];
|
|
26
|
+
/**
|
|
27
|
+
* The states each state may move to.
|
|
28
|
+
*
|
|
29
|
+
* The map is the whole of the lifecycle. A state with no entry in it is
|
|
30
|
+
* final: a rejected lesson, a rolled back one and a superseded one are
|
|
31
|
+
* history, and a history is not edited.
|
|
32
|
+
*/
|
|
33
|
+
const NEXT = {
|
|
34
|
+
candidate: ['validated', 'rejected', 'open_question', 'no_delta'],
|
|
35
|
+
open_question: ['candidate', 'rejected'],
|
|
36
|
+
validated: ['applied', 'rejected'],
|
|
37
|
+
applied: ['superseded', 'rolled_back'],
|
|
38
|
+
rejected: [],
|
|
39
|
+
superseded: [],
|
|
40
|
+
rolled_back: [],
|
|
41
|
+
no_delta: [],
|
|
42
|
+
};
|
|
43
|
+
/** The owners that cannot enforce anything, so they need a reason. */
|
|
44
|
+
const WEAK_OWNERS = ['instructions', 'memory'];
|
|
45
|
+
/**
|
|
46
|
+
* The error the lesson model raises.
|
|
47
|
+
*
|
|
48
|
+
* It carries a hint, like every other error here: the message says what
|
|
49
|
+
* happened, the hint says what to do next.
|
|
50
|
+
*/
|
|
51
|
+
export class LessonError extends Error {
|
|
52
|
+
hint;
|
|
53
|
+
constructor(message, hint) {
|
|
54
|
+
super(message);
|
|
55
|
+
this.name = 'LessonError';
|
|
56
|
+
this.hint = hint;
|
|
57
|
+
}
|
|
58
|
+
}
|
|
59
|
+
/**
|
|
60
|
+
* Whether a value names an owner.
|
|
61
|
+
*/
|
|
62
|
+
export function isOwner(value) {
|
|
63
|
+
if (typeof value !== 'string' || value.length === 0)
|
|
64
|
+
return false;
|
|
65
|
+
if (WEAK_OWNERS.includes(value))
|
|
66
|
+
return true;
|
|
67
|
+
return /^(test|validator|skill|role):\S+$/.test(value);
|
|
68
|
+
}
|
|
69
|
+
/**
|
|
70
|
+
* Whether the owner is one that cannot hold the lesson in code.
|
|
71
|
+
*/
|
|
72
|
+
export function isWeakOwner(owner) {
|
|
73
|
+
return WEAK_OWNERS.includes(owner);
|
|
74
|
+
}
|
|
75
|
+
/**
|
|
76
|
+
* Whether a value says something.
|
|
77
|
+
*/
|
|
78
|
+
function hasContent(value) {
|
|
79
|
+
return typeof value === 'string' && value.trim() !== '';
|
|
80
|
+
}
|
|
81
|
+
/**
|
|
82
|
+
* Whether a list holds something.
|
|
83
|
+
*
|
|
84
|
+
* An empty entry is nothing: a list of empty strings says no more than an
|
|
85
|
+
* empty list, and a packet that says no more than nothing is not complete.
|
|
86
|
+
*/
|
|
87
|
+
function hasItems(values) {
|
|
88
|
+
return Array.isArray(values) && values.length > 0 && values.every(hasContent);
|
|
89
|
+
}
|
|
90
|
+
/**
|
|
91
|
+
* Whether the packet says everything it has to say.
|
|
92
|
+
*
|
|
93
|
+
* A weak owner needs its reason: an instruction or a memory note holds a
|
|
94
|
+
* lesson that code cannot, and the person has to read why.
|
|
95
|
+
*/
|
|
96
|
+
export function packetComplete(packet) {
|
|
97
|
+
const owner = packet.owner;
|
|
98
|
+
if (!isOwner(owner))
|
|
99
|
+
return false;
|
|
100
|
+
if (!hasContent(packet.futureBehavior) || !hasContent(packet.representativeTask))
|
|
101
|
+
return false;
|
|
102
|
+
if (!hasItems(packet.evidence) || !hasItems(packet.writeBoundary))
|
|
103
|
+
return false;
|
|
104
|
+
if (!hasContent(packet.proof))
|
|
105
|
+
return false;
|
|
106
|
+
if (isWeakOwner(owner) && !hasContent(packet.ownerJustification))
|
|
107
|
+
return false;
|
|
108
|
+
return true;
|
|
109
|
+
}
|
|
110
|
+
/**
|
|
111
|
+
* The states a lesson may move to from where it is.
|
|
112
|
+
*/
|
|
113
|
+
export function nextStates(state) {
|
|
114
|
+
return NEXT[state];
|
|
115
|
+
}
|
|
116
|
+
/**
|
|
117
|
+
* Move a lesson to another state.
|
|
118
|
+
*
|
|
119
|
+
* The move throws in four cases, and only in these four:
|
|
120
|
+
*
|
|
121
|
+
* - the move is not one the lifecycle allows;
|
|
122
|
+
* - the move is not a rejection while the start packet is incomplete, so a
|
|
123
|
+
* candidate cannot leave without saying what it asks for;
|
|
124
|
+
* - the move validates without a passing eval result;
|
|
125
|
+
* - the move applies without an owner and a change reference.
|
|
126
|
+
*/
|
|
127
|
+
export function transition(lesson, to, decision, now = () => new Date()) {
|
|
128
|
+
if (!NEXT[lesson.state].includes(to)) {
|
|
129
|
+
throw new LessonError(`illegal lesson transition ${lesson.state} → ${to}`, `a lesson can move to ${NEXT[lesson.state].join(', ') || 'nothing'}`);
|
|
130
|
+
}
|
|
131
|
+
if (to !== 'rejected' && to !== 'no_delta' && !packetComplete(lesson.packet)) {
|
|
132
|
+
throw new LessonError('the start packet is incomplete', 'a lesson needs a future behaviour, a representative task, evidence, an owner, a write boundary and a proof');
|
|
133
|
+
}
|
|
134
|
+
if (to === 'validated' && decision.evalResult?.passed !== true) {
|
|
135
|
+
throw new LessonError('validation needs a passing eval result for this lesson', 'run the proof and hand the result to the decision');
|
|
136
|
+
}
|
|
137
|
+
if (to === 'applied' && (!decision.changeRef || !lesson.packet.owner)) {
|
|
138
|
+
throw new LessonError('apply needs one owner and a change reference', 'name the commit or the change that carries the lesson, and the owner that holds it');
|
|
139
|
+
}
|
|
140
|
+
const stamp = now().toISOString();
|
|
141
|
+
const entry = {
|
|
142
|
+
from: lesson.state,
|
|
143
|
+
to,
|
|
144
|
+
decidedBy: decision.decidedBy,
|
|
145
|
+
reason: decision.reason,
|
|
146
|
+
ts: stamp,
|
|
147
|
+
...(decision.changeRef === undefined ? {} : { changeRef: decision.changeRef }),
|
|
148
|
+
...(decision.evalResult === undefined ? {} : { evalPassed: decision.evalResult.passed }),
|
|
149
|
+
};
|
|
150
|
+
return {
|
|
151
|
+
...lesson,
|
|
152
|
+
state: to,
|
|
153
|
+
history: [...lesson.history, entry],
|
|
154
|
+
updated_at: stamp,
|
|
155
|
+
};
|
|
156
|
+
}
|
|
157
|
+
/**
|
|
158
|
+
* A text as the dedup sees it: lowercased, whitespace collapsed, and
|
|
159
|
+
* without the punctuation that carries no meaning.
|
|
160
|
+
*
|
|
161
|
+
* The summary of a lesson and the detail of a waste are both compared this
|
|
162
|
+
* way, so "Read the file." and "read the file" are one lesson.
|
|
163
|
+
*/
|
|
164
|
+
export function normalizeText(text) {
|
|
165
|
+
return text
|
|
166
|
+
.toLowerCase()
|
|
167
|
+
.replace(/[^a-z0-9\s]+/g, ' ')
|
|
168
|
+
.replace(/\s+/g, ' ')
|
|
169
|
+
.trim();
|
|
170
|
+
}
|
|
171
|
+
/**
|
|
172
|
+
* Whether a proof points at an evals/ suite rather than at an artifact.
|
|
173
|
+
*
|
|
174
|
+
* A suite runs through the safety suite's own runner, so it is judged by
|
|
175
|
+
* three runs instead of one.
|
|
176
|
+
*/
|
|
177
|
+
export function isSuiteProof(proof) {
|
|
178
|
+
const segments = proof.split(/[\\/]+/).filter((segment) => segment.length > 0);
|
|
179
|
+
return segments.includes('evals');
|
|
180
|
+
}
|
|
181
|
+
//# sourceMappingURL=model.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"model.js","sourceRoot":"","sources":["../../src/lessons/model.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"model.test.d.ts","sourceRoot":"","sources":["../../src/lessons/model.test.ts"],"names":[],"mappings":""}
|
|
@@ -0,0 +1,178 @@
|
|
|
1
|
+
import { describe, expect, it } from 'vitest';
|
|
2
|
+
import { isSuiteProof, LESSON_STATES, normalizeText, packetComplete, transition, } from './model.js';
|
|
3
|
+
/**
|
|
4
|
+
* The transition table, as the plan gives it.
|
|
5
|
+
*
|
|
6
|
+
* It is written as one string so the test does not copy the shape of the
|
|
7
|
+
* source: the table is the specification, and a test that reads the
|
|
8
|
+
* specification says what is allowed.
|
|
9
|
+
*/
|
|
10
|
+
const ALLOWED = 'candidate=validated,rejected,open_question,no_delta;' +
|
|
11
|
+
'open_question=candidate,rejected;' +
|
|
12
|
+
'validated=applied,rejected;' +
|
|
13
|
+
'applied=superseded,rolled_back';
|
|
14
|
+
function allowedFrom(state) {
|
|
15
|
+
const entry = ALLOWED.split(';').find((part) => part.startsWith(`${state}=`));
|
|
16
|
+
if (entry === undefined)
|
|
17
|
+
return [];
|
|
18
|
+
return entry.slice(`${state}=`.length).split(',');
|
|
19
|
+
}
|
|
20
|
+
/** A packet that says everything it has to say. */
|
|
21
|
+
function completePacket() {
|
|
22
|
+
return {
|
|
23
|
+
futureBehavior: 'read the file before writing to it',
|
|
24
|
+
representativeTask: 'the agent writes to a file that does not exist',
|
|
25
|
+
evidence: ['session-1'],
|
|
26
|
+
owner: 'test:missing-file',
|
|
27
|
+
writeBoundary: ['packages/hooks/src/**'],
|
|
28
|
+
proof: 'evals/missing-file.json',
|
|
29
|
+
};
|
|
30
|
+
}
|
|
31
|
+
/** A lesson in a state, with a packet that is complete unless told otherwise. */
|
|
32
|
+
function lessonIn(state, packet) {
|
|
33
|
+
return {
|
|
34
|
+
id: 'oa-lesson-test0001',
|
|
35
|
+
state,
|
|
36
|
+
summary: 'the file was missing',
|
|
37
|
+
occurrences: 1,
|
|
38
|
+
packet: packet ?? completePacket(),
|
|
39
|
+
history: [],
|
|
40
|
+
kind: 'missing-file',
|
|
41
|
+
detail: 'the file was missing',
|
|
42
|
+
sessions: ['session-1'],
|
|
43
|
+
created_at: '2026-10-27T00:00:00.000Z',
|
|
44
|
+
updated_at: '2026-10-27T00:00:00.000Z',
|
|
45
|
+
};
|
|
46
|
+
}
|
|
47
|
+
/** A passing proof. */
|
|
48
|
+
function passingResult(runs = 3) {
|
|
49
|
+
return {
|
|
50
|
+
schema_version: '1',
|
|
51
|
+
lesson_id: 'oa-lesson-test0001',
|
|
52
|
+
artifact_sha256: 'a'.repeat(64),
|
|
53
|
+
started_at: '2026-10-27T00:00:00.000Z',
|
|
54
|
+
finished_at: '2026-10-27T00:00:01.000Z',
|
|
55
|
+
passed: true,
|
|
56
|
+
runs,
|
|
57
|
+
scenarios: [],
|
|
58
|
+
};
|
|
59
|
+
}
|
|
60
|
+
/** A decision that satisfies the guard of the move it makes. */
|
|
61
|
+
function decisionFor(to) {
|
|
62
|
+
return {
|
|
63
|
+
decidedBy: 'human',
|
|
64
|
+
reason: 'because',
|
|
65
|
+
...(to === 'validated' ? { evalResult: passingResult() } : {}),
|
|
66
|
+
...(to === 'applied' ? { changeRef: 'abc1234' } : {}),
|
|
67
|
+
};
|
|
68
|
+
}
|
|
69
|
+
describe('the transition table', () => {
|
|
70
|
+
for (const from of LESSON_STATES) {
|
|
71
|
+
for (const to of LESSON_STATES) {
|
|
72
|
+
it(`${from} to ${to} ${allowedFrom(from).includes(to) ? 'succeeds' : 'throws'}`, () => {
|
|
73
|
+
const lesson = lessonIn(from);
|
|
74
|
+
if (allowedFrom(from).includes(to)) {
|
|
75
|
+
const moved = transition(lesson, to, decisionFor(to));
|
|
76
|
+
expect(moved.state).toBe(to);
|
|
77
|
+
expect(moved.history).toHaveLength(1);
|
|
78
|
+
expect(moved.history[0]?.from).toBe(from);
|
|
79
|
+
expect(moved.history[0]?.to).toBe(to);
|
|
80
|
+
expect(moved.history[0]?.decidedBy).toBe('human');
|
|
81
|
+
}
|
|
82
|
+
else {
|
|
83
|
+
expect(() => transition(lesson, to, decisionFor(to))).toThrow(/illegal lesson transition/);
|
|
84
|
+
}
|
|
85
|
+
});
|
|
86
|
+
}
|
|
87
|
+
}
|
|
88
|
+
});
|
|
89
|
+
describe('the start packet', () => {
|
|
90
|
+
it('is complete when it says everything it has to say', () => {
|
|
91
|
+
expect(packetComplete(completePacket())).toBe(true);
|
|
92
|
+
});
|
|
93
|
+
it('is incomplete without a future behaviour', () => {
|
|
94
|
+
expect(packetComplete({ ...completePacket(), futureBehavior: ' ' })).toBe(false);
|
|
95
|
+
});
|
|
96
|
+
it('is incomplete without a representative task', () => {
|
|
97
|
+
expect(packetComplete({ ...completePacket(), representativeTask: '' })).toBe(false);
|
|
98
|
+
});
|
|
99
|
+
it('is incomplete without evidence', () => {
|
|
100
|
+
expect(packetComplete({ ...completePacket(), evidence: [] })).toBe(false);
|
|
101
|
+
});
|
|
102
|
+
it('is incomplete without an owner', () => {
|
|
103
|
+
const { owner: _owner, ...packet } = completePacket();
|
|
104
|
+
expect(packetComplete(packet)).toBe(false);
|
|
105
|
+
});
|
|
106
|
+
it('is incomplete without a write boundary', () => {
|
|
107
|
+
expect(packetComplete({ ...completePacket(), writeBoundary: [] })).toBe(false);
|
|
108
|
+
});
|
|
109
|
+
it('is incomplete with an empty entry in a list', () => {
|
|
110
|
+
expect(packetComplete({ ...completePacket(), evidence: [''] })).toBe(false);
|
|
111
|
+
expect(packetComplete({ ...completePacket(), writeBoundary: [' '] })).toBe(false);
|
|
112
|
+
});
|
|
113
|
+
it('is incomplete without a proof', () => {
|
|
114
|
+
expect(packetComplete({ ...completePacket(), proof: '' })).toBe(false);
|
|
115
|
+
});
|
|
116
|
+
it('needs a reason for an instructions owner', () => {
|
|
117
|
+
expect(packetComplete({ ...completePacket(), owner: 'instructions' })).toBe(false);
|
|
118
|
+
expect(packetComplete({
|
|
119
|
+
...completePacket(),
|
|
120
|
+
owner: 'instructions',
|
|
121
|
+
ownerJustification: 'no test can hold this',
|
|
122
|
+
})).toBe(true);
|
|
123
|
+
});
|
|
124
|
+
it('needs a reason for a memory owner', () => {
|
|
125
|
+
expect(packetComplete({ ...completePacket(), owner: 'memory' })).toBe(false);
|
|
126
|
+
});
|
|
127
|
+
});
|
|
128
|
+
describe('the transition', () => {
|
|
129
|
+
it('does not let an incomplete packet leave a candidate', () => {
|
|
130
|
+
const candidate = lessonIn('candidate', { futureBehavior: 'read the file first' });
|
|
131
|
+
expect(() => transition(candidate, 'validated', decisionFor('validated'))).toThrow(/start packet is incomplete/);
|
|
132
|
+
expect(() => transition(candidate, 'open_question', decisionFor('open_question'))).toThrow(/start packet is incomplete/);
|
|
133
|
+
});
|
|
134
|
+
it('lets a candidate be rejected and marked as no delta without a packet', () => {
|
|
135
|
+
const candidate = lessonIn('candidate', {});
|
|
136
|
+
expect(transition(candidate, 'rejected', decisionFor('rejected')).state).toBe('rejected');
|
|
137
|
+
expect(transition(candidate, 'no_delta', decisionFor('no_delta')).state).toBe('no_delta');
|
|
138
|
+
});
|
|
139
|
+
it('does not validate without a passing eval result', () => {
|
|
140
|
+
const candidate = lessonIn('candidate');
|
|
141
|
+
expect(() => transition(candidate, 'validated', { decidedBy: 'human', reason: 'because' })).toThrow(/validation needs a passing eval result/);
|
|
142
|
+
expect(() => transition(candidate, 'validated', {
|
|
143
|
+
decidedBy: 'human',
|
|
144
|
+
reason: 'because',
|
|
145
|
+
evalResult: passingResult(),
|
|
146
|
+
})).not.toThrow();
|
|
147
|
+
});
|
|
148
|
+
it('does not apply without a change reference', () => {
|
|
149
|
+
const validated = lessonIn('validated');
|
|
150
|
+
expect(() => transition(validated, 'applied', { decidedBy: 'human', reason: 'because' })).toThrow(/apply needs one owner and a change reference/);
|
|
151
|
+
});
|
|
152
|
+
it('does not apply without an owner', () => {
|
|
153
|
+
const { owner: _owner, ...packet } = completePacket();
|
|
154
|
+
const validated = lessonIn('validated', packet);
|
|
155
|
+
expect(() => transition(validated, 'applied', { decidedBy: 'human', reason: 'because', changeRef: 'abc' })).toThrow();
|
|
156
|
+
});
|
|
157
|
+
it('keeps the history of the lesson', () => {
|
|
158
|
+
const candidate = lessonIn('candidate');
|
|
159
|
+
const validated = transition(candidate, 'validated', decisionFor('validated'));
|
|
160
|
+
const applied = transition(validated, 'applied', decisionFor('applied'));
|
|
161
|
+
expect(applied.history).toHaveLength(2);
|
|
162
|
+
expect(applied.history[1]?.changeRef).toBe('abc1234');
|
|
163
|
+
expect(applied.history[0]?.evalPassed).toBe(true);
|
|
164
|
+
});
|
|
165
|
+
});
|
|
166
|
+
describe('the normalised text', () => {
|
|
167
|
+
it('reads two spellings of the same thing as one', () => {
|
|
168
|
+
expect(normalizeText('The file was missing.')).toBe(normalizeText('the file was missing'));
|
|
169
|
+
});
|
|
170
|
+
});
|
|
171
|
+
describe('the proof', () => {
|
|
172
|
+
it('knows a suite from an artifact', () => {
|
|
173
|
+
expect(isSuiteProof('evals/missing-file.json')).toBe(true);
|
|
174
|
+
expect(isSuiteProof('packages/hooks/evals/suite')).toBe(true);
|
|
175
|
+
expect(isSuiteProof('lessons/missing-file.json')).toBe(false);
|
|
176
|
+
});
|
|
177
|
+
});
|
|
178
|
+
//# sourceMappingURL=model.test.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"model.test.js","sourceRoot":"","sources":["../../src/lessons/model.test.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"permissions.test.d.ts","sourceRoot":"","sources":["../../src/lessons/permissions.test.ts"],"names":[],"mappings":""}
|
|
@@ -0,0 +1,88 @@
|
|
|
1
|
+
import { join } from 'node:path';
|
|
2
|
+
import { fileURLToPath } from 'node:url';
|
|
3
|
+
import { loadRoles } from '@openagentsy/core';
|
|
4
|
+
import { describe, expect, it } from 'vitest';
|
|
5
|
+
/**
|
|
6
|
+
* The permission map and the Lead's recovery text.
|
|
7
|
+
*
|
|
8
|
+
* Both live in the roles file, which is the source of truth for every
|
|
9
|
+
* generated agent, so the check reads the real file rather than a fixture.
|
|
10
|
+
*/
|
|
11
|
+
/** The root of the repository the tests run in. */
|
|
12
|
+
const repositoryRoot = fileURLToPath(new URL('../../../../', import.meta.url));
|
|
13
|
+
/**
|
|
14
|
+
* The tools a person decides with, in both the name the map uses and the
|
|
15
|
+
* name the MCP server exposes them under.
|
|
16
|
+
*/
|
|
17
|
+
const HUMAN_ONLY_TOOLS = [
|
|
18
|
+
'oa_lesson_decide',
|
|
19
|
+
'openagentsy_lesson_decide',
|
|
20
|
+
'openagentsy_oa_lesson_decide',
|
|
21
|
+
'oa_quality_waive',
|
|
22
|
+
'openagentsy_quality_waive',
|
|
23
|
+
'openagentsy_oa_quality_waive',
|
|
24
|
+
'oa_memory_decide',
|
|
25
|
+
'openagentsy_memory_decide',
|
|
26
|
+
'openagentsy_oa_memory_decide',
|
|
27
|
+
];
|
|
28
|
+
/**
|
|
29
|
+
* The recovery text, from the Lead's guidance.
|
|
30
|
+
*
|
|
31
|
+
* The block is the last one in the guidance, so it runs from the marker to
|
|
32
|
+
* the end of the text.
|
|
33
|
+
*/
|
|
34
|
+
function recoveryText(guidance) {
|
|
35
|
+
const lines = guidance.split('\n');
|
|
36
|
+
const start = lines.findIndex((line) => line.trim().startsWith('Recovery loop:'));
|
|
37
|
+
if (start === -1)
|
|
38
|
+
return '';
|
|
39
|
+
return lines.slice(start).join(' ').trim();
|
|
40
|
+
}
|
|
41
|
+
/** The words of a text. A token is never shorter than a word. */
|
|
42
|
+
function words(text) {
|
|
43
|
+
return text.split(/\s+/).filter((word) => word.length > 0);
|
|
44
|
+
}
|
|
45
|
+
describe('the permission map', () => {
|
|
46
|
+
it('denies the three human-only tools to every agent role, in every name', async () => {
|
|
47
|
+
const roles = await loadRoles(join(repositoryRoot, 'roles.yaml'));
|
|
48
|
+
expect(Object.keys(roles.roles).length).toBeGreaterThan(0);
|
|
49
|
+
for (const [name, role] of Object.entries(roles.roles)) {
|
|
50
|
+
for (const tool of HUMAN_ONLY_TOOLS) {
|
|
51
|
+
const rules = role.permissions[tool];
|
|
52
|
+
expect(rules, `${name} must carry ${tool}`).toBeDefined();
|
|
53
|
+
expect(rules?.[0]?.effect, `${name} must deny ${tool}`).toBe('deny');
|
|
54
|
+
}
|
|
55
|
+
}
|
|
56
|
+
});
|
|
57
|
+
});
|
|
58
|
+
describe('the Lead recovery text', () => {
|
|
59
|
+
it('is in the Lead guidance', async () => {
|
|
60
|
+
const roles = await loadRoles(join(repositoryRoot, 'roles.yaml'));
|
|
61
|
+
const lead = roles.roles.lead;
|
|
62
|
+
expect(lead).toBeDefined();
|
|
63
|
+
expect(recoveryText(lead?.guidance ?? '')).not.toBe('');
|
|
64
|
+
});
|
|
65
|
+
it('names the five stages in order', async () => {
|
|
66
|
+
const roles = await loadRoles(join(repositoryRoot, 'roles.yaml'));
|
|
67
|
+
const text = recoveryText(roles.roles.lead?.guidance ?? '');
|
|
68
|
+
const order = ['READ', 'RECOVER', 'DECIDE', 'PERSIST', 'REPORT'].map((stage) => text.indexOf(stage));
|
|
69
|
+
expect(order.every((index) => index >= 0)).toBe(true);
|
|
70
|
+
expect([...order].sort((a, b) => a - b)).toEqual(order);
|
|
71
|
+
});
|
|
72
|
+
it('says the report speaks only after the state was written', async () => {
|
|
73
|
+
const roles = await loadRoles(join(repositoryRoot, 'roles.yaml'));
|
|
74
|
+
const text = recoveryText(roles.roles.lead?.guidance ?? '');
|
|
75
|
+
expect(text).toMatch(/REPORT speaks only after PERSIST wrote the state/);
|
|
76
|
+
});
|
|
77
|
+
it('is under 120 tokens', async () => {
|
|
78
|
+
const roles = await loadRoles(join(repositoryRoot, 'roles.yaml'));
|
|
79
|
+
const text = recoveryText(roles.roles.lead?.guidance ?? '');
|
|
80
|
+
const count = words(text).length;
|
|
81
|
+
// A token is never shorter than a word, and a word is never shorter than
|
|
82
|
+
// about one and a half tokens in English, so the estimate is the count
|
|
83
|
+
// times 1.4.
|
|
84
|
+
expect(count).toBeLessThan(120);
|
|
85
|
+
expect(count * 1.4).toBeLessThan(120);
|
|
86
|
+
});
|
|
87
|
+
});
|
|
88
|
+
//# sourceMappingURL=permissions.test.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"permissions.test.js","sourceRoot":"","sources":["../../src/lessons/permissions.test.ts"],"names":[],"mappings":"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"}
|