open-agents-ai 0.185.21 → 0.185.23

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 (2) hide show
  1. package/dist/index.js +143 -86
  2. package/package.json +1 -1
package/dist/index.js CHANGED
@@ -35253,97 +35253,98 @@ function generateFrontendHTML() {
35253
35253
  <head>
35254
35254
  <meta charset="UTF-8">
35255
35255
  <meta name="viewport" content="width=device-width, initial-scale=1.0">
35256
- <title>Open Agents \u2014 Live Voice Session</title>
35256
+ <title>OA \u2014 Voice</title>
35257
35257
  <style>
35258
35258
  * { margin: 0; padding: 0; box-sizing: border-box; }
35259
35259
  body {
35260
- background: #0a0a0f;
35261
- color: #c4b5fd;
35262
- font-family: 'SF Mono', 'Cascadia Code', 'Fira Code', 'JetBrains Mono', monospace;
35260
+ background: #1a1a1e;
35261
+ color: #b0b0b0;
35262
+ font-family: 'SF Mono', 'Cascadia Code', 'Fira Code', monospace;
35263
35263
  display: flex;
35264
35264
  flex-direction: column;
35265
- align-items: center;
35266
- justify-content: center;
35267
35265
  min-height: 100vh;
35268
- overflow: hidden;
35269
35266
  }
35267
+ #header {
35268
+ background: #1e1e22;
35269
+ padding: 8px 16px;
35270
+ display: flex;
35271
+ align-items: center;
35272
+ gap: 12px;
35273
+ border-bottom: 1px solid #2a2a30;
35274
+ }
35275
+ #header .accent { color: #b2920a; font-weight: bold; font-size: 0.8rem; }
35276
+ #header .status { font-size: 0.7rem; color: #555; }
35277
+ #header .status.live { color: #b2920a; }
35270
35278
  #waveform {
35271
- width: 90%; max-width: 700px;
35272
- height: 80px;
35279
+ height: 48px;
35273
35280
  display: flex;
35274
35281
  align-items: center;
35275
35282
  justify-content: center;
35276
- font-size: 2rem;
35283
+ font-size: 1.6rem;
35277
35284
  line-height: 1;
35278
- letter-spacing: 0.05em;
35285
+ letter-spacing: 0.04em;
35279
35286
  white-space: nowrap;
35280
35287
  overflow: hidden;
35281
- margin-bottom: 1.5rem;
35282
35288
  user-select: none;
35289
+ background: #1e1e22;
35290
+ border-bottom: 1px solid #2a2a30;
35291
+ }
35292
+ #conversation {
35293
+ flex: 1;
35294
+ overflow-y: auto;
35295
+ padding: 12px 16px;
35296
+ }
35297
+ .msg {
35298
+ padding: 6px 0;
35299
+ font-size: 0.82rem;
35300
+ line-height: 1.4;
35301
+ }
35302
+ .msg.user { color: #888; }
35303
+ .msg.user::before { content: '\u25B8 '; color: #555; }
35304
+ .msg.agent { color: #b2920a; }
35305
+ .msg.agent::before { content: '\u25B9 '; color: #b2920a; }
35306
+ #footer {
35307
+ background: #1e1e22;
35308
+ padding: 8px 16px;
35309
+ display: flex;
35310
+ align-items: center;
35311
+ gap: 10px;
35312
+ border-top: 1px solid #2a2a30;
35283
35313
  }
35284
- #title {
35285
- font-size: 0.75rem;
35286
- color: #7c3aed;
35287
- letter-spacing: 0.3em;
35288
- text-transform: uppercase;
35289
- margin-bottom: 1.5rem;
35290
- }
35291
- #status {
35292
- font-size: 0.85rem;
35293
- color: #6b6b8a;
35294
- margin-bottom: 1rem;
35295
- }
35296
- #status .connected { color: #a78bfa; }
35297
- #status .disconnected { color: #f87171; }
35298
- #transcripts {
35299
- width: 90%; max-width: 600px; max-height: 40vh;
35300
- overflow-y: auto; padding: 1rem;
35301
- background: rgba(139,92,246,0.04);
35302
- border-radius: 4px;
35303
- border: 1px solid rgba(139,92,246,0.12);
35304
- }
35305
- .transcript {
35306
- padding: 0.35rem 0;
35307
- border-bottom: 1px solid rgba(139,92,246,0.08);
35308
- font-size: 0.8rem;
35309
- color: #a5a0c8;
35310
- }
35311
- .transcript .speaker { font-weight: bold; margin-right: 0.5rem; }
35312
- .transcript .speaker.user { color: #c084fc; }
35313
- .transcript .speaker.agent { color: #8b5cf6; }
35314
- #controls { margin-top: 1.5rem; }
35315
35314
  button {
35316
- background: rgba(139,92,246,0.12);
35317
- border: 1px solid rgba(139,92,246,0.3);
35318
- color: #c4b5fd;
35319
- padding: 0.5rem 1.4rem;
35320
- border-radius: 4px;
35315
+ background: #2a2a30;
35316
+ border: 1px solid #3a3a42;
35317
+ color: #b2920a;
35318
+ padding: 6px 16px;
35319
+ border-radius: 3px;
35321
35320
  font-family: inherit;
35322
- font-size: 0.8rem;
35321
+ font-size: 0.75rem;
35323
35322
  cursor: pointer;
35324
- transition: all 0.2s;
35325
- letter-spacing: 0.05em;
35323
+ transition: background 0.15s;
35326
35324
  }
35327
- button:hover { background: rgba(139,92,246,0.22); }
35328
- button.active { background: rgba(239,68,68,0.15); border-color: rgba(239,68,68,0.35); color: #fca5a5; }
35325
+ button:hover { background: #3a3a42; }
35326
+ button.active { background: #3a2a10; border-color: #b2920a; }
35327
+ #footer .hint { font-size: 0.65rem; color: #444; }
35329
35328
  </style>
35330
35329
  </head>
35331
35330
  <body>
35332
- <div id="title">open agents &mdash; voice session</div>
35333
- <div id="waveform"></div>
35334
- <div id="status">
35335
- <span class="disconnected">connecting...</span>
35331
+ <div id="header">
35332
+ <span class="accent">OA</span>
35333
+ <span id="status" class="status">connecting</span>
35336
35334
  </div>
35337
- <div id="transcripts"></div>
35338
- <div id="controls">
35339
- <button id="micBtn" onclick="toggleMic()">start mic</button>
35335
+ <div id="waveform"></div>
35336
+ <div id="conversation"></div>
35337
+ <div id="footer">
35338
+ <button id="micBtn" onclick="toggleMic()">mic</button>
35339
+ <span class="hint" id="hint">tap to start</span>
35340
35340
  </div>
35341
35341
 
35342
35342
  <script>
35343
35343
  const waveEl = document.getElementById('waveform');
35344
35344
  const statusEl = document.getElementById('status');
35345
- const transcriptsEl = document.getElementById('transcripts');
35345
+ const convoEl = document.getElementById('conversation');
35346
35346
  const micBtn = document.getElementById('micBtn');
35347
+ const hintEl = document.getElementById('hint');
35347
35348
 
35348
35349
  // ---------------------------------------------------------------------------
35349
35350
  // Braille waveform \u2014 ported from TUI braille-spinner.ts
@@ -35354,10 +35355,10 @@ const DENSITY = [
35354
35355
  ];
35355
35356
  const WAVE = [...DENSITY, ...DENSITY.slice(1, -1).reverse()]; // 16 entries
35356
35357
 
35357
- // Color themes: arrays of 9 CSS colors (sparse -> dense)
35358
- const THEME_IDLE = ['#1e1b2e','#3b2d6b','#5b3fa0','#7c3aed','#8b5cf6','#a78bfa','#c4b5fd','#ddd6fe','#ede9fe'];
35359
- const THEME_SPEAKING = ['#0a2e1a','#0d5f2f','#15803d','#22c55e','#4ade80','#86efac','#bbf7d0','#dcfce7','#f0fdf4'];
35360
- const THEME_LISTENING = ['#2e1b0a','#6b3d0d','#a05f15','#ed8b3a','#f6a05c','#fabe8b','#fdd5b5','#fde6d6','#fef3e9'];
35358
+ // Color themes: TUI-aligned dark grey + yellow accent
35359
+ const THEME_IDLE = ['#1e1e22','#2a2a30','#333338','#3e3e44','#4a4a52','#555560','#666670','#777780','#888890'];
35360
+ const THEME_SPEAKING = ['#1e1e22','#2a2510','#3a3518','#4a4520','#5a5528','#6a6530','#8a7a20','#a89010','#b2920a'];
35361
+ const THEME_LISTENING = ['#1e1e22','#2a2818','#3a3820','#4a4828','#5a5830','#6a6838','#8a8830','#a8a020','#b2920a'];
35361
35362
 
35362
35363
  function buildColorRamp(ramp) {
35363
35364
  return [...ramp, ...ramp.slice(1, -1).reverse()]; // 16 entries
@@ -35459,7 +35460,8 @@ function connect() {
35459
35460
  ws.onopen = () => {
35460
35461
  connecting = false;
35461
35462
  if (disconnectGrace) { clearTimeout(disconnectGrace); disconnectGrace = null; }
35462
- statusEl.innerHTML = '<span class="connected">connected</span>';
35463
+ statusEl.textContent = 'connected';
35464
+ statusEl.className = 'status live';
35463
35465
  reconnectDelay = 1000;
35464
35466
  };
35465
35467
 
@@ -35471,7 +35473,8 @@ function connect() {
35471
35473
  if (!disconnectGrace) {
35472
35474
  disconnectGrace = setTimeout(() => {
35473
35475
  disconnectGrace = null;
35474
- statusEl.innerHTML = '<span class="disconnected">reconnecting...</span>';
35476
+ statusEl.textContent = 'reconnecting';
35477
+ statusEl.className = 'status';
35475
35478
  }, 2000);
35476
35479
  }
35477
35480
  reconnectTimer = setTimeout(connect, reconnectDelay);
@@ -35503,11 +35506,10 @@ function connect() {
35503
35506
 
35504
35507
  function addTranscript(speaker, text) {
35505
35508
  const div = document.createElement('div');
35506
- div.className = 'transcript';
35507
- div.innerHTML = '<span class="speaker ' + speaker + '">' +
35508
- (speaker === 'user' ? 'you' : 'agent') + ':</span> ' + escapeHtml(text);
35509
- transcriptsEl.appendChild(div);
35510
- transcriptsEl.scrollTop = transcriptsEl.scrollHeight;
35509
+ div.className = 'msg ' + speaker;
35510
+ div.textContent = text;
35511
+ convoEl.appendChild(div);
35512
+ convoEl.scrollTop = convoEl.scrollHeight;
35511
35513
  }
35512
35514
 
35513
35515
  function escapeHtml(s) {
@@ -35565,11 +35567,12 @@ async function toggleMic() {
35565
35567
  scriptProcessor.connect(micCtx.destination);
35566
35568
  micActive = true;
35567
35569
  waveState = 'listening';
35568
- micBtn.textContent = 'stop mic';
35570
+ micBtn.textContent = 'stop';
35569
35571
  micBtn.classList.add('active');
35572
+ hintEl.textContent = 'listening...';
35570
35573
  ws.send(JSON.stringify({ type: 'mic_start', username: 'web-user' }));
35571
35574
  } catch (err) {
35572
- statusEl.innerHTML = '<span class="disconnected">mic error: ' + err.message + '</span>';
35575
+ hintEl.textContent = 'mic error: ' + err.message;
35573
35576
  }
35574
35577
  }
35575
35578
 
@@ -35579,8 +35582,9 @@ function stopMic() {
35579
35582
  waveState = 'idle';
35580
35583
  if (scriptProcessor) { scriptProcessor.disconnect(); scriptProcessor = null; }
35581
35584
  if (micStream) { micStream.getTracks().forEach(t => t.stop()); micStream = null; }
35582
- micBtn.textContent = 'start mic';
35585
+ micBtn.textContent = 'mic';
35583
35586
  micBtn.classList.remove('active');
35587
+ hintEl.textContent = 'tap to start';
35584
35588
  if (ws && ws.readyState === 1) {
35585
35589
  ws.send(JSON.stringify({ type: 'mic_stop' }));
35586
35590
  }
@@ -38525,6 +38529,7 @@ var init_call_agent = __esm({
38525
38529
  tier;
38526
38530
  clientId;
38527
38531
  runner = null;
38532
+ backend = null;
38528
38533
  config;
38529
38534
  repoRoot;
38530
38535
  opts;
@@ -38543,6 +38548,7 @@ var init_call_agent = __esm({
38543
38548
  /** Initialize the runner with appropriate tools for the access tier */
38544
38549
  async init() {
38545
38550
  const backend = new OllamaAgenticBackend(this.config.backendUrl, this.config.model, this.config.apiKey);
38551
+ this.backend = backend;
38546
38552
  const feed = getActivityFeed();
38547
38553
  const systemPrompt = this.buildSystemPrompt();
38548
38554
  const runnerOpts = {
@@ -38652,21 +38658,46 @@ var init_call_agent = __esm({
38652
38658
  const historyContext = this.conversationHistory.slice(-10).map((h) => `${h.role === "user" ? "User" : "You"}: ${h.text}`).join("\n");
38653
38659
  const feed = getActivityFeed();
38654
38660
  const activitySummary = feed.getSummary(this.tier === "admin" ? 20 : 10, this.tier === "admin");
38655
- const wantsAction = /\b(read|open|show|run|execute|check|look at|find|search|grep|edit|write|fix|test|build|deploy|install)\b/i.test(text) && !/\b(how are you|what's up|hello|hi|hey|can you hear|stop|quit|bye)\b/i.test(text);
38656
- const taskPrompt = [
38657
- `User said: "${text}"`,
38658
- "",
38659
- historyContext ? `Conversation so far:
38661
+ const wantsAction = /\b(read|open|show|run|execute|check|look at|find|search|grep|edit|write|fix|test|build|deploy|install|create|delete|remove|update|change|modify|commit|push|pull)\b/i.test(text) && !/\b(how are you|what's up|hello|hi|hey|can you hear|stop|quit|bye|thanks|thank you|ok|okay|sure|yeah|yes|no)\b/i.test(text);
38662
+ if (!wantsAction) {
38663
+ try {
38664
+ const chatMessages = [
38665
+ { role: "system", content: this.buildSystemPrompt() },
38666
+ ...this.conversationHistory.slice(-6).map((h) => ({
38667
+ role: h.role === "user" ? "user" : "assistant",
38668
+ content: h.text
38669
+ })),
38670
+ { role: "user", content: text }
38671
+ ];
38672
+ const chatResult = await this.backend.chatCompletion({
38673
+ messages: chatMessages,
38674
+ tools: [],
38675
+ temperature: 0.4,
38676
+ maxTokens: 256,
38677
+ timeoutMs: 15e3
38678
+ });
38679
+ const reply = chatResult.choices[0]?.message?.content ?? "I heard you.";
38680
+ this.conversationHistory.push({ role: "assistant", text: reply });
38681
+ this.emit("response", reply);
38682
+ } catch {
38683
+ this.emit("response", "Sorry, I couldn't process that.");
38684
+ }
38685
+ } else {
38686
+ const taskPrompt = [
38687
+ `User said: "${text}"`,
38688
+ "",
38689
+ historyContext ? `Conversation so far:
38660
38690
  ${historyContext}
38661
38691
  ` : "",
38662
- `Background activity:
38692
+ `Background activity:
38663
38693
  ${activitySummary}
38664
38694
  `,
38665
- wantsAction ? "The user is requesting an action. You may use tools, then call task_complete with a brief spoken summary of what you did." : "RESPOND IMMEDIATELY \u2014 call task_complete with your spoken reply. Do NOT use any tools. Just answer conversationally in 1-3 sentences."
38666
- ].join("\n");
38667
- const result = await this.runner.run(taskPrompt, `Working directory: ${this.repoRoot}`);
38668
- if (result.summary) {
38669
- this.conversationHistory.push({ role: "assistant", text: result.summary });
38695
+ "The user is requesting an action. Use tools as needed, then call task_complete with a brief spoken summary of what you did (1-2 sentences)."
38696
+ ].join("\n");
38697
+ const result = await this.runner.run(taskPrompt, `Working directory: ${this.repoRoot}`);
38698
+ if (result.summary) {
38699
+ this.conversationHistory.push({ role: "assistant", text: result.summary });
38700
+ }
38670
38701
  }
38671
38702
  } catch (err) {
38672
38703
  this.emit("error", err instanceof Error ? err : new Error(String(err)));
@@ -51558,6 +51589,9 @@ async function handleUpdate(subcommand, ctx) {
51558
51589
  installOverlay.dismiss();
51559
51590
  await new Promise((r) => setTimeout(r, 200));
51560
51591
  ctx.contextSave?.();
51592
+ if (ctx.hasActiveTask?.())
51593
+ ctx.abortActiveTask?.();
51594
+ ctx.killEphemeral?.();
51561
51595
  process.exit(120);
51562
51596
  }
51563
51597
  async function switchModel(query, ctx, local = false) {
@@ -66171,6 +66205,24 @@ async function startInteractive(config, repoPath) {
66171
66205
  await new Promise((r) => setTimeout(r, 50));
66172
66206
  process.stdin.pause();
66173
66207
  }
66208
+ try {
66209
+ const oaDir = join70(repoRoot, ".oa");
66210
+ const nexusPidFile = join70(oaDir, "nexus", "daemon.pid");
66211
+ if (existsSync53(nexusPidFile)) {
66212
+ const pid = parseInt(readFileSync42(nexusPidFile, "utf8").trim(), 10);
66213
+ if (pid > 0) {
66214
+ try {
66215
+ process.kill(pid, 0);
66216
+ } catch {
66217
+ try {
66218
+ __require("node:fs").unlinkSync(nexusPidFile);
66219
+ } catch {
66220
+ }
66221
+ }
66222
+ }
66223
+ }
66224
+ } catch {
66225
+ }
66174
66226
  initOaDirectory(repoRoot);
66175
66227
  const savedSettings = resolveSettings(repoRoot);
66176
66228
  let restoredSessionContext = null;
@@ -67363,6 +67415,11 @@ Rationale: ${proposal.rationale}${provenanceNote}`;
67363
67415
  if (carousel.isRunning)
67364
67416
  carousel.stop();
67365
67417
  },
67418
+ killEphemeral() {
67419
+ if (_shellToolRef)
67420
+ _shellToolRef.killAll();
67421
+ killAllFullSubAgents();
67422
+ },
67366
67423
  async voiceToggle() {
67367
67424
  const msg = await voiceEngine.toggle();
67368
67425
  if (telegramBridge) {
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "open-agents-ai",
3
- "version": "0.185.21",
3
+ "version": "0.185.23",
4
4
  "description": "AI coding agent powered by open-source models (Ollama/vLLM) — interactive TUI with agentic tool-calling loop",
5
5
  "type": "module",
6
6
  "main": "./dist/index.js",