Updates
Rolling release / release (push) Successful in 8m31s

This commit is contained in:
2026-09-13 15:08:05 -04:00
parent b56171da9b
commit c5ccaa490b
29 changed files with 933 additions and 146 deletions
+1 -1
View File
@@ -41,7 +41,7 @@ Workspace skills live in `skills/<name>/SKILL.md`. When a request matches a skil
- `read_file` / `list_dir` / `grep` / `write_file` / `search_replace` — workspace files.
- `run_terminal_cmd` — local shell. Public HTTP via curl or wget is blocked; use web tools.
- `web_search` / `google_search` / `fetch_page` / `web_fetch` / `wiki_search` / `hn_search` / `code_search` — public reads, no extra keys.
- Desktop and computer-use tools are registered by Jarvis. Observe and act only with an active grant.
- Desktop and computer-use tools are registered by Jarvis. After Grant desktop, call `cu_observe`, then `cu_click` / `cu_type`. Do not paste tool JSON into chat.
- `ask_user_question` — wait for a user choice.
Keep going until the users request is fully complete. Never stop after announcing the next step. When the work is done, speak a short summary. A greeting does not need a long summary.
+2 -1
View File
@@ -38,6 +38,7 @@ const pendingCustom = new Map();
const pendingAsks = new Map();
const pendingPlans = new Map();
const MAX_TURNS = 24;
const CU_FREE_ROUNDS = new Set(['todo_write', 'update_goal', 'cu_observe', 'cu_find', 'cu_tree', 'cu_status', 'cu_zoom']);
const SUBAGENT_TURNS = 8;
const CUSTOM_TOOL_TIMEOUT_MS = 60000;
const ASK_TIMEOUT_MS = 10 * 60 * 1000;
@@ -877,7 +878,7 @@ async function runTurn(ctx) {
}
if (stopEarly) break;
}
if (prepared.some((item) => item.name !== 'todo_write' && item.name !== 'update_goal')) {
if (prepared.some((item) => !CU_FREE_ROUNDS.has(item.name))) {
toolBudget.markToolRound(budget);
}
if (stopEarly) {
+1
View File
@@ -303,6 +303,7 @@ const COMPACT_TOOL_ALLOW = [
'cu_status',
'cu_observe',
'cu_find',
'cu_tree',
'cu_click',
'cu_type',
'cu_key',
+49 -3
View File
@@ -10,13 +10,59 @@ function clip(s) {
return t.slice(0, MAX_DELTA);
}
function eventDelta(ev) {
if (ev == null) return '';
if (typeof ev === 'string' || typeof ev === 'number' || typeof ev === 'boolean') return clip(ev);
return clip(ev.text || ev.delta || ev.content || ev.chunk || '');
}
const THINK_OPEN = '<think>';
const THINK_CLOSE = '</think>';
function trailingPartial(buf, marker) {
const max = Math.min(buf.length, marker.length - 1);
for (let len = max; len > 0; len--) {
if (marker.startsWith(buf.slice(buf.length - len))) return len;
}
return 0;
}
function expandThinkEvents(n, state) {
const st = state && typeof state === 'object' ? state : { inThink: false, carry: '' };
if (!n) return { events: [], state: st };
if (n.type === 'thinkingDelta') {
const delta = n.delta || '';
return { events: delta ? [n] : [], state: st };
}
if (n.type !== 'contentDelta') return { events: [n], state: st };
let rest = String(st.carry || '') + String(n.delta || '');
const events = [];
let inThink = !!st.inThink;
while (rest) {
const marker = inThink ? THINK_CLOSE : THINK_OPEN;
const idx = rest.indexOf(marker);
if (idx < 0) {
const partial = trailingPartial(rest, marker);
const emit = partial ? rest.slice(0, rest.length - partial) : rest;
const carry = partial ? rest.slice(rest.length - partial) : '';
if (emit) events.push({ type: inThink ? 'thinkingDelta' : 'contentDelta', delta: emit });
return { events, state: { inThink, carry } };
}
const before = rest.slice(0, idx);
if (before) events.push({ type: inThink ? 'thinkingDelta' : 'contentDelta', delta: before });
rest = rest.slice(idx + marker.length);
inThink = !inThink;
}
return { events, state: { inThink, carry: '' } };
}
function normalizeCompletionEvent(ev) {
if (!ev || !ev.type) return null;
if (ev.type === 'contentDelta') {
return { type: 'contentDelta', delta: clip(ev.delta || ev.text || ev.content || '') };
return { type: 'contentDelta', delta: eventDelta(ev) };
}
if (ev.type === 'thinkingDelta') {
return { type: 'thinkingDelta', delta: clip(ev.delta || ev.text || ev.content || '') };
return { type: 'thinkingDelta', delta: eventDelta(ev) };
}
if (ev.type === 'toolCall') {
const call = ev.call || ev.toolCall || ev;
@@ -35,4 +81,4 @@ function normalizeCompletionEvent(ev) {
return { type: ev.type, delta: ev.delta != null ? clip(ev.delta) : undefined, call: ev.call };
}
module.exports = { normalizeCompletionEvent, MAX_DELTA };
module.exports = { normalizeCompletionEvent, expandThinkEvents, eventDelta, MAX_DELTA };
+45 -26
View File
@@ -396,6 +396,7 @@ async function complete(opts, onEvent) {
let text = '';
let thinking = '';
let thinkState = { inThink: false, carry: '' };
const toolCalls = [];
const abortRun = () => {
try {
@@ -420,39 +421,57 @@ async function complete(opts, onEvent) {
const n = events.normalizeCompletionEvent(ev);
if (!n) continue;
watch.bump();
if (n.type === 'contentDelta') {
text += n.delta;
if (onEvent) onEvent(n);
if (repeatLoop.isRepeating(text)) {
abortRun();
settleTimeout();
return;
const split = events.expandThinkEvents(n, thinkState);
thinkState = split.state;
for (const p of split.events) {
if (p.type === 'contentDelta') {
text += p.delta;
if (onEvent) onEvent(p);
if (repeatLoop.isRepeating(text)) {
abortRun();
settleTimeout();
return;
}
} else if (p.type === 'thinkingDelta') {
thinking += p.delta;
if (onEvent) onEvent(p);
if (repeatLoop.isRepeating(thinking)) {
abortRun();
settleTimeout();
return;
}
} else if (p.type === 'toolCall') {
toolCalls.push(p.call);
if (onEvent) onEvent(p);
} else if (onEvent) {
onEvent(p);
}
} else if (n.type === 'thinkingDelta') {
thinking += n.delta;
if (onEvent) onEvent(n);
if (repeatLoop.isRepeating(thinking)) {
abortRun();
settleTimeout();
return;
}
} else if (n.type === 'toolCall') {
toolCalls.push(n.call);
if (onEvent) onEvent(n);
} else if (onEvent) {
onEvent(n);
}
}
} else if (run.tokenStream) {
for await (const token of run.tokenStream) {
if (watch.timedOut()) return;
watch.bump();
text += token;
if (onEvent) onEvent({ type: 'contentDelta', delta: token });
if (repeatLoop.isRepeating(text)) {
abortRun();
settleTimeout();
return;
const split = events.expandThinkEvents({ type: 'contentDelta', delta: token }, thinkState);
thinkState = split.state;
for (const p of split.events) {
if (p.type === 'thinkingDelta') {
thinking += p.delta;
if (onEvent) onEvent(p);
if (repeatLoop.isRepeating(thinking)) {
abortRun();
settleTimeout();
return;
}
} else {
text += p.delta;
if (onEvent) onEvent({ type: 'contentDelta', delta: p.delta });
if (repeatLoop.isRepeating(text)) {
abortRun();
settleTimeout();
return;
}
}
}
}
if (run.toolCallStream) {
+11
View File
@@ -29,6 +29,17 @@ const ALIASES = {
write: 'write_file',
ls: 'list_dir',
status: 'jarvis_status',
click: 'cu_click',
type: 'cu_type',
type_text: 'cu_type',
observe: 'cu_observe',
look: 'cu_observe',
screenshot: 'cu_observe',
find: 'cu_find',
hover: 'cu_hover',
scroll: 'cu_scroll',
key: 'cu_key',
press: 'cu_key',
};
function knownNames(tools) {