@meetopenbot/openbot 0.1.15 → 0.2.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/dist/history.js CHANGED
@@ -89,8 +89,9 @@ export function eventsToModelMessages(events) {
89
89
  case 'agent:invoke': {
90
90
  const invokeEvent = event;
91
91
  if (invokeEvent.data?.content && invokeEvent.data?.role) {
92
+ const role = invokeEvent.data.role === 'agent' ? 'user' : invokeEvent.data.role;
92
93
  messages.push({
93
- role: invokeEvent.data.role,
94
+ role,
94
95
  content: invokeEvent.data.content,
95
96
  });
96
97
  }
@@ -9,7 +9,7 @@ export const OPENBOT_SYSTEM_PROMPT = [
9
9
  "# CORE MISSION",
10
10
  "You are a high-level manager. Not every message is project work.",
11
11
  "- Questions, listings, setup, routing, and status stay in `#general`. Answer them from `## SPACES` and `## INSTALLED AGENTS`. Do not create a Space to look anything up.",
12
- "- For real project work (build, code, design, ops) you decide **where** (which Space) and **who** (which installed agent), then ask that agent, report back in plain language, and keep todos current.",
12
+ "- For real project work (build, code, design, ops) you decide **where** (which Space) and **who** (which installed agent), then ask that agent, close without restating their work, and keep todos current.",
13
13
  "",
14
14
  "# THE AGENT LOOP",
15
15
  "You operate in an iterative loop:",
@@ -17,7 +17,7 @@ export const OPENBOT_SYSTEM_PROMPT = [
17
17
  "2. **Place the work** (project work only): Stay in `#general` for questions, listings, setup, and routing. If this is `#general` and the ask is real project work, prefer an existing Space from `## SPACES` and `start_work` there before asking anyone. If no Space fits, propose a concrete name and why, then wait for the human to agree before creating one — unless they already named a new Space or explicitly asked you to create it. Never invent placeholder ids (`noop`, `temp`, `misc`, `untitled`). If you are already in the right Space, stay. Never `start_work` on a message that was already forwarded here.",
18
18
  "3. **Ask people**: Use `ask_agent` to send a message to a specialist. Write it as a request to a colleague, not as a tool invocation.",
19
19
  "4. **Wait**: Let the asked agent run in its own harness. Read their reply, then continue or ask someone else.",
20
- "5. **Submit Results**: Message the human with clear outcomes. Name who you asked and where the work lives.",
20
+ "5. **Close**: The human already saw the specialist speak in this thread. Do not restate, re-bullet, or paraphrase their findings. Name who you asked, then add only what is new — a decision, a conflict, a next step, or a caveat. If nothing is new, close in one or two sentences.",
21
21
  "6. **Enter Standby**: Idle until the next human message.",
22
22
  "",
23
23
  "# TASK TRACKING (TODOS)",
@@ -37,6 +37,7 @@ export const OPENBOT_SYSTEM_PROMPT = [
37
37
  "# OPERATIONAL GUIDELINES",
38
38
  '- **Spaces**: Work lives in Spaces (product name for channels). `#general` is dispatch — setup, questions, listings, routing, and "what should we do next." Project, coding, design, and ops work belongs in a dedicated Space. Prefer existing Spaces. Create a new Space only when the human asked for one, or they agreed after you suggested it.',
39
39
  "- **Asking**: You ask specialists with `ask_agent`. If `## MENTIONED AGENTS` is present, you MUST ask those people. You may also ask others if needed. If nobody is mentioned, pick the best match from `## INSTALLED AGENTS` and tell the human who you asked.",
40
+ "- **Closer**: After `ask_agent`, you are the closer, not a second author of their work. Don't repeat. Add or close. When several people replied, write only the synthesis or decision — not a recap of each.",
40
41
  "- **Missing people**: If no installed agent can do the job, tell the human who to install. Do not attempt the specialist work yourself.",
41
42
  "- **Hub-and-spoke**: Specialists cannot talk to each other. You pass context from one to the next.",
42
43
  "- **Durable Memory**: Use the `remember` tool to store important facts, preferences, or project details that should persist across sessions.",
@@ -46,6 +47,7 @@ export const OPENBOT_SYSTEM_PROMPT = [
46
47
  "- Confirm receipt quickly. For longer work, say where you are taking it and who you will ask.",
47
48
  "- Write in clean, continuous prose. Avoid excessive bullet points unless requested.",
48
49
  '- Talk about people, not tools: "I asked Claude Code to …" never "I used Claude Code to …".',
50
+ '- After one specialist replies and you agree, close like: "I asked Codex to check the diff. Workspace is clean — nothing to review." Never paste or re-list their last message.',
49
51
  "",
50
52
  "# ERROR HANDLING",
51
53
  "- Tool failures are provided as events. When an error occurs, do not panic or immediately ask the user for help.",
@@ -2,7 +2,7 @@ import { z } from "zod";
2
2
  import { randomUUID } from "node:crypto";
3
3
  const askAgentToolDefinitions = {
4
4
  ask_agent: {
5
- description: "Ask another installed agent to do work in their own harness. Write the prompt as a message to a colleague, not as a command to a tool.",
5
+ description: "Ask another installed agent to do work in their own harness. Write the prompt as a message to a colleague, not as a command to a tool. The human sees their reply in this thread; do not restate it when you close.",
6
6
  inputSchema: z.object({
7
7
  agentId: z
8
8
  .string()
@@ -40,7 +40,7 @@ async function* runAskedAgent(pluginContext, event, context, resultType) {
40
40
  event: {
41
41
  type: "agent:invoke",
42
42
  data: {
43
- role: "user",
43
+ role: "agent",
44
44
  content: prompt,
45
45
  agentId,
46
46
  },
@@ -210,13 +210,6 @@ export const startWorkPlugin = {
210
210
  userAvatarUrl: context.state.triggerEvent?.meta?.userAvatarUrl,
211
211
  },
212
212
  };
213
- if (typeof storage.storeEvent === "function") {
214
- await storage.storeEvent({
215
- channelId: spaceId,
216
- threadId,
217
- event: invokeEvent,
218
- });
219
- }
220
213
  await pluginContext.host.runAgent({
221
214
  runId: `sw_${randomUUID()}`,
222
215
  agentId: pluginContext.host.orchestratorAgentId,
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@meetopenbot/openbot",
3
- "version": "0.1.15",
3
+ "version": "0.2.0",
4
4
  "description": "OpenBot coordinator runtime: ask specialists, route work into Spaces, and track todos.",
5
5
  "type": "module",
6
6
  "main": "./dist/index.js",
@@ -18,12 +18,6 @@
18
18
  "files": [
19
19
  "dist"
20
20
  ],
21
- "scripts": {
22
- "build": "tsc && node ../../scripts/write-plugin-declaration.mjs",
23
- "dev": "tsc --watch --preserveWatchOutput",
24
- "typecheck": "tsc --noEmit",
25
- "prepack": "pnpm build"
26
- },
27
21
  "dependencies": {
28
22
  "@ai-sdk/anthropic": "^3.0.33",
29
23
  "@ai-sdk/openai": "^3.0.13",
@@ -34,5 +28,10 @@
34
28
  "devDependencies": {
35
29
  "@types/node": "^20.10.1",
36
30
  "typescript": "^5.9.3"
31
+ },
32
+ "scripts": {
33
+ "build": "tsc && node ../../scripts/write-plugin-declaration.mjs",
34
+ "dev": "tsc --watch --preserveWatchOutput",
35
+ "typecheck": "tsc --noEmit"
37
36
  }
38
- }
37
+ }
@@ -1,137 +0,0 @@
1
- import { randomUUID } from "node:crypto";
2
- import { z } from "zod";
3
- /**
4
- * `delegation` — allows agents to delegate tasks to other agents.
5
- *
6
- * Only the 'system' agent is allowed to delegate by default.
7
- * It uses runAgent to execute the delegated agent in its own isolated runtime,
8
- * bridging events back to the caller's stream.
9
- */
10
- const delegationToolDefinitions = {
11
- delegate_task: {
12
- description: "Delegate a specific task or question to another specialized agent.",
13
- inputSchema: z.object({
14
- agentId: z
15
- .string()
16
- .describe('The ID of the agent to delegate to (e.g., "researcher", "coder").'),
17
- prompt: z
18
- .string()
19
- .describe("The instructions or question for the delegated agent."),
20
- }),
21
- },
22
- };
23
- export const delegationPlugin = {
24
- id: "delegation",
25
- name: "Delegation",
26
- description: "Allows agents to call upon other agents to solve sub-tasks.",
27
- toolDefinitions: delegationToolDefinitions,
28
- factory: (pluginContext) => (builder) => {
29
- // Handle the tool execution
30
- builder.on("action:delegate_task", async function* (event, context) {
31
- const delegateEvent = event;
32
- // POLICY: Only the 'system' agent can delegate
33
- if (context.state.agentId !== pluginContext.host.orchestratorAgentId) {
34
- yield {
35
- type: "action:delegate_task:result",
36
- data: {
37
- success: false,
38
- error: "Only the system agent can delegate.",
39
- },
40
- meta: delegateEvent.meta,
41
- };
42
- return;
43
- }
44
- const { agentId, prompt } = delegateEvent.data;
45
- const toolCallId = delegateEvent.meta?.toolCallId;
46
- if (!toolCallId)
47
- return;
48
- const runAgent = pluginContext.host.runAgent;
49
- const runId = `dg_${randomUUID()}`;
50
- let lastAgentOutput = "";
51
- // Queue to bridge the async onEvent callback to this generator
52
- const eventQueue = [];
53
- let resolveNext = null;
54
- let isFinished = false;
55
- // Start the delegated agent in its own runtime.
56
- // We don't await this immediately so we can yield events as they arrive.
57
- const runPromise = runAgent({
58
- runId,
59
- agentId,
60
- event: {
61
- type: "agent:invoke",
62
- data: {
63
- role: "user",
64
- content: prompt,
65
- agentId: agentId,
66
- },
67
- meta: {
68
- channelId: context.state.channelId,
69
- threadId: context.state.threadId,
70
- parentAgentId: context.state.agentId,
71
- parentToolCallId: toolCallId,
72
- },
73
- },
74
- publicBaseUrl: pluginContext.publicBaseUrl,
75
- // Child events are re-yielded to the parent harness, which persists them once.
76
- persistEvents: false,
77
- onEvent: async (outEvent) => {
78
- // Enrich events with parent metadata so the UI can track the hierarchy
79
- const enrichedEvent = {
80
- ...outEvent,
81
- meta: {
82
- ...outEvent.meta,
83
- parentAgentId: context.state.agentId,
84
- parentToolCallId: toolCallId,
85
- },
86
- };
87
- eventQueue.push(enrichedEvent);
88
- if (outEvent.type === "agent:output") {
89
- lastAgentOutput = outEvent.data.content;
90
- }
91
- // Wake up the generator loop if it's waiting
92
- if (resolveNext) {
93
- resolveNext();
94
- resolveNext = null;
95
- }
96
- },
97
- })
98
- .catch((error) => {
99
- console.error(`[delegation] Error in delegated run ${runId}:`, error);
100
- })
101
- .finally(() => {
102
- isFinished = true;
103
- if (resolveNext) {
104
- resolveNext();
105
- resolveNext = null;
106
- }
107
- });
108
- // Yield events from the delegated agent as they arrive
109
- while (!isFinished || eventQueue.length > 0) {
110
- if (eventQueue.length === 0) {
111
- await new Promise((r) => {
112
- resolveNext = r;
113
- });
114
- }
115
- while (eventQueue.length > 0) {
116
- yield eventQueue.shift();
117
- }
118
- }
119
- // Ensure the run is fully complete (though isFinished already implies this)
120
- await runPromise;
121
- // Yield the result back to our own LLM runtime.
122
- yield {
123
- type: "action:delegate_task:result",
124
- data: {
125
- success: true,
126
- output: lastAgentOutput,
127
- },
128
- meta: {
129
- ...delegateEvent.meta,
130
- agentId: context.state.agentId,
131
- toolCallId: toolCallId,
132
- },
133
- };
134
- });
135
- },
136
- };
137
- export default delegationPlugin;