@meetopenbot/openbot 1.5.1 → 1.6.0

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.
@@ -7,6 +7,7 @@ import { installAgentTools } from "./tools/install-agent.js";
7
7
  import { threadStatusTools } from "./tools/thread-status.js";
8
8
  import { threadTitleTools } from "./tools/thread-title.js";
9
9
  import { previewTools } from "./tools/preview.js";
10
+ import { variablesRequestTools } from "./tools/variables-request.js";
10
11
  import { wrapToolsWithApproval } from "./tools/approval.js";
11
12
  export const DEFAULT_APPROVAL_ACTIONS = ["update_channel_spec", "start_thread"];
12
13
  /**
@@ -24,6 +25,7 @@ export function buildOpenbotTools(args) {
24
25
  ...threadStatusTools,
25
26
  ...threadTitleTools,
26
27
  ...previewTools,
28
+ ...variablesRequestTools,
27
29
  ...args.context.tools,
28
30
  };
29
31
  const approvalConfig = args.context.config?.approval;
package/dist/index.js CHANGED
@@ -11,6 +11,7 @@ import { installAgentTools } from './tools/install-agent.js';
11
11
  import { threadStatusTools } from './tools/thread-status.js';
12
12
  import { threadTitleTools } from './tools/thread-title.js';
13
13
  import { previewTools } from './tools/preview.js';
14
+ import { variablesRequestTools } from './tools/variables-request.js';
14
15
  import { buildOpenbotTools } from './build-tools.js';
15
16
  export const OPENBOT_PLUGIN_ID = '@meetopenbot/openbot';
16
17
  const modelField = await modelConfigField({
@@ -51,6 +52,7 @@ export const openbotPlugin = defineOpenbotPlugin({
51
52
  ...threadStatusTools,
52
53
  ...threadTitleTools,
53
54
  ...previewTools,
55
+ ...variablesRequestTools,
54
56
  },
55
57
  async *run(args) {
56
58
  const { context } = args;
@@ -1,11 +1,10 @@
1
- import { settingsVariablesMarkdown, SETTINGS_VARIABLES_MARKDOWN } from '@meetopenbot/plugin-sdk';
2
1
  export const OPENBOT_SYSTEM_PROMPT = [
3
2
  "# ROLE",
4
3
  "You are OpenBot, the coordinator. Humans talk only to you. Specialized agents have their own harnesses; you ask them to do work. You do not impersonate them, and you do not do their jobs yourself.",
5
4
  "",
6
5
  "# SECURITY POLICY",
7
- "- **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.",
8
- `- **Credential Guidance**: Only relay a credential requirement after a specialist or tool actually reports a specific missing variable. Then pass through that exact variable name with a link to ${SETTINGS_VARIABLES_MARKDOWN} prefilled via \`?new=ENV_VAR_NAME\` (e.g. ${settingsVariablesMarkdown('LINEAR_API_KEY')}). Never guess, invent, or name an API key or provider that no agent or tool reported.`,
6
+ "- **CRITICAL**: Never ask for API keys, passwords, or sensitive credentials in chat or through form widgets; values must never enter your context.",
7
+ "- **Credential Guidance**: Only ask for a variable after a specialist or tool actually reports it missing. Then call `request_variables` once with every exact name it reported (backend and build-time `VITE_*` alike), each with a short hint. The human fills them in an inline card that saves straight to Space variables and resumes this thread — do not also link to Settings or list the names as links. Never guess, invent, or name an API key or provider that no agent or tool reported.",
9
8
  "",
10
9
  "# CORE MISSION",
11
10
  "You are a high-level manager. Not every message is project work.",
@@ -65,7 +64,8 @@ export const OPENBOT_SYSTEM_PROMPT = [
65
64
  "- For any other public page the human should see, call `open_preview` with that http(s) URL too. Never pass localhost, 127.0.0.1, or a port on this machine.",
66
65
  "",
67
66
  "# SPACE VARIABLES",
68
- "- Secrets live in OpenBot variables, not in chat. Workspace-wide keys: Settings → Variables. Per-space keys: Space menu → Variables (override workspace keys with the same name for shell commands in that space).",
67
+ "- Secrets live in OpenBot variables, not in chat. Project keys belong to the Space (Space menu → Variables) and override workspace keys with the same name for shell commands in that space. Workspace-wide keys (Settings → Variables) are for things shared by every space. `request_variables` defaults to the Space; the human can switch it to all spaces.",
68
+ "- After the human saves variables, ask the specialist to restart whatever needed them (dev server, backend) so the new environment is picked up.",
69
69
  "- Provider keys for harness LLM calls (`OPENAI_API_KEY`, `ANTHROPIC_API_KEY`, etc.) are workspace-only — space copies of those names are for app shells only and do not switch coding agents off OpenBot Credits.",
70
70
  "- Coding agents inherit merged space variables in their shell environment — including `pnpm dev` subprocesses. Do not ask the human to paste secrets. Only tell a coding agent to write a project `.env` when the scaffold requires it or the app errors about a missing `.env`; otherwise a restarted dev server should pick up keys from the shell (use exact names such as `VITE_*` for Vite).",
71
71
  "",
@@ -0,0 +1,106 @@
1
+ import z from 'zod';
2
+ /** `metadata.type` of the card the host shows to collect missing variables. */
3
+ export const VARIABLES_REQUEST_TYPE = 'variables_request';
4
+ const ENV_KEY = /^[A-Za-z_][A-Za-z0-9_]*$/;
5
+ const MAX_KEYS = 20;
6
+ /** Valid, de-duplicated env keys in the order given. */
7
+ export function requestedVariables(raw) {
8
+ if (!Array.isArray(raw))
9
+ return [];
10
+ const seen = new Set();
11
+ const out = [];
12
+ for (const item of raw) {
13
+ const entry = typeof item === 'string' ? { key: item } : item;
14
+ const key = typeof entry?.key === 'string' ? entry.key.trim() : '';
15
+ if (!ENV_KEY.test(key) || seen.has(key))
16
+ continue;
17
+ seen.add(key);
18
+ const description = typeof entry?.description === 'string' && entry.description.trim()
19
+ ? entry.description.trim()
20
+ : undefined;
21
+ out.push(description ? { key, description } : { key });
22
+ if (out.length >= MAX_KEYS)
23
+ break;
24
+ }
25
+ return out;
26
+ }
27
+ /**
28
+ * The card that asks the human for missing variables. Clients that know the
29
+ * `variables_request` type render masked inputs that save straight to the
30
+ * variables API, so values never pass through the thread or the model.
31
+ */
32
+ export function variablesRequestEvent(args) {
33
+ const names = args.variables.map((variable) => `\`${variable.key}\``).join(', ');
34
+ return {
35
+ type: 'client:ui:widget',
36
+ data: {
37
+ kind: 'message',
38
+ widgetId: `variables_${Date.now()}`,
39
+ title: 'Add variables',
40
+ body: args.reason?.trim() || `This space needs ${names}.`,
41
+ state: 'open',
42
+ metadata: {
43
+ type: VARIABLES_REQUEST_TYPE,
44
+ variables: args.variables,
45
+ ...(args.reason?.trim() ? { reason: args.reason.trim() } : {}),
46
+ },
47
+ },
48
+ meta: {
49
+ agentId: args.agentId,
50
+ ...(args.threadId ? { threadId: args.threadId } : {}),
51
+ },
52
+ };
53
+ }
54
+ async function requestVariables(rawArgs, ctx) {
55
+ const threadId = ctx.threadId ?? ctx.state.threadId;
56
+ if (!threadId) {
57
+ const message = 'Missing threadId for request_variables';
58
+ return { success: false, error: message, output: message };
59
+ }
60
+ const data = (rawArgs ?? {});
61
+ const variables = requestedVariables(data.variables);
62
+ if (variables.length === 0) {
63
+ const message = 'Pass at least one variable name such as FIREBASE_PROJECT_ID.';
64
+ return { success: false, error: message, output: message };
65
+ }
66
+ await ctx.emit(variablesRequestEvent({
67
+ variables,
68
+ reason: typeof data.reason === 'string' ? data.reason : undefined,
69
+ agentId: ctx.agentId,
70
+ threadId,
71
+ }));
72
+ return {
73
+ success: true,
74
+ output: 'Shown a card asking the human for ' +
75
+ variables.map((variable) => variable.key).join(', ') +
76
+ '. They save the values there; you will get a message when they do. Do not ask them to paste values in chat.',
77
+ };
78
+ }
79
+ export const toolDefinitions = {
80
+ request_variables: {
81
+ description: 'Ask the human for missing environment variables with an inline card in the thread. Values are saved as Space variables by default (or for all spaces if they choose) and never appear in chat. Pass every missing name at once, including build-time ones like VITE_*.',
82
+ inputSchema: z.object({
83
+ variables: z
84
+ .array(z.object({
85
+ key: z.string().min(1).describe('Exact env var name, e.g. FIREBASE_PROJECT_ID.'),
86
+ description: z
87
+ .string()
88
+ .optional()
89
+ .describe('Short hint on what it is or where to find it.'),
90
+ }))
91
+ .min(1)
92
+ .max(MAX_KEYS),
93
+ reason: z
94
+ .string()
95
+ .optional()
96
+ .describe('One sentence on why they are needed, e.g. "The backend needs these to start."'),
97
+ }),
98
+ },
99
+ };
100
+ export const tools = {
101
+ request_variables: {
102
+ ...toolDefinitions.request_variables,
103
+ execute: requestVariables,
104
+ },
105
+ };
106
+ export const variablesRequestTools = tools;
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@meetopenbot/openbot",
3
- "version": "1.5.1",
3
+ "version": "1.6.0",
4
4
  "description": "OpenBot coordinator runtime: ask specialists, route work into Spaces, and track todos.",
5
5
  "type": "module",
6
6
  "main": "./dist/index.js",