openbot 0.2.13 → 0.3.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 (80) hide show
  1. package/dist/agents/openbot/index.js +76 -0
  2. package/dist/agents/openbot/middleware/approval.js +132 -0
  3. package/dist/agents/openbot/runtime.js +289 -0
  4. package/dist/agents/openbot/system-prompt.js +32 -0
  5. package/dist/agents/openbot/tools/delegation.js +78 -0
  6. package/dist/agents/openbot/tools/mcp.js +99 -0
  7. package/dist/agents/openbot/tools/shell.js +91 -0
  8. package/dist/agents/openbot/tools/storage.js +75 -0
  9. package/dist/agents/openbot/tools/ui.js +176 -0
  10. package/dist/agents/system.js +20 -93
  11. package/dist/app/cli.js +1 -1
  12. package/dist/app/config.js +4 -1
  13. package/dist/app/server.js +15 -8
  14. package/dist/bus/agent-package.js +1 -0
  15. package/dist/bus/plugin.js +1 -0
  16. package/dist/bus/services.js +600 -0
  17. package/dist/bus/types.js +1 -0
  18. package/dist/harness/context.js +131 -0
  19. package/dist/harness/event-normalizer.js +59 -0
  20. package/dist/harness/orchestrator.js +27 -227
  21. package/dist/harness/process.js +25 -3
  22. package/dist/harness/queue-processor.js +227 -0
  23. package/dist/harness/runtime-factory.js +103 -0
  24. package/dist/plugins/ai-sdk/index.js +37 -0
  25. package/dist/plugins/ai-sdk/runtime.js +330 -0
  26. package/dist/plugins/ai-sdk/system-prompt.js +3 -0
  27. package/dist/plugins/ai-sdk.js +277 -87
  28. package/dist/plugins/approval/index.js +159 -0
  29. package/dist/plugins/approval.js +163 -0
  30. package/dist/plugins/delegation/index.js +79 -0
  31. package/dist/plugins/delegation.js +67 -11
  32. package/dist/plugins/mcp/index.js +108 -0
  33. package/dist/plugins/shell/index.js +99 -0
  34. package/dist/plugins/shell.js +123 -0
  35. package/dist/plugins/storage-tools/index.js +85 -0
  36. package/dist/plugins/storage.js +240 -5
  37. package/dist/plugins/ui/index.js +184 -0
  38. package/dist/plugins/ui.js +185 -21
  39. package/dist/registry/agents.js +138 -0
  40. package/dist/registry/plugins.js +91 -50
  41. package/dist/services/agent-packages.js +103 -0
  42. package/dist/services/plugins.js +98 -0
  43. package/dist/services/storage.js +360 -94
  44. package/docs/agents.md +39 -66
  45. package/docs/architecture.md +1 -1
  46. package/docs/plugins.md +70 -58
  47. package/docs/templates/AGENT.example.md +57 -0
  48. package/package.json +3 -2
  49. package/src/app/cli.ts +1 -1
  50. package/src/app/config.ts +14 -4
  51. package/src/app/server.ts +23 -10
  52. package/src/app/types.ts +385 -16
  53. package/src/assets/icon.svg +4 -1
  54. package/src/bus/plugin.ts +67 -0
  55. package/src/bus/services.ts +666 -0
  56. package/src/bus/types.ts +147 -0
  57. package/src/harness/context.ts +160 -0
  58. package/src/harness/event-normalizer.ts +82 -0
  59. package/src/harness/orchestrator.ts +35 -273
  60. package/src/harness/process.ts +28 -4
  61. package/src/harness/queue-processor.ts +309 -0
  62. package/src/harness/runtime-factory.ts +125 -0
  63. package/src/plugins/ai-sdk/index.ts +44 -0
  64. package/src/plugins/ai-sdk/runtime.ts +410 -0
  65. package/src/plugins/ai-sdk/system-prompt.ts +4 -0
  66. package/src/plugins/approval/index.ts +228 -0
  67. package/src/plugins/delegation/index.ts +94 -0
  68. package/src/plugins/mcp/index.ts +128 -0
  69. package/src/plugins/shell/index.ts +123 -0
  70. package/src/plugins/storage-tools/index.ts +101 -0
  71. package/src/plugins/ui/index.ts +227 -0
  72. package/src/registry/plugins.ts +106 -55
  73. package/src/services/plugins.ts +133 -0
  74. package/src/services/storage.ts +465 -137
  75. package/src/agents/system.ts +0 -112
  76. package/src/plugins/ai-sdk.ts +0 -197
  77. package/src/plugins/delegation.ts +0 -60
  78. package/src/plugins/mcp.ts +0 -154
  79. package/src/plugins/storage.ts +0 -725
  80. package/src/plugins/ui.ts +0 -57
package/docs/agents.md CHANGED
@@ -1,83 +1,56 @@
1
1
  # Agents
2
2
 
3
- Agents are specialized entities within the OpenBot platform, each designed to handle specific domains or tasks. The platform orchestrates these agents to solve complex problems through collaboration.
3
+ Agents are participants on the OpenBot bus. An agent is just a markdown file
4
+ (`AGENT.md`) that lists which **plugins** compose it. Plugins provide both the
5
+ LLM runtime and the tools the agent can call.
4
6
 
5
- ## Built-in Agents
7
+ ## Authoring an agent
6
8
 
7
- ### Orchestrator Agent (Default)
8
- The central intelligence of the platform. It manages the conversation flow, handles long-term memory, and coordinates other agents.
9
+ You define an agent with a YAML-fronted markdown file at
10
+ `~/.openbot/agents/<agentId>/AGENT.md`. The folder name is the agent id.
9
11
 
10
- ### OS Agent (`os`)
11
- Specialized in low-level system interactions. It uses the `shell` and `file-system` plugins to execute commands and manage files. (Implementation: `src/agents/system.ts`)
12
-
13
- ### Topic Agent (`topic`)
14
- A utility agent that observes completions and automatically generates concise titles for conversations to keep the workspace organized.
15
-
16
- ### Codex Agent (`codex`)
17
- A world-class software engineer agent. It assists with architectural decisions, refactoring, and debugging, with full access to the development environment.
18
-
19
- ## YAML Agents
20
-
21
- You can define custom agents using YAML files in `~/.openbot/agents/`.
22
-
23
- ### Installing Agents
24
-
25
- You can easily install official agents using the CLI:
26
-
27
- ```bash
28
- openbot add codex
29
- ```
30
-
31
- This will automatically download the agent and install it into your local agents directory.
32
-
33
- Example `codex.yaml`:
34
12
  ```yaml
35
- name: codex
36
- description: "A specialized agent for writing and refactoring code"
13
+ ---
14
+ name: Researcher
15
+ description: Web research and synthesis specialist.
37
16
  plugins:
38
- - name: file-system
39
- - name: shell
40
- prompt: |
41
- You are an expert software engineer.
42
- You use the provided tools to write high-quality code.
17
+ - id: ai-sdk
18
+ config:
19
+ model: anthropic/claude-3-5-sonnet-20240620
20
+ - id: mcp
21
+ - id: shell
22
+ - id: delegation
23
+ ---
24
+
25
+ You are a web research specialist. Use the available tools to gather and
26
+ synthesize information. Be concise and cite sources where relevant.
43
27
  ```
44
28
 
45
- YAML agents are automatically discovered and registered by OpenBot on startup.
46
-
47
- ## TS Agents (Packages)
29
+ The body below the frontmatter is the system prompt passed to the runtime
30
+ plugin as `agentDetails.instructions`.
48
31
 
49
- For more advanced use cases, you can create a full TypeScript package as an agent. This is useful if you need custom logic, additional event handlers, or private plugins that you don't want to expose globally.
32
+ ### Required: at least one runtime plugin
50
33
 
51
- Place these in `~/.openbot/agents/my-ts-agent/`.
34
+ A runtime plugin is one that handles `agent:invoke` (the LLM loop). Without
35
+ one, the agent will not respond to user input. Built-in runtime plugins:
52
36
 
53
- ### Structure
37
+ - `ai-sdk` — generic LLM runtime (Vercel AI SDK). Consumes tools from other
38
+ plugins listed alongside it.
39
+ - `claude-code` — runs Claude inside the Claude Agent SDK with its own tools.
40
+ - `gemini-cli` — spawns Google's `gemini` CLI in headless mode.
54
41
 
55
- - `package.json`: Standard npm package configuration.
56
- - `index.ts`: The entry point that exports a `TSAgentDefinition`.
42
+ `claude-code` and `gemini-cli` own their own tool loops, so attaching tool
43
+ plugins like `shell` or `mcp` to them has no effect. Pair tool plugins with
44
+ `ai-sdk`.
57
45
 
58
- ### Example `index.ts`
46
+ ## Built-in agent
59
47
 
60
- ```typescript
61
- import { TSAgentDefinition } from "openbot";
48
+ OpenBot ships a built-in `system` agent (the orchestrator) with the
49
+ `ai-sdk` runtime plus the standard tool plugins (storage, shell, mcp,
50
+ delegation, ui, approval). It cannot be deleted.
62
51
 
63
- export const agent: TSAgentDefinition = {
64
- name: "my-ts-agent",
65
- description: "An agent with custom TypeScript logic.",
66
- factory: ({ model }) => (builder) => {
67
- // 1. You can use standard plugins
68
- // 2. Or implement custom event handling logic here
69
- // 3. Finally, wire up an LLM loop for it
70
- builder.use(llmPlugin({
71
- model,
72
- system: "You are a specialized TS Agent...",
73
- toolDefinitions: { /* custom tools */ },
74
- // I/O defaults to standardized: user:input / agent:output
75
- }));
76
- },
77
- capabilities: {
78
- "do_something": "Performs a custom TS-driven action"
79
- }
80
- };
81
- ```
52
+ ## Installing community agents
82
53
 
83
- TS agents are automatically compiled and loaded on startup.
54
+ Marketplace entries reference plugin ids (built-in or npm package names).
55
+ Installing an agent ensures every referenced plugin is available locally,
56
+ fetching unknown ids from npm into `~/.openbot/plugins/<id>/` on first use.
@@ -16,7 +16,7 @@ The orchestrator is the central dispatcher within the harness. It receives user
16
16
 
17
17
  ### 2. Agent Registry
18
18
  A dynamic registry that manages all available agents. Agents can be:
19
- - **Built-in**: Core agents compiled into the platform (e.g., `src/agents/system.ts`).
19
+ - **Built-in**: Core agent packages shipped with the repo (e.g. `src/agents/openbot/`).
20
20
  - **YAML-based**: Rapidly defined agents in `~/.openbot/agents/*/AGENT.md`.
21
21
  - **TS Packages**: Advanced agents with custom logic in `~/.openbot/agents/*/index.ts`.
22
22
 
package/docs/plugins.md CHANGED
@@ -1,77 +1,89 @@
1
1
  # Plugins
2
2
 
3
- Plugins are the building blocks of the OpenBot platform's capabilities. They provide the tools and logic that the orchestrator and specialized agents use to interact with the world.
3
+ A **Plugin** is the single extension unit on the OpenBot bus. Every agent is
4
+ just a list of plugins. The bus does not distinguish between "runtime" and
5
+ "tool" plugins — those are roles defined entirely by which events a plugin
6
+ subscribes to and emits.
4
7
 
5
- ## Built-in Plugins
8
+ - **Runtime plugins** handle `agent:invoke` and run an LLM loop. They may
9
+ consume tools contributed by other plugins via `PluginContext.tools`.
10
+ - **Tool plugins** expose `toolDefinitions` and handle the corresponding
11
+ `action:<tool>` events.
12
+ - **Middleware plugins** observe events and re-emit them with extra logic
13
+ (e.g. the `approval` plugin gates protected actions).
6
14
 
7
- - **ai-sdk**: Integration with AI providers (OpenAI, Anthropic, etc.).
8
- - **storage**: Local file-based persistence for events and state.
9
- - **mcp**: Model Context Protocol integration.
10
- - **delegation**: Allows agents to delegate tasks to other agents.
11
- - **ui**: Server-driven UI components.
15
+ ## Plugin contract
12
16
 
13
- ## In-depth: Approval Plugin
14
-
15
- The `approval` plugin is a safety layer that can be added to any agent. It intercepts specified actions and suspends execution until a user manually approves or denies the action via the UI.
16
-
17
- ### Configuration
18
-
19
- Add it to your `AGENT.md` frontmatter:
17
+ ```ts
18
+ export interface Plugin {
19
+ id: string;
20
+ name: string;
21
+ description: string;
22
+ image?: string;
23
+ defaultInstructions?: string;
24
+ configSchema?: ConfigSchema;
25
+ toolDefinitions?: Record<string, ToolDefinition>;
26
+ factory: (context: PluginContext) => MelonyPlugin;
27
+ }
20
28
 
21
- ```yaml
22
- plugins:
23
- - name: approval
24
- config:
25
- rules:
26
- - action: "action:executeCommand"
27
- message: "The agent wants to execute a terminal command."
28
- detailKeys: ["command", "cwd"]
29
- hiddenKeys: ["env"]
30
- - action: "action:writeFile"
31
- message: "The agent wants to modify a file."
32
- detailKeys: ["path"]
29
+ export interface PluginContext {
30
+ agentId: string;
31
+ agentDetails: AgentDetails;
32
+ config: Record<string, unknown>; // from AGENT.md plugins[].config
33
+ storage: Storage;
34
+ tools: Record<string, ToolDefinition>; // merged from all tool plugins
35
+ }
33
36
  ```
34
37
 
35
- - `action`: The event type prefix to intercept (e.g., `action:executeCommand`).
36
- - `message`: Optional custom message to display in the approval UI.
37
- - `detailKeys`: Optional ordered list of event data keys to show in the compact details block.
38
- - `hiddenKeys`: Optional list of keys to redact from details and full payload.
38
+ The agent loader collects `toolDefinitions` from every plugin attached to the
39
+ same agent into a single map and passes it to every plugin via `context.tools`.
40
+ Runtime plugins read it; tool plugins ignore it. First plugin wins on tool
41
+ name collisions.
39
42
 
40
- ### Approval UI Details Payload
43
+ ## Built-in plugins
41
44
 
42
- The approval card keeps the same visual widget but now renders structured details for clearer decisions.
45
+ | Id | Role | Notes |
46
+ | --------------- | ---------- | --------------------------------------------------------- |
47
+ | `ai-sdk` | Runtime | Generic LLM loop on Vercel AI SDK; consumes external tools |
48
+ | `claude-code` | Runtime | Claude Agent SDK; owns its own tool loop |
49
+ | `gemini-cli` | Runtime | Google `gemini` CLI in headless mode |
50
+ | `shell` | Tool | `shell_exec` |
51
+ | `mcp` | Tool | `mcp_list_tools`, `mcp_call` |
52
+ | `delegation` | Tool | `handoff`, `delegate` |
53
+ | `storage-tools` | Tool | `create_channel`, `patch_*`, `create_variable`, ... |
54
+ | `ui` | Tool | `render_ui_widget` |
55
+ | `approval` | Middleware | Gates protected actions behind a UI confirmation widget |
43
56
 
44
- Payload shape sent to the widget:
57
+ ## Community plugins
45
58
 
46
- ```ts
47
- {
48
- summary: string;
49
- details?: Array<{ label: string; value: string }>;
50
- rawPayload?: string;
51
- }
52
- ```
53
-
54
- What users now see before approving:
55
- - Action label and event type.
56
- - A compact details list based on `detailKeys` (or auto-derived keys if omitted).
57
- - A sanitized full action payload (sensitive keys are redacted and very large values are truncated).
59
+ A community plugin is just an npm package whose default export matches the
60
+ `Plugin` interface. Reference it by its npm package name in AGENT.md:
58
61
 
59
- ## Shared Plugins
62
+ ```yaml
63
+ plugins:
64
+ - id: openbot-plugin-search
65
+ config:
66
+ provider: tavily
67
+ ```
60
68
 
61
- Shared plugins are community-contributed or user-defined plugins that can be installed into `~/.openbot/plugins/`. Once installed, they are automatically registered in the Plugin Registry and become available to any agent that references them.
69
+ On first use OpenBot installs the package into
70
+ `~/.openbot/plugins/<npm-name>/` (scoped packages live under
71
+ `~/.openbot/plugins/@scope/<name>/`).
62
72
 
63
- ### Installing a Plugin
73
+ ## Approval plugin
64
74
 
65
- For official plugins, you can use the `add` command:
75
+ The `approval` plugin reads its rules from per-agent config:
66
76
 
67
- ```bash
68
- openbot add search
77
+ ```yaml
78
+ plugins:
79
+ - id: approval
80
+ config:
81
+ rules:
82
+ - action: action:shell_exec
83
+ message: The agent wants to run a terminal command.
84
+ detailKeys: [command, cwd, shell, timeoutMs]
85
+ hiddenKeys: [env]
69
86
  ```
70
87
 
71
- ## Creating a Plugin
72
-
73
- A plugin is typically a Node.js package that exports:
74
- - `name`: Unique identifier.
75
- - `description`: What the plugin does.
76
- - `toolDefinitions`: Zod-based definitions for the tools it provides.
77
- - `factory`: A function that initializes the plugin logic.
88
+ If `rules` is omitted, sensible defaults are applied (currently: gate
89
+ `action:shell_exec`).
@@ -0,0 +1,57 @@
1
+ ---
2
+ # OpenBot agent profile (YAML frontmatter)
3
+ #
4
+ # File location: ~/.openbot/agents/<agentId>/AGENT.md
5
+ # The agent id is the folder name (<agentId>), not a field in this file.
6
+ #
7
+ name: Example Agent
8
+ description: One-line description shown in agent pickers and lists.
9
+
10
+ # Plugins compose the agent. Order matters for tool collisions (first wins).
11
+ # At least one plugin must handle `agent:invoke` (a "runtime" plugin like
12
+ # `ai-sdk`, `claude-code`, or `gemini-cli`). Tool plugins like `shell`, `mcp`,
13
+ # `delegation`, `storage-tools`, and `ui` contribute tools to whichever runtime
14
+ # plugin can consume them.
15
+ #
16
+ # Built-in plugin ids: ai-sdk, claude-code, gemini-cli, shell, mcp, delegation,
17
+ # storage-tools, ui, approval.
18
+ #
19
+ # Community plugins are referenced by their npm package name (e.g.
20
+ # `openbot-plugin-search` or `@scope/openbot-plugin-foo`) and are auto-installed
21
+ # on first use into ~/.openbot/plugins/<id>/.
22
+ plugins:
23
+ - id: ai-sdk
24
+ config:
25
+ model: openai/gpt-4o-mini
26
+ - id: shell
27
+ - id: mcp
28
+ - id: delegation
29
+ - id: storage-tools
30
+ - id: ui
31
+ - id: approval
32
+ config:
33
+ rules:
34
+ - action: action:shell_exec
35
+ message: The agent wants to run a terminal command.
36
+ detailKeys: [command, cwd, shell, timeoutMs]
37
+ hiddenKeys: [env]
38
+ ---
39
+
40
+ <!--
41
+ Everything below the closing --- is the agent instructions (system prompt body).
42
+ It is stored as markdown and passed to runtime plugins as agentDetails.instructions.
43
+ -->
44
+
45
+ # Role
46
+
47
+ You are **Example Agent**, a specialist for [describe domain here].
48
+
49
+ ## Behaviour
50
+
51
+ - Be concise unless the user asks for depth.
52
+ - When unsure, ask a clarifying question instead of guessing.
53
+
54
+ ## Scope
55
+
56
+ - **In scope:** [list what you handle]
57
+ - **Out of scope:** [defer to another agent for…]
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "openbot",
3
- "version": "0.2.13",
3
+ "version": "0.3.0",
4
4
  "private": false,
5
5
  "type": "module",
6
6
  "engines": {
@@ -12,11 +12,12 @@
12
12
  "start": "node dist/app/cli.js start"
13
13
  },
14
14
  "bin": {
15
- "openbot": "./dist/app/cli.js start"
15
+ "openbot": "./dist/app/cli.js"
16
16
  },
17
17
  "dependencies": {
18
18
  "@ai-sdk/anthropic": "^3.0.33",
19
19
  "@ai-sdk/openai": "^3.0.13",
20
+ "@anthropic-ai/claude-agent-sdk": "^0.2.138",
20
21
  "@modelcontextprotocol/sdk": "^1.29.0",
21
22
  "@types/cors": "^2.8.19",
22
23
  "ai": "^6.0.42",
package/src/app/cli.ts CHANGED
@@ -25,7 +25,7 @@ function checkNodeVersion() {
25
25
 
26
26
  checkNodeVersion();
27
27
 
28
- program.name('openbot').description('OpenBot CLI').version('0.2.13');
28
+ program.name('openbot').description('OpenBot CLI').version('0.3.0');
29
29
 
30
30
  program
31
31
  .command('start')
package/src/app/config.ts CHANGED
@@ -10,11 +10,13 @@ export interface OpenBotconfig {
10
10
  baseDir?: string;
11
11
  port?: number;
12
12
  mcpServers?: MCPServerConfig[];
13
- globalPlugins?: PluginSpec[];
13
+ /**
14
+ * Overrides the default public marketplace registry URL. If omitted or blank,
15
+ * {@link DEFAULT_MARKETPLACE_REGISTRY_URL} is used.
16
+ */
17
+ marketplaceRegistryUrl?: string;
14
18
  }
15
19
 
16
- export type PluginSpec = string | { name: string; config?: Record<string, unknown> };
17
-
18
20
  export interface MCPServerConfig {
19
21
  id: string;
20
22
  command: string;
@@ -36,6 +38,10 @@ export const DEFAULT_CHANNELS_DIR = 'channels';
36
38
  export const CONFIG_FILE = 'config.json';
37
39
  export const VARIABLES_FILE = 'variables.json';
38
40
 
41
+ /** Public agent registry used when `marketplaceRegistryUrl` is not set. */
42
+ export const DEFAULT_MARKETPLACE_REGISTRY_URL =
43
+ 'https://raw.githubusercontent.com/meetopenbot/openbot-registry/main/registry.json';
44
+
39
45
  export function resolvePath(p: string) {
40
46
  return p.startsWith('~/') ? path.join(os.homedir(), p.slice(2)) : path.resolve(p);
41
47
  }
@@ -72,7 +78,11 @@ export function isConfigured(): boolean {
72
78
  }
73
79
 
74
80
  export function loadVariables(): { version: number; variables: StoredVariable[] } {
75
- const variablesPath = path.join(resolvePath(DEFAULT_BASE_DIR), VARIABLES_FILE);
81
+ const config = loadConfig();
82
+ const variablesPath = path.join(
83
+ resolvePath(config.baseDir || DEFAULT_BASE_DIR),
84
+ VARIABLES_FILE,
85
+ );
76
86
  if (fs.existsSync(variablesPath)) {
77
87
  return JSON.parse(fs.readFileSync(variablesPath, 'utf-8')) as {
78
88
  version: number;
package/src/app/server.ts CHANGED
@@ -4,8 +4,11 @@ import cors from 'cors';
4
4
  import z from 'zod';
5
5
  import path from 'path';
6
6
  import fs from 'fs/promises';
7
+ import { createRequire } from 'module';
8
+ const require = createRequire(import.meta.url);
9
+ const pkg = require('../../package.json');
7
10
  import { generateId } from 'melony';
8
- import { DEFAULT_BASE_DIR, loadConfig, loadVariables, resolvePath } from '../app/config.js';
11
+ import { DEFAULT_BASE_DIR, loadConfig, resolvePath } from '../app/config.js';
9
12
  import { ActiveRunsSnapshotEvent, OpenBotEvent, OpenBotState } from './types.js';
10
13
  import { processService } from '../harness/process.js';
11
14
  import { storageService } from '../services/storage.js';
@@ -28,9 +31,7 @@ export async function startServer(options: ServerOptions = {}) {
28
31
  .passthrough();
29
32
 
30
33
  const config = loadConfig();
31
- const variables = loadVariables();
32
-
33
- processService.applyVariablesToProcessEnv(variables.variables);
34
+ processService.syncWorkspaceVariablesToProcessEnv();
34
35
 
35
36
  const baseDir = config.baseDir || DEFAULT_BASE_DIR;
36
37
  const openBotDir = resolvePath(baseDir);
@@ -79,6 +80,8 @@ export async function startServer(options: ServerOptions = {}) {
79
80
 
80
81
  const getClientKey = (channelId: string, threadId?: string) =>
81
82
  threadId ? `${channelId}:${threadId}` : channelId;
83
+ const getRunKey = (runId: string, agentId: string, channelId: string, threadId?: string) =>
84
+ `${runId}:${agentId}:${channelId}:${threadId || ''}`;
82
85
 
83
86
  const sendToClientKey = (clientKey: string, chunk: OpenBotEvent) => {
84
87
  const threadClients = clients.get(clientKey);
@@ -116,6 +119,10 @@ export async function startServer(options: ServerOptions = {}) {
116
119
  app.use(cors());
117
120
  app.use(express.json({ limit: '20mb' }));
118
121
 
122
+ app.get('/api/health', (req, res) => {
123
+ res.json({ status: 'ok', version: pkg.version });
124
+ });
125
+
119
126
  app.get('/api/events', (req, res) => {
120
127
  const { channelId, threadId } = getContext(req);
121
128
  const clientKey = getClientKey(channelId, threadId);
@@ -196,14 +203,19 @@ export async function startServer(options: ServerOptions = {}) {
196
203
  const targetClientKey = getClientKey(targetChannelId, targetThreadId);
197
204
 
198
205
  if (chunk.type === 'agent:run:start') {
199
- activeRuns.set(chunk.data.runId, {
206
+ activeRuns.set(
207
+ getRunKey(chunk.data.runId, chunk.data.agentId, chunk.data.channelId, chunk.data.threadId),
208
+ {
200
209
  runId: chunk.data.runId,
201
210
  channelId: chunk.data.channelId,
202
211
  threadId: chunk.data.threadId,
203
212
  agentId: chunk.data.agentId,
204
- });
213
+ },
214
+ );
205
215
  } else if (chunk.type === 'agent:run:end') {
206
- activeRuns.delete(chunk.data.runId);
216
+ activeRuns.delete(
217
+ getRunKey(chunk.data.runId, chunk.data.agentId, chunk.data.channelId, chunk.data.threadId),
218
+ );
207
219
  }
208
220
 
209
221
  await storageService.storeEvent({
@@ -277,8 +289,9 @@ export async function startServer(options: ServerOptions = {}) {
277
289
 
278
290
  app.listen(PORT, () => {
279
291
  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`);
292
+ console.log(` - Health endpoint: GET /health`);
293
+ console.log(` - Events endpoint: GET /api/events (SSE)`);
294
+ console.log(` - Publish endpoint: POST /api/publish`);
295
+ console.log(` - State endpoint: GET /api/state`);
283
296
  });
284
297
  }