openbot 0.2.12 → 0.2.14

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 (141) hide show
  1. package/.prettierrc +8 -0
  2. package/AGENTS.md +68 -0
  3. package/CONTRIBUTING.md +74 -0
  4. package/LICENSE +21 -0
  5. package/README.md +117 -14
  6. package/dist/agents/system.js +106 -0
  7. package/dist/app/cli.js +27 -0
  8. package/dist/app/config.js +64 -0
  9. package/dist/app/server.js +237 -0
  10. package/dist/app/utils.js +35 -0
  11. package/dist/harness/agent-harness.js +45 -0
  12. package/dist/harness/mcp.js +61 -0
  13. package/dist/harness/orchestrator.js +273 -0
  14. package/dist/harness/process.js +7 -0
  15. package/dist/plugins/ai-sdk.js +141 -0
  16. package/dist/plugins/delegation.js +52 -0
  17. package/dist/plugins/mcp.js +140 -0
  18. package/dist/plugins/storage.js +502 -0
  19. package/dist/plugins/ui.js +47 -0
  20. package/dist/registry/plugins.js +73 -0
  21. package/dist/services/storage.js +724 -0
  22. package/docs/README.md +7 -0
  23. package/docs/agents.md +83 -0
  24. package/docs/architecture.md +34 -0
  25. package/docs/plugins.md +77 -0
  26. package/logo-black.png +0 -0
  27. package/{dist/assets/logo.js → logo-black.svg} +24 -24
  28. package/{dist/ui/sidebar.js → logo-white.svg} +23 -88
  29. package/package.json +6 -5
  30. package/src/agents/system.ts +112 -0
  31. package/src/app/cli.ts +38 -0
  32. package/src/app/config.ts +104 -0
  33. package/src/app/server.ts +284 -0
  34. package/src/app/types.ts +476 -0
  35. package/src/app/utils.ts +43 -0
  36. package/src/assets/icon.svg +1 -0
  37. package/src/harness/agent-harness.ts +58 -0
  38. package/src/harness/mcp.ts +78 -0
  39. package/src/harness/orchestrator.ts +342 -0
  40. package/src/harness/process.ts +9 -0
  41. package/src/harness/types.ts +34 -0
  42. package/src/plugins/ai-sdk.ts +197 -0
  43. package/src/plugins/delegation.ts +60 -0
  44. package/src/plugins/mcp.ts +154 -0
  45. package/src/plugins/storage.ts +725 -0
  46. package/src/plugins/ui.ts +57 -0
  47. package/src/registry/plugins.ts +85 -0
  48. package/src/services/storage.ts +957 -0
  49. package/tsconfig.json +18 -0
  50. package/dist/agents/agent-creator.js +0 -74
  51. package/dist/agents/browser-agent.js +0 -31
  52. package/dist/agents/os-agent.js +0 -32
  53. package/dist/agents/planner-agent.js +0 -32
  54. package/dist/agents/topic-agent.js +0 -46
  55. package/dist/architecture/execution-engine.js +0 -151
  56. package/dist/architecture/intent-classifier.js +0 -26
  57. package/dist/architecture/planner.js +0 -106
  58. package/dist/automation-worker.js +0 -121
  59. package/dist/automations.js +0 -52
  60. package/dist/cli.js +0 -279
  61. package/dist/config.js +0 -53
  62. package/dist/core/agents.js +0 -41
  63. package/dist/core/delegation.js +0 -230
  64. package/dist/core/manager.js +0 -96
  65. package/dist/core/plugins.js +0 -74
  66. package/dist/core/router.js +0 -191
  67. package/dist/handlers/init.js +0 -29
  68. package/dist/handlers/session-change.js +0 -21
  69. package/dist/handlers/settings.js +0 -47
  70. package/dist/handlers/tab-change.js +0 -14
  71. package/dist/installers.js +0 -156
  72. package/dist/marketplace.js +0 -80
  73. package/dist/model-catalog.js +0 -132
  74. package/dist/model-defaults.js +0 -25
  75. package/dist/models.js +0 -47
  76. package/dist/open-bot.js +0 -51
  77. package/dist/orchestrator/direct-invocation.js +0 -13
  78. package/dist/orchestrator/events.js +0 -36
  79. package/dist/orchestrator/state.js +0 -54
  80. package/dist/orchestrator.js +0 -422
  81. package/dist/plugins/agent/index.js +0 -81
  82. package/dist/plugins/approval/index.js +0 -100
  83. package/dist/plugins/brain/identity.js +0 -77
  84. package/dist/plugins/brain/index.js +0 -204
  85. package/dist/plugins/brain/memory.js +0 -120
  86. package/dist/plugins/brain/prompt.js +0 -46
  87. package/dist/plugins/brain/types.js +0 -45
  88. package/dist/plugins/brain/ui.js +0 -7
  89. package/dist/plugins/browser/index.js +0 -629
  90. package/dist/plugins/browser/ui.js +0 -13
  91. package/dist/plugins/file-system/index.js +0 -171
  92. package/dist/plugins/file-system/ui.js +0 -6
  93. package/dist/plugins/llm/context-budget.js +0 -139
  94. package/dist/plugins/llm/context-shaping.js +0 -177
  95. package/dist/plugins/llm/index.js +0 -380
  96. package/dist/plugins/memory/index.js +0 -220
  97. package/dist/plugins/memory/memory.js +0 -122
  98. package/dist/plugins/memory/prompt.js +0 -55
  99. package/dist/plugins/memory/types.js +0 -45
  100. package/dist/plugins/meta-agent/index.js +0 -570
  101. package/dist/plugins/meta-agent/ui.js +0 -11
  102. package/dist/plugins/shell/index.js +0 -100
  103. package/dist/plugins/shell/ui.js +0 -6
  104. package/dist/plugins/skills/index.js +0 -286
  105. package/dist/plugins/skills/types.js +0 -50
  106. package/dist/plugins/skills/ui.js +0 -12
  107. package/dist/registry/agent-registry.js +0 -35
  108. package/dist/registry/index.js +0 -2
  109. package/dist/registry/plugin-loader.js +0 -499
  110. package/dist/registry/plugin-registry.js +0 -44
  111. package/dist/registry/ts-agent-loader.js +0 -82
  112. package/dist/registry/yaml-agent-loader.js +0 -246
  113. package/dist/runtime/execution-trace.js +0 -41
  114. package/dist/runtime/intent-routing.js +0 -26
  115. package/dist/runtime/openbot-runtime.js +0 -354
  116. package/dist/server.js +0 -890
  117. package/dist/session.js +0 -179
  118. package/dist/ui/block.js +0 -12
  119. package/dist/ui/header.js +0 -52
  120. package/dist/ui/layout.js +0 -26
  121. package/dist/ui/navigation.js +0 -15
  122. package/dist/ui/settings.js +0 -106
  123. package/dist/ui/skills.js +0 -7
  124. package/dist/ui/thread.js +0 -16
  125. package/dist/ui/widgets/action-list.js +0 -2
  126. package/dist/ui/widgets/approval-card.js +0 -9
  127. package/dist/ui/widgets/code-snippet.js +0 -2
  128. package/dist/ui/widgets/data-block.js +0 -2
  129. package/dist/ui/widgets/data-table.js +0 -2
  130. package/dist/ui/widgets/delegation.js +0 -29
  131. package/dist/ui/widgets/empty-state.js +0 -2
  132. package/dist/ui/widgets/index.js +0 -23
  133. package/dist/ui/widgets/inquiry.js +0 -7
  134. package/dist/ui/widgets/key-value.js +0 -2
  135. package/dist/ui/widgets/progress-step.js +0 -2
  136. package/dist/ui/widgets/resource-card.js +0 -2
  137. package/dist/ui/widgets/status.js +0 -2
  138. package/dist/ui/widgets/todo-list.js +0 -2
  139. package/dist/version.js +0 -62
  140. /package/dist/{types.js → app/types.js} +0 -0
  141. /package/dist/{architecture/contracts.js → harness/types.js} +0 -0
@@ -0,0 +1,284 @@
1
+ import 'dotenv/config';
2
+ import express from 'express';
3
+ import cors from 'cors';
4
+ import z from 'zod';
5
+ import path from 'path';
6
+ import fs from 'fs/promises';
7
+ import { generateId } from 'melony';
8
+ import { DEFAULT_BASE_DIR, loadConfig, loadVariables, resolvePath } from '../app/config.js';
9
+ import { ActiveRunsSnapshotEvent, OpenBotEvent, OpenBotState } from './types.js';
10
+ import { processService } from '../harness/process.js';
11
+ import { storageService } from '../services/storage.js';
12
+ import { AgentHarness } from '../harness/agent-harness.js';
13
+ import { initPlugins } from '../registry/plugins.js';
14
+ import { ensureEventId, openBotEventFromQuery } from './utils.js';
15
+
16
+ export interface ServerOptions {
17
+ port?: number;
18
+ }
19
+
20
+ export async function startServer(options: ServerOptions = {}) {
21
+ const publishEventSchema = z
22
+ .object({
23
+ id: z.string().optional(),
24
+ type: z.string().min(1, 'Event type is required'),
25
+ data: z.unknown().optional(),
26
+ meta: z.unknown().optional(),
27
+ })
28
+ .passthrough();
29
+
30
+ const config = loadConfig();
31
+ const variables = loadVariables();
32
+
33
+ processService.applyVariablesToProcessEnv(variables.variables);
34
+
35
+ const baseDir = config.baseDir || DEFAULT_BASE_DIR;
36
+ const openBotDir = resolvePath(baseDir);
37
+ const PORT = Number(options.port ?? config.port ?? process.env.PORT ?? 4132);
38
+ const app = express();
39
+ const clients: Map<string, express.Response[]> = new Map();
40
+ const GLOBAL_CHANNEL_ID = '__global__';
41
+ const activeRuns = new Map<
42
+ string,
43
+ { runId: string; channelId: string; threadId?: string; agentId: string }
44
+ >();
45
+
46
+ const agentsDir = path.join(openBotDir, 'agents');
47
+ const pluginsDir = path.join(openBotDir, 'plugins');
48
+
49
+ await fs.mkdir(agentsDir, { recursive: true });
50
+ await fs.mkdir(pluginsDir, { recursive: true });
51
+
52
+ initPlugins(pluginsDir);
53
+
54
+ const getContext = (req: express.Request) => {
55
+ const channelId =
56
+ req.get('x-openbot-channel-id') || req.query.channelId || (req.body && req.body.channelId);
57
+ const threadId =
58
+ req.get('x-openbot-thread-id') || req.query.threadId || (req.body && req.body.threadId);
59
+ const agentId =
60
+ req.get('x-openbot-agent-id') || req.query.agentId || (req.body && req.body.agentId);
61
+ const runId =
62
+ req.get('x-openbot-run-id') ||
63
+ req.query.runId ||
64
+ (req.body && req.body.runId) ||
65
+ `run_${generateId()}`;
66
+ const responseType =
67
+ req.get('x-openbot-response-type') ||
68
+ req.query.responseType ||
69
+ (req.body && req.body.responseType);
70
+
71
+ return {
72
+ channelId: (channelId || (threadId ? 'general' : 'general')) as string, // Default to general if none
73
+ threadId: threadId as string | undefined,
74
+ agentId: agentId as string | undefined,
75
+ runId: runId as string,
76
+ responseType: responseType as string | undefined,
77
+ };
78
+ };
79
+
80
+ const getClientKey = (channelId: string, threadId?: string) =>
81
+ threadId ? `${channelId}:${threadId}` : channelId;
82
+
83
+ const sendToClientKey = (clientKey: string, chunk: OpenBotEvent) => {
84
+ const threadClients = clients.get(clientKey);
85
+ if (!threadClients) return;
86
+ threadClients.forEach((client) => {
87
+ if (!client.writableEnded) {
88
+ client.write(`data: ${JSON.stringify(chunk)}\n\n`);
89
+ }
90
+ });
91
+ };
92
+
93
+ const buildActiveRunsSnapshot = (): ActiveRunsSnapshotEvent => {
94
+ const byChannel = new Map<string, { activeCount: number; agentIds: Set<string> }>();
95
+ for (const run of activeRuns.values()) {
96
+ const existing = byChannel.get(run.channelId) ?? {
97
+ activeCount: 0,
98
+ agentIds: new Set<string>(),
99
+ };
100
+ existing.activeCount += 1;
101
+ existing.agentIds.add(run.agentId);
102
+ byChannel.set(run.channelId, existing);
103
+ }
104
+ return {
105
+ type: 'agent:active-runs:snapshot',
106
+ data: {
107
+ channels: Array.from(byChannel.entries()).map(([channelId, value]) => ({
108
+ channelId,
109
+ activeCount: value.activeCount,
110
+ agentIds: Array.from(value.agentIds),
111
+ })),
112
+ },
113
+ };
114
+ };
115
+
116
+ app.use(cors());
117
+ app.use(express.json({ limit: '20mb' }));
118
+
119
+ app.get('/api/events', (req, res) => {
120
+ const { channelId, threadId } = getContext(req);
121
+ const clientKey = getClientKey(channelId, threadId);
122
+
123
+ // SSE response headers: keep the HTTP connection open and unbuffered.
124
+ res.setHeader('Content-Type', 'text/event-stream');
125
+ res.setHeader('Cache-Control', 'no-cache');
126
+ res.setHeader('Connection', 'keep-alive');
127
+ // Helpful behind proxies (for example nginx) to avoid response buffering.
128
+ res.setHeader('X-Accel-Buffering', 'no');
129
+ // Flush headers immediately so the browser moves from "connecting" to "open".
130
+ res.flushHeaders();
131
+
132
+ // Tell EventSource clients how long to wait before reconnecting.
133
+ res.write('retry: 3000\n');
134
+ // Initial comment frame so the stream has activity right after subscribe.
135
+ res.write(': connected\n\n');
136
+
137
+ // Track all active SSE subscribers for fan-out in /api/publish.
138
+ if (!clients.has(clientKey)) {
139
+ clients.set(clientKey, []);
140
+ }
141
+ clients.get(clientKey)!.push(res);
142
+
143
+ if (channelId === GLOBAL_CHANNEL_ID) {
144
+ const snapshot = buildActiveRunsSnapshot();
145
+ ensureEventId(snapshot);
146
+ res.write(`data: ${JSON.stringify(snapshot)}\n\n`);
147
+ }
148
+
149
+ // Keep connection alive through intermediaries that close idle streams.
150
+ const heartbeat = setInterval(() => {
151
+ if (!res.writableEnded) {
152
+ res.write(': keepalive\n\n');
153
+ }
154
+ }, 25000);
155
+
156
+ req.on('close', () => {
157
+ // Cleanup heartbeat + subscriber when the client disconnects.
158
+ clearInterval(heartbeat);
159
+ const threadClients = clients.get(clientKey);
160
+ if (threadClients) {
161
+ const index = threadClients.indexOf(res);
162
+ if (index !== -1) {
163
+ threadClients.splice(index, 1);
164
+ }
165
+ if (threadClients.length === 0) {
166
+ clients.delete(clientKey);
167
+ }
168
+ }
169
+ });
170
+ });
171
+
172
+ app.post('/api/publish', async (req, res) => {
173
+ const parseResult = publishEventSchema.safeParse(req.body);
174
+ if (!parseResult.success) {
175
+ res.status(400).json({
176
+ error: 'Invalid publish event payload',
177
+ details: parseResult.error.issues.map((issue) => issue.message),
178
+ });
179
+ return;
180
+ }
181
+
182
+ const event = parseResult.data as OpenBotEvent;
183
+
184
+ const { channelId, threadId, agentId, runId } = getContext(req);
185
+
186
+ if (!channelId || !channelId.trim()) {
187
+ res.status(400).json({ error: 'channelId is required' });
188
+ return;
189
+ }
190
+
191
+ const onEvent = async (chunk: OpenBotEvent, state?: OpenBotState) => {
192
+ ensureEventId(chunk);
193
+
194
+ const targetChannelId = state?.channelId || channelId;
195
+ const targetThreadId = state?.threadId || threadId;
196
+ const targetClientKey = getClientKey(targetChannelId, targetThreadId);
197
+
198
+ if (chunk.type === 'agent:run:start') {
199
+ activeRuns.set(chunk.data.runId, {
200
+ runId: chunk.data.runId,
201
+ channelId: chunk.data.channelId,
202
+ threadId: chunk.data.threadId,
203
+ agentId: chunk.data.agentId,
204
+ });
205
+ } else if (chunk.type === 'agent:run:end') {
206
+ activeRuns.delete(chunk.data.runId);
207
+ }
208
+
209
+ await storageService.storeEvent({
210
+ channelId: targetChannelId,
211
+ threadId: targetThreadId,
212
+ event: chunk,
213
+ });
214
+
215
+ sendToClientKey(targetClientKey, chunk);
216
+
217
+ if (chunk.type === 'agent:run:start' || chunk.type === 'agent:run:end') {
218
+ sendToClientKey(GLOBAL_CHANNEL_ID, chunk);
219
+ }
220
+ };
221
+
222
+ try {
223
+ const harness = new AgentHarness({
224
+ runId,
225
+ agentId: agentId || 'system',
226
+ channelId,
227
+ threadId,
228
+ onEvent,
229
+ });
230
+
231
+ await harness.dispatch(event);
232
+ res.sendStatus(200);
233
+ } catch (error) {
234
+ console.error('[publish] Failed to dispatch event', {
235
+ runId,
236
+ channelId,
237
+ threadId,
238
+ eventType: event.type,
239
+ error,
240
+ });
241
+ res.status(500).json({ error: 'Failed to process publish event' });
242
+ }
243
+ });
244
+
245
+ app.get('/api/state', async (req, res) => {
246
+ let event: OpenBotEvent;
247
+ try {
248
+ event = openBotEventFromQuery(req.query);
249
+ } catch (e) {
250
+ const message = e instanceof Error ? e.message : 'Invalid query';
251
+ res.status(400).json({ error: message });
252
+ return;
253
+ }
254
+
255
+ const { channelId, threadId, agentId, runId } = getContext(req);
256
+ const events: OpenBotEvent[] = [];
257
+
258
+ const onEvent = async (chunk: OpenBotEvent) => {
259
+ events.push(chunk);
260
+ };
261
+
262
+ try {
263
+ const harness = new AgentHarness({
264
+ runId,
265
+ agentId: agentId || 'system',
266
+ channelId,
267
+ threadId,
268
+ onEvent,
269
+ });
270
+
271
+ await harness.dispatch(event);
272
+ res.json({ events });
273
+ } catch (error) {
274
+ res.status(500).json({ error: 'Failed to process state request' });
275
+ }
276
+ });
277
+
278
+ app.listen(PORT, () => {
279
+ console.log(`\x1b[32mOpenBot server listening at http://localhost:${PORT}\x1b[0m`);
280
+ console.log(` - Events endpoint: GET /events (SSE)`);
281
+ console.log(` - Publish endpoint: POST /publish`);
282
+ console.log(` - State endpoint: GET /state`);
283
+ });
284
+ }