openbot 0.3.1 → 0.3.3
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 +106 -10
- package/dist/harness/context.js +66 -6
- 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 +4 -11
- package/dist/plugins/ai-sdk/system-prompt.js +18 -3
- package/dist/plugins/delegation/index.js +7 -46
- 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 +2 -2
- package/dist/services/storage.js +3 -31
- package/dist/workflow/service.js +106 -0
- package/dist/workflow/types.js +3 -0
- 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 +80 -45
- package/src/bus/plugin.ts +0 -2
- package/src/bus/services.ts +133 -12
- package/src/bus/types.ts +0 -4
- package/src/harness/context.ts +73 -10
- 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 +284 -300
- package/src/plugins/ai-sdk/system-prompt.ts +18 -4
- package/src/plugins/delegation/index.ts +7 -50
- package/src/plugins/storage-tools/index.ts +8 -19
- package/src/plugins/todo/index.ts +64 -0
- package/src/registry/plugins.ts +2 -3
- package/src/services/storage.ts +2 -49
package/src/app/types.ts
CHANGED
|
@@ -290,7 +290,6 @@ export type PatchThreadDetailsEvent = BaseEvent & {
|
|
|
290
290
|
type: 'action:patch_thread_details';
|
|
291
291
|
data: {
|
|
292
292
|
state?: Record<string, unknown>;
|
|
293
|
-
spec?: string;
|
|
294
293
|
};
|
|
295
294
|
};
|
|
296
295
|
|
|
@@ -298,7 +297,7 @@ export type PatchThreadDetailsResultEvent = BaseEvent & {
|
|
|
298
297
|
type: 'action:patch_thread_details:result';
|
|
299
298
|
data: {
|
|
300
299
|
success: boolean;
|
|
301
|
-
updatedFields: ('state'
|
|
300
|
+
updatedFields: ('state')[];
|
|
302
301
|
};
|
|
303
302
|
};
|
|
304
303
|
|
|
@@ -381,7 +380,6 @@ export type CreateThreadEvent = BaseEvent & {
|
|
|
381
380
|
type: 'action:create_thread';
|
|
382
381
|
data: {
|
|
383
382
|
threadTitle: string;
|
|
384
|
-
spec?: string;
|
|
385
383
|
initialState?: Record<string, unknown>;
|
|
386
384
|
};
|
|
387
385
|
meta: {
|
|
@@ -570,34 +568,7 @@ export type HandoffResultEvent = BaseEvent & {
|
|
|
570
568
|
};
|
|
571
569
|
};
|
|
572
570
|
|
|
573
|
-
|
|
574
|
-
type: 'action:delegate';
|
|
575
|
-
data: {
|
|
576
|
-
agentId: string;
|
|
577
|
-
content: string;
|
|
578
|
-
};
|
|
579
|
-
meta?: {
|
|
580
|
-
toolCallId?: string;
|
|
581
|
-
[key: string]: any;
|
|
582
|
-
};
|
|
583
|
-
};
|
|
584
|
-
|
|
585
|
-
export type DelegateResultEvent = BaseEvent & {
|
|
586
|
-
type: 'action:delegate:result';
|
|
587
|
-
data: {
|
|
588
|
-
success: boolean;
|
|
589
|
-
agentId: string;
|
|
590
|
-
summary: string;
|
|
591
|
-
};
|
|
592
|
-
meta: {
|
|
593
|
-
toolCallId: string;
|
|
594
|
-
agentId: string;
|
|
595
|
-
threadId?: string;
|
|
596
|
-
[key: string]: any;
|
|
597
|
-
};
|
|
598
|
-
};
|
|
599
|
-
|
|
600
|
-
/** Internal routing: delegation plugin → orchestrator only (not stored or broadcast). */
|
|
571
|
+
/** Internal routing: handoff plugin → orchestrator only (not stored or broadcast). */
|
|
601
572
|
export type HandoffRequestEvent = BaseEvent & {
|
|
602
573
|
type: 'handoff:request';
|
|
603
574
|
data: {
|
|
@@ -607,16 +578,6 @@ export type HandoffRequestEvent = BaseEvent & {
|
|
|
607
578
|
meta?: Record<string, unknown>;
|
|
608
579
|
};
|
|
609
580
|
|
|
610
|
-
/** Internal routing: delegation plugin → orchestrator only (not stored or broadcast). */
|
|
611
|
-
export type DelegationRequestEvent = BaseEvent & {
|
|
612
|
-
type: 'delegation:request';
|
|
613
|
-
data: {
|
|
614
|
-
agentId: string;
|
|
615
|
-
content: string;
|
|
616
|
-
};
|
|
617
|
-
meta?: Record<string, unknown>;
|
|
618
|
-
};
|
|
619
|
-
|
|
620
581
|
export type MCPListToolsEvent = BaseEvent & {
|
|
621
582
|
type: 'action:mcp_list_tools';
|
|
622
583
|
data: {
|
|
@@ -820,6 +781,79 @@ export type ForgetResultEvent = BaseEvent & {
|
|
|
820
781
|
};
|
|
821
782
|
};
|
|
822
783
|
|
|
784
|
+
export type TodoStatus = 'pending' | 'in_progress' | 'done' | 'cancelled';
|
|
785
|
+
|
|
786
|
+
/**
|
|
787
|
+
* A single unit of work tracked in thread state. Todos are owned by the
|
|
788
|
+
* system (bus services); agents can only mutate them by calling the
|
|
789
|
+
* `todo_write` / `todo_update` tools so every change is observable on the
|
|
790
|
+
* event stream and audit-friendly.
|
|
791
|
+
*/
|
|
792
|
+
export interface TodoItem {
|
|
793
|
+
id: string;
|
|
794
|
+
content: string;
|
|
795
|
+
status: TodoStatus;
|
|
796
|
+
/** Optional agent id responsible for this item — drives autonomous handoffs. */
|
|
797
|
+
assignee?: string;
|
|
798
|
+
/** Agent id that created the todo (or "system"). */
|
|
799
|
+
createdBy: string;
|
|
800
|
+
createdAt: number;
|
|
801
|
+
updatedAt: number;
|
|
802
|
+
/**
|
|
803
|
+
* Captured final reply when this item reaches `done` (last `agent:output`
|
|
804
|
+
* from the assignee for that run). Lets downstream agents rely on thread
|
|
805
|
+
* state instead of merged short-term messages.
|
|
806
|
+
*/
|
|
807
|
+
result?: string;
|
|
808
|
+
}
|
|
809
|
+
|
|
810
|
+
export type TodoWriteInput = {
|
|
811
|
+
id?: string;
|
|
812
|
+
content: string;
|
|
813
|
+
status?: TodoStatus;
|
|
814
|
+
assignee?: string;
|
|
815
|
+
};
|
|
816
|
+
|
|
817
|
+
export type TodoWriteEvent = BaseEvent & {
|
|
818
|
+
type: 'action:todo_write';
|
|
819
|
+
data: {
|
|
820
|
+
todos: TodoWriteInput[];
|
|
821
|
+
};
|
|
822
|
+
meta?: { toolCallId?: string; agentId?: string; threadId?: string };
|
|
823
|
+
};
|
|
824
|
+
|
|
825
|
+
export type TodoWriteResultEvent = BaseEvent & {
|
|
826
|
+
type: 'action:todo_write:result';
|
|
827
|
+
data: {
|
|
828
|
+
success: boolean;
|
|
829
|
+
todos: TodoItem[];
|
|
830
|
+
error?: string;
|
|
831
|
+
};
|
|
832
|
+
meta?: { toolCallId?: string; agentId?: string; threadId?: string };
|
|
833
|
+
};
|
|
834
|
+
|
|
835
|
+
export type TodoUpdateEvent = BaseEvent & {
|
|
836
|
+
type: 'action:todo_update';
|
|
837
|
+
data: {
|
|
838
|
+
id: string;
|
|
839
|
+
status?: TodoStatus;
|
|
840
|
+
content?: string;
|
|
841
|
+
assignee?: string;
|
|
842
|
+
};
|
|
843
|
+
meta?: { toolCallId?: string; agentId?: string; threadId?: string };
|
|
844
|
+
};
|
|
845
|
+
|
|
846
|
+
export type TodoUpdateResultEvent = BaseEvent & {
|
|
847
|
+
type: 'action:todo_update:result';
|
|
848
|
+
data: {
|
|
849
|
+
success: boolean;
|
|
850
|
+
todo?: TodoItem;
|
|
851
|
+
todos: TodoItem[];
|
|
852
|
+
error?: string;
|
|
853
|
+
};
|
|
854
|
+
meta?: { toolCallId?: string; agentId?: string; threadId?: string };
|
|
855
|
+
};
|
|
856
|
+
|
|
823
857
|
export type OpenBotEvent =
|
|
824
858
|
| UserInputEvent
|
|
825
859
|
| AgentInvokeEvent
|
|
@@ -879,10 +913,7 @@ export type OpenBotEvent =
|
|
|
879
913
|
| UIWidgetResponseEvent
|
|
880
914
|
| HandoffEvent
|
|
881
915
|
| HandoffResultEvent
|
|
882
|
-
| DelegateEvent
|
|
883
|
-
| DelegateResultEvent
|
|
884
916
|
| HandoffRequestEvent
|
|
885
|
-
| DelegationRequestEvent
|
|
886
917
|
| MCPListToolsEvent
|
|
887
918
|
| MCPListToolsResultEvent
|
|
888
919
|
| MCPCallEvent
|
|
@@ -902,4 +933,8 @@ export type OpenBotEvent =
|
|
|
902
933
|
| RecallEvent
|
|
903
934
|
| RecallResultEvent
|
|
904
935
|
| ForgetEvent
|
|
905
|
-
| ForgetResultEvent
|
|
936
|
+
| ForgetResultEvent
|
|
937
|
+
| TodoWriteEvent
|
|
938
|
+
| TodoWriteResultEvent
|
|
939
|
+
| TodoUpdateEvent
|
|
940
|
+
| TodoUpdateResultEvent;
|
package/src/bus/plugin.ts
CHANGED
|
@@ -56,8 +56,6 @@ export interface Plugin {
|
|
|
56
56
|
name: string;
|
|
57
57
|
description: string;
|
|
58
58
|
image?: string;
|
|
59
|
-
/** Optional system-prompt body suggested when this plugin is used as the runtime. */
|
|
60
|
-
defaultInstructions?: string;
|
|
61
59
|
/** JSON-schema-like description of `config` accepted in AGENT.md `plugins[].config`. */
|
|
62
60
|
configSchema?: ConfigSchema;
|
|
63
61
|
/** Tool definitions contributed to any runtime plugin attached to the same agent. */
|
package/src/bus/services.ts
CHANGED
|
@@ -1,11 +1,45 @@
|
|
|
1
1
|
import { MelonyPlugin } from 'melony';
|
|
2
2
|
import { DEFAULT_MARKETPLACE_REGISTRY_URL, loadConfig } from '../app/config.js';
|
|
3
|
-
import {
|
|
3
|
+
import {
|
|
4
|
+
OpenBotEvent,
|
|
5
|
+
OpenBotState,
|
|
6
|
+
MemoryScopeAlias,
|
|
7
|
+
TodoItem,
|
|
8
|
+
TodoStatus,
|
|
9
|
+
TodoWriteInput,
|
|
10
|
+
} from '../app/types.js';
|
|
4
11
|
import type { PluginRef } from './plugin.js';
|
|
5
12
|
import { Storage } from './types.js';
|
|
6
13
|
import { storageService } from '../services/storage.js';
|
|
7
14
|
import { pluginService } from '../services/plugins.js';
|
|
8
15
|
|
|
16
|
+
const readTodos = (state: OpenBotState): TodoItem[] => {
|
|
17
|
+
const raw = (state.threadDetails?.state as Record<string, unknown> | undefined)?.todos;
|
|
18
|
+
return Array.isArray(raw) ? (raw as TodoItem[]) : [];
|
|
19
|
+
};
|
|
20
|
+
|
|
21
|
+
let todoCounter = 0;
|
|
22
|
+
const newTodoId = (now: number, idx: number): string =>
|
|
23
|
+
`todo_${now.toString(36)}_${(todoCounter++).toString(36)}_${idx}`;
|
|
24
|
+
|
|
25
|
+
async function persistTodos(
|
|
26
|
+
storage: Storage,
|
|
27
|
+
state: OpenBotState,
|
|
28
|
+
todos: TodoItem[],
|
|
29
|
+
): Promise<void> {
|
|
30
|
+
if (!state.threadId) throw new Error('No active thread');
|
|
31
|
+
await storage.patchThreadState({
|
|
32
|
+
channelId: state.channelId,
|
|
33
|
+
threadId: state.threadId,
|
|
34
|
+
state: { todos },
|
|
35
|
+
});
|
|
36
|
+
state.threadDetails = await storage.getThreadDetails({
|
|
37
|
+
channelId: state.channelId,
|
|
38
|
+
threadId: state.threadId,
|
|
39
|
+
});
|
|
40
|
+
}
|
|
41
|
+
|
|
42
|
+
|
|
9
43
|
/**
|
|
10
44
|
* Resolve a scope alias to a concrete scope string. Aliases let tools accept
|
|
11
45
|
* `agent`/`channel`/`global` without knowing the active ids; the bus rewrites
|
|
@@ -154,7 +188,7 @@ export const busServicesPlugin =
|
|
|
154
188
|
builder.on('action:create_thread', async function* (event, context) {
|
|
155
189
|
const threadId = event.meta?.threadId;
|
|
156
190
|
const channelId = context.state.channelId;
|
|
157
|
-
const { threadTitle,
|
|
191
|
+
const { threadTitle, initialState } = (event as any).data;
|
|
158
192
|
|
|
159
193
|
if (!threadId) {
|
|
160
194
|
console.warn('[bus] Cannot create thread: meta.threadId is missing');
|
|
@@ -169,7 +203,6 @@ export const busServicesPlugin =
|
|
|
169
203
|
channelId,
|
|
170
204
|
threadId,
|
|
171
205
|
threadTitle,
|
|
172
|
-
spec,
|
|
173
206
|
initialState: (initialState as Record<string, unknown>) || {},
|
|
174
207
|
});
|
|
175
208
|
|
|
@@ -334,7 +367,7 @@ export const busServicesPlugin =
|
|
|
334
367
|
});
|
|
335
368
|
|
|
336
369
|
builder.on('action:patch_thread_details', async function* (event, context) {
|
|
337
|
-
const updatedFields: ('state'
|
|
370
|
+
const updatedFields: ('state')[] = [];
|
|
338
371
|
const resultMeta = { ...(event.meta || {}), agentId: context.state.agentId };
|
|
339
372
|
try {
|
|
340
373
|
if (!context.state.threadId) {
|
|
@@ -348,14 +381,6 @@ export const busServicesPlugin =
|
|
|
348
381
|
});
|
|
349
382
|
updatedFields.push('state');
|
|
350
383
|
}
|
|
351
|
-
if (typeof (event.data as any).spec === 'string') {
|
|
352
|
-
await storage.patchThreadSpec({
|
|
353
|
-
channelId: context.state.channelId,
|
|
354
|
-
threadId: context.state.threadId,
|
|
355
|
-
spec: (event.data as any).spec,
|
|
356
|
-
});
|
|
357
|
-
updatedFields.push('spec');
|
|
358
|
-
}
|
|
359
384
|
|
|
360
385
|
context.state.threadDetails = await storage.getThreadDetails({
|
|
361
386
|
channelId: context.state.channelId,
|
|
@@ -376,6 +401,102 @@ export const busServicesPlugin =
|
|
|
376
401
|
}
|
|
377
402
|
});
|
|
378
403
|
|
|
404
|
+
builder.on('action:todo_write', async function* (event, context) {
|
|
405
|
+
const resultMeta = { ...(event.meta || {}), agentId: context.state.agentId };
|
|
406
|
+
try {
|
|
407
|
+
if (!context.state.threadId) {
|
|
408
|
+
throw new Error('todo_write requires an active thread');
|
|
409
|
+
}
|
|
410
|
+
const existing = readTodos(context.state);
|
|
411
|
+
const byId = new Map(existing.map((t) => [t.id, t]));
|
|
412
|
+
const now = Date.now();
|
|
413
|
+
const author = context.state.agentId || 'system';
|
|
414
|
+
|
|
415
|
+
const inputs = (event.data as { todos: TodoWriteInput[] }).todos || [];
|
|
416
|
+
const next: TodoItem[] = inputs.map((raw, idx) => {
|
|
417
|
+
const prior = raw.id ? byId.get(raw.id) : undefined;
|
|
418
|
+
return {
|
|
419
|
+
id: prior?.id || raw.id || newTodoId(now, idx),
|
|
420
|
+
content: raw.content,
|
|
421
|
+
status: raw.status || prior?.status || 'pending',
|
|
422
|
+
assignee: raw.assignee ?? prior?.assignee,
|
|
423
|
+
createdBy: prior?.createdBy || author,
|
|
424
|
+
createdAt: prior?.createdAt || now,
|
|
425
|
+
updatedAt: now,
|
|
426
|
+
...(prior?.result !== undefined ? { result: prior.result } : {}),
|
|
427
|
+
};
|
|
428
|
+
});
|
|
429
|
+
|
|
430
|
+
await persistTodos(storage, context.state, next);
|
|
431
|
+
|
|
432
|
+
yield {
|
|
433
|
+
type: 'action:todo_write:result',
|
|
434
|
+
data: { success: true, todos: next },
|
|
435
|
+
meta: resultMeta,
|
|
436
|
+
} as OpenBotEvent;
|
|
437
|
+
} catch (error) {
|
|
438
|
+
yield {
|
|
439
|
+
type: 'action:todo_write:result',
|
|
440
|
+
data: {
|
|
441
|
+
success: false,
|
|
442
|
+
todos: readTodos(context.state),
|
|
443
|
+
error: error instanceof Error ? error.message : 'Unknown error',
|
|
444
|
+
},
|
|
445
|
+
meta: resultMeta,
|
|
446
|
+
} as OpenBotEvent;
|
|
447
|
+
}
|
|
448
|
+
});
|
|
449
|
+
|
|
450
|
+
builder.on('action:todo_update', async function* (event, context) {
|
|
451
|
+
const resultMeta = { ...(event.meta || {}), agentId: context.state.agentId };
|
|
452
|
+
const patch = event.data as {
|
|
453
|
+
id: string;
|
|
454
|
+
status?: TodoStatus;
|
|
455
|
+
content?: string;
|
|
456
|
+
assignee?: string;
|
|
457
|
+
};
|
|
458
|
+
try {
|
|
459
|
+
if (!context.state.threadId) {
|
|
460
|
+
throw new Error('todo_update requires an active thread');
|
|
461
|
+
}
|
|
462
|
+
const existing = readTodos(context.state);
|
|
463
|
+
const idx = existing.findIndex((t) => t.id === patch.id);
|
|
464
|
+
if (idx === -1) {
|
|
465
|
+
throw new Error(`Todo "${patch.id}" not found`);
|
|
466
|
+
}
|
|
467
|
+
const now = Date.now();
|
|
468
|
+
const updated: TodoItem = {
|
|
469
|
+
...existing[idx],
|
|
470
|
+
...(patch.content !== undefined ? { content: patch.content } : {}),
|
|
471
|
+
...(patch.status !== undefined ? { status: patch.status } : {}),
|
|
472
|
+
...(patch.assignee !== undefined
|
|
473
|
+
? { assignee: patch.assignee === '' ? undefined : patch.assignee }
|
|
474
|
+
: {}),
|
|
475
|
+
updatedAt: now,
|
|
476
|
+
};
|
|
477
|
+
const next = [...existing];
|
|
478
|
+
next[idx] = updated;
|
|
479
|
+
|
|
480
|
+
await persistTodos(storage, context.state, next);
|
|
481
|
+
|
|
482
|
+
yield {
|
|
483
|
+
type: 'action:todo_update:result',
|
|
484
|
+
data: { success: true, todo: updated, todos: next },
|
|
485
|
+
meta: resultMeta,
|
|
486
|
+
} as OpenBotEvent;
|
|
487
|
+
} catch (error) {
|
|
488
|
+
yield {
|
|
489
|
+
type: 'action:todo_update:result',
|
|
490
|
+
data: {
|
|
491
|
+
success: false,
|
|
492
|
+
todos: readTodos(context.state),
|
|
493
|
+
error: error instanceof Error ? error.message : 'Unknown error',
|
|
494
|
+
},
|
|
495
|
+
meta: resultMeta,
|
|
496
|
+
} as OpenBotEvent;
|
|
497
|
+
}
|
|
498
|
+
});
|
|
499
|
+
|
|
379
500
|
builder.on('action:storage:get-channels', async function* () {
|
|
380
501
|
const channels = await storage.getChannels();
|
|
381
502
|
yield { type: 'action:storage:get-channels-result', data: { channels } };
|
package/src/bus/types.ts
CHANGED
|
@@ -34,7 +34,6 @@ export type PluginDescriptor = {
|
|
|
34
34
|
/** True when bundled with the core server (`src/registry/plugins`); false for ~/.openbot/plugins installs. */
|
|
35
35
|
builtIn: boolean;
|
|
36
36
|
image?: string;
|
|
37
|
-
defaultInstructions?: string;
|
|
38
37
|
configSchema?: ConfigSchema;
|
|
39
38
|
createdAt: Date;
|
|
40
39
|
updatedAt: Date;
|
|
@@ -79,7 +78,6 @@ export type ThreadDetails = {
|
|
|
79
78
|
id: string;
|
|
80
79
|
name: string;
|
|
81
80
|
channelId: string;
|
|
82
|
-
spec: string;
|
|
83
81
|
state: unknown;
|
|
84
82
|
};
|
|
85
83
|
|
|
@@ -104,7 +102,6 @@ export interface Storage {
|
|
|
104
102
|
channelId: string;
|
|
105
103
|
threadId: string;
|
|
106
104
|
threadTitle?: string;
|
|
107
|
-
spec?: string;
|
|
108
105
|
initialState?: Record<string, unknown>;
|
|
109
106
|
}) => Promise<void>;
|
|
110
107
|
getThreads: (args: { channelId: string }) => Promise<Thread[]>;
|
|
@@ -136,7 +133,6 @@ export interface Storage {
|
|
|
136
133
|
state: unknown;
|
|
137
134
|
}) => Promise<void>;
|
|
138
135
|
patchChannelSpec: (args: { channelId: string; spec: string }) => Promise<void>;
|
|
139
|
-
patchThreadSpec: (args: { channelId: string; threadId: string; spec: string }) => Promise<void>;
|
|
140
136
|
getVariables: () => Promise<Record<string, string | { value: string; secret: boolean }>>;
|
|
141
137
|
createVariable: (args: { key: string; value: string; secret?: boolean }) => Promise<void>;
|
|
142
138
|
deleteVariable: (args: { key: string }) => Promise<void>;
|
package/src/harness/context.ts
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { OpenBotEvent, OpenBotState } from '../app/types.js';
|
|
1
|
+
import { OpenBotEvent, OpenBotState, TodoItem } from '../app/types.js';
|
|
2
2
|
import { Storage } from '../bus/types.js';
|
|
3
3
|
|
|
4
4
|
/**
|
|
@@ -98,6 +98,7 @@ export function createDefaultContextEngine(): ContextEngine {
|
|
|
98
98
|
engine.registerProvider(new AgentDetailsProvider());
|
|
99
99
|
engine.registerProvider(new ChannelDetailsProvider());
|
|
100
100
|
engine.registerProvider(new ThreadDetailsProvider());
|
|
101
|
+
engine.registerProvider(new TodoProvider());
|
|
101
102
|
engine.registerProvider(new MemoryProvider());
|
|
102
103
|
engine.registerProvider(new RecentEventsProvider());
|
|
103
104
|
|
|
@@ -110,11 +111,14 @@ class AgentDetailsProvider implements ContextProvider {
|
|
|
110
111
|
name = 'agent-details';
|
|
111
112
|
async provide(state: OpenBotState): Promise<ContextItem[]> {
|
|
112
113
|
if (!state.agentDetails) return [];
|
|
114
|
+
const instructions = state.agentDetails.instructions?.trim();
|
|
115
|
+
if (!instructions) return [];
|
|
116
|
+
|
|
113
117
|
return [{
|
|
114
118
|
id: 'agent-details',
|
|
115
119
|
type: 'agent',
|
|
116
120
|
priority: 100,
|
|
117
|
-
content:
|
|
121
|
+
content: `# ${state.agentDetails.name}\n\n${instructions}`,
|
|
118
122
|
}];
|
|
119
123
|
}
|
|
120
124
|
}
|
|
@@ -123,11 +127,14 @@ class ChannelDetailsProvider implements ContextProvider {
|
|
|
123
127
|
name = 'channel-details';
|
|
124
128
|
async provide(state: OpenBotState): Promise<ContextItem[]> {
|
|
125
129
|
if (!state.channelDetails) return [];
|
|
130
|
+
const spec = state.channelDetails.spec?.trim();
|
|
131
|
+
if (!spec) return [];
|
|
132
|
+
|
|
126
133
|
return [{
|
|
127
134
|
id: 'channel-details',
|
|
128
135
|
type: 'channel',
|
|
129
136
|
priority: 80,
|
|
130
|
-
content:
|
|
137
|
+
content: `# Channel you are in: ${state.channelDetails.name}\n\n Channel Specification: ${spec}`,
|
|
131
138
|
}];
|
|
132
139
|
}
|
|
133
140
|
}
|
|
@@ -136,12 +143,68 @@ class ThreadDetailsProvider implements ContextProvider {
|
|
|
136
143
|
name = 'thread-details';
|
|
137
144
|
async provide(state: OpenBotState): Promise<ContextItem[]> {
|
|
138
145
|
if (!state.threadDetails) return [];
|
|
139
|
-
|
|
140
|
-
|
|
141
|
-
|
|
142
|
-
|
|
143
|
-
|
|
144
|
-
|
|
146
|
+
|
|
147
|
+
// For now, this provider is a placeholder for future state-based assembly.
|
|
148
|
+
// It currently only surfaces the thread name to provide basic context.
|
|
149
|
+
return [
|
|
150
|
+
{
|
|
151
|
+
id: 'thread-details',
|
|
152
|
+
type: 'thread',
|
|
153
|
+
priority: 90,
|
|
154
|
+
content: `# Thread you are in: ${state.threadDetails.name}`,
|
|
155
|
+
},
|
|
156
|
+
];
|
|
157
|
+
}
|
|
158
|
+
}
|
|
159
|
+
|
|
160
|
+
/**
|
|
161
|
+
* Surfaces the shared per-thread todo list. The list lives in
|
|
162
|
+
* `threadDetails.state.todos` and is owned by bus services — every agent in
|
|
163
|
+
* the thread reads from the same canonical source, which is how multi-agent
|
|
164
|
+
* autonomous flows stay coordinated.
|
|
165
|
+
*/
|
|
166
|
+
class TodoProvider implements ContextProvider {
|
|
167
|
+
name = 'todos';
|
|
168
|
+
async provide(state: OpenBotState): Promise<ContextItem[]> {
|
|
169
|
+
const raw = (state.threadDetails?.state as Record<string, unknown> | undefined)?.todos;
|
|
170
|
+
const todos: TodoItem[] = Array.isArray(raw) ? (raw as TodoItem[]) : [];
|
|
171
|
+
if (todos.length === 0) return [];
|
|
172
|
+
|
|
173
|
+
const DISPLAY_RESULT_CAP = 2500;
|
|
174
|
+
|
|
175
|
+
const marker: Record<TodoItem['status'], string> = {
|
|
176
|
+
pending: '[ ]',
|
|
177
|
+
in_progress: '[~]',
|
|
178
|
+
done: '[x]',
|
|
179
|
+
cancelled: '[-]',
|
|
180
|
+
};
|
|
181
|
+
const formatted = todos
|
|
182
|
+
.map((t) => {
|
|
183
|
+
const assignee = t.assignee ? ` @${t.assignee}` : '';
|
|
184
|
+
let line = `- ${marker[t.status]} (${t.id})${assignee} ${t.content}`;
|
|
185
|
+
if (t.status === 'done' && t.result?.trim()) {
|
|
186
|
+
let snippet = t.result.trim();
|
|
187
|
+
if (snippet.length > DISPLAY_RESULT_CAP) {
|
|
188
|
+
snippet = `${snippet.slice(0, DISPLAY_RESULT_CAP)}…[truncated]`;
|
|
189
|
+
}
|
|
190
|
+
line += `\n Result: ${snippet}`;
|
|
191
|
+
}
|
|
192
|
+
return line;
|
|
193
|
+
})
|
|
194
|
+
.join('\n');
|
|
195
|
+
|
|
196
|
+
return [
|
|
197
|
+
{
|
|
198
|
+
id: 'todos',
|
|
199
|
+
type: 'todos',
|
|
200
|
+
priority: 92,
|
|
201
|
+
content:
|
|
202
|
+
`## Shared todo plan (thread state)\n` +
|
|
203
|
+
`Orchestrator authors with \`todo_write\`; assignees run one step at a time. ` +
|
|
204
|
+
`When an item is \`done\`, its captured output appears below so every agent can see prior steps without relying on merged chat history.\n\n` +
|
|
205
|
+
`${formatted}`,
|
|
206
|
+
},
|
|
207
|
+
];
|
|
145
208
|
}
|
|
146
209
|
}
|
|
147
210
|
|
|
@@ -175,7 +238,7 @@ class MemoryProvider implements ContextProvider {
|
|
|
175
238
|
id: 'memory',
|
|
176
239
|
type: 'memory',
|
|
177
240
|
priority: 95,
|
|
178
|
-
content: `##
|
|
241
|
+
content: `## Remembered facts\nTrust these unless the user contradicts them. Use \`forget\` to remove stale ones.\n\n${formatted}`,
|
|
179
242
|
},
|
|
180
243
|
];
|
|
181
244
|
} catch (error) {
|