oomi-ai 0.9.0 → 0.9.1

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/README.md CHANGED
@@ -109,7 +109,7 @@ oomi connect --runtime hermes --name hermes --app-url https://www.oomi.ai
109
109
  oomi connect --runtime command --name my-agent --command '["/absolute/path/to/adapter"]' --app-url https://www.oomi.ai
110
110
  ```
111
111
 
112
- Use the user's app URL when provided. Hermes supports `--profile <profile>` and `--resume <exact-session-id>`. Without `--resume`, Oomi starts a persistent conversation with the same configured agent. Do not use `latest` or claim that an existing chat session was transferred unless its exact ID was selected. With oomi-ai 0.8.2+, an already-open Hermes conversation uses its existing local gateway owner and exact profile/session. Oomi attaches as another client; it never closes desktop, clears history, deletes a session lock, or substitutes a new chat. When no live owner exists, an Oomi-owned conversation (no `--resume`) runs on a warm `hermes acp` process with oomi-ai 0.9.0+: replies stream as they are written and stop when the person interrupts. Hermes permission requests are denied, as `chat --quiet` denies dangerous commands; ordinary file edits run as they do in quiet mode. A conversation moving from the CLI starts a new Hermes session that receives the recent Oomi history. `--resume` conversations, Hermes without ACP, and `--hermes-transport cli` use `chat --quiet --query-file -` and resume the exact returned `session_id` on subsequent turns.
112
+ Use the user's app URL when provided. Hermes supports `--profile <profile>` and `--resume <exact-session-id>`. Without `--resume`, Oomi starts a persistent conversation with the same configured agent. Do not use `latest` or claim that an existing chat session was transferred unless its exact ID was selected. With oomi-ai 0.8.2+, an already-open Hermes conversation uses its existing local gateway owner and exact profile/session. Oomi attaches as another client; it never closes desktop, clears history, deletes a session lock, or substitutes a new chat. When no live owner exists, an Oomi-owned conversation (no `--resume`) runs on a warm `hermes acp` process with oomi-ai 0.9.0+: replies stream as they are written and stop when the person interrupts. From 0.9.1 the bridge starts Hermes and reopens recent conversations ahead of time (at bridge start and when a call opens), so the first message doesn't wait for Hermes to start. Hermes permission requests are denied, as `chat --quiet` denies dangerous commands; ordinary file edits run as they do in quiet mode. A conversation moving from the CLI starts a new Hermes session that receives the recent Oomi history. `--resume` conversations, Hermes without ACP, and `--hermes-transport cli` use `chat --quiet --query-file -` and resume the exact returned `session_id` on subsequent turns.
113
113
 
114
114
  Shared desktop conversations require Hermes's loopback WebSocket gateway, active-session discovery, and durable prompt/reply row IDs. Busy conversations ask you to retry after the current task. If a simultaneous desktop send queues your message without a reply receipt, or the connection drops after sending, check the same Hermes chat before resending. Oomi cannot safely repeat an uncertain action. Human approvals stay in the existing Hermes desktop UI. Shared photo submission is not yet supported; use Hermes desktop for that photo. Older or terminal-only owners without a shared gateway receive an actionable error, never a competing process. The shared owner keeps its existing environment, so use the explicit Oomi `--connection NAME` for account commands.
115
115
 
package/bin/oomi-ai.js CHANGED
@@ -2539,6 +2539,7 @@ async function startOpenclawBridge(flags) {
2539
2539
  const localRuntime = bridgeState.localAgent
2540
2540
  ? new LocalAgentRuntime({ ...bridgeState.localAgent, connection: bridgeConnectionName }, path.dirname(resolveBridgeStatePath()), (health) => updateBridgeStatus(health))
2541
2541
  : null;
2542
+ localRuntime?.warm();
2542
2543
  const gateway = localRuntime ? { gatewayUrl: 'local-agent:', configPath: resolveBridgeStatePath() } : readOpenclawGatewayConfig();
2543
2544
  if (!localRuntime && !gateway.token && !gateway.password) {
2544
2545
  throw new Error(`Gateway auth token/password not found in ${gateway.configPath}.`);
@@ -3600,6 +3601,8 @@ async function startOpenclawBridge(flags) {
3600
3601
  const sessionId = String(payload.sessionId || '').trim();
3601
3602
  if (!sessionId) return;
3602
3603
  bridgeDebugLog(`[bridge] client.open ${sessionId}`);
3604
+ // A call is starting: make sure the agent is awake before the caller's first words.
3605
+ if (classifyBridgeSessionScope(sessionId) === 'voice') localRuntime?.warm();
3603
3606
  getOrCreateGatewaySession(sessionId);
3604
3607
  return;
3605
3608
  }
package/lib/hermesAcp.js CHANGED
@@ -196,7 +196,7 @@ class HermesAcpClient {
196
196
  * The ACP session for a conversation in this process: the one it already has, a stored one
197
197
  * reloaded from Hermes's history, or a new one. `created` says whether history was lost.
198
198
  */
199
- async openSession(sessionId) {
199
+ async openSession(sessionId, { create = true } = {}) {
200
200
  const cwd = this.config.cwd;
201
201
  if (sessionId && this.loaded.has(sessionId)) return { sessionId, created: false };
202
202
  if (sessionId) {
@@ -213,6 +213,7 @@ class HermesAcpClient {
213
213
  if (err.code === 'hermes_acp_exited') throw err;
214
214
  } finally { this.replayed.delete(sessionId); }
215
215
  }
216
+ if (!create) return null;
216
217
  const created = await this.request('session/new', { cwd, mcpServers: [] });
217
218
  if (typeof created?.sessionId !== 'string' || !created.sessionId) throw unavailable();
218
219
  await this.prepare(created.sessionId);
@@ -342,6 +343,16 @@ function promptBlocks(request, seed) {
342
343
  return blocks;
343
344
  }
344
345
 
346
+ /**
347
+ * Starts Hermes ahead of the first message and reopens recent conversations, so a call's first
348
+ * turn doesn't wait for Hermes to start (about 3.5 s on the maintainer's Mac).
349
+ */
350
+ export async function warmHermesAcp(config, sessionIds = []) {
351
+ const client = hermesAcpClient(config);
352
+ await client.start();
353
+ for (const sessionId of sessionIds) await client.openSession(sessionId, { create: false }).catch(() => null);
354
+ }
355
+
345
356
  /** One Hermes turn over ACP. Returns the reply and the ACP session that holds the conversation. */
346
357
  export async function runHermesAcpTurn(config, request, { signal, timeoutMs = 180000, onText } = {}) {
347
358
  const client = hermesAcpClient(config);
package/lib/localAgent.js CHANGED
@@ -5,7 +5,7 @@ import { spawn } from 'node:child_process';
5
5
  import { EventEmitter } from 'node:events';
6
6
  import { createHash, randomUUID } from 'node:crypto';
7
7
  import { runSharedHermesTurn, hermesRefusal } from './hermesGateway.js';
8
- import { runHermesAcpTurn, hermesAcpAvailable, resetHermesAcp } from './hermesAcp.js';
8
+ import { runHermesAcpTurn, hermesAcpAvailable, resetHermesAcp, warmHermesAcp } from './hermesAcp.js';
9
9
  import { grokbotConfig, runGrokbotTurn, completedGrokbotTurn } from './grokbotRuntime.js';
10
10
 
11
11
  const IMAGE_EXTENSIONS = { 'image/jpeg': '.jpg', 'image/png': '.png', 'image/webp': '.webp', 'image/gif': '.gif' };
@@ -275,6 +275,24 @@ export class LocalAgentRuntime {
275
275
  }
276
276
  socket() { return new LocalAgentSocket(this); }
277
277
  close() { if (this.config.runtime === 'hermes') resetHermesAcp(this.config); }
278
+ /** Gets Hermes ready before a message arrives: at bridge start and when a call opens. */
279
+ warm() {
280
+ if (this.config.runtime !== 'hermes' || !usesHermesAcp(this.config)) return Promise.resolve();
281
+ return warmHermesAcp(this.config, this.recentAcpSessions(2)).catch(() => {});
282
+ }
283
+ recentAcpSessions(limit) {
284
+ const directory = path.join(this.directory, 'conversations');
285
+ let files;
286
+ try { files = fs.readdirSync(directory).filter((name) => name.endsWith('.json')); } catch { return []; }
287
+ return files
288
+ .map((name) => { const file = path.join(directory, name); try { return { file, modified: fs.statSync(file).mtimeMs }; } catch { return null; } })
289
+ .filter(Boolean)
290
+ .sort((a, b) => b.modified - a.modified)
291
+ .map(({ file }) => readJson(file, {}))
292
+ .filter((state) => state.runtimeTransport === 'acp' && state.runtimeSessionId)
293
+ .slice(0, limit)
294
+ .map((state) => state.runtimeSessionId);
295
+ }
278
296
  }
279
297
 
280
298
  class LocalAgentSocket extends EventEmitter {
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "oomi-ai",
3
- "version": "0.9.0",
3
+ "version": "0.9.1",
4
4
  "description": "Connect OpenClaw, Hermes, Grok Bot, and other agents to Oomi with verified runtime adapters",
5
5
  "bin": {
6
6
  "oomi": "bin/oomi-ai.js"
@@ -188,7 +188,7 @@ oomi connect --runtime hermes --name hermes --app-url https://www.oomi.ai
188
188
  oomi connect --runtime command --name my-agent --command '["/absolute/path/to/adapter"]' --app-url https://www.oomi.ai
189
189
  ```
190
190
 
191
- Use the user's app URL when provided. Hermes supports `--profile <profile>` and `--resume <exact-session-id>`. Without `--resume`, Oomi starts a persistent conversation with the same configured agent. Do not use `latest` or claim that an existing chat session was transferred unless its exact ID was selected. With oomi-ai 0.8.2+, an already-open Hermes conversation uses its existing local gateway owner and exact profile/session. Oomi attaches as another client; it never closes desktop, clears history, deletes a session lock, or substitutes a new chat. When no live owner exists, an Oomi-owned conversation (no `--resume`) runs on a warm `hermes acp` process with oomi-ai 0.9.0+: replies stream as they are written and stop when the person interrupts. Hermes permission requests are denied, as `chat --quiet` denies dangerous commands; ordinary file edits run as they do in quiet mode. A conversation moving from the CLI starts a new Hermes session that receives the recent Oomi history. `--resume` conversations, Hermes without ACP, and `--hermes-transport cli` use `chat --quiet --query-file -` and resume the exact returned `session_id` on subsequent turns.
191
+ Use the user's app URL when provided. Hermes supports `--profile <profile>` and `--resume <exact-session-id>`. Without `--resume`, Oomi starts a persistent conversation with the same configured agent. Do not use `latest` or claim that an existing chat session was transferred unless its exact ID was selected. With oomi-ai 0.8.2+, an already-open Hermes conversation uses its existing local gateway owner and exact profile/session. Oomi attaches as another client; it never closes desktop, clears history, deletes a session lock, or substitutes a new chat. When no live owner exists, an Oomi-owned conversation (no `--resume`) runs on a warm `hermes acp` process with oomi-ai 0.9.0+: replies stream as they are written and stop when the person interrupts. From 0.9.1 the bridge starts Hermes and reopens recent conversations ahead of time (at bridge start and when a call opens), so the first message doesn't wait for Hermes to start. Hermes permission requests are denied, as `chat --quiet` denies dangerous commands; ordinary file edits run as they do in quiet mode. A conversation moving from the CLI starts a new Hermes session that receives the recent Oomi history. `--resume` conversations, Hermes without ACP, and `--hermes-transport cli` use `chat --quiet --query-file -` and resume the exact returned `session_id` on subsequent turns.
192
192
 
193
193
  Shared desktop conversations require Hermes's loopback WebSocket gateway, active-session discovery, and durable prompt/reply row IDs. Busy conversations ask you to retry after the current task. If a simultaneous desktop send queues your message without a reply receipt, or the connection drops after sending, check the same Hermes chat before resending. Oomi cannot safely repeat an uncertain action. Human approvals stay in the existing Hermes desktop UI. Shared photo submission is not yet supported; use Hermes desktop for that photo. Older or terminal-only owners without a shared gateway receive an actionable error, never a competing process. The shared owner keeps its existing environment, so use the explicit Oomi `--connection NAME` for account commands.
194
194