openbot 0.4.6 → 0.4.7
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/responding-agent.js +32 -0
- package/dist/app/server.js +9 -3
- package/dist/plugins/openbot/index.js +2 -1
- package/dist/plugins/openbot/system-prompt.js +1 -1
- package/package.json +1 -1
- package/src/app/cli.ts +1 -1
- package/src/app/responding-agent.ts +46 -0
- package/src/app/server.ts +10 -3
- package/src/app/types.ts +5 -0
- package/src/plugins/openbot/index.ts +2 -1
- package/src/plugins/openbot/system-prompt.ts +1 -1
package/dist/app/cli.js
CHANGED
|
@@ -16,7 +16,7 @@ function checkNodeVersion() {
|
|
|
16
16
|
}
|
|
17
17
|
}
|
|
18
18
|
checkNodeVersion();
|
|
19
|
-
program.name('openbot').description('OpenBot CLI').version('0.4.
|
|
19
|
+
program.name('openbot').description('OpenBot CLI').version('0.4.7');
|
|
20
20
|
program
|
|
21
21
|
.command('start')
|
|
22
22
|
.description('Start the OpenBot harness')
|
|
@@ -2,6 +2,17 @@ import { ORCHESTRATOR_AGENT_ID } from './agent-ids.js';
|
|
|
2
2
|
import { storageService } from '../plugins/storage/service.js';
|
|
3
3
|
/** Thread `state.json` key for the sticky responding agent id. */
|
|
4
4
|
export const THREAD_RESPONDING_AGENT_ID_KEY = 'respondingAgentId';
|
|
5
|
+
/** Publish events that continue a pending UI interaction rather than a new user turn. */
|
|
6
|
+
export const CONTINUATION_EVENT_TYPES = new Set(['client:ui:widget:response']);
|
|
7
|
+
const readMetaAgentId = (meta) => {
|
|
8
|
+
if (!meta || typeof meta !== 'object')
|
|
9
|
+
return undefined;
|
|
10
|
+
const value = meta.agentId;
|
|
11
|
+
if (typeof value !== 'string')
|
|
12
|
+
return undefined;
|
|
13
|
+
const trimmed = value.trim();
|
|
14
|
+
return trimmed || undefined;
|
|
15
|
+
};
|
|
5
16
|
const readBoundAgentId = (state) => {
|
|
6
17
|
if (!state || typeof state !== 'object')
|
|
7
18
|
return undefined;
|
|
@@ -46,3 +57,24 @@ export async function resolveRespondingAgentId(options) {
|
|
|
46
57
|
});
|
|
47
58
|
return { agentId: fallback, bound: true, overridden: false };
|
|
48
59
|
}
|
|
60
|
+
/**
|
|
61
|
+
* Resolves the agent that should handle a publish event.
|
|
62
|
+
* UI continuations route to the widget origin agent; everything else uses sticky thread binding.
|
|
63
|
+
*/
|
|
64
|
+
export async function resolvePublishTargetAgentId(options) {
|
|
65
|
+
const { eventType, channelId, threadId, requestedAgentId, eventMeta, bindIfUnbound } = options;
|
|
66
|
+
if (CONTINUATION_EVENT_TYPES.has(eventType)) {
|
|
67
|
+
const originAgentId = readMetaAgentId(eventMeta) || requestedAgentId?.trim();
|
|
68
|
+
if (!originAgentId) {
|
|
69
|
+
return { error: 'agentId_required' };
|
|
70
|
+
}
|
|
71
|
+
return { agentId: originAgentId };
|
|
72
|
+
}
|
|
73
|
+
const resolved = await resolveRespondingAgentId({
|
|
74
|
+
channelId,
|
|
75
|
+
threadId,
|
|
76
|
+
requestedAgentId,
|
|
77
|
+
bindIfUnbound,
|
|
78
|
+
});
|
|
79
|
+
return { agentId: resolved.agentId };
|
|
80
|
+
}
|
package/dist/app/server.js
CHANGED
|
@@ -16,7 +16,7 @@ import { storageService } from '../plugins/storage/service.js';
|
|
|
16
16
|
import { buildWorkspaceFileUrl, getPublicBaseUrl, openChannelFileStream, } from '../plugins/storage/files.js';
|
|
17
17
|
import { ensureEventId, openBotEventFromQuery } from './utils.js';
|
|
18
18
|
import { abortRegistry, abortKey } from '../services/abort.js';
|
|
19
|
-
import { resolveRespondingAgentId } from './responding-agent.js';
|
|
19
|
+
import { resolvePublishTargetAgentId, resolveRespondingAgentId } from './responding-agent.js';
|
|
20
20
|
import { DEFAULT_UNCATEGORIZED_SPEC, UNCATEGORIZED_CHANNEL_ID, } from './channel-ids.js';
|
|
21
21
|
export async function startServer(options = {}) {
|
|
22
22
|
const publishEventSchema = z
|
|
@@ -457,15 +457,21 @@ export async function startServer(options = {}) {
|
|
|
457
457
|
});
|
|
458
458
|
}
|
|
459
459
|
const bindIfUnbound = event.type === 'agent:invoke';
|
|
460
|
-
const
|
|
460
|
+
const target = await resolvePublishTargetAgentId({
|
|
461
|
+
eventType: event.type,
|
|
461
462
|
channelId,
|
|
462
463
|
threadId,
|
|
463
464
|
requestedAgentId: agentId,
|
|
465
|
+
eventMeta: event.meta,
|
|
464
466
|
bindIfUnbound,
|
|
465
467
|
});
|
|
468
|
+
if ('error' in target) {
|
|
469
|
+
res.status(400).json({ error: 'agentId is required for widget response' });
|
|
470
|
+
return;
|
|
471
|
+
}
|
|
466
472
|
await runAgent({
|
|
467
473
|
runId,
|
|
468
|
-
agentId:
|
|
474
|
+
agentId: target.agentId,
|
|
469
475
|
event,
|
|
470
476
|
channelId,
|
|
471
477
|
threadId,
|
|
@@ -45,7 +45,8 @@ export const openbotPlugin = {
|
|
|
45
45
|
...memoryPlugin.toolDefinitions,
|
|
46
46
|
...storagePlugin.toolDefinitions,
|
|
47
47
|
...delegationPlugin.toolDefinitions,
|
|
48
|
-
|
|
48
|
+
// this is the capability to render UI widgets to the user. We dont need it for now.
|
|
49
|
+
// ...uiPlugin.toolDefinitions,
|
|
49
50
|
},
|
|
50
51
|
factory: (context) => (builder) => {
|
|
51
52
|
const { config, storage, tools, abortSignal } = context;
|
|
@@ -15,7 +15,7 @@ export const OPENBOT_SYSTEM_PROMPT = [
|
|
|
15
15
|
'- **Bash Tool Usage**: You should use the `bash` tool very rarely. Only use it when the user explicitly requests a command to be run or when it is absolutely necessary for a task that no other agent can handle.',
|
|
16
16
|
'- **Context Awareness**: Use the provided ENVIRONMENT, CHANNEL SPECIFICATION, and MEMORIES to maintain continuity. Do not ask for information already present in these sections.',
|
|
17
17
|
'- **Durable Memory**: Use the `remember` tool to store important facts, preferences, or project details that should persist across sessions.',
|
|
18
|
-
'- **
|
|
18
|
+
'- **Hub-and-Spoke**: Specialized agents cannot communicate directly; as coordinator, you must pass relevant data from one agent to another.',
|
|
19
19
|
'',
|
|
20
20
|
'# COMMUNICATION STYLE',
|
|
21
21
|
'- Be always concise, professional, and proactive.',
|
package/package.json
CHANGED
package/src/app/cli.ts
CHANGED
|
@@ -4,6 +4,9 @@ import { storageService } from '../plugins/storage/service.js';
|
|
|
4
4
|
/** Thread `state.json` key for the sticky responding agent id. */
|
|
5
5
|
export const THREAD_RESPONDING_AGENT_ID_KEY = 'respondingAgentId';
|
|
6
6
|
|
|
7
|
+
/** Publish events that continue a pending UI interaction rather than a new user turn. */
|
|
8
|
+
export const CONTINUATION_EVENT_TYPES = new Set(['client:ui:widget:response']);
|
|
9
|
+
|
|
7
10
|
export type ResolveRespondingAgentOptions = {
|
|
8
11
|
channelId: string;
|
|
9
12
|
threadId?: string;
|
|
@@ -20,6 +23,14 @@ export type ResolveRespondingAgentResult = {
|
|
|
20
23
|
overridden: boolean;
|
|
21
24
|
};
|
|
22
25
|
|
|
26
|
+
const readMetaAgentId = (meta: unknown): string | undefined => {
|
|
27
|
+
if (!meta || typeof meta !== 'object') return undefined;
|
|
28
|
+
const value = (meta as Record<string, unknown>).agentId;
|
|
29
|
+
if (typeof value !== 'string') return undefined;
|
|
30
|
+
const trimmed = value.trim();
|
|
31
|
+
return trimmed || undefined;
|
|
32
|
+
};
|
|
33
|
+
|
|
23
34
|
const readBoundAgentId = (state: unknown): string | undefined => {
|
|
24
35
|
if (!state || typeof state !== 'object') return undefined;
|
|
25
36
|
const value = (state as Record<string, unknown>)[THREAD_RESPONDING_AGENT_ID_KEY];
|
|
@@ -72,3 +83,38 @@ export async function resolveRespondingAgentId(
|
|
|
72
83
|
|
|
73
84
|
return { agentId: fallback, bound: true, overridden: false };
|
|
74
85
|
}
|
|
86
|
+
|
|
87
|
+
export type ResolvePublishTargetAgentOptions = {
|
|
88
|
+
eventType: string;
|
|
89
|
+
channelId: string;
|
|
90
|
+
threadId?: string;
|
|
91
|
+
requestedAgentId?: string;
|
|
92
|
+
eventMeta?: unknown;
|
|
93
|
+
bindIfUnbound?: boolean;
|
|
94
|
+
};
|
|
95
|
+
|
|
96
|
+
/**
|
|
97
|
+
* Resolves the agent that should handle a publish event.
|
|
98
|
+
* UI continuations route to the widget origin agent; everything else uses sticky thread binding.
|
|
99
|
+
*/
|
|
100
|
+
export async function resolvePublishTargetAgentId(
|
|
101
|
+
options: ResolvePublishTargetAgentOptions,
|
|
102
|
+
): Promise<{ agentId: string } | { error: 'agentId_required' }> {
|
|
103
|
+
const { eventType, channelId, threadId, requestedAgentId, eventMeta, bindIfUnbound } = options;
|
|
104
|
+
|
|
105
|
+
if (CONTINUATION_EVENT_TYPES.has(eventType)) {
|
|
106
|
+
const originAgentId = readMetaAgentId(eventMeta) || requestedAgentId?.trim();
|
|
107
|
+
if (!originAgentId) {
|
|
108
|
+
return { error: 'agentId_required' };
|
|
109
|
+
}
|
|
110
|
+
return { agentId: originAgentId };
|
|
111
|
+
}
|
|
112
|
+
|
|
113
|
+
const resolved = await resolveRespondingAgentId({
|
|
114
|
+
channelId,
|
|
115
|
+
threadId,
|
|
116
|
+
requestedAgentId,
|
|
117
|
+
bindIfUnbound,
|
|
118
|
+
});
|
|
119
|
+
return { agentId: resolved.agentId };
|
|
120
|
+
}
|
package/src/app/server.ts
CHANGED
|
@@ -21,7 +21,7 @@ import {
|
|
|
21
21
|
} from '../plugins/storage/files.js';
|
|
22
22
|
import { ensureEventId, openBotEventFromQuery } from './utils.js';
|
|
23
23
|
import { abortRegistry, abortKey } from '../services/abort.js';
|
|
24
|
-
import { resolveRespondingAgentId } from './responding-agent.js';
|
|
24
|
+
import { resolvePublishTargetAgentId, resolveRespondingAgentId } from './responding-agent.js';
|
|
25
25
|
import {
|
|
26
26
|
DEFAULT_UNCATEGORIZED_SPEC,
|
|
27
27
|
UNCATEGORIZED_CHANNEL_ID,
|
|
@@ -561,16 +561,23 @@ export async function startServer(options: ServerOptions = {}) {
|
|
|
561
561
|
}
|
|
562
562
|
|
|
563
563
|
const bindIfUnbound = event.type === 'agent:invoke';
|
|
564
|
-
const
|
|
564
|
+
const target = await resolvePublishTargetAgentId({
|
|
565
|
+
eventType: event.type,
|
|
565
566
|
channelId,
|
|
566
567
|
threadId,
|
|
567
568
|
requestedAgentId: agentId,
|
|
569
|
+
eventMeta: event.meta,
|
|
568
570
|
bindIfUnbound,
|
|
569
571
|
});
|
|
570
572
|
|
|
573
|
+
if ('error' in target) {
|
|
574
|
+
res.status(400).json({ error: 'agentId is required for widget response' });
|
|
575
|
+
return;
|
|
576
|
+
}
|
|
577
|
+
|
|
571
578
|
await runAgent({
|
|
572
579
|
runId,
|
|
573
|
-
agentId:
|
|
580
|
+
agentId: target.agentId,
|
|
574
581
|
event,
|
|
575
582
|
channelId,
|
|
576
583
|
threadId,
|
package/src/app/types.ts
CHANGED
|
@@ -730,6 +730,11 @@ export type UIWidgetResponseEvent = BaseEvent & {
|
|
|
730
730
|
values?: Record<string, unknown>;
|
|
731
731
|
metadata?: Record<string, unknown>;
|
|
732
732
|
};
|
|
733
|
+
meta?: {
|
|
734
|
+
agentId?: string;
|
|
735
|
+
threadId?: string;
|
|
736
|
+
[key: string]: unknown;
|
|
737
|
+
};
|
|
733
738
|
};
|
|
734
739
|
|
|
735
740
|
export type BashEvent = BaseEvent & {
|
|
@@ -49,7 +49,8 @@ export const openbotPlugin: Plugin = {
|
|
|
49
49
|
...memoryPlugin.toolDefinitions,
|
|
50
50
|
...storagePlugin.toolDefinitions,
|
|
51
51
|
...delegationPlugin.toolDefinitions,
|
|
52
|
-
|
|
52
|
+
// this is the capability to render UI widgets to the user. We dont need it for now.
|
|
53
|
+
// ...uiPlugin.toolDefinitions,
|
|
53
54
|
},
|
|
54
55
|
factory: (context) => (builder) => {
|
|
55
56
|
const { config, storage, tools, abortSignal } = context;
|
|
@@ -15,7 +15,7 @@ export const OPENBOT_SYSTEM_PROMPT = [
|
|
|
15
15
|
'- **Bash Tool Usage**: You should use the `bash` tool very rarely. Only use it when the user explicitly requests a command to be run or when it is absolutely necessary for a task that no other agent can handle.',
|
|
16
16
|
'- **Context Awareness**: Use the provided ENVIRONMENT, CHANNEL SPECIFICATION, and MEMORIES to maintain continuity. Do not ask for information already present in these sections.',
|
|
17
17
|
'- **Durable Memory**: Use the `remember` tool to store important facts, preferences, or project details that should persist across sessions.',
|
|
18
|
-
'- **
|
|
18
|
+
'- **Hub-and-Spoke**: Specialized agents cannot communicate directly; as coordinator, you must pass relevant data from one agent to another.',
|
|
19
19
|
'',
|
|
20
20
|
'# COMMUNICATION STYLE',
|
|
21
21
|
'- Be always concise, professional, and proactive.',
|