@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,236 @@
|
|
|
1
|
+
import { test, expect, beforeEach, afterEach, describe } from 'bun:test';
|
|
2
|
+
import { initDatabase, closeDb } from '../vault/schema.ts';
|
|
3
|
+
import {
|
|
4
|
+
getPersonality,
|
|
5
|
+
savePersonality,
|
|
6
|
+
updatePersonality,
|
|
7
|
+
type PersonalityModel,
|
|
8
|
+
} from './model.ts';
|
|
9
|
+
import {
|
|
10
|
+
extractSignals,
|
|
11
|
+
applySignals,
|
|
12
|
+
recordInteraction,
|
|
13
|
+
} from './learner.ts';
|
|
14
|
+
import {
|
|
15
|
+
getChannelPersonality,
|
|
16
|
+
personalityToPrompt,
|
|
17
|
+
} from './adapter.ts';
|
|
18
|
+
|
|
19
|
+
describe('Personality Engine', () => {
|
|
20
|
+
beforeEach(() => {
|
|
21
|
+
// Initialize in-memory database for each test
|
|
22
|
+
initDatabase(':memory:');
|
|
23
|
+
});
|
|
24
|
+
|
|
25
|
+
afterEach(() => {
|
|
26
|
+
// Close database connection after each test
|
|
27
|
+
closeDb();
|
|
28
|
+
});
|
|
29
|
+
|
|
30
|
+
describe('Model', () => {
|
|
31
|
+
test('should get default personality', () => {
|
|
32
|
+
const personality = getPersonality();
|
|
33
|
+
|
|
34
|
+
expect(personality.core_traits).toEqual(['direct', 'strategic', 'resourceful']);
|
|
35
|
+
expect(personality.learned_preferences.verbosity).toBe(5);
|
|
36
|
+
expect(personality.learned_preferences.formality).toBe(5);
|
|
37
|
+
expect(personality.relationship.message_count).toBe(0);
|
|
38
|
+
});
|
|
39
|
+
|
|
40
|
+
test('should save and load personality', () => {
|
|
41
|
+
const personality = getPersonality();
|
|
42
|
+
personality.learned_preferences.verbosity = 8;
|
|
43
|
+
personality.relationship.message_count = 42;
|
|
44
|
+
|
|
45
|
+
savePersonality(personality);
|
|
46
|
+
|
|
47
|
+
const loaded = getPersonality();
|
|
48
|
+
expect(loaded.learned_preferences.verbosity).toBe(8);
|
|
49
|
+
expect(loaded.relationship.message_count).toBe(42);
|
|
50
|
+
});
|
|
51
|
+
|
|
52
|
+
test('should update personality with deep merge', () => {
|
|
53
|
+
const updated = updatePersonality({
|
|
54
|
+
learned_preferences: {
|
|
55
|
+
verbosity: 7,
|
|
56
|
+
},
|
|
57
|
+
});
|
|
58
|
+
|
|
59
|
+
expect(updated.learned_preferences.verbosity).toBe(7);
|
|
60
|
+
expect(updated.learned_preferences.formality).toBe(5); // unchanged
|
|
61
|
+
expect(updated.core_traits).toEqual(['direct', 'strategic', 'resourceful']); // unchanged
|
|
62
|
+
});
|
|
63
|
+
});
|
|
64
|
+
|
|
65
|
+
describe('Learner', () => {
|
|
66
|
+
test('should extract verbosity signals', () => {
|
|
67
|
+
const signals = extractSignals('Please keep it brief and concise', 'Here is a long response...');
|
|
68
|
+
|
|
69
|
+
expect(signals.length).toBeGreaterThan(0);
|
|
70
|
+
const verbositySignal = signals.find((s) => s.data.preference === 'verbosity');
|
|
71
|
+
expect(verbositySignal).toBeDefined();
|
|
72
|
+
expect(verbositySignal?.data.direction).toBe(-1);
|
|
73
|
+
});
|
|
74
|
+
|
|
75
|
+
test('should extract formality signals', () => {
|
|
76
|
+
const signals = extractSignals('Be more casual please', 'Understood.');
|
|
77
|
+
|
|
78
|
+
const formalitySignal = signals.find((s) => s.data.preference === 'formality');
|
|
79
|
+
expect(formalitySignal).toBeDefined();
|
|
80
|
+
expect(formalitySignal?.data.direction).toBe(-1);
|
|
81
|
+
});
|
|
82
|
+
|
|
83
|
+
test('should extract emoji usage signals', () => {
|
|
84
|
+
const signals = extractSignals('Great work! 🎉✨', 'Thank you!');
|
|
85
|
+
|
|
86
|
+
const emojiSignal = signals.find((s) => s.data.preference === 'emoji_usage');
|
|
87
|
+
expect(emojiSignal).toBeDefined();
|
|
88
|
+
expect(emojiSignal?.data.value).toBe(true);
|
|
89
|
+
});
|
|
90
|
+
|
|
91
|
+
test('should extract format preference signals', () => {
|
|
92
|
+
const signals = extractSignals('Can you show this as a table?', 'Sure!');
|
|
93
|
+
|
|
94
|
+
const formatSignal = signals.find((s) => s.data.preference === 'preferred_format');
|
|
95
|
+
expect(formatSignal).toBeDefined();
|
|
96
|
+
expect(formatSignal?.data.value).toBe('tables');
|
|
97
|
+
});
|
|
98
|
+
|
|
99
|
+
test('should apply signals to personality', () => {
|
|
100
|
+
const personality = getPersonality();
|
|
101
|
+
const initialVerbosity = personality.learned_preferences.verbosity;
|
|
102
|
+
const initialFormality = personality.learned_preferences.formality;
|
|
103
|
+
|
|
104
|
+
const signals = [
|
|
105
|
+
{ type: 'user_feedback' as const, data: { preference: 'verbosity', direction: 2 } },
|
|
106
|
+
{ type: 'explicit_preference' as const, data: { preference: 'formality', direction: -1 } },
|
|
107
|
+
];
|
|
108
|
+
|
|
109
|
+
const updated = applySignals(personality, signals);
|
|
110
|
+
|
|
111
|
+
expect(updated.learned_preferences.verbosity).toBe(initialVerbosity + 2);
|
|
112
|
+
expect(updated.learned_preferences.formality).toBe(initialFormality - 1);
|
|
113
|
+
});
|
|
114
|
+
|
|
115
|
+
test('should clamp values to 0-10 range', () => {
|
|
116
|
+
let personality = getPersonality();
|
|
117
|
+
personality.learned_preferences.verbosity = 9;
|
|
118
|
+
|
|
119
|
+
const signals = [
|
|
120
|
+
{ type: 'user_feedback' as const, data: { preference: 'verbosity', direction: 5 } },
|
|
121
|
+
];
|
|
122
|
+
|
|
123
|
+
personality = applySignals(personality, signals);
|
|
124
|
+
|
|
125
|
+
expect(personality.learned_preferences.verbosity).toBe(10); // clamped
|
|
126
|
+
});
|
|
127
|
+
|
|
128
|
+
test('should record interactions and increase trust', () => {
|
|
129
|
+
let personality = getPersonality();
|
|
130
|
+
const initialCount = personality.relationship.message_count;
|
|
131
|
+
|
|
132
|
+
// Simulate 50 interactions
|
|
133
|
+
for (let i = 0; i < 50; i++) {
|
|
134
|
+
personality = recordInteraction(personality);
|
|
135
|
+
}
|
|
136
|
+
|
|
137
|
+
expect(personality.relationship.message_count).toBe(initialCount + 50);
|
|
138
|
+
// Trust should increase based on message count
|
|
139
|
+
expect(personality.relationship.trust_level).toBeGreaterThan(3);
|
|
140
|
+
});
|
|
141
|
+
});
|
|
142
|
+
|
|
143
|
+
describe('Adapter', () => {
|
|
144
|
+
test('should adapt personality for WhatsApp', () => {
|
|
145
|
+
const base = getPersonality();
|
|
146
|
+
const adapted = getChannelPersonality(base, 'whatsapp');
|
|
147
|
+
|
|
148
|
+
expect(adapted.learned_preferences.emoji_usage).toBe(true);
|
|
149
|
+
expect(adapted.learned_preferences.verbosity).toBe(4);
|
|
150
|
+
expect(adapted.learned_preferences.formality).toBe(3);
|
|
151
|
+
});
|
|
152
|
+
|
|
153
|
+
test('should adapt personality for email', () => {
|
|
154
|
+
const base = getPersonality();
|
|
155
|
+
const adapted = getChannelPersonality(base, 'email');
|
|
156
|
+
|
|
157
|
+
expect(adapted.learned_preferences.emoji_usage).toBe(false);
|
|
158
|
+
expect(adapted.learned_preferences.verbosity).toBe(7);
|
|
159
|
+
expect(adapted.learned_preferences.formality).toBe(8);
|
|
160
|
+
});
|
|
161
|
+
|
|
162
|
+
test('should use stored channel overrides', () => {
|
|
163
|
+
const personality = getPersonality();
|
|
164
|
+
personality.channel_overrides.custom_channel = {
|
|
165
|
+
learned_preferences: {
|
|
166
|
+
verbosity: 10,
|
|
167
|
+
formality: 0,
|
|
168
|
+
humor_level: 10,
|
|
169
|
+
emoji_usage: true,
|
|
170
|
+
preferred_format: 'lists',
|
|
171
|
+
},
|
|
172
|
+
};
|
|
173
|
+
|
|
174
|
+
const adapted = getChannelPersonality(personality, 'custom_channel');
|
|
175
|
+
|
|
176
|
+
expect(adapted.learned_preferences.verbosity).toBe(10);
|
|
177
|
+
expect(adapted.learned_preferences.formality).toBe(0);
|
|
178
|
+
});
|
|
179
|
+
|
|
180
|
+
test('should generate personality prompt', () => {
|
|
181
|
+
const personality = getPersonality();
|
|
182
|
+
personality.relationship.message_count = 25;
|
|
183
|
+
personality.relationship.shared_references = ['Project X', 'Anna'];
|
|
184
|
+
|
|
185
|
+
const prompt = personalityToPrompt(personality);
|
|
186
|
+
|
|
187
|
+
expect(prompt).toContain('Personality');
|
|
188
|
+
expect(prompt).toContain('direct, strategic, resourceful');
|
|
189
|
+
expect(prompt).toContain('25 interactions');
|
|
190
|
+
expect(prompt).toContain('Project X, Anna');
|
|
191
|
+
});
|
|
192
|
+
});
|
|
193
|
+
|
|
194
|
+
describe('Integration', () => {
|
|
195
|
+
test('should handle full learning cycle', () => {
|
|
196
|
+
// Start with default personality
|
|
197
|
+
let personality = getPersonality();
|
|
198
|
+
const initialVerbosity = personality.learned_preferences.verbosity;
|
|
199
|
+
|
|
200
|
+
// User sends multiple messages asking for brevity
|
|
201
|
+
const userMessages = [
|
|
202
|
+
'Keep it short please',
|
|
203
|
+
'TLDR version?',
|
|
204
|
+
'Too long, can you summarize?',
|
|
205
|
+
];
|
|
206
|
+
|
|
207
|
+
userMessages.forEach((msg) => {
|
|
208
|
+
const signals = extractSignals(msg, 'Response');
|
|
209
|
+
personality = applySignals(personality, signals);
|
|
210
|
+
personality = recordInteraction(personality);
|
|
211
|
+
});
|
|
212
|
+
|
|
213
|
+
// Save the learned personality
|
|
214
|
+
savePersonality(personality);
|
|
215
|
+
|
|
216
|
+
// Reload and verify
|
|
217
|
+
const loaded = getPersonality();
|
|
218
|
+
// Verbosity should have decreased from initial value
|
|
219
|
+
expect(loaded.learned_preferences.verbosity).toBeLessThan(initialVerbosity);
|
|
220
|
+
// Message count should have increased by 3
|
|
221
|
+
expect(loaded.relationship.message_count).toBeGreaterThanOrEqual(3);
|
|
222
|
+
});
|
|
223
|
+
|
|
224
|
+
test('should adapt for channel and generate prompt', () => {
|
|
225
|
+
let personality = getPersonality();
|
|
226
|
+
personality.relationship.message_count = 100;
|
|
227
|
+
|
|
228
|
+
const whatsappPersonality = getChannelPersonality(personality, 'whatsapp');
|
|
229
|
+
const prompt = personalityToPrompt(whatsappPersonality);
|
|
230
|
+
|
|
231
|
+
expect(prompt).toContain('Personality');
|
|
232
|
+
expect(prompt).toContain('100 interactions');
|
|
233
|
+
expect(whatsappPersonality.learned_preferences.emoji_usage).toBe(true);
|
|
234
|
+
});
|
|
235
|
+
});
|
|
236
|
+
});
|
|
@@ -0,0 +1,252 @@
|
|
|
1
|
+
# Role Engine
|
|
2
|
+
|
|
3
|
+
The Role Engine is a core component of Project J.A.R.V.I.S. that defines, loads, and manages AI agent roles with their permissions, responsibilities, and behavior patterns.
|
|
4
|
+
|
|
5
|
+
## Overview
|
|
6
|
+
|
|
7
|
+
The Role Engine provides:
|
|
8
|
+
|
|
9
|
+
- **Role Definitions**: YAML-based role configuration with clear responsibilities and boundaries
|
|
10
|
+
- **Authority System**: 10-level permission system controlling what actions agents can perform
|
|
11
|
+
- **System Prompt Generation**: Automatic generation of detailed system prompts from role definitions
|
|
12
|
+
- **Validation**: Type-safe role loading with comprehensive validation
|
|
13
|
+
- **Multi-Role Management**: Load and manage multiple roles from a directory
|
|
14
|
+
|
|
15
|
+
## Architecture
|
|
16
|
+
|
|
17
|
+
```
|
|
18
|
+
src/roles/
|
|
19
|
+
├── types.ts # TypeScript type definitions
|
|
20
|
+
├── loader.ts # YAML loading and validation
|
|
21
|
+
├── prompt-builder.ts # System prompt generation
|
|
22
|
+
├── authority.ts # Permission and authority system
|
|
23
|
+
└── index.ts # Public API exports
|
|
24
|
+
```
|
|
25
|
+
|
|
26
|
+
## Usage
|
|
27
|
+
|
|
28
|
+
### Loading a Single Role
|
|
29
|
+
|
|
30
|
+
```typescript
|
|
31
|
+
import { loadRole } from './roles/index.ts';
|
|
32
|
+
|
|
33
|
+
const role = loadRole('/path/to/role.yaml');
|
|
34
|
+
console.log(role.name, role.authority_level);
|
|
35
|
+
```
|
|
36
|
+
|
|
37
|
+
### Loading Multiple Roles
|
|
38
|
+
|
|
39
|
+
```typescript
|
|
40
|
+
import { loadRolesFromDir } from './roles/index.ts';
|
|
41
|
+
|
|
42
|
+
const roles = loadRolesFromDir('/config/roles');
|
|
43
|
+
for (const [id, role] of roles) {
|
|
44
|
+
console.log(`${id}: ${role.name}`);
|
|
45
|
+
}
|
|
46
|
+
```
|
|
47
|
+
|
|
48
|
+
### Building System Prompts
|
|
49
|
+
|
|
50
|
+
```typescript
|
|
51
|
+
import { buildSystemPrompt } from './roles/index.ts';
|
|
52
|
+
|
|
53
|
+
const prompt = buildSystemPrompt(role, {
|
|
54
|
+
userName: 'John Doe',
|
|
55
|
+
currentTime: new Date().toLocaleString(),
|
|
56
|
+
activeCommitments: ['Finish report by Friday'],
|
|
57
|
+
recentObservations: ['User prefers morning meetings'],
|
|
58
|
+
agentHierarchy: 'Manager > Assistant (you) > Specialist',
|
|
59
|
+
});
|
|
60
|
+
```
|
|
61
|
+
|
|
62
|
+
### Checking Permissions
|
|
63
|
+
|
|
64
|
+
```typescript
|
|
65
|
+
import { canPerform, getRolePermissionsSummary } from './roles/index.ts';
|
|
66
|
+
|
|
67
|
+
// Check specific action
|
|
68
|
+
if (canPerform(role, 'execute_command')) {
|
|
69
|
+
console.log('Can execute commands');
|
|
70
|
+
}
|
|
71
|
+
|
|
72
|
+
// Get full permission summary
|
|
73
|
+
const summary = getRolePermissionsSummary(role);
|
|
74
|
+
console.log(summary.allowed); // List of allowed actions
|
|
75
|
+
console.log(summary.denied); // List of denied actions
|
|
76
|
+
```
|
|
77
|
+
|
|
78
|
+
## Role Definition Format
|
|
79
|
+
|
|
80
|
+
Roles are defined in YAML files with the following structure:
|
|
81
|
+
|
|
82
|
+
```yaml
|
|
83
|
+
id: unique_role_id
|
|
84
|
+
name: Human-Readable Role Name
|
|
85
|
+
description: Brief description of the role's purpose
|
|
86
|
+
|
|
87
|
+
responsibilities:
|
|
88
|
+
- First responsibility
|
|
89
|
+
- Second responsibility
|
|
90
|
+
|
|
91
|
+
autonomous_actions:
|
|
92
|
+
- Actions the agent can take without asking
|
|
93
|
+
|
|
94
|
+
approval_required:
|
|
95
|
+
- Actions that require user approval
|
|
96
|
+
|
|
97
|
+
kpis:
|
|
98
|
+
- name: KPI Name
|
|
99
|
+
metric: What to measure
|
|
100
|
+
target: Target value
|
|
101
|
+
check_interval: How often to check
|
|
102
|
+
|
|
103
|
+
communication_style:
|
|
104
|
+
tone: Description of communication tone
|
|
105
|
+
verbosity: concise | detailed | adaptive
|
|
106
|
+
formality: formal | casual | adaptive
|
|
107
|
+
|
|
108
|
+
heartbeat_instructions: |
|
|
109
|
+
Instructions for periodic check-ins.
|
|
110
|
+
What to monitor and when to notify the user.
|
|
111
|
+
|
|
112
|
+
sub_roles:
|
|
113
|
+
- role_id: sub_role_id
|
|
114
|
+
name: Sub-Role Name
|
|
115
|
+
description: What this sub-role does
|
|
116
|
+
spawned_by: parent_role_id
|
|
117
|
+
reports_to: parent_role_id
|
|
118
|
+
max_budget_per_task: 100
|
|
119
|
+
|
|
120
|
+
tools:
|
|
121
|
+
- tool_name_1
|
|
122
|
+
- tool_name_2
|
|
123
|
+
|
|
124
|
+
authority_level: 6 # 1-10
|
|
125
|
+
```
|
|
126
|
+
|
|
127
|
+
## Authority Levels
|
|
128
|
+
|
|
129
|
+
The authority system uses a 1-10 scale:
|
|
130
|
+
|
|
131
|
+
| Level | Capabilities |
|
|
132
|
+
|-------|-------------|
|
|
133
|
+
| 1-2 | **Read Only**: Can read data but cannot modify anything |
|
|
134
|
+
| 3-4 | **Read & Write**: Can read/write data and send messages |
|
|
135
|
+
| 5-6 | **Command Execution**: Can execute commands, control apps, access browser |
|
|
136
|
+
| 7-8 | **Agent Management**: Can spawn agents, send emails, install software |
|
|
137
|
+
| 9-10 | **Full Access**: Can make payments, modify settings, delete data, terminate agents |
|
|
138
|
+
|
|
139
|
+
### Action Categories
|
|
140
|
+
|
|
141
|
+
The following action categories are enforced:
|
|
142
|
+
|
|
143
|
+
**Level 1+**: `read_data`
|
|
144
|
+
|
|
145
|
+
**Level 3+**: `write_data`, `send_message`
|
|
146
|
+
|
|
147
|
+
**Level 5+**: `execute_command`, `access_browser`, `control_app`
|
|
148
|
+
|
|
149
|
+
**Level 7+**: `spawn_agent`, `send_email`, `install_software`
|
|
150
|
+
|
|
151
|
+
**Level 9+**: `make_payment`, `modify_settings`, `delete_data`, `terminate_agent`
|
|
152
|
+
|
|
153
|
+
## Example Roles
|
|
154
|
+
|
|
155
|
+
See `/config/roles/` for example role definitions:
|
|
156
|
+
|
|
157
|
+
- **Executive Assistant** (Level 6): Manages schedule, communications, and tasks
|
|
158
|
+
- **Research Specialist** (Level 4): Deep research and report generation
|
|
159
|
+
- **System Administrator** (Level 9): System maintenance and infrastructure
|
|
160
|
+
|
|
161
|
+
## Type Definitions
|
|
162
|
+
|
|
163
|
+
### RoleDefinition
|
|
164
|
+
|
|
165
|
+
```typescript
|
|
166
|
+
type RoleDefinition = {
|
|
167
|
+
id: string;
|
|
168
|
+
name: string;
|
|
169
|
+
description: string;
|
|
170
|
+
responsibilities: string[];
|
|
171
|
+
autonomous_actions: string[];
|
|
172
|
+
approval_required: string[];
|
|
173
|
+
kpis: KPI[];
|
|
174
|
+
communication_style: CommunicationStyle;
|
|
175
|
+
heartbeat_instructions: string;
|
|
176
|
+
sub_roles: SubRoleTemplate[];
|
|
177
|
+
tools: string[];
|
|
178
|
+
authority_level: number; // 1-10
|
|
179
|
+
};
|
|
180
|
+
```
|
|
181
|
+
|
|
182
|
+
### KPI
|
|
183
|
+
|
|
184
|
+
```typescript
|
|
185
|
+
type KPI = {
|
|
186
|
+
name: string;
|
|
187
|
+
metric: string;
|
|
188
|
+
target: string;
|
|
189
|
+
check_interval: string;
|
|
190
|
+
};
|
|
191
|
+
```
|
|
192
|
+
|
|
193
|
+
### CommunicationStyle
|
|
194
|
+
|
|
195
|
+
```typescript
|
|
196
|
+
type CommunicationStyle = {
|
|
197
|
+
tone: string;
|
|
198
|
+
verbosity: 'concise' | 'detailed' | 'adaptive';
|
|
199
|
+
formality: 'formal' | 'casual' | 'adaptive';
|
|
200
|
+
};
|
|
201
|
+
```
|
|
202
|
+
|
|
203
|
+
### SubRoleTemplate
|
|
204
|
+
|
|
205
|
+
```typescript
|
|
206
|
+
type SubRoleTemplate = {
|
|
207
|
+
role_id: string;
|
|
208
|
+
name: string;
|
|
209
|
+
description: string;
|
|
210
|
+
spawned_by: string;
|
|
211
|
+
reports_to: string;
|
|
212
|
+
max_budget_per_task: number;
|
|
213
|
+
};
|
|
214
|
+
```
|
|
215
|
+
|
|
216
|
+
## Testing
|
|
217
|
+
|
|
218
|
+
Run the test suite:
|
|
219
|
+
|
|
220
|
+
```bash
|
|
221
|
+
# Test single role loading
|
|
222
|
+
bun run src/roles/test.ts
|
|
223
|
+
|
|
224
|
+
# Test multi-role loading
|
|
225
|
+
bun run src/roles/test-multi.ts
|
|
226
|
+
```
|
|
227
|
+
|
|
228
|
+
## Integration with J.A.R.V.I.S.
|
|
229
|
+
|
|
230
|
+
The Role Engine integrates with:
|
|
231
|
+
|
|
232
|
+
1. **Agent System**: Agents are instantiated with specific roles
|
|
233
|
+
2. **LLM Integration**: System prompts are generated for LLM context
|
|
234
|
+
3. **Permission System**: Authority levels enforce action restrictions
|
|
235
|
+
4. **Daemon**: Roles are loaded at startup and managed by the daemon
|
|
236
|
+
|
|
237
|
+
## Best Practices
|
|
238
|
+
|
|
239
|
+
1. **Authority Principle**: Assign the minimum authority level needed for the role
|
|
240
|
+
2. **Clear Boundaries**: Explicitly define autonomous vs approval-required actions
|
|
241
|
+
3. **Measurable KPIs**: Define concrete, measurable success metrics
|
|
242
|
+
4. **Heartbeat Clarity**: Provide clear, actionable heartbeat instructions
|
|
243
|
+
5. **Tool Assignment**: Only assign tools the role actually needs
|
|
244
|
+
6. **Communication Style**: Match style to the role's purpose and audience
|
|
245
|
+
|
|
246
|
+
## Future Enhancements
|
|
247
|
+
|
|
248
|
+
- Role templates and inheritance
|
|
249
|
+
- Dynamic authority adjustment based on performance
|
|
250
|
+
- Role-based routing and delegation
|
|
251
|
+
- Automatic KPI tracking and reporting
|
|
252
|
+
- Role versioning and migration
|
|
@@ -0,0 +1,119 @@
|
|
|
1
|
+
import type { RoleDefinition } from './types.ts';
|
|
2
|
+
|
|
3
|
+
export type ActionCategory =
|
|
4
|
+
| 'read_data' | 'write_data' | 'delete_data'
|
|
5
|
+
| 'send_message' | 'send_email'
|
|
6
|
+
| 'execute_command' | 'install_software'
|
|
7
|
+
| 'make_payment' | 'modify_settings'
|
|
8
|
+
| 'spawn_agent' | 'terminate_agent'
|
|
9
|
+
| 'access_browser' | 'control_app';
|
|
10
|
+
|
|
11
|
+
/**
|
|
12
|
+
* Maps action categories to minimum required authority level
|
|
13
|
+
*
|
|
14
|
+
* Authority levels:
|
|
15
|
+
* - 1-2: Read only (read_data)
|
|
16
|
+
* - 3-4: Read + write + send messages (write_data, send_message)
|
|
17
|
+
* - 5-6: + execute commands, control apps (execute_command, access_browser, control_app)
|
|
18
|
+
* - 7-8: + spawn agents, send email, install software (spawn_agent, send_email, install_software)
|
|
19
|
+
* - 9-10: Full access including payments and settings (make_payment, modify_settings, delete_data, terminate_agent)
|
|
20
|
+
*/
|
|
21
|
+
export const AUTHORITY_REQUIREMENTS: Record<ActionCategory, number> = {
|
|
22
|
+
// Level 1-2: Read only
|
|
23
|
+
'read_data': 1,
|
|
24
|
+
|
|
25
|
+
// Level 3-4: Read + write + send messages
|
|
26
|
+
'write_data': 3,
|
|
27
|
+
'send_message': 3,
|
|
28
|
+
|
|
29
|
+
// Level 5-6: + execute commands, control apps
|
|
30
|
+
'execute_command': 5,
|
|
31
|
+
'access_browser': 5,
|
|
32
|
+
'control_app': 5,
|
|
33
|
+
|
|
34
|
+
// Level 7-8: + spawn agents, send email, install software
|
|
35
|
+
'spawn_agent': 7,
|
|
36
|
+
'send_email': 7,
|
|
37
|
+
'install_software': 7,
|
|
38
|
+
|
|
39
|
+
// Level 9-10: Full access including payments and settings
|
|
40
|
+
'make_payment': 9,
|
|
41
|
+
'modify_settings': 9,
|
|
42
|
+
'delete_data': 9,
|
|
43
|
+
'terminate_agent': 9,
|
|
44
|
+
};
|
|
45
|
+
|
|
46
|
+
/**
|
|
47
|
+
* Check if a role can perform a specific action
|
|
48
|
+
*/
|
|
49
|
+
export function canPerform(role: RoleDefinition, action: ActionCategory): boolean {
|
|
50
|
+
const requiredLevel = AUTHORITY_REQUIREMENTS[action];
|
|
51
|
+
return role.authority_level >= requiredLevel;
|
|
52
|
+
}
|
|
53
|
+
|
|
54
|
+
/**
|
|
55
|
+
* Get the required authority level for an action
|
|
56
|
+
*/
|
|
57
|
+
export function getRequiredLevel(action: ActionCategory): number {
|
|
58
|
+
return AUTHORITY_REQUIREMENTS[action];
|
|
59
|
+
}
|
|
60
|
+
|
|
61
|
+
/**
|
|
62
|
+
* List all actions a role is allowed to perform
|
|
63
|
+
*/
|
|
64
|
+
export function listAllowedActions(role: RoleDefinition): ActionCategory[] {
|
|
65
|
+
const actions = Object.keys(AUTHORITY_REQUIREMENTS) as ActionCategory[];
|
|
66
|
+
return actions.filter(action => canPerform(role, action));
|
|
67
|
+
}
|
|
68
|
+
|
|
69
|
+
/**
|
|
70
|
+
* List all actions a role is NOT allowed to perform
|
|
71
|
+
*/
|
|
72
|
+
export function listDeniedActions(role: RoleDefinition): ActionCategory[] {
|
|
73
|
+
const actions = Object.keys(AUTHORITY_REQUIREMENTS) as ActionCategory[];
|
|
74
|
+
return actions.filter(action => !canPerform(role, action));
|
|
75
|
+
}
|
|
76
|
+
|
|
77
|
+
/**
|
|
78
|
+
* Get a human-readable description of what an authority level allows
|
|
79
|
+
*/
|
|
80
|
+
export function describeAuthorityLevel(level: number): string {
|
|
81
|
+
if (level < 1 || level > 10) {
|
|
82
|
+
return 'Invalid authority level';
|
|
83
|
+
}
|
|
84
|
+
|
|
85
|
+
if (level <= 2) {
|
|
86
|
+
return 'Read-only access. Can read data but cannot modify anything.';
|
|
87
|
+
}
|
|
88
|
+
|
|
89
|
+
if (level <= 4) {
|
|
90
|
+
return 'Read and write access. Can read/write data and send messages.';
|
|
91
|
+
}
|
|
92
|
+
|
|
93
|
+
if (level <= 6) {
|
|
94
|
+
return 'Command execution. Can execute commands, control apps, and access browser.';
|
|
95
|
+
}
|
|
96
|
+
|
|
97
|
+
if (level <= 8) {
|
|
98
|
+
return 'Agent management. Can spawn agents, send emails, and install software.';
|
|
99
|
+
}
|
|
100
|
+
|
|
101
|
+
return 'Full access. Can make payments, modify settings, delete data, and terminate agents.';
|
|
102
|
+
}
|
|
103
|
+
|
|
104
|
+
/**
|
|
105
|
+
* Get a summary of a role's permissions
|
|
106
|
+
*/
|
|
107
|
+
export function getRolePermissionsSummary(role: RoleDefinition): {
|
|
108
|
+
level: number;
|
|
109
|
+
description: string;
|
|
110
|
+
allowed: ActionCategory[];
|
|
111
|
+
denied: ActionCategory[];
|
|
112
|
+
} {
|
|
113
|
+
return {
|
|
114
|
+
level: role.authority_level,
|
|
115
|
+
description: describeAuthorityLevel(role.authority_level),
|
|
116
|
+
allowed: listAllowedActions(role),
|
|
117
|
+
denied: listDeniedActions(role),
|
|
118
|
+
};
|
|
119
|
+
}
|