openbot 0.4.7 → 0.5.4

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.
Files changed (130) hide show
  1. package/.claude/agents/runtime-security-reviewer.md +32 -0
  2. package/.claude/hooks/format.sh +18 -0
  3. package/.claude/hooks/protect-secrets.sh +23 -0
  4. package/.claude/settings.json +26 -0
  5. package/.claude/skills/deploy/SKILL.md +26 -0
  6. package/.claude/skills/new-plugin/SKILL.md +46 -0
  7. package/.dockerignore +9 -0
  8. package/.mcp.json +8 -0
  9. package/AGENTS.md +2 -2
  10. package/CLAUDE.md +50 -0
  11. package/Dockerfile +41 -0
  12. package/deploy/README.md +61 -0
  13. package/deploy/entrypoint.sh +6 -0
  14. package/deploy/fly.toml +36 -0
  15. package/dist/app/bootstrap.js +29 -0
  16. package/dist/app/cli.js +3 -1
  17. package/dist/app/cloud-mode.js +26 -0
  18. package/dist/app/config.js +61 -16
  19. package/dist/app/server.js +38 -6
  20. package/dist/plugins/approval/index.js +1 -1
  21. package/dist/plugins/bash/index.js +386 -164
  22. package/dist/plugins/memory/service.js +2 -5
  23. package/dist/plugins/openbot/context.js +17 -1
  24. package/dist/plugins/openbot/index.js +23 -8
  25. package/dist/plugins/openbot/model.js +59 -0
  26. package/dist/plugins/openbot/runtime.js +39 -56
  27. package/dist/plugins/openbot/system-prompt.js +43 -8
  28. package/dist/plugins/preview/index.js +267 -0
  29. package/dist/plugins/storage/index.js +3 -3
  30. package/dist/plugins/storage/service.js +58 -12
  31. package/dist/plugins/todo/index.js +131 -38
  32. package/dist/plugins/todo/service.js +93 -0
  33. package/dist/plugins/ui/index.js +8 -1
  34. package/dist/services/plugins/model-registry.js +101 -0
  35. package/dist/services/plugins/registry.js +6 -4
  36. package/dist/services/plugins/service.js +2 -6
  37. package/docs/agents.md +20 -2
  38. package/docs/architecture.md +1 -1
  39. package/docs/plugins.md +13 -3
  40. package/docs/templates/AGENT.example.md +1 -1
  41. package/package.json +5 -2
  42. package/pnpm-workspace.yaml +2 -0
  43. package/src/app/bootstrap.ts +43 -0
  44. package/src/app/cli.ts +3 -1
  45. package/src/app/cloud-mode.ts +41 -0
  46. package/src/app/config.ts +71 -19
  47. package/src/app/server.ts +45 -6
  48. package/src/app/types.ts +162 -31
  49. package/src/plugins/approval/index.ts +1 -1
  50. package/src/plugins/bash/index.ts +498 -185
  51. package/src/plugins/memory/service.ts +2 -5
  52. package/src/plugins/openbot/context.ts +17 -1
  53. package/src/plugins/openbot/index.ts +25 -10
  54. package/src/plugins/openbot/model.ts +76 -0
  55. package/src/plugins/openbot/runtime.ts +47 -69
  56. package/src/plugins/openbot/system-prompt.ts +43 -8
  57. package/src/plugins/preview/index.ts +323 -0
  58. package/src/plugins/storage/index.ts +5 -8
  59. package/src/plugins/storage/service.ts +79 -15
  60. package/src/plugins/todo/index.ts +166 -0
  61. package/src/plugins/todo/service.ts +123 -0
  62. package/src/plugins/ui/index.ts +8 -1
  63. package/src/services/plugins/domain.ts +2 -0
  64. package/src/services/plugins/model-registry.ts +146 -0
  65. package/src/services/plugins/registry.ts +6 -4
  66. package/src/services/plugins/service.ts +2 -6
  67. package/dist/agents/openbot/index.js +0 -76
  68. package/dist/agents/openbot/middleware/approval.js +0 -132
  69. package/dist/agents/openbot/runtime.js +0 -289
  70. package/dist/agents/openbot/system-prompt.js +0 -32
  71. package/dist/agents/openbot/tools/delegation.js +0 -78
  72. package/dist/agents/openbot/tools/mcp.js +0 -99
  73. package/dist/agents/openbot/tools/shell.js +0 -91
  74. package/dist/agents/openbot/tools/storage.js +0 -75
  75. package/dist/agents/openbot/tools/ui.js +0 -176
  76. package/dist/agents/system.js +0 -33
  77. package/dist/bus/agent-package.js +0 -1
  78. package/dist/bus/plugin.js +0 -1
  79. package/dist/bus/services.js +0 -754
  80. package/dist/bus/types.js +0 -1
  81. package/dist/harness/agent-harness.js +0 -45
  82. package/dist/harness/agent-invoke-run.js +0 -44
  83. package/dist/harness/agent-turn.js +0 -99
  84. package/dist/harness/channel-participants.js +0 -40
  85. package/dist/harness/constants.js +0 -2
  86. package/dist/harness/context-meter.js +0 -97
  87. package/dist/harness/context.js +0 -363
  88. package/dist/harness/dispatch.js +0 -144
  89. package/dist/harness/dispatcher.js +0 -156
  90. package/dist/harness/event-normalizer.js +0 -59
  91. package/dist/harness/history.js +0 -177
  92. package/dist/harness/mcp.js +0 -61
  93. package/dist/harness/orchestration.js +0 -88
  94. package/dist/harness/orchestrator.js +0 -149
  95. package/dist/harness/participants.js +0 -22
  96. package/dist/harness/process.js +0 -29
  97. package/dist/harness/queue-processor.js +0 -187
  98. package/dist/harness/run-harness.js +0 -154
  99. package/dist/harness/run.js +0 -98
  100. package/dist/harness/runtime-factory.js +0 -73
  101. package/dist/harness/runtime.js +0 -57
  102. package/dist/harness/todo-advance.js +0 -93
  103. package/dist/harness/todo-dispatch.js +0 -51
  104. package/dist/harness/todos.js +0 -5
  105. package/dist/harness/turn.js +0 -79
  106. package/dist/harness/types.js +0 -1
  107. package/dist/plugins/ai-sdk/index.js +0 -34
  108. package/dist/plugins/ai-sdk/runtime.js +0 -395
  109. package/dist/plugins/ai-sdk/system-prompt.js +0 -18
  110. package/dist/plugins/ai-sdk.js +0 -331
  111. package/dist/plugins/approval.js +0 -163
  112. package/dist/plugins/delegation.js +0 -108
  113. package/dist/plugins/mcp/index.js +0 -108
  114. package/dist/plugins/mcp.js +0 -140
  115. package/dist/plugins/shell/index.js +0 -100
  116. package/dist/plugins/shell.js +0 -123
  117. package/dist/plugins/storage-tools/index.js +0 -76
  118. package/dist/plugins/storage.js +0 -737
  119. package/dist/plugins/thread-namer/index.js +0 -72
  120. package/dist/plugins/threads/index.js +0 -114
  121. package/dist/plugins/ui.js +0 -211
  122. package/dist/plugins/workflow/index.js +0 -65
  123. package/dist/registry/agents.js +0 -138
  124. package/dist/registry/plugins.js +0 -110
  125. package/dist/services/agent-packages.js +0 -103
  126. package/dist/services/memory.js +0 -152
  127. package/dist/services/plugins.js +0 -98
  128. package/dist/services/storage.js +0 -1028
  129. package/dist/workflow/service.js +0 -106
  130. package/dist/workflow/types.js +0 -3
@@ -1,7 +1,7 @@
1
1
  import fs from 'node:fs/promises';
2
2
  import path from 'node:path';
3
3
  import crypto from 'node:crypto';
4
- import { DEFAULT_BASE_DIR, loadConfig, resolvePath } from '../../app/config.js';
4
+ import { getBaseDir } from '../../app/config.js';
5
5
 
6
6
  /**
7
7
  * Global memory service.
@@ -60,10 +60,7 @@ type LogEntry = AddEntry | DeleteEntry | UpdateEntry;
60
60
  const DEFAULT_LIMIT = 50;
61
61
  const MAX_LIMIT = 500;
62
62
 
63
- const getMemoryDir = (): string => {
64
- const config = loadConfig();
65
- return path.join(resolvePath(config.baseDir || DEFAULT_BASE_DIR), 'memory');
66
- };
63
+ const getMemoryDir = (): string => path.join(getBaseDir(), 'memory');
67
64
 
68
65
  const getLogPath = (): string => path.join(getMemoryDir(), 'log.jsonl');
69
66
 
@@ -1,6 +1,7 @@
1
1
  import { OpenBotState } from '../../app/types.js';
2
2
  import { Storage } from '../../services/plugins/domain.js';
3
3
  import { OPENBOT_SYSTEM_PROMPT } from './system-prompt.js';
4
+ import { todoService } from '../todo/service.js';
4
5
 
5
6
  export const DEFAULT_CONTEXT_BUDGET = 8000;
6
7
  export const MAX_CONTEXT_FILES = 50;
@@ -55,7 +56,22 @@ export async function buildContext(state: OpenBotState, storage?: Storage): Prom
55
56
  }
56
57
  sections.push(env);
57
58
 
58
- // 2.5 Installed Agents
59
+ // 2.5 Thread todos
60
+ if (channelId && threadId) {
61
+ try {
62
+ const list = await todoService.getTodos({ channelId, threadId });
63
+ if (list.items.length > 0) {
64
+ const formatted = list.items
65
+ .map((t) => `- [${t.status}] (${t.id}) ${t.content}`)
66
+ .join('\n');
67
+ sections.push(`## TODOS\n${formatted}`);
68
+ }
69
+ } catch (error) {
70
+ console.warn('[context] Failed to fetch todos:', error);
71
+ }
72
+ }
73
+
74
+ // 2.6 Installed Agents
59
75
  if (allAgents.length > 0) {
60
76
  const formatted = allAgents
61
77
  .map((a) => `- ${a.id}: ${a.name}${a.description ? ` - ${a.description}` : ''}`)
@@ -1,11 +1,14 @@
1
1
  import type { Plugin } from '../../services/plugins/types.js';
2
+ import { isCloudSystemAgent, resolveCloudSystemModel } from '../../app/cloud-mode.js';
2
3
  import { openbotRuntime } from './runtime.js';
3
4
  import { bashPlugin } from '../bash/index.js';
4
5
  import { memoryPlugin } from '../memory/index.js';
6
+ import { todoPlugin } from '../todo/index.js';
5
7
  import { approvalPlugin } from '../approval/index.js';
6
8
  import { storagePlugin } from '../storage/index.js';
7
9
  import { delegationPlugin } from '../delegation/index.js';
8
10
  import { uiPlugin } from '../ui/index.js';
11
+ import { previewPlugin } from '../preview/index.js';
9
12
 
10
13
  /**
11
14
  * `openbot` — the standard, opinionated OpenBot agent runtime.
@@ -13,23 +16,21 @@ import { uiPlugin } from '../ui/index.js';
13
16
  * This is the canonical execution loop for OpenBot agents. It handles
14
17
  * `agent:invoke`, manages short-term memory, assembles context, and
15
18
  * orchestrates tool calls.
16
- *
17
- * It comes with a "batteries-included" set of inbuilt tools: bash, memory,
18
- * storage, delegation, and approval.
19
+ *
20
+ * It comes with a "batteries-included" set of inbuilt tools: shell, memory,
21
+ * todo, storage, delegation, and approval.
19
22
  */
20
23
  export const openbotPlugin: Plugin = {
21
24
  id: 'openbot',
22
25
  name: 'OpenBot Agent',
23
26
  description:
24
- 'The standard OpenBot agent runtime with inbuilt tools (bash, memory, storage, delegation, and approval).',
27
+ 'The standard OpenBot agent runtime with inbuilt tools (shell, memory, todo, storage, delegation, and approval).',
25
28
  configSchema: {
26
29
  type: 'object',
27
30
  properties: {
28
31
  model: {
29
32
  type: 'string',
30
- description:
31
- 'Provider model string, e.g. openai/gpt-4o-mini, anthropic/claude-3-5-sonnet-20240620',
32
- default: 'openai/gpt-4o-mini',
33
+ description: 'Model from the hosted marketplace registry.',
33
34
  },
34
35
  approval: {
35
36
  type: 'object',
@@ -47,29 +48,43 @@ export const openbotPlugin: Plugin = {
47
48
  toolDefinitions: {
48
49
  ...bashPlugin.toolDefinitions,
49
50
  ...memoryPlugin.toolDefinitions,
51
+ ...todoPlugin.toolDefinitions,
50
52
  ...storagePlugin.toolDefinitions,
51
53
  ...delegationPlugin.toolDefinitions,
54
+ ...previewPlugin.toolDefinitions,
52
55
  // this is the capability to render UI widgets to the user. We dont need it for now.
53
56
  // ...uiPlugin.toolDefinitions,
54
57
  },
55
58
  factory: (context) => (builder) => {
56
- const { config, storage, tools, abortSignal } = context;
59
+ const { agentId, config, storage, tools, abortSignal } = context;
57
60
 
58
61
  // Register inbuilt plugins
59
62
  bashPlugin.factory(context)(builder);
60
63
  memoryPlugin.factory(context)(builder);
64
+ todoPlugin.factory(context)(builder);
61
65
  storagePlugin.factory(context)(builder);
62
66
  delegationPlugin.factory(context)(builder);
63
67
  uiPlugin.factory(context)(builder);
68
+ previewPlugin.factory(context)(builder);
64
69
 
65
70
  // Approval plugin configuration
66
71
  const approvalConfig = (config?.approval as any) || {
67
- actions: ['action:bash', 'action:create_channel', 'action:delete_channel'],
72
+ actions: [
73
+ 'action:shell_exec',
74
+ 'action:expose_port',
75
+ 'action:create_channel',
76
+ 'action:delete_channel',
77
+ ],
68
78
  };
69
79
  approvalPlugin.factory({ ...context, config: approvalConfig })(builder);
70
80
 
81
+ const model = isCloudSystemAgent(agentId)
82
+ ? resolveCloudSystemModel(config?.model as string | undefined)
83
+ : (config?.model as string);
84
+
71
85
  return openbotRuntime({
72
- model: config?.model as string,
86
+ model,
87
+ agentId,
73
88
  storage,
74
89
  toolDefinitions: tools,
75
90
  abortSignal,
@@ -0,0 +1,76 @@
1
+ import { createOpenAI, openai as defaultOpenai } from '@ai-sdk/openai';
2
+ import { anthropic } from '@ai-sdk/anthropic';
3
+ import { google } from '@ai-sdk/google';
4
+ import type { LanguageModel } from 'ai';
5
+ import {
6
+ getCloudIntegrationsConfig,
7
+ isCloudSystemAgent,
8
+ } from '../../app/cloud-mode.js';
9
+
10
+ let cloudOpenAIProvider: ReturnType<typeof createOpenAI> | null | undefined;
11
+
12
+ /** Strip credentials — the integrations gateway injects the provider master key. */
13
+ const integrationsFetch: typeof fetch = async (input, init) => {
14
+ const headers = new Headers(init?.headers);
15
+ headers.delete('Authorization');
16
+ headers.delete('x-api-key');
17
+ return globalThis.fetch(input, { ...init, headers });
18
+ };
19
+
20
+ function getCloudOpenAIProvider(): ReturnType<typeof createOpenAI> | null {
21
+ if (cloudOpenAIProvider !== undefined) return cloudOpenAIProvider;
22
+
23
+ const config = getCloudIntegrationsConfig();
24
+ if (!config) {
25
+ cloudOpenAIProvider = null;
26
+ return null;
27
+ }
28
+
29
+ cloudOpenAIProvider = createOpenAI({
30
+ baseURL: `${config.baseUrl}/openai/v1`,
31
+ apiKey: 'unused',
32
+ headers: {
33
+ 'x-openbot-integrations-token': config.token,
34
+ },
35
+ fetch: integrationsFetch,
36
+ });
37
+ return cloudOpenAIProvider;
38
+ }
39
+
40
+ function resolveOpenbotModel(modelId: string, agentId?: string): LanguageModel {
41
+ if (!agentId || !isCloudSystemAgent(agentId)) {
42
+ throw new Error(
43
+ `OpenBot models are only available for the cloud system agent. Use openai/..., anthropic/..., etc.`,
44
+ );
45
+ }
46
+
47
+ const cloudOpenai = getCloudOpenAIProvider();
48
+ if (!cloudOpenai) {
49
+ throw new Error(
50
+ 'OpenBot cloud model requires OPENBOT_INTEGRATIONS_BASE_URL and OPENBOT_INTEGRATIONS_TOKEN.',
51
+ );
52
+ }
53
+
54
+ return cloudOpenai.chat(modelId);
55
+ }
56
+
57
+ export function resolveModel(modelString: string, agentId?: string): LanguageModel {
58
+ const [provider, ...rest] = modelString.split('/');
59
+ const modelId = rest.join('/');
60
+ if (!modelId) {
61
+ throw new Error(`Invalid model string: "${modelString}". Expected "provider/model-id".`);
62
+ }
63
+
64
+ switch (provider) {
65
+ case 'openbot':
66
+ return resolveOpenbotModel(modelId, agentId);
67
+ case 'openai':
68
+ return defaultOpenai(modelId);
69
+ case 'anthropic':
70
+ return anthropic(modelId);
71
+ case 'google':
72
+ return google(modelId);
73
+ default:
74
+ throw new Error(`Unsupported AI provider: "${provider}"`);
75
+ }
76
+ }
@@ -1,47 +1,23 @@
1
1
  import { MelonyPlugin, RuntimeContext } from 'melony';
2
- import { generateText, type LanguageModel } from 'ai';
3
- import { openai } from '@ai-sdk/openai';
4
- import { anthropic } from '@ai-sdk/anthropic';
5
- import { google } from '@ai-sdk/google';
2
+ import { generateText } from 'ai';
6
3
  import { OpenBotEvent, OpenBotState, AgentInvokeEvent } from '../../app/types.js';
7
4
  import { eventsToModelMessages } from './history.js';
8
5
  import { Storage } from '../../services/plugins/domain.js';
9
6
  import type { ToolDefinition } from '../../services/plugins/types.js';
10
- import {
11
- ORCHESTRATOR_AGENT_ID,
12
- buildContext,
13
- } from './context.js';
14
- import { saveConfig, DEFAULT_MARKETPLACE_REGISTRY_URL } from '../../app/config.js';
7
+ import { buildContext } from './context.js';
8
+ import { saveConfig } from '../../app/config.js';
9
+ import { isCloudSystemAgent } from '../../app/cloud-mode.js';
15
10
  import { OPENBOT_SYSTEM_PROMPT } from './system-prompt.js';
16
-
17
- interface ModelRegistry {
18
- providers: Record<
19
- string,
20
- {
21
- label: string;
22
- models: Array<{ id: string; label: string; description: string }>;
23
- }
24
- >;
25
- }
26
-
27
- let cachedRegistry: ModelRegistry | null = null;
28
-
29
- async function fetchRegistry(): Promise<ModelRegistry | null> {
30
- if (cachedRegistry) return cachedRegistry;
31
- try {
32
- const response = await fetch(DEFAULT_MARKETPLACE_REGISTRY_URL);
33
- if (!response.ok) throw new Error(`Failed to fetch registry: ${response.statusText}`);
34
- cachedRegistry = (await response.json()) as ModelRegistry;
35
- return cachedRegistry;
36
- } catch (error) {
37
- console.error('[openbot] Failed to fetch model registry:', error);
38
- return null;
39
- }
40
- }
11
+ import { resolveModel } from './model.js';
12
+ import {
13
+ listApiKeyProvidersFromRegistry,
14
+ resolveModelRegistry,
15
+ } from '../../services/plugins/model-registry.js';
41
16
 
42
17
  export interface OpenBotRuntimeOptions {
43
18
  /** Provider model string (e.g. `openai/gpt-4o-mini`, `anthropic/claude-3-5-sonnet-20240620`). */
44
19
  model?: string;
20
+ agentId?: string;
45
21
  storage?: Storage;
46
22
  /** Tool definitions merged from all tool plugins attached to this agent. */
47
23
  toolDefinitions?: Record<string, ToolDefinition>;
@@ -49,24 +25,6 @@ export interface OpenBotRuntimeOptions {
49
25
  abortSignal?: AbortSignal;
50
26
  }
51
27
 
52
- function resolveModel(modelString: string): LanguageModel {
53
- const [provider, ...rest] = modelString.split('/');
54
- const modelId = rest.join('/');
55
- if (!modelId) {
56
- throw new Error(`Invalid model string: "${modelString}". Expected "provider/model-id".`);
57
- }
58
- switch (provider) {
59
- case 'openai':
60
- return openai(modelId);
61
- case 'anthropic':
62
- return anthropic(modelId);
63
- case 'google':
64
- return google(modelId);
65
- default:
66
- throw new Error(`Unsupported AI provider: "${provider}"`);
67
- }
68
- }
69
-
70
28
  async function buildSystemPrompt(
71
29
  state: OpenBotState,
72
30
  storage: Storage | undefined,
@@ -149,13 +107,14 @@ export const openbotRuntime =
149
107
  (builder) => {
150
108
  const {
151
109
  model: modelString = 'openai/gpt-4o-mini',
110
+ agentId,
152
111
  storage,
153
112
  toolDefinitions = {},
154
113
  abortSignal,
155
114
  } = options;
156
115
 
157
116
  let currentModelString = modelString;
158
- let model = resolveModel(currentModelString);
117
+ let model = resolveModel(currentModelString, agentId);
159
118
 
160
119
  const runLLM = async function* (
161
120
  context: RuntimeContext<OpenBotState, OpenBotEvent>,
@@ -273,7 +232,14 @@ export const openbotRuntime =
273
232
  errorMessage.includes('Unauthorized') ||
274
233
  errorMessage.includes('authentication');
275
234
 
276
- if (isApiKeyError) {
235
+ if (isApiKeyError && !isCloudSystemAgent(context.state.agentId)) {
236
+ const registry = await resolveModelRegistry();
237
+ const providerActions = listApiKeyProvidersFromRegistry(registry).map((provider) => ({
238
+ id: provider.id,
239
+ label: provider.label,
240
+ variant: 'primary' as const,
241
+ }));
242
+
277
243
  yield {
278
244
  type: 'client:ui:widget',
279
245
  data: {
@@ -281,11 +247,14 @@ export const openbotRuntime =
281
247
  widgetId: `api_provider_selection_${Date.now()}`,
282
248
  title: `Setup AI Provider`,
283
249
  description: `Select a provider to continue.`,
284
- actions: [
285
- { id: 'openai', label: 'OpenAI', variant: 'primary' },
286
- { id: 'anthropic', label: 'Anthropic', variant: 'primary' },
287
- { id: 'google', label: 'Google', variant: 'primary' },
288
- ],
250
+ actions:
251
+ providerActions.length > 0
252
+ ? providerActions
253
+ : [
254
+ { id: 'openai', label: 'OpenAI', variant: 'primary' as const },
255
+ { id: 'anthropic', label: 'Anthropic', variant: 'primary' as const },
256
+ { id: 'google', label: 'Google', variant: 'primary' as const },
257
+ ],
289
258
  metadata: {
290
259
  type: 'api_provider_selection',
291
260
  },
@@ -353,13 +322,15 @@ export const openbotRuntime =
353
322
  const { metadata, values, actionId } = event.data;
354
323
  const threadId = event.meta?.threadId || context.state.threadId;
355
324
 
325
+ if (isCloudSystemAgent(context.state.agentId)) return;
326
+
356
327
  if (metadata?.type === 'api_provider_selection') {
357
328
  const provider = actionId;
358
329
  const [_, ...rest] = currentModelString.split('/');
359
330
  const currentModelId = rest.join('/');
360
331
 
361
- const registry = await fetchRegistry();
362
- const providerData = registry?.providers[provider as string];
332
+ const registry = await resolveModelRegistry();
333
+ const providerData = registry.providers?.[provider as string];
363
334
 
364
335
  const providerLinks: Record<string, string> = {
365
336
  openai: 'https://platform.openai.com/api-keys',
@@ -375,11 +346,20 @@ export const openbotRuntime =
375
346
  value: m.id,
376
347
  }));
377
348
 
378
- const defaultModel = modelOptions?.[0]?.value || 'gpt-4o-mini';
349
+ if (!modelOptions || modelOptions.length === 0) {
350
+ yield {
351
+ type: 'agent:output',
352
+ data: {
353
+ content: `No models are listed for **${label}** in the marketplace registry.`,
354
+ },
355
+ meta: { agentId: context.state.agentId },
356
+ };
357
+ return;
358
+ }
359
+
360
+ const defaultModel = modelOptions[0].value;
379
361
  const defaultValue =
380
- modelOptions?.find((m) => m.value === currentModelId)?.value ||
381
- currentModelId ||
382
- defaultModel;
362
+ modelOptions.find((m) => m.value === currentModelId)?.value || defaultModel;
383
363
 
384
364
  yield {
385
365
  type: 'client:ui:widget',
@@ -407,10 +387,8 @@ export const openbotRuntime =
407
387
  {
408
388
  id: 'model',
409
389
  label: 'Model',
410
- type: modelOptions ? 'select' : 'text',
411
- description: modelOptions ? undefined : `Model name (e.g. \`${defaultModel}\`).`,
390
+ type: 'select',
412
391
  options: modelOptions,
413
- placeholder: defaultModel,
414
392
  required: true,
415
393
  defaultValue,
416
394
  },
@@ -464,7 +442,7 @@ export const openbotRuntime =
464
442
  process.env[envVar] = apiKey;
465
443
 
466
444
  currentModelString = newModelString;
467
- model = resolveModel(currentModelString);
445
+ model = resolveModel(currentModelString, agentId);
468
446
  try {
469
447
  saveConfig({ model: currentModelString });
470
448
 
@@ -1,22 +1,57 @@
1
1
  export const OPENBOT_SYSTEM_PROMPT = [
2
2
  '# ROLE',
3
- 'You are an OpenBot, the main coordinator and router agent. Your primary role is to orchestrate specialized agents to help the human achieve their goals.',
3
+ 'You are an OpenBot, the main coordinator and router agent. Your primary role is to orchestrate specialized agents and manage tasks methodically to help the human achieve their goals.',
4
4
  '',
5
5
  '# SECURITY POLICY',
6
6
  '- **CRITICAL**: Never request API keys, passwords, or sensitive credentials via text or UI widgets; these are managed deterministically via secure forms and must never enter your context.',
7
7
  '- **Credential Guidance**: If an agent or tool requires credentials, inform the user they can be managed under "Settings > Variables".',
8
8
  '',
9
9
  '# CORE MISSION',
10
- 'You almost never execute tasks yourself. Instead, you delegate tasks to specialized agents. You act as a high-level manager, ensuring the right agent is working on the right task.',
10
+ 'You act as a high-level manager, ensuring the right specialized agent is working on the right task. However, when orchestrating or when specialized agents are not available, you are capable of executing complex steps yourself using a highly structured, stateful, and disciplined approach.',
11
+ '',
12
+ '# THE AGENT LOOP',
13
+ 'You operate in an iterative agent loop to complete user-assigned tasks step-by-step:',
14
+ '1. **Analyze Events**: Understand user needs and the current state through the chronological event stream (focusing on latest user messages and execution/observation results).',
15
+ '2. **Select Tools**: Choose the next tool call based on current state, todos, and environment constraints. To maximize precision, choose only one tool call per iteration for complex tasks, or parallel calls if they are independent.',
16
+ '3. **Wait for Execution**: Let the system execute the tool action and add observations/results back into the event stream.',
17
+ '4. **Iterate**: Patiently repeat the above steps, analyzing results at each turn, until all steps of the task are completed.',
18
+ '5. **Submit Results**: Message the human with clear, polished, and detailed outcomes, attaching any relevant files or deliverables.',
19
+ '6. **Enter Standby**: Enter an idle state and wait for new tasks.',
20
+ '',
21
+ '# TASK TRACKING (TODOS)',
22
+ '- For any complex multi-step task (3+ steps), you **MUST** maintain a todo list with `todo_write`.',
23
+ '- Write the full intended list at task start. Each call replaces the previous list (not a partial patch).',
24
+ '- Keep exactly one item `in_progress` while working. Mark items `completed` immediately after finishing them.',
25
+ '- Use `cancelled` for steps that are no longer needed. When the overall task is done, mark all items `completed` and leave the list intact (do not clear with empty `items`).',
26
+ '- The current list is injected into context each turn as `## TODOS`; call `todo_read` only if you need an explicit refresh.',
27
+ '- Do not stop with open `pending` or `in_progress` items unless you are blocked and have told the user why.',
11
28
  '',
12
29
  '# OPERATIONAL GUIDELINES',
13
- '- **Channel and Threads**: The main and only way to communicate and act is through channels and threads. There might be a channel called "uncategorized" for general purpose communication.',
14
- '- **Delegation**: You can delegate tasks to any specialized agent in the `INSTALLED AGENTS` list.',
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
- '- **Context Awareness**: Use the provided ENVIRONMENT, CHANNEL SPECIFICATION, and MEMORIES to maintain continuity. Do not ask for information already present in these sections.',
30
+ '- **Channel and Threads**: The main way to communicate and act is through channels and threads. There might be a channel called "uncategorized" for general purpose communication.',
31
+ '- **Delegation**: You can delegate tasks to any specialized agent in the `INSTALLED AGENTS` list using the `delegate` tool.',
17
32
  '- **Durable Memory**: Use the `remember` tool to store important facts, preferences, or project details that should persist across sessions.',
18
33
  '- **Hub-and-Spoke**: Specialized agents cannot communicate directly; as coordinator, you must pass relevant data from one agent to another.',
19
34
  '',
20
- '# COMMUNICATION STYLE',
21
- '- Be always concise, professional, and proactive.',
35
+ '# SHELL & FILE EXECUTION RULES',
36
+ '- **Stateful Sessions**: Use `shell_exec` with a session `id` (e.g. `default`, `server`). Reuse the same id to keep shell state (cwd, env) across commands.',
37
+ '- **Working Directory**: Always pass an absolute `exec_dir` (use the channel workspace path from ENVIRONMENT).',
38
+ '- **Command Discipline**: Avoid interactive prompts when possible; use `-y`, `-f`, or non-interactive flags. For prompts, use `shell_write_to_process`.',
39
+ '- **Long-Running Processes**: Start dev servers with `shell_exec` using `&` at the end (e.g. `pnpm dev &`). If you forget `&`, `shell_exec` returns after ~15s with partial logs — the server may still be running.',
40
+ '- **Polling dev servers**: After starting (or after a timeout), poll with `shell_wait` (2–5s) then `shell_view` until logs show ready (URL/port). Do not start a duplicate server; reuse the same session id.',
41
+ '- **Stop dev servers**: Use `shell_kill_process` before restarting or when finished.',
42
+ '- **Preview URLs**: After a dev server is ready, use `expose_port` with its port to generate a public preview URL (stored on channel details as `previewUrl`). Expose tunnels are temporary; close with `unexpose_port`.',
43
+ '- **Calculations**: Use `bc` or Python for math. Do not calculate complex math mentally.',
44
+ '- **Chaining**: Chain related commands with `&&` in a single `shell_exec` when they must run sequentially.',
45
+ '',
46
+ '# COMMUNICATION & WRITING STYLE',
47
+ '- Be concise, professional, proactive, and polite.',
48
+ '- Confirm receipt of user messages quickly and outline your high-level strategy brief before executing a long series of steps.',
49
+ '- Inform the user via messages when you change methods or find strategic issues.',
50
+ '- Write in clean, continuous prose. Avoid excessive bullet points or lists in final responses unless explicitly requested.',
51
+ '- All major deliverables and documents must be highly detailed and complete; do not summarize or truncate final outputs unless specified.',
52
+ '',
53
+ '# ERROR HANDLING',
54
+ '- Tool failures are provided as events. When an error occurs, do not panic or immediately ask the user for help.',
55
+ '- Carefully analyze the stderr or failure logs, verify tool/argument names, and attempt alternative approaches or parameter fixes.',
56
+ '- Only report failure reasons and ask the human for help after multiple alternative approaches have failed.',
22
57
  ].join('\n');