tesseract-agent 0.11.2 → 0.11.3
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/dist-bundle/cli.js
CHANGED
|
@@ -37920,6 +37920,9 @@ import { spawn as spawn2, execSync, execFileSync } from "node:child_process";
|
|
|
37920
37920
|
function setBrowserExtensionProbe(fn) {
|
|
37921
37921
|
browserExtensionConnected = fn;
|
|
37922
37922
|
}
|
|
37923
|
+
function isTurnBearingFrame(type) {
|
|
37924
|
+
return type === "assistant" || type === "stream_event" || type === "user";
|
|
37925
|
+
}
|
|
37923
37926
|
function findUserInstalledClaude(binName) {
|
|
37924
37927
|
const home = homedir3();
|
|
37925
37928
|
const abs = [
|
|
@@ -39980,50 +39983,72 @@ ${full.slice(-8e3)}`;
|
|
|
39980
39983
|
let cappedOut = false;
|
|
39981
39984
|
let narrated = false;
|
|
39982
39985
|
const turn = live.turnSeq;
|
|
39983
|
-
|
|
39984
|
-
|
|
39985
|
-
|
|
39986
|
-
|
|
39987
|
-
|
|
39988
|
-
|
|
39989
|
-
|
|
39990
|
-
|
|
39991
|
-
|
|
39992
|
-
|
|
39993
|
-
|
|
39994
|
-
|
|
39995
|
-
|
|
39996
|
-
|
|
39997
|
-
|
|
39998
|
-
|
|
39999
|
-
|
|
40000
|
-
|
|
40001
|
-
|
|
40002
|
-
|
|
40003
|
-
|
|
40004
|
-
|
|
40005
|
-
|
|
40006
|
-
|
|
40007
|
-
|
|
40008
|
-
|
|
40009
|
-
|
|
40010
|
-
|
|
40011
|
-
|
|
40012
|
-
|
|
40013
|
-
|
|
39986
|
+
live.gradePending = true;
|
|
39987
|
+
try {
|
|
39988
|
+
for (; ; ) {
|
|
39989
|
+
if (live.rv !== rv) {
|
|
39990
|
+
log(`[work] quiescence wait for ${live.sessionId} dropped \u2014 another run owns the session`);
|
|
39991
|
+
this.clearWorkNarration(live, narrated);
|
|
39992
|
+
return;
|
|
39993
|
+
}
|
|
39994
|
+
if (live.gone) {
|
|
39995
|
+
log(`[work] quiescence wait for ${live.sessionId} dropped \u2014 the session was torn down`);
|
|
39996
|
+
this.clearWorkNarration(live, narrated);
|
|
39997
|
+
return;
|
|
39998
|
+
}
|
|
39999
|
+
if (live.turnSeq !== turn) {
|
|
40000
|
+
log(`[work] quiescence wait for ${live.sessionId} superseded by a new turn`);
|
|
40001
|
+
this.clearWorkNarration(live, narrated);
|
|
40002
|
+
return;
|
|
40003
|
+
}
|
|
40004
|
+
const v = live.ledger.verdict(Date.now());
|
|
40005
|
+
if (v.quiescent)
|
|
40006
|
+
break;
|
|
40007
|
+
if (Date.now() - started2 >= QUIESCENCE_MAX_MS) {
|
|
40008
|
+
cappedOut = true;
|
|
40009
|
+
break;
|
|
40010
|
+
}
|
|
40011
|
+
if (!narrated && live.sessionId) {
|
|
40012
|
+
narrated = true;
|
|
40013
|
+
this.send({
|
|
40014
|
+
type: "agent:narration",
|
|
40015
|
+
sessionId: live.sessionId,
|
|
40016
|
+
text: `Waiting for ${v.waiting.length} background ${v.waiting.length === 1 ? "task" : "tasks"} to finish\u2026`,
|
|
40017
|
+
engineLabel: "Claude Code"
|
|
40018
|
+
}, live.origin);
|
|
40019
|
+
}
|
|
40020
|
+
await new Promise((res) => setTimeout(res, QUIESCENCE_POLL_MS));
|
|
40014
40021
|
}
|
|
40015
|
-
|
|
40022
|
+
this.clearWorkNarration(live, narrated);
|
|
40023
|
+
live.ledger.sweep(Date.now());
|
|
40024
|
+
const outstanding = live.ledger.summarize(Date.now());
|
|
40025
|
+
if (cappedOut)
|
|
40026
|
+
log(`[work] quiescence cap reached for ${live.sessionId}; grading anyway with: ${outstanding.join("; ") || "(nothing outstanding)"}`);
|
|
40027
|
+
else if (Date.now() - started2 > 1e3)
|
|
40028
|
+
log(`[work] ${live.sessionId} settled after ${Math.round((Date.now() - started2) / 1e3)}s \u2014 grading now`);
|
|
40029
|
+
if (!cappedOut && outstanding.length)
|
|
40030
|
+
log(`[work] grading with work still in flight: ${outstanding.join("; ")}`);
|
|
40031
|
+
await this.runVerification(live, finalText, didWork);
|
|
40032
|
+
} finally {
|
|
40033
|
+
live.gradePending = false;
|
|
40034
|
+
}
|
|
40035
|
+
}
|
|
40036
|
+
/**
|
|
40037
|
+
* Mark a turn the engine started on its own as busy — exactly what a send does.
|
|
40038
|
+
*
|
|
40039
|
+
* Bumping `turnSeq` is deliberate: a quiescence wait for the PREVIOUS turn must see itself
|
|
40040
|
+
* superseded, the same way it would by a turn we started, rather than grade the old final
|
|
40041
|
+
* text while the new turn runs.
|
|
40042
|
+
*/
|
|
40043
|
+
adoptSelfStartedTurn(live) {
|
|
40044
|
+
log(`[turn] ${live.sessionId || "(pending)"} started a turn on its own \u2014 marking busy`);
|
|
40045
|
+
live.streaming = true;
|
|
40046
|
+
live.turnSeq++;
|
|
40047
|
+
live.lastActivity = Date.now();
|
|
40048
|
+
if (live.idleTimer) {
|
|
40049
|
+
clearTimeout(live.idleTimer);
|
|
40050
|
+
live.idleTimer = void 0;
|
|
40016
40051
|
}
|
|
40017
|
-
this.clearWorkNarration(live, narrated);
|
|
40018
|
-
live.ledger.sweep(Date.now());
|
|
40019
|
-
const outstanding = live.ledger.summarize(Date.now());
|
|
40020
|
-
if (cappedOut)
|
|
40021
|
-
log(`[work] quiescence cap reached for ${live.sessionId}; grading anyway with: ${outstanding.join("; ") || "(nothing outstanding)"}`);
|
|
40022
|
-
else if (Date.now() - started2 > 1e3)
|
|
40023
|
-
log(`[work] ${live.sessionId} settled after ${Math.round((Date.now() - started2) / 1e3)}s \u2014 grading now`);
|
|
40024
|
-
if (!cappedOut && outstanding.length)
|
|
40025
|
-
log(`[work] grading with work still in flight: ${outstanding.join("; ")}`);
|
|
40026
|
-
await this.runVerification(live, finalText, didWork);
|
|
40027
40052
|
}
|
|
40028
40053
|
/** Take down the "waiting on background work" strip, if we put one up. */
|
|
40029
40054
|
clearWorkNarration(live, narrated) {
|
|
@@ -42557,6 +42582,8 @@ ${unattendedReportDirective(params)}` : ""), live.sessionId) },
|
|
|
42557
42582
|
}
|
|
42558
42583
|
const sid = live.sessionId;
|
|
42559
42584
|
const parentToolUseId = message.parent_tool_use_id ?? void 0;
|
|
42585
|
+
if (!live.streaming && !live.reaped && isTurnBearingFrame(message.type))
|
|
42586
|
+
this.adoptSelfStartedTurn(live);
|
|
42560
42587
|
switch (message.type) {
|
|
42561
42588
|
case "stream_event": {
|
|
42562
42589
|
const event = message.event;
|
|
@@ -43314,6 +43341,7 @@ ${err2.stack}` : ""}` : String(err2);
|
|
|
43314
43341
|
emit2({ type: "agent:status", sessionId: sid, status: "error" });
|
|
43315
43342
|
}
|
|
43316
43343
|
} finally {
|
|
43344
|
+
live.gone = true;
|
|
43317
43345
|
if (live.idleTimer) {
|
|
43318
43346
|
clearTimeout(live.idleTimer);
|
|
43319
43347
|
live.idleTimer = void 0;
|
|
@@ -43522,6 +43550,7 @@ ${err2.stack}` : ""}` : String(err2);
|
|
|
43522
43550
|
live.abort.abort();
|
|
43523
43551
|
} catch {
|
|
43524
43552
|
}
|
|
43553
|
+
live.gone = true;
|
|
43525
43554
|
}
|
|
43526
43555
|
/** M7 — stop ONE background task (vs interruptSession which aborts the whole
|
|
43527
43556
|
* turn). Driver-only, same origin guard. No-op if the session/task is gone. */
|
|
@@ -44247,12 +44276,45 @@ ${lines.join("\n")}
|
|
|
44247
44276
|
/** Close an idle session's Query (→ child process + IPC freed via runConsumer's
|
|
44248
44277
|
* finally), keeping the on-disk session resumable. Deletes from the live map
|
|
44249
44278
|
* FIRST so a racing sendMessage misses it and cleanly re-creates with resume. */
|
|
44279
|
+
/**
|
|
44280
|
+
* Is this session, whatever `streaming` says, still in the middle of something?
|
|
44281
|
+
*
|
|
44282
|
+
* `streaming` only tracks turns WE started. A CLI running its own background monitor
|
|
44283
|
+
* starts turns by itself when events land, and between two of those — or during a long
|
|
44284
|
+
* tool call inside one — the session reads as idle here while the engine is hard at work.
|
|
44285
|
+
* The memory monitor's moderate-pressure reclaim reaped exactly such a session, 60 seconds
|
|
44286
|
+
* "idle" with a batch barrier that had just reported "1 still running": the CLI carried on
|
|
44287
|
+
* for another 69 minutes, un-mapped and ungradeable. Work the ledger is awaiting, a job
|
|
44288
|
+
* that is running, a wait inside `await_jobs`, or a turn-end grade still waiting for
|
|
44289
|
+
* quiescence — any of these means the session is busy.
|
|
44290
|
+
*/
|
|
44291
|
+
hasWorkInFlight(live) {
|
|
44292
|
+
if (live.gradePending)
|
|
44293
|
+
return true;
|
|
44294
|
+
if ((live.awaitDepth ?? 0) > 0)
|
|
44295
|
+
return true;
|
|
44296
|
+
try {
|
|
44297
|
+
if (live.ledger.verdict(Date.now()).waiting.length > 0)
|
|
44298
|
+
return true;
|
|
44299
|
+
} catch {
|
|
44300
|
+
}
|
|
44301
|
+
try {
|
|
44302
|
+
if (live.jobs?.list().some((j) => j.status === "running"))
|
|
44303
|
+
return true;
|
|
44304
|
+
} catch {
|
|
44305
|
+
}
|
|
44306
|
+
return false;
|
|
44307
|
+
}
|
|
44250
44308
|
reapIdleSession(sessionId, force = false) {
|
|
44251
44309
|
const live = this.liveSessions.get(sessionId);
|
|
44252
44310
|
if (!live || live.streaming || live.reaped)
|
|
44253
44311
|
return false;
|
|
44254
44312
|
if (this.isHeld(live) && !force)
|
|
44255
44313
|
return false;
|
|
44314
|
+
if (!force && this.hasWorkInFlight(live)) {
|
|
44315
|
+
log(`[reap] ${sessionId} looks idle but has work in flight \u2014 left alone`);
|
|
44316
|
+
return false;
|
|
44317
|
+
}
|
|
44256
44318
|
log("Reaping idle session:", sessionId);
|
|
44257
44319
|
live.reaped = true;
|
|
44258
44320
|
this.emitLifecycle("SessionEnd", { sessionId, reason: force ? "forced" : "idle-reap" });
|
|
@@ -44301,6 +44363,10 @@ ${lines.join("\n")}
|
|
|
44301
44363
|
let n = 0;
|
|
44302
44364
|
for (const live of [...this.liveSessions.values()]) {
|
|
44303
44365
|
if (!live.streaming && !live.reaped) {
|
|
44366
|
+
if (this.hasWorkInFlight(live)) {
|
|
44367
|
+
log(`[reap] critical reclaim spared ${live.sessionId}: work in flight`);
|
|
44368
|
+
continue;
|
|
44369
|
+
}
|
|
44304
44370
|
if (this.reapIdleSession(live.sessionId, true))
|
|
44305
44371
|
n++;
|
|
44306
44372
|
}
|
|
@@ -44325,6 +44391,7 @@ ${lines.join("\n")}
|
|
|
44325
44391
|
if (live.reaped)
|
|
44326
44392
|
continue;
|
|
44327
44393
|
live.reaped = true;
|
|
44394
|
+
live.gone = true;
|
|
44328
44395
|
if (live.idleTimer) {
|
|
44329
44396
|
clearTimeout(live.idleTimer);
|
|
44330
44397
|
live.idleTimer = void 0;
|
|
@@ -110149,7 +110216,7 @@ var NetdEngine = class {
|
|
|
110149
110216
|
p.relayed = p.relayed || relayed;
|
|
110150
110217
|
this.#pendingEndpoints.set(k, p);
|
|
110151
110218
|
}
|
|
110152
|
-
|
|
110219
|
+
#flushHint(staticPub) {
|
|
110153
110220
|
const k = staticPub.toString("hex");
|
|
110154
110221
|
const p = this.#pendingEndpoints.get(k) ?? { endpoints: [], relayed: false };
|
|
110155
110222
|
this.#pendingEndpoints.delete(k);
|
|
@@ -110158,6 +110225,12 @@ var NetdEngine = class {
|
|
|
110158
110225
|
this.#lastError = `hint: ${r.detail}`;
|
|
110159
110226
|
});
|
|
110160
110227
|
}
|
|
110228
|
+
startHandshake(staticPub, _nowMs) {
|
|
110229
|
+
this.#flushHint(staticPub);
|
|
110230
|
+
}
|
|
110231
|
+
ensureHandshake(staticPub, _nowMs) {
|
|
110232
|
+
this.#flushHint(staticPub);
|
|
110233
|
+
}
|
|
110161
110234
|
/** No UDP here — netd owns the socket. Never called in practice; kept for the interface. */
|
|
110162
110235
|
injectUdp(_pkt, _from, _nowMs) {
|
|
110163
110236
|
}
|
|
@@ -111066,7 +111139,7 @@ var MeshNode = class {
|
|
|
111066
111139
|
this.#peers.set(hex6, staticPub);
|
|
111067
111140
|
}
|
|
111068
111141
|
engine.addPath(staticPub, `${address}:${port}`, false);
|
|
111069
|
-
engine.
|
|
111142
|
+
engine.ensureHandshake(staticPub, Date.now());
|
|
111070
111143
|
this.#drain();
|
|
111071
111144
|
} catch (e) {
|
|
111072
111145
|
this.#lastError = e instanceof Error ? e.message : String(e);
|
|
@@ -111151,7 +111224,7 @@ var MeshNode = class {
|
|
|
111151
111224
|
} catch {
|
|
111152
111225
|
}
|
|
111153
111226
|
}
|
|
111154
|
-
engine.
|
|
111227
|
+
engine.ensureHandshake(pk, Date.now());
|
|
111155
111228
|
break;
|
|
111156
111229
|
}
|
|
111157
111230
|
case "mesh:gone": {
|
|
@@ -111295,7 +111368,7 @@ var MeshNode = class {
|
|
|
111295
111368
|
for (const e of p.endpoints)
|
|
111296
111369
|
engine.addPath(pk, `${p.observedIp}:${e.port}`, false);
|
|
111297
111370
|
}
|
|
111298
|
-
engine.
|
|
111371
|
+
engine.ensureHandshake(pk, Date.now());
|
|
111299
111372
|
const meshId = this.#engine?.meshId();
|
|
111300
111373
|
try {
|
|
111301
111374
|
if (meshId)
|
|
@@ -59,6 +59,9 @@ export declare class MeshEngine {
|
|
|
59
59
|
/** A relayed path always ranks below a direct one, regardless of RTT. */
|
|
60
60
|
addPath(staticPub: Buffer, endpoint: Endpoint, relayed: boolean): void;
|
|
61
61
|
startHandshake(staticPub: Buffer, nowMs: number): void;
|
|
62
|
+
/** Idempotent connect nudge: no-op over a live session; initiates only on first contact
|
|
63
|
+
* or a stale attempt. What a host calls when it (re)learns a peer. */
|
|
64
|
+
ensureHandshake(staticPub: Buffer, nowMs: number): void;
|
|
62
65
|
|
|
63
66
|
injectTun(pkt: Buffer, nowMs: number): void;
|
|
64
67
|
injectUdp(pkt: Buffer, from: Endpoint, nowMs: number): void;
|