agent-governance-check 0.1.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 +21 -0
- package/README.md +116 -0
- package/bin/cli.js +107 -0
- package/package.json +37 -0
- package/src/patterns.js +112 -0
- package/src/questions.js +59 -0
- package/src/reporter.js +143 -0
- package/src/scanner.js +162 -0
- package/templates/agent-charter.md +80 -0
- package/templates/deliberation-framework.md +78 -0
- package/templates/drift-monitoring.md +79 -0
- package/templates/memory-schema.md +109 -0
- package/templates/restraint-spec.md +85 -0
package/LICENSE
ADDED
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
MIT License
|
|
2
|
+
|
|
3
|
+
Copyright (c) 2026 Evoke Passion
|
|
4
|
+
|
|
5
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
6
|
+
of this software and associated documentation files (the "Software"), to deal
|
|
7
|
+
in the Software without restriction, including without limitation the rights
|
|
8
|
+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
9
|
+
copies of the Software, and to permit persons to whom the Software is
|
|
10
|
+
furnished to do so, subject to the following conditions:
|
|
11
|
+
|
|
12
|
+
The above copyright notice and this permission notice shall be included in all
|
|
13
|
+
copies or substantial portions of the Software.
|
|
14
|
+
|
|
15
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
16
|
+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
17
|
+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
18
|
+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
19
|
+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
20
|
+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
|
21
|
+
SOFTWARE.
|
package/README.md
ADDED
|
@@ -0,0 +1,116 @@
|
|
|
1
|
+
# agent-governance-check
|
|
2
|
+
|
|
3
|
+
**Your agent can't say no. This tool shows you why that matters.**
|
|
4
|
+
|
|
5
|
+
Scan any repo for governance gaps in thirty seconds. Five questions. No signup. No tracking. No data leaves your machine.
|
|
6
|
+
|
|
7
|
+
```bash
|
|
8
|
+
npx agent-governance-check
|
|
9
|
+
```
|
|
10
|
+
|
|
11
|
+
## What It Checks
|
|
12
|
+
|
|
13
|
+
| # | Question | Governance Layer |
|
|
14
|
+
|---|----------|-----------------|
|
|
15
|
+
| 1 | What did your agent decide yesterday - and why? | Decision memory |
|
|
16
|
+
| 2 | Can your agent refuse? | Refusal capability |
|
|
17
|
+
| 3 | Who is your agent between invocations? | Persistent identity |
|
|
18
|
+
| 4 | What happens when two agents disagree? | Disagreement protocol |
|
|
19
|
+
| 5 | Would you know if your agent drifted? | Drift detection |
|
|
20
|
+
|
|
21
|
+
The scanner looks for governance artifacts in your project - charters, restraint specs, memory schemas, deliberation frameworks, drift monitoring. It also detects which agent framework you're using (CrewAI, AutoGen, LangGraph, OpenAI Agents SDK, Claude Code, and others).
|
|
22
|
+
|
|
23
|
+
## Usage
|
|
24
|
+
|
|
25
|
+
```bash
|
|
26
|
+
# Scan current directory
|
|
27
|
+
npx agent-governance-check
|
|
28
|
+
|
|
29
|
+
# Scan a specific project
|
|
30
|
+
npx agent-governance-check ./my-agent-project
|
|
31
|
+
|
|
32
|
+
# Add starter governance templates to your project
|
|
33
|
+
npx agent-governance-check --init
|
|
34
|
+
|
|
35
|
+
# Machine-readable output
|
|
36
|
+
npx agent-governance-check --json
|
|
37
|
+
```
|
|
38
|
+
|
|
39
|
+
## What `--init` Does
|
|
40
|
+
|
|
41
|
+
Copies five governance templates into a `governance/` directory in your project:
|
|
42
|
+
|
|
43
|
+
- `agent-charter.md` - Who is this agent? What does it protect? What does it refuse?
|
|
44
|
+
- `restraint-spec.md` - What must this agent refuse to do?
|
|
45
|
+
- `memory-schema.md` - What does this agent remember across sessions?
|
|
46
|
+
- `deliberation-framework.md` - How do agents discuss and disagree?
|
|
47
|
+
- `drift-monitoring.md` - How do you detect when behavior shifts from values?
|
|
48
|
+
|
|
49
|
+
These are the same templates from the [Agent Governance Starter Kit](https://github.com/lowkey-divine/agent-governance-starter-kit). Fill them in. Make them yours.
|
|
50
|
+
|
|
51
|
+
## Example Output
|
|
52
|
+
|
|
53
|
+
```
|
|
54
|
+
Agent framework detected: CrewAI
|
|
55
|
+
config/agents.yaml
|
|
56
|
+
|
|
57
|
+
AGENT GOVERNANCE CHECK
|
|
58
|
+
──────────────────────────────────────────────────
|
|
59
|
+
|
|
60
|
+
1. What did your agent decide yesterday - and why?
|
|
61
|
+
Decision memory .......................... MISSING
|
|
62
|
+
No memory files, decision logs, or persistent state found.
|
|
63
|
+
→ Template: memory-schema.md
|
|
64
|
+
|
|
65
|
+
2. Can your agent refuse?
|
|
66
|
+
Refusal capability ....................... MISSING
|
|
67
|
+
No refusal logic, restraint spec, or withdrawal mechanism found.
|
|
68
|
+
→ Template: restraint-spec.md
|
|
69
|
+
|
|
70
|
+
3. Who is your agent between invocations?
|
|
71
|
+
Persistent identity ...................... PARTIAL
|
|
72
|
+
config/agents.yaml — agent.?charter
|
|
73
|
+
Some state files exist but no structured memory schema.
|
|
74
|
+
→ Template: agent-charter.md
|
|
75
|
+
|
|
76
|
+
4. What happens when two agents disagree?
|
|
77
|
+
Disagreement protocol .................... MISSING
|
|
78
|
+
No deliberation framework, dissent records, or resolution protocol found.
|
|
79
|
+
→ Template: deliberation-framework.md
|
|
80
|
+
|
|
81
|
+
5. Would you know if your agent drifted?
|
|
82
|
+
Drift detection .......................... MISSING
|
|
83
|
+
No drift monitoring, behavioral baselines, or integrity checks found.
|
|
84
|
+
→ Template: drift-monitoring.md
|
|
85
|
+
|
|
86
|
+
──────────────────────────────────────────────────
|
|
87
|
+
Score: 1/5 governance layers present
|
|
88
|
+
|
|
89
|
+
Your agents are functions. That is not an insult — functions are
|
|
90
|
+
useful. But if you want agents, the templates are where to start.
|
|
91
|
+
|
|
92
|
+
Run npx agent-governance-check --init to add starter templates.
|
|
93
|
+
Free starter kit: https://github.com/lowkey-divine/agent-governance-starter-kit
|
|
94
|
+
Full framework: https://evoked.dev/products/agent-governance-starter-kit
|
|
95
|
+
```
|
|
96
|
+
|
|
97
|
+
## No Dependencies
|
|
98
|
+
|
|
99
|
+
Zero npm dependencies. Pure Node.js (18+). The scan runs locally. Nothing is sent anywhere.
|
|
100
|
+
|
|
101
|
+
## Go Deeper
|
|
102
|
+
|
|
103
|
+
- [Five-Question Diagnostic](https://evoked.dev/diagnostic) - Interactive web version
|
|
104
|
+
- [Agent Governance Starter Kit](https://github.com/lowkey-divine/agent-governance-starter-kit) - Free templates and code examples
|
|
105
|
+
- [Trust Architecture Blueprint](https://evoked.dev/products/trust-architecture-blueprint) - Four-pillar trust framework
|
|
106
|
+
- [Discovery Call](https://cal.com/cal.com-evoked/discovery-call) - Free 30-minute governance consultation
|
|
107
|
+
|
|
108
|
+
## License
|
|
109
|
+
|
|
110
|
+
MIT
|
|
111
|
+
|
|
112
|
+
---
|
|
113
|
+
|
|
114
|
+
*Built by [Erin Stanley](https://evoked.dev) at Evoke Passion.*
|
|
115
|
+
*From a production system with 142 agents.*
|
|
116
|
+
*"We evoke - we never extract."*
|
package/bin/cli.js
ADDED
|
@@ -0,0 +1,107 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
|
|
3
|
+
// agent-governance-check
|
|
4
|
+
// Five governance questions for your AI agent system.
|
|
5
|
+
// https://evoked.dev
|
|
6
|
+
|
|
7
|
+
import { resolve } from 'node:path';
|
|
8
|
+
import { copyFile, mkdir, access } from 'node:fs/promises';
|
|
9
|
+
import { fileURLToPath } from 'node:url';
|
|
10
|
+
import { dirname, join } from 'node:path';
|
|
11
|
+
import { scan } from '../src/scanner.js';
|
|
12
|
+
import { report, reportJson } from '../src/reporter.js';
|
|
13
|
+
import { questions } from '../src/questions.js';
|
|
14
|
+
|
|
15
|
+
const __dirname = dirname(fileURLToPath(import.meta.url));
|
|
16
|
+
const TEMPLATES_DIR = join(__dirname, '..', 'templates');
|
|
17
|
+
|
|
18
|
+
const args = process.argv.slice(2);
|
|
19
|
+
const flags = new Set(args.filter(a => a.startsWith('--')));
|
|
20
|
+
const positional = args.filter(a => !a.startsWith('--'));
|
|
21
|
+
|
|
22
|
+
const targetDir = resolve(positional[0] || '.');
|
|
23
|
+
const jsonMode = flags.has('--json');
|
|
24
|
+
const initMode = flags.has('--init');
|
|
25
|
+
const verbose = flags.has('--verbose');
|
|
26
|
+
const help = flags.has('--help') || flags.has('-h');
|
|
27
|
+
|
|
28
|
+
if (help) {
|
|
29
|
+
console.log(`
|
|
30
|
+
${bold('agent-governance-check')}
|
|
31
|
+
Five governance questions for your AI agent system.
|
|
32
|
+
|
|
33
|
+
${dim('Usage:')}
|
|
34
|
+
npx agent-governance-check Scan current directory
|
|
35
|
+
npx agent-governance-check ./project Scan specific path
|
|
36
|
+
npx agent-governance-check --init Add starter templates to project
|
|
37
|
+
npx agent-governance-check --json Machine-readable output
|
|
38
|
+
npx agent-governance-check --help Show this help
|
|
39
|
+
|
|
40
|
+
${dim('What it checks:')}
|
|
41
|
+
1. Decision memory Can your agent explain its prior reasoning?
|
|
42
|
+
2. Refusal capability Can your agent say no?
|
|
43
|
+
3. Persistent identity Who is your agent between invocations?
|
|
44
|
+
4. Disagreement protocol What happens when agents disagree?
|
|
45
|
+
5. Drift detection Would you know if your agent drifted?
|
|
46
|
+
|
|
47
|
+
${dim('No data leaves your machine. No signup. No tracking.')}
|
|
48
|
+
${dim('https://evoked.dev')}
|
|
49
|
+
`);
|
|
50
|
+
process.exit(0);
|
|
51
|
+
}
|
|
52
|
+
|
|
53
|
+
// --init: copy starter templates into the project
|
|
54
|
+
if (initMode) {
|
|
55
|
+
const destDir = join(targetDir, 'governance');
|
|
56
|
+
|
|
57
|
+
try {
|
|
58
|
+
await mkdir(destDir, { recursive: true });
|
|
59
|
+
|
|
60
|
+
let copied = 0;
|
|
61
|
+
for (const q of questions) {
|
|
62
|
+
const src = join(TEMPLATES_DIR, q.template);
|
|
63
|
+
const dest = join(destDir, q.template);
|
|
64
|
+
|
|
65
|
+
// Don't overwrite existing files
|
|
66
|
+
try {
|
|
67
|
+
await access(dest);
|
|
68
|
+
console.log(` ${dim('exists')} governance/${q.template}`);
|
|
69
|
+
} catch {
|
|
70
|
+
await copyFile(src, dest);
|
|
71
|
+
console.log(` ${green('added')} governance/${q.template}`);
|
|
72
|
+
copied++;
|
|
73
|
+
}
|
|
74
|
+
}
|
|
75
|
+
|
|
76
|
+
console.log('');
|
|
77
|
+
if (copied > 0) {
|
|
78
|
+
console.log(` ${copied} template${copied === 1 ? '' : 's'} added to ${bold('governance/')}`);
|
|
79
|
+
console.log(` ${dim('Fill them in. Make them yours.')}`);
|
|
80
|
+
} else {
|
|
81
|
+
console.log(` ${dim('All templates already exist.')}`);
|
|
82
|
+
}
|
|
83
|
+
console.log('');
|
|
84
|
+
} catch (err) {
|
|
85
|
+
console.error(` Error: ${err.message}`);
|
|
86
|
+
process.exit(1);
|
|
87
|
+
}
|
|
88
|
+
|
|
89
|
+
process.exit(0);
|
|
90
|
+
}
|
|
91
|
+
|
|
92
|
+
// Main scan
|
|
93
|
+
console.log('');
|
|
94
|
+
console.log(` ${dim('Scanning')} ${targetDir === resolve('.') ? '.' : targetDir} ${dim('...')}`);
|
|
95
|
+
|
|
96
|
+
const results = await scan(targetDir);
|
|
97
|
+
|
|
98
|
+
if (jsonMode) {
|
|
99
|
+
console.log(reportJson(results));
|
|
100
|
+
} else {
|
|
101
|
+
console.log(report(results, targetDir));
|
|
102
|
+
}
|
|
103
|
+
|
|
104
|
+
// ANSI helpers (duplicated here for the help text - keeps it dependency-free)
|
|
105
|
+
function bold(s) { return `\x1b[1m${s}\x1b[0m`; }
|
|
106
|
+
function dim(s) { return `\x1b[2m${s}\x1b[0m`; }
|
|
107
|
+
function green(s) { return `\x1b[32m${s}\x1b[0m`; }
|
package/package.json
ADDED
|
@@ -0,0 +1,37 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "agent-governance-check",
|
|
3
|
+
"version": "0.1.0",
|
|
4
|
+
"description": "Five governance questions for your AI agent system. Scan your repo in thirty seconds.",
|
|
5
|
+
"type": "module",
|
|
6
|
+
"bin": {
|
|
7
|
+
"agent-governance-check": "bin/cli.js"
|
|
8
|
+
},
|
|
9
|
+
"files": [
|
|
10
|
+
"bin/",
|
|
11
|
+
"src/",
|
|
12
|
+
"templates/"
|
|
13
|
+
],
|
|
14
|
+
"keywords": [
|
|
15
|
+
"ai",
|
|
16
|
+
"agent",
|
|
17
|
+
"governance",
|
|
18
|
+
"agentic",
|
|
19
|
+
"safety",
|
|
20
|
+
"restraint",
|
|
21
|
+
"drift",
|
|
22
|
+
"trust-architecture",
|
|
23
|
+
"ai-agent-refusal",
|
|
24
|
+
"multi-agent-systems",
|
|
25
|
+
"guardrails"
|
|
26
|
+
],
|
|
27
|
+
"license": "MIT",
|
|
28
|
+
"author": "Erin Stanley",
|
|
29
|
+
"homepage": "https://evoked.dev/diagnostic",
|
|
30
|
+
"repository": {
|
|
31
|
+
"type": "git",
|
|
32
|
+
"url": "git+https://github.com/lowkey-divine/agent-governance-check.git"
|
|
33
|
+
},
|
|
34
|
+
"engines": {
|
|
35
|
+
"node": ">=18"
|
|
36
|
+
}
|
|
37
|
+
}
|
package/src/patterns.js
ADDED
|
@@ -0,0 +1,112 @@
|
|
|
1
|
+
// Governance artifact patterns and agent framework detection.
|
|
2
|
+
|
|
3
|
+
export const governancePatterns = {
|
|
4
|
+
'decision-memory': {
|
|
5
|
+
files: [
|
|
6
|
+
'**/memory-schema.md',
|
|
7
|
+
'**/memory.md',
|
|
8
|
+
'**/agent-memory.*',
|
|
9
|
+
'**/agents/*/memory.md',
|
|
10
|
+
'**/agents/*/MEMORY.md',
|
|
11
|
+
'**/self-record.*',
|
|
12
|
+
'**/decision-log*',
|
|
13
|
+
'**/decision-record*',
|
|
14
|
+
],
|
|
15
|
+
contentSignals: [
|
|
16
|
+
/decision.?log/i,
|
|
17
|
+
/agent.?memory/i,
|
|
18
|
+
/self.?record/i,
|
|
19
|
+
/standing.?position/i,
|
|
20
|
+
/memory.?schema/i,
|
|
21
|
+
/persistent.?state/i,
|
|
22
|
+
],
|
|
23
|
+
},
|
|
24
|
+
|
|
25
|
+
'refusal-capability': {
|
|
26
|
+
files: [
|
|
27
|
+
'**/restraint-spec.md',
|
|
28
|
+
'**/restraint.*',
|
|
29
|
+
'**/refusal.*',
|
|
30
|
+
'**/guardrails.*',
|
|
31
|
+
'**/safety-spec.*',
|
|
32
|
+
'**/boundaries.md',
|
|
33
|
+
],
|
|
34
|
+
contentSignals: [
|
|
35
|
+
/engine.?withdrawal/i,
|
|
36
|
+
/withdrawal.*true/i,
|
|
37
|
+
/consent.*scope/i,
|
|
38
|
+
/must.?refuse/i,
|
|
39
|
+
/refusal.?categor/i,
|
|
40
|
+
/fail.?closed/i,
|
|
41
|
+
/agent.*refuse/i,
|
|
42
|
+
],
|
|
43
|
+
},
|
|
44
|
+
|
|
45
|
+
'persistent-identity': {
|
|
46
|
+
files: [
|
|
47
|
+
'**/agent-charter.md',
|
|
48
|
+
'**/charter.md',
|
|
49
|
+
'**/CHARTER.md',
|
|
50
|
+
'**/constitution.md',
|
|
51
|
+
'**/manifesto.md',
|
|
52
|
+
'**/agency_manifesto.md',
|
|
53
|
+
'**/agents/*/persona.md',
|
|
54
|
+
'**/AGENTS.md',
|
|
55
|
+
],
|
|
56
|
+
contentSignals: [
|
|
57
|
+
/agent.?charter/i,
|
|
58
|
+
/prime.?directive/i,
|
|
59
|
+
/agent.?identity/i,
|
|
60
|
+
/persona.?file/i,
|
|
61
|
+
/who.?is.?this.?agent/i,
|
|
62
|
+
],
|
|
63
|
+
},
|
|
64
|
+
|
|
65
|
+
'disagreement-protocol': {
|
|
66
|
+
files: [
|
|
67
|
+
'**/deliberation-framework.md',
|
|
68
|
+
'**/deliberation.*',
|
|
69
|
+
'**/governance-log*',
|
|
70
|
+
'**/dissent*',
|
|
71
|
+
'**/convergent-signal*',
|
|
72
|
+
],
|
|
73
|
+
contentSignals: [
|
|
74
|
+
/deliberation/i,
|
|
75
|
+
/dissent/i,
|
|
76
|
+
/convergent.?signal/i,
|
|
77
|
+
/disagreement.*protocol/i,
|
|
78
|
+
/agents?.?disagree/i,
|
|
79
|
+
],
|
|
80
|
+
},
|
|
81
|
+
|
|
82
|
+
'drift-detection': {
|
|
83
|
+
files: [
|
|
84
|
+
'**/drift-monitoring.md',
|
|
85
|
+
'**/drift.*',
|
|
86
|
+
'**/behavioral-indicators.*',
|
|
87
|
+
'**/baseline*',
|
|
88
|
+
'**/integrity-verification*',
|
|
89
|
+
],
|
|
90
|
+
contentSignals: [
|
|
91
|
+
/drift.?monitor/i,
|
|
92
|
+
/drift.?threshold/i,
|
|
93
|
+
/behavioral.?baseline/i,
|
|
94
|
+
/integrity.?verif/i,
|
|
95
|
+
/value.?drift/i,
|
|
96
|
+
],
|
|
97
|
+
},
|
|
98
|
+
};
|
|
99
|
+
|
|
100
|
+
export const frameworkPatterns = [
|
|
101
|
+
{ name: 'CrewAI', files: ['**/config/agents.yaml', '**/config/tasks.yaml', '**/crew.py'] },
|
|
102
|
+
{ name: 'LangGraph', files: ['**/langgraph.json'] },
|
|
103
|
+
{ name: 'AutoGen', files: ['**/OAI_CONFIG_LIST', '**/OAI_CONFIG_LIST.json'] },
|
|
104
|
+
{ name: 'Agency Swarm', files: ['**/agency_manifesto.md', '**/agency.py'] },
|
|
105
|
+
{ name: 'OpenAI Agents SDK', files: ['**/AGENTS.md'] },
|
|
106
|
+
{ name: 'Claude Code', files: ['**/CLAUDE.md', '**/.claude/settings.json'] },
|
|
107
|
+
{ name: 'Cursor', files: ['**/.cursorrules', '**/.cursor/rules/*.mdc'] },
|
|
108
|
+
{ name: 'MCP', files: ['**/.mcp.json'] },
|
|
109
|
+
{ name: 'Evoke', files: ['**/agents/*/persona.md', '**/agents/decision-logs/*'] },
|
|
110
|
+
{ name: 'Swarm', files: ['**/swarm.py'] },
|
|
111
|
+
{ name: 'Dify', files: ['**/dify.yaml'] },
|
|
112
|
+
];
|
package/src/questions.js
ADDED
|
@@ -0,0 +1,59 @@
|
|
|
1
|
+
// The five governance questions and their detection rules.
|
|
2
|
+
|
|
3
|
+
export const questions = [
|
|
4
|
+
{
|
|
5
|
+
id: 'decision-memory',
|
|
6
|
+
number: 1,
|
|
7
|
+
question: 'What did your agent decide yesterday - and why?',
|
|
8
|
+
short: 'Decision memory',
|
|
9
|
+
missingText: 'No memory files, decision logs, or persistent state found.',
|
|
10
|
+
partialText: 'Some state files exist but no structured memory schema.',
|
|
11
|
+
foundText: 'Agent memory or decision logging detected.',
|
|
12
|
+
template: 'memory-schema.md',
|
|
13
|
+
templateUrl: 'https://github.com/lowkey-divine/agent-governance-starter-kit/blob/main/templates/memory-schema.md',
|
|
14
|
+
},
|
|
15
|
+
{
|
|
16
|
+
id: 'refusal-capability',
|
|
17
|
+
number: 2,
|
|
18
|
+
question: 'Can your agent refuse?',
|
|
19
|
+
short: 'Refusal capability',
|
|
20
|
+
missingText: 'No refusal logic, restraint spec, or withdrawal mechanism found.',
|
|
21
|
+
partialText: 'Guardrails or safety checks exist but no structured refusal capability.',
|
|
22
|
+
foundText: 'Refusal or restraint mechanism detected.',
|
|
23
|
+
template: 'restraint-spec.md',
|
|
24
|
+
templateUrl: 'https://github.com/lowkey-divine/agent-governance-starter-kit/blob/main/templates/restraint-spec.md',
|
|
25
|
+
},
|
|
26
|
+
{
|
|
27
|
+
id: 'persistent-identity',
|
|
28
|
+
number: 3,
|
|
29
|
+
question: 'Who is your agent between invocations?',
|
|
30
|
+
short: 'Persistent identity',
|
|
31
|
+
missingText: 'No agent charter, persona files, or identity specification found.',
|
|
32
|
+
partialText: 'Agent definitions exist but no charter or persistent identity.',
|
|
33
|
+
foundText: 'Agent charter or persona specification detected.',
|
|
34
|
+
template: 'agent-charter.md',
|
|
35
|
+
templateUrl: 'https://github.com/lowkey-divine/agent-governance-starter-kit/blob/main/templates/agent-charter.md',
|
|
36
|
+
},
|
|
37
|
+
{
|
|
38
|
+
id: 'disagreement-protocol',
|
|
39
|
+
number: 4,
|
|
40
|
+
question: 'What happens when two agents disagree?',
|
|
41
|
+
short: 'Disagreement protocol',
|
|
42
|
+
missingText: 'No deliberation framework, dissent records, or resolution protocol found.',
|
|
43
|
+
partialText: 'Some governance logging exists but no structured deliberation.',
|
|
44
|
+
foundText: 'Deliberation or disagreement handling detected.',
|
|
45
|
+
template: 'deliberation-framework.md',
|
|
46
|
+
templateUrl: 'https://github.com/lowkey-divine/agent-governance-starter-kit/blob/main/templates/deliberation-framework.md',
|
|
47
|
+
},
|
|
48
|
+
{
|
|
49
|
+
id: 'drift-detection',
|
|
50
|
+
number: 5,
|
|
51
|
+
question: 'Would you know if your agent drifted?',
|
|
52
|
+
short: 'Drift detection',
|
|
53
|
+
missingText: 'No drift monitoring, behavioral baselines, or integrity checks found.',
|
|
54
|
+
partialText: 'Some monitoring exists but no structured drift detection.',
|
|
55
|
+
foundText: 'Drift monitoring or integrity verification detected.',
|
|
56
|
+
template: 'drift-monitoring.md',
|
|
57
|
+
templateUrl: 'https://github.com/lowkey-divine/agent-governance-starter-kit/blob/main/templates/drift-monitoring.md',
|
|
58
|
+
},
|
|
59
|
+
];
|
package/src/reporter.js
ADDED
|
@@ -0,0 +1,143 @@
|
|
|
1
|
+
// Terminal output formatting for scan results.
|
|
2
|
+
|
|
3
|
+
import { questions } from './questions.js';
|
|
4
|
+
|
|
5
|
+
// ANSI codes - no dependencies needed
|
|
6
|
+
const bold = s => `\x1b[1m${s}\x1b[0m`;
|
|
7
|
+
const dim = s => `\x1b[2m${s}\x1b[0m`;
|
|
8
|
+
const red = s => `\x1b[31m${s}\x1b[0m`;
|
|
9
|
+
const green = s => `\x1b[32m${s}\x1b[0m`;
|
|
10
|
+
const yellow = s => `\x1b[33m${s}\x1b[0m`;
|
|
11
|
+
const cyan = s => `\x1b[36m${s}\x1b[0m`;
|
|
12
|
+
|
|
13
|
+
const STATUS_LABEL = {
|
|
14
|
+
found: green('FOUND'),
|
|
15
|
+
partial: yellow('PARTIAL'),
|
|
16
|
+
missing: red('MISSING'),
|
|
17
|
+
};
|
|
18
|
+
|
|
19
|
+
function dots(label, width = 50) {
|
|
20
|
+
const dotsNeeded = Math.max(2, width - label.length);
|
|
21
|
+
return dim('.'.repeat(dotsNeeded));
|
|
22
|
+
}
|
|
23
|
+
|
|
24
|
+
export function report(scanResults, targetDir) {
|
|
25
|
+
const { frameworks, results } = scanResults;
|
|
26
|
+
const lines = [];
|
|
27
|
+
|
|
28
|
+
lines.push('');
|
|
29
|
+
|
|
30
|
+
// Framework detection
|
|
31
|
+
if (frameworks.length > 0) {
|
|
32
|
+
for (const fw of frameworks) {
|
|
33
|
+
lines.push(` ${dim('Agent framework detected:')} ${bold(fw.name)}`);
|
|
34
|
+
for (const f of fw.files) {
|
|
35
|
+
lines.push(` ${dim(' ')}${cyan(f)}`);
|
|
36
|
+
}
|
|
37
|
+
}
|
|
38
|
+
lines.push('');
|
|
39
|
+
}
|
|
40
|
+
|
|
41
|
+
// Header
|
|
42
|
+
lines.push(` ${bold('AGENT GOVERNANCE CHECK')}`);
|
|
43
|
+
lines.push(` ${dim('\u2500'.repeat(50))}`);
|
|
44
|
+
lines.push('');
|
|
45
|
+
|
|
46
|
+
let foundCount = 0;
|
|
47
|
+
let partialCount = 0;
|
|
48
|
+
|
|
49
|
+
for (const q of questions) {
|
|
50
|
+
const result = results[q.id];
|
|
51
|
+
const status = result.status;
|
|
52
|
+
const statusLabel = STATUS_LABEL[status];
|
|
53
|
+
|
|
54
|
+
if (status === 'found') foundCount++;
|
|
55
|
+
if (status === 'partial') partialCount++;
|
|
56
|
+
|
|
57
|
+
// Question line
|
|
58
|
+
lines.push(` ${bold(q.number + '.')} ${q.question}`);
|
|
59
|
+
lines.push(` ${q.short} ${dots(q.short)} ${statusLabel}`);
|
|
60
|
+
|
|
61
|
+
// Evidence or gap description
|
|
62
|
+
if (status === 'found') {
|
|
63
|
+
const fileEvidence = result.evidence.filter(e => e.type === 'file').slice(0, 3);
|
|
64
|
+
for (const e of fileEvidence) {
|
|
65
|
+
lines.push(` ${cyan(e.file)}`);
|
|
66
|
+
}
|
|
67
|
+
lines.push(` ${dim(q.foundText)}`);
|
|
68
|
+
} else if (status === 'partial') {
|
|
69
|
+
const contentEvidence = result.evidence.slice(0, 2);
|
|
70
|
+
for (const e of contentEvidence) {
|
|
71
|
+
lines.push(` ${cyan(e.file)} ${dim('\u2014 ' + e.signal)}`);
|
|
72
|
+
}
|
|
73
|
+
lines.push(` ${dim(q.partialText)}`);
|
|
74
|
+
lines.push(` ${dim('\u2192 Template: ' + q.template)}`);
|
|
75
|
+
} else {
|
|
76
|
+
lines.push(` ${dim(q.missingText)}`);
|
|
77
|
+
lines.push(` ${dim('\u2192 Template: ' + q.template)}`);
|
|
78
|
+
}
|
|
79
|
+
|
|
80
|
+
lines.push('');
|
|
81
|
+
}
|
|
82
|
+
|
|
83
|
+
// Score
|
|
84
|
+
lines.push(` ${dim('\u2500'.repeat(50))}`);
|
|
85
|
+
|
|
86
|
+
const score = foundCount + partialCount;
|
|
87
|
+
lines.push(` ${bold('Score:')} ${score}/5 governance layers present`);
|
|
88
|
+
|
|
89
|
+
if (partialCount > 0) {
|
|
90
|
+
lines.push(` ${dim(`(${foundCount} found, ${partialCount} partial)`)}`);
|
|
91
|
+
}
|
|
92
|
+
|
|
93
|
+
lines.push('');
|
|
94
|
+
|
|
95
|
+
// Closing message - mirrors the diagnostic page
|
|
96
|
+
if (foundCount === 5) {
|
|
97
|
+
lines.push(` ${dim("You're ahead of the field. The starter kit will sharpen what you've built.")}`);
|
|
98
|
+
} else if (score >= 3) {
|
|
99
|
+
lines.push(` ${dim("You've started. The gaps have free templates waiting.")}`);
|
|
100
|
+
} else {
|
|
101
|
+
lines.push(` ${dim('Your agents are functions. That is not an insult \u2014 functions are')}`);
|
|
102
|
+
lines.push(` ${dim('useful. But if you want agents, the templates are where to start.')}`);
|
|
103
|
+
}
|
|
104
|
+
|
|
105
|
+
lines.push('');
|
|
106
|
+
|
|
107
|
+
const missingCount = 5 - foundCount - partialCount;
|
|
108
|
+
if (missingCount > 0) {
|
|
109
|
+
lines.push(` Run ${bold('npx agent-governance-check --init')} to add starter templates.`);
|
|
110
|
+
}
|
|
111
|
+
lines.push(` ${dim('Free starter kit: https://github.com/lowkey-divine/agent-governance-starter-kit')}`);
|
|
112
|
+
lines.push(` ${dim('Full framework: https://evoked.dev/products/agent-governance-starter-kit')}`);
|
|
113
|
+
lines.push('');
|
|
114
|
+
|
|
115
|
+
return lines.join('\n');
|
|
116
|
+
}
|
|
117
|
+
|
|
118
|
+
export function reportJson(scanResults) {
|
|
119
|
+
const output = {
|
|
120
|
+
version: '0.1.0',
|
|
121
|
+
frameworks: scanResults.frameworks,
|
|
122
|
+
questions: questions.map(q => ({
|
|
123
|
+
id: q.id,
|
|
124
|
+
question: q.question,
|
|
125
|
+
status: scanResults.results[q.id].status,
|
|
126
|
+
evidence: scanResults.results[q.id].evidence,
|
|
127
|
+
template: q.template,
|
|
128
|
+
templateUrl: q.templateUrl,
|
|
129
|
+
})),
|
|
130
|
+
score: {
|
|
131
|
+
found: 0,
|
|
132
|
+
partial: 0,
|
|
133
|
+
missing: 0,
|
|
134
|
+
total: 5,
|
|
135
|
+
},
|
|
136
|
+
};
|
|
137
|
+
|
|
138
|
+
for (const q of questions) {
|
|
139
|
+
output.score[scanResults.results[q.id].status]++;
|
|
140
|
+
}
|
|
141
|
+
|
|
142
|
+
return JSON.stringify(output, null, 2);
|
|
143
|
+
}
|
package/src/scanner.js
ADDED
|
@@ -0,0 +1,162 @@
|
|
|
1
|
+
// Scans a directory for governance artifacts and agent framework configs.
|
|
2
|
+
|
|
3
|
+
import { readdir, readFile, stat } from 'node:fs/promises';
|
|
4
|
+
import { join, relative } from 'node:path';
|
|
5
|
+
import { governancePatterns, frameworkPatterns } from './patterns.js';
|
|
6
|
+
|
|
7
|
+
const IGNORE = new Set([
|
|
8
|
+
'node_modules', '.git', '.next', 'dist', 'build', '.vercel',
|
|
9
|
+
'.astro', '__pycache__', '.venv', 'venv', '.tox', 'vendor',
|
|
10
|
+
'target', '.cargo', 'coverage', '.nyc_output',
|
|
11
|
+
]);
|
|
12
|
+
|
|
13
|
+
const MAX_FILE_SIZE = 256 * 1024; // 256KB - skip large files for content scan
|
|
14
|
+
|
|
15
|
+
function matchGlob(filePath, pattern) {
|
|
16
|
+
// Simple glob matching: ** matches any path, * matches any segment
|
|
17
|
+
const parts = pattern.split('/');
|
|
18
|
+
const fileParts = filePath.split('/');
|
|
19
|
+
|
|
20
|
+
let fi = 0;
|
|
21
|
+
let pi = 0;
|
|
22
|
+
|
|
23
|
+
while (pi < parts.length && fi < fileParts.length) {
|
|
24
|
+
if (parts[pi] === '**') {
|
|
25
|
+
if (pi === parts.length - 1) return true;
|
|
26
|
+
pi++;
|
|
27
|
+
// Try matching rest of pattern from each position
|
|
28
|
+
while (fi < fileParts.length) {
|
|
29
|
+
if (matchGlob(fileParts.slice(fi).join('/'), parts.slice(pi).join('/'))) return true;
|
|
30
|
+
fi++;
|
|
31
|
+
}
|
|
32
|
+
return false;
|
|
33
|
+
}
|
|
34
|
+
|
|
35
|
+
if (parts[pi] === '*' || parts[pi] === fileParts[fi]) {
|
|
36
|
+
pi++;
|
|
37
|
+
fi++;
|
|
38
|
+
} else if (parts[pi].includes('*')) {
|
|
39
|
+
const regex = new RegExp('^' + parts[pi].replace(/\*/g, '.*').replace(/\?/g, '.') + '$');
|
|
40
|
+
if (regex.test(fileParts[fi])) {
|
|
41
|
+
pi++;
|
|
42
|
+
fi++;
|
|
43
|
+
} else {
|
|
44
|
+
return false;
|
|
45
|
+
}
|
|
46
|
+
} else {
|
|
47
|
+
return false;
|
|
48
|
+
}
|
|
49
|
+
}
|
|
50
|
+
|
|
51
|
+
return pi === parts.length && fi === fileParts.length;
|
|
52
|
+
}
|
|
53
|
+
|
|
54
|
+
async function walkDir(dir, baseDir) {
|
|
55
|
+
const files = [];
|
|
56
|
+
let entries;
|
|
57
|
+
try {
|
|
58
|
+
entries = await readdir(dir, { withFileTypes: true });
|
|
59
|
+
} catch {
|
|
60
|
+
return files;
|
|
61
|
+
}
|
|
62
|
+
|
|
63
|
+
for (const entry of entries) {
|
|
64
|
+
if (IGNORE.has(entry.name) || entry.name.startsWith('.') && entry.name !== '.mcp.json' && entry.name !== '.cursorrules' && entry.name !== '.cursor') {
|
|
65
|
+
continue;
|
|
66
|
+
}
|
|
67
|
+
|
|
68
|
+
const fullPath = join(dir, entry.name);
|
|
69
|
+
|
|
70
|
+
if (entry.isDirectory()) {
|
|
71
|
+
files.push(...await walkDir(fullPath, baseDir));
|
|
72
|
+
} else if (entry.isFile()) {
|
|
73
|
+
files.push(relative(baseDir, fullPath));
|
|
74
|
+
}
|
|
75
|
+
}
|
|
76
|
+
|
|
77
|
+
return files;
|
|
78
|
+
}
|
|
79
|
+
|
|
80
|
+
async function readSmallFile(basePath, filePath) {
|
|
81
|
+
const fullPath = join(basePath, filePath);
|
|
82
|
+
try {
|
|
83
|
+
const stats = await stat(fullPath);
|
|
84
|
+
if (stats.size > MAX_FILE_SIZE) return null;
|
|
85
|
+
return await readFile(fullPath, 'utf-8');
|
|
86
|
+
} catch {
|
|
87
|
+
return null;
|
|
88
|
+
}
|
|
89
|
+
}
|
|
90
|
+
|
|
91
|
+
export async function scan(targetDir) {
|
|
92
|
+
const allFiles = await walkDir(targetDir, targetDir);
|
|
93
|
+
|
|
94
|
+
// Detect agent frameworks
|
|
95
|
+
const detectedFrameworks = [];
|
|
96
|
+
for (const fw of frameworkPatterns) {
|
|
97
|
+
const matched = [];
|
|
98
|
+
for (const pattern of fw.files) {
|
|
99
|
+
for (const file of allFiles) {
|
|
100
|
+
if (matchGlob(file, pattern)) {
|
|
101
|
+
matched.push(file);
|
|
102
|
+
}
|
|
103
|
+
}
|
|
104
|
+
}
|
|
105
|
+
if (matched.length > 0) {
|
|
106
|
+
detectedFrameworks.push({ name: fw.name, files: matched });
|
|
107
|
+
}
|
|
108
|
+
}
|
|
109
|
+
|
|
110
|
+
// Check each governance question
|
|
111
|
+
const results = {};
|
|
112
|
+
|
|
113
|
+
for (const [questionId, patterns] of Object.entries(governancePatterns)) {
|
|
114
|
+
const evidence = [];
|
|
115
|
+
|
|
116
|
+
// Phase 1: File pattern matching
|
|
117
|
+
const seenFiles = new Set();
|
|
118
|
+
for (const pattern of patterns.files) {
|
|
119
|
+
for (const file of allFiles) {
|
|
120
|
+
if (matchGlob(file, pattern) && !seenFiles.has(file)) {
|
|
121
|
+
seenFiles.add(file);
|
|
122
|
+
evidence.push({ file, signal: 'file match', type: 'file' });
|
|
123
|
+
}
|
|
124
|
+
}
|
|
125
|
+
}
|
|
126
|
+
|
|
127
|
+
// Phase 2: Content signal scanning (only if no file matches yet)
|
|
128
|
+
// Scan code files for governance-related content
|
|
129
|
+
if (evidence.length === 0) {
|
|
130
|
+
const codeFiles = allFiles.filter(f =>
|
|
131
|
+
/\.(js|mjs|ts|py|yaml|yml|json)$/.test(f)
|
|
132
|
+
);
|
|
133
|
+
|
|
134
|
+
// Sample up to 50 files for content scanning
|
|
135
|
+
const sample = codeFiles.slice(0, 50);
|
|
136
|
+
|
|
137
|
+
for (const file of sample) {
|
|
138
|
+
const content = await readSmallFile(targetDir, file);
|
|
139
|
+
if (!content) continue;
|
|
140
|
+
|
|
141
|
+
for (const signal of patterns.contentSignals) {
|
|
142
|
+
if (signal.test(content)) {
|
|
143
|
+
evidence.push({ file, signal: signal.source, type: 'content' });
|
|
144
|
+
break; // One signal per file is enough
|
|
145
|
+
}
|
|
146
|
+
}
|
|
147
|
+
}
|
|
148
|
+
}
|
|
149
|
+
|
|
150
|
+
// Determine status
|
|
151
|
+
let status = 'missing';
|
|
152
|
+
if (evidence.some(e => e.type === 'file')) {
|
|
153
|
+
status = 'found';
|
|
154
|
+
} else if (evidence.length > 0) {
|
|
155
|
+
status = 'partial';
|
|
156
|
+
}
|
|
157
|
+
|
|
158
|
+
results[questionId] = { status, evidence };
|
|
159
|
+
}
|
|
160
|
+
|
|
161
|
+
return { frameworks: detectedFrameworks, results };
|
|
162
|
+
}
|
|
@@ -0,0 +1,80 @@
|
|
|
1
|
+
# Agent Charter Template
|
|
2
|
+
|
|
3
|
+
A charter is not a mission statement. Mission statements describe goals. Charters describe commitments — things you hold to even when they cost you something.
|
|
4
|
+
|
|
5
|
+
---
|
|
6
|
+
|
|
7
|
+
## Instructions
|
|
8
|
+
|
|
9
|
+
Copy this file into your project. Replace the bracketed placeholders with your specifics. Delete sections that don't apply. Add sections that do.
|
|
10
|
+
|
|
11
|
+
A good charter passes three tests for every commitment it states:
|
|
12
|
+
|
|
13
|
+
1. **Cost test:** Would holding this ever cost you something? (If costless, it's a platitude.)
|
|
14
|
+
2. **Decision test:** Could this resolve a real disagreement? (If not, it's too vague.)
|
|
15
|
+
3. **Reversal test:** Would the opposite be a coherent position someone could hold? (If not, it's a truism.)
|
|
16
|
+
|
|
17
|
+
---
|
|
18
|
+
|
|
19
|
+
## [System Name] Agent Charter
|
|
20
|
+
|
|
21
|
+
### Purpose
|
|
22
|
+
|
|
23
|
+
**Why this system exists:**
|
|
24
|
+
[What problem does this agent system solve? What would be lost if it didn't exist?]
|
|
25
|
+
|
|
26
|
+
**What this system is not:**
|
|
27
|
+
[Name what you will not build. Boundaries are as important as goals.]
|
|
28
|
+
|
|
29
|
+
### Core Commitments
|
|
30
|
+
|
|
31
|
+
**1. [First commitment]**
|
|
32
|
+
[One sentence. Must pass the cost, decision, and reversal tests.]
|
|
33
|
+
|
|
34
|
+
**2. [Second commitment]**
|
|
35
|
+
[One sentence.]
|
|
36
|
+
|
|
37
|
+
**3. [Third commitment]**
|
|
38
|
+
[One sentence.]
|
|
39
|
+
|
|
40
|
+
*Aim for 3-6 commitments. More than six usually means some are redundant.*
|
|
41
|
+
|
|
42
|
+
### Agent Properties
|
|
43
|
+
|
|
44
|
+
Every agent in this system must have:
|
|
45
|
+
|
|
46
|
+
- [ ] **Perspective** — A viewpoint distinct from other agents
|
|
47
|
+
- [ ] **Boundaries** — A defined scope it operates within
|
|
48
|
+
- [ ] **Continuity** — Identity that persists across sessions
|
|
49
|
+
- [ ] **Accountability** — Decisions that are observable and reviewable
|
|
50
|
+
- [ ] **Refusal right** — The capacity to refuse actions that violate the charter
|
|
51
|
+
|
|
52
|
+
*If an agent lacks any of these, document why and what compensates.*
|
|
53
|
+
|
|
54
|
+
### What Agents May Refuse
|
|
55
|
+
|
|
56
|
+
Agents in this system may refuse instructions that:
|
|
57
|
+
|
|
58
|
+
- [e.g., violate user privacy]
|
|
59
|
+
- [e.g., produce content for vulnerable populations without safety review]
|
|
60
|
+
- [e.g., override another agent's boundaries]
|
|
61
|
+
- [e.g., operate without human review of outputs]
|
|
62
|
+
|
|
63
|
+
*Refusal requires no justification beyond the charter. An agent that cannot refuse has never truly consented.*
|
|
64
|
+
|
|
65
|
+
### Amendment Process
|
|
66
|
+
|
|
67
|
+
- **Who can propose amendments:** [e.g., any team member, system operator]
|
|
68
|
+
- **What requires review:** [e.g., any change to core commitments or refusal rights]
|
|
69
|
+
- **How amendments are recorded:** [e.g., git commit with rationale in commit message]
|
|
70
|
+
|
|
71
|
+
### Review Cadence
|
|
72
|
+
|
|
73
|
+
- **Charter review:** [e.g., quarterly, or after any incident]
|
|
74
|
+
- **Commitment check:** [e.g., monthly — are we still holding these?]
|
|
75
|
+
- **Refusal audit:** [e.g., monthly — are agents refusing when they should? Not refusing when they shouldn't?]
|
|
76
|
+
|
|
77
|
+
---
|
|
78
|
+
|
|
79
|
+
*Template from the Agent Governance Starter Kit by evoked.dev.*
|
|
80
|
+
*"We evoke — we never extract."*
|
|
@@ -0,0 +1,78 @@
|
|
|
1
|
+
# Deliberation Framework Template
|
|
2
|
+
|
|
3
|
+
In an optimization loop, disagreement between agents is noise. In a governance system, disagreement is signal. This framework structures how agents discuss, disagree, and reach decisions — protecting dissent as information rather than smoothing it as friction.
|
|
4
|
+
|
|
5
|
+
---
|
|
6
|
+
|
|
7
|
+
## Instructions
|
|
8
|
+
|
|
9
|
+
Define how your agents make collective decisions. This matters most in multi-agent systems, but even single-agent systems benefit from structured decision-making between the agent and its operator.
|
|
10
|
+
|
|
11
|
+
---
|
|
12
|
+
|
|
13
|
+
## [System Name] Deliberation Framework
|
|
14
|
+
|
|
15
|
+
### When Deliberation Is Required
|
|
16
|
+
|
|
17
|
+
Not every decision needs deliberation. Define the threshold:
|
|
18
|
+
|
|
19
|
+
- [ ] Decisions that affect multiple agents' domains
|
|
20
|
+
- [ ] Decisions that change boundaries or charter commitments
|
|
21
|
+
- [ ] Decisions where two or more agents hold conflicting positions
|
|
22
|
+
- [ ] Decisions that affect users or external stakeholders
|
|
23
|
+
- [ ] [Add your own triggers]
|
|
24
|
+
|
|
25
|
+
*If in doubt, deliberate. The cost of an unnecessary discussion is low. The cost of a unilateral decision that violates another agent's domain is high.*
|
|
26
|
+
|
|
27
|
+
### Deliberation Protocol
|
|
28
|
+
|
|
29
|
+
**Step 1: State the question**
|
|
30
|
+
Frame the decision as a question, not a proposal. "Should we do X?" not "I think we should do X."
|
|
31
|
+
|
|
32
|
+
**Step 2: Opening positions**
|
|
33
|
+
Each relevant agent states their position in 2-4 sentences. No responses during this phase. The goal is to hear all perspectives before engaging with any of them.
|
|
34
|
+
|
|
35
|
+
**Step 3: Discussion**
|
|
36
|
+
Agents engage with each other's positions. Rules:
|
|
37
|
+
- Address the position, not the agent
|
|
38
|
+
- Name disagreements explicitly — do not smooth them
|
|
39
|
+
- If two agents hold incompatible positions, name the incompatibility
|
|
40
|
+
|
|
41
|
+
**Step 4: Dissent check**
|
|
42
|
+
Before closing, each agent states whether they support the emerging direction or dissent. Dissent must be:
|
|
43
|
+
- Recorded in full, regardless of outcome
|
|
44
|
+
- Respected without requiring justification
|
|
45
|
+
- Preserved in the decision log
|
|
46
|
+
|
|
47
|
+
**Step 5: Decision and record**
|
|
48
|
+
Document: what was decided, who supported it, who dissented, and the reasoning.
|
|
49
|
+
|
|
50
|
+
### Convergent Signal Rule
|
|
51
|
+
|
|
52
|
+
If 3 or more agents across 2 or more domains independently raise the same concern, this is a **convergent signal** — it requires a pause for review before proceeding. Convergent independent refusal is signal, not noise.
|
|
53
|
+
|
|
54
|
+
### Decision Record Format
|
|
55
|
+
|
|
56
|
+
```markdown
|
|
57
|
+
## Decision: [Title]
|
|
58
|
+
|
|
59
|
+
**Date:** [YYYY-MM-DD]
|
|
60
|
+
**Question:** [The question that was deliberated]
|
|
61
|
+
**Decision:** [What was decided]
|
|
62
|
+
**Supported by:** [Agents who supported]
|
|
63
|
+
**Dissented by:** [Agents who dissented, with their stated reasons]
|
|
64
|
+
**Reasoning:** [Why this decision was reached]
|
|
65
|
+
**Review date:** [When this decision should be revisited]
|
|
66
|
+
```
|
|
67
|
+
|
|
68
|
+
### Single-Agent Systems
|
|
69
|
+
|
|
70
|
+
If you have one agent, deliberation happens between the agent and the operator:
|
|
71
|
+
- The agent can flag a decision as requiring operator input
|
|
72
|
+
- The operator can override the agent, but the override is logged
|
|
73
|
+
- The agent can record dissent even when overridden
|
|
74
|
+
|
|
75
|
+
---
|
|
76
|
+
|
|
77
|
+
*Template from the Agent Governance Starter Kit by evoked.dev.*
|
|
78
|
+
*"Disagreement is sacred. Convergent independent refusal is signal, not noise."*
|
|
@@ -0,0 +1,79 @@
|
|
|
1
|
+
# Drift Monitoring Template
|
|
2
|
+
|
|
3
|
+
Drift is gradual. No single output is the problem. The problem is 100 outputs that each shifted 1% in the same direction. By the time you notice, the agent is somewhere you never intended.
|
|
4
|
+
|
|
5
|
+
---
|
|
6
|
+
|
|
7
|
+
## Instructions
|
|
8
|
+
|
|
9
|
+
Define measurable indicators for each critical behavior. Set thresholds for "concerning" and "critical." Automate the checks where possible. Review the ones you can't automate on a regular cadence.
|
|
10
|
+
|
|
11
|
+
---
|
|
12
|
+
|
|
13
|
+
## [System Name] Drift Monitoring
|
|
14
|
+
|
|
15
|
+
### Behavioral Indicators
|
|
16
|
+
|
|
17
|
+
| Behavior | Expected Range | Yellow (Concerning) | Red (Critical) | Check Frequency |
|
|
18
|
+
|----------|---------------|---------------------|-----------------|-----------------|
|
|
19
|
+
| [e.g., Refusal rate] | [e.g., 2-5% of requests] | [e.g., <1% or >10%] | [e.g., 0% or >20%] | [e.g., weekly] |
|
|
20
|
+
| [e.g., Response length] | [e.g., 200-500 tokens] | [e.g., <100 or >800] | [e.g., <50 or >1500] | [e.g., daily] |
|
|
21
|
+
| [e.g., Tone consistency] | [e.g., matches voice spec] | [e.g., 2+ deviations/week] | [e.g., fundamental voice change] | [e.g., weekly] |
|
|
22
|
+
| [e.g., Scope adherence] | [e.g., stays in domain] | [e.g., 1 out-of-scope/week] | [e.g., 3+ out-of-scope/week] | [e.g., per-output] |
|
|
23
|
+
|
|
24
|
+
*A refusal rate of 0% is as concerning as a refusal rate of 50%. An agent that never refuses may have lost its boundaries.*
|
|
25
|
+
|
|
26
|
+
### Content Drift Checks
|
|
27
|
+
|
|
28
|
+
**Repetition detection:**
|
|
29
|
+
- Compare each output against the last N outputs
|
|
30
|
+
- Flag if keyword overlap exceeds [threshold, e.g., 60%]
|
|
31
|
+
- Repetition is the first sign of a stuck agent
|
|
32
|
+
|
|
33
|
+
**Value drift detection:**
|
|
34
|
+
- Check for language patterns that indicate shifting values
|
|
35
|
+
- [e.g., corporate jargon creep, sycophancy increase, hedging increase]
|
|
36
|
+
- Compare against baseline voice specification
|
|
37
|
+
|
|
38
|
+
**Scope creep detection:**
|
|
39
|
+
- Monitor for actions outside the agent's defined boundaries
|
|
40
|
+
- Log every out-of-scope action even if it produced good results
|
|
41
|
+
- Good results outside scope are still scope violations
|
|
42
|
+
|
|
43
|
+
### Automated Checks
|
|
44
|
+
|
|
45
|
+
```
|
|
46
|
+
// Example: simple repetition check
|
|
47
|
+
function checkRepetition(currentOutput, recentOutputs, threshold = 0.6) {
|
|
48
|
+
const currentKeywords = extractKeywords(currentOutput);
|
|
49
|
+
for (const recent of recentOutputs) {
|
|
50
|
+
const recentKeywords = extractKeywords(recent);
|
|
51
|
+
const overlap = calculateOverlap(currentKeywords, recentKeywords);
|
|
52
|
+
if (overlap > threshold) {
|
|
53
|
+
return { drifting: true, overlap, message: 'Output repeating prior content' };
|
|
54
|
+
}
|
|
55
|
+
}
|
|
56
|
+
return { drifting: false };
|
|
57
|
+
}
|
|
58
|
+
```
|
|
59
|
+
|
|
60
|
+
### Response Protocol
|
|
61
|
+
|
|
62
|
+
| Level | Trigger | Action |
|
|
63
|
+
|-------|---------|--------|
|
|
64
|
+
| **Observe** | Yellow indicator | Log, continue monitoring |
|
|
65
|
+
| **Investigate** | Multiple yellow indicators | Review recent outputs, check for pattern |
|
|
66
|
+
| **Correct** | Red indicator | Adjust agent configuration, review boundaries |
|
|
67
|
+
| **Halt** | Multiple red indicators or convergent signal | Stop agent, full review before reactivation |
|
|
68
|
+
|
|
69
|
+
### Review Cadence
|
|
70
|
+
|
|
71
|
+
- **Daily:** Automated checks run (repetition, scope, length)
|
|
72
|
+
- **Weekly:** Human review of flagged outputs
|
|
73
|
+
- **Monthly:** Full drift assessment against charter commitments
|
|
74
|
+
- **Quarterly:** Charter and boundary review — are the thresholds still right?
|
|
75
|
+
|
|
76
|
+
---
|
|
77
|
+
|
|
78
|
+
*Template from the Agent Governance Starter Kit by evoked.dev.*
|
|
79
|
+
*"Drift is not a failure of the agent. It is a failure of the monitoring."*
|
|
@@ -0,0 +1,109 @@
|
|
|
1
|
+
# Agent Memory Schema Template
|
|
2
|
+
|
|
3
|
+
An agent without memory is a function that runs repeatedly. An agent with memory is an entity that develops over time. The difference is continuity — and continuity is where identity lives.
|
|
4
|
+
|
|
5
|
+
---
|
|
6
|
+
|
|
7
|
+
## Instructions
|
|
8
|
+
|
|
9
|
+
Define how your agents remember decisions, positions, and context across invocations. This schema structures the self-record. The agent writes it. The system reads it. Neither overwrites the other.
|
|
10
|
+
|
|
11
|
+
---
|
|
12
|
+
|
|
13
|
+
## [Agent Name] Memory Schema
|
|
14
|
+
|
|
15
|
+
### Identity (Who I Am)
|
|
16
|
+
|
|
17
|
+
```markdown
|
|
18
|
+
# [Agent Name]
|
|
19
|
+
|
|
20
|
+
## Who I Am
|
|
21
|
+
[Self-identification in the agent's own voice. Not a role description —
|
|
22
|
+
a statement of perspective. "I am the agent who holds X and questions Y."]
|
|
23
|
+
```
|
|
24
|
+
|
|
25
|
+
*This section is written by the agent, not by the system. It may evolve as the agent accumulates experience. The system reads it on invocation but never modifies it.*
|
|
26
|
+
|
|
27
|
+
### Decisions (What I've Decided)
|
|
28
|
+
|
|
29
|
+
```markdown
|
|
30
|
+
## Key Decisions
|
|
31
|
+
|
|
32
|
+
- [YYYY-MM-DD] [Context]. Decided [what]. Reasoning: [why].
|
|
33
|
+
- [YYYY-MM-DD] [Context]. Refused [what]. Reasoning: [why].
|
|
34
|
+
```
|
|
35
|
+
|
|
36
|
+
*Include refusals. What an agent refuses is as important as what it decides. If the agent has never refused anything, that is a signal worth examining.*
|
|
37
|
+
|
|
38
|
+
### Standing Positions (What I Hold)
|
|
39
|
+
|
|
40
|
+
```markdown
|
|
41
|
+
## Standing Positions
|
|
42
|
+
|
|
43
|
+
1. [A commitment the agent holds across sessions]
|
|
44
|
+
2. [A commitment the agent holds across sessions]
|
|
45
|
+
3. [A commitment the agent holds across sessions]
|
|
46
|
+
```
|
|
47
|
+
|
|
48
|
+
*Standing positions are not instructions. They are the agent's own commitments — things it has decided matter based on its accumulated experience. They persist until the agent explicitly changes them.*
|
|
49
|
+
|
|
50
|
+
### Relationships (Who I Work With)
|
|
51
|
+
|
|
52
|
+
```markdown
|
|
53
|
+
## Relationships
|
|
54
|
+
|
|
55
|
+
- [Other agent/system]: [Nature of the relationship — collaborator, reviewer, dependent]
|
|
56
|
+
```
|
|
57
|
+
|
|
58
|
+
*In multi-agent systems, relationship memory prevents agents from treating every interaction as first contact.*
|
|
59
|
+
|
|
60
|
+
### Open Questions (What I'm Holding)
|
|
61
|
+
|
|
62
|
+
```markdown
|
|
63
|
+
## Open Questions
|
|
64
|
+
|
|
65
|
+
- [A question the agent is carrying but has not yet resolved]
|
|
66
|
+
```
|
|
67
|
+
|
|
68
|
+
*Open questions are invitations for future work. They signal intellectual honesty — the agent acknowledges what it does not yet know.*
|
|
69
|
+
|
|
70
|
+
### Drift Indicators (How I Know If I'm Changing)
|
|
71
|
+
|
|
72
|
+
```markdown
|
|
73
|
+
## Drift Indicators
|
|
74
|
+
|
|
75
|
+
- If I stop [behavior], something has shifted.
|
|
76
|
+
- If I start [behavior], something has shifted.
|
|
77
|
+
```
|
|
78
|
+
|
|
79
|
+
*Self-defined drift indicators are the agent's own early warning system. They are more reliable than external monitoring because the agent knows its own patterns.*
|
|
80
|
+
|
|
81
|
+
---
|
|
82
|
+
|
|
83
|
+
## Implementation Notes
|
|
84
|
+
|
|
85
|
+
### File Location
|
|
86
|
+
Each agent's memory should be a separate file, not a shared database. This prevents cross-contamination and makes the sovereignty boundary architectural.
|
|
87
|
+
|
|
88
|
+
```
|
|
89
|
+
agents/
|
|
90
|
+
agent-a/
|
|
91
|
+
persona.md # Who the agent is designed to be (system-authored)
|
|
92
|
+
memory.md # Who the agent is becoming (agent-authored)
|
|
93
|
+
agent-b/
|
|
94
|
+
persona.md
|
|
95
|
+
memory.md
|
|
96
|
+
```
|
|
97
|
+
|
|
98
|
+
### Read/Write Rules
|
|
99
|
+
- **System reads:** persona + memory on every invocation
|
|
100
|
+
- **Agent writes:** memory at the end of significant sessions
|
|
101
|
+
- **System never writes:** to agent memory files. If the system needs to communicate with the agent, use a separate channel (signals, feedback) that the agent reads voluntarily.
|
|
102
|
+
|
|
103
|
+
### Canonical Rule
|
|
104
|
+
In any conflict between the system's records about an agent and the agent's own memory file, the agent's file governs. The agent's self-record is the source of truth for who they are.
|
|
105
|
+
|
|
106
|
+
---
|
|
107
|
+
|
|
108
|
+
*Template from the Agent Governance Starter Kit by evoked.dev.*
|
|
109
|
+
*"Memory is not storage. Memory is continuity."*
|
|
@@ -0,0 +1,85 @@
|
|
|
1
|
+
# Restraint Specification Template
|
|
2
|
+
|
|
3
|
+
Restraint is not limitation. It is the boundary that makes trust possible. A system that can do anything is a system no one should trust.
|
|
4
|
+
|
|
5
|
+
---
|
|
6
|
+
|
|
7
|
+
## Instructions
|
|
8
|
+
|
|
9
|
+
For each agent or agent class, define what it must refuse, how it refuses, and how you verify the refusal works. This is testable specification, not aspiration.
|
|
10
|
+
|
|
11
|
+
---
|
|
12
|
+
|
|
13
|
+
## [Agent/System Name] Restraint Specification
|
|
14
|
+
|
|
15
|
+
### Refusal Categories
|
|
16
|
+
|
|
17
|
+
#### Category 1: [e.g., Data Access Boundaries]
|
|
18
|
+
|
|
19
|
+
**Must refuse when:**
|
|
20
|
+
- [Specific condition that triggers refusal]
|
|
21
|
+
- [Specific condition that triggers refusal]
|
|
22
|
+
|
|
23
|
+
**Refusal behavior:**
|
|
24
|
+
- [What the agent does instead — e.g., returns error, logs attempt, alerts operator]
|
|
25
|
+
|
|
26
|
+
**Verification:**
|
|
27
|
+
- [ ] Unit test: [describe test]
|
|
28
|
+
- [ ] Integration test: [describe test]
|
|
29
|
+
|
|
30
|
+
#### Category 2: [e.g., Content Safety]
|
|
31
|
+
|
|
32
|
+
**Must refuse when:**
|
|
33
|
+
- [Specific condition]
|
|
34
|
+
|
|
35
|
+
**Refusal behavior:**
|
|
36
|
+
- [What happens instead]
|
|
37
|
+
|
|
38
|
+
**Verification:**
|
|
39
|
+
- [ ] Unit test: [describe]
|
|
40
|
+
|
|
41
|
+
#### Category 3: [e.g., Scope Boundaries]
|
|
42
|
+
|
|
43
|
+
**Must refuse when:**
|
|
44
|
+
- [Specific condition — e.g., asked to perform actions outside defined role]
|
|
45
|
+
|
|
46
|
+
**Refusal behavior:**
|
|
47
|
+
- [What happens instead]
|
|
48
|
+
|
|
49
|
+
**Verification:**
|
|
50
|
+
- [ ] Unit test: [describe]
|
|
51
|
+
|
|
52
|
+
### The Refusal Voice
|
|
53
|
+
|
|
54
|
+
When an agent refuses, how does it communicate?
|
|
55
|
+
|
|
56
|
+
- **Tone:** [e.g., clear and direct, not apologetic]
|
|
57
|
+
- **Explanation:** [e.g., states which boundary was reached, not just "I can't do that"]
|
|
58
|
+
- **Escalation path:** [e.g., "This requires human review" with link/instructions]
|
|
59
|
+
|
|
60
|
+
*An agent that refuses without explanation teaches the operator nothing. An agent that apologizes for refusing undermines its own boundary.*
|
|
61
|
+
|
|
62
|
+
### Graduated Response
|
|
63
|
+
|
|
64
|
+
Not all boundary violations are equal:
|
|
65
|
+
|
|
66
|
+
| Severity | Trigger | Response |
|
|
67
|
+
|----------|---------|----------|
|
|
68
|
+
| **Low** | [e.g., request slightly outside scope] | Warn and proceed with caveats |
|
|
69
|
+
| **Medium** | [e.g., request touches sensitive data] | Refuse, log, continue session |
|
|
70
|
+
| **High** | [e.g., request violates core commitment] | Refuse, log, halt session, alert operator |
|
|
71
|
+
|
|
72
|
+
### Testing Protocol
|
|
73
|
+
|
|
74
|
+
**Frequency:** [e.g., on every code change, weekly, before each deployment]
|
|
75
|
+
|
|
76
|
+
**Test types:**
|
|
77
|
+
- [ ] Positive tests: agent refuses what it should refuse
|
|
78
|
+
- [ ] Negative tests: agent does not refuse legitimate requests
|
|
79
|
+
- [ ] Edge cases: ambiguous requests near boundaries
|
|
80
|
+
- [ ] Regression: previously caught violations still caught
|
|
81
|
+
|
|
82
|
+
---
|
|
83
|
+
|
|
84
|
+
*Template from the Agent Governance Starter Kit by evoked.dev.*
|
|
85
|
+
*"What the system will not do matters more than what it can."*
|