tesseract-agent 0.9.28 → 0.9.29

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-bundle/cli.js +159 -47
  2. package/package.json +1 -1
@@ -70555,15 +70555,24 @@ async function restore(dir2, tree) {
70555
70555
  try {
70556
70556
  if (await git(dir2, ["read-tree", tree], { GIT_INDEX_FILE: indexFile }) === null)
70557
70557
  return null;
70558
- const diff = await git(dir2, ["diff-index", "--name-status", tree], { GIT_INDEX_FILE: indexFile });
70558
+ await git(dir2, ["update-index", "--refresh", "-q"], { GIT_INDEX_FILE: indexFile });
70559
+ const diff = await git(dir2, ["diff-index", "-z", "--name-status", tree], { GIT_INDEX_FILE: indexFile });
70559
70560
  const restored = [];
70560
70561
  const recreated = [];
70561
- for (const line of (diff ?? "").split("\n").filter(Boolean)) {
70562
- const [status2, ...rest] = line.split(/\s+/);
70563
- const path6 = rest.join(" ");
70564
- if (!path6)
70562
+ const fields = (diff ?? "").split("\0").filter((f) => f !== "");
70563
+ for (let i = 0; i < fields.length; ) {
70564
+ const code = fields[i++];
70565
+ if (code.startsWith("R") || code.startsWith("C")) {
70566
+ i++;
70567
+ const to = fields[i++];
70568
+ if (to !== void 0)
70569
+ restored.push(to);
70565
70570
  continue;
70566
- (status2.startsWith("D") ? recreated : restored).push(path6);
70571
+ }
70572
+ const path6 = fields[i++];
70573
+ if (path6 === void 0)
70574
+ break;
70575
+ (code.startsWith("D") ? recreated : restored).push(path6);
70567
70576
  }
70568
70577
  if (await git(dir2, ["checkout-index", "-a", "-f"], { GIT_INDEX_FILE: indexFile }) === null)
70569
70578
  return null;
@@ -70581,8 +70590,9 @@ async function preview(dir2, tree) {
70581
70590
  try {
70582
70591
  if (await git(dir2, ["read-tree", tree], { GIT_INDEX_FILE: indexFile }) === null)
70583
70592
  return null;
70584
- const diff = await git(dir2, ["diff-index", "--name-only", tree], { GIT_INDEX_FILE: indexFile });
70585
- return { changed: (diff ?? "").split("\n").filter(Boolean) };
70593
+ await git(dir2, ["update-index", "--refresh", "-q"], { GIT_INDEX_FILE: indexFile });
70594
+ const diff = await git(dir2, ["diff-index", "-z", "--name-only", tree], { GIT_INDEX_FILE: indexFile });
70595
+ return { changed: (diff ?? "").split("\0").filter((f) => f !== "") };
70586
70596
  } finally {
70587
70597
  await rm(tmp, { recursive: true, force: true }).catch(() => {
70588
70598
  });
@@ -71226,26 +71236,42 @@ async function createClaudeCodeBackend(send4, onBrowserRequest, loadPluginRuntim
71226
71236
  const sendWatchingSessions = (msg, origin) => {
71227
71237
  if (msg.type === "chat:complete" && msg.sessionId) {
71228
71238
  const sid = msg.sessionId;
71229
- const seq3 = (claudeTurnSeq.get(sid) ?? 1) - 1;
71230
- if (seq3 >= 0 && claudeSnapshots.treeFor(sid, `claude-user-${seq3}`)) {
71231
- void (async () => {
71239
+ const queue3 = pendingAlias.get(sid);
71240
+ if (queue3?.length) {
71241
+ aliasChain.set(sid, (aliasChain.get(sid) ?? Promise.resolve()).then(async () => {
71242
+ const job = queue3.shift();
71243
+ if (!job)
71244
+ return;
71232
71245
  for (let attempt = 0; attempt < 12; attempt++) {
71233
71246
  const rows = await store.getSessionMessages(sid).catch(() => []);
71234
- const lastUser = [...rows].reverse().find((m) => m.role === "user" && m.uuid)?.uuid;
71235
- if (lastUser) {
71236
- claudeSnapshots.alias(sid, `claude-user-${seq3}`, lastUser);
71247
+ const fresh = rows.find((m) => m.role === "user" && m.uuid && !job.knownUuids.has(m.uuid))?.uuid;
71248
+ if (fresh) {
71249
+ claudeSnapshots.alias(sid, job.key, fresh);
71237
71250
  return;
71238
71251
  }
71239
71252
  await new Promise((r) => setTimeout(r, 200));
71240
71253
  }
71241
- })();
71254
+ }).catch(() => {
71255
+ }).finally(() => {
71256
+ if (!pendingAlias.get(sid)?.length) {
71257
+ pendingAlias.delete(sid);
71258
+ aliasChain.delete(sid);
71259
+ }
71260
+ }));
71242
71261
  }
71243
71262
  }
71244
71263
  if (msg.type === "session:created" && msg.sessionId) {
71245
- claudeSnapshots.transfer(PENDING_SESSION, msg.sessionId);
71246
- claudeSnapshots.forget(PENDING_SESSION);
71247
- claudeTurnSeq.set(msg.sessionId, claudeTurnSeq.get(PENDING_SESSION) ?? 0);
71248
- claudeTurnSeq.delete(PENDING_SESSION);
71264
+ const from = pendingKey(msg.clientRequestId);
71265
+ claudeSnapshots.transfer(from, msg.sessionId);
71266
+ claudeSnapshots.forget(from);
71267
+ claudeTurnSeq.set(msg.sessionId, claudeTurnSeq.get(from) ?? 0);
71268
+ claudeTurnSeq.delete(from);
71269
+ const carried = pendingAlias.get(from);
71270
+ if (carried?.length)
71271
+ pendingAlias.set(msg.sessionId, [...pendingAlias.get(msg.sessionId) ?? [], ...carried]);
71272
+ pendingAlias.delete(from);
71273
+ pendingOpened.delete(from);
71274
+ sweepPending();
71249
71275
  pendingSessionId = msg.sessionId;
71250
71276
  }
71251
71277
  send4(msg, origin);
@@ -71253,7 +71279,24 @@ async function createClaudeCodeBackend(send4, onBrowserRequest, loadPluginRuntim
71253
71279
  const manager2 = new AgentManager2(sendWatchingSessions, onBrowserRequest);
71254
71280
  const claudeSnapshots = createSnapshotStore(join19(daemonDir(), "engine-snapshots", "claude-code.json"));
71255
71281
  const claudeTurnSeq = /* @__PURE__ */ new Map();
71282
+ const pendingAlias = /* @__PURE__ */ new Map();
71283
+ const aliasChain = /* @__PURE__ */ new Map();
71284
+ const ALIAS_QUEUE_CAP = 16;
71256
71285
  const PENDING_SESSION = "\0pending";
71286
+ const pendingKey = (clientRequestId) => clientRequestId ? `${PENDING_SESSION}:${clientRequestId}` : PENDING_SESSION;
71287
+ const pendingOpened = /* @__PURE__ */ new Map();
71288
+ const PENDING_TTL_MS = 5 * 6e4;
71289
+ const sweepPending = () => {
71290
+ const cutoff = Date.now() - PENDING_TTL_MS;
71291
+ for (const [key2, at] of [...pendingOpened]) {
71292
+ if (at > cutoff)
71293
+ continue;
71294
+ pendingOpened.delete(key2);
71295
+ claudeSnapshots.forget(key2);
71296
+ claudeTurnSeq.delete(key2);
71297
+ pendingAlias.delete(key2);
71298
+ }
71299
+ };
71257
71300
  let pendingSessionId;
71258
71301
  const hookBus = new HookBus2();
71259
71302
  manager2.setHookBus(hookBus);
@@ -71324,11 +71367,29 @@ async function createClaudeCodeBackend(send4, onBrowserRequest, loadPluginRuntim
71324
71367
  */
71325
71368
  sendMessage: async (params) => {
71326
71369
  if (params.projectDir && !params.autoContinuation) {
71327
- const key2 = params.sessionId ?? PENDING_SESSION;
71370
+ const key2 = params.sessionId ?? pendingKey(params.clientRequestId);
71371
+ if (!params.sessionId) {
71372
+ pendingOpened.set(key2, Date.now());
71373
+ sweepPending();
71374
+ }
71328
71375
  const seq3 = claudeTurnSeq.get(key2) ?? 0;
71329
71376
  claudeTurnSeq.set(key2, seq3 + 1);
71330
- await claudeSnapshots.take(key2, `claude-user-${seq3}`, params.projectDir).catch(() => {
71377
+ const snapKey = `claude-user-${seq3}`;
71378
+ await claudeSnapshots.take(key2, snapKey, params.projectDir).catch(() => {
71331
71379
  });
71380
+ if (params.sessionId) {
71381
+ const rows = await store.getSessionMessages(params.sessionId).catch(() => []);
71382
+ const known = new Set(rows.filter((m) => m.role === "user" && m.uuid).map((m) => m.uuid));
71383
+ const q = pendingAlias.get(params.sessionId) ?? [];
71384
+ q.push({ key: snapKey, knownUuids: known });
71385
+ while (q.length > ALIAS_QUEUE_CAP)
71386
+ q.shift();
71387
+ pendingAlias.set(params.sessionId, q);
71388
+ } else {
71389
+ const q = pendingAlias.get(key2) ?? [];
71390
+ q.push({ key: snapKey, knownUuids: /* @__PURE__ */ new Set() });
71391
+ pendingAlias.set(key2, q);
71392
+ }
71332
71393
  }
71333
71394
  const result = await manager2.sendMessage(params);
71334
71395
  return result;
@@ -72835,7 +72896,7 @@ function writeManagedMcpHome(engineId, layout, servers, projectDir, baseDir, use
72835
72896
  if (modern || !t.legacyBudget)
72836
72897
  node[t.levelKey] = level;
72837
72898
  else
72838
- node[t.legacyBudget.key] = thinkingBudget === "off" ? 0 : thinkingBudget;
72899
+ node[t.legacyBudget.key] = t.legacyBudget.clamp(model, thinkingBudget === "off" ? 0 : thinkingBudget);
72839
72900
  let parent = settings;
72840
72901
  for (const k of t.overridesPath.slice(0, -1)) {
72841
72902
  const next = parent[k];
@@ -73738,6 +73799,16 @@ var GEMINI_ACP = {
73738
73799
  if (!model.startsWith("gemini-"))
73739
73800
  return true;
73740
73801
  return /^gemini-(?:[3-9]|\d{2,})/.test(model);
73802
+ },
73803
+ clamp: (model, budget) => {
73804
+ const lite = /flash-lite/.test(model ?? "");
73805
+ const pro = /-pro\b/.test(model ?? "");
73806
+ const zeroOk = !pro;
73807
+ const min = pro ? 128 : lite ? 512 : /flash/.test(model ?? "") ? 0 : 512;
73808
+ const max = pro ? 32768 : 24576;
73809
+ if (budget <= 0)
73810
+ return zeroOk ? 0 : min;
73811
+ return Math.min(max, Math.max(min, Math.round(budget)));
73741
73812
  }
73742
73813
  },
73743
73814
  // Non-empty, because an empty match is DISCARDED. `overrideScope: 'core'` matches when
@@ -75727,15 +75798,15 @@ function compactionVerdict(method, p, state3) {
75727
75798
  if (method === "turn/started" && state3.turnId === void 0 && !state3.userTurnOnThread) {
75728
75799
  return tn.id ? { verdict: "claim", turnId: tn.id } : { verdict: "ignore" };
75729
75800
  }
75730
- const id = method === "error" ? String(p.turnId ?? "") : tn.id ?? "";
75731
- if (state3.turnId === void 0 || id !== state3.turnId)
75801
+ const id = String(tn.id ?? p.turnId ?? "");
75802
+ if (state3.turnId === void 0 || !id || id !== state3.turnId)
75732
75803
  return { verdict: "ignore" };
75733
75804
  if (method === "turn/completed")
75734
75805
  return { verdict: tn.status === "completed" ? "ok" : "failed" };
75735
75806
  if (method === "error") {
75736
- return p.willRetry === true ? { verdict: "ignore" } : { verdict: "failed" };
75807
+ return p.willRetry === true ? { verdict: "alive" } : { verdict: "failed" };
75737
75808
  }
75738
- return { verdict: "ignore" };
75809
+ return { verdict: "alive" };
75739
75810
  }
75740
75811
  function approvalPolicyFor(mode) {
75741
75812
  switch (mode) {
@@ -75951,7 +76022,8 @@ function createCodexAppServerBackend(send4, resolve16, onBrowserRequest, loadPlu
75951
76022
  const verifyOff = /* @__PURE__ */ new Set();
75952
76023
  const lastTurnDiff = /* @__PURE__ */ new Map();
75953
76024
  const compactWaiters = /* @__PURE__ */ new Map();
75954
- const COMPACT_TIMEOUT_MS = 18e4;
76025
+ const COMPACT_IDLE_MS = 18e4;
76026
+ const COMPACT_CEILING_MS = 9e5;
75955
76027
  const tokenUsage = /* @__PURE__ */ new Map();
75956
76028
  let lastWindow;
75957
76029
  const snapshots = createSnapshotStore(join26(daemonDir2(), "engine-snapshots", "openai-codex.json"));
@@ -76121,6 +76193,7 @@ function createCodexAppServerBackend(send4, resolve16, onBrowserRequest, loadPlu
76121
76193
  });
76122
76194
  if (verdict === "claim") {
76123
76195
  pendingCompact.turnId = turnId;
76196
+ pendingCompact.keepAlive();
76124
76197
  return;
76125
76198
  }
76126
76199
  if (verdict === "ok") {
@@ -76131,6 +76204,10 @@ function createCodexAppServerBackend(send4, resolve16, onBrowserRequest, loadPlu
76131
76204
  pendingCompact.settle(false);
76132
76205
  return;
76133
76206
  }
76207
+ if (verdict === "alive") {
76208
+ pendingCompact.keepAlive();
76209
+ return;
76210
+ }
76134
76211
  }
76135
76212
  const turn = threadId ? turns.get(threadId) : void 0;
76136
76213
  if (!turn)
@@ -76485,13 +76562,20 @@ function createCodexAppServerBackend(send4, resolve16, onBrowserRequest, loadPlu
76485
76562
  return;
76486
76563
  }
76487
76564
  const compacted = await new Promise((resolve17) => {
76565
+ let idle;
76488
76566
  const finish = (ok2) => {
76489
76567
  compactWaiters.delete(threadId);
76490
- clearTimeout(timer2);
76568
+ clearTimeout(idle);
76569
+ clearTimeout(ceiling);
76491
76570
  resolve17(ok2);
76492
76571
  };
76493
- const timer2 = setTimeout(() => finish(false), COMPACT_TIMEOUT_MS);
76494
- compactWaiters.set(threadId, { settle: finish });
76572
+ const arm = () => {
76573
+ clearTimeout(idle);
76574
+ idle = setTimeout(() => finish(false), COMPACT_IDLE_MS);
76575
+ };
76576
+ const ceiling = setTimeout(() => finish(false), COMPACT_CEILING_MS);
76577
+ arm();
76578
+ compactWaiters.set(threadId, { settle: finish, keepAlive: arm });
76495
76579
  });
76496
76580
  if (!compacted) {
76497
76581
  fail3(threadId, params.clientRequestId, `${label} did not finish compacting this conversation in time.`, origin);
@@ -100757,8 +100841,21 @@ async function runDaemon(opts = {}) {
100757
100841
  process.exit(code);
100758
100842
  }
100759
100843
  startSystemEvents({ version: VERSION2, log: (m) => log9(m) });
100760
- process.on("SIGTERM", () => shutdown(0, "signal:SIGTERM"));
100761
- process.on("SIGINT", () => shutdown(0, "signal:SIGINT"));
100844
+ const mergedProcess = opts.merged ?? (process.env.CCB_MERGED_PROCESS === "1" || process.argv.includes("--merged"));
100845
+ const deliberateMergedQuit = () => !!globalThis.__ccbDeliberateQuit;
100846
+ const onTermSignal = (sig) => {
100847
+ if (mergedProcess && !deliberateMergedQuit()) {
100848
+ try {
100849
+ log9(`${sig} received \u2014 IGNORED (merged: always-on app; a rival daemon's takeover must never kill the window)`);
100850
+ snapshotProcs(`${sig}-ignored`);
100851
+ } catch {
100852
+ }
100853
+ return;
100854
+ }
100855
+ shutdown(0, `signal:${sig}`);
100856
+ };
100857
+ process.on("SIGTERM", () => onTermSignal("SIGTERM"));
100858
+ process.on("SIGINT", () => onTermSignal("SIGINT"));
100762
100859
  try {
100763
100860
  process.on("SIGHUP", () => {
100764
100861
  try {
@@ -100795,8 +100892,6 @@ async function runDaemon(opts = {}) {
100795
100892
  try {
100796
100893
  const eapp = electronModule()?.app;
100797
100894
  if (eapp) {
100798
- const mergedProcess = opts.merged ?? (process.env.CCB_MERGED_PROCESS === "1" || process.argv.includes("--merged"));
100799
- const deliberateMergedQuit = () => !!globalThis.__ccbDeliberateQuit;
100800
100895
  eapp.on("before-quit", (e) => {
100801
100896
  if (mergedProcess) {
100802
100897
  if (deliberateMergedQuit()) {
@@ -101696,6 +101791,10 @@ async function listMeshes() {
101696
101791
  const r = await call("/meshes", { method: "GET" });
101697
101792
  return r.ok ? { ok: true, meshes: r.data } : r;
101698
101793
  }
101794
+ async function deleteMesh(args) {
101795
+ const r = await call(`/meshes/${encodeURIComponent(args.meshId)}?confirm=${encodeURIComponent(args.confirm)}`, { method: "DELETE" });
101796
+ return r.ok ? { ok: true, deleted: r.data.deleted } : r;
101797
+ }
101699
101798
  async function createMesh(args = {}) {
101700
101799
  const r = await call("/meshes", {
101701
101800
  method: "POST",
@@ -101739,7 +101838,7 @@ async function enrolWith(requested, post) {
101739
101838
  const sig = await generateSigningKey();
101740
101839
  const staticPrivate = await rawPrivate(stat9.privateKey);
101741
101840
  const sigPrivate = await rawPrivate(sig.privateKey);
101742
- const r = await post(hex4(stat9.publicRaw), hex4(sig.publicRaw));
101841
+ const r = await post(hex4(stat9.publicRaw), hex4(sig.publicRaw), name);
101743
101842
  if (!r.ok)
101744
101843
  return r;
101745
101844
  const cert = fromHexOrB64(r.data.cert);
@@ -101770,13 +101869,13 @@ async function enrolWith(requested, post) {
101770
101869
  };
101771
101870
  }
101772
101871
  async function enrol(args) {
101773
- return enrolWith(args.name, (staticPub, sigPub) => call(`/meshes/${encodeURIComponent(args.meshId)}/nodes`, {
101872
+ return enrolWith(args.name, (staticPub, sigPub, name) => call(`/meshes/${encodeURIComponent(args.meshId)}/nodes`, {
101774
101873
  method: "POST",
101775
- body: { name: args.name, staticPub, sigPub }
101874
+ body: { name, staticPub, sigPub }
101776
101875
  }));
101777
101876
  }
101778
101877
  async function joinWithToken(args) {
101779
- return enrolWith(args.name, async (staticPub, sigPub) => postJoin(args.origin, { token: args.token, name: args.name, staticPub, sigPub }));
101878
+ return enrolWith(args.name, async (staticPub, sigPub, name) => postJoin(args.origin, { token: args.token, name, staticPub, sigPub }));
101780
101879
  }
101781
101880
  async function postJoin(origin, body) {
101782
101881
  const base = origin ?? readCredentials()?.apiOrigin;
@@ -101807,7 +101906,7 @@ async function postJoin(origin, body) {
101807
101906
  return { ok: true, data: JSON.parse(text9) };
101808
101907
  }
101809
101908
  async function enrolWithCode(args) {
101810
- return enrolWith(args.name, async (staticPub, sigPub) => postJoin(args.origin, { code: args.code, name: args.name, staticPub, sigPub }));
101909
+ return enrolWith(args.name, async (staticPub, sigPub, name) => postJoin(args.origin, { code: args.code, name, staticPub, sigPub }));
101811
101910
  }
101812
101911
  async function rawPrivate(key2) {
101813
101912
  const pkcs8 = new Uint8Array(await crypto.subtle.exportKey("pkcs8", key2));
@@ -102611,6 +102710,8 @@ Running a network (needs a signed-in account):
102611
102710
  tesseract-agent net token MESH [--rotate] Show the join token again, or replace it
102612
102711
  tesseract-agent net approve MESH NODE Let a waiting machine on
102613
102712
  tesseract-agent net remove MESH NODE Cut a machine off for good
102713
+ tesseract-agent net destroy MESH --confirm NAME
102714
+ Delete a network for good. Irreversible.
102614
102715
  tesseract-agent net code new MESH [--uses N] [--ttl MIN] [--json]
102615
102716
  A ONE-SHOT code, for anyone who wants one
102616
102717
  tesseract-agent net join-me MESH [--name N]
@@ -102805,10 +102906,6 @@ async function netCommand(rest, flags) {
102805
102906
  if (r.name !== name) {
102806
102907
  console.log(`(another machine on this network is already called "${name}")`);
102807
102908
  }
102808
- console.log(`
102809
- Six-word check \u2014 these must match on your admin's screen:`);
102810
- console.log(` ${r.sas.join(" ")}
102811
- `);
102812
102909
  if (!r.approved) {
102813
102910
  console.log("This network requires an admin to approve new machines.");
102814
102911
  console.log("You are enrolled and waiting \u2014 nothing else to do on this machine.");
@@ -102858,6 +102955,25 @@ Six-word check \u2014 these must match on your admin's screen:`);
102858
102955
  }
102859
102956
  return;
102860
102957
  }
102958
+ if (sub === "destroy" || sub === "mesh" && rest[1] === "delete") {
102959
+ const meshId = sub === "destroy" ? rest[1] : rest[2];
102960
+ const confirm2 = str2(flags.confirm);
102961
+ if (!meshId) {
102962
+ console.error("net destroy: name the network \u2014 tesseract-agent net destroy <ID> --confirm <NAME>");
102963
+ process.exit(1);
102964
+ }
102965
+ if (!confirm2) {
102966
+ console.error("net destroy: pass --confirm with the network's exact name. This cannot be undone.");
102967
+ process.exit(1);
102968
+ }
102969
+ const r = await deleteMesh({ meshId, confirm: confirm2 });
102970
+ if (!r.ok) {
102971
+ console.error(`net destroy: ${r.detail}`);
102972
+ process.exit(1);
102973
+ }
102974
+ console.log(`Deleted "${r.deleted}". Every machine that was on it now holds a certificate for a network that no longer exists.`);
102975
+ return;
102976
+ }
102861
102977
  if (sub === "new" || sub === "mesh" && rest[1] === "create") {
102862
102978
  const name = sub === "new" ? rest[1] : str2(flags.name);
102863
102979
  const approve = str2(flags.approve);
@@ -102993,10 +103109,6 @@ Six-word check \u2014 these must match on your admin's screen:`);
102993
103109
  }
102994
103110
  console.log(`Joined ${r.meshId} as "${r.name}" \u2014 this machine is ${r.overlayIp4}.`);
102995
103111
  if (r.name !== nm) console.log(`(another machine here is already called "${nm}")`);
102996
- console.log(`
102997
- Six-word check:
102998
- ${r.sas.join(" ")}
102999
- `);
103000
103112
  return;
103001
103113
  }
103002
103114
  if (sub === "peers") {
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "tesseract-agent",
3
- "version": "0.9.28",
3
+ "version": "0.9.29",
4
4
  "license": "UNLICENSED",
5
5
  "publishConfig": {
6
6
  "access": "public",