Updates
Rolling release / release (push) Successful in 15m15s

This commit is contained in:
2026-09-11 20:34:36 -04:00
parent 9b1ceda01f
commit 06c9a23e19
2 changed files with 33 additions and 1 deletions
+18 -1
View File
@@ -42,7 +42,24 @@ export class HarnessBridge extends EventEmitter {
async ask(text) { async ask(text) {
if (!this.session) await this.start(); if (!this.session) await this.start();
return this.session.prompt(text); // Some QVAC runs deliver the final assistant text through the stream but
// omit it from the final envelope after a tool call. Keep the current
// post-tool stream as a fallback so the daemon can still speak the reply.
let streamed = '';
const onChunk = (payload) => { streamed += String(payload?.text || payload?.delta || ''); };
const onToolCall = () => { streamed = ''; };
const chunkSubscription = this.session.on('agent_message_chunk', onChunk);
const toolSubscription = this.session.on('tool_call', onToolCall);
const offChunk = typeof chunkSubscription === 'function' ? chunkSubscription : () => this.session.off?.('agent_message_chunk', onChunk);
const offToolCall = typeof toolSubscription === 'function' ? toolSubscription : () => this.session.off?.('tool_call', onToolCall);
try {
const reply = await this.session.prompt(text);
if (reply && !reply.text && streamed.trim()) return { ...reply, text: streamed };
return reply;
} finally {
offChunk?.();
offToolCall?.();
}
} }
cancel() { this.session?.cancel(); } cancel() { this.session?.cancel(); }
+15
View File
@@ -3,6 +3,7 @@ import assert from 'node:assert/strict';
import { VoiceStateMachine } from '../daemon/voice-state.js'; import { VoiceStateMachine } from '../daemon/voice-state.js';
import { QvacScheduler } from '../daemon/qvac-scheduler.js'; import { QvacScheduler } from '../daemon/qvac-scheduler.js';
import { JarvisDaemon } from '../daemon/index.js'; import { JarvisDaemon } from '../daemon/index.js';
import { HarnessBridge } from '../daemon/harness-bridge.js';
import { spokenReply } from '../skills/voice-prompt.js'; import { spokenReply } from '../skills/voice-prompt.js';
test('voice state machine handles wake, reply, cancel, and idle sleep', () => { test('voice state machine handles wake, reply, cancel, and idle sleep', () => {
@@ -53,6 +54,20 @@ test('ask extracts harness reply text instead of stringifying the object', async
} }
}); });
test('harness bridge recovers streamed text when final envelope is empty after a tool call', async () => {
const session = new (await import('node:events')).EventEmitter();
session.prompt = async () => {
session.emit('agent_message_chunk', { text: 'I will check that. ' });
session.emit('tool_call', { call: { name: 'runtime_status' } });
session.emit('agent_message_chunk', { text: 'Your computer is ready.' });
return { ok: true, text: '', reason: 'stop' };
};
const bridge = Object.create(HarnessBridge.prototype);
bridge.session = session;
const reply = await bridge.ask('How is my computer?');
assert.equal(reply.text, 'Your computer is ready.');
});
test('QVAC scheduler prioritizes voice and keeps one active job', async () => { test('QVAC scheduler prioritizes voice and keeps one active job', async () => {
const scheduler = new QvacScheduler(); const scheduler = new QvacScheduler();
const order = []; const order = [];