@usejarvis/brain 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 +153 -0
- package/README.md +278 -0
- package/bin/jarvis.ts +413 -0
- package/package.json +74 -0
- package/scripts/ensure-bun.cjs +8 -0
- package/src/actions/README.md +421 -0
- package/src/actions/app-control/desktop-controller.test.ts +26 -0
- package/src/actions/app-control/desktop-controller.ts +438 -0
- package/src/actions/app-control/interface.ts +64 -0
- package/src/actions/app-control/linux.ts +273 -0
- package/src/actions/app-control/macos.ts +54 -0
- package/src/actions/app-control/sidecar-launcher.test.ts +23 -0
- package/src/actions/app-control/sidecar-launcher.ts +286 -0
- package/src/actions/app-control/windows.ts +44 -0
- package/src/actions/browser/cdp.ts +138 -0
- package/src/actions/browser/chrome-launcher.ts +252 -0
- package/src/actions/browser/session.ts +437 -0
- package/src/actions/browser/stealth.ts +49 -0
- package/src/actions/index.ts +20 -0
- package/src/actions/terminal/executor.ts +157 -0
- package/src/actions/terminal/wsl-bridge.ts +126 -0
- package/src/actions/test.ts +93 -0
- package/src/actions/tools/agents.ts +321 -0
- package/src/actions/tools/builtin.ts +846 -0
- package/src/actions/tools/commitments.ts +192 -0
- package/src/actions/tools/content.ts +217 -0
- package/src/actions/tools/delegate.ts +147 -0
- package/src/actions/tools/desktop.test.ts +55 -0
- package/src/actions/tools/desktop.ts +305 -0
- package/src/actions/tools/goals.ts +376 -0
- package/src/actions/tools/local-tools-guard.ts +20 -0
- package/src/actions/tools/registry.ts +171 -0
- package/src/actions/tools/research.ts +111 -0
- package/src/actions/tools/sidecar-list.ts +57 -0
- package/src/actions/tools/sidecar-route.ts +105 -0
- package/src/actions/tools/workflows.ts +216 -0
- package/src/agents/agent.ts +132 -0
- package/src/agents/delegation.ts +107 -0
- package/src/agents/hierarchy.ts +113 -0
- package/src/agents/index.ts +19 -0
- package/src/agents/messaging.ts +125 -0
- package/src/agents/orchestrator.ts +576 -0
- package/src/agents/role-discovery.ts +61 -0
- package/src/agents/sub-agent-runner.ts +307 -0
- package/src/agents/task-manager.ts +151 -0
- package/src/authority/approval-delivery.ts +59 -0
- package/src/authority/approval.ts +196 -0
- package/src/authority/audit.ts +158 -0
- package/src/authority/authority.test.ts +519 -0
- package/src/authority/deferred-executor.ts +103 -0
- package/src/authority/emergency.ts +66 -0
- package/src/authority/engine.ts +297 -0
- package/src/authority/index.ts +12 -0
- package/src/authority/learning.ts +111 -0
- package/src/authority/tool-action-map.ts +74 -0
- package/src/awareness/analytics.ts +466 -0
- package/src/awareness/awareness.test.ts +332 -0
- package/src/awareness/capture-engine.ts +305 -0
- package/src/awareness/context-graph.ts +130 -0
- package/src/awareness/context-tracker.ts +349 -0
- package/src/awareness/index.ts +25 -0
- package/src/awareness/intelligence.ts +321 -0
- package/src/awareness/ocr-engine.ts +88 -0
- package/src/awareness/service.ts +528 -0
- package/src/awareness/struggle-detector.ts +342 -0
- package/src/awareness/suggestion-engine.ts +476 -0
- package/src/awareness/types.ts +201 -0
- package/src/cli/autostart.ts +241 -0
- package/src/cli/deps.ts +449 -0
- package/src/cli/doctor.ts +230 -0
- package/src/cli/helpers.ts +401 -0
- package/src/cli/onboard.ts +580 -0
- package/src/comms/README.md +329 -0
- package/src/comms/auth-error.html +48 -0
- package/src/comms/channels/discord.ts +228 -0
- package/src/comms/channels/signal.ts +56 -0
- package/src/comms/channels/telegram.ts +316 -0
- package/src/comms/channels/whatsapp.ts +60 -0
- package/src/comms/channels.test.ts +173 -0
- package/src/comms/desktop-notify.ts +114 -0
- package/src/comms/example.ts +129 -0
- package/src/comms/index.ts +129 -0
- package/src/comms/streaming.ts +142 -0
- package/src/comms/voice.test.ts +152 -0
- package/src/comms/voice.ts +291 -0
- package/src/comms/websocket.test.ts +409 -0
- package/src/comms/websocket.ts +473 -0
- package/src/config/README.md +387 -0
- package/src/config/index.ts +6 -0
- package/src/config/loader.test.ts +137 -0
- package/src/config/loader.ts +142 -0
- package/src/config/types.ts +260 -0
- package/src/daemon/README.md +232 -0
- package/src/daemon/agent-service-interface.ts +9 -0
- package/src/daemon/agent-service.ts +600 -0
- package/src/daemon/api-routes.ts +2119 -0
- package/src/daemon/background-agent-service.ts +396 -0
- package/src/daemon/background-agent.test.ts +78 -0
- package/src/daemon/channel-service.ts +201 -0
- package/src/daemon/commitment-executor.ts +297 -0
- package/src/daemon/event-classifier.ts +239 -0
- package/src/daemon/event-coalescer.ts +123 -0
- package/src/daemon/event-reactor.ts +214 -0
- package/src/daemon/health.ts +220 -0
- package/src/daemon/index.ts +1004 -0
- package/src/daemon/llm-settings.ts +316 -0
- package/src/daemon/observer-service.ts +150 -0
- package/src/daemon/pid.ts +98 -0
- package/src/daemon/research-queue.ts +155 -0
- package/src/daemon/services.ts +175 -0
- package/src/daemon/ws-service.ts +788 -0
- package/src/goals/accountability.ts +240 -0
- package/src/goals/awareness-bridge.ts +185 -0
- package/src/goals/estimator.ts +185 -0
- package/src/goals/events.ts +28 -0
- package/src/goals/goals.test.ts +400 -0
- package/src/goals/integration.test.ts +329 -0
- package/src/goals/nl-builder.test.ts +220 -0
- package/src/goals/nl-builder.ts +256 -0
- package/src/goals/rhythm.test.ts +177 -0
- package/src/goals/rhythm.ts +275 -0
- package/src/goals/service.test.ts +135 -0
- package/src/goals/service.ts +348 -0
- package/src/goals/types.ts +106 -0
- package/src/goals/workflow-bridge.ts +96 -0
- package/src/integrations/google-api.ts +134 -0
- package/src/integrations/google-auth.ts +175 -0
- package/src/llm/README.md +291 -0
- package/src/llm/anthropic.ts +386 -0
- package/src/llm/gemini.ts +371 -0
- package/src/llm/index.ts +19 -0
- package/src/llm/manager.ts +153 -0
- package/src/llm/ollama.ts +307 -0
- package/src/llm/openai.ts +350 -0
- package/src/llm/provider.test.ts +231 -0
- package/src/llm/provider.ts +60 -0
- package/src/llm/test.ts +87 -0
- package/src/observers/README.md +278 -0
- package/src/observers/calendar.ts +113 -0
- package/src/observers/clipboard.ts +136 -0
- package/src/observers/email.ts +109 -0
- package/src/observers/example.ts +58 -0
- package/src/observers/file-watcher.ts +124 -0
- package/src/observers/index.ts +159 -0
- package/src/observers/notifications.ts +197 -0
- package/src/observers/observers.test.ts +203 -0
- package/src/observers/processes.ts +225 -0
- package/src/personality/README.md +61 -0
- package/src/personality/adapter.ts +196 -0
- package/src/personality/index.ts +20 -0
- package/src/personality/learner.ts +209 -0
- package/src/personality/model.ts +132 -0
- package/src/personality/personality.test.ts +236 -0
- package/src/roles/README.md +252 -0
- package/src/roles/authority.ts +119 -0
- package/src/roles/example-usage.ts +198 -0
- package/src/roles/index.ts +42 -0
- package/src/roles/loader.ts +143 -0
- package/src/roles/prompt-builder.ts +194 -0
- package/src/roles/test-multi.ts +102 -0
- package/src/roles/test-role.yaml +77 -0
- package/src/roles/test-utils.ts +93 -0
- package/src/roles/test.ts +106 -0
- package/src/roles/tool-guide.ts +190 -0
- package/src/roles/types.ts +36 -0
- package/src/roles/utils.ts +200 -0
- package/src/scripts/google-setup.ts +168 -0
- package/src/sidecar/connection.ts +179 -0
- package/src/sidecar/index.ts +6 -0
- package/src/sidecar/manager.ts +542 -0
- package/src/sidecar/protocol.ts +85 -0
- package/src/sidecar/rpc.ts +161 -0
- package/src/sidecar/scheduler.ts +136 -0
- package/src/sidecar/types.ts +112 -0
- package/src/sidecar/validator.ts +144 -0
- package/src/vault/README.md +110 -0
- package/src/vault/awareness.ts +341 -0
- package/src/vault/commitments.ts +299 -0
- package/src/vault/content-pipeline.ts +260 -0
- package/src/vault/conversations.ts +173 -0
- package/src/vault/entities.ts +180 -0
- package/src/vault/extractor.test.ts +356 -0
- package/src/vault/extractor.ts +345 -0
- package/src/vault/facts.ts +190 -0
- package/src/vault/goals.ts +477 -0
- package/src/vault/index.ts +87 -0
- package/src/vault/keychain.ts +99 -0
- package/src/vault/observations.ts +115 -0
- package/src/vault/relationships.ts +178 -0
- package/src/vault/retrieval.test.ts +126 -0
- package/src/vault/retrieval.ts +227 -0
- package/src/vault/schema.ts +658 -0
- package/src/vault/settings.ts +38 -0
- package/src/vault/vectors.ts +92 -0
- package/src/vault/workflows.ts +403 -0
- package/src/workflows/auto-suggest.ts +290 -0
- package/src/workflows/engine.ts +366 -0
- package/src/workflows/events.ts +24 -0
- package/src/workflows/executor.ts +207 -0
- package/src/workflows/nl-builder.ts +198 -0
- package/src/workflows/nodes/actions/agent-task.ts +73 -0
- package/src/workflows/nodes/actions/calendar-action.ts +85 -0
- package/src/workflows/nodes/actions/code-execution.ts +73 -0
- package/src/workflows/nodes/actions/discord.ts +77 -0
- package/src/workflows/nodes/actions/file-write.ts +73 -0
- package/src/workflows/nodes/actions/gmail.ts +69 -0
- package/src/workflows/nodes/actions/http-request.ts +117 -0
- package/src/workflows/nodes/actions/notification.ts +85 -0
- package/src/workflows/nodes/actions/run-tool.ts +55 -0
- package/src/workflows/nodes/actions/send-message.ts +82 -0
- package/src/workflows/nodes/actions/shell-command.ts +76 -0
- package/src/workflows/nodes/actions/telegram.ts +60 -0
- package/src/workflows/nodes/builtin.ts +119 -0
- package/src/workflows/nodes/error/error-handler.ts +37 -0
- package/src/workflows/nodes/error/fallback.ts +47 -0
- package/src/workflows/nodes/error/retry.ts +82 -0
- package/src/workflows/nodes/logic/delay.ts +42 -0
- package/src/workflows/nodes/logic/if-else.ts +41 -0
- package/src/workflows/nodes/logic/loop.ts +90 -0
- package/src/workflows/nodes/logic/merge.ts +38 -0
- package/src/workflows/nodes/logic/race.ts +40 -0
- package/src/workflows/nodes/logic/switch.ts +59 -0
- package/src/workflows/nodes/logic/template-render.ts +53 -0
- package/src/workflows/nodes/logic/variable-get.ts +37 -0
- package/src/workflows/nodes/logic/variable-set.ts +59 -0
- package/src/workflows/nodes/registry.ts +99 -0
- package/src/workflows/nodes/transform/aggregate.ts +99 -0
- package/src/workflows/nodes/transform/csv-parse.ts +70 -0
- package/src/workflows/nodes/transform/json-parse.ts +63 -0
- package/src/workflows/nodes/transform/map-filter.ts +84 -0
- package/src/workflows/nodes/transform/regex-match.ts +89 -0
- package/src/workflows/nodes/triggers/calendar.ts +33 -0
- package/src/workflows/nodes/triggers/clipboard.ts +32 -0
- package/src/workflows/nodes/triggers/cron.ts +40 -0
- package/src/workflows/nodes/triggers/email.ts +40 -0
- package/src/workflows/nodes/triggers/file-change.ts +45 -0
- package/src/workflows/nodes/triggers/git.ts +46 -0
- package/src/workflows/nodes/triggers/manual.ts +23 -0
- package/src/workflows/nodes/triggers/poll.ts +81 -0
- package/src/workflows/nodes/triggers/process.ts +44 -0
- package/src/workflows/nodes/triggers/screen-event.ts +37 -0
- package/src/workflows/nodes/triggers/webhook.ts +39 -0
- package/src/workflows/safe-eval.ts +139 -0
- package/src/workflows/template.ts +118 -0
- package/src/workflows/triggers/cron.ts +311 -0
- package/src/workflows/triggers/manager.ts +285 -0
- package/src/workflows/triggers/observer-bridge.ts +172 -0
- package/src/workflows/triggers/poller.ts +201 -0
- package/src/workflows/triggers/screen-condition.ts +218 -0
- package/src/workflows/triggers/triggers.test.ts +740 -0
- package/src/workflows/triggers/webhook.ts +191 -0
- package/src/workflows/types.ts +133 -0
- package/src/workflows/variables.ts +72 -0
- package/src/workflows/workflows.test.ts +383 -0
- package/src/workflows/yaml.ts +104 -0
- package/ui/dist/index-j75njzc1.css +1199 -0
- package/ui/dist/index-p2zh407q.js +80603 -0
- package/ui/dist/index.html +13 -0
- package/ui/public/openwakeword/models/embedding_model.onnx +0 -0
- package/ui/public/openwakeword/models/hey_jarvis_v0.1.onnx +0 -0
- package/ui/public/openwakeword/models/melspectrogram.onnx +0 -0
- package/ui/public/openwakeword/models/silero_vad.onnx +0 -0
- package/ui/public/ort/ort-wasm-simd-threaded.jsep.mjs +106 -0
- package/ui/public/ort/ort-wasm-simd-threaded.jsep.wasm +0 -0
- package/ui/public/ort/ort-wasm-simd-threaded.mjs +59 -0
- package/ui/public/ort/ort-wasm-simd-threaded.wasm +0 -0
|
@@ -0,0 +1,103 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Deferred Executor — Runs approved tool calls that were waiting for approval.
|
|
3
|
+
*/
|
|
4
|
+
|
|
5
|
+
import type { ToolRegistry } from '../actions/tools/registry.ts';
|
|
6
|
+
import type { ApprovalManager, ApprovalRequest } from './approval.ts';
|
|
7
|
+
import type { AuditTrail } from './audit.ts';
|
|
8
|
+
import type { AuthorityLearner } from './learning.ts';
|
|
9
|
+
import type { ActionCategory } from '../roles/authority.ts';
|
|
10
|
+
|
|
11
|
+
export type ExecutionResultCallback = (requestId: string, request: ApprovalRequest, result: string) => void;
|
|
12
|
+
|
|
13
|
+
export class DeferredExecutor {
|
|
14
|
+
private toolRegistry: ToolRegistry | null = null;
|
|
15
|
+
private approvalManager: ApprovalManager;
|
|
16
|
+
private auditTrail: AuditTrail;
|
|
17
|
+
private learner: AuthorityLearner | null = null;
|
|
18
|
+
private onResult: ExecutionResultCallback | null = null;
|
|
19
|
+
|
|
20
|
+
constructor(approvalManager: ApprovalManager, auditTrail: AuditTrail) {
|
|
21
|
+
this.approvalManager = approvalManager;
|
|
22
|
+
this.auditTrail = auditTrail;
|
|
23
|
+
}
|
|
24
|
+
|
|
25
|
+
setToolRegistry(registry: ToolRegistry): void {
|
|
26
|
+
this.toolRegistry = registry;
|
|
27
|
+
}
|
|
28
|
+
|
|
29
|
+
setLearner(learner: AuthorityLearner): void {
|
|
30
|
+
this.learner = learner;
|
|
31
|
+
}
|
|
32
|
+
|
|
33
|
+
setResultCallback(cb: ExecutionResultCallback): void {
|
|
34
|
+
this.onResult = cb;
|
|
35
|
+
}
|
|
36
|
+
|
|
37
|
+
/**
|
|
38
|
+
* Execute a previously approved request.
|
|
39
|
+
*/
|
|
40
|
+
async executeApproved(requestId: string): Promise<string> {
|
|
41
|
+
const request = this.approvalManager.getRequest(requestId);
|
|
42
|
+
if (!request || request.status !== 'approved') {
|
|
43
|
+
return `Error: Request ${requestId} not found or not in approved state`;
|
|
44
|
+
}
|
|
45
|
+
|
|
46
|
+
if (!this.toolRegistry) {
|
|
47
|
+
return 'Error: No tool registry configured';
|
|
48
|
+
}
|
|
49
|
+
|
|
50
|
+
const startTime = Date.now();
|
|
51
|
+
|
|
52
|
+
try {
|
|
53
|
+
const args = JSON.parse(request.tool_arguments);
|
|
54
|
+
const raw = await this.toolRegistry.execute(request.tool_name, args);
|
|
55
|
+
const result = typeof raw === 'string' ? raw : JSON.stringify(raw);
|
|
56
|
+
|
|
57
|
+
const executionTimeMs = Date.now() - startTime;
|
|
58
|
+
|
|
59
|
+
// Mark as executed
|
|
60
|
+
this.approvalManager.markExecuted(requestId, result.slice(0, 2000));
|
|
61
|
+
|
|
62
|
+
// Log to audit trail
|
|
63
|
+
this.auditTrail.log({
|
|
64
|
+
agent_id: request.agent_id,
|
|
65
|
+
agent_name: request.agent_name,
|
|
66
|
+
tool_name: request.tool_name,
|
|
67
|
+
action_category: request.action_category as ActionCategory,
|
|
68
|
+
authority_decision: 'approval_required',
|
|
69
|
+
approval_id: requestId,
|
|
70
|
+
executed: true,
|
|
71
|
+
execution_time_ms: executionTimeMs,
|
|
72
|
+
});
|
|
73
|
+
|
|
74
|
+
// Record approval for learning
|
|
75
|
+
this.learner?.recordDecision(
|
|
76
|
+
request.action_category as ActionCategory,
|
|
77
|
+
request.tool_name,
|
|
78
|
+
true
|
|
79
|
+
);
|
|
80
|
+
|
|
81
|
+
// Notify
|
|
82
|
+
this.onResult?.(requestId, request, result);
|
|
83
|
+
|
|
84
|
+
return result;
|
|
85
|
+
} catch (err) {
|
|
86
|
+
const errorStr = `Error executing ${request.tool_name}: ${err instanceof Error ? err.message : String(err)}`;
|
|
87
|
+
this.approvalManager.markExecuted(requestId, errorStr);
|
|
88
|
+
this.onResult?.(requestId, request, errorStr);
|
|
89
|
+
return errorStr;
|
|
90
|
+
}
|
|
91
|
+
}
|
|
92
|
+
|
|
93
|
+
/**
|
|
94
|
+
* Handle a denial — record for learning.
|
|
95
|
+
*/
|
|
96
|
+
recordDenial(request: ApprovalRequest): void {
|
|
97
|
+
this.learner?.recordDecision(
|
|
98
|
+
request.action_category as ActionCategory,
|
|
99
|
+
request.tool_name,
|
|
100
|
+
false
|
|
101
|
+
);
|
|
102
|
+
}
|
|
103
|
+
}
|
|
@@ -0,0 +1,66 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Emergency controls: pause and kill switch.
|
|
3
|
+
*/
|
|
4
|
+
|
|
5
|
+
export type EmergencyState = 'normal' | 'paused' | 'killed';
|
|
6
|
+
|
|
7
|
+
export class EmergencyController {
|
|
8
|
+
private state: EmergencyState = 'normal';
|
|
9
|
+
private onStateChange: ((state: EmergencyState) => void) | null = null;
|
|
10
|
+
|
|
11
|
+
getState(): EmergencyState {
|
|
12
|
+
return this.state;
|
|
13
|
+
}
|
|
14
|
+
|
|
15
|
+
/**
|
|
16
|
+
* Pause: freeze all agent tool execution. Agents can still receive messages
|
|
17
|
+
* but all tools return [SYSTEM PAUSED].
|
|
18
|
+
*/
|
|
19
|
+
pause(): void {
|
|
20
|
+
if (this.state === 'killed') return; // Can't pause from killed
|
|
21
|
+
this.state = 'paused';
|
|
22
|
+
this.onStateChange?.(this.state);
|
|
23
|
+
console.log('[EmergencyController] System PAUSED — all tool execution suspended');
|
|
24
|
+
}
|
|
25
|
+
|
|
26
|
+
/**
|
|
27
|
+
* Resume from paused state.
|
|
28
|
+
*/
|
|
29
|
+
resume(): void {
|
|
30
|
+
if (this.state !== 'paused') return;
|
|
31
|
+
this.state = 'normal';
|
|
32
|
+
this.onStateChange?.(this.state);
|
|
33
|
+
console.log('[EmergencyController] System RESUMED — tool execution restored');
|
|
34
|
+
}
|
|
35
|
+
|
|
36
|
+
/**
|
|
37
|
+
* Kill: terminate all agents, cancel all pending.
|
|
38
|
+
* Requires explicit reset() to recover.
|
|
39
|
+
*/
|
|
40
|
+
kill(): void {
|
|
41
|
+
this.state = 'killed';
|
|
42
|
+
this.onStateChange?.(this.state);
|
|
43
|
+
console.log('[EmergencyController] System KILLED — all agents terminated');
|
|
44
|
+
}
|
|
45
|
+
|
|
46
|
+
/**
|
|
47
|
+
* Reset from killed state back to normal.
|
|
48
|
+
*/
|
|
49
|
+
reset(): void {
|
|
50
|
+
if (this.state !== 'killed') return;
|
|
51
|
+
this.state = 'normal';
|
|
52
|
+
this.onStateChange?.(this.state);
|
|
53
|
+
console.log('[EmergencyController] System RESET — back to normal');
|
|
54
|
+
}
|
|
55
|
+
|
|
56
|
+
/**
|
|
57
|
+
* Check if tool execution is allowed.
|
|
58
|
+
*/
|
|
59
|
+
canExecute(): boolean {
|
|
60
|
+
return this.state === 'normal';
|
|
61
|
+
}
|
|
62
|
+
|
|
63
|
+
setStateChangeCallback(cb: (state: EmergencyState) => void): void {
|
|
64
|
+
this.onStateChange = cb;
|
|
65
|
+
}
|
|
66
|
+
}
|
|
@@ -0,0 +1,297 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Authority Engine — Central decision maker for tool execution authorization.
|
|
3
|
+
*
|
|
4
|
+
* Decision order:
|
|
5
|
+
* 1. Temporary grants (parent escalation) → allow
|
|
6
|
+
* 2. Per-action overrides for this role → explicit allow/deny
|
|
7
|
+
* 3. Context rules (time-based, tool-name-based) → allow/deny/require_approval
|
|
8
|
+
* 4. Numeric level check: level >= AUTHORITY_REQUIREMENTS[action]
|
|
9
|
+
* 5. Governed category check: if allowed but governed → requiresApproval
|
|
10
|
+
*/
|
|
11
|
+
|
|
12
|
+
import type { ActionCategory } from '../roles/authority.ts';
|
|
13
|
+
import { AUTHORITY_REQUIREMENTS } from '../roles/authority.ts';
|
|
14
|
+
|
|
15
|
+
export type PerActionOverride = {
|
|
16
|
+
action: ActionCategory;
|
|
17
|
+
role_id?: string; // if unset, applies globally
|
|
18
|
+
allowed: boolean; // true = always allow, false = always deny
|
|
19
|
+
requires_approval?: boolean; // if true, soft gate even when allowed
|
|
20
|
+
};
|
|
21
|
+
|
|
22
|
+
export type ContextRule = {
|
|
23
|
+
id: string;
|
|
24
|
+
action: ActionCategory;
|
|
25
|
+
condition: 'time_range' | 'tool_name' | 'always';
|
|
26
|
+
params: Record<string, unknown>;
|
|
27
|
+
effect: 'allow' | 'deny' | 'require_approval';
|
|
28
|
+
description: string;
|
|
29
|
+
};
|
|
30
|
+
|
|
31
|
+
export type AuthorityConfig = {
|
|
32
|
+
default_level: number;
|
|
33
|
+
governed_categories: ActionCategory[];
|
|
34
|
+
overrides: PerActionOverride[];
|
|
35
|
+
context_rules: ContextRule[];
|
|
36
|
+
learning: {
|
|
37
|
+
enabled: boolean;
|
|
38
|
+
suggest_threshold: number;
|
|
39
|
+
};
|
|
40
|
+
emergency_state: 'normal' | 'paused' | 'killed';
|
|
41
|
+
};
|
|
42
|
+
|
|
43
|
+
export type AuthorityDecision = {
|
|
44
|
+
allowed: boolean;
|
|
45
|
+
requiresApproval: boolean;
|
|
46
|
+
reason: string;
|
|
47
|
+
actionCategory: ActionCategory;
|
|
48
|
+
contextRule?: string;
|
|
49
|
+
};
|
|
50
|
+
|
|
51
|
+
export type AuthorityCheckParams = {
|
|
52
|
+
agentId: string;
|
|
53
|
+
agentAuthorityLevel: number;
|
|
54
|
+
agentRoleId: string;
|
|
55
|
+
toolName: string;
|
|
56
|
+
toolCategory: string;
|
|
57
|
+
actionCategory: ActionCategory;
|
|
58
|
+
temporaryGrants: Map<string, ActionCategory[]>;
|
|
59
|
+
};
|
|
60
|
+
|
|
61
|
+
export class AuthorityEngine {
|
|
62
|
+
private config: AuthorityConfig;
|
|
63
|
+
|
|
64
|
+
constructor(config: AuthorityConfig) {
|
|
65
|
+
this.config = config;
|
|
66
|
+
}
|
|
67
|
+
|
|
68
|
+
/**
|
|
69
|
+
* Core decision function — determines if an action is allowed.
|
|
70
|
+
*/
|
|
71
|
+
checkAuthority(params: AuthorityCheckParams): AuthorityDecision {
|
|
72
|
+
const { agentId, agentAuthorityLevel, agentRoleId, toolName, actionCategory, temporaryGrants } = params;
|
|
73
|
+
|
|
74
|
+
// 1. Check temporary grants (parent escalation)
|
|
75
|
+
const grants = temporaryGrants.get(agentId);
|
|
76
|
+
if (grants?.includes(actionCategory)) {
|
|
77
|
+
return {
|
|
78
|
+
allowed: true,
|
|
79
|
+
requiresApproval: false,
|
|
80
|
+
reason: 'Temporarily granted by parent agent',
|
|
81
|
+
actionCategory,
|
|
82
|
+
};
|
|
83
|
+
}
|
|
84
|
+
|
|
85
|
+
// 2. Check per-action overrides for this role
|
|
86
|
+
const override = this.findOverride(actionCategory, agentRoleId);
|
|
87
|
+
if (override) {
|
|
88
|
+
if (!override.allowed) {
|
|
89
|
+
return {
|
|
90
|
+
allowed: false,
|
|
91
|
+
requiresApproval: false,
|
|
92
|
+
reason: `Explicitly denied by override for ${agentRoleId || 'global'}`,
|
|
93
|
+
actionCategory,
|
|
94
|
+
};
|
|
95
|
+
}
|
|
96
|
+
if (override.requires_approval) {
|
|
97
|
+
return {
|
|
98
|
+
allowed: true,
|
|
99
|
+
requiresApproval: true,
|
|
100
|
+
reason: `Override requires approval for ${actionCategory}`,
|
|
101
|
+
actionCategory,
|
|
102
|
+
};
|
|
103
|
+
}
|
|
104
|
+
return {
|
|
105
|
+
allowed: true,
|
|
106
|
+
requiresApproval: false,
|
|
107
|
+
reason: `Explicitly allowed by override for ${agentRoleId || 'global'}`,
|
|
108
|
+
actionCategory,
|
|
109
|
+
};
|
|
110
|
+
}
|
|
111
|
+
|
|
112
|
+
// 3. Check context rules
|
|
113
|
+
const contextResult = this.evaluateContextRules(actionCategory, toolName);
|
|
114
|
+
if (contextResult) {
|
|
115
|
+
if (contextResult.effect === 'deny') {
|
|
116
|
+
return {
|
|
117
|
+
allowed: false,
|
|
118
|
+
requiresApproval: false,
|
|
119
|
+
reason: contextResult.description,
|
|
120
|
+
actionCategory,
|
|
121
|
+
contextRule: contextResult.id,
|
|
122
|
+
};
|
|
123
|
+
}
|
|
124
|
+
if (contextResult.effect === 'require_approval') {
|
|
125
|
+
return {
|
|
126
|
+
allowed: true,
|
|
127
|
+
requiresApproval: true,
|
|
128
|
+
reason: contextResult.description,
|
|
129
|
+
actionCategory,
|
|
130
|
+
contextRule: contextResult.id,
|
|
131
|
+
};
|
|
132
|
+
}
|
|
133
|
+
if (contextResult.effect === 'allow') {
|
|
134
|
+
return {
|
|
135
|
+
allowed: true,
|
|
136
|
+
requiresApproval: false,
|
|
137
|
+
reason: contextResult.description,
|
|
138
|
+
actionCategory,
|
|
139
|
+
contextRule: contextResult.id,
|
|
140
|
+
};
|
|
141
|
+
}
|
|
142
|
+
}
|
|
143
|
+
|
|
144
|
+
// 4. Numeric level check
|
|
145
|
+
const requiredLevel = AUTHORITY_REQUIREMENTS[actionCategory];
|
|
146
|
+
if (agentAuthorityLevel < requiredLevel) {
|
|
147
|
+
return {
|
|
148
|
+
allowed: false,
|
|
149
|
+
requiresApproval: false,
|
|
150
|
+
reason: `Authority level ${agentAuthorityLevel} is below required ${requiredLevel} for ${actionCategory}`,
|
|
151
|
+
actionCategory,
|
|
152
|
+
};
|
|
153
|
+
}
|
|
154
|
+
|
|
155
|
+
// 5. Governed category check — if level is sufficient but action is governed, require approval
|
|
156
|
+
if (this.config.governed_categories.includes(actionCategory)) {
|
|
157
|
+
return {
|
|
158
|
+
allowed: true,
|
|
159
|
+
requiresApproval: true,
|
|
160
|
+
reason: `${actionCategory} is a governed action requiring user approval`,
|
|
161
|
+
actionCategory,
|
|
162
|
+
};
|
|
163
|
+
}
|
|
164
|
+
|
|
165
|
+
// Allowed without approval
|
|
166
|
+
return {
|
|
167
|
+
allowed: true,
|
|
168
|
+
requiresApproval: false,
|
|
169
|
+
reason: `Authority level ${agentAuthorityLevel} meets requirement ${requiredLevel}`,
|
|
170
|
+
actionCategory,
|
|
171
|
+
};
|
|
172
|
+
}
|
|
173
|
+
|
|
174
|
+
/**
|
|
175
|
+
* Generate human-readable authority rules for the system prompt.
|
|
176
|
+
*/
|
|
177
|
+
describeRulesForAgent(authorityLevel: number, roleId: string): string {
|
|
178
|
+
const lines: string[] = [];
|
|
179
|
+
|
|
180
|
+
lines.push(`Your authority level: ${authorityLevel}/10`);
|
|
181
|
+
lines.push('');
|
|
182
|
+
|
|
183
|
+
// Governed categories
|
|
184
|
+
if (this.config.governed_categories.length > 0) {
|
|
185
|
+
lines.push('Actions requiring user approval before execution:');
|
|
186
|
+
for (const cat of this.config.governed_categories) {
|
|
187
|
+
lines.push(` - ${cat}`);
|
|
188
|
+
}
|
|
189
|
+
lines.push('');
|
|
190
|
+
}
|
|
191
|
+
|
|
192
|
+
// Active overrides for this role
|
|
193
|
+
const roleOverrides = this.config.overrides.filter(
|
|
194
|
+
o => !o.role_id || o.role_id === roleId
|
|
195
|
+
);
|
|
196
|
+
if (roleOverrides.length > 0) {
|
|
197
|
+
lines.push('Special permission overrides:');
|
|
198
|
+
for (const o of roleOverrides) {
|
|
199
|
+
const scope = o.role_id ? `[${o.role_id}]` : '[global]';
|
|
200
|
+
const status = o.allowed
|
|
201
|
+
? (o.requires_approval ? 'allowed with approval' : 'always allowed')
|
|
202
|
+
: 'denied';
|
|
203
|
+
lines.push(` - ${o.action}: ${status} ${scope}`);
|
|
204
|
+
}
|
|
205
|
+
lines.push('');
|
|
206
|
+
}
|
|
207
|
+
|
|
208
|
+
// Active context rules
|
|
209
|
+
if (this.config.context_rules.length > 0) {
|
|
210
|
+
lines.push('Context-based rules:');
|
|
211
|
+
for (const rule of this.config.context_rules) {
|
|
212
|
+
lines.push(` - ${rule.description}`);
|
|
213
|
+
}
|
|
214
|
+
}
|
|
215
|
+
|
|
216
|
+
return lines.join('\n');
|
|
217
|
+
}
|
|
218
|
+
|
|
219
|
+
// --- Config management ---
|
|
220
|
+
|
|
221
|
+
addOverride(override: PerActionOverride): void {
|
|
222
|
+
// Remove existing override for same action+role before adding
|
|
223
|
+
this.removeOverride(override.action, override.role_id);
|
|
224
|
+
this.config.overrides.push(override);
|
|
225
|
+
}
|
|
226
|
+
|
|
227
|
+
removeOverride(action: ActionCategory, roleId?: string): void {
|
|
228
|
+
this.config.overrides = this.config.overrides.filter(
|
|
229
|
+
o => !(o.action === action && o.role_id === roleId)
|
|
230
|
+
);
|
|
231
|
+
}
|
|
232
|
+
|
|
233
|
+
addContextRule(rule: ContextRule): void {
|
|
234
|
+
this.config.context_rules.push(rule);
|
|
235
|
+
}
|
|
236
|
+
|
|
237
|
+
removeContextRule(id: string): void {
|
|
238
|
+
this.config.context_rules = this.config.context_rules.filter(r => r.id !== id);
|
|
239
|
+
}
|
|
240
|
+
|
|
241
|
+
setGovernedCategories(categories: ActionCategory[]): void {
|
|
242
|
+
this.config.governed_categories = categories;
|
|
243
|
+
}
|
|
244
|
+
|
|
245
|
+
getConfig(): AuthorityConfig {
|
|
246
|
+
return { ...this.config };
|
|
247
|
+
}
|
|
248
|
+
|
|
249
|
+
updateConfig(config: AuthorityConfig): void {
|
|
250
|
+
this.config = config;
|
|
251
|
+
}
|
|
252
|
+
|
|
253
|
+
// --- Private helpers ---
|
|
254
|
+
|
|
255
|
+
private findOverride(action: ActionCategory, roleId: string): PerActionOverride | null {
|
|
256
|
+
// Role-specific override takes priority over global
|
|
257
|
+
const roleSpecific = this.config.overrides.find(
|
|
258
|
+
o => o.action === action && o.role_id === roleId
|
|
259
|
+
);
|
|
260
|
+
if (roleSpecific) return roleSpecific;
|
|
261
|
+
|
|
262
|
+
const global = this.config.overrides.find(
|
|
263
|
+
o => o.action === action && !o.role_id
|
|
264
|
+
);
|
|
265
|
+
return global ?? null;
|
|
266
|
+
}
|
|
267
|
+
|
|
268
|
+
private evaluateContextRules(action: ActionCategory, toolName: string): ContextRule | null {
|
|
269
|
+
for (const rule of this.config.context_rules) {
|
|
270
|
+
if (rule.action !== action) continue;
|
|
271
|
+
|
|
272
|
+
switch (rule.condition) {
|
|
273
|
+
case 'time_range': {
|
|
274
|
+
const now = new Date();
|
|
275
|
+
const hour = now.getHours();
|
|
276
|
+
const startHour = (rule.params.start_hour as number) ?? 0;
|
|
277
|
+
const endHour = (rule.params.end_hour as number) ?? 24;
|
|
278
|
+
if (hour >= startHour && hour < endHour) {
|
|
279
|
+
return rule;
|
|
280
|
+
}
|
|
281
|
+
break;
|
|
282
|
+
}
|
|
283
|
+
case 'tool_name': {
|
|
284
|
+
const toolPattern = rule.params.tool_name as string;
|
|
285
|
+
if (toolPattern && (toolName === toolPattern || toolName.startsWith(toolPattern))) {
|
|
286
|
+
return rule;
|
|
287
|
+
}
|
|
288
|
+
break;
|
|
289
|
+
}
|
|
290
|
+
case 'always': {
|
|
291
|
+
return rule;
|
|
292
|
+
}
|
|
293
|
+
}
|
|
294
|
+
}
|
|
295
|
+
return null;
|
|
296
|
+
}
|
|
297
|
+
}
|
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Authority & Autonomy Engine — Barrel exports
|
|
3
|
+
*/
|
|
4
|
+
|
|
5
|
+
export { AuthorityEngine, type AuthorityConfig, type AuthorityDecision, type AuthorityCheckParams, type PerActionOverride, type ContextRule } from './engine.ts';
|
|
6
|
+
export { ApprovalManager, type ApprovalRequest, type ApprovalStatus, type ApprovalUrgency } from './approval.ts';
|
|
7
|
+
export { AuditTrail, type AuditEntry, type AuthorityDecisionType } from './audit.ts';
|
|
8
|
+
export { AuthorityLearner } from './learning.ts';
|
|
9
|
+
export { EmergencyController, type EmergencyState } from './emergency.ts';
|
|
10
|
+
export { ApprovalDelivery } from './approval-delivery.ts';
|
|
11
|
+
export { DeferredExecutor } from './deferred-executor.ts';
|
|
12
|
+
export { getActionForTool, TOOL_ACTION_MAP, CATEGORY_ACTION_MAP } from './tool-action-map.ts';
|
|
@@ -0,0 +1,111 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Authority Learner — Tracks approval patterns and suggests auto-approve rules.
|
|
3
|
+
*
|
|
4
|
+
* After N consecutive approvals of the same action+tool pattern,
|
|
5
|
+
* suggests adding a persistent override so the user doesn't have to
|
|
6
|
+
* keep approving the same thing.
|
|
7
|
+
*/
|
|
8
|
+
|
|
9
|
+
import { getDb, generateId } from '../vault/schema.ts';
|
|
10
|
+
import type { ActionCategory } from '../roles/authority.ts';
|
|
11
|
+
import type { PerActionOverride } from './engine.ts';
|
|
12
|
+
|
|
13
|
+
const DEFAULT_SUGGEST_THRESHOLD = 5;
|
|
14
|
+
|
|
15
|
+
export class AuthorityLearner {
|
|
16
|
+
private threshold: number;
|
|
17
|
+
|
|
18
|
+
constructor(threshold: number = DEFAULT_SUGGEST_THRESHOLD) {
|
|
19
|
+
this.threshold = threshold;
|
|
20
|
+
}
|
|
21
|
+
|
|
22
|
+
/**
|
|
23
|
+
* Record an approval or denial decision.
|
|
24
|
+
* Approvals increment the consecutive count; denials reset it.
|
|
25
|
+
*/
|
|
26
|
+
recordDecision(actionCategory: ActionCategory, toolName: string, approved: boolean): void {
|
|
27
|
+
const db = getDb();
|
|
28
|
+
const now = Date.now();
|
|
29
|
+
|
|
30
|
+
if (approved) {
|
|
31
|
+
// Try to increment existing pattern
|
|
32
|
+
const result = db.run(
|
|
33
|
+
`UPDATE approval_patterns
|
|
34
|
+
SET consecutive_approvals = consecutive_approvals + 1, last_approval_at = ?
|
|
35
|
+
WHERE action_category = ? AND tool_name = ?`,
|
|
36
|
+
[now, actionCategory, toolName]
|
|
37
|
+
);
|
|
38
|
+
|
|
39
|
+
// Create pattern if it doesn't exist
|
|
40
|
+
if (result.changes === 0) {
|
|
41
|
+
db.run(
|
|
42
|
+
`INSERT INTO approval_patterns (id, action_category, tool_name, consecutive_approvals, last_approval_at, suggestion_sent)
|
|
43
|
+
VALUES (?, ?, ?, 1, ?, 0)`,
|
|
44
|
+
[generateId(), actionCategory, toolName, now]
|
|
45
|
+
);
|
|
46
|
+
}
|
|
47
|
+
} else {
|
|
48
|
+
// Denial resets the consecutive count
|
|
49
|
+
db.run(
|
|
50
|
+
`UPDATE approval_patterns
|
|
51
|
+
SET consecutive_approvals = 0, suggestion_sent = 0
|
|
52
|
+
WHERE action_category = ? AND tool_name = ?`,
|
|
53
|
+
[actionCategory, toolName]
|
|
54
|
+
);
|
|
55
|
+
}
|
|
56
|
+
}
|
|
57
|
+
|
|
58
|
+
/**
|
|
59
|
+
* Get patterns that have crossed the suggestion threshold.
|
|
60
|
+
*/
|
|
61
|
+
getSuggestions(): Array<{
|
|
62
|
+
actionCategory: ActionCategory;
|
|
63
|
+
toolName: string;
|
|
64
|
+
consecutiveApprovals: number;
|
|
65
|
+
suggestedRule: PerActionOverride;
|
|
66
|
+
}> {
|
|
67
|
+
const db = getDb();
|
|
68
|
+
const rows = db.query(
|
|
69
|
+
`SELECT * FROM approval_patterns
|
|
70
|
+
WHERE consecutive_approvals >= ? AND suggestion_sent = 0
|
|
71
|
+
ORDER BY consecutive_approvals DESC`
|
|
72
|
+
).all(this.threshold) as Array<{
|
|
73
|
+
action_category: string;
|
|
74
|
+
tool_name: string;
|
|
75
|
+
consecutive_approvals: number;
|
|
76
|
+
}>;
|
|
77
|
+
|
|
78
|
+
return rows.map(row => ({
|
|
79
|
+
actionCategory: row.action_category as ActionCategory,
|
|
80
|
+
toolName: row.tool_name,
|
|
81
|
+
consecutiveApprovals: row.consecutive_approvals,
|
|
82
|
+
suggestedRule: {
|
|
83
|
+
action: row.action_category as ActionCategory,
|
|
84
|
+
allowed: true,
|
|
85
|
+
requires_approval: false, // Auto-approve
|
|
86
|
+
},
|
|
87
|
+
}));
|
|
88
|
+
}
|
|
89
|
+
|
|
90
|
+
/**
|
|
91
|
+
* Mark a suggestion as sent so we don't re-suggest.
|
|
92
|
+
*/
|
|
93
|
+
markSuggestionSent(actionCategory: ActionCategory, toolName: string): void {
|
|
94
|
+
const db = getDb();
|
|
95
|
+
db.run(
|
|
96
|
+
`UPDATE approval_patterns SET suggestion_sent = 1 WHERE action_category = ? AND tool_name = ?`,
|
|
97
|
+
[actionCategory, toolName]
|
|
98
|
+
);
|
|
99
|
+
}
|
|
100
|
+
|
|
101
|
+
/**
|
|
102
|
+
* Reset a pattern (e.g., when user dismisses a suggestion).
|
|
103
|
+
*/
|
|
104
|
+
resetPattern(actionCategory: ActionCategory, toolName: string): void {
|
|
105
|
+
const db = getDb();
|
|
106
|
+
db.run(
|
|
107
|
+
`UPDATE approval_patterns SET consecutive_approvals = 0, suggestion_sent = 0 WHERE action_category = ? AND tool_name = ?`,
|
|
108
|
+
[actionCategory, toolName]
|
|
109
|
+
);
|
|
110
|
+
}
|
|
111
|
+
}
|
|
@@ -0,0 +1,74 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Maps tool names and categories to ActionCategory for authority checks.
|
|
3
|
+
*/
|
|
4
|
+
|
|
5
|
+
import type { ActionCategory } from '../roles/authority.ts';
|
|
6
|
+
|
|
7
|
+
/**
|
|
8
|
+
* Explicit mapping from tool name -> ActionCategory
|
|
9
|
+
*/
|
|
10
|
+
export const TOOL_ACTION_MAP: Record<string, ActionCategory> = {
|
|
11
|
+
// Terminal
|
|
12
|
+
run_command: 'execute_command',
|
|
13
|
+
|
|
14
|
+
// File ops
|
|
15
|
+
read_file: 'read_data',
|
|
16
|
+
write_file: 'write_data',
|
|
17
|
+
list_directory: 'read_data',
|
|
18
|
+
|
|
19
|
+
// Browser
|
|
20
|
+
browser_navigate: 'access_browser',
|
|
21
|
+
browser_snapshot: 'access_browser',
|
|
22
|
+
browser_click: 'access_browser',
|
|
23
|
+
browser_type: 'access_browser',
|
|
24
|
+
browser_scroll: 'access_browser',
|
|
25
|
+
browser_evaluate: 'access_browser',
|
|
26
|
+
browser_screenshot: 'access_browser',
|
|
27
|
+
|
|
28
|
+
// Desktop
|
|
29
|
+
desktop_list_windows: 'control_app',
|
|
30
|
+
desktop_focus_window: 'control_app',
|
|
31
|
+
desktop_snapshot: 'control_app',
|
|
32
|
+
desktop_click: 'control_app',
|
|
33
|
+
desktop_type: 'control_app',
|
|
34
|
+
desktop_press_keys: 'control_app',
|
|
35
|
+
desktop_launch_app: 'control_app',
|
|
36
|
+
desktop_screenshot: 'control_app',
|
|
37
|
+
|
|
38
|
+
// Delegation
|
|
39
|
+
delegate_task: 'spawn_agent',
|
|
40
|
+
manage_agents: 'spawn_agent',
|
|
41
|
+
|
|
42
|
+
// Content / tasks
|
|
43
|
+
content_pipeline: 'write_data',
|
|
44
|
+
commitments: 'write_data',
|
|
45
|
+
research_queue: 'read_data',
|
|
46
|
+
};
|
|
47
|
+
|
|
48
|
+
/**
|
|
49
|
+
* Fallback mapping from tool category -> ActionCategory
|
|
50
|
+
*/
|
|
51
|
+
export const CATEGORY_ACTION_MAP: Record<string, ActionCategory> = {
|
|
52
|
+
terminal: 'execute_command',
|
|
53
|
+
'file-ops': 'write_data',
|
|
54
|
+
browser: 'access_browser',
|
|
55
|
+
desktop: 'control_app',
|
|
56
|
+
delegation: 'spawn_agent',
|
|
57
|
+
content: 'write_data',
|
|
58
|
+
tasks: 'write_data',
|
|
59
|
+
productivity: 'read_data',
|
|
60
|
+
};
|
|
61
|
+
|
|
62
|
+
/**
|
|
63
|
+
* Resolve the ActionCategory for a given tool.
|
|
64
|
+
* Checks explicit tool name map first, then falls back to category map, then defaults to read_data.
|
|
65
|
+
*/
|
|
66
|
+
export function getActionForTool(toolName: string, toolCategory: string): ActionCategory {
|
|
67
|
+
if (TOOL_ACTION_MAP[toolName]) {
|
|
68
|
+
return TOOL_ACTION_MAP[toolName];
|
|
69
|
+
}
|
|
70
|
+
if (CATEGORY_ACTION_MAP[toolCategory]) {
|
|
71
|
+
return CATEGORY_ACTION_MAP[toolCategory];
|
|
72
|
+
}
|
|
73
|
+
return 'read_data';
|
|
74
|
+
}
|