openbot 0.3.0 → 0.3.2
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/app/cli.js +1 -1
- package/dist/app/server.js +1 -4
- package/dist/bus/services.js +222 -15
- package/dist/harness/context.js +205 -26
- package/dist/harness/queue-processor.js +44 -110
- package/dist/harness/runtime-factory.js +11 -7
- package/dist/harness/todo-advance.js +93 -0
- package/dist/plugins/ai-sdk/index.js +0 -3
- package/dist/plugins/ai-sdk/runtime.js +78 -13
- package/dist/plugins/ai-sdk/system-prompt.js +18 -3
- package/dist/plugins/delegation/index.js +7 -46
- package/dist/plugins/memory/index.js +71 -0
- package/dist/plugins/storage-tools/index.js +2 -11
- package/dist/plugins/todo/index.js +54 -0
- package/dist/plugins/workflow/index.js +65 -0
- package/dist/registry/plugins.js +4 -2
- package/dist/services/memory.js +152 -0
- package/dist/services/storage.js +9 -31
- package/dist/workflow/service.js +106 -0
- package/dist/workflow/types.js +3 -0
- package/docs/agents.md +15 -1
- package/docs/plugins.md +0 -1
- package/package.json +1 -1
- package/src/app/cli.ts +1 -1
- package/src/app/server.ts +3 -4
- package/src/app/types.ts +140 -45
- package/src/bus/plugin.ts +0 -2
- package/src/bus/services.ts +258 -17
- package/src/bus/types.ts +13 -4
- package/src/harness/context.ts +233 -37
- package/src/harness/queue-processor.ts +54 -143
- package/src/harness/runtime-factory.ts +11 -7
- package/src/harness/todo-advance.ts +128 -0
- package/src/plugins/ai-sdk/index.ts +0 -3
- package/src/plugins/ai-sdk/runtime.ts +356 -298
- package/src/plugins/ai-sdk/system-prompt.ts +18 -4
- package/src/plugins/delegation/index.ts +7 -50
- package/src/plugins/memory/index.ts +85 -0
- package/src/plugins/storage-tools/index.ts +8 -19
- package/src/plugins/todo/index.ts +64 -0
- package/src/registry/plugins.ts +4 -3
- package/src/services/memory.ts +213 -0
- package/src/services/storage.ts +9 -49
|
@@ -0,0 +1,128 @@
|
|
|
1
|
+
import { TodoItem, TodoStatus } from '../app/types.js';
|
|
2
|
+
import type { Storage } from '../bus/types.js';
|
|
3
|
+
|
|
4
|
+
/** Stored on each todo and inlined into the next assignee's invoke payload. */
|
|
5
|
+
const RESULT_MAX_CHARS = 12000;
|
|
6
|
+
|
|
7
|
+
/**
|
|
8
|
+
* Shared helpers that drive the autonomous todo loop. The queue processor
|
|
9
|
+
* calls `advanceAfterRun` once per `agent:run:end`; that is the only place
|
|
10
|
+
* todos are completed and dispatched, which keeps the autonomous flow
|
|
11
|
+
* single-threaded and easy to reason about.
|
|
12
|
+
*/
|
|
13
|
+
|
|
14
|
+
export const readTodosFromState = (state: unknown): TodoItem[] => {
|
|
15
|
+
const raw = (state as Record<string, unknown> | undefined)?.todos;
|
|
16
|
+
return Array.isArray(raw) ? (raw as TodoItem[]) : [];
|
|
17
|
+
};
|
|
18
|
+
|
|
19
|
+
export function truncateTodoResult(text: string, maxChars = RESULT_MAX_CHARS): string | undefined {
|
|
20
|
+
const trimmed = text.trim();
|
|
21
|
+
if (!trimmed) return undefined;
|
|
22
|
+
if (trimmed.length <= maxChars) return trimmed;
|
|
23
|
+
return `${trimmed.slice(0, maxChars)}\n…[truncated]`;
|
|
24
|
+
}
|
|
25
|
+
|
|
26
|
+
export interface AdvanceResult {
|
|
27
|
+
/** Updated todo list (after marking finished + flipping next to in_progress). */
|
|
28
|
+
todos: TodoItem[];
|
|
29
|
+
/** Next agent to invoke, if any. */
|
|
30
|
+
handoff: { agentId: string; content: string; todoId: string } | null;
|
|
31
|
+
}
|
|
32
|
+
|
|
33
|
+
/**
|
|
34
|
+
* Apply a single advance step:
|
|
35
|
+
* 1. If a todo is `in_progress` and `assignee` matches the agent whose run
|
|
36
|
+
* just ended, mark it `done` and attach `result` from `lastOutput` when present.
|
|
37
|
+
* 2. Pick the next `pending` todo with an `assignee` and flip it to
|
|
38
|
+
* `in_progress`. That assignee gets handed off to; `invoke content` includes
|
|
39
|
+
* the previous step output when available so agents without short-term
|
|
40
|
+
* history still see prior work.
|
|
41
|
+
*
|
|
42
|
+
* If a todo is already `in_progress` and the just-ended agent wasn't its
|
|
43
|
+
* assignee, leave it alone — someone else is working.
|
|
44
|
+
*/
|
|
45
|
+
export function advanceTodos(
|
|
46
|
+
todos: TodoItem[],
|
|
47
|
+
endedAgentId: string,
|
|
48
|
+
lastOutput?: string,
|
|
49
|
+
): AdvanceResult {
|
|
50
|
+
const now = Date.now();
|
|
51
|
+
const truncated = truncateTodoResult(lastOutput ?? '');
|
|
52
|
+
|
|
53
|
+
let completedOutput: string | undefined;
|
|
54
|
+
|
|
55
|
+
let working = todos.map((t) => {
|
|
56
|
+
if (t.status === 'in_progress' && t.assignee === endedAgentId) {
|
|
57
|
+
completedOutput = truncated;
|
|
58
|
+
return {
|
|
59
|
+
...t,
|
|
60
|
+
status: 'done' as TodoStatus,
|
|
61
|
+
updatedAt: now,
|
|
62
|
+
...(truncated !== undefined ? { result: truncated } : {}),
|
|
63
|
+
};
|
|
64
|
+
}
|
|
65
|
+
return t;
|
|
66
|
+
});
|
|
67
|
+
|
|
68
|
+
if (working.some((t) => t.status === 'in_progress')) {
|
|
69
|
+
return { todos: working, handoff: null };
|
|
70
|
+
}
|
|
71
|
+
|
|
72
|
+
const idx = working.findIndex((t) => t.status === 'pending' && t.assignee);
|
|
73
|
+
if (idx === -1) return { todos: working, handoff: null };
|
|
74
|
+
|
|
75
|
+
const picked = working[idx];
|
|
76
|
+
working = working.map((t, i) =>
|
|
77
|
+
i === idx ? { ...t, status: 'in_progress' as TodoStatus, updatedAt: now } : t,
|
|
78
|
+
);
|
|
79
|
+
|
|
80
|
+
const invokeContent =
|
|
81
|
+
completedOutput !== undefined && completedOutput !== ''
|
|
82
|
+
? `${picked.content}\n\n--- Output from previous step ---\n${completedOutput}`
|
|
83
|
+
: picked.content;
|
|
84
|
+
|
|
85
|
+
return {
|
|
86
|
+
todos: working,
|
|
87
|
+
handoff: {
|
|
88
|
+
agentId: picked.assignee!,
|
|
89
|
+
content: invokeContent,
|
|
90
|
+
todoId: picked.id,
|
|
91
|
+
},
|
|
92
|
+
};
|
|
93
|
+
}
|
|
94
|
+
|
|
95
|
+
export async function advanceAfterRun(options: {
|
|
96
|
+
storage: Storage;
|
|
97
|
+
channelId: string;
|
|
98
|
+
threadId?: string;
|
|
99
|
+
endedAgentId: string;
|
|
100
|
+
lastAgentOutput?: string;
|
|
101
|
+
}): Promise<AdvanceResult['handoff']> {
|
|
102
|
+
const { storage, channelId, threadId, endedAgentId, lastAgentOutput } = options;
|
|
103
|
+
if (!threadId) return null;
|
|
104
|
+
|
|
105
|
+
const details = await storage.getThreadDetails({ channelId, threadId });
|
|
106
|
+
const todos = readTodosFromState(details?.state);
|
|
107
|
+
if (todos.length === 0) return null;
|
|
108
|
+
|
|
109
|
+
const { todos: nextList, handoff } = advanceTodos(todos, endedAgentId, lastAgentOutput);
|
|
110
|
+
|
|
111
|
+
const changed =
|
|
112
|
+
nextList.length !== todos.length ||
|
|
113
|
+
nextList.some((t, i) => {
|
|
114
|
+
const u = todos[i];
|
|
115
|
+
if (!u) return true;
|
|
116
|
+
return (
|
|
117
|
+
t.status !== u.status ||
|
|
118
|
+
t.updatedAt !== u.updatedAt ||
|
|
119
|
+
t.result !== u.result ||
|
|
120
|
+
t.assignee !== u.assignee ||
|
|
121
|
+
t.content !== u.content
|
|
122
|
+
);
|
|
123
|
+
});
|
|
124
|
+
if (changed) {
|
|
125
|
+
await storage.patchThreadState({ channelId, threadId, state: { todos: nextList } });
|
|
126
|
+
}
|
|
127
|
+
return handoff;
|
|
128
|
+
}
|
|
@@ -1,6 +1,5 @@
|
|
|
1
1
|
import type { Plugin } from '../../bus/plugin.js';
|
|
2
2
|
import { aiSdkRuntime } from './runtime.js';
|
|
3
|
-
import { AI_SDK_SYSTEM_PROMPT } from './system-prompt.js';
|
|
4
3
|
|
|
5
4
|
/**
|
|
6
5
|
* `ai-sdk` — generic LLM runtime plugin built on the Vercel AI SDK.
|
|
@@ -14,7 +13,6 @@ export const aiSdkPlugin: Plugin = {
|
|
|
14
13
|
name: 'AI SDK Runtime',
|
|
15
14
|
description:
|
|
16
15
|
'Generic LLM runtime built on the Vercel AI SDK. Consumes tools contributed by other plugins.',
|
|
17
|
-
defaultInstructions: AI_SDK_SYSTEM_PROMPT,
|
|
18
16
|
configSchema: {
|
|
19
17
|
type: 'object',
|
|
20
18
|
properties: {
|
|
@@ -34,7 +32,6 @@ export const aiSdkPlugin: Plugin = {
|
|
|
34
32
|
|
|
35
33
|
return aiSdkRuntime({
|
|
36
34
|
model,
|
|
37
|
-
system: agentDetails.instructions || AI_SDK_SYSTEM_PROMPT,
|
|
38
35
|
storage,
|
|
39
36
|
toolDefinitions: tools,
|
|
40
37
|
});
|