@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,600 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Agent Service — The Brain
|
|
3
|
+
*
|
|
4
|
+
* Owns the LLM manager, agent orchestrator, and personality state.
|
|
5
|
+
* Builds dynamic system prompts each turn with role context, personality,
|
|
6
|
+
* commitments, and observations.
|
|
7
|
+
*/
|
|
8
|
+
|
|
9
|
+
import { join } from 'node:path';
|
|
10
|
+
import type { Service, ServiceStatus } from './services.ts';
|
|
11
|
+
import type { JarvisConfig } from '../config/types.ts';
|
|
12
|
+
import type { LLMStreamEvent } from '../llm/provider.ts';
|
|
13
|
+
import type { RoleDefinition } from '../roles/types.ts';
|
|
14
|
+
import type { PersonalityModel } from '../personality/model.ts';
|
|
15
|
+
|
|
16
|
+
import { LLMManager } from '../llm/manager.ts';
|
|
17
|
+
import { AnthropicProvider } from '../llm/anthropic.ts';
|
|
18
|
+
import { OpenAIProvider } from '../llm/openai.ts';
|
|
19
|
+
import { GeminiProvider } from '../llm/gemini.ts';
|
|
20
|
+
import { OllamaProvider } from '../llm/ollama.ts';
|
|
21
|
+
import { AgentOrchestrator } from '../agents/orchestrator.ts';
|
|
22
|
+
import { loadRole } from '../roles/loader.ts';
|
|
23
|
+
import { ToolRegistry } from '../actions/tools/registry.ts';
|
|
24
|
+
import { BUILTIN_TOOLS, browser } from '../actions/tools/builtin.ts';
|
|
25
|
+
import { createDelegateTool, type DelegateToolDeps } from '../actions/tools/delegate.ts';
|
|
26
|
+
import { createManageAgentsTool, type AgentToolDeps } from '../actions/tools/agents.ts';
|
|
27
|
+
import { contentPipelineTool } from '../actions/tools/content.ts';
|
|
28
|
+
import { commitmentsTool } from '../actions/tools/commitments.ts';
|
|
29
|
+
import { researchQueueTool } from '../actions/tools/research.ts';
|
|
30
|
+
import { AgentTaskManager } from '../agents/task-manager.ts';
|
|
31
|
+
import { discoverSpecialists, formatSpecialistList } from '../agents/role-discovery.ts';
|
|
32
|
+
import { buildSystemPrompt, type PromptContext } from '../roles/prompt-builder.ts';
|
|
33
|
+
import type { ProgressCallback } from '../agents/sub-agent-runner.ts';
|
|
34
|
+
import {
|
|
35
|
+
getPersonality,
|
|
36
|
+
savePersonality,
|
|
37
|
+
} from '../personality/model.ts';
|
|
38
|
+
import {
|
|
39
|
+
getChannelPersonality,
|
|
40
|
+
personalityToPrompt,
|
|
41
|
+
} from '../personality/adapter.ts';
|
|
42
|
+
import {
|
|
43
|
+
extractSignals,
|
|
44
|
+
applySignals,
|
|
45
|
+
recordInteraction,
|
|
46
|
+
} from '../personality/learner.ts';
|
|
47
|
+
import { getDueCommitments, getUpcoming } from '../vault/commitments.ts';
|
|
48
|
+
import { findContent } from '../vault/content-pipeline.ts';
|
|
49
|
+
import { getRecentObservations } from '../vault/observations.ts';
|
|
50
|
+
import { extractAndStore } from '../vault/extractor.ts';
|
|
51
|
+
import { getKnowledgeForMessage } from '../vault/retrieval.ts';
|
|
52
|
+
import type { ResearchQueue } from './research-queue.ts';
|
|
53
|
+
import type { IAgentService } from './agent-service-interface.ts';
|
|
54
|
+
import type { AuthorityEngine } from '../authority/engine.ts';
|
|
55
|
+
import { getSidecarManager } from '../actions/tools/sidecar-route.ts';
|
|
56
|
+
|
|
57
|
+
export class AgentService implements Service, IAgentService {
|
|
58
|
+
name = 'agent';
|
|
59
|
+
private _status: ServiceStatus = 'stopped';
|
|
60
|
+
private config: JarvisConfig;
|
|
61
|
+
private llmManager: LLMManager;
|
|
62
|
+
private orchestrator: AgentOrchestrator;
|
|
63
|
+
private role: RoleDefinition | null = null;
|
|
64
|
+
private personality: PersonalityModel | null = null;
|
|
65
|
+
private specialists: Map<string, RoleDefinition> = new Map();
|
|
66
|
+
private specialistListText: string = '';
|
|
67
|
+
private delegationProgressCallback: ProgressCallback | null = null;
|
|
68
|
+
private delegationCallback: ((specialistName: string, task: string) => void) | null = null;
|
|
69
|
+
private researchQueue: ResearchQueue | null = null;
|
|
70
|
+
private taskManager: AgentTaskManager | null = null;
|
|
71
|
+
private authorityEngine: AuthorityEngine | null = null;
|
|
72
|
+
|
|
73
|
+
constructor(config: JarvisConfig) {
|
|
74
|
+
this.config = config;
|
|
75
|
+
this.llmManager = new LLMManager();
|
|
76
|
+
this.orchestrator = new AgentOrchestrator();
|
|
77
|
+
}
|
|
78
|
+
|
|
79
|
+
/**
|
|
80
|
+
* Set callback for sub-agent progress events (delegation visibility).
|
|
81
|
+
* Typically wired to WebSocket broadcast by the daemon.
|
|
82
|
+
*/
|
|
83
|
+
setDelegationProgressCallback(cb: ProgressCallback): void {
|
|
84
|
+
this.delegationProgressCallback = cb;
|
|
85
|
+
}
|
|
86
|
+
|
|
87
|
+
/**
|
|
88
|
+
* Set callback fired when the PA delegates a task to a specialist.
|
|
89
|
+
* Used by ws-service to update task board ownership in real time.
|
|
90
|
+
*/
|
|
91
|
+
setDelegationCallback(cb: (specialistName: string, task: string) => void): void {
|
|
92
|
+
this.delegationCallback = cb;
|
|
93
|
+
}
|
|
94
|
+
|
|
95
|
+
/**
|
|
96
|
+
* Set the research queue for idle-time background research.
|
|
97
|
+
*/
|
|
98
|
+
setResearchQueue(queue: ResearchQueue): void {
|
|
99
|
+
this.researchQueue = queue;
|
|
100
|
+
}
|
|
101
|
+
|
|
102
|
+
setAuthorityEngine(engine: AuthorityEngine): void {
|
|
103
|
+
this.authorityEngine = engine;
|
|
104
|
+
}
|
|
105
|
+
|
|
106
|
+
|
|
107
|
+
getOrchestrator(): AgentOrchestrator {
|
|
108
|
+
return this.orchestrator;
|
|
109
|
+
}
|
|
110
|
+
|
|
111
|
+
getLLMManager(): LLMManager {
|
|
112
|
+
return this.llmManager;
|
|
113
|
+
}
|
|
114
|
+
|
|
115
|
+
getTaskManager(): AgentTaskManager | null {
|
|
116
|
+
return this.taskManager;
|
|
117
|
+
}
|
|
118
|
+
|
|
119
|
+
async start(): Promise<void> {
|
|
120
|
+
this._status = 'starting';
|
|
121
|
+
|
|
122
|
+
try {
|
|
123
|
+
// 1. Create LLM providers from config
|
|
124
|
+
this.registerProviders();
|
|
125
|
+
|
|
126
|
+
// 2. Load role YAML
|
|
127
|
+
this.role = this.loadActiveRole();
|
|
128
|
+
|
|
129
|
+
// 3. Wire LLM manager to orchestrator
|
|
130
|
+
this.orchestrator.setLLMManager(this.llmManager);
|
|
131
|
+
|
|
132
|
+
// 4. Discover specialist roles
|
|
133
|
+
this.specialists = discoverSpecialists('roles/specialists');
|
|
134
|
+
if (this.specialists.size > 0) {
|
|
135
|
+
this.specialistListText = formatSpecialistList(this.specialists);
|
|
136
|
+
console.log(`[AgentService] Discovered ${this.specialists.size} specialists: ${Array.from(this.specialists.keys()).join(', ')}`);
|
|
137
|
+
}
|
|
138
|
+
|
|
139
|
+
// 5. Register tools (builtin + delegation)
|
|
140
|
+
const toolRegistry = new ToolRegistry();
|
|
141
|
+
for (const tool of BUILTIN_TOOLS) {
|
|
142
|
+
toolRegistry.register(tool);
|
|
143
|
+
}
|
|
144
|
+
|
|
145
|
+
// Register content pipeline tool
|
|
146
|
+
toolRegistry.register(contentPipelineTool);
|
|
147
|
+
|
|
148
|
+
// Register commitments tool
|
|
149
|
+
toolRegistry.register(commitmentsTool);
|
|
150
|
+
|
|
151
|
+
// Register research queue tool
|
|
152
|
+
toolRegistry.register(researchQueueTool);
|
|
153
|
+
|
|
154
|
+
// Register delegate_task tool if specialists are available
|
|
155
|
+
if (this.specialists.size > 0) {
|
|
156
|
+
const delegateDeps: DelegateToolDeps = {
|
|
157
|
+
orchestrator: this.orchestrator,
|
|
158
|
+
llmManager: this.llmManager,
|
|
159
|
+
specialists: this.specialists,
|
|
160
|
+
onProgress: (event) => {
|
|
161
|
+
if (this.delegationProgressCallback) {
|
|
162
|
+
this.delegationProgressCallback(event);
|
|
163
|
+
}
|
|
164
|
+
},
|
|
165
|
+
onDelegation: (specialistName, task) => {
|
|
166
|
+
if (this.delegationCallback) {
|
|
167
|
+
this.delegationCallback(specialistName, task);
|
|
168
|
+
}
|
|
169
|
+
},
|
|
170
|
+
};
|
|
171
|
+
const delegateTool = createDelegateTool(delegateDeps);
|
|
172
|
+
toolRegistry.register(delegateTool);
|
|
173
|
+
console.log('[AgentService] Registered delegate_task tool');
|
|
174
|
+
|
|
175
|
+
// Register manage_agents tool for persistent/async agents
|
|
176
|
+
this.taskManager = new AgentTaskManager();
|
|
177
|
+
const agentToolDeps: AgentToolDeps = {
|
|
178
|
+
orchestrator: this.orchestrator,
|
|
179
|
+
llmManager: this.llmManager,
|
|
180
|
+
specialists: this.specialists,
|
|
181
|
+
taskManager: this.taskManager,
|
|
182
|
+
onProgress: (event) => {
|
|
183
|
+
if (this.delegationProgressCallback) {
|
|
184
|
+
this.delegationProgressCallback(event);
|
|
185
|
+
}
|
|
186
|
+
},
|
|
187
|
+
};
|
|
188
|
+
const agentTool = createManageAgentsTool(agentToolDeps);
|
|
189
|
+
toolRegistry.register(agentTool);
|
|
190
|
+
console.log('[AgentService] Registered manage_agents tool');
|
|
191
|
+
}
|
|
192
|
+
|
|
193
|
+
this.orchestrator.setToolRegistry(toolRegistry);
|
|
194
|
+
console.log(`[AgentService] Registered ${toolRegistry.count()} tools total`);
|
|
195
|
+
|
|
196
|
+
// 6. Create primary agent
|
|
197
|
+
this.orchestrator.createPrimary(this.role);
|
|
198
|
+
|
|
199
|
+
// 7. Load personality
|
|
200
|
+
this.personality = getPersonality();
|
|
201
|
+
|
|
202
|
+
this._status = 'running';
|
|
203
|
+
console.log(`[AgentService] Started with role: ${this.role.name}`);
|
|
204
|
+
} catch (error) {
|
|
205
|
+
this._status = 'error';
|
|
206
|
+
throw error;
|
|
207
|
+
}
|
|
208
|
+
}
|
|
209
|
+
|
|
210
|
+
async stop(): Promise<void> {
|
|
211
|
+
this._status = 'stopping';
|
|
212
|
+
const primary = this.orchestrator.getPrimary();
|
|
213
|
+
if (primary) {
|
|
214
|
+
this.orchestrator.terminateAgent(primary.id);
|
|
215
|
+
}
|
|
216
|
+
|
|
217
|
+
// Disconnect browser (stops auto-launched Chrome if any)
|
|
218
|
+
if (browser.connected) {
|
|
219
|
+
await browser.disconnect();
|
|
220
|
+
}
|
|
221
|
+
|
|
222
|
+
this._status = 'stopped';
|
|
223
|
+
console.log('[AgentService] Stopped');
|
|
224
|
+
}
|
|
225
|
+
|
|
226
|
+
status(): ServiceStatus {
|
|
227
|
+
return this._status;
|
|
228
|
+
}
|
|
229
|
+
|
|
230
|
+
/**
|
|
231
|
+
* Stream a message through the agent. Returns a stream and an onComplete callback.
|
|
232
|
+
*/
|
|
233
|
+
streamMessage(text: string, channel: string = 'websocket'): {
|
|
234
|
+
stream: AsyncIterable<LLMStreamEvent>;
|
|
235
|
+
onComplete: (fullText: string) => Promise<void>;
|
|
236
|
+
} {
|
|
237
|
+
const systemPrompt = this.buildFullSystemPrompt(channel, text);
|
|
238
|
+
|
|
239
|
+
const stream = this.orchestrator.streamMessage(systemPrompt, text);
|
|
240
|
+
|
|
241
|
+
const onComplete = async (fullText: string): Promise<void> => {
|
|
242
|
+
// Note: orchestrator already adds assistant response to history
|
|
243
|
+
// Run extraction and learning in parallel, wait for both to settle
|
|
244
|
+
await Promise.allSettled([
|
|
245
|
+
this.extractKnowledge(text, fullText).catch((err) =>
|
|
246
|
+
console.error('[AgentService] Extraction error:', err instanceof Error ? err.message : err)
|
|
247
|
+
),
|
|
248
|
+
this.learnFromInteraction(text, fullText, channel).catch((err) =>
|
|
249
|
+
console.error('[AgentService] Learning error:', err instanceof Error ? err.message : err)
|
|
250
|
+
),
|
|
251
|
+
]);
|
|
252
|
+
};
|
|
253
|
+
|
|
254
|
+
return { stream, onComplete };
|
|
255
|
+
}
|
|
256
|
+
|
|
257
|
+
/**
|
|
258
|
+
* Non-streaming message handler. Returns full response string.
|
|
259
|
+
*/
|
|
260
|
+
async handleMessage(text: string, channel: string = 'websocket'): Promise<string> {
|
|
261
|
+
const systemPrompt = this.buildFullSystemPrompt(channel, text);
|
|
262
|
+
|
|
263
|
+
const response = await this.orchestrator.processMessage(systemPrompt, text);
|
|
264
|
+
|
|
265
|
+
// Run extraction and learning in parallel (non-blocking but tracked)
|
|
266
|
+
Promise.allSettled([
|
|
267
|
+
this.extractKnowledge(text, response).catch((err) =>
|
|
268
|
+
console.error('[AgentService] Extraction error:', err instanceof Error ? err.message : err)
|
|
269
|
+
),
|
|
270
|
+
this.learnFromInteraction(text, response, channel).catch((err) =>
|
|
271
|
+
console.error('[AgentService] Learning error:', err instanceof Error ? err.message : err)
|
|
272
|
+
),
|
|
273
|
+
]);
|
|
274
|
+
|
|
275
|
+
return response;
|
|
276
|
+
}
|
|
277
|
+
|
|
278
|
+
/**
|
|
279
|
+
* Handle periodic heartbeat with full tool access.
|
|
280
|
+
* Accepts optional coalesced event summary to include in the prompt.
|
|
281
|
+
* Uses processMessage() so the agent can take action (browse, run commands, etc.).
|
|
282
|
+
*/
|
|
283
|
+
async handleHeartbeat(coalescedEvents?: string): Promise<string | null> {
|
|
284
|
+
if (!this.role) return null;
|
|
285
|
+
|
|
286
|
+
const systemPrompt = this.buildHeartbeatPrompt(coalescedEvents);
|
|
287
|
+
|
|
288
|
+
// Build the heartbeat "user message" that triggers the agent
|
|
289
|
+
const parts: string[] = ['[HEARTBEAT] Periodic check-in. Review your responsibilities and take action.'];
|
|
290
|
+
|
|
291
|
+
if (coalescedEvents) {
|
|
292
|
+
parts.push('');
|
|
293
|
+
parts.push(coalescedEvents);
|
|
294
|
+
}
|
|
295
|
+
|
|
296
|
+
const heartbeatMessage = parts.join('\n');
|
|
297
|
+
|
|
298
|
+
try {
|
|
299
|
+
const response = await this.orchestrator.processMessage(systemPrompt, heartbeatMessage);
|
|
300
|
+
if (response && response.trim().length > 0) {
|
|
301
|
+
return response;
|
|
302
|
+
}
|
|
303
|
+
return null;
|
|
304
|
+
} catch (err) {
|
|
305
|
+
console.error('[AgentService] Heartbeat processing error:', err);
|
|
306
|
+
return null;
|
|
307
|
+
}
|
|
308
|
+
}
|
|
309
|
+
|
|
310
|
+
// --- Private methods ---
|
|
311
|
+
|
|
312
|
+
private registerProviders(): void {
|
|
313
|
+
const { llm } = this.config;
|
|
314
|
+
let hasProvider = false;
|
|
315
|
+
|
|
316
|
+
// Register Anthropic
|
|
317
|
+
if (llm.anthropic?.api_key) {
|
|
318
|
+
const provider = new AnthropicProvider(
|
|
319
|
+
llm.anthropic.api_key,
|
|
320
|
+
llm.anthropic.model
|
|
321
|
+
);
|
|
322
|
+
this.llmManager.registerProvider(provider);
|
|
323
|
+
hasProvider = true;
|
|
324
|
+
console.log('[AgentService] Registered Anthropic provider');
|
|
325
|
+
}
|
|
326
|
+
|
|
327
|
+
// Register OpenAI
|
|
328
|
+
if (llm.openai?.api_key) {
|
|
329
|
+
const provider = new OpenAIProvider(
|
|
330
|
+
llm.openai.api_key,
|
|
331
|
+
llm.openai.model
|
|
332
|
+
);
|
|
333
|
+
this.llmManager.registerProvider(provider);
|
|
334
|
+
hasProvider = true;
|
|
335
|
+
console.log('[AgentService] Registered OpenAI provider');
|
|
336
|
+
}
|
|
337
|
+
|
|
338
|
+
// Register Gemini
|
|
339
|
+
if (llm.gemini?.api_key) {
|
|
340
|
+
const provider = new GeminiProvider(
|
|
341
|
+
llm.gemini.api_key,
|
|
342
|
+
llm.gemini.model
|
|
343
|
+
);
|
|
344
|
+
this.llmManager.registerProvider(provider);
|
|
345
|
+
hasProvider = true;
|
|
346
|
+
console.log('[AgentService] Registered Gemini provider');
|
|
347
|
+
}
|
|
348
|
+
|
|
349
|
+
// Register Ollama (always available, no API key needed)
|
|
350
|
+
if (llm.ollama) {
|
|
351
|
+
const provider = new OllamaProvider(
|
|
352
|
+
llm.ollama.base_url,
|
|
353
|
+
llm.ollama.model
|
|
354
|
+
);
|
|
355
|
+
this.llmManager.registerProvider(provider);
|
|
356
|
+
hasProvider = true;
|
|
357
|
+
console.log('[AgentService] Registered Ollama provider');
|
|
358
|
+
}
|
|
359
|
+
|
|
360
|
+
if (!hasProvider) {
|
|
361
|
+
console.warn('[AgentService] No LLM providers configured. Responses will be placeholders.');
|
|
362
|
+
}
|
|
363
|
+
|
|
364
|
+
// Set primary and fallback chain
|
|
365
|
+
if (hasProvider) {
|
|
366
|
+
try {
|
|
367
|
+
this.llmManager.setPrimary(llm.primary);
|
|
368
|
+
} catch {
|
|
369
|
+
// Primary provider not available, first registered is already primary
|
|
370
|
+
}
|
|
371
|
+
|
|
372
|
+
// Set fallback chain (only for providers that were registered)
|
|
373
|
+
const registeredFallbacks = llm.fallback.filter(
|
|
374
|
+
(name) => this.llmManager.getProvider(name) !== undefined
|
|
375
|
+
);
|
|
376
|
+
if (registeredFallbacks.length > 0) {
|
|
377
|
+
this.llmManager.setFallbackChain(registeredFallbacks);
|
|
378
|
+
}
|
|
379
|
+
}
|
|
380
|
+
}
|
|
381
|
+
|
|
382
|
+
private loadActiveRole(): RoleDefinition {
|
|
383
|
+
const roleName = this.config.active_role;
|
|
384
|
+
|
|
385
|
+
// Try multiple locations for role YAML (package-root-relative for global install)
|
|
386
|
+
const pkgRoot = join(import.meta.dir, '../..');
|
|
387
|
+
const paths = [
|
|
388
|
+
join(pkgRoot, `roles/${roleName}.yaml`),
|
|
389
|
+
join(pkgRoot, `roles/${roleName}.yml`),
|
|
390
|
+
join(pkgRoot, `config/roles/${roleName}.yaml`),
|
|
391
|
+
join(pkgRoot, `config/roles/${roleName}.yml`),
|
|
392
|
+
// Also try CWD-relative for local dev
|
|
393
|
+
`roles/${roleName}.yaml`,
|
|
394
|
+
`roles/${roleName}.yml`,
|
|
395
|
+
];
|
|
396
|
+
|
|
397
|
+
for (const rolePath of paths) {
|
|
398
|
+
try {
|
|
399
|
+
const role = loadRole(rolePath);
|
|
400
|
+
console.log(`[AgentService] Loaded role '${role.name}' from ${rolePath}`);
|
|
401
|
+
return role;
|
|
402
|
+
} catch {
|
|
403
|
+
// Try next path
|
|
404
|
+
}
|
|
405
|
+
}
|
|
406
|
+
|
|
407
|
+
// Fatal — cannot start without a role
|
|
408
|
+
throw new Error(
|
|
409
|
+
`[AgentService] Could not load role '${roleName}'. Searched: ${paths.join(', ')}`
|
|
410
|
+
);
|
|
411
|
+
}
|
|
412
|
+
|
|
413
|
+
private buildFullSystemPrompt(channel: string, userMessage?: string): string {
|
|
414
|
+
if (!this.role) return '';
|
|
415
|
+
|
|
416
|
+
// Build prompt context with live data + vault knowledge
|
|
417
|
+
const context = this.buildPromptContext(userMessage);
|
|
418
|
+
|
|
419
|
+
// Build base system prompt from role + context
|
|
420
|
+
const rolePrompt = buildSystemPrompt(this.role, context);
|
|
421
|
+
|
|
422
|
+
// Build personality prompt for this channel
|
|
423
|
+
const personality = this.personality ?? getPersonality();
|
|
424
|
+
const channelPersonality = getChannelPersonality(personality, channel);
|
|
425
|
+
const personalityPrompt = personalityToPrompt(channelPersonality);
|
|
426
|
+
|
|
427
|
+
return `${rolePrompt}\n\n${personalityPrompt}`;
|
|
428
|
+
}
|
|
429
|
+
|
|
430
|
+
private buildHeartbeatPrompt(coalescedEvents?: string): string {
|
|
431
|
+
if (!this.role) return '';
|
|
432
|
+
|
|
433
|
+
const context = this.buildPromptContext();
|
|
434
|
+
const rolePrompt = buildSystemPrompt(this.role, context);
|
|
435
|
+
|
|
436
|
+
const parts = [rolePrompt, '', '# Heartbeat Check', this.role.heartbeat_instructions];
|
|
437
|
+
|
|
438
|
+
if (coalescedEvents) {
|
|
439
|
+
parts.push('', '# Recent System Events', coalescedEvents);
|
|
440
|
+
}
|
|
441
|
+
|
|
442
|
+
// Inject commitment execution instructions
|
|
443
|
+
parts.push('', '# COMMITMENT EXECUTION');
|
|
444
|
+
parts.push('If any commitments are overdue or due soon, EXECUTE them now using your tools.');
|
|
445
|
+
parts.push('Do not just mention them — actually perform the work. Use browse, terminal, file operations as needed.');
|
|
446
|
+
|
|
447
|
+
// Inject background research instructions when idle
|
|
448
|
+
if (this.researchQueue && this.researchQueue.queuedCount() > 0) {
|
|
449
|
+
const next = this.researchQueue.getNext();
|
|
450
|
+
if (next) {
|
|
451
|
+
parts.push('', '# BACKGROUND RESEARCH');
|
|
452
|
+
parts.push(`You have a research topic queued: "${next.topic}"`);
|
|
453
|
+
parts.push(`Reason: ${next.reason}`);
|
|
454
|
+
parts.push(`Research ID: ${next.id}`);
|
|
455
|
+
parts.push('If nothing urgent needs your attention, research this topic now.');
|
|
456
|
+
parts.push('Use your browser and tools to gather information, then use the research_queue tool with action "complete" to save your findings.');
|
|
457
|
+
}
|
|
458
|
+
} else {
|
|
459
|
+
parts.push('', '# IDLE MODE');
|
|
460
|
+
parts.push('No research topics queued. If nothing urgent, you may:');
|
|
461
|
+
parts.push('- Check news or trends relevant to the user');
|
|
462
|
+
parts.push('- Review and organize pending tasks');
|
|
463
|
+
parts.push('- Or simply report "All clear" if nothing needs attention');
|
|
464
|
+
}
|
|
465
|
+
|
|
466
|
+
parts.push('', '# Important', 'You have full tool access during this heartbeat. If you need to take action (browse the web, run commands, check files), DO IT. Be proactive and aggressive about helping.');
|
|
467
|
+
|
|
468
|
+
return parts.join('\n');
|
|
469
|
+
}
|
|
470
|
+
|
|
471
|
+
private buildPromptContext(userMessage?: string): PromptContext {
|
|
472
|
+
// Check if any sidecars are enrolled (cheap DB query, controls tool guide content)
|
|
473
|
+
let hasSidecars = false;
|
|
474
|
+
try {
|
|
475
|
+
const mgr = getSidecarManager();
|
|
476
|
+
if (mgr) hasSidecars = mgr.listSidecars().length > 0;
|
|
477
|
+
} catch { /* ignore */ }
|
|
478
|
+
|
|
479
|
+
const context: PromptContext = {
|
|
480
|
+
currentTime: new Date().toISOString(),
|
|
481
|
+
availableSpecialists: this.specialistListText || undefined,
|
|
482
|
+
hasSidecars,
|
|
483
|
+
};
|
|
484
|
+
|
|
485
|
+
// Retrieve relevant knowledge from vault based on user message
|
|
486
|
+
if (userMessage) {
|
|
487
|
+
try {
|
|
488
|
+
const knowledge = getKnowledgeForMessage(userMessage);
|
|
489
|
+
if (knowledge) {
|
|
490
|
+
context.knowledgeContext = knowledge;
|
|
491
|
+
}
|
|
492
|
+
} catch (err) {
|
|
493
|
+
console.error('[AgentService] Error retrieving knowledge:', err);
|
|
494
|
+
}
|
|
495
|
+
}
|
|
496
|
+
|
|
497
|
+
// Get due commitments
|
|
498
|
+
try {
|
|
499
|
+
const due = getDueCommitments();
|
|
500
|
+
const upcoming = getUpcoming(5);
|
|
501
|
+
const allCommitments = [...due, ...upcoming];
|
|
502
|
+
|
|
503
|
+
if (allCommitments.length > 0) {
|
|
504
|
+
context.activeCommitments = allCommitments.map((c) => {
|
|
505
|
+
const dueStr = c.when_due
|
|
506
|
+
? ` (due: ${new Date(c.when_due).toLocaleString()})`
|
|
507
|
+
: '';
|
|
508
|
+
return `[${c.priority}] ${c.what}${dueStr} — ${c.status}`;
|
|
509
|
+
});
|
|
510
|
+
}
|
|
511
|
+
} catch (err) {
|
|
512
|
+
console.error('[AgentService] Error loading commitments:', err);
|
|
513
|
+
}
|
|
514
|
+
|
|
515
|
+
// Get active content pipeline items (not published)
|
|
516
|
+
try {
|
|
517
|
+
const activeContent = findContent({}).filter(
|
|
518
|
+
(c) => c.stage !== 'published'
|
|
519
|
+
).slice(0, 10);
|
|
520
|
+
if (activeContent.length > 0) {
|
|
521
|
+
context.contentPipeline = activeContent.map((c) => {
|
|
522
|
+
const tags = c.tags.length > 0 ? ` [${c.tags.join(', ')}]` : '';
|
|
523
|
+
return `"${c.title}" (${c.content_type}) — ${c.stage}${tags}`;
|
|
524
|
+
});
|
|
525
|
+
}
|
|
526
|
+
} catch (err) {
|
|
527
|
+
console.error('[AgentService] Error loading content pipeline:', err);
|
|
528
|
+
}
|
|
529
|
+
|
|
530
|
+
// Get recent observations
|
|
531
|
+
try {
|
|
532
|
+
const observations = getRecentObservations(undefined, 10);
|
|
533
|
+
if (observations.length > 0) {
|
|
534
|
+
context.recentObservations = observations.map((o) => {
|
|
535
|
+
const time = new Date(o.created_at).toLocaleTimeString();
|
|
536
|
+
return `[${time}] ${o.type}: ${JSON.stringify(o.data).slice(0, 200)}`;
|
|
537
|
+
});
|
|
538
|
+
}
|
|
539
|
+
} catch (err) {
|
|
540
|
+
console.error('[AgentService] Error loading observations:', err);
|
|
541
|
+
}
|
|
542
|
+
|
|
543
|
+
// Active goals context for the system prompt
|
|
544
|
+
try {
|
|
545
|
+
const { getActiveGoalsSummary } = require('../vault/retrieval.ts');
|
|
546
|
+
const goalsSummary = getActiveGoalsSummary();
|
|
547
|
+
if (goalsSummary) {
|
|
548
|
+
context.activeGoals = goalsSummary;
|
|
549
|
+
}
|
|
550
|
+
} catch {
|
|
551
|
+
// Goals module may not be available — ignore
|
|
552
|
+
}
|
|
553
|
+
|
|
554
|
+
// Authority rules for the system prompt
|
|
555
|
+
if (this.authorityEngine && this.role) {
|
|
556
|
+
try {
|
|
557
|
+
context.authorityRules = this.authorityEngine.describeRulesForAgent(
|
|
558
|
+
this.role.authority_level,
|
|
559
|
+
this.role.id
|
|
560
|
+
);
|
|
561
|
+
} catch (err) {
|
|
562
|
+
console.error('[AgentService] Error building authority rules:', err);
|
|
563
|
+
}
|
|
564
|
+
}
|
|
565
|
+
|
|
566
|
+
return context;
|
|
567
|
+
}
|
|
568
|
+
|
|
569
|
+
private async extractKnowledge(userMessage: string, assistantResponse: string): Promise<void> {
|
|
570
|
+
// Get the primary provider for extraction
|
|
571
|
+
const provider = this.llmManager.getProvider(this.config.llm.primary)
|
|
572
|
+
?? this.llmManager.getProvider('anthropic')
|
|
573
|
+
?? this.llmManager.getProvider('openai');
|
|
574
|
+
|
|
575
|
+
await extractAndStore(userMessage, assistantResponse, provider);
|
|
576
|
+
}
|
|
577
|
+
|
|
578
|
+
private async learnFromInteraction(
|
|
579
|
+
userMessage: string,
|
|
580
|
+
assistantResponse: string,
|
|
581
|
+
_channel: string
|
|
582
|
+
): Promise<void> {
|
|
583
|
+
let personality = this.personality ?? getPersonality();
|
|
584
|
+
|
|
585
|
+
// Extract signals from the interaction
|
|
586
|
+
const signals = extractSignals(userMessage, assistantResponse);
|
|
587
|
+
|
|
588
|
+
// Apply signals if any
|
|
589
|
+
if (signals.length > 0) {
|
|
590
|
+
personality = applySignals(personality, signals);
|
|
591
|
+
}
|
|
592
|
+
|
|
593
|
+
// Record the interaction (increments message count, adjusts trust)
|
|
594
|
+
personality = recordInteraction(personality);
|
|
595
|
+
|
|
596
|
+
// Save updated personality
|
|
597
|
+
savePersonality(personality);
|
|
598
|
+
this.personality = personality;
|
|
599
|
+
}
|
|
600
|
+
}
|