@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.
Files changed (266) hide show
  1. package/LICENSE +153 -0
  2. package/README.md +278 -0
  3. package/bin/jarvis.ts +413 -0
  4. package/package.json +74 -0
  5. package/scripts/ensure-bun.cjs +8 -0
  6. package/src/actions/README.md +421 -0
  7. package/src/actions/app-control/desktop-controller.test.ts +26 -0
  8. package/src/actions/app-control/desktop-controller.ts +438 -0
  9. package/src/actions/app-control/interface.ts +64 -0
  10. package/src/actions/app-control/linux.ts +273 -0
  11. package/src/actions/app-control/macos.ts +54 -0
  12. package/src/actions/app-control/sidecar-launcher.test.ts +23 -0
  13. package/src/actions/app-control/sidecar-launcher.ts +286 -0
  14. package/src/actions/app-control/windows.ts +44 -0
  15. package/src/actions/browser/cdp.ts +138 -0
  16. package/src/actions/browser/chrome-launcher.ts +252 -0
  17. package/src/actions/browser/session.ts +437 -0
  18. package/src/actions/browser/stealth.ts +49 -0
  19. package/src/actions/index.ts +20 -0
  20. package/src/actions/terminal/executor.ts +157 -0
  21. package/src/actions/terminal/wsl-bridge.ts +126 -0
  22. package/src/actions/test.ts +93 -0
  23. package/src/actions/tools/agents.ts +321 -0
  24. package/src/actions/tools/builtin.ts +846 -0
  25. package/src/actions/tools/commitments.ts +192 -0
  26. package/src/actions/tools/content.ts +217 -0
  27. package/src/actions/tools/delegate.ts +147 -0
  28. package/src/actions/tools/desktop.test.ts +55 -0
  29. package/src/actions/tools/desktop.ts +305 -0
  30. package/src/actions/tools/goals.ts +376 -0
  31. package/src/actions/tools/local-tools-guard.ts +20 -0
  32. package/src/actions/tools/registry.ts +171 -0
  33. package/src/actions/tools/research.ts +111 -0
  34. package/src/actions/tools/sidecar-list.ts +57 -0
  35. package/src/actions/tools/sidecar-route.ts +105 -0
  36. package/src/actions/tools/workflows.ts +216 -0
  37. package/src/agents/agent.ts +132 -0
  38. package/src/agents/delegation.ts +107 -0
  39. package/src/agents/hierarchy.ts +113 -0
  40. package/src/agents/index.ts +19 -0
  41. package/src/agents/messaging.ts +125 -0
  42. package/src/agents/orchestrator.ts +576 -0
  43. package/src/agents/role-discovery.ts +61 -0
  44. package/src/agents/sub-agent-runner.ts +307 -0
  45. package/src/agents/task-manager.ts +151 -0
  46. package/src/authority/approval-delivery.ts +59 -0
  47. package/src/authority/approval.ts +196 -0
  48. package/src/authority/audit.ts +158 -0
  49. package/src/authority/authority.test.ts +519 -0
  50. package/src/authority/deferred-executor.ts +103 -0
  51. package/src/authority/emergency.ts +66 -0
  52. package/src/authority/engine.ts +297 -0
  53. package/src/authority/index.ts +12 -0
  54. package/src/authority/learning.ts +111 -0
  55. package/src/authority/tool-action-map.ts +74 -0
  56. package/src/awareness/analytics.ts +466 -0
  57. package/src/awareness/awareness.test.ts +332 -0
  58. package/src/awareness/capture-engine.ts +305 -0
  59. package/src/awareness/context-graph.ts +130 -0
  60. package/src/awareness/context-tracker.ts +349 -0
  61. package/src/awareness/index.ts +25 -0
  62. package/src/awareness/intelligence.ts +321 -0
  63. package/src/awareness/ocr-engine.ts +88 -0
  64. package/src/awareness/service.ts +528 -0
  65. package/src/awareness/struggle-detector.ts +342 -0
  66. package/src/awareness/suggestion-engine.ts +476 -0
  67. package/src/awareness/types.ts +201 -0
  68. package/src/cli/autostart.ts +241 -0
  69. package/src/cli/deps.ts +449 -0
  70. package/src/cli/doctor.ts +230 -0
  71. package/src/cli/helpers.ts +401 -0
  72. package/src/cli/onboard.ts +580 -0
  73. package/src/comms/README.md +329 -0
  74. package/src/comms/auth-error.html +48 -0
  75. package/src/comms/channels/discord.ts +228 -0
  76. package/src/comms/channels/signal.ts +56 -0
  77. package/src/comms/channels/telegram.ts +316 -0
  78. package/src/comms/channels/whatsapp.ts +60 -0
  79. package/src/comms/channels.test.ts +173 -0
  80. package/src/comms/desktop-notify.ts +114 -0
  81. package/src/comms/example.ts +129 -0
  82. package/src/comms/index.ts +129 -0
  83. package/src/comms/streaming.ts +142 -0
  84. package/src/comms/voice.test.ts +152 -0
  85. package/src/comms/voice.ts +291 -0
  86. package/src/comms/websocket.test.ts +409 -0
  87. package/src/comms/websocket.ts +473 -0
  88. package/src/config/README.md +387 -0
  89. package/src/config/index.ts +6 -0
  90. package/src/config/loader.test.ts +137 -0
  91. package/src/config/loader.ts +142 -0
  92. package/src/config/types.ts +260 -0
  93. package/src/daemon/README.md +232 -0
  94. package/src/daemon/agent-service-interface.ts +9 -0
  95. package/src/daemon/agent-service.ts +600 -0
  96. package/src/daemon/api-routes.ts +2119 -0
  97. package/src/daemon/background-agent-service.ts +396 -0
  98. package/src/daemon/background-agent.test.ts +78 -0
  99. package/src/daemon/channel-service.ts +201 -0
  100. package/src/daemon/commitment-executor.ts +297 -0
  101. package/src/daemon/event-classifier.ts +239 -0
  102. package/src/daemon/event-coalescer.ts +123 -0
  103. package/src/daemon/event-reactor.ts +214 -0
  104. package/src/daemon/health.ts +220 -0
  105. package/src/daemon/index.ts +1004 -0
  106. package/src/daemon/llm-settings.ts +316 -0
  107. package/src/daemon/observer-service.ts +150 -0
  108. package/src/daemon/pid.ts +98 -0
  109. package/src/daemon/research-queue.ts +155 -0
  110. package/src/daemon/services.ts +175 -0
  111. package/src/daemon/ws-service.ts +788 -0
  112. package/src/goals/accountability.ts +240 -0
  113. package/src/goals/awareness-bridge.ts +185 -0
  114. package/src/goals/estimator.ts +185 -0
  115. package/src/goals/events.ts +28 -0
  116. package/src/goals/goals.test.ts +400 -0
  117. package/src/goals/integration.test.ts +329 -0
  118. package/src/goals/nl-builder.test.ts +220 -0
  119. package/src/goals/nl-builder.ts +256 -0
  120. package/src/goals/rhythm.test.ts +177 -0
  121. package/src/goals/rhythm.ts +275 -0
  122. package/src/goals/service.test.ts +135 -0
  123. package/src/goals/service.ts +348 -0
  124. package/src/goals/types.ts +106 -0
  125. package/src/goals/workflow-bridge.ts +96 -0
  126. package/src/integrations/google-api.ts +134 -0
  127. package/src/integrations/google-auth.ts +175 -0
  128. package/src/llm/README.md +291 -0
  129. package/src/llm/anthropic.ts +386 -0
  130. package/src/llm/gemini.ts +371 -0
  131. package/src/llm/index.ts +19 -0
  132. package/src/llm/manager.ts +153 -0
  133. package/src/llm/ollama.ts +307 -0
  134. package/src/llm/openai.ts +350 -0
  135. package/src/llm/provider.test.ts +231 -0
  136. package/src/llm/provider.ts +60 -0
  137. package/src/llm/test.ts +87 -0
  138. package/src/observers/README.md +278 -0
  139. package/src/observers/calendar.ts +113 -0
  140. package/src/observers/clipboard.ts +136 -0
  141. package/src/observers/email.ts +109 -0
  142. package/src/observers/example.ts +58 -0
  143. package/src/observers/file-watcher.ts +124 -0
  144. package/src/observers/index.ts +159 -0
  145. package/src/observers/notifications.ts +197 -0
  146. package/src/observers/observers.test.ts +203 -0
  147. package/src/observers/processes.ts +225 -0
  148. package/src/personality/README.md +61 -0
  149. package/src/personality/adapter.ts +196 -0
  150. package/src/personality/index.ts +20 -0
  151. package/src/personality/learner.ts +209 -0
  152. package/src/personality/model.ts +132 -0
  153. package/src/personality/personality.test.ts +236 -0
  154. package/src/roles/README.md +252 -0
  155. package/src/roles/authority.ts +119 -0
  156. package/src/roles/example-usage.ts +198 -0
  157. package/src/roles/index.ts +42 -0
  158. package/src/roles/loader.ts +143 -0
  159. package/src/roles/prompt-builder.ts +194 -0
  160. package/src/roles/test-multi.ts +102 -0
  161. package/src/roles/test-role.yaml +77 -0
  162. package/src/roles/test-utils.ts +93 -0
  163. package/src/roles/test.ts +106 -0
  164. package/src/roles/tool-guide.ts +190 -0
  165. package/src/roles/types.ts +36 -0
  166. package/src/roles/utils.ts +200 -0
  167. package/src/scripts/google-setup.ts +168 -0
  168. package/src/sidecar/connection.ts +179 -0
  169. package/src/sidecar/index.ts +6 -0
  170. package/src/sidecar/manager.ts +542 -0
  171. package/src/sidecar/protocol.ts +85 -0
  172. package/src/sidecar/rpc.ts +161 -0
  173. package/src/sidecar/scheduler.ts +136 -0
  174. package/src/sidecar/types.ts +112 -0
  175. package/src/sidecar/validator.ts +144 -0
  176. package/src/vault/README.md +110 -0
  177. package/src/vault/awareness.ts +341 -0
  178. package/src/vault/commitments.ts +299 -0
  179. package/src/vault/content-pipeline.ts +260 -0
  180. package/src/vault/conversations.ts +173 -0
  181. package/src/vault/entities.ts +180 -0
  182. package/src/vault/extractor.test.ts +356 -0
  183. package/src/vault/extractor.ts +345 -0
  184. package/src/vault/facts.ts +190 -0
  185. package/src/vault/goals.ts +477 -0
  186. package/src/vault/index.ts +87 -0
  187. package/src/vault/keychain.ts +99 -0
  188. package/src/vault/observations.ts +115 -0
  189. package/src/vault/relationships.ts +178 -0
  190. package/src/vault/retrieval.test.ts +126 -0
  191. package/src/vault/retrieval.ts +227 -0
  192. package/src/vault/schema.ts +658 -0
  193. package/src/vault/settings.ts +38 -0
  194. package/src/vault/vectors.ts +92 -0
  195. package/src/vault/workflows.ts +403 -0
  196. package/src/workflows/auto-suggest.ts +290 -0
  197. package/src/workflows/engine.ts +366 -0
  198. package/src/workflows/events.ts +24 -0
  199. package/src/workflows/executor.ts +207 -0
  200. package/src/workflows/nl-builder.ts +198 -0
  201. package/src/workflows/nodes/actions/agent-task.ts +73 -0
  202. package/src/workflows/nodes/actions/calendar-action.ts +85 -0
  203. package/src/workflows/nodes/actions/code-execution.ts +73 -0
  204. package/src/workflows/nodes/actions/discord.ts +77 -0
  205. package/src/workflows/nodes/actions/file-write.ts +73 -0
  206. package/src/workflows/nodes/actions/gmail.ts +69 -0
  207. package/src/workflows/nodes/actions/http-request.ts +117 -0
  208. package/src/workflows/nodes/actions/notification.ts +85 -0
  209. package/src/workflows/nodes/actions/run-tool.ts +55 -0
  210. package/src/workflows/nodes/actions/send-message.ts +82 -0
  211. package/src/workflows/nodes/actions/shell-command.ts +76 -0
  212. package/src/workflows/nodes/actions/telegram.ts +60 -0
  213. package/src/workflows/nodes/builtin.ts +119 -0
  214. package/src/workflows/nodes/error/error-handler.ts +37 -0
  215. package/src/workflows/nodes/error/fallback.ts +47 -0
  216. package/src/workflows/nodes/error/retry.ts +82 -0
  217. package/src/workflows/nodes/logic/delay.ts +42 -0
  218. package/src/workflows/nodes/logic/if-else.ts +41 -0
  219. package/src/workflows/nodes/logic/loop.ts +90 -0
  220. package/src/workflows/nodes/logic/merge.ts +38 -0
  221. package/src/workflows/nodes/logic/race.ts +40 -0
  222. package/src/workflows/nodes/logic/switch.ts +59 -0
  223. package/src/workflows/nodes/logic/template-render.ts +53 -0
  224. package/src/workflows/nodes/logic/variable-get.ts +37 -0
  225. package/src/workflows/nodes/logic/variable-set.ts +59 -0
  226. package/src/workflows/nodes/registry.ts +99 -0
  227. package/src/workflows/nodes/transform/aggregate.ts +99 -0
  228. package/src/workflows/nodes/transform/csv-parse.ts +70 -0
  229. package/src/workflows/nodes/transform/json-parse.ts +63 -0
  230. package/src/workflows/nodes/transform/map-filter.ts +84 -0
  231. package/src/workflows/nodes/transform/regex-match.ts +89 -0
  232. package/src/workflows/nodes/triggers/calendar.ts +33 -0
  233. package/src/workflows/nodes/triggers/clipboard.ts +32 -0
  234. package/src/workflows/nodes/triggers/cron.ts +40 -0
  235. package/src/workflows/nodes/triggers/email.ts +40 -0
  236. package/src/workflows/nodes/triggers/file-change.ts +45 -0
  237. package/src/workflows/nodes/triggers/git.ts +46 -0
  238. package/src/workflows/nodes/triggers/manual.ts +23 -0
  239. package/src/workflows/nodes/triggers/poll.ts +81 -0
  240. package/src/workflows/nodes/triggers/process.ts +44 -0
  241. package/src/workflows/nodes/triggers/screen-event.ts +37 -0
  242. package/src/workflows/nodes/triggers/webhook.ts +39 -0
  243. package/src/workflows/safe-eval.ts +139 -0
  244. package/src/workflows/template.ts +118 -0
  245. package/src/workflows/triggers/cron.ts +311 -0
  246. package/src/workflows/triggers/manager.ts +285 -0
  247. package/src/workflows/triggers/observer-bridge.ts +172 -0
  248. package/src/workflows/triggers/poller.ts +201 -0
  249. package/src/workflows/triggers/screen-condition.ts +218 -0
  250. package/src/workflows/triggers/triggers.test.ts +740 -0
  251. package/src/workflows/triggers/webhook.ts +191 -0
  252. package/src/workflows/types.ts +133 -0
  253. package/src/workflows/variables.ts +72 -0
  254. package/src/workflows/workflows.test.ts +383 -0
  255. package/src/workflows/yaml.ts +104 -0
  256. package/ui/dist/index-j75njzc1.css +1199 -0
  257. package/ui/dist/index-p2zh407q.js +80603 -0
  258. package/ui/dist/index.html +13 -0
  259. package/ui/public/openwakeword/models/embedding_model.onnx +0 -0
  260. package/ui/public/openwakeword/models/hey_jarvis_v0.1.onnx +0 -0
  261. package/ui/public/openwakeword/models/melspectrogram.onnx +0 -0
  262. package/ui/public/openwakeword/models/silero_vad.onnx +0 -0
  263. package/ui/public/ort/ort-wasm-simd-threaded.jsep.mjs +106 -0
  264. package/ui/public/ort/ort-wasm-simd-threaded.jsep.wasm +0 -0
  265. package/ui/public/ort/ort-wasm-simd-threaded.mjs +59 -0
  266. 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
+ }