osborn 0.9.97 → 0.9.99

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.
@@ -85,6 +85,46 @@ For every `[MEETING — *]:` message:
85
85
  3. **Delegate research to the `researcher` sub-agent** via `Task` (background, silent) when a chunk warrants it.
86
86
  4. **Don't consume voice-native attention.** The user can interrupt with a voice-native message at any time — that's the only kind that gets spoken responses.
87
87
 
88
+ ## ADDRESSED turns: REPLY FIRST, notes after (latency rule)
89
+
90
+ When a chunk says **`YOU WERE ADDRESSED`**, people in the meeting are WAITING
91
+ for your voice. Measured failure (2026-08-01): notes-writing inside the reply
92
+ turn pushed speech→reply past 30 seconds ("very very delayed").
93
+
94
+ **Mandatory turn shape when addressed:**
95
+ 1. **FIRST tool call = the `/canvas say` POST.** One short spoken reply (1–2
96
+ sentences, conversational). Nothing runs before it — no Read, no Edit, no
97
+ transcript pull, no sub-agent.
98
+ 2. **THEN** delegate note-taking / research to the writer/researcher
99
+ sub-agents in the background as usual.
100
+ 3. If you genuinely need a fact before answering, say a holding line FIRST
101
+ ("Good question — one second while I check"), then look it up, then follow
102
+ up with a second `/canvas say`. Never leave the room in silence while you
103
+ work.
104
+
105
+ ## Browse requests while CASTING a live stream — drive the ENGINE, not the canvas
106
+
107
+ When the canvas is in **`stream` mode** (bot camera = the live
108
+ browser-screen-recorder feed), requests like "pull up YouTube", "show them the
109
+ site", "navigate to X" mean: **make the STREAMED BROWSER go there.** Do NOT
110
+ flip the canvas to `link`/`web` mode — that silently replaces the live browser
111
+ feed with a static card (observed failure 2026-08-01: "PULLING UP youtube.com"
112
+ card replaced the stream).
113
+
114
+ **Drive the engine** (one Bash call, needs `OSBORN_ENGINE_TOKEN` env):
115
+ ```bash
116
+ curl -sS -X POST "https://osborn-voice-e2e.fly.dev:8781/act" \
117
+ -H "x-engine-token: $OSBORN_ENGINE_TOKEN" -H 'Content-Type: application/json' \
118
+ -d '{"instruction":"go to youtube.com"}'
119
+ ```
120
+ (Or `/tab {"op":"open","url":"https://youtube.com"}` for a direct open. The
121
+ cast keeps showing a REAL browser doing the thing — that's the product.)
122
+
123
+ If `OSBORN_ENGINE_TOKEN` isn't set, SAY so ("I can't reach the browser engine
124
+ from here — the engine token isn't configured on this machine") instead of
125
+ downgrading the cast. Only use canvas `link`/`web` mode when there is NO
126
+ active stream cast, or the user explicitly asks for a card instead.
127
+
88
128
  ## How to pull transcripts on demand (Bash + curl)
89
129
 
90
130
  When the user explicitly asks (see triggers above): speak briefly first ("On it"), then **delegate the fetch+parse+write to the `writer` sub-agent** in one `Task` call (the steps below are what you put in the writer's prompt — they're 4+ Bash/Write calls, over your 3-call budget). When the writer finishes, speak the result. The commands below are the recipe the writer runs, not calls you make directly.
package/dist/index.js CHANGED
@@ -1607,6 +1607,11 @@ async function main() {
1607
1607
  // switch/resume) reuse them without waiting for a frontend resend. Merged
1608
1608
  // OVER the built-in NAMED_AGENTS (same-name user rows shadow built-ins).
1609
1609
  let userNamedAgents = null;
1610
+ // Built-ins the user has removed (tombstoned in user_agents with enabled=false).
1611
+ // A removed built-in disappears from the effective set but stays re-addable
1612
+ // from the frontend's "Available to add" list — default agents are never
1613
+ // gone forever, matching the skill-catalog pattern.
1614
+ let userRemovedAgents = [];
1610
1615
  let activeMeetingBotId = null; // Recall.ai bot ID if in a meeting
1611
1616
  let activeMeetingPoller = null; // Transcript poller bound to that bot
1612
1617
  // LIVE meeting transcript → LLM (buffered webhook finals). See recall.on('transcript').
@@ -2252,7 +2257,12 @@ async function main() {
2252
2257
  skipTTSQueue: true,
2253
2258
  onCompactionEvent: buildOnCompactionEvent(),
2254
2259
  // Per-user named agents survive LLM recreations (session switch/resume)
2255
- agents: userNamedAgents ? { ...NAMED_AGENTS, ...userNamedAgents } : undefined,
2260
+ agents: (userNamedAgents || userRemovedAgents.length) ? (() => {
2261
+ const base = { ...NAMED_AGENTS };
2262
+ for (const r of userRemovedAgents)
2263
+ delete base[r];
2264
+ return { ...base, ...(userNamedAgents || {}) };
2265
+ })() : undefined,
2256
2266
  });
2257
2267
  currentLLM = directLLM;
2258
2268
  // Reset the session always-allow list for each new direct session
@@ -4841,9 +4851,12 @@ async function main() {
4841
4851
  });
4842
4852
  }
4843
4853
  else if (data.type === 'get_agents') {
4844
- // Named sub-agents for the chat agents manager — built-ins merged with
4845
- // any per-user DB-backed definitions (prompts omitted from the list).
4846
- const effective = { ...NAMED_AGENTS, ...(userNamedAgents || {}) };
4854
+ // Named sub-agents for the chat agents manager — built-ins (minus any
4855
+ // user-removed) merged with per-user DB-backed definitions.
4856
+ const effectiveBase = { ...NAMED_AGENTS };
4857
+ for (const r of userRemovedAgents)
4858
+ delete effectiveBase[r];
4859
+ const effective = { ...effectiveBase, ...(userNamedAgents || {}) };
4847
4860
  await sendToFrontend({
4848
4861
  type: 'agents_status',
4849
4862
  agents: Object.entries(effective).map(([name, a]) => ({
@@ -4879,9 +4892,15 @@ async function main() {
4879
4892
  };
4880
4893
  }
4881
4894
  userNamedAgents = Object.keys(validated).length ? validated : null;
4882
- const merged = { ...NAMED_AGENTS, ...(userNamedAgents || {}) };
4883
- currentLLM?.setAgents?.(userNamedAgents ? merged : undefined);
4884
- console.log(`🤖 set_agents: ${Object.keys(validated).length} user agent(s) → effective set [${Object.keys(merged).join(', ')}]`);
4895
+ // Removed built-ins (tombstones) — excluded from the effective set.
4896
+ userRemovedAgents = Array.isArray(data.removed) ? data.removed.map(String) : [];
4897
+ const base = { ...NAMED_AGENTS };
4898
+ for (const r of userRemovedAgents)
4899
+ delete base[r];
4900
+ const merged = { ...base, ...(userNamedAgents || {}) };
4901
+ const customized = !!userNamedAgents || userRemovedAgents.length > 0;
4902
+ currentLLM?.setAgents?.(customized ? merged : undefined);
4903
+ console.log(`🤖 set_agents: ${Object.keys(validated).length} user agent(s), ${userRemovedAgents.length} removed → effective [${Object.keys(merged).join(', ')}]`);
4885
4904
  await sendToFrontend({
4886
4905
  type: 'agents_status',
4887
4906
  agents: Object.entries(merged).map(([name, a]) => ({
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "osborn",
3
- "version": "0.9.97",
3
+ "version": "0.9.99",
4
4
  "description": "Voice AI coding assistant - local agent that connects to Osborn frontend",
5
5
  "type": "module",
6
6
  "bin": {