@auxiora/autonomy 1.0.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 +191 -0
- package/dist/audit-trail.d.ts +21 -0
- package/dist/audit-trail.d.ts.map +1 -0
- package/dist/audit-trail.js +74 -0
- package/dist/audit-trail.js.map +1 -0
- package/dist/index.d.ts +7 -0
- package/dist/index.d.ts.map +1 -0
- package/dist/index.js +6 -0
- package/dist/index.js.map +1 -0
- package/dist/rollback.d.ts +15 -0
- package/dist/rollback.d.ts.map +1 -0
- package/dist/rollback.js +30 -0
- package/dist/rollback.js.map +1 -0
- package/dist/trust-engine.d.ts +21 -0
- package/dist/trust-engine.d.ts.map +1 -0
- package/dist/trust-engine.js +186 -0
- package/dist/trust-engine.js.map +1 -0
- package/dist/trust-gate.d.ts +15 -0
- package/dist/trust-gate.d.ts.map +1 -0
- package/dist/trust-gate.js +22 -0
- package/dist/trust-gate.js.map +1 -0
- package/dist/types.d.ts +65 -0
- package/dist/types.d.ts.map +1 -0
- package/dist/types.js +26 -0
- package/dist/types.js.map +1 -0
- package/package.json +25 -0
- package/src/audit-trail.ts +93 -0
- package/src/index.ts +19 -0
- package/src/rollback.ts +44 -0
- package/src/trust-engine.ts +218 -0
- package/src/trust-gate.ts +36 -0
- package/src/types.ts +105 -0
- package/tests/audit-trail.test.ts +167 -0
- package/tests/rollback.test.ts +135 -0
- package/tests/trust-engine.test.ts +182 -0
- package/tests/trust-gate.test.ts +54 -0
- package/tsconfig.json +11 -0
- package/tsconfig.tsbuildinfo +1 -0
package/dist/types.d.ts
ADDED
|
@@ -0,0 +1,65 @@
|
|
|
1
|
+
/** Trust level from 0 (no autonomy) to 4 (full autonomy). */
|
|
2
|
+
export type TrustLevel = 0 | 1 | 2 | 3 | 4;
|
|
3
|
+
/** Named trust domains that can have independent trust levels. */
|
|
4
|
+
export type TrustDomain = 'messaging' | 'files' | 'web' | 'shell' | 'finance' | 'calendar' | 'email' | 'integrations' | 'system';
|
|
5
|
+
export interface TrustConfig {
|
|
6
|
+
/** Default trust level for new domains. */
|
|
7
|
+
defaultLevel: TrustLevel;
|
|
8
|
+
/** Whether automatic promotion is enabled. */
|
|
9
|
+
autoPromote: boolean;
|
|
10
|
+
/** Minimum successful actions before promotion is considered. */
|
|
11
|
+
promotionThreshold: number;
|
|
12
|
+
/** Number of failures before automatic demotion. */
|
|
13
|
+
demotionThreshold: number;
|
|
14
|
+
/** Maximum trust level that auto-promotion can reach. */
|
|
15
|
+
autoPromoteCeiling: TrustLevel;
|
|
16
|
+
}
|
|
17
|
+
export interface TrustEvidence {
|
|
18
|
+
/** Number of successful actions in this domain. */
|
|
19
|
+
successes: number;
|
|
20
|
+
/** Number of failed actions in this domain. */
|
|
21
|
+
failures: number;
|
|
22
|
+
/** Timestamp of last action. */
|
|
23
|
+
lastActionAt: number;
|
|
24
|
+
/** Timestamp of last promotion. */
|
|
25
|
+
lastPromotedAt?: number;
|
|
26
|
+
/** Timestamp of last demotion. */
|
|
27
|
+
lastDemotedAt?: number;
|
|
28
|
+
}
|
|
29
|
+
export interface TrustPromotion {
|
|
30
|
+
domain: TrustDomain;
|
|
31
|
+
fromLevel: TrustLevel;
|
|
32
|
+
toLevel: TrustLevel;
|
|
33
|
+
reason: string;
|
|
34
|
+
timestamp: number;
|
|
35
|
+
automatic: boolean;
|
|
36
|
+
}
|
|
37
|
+
export interface TrustDemotion {
|
|
38
|
+
domain: TrustDomain;
|
|
39
|
+
fromLevel: TrustLevel;
|
|
40
|
+
toLevel: TrustLevel;
|
|
41
|
+
reason: string;
|
|
42
|
+
timestamp: number;
|
|
43
|
+
}
|
|
44
|
+
export interface ActionAudit {
|
|
45
|
+
id: string;
|
|
46
|
+
timestamp: number;
|
|
47
|
+
trustLevel: TrustLevel;
|
|
48
|
+
domain: TrustDomain;
|
|
49
|
+
intent: string;
|
|
50
|
+
plan: string;
|
|
51
|
+
executed: boolean;
|
|
52
|
+
outcome: 'success' | 'failure' | 'pending' | 'rolled_back';
|
|
53
|
+
reasoning: string;
|
|
54
|
+
rollbackAvailable: boolean;
|
|
55
|
+
}
|
|
56
|
+
export interface TrustState {
|
|
57
|
+
levels: Record<TrustDomain, TrustLevel>;
|
|
58
|
+
evidence: Record<TrustDomain, TrustEvidence>;
|
|
59
|
+
promotions: TrustPromotion[];
|
|
60
|
+
demotions: TrustDemotion[];
|
|
61
|
+
}
|
|
62
|
+
export declare const DEFAULT_TRUST_CONFIG: TrustConfig;
|
|
63
|
+
export declare const TRUST_LEVEL_NAMES: Record<TrustLevel, string>;
|
|
64
|
+
export declare const ALL_TRUST_DOMAINS: TrustDomain[];
|
|
65
|
+
//# sourceMappingURL=types.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"types.d.ts","sourceRoot":"","sources":["../src/types.ts"],"names":[],"mappings":"AAAA,6DAA6D;AAC7D,MAAM,MAAM,UAAU,GAAG,CAAC,GAAG,CAAC,GAAG,CAAC,GAAG,CAAC,GAAG,CAAC,CAAC;AAE3C,kEAAkE;AAClE,MAAM,MAAM,WAAW,GACnB,WAAW,GACX,OAAO,GACP,KAAK,GACL,OAAO,GACP,SAAS,GACT,UAAU,GACV,OAAO,GACP,cAAc,GACd,QAAQ,CAAC;AAEb,MAAM,WAAW,WAAW;IAC1B,2CAA2C;IAC3C,YAAY,EAAE,UAAU,CAAC;IACzB,8CAA8C;IAC9C,WAAW,EAAE,OAAO,CAAC;IACrB,iEAAiE;IACjE,kBAAkB,EAAE,MAAM,CAAC;IAC3B,oDAAoD;IACpD,iBAAiB,EAAE,MAAM,CAAC;IAC1B,yDAAyD;IACzD,kBAAkB,EAAE,UAAU,CAAC;CAChC;AAED,MAAM,WAAW,aAAa;IAC5B,mDAAmD;IACnD,SAAS,EAAE,MAAM,CAAC;IAClB,+CAA+C;IAC/C,QAAQ,EAAE,MAAM,CAAC;IACjB,gCAAgC;IAChC,YAAY,EAAE,MAAM,CAAC;IACrB,mCAAmC;IACnC,cAAc,CAAC,EAAE,MAAM,CAAC;IACxB,kCAAkC;IAClC,aAAa,CAAC,EAAE,MAAM,CAAC;CACxB;AAED,MAAM,WAAW,cAAc;IAC7B,MAAM,EAAE,WAAW,CAAC;IACpB,SAAS,EAAE,UAAU,CAAC;IACtB,OAAO,EAAE,UAAU,CAAC;IACpB,MAAM,EAAE,MAAM,CAAC;IACf,SAAS,EAAE,MAAM,CAAC;IAClB,SAAS,EAAE,OAAO,CAAC;CACpB;AAED,MAAM,WAAW,aAAa;IAC5B,MAAM,EAAE,WAAW,CAAC;IACpB,SAAS,EAAE,UAAU,CAAC;IACtB,OAAO,EAAE,UAAU,CAAC;IACpB,MAAM,EAAE,MAAM,CAAC;IACf,SAAS,EAAE,MAAM,CAAC;CACnB;AAED,MAAM,WAAW,WAAW;IAC1B,EAAE,EAAE,MAAM,CAAC;IACX,SAAS,EAAE,MAAM,CAAC;IAClB,UAAU,EAAE,UAAU,CAAC;IACvB,MAAM,EAAE,WAAW,CAAC;IACpB,MAAM,EAAE,MAAM,CAAC;IACf,IAAI,EAAE,MAAM,CAAC;IACb,QAAQ,EAAE,OAAO,CAAC;IAClB,OAAO,EAAE,SAAS,GAAG,SAAS,GAAG,SAAS,GAAG,aAAa,CAAC;IAC3D,SAAS,EAAE,MAAM,CAAC;IAClB,iBAAiB,EAAE,OAAO,CAAC;CAC5B;AAED,MAAM,WAAW,UAAU;IACzB,MAAM,EAAE,MAAM,CAAC,WAAW,EAAE,UAAU,CAAC,CAAC;IACxC,QAAQ,EAAE,MAAM,CAAC,WAAW,EAAE,aAAa,CAAC,CAAC;IAC7C,UAAU,EAAE,cAAc,EAAE,CAAC;IAC7B,SAAS,EAAE,aAAa,EAAE,CAAC;CAC5B;AAED,eAAO,MAAM,oBAAoB,EAAE,WAMlC,CAAC;AAEF,eAAO,MAAM,iBAAiB,EAAE,MAAM,CAAC,UAAU,EAAE,MAAM,CAMxD,CAAC;AAEF,eAAO,MAAM,iBAAiB,EAAE,WAAW,EAU1C,CAAC"}
|
package/dist/types.js
ADDED
|
@@ -0,0 +1,26 @@
|
|
|
1
|
+
export const DEFAULT_TRUST_CONFIG = {
|
|
2
|
+
defaultLevel: 0,
|
|
3
|
+
autoPromote: true,
|
|
4
|
+
promotionThreshold: 10,
|
|
5
|
+
demotionThreshold: 3,
|
|
6
|
+
autoPromoteCeiling: 3,
|
|
7
|
+
};
|
|
8
|
+
export const TRUST_LEVEL_NAMES = {
|
|
9
|
+
0: 'None',
|
|
10
|
+
1: 'Inform',
|
|
11
|
+
2: 'Suggest',
|
|
12
|
+
3: 'Act & Report',
|
|
13
|
+
4: 'Full Autonomy',
|
|
14
|
+
};
|
|
15
|
+
export const ALL_TRUST_DOMAINS = [
|
|
16
|
+
'messaging',
|
|
17
|
+
'files',
|
|
18
|
+
'web',
|
|
19
|
+
'shell',
|
|
20
|
+
'finance',
|
|
21
|
+
'calendar',
|
|
22
|
+
'email',
|
|
23
|
+
'integrations',
|
|
24
|
+
'system',
|
|
25
|
+
];
|
|
26
|
+
//# sourceMappingURL=types.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"types.js","sourceRoot":"","sources":["../src/types.ts"],"names":[],"mappings":"AA8EA,MAAM,CAAC,MAAM,oBAAoB,GAAgB;IAC/C,YAAY,EAAE,CAAC;IACf,WAAW,EAAE,IAAI;IACjB,kBAAkB,EAAE,EAAE;IACtB,iBAAiB,EAAE,CAAC;IACpB,kBAAkB,EAAE,CAAC;CACtB,CAAC;AAEF,MAAM,CAAC,MAAM,iBAAiB,GAA+B;IAC3D,CAAC,EAAE,MAAM;IACT,CAAC,EAAE,QAAQ;IACX,CAAC,EAAE,SAAS;IACZ,CAAC,EAAE,cAAc;IACjB,CAAC,EAAE,eAAe;CACnB,CAAC;AAEF,MAAM,CAAC,MAAM,iBAAiB,GAAkB;IAC9C,WAAW;IACX,OAAO;IACP,KAAK;IACL,OAAO;IACP,SAAS;IACT,UAAU;IACV,OAAO;IACP,cAAc;IACd,QAAQ;CACT,CAAC"}
|
package/package.json
ADDED
|
@@ -0,0 +1,25 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "@auxiora/autonomy",
|
|
3
|
+
"version": "1.0.0",
|
|
4
|
+
"description": "Trust engine with 5-level autonomy, escalation, demotion, and audit trail",
|
|
5
|
+
"type": "module",
|
|
6
|
+
"main": "dist/index.js",
|
|
7
|
+
"types": "dist/index.d.ts",
|
|
8
|
+
"exports": {
|
|
9
|
+
".": {
|
|
10
|
+
"types": "./dist/index.d.ts",
|
|
11
|
+
"import": "./dist/index.js"
|
|
12
|
+
}
|
|
13
|
+
},
|
|
14
|
+
"dependencies": {
|
|
15
|
+
"@auxiora/core": "1.0.0"
|
|
16
|
+
},
|
|
17
|
+
"engines": {
|
|
18
|
+
"node": ">=22.0.0"
|
|
19
|
+
},
|
|
20
|
+
"scripts": {
|
|
21
|
+
"build": "tsc",
|
|
22
|
+
"clean": "rm -rf dist",
|
|
23
|
+
"typecheck": "tsc --noEmit"
|
|
24
|
+
}
|
|
25
|
+
}
|
|
@@ -0,0 +1,93 @@
|
|
|
1
|
+
import * as fs from 'node:fs/promises';
|
|
2
|
+
import * as path from 'node:path';
|
|
3
|
+
import * as crypto from 'node:crypto';
|
|
4
|
+
import { paths } from '@auxiora/core';
|
|
5
|
+
import type { ActionAudit, TrustDomain, TrustLevel } from './types.js';
|
|
6
|
+
|
|
7
|
+
export interface AuditQueryFilters {
|
|
8
|
+
domain?: TrustDomain;
|
|
9
|
+
outcome?: ActionAudit['outcome'];
|
|
10
|
+
fromTimestamp?: number;
|
|
11
|
+
toTimestamp?: number;
|
|
12
|
+
limit?: number;
|
|
13
|
+
}
|
|
14
|
+
|
|
15
|
+
export class ActionAuditTrail {
|
|
16
|
+
private entries: ActionAudit[] = [];
|
|
17
|
+
private filePath: string;
|
|
18
|
+
|
|
19
|
+
constructor(filePath?: string) {
|
|
20
|
+
this.filePath = filePath ?? path.join(paths.data(), 'trust-audit.json');
|
|
21
|
+
}
|
|
22
|
+
|
|
23
|
+
async load(): Promise<void> {
|
|
24
|
+
try {
|
|
25
|
+
const raw = await fs.readFile(this.filePath, 'utf-8');
|
|
26
|
+
this.entries = JSON.parse(raw) as ActionAudit[];
|
|
27
|
+
} catch (error) {
|
|
28
|
+
if ((error as NodeJS.ErrnoException).code !== 'ENOENT') {
|
|
29
|
+
throw error;
|
|
30
|
+
}
|
|
31
|
+
}
|
|
32
|
+
}
|
|
33
|
+
|
|
34
|
+
async save(): Promise<void> {
|
|
35
|
+
const dir = path.dirname(this.filePath);
|
|
36
|
+
await fs.mkdir(dir, { recursive: true });
|
|
37
|
+
await fs.writeFile(this.filePath, JSON.stringify(this.entries, null, 2), 'utf-8');
|
|
38
|
+
}
|
|
39
|
+
|
|
40
|
+
async record(entry: Omit<ActionAudit, 'id' | 'timestamp'>): Promise<ActionAudit> {
|
|
41
|
+
const audit: ActionAudit = {
|
|
42
|
+
id: crypto.randomUUID(),
|
|
43
|
+
timestamp: Date.now(),
|
|
44
|
+
...entry,
|
|
45
|
+
};
|
|
46
|
+
this.entries.push(audit);
|
|
47
|
+
await this.save();
|
|
48
|
+
return audit;
|
|
49
|
+
}
|
|
50
|
+
|
|
51
|
+
query(filters: AuditQueryFilters = {}): ActionAudit[] {
|
|
52
|
+
let result = [...this.entries];
|
|
53
|
+
|
|
54
|
+
if (filters.domain) {
|
|
55
|
+
result = result.filter((e) => e.domain === filters.domain);
|
|
56
|
+
}
|
|
57
|
+
if (filters.outcome) {
|
|
58
|
+
result = result.filter((e) => e.outcome === filters.outcome);
|
|
59
|
+
}
|
|
60
|
+
if (filters.fromTimestamp !== undefined) {
|
|
61
|
+
result = result.filter((e) => e.timestamp >= filters.fromTimestamp!);
|
|
62
|
+
}
|
|
63
|
+
if (filters.toTimestamp !== undefined) {
|
|
64
|
+
result = result.filter((e) => e.timestamp <= filters.toTimestamp!);
|
|
65
|
+
}
|
|
66
|
+
|
|
67
|
+
// Sort newest first
|
|
68
|
+
result.sort((a, b) => b.timestamp - a.timestamp);
|
|
69
|
+
|
|
70
|
+
if (filters.limit !== undefined && filters.limit > 0) {
|
|
71
|
+
result = result.slice(0, filters.limit);
|
|
72
|
+
}
|
|
73
|
+
|
|
74
|
+
return result;
|
|
75
|
+
}
|
|
76
|
+
|
|
77
|
+
getById(id: string): ActionAudit | undefined {
|
|
78
|
+
return this.entries.find((e) => e.id === id);
|
|
79
|
+
}
|
|
80
|
+
|
|
81
|
+
async markRolledBack(id: string): Promise<boolean> {
|
|
82
|
+
const entry = this.entries.find((e) => e.id === id);
|
|
83
|
+
if (!entry) return false;
|
|
84
|
+
entry.outcome = 'rolled_back';
|
|
85
|
+
entry.rollbackAvailable = false;
|
|
86
|
+
await this.save();
|
|
87
|
+
return true;
|
|
88
|
+
}
|
|
89
|
+
|
|
90
|
+
getAll(): ActionAudit[] {
|
|
91
|
+
return [...this.entries];
|
|
92
|
+
}
|
|
93
|
+
}
|
package/src/index.ts
ADDED
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
export type {
|
|
2
|
+
TrustLevel,
|
|
3
|
+
TrustDomain,
|
|
4
|
+
TrustConfig,
|
|
5
|
+
TrustEvidence,
|
|
6
|
+
TrustPromotion,
|
|
7
|
+
TrustDemotion,
|
|
8
|
+
ActionAudit,
|
|
9
|
+
TrustState,
|
|
10
|
+
} from './types.js';
|
|
11
|
+
export {
|
|
12
|
+
DEFAULT_TRUST_CONFIG,
|
|
13
|
+
TRUST_LEVEL_NAMES,
|
|
14
|
+
ALL_TRUST_DOMAINS,
|
|
15
|
+
} from './types.js';
|
|
16
|
+
export { TrustEngine } from './trust-engine.js';
|
|
17
|
+
export { ActionAuditTrail, type AuditQueryFilters } from './audit-trail.js';
|
|
18
|
+
export { RollbackManager, type RollbackResult } from './rollback.js';
|
|
19
|
+
export { TrustGate, type GateResult } from './trust-gate.js';
|
package/src/rollback.ts
ADDED
|
@@ -0,0 +1,44 @@
|
|
|
1
|
+
import type { ActionAuditTrail } from './audit-trail.js';
|
|
2
|
+
import type { ActionAudit } from './types.js';
|
|
3
|
+
|
|
4
|
+
export interface RollbackResult {
|
|
5
|
+
success: boolean;
|
|
6
|
+
auditId: string;
|
|
7
|
+
error?: string;
|
|
8
|
+
}
|
|
9
|
+
|
|
10
|
+
export class RollbackManager {
|
|
11
|
+
private auditTrail: ActionAuditTrail;
|
|
12
|
+
|
|
13
|
+
constructor(auditTrail: ActionAuditTrail) {
|
|
14
|
+
this.auditTrail = auditTrail;
|
|
15
|
+
}
|
|
16
|
+
|
|
17
|
+
canRollback(auditId: string): boolean {
|
|
18
|
+
const entry = this.auditTrail.getById(auditId);
|
|
19
|
+
if (!entry) return false;
|
|
20
|
+
return entry.rollbackAvailable && entry.outcome !== 'rolled_back';
|
|
21
|
+
}
|
|
22
|
+
|
|
23
|
+
async rollback(auditId: string): Promise<RollbackResult> {
|
|
24
|
+
const entry = this.auditTrail.getById(auditId);
|
|
25
|
+
if (!entry) {
|
|
26
|
+
return { success: false, auditId, error: 'Audit entry not found' };
|
|
27
|
+
}
|
|
28
|
+
|
|
29
|
+
if (entry.outcome === 'rolled_back') {
|
|
30
|
+
return { success: false, auditId, error: 'Action already rolled back' };
|
|
31
|
+
}
|
|
32
|
+
|
|
33
|
+
if (!entry.rollbackAvailable) {
|
|
34
|
+
return { success: false, auditId, error: 'Rollback not available for this action' };
|
|
35
|
+
}
|
|
36
|
+
|
|
37
|
+
await this.auditTrail.markRolledBack(auditId);
|
|
38
|
+
return { success: true, auditId };
|
|
39
|
+
}
|
|
40
|
+
|
|
41
|
+
getHistory(): ActionAudit[] {
|
|
42
|
+
return this.auditTrail.query({ outcome: 'rolled_back' });
|
|
43
|
+
}
|
|
44
|
+
}
|
|
@@ -0,0 +1,218 @@
|
|
|
1
|
+
import * as fs from 'node:fs/promises';
|
|
2
|
+
import * as path from 'node:path';
|
|
3
|
+
import { paths } from '@auxiora/core';
|
|
4
|
+
import type {
|
|
5
|
+
TrustLevel,
|
|
6
|
+
TrustDomain,
|
|
7
|
+
TrustConfig,
|
|
8
|
+
TrustEvidence,
|
|
9
|
+
TrustPromotion,
|
|
10
|
+
TrustDemotion,
|
|
11
|
+
TrustState,
|
|
12
|
+
} from './types.js';
|
|
13
|
+
import {
|
|
14
|
+
DEFAULT_TRUST_CONFIG,
|
|
15
|
+
ALL_TRUST_DOMAINS,
|
|
16
|
+
} from './types.js';
|
|
17
|
+
|
|
18
|
+
function makeFreshEvidence(): TrustEvidence {
|
|
19
|
+
return { successes: 0, failures: 0, lastActionAt: 0 };
|
|
20
|
+
}
|
|
21
|
+
|
|
22
|
+
function makeFreshState(defaultLevel: TrustLevel): TrustState {
|
|
23
|
+
const levels = {} as Record<TrustDomain, TrustLevel>;
|
|
24
|
+
const evidence = {} as Record<TrustDomain, TrustEvidence>;
|
|
25
|
+
for (const domain of ALL_TRUST_DOMAINS) {
|
|
26
|
+
levels[domain] = defaultLevel;
|
|
27
|
+
evidence[domain] = makeFreshEvidence();
|
|
28
|
+
}
|
|
29
|
+
return { levels, evidence, promotions: [], demotions: [] };
|
|
30
|
+
}
|
|
31
|
+
|
|
32
|
+
export class TrustEngine {
|
|
33
|
+
private state: TrustState;
|
|
34
|
+
private config: TrustConfig;
|
|
35
|
+
private statePath: string;
|
|
36
|
+
|
|
37
|
+
constructor(config?: Partial<TrustConfig>, statePath?: string) {
|
|
38
|
+
this.config = { ...DEFAULT_TRUST_CONFIG, ...config };
|
|
39
|
+
this.statePath = statePath ?? path.join(paths.data(), 'trust-state.json');
|
|
40
|
+
this.state = makeFreshState(this.config.defaultLevel);
|
|
41
|
+
}
|
|
42
|
+
|
|
43
|
+
async load(): Promise<void> {
|
|
44
|
+
try {
|
|
45
|
+
const raw = await fs.readFile(this.statePath, 'utf-8');
|
|
46
|
+
const parsed = JSON.parse(raw) as TrustState;
|
|
47
|
+
this.state = parsed;
|
|
48
|
+
|
|
49
|
+
// Ensure all domains exist (in case new ones were added)
|
|
50
|
+
for (const domain of ALL_TRUST_DOMAINS) {
|
|
51
|
+
if (this.state.levels[domain] === undefined) {
|
|
52
|
+
this.state.levels[domain] = this.config.defaultLevel;
|
|
53
|
+
}
|
|
54
|
+
if (!this.state.evidence[domain]) {
|
|
55
|
+
this.state.evidence[domain] = makeFreshEvidence();
|
|
56
|
+
}
|
|
57
|
+
}
|
|
58
|
+
} catch (error) {
|
|
59
|
+
if ((error as NodeJS.ErrnoException).code !== 'ENOENT') {
|
|
60
|
+
throw error;
|
|
61
|
+
}
|
|
62
|
+
// File doesn't exist, use fresh state
|
|
63
|
+
}
|
|
64
|
+
}
|
|
65
|
+
|
|
66
|
+
async save(): Promise<void> {
|
|
67
|
+
const dir = path.dirname(this.statePath);
|
|
68
|
+
await fs.mkdir(dir, { recursive: true });
|
|
69
|
+
await fs.writeFile(this.statePath, JSON.stringify(this.state, null, 2), 'utf-8');
|
|
70
|
+
}
|
|
71
|
+
|
|
72
|
+
getTrustLevel(domain: TrustDomain): TrustLevel {
|
|
73
|
+
return this.state.levels[domain] ?? this.config.defaultLevel;
|
|
74
|
+
}
|
|
75
|
+
|
|
76
|
+
getAllLevels(): Record<TrustDomain, TrustLevel> {
|
|
77
|
+
return { ...this.state.levels };
|
|
78
|
+
}
|
|
79
|
+
|
|
80
|
+
getEvidence(domain: TrustDomain): TrustEvidence {
|
|
81
|
+
return { ...(this.state.evidence[domain] ?? makeFreshEvidence()) };
|
|
82
|
+
}
|
|
83
|
+
|
|
84
|
+
async setTrustLevel(domain: TrustDomain, level: TrustLevel, reason: string): Promise<void> {
|
|
85
|
+
const current = this.getTrustLevel(domain);
|
|
86
|
+
if (level === current) return;
|
|
87
|
+
|
|
88
|
+
if (level > current) {
|
|
89
|
+
this.state.promotions.push({
|
|
90
|
+
domain,
|
|
91
|
+
fromLevel: current,
|
|
92
|
+
toLevel: level,
|
|
93
|
+
reason,
|
|
94
|
+
timestamp: Date.now(),
|
|
95
|
+
automatic: false,
|
|
96
|
+
});
|
|
97
|
+
this.state.evidence[domain].lastPromotedAt = Date.now();
|
|
98
|
+
} else {
|
|
99
|
+
this.state.demotions.push({
|
|
100
|
+
domain,
|
|
101
|
+
fromLevel: current,
|
|
102
|
+
toLevel: level,
|
|
103
|
+
reason,
|
|
104
|
+
timestamp: Date.now(),
|
|
105
|
+
});
|
|
106
|
+
this.state.evidence[domain].lastDemotedAt = Date.now();
|
|
107
|
+
}
|
|
108
|
+
|
|
109
|
+
this.state.levels[domain] = level;
|
|
110
|
+
await this.save();
|
|
111
|
+
}
|
|
112
|
+
|
|
113
|
+
checkPermission(domain: TrustDomain, requiredLevel: TrustLevel): boolean {
|
|
114
|
+
return this.getTrustLevel(domain) >= requiredLevel;
|
|
115
|
+
}
|
|
116
|
+
|
|
117
|
+
async recordOutcome(domain: TrustDomain, success: boolean): Promise<TrustPromotion | TrustDemotion | null> {
|
|
118
|
+
const ev = this.state.evidence[domain] ?? makeFreshEvidence();
|
|
119
|
+
ev.lastActionAt = Date.now();
|
|
120
|
+
|
|
121
|
+
if (success) {
|
|
122
|
+
ev.successes++;
|
|
123
|
+
ev.failures = 0; // Reset consecutive failure count on success
|
|
124
|
+
} else {
|
|
125
|
+
ev.failures++;
|
|
126
|
+
}
|
|
127
|
+
|
|
128
|
+
this.state.evidence[domain] = ev;
|
|
129
|
+
|
|
130
|
+
// Check for auto-demotion
|
|
131
|
+
if (ev.failures >= this.config.demotionThreshold) {
|
|
132
|
+
const current = this.getTrustLevel(domain);
|
|
133
|
+
if (current > 0) {
|
|
134
|
+
const newLevel = (current - 1) as TrustLevel;
|
|
135
|
+
const demotion: TrustDemotion = {
|
|
136
|
+
domain,
|
|
137
|
+
fromLevel: current,
|
|
138
|
+
toLevel: newLevel,
|
|
139
|
+
reason: `Automatic demotion after ${ev.failures} consecutive failures`,
|
|
140
|
+
timestamp: Date.now(),
|
|
141
|
+
};
|
|
142
|
+
this.state.levels[domain] = newLevel;
|
|
143
|
+
this.state.demotions.push(demotion);
|
|
144
|
+
ev.failures = 0;
|
|
145
|
+
ev.lastDemotedAt = Date.now();
|
|
146
|
+
await this.save();
|
|
147
|
+
return demotion;
|
|
148
|
+
}
|
|
149
|
+
}
|
|
150
|
+
|
|
151
|
+
// Check for auto-promotion
|
|
152
|
+
const promotion = this.evaluatePromotion(domain);
|
|
153
|
+
if (promotion) {
|
|
154
|
+
this.state.levels[domain] = promotion.toLevel;
|
|
155
|
+
this.state.promotions.push(promotion);
|
|
156
|
+
ev.successes = 0; // Reset counter after promotion
|
|
157
|
+
ev.lastPromotedAt = Date.now();
|
|
158
|
+
await this.save();
|
|
159
|
+
return promotion;
|
|
160
|
+
}
|
|
161
|
+
|
|
162
|
+
await this.save();
|
|
163
|
+
return null;
|
|
164
|
+
}
|
|
165
|
+
|
|
166
|
+
evaluatePromotion(domain: TrustDomain): TrustPromotion | null {
|
|
167
|
+
if (!this.config.autoPromote) return null;
|
|
168
|
+
|
|
169
|
+
const current = this.getTrustLevel(domain);
|
|
170
|
+
if (current >= this.config.autoPromoteCeiling) return null;
|
|
171
|
+
if (current >= 4) return null;
|
|
172
|
+
|
|
173
|
+
const ev = this.state.evidence[domain];
|
|
174
|
+
if (!ev || ev.successes < this.config.promotionThreshold) return null;
|
|
175
|
+
|
|
176
|
+
const newLevel = (current + 1) as TrustLevel;
|
|
177
|
+
return {
|
|
178
|
+
domain,
|
|
179
|
+
fromLevel: current,
|
|
180
|
+
toLevel: newLevel,
|
|
181
|
+
reason: `Automatic promotion after ${ev.successes} successful actions`,
|
|
182
|
+
timestamp: Date.now(),
|
|
183
|
+
automatic: true,
|
|
184
|
+
};
|
|
185
|
+
}
|
|
186
|
+
|
|
187
|
+
async demote(domain: TrustDomain, reason: string): Promise<TrustDemotion | null> {
|
|
188
|
+
const current = this.getTrustLevel(domain);
|
|
189
|
+
if (current <= 0) return null;
|
|
190
|
+
|
|
191
|
+
const newLevel = (current - 1) as TrustLevel;
|
|
192
|
+
const demotion: TrustDemotion = {
|
|
193
|
+
domain,
|
|
194
|
+
fromLevel: current,
|
|
195
|
+
toLevel: newLevel,
|
|
196
|
+
reason,
|
|
197
|
+
timestamp: Date.now(),
|
|
198
|
+
};
|
|
199
|
+
|
|
200
|
+
this.state.levels[domain] = newLevel;
|
|
201
|
+
this.state.demotions.push(demotion);
|
|
202
|
+
this.state.evidence[domain].lastDemotedAt = Date.now();
|
|
203
|
+
await this.save();
|
|
204
|
+
return demotion;
|
|
205
|
+
}
|
|
206
|
+
|
|
207
|
+
getPromotions(): TrustPromotion[] {
|
|
208
|
+
return [...this.state.promotions];
|
|
209
|
+
}
|
|
210
|
+
|
|
211
|
+
getDemotions(): TrustDemotion[] {
|
|
212
|
+
return [...this.state.demotions];
|
|
213
|
+
}
|
|
214
|
+
|
|
215
|
+
getState(): TrustState {
|
|
216
|
+
return JSON.parse(JSON.stringify(this.state)) as TrustState;
|
|
217
|
+
}
|
|
218
|
+
}
|
|
@@ -0,0 +1,36 @@
|
|
|
1
|
+
import type { TrustEngine } from './trust-engine.js';
|
|
2
|
+
import type { TrustDomain, TrustLevel } from './types.js';
|
|
3
|
+
import { TRUST_LEVEL_NAMES } from './types.js';
|
|
4
|
+
|
|
5
|
+
export interface GateResult {
|
|
6
|
+
allowed: boolean;
|
|
7
|
+
currentLevel: TrustLevel;
|
|
8
|
+
requiredLevel: TrustLevel;
|
|
9
|
+
domain: TrustDomain;
|
|
10
|
+
message: string;
|
|
11
|
+
}
|
|
12
|
+
|
|
13
|
+
export class TrustGate {
|
|
14
|
+
private engine: TrustEngine;
|
|
15
|
+
|
|
16
|
+
constructor(engine: TrustEngine) {
|
|
17
|
+
this.engine = engine;
|
|
18
|
+
}
|
|
19
|
+
|
|
20
|
+
gate(domain: TrustDomain, action: string, requiredLevel: TrustLevel): GateResult {
|
|
21
|
+
const currentLevel = this.engine.getTrustLevel(domain);
|
|
22
|
+
const allowed = currentLevel >= requiredLevel;
|
|
23
|
+
|
|
24
|
+
const message = allowed
|
|
25
|
+
? `Action "${action}" allowed at trust level ${TRUST_LEVEL_NAMES[currentLevel]}`
|
|
26
|
+
: `Action "${action}" denied: requires ${TRUST_LEVEL_NAMES[requiredLevel]} (current: ${TRUST_LEVEL_NAMES[currentLevel]})`;
|
|
27
|
+
|
|
28
|
+
return {
|
|
29
|
+
allowed,
|
|
30
|
+
currentLevel,
|
|
31
|
+
requiredLevel,
|
|
32
|
+
domain,
|
|
33
|
+
message,
|
|
34
|
+
};
|
|
35
|
+
}
|
|
36
|
+
}
|
package/src/types.ts
ADDED
|
@@ -0,0 +1,105 @@
|
|
|
1
|
+
/** Trust level from 0 (no autonomy) to 4 (full autonomy). */
|
|
2
|
+
export type TrustLevel = 0 | 1 | 2 | 3 | 4;
|
|
3
|
+
|
|
4
|
+
/** Named trust domains that can have independent trust levels. */
|
|
5
|
+
export type TrustDomain =
|
|
6
|
+
| 'messaging'
|
|
7
|
+
| 'files'
|
|
8
|
+
| 'web'
|
|
9
|
+
| 'shell'
|
|
10
|
+
| 'finance'
|
|
11
|
+
| 'calendar'
|
|
12
|
+
| 'email'
|
|
13
|
+
| 'integrations'
|
|
14
|
+
| 'system';
|
|
15
|
+
|
|
16
|
+
export interface TrustConfig {
|
|
17
|
+
/** Default trust level for new domains. */
|
|
18
|
+
defaultLevel: TrustLevel;
|
|
19
|
+
/** Whether automatic promotion is enabled. */
|
|
20
|
+
autoPromote: boolean;
|
|
21
|
+
/** Minimum successful actions before promotion is considered. */
|
|
22
|
+
promotionThreshold: number;
|
|
23
|
+
/** Number of failures before automatic demotion. */
|
|
24
|
+
demotionThreshold: number;
|
|
25
|
+
/** Maximum trust level that auto-promotion can reach. */
|
|
26
|
+
autoPromoteCeiling: TrustLevel;
|
|
27
|
+
}
|
|
28
|
+
|
|
29
|
+
export interface TrustEvidence {
|
|
30
|
+
/** Number of successful actions in this domain. */
|
|
31
|
+
successes: number;
|
|
32
|
+
/** Number of failed actions in this domain. */
|
|
33
|
+
failures: number;
|
|
34
|
+
/** Timestamp of last action. */
|
|
35
|
+
lastActionAt: number;
|
|
36
|
+
/** Timestamp of last promotion. */
|
|
37
|
+
lastPromotedAt?: number;
|
|
38
|
+
/** Timestamp of last demotion. */
|
|
39
|
+
lastDemotedAt?: number;
|
|
40
|
+
}
|
|
41
|
+
|
|
42
|
+
export interface TrustPromotion {
|
|
43
|
+
domain: TrustDomain;
|
|
44
|
+
fromLevel: TrustLevel;
|
|
45
|
+
toLevel: TrustLevel;
|
|
46
|
+
reason: string;
|
|
47
|
+
timestamp: number;
|
|
48
|
+
automatic: boolean;
|
|
49
|
+
}
|
|
50
|
+
|
|
51
|
+
export interface TrustDemotion {
|
|
52
|
+
domain: TrustDomain;
|
|
53
|
+
fromLevel: TrustLevel;
|
|
54
|
+
toLevel: TrustLevel;
|
|
55
|
+
reason: string;
|
|
56
|
+
timestamp: number;
|
|
57
|
+
}
|
|
58
|
+
|
|
59
|
+
export interface ActionAudit {
|
|
60
|
+
id: string;
|
|
61
|
+
timestamp: number;
|
|
62
|
+
trustLevel: TrustLevel;
|
|
63
|
+
domain: TrustDomain;
|
|
64
|
+
intent: string;
|
|
65
|
+
plan: string;
|
|
66
|
+
executed: boolean;
|
|
67
|
+
outcome: 'success' | 'failure' | 'pending' | 'rolled_back';
|
|
68
|
+
reasoning: string;
|
|
69
|
+
rollbackAvailable: boolean;
|
|
70
|
+
}
|
|
71
|
+
|
|
72
|
+
export interface TrustState {
|
|
73
|
+
levels: Record<TrustDomain, TrustLevel>;
|
|
74
|
+
evidence: Record<TrustDomain, TrustEvidence>;
|
|
75
|
+
promotions: TrustPromotion[];
|
|
76
|
+
demotions: TrustDemotion[];
|
|
77
|
+
}
|
|
78
|
+
|
|
79
|
+
export const DEFAULT_TRUST_CONFIG: TrustConfig = {
|
|
80
|
+
defaultLevel: 0,
|
|
81
|
+
autoPromote: true,
|
|
82
|
+
promotionThreshold: 10,
|
|
83
|
+
demotionThreshold: 3,
|
|
84
|
+
autoPromoteCeiling: 3,
|
|
85
|
+
};
|
|
86
|
+
|
|
87
|
+
export const TRUST_LEVEL_NAMES: Record<TrustLevel, string> = {
|
|
88
|
+
0: 'None',
|
|
89
|
+
1: 'Inform',
|
|
90
|
+
2: 'Suggest',
|
|
91
|
+
3: 'Act & Report',
|
|
92
|
+
4: 'Full Autonomy',
|
|
93
|
+
};
|
|
94
|
+
|
|
95
|
+
export const ALL_TRUST_DOMAINS: TrustDomain[] = [
|
|
96
|
+
'messaging',
|
|
97
|
+
'files',
|
|
98
|
+
'web',
|
|
99
|
+
'shell',
|
|
100
|
+
'finance',
|
|
101
|
+
'calendar',
|
|
102
|
+
'email',
|
|
103
|
+
'integrations',
|
|
104
|
+
'system',
|
|
105
|
+
];
|