openbot 0.5.8 → 0.5.10

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.
@@ -9,6 +9,7 @@ import { abortRegistry, abortKey } from '../services/abort.js';
9
9
  import { loadConfig } from '../app/config.js';
10
10
  import { getPublicBaseUrl } from '../plugins/storage/files.js';
11
11
  import { createPluginHost } from '../services/plugins/host.js';
12
+ import { getInvokeConfigOverrides, mergePluginConfig } from './merge-plugin-config.js';
12
13
 
13
14
  export { STATE_AGENT_ID, ORCHESTRATOR_AGENT_ID };
14
15
 
@@ -16,8 +17,6 @@ export interface RunAgentOptions {
16
17
  runId: string;
17
18
  agentId: string;
18
19
  event: OpenBotEvent;
19
- channelId: string;
20
- threadId?: string;
21
20
  persistEvents?: boolean;
22
21
  /** Resolved public base URL for the server. */
23
22
  publicBaseUrl?: string;
@@ -29,15 +28,11 @@ async function emitEvent(
29
28
  state: OpenBotState | undefined,
30
29
  {
31
30
  persistEvents,
32
- channelId,
33
- threadId,
34
31
  onEvent,
35
32
  parentAgentId,
36
33
  parentToolCallId,
37
34
  }: {
38
35
  persistEvents: boolean;
39
- channelId: string;
40
- threadId?: string;
41
36
  onEvent: RunAgentOptions['onEvent'];
42
37
  parentAgentId?: string;
43
38
  parentToolCallId?: string;
@@ -54,10 +49,11 @@ async function emitEvent(
54
49
  };
55
50
  }
56
51
 
57
- if (persistEvents) {
52
+ const channelId = state?.channelId;
53
+ if (persistEvents && channelId) {
58
54
  await storageService.storeEvent({
59
- channelId: state?.channelId || channelId,
60
- threadId: state?.threadId || threadId,
55
+ channelId,
56
+ threadId: state?.threadId,
61
57
  event: chunk,
62
58
  });
63
59
  }
@@ -70,7 +66,7 @@ async function emitEvent(
70
66
  * Fire and forget.
71
67
  */
72
68
  export async function runAgent(options: RunAgentOptions): Promise<void> {
73
- const { runId, agentId, event, channelId, threadId, onEvent } = options;
69
+ const { runId, agentId, event, onEvent } = options;
74
70
  const persistEvents = options.persistEvents !== false;
75
71
 
76
72
  let publicBaseUrl = options.publicBaseUrl;
@@ -88,23 +84,21 @@ export async function runAgent(options: RunAgentOptions): Promise<void> {
88
84
  const state = await storageService.getOpenBotState({
89
85
  runId,
90
86
  agentId,
91
- channelId,
92
- threadId,
93
87
  event,
94
88
  });
95
89
 
96
90
  // Shared per-thread abort signal so a stop request cancels this run and any
97
91
  // delegated sub-agent runs (which execute in the same channel/thread).
98
- const runKey = abortKey(channelId, threadId);
92
+ const runKey = state.channelId ? abortKey(state.channelId, state.threadId) : runId;
99
93
  const abortSignal = abortRegistry.acquire(runKey);
100
94
 
101
95
  await emitEvent(
102
96
  {
103
97
  type: 'agent:run:start',
104
- data: { runId, agentId, channelId, threadId },
98
+ data: { runId, agentId, channelId: state.channelId, threadId: state.threadId },
105
99
  } as OpenBotEvent,
106
100
  state,
107
- { persistEvents, channelId, threadId, onEvent, parentAgentId, parentToolCallId },
101
+ { persistEvents, onEvent, parentAgentId, parentToolCallId },
108
102
  );
109
103
 
110
104
  try {
@@ -121,6 +115,13 @@ export async function runAgent(options: RunAgentOptions): Promise<void> {
121
115
  const builder = melony<OpenBotState, OpenBotEvent>().initialState(state);
122
116
 
123
117
  const pluginHost = createPluginHost(runAgent);
118
+ const invokeConfigOverrides = getInvokeConfigOverrides(event);
119
+ const pluginConfigs = Object.fromEntries(
120
+ pluginRefs.map((ref) => [
121
+ ref.id,
122
+ mergePluginConfig(ref.config, invokeConfigOverrides, ref.id),
123
+ ]),
124
+ );
124
125
 
125
126
  for (const ref of pluginRefs) {
126
127
  const plugin = await resolvePlugin(ref.id);
@@ -130,7 +131,7 @@ export async function runAgent(options: RunAgentOptions): Promise<void> {
130
131
  plugin.factory({
131
132
  agentId,
132
133
  agentDetails,
133
- config: ref.config ?? {},
134
+ config: pluginConfigs[ref.id],
134
135
  storage: storageService,
135
136
  tools,
136
137
  publicBaseUrl,
@@ -147,8 +148,6 @@ export async function runAgent(options: RunAgentOptions): Promise<void> {
147
148
  if (abortSignal.aborted) break;
148
149
  await emitEvent(outputEvent, state, {
149
150
  persistEvents,
150
- channelId,
151
- threadId,
152
151
  onEvent,
153
152
  parentAgentId,
154
153
  parentToolCallId,
@@ -161,10 +160,10 @@ export async function runAgent(options: RunAgentOptions): Promise<void> {
161
160
  await emitEvent(
162
161
  {
163
162
  type: 'agent:run:end',
164
- data: { runId, agentId, channelId, threadId },
163
+ data: { runId, agentId, channelId: state.channelId, threadId: state.threadId },
165
164
  } as OpenBotEvent,
166
165
  state,
167
- { persistEvents, channelId, threadId, onEvent, parentAgentId, parentToolCallId },
166
+ { persistEvents, onEvent, parentAgentId, parentToolCallId },
168
167
  );
169
168
  }
170
169
  }
@@ -0,0 +1,35 @@
1
+ import { OpenBotEvent } from '../app/types.js';
2
+
3
+ function isPlainObject(value: unknown): value is Record<string, unknown> {
4
+ return typeof value === 'object' && value !== null && !Array.isArray(value);
5
+ }
6
+
7
+ function isPluginKeyedConfig(config: Record<string, unknown>): boolean {
8
+ return Object.values(config).every(isPlainObject);
9
+ }
10
+
11
+ export function getInvokeConfigOverrides(event: OpenBotEvent): Record<string, unknown> | undefined {
12
+ if (event.type !== 'agent:invoke') return undefined;
13
+
14
+ const config = event.data?.config;
15
+ if (!isPlainObject(config) || Object.keys(config).length === 0) return undefined;
16
+
17
+ return config;
18
+ }
19
+
20
+ export function mergePluginConfig(
21
+ base: Record<string, unknown> | undefined,
22
+ overrides: Record<string, unknown> | undefined,
23
+ pluginId: string,
24
+ ): Record<string, unknown> {
25
+ const baseConfig = base ?? {};
26
+ if (!overrides) return baseConfig;
27
+
28
+ if (isPluginKeyedConfig(overrides)) {
29
+ const pluginOverride = overrides[pluginId];
30
+ if (!isPlainObject(pluginOverride)) return baseConfig;
31
+ return { ...baseConfig, ...pluginOverride };
32
+ }
33
+
34
+ return { ...baseConfig, ...overrides };
35
+ }