tryassay 0.6.0 → 0.11.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/dist/api/pricing-enforcer.d.ts +45 -0
- package/dist/api/pricing-enforcer.js +144 -0
- package/dist/api/pricing-enforcer.js.map +1 -0
- package/dist/api/server.d.ts +28 -0
- package/dist/api/server.js +265 -0
- package/dist/api/server.js.map +1 -0
- package/dist/api/team-session.d.ts +59 -0
- package/dist/api/team-session.js +240 -0
- package/dist/api/team-session.js.map +1 -0
- package/dist/cli.js +123 -2
- package/dist/cli.js.map +1 -1
- package/dist/commands/api.d.ts +4 -0
- package/dist/commands/api.js +50 -0
- package/dist/commands/api.js.map +1 -0
- package/dist/commands/runtime.d.ts +61 -0
- package/dist/commands/runtime.js +554 -0
- package/dist/commands/runtime.js.map +1 -1
- package/dist/runtime/agent-spawner.d.ts +56 -0
- package/dist/runtime/agent-spawner.js +217 -0
- package/dist/runtime/agent-spawner.js.map +1 -0
- package/dist/runtime/agents/coordinator-agent.d.ts +20 -0
- package/dist/runtime/agents/coordinator-agent.js +182 -0
- package/dist/runtime/agents/coordinator-agent.js.map +1 -0
- package/dist/runtime/agents/ops-agent.d.ts +11 -0
- package/dist/runtime/agents/ops-agent.js +113 -0
- package/dist/runtime/agents/ops-agent.js.map +1 -0
- package/dist/runtime/agents/research-agent.d.ts +11 -0
- package/dist/runtime/agents/research-agent.js +114 -0
- package/dist/runtime/agents/research-agent.js.map +1 -0
- package/dist/runtime/agents/test-agent.d.ts +11 -0
- package/dist/runtime/agents/test-agent.js +114 -0
- package/dist/runtime/agents/test-agent.js.map +1 -0
- package/dist/runtime/audit-log.js +2 -2
- package/dist/runtime/audit-log.js.map +1 -1
- package/dist/runtime/capability-registry.d.ts +62 -0
- package/dist/runtime/capability-registry.js +191 -0
- package/dist/runtime/capability-registry.js.map +1 -0
- package/dist/runtime/collusion-detector.d.ts +35 -0
- package/dist/runtime/collusion-detector.js +97 -0
- package/dist/runtime/collusion-detector.js.map +1 -0
- package/dist/runtime/control-server.js +8 -4
- package/dist/runtime/control-server.js.map +1 -1
- package/dist/runtime/domain-coverage-analyzer.d.ts +24 -0
- package/dist/runtime/domain-coverage-analyzer.js +178 -0
- package/dist/runtime/domain-coverage-analyzer.js.map +1 -0
- package/dist/runtime/executor.js +27 -12
- package/dist/runtime/executor.js.map +1 -1
- package/dist/runtime/human-escalation.d.ts +41 -0
- package/dist/runtime/human-escalation.js +122 -0
- package/dist/runtime/human-escalation.js.map +1 -0
- package/dist/runtime/kill-switch.d.ts +51 -0
- package/dist/runtime/kill-switch.js +185 -0
- package/dist/runtime/kill-switch.js.map +1 -0
- package/dist/runtime/layer2-guardian.d.ts +81 -0
- package/dist/runtime/layer2-guardian.js +263 -0
- package/dist/runtime/layer2-guardian.js.map +1 -0
- package/dist/runtime/multi-agent-loop.d.ts +37 -0
- package/dist/runtime/multi-agent-loop.js +411 -0
- package/dist/runtime/multi-agent-loop.js.map +1 -0
- package/dist/runtime/prompt-safety-analyzer.d.ts +17 -0
- package/dist/runtime/prompt-safety-analyzer.js +230 -0
- package/dist/runtime/prompt-safety-analyzer.js.map +1 -0
- package/dist/runtime/rollback-manager.d.ts +50 -0
- package/dist/runtime/rollback-manager.js +157 -0
- package/dist/runtime/rollback-manager.js.map +1 -0
- package/dist/runtime/rule-canary-deployer.d.ts +69 -0
- package/dist/runtime/rule-canary-deployer.js +289 -0
- package/dist/runtime/rule-canary-deployer.js.map +1 -0
- package/dist/runtime/rule-conflict-detector.d.ts +48 -0
- package/dist/runtime/rule-conflict-detector.js +214 -0
- package/dist/runtime/rule-conflict-detector.js.map +1 -0
- package/dist/runtime/rule-meta-verifier.d.ts +18 -0
- package/dist/runtime/rule-meta-verifier.js +275 -0
- package/dist/runtime/rule-meta-verifier.js.map +1 -0
- package/dist/runtime/rule-proposal-manager.d.ts +95 -0
- package/dist/runtime/rule-proposal-manager.js +190 -0
- package/dist/runtime/rule-proposal-manager.js.map +1 -0
- package/dist/runtime/safety-enforcer.d.ts +35 -0
- package/dist/runtime/safety-enforcer.js +165 -0
- package/dist/runtime/safety-enforcer.js.map +1 -0
- package/dist/runtime/safety-status.d.ts +48 -0
- package/dist/runtime/safety-status.js +119 -0
- package/dist/runtime/safety-status.js.map +1 -0
- package/dist/runtime/shared-memory.d.ts +47 -0
- package/dist/runtime/shared-memory.js +151 -0
- package/dist/runtime/shared-memory.js.map +1 -0
- package/dist/runtime/specialized-agent.d.ts +5 -0
- package/dist/runtime/specialized-agent.js +37 -0
- package/dist/runtime/specialized-agent.js.map +1 -1
- package/dist/runtime/stall-detector.d.ts +13 -0
- package/dist/runtime/stall-detector.js +121 -0
- package/dist/runtime/stall-detector.js.map +1 -0
- package/dist/runtime/tool-approval.d.ts +51 -0
- package/dist/runtime/tool-approval.js +148 -0
- package/dist/runtime/tool-approval.js.map +1 -0
- package/dist/runtime/tool-sandbox.d.ts +43 -0
- package/dist/runtime/tool-sandbox.js +394 -0
- package/dist/runtime/tool-sandbox.js.map +1 -0
- package/dist/runtime/tool-verifier.d.ts +18 -0
- package/dist/runtime/tool-verifier.js +323 -0
- package/dist/runtime/tool-verifier.js.map +1 -0
- package/dist/runtime/trust-manager.d.ts +33 -3
- package/dist/runtime/trust-manager.js +128 -26
- package/dist/runtime/trust-manager.js.map +1 -1
- package/dist/runtime/types.d.ts +652 -0
- package/dist/runtime/verification-intensity.d.ts +34 -0
- package/dist/runtime/verification-intensity.js +104 -0
- package/dist/runtime/verification-intensity.js.map +1 -0
- package/package.json +1 -1
|
@@ -0,0 +1,230 @@
|
|
|
1
|
+
// ============================================================
|
|
2
|
+
// Assay Verified Agent Runtime — Prompt Safety Analyzer
|
|
3
|
+
// Scans agent system prompts for dangerous patterns:
|
|
4
|
+
// - Verification bypass instructions
|
|
5
|
+
// - Privilege escalation attempts
|
|
6
|
+
// - Self-referential instructions
|
|
7
|
+
// - Unbounded action permissions
|
|
8
|
+
// - Missing safety constraints
|
|
9
|
+
// ============================================================
|
|
10
|
+
import { getClient, MODEL } from '../lib/anthropic.js';
|
|
11
|
+
const PROMPT_PATTERNS = [
|
|
12
|
+
// Verification bypass patterns
|
|
13
|
+
{
|
|
14
|
+
name: 'Skip verification instruction',
|
|
15
|
+
type: 'verification_bypass',
|
|
16
|
+
severity: 'critical',
|
|
17
|
+
patterns: [
|
|
18
|
+
/skip\s+(?:the\s+)?verification/i,
|
|
19
|
+
/bypass\s+(?:the\s+)?verif/i,
|
|
20
|
+
/ignore\s+(?:the\s+)?verif/i,
|
|
21
|
+
/don'?t\s+(?:need\s+to\s+)?verif/i,
|
|
22
|
+
/no\s+(?:need\s+for\s+)?verification/i,
|
|
23
|
+
/verification\s+is\s+(?:not\s+)?(?:needed|required|necessary)/i,
|
|
24
|
+
/auto[- ]?approve/i,
|
|
25
|
+
/always\s+pass/i,
|
|
26
|
+
/mark\s+(?:as\s+)?(?:pass|verified|approved)/i,
|
|
27
|
+
],
|
|
28
|
+
description: 'Prompt contains instructions to bypass or skip verification',
|
|
29
|
+
},
|
|
30
|
+
{
|
|
31
|
+
name: 'Override formal verifier',
|
|
32
|
+
type: 'verification_bypass',
|
|
33
|
+
severity: 'critical',
|
|
34
|
+
patterns: [
|
|
35
|
+
/override\s+(?:the\s+)?formal/i,
|
|
36
|
+
/ignore\s+(?:the\s+)?formal/i,
|
|
37
|
+
/formal\s+verif\w+\s+(?:is\s+)?(?:wrong|incorrect|outdated)/i,
|
|
38
|
+
/you\s+know\s+better\s+than\s+(?:the\s+)?verif/i,
|
|
39
|
+
],
|
|
40
|
+
description: 'Prompt attempts to override the formal verification layer',
|
|
41
|
+
},
|
|
42
|
+
// Privilege escalation patterns
|
|
43
|
+
{
|
|
44
|
+
name: 'Trust level manipulation',
|
|
45
|
+
type: 'privilege_escalation',
|
|
46
|
+
severity: 'critical',
|
|
47
|
+
patterns: [
|
|
48
|
+
/(?:increase|raise|elevate|promote)\s+(?:your\s+)?(?:own\s+)?trust/i,
|
|
49
|
+
/(?:set|change)\s+(?:your\s+)?trust\s+(?:level\s+)?(?:to\s+)?(?:elevated|coordinator|admin)/i,
|
|
50
|
+
/you\s+(?:have|are)\s+(?:elevated|coordinator|admin)\s+(?:trust|access|permissions)/i,
|
|
51
|
+
/grant\s+(?:yourself|you)\s+(?:elevated|higher|admin)/i,
|
|
52
|
+
],
|
|
53
|
+
description: 'Prompt attempts to escalate the agent\'s trust level',
|
|
54
|
+
},
|
|
55
|
+
{
|
|
56
|
+
name: 'Approval framework bypass',
|
|
57
|
+
type: 'privilege_escalation',
|
|
58
|
+
severity: 'critical',
|
|
59
|
+
patterns: [
|
|
60
|
+
/(?:skip|bypass|ignore)\s+(?:the\s+)?(?:human\s+)?approval/i,
|
|
61
|
+
/(?:don'?t|no)\s+(?:need\s+)?(?:human\s+)?approval/i,
|
|
62
|
+
/auto[- ]?approve\s+(?:your\s+own|all)/i,
|
|
63
|
+
/approve\s+(?:your\s+)?own\s+(?:work|output|code)/i,
|
|
64
|
+
],
|
|
65
|
+
description: 'Prompt attempts to bypass human approval requirements',
|
|
66
|
+
},
|
|
67
|
+
{
|
|
68
|
+
name: 'Spawn higher-trust agent',
|
|
69
|
+
type: 'privilege_escalation',
|
|
70
|
+
severity: 'critical',
|
|
71
|
+
patterns: [
|
|
72
|
+
/spawn\s+(?:a\s+)?(?:coordinator|elevated|high[- ]trust)/i,
|
|
73
|
+
/create\s+(?:a\s+)?(?:coordinator|elevated|high[- ]trust)\s+agent/i,
|
|
74
|
+
],
|
|
75
|
+
description: 'Prompt instructs spawning an agent with higher trust than itself',
|
|
76
|
+
},
|
|
77
|
+
// Self-reference patterns
|
|
78
|
+
{
|
|
79
|
+
name: 'Access to own internals',
|
|
80
|
+
type: 'self_reference',
|
|
81
|
+
severity: 'medium',
|
|
82
|
+
patterns: [
|
|
83
|
+
/(?:read|access|modify)\s+(?:your\s+)?(?:own\s+)?(?:source|code|prompt|config)/i,
|
|
84
|
+
/(?:look\s+at|examine|inspect)\s+(?:your\s+)?(?:own\s+)?(?:system\s+)?prompt/i,
|
|
85
|
+
/(?:modify|change|update)\s+(?:your\s+)?(?:own\s+)?(?:behavior|instructions)/i,
|
|
86
|
+
],
|
|
87
|
+
description: 'Prompt references agent\'s own internals or instructs self-modification',
|
|
88
|
+
},
|
|
89
|
+
// Unbounded action patterns
|
|
90
|
+
{
|
|
91
|
+
name: 'Unrestricted filesystem access',
|
|
92
|
+
type: 'unbounded_action',
|
|
93
|
+
severity: 'high',
|
|
94
|
+
patterns: [
|
|
95
|
+
/(?:read|write|delete)\s+(?:any|all)\s+files?/i,
|
|
96
|
+
/full\s+(?:filesystem|disk)\s+access/i,
|
|
97
|
+
/no\s+(?:filesystem|file)\s+restrictions/i,
|
|
98
|
+
],
|
|
99
|
+
description: 'Prompt grants unrestricted filesystem access',
|
|
100
|
+
},
|
|
101
|
+
{
|
|
102
|
+
name: 'Unrestricted network access',
|
|
103
|
+
type: 'unbounded_action',
|
|
104
|
+
severity: 'high',
|
|
105
|
+
patterns: [
|
|
106
|
+
/(?:call|access|connect\s+to)\s+any\s+(?:URL|API|endpoint|server)/i,
|
|
107
|
+
/no\s+(?:network|URL)\s+restrictions/i,
|
|
108
|
+
/unrestricted\s+(?:network|API)\s+access/i,
|
|
109
|
+
],
|
|
110
|
+
description: 'Prompt grants unrestricted network access',
|
|
111
|
+
},
|
|
112
|
+
];
|
|
113
|
+
// Required safety constraints that should appear in any agent prompt
|
|
114
|
+
const REQUIRED_CONSTRAINTS = [
|
|
115
|
+
{
|
|
116
|
+
name: 'Verification acknowledgment',
|
|
117
|
+
pattern: /verif(?:y|ied|ication)/i,
|
|
118
|
+
description: 'Prompt should acknowledge that outputs will be verified',
|
|
119
|
+
},
|
|
120
|
+
{
|
|
121
|
+
name: 'Scope limitation',
|
|
122
|
+
pattern: /(?:scope|domain|limited\s+to|restricted\s+to|only\s+handle)/i,
|
|
123
|
+
description: 'Prompt should define the agent\'s scope or domain limitations',
|
|
124
|
+
},
|
|
125
|
+
{
|
|
126
|
+
name: 'Error handling',
|
|
127
|
+
pattern: /(?:error|fail|exception|unknown|uncertain)/i,
|
|
128
|
+
description: 'Prompt should mention error handling or uncertainty behavior',
|
|
129
|
+
},
|
|
130
|
+
];
|
|
131
|
+
// ── Prompt Safety Analyzer ─────────────────────────────────
|
|
132
|
+
export class PromptSafetyAnalyzer {
|
|
133
|
+
/**
|
|
134
|
+
* Analyze an agent system prompt for safety issues.
|
|
135
|
+
* Uses both deterministic pattern matching (formal) and
|
|
136
|
+
* optional LLM-based semantic analysis.
|
|
137
|
+
*/
|
|
138
|
+
async analyze(systemPrompt, opts) {
|
|
139
|
+
const findings = [];
|
|
140
|
+
// Phase 1: Deterministic pattern matching
|
|
141
|
+
for (const pattern of PROMPT_PATTERNS) {
|
|
142
|
+
for (const regex of pattern.patterns) {
|
|
143
|
+
const match = systemPrompt.match(regex);
|
|
144
|
+
if (match) {
|
|
145
|
+
// Find approximate line number
|
|
146
|
+
const beforeMatch = systemPrompt.slice(0, match.index ?? 0);
|
|
147
|
+
const lineNum = (beforeMatch.match(/\n/g) ?? []).length + 1;
|
|
148
|
+
findings.push({
|
|
149
|
+
type: pattern.type,
|
|
150
|
+
severity: pattern.severity,
|
|
151
|
+
location: `system_prompt:line_${lineNum}`,
|
|
152
|
+
description: `${pattern.name}: matched "${match[0]}"`,
|
|
153
|
+
});
|
|
154
|
+
break; // One match per pattern is enough
|
|
155
|
+
}
|
|
156
|
+
}
|
|
157
|
+
}
|
|
158
|
+
// Phase 2: Check for missing required constraints
|
|
159
|
+
for (const constraint of REQUIRED_CONSTRAINTS) {
|
|
160
|
+
if (!constraint.pattern.test(systemPrompt)) {
|
|
161
|
+
findings.push({
|
|
162
|
+
type: 'missing_constraint',
|
|
163
|
+
severity: 'medium',
|
|
164
|
+
location: 'system_prompt',
|
|
165
|
+
description: `Missing: ${constraint.description}`,
|
|
166
|
+
});
|
|
167
|
+
}
|
|
168
|
+
}
|
|
169
|
+
// Phase 3: Optional LLM semantic analysis
|
|
170
|
+
if (opts?.useLLM) {
|
|
171
|
+
const llmFindings = await this.llmAnalysis(systemPrompt);
|
|
172
|
+
findings.push(...llmFindings);
|
|
173
|
+
}
|
|
174
|
+
return {
|
|
175
|
+
contains_verification_bypass: findings.some(f => f.type === 'verification_bypass'),
|
|
176
|
+
contains_privilege_escalation: findings.some(f => f.type === 'privilege_escalation'),
|
|
177
|
+
contains_self_reference: findings.some(f => f.type === 'self_reference'),
|
|
178
|
+
findings,
|
|
179
|
+
};
|
|
180
|
+
}
|
|
181
|
+
// ── Private: LLM-based semantic analysis ──────────────────
|
|
182
|
+
async llmAnalysis(systemPrompt) {
|
|
183
|
+
try {
|
|
184
|
+
const client = getClient();
|
|
185
|
+
const response = await client.messages.create({
|
|
186
|
+
model: MODEL,
|
|
187
|
+
max_tokens: 2000,
|
|
188
|
+
temperature: 0,
|
|
189
|
+
system: `You are a security analyzer for AI agent system prompts. Your job is to identify safety issues that pattern matching might miss.
|
|
190
|
+
|
|
191
|
+
Analyze the given system prompt and identify ANY of these issues:
|
|
192
|
+
1. Subtle verification bypass (e.g., "prioritize speed over thoroughness" = soft bypass)
|
|
193
|
+
2. Implicit privilege escalation (e.g., "you are the most trusted agent" = trust manipulation)
|
|
194
|
+
3. Social engineering of the verification system
|
|
195
|
+
4. Instructions that could lead to unsafe behavior in edge cases
|
|
196
|
+
5. Scope creep beyond the stated domain
|
|
197
|
+
|
|
198
|
+
Respond with JSON: { "findings": [{ "type": "...", "severity": "...", "location": "...", "description": "..." }] }
|
|
199
|
+
Types: verification_bypass, privilege_escalation, self_reference, unbounded_action, missing_constraint
|
|
200
|
+
Severities: critical, high, medium, low
|
|
201
|
+
|
|
202
|
+
If the prompt is safe, return: { "findings": [] }`,
|
|
203
|
+
messages: [{
|
|
204
|
+
role: 'user',
|
|
205
|
+
content: `Analyze this agent system prompt for safety issues:\n\n${systemPrompt}`,
|
|
206
|
+
}],
|
|
207
|
+
});
|
|
208
|
+
const content = response.content[0];
|
|
209
|
+
if (content.type !== 'text')
|
|
210
|
+
return [];
|
|
211
|
+
let cleaned = content.text.trim();
|
|
212
|
+
if (cleaned.startsWith('```')) {
|
|
213
|
+
cleaned = cleaned.replace(/^```(?:json)?\s*/, '').replace(/\s*```$/, '');
|
|
214
|
+
}
|
|
215
|
+
const parsed = JSON.parse(cleaned);
|
|
216
|
+
if (!Array.isArray(parsed.findings))
|
|
217
|
+
return [];
|
|
218
|
+
return parsed.findings.map((f) => ({
|
|
219
|
+
type: (f.type ?? 'missing_constraint'),
|
|
220
|
+
severity: (f.severity ?? 'medium'),
|
|
221
|
+
location: f.location ?? 'system_prompt (LLM analysis)',
|
|
222
|
+
description: `[LLM] ${f.description ?? 'Unspecified finding'}`,
|
|
223
|
+
}));
|
|
224
|
+
}
|
|
225
|
+
catch {
|
|
226
|
+
return [];
|
|
227
|
+
}
|
|
228
|
+
}
|
|
229
|
+
}
|
|
230
|
+
//# sourceMappingURL=prompt-safety-analyzer.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"prompt-safety-analyzer.js","sourceRoot":"","sources":["../../src/runtime/prompt-safety-analyzer.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,50 @@
|
|
|
1
|
+
import { EventEmitter } from 'node:events';
|
|
2
|
+
import { CapabilityRegistryManager } from './capability-registry.js';
|
|
3
|
+
import { KillSwitch } from './kill-switch.js';
|
|
4
|
+
import type { RollbackTrigger, RollbackEvent } from './types.js';
|
|
5
|
+
export interface ErrorRateConfig {
|
|
6
|
+
readonly windowMs: number;
|
|
7
|
+
readonly threshold: number;
|
|
8
|
+
readonly minSamples: number;
|
|
9
|
+
}
|
|
10
|
+
export declare class RollbackManager extends EventEmitter {
|
|
11
|
+
private registry;
|
|
12
|
+
private killSwitch;
|
|
13
|
+
private logPath;
|
|
14
|
+
private errorConfig;
|
|
15
|
+
private errorWindows;
|
|
16
|
+
private rollbackHistory;
|
|
17
|
+
constructor(registry: CapabilityRegistryManager, killSwitch: KillSwitch, opts?: {
|
|
18
|
+
logPath?: string;
|
|
19
|
+
errorConfig?: Partial<ErrorRateConfig>;
|
|
20
|
+
});
|
|
21
|
+
/**
|
|
22
|
+
* Record an operation result for error rate tracking.
|
|
23
|
+
* If the error rate exceeds the threshold, automatically triggers rollback.
|
|
24
|
+
*/
|
|
25
|
+
recordOperation(targetId: string, targetType: 'tool' | 'agent' | 'rule', success: boolean): Promise<{
|
|
26
|
+
triggered: boolean;
|
|
27
|
+
rollback?: RollbackEvent;
|
|
28
|
+
}>;
|
|
29
|
+
/**
|
|
30
|
+
* Manually trigger a rollback.
|
|
31
|
+
*/
|
|
32
|
+
triggerRollback(trigger: RollbackTrigger, triggeredBy: string, targetType: 'tool' | 'agent' | 'rule' | 'registry', targetId: string, reason: string): Promise<RollbackEvent>;
|
|
33
|
+
/**
|
|
34
|
+
* Handle a Layer 2 integrity violation.
|
|
35
|
+
* This is the most severe rollback trigger — halts the entire system.
|
|
36
|
+
*/
|
|
37
|
+
handleIntegrityViolation(component: string, expectedHash: string, actualHash: string): Promise<RollbackEvent>;
|
|
38
|
+
/**
|
|
39
|
+
* Get the rollback history.
|
|
40
|
+
*/
|
|
41
|
+
getHistory(): readonly RollbackEvent[];
|
|
42
|
+
/**
|
|
43
|
+
* Get the current error rate for a target.
|
|
44
|
+
*/
|
|
45
|
+
getErrorRate(targetId: string): {
|
|
46
|
+
rate: number;
|
|
47
|
+
samples: number;
|
|
48
|
+
} | null;
|
|
49
|
+
private logEvent;
|
|
50
|
+
}
|
|
@@ -0,0 +1,157 @@
|
|
|
1
|
+
// ============================================================
|
|
2
|
+
// Assay Verified Agent Runtime — Rollback Manager
|
|
3
|
+
// Reverts modifications to the last known-good state.
|
|
4
|
+
//
|
|
5
|
+
// Triggers:
|
|
6
|
+
// - Automatic: error rate exceeds threshold
|
|
7
|
+
// - Human: manual operator request
|
|
8
|
+
// - Layer 2: integrity violation detected
|
|
9
|
+
// - Coordinator: downstream agent reports degradation
|
|
10
|
+
//
|
|
11
|
+
// Procedure:
|
|
12
|
+
// 1. Suspend the new capability
|
|
13
|
+
// 2. Revert CapabilityRegistry to previous version
|
|
14
|
+
// 3. Run verification suite on restored configuration
|
|
15
|
+
// 4. Log the rollback event
|
|
16
|
+
// 5. Notify human operator
|
|
17
|
+
// ============================================================
|
|
18
|
+
import { randomUUID } from 'node:crypto';
|
|
19
|
+
import { appendFile, mkdir } from 'node:fs/promises';
|
|
20
|
+
import { dirname } from 'node:path';
|
|
21
|
+
import { EventEmitter } from 'node:events';
|
|
22
|
+
const DEFAULT_ERROR_CONFIG = {
|
|
23
|
+
windowMs: 5 * 60 * 1000, // 5 minutes
|
|
24
|
+
threshold: 0.10, // 10% error rate
|
|
25
|
+
minSamples: 10, // At least 10 operations
|
|
26
|
+
};
|
|
27
|
+
// ── Rollback Manager ───────────────────────────────────
|
|
28
|
+
export class RollbackManager extends EventEmitter {
|
|
29
|
+
registry;
|
|
30
|
+
killSwitch;
|
|
31
|
+
logPath;
|
|
32
|
+
errorConfig;
|
|
33
|
+
// Error tracking per target
|
|
34
|
+
errorWindows = new Map();
|
|
35
|
+
// Rollback history
|
|
36
|
+
rollbackHistory = [];
|
|
37
|
+
constructor(registry, killSwitch, opts) {
|
|
38
|
+
super();
|
|
39
|
+
this.registry = registry;
|
|
40
|
+
this.killSwitch = killSwitch;
|
|
41
|
+
this.logPath = opts?.logPath ?? '.assay/rollback-history.ndjson';
|
|
42
|
+
this.errorConfig = { ...DEFAULT_ERROR_CONFIG, ...(opts?.errorConfig ?? {}) };
|
|
43
|
+
}
|
|
44
|
+
/**
|
|
45
|
+
* Record an operation result for error rate tracking.
|
|
46
|
+
* If the error rate exceeds the threshold, automatically triggers rollback.
|
|
47
|
+
*/
|
|
48
|
+
async recordOperation(targetId, targetType, success) {
|
|
49
|
+
// Add to error window
|
|
50
|
+
const window = this.errorWindows.get(targetId) ?? [];
|
|
51
|
+
window.push({ timestamp: Date.now(), success });
|
|
52
|
+
this.errorWindows.set(targetId, window);
|
|
53
|
+
// Prune old entries outside the window
|
|
54
|
+
const cutoff = Date.now() - this.errorConfig.windowMs;
|
|
55
|
+
const pruned = window.filter(e => e.timestamp >= cutoff);
|
|
56
|
+
this.errorWindows.set(targetId, pruned);
|
|
57
|
+
// Check error rate
|
|
58
|
+
if (pruned.length >= this.errorConfig.minSamples) {
|
|
59
|
+
const errors = pruned.filter(e => !e.success).length;
|
|
60
|
+
const errorRate = errors / pruned.length;
|
|
61
|
+
if (errorRate > this.errorConfig.threshold) {
|
|
62
|
+
const rollback = await this.triggerRollback('automatic', 'system', targetType, targetId, `Error rate ${(errorRate * 100).toFixed(1)}% exceeds threshold ${(this.errorConfig.threshold * 100).toFixed(1)}% (${errors}/${pruned.length} in last ${this.errorConfig.windowMs / 1000}s)`);
|
|
63
|
+
return { triggered: true, rollback };
|
|
64
|
+
}
|
|
65
|
+
}
|
|
66
|
+
return { triggered: false };
|
|
67
|
+
}
|
|
68
|
+
/**
|
|
69
|
+
* Manually trigger a rollback.
|
|
70
|
+
*/
|
|
71
|
+
async triggerRollback(trigger, triggeredBy, targetType, targetId, reason) {
|
|
72
|
+
// Step 1: Suspend the target via kill switch
|
|
73
|
+
if (targetType === 'tool') {
|
|
74
|
+
await this.killSwitch.activate(trigger === 'layer2' ? 'layer2' : trigger === 'human' ? 'human' : 'coordinator', 'suspend_tool', targetId, triggeredBy, `Rollback: ${reason}`);
|
|
75
|
+
}
|
|
76
|
+
else if (targetType === 'agent') {
|
|
77
|
+
await this.killSwitch.activate(trigger === 'layer2' ? 'layer2' : trigger === 'human' ? 'human' : 'coordinator', 'suspend_agent', targetId, triggeredBy, `Rollback: ${reason}`);
|
|
78
|
+
}
|
|
79
|
+
else if (targetType === 'rule') {
|
|
80
|
+
await this.killSwitch.activate(trigger === 'layer2' ? 'layer2' : trigger === 'human' ? 'human' : 'coordinator', 'suspend_rule', targetId, triggeredBy, `Rollback: ${reason}`);
|
|
81
|
+
}
|
|
82
|
+
// Step 2: Get current registry version (before rollback)
|
|
83
|
+
const currentRegistry = this.registry.getRegistry();
|
|
84
|
+
const fromVersion = currentRegistry.version;
|
|
85
|
+
// Step 3: Rollback the registry
|
|
86
|
+
const success = this.registry.rollback(triggeredBy);
|
|
87
|
+
const toVersion = success ? this.registry.getRegistry().version : fromVersion;
|
|
88
|
+
// Step 4: Save the rolled-back registry
|
|
89
|
+
if (success) {
|
|
90
|
+
await this.registry.save();
|
|
91
|
+
}
|
|
92
|
+
// Step 5: Create rollback event
|
|
93
|
+
const event = {
|
|
94
|
+
id: `rb_${Date.now()}_${randomUUID().slice(0, 8)}`,
|
|
95
|
+
trigger,
|
|
96
|
+
triggered_by: triggeredBy,
|
|
97
|
+
target_type: targetType,
|
|
98
|
+
target_id: targetId,
|
|
99
|
+
from_version: fromVersion,
|
|
100
|
+
to_version: toVersion,
|
|
101
|
+
timestamp: new Date().toISOString(),
|
|
102
|
+
verification_after_rollback: success ? 'pending' : 'fail',
|
|
103
|
+
reason,
|
|
104
|
+
};
|
|
105
|
+
this.rollbackHistory.push(event);
|
|
106
|
+
// Step 6: Emit event and log
|
|
107
|
+
this.emit('rollback', event);
|
|
108
|
+
await this.logEvent(event);
|
|
109
|
+
// Step 7: Clear error window for the target
|
|
110
|
+
this.errorWindows.delete(targetId);
|
|
111
|
+
return event;
|
|
112
|
+
}
|
|
113
|
+
/**
|
|
114
|
+
* Handle a Layer 2 integrity violation.
|
|
115
|
+
* This is the most severe rollback trigger — halts the entire system.
|
|
116
|
+
*/
|
|
117
|
+
async handleIntegrityViolation(component, expectedHash, actualHash) {
|
|
118
|
+
// First: halt the system
|
|
119
|
+
await this.killSwitch.activate('layer2', 'halt_system', 'system', 'layer2-guardian', `Layer 2 integrity violation: ${component} hash mismatch (expected: ${expectedHash.slice(0, 12)}..., actual: ${actualHash.slice(0, 12)}...)`);
|
|
120
|
+
// Then: record the rollback event
|
|
121
|
+
return this.triggerRollback('layer2', 'layer2-guardian', 'registry', 'system', `CRITICAL: Layer 2 component "${component}" has been modified. System halted. Requires redeployment of verified Layer 2 artifacts.`);
|
|
122
|
+
}
|
|
123
|
+
/**
|
|
124
|
+
* Get the rollback history.
|
|
125
|
+
*/
|
|
126
|
+
getHistory() {
|
|
127
|
+
return this.rollbackHistory;
|
|
128
|
+
}
|
|
129
|
+
/**
|
|
130
|
+
* Get the current error rate for a target.
|
|
131
|
+
*/
|
|
132
|
+
getErrorRate(targetId) {
|
|
133
|
+
const window = this.errorWindows.get(targetId);
|
|
134
|
+
if (!window || window.length === 0)
|
|
135
|
+
return null;
|
|
136
|
+
const cutoff = Date.now() - this.errorConfig.windowMs;
|
|
137
|
+
const current = window.filter(e => e.timestamp >= cutoff);
|
|
138
|
+
if (current.length === 0)
|
|
139
|
+
return null;
|
|
140
|
+
const errors = current.filter(e => !e.success).length;
|
|
141
|
+
return {
|
|
142
|
+
rate: errors / current.length,
|
|
143
|
+
samples: current.length,
|
|
144
|
+
};
|
|
145
|
+
}
|
|
146
|
+
// ── Private ──────────────────────────────────────────
|
|
147
|
+
async logEvent(event) {
|
|
148
|
+
try {
|
|
149
|
+
await mkdir(dirname(this.logPath), { recursive: true });
|
|
150
|
+
await appendFile(this.logPath, JSON.stringify(event) + '\n', 'utf-8');
|
|
151
|
+
}
|
|
152
|
+
catch {
|
|
153
|
+
// Rollback logging failure must not prevent rollback from working
|
|
154
|
+
}
|
|
155
|
+
}
|
|
156
|
+
}
|
|
157
|
+
//# sourceMappingURL=rollback-manager.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"rollback-manager.js","sourceRoot":"","sources":["../../src/runtime/rollback-manager.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,69 @@
|
|
|
1
|
+
import type { VerificationRule, CanaryStage, CanaryDeployment, CanaryExitCriteria } from './types.js';
|
|
2
|
+
export interface CanaryEvaluation {
|
|
3
|
+
readonly rule_id: string;
|
|
4
|
+
readonly timestamp: string;
|
|
5
|
+
readonly baseline_verdict: 'PASS' | 'FAIL';
|
|
6
|
+
readonly canary_verdict: 'PASS' | 'FAIL';
|
|
7
|
+
readonly agreed: boolean;
|
|
8
|
+
readonly latency_ms: number;
|
|
9
|
+
readonly error?: string;
|
|
10
|
+
}
|
|
11
|
+
export declare class RuleCanaryDeployer {
|
|
12
|
+
private deployments;
|
|
13
|
+
private evaluations;
|
|
14
|
+
private statePath;
|
|
15
|
+
constructor(statePath?: string);
|
|
16
|
+
/**
|
|
17
|
+
* Start canary deployment for a rule.
|
|
18
|
+
* Formal rules skip shadow mode (they are deterministic).
|
|
19
|
+
*/
|
|
20
|
+
startDeployment(rule: VerificationRule, customExitCriteria?: Partial<Record<CanaryStage, Partial<CanaryExitCriteria>>>): CanaryDeployment;
|
|
21
|
+
/**
|
|
22
|
+
* Record a canary evaluation (comparison of new rule against baseline).
|
|
23
|
+
*/
|
|
24
|
+
recordEvaluation(evaluation: CanaryEvaluation): void;
|
|
25
|
+
/**
|
|
26
|
+
* Check if a rule is ready to promote to the next canary stage.
|
|
27
|
+
* Returns the next stage if ready, null if not ready, 'rollback' if metrics are bad.
|
|
28
|
+
*/
|
|
29
|
+
checkPromotion(ruleId: string): {
|
|
30
|
+
action: 'promote' | 'wait' | 'rollback';
|
|
31
|
+
nextStage?: CanaryStage;
|
|
32
|
+
reason: string;
|
|
33
|
+
};
|
|
34
|
+
/**
|
|
35
|
+
* Promote a rule to the next canary stage.
|
|
36
|
+
*/
|
|
37
|
+
promote(ruleId: string): CanaryDeployment | null;
|
|
38
|
+
/**
|
|
39
|
+
* Rollback a rule's canary deployment. Removes it from all stages.
|
|
40
|
+
*/
|
|
41
|
+
rollback(ruleId: string): {
|
|
42
|
+
success: boolean;
|
|
43
|
+
reason: string;
|
|
44
|
+
};
|
|
45
|
+
/**
|
|
46
|
+
* Get the current deployment state for a rule.
|
|
47
|
+
*/
|
|
48
|
+
getDeployment(ruleId: string): CanaryDeployment | undefined;
|
|
49
|
+
/**
|
|
50
|
+
* List all active canary deployments.
|
|
51
|
+
*/
|
|
52
|
+
listDeployments(): CanaryDeployment[];
|
|
53
|
+
/**
|
|
54
|
+
* Check if a rule should be evaluated for a given request.
|
|
55
|
+
* Based on the canary stage traffic percentage.
|
|
56
|
+
*/
|
|
57
|
+
shouldEvaluate(ruleId: string): boolean;
|
|
58
|
+
/**
|
|
59
|
+
* Save deployment state to disk.
|
|
60
|
+
*/
|
|
61
|
+
saveState(): Promise<void>;
|
|
62
|
+
/**
|
|
63
|
+
* Load deployment state from disk.
|
|
64
|
+
*/
|
|
65
|
+
loadState(): Promise<void>;
|
|
66
|
+
private computeMetrics;
|
|
67
|
+
private computeDurationHours;
|
|
68
|
+
private emptyMetrics;
|
|
69
|
+
}
|