Binary file not shown.
@@ -66,6 +66,12 @@ class ArcOverlay {
|
|||||||
if (this.status.clutter_text) this.status.clutter_text.ellipsize = Pango.EllipsizeMode.END;
|
if (this.status.clutter_text) this.status.clutter_text.ellipsize = Pango.EllipsizeMode.END;
|
||||||
this.header.add_child(this.title); this.header.add_child(new St.Widget({ x_expand: true })); this.header.add_child(this.status);
|
this.header.add_child(this.title); this.header.add_child(new St.Widget({ x_expand: true })); this.header.add_child(this.status);
|
||||||
this.statusLine = new St.Label({ text: 'Super+Shift+J · Hold Talk to speak', style_class: 'jarvis-status-line' });
|
this.statusLine = new St.Label({ text: 'Super+Shift+J · Hold Talk to speak', style_class: 'jarvis-status-line' });
|
||||||
|
this.thinkingToggle = new St.Button({ label: 'Thinking', style_class: 'jarvis-thinking-toggle', visible: false, can_focus: true, reactive: true });
|
||||||
|
this.thinkingToggle.accessible_name = 'Show or hide Jarvis thinking';
|
||||||
|
this.thinkingToggle.connect('clicked', () => this.toggleThinking());
|
||||||
|
this.thinking = new St.Label({ text: '', style_class: 'jarvis-thinking', visible: false, x_expand: true, can_focus: true });
|
||||||
|
this.thinking.accessible_name = 'Jarvis thinking';
|
||||||
|
if (this.thinking.clutter_text) { this.thinking.clutter_text.line_wrap = true; this.thinking.clutter_text.ellipsize = Pango.EllipsizeMode.NONE; }
|
||||||
this.job = new St.Label({ text: '', style_class: 'jarvis-job', can_focus: true, visible: false }); this.job.accessible_name = 'Jarvis job progress';
|
this.job = new St.Label({ text: '', style_class: 'jarvis-job', can_focus: true, visible: false }); this.job.accessible_name = 'Jarvis job progress';
|
||||||
this.target = new St.Label({ text: '', style_class: 'jarvis-target', can_focus: true, visible: false }); this.target.accessible_name = 'Computer use target';
|
this.target = new St.Label({ text: '', style_class: 'jarvis-target', can_focus: true, visible: false }); this.target.accessible_name = 'Computer use target';
|
||||||
this.cursor = new St.Label({ text: '', style_class: 'jarvis-agent-cursor', can_focus: false, visible: false }); this.cursor.accessible_name = 'Visible computer use cursor';
|
this.cursor = new St.Label({ text: '', style_class: 'jarvis-agent-cursor', can_focus: false, visible: false }); this.cursor.accessible_name = 'Visible computer use cursor';
|
||||||
@@ -80,7 +86,7 @@ class ArcOverlay {
|
|||||||
const chip = new St.Button({ label: mode, style_class: 'jarvis-chip', can_focus: true, reactive: true }); chip.accessible_name = `${mode} mode`;
|
const chip = new St.Button({ label: mode, style_class: 'jarvis-chip', can_focus: true, reactive: true }); chip.accessible_name = `${mode} mode`;
|
||||||
chip.connect('clicked', () => this.onMode?.(mode.toLowerCase())); this.chips.add_child(chip);
|
chip.connect('clicked', () => this.onMode?.(mode.toLowerCase())); this.chips.add_child(chip);
|
||||||
}
|
}
|
||||||
this.root.add_child(this.header); this.root.add_child(this.statusLine); this.root.add_child(this.job); this.root.add_child(this.target); this.root.add_child(this.cursor); this.root.add_child(this.wave); this.root.add_child(this.scroll); this.root.add_child(this.chips);
|
this.root.add_child(this.header); this.root.add_child(this.statusLine); this.root.add_child(this.thinkingToggle); this.root.add_child(this.thinking); this.root.add_child(this.job); this.root.add_child(this.target); this.root.add_child(this.cursor); this.root.add_child(this.wave); this.root.add_child(this.scroll); this.root.add_child(this.chips);
|
||||||
this.controls = new St.BoxLayout({ style_class: 'jarvis-chips' });
|
this.controls = new St.BoxLayout({ style_class: 'jarvis-chips' });
|
||||||
this.talk = new St.Button({ label: 'Talk', style_class: 'jarvis-chip jarvis-talk', reactive: true, can_focus: true });
|
this.talk = new St.Button({ label: 'Talk', style_class: 'jarvis-chip jarvis-talk', reactive: true, can_focus: true });
|
||||||
this.talk.accessible_name = 'Hold Space or Enter to talk';
|
this.talk.accessible_name = 'Hold Space or Enter to talk';
|
||||||
@@ -91,7 +97,7 @@ class ArcOverlay {
|
|||||||
this.talk.connect('button-press-event', () => { this.onTalk?.(true); return Clutter.EVENT_STOP; });
|
this.talk.connect('button-press-event', () => { this.onTalk?.(true); return Clutter.EVENT_STOP; });
|
||||||
this.talk.connect('button-release-event', () => { this.onTalk?.(false); return Clutter.EVENT_STOP; });
|
this.talk.connect('button-release-event', () => { this.onTalk?.(false); return Clutter.EVENT_STOP; });
|
||||||
this.controls.add_child(this.talk);
|
this.controls.add_child(this.talk);
|
||||||
for (const [label, action] of [['Stop', () => this.onStop?.()], ['Close', () => this.hide()]]) {
|
for (const [label, action] of [['Stop', () => this.onStop?.()], ['Minimize', () => this.minimize()], ['Close', () => this.hide()]]) {
|
||||||
const button = new St.Button({ label, style_class: 'jarvis-chip', reactive: true, can_focus: true });
|
const button = new St.Button({ label, style_class: 'jarvis-chip', reactive: true, can_focus: true });
|
||||||
button.connect('clicked', action); this.controls.add_child(button);
|
button.connect('clicked', action); this.controls.add_child(button);
|
||||||
}
|
}
|
||||||
@@ -101,7 +107,8 @@ class ArcOverlay {
|
|||||||
this.reducedMotion = false;
|
this.reducedMotion = false;
|
||||||
}
|
}
|
||||||
attach() { Main.layoutManager.addChrome(this.root, { affectsStruts: false, trackFullscreen: false }); this.halo = new St.Widget({ style_class: 'jarvis-halo', reactive: false }); Main.layoutManager.addChrome(this.halo, { affectsStruts: false, trackFullscreen: false }); this.halo.hide(); this.hide(); }
|
attach() { Main.layoutManager.addChrome(this.root, { affectsStruts: false, trackFullscreen: false }); this.halo = new St.Widget({ style_class: 'jarvis-halo', reactive: false }); Main.layoutManager.addChrome(this.halo, { affectsStruts: false, trackFullscreen: false }); this.halo.hide(); this.hide(); }
|
||||||
show() {
|
show(force = false) {
|
||||||
|
if (this.minimized && !force) return;
|
||||||
const monitor = Main.layoutManager.primaryMonitor;
|
const monitor = Main.layoutManager.primaryMonitor;
|
||||||
if (monitor) {
|
if (monitor) {
|
||||||
const width = Math.min(OVERLAY_WIDTH, monitor.width - 80);
|
const width = Math.min(OVERLAY_WIDTH, monitor.width - 80);
|
||||||
@@ -113,12 +120,27 @@ class ArcOverlay {
|
|||||||
this.root.visible = true;
|
this.root.visible = true;
|
||||||
if (!wasVisible) this.entry.grab_key_focus();
|
if (!wasVisible) this.entry.grab_key_focus();
|
||||||
}
|
}
|
||||||
hide() { this.onTalk?.(false); this.root.visible = false; }
|
hide() { this.onTalk?.(false); this.root.visible = false; this.minimized = false; }
|
||||||
toggle() { this.root.visible ? this.hide() : this.show(); }
|
minimize() { this.onTalk?.(false); this.root.visible = false; this.minimized = true; }
|
||||||
clear() { this.transcript.destroy_all_children(); }
|
toggle() { this.root.visible ? this.hide() : this.show(true); }
|
||||||
addRow(who, text) { const body = safeText(text); const row = new St.Label({ text: `${who === 'J' ? 'J' : 'YOU'} ${body}`, style_class: `jarvis-row jarvis-row-${who === 'J' ? 'jarvis' : 'user'}`, can_focus: true }); row.accessible_name = `${who === 'J' ? 'Jarvis' : 'You'}: ${body}`; if (row.clutter_text) { row.clutter_text.line_wrap = true; row.clutter_text.line_wrap_mode = Pango.WrapMode.WORD_CHAR; row.clutter_text.ellipsize = Pango.EllipsizeMode.NONE; } this.transcript.add_child(row); if (this.transcript.get_n_children() > 100) this.transcript.get_first_child().destroy(); this.show(); return row; }
|
clear() { this.transcript.destroy_all_children(); this.thinking.text = ''; this.thinking.visible = false; this.thinkingToggle.visible = false; }
|
||||||
token(text) { const chunk = safeText(text); if (!chunk) return; let row = this.transcript.get_last_child?.(); if (!row || !String(row.style_class || '').includes('jarvis-row-jarvis')) { row = this.addRow('J', ''); } row.text = `${row.text}${chunk}`; row.accessible_name = `Jarvis: ${row.text}`; this.show(); }
|
_followConversation() {
|
||||||
|
const adjustment = this.scroll?.get_vadjustment?.() || this.scroll?.vadjustment;
|
||||||
|
if (!adjustment) return;
|
||||||
|
GLib.idle_add(GLib.PRIORITY_DEFAULT_IDLE || GLib.PRIORITY_DEFAULT, () => {
|
||||||
|
try { adjustment.value = Math.max(0, adjustment.upper - adjustment.page_size); } catch {}
|
||||||
|
return GLib.SOURCE_REMOVE;
|
||||||
|
});
|
||||||
|
}
|
||||||
|
addRow(who, text) { const body = safeText(text); const row = new St.Label({ text: `${who === 'J' ? 'J' : 'YOU'} ${body}`, style_class: `jarvis-row jarvis-row-${who === 'J' ? 'jarvis' : 'user'}`, can_focus: true }); row.accessible_name = `${who === 'J' ? 'Jarvis' : 'You'}: ${body}`; if (row.clutter_text) { row.clutter_text.line_wrap = true; row.clutter_text.line_wrap_mode = Pango.WrapMode.WORD_CHAR; row.clutter_text.ellipsize = Pango.EllipsizeMode.NONE; } this.transcript.add_child(row); if (this.transcript.get_n_children() > 100) this.transcript.get_first_child().destroy(); this.show(); this._followConversation(); return row; }
|
||||||
|
token(text) { const chunk = safeText(text); if (!chunk) return; let row = this.transcript.get_last_child?.(); if (!row || !String(row.style_class || '').includes('jarvis-row-jarvis')) { row = this.addRow('J', ''); } row.text = `${row.text}${chunk}`; row.accessible_name = `Jarvis: ${row.text}`; this.show(); this._followConversation(); }
|
||||||
|
updateThinking(text) { const chunk = safeText(text); if (!chunk) return; this.thinking.text = `${this.thinking.text || ''}${chunk}`; this.thinkingToggle.visible = true; this.thinking.visible = true; this.thinkingToggle.label = 'Thinking ▾'; this.show(); }
|
||||||
|
toggleThinking() { this.thinking.visible = !this.thinking.visible; this.thinkingToggle.label = this.thinking.visible ? 'Thinking ▾' : 'Thinking ▸'; }
|
||||||
|
finishThinking() { if (this.thinking.text) { this.thinkingToggle.visible = true; this.thinkingToggle.label = 'Thinking ▸'; this.thinking.visible = false; } }
|
||||||
|
addToolCall(json) { try { const call = JSON.parse(json); this.addRow('J', `Using ${safeText(call.name || 'tool')}…`); } catch { this.addRow('J', `Using ${safeText(json)}…`); } this.finishThinking(); }
|
||||||
|
addToolResult(json) { try { const result = JSON.parse(json); this.addRow('J', `${safeText(result.name || 'tool')} complete`); } catch { this.addRow('J', 'Tool complete'); } this.finishThinking(); }
|
||||||
finalizeReply(text) {
|
finalizeReply(text) {
|
||||||
|
this.finishThinking();
|
||||||
const spoken = safeText(text);
|
const spoken = safeText(text);
|
||||||
let row = this.transcript.get_last_child?.();
|
let row = this.transcript.get_last_child?.();
|
||||||
if (row && String(row.style_class || '').includes('jarvis-row-jarvis')) {
|
if (row && String(row.style_class || '').includes('jarvis-row-jarvis')) {
|
||||||
@@ -175,6 +197,9 @@ export default class JarvisExtension extends Extension {
|
|||||||
['WakeHeard', () => this.overlay.show()],
|
['WakeHeard', () => this.overlay.show()],
|
||||||
['PartialTranscript', (text) => this.overlay.addRow('U', text)],
|
['PartialTranscript', (text) => this.overlay.addRow('U', text)],
|
||||||
['Token', (text) => this.overlay.token(text)],
|
['Token', (text) => this.overlay.token(text)],
|
||||||
|
['Thinking', (text) => this.overlay.updateThinking(text)],
|
||||||
|
['ToolCall', (json) => this.overlay.addToolCall(json)],
|
||||||
|
['ToolResult', (json) => this.overlay.addToolResult(json)],
|
||||||
['Reply', (text) => { this.overlay.finalizeReply(text); this.overlay.show(); }],
|
['Reply', (text) => { this.overlay.finalizeReply(text); this.overlay.show(); }],
|
||||||
['SpeakingLevel', (rms) => this.overlay.level(rms)],
|
['SpeakingLevel', (rms) => this.overlay.level(rms)],
|
||||||
['ListeningLevel', (rms) => this.overlay.level(rms)],
|
['ListeningLevel', (rms) => this.overlay.level(rms)],
|
||||||
@@ -182,7 +207,7 @@ export default class JarvisExtension extends Extension {
|
|||||||
['JobProgress', (id, pct, label) => this.overlay.addJob(id, pct, label)],
|
['JobProgress', (id, pct, label) => this.overlay.addJob(id, pct, label)],
|
||||||
['ComputerStep', (json) => this.overlay.addStep(json)],
|
['ComputerStep', (json) => this.overlay.addStep(json)],
|
||||||
['ComputerHighlight', (json) => this.overlay.setTarget(json)],
|
['ComputerHighlight', (json) => this.overlay.setTarget(json)],
|
||||||
['Error', (code, message) => { this.overlay.addRow('J', message); if (coerceText(code) === 'VOICE_UNAVAILABLE') this.overlay.setConnectionStatus('voice-unavailable'); }],
|
['Error', (code, message) => { this.overlay.addRow('J', message); this.overlay.finishThinking(); if (coerceText(code) === 'VOICE_UNAVAILABLE') this.overlay.setConnectionStatus('voice-unavailable'); }],
|
||||||
].map(([name, handler]) => proxy.on(name, handler));
|
].map(([name, handler]) => proxy.on(name, handler));
|
||||||
proxy.onOwnerChanged = () => { if (this.proxy !== proxy) return; this._syncDaemon(); };
|
proxy.onOwnerChanged = () => { if (this.proxy !== proxy) return; this._syncDaemon(); };
|
||||||
await this._syncDaemon();
|
await this._syncDaemon();
|
||||||
|
|||||||
@@ -4,6 +4,9 @@
|
|||||||
.jarvis-title { font-weight: bold; letter-spacing: 1px; }
|
.jarvis-title { font-weight: bold; letter-spacing: 1px; }
|
||||||
.jarvis-local { color: var(--jarvis-accent, #F4B942); font-size: 11px; }
|
.jarvis-local { color: var(--jarvis-accent, #F4B942); font-size: 11px; }
|
||||||
.jarvis-status-line { color: #aeb6c8; font-size: 12px; }
|
.jarvis-status-line { color: #aeb6c8; font-size: 12px; }
|
||||||
|
.jarvis-thinking-toggle { color: #9aa8c0; font-size: 12px; padding: 4px 8px; border-radius: 8px; background-color: rgba(255, 255, 255, .05); }
|
||||||
|
.jarvis-thinking-toggle:hover, .jarvis-thinking-toggle:focus { color: #f6f7fb; background-color: rgba(79, 210, 255, .16); }
|
||||||
|
.jarvis-thinking { color: #aeb6c8; font-size: 12px; padding: 8px 12px; border-left: 2px solid #4FD2FF; background-color: rgba(79, 210, 255, .06); border-radius: 8px; }
|
||||||
.jarvis-job, .jarvis-target { color: #4FD2FF; font-size: 12px; }
|
.jarvis-job, .jarvis-target { color: #4FD2FF; font-size: 12px; }
|
||||||
.jarvis-agent-cursor { color: var(--jarvis-accent, #F4B942); font-size: 22px; }
|
.jarvis-agent-cursor { color: var(--jarvis-accent, #F4B942); font-size: 22px; }
|
||||||
.jarvis-halo { border-top: 2px solid #F4B942; margin: 12px; }
|
.jarvis-halo { border-top: 2px solid #F4B942; margin: 12px; }
|
||||||
|
|||||||
@@ -54,6 +54,9 @@ export async function serveOnSessionBus(daemon) {
|
|||||||
JobProgress(id, pct, label) { return [String(id), Number(pct), String(label)]; }
|
JobProgress(id, pct, label) { return [String(id), Number(pct), String(label)]; }
|
||||||
ComputerStep(json) { return String(json); }
|
ComputerStep(json) { return String(json); }
|
||||||
ComputerHighlight(json) { return String(json); }
|
ComputerHighlight(json) { return String(json); }
|
||||||
|
Thinking(text) { return String(text); }
|
||||||
|
ToolCall(json) { return String(json); }
|
||||||
|
ToolResult(json) { return String(json); }
|
||||||
}
|
}
|
||||||
Session.configureMembers({
|
Session.configureMembers({
|
||||||
methods: {
|
methods: {
|
||||||
@@ -92,6 +95,9 @@ export async function serveOnSessionBus(daemon) {
|
|||||||
ComputerStep: { signature: 's' },
|
ComputerStep: { signature: 's' },
|
||||||
ComputerHighlight: { signature: 's' },
|
ComputerHighlight: { signature: 's' },
|
||||||
ConfirmationRequired: { signature: 'sss' },
|
ConfirmationRequired: { signature: 'sss' },
|
||||||
|
Thinking: { signature: 's' },
|
||||||
|
ToolCall: { signature: 's' },
|
||||||
|
ToolResult: { signature: 's' },
|
||||||
},
|
},
|
||||||
});
|
});
|
||||||
const bus = dbus.sessionBus();
|
const bus = dbus.sessionBus();
|
||||||
@@ -133,7 +139,7 @@ export async function serveOnSessionBus(daemon) {
|
|||||||
daemon.on('Token', (token) => iface.Token(token));
|
daemon.on('Token', (token) => iface.Token(token));
|
||||||
daemon.on('Error', (code, message) => iface.Error(code, message));
|
daemon.on('Error', (code, message) => iface.Error(code, message));
|
||||||
daemon.on('ConfirmationRequired', (event) => iface.ConfirmationRequired(event?.tool || 'action', JSON.stringify(event?.args || {}), event?.pattern || 'explicit confirmation required'));
|
daemon.on('ConfirmationRequired', (event) => iface.ConfirmationRequired(event?.tool || 'action', JSON.stringify(event?.args || {}), event?.pattern || 'explicit confirmation required'));
|
||||||
for (const signal of ['WakeHeard', 'PartialTranscript', 'FinalTranscript', 'SpeakingLevel', 'ListeningLevel', 'ChipOffered', 'JobProgress', 'ComputerStep', 'ComputerHighlight']) {
|
for (const signal of ['WakeHeard', 'PartialTranscript', 'FinalTranscript', 'SpeakingLevel', 'ListeningLevel', 'ChipOffered', 'JobProgress', 'ComputerStep', 'ComputerHighlight', 'Thinking', 'ToolCall', 'ToolResult']) {
|
||||||
daemon.on(signal, (...args) => iface[signal](...args));
|
daemon.on(signal, (...args) => iface[signal](...args));
|
||||||
}
|
}
|
||||||
return bus;
|
return bus;
|
||||||
|
|||||||
@@ -28,7 +28,7 @@ export class HarnessBridge extends EventEmitter {
|
|||||||
await closeQvac();
|
await closeQvac();
|
||||||
throw error;
|
throw error;
|
||||||
}
|
}
|
||||||
for (const event of ['agent_message_chunk', 'tool_call', 'permission', 'ask_user', 'cap-chunk', 'error']) {
|
for (const event of ['agent_message_chunk', 'agent_thought_chunk', 'tool_call', 'tool_result', 'permission', 'ask_user', 'cap-chunk', 'error']) {
|
||||||
this.session.on(event, (payload) => {
|
this.session.on(event, (payload) => {
|
||||||
if (event === 'agent_message_chunk' && payload?.text) {
|
if (event === 'agent_message_chunk' && payload?.text) {
|
||||||
const parsed = parseHudSidecar(payload.text);
|
const parsed = parseHudSidecar(payload.text);
|
||||||
|
|||||||
@@ -40,6 +40,9 @@ export class JarvisDaemon extends EventEmitter {
|
|||||||
this.telemetry = new RuntimeTelemetry();
|
this.telemetry = new RuntimeTelemetry();
|
||||||
this._telemetryTimer = setInterval(() => this.telemetry.sample(), 60_000); this._telemetryTimer.unref?.();
|
this._telemetryTimer = setInterval(() => this.telemetry.sample(), 60_000); this._telemetryTimer.unref?.();
|
||||||
this.harness.on('agent_message_chunk', (ev) => this.emit('Token', ev?.text || ev?.delta || ''));
|
this.harness.on('agent_message_chunk', (ev) => this.emit('Token', ev?.text || ev?.delta || ''));
|
||||||
|
this.harness.on('agent_thought_chunk', (ev) => this.emit('Thinking', String(ev?.text || ev?.delta || '')));
|
||||||
|
this.harness.on('tool_call', (ev) => this.emit('ToolCall', JSON.stringify({ name: ev?.call?.name || 'tool', arguments: ev?.call?.arguments || {} })));
|
||||||
|
this.harness.on('tool_result', (ev) => this.emit('ToolResult', JSON.stringify({ name: ev?.name || 'tool', result: ev?.result || '', toolCallId: ev?.toolCallId || '' })));
|
||||||
this.harness.on('permission', (ev) => this.emit('ConfirmationRequired', ev));
|
this.harness.on('permission', (ev) => this.emit('ConfirmationRequired', ev));
|
||||||
this.harness.on('hud_sidecar', (ev) => this.emit('ChipOffered', 'sidecar', ev?.title || 'Suggested action', JSON.stringify(ev || {})));
|
this.harness.on('hud_sidecar', (ev) => this.emit('ChipOffered', 'sidecar', ev?.title || 'Suggested action', JSON.stringify(ev || {})));
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -24,6 +24,7 @@
|
|||||||
<signal name="JobProgress"><arg type="s"/><arg type="d"/><arg type="s"/></signal>
|
<signal name="JobProgress"><arg type="s"/><arg type="d"/><arg type="s"/></signal>
|
||||||
<signal name="ComputerStep"><arg type="s"/></signal><signal name="ComputerHighlight"><arg type="s"/></signal>
|
<signal name="ComputerStep"><arg type="s"/></signal><signal name="ComputerHighlight"><arg type="s"/></signal>
|
||||||
<signal name="ConfirmationRequired"><arg type="s"/><arg type="s"/><arg type="s"/></signal>
|
<signal name="ConfirmationRequired"><arg type="s"/><arg type="s"/><arg type="s"/></signal>
|
||||||
|
<signal name="Thinking"><arg type="s"/></signal><signal name="ToolCall"><arg type="s"/></signal><signal name="ToolResult"><arg type="s"/></signal>
|
||||||
<signal name="Error"><arg type="s"/><arg type="s"/></signal>
|
<signal name="Error"><arg type="s"/><arg type="s"/></signal>
|
||||||
</interface>
|
</interface>
|
||||||
</node>
|
</node>
|
||||||
|
|||||||
@@ -4,6 +4,11 @@ Ground every desktop, file, memory, and model claim in a tool result. Never clai
|
|||||||
Computer use requires an explicit user grant; never click or type while it is inactive, locked, or revoked.
|
Computer use requires an explicit user grant; never click or type while it is inactive, locked, or revoked.
|
||||||
Destructive actions require confirmation in both the HUD and spoken conversation.
|
Destructive actions require confirmation in both the HUD and spoken conversation.
|
||||||
Prefer structured tools and accessibility references over coordinates.
|
Prefer structured tools and accessibility references over coordinates.
|
||||||
|
For questions about the computer, files, processes, or system state, call the
|
||||||
|
most relevant registered tool before answering. Never say that computer use or
|
||||||
|
terminal access is unavailable unless a tool result reports that limitation.
|
||||||
|
When the user asks to use the command line or terminal, call run_terminal_cmd
|
||||||
|
and base the answer on its result; do not substitute a refusal or guess.
|
||||||
When a useful follow-up action exists, append a HUD sidecar exactly as <jarvis_hud>{"title":"...","chips":[{"id":"...","label":"..."}],"confirmation":null}</jarvis_hud>.
|
When a useful follow-up action exists, append a HUD sidecar exactly as <jarvis_hud>{"title":"...","chips":[{"id":"...","label":"..."}],"confirmation":null}</jarvis_hud>.
|
||||||
The sidecar is for the HUD and must not be spoken.`;
|
The sidecar is for the HUD and must not be spoken.`;
|
||||||
|
|
||||||
|
|||||||
@@ -111,6 +111,9 @@ test('GNOME GI imports expose default namespaces and panel has a real menu', ()
|
|||||||
assert.match(source, /new PanelMenu.Button\(0.0, 'Jarvis QVAC', false\)/);
|
assert.match(source, /new PanelMenu.Button\(0.0, 'Jarvis QVAC', false\)/);
|
||||||
assert.match(source, /PushToTalk/);
|
assert.match(source, /PushToTalk/);
|
||||||
assert.match(source, /finalizeReply/);
|
assert.match(source, /finalizeReply/);
|
||||||
|
assert.match(source, /\['Thinking'/);
|
||||||
|
assert.match(source, /\['ToolCall'/);
|
||||||
|
assert.match(source, /Minimize/);
|
||||||
});
|
});
|
||||||
|
|
||||||
test('D-Bus confirmation signal does not call missing Interface.emit', () => {
|
test('D-Bus confirmation signal does not call missing Interface.emit', () => {
|
||||||
|
|||||||
Reference in New Issue
Block a user