bloby-bot 0.53.10 → 0.54.11
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/package.json +2 -2
- package/shared/config.ts +5 -0
- package/supervisor/backend.ts +29 -4
- package/supervisor/channels/manager.ts +81 -19
- package/supervisor/channels/types.ts +5 -0
- package/supervisor/chat/src/components/Chat/EnvForm.tsx +2 -1
- package/supervisor/harnesses/claude.ts +12 -2
- package/supervisor/harnesses/codex.ts +289 -43
- package/supervisor/harnesses/pi/index.ts +8 -1
- package/supervisor/index.ts +126 -7
- package/worker/prompts/bloby-system-prompt-codex.txt +778 -0
- package/worker/prompts/bloby-system-prompt-pi.txt +778 -0
- package/worker/prompts/prompt-assembler.ts +49 -14
- package/workspace/skills/alexa/SKILL.md +5 -0
- package/workspace/skills/mac/SKILL.md +5 -0
- package/workspace/skills/plaud/SKILL.md +5 -0
- package/workspace/skills/whatsapp/SKILL.md +30 -2
|
@@ -12,9 +12,30 @@ import path from 'path';
|
|
|
12
12
|
import { log } from '../../shared/logger.js';
|
|
13
13
|
import { conditions, type ConditionResult } from './prompt-conditions.js';
|
|
14
14
|
|
|
15
|
-
const PROMPT_FILE = path.join(import.meta.dirname, 'bloby-system-prompt.txt');
|
|
16
15
|
const FRAGMENTS_FILE = path.join(import.meta.dirname, 'prompt-fragments.json');
|
|
17
16
|
|
|
17
|
+
/**
|
|
18
|
+
* One base system prompt per harness. The dynamic-fragment machinery
|
|
19
|
+
* (markers + prompt-fragments.json + prompt-conditions.ts) is shared across
|
|
20
|
+
* all three — each file carries the same `<!-- dynamic:* -->` markers, so a
|
|
21
|
+
* fragment applies regardless of which harness is active. Copy the markers
|
|
22
|
+
* into every file when adding a new dynamic section (see DYNAMIC-PROMPTS.md).
|
|
23
|
+
*/
|
|
24
|
+
export type Harness = 'claude' | 'codex' | 'pi';
|
|
25
|
+
|
|
26
|
+
const DEFAULT_PROMPT_FILENAME = 'bloby-system-prompt.txt';
|
|
27
|
+
const PROMPT_FILENAMES: Record<Harness, string> = {
|
|
28
|
+
claude: 'bloby-system-prompt.txt',
|
|
29
|
+
codex: 'bloby-system-prompt-codex.txt',
|
|
30
|
+
pi: 'bloby-system-prompt-pi.txt',
|
|
31
|
+
};
|
|
32
|
+
|
|
33
|
+
/** Absolute path to the base prompt file for a given harness. */
|
|
34
|
+
function promptFilePath(harness: Harness): string {
|
|
35
|
+
const filename = PROMPT_FILENAMES[harness] ?? DEFAULT_PROMPT_FILENAME;
|
|
36
|
+
return path.join(import.meta.dirname, filename);
|
|
37
|
+
}
|
|
38
|
+
|
|
18
39
|
// ── Types ────────────────────────────────────────────────────────────────────
|
|
19
40
|
|
|
20
41
|
export interface PromptFragment {
|
|
@@ -43,19 +64,32 @@ function loadFragments(): PromptFragment[] {
|
|
|
43
64
|
}
|
|
44
65
|
}
|
|
45
66
|
|
|
46
|
-
/**
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
67
|
+
/**
|
|
68
|
+
* Read the base system prompt for `harness` with $BOT / $HUMAN replacement.
|
|
69
|
+
* If the harness-specific file is missing or empty, fall back to the Claude
|
|
70
|
+
* prompt (`bloby-system-prompt.txt`) so a not-yet-created codex/pi file never
|
|
71
|
+
* collapses the agent to the minimal stub.
|
|
72
|
+
*/
|
|
73
|
+
function readBasePrompt(botName = 'Bloby', humanName = 'Human', harness: Harness = 'claude'): string {
|
|
74
|
+
const primary = promptFilePath(harness);
|
|
75
|
+
const fallback = path.join(import.meta.dirname, DEFAULT_PROMPT_FILENAME);
|
|
76
|
+
const candidates = primary === fallback ? [primary] : [primary, fallback];
|
|
77
|
+
|
|
78
|
+
for (const file of candidates) {
|
|
79
|
+
try {
|
|
80
|
+
const raw = fs.readFileSync(file, 'utf-8').trim();
|
|
81
|
+
if (!raw) continue;
|
|
82
|
+
if (file === fallback && primary !== fallback) {
|
|
83
|
+
log.warn(`[prompt] "${harness}" prompt (${path.basename(primary)}) missing/empty — falling back to ${DEFAULT_PROMPT_FILENAME}`);
|
|
84
|
+
}
|
|
85
|
+
return raw.replace(/\$BOT/g, botName).replace(/\$HUMAN/g, humanName);
|
|
86
|
+
} catch {
|
|
87
|
+
// try next candidate
|
|
53
88
|
}
|
|
54
|
-
return raw.replace(/\$BOT/g, botName).replace(/\$HUMAN/g, humanName);
|
|
55
|
-
} catch {
|
|
56
|
-
log.warn('System prompt file not found — using minimal fallback');
|
|
57
|
-
return `You are ${botName}, a helpful AI agent. Your human is ${humanName}.`;
|
|
58
89
|
}
|
|
90
|
+
|
|
91
|
+
log.warn('System prompt file not found — using minimal fallback');
|
|
92
|
+
return `You are ${botName}, a helpful AI agent. Your human is ${humanName}.`;
|
|
59
93
|
}
|
|
60
94
|
|
|
61
95
|
/** Interpolate {{variable}} placeholders in content using vars map */
|
|
@@ -84,8 +118,9 @@ function markerRegex(target: string): RegExp {
|
|
|
84
118
|
export async function assembleSystemPrompt(
|
|
85
119
|
botName = 'Bloby',
|
|
86
120
|
humanName = 'Human',
|
|
121
|
+
harness: Harness = 'claude',
|
|
87
122
|
): Promise<string> {
|
|
88
|
-
let prompt = readBasePrompt(botName, humanName);
|
|
123
|
+
let prompt = readBasePrompt(botName, humanName, harness);
|
|
89
124
|
const fragments = loadFragments();
|
|
90
125
|
|
|
91
126
|
if (!fragments.length) {
|
|
@@ -93,7 +128,7 @@ export async function assembleSystemPrompt(
|
|
|
93
128
|
return prompt;
|
|
94
129
|
}
|
|
95
130
|
|
|
96
|
-
log.info(`[prompt]
|
|
131
|
+
log.info(`[prompt] Harness "${harness}" (${PROMPT_FILENAMES[harness] ?? DEFAULT_PROMPT_FILENAME}) — evaluating ${fragments.length} fragment(s)...`);
|
|
97
132
|
|
|
98
133
|
// Sort by priority (lower = first)
|
|
99
134
|
const sorted = [...fragments].sort((a, b) => a.priority - b.priority);
|
|
@@ -1,3 +1,8 @@
|
|
|
1
|
+
---
|
|
2
|
+
name: mac
|
|
3
|
+
description: "Morphy native macOS companion. Activates on the [Mac] tag. Output is a concise spoken reply (TTS); optionally accompany it with a <notch_html>…</notch_html> visual card rendered inside the MacBook notch (383×147pt, transparent over black). Frequent snippets are cached in workspace/skills/mac/frequentSnippets/ for instant re-use."
|
|
4
|
+
---
|
|
5
|
+
|
|
1
6
|
# Mac (Morphy notch)
|
|
2
7
|
|
|
3
8
|
## What This Is
|
|
@@ -1,3 +1,8 @@
|
|
|
1
|
+
---
|
|
2
|
+
name: plaud
|
|
3
|
+
description: "Plaud Note integration. Pairs the user's Plaud account (email OTP or paste-token for Google/Apple identities), pulls recordings into workspace/files/audio/plaud/, and routes transcription through either the Bloby Marketplace audio-to-text service (pay-per-minute) or the human's own provider (Groq / OpenAI Whisper / Mistral Voxtral / local)."
|
|
4
|
+
---
|
|
5
|
+
|
|
1
6
|
# Plaud
|
|
2
7
|
|
|
3
8
|
## What This Is
|
|
@@ -1,3 +1,8 @@
|
|
|
1
|
+
---
|
|
2
|
+
name: whatsapp
|
|
3
|
+
description: "WhatsApp channel for your agent via Baileys. QR auth, messaging, voice transcription, channel and business modes."
|
|
4
|
+
---
|
|
5
|
+
|
|
1
6
|
# WhatsApp
|
|
2
7
|
|
|
3
8
|
## What This Is
|
|
@@ -57,7 +62,7 @@ The mode determines the routing/security policy for inbound messages. The core (
|
|
|
57
62
|
|
|
58
63
|
**Business Mode**: Bloby has its own dedicated WhatsApp number. Numbers in the `admins` array get admin access (main system prompt). Everyone else is a customer and gets the support prompt from the active skill's SCRIPT.md.
|
|
59
64
|
|
|
60
|
-
**Assistant Mode**: Your personal assistant inside your own conversations. Self-chat works as a normal admin channel. When other people message you, their messages are silently stored for context. When YOU type `@botname:` followed by a command in someone's chat, the agent activates with full conversation context and responds in that chat. The trigger uses the bot's configured name (from `config.json` `username` field) and is case-insensitive.
|
|
65
|
+
**Assistant Mode**: Your personal assistant inside your own conversations. Self-chat works as a normal admin channel. When other people message you, their messages are silently stored for context. When YOU type `@botname:` followed by a command in someone's chat, the agent activates with full conversation context and responds in that chat. The trigger uses the bot's configured name (from `config.json` `username` field) and is case-insensitive. By default nobody else can trigger the agent — only you (the account owner). (This can be opened up with the dangerous `allowOthersToTrigger` flag — see the disclaimer under Setup.) Uses the active skill's SCRIPT.md for the system prompt and `customer_data/` for per-contact memory.
|
|
61
66
|
|
|
62
67
|
### Group chats (`allowGroups`)
|
|
63
68
|
|
|
@@ -131,10 +136,33 @@ curl -s -X POST http://localhost:7400/api/channels/whatsapp/configure \
|
|
|
131
136
|
-d '{"mode":"assistant","skill":"SKILL_FOLDER_NAME"}'
|
|
132
137
|
```
|
|
133
138
|
|
|
134
|
-
The trigger uses the bot's name from `config.json` `username` field (e.g. if username is "bloby", trigger is `@bloby:`).
|
|
139
|
+
The trigger uses the bot's name from `config.json` `username` field (e.g. if username is "bloby", trigger is `@bloby:`). By default **only the account owner can trigger** — other people's messages are context only.
|
|
135
140
|
|
|
136
141
|
To also let assistant mode operate in group chats your human is in, add `"allowGroups":true` to the configure call (see "Group chats" above).
|
|
137
142
|
|
|
143
|
+
### ⚠️ Shared control — `allowOthersToTrigger` (DANGEROUS)
|
|
144
|
+
|
|
145
|
+
By default, **only your human** (the account owner) can drive the agent with `@botname:`. Everyone else's messages are stored as context but cannot trigger anything.
|
|
146
|
+
|
|
147
|
+
Setting `allowOthersToTrigger: true` changes that: **anyone** who tags `@botname:` — in a 1:1 chat or in any group your human is in — can drive the agent.
|
|
148
|
+
|
|
149
|
+
```bash
|
|
150
|
+
curl -s -X POST http://localhost:7400/api/channels/whatsapp/configure \
|
|
151
|
+
-H "Content-Type: application/json" \
|
|
152
|
+
-d '{"mode":"assistant","allowOthersToTrigger":true}'
|
|
153
|
+
```
|
|
154
|
+
|
|
155
|
+
**Before enabling this, you MUST warn your human, in your own words, that this is dangerous:** anyone who can tag the bot gains control of an agent that can run Bash, read and write files, send messages, spend money, and act with your human's full permissions. A stranger added to a group — or anyone who learns the bot's name — could issue commands. There is no per-command confirmation.
|
|
156
|
+
|
|
157
|
+
It exists for genuinely trusted shared use — e.g. sharing one assistant with a partner. Do not enable it on a whim. Confirm explicitly with your human first, and remind them it can be turned off at any time:
|
|
158
|
+
|
|
159
|
+
```bash
|
|
160
|
+
curl -s -X POST http://localhost:7400/api/channels/whatsapp/configure \
|
|
161
|
+
-H "Content-Type: application/json" -d '{"allowOthersToTrigger":false}'
|
|
162
|
+
```
|
|
163
|
+
|
|
164
|
+
Default is `false`. Leave it off unless your human clearly understands the risk and asks for it.
|
|
165
|
+
|
|
138
166
|
### 3. Verify
|
|
139
167
|
|
|
140
168
|
```bash
|