openmausbot 0.1.91 → 0.1.93
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/assets/DesktopViewer-CKNR3SEW.js +5 -0
- package/dist/assets/{abnfDiagram-O67JEVCF-yP-w8pRq.js → abnfDiagram-O67JEVCF-Ckrm1I1X.js} +1 -1
- package/dist/assets/{arc-DsivfZEh.js → arc-BBdBuCgB.js} +1 -1
- package/dist/assets/{architectureDiagram-NJMV4G6O-CILw7lzT.js → architectureDiagram-NJMV4G6O-CDdA3Hjs.js} +1 -1
- package/dist/assets/{blockDiagram-BEXU5L5S-DEQUV3HF.js → blockDiagram-BEXU5L5S-AUrsx1YR.js} +1 -1
- package/dist/assets/{c4Diagram-YGBWAQC7-lSXCZoKA.js → c4Diagram-YGBWAQC7-BEqQ0Unh.js} +1 -1
- package/dist/assets/channel-BU94VpxE.js +1 -0
- package/dist/assets/{chunk-2Q5K7J3B-QCD5bgWP.js → chunk-2Q5K7J3B-CEEuvd4C.js} +1 -1
- package/dist/assets/{chunk-3FUC2YCW-C5-iZJuL.js → chunk-3FUC2YCW-DyANCiHl.js} +2 -2
- package/dist/assets/{chunk-5VM5RSS4-cxsawMpY.js → chunk-5VM5RSS4-Dx_Slpw9.js} +1 -1
- package/dist/assets/{chunk-GTNCS2PH-DV8tV8H7.js → chunk-GTNCS2PH-Dl7h7iiD.js} +1 -1
- package/dist/assets/{chunk-GWA4HPMP-CJFZyfbW.js → chunk-GWA4HPMP-BLx07SPb.js} +1 -1
- package/dist/assets/{chunk-JWPE2WC7-BigbMXco.js → chunk-JWPE2WC7-BMxDj3n7.js} +1 -1
- package/dist/assets/{chunk-NETBCI7D-oVxTfpDy.js → chunk-NETBCI7D-CN66PT4H.js} +1 -1
- package/dist/assets/{chunk-WEXAMYUT-CH4BiroW.js → chunk-WEXAMYUT-6HwhsDKw.js} +1 -1
- package/dist/assets/{chunk-XXDRQBXY-KEDa6gax.js → chunk-XXDRQBXY-gWnLt7SN.js} +1 -1
- package/dist/assets/{classDiagram-v2-NBCMYWYE-BiPjfqVv.js → classDiagram-v2-NBCMYWYE-CMS5l2ea.js} +1 -1
- package/dist/assets/{cose-bilkent-JH36ORCC-Caxew3OY.js → cose-bilkent-JH36ORCC-CV7sQwIG.js} +1 -1
- package/dist/assets/{cynefin-OW5HDTMX-B_Pui369.js → cynefin-OW5HDTMX-CSyl-7vl.js} +1 -1
- package/dist/assets/{cynefinDiagram-VND7K2PF-X64ydIhP.js → cynefinDiagram-VND7K2PF-BCLKL6az.js} +1 -1
- package/dist/assets/{dagre-6A5THRUB-TqYCI1E1.js → dagre-6A5THRUB-DiaZs6S1.js} +1 -1
- package/dist/assets/{diagram-22UHCM2B-TeOt3rAQ.js → diagram-22UHCM2B-BeTtmt3I.js} +1 -1
- package/dist/assets/{diagram-3UASUU5V-D70BLhDA.js → diagram-3UASUU5V-CsdBG-QQ.js} +1 -1
- package/dist/assets/{diagram-ATOU4E4O-BbQDLwUC.js → diagram-ATOU4E4O-BczvdbR1.js} +1 -1
- package/dist/assets/{diagram-CDSNMT55-ChNJZ1u-.js → diagram-CDSNMT55-DHTTTMhi.js} +1 -1
- package/dist/assets/{diagram-MLGK6HIB-DMBl2APa.js → diagram-MLGK6HIB-RTjHyd8z.js} +1 -1
- package/dist/assets/{diagram-MPIPVDR6-CEqDchOp.js → diagram-MPIPVDR6-BDenHEH_.js} +1 -1
- package/dist/assets/{ebnfDiagram-ZINNZB2B-BVDKRp_-.js → ebnfDiagram-ZINNZB2B-BSiRSbtL.js} +1 -1
- package/dist/assets/{elk-276RUBZZ-CRkofO9B.js → elk-276RUBZZ-BCAElss-.js} +1 -1
- package/dist/assets/{erDiagram-OPXOYQCR-DruOMh5e.js → erDiagram-OPXOYQCR-C4_1ZPJe.js} +1 -1
- package/dist/assets/{flowDiagram-KWPJA3E3-8GMQeB7t.js → flowDiagram-KWPJA3E3-Bcb4Z75S.js} +1 -1
- package/dist/assets/{ganttDiagram-FUAMR5RP-ORE9jcsM.js → ganttDiagram-FUAMR5RP-BiPOzxzb.js} +1 -1
- package/dist/assets/{gitGraphDiagram-X574FWY7-BmsedsWh.js → gitGraphDiagram-X574FWY7-B1wtF3ZS.js} +1 -1
- package/dist/assets/index-Biji-bfL.css +1 -0
- package/dist/assets/{index-DKNEM8Q6.js → index-BkYwilkG.js} +1 -1
- package/dist/assets/index-DZlhoES0.js +597 -0
- package/dist/assets/{infoDiagram-VRGFBTTK-E_p7AooJ.js → infoDiagram-VRGFBTTK-A19No71U.js} +1 -1
- package/dist/assets/{ishikawaDiagram-OU5B5YK6-Y-B2mKda.js → ishikawaDiagram-OU5B5YK6-BQsqJ_cV.js} +1 -1
- package/dist/assets/{journeyDiagram-ZHPQQLJL-eA8RELcK.js → journeyDiagram-ZHPQQLJL-DAO0pVan.js} +1 -1
- package/dist/assets/{kanban-definition-PNTS6WVX-DqIL2e8e.js → kanban-definition-PNTS6WVX-NepmKEe2.js} +1 -1
- package/dist/assets/{linear-yC_29R6e.js → linear-bq1PTDTW.js} +1 -1
- package/dist/assets/{mermaid.core-VhEMkSND.js → mermaid.core-s4LTbQle.js} +5 -5
- package/dist/assets/{mindmap-definition-NLK3R4M7-oJ7kyQZy.js → mindmap-definition-NLK3R4M7-LtdbgHFH.js} +1 -1
- package/dist/assets/{pegDiagram-GJSIUBJH-BmfYcY0O.js → pegDiagram-GJSIUBJH-tdbaCxiK.js} +1 -1
- package/dist/assets/{pieDiagram-5QR66LMP-DKvgP7w9.js → pieDiagram-5QR66LMP-DvkbpW8l.js} +1 -1
- package/dist/assets/{quadrantDiagram-O4NWA36T-BEKL3W0L.js → quadrantDiagram-O4NWA36T-Do4KaH-E.js} +1 -1
- package/dist/assets/{railroadDiagram-XR7U4H2S-BEsiqR5s.js → railroadDiagram-XR7U4H2S-BQWULOAs.js} +1 -1
- package/dist/assets/{requirementDiagram-PLB6GJNP-DWBcWvAP.js → requirementDiagram-PLB6GJNP-w9kei1FU.js} +1 -1
- package/dist/assets/{sankeyDiagram-IPEJSGJF-rpZ2_O4s.js → sankeyDiagram-IPEJSGJF-D2gRy0e8.js} +1 -1
- package/dist/assets/{sequenceDiagram-PO4LG4MO-B99HQdJR.js → sequenceDiagram-PO4LG4MO-a2sO5_Eo.js} +1 -1
- package/dist/assets/{sizeCapture-INFHLROL-BjN0qT5T.js → sizeCapture-INFHLROL-CiPJmFEB.js} +1 -1
- package/dist/assets/{stateDiagram-v2-GCMORJYK-BbsVgGsC.js → stateDiagram-v2-GCMORJYK-DAkUUlL7.js} +1 -1
- package/dist/assets/{swimlanes-2SLR337P-DG3gKkeR.js → swimlanes-2SLR337P-DANf2R8Y.js} +1 -1
- package/dist/assets/swimlanesDiagram-TC7HE7FX-CdHxG8aM.js +8 -0
- package/dist/assets/{timeline-definition-EJHVYXUP-BTHOdtF_.js → timeline-definition-EJHVYXUP-BKpQmzrT.js} +1 -1
- package/dist/assets/{usecaseDiagram-POWQR4AR-acLSLFwc.js → usecaseDiagram-POWQR4AR-BtI7qLN-.js} +1 -1
- package/dist/assets/{vennDiagram-UO4OBE2U-C5JzNX9G.js → vennDiagram-UO4OBE2U-gXJXrAG_.js} +1 -1
- package/dist/assets/{wardleyDiagram-VNRHLVJA-DL1-PrR_.js → wardleyDiagram-VNRHLVJA-GU8Po5-n.js} +1 -1
- package/dist/assets/{xychartDiagram-PMCCYNJV-Bvr1477p.js → xychartDiagram-PMCCYNJV-BF4J30g6.js} +1 -1
- package/dist/index.html +2 -2
- package/dist/novnc-NOTICE.txt +118 -0
- package/dist-server/cloud-home-start.js +112 -14
- package/dist-server/companion/src/routes.js +24 -1
- package/dist-server/container-mcp.js +201 -16
- package/dist-server/drivers/agents-proxy.js +7 -4
- package/dist-server/enterprise/server/index.js +5 -1
- package/dist-server/index.js +10698 -4737
- package/dist-server/local-computer-proxy.js +133 -8
- package/dist-server/mcp-gate.js +16 -6
- package/dist-server/model-catalog/models-dev.snapshot.json +7299 -0
- package/dist-server/openmausbot.js +4656 -1258
- package/dist-server/pair-cli.js +4656 -1258
- package/dist-server/server/boat.js +5 -5
- package/dist-server/server/browser-engine.js +10 -1
- package/dist-server/server/browser-live.js +46 -2
- package/dist-server/server/browser-runtime.js +74 -8
- package/dist-server/server/browser-tool-shape.js +6 -4
- package/dist-server/server/chief-of-staff.js +1 -1
- package/dist-server/server/cloud-home-start.js +118 -26
- package/dist-server/server/cloud-home.js +39 -3
- package/dist-server/server/cloud-lending.js +74 -10
- package/dist-server/server/cloud-owner.js +196 -0
- package/dist-server/server/cloud-secrets-boot.js +6 -0
- package/dist-server/server/cloud-secrets.js +65 -0
- package/dist-server/server/computer-control.js +6 -0
- package/dist-server/server/config.js +187 -14
- package/dist-server/server/container-computer.js +46 -34
- package/dist-server/server/desktop-viewer-targets.js +26 -0
- package/dist-server/server/digest.js +12 -2
- package/dist-server/server/drivers/acp/core.js +339 -33
- package/dist-server/server/drivers/acp/cursor.js +13 -1
- package/dist-server/server/drivers/acp/opencode-go.js +614 -85
- package/dist-server/server/drivers/acp/quiet-status.js +257 -0
- package/dist-server/server/drivers/acp/qwen.js +18 -0
- package/dist-server/server/drivers/agents-catalog.js +6 -6
- package/dist-server/server/drivers/antigravity-acp.js +69 -11
- package/dist-server/server/drivers/antigravity-temp.js +360 -0
- package/dist-server/server/drivers/antigravity.js +21 -0
- package/dist-server/server/drivers/builtIn.js +2 -0
- package/dist-server/server/drivers/cerebras.js +108 -0
- package/dist-server/server/drivers/chat-mcp-tools.js +20 -3
- package/dist-server/server/drivers/chatgpt-plan-auth.js +481 -0
- package/dist-server/server/drivers/claude.js +329 -37
- package/dist-server/server/drivers/codex.js +228 -18
- package/dist-server/server/drivers/grok.js +2 -2
- package/dist-server/server/drivers/mistral.js +4 -3
- package/dist-server/server/drivers/openai-chat-protocol.js +4 -3
- package/dist-server/server/drivers/openai-chat.js +66 -4
- package/dist-server/server/drivers/openai-compat.js +73 -23
- package/dist-server/server/drivers/prompt-split.js +2 -0
- package/dist-server/server/engine-install.js +46 -12
- package/dist-server/server/env-path.js +21 -5
- package/dist-server/server/harness/registry.js +2 -2
- package/dist-server/server/index.js +1545 -238
- package/dist-server/server/lending-memory.js +479 -0
- package/dist-server/server/local-vm-idle.js +24 -6
- package/dist-server/server/local-vm-stop-reason.js +26 -0
- package/dist-server/server/managed-desktop.js +3 -0
- package/dist-server/server/mcp-bridge.js +5 -3
- package/dist-server/server/mcp-http.js +6 -3
- package/dist-server/server/mcp-oauth-discovery.js +133 -0
- package/dist-server/server/mcp-oauth-store.js +70 -0
- package/dist-server/server/mcp-oauth.js +550 -0
- package/dist-server/server/mcp-probe.js +14 -1
- package/dist-server/server/mcp-registry.js +43 -0
- package/dist-server/server/memory-upkeep.js +94 -84
- package/dist-server/server/model-catalog/catalog.js +374 -0
- package/dist-server/server/model-catalog/presets.js +176 -0
- package/dist-server/server/model-catalog/trim.js +219 -0
- package/dist-server/server/provider-key-check.js +5 -2
- package/dist-server/server/recent-work.js +1 -1
- package/dist-server/server/request-auth.js +23 -0
- package/dist-server/server/routes/antigravity-leftovers.js +45 -0
- package/dist-server/server/routes/bot-memory.js +31 -6
- package/dist-server/server/routes/desktop-viewer.js +238 -0
- package/dist-server/server/routine-requests.js +1 -1
- package/dist-server/server/routines.js +6 -3
- package/dist-server/server/sessions.js +56 -9
- package/dist-server/server/skills.js +25 -10
- package/dist-server/server/store.js +116 -122
- package/dist-server/server/thread-events.js +2 -0
- package/dist-server/server/thread-starters.js +33 -0
- package/dist-server/server/thread-title.js +46 -0
- package/dist-server/server/tts/fish.js +4 -5
- package/dist-server/server/tts/index.js +5 -3
- package/dist-server/server/vps-computer.js +80 -13
- package/dist-server/server/webhooks.js +18 -1
- package/dist-server/server/workspace-backup-policy.js +3 -0
- package/dist-server/server/workspace-backup.js +25 -0
- package/dist-server/server/workspace.js +66 -16
- package/dist-server/shared/approval-mode.js +1 -1
- package/dist-server/shared/redact.js +1 -0
- package/dist-server/shared/tool-name.js +22 -0
- package/dist-server/shared/workspace-backup-client.js +1 -0
- package/dist-server/src/lib/citations.js +177 -0
- package/dist-server/src/lib/composer-attachments.js +7 -1
- package/dist-server/vps-container-mcp.js +202 -16
- package/dist-server/workspace-backup.worker.js +87 -9
- package/enterprise/server/index.js +5 -1
- package/package.json +1 -1
- package/dist/assets/channel-DrfGMu7X.js +0 -1
- package/dist/assets/index-BlnCEcU6.js +0 -588
- package/dist/assets/index-DVxjOvID.css +0 -1
- package/dist/assets/swimlanesDiagram-TC7HE7FX-BCZhxl7e.js +0 -8
|
@@ -0,0 +1,257 @@
|
|
|
1
|
+
// What an ACP agent is doing while it sends nothing.
|
|
2
|
+
//
|
|
3
|
+
// ACP has no "still working" message, and agents go quiet for minutes in
|
|
4
|
+
// normal work: Qwen Code 0.24 says nothing over the wire while it waits out
|
|
5
|
+
// a rate limit, while its SDK retries a request, or while one slow model
|
|
6
|
+
// call runs. Silence alone cannot tell that from a wedged process, so a
|
|
7
|
+
// quiet turn is probed from outside: the agent's own debug log where it
|
|
8
|
+
// keeps one, and the process tree's open connections and CPU time. The
|
|
9
|
+
// result becomes a plain-words notice in the chat, so the person is never
|
|
10
|
+
// left watching a spinner without knowing why.
|
|
11
|
+
import { execFile } from "node:child_process";
|
|
12
|
+
import { closeSync, fstatSync, openSync, readSync } from "node:fs";
|
|
13
|
+
/** Qwen Code's debug log (QWEN_DEBUG_LOG_FILE=1), lines like
|
|
14
|
+
* `2026-10-02T02:50:41.757Z [WARN] [RETRY] Attempt 1 failed with status 429. Retrying after explicit delay of 20000ms...`.
|
|
15
|
+
* Only the facts worth telling a person are parsed; every other line still
|
|
16
|
+
* counts as proof the process is alive. */
|
|
17
|
+
export function parseQwenLogLine(line) {
|
|
18
|
+
if (line.includes("[RETRY]") && /\bAttempt \d+ failed\b/.test(line)) {
|
|
19
|
+
const attempt = /\bAttempt (\d+) failed/.exec(line);
|
|
20
|
+
const status = /\bwith status (\d{3})\b/.exec(line);
|
|
21
|
+
const explicit = /explicit delay of (\d+)\s*ms/.exec(line);
|
|
22
|
+
const seconds = /Retrying in (\d+)\s*s\b/.exec(line);
|
|
23
|
+
return {
|
|
24
|
+
kind: "retry",
|
|
25
|
+
attempt: attempt ? Number(attempt[1]) : null,
|
|
26
|
+
status: status ? Number(status[1]) : null,
|
|
27
|
+
delayMs: explicit ? Number(explicit[1]) : seconds ? Number(seconds[1]) * 1000 : null,
|
|
28
|
+
};
|
|
29
|
+
}
|
|
30
|
+
if (/\[compaction\] hard-tier rescue triggered|Reactive compression|tryCompress/.test(line)
|
|
31
|
+
&& !/succeeded|failed|skipped|stopped/.test(line)) {
|
|
32
|
+
return { kind: "compressing" };
|
|
33
|
+
}
|
|
34
|
+
const gate = /\[compaction\] cheap-gate NOOP: effectiveTokens=(\d+), auto=(\d+)/.exec(line);
|
|
35
|
+
if (gate)
|
|
36
|
+
return { kind: "context", tokens: Number(gate[1]), threshold: Number(gate[2]) };
|
|
37
|
+
return null;
|
|
38
|
+
}
|
|
39
|
+
/** Reads the lines appended to a log file since the last read. A file that
|
|
40
|
+
* does not exist yet is simply empty; the first read skips what was already
|
|
41
|
+
* there, so an old conversation's log is not replayed as news. */
|
|
42
|
+
export class LogTail {
|
|
43
|
+
offset = null;
|
|
44
|
+
partial = "";
|
|
45
|
+
path;
|
|
46
|
+
maxBytes;
|
|
47
|
+
constructor(path, maxBytes = 256 * 1024) {
|
|
48
|
+
this.path = path;
|
|
49
|
+
this.maxBytes = maxBytes;
|
|
50
|
+
}
|
|
51
|
+
read() {
|
|
52
|
+
let fd;
|
|
53
|
+
try {
|
|
54
|
+
fd = openSync(this.path, "r");
|
|
55
|
+
}
|
|
56
|
+
catch {
|
|
57
|
+
if (this.offset === null)
|
|
58
|
+
this.offset = 0;
|
|
59
|
+
return [];
|
|
60
|
+
}
|
|
61
|
+
try {
|
|
62
|
+
const size = fstatSync(fd).size;
|
|
63
|
+
if (this.offset === null) {
|
|
64
|
+
this.offset = size;
|
|
65
|
+
return [];
|
|
66
|
+
}
|
|
67
|
+
// truncated or replaced: start over from its beginning
|
|
68
|
+
if (size < this.offset) {
|
|
69
|
+
this.offset = 0;
|
|
70
|
+
this.partial = "";
|
|
71
|
+
}
|
|
72
|
+
if (size === this.offset)
|
|
73
|
+
return [];
|
|
74
|
+
// a burst bigger than the cap keeps only its tail
|
|
75
|
+
const start = Math.max(this.offset, size - this.maxBytes);
|
|
76
|
+
if (start > this.offset)
|
|
77
|
+
this.partial = "";
|
|
78
|
+
const buf = Buffer.alloc(size - start);
|
|
79
|
+
readSync(fd, buf, 0, buf.length, start);
|
|
80
|
+
this.offset = size;
|
|
81
|
+
const text = this.partial + buf.toString("utf8");
|
|
82
|
+
const lines = text.split(/\r?\n/);
|
|
83
|
+
this.partial = lines.pop() ?? "";
|
|
84
|
+
return lines.filter((l) => l.trim());
|
|
85
|
+
}
|
|
86
|
+
catch {
|
|
87
|
+
return [];
|
|
88
|
+
}
|
|
89
|
+
finally {
|
|
90
|
+
try {
|
|
91
|
+
closeSync(fd);
|
|
92
|
+
}
|
|
93
|
+
catch { }
|
|
94
|
+
}
|
|
95
|
+
}
|
|
96
|
+
}
|
|
97
|
+
const run = (file, args, timeoutMs = 4_000) => new Promise((resolve) => {
|
|
98
|
+
execFile(file, args, { timeout: timeoutMs, maxBuffer: 4 * 1024 * 1024, windowsHide: true }, (err, stdout) => {
|
|
99
|
+
// lsof exits 1 when it finds nothing to list; its stdout is still the answer
|
|
100
|
+
if (err && !(typeof stdout === "string" && err.code === 1))
|
|
101
|
+
resolve(null);
|
|
102
|
+
else
|
|
103
|
+
resolve(String(stdout));
|
|
104
|
+
});
|
|
105
|
+
});
|
|
106
|
+
/** `ps` TIME: [[dd-]hh:]mm:ss[.cc] */
|
|
107
|
+
function psTimeMs(raw) {
|
|
108
|
+
const m = /^(?:(\d+)-)?(?:(\d+):)?(\d+):(\d+(?:\.\d+)?)$/.exec(raw.trim());
|
|
109
|
+
if (!m)
|
|
110
|
+
return null;
|
|
111
|
+
const [, d, h, min, s] = m;
|
|
112
|
+
return ((Number(d ?? 0) * 24 + Number(h ?? 0)) * 60 + Number(min)) * 60_000 + Number(s) * 1000;
|
|
113
|
+
}
|
|
114
|
+
/** The root and every descendant, from a `pid ppid ...` table. */
|
|
115
|
+
export function treePids(rows, root) {
|
|
116
|
+
const kids = new Map();
|
|
117
|
+
for (const r of rows) {
|
|
118
|
+
const list = kids.get(r.ppid);
|
|
119
|
+
if (list)
|
|
120
|
+
list.push(r.pid);
|
|
121
|
+
else
|
|
122
|
+
kids.set(r.ppid, [r.pid]);
|
|
123
|
+
}
|
|
124
|
+
const out = [];
|
|
125
|
+
const queue = [root];
|
|
126
|
+
while (queue.length && out.length < 512) {
|
|
127
|
+
const pid = queue.shift();
|
|
128
|
+
if (out.includes(pid))
|
|
129
|
+
continue;
|
|
130
|
+
out.push(pid);
|
|
131
|
+
queue.push(...(kids.get(pid) ?? []));
|
|
132
|
+
}
|
|
133
|
+
return out;
|
|
134
|
+
}
|
|
135
|
+
async function samplePosix(root) {
|
|
136
|
+
const table = await run("ps", ["-A", "-o", "pid=,ppid=,time="]);
|
|
137
|
+
if (table === null)
|
|
138
|
+
return { cpuMs: null, connections: null };
|
|
139
|
+
const rows = [];
|
|
140
|
+
for (const line of table.split("\n")) {
|
|
141
|
+
const [pid, ppid, time] = line.trim().split(/\s+/);
|
|
142
|
+
if (!pid || !ppid || !time)
|
|
143
|
+
continue;
|
|
144
|
+
rows.push({ pid: Number(pid), ppid: Number(ppid), cpuMs: psTimeMs(time) });
|
|
145
|
+
}
|
|
146
|
+
const pids = treePids(rows, root);
|
|
147
|
+
if (!rows.some((r) => r.pid === root))
|
|
148
|
+
return { cpuMs: null, connections: null };
|
|
149
|
+
let cpuMs = 0;
|
|
150
|
+
for (const r of rows)
|
|
151
|
+
if (pids.includes(r.pid))
|
|
152
|
+
cpuMs += r.cpuMs ?? 0;
|
|
153
|
+
const lsof = await run("lsof", ["-nP", "-a", "-p", pids.join(","), "-iTCP", "-sTCP:ESTABLISHED", "-Fn"]);
|
|
154
|
+
// -Fn prints one `n<addr>` line per connection
|
|
155
|
+
const connections = lsof === null ? null : lsof.split("\n").filter((l) => l.startsWith("n")).length;
|
|
156
|
+
return { cpuMs, connections };
|
|
157
|
+
}
|
|
158
|
+
async function sampleWindows(root) {
|
|
159
|
+
const script = "$ErrorActionPreference='SilentlyContinue';" +
|
|
160
|
+
"$all=Get-CimInstance Win32_Process|Select-Object ProcessId,ParentProcessId,KernelModeTime,UserModeTime;" +
|
|
161
|
+
`$ids=@(${root});$q=@(${root});` +
|
|
162
|
+
"while($q.Count){$p=$q[0];$q=@($q|Select-Object -Skip 1);foreach($c in $all|Where-Object{$_.ParentProcessId -eq $p}){if($ids -notcontains $c.ProcessId){$ids+=$c.ProcessId;$q+=$c.ProcessId}}};" +
|
|
163
|
+
"$cpu=($all|Where-Object{$ids -contains $_.ProcessId}|ForEach-Object{[double]$_.KernelModeTime+[double]$_.UserModeTime}|Measure-Object -Sum).Sum/10000;" +
|
|
164
|
+
"$n=@(Get-NetTCPConnection -State Established|Where-Object{$ids -contains $_.OwningProcess}).Count;" +
|
|
165
|
+
"Write-Output \"$cpu $n\"";
|
|
166
|
+
const out = await run("powershell.exe", ["-NoProfile", "-NonInteractive", "-Command", script], 8_000);
|
|
167
|
+
const [cpu, n] = (out ?? "").trim().split(/\s+/);
|
|
168
|
+
const cpuMs = Number(cpu);
|
|
169
|
+
const connections = Number(n);
|
|
170
|
+
return {
|
|
171
|
+
cpuMs: cpu !== undefined && Number.isFinite(cpuMs) ? cpuMs : null,
|
|
172
|
+
connections: n !== undefined && Number.isFinite(connections) ? connections : null,
|
|
173
|
+
};
|
|
174
|
+
}
|
|
175
|
+
/** CPU time and open connections of a process and its children. Best
|
|
176
|
+
* effort: a missing tool or a refused query reads as null (unknown), never
|
|
177
|
+
* as "idle". */
|
|
178
|
+
export function sampleProcessTree(pid) {
|
|
179
|
+
return (process.platform === "win32" ? sampleWindows(pid) : samplePosix(pid))
|
|
180
|
+
.catch(() => ({ cpuMs: null, connections: null }));
|
|
181
|
+
}
|
|
182
|
+
/** CPU a process tree can burn on bookkeeping while doing no real work. */
|
|
183
|
+
const BUSY_CPU_MS_PER_MIN = 3_000;
|
|
184
|
+
export function classifyQuiet(input) {
|
|
185
|
+
const { logged, sample, previous } = input;
|
|
186
|
+
if (logged?.kind === "retry")
|
|
187
|
+
return { kind: "retrying", status: logged.status, delayMs: logged.delayMs, attempt: logged.attempt };
|
|
188
|
+
if (logged?.kind === "compressing")
|
|
189
|
+
return { kind: "compressing" };
|
|
190
|
+
const nearFullContext = input.context !== null && input.context.threshold > 0
|
|
191
|
+
&& input.context.tokens >= input.context.threshold * 0.9;
|
|
192
|
+
if (sample.connections !== null && sample.connections > 0)
|
|
193
|
+
return { kind: "waiting-model", nearFullContext };
|
|
194
|
+
const cpuDelta = sample.cpuMs !== null && previous?.cpuMs != null ? sample.cpuMs - previous.cpuMs : null;
|
|
195
|
+
if (cpuDelta !== null && input.sinceMs > 0 && cpuDelta > BUSY_CPU_MS_PER_MIN * (input.sinceMs / 60_000))
|
|
196
|
+
return { kind: "busy" };
|
|
197
|
+
if (sample.connections === 0 && cpuDelta !== null)
|
|
198
|
+
return { kind: "no-signs" };
|
|
199
|
+
return { kind: "unknown" };
|
|
200
|
+
}
|
|
201
|
+
/** Two states read the same to a person when this matches; a notice is
|
|
202
|
+
* sent only when it changes. */
|
|
203
|
+
export function quietKey(state) {
|
|
204
|
+
if (state.kind === "retrying")
|
|
205
|
+
return `retrying:${state.attempt ?? "?"}:${state.status ?? "?"}`;
|
|
206
|
+
if (state.kind === "waiting-model")
|
|
207
|
+
return `waiting-model:${state.nearFullContext}`;
|
|
208
|
+
return state.kind;
|
|
209
|
+
}
|
|
210
|
+
const minutes = (ms) => {
|
|
211
|
+
if (ms < 90_000)
|
|
212
|
+
return `${Math.max(1, Math.round(ms / 1000))} s`;
|
|
213
|
+
return `${Math.round(ms / 60_000)} min`;
|
|
214
|
+
};
|
|
215
|
+
/** The chat notice for a quiet state. */
|
|
216
|
+
export function describeQuiet(name, state, quietMs, stopAfterMs) {
|
|
217
|
+
const quiet = minutes(quietMs);
|
|
218
|
+
switch (state.kind) {
|
|
219
|
+
case "retrying": {
|
|
220
|
+
const why = state.status === 429 ? "hit a rate limit (HTTP 429)"
|
|
221
|
+
: state.status !== null && state.status >= 500 ? `got a server error from its model provider (HTTP ${state.status})`
|
|
222
|
+
: state.status !== null ? `got an error from its model provider (HTTP ${state.status})`
|
|
223
|
+
: "had a failed model request";
|
|
224
|
+
const next = state.delayMs !== null ? ` Next try in ${Math.max(1, Math.round(state.delayMs / 1000))} s.` : "";
|
|
225
|
+
const attempt = state.attempt !== null ? ` (attempt ${state.attempt + 1})` : "";
|
|
226
|
+
return `${name} ${why} and is retrying${attempt}.${next} It is still working.`;
|
|
227
|
+
}
|
|
228
|
+
case "compressing":
|
|
229
|
+
return `${name} is compressing its conversation history to make room. This can take a few minutes; it is still working.`;
|
|
230
|
+
case "waiting-model":
|
|
231
|
+
return state.nearFullContext
|
|
232
|
+
? `${name} is waiting on its model: no reply for ${quiet}, but its request is still open. Its history is nearly full, so it may be compressing it.`
|
|
233
|
+
: `${name} is waiting on its model: no reply for ${quiet}, but its request is still open. Slow models and rate limits look like this.`;
|
|
234
|
+
case "busy":
|
|
235
|
+
return `${name} is busy on this computer but has sent nothing for ${quiet}.`;
|
|
236
|
+
case "between-requests":
|
|
237
|
+
return `${name} has sent nothing for ${quiet} and has no request open to its model right now. ` +
|
|
238
|
+
"It may be waiting before a retry. Still waiting; press Stop to end the turn.";
|
|
239
|
+
case "no-signs":
|
|
240
|
+
return `${name} has sent nothing for ${quiet}, has no open request to its model, and is not using the CPU. It may be stuck. ` +
|
|
241
|
+
`Press Stop to end the turn; otherwise it is stopped after ${minutes(stopAfterMs)} of silence.`;
|
|
242
|
+
case "unknown":
|
|
243
|
+
return `${name} has sent nothing for ${quiet}. Still waiting; press Stop to end the turn.`;
|
|
244
|
+
}
|
|
245
|
+
}
|
|
246
|
+
/** A short phrase for the final stuck error: what the agent looked like last. */
|
|
247
|
+
export function lastSeenPhrase(state) {
|
|
248
|
+
switch (state.kind) {
|
|
249
|
+
case "retrying": return "retrying a failed model request";
|
|
250
|
+
case "compressing": return "compressing its history";
|
|
251
|
+
case "waiting-model": return "waiting on its model with a request open";
|
|
252
|
+
case "busy": return "busy on this computer";
|
|
253
|
+
case "between-requests": return "no request open to its model";
|
|
254
|
+
case "no-signs": return "no open request and no CPU use";
|
|
255
|
+
case "unknown": return "no readable process state";
|
|
256
|
+
}
|
|
257
|
+
}
|
|
@@ -6,6 +6,7 @@ import { createHash } from "node:crypto";
|
|
|
6
6
|
import { join } from "node:path";
|
|
7
7
|
import { qualifiedModelLabel } from "../../contracts.js";
|
|
8
8
|
import { harnessHome } from "../../env-path.js";
|
|
9
|
+
import { parseQwenLogLine } from "./quiet-status.js";
|
|
9
10
|
import { decodeInjectId, hostApiKey, localHost, mergeLocalInject } from "../local-inject.js";
|
|
10
11
|
import { createAcpDriver } from "./core.js";
|
|
11
12
|
const EMPTY = { default: "", options: [] };
|
|
@@ -199,6 +200,15 @@ export function resolveQwenTurnModel(model, env) {
|
|
|
199
200
|
throw new Error("This Qwen model is no longer configured. Refresh models and select it again.");
|
|
200
201
|
return matches[0].id;
|
|
201
202
|
}
|
|
203
|
+
/** Where Qwen Code 0.24 writes a session's debug log: `<runtime dir>/debug/<session id>.txt`,
|
|
204
|
+
* the runtime dir being QWEN_RUNTIME_DIR, else QWEN_HOME, else ~/.qwen. */
|
|
205
|
+
export function qwenDebugLogPath(env, sessionId) {
|
|
206
|
+
const off = ["", "0", "false", "off", "no"].includes((env.QWEN_DEBUG_LOG_FILE ?? "").trim().toLowerCase());
|
|
207
|
+
if (off || !/^[\w-]{1,128}$/.test(sessionId))
|
|
208
|
+
return null;
|
|
209
|
+
const base = env.QWEN_RUNTIME_DIR || env.QWEN_HOME || harnessHome("qwen", env);
|
|
210
|
+
return join(base, "debug", `${sessionId}.txt`);
|
|
211
|
+
}
|
|
202
212
|
/** Qwen Code's own approval ladder, passed through (qwen --help, 0.24):
|
|
203
213
|
* `--approval-mode default` asks, `auto-edit` approves file edits, `auto`
|
|
204
214
|
* runs Qwen's LLM classifier that approves safe actions and blocks risky
|
|
@@ -243,5 +253,13 @@ const support = {
|
|
|
243
253
|
authFailure: "continue",
|
|
244
254
|
isAuthenticated: () => true,
|
|
245
255
|
buildPromptText: (turn) => (turn.system ? `${turn.system}\n\n${turn.text}` : turn.text),
|
|
256
|
+
// Qwen says nothing over ACP while it retries a rate limit or compresses
|
|
257
|
+
// its history, but its debug log does. Turn the log on (unless the person
|
|
258
|
+
// turned it off) and read it while a prompt is quiet.
|
|
259
|
+
transformEnv: (env) => {
|
|
260
|
+
if (env.QWEN_DEBUG_LOG_FILE === undefined)
|
|
261
|
+
env.QWEN_DEBUG_LOG_FILE = "1";
|
|
262
|
+
},
|
|
263
|
+
statusLog: { path: qwenDebugLogPath, parse: parseQwenLogLine },
|
|
246
264
|
};
|
|
247
265
|
export const QwenAgentDriver = createAcpDriver(support);
|
|
@@ -25,6 +25,7 @@ export function catalogProfileFromEnv(env) {
|
|
|
25
25
|
sharedComputers: env.OMB_SHARED_COMPUTERS_ENABLED === "1",
|
|
26
26
|
voiceNotes: env.OMB_VOICE_NOTES === "1",
|
|
27
27
|
cloudHome: env.OMB_CLOUD_HOME === "1",
|
|
28
|
+
memoryEnabled: env.OMB_MEMORY_ENABLED !== "0",
|
|
28
29
|
botId: env.OMB_BOT_ID ?? "",
|
|
29
30
|
};
|
|
30
31
|
}
|
|
@@ -210,14 +211,14 @@ const toolDefinitions = (externalRuntime) => [
|
|
|
210
211
|
},
|
|
211
212
|
{
|
|
212
213
|
name: "coordinate_bots",
|
|
213
|
-
description: "Ask existing OpenMausBot teammates for advice or assign concrete work. From normal chat
|
|
214
|
+
description: "Ask existing OpenMausBot teammates for advice or assign concrete work. From normal chat each distinct assignment starts a fresh thread for that teammate; from a room it defaults to this room. Give a self-contained brief. Use group_id from list_room_targets for a specific room. Give 1-4 bot_ids — teammate ids as list_bots or your roster prints them; a unique teammate name also resolves: they receive only your brief and use their own model, tools and permissions. Busy bots queue. They can consult their specialists; all results return here and resume you automatically. Include exact file paths, constraints and what must be verified. After sending all assignments, END your turn; do not poll or wait. On return, resolve tradeoffs, verify the requested outcome and request concrete corrections if necessary before giving one final answer. Do not send acknowledgements as new work.",
|
|
214
215
|
inputSchema: { type: "object", additionalProperties: false, properties: {
|
|
215
|
-
group_id: { type: "string", description: "Optional destination room. Omit for this room, or
|
|
216
|
+
group_id: { type: "string", description: "Optional destination room. Omit for this room, or a fresh thread for each teammate when chatting directly." },
|
|
216
217
|
bot_ids: { type: "array", items: { type: "string", description: "A teammate's id exactly as list_bots or your roster prints it ([id: …]). A teammate's unique display name also resolves; a name shared by two reachable teammates is refused." }, minItems: 1, maxItems: 4, uniqueItems: true },
|
|
217
218
|
message: { type: "string", minLength: 1, maxLength: 4000, description: "Self-contained question or task for these teammates. Send separate requests when responsibilities differ." },
|
|
218
219
|
request_key: { type: "string", description: "A short unique assignment key. Reuse for an identical retry." },
|
|
219
220
|
rework: { type: "boolean", description: "True only for concrete additional work from someone who already completed a request." },
|
|
220
|
-
label: { type: "string", description: "Optional short name (one line, at most 60 characters) for this job.
|
|
221
|
+
label: { type: "string", description: "Optional short name (one line, at most 60 characters) for this job. Names the new direct assignment thread." },
|
|
221
222
|
}, required: ["bot_ids", "message", "request_key"] },
|
|
222
223
|
},
|
|
223
224
|
{
|
|
@@ -778,9 +779,8 @@ export function availableTools(profile) {
|
|
|
778
779
|
}
|
|
779
780
|
function catalogTools(profile) {
|
|
780
781
|
const TOOLS = toolDefinitions(profile.externalRuntime);
|
|
781
|
-
const BOT_SCOPED_TOOLS = profile.botId === WATCHER_OPTIONS_CARD_BOT_ID
|
|
782
|
-
|
|
783
|
-
: TOOLS.filter((tool) => !WATCHER_TOOL_NAMES.has(tool.name));
|
|
782
|
+
const BOT_SCOPED_TOOLS = TOOLS.filter((tool) => (profile.botId === WATCHER_OPTIONS_CARD_BOT_ID || !WATCHER_TOOL_NAMES.has(tool.name)) &&
|
|
783
|
+
(profile.memoryEnabled !== false || (tool.name !== "memory_update" && tool.name !== "memory_log")));
|
|
784
784
|
const AUTHORING_TOOLS = profile.skillAuthoring
|
|
785
785
|
? BOT_SCOPED_TOOLS
|
|
786
786
|
: BOT_SCOPED_TOOLS.filter((tool) => !SKILL_TOOL_NAMES.has(tool.name));
|
|
@@ -6,6 +6,7 @@ import { delimiter, join, resolve } from "node:path";
|
|
|
6
6
|
import { DATA_DIR, stripWorkspaceCredentialEnv } from "../config.js";
|
|
7
7
|
import { writeFileAtomic } from "../atomic.js";
|
|
8
8
|
import { killCliTree, spawnCli } from "../procs.js";
|
|
9
|
+
import { VERIFICATION_TEMP_KEY, antigravityTempDir, setAntigravityTempEnvironment, sweepAntigravityTemp, } from "./antigravity-temp.js";
|
|
9
10
|
// Printed on stderr by Google's server, not stdout.
|
|
10
11
|
export const ANTIGRAVITY_AUTH_PREFIX = "Open the following link to authenticate the ACP server: ";
|
|
11
12
|
const AUTH_TIMEOUT_MS = 5 * 60_000;
|
|
@@ -16,6 +17,10 @@ const MAX_PROTOCOL_LINE_BYTES = 16 * 1024 * 1024;
|
|
|
16
17
|
// Past the 16 KiB ceiling parseAntigravityAuthorizationUrl accepts, so a
|
|
17
18
|
// partial stderr line is never dropped while it could still become a link.
|
|
18
19
|
const MAX_DIAGNOSTIC_LINE_CHARS = 64 * 1024;
|
|
20
|
+
// How long a closed runtime gets to exit on its own after its input ends.
|
|
21
|
+
// A forced stop on Windows skips the runtime's own cleanup of the files it
|
|
22
|
+
// unpacked; exiting by itself removes them. Same grace as the ACP pool.
|
|
23
|
+
const GRACEFUL_EXIT_MS = 5_000;
|
|
19
24
|
const REMOVED_ENVIRONMENT_KEYS = new Set([
|
|
20
25
|
"GEMINI_API_KEY",
|
|
21
26
|
"GOOGLE_API_KEY",
|
|
@@ -28,6 +33,8 @@ const REMOVED_ENVIRONMENT_KEYS = new Set([
|
|
|
28
33
|
"CLOUDSDK_CORE_PROJECT",
|
|
29
34
|
"AGY_ACP_CCPA_PROJECT",
|
|
30
35
|
"AGY_ACP_ENABLE_OAUTH",
|
|
36
|
+
// OMB sets the new-session model itself (antigravity.ts sessionModelEnv).
|
|
37
|
+
"AGY_ACP_DEFAULT_MODEL",
|
|
31
38
|
"GEMINI_HOME",
|
|
32
39
|
"AGY_ACP_FORCE_FILE_STORAGE",
|
|
33
40
|
"ANTIGRAVITY_HARNESS_PATH",
|
|
@@ -121,7 +128,22 @@ export async function prepareAntigravityProfile(input) {
|
|
|
121
128
|
PYTHONUNBUFFERED: "1",
|
|
122
129
|
ELECTRON_RUN_AS_NODE: "1",
|
|
123
130
|
});
|
|
124
|
-
|
|
131
|
+
// On Windows the runtime unpacks 0.34-1.26 GB into TEMP on every launch.
|
|
132
|
+
// Keep that in a folder OMB owns and can clean. It is the same folder for
|
|
133
|
+
// every launch of this instance, so the pooled process's spawn contract
|
|
134
|
+
// (which hashes this environment) does not change from turn to turn.
|
|
135
|
+
let tempDirectory;
|
|
136
|
+
if ((input.platform ?? process.platform) === "win32") {
|
|
137
|
+
tempDirectory = resolve(input.tempDirectory ?? antigravityTempDir(input.baseDir ?? DATA_DIR, input.instanceId));
|
|
138
|
+
await mkdir(tempDirectory, { recursive: true });
|
|
139
|
+
setAntigravityTempEnvironment(environment, tempDirectory, "win32");
|
|
140
|
+
}
|
|
141
|
+
return {
|
|
142
|
+
directory,
|
|
143
|
+
tokenPath: join(acpDirectory, "acp_token.json"),
|
|
144
|
+
environment,
|
|
145
|
+
...(tempDirectory ? { tempDirectory } : {}),
|
|
146
|
+
};
|
|
125
147
|
}
|
|
126
148
|
export async function antigravityProfileAuthenticated(profile) {
|
|
127
149
|
try {
|
|
@@ -177,6 +199,10 @@ export class AntigravityAcpClient {
|
|
|
177
199
|
});
|
|
178
200
|
}
|
|
179
201
|
consume(chunk) {
|
|
202
|
+
// Output after close has no reader; do not buffer it while the process
|
|
203
|
+
// is given time to exit.
|
|
204
|
+
if (this.closed)
|
|
205
|
+
return;
|
|
180
206
|
if (!this.initializationComplete)
|
|
181
207
|
this.startupOutputBytes += Buffer.byteLength(chunk);
|
|
182
208
|
this.buffer += chunk;
|
|
@@ -287,15 +313,40 @@ export class AntigravityAcpClient {
|
|
|
287
313
|
this.nativeStartupHint = undefined;
|
|
288
314
|
return initialized;
|
|
289
315
|
}
|
|
316
|
+
/** End the runtime's input and let it exit by itself, so it removes what
|
|
317
|
+
* it unpacked; stop it by force only if it is still running after
|
|
318
|
+
* GRACEFUL_EXIT_MS. killCliTree also runs after a clean exit. On macOS and
|
|
319
|
+
* Linux that reaps any helper still in the process group. On Windows it
|
|
320
|
+
* does nothing once the runtime has exited (procs.ts stopCliTree), so a
|
|
321
|
+
* helper that outlives a clean exit is left running, as in the ACP pool. */
|
|
290
322
|
close() {
|
|
291
323
|
if (this.closed)
|
|
292
324
|
return;
|
|
293
325
|
this.closed = true;
|
|
294
326
|
this.failAll(new Error("Antigravity ACP was closed."));
|
|
295
|
-
this.stopping =
|
|
327
|
+
this.stopping = this.stopGracefully();
|
|
296
328
|
}
|
|
297
|
-
|
|
298
|
-
|
|
329
|
+
async stopGracefully() {
|
|
330
|
+
try {
|
|
331
|
+
this.child.stdin?.end();
|
|
332
|
+
}
|
|
333
|
+
catch {
|
|
334
|
+
/* already closed; the kill below still applies */
|
|
335
|
+
}
|
|
336
|
+
let timer;
|
|
337
|
+
await Promise.race([
|
|
338
|
+
this.exited,
|
|
339
|
+
new Promise((resolve) => {
|
|
340
|
+
timer = setTimeout(resolve, GRACEFUL_EXIT_MS);
|
|
341
|
+
timer.unref?.();
|
|
342
|
+
}),
|
|
343
|
+
]);
|
|
344
|
+
clearTimeout(timer);
|
|
345
|
+
return killCliTree(this.child);
|
|
346
|
+
}
|
|
347
|
+
/** Allow the graceful exit, the shared 5s TERM grace, and the 1s
|
|
348
|
+
* force-stop verification to finish. */
|
|
349
|
+
async closeAndWait(timeoutMs = 12_000) {
|
|
299
350
|
this.close();
|
|
300
351
|
let timer;
|
|
301
352
|
const timedOut = new Promise((resolve) => {
|
|
@@ -478,20 +529,19 @@ export function isValidAntigravityInitializeResult(initialized, expectedVersion)
|
|
|
478
529
|
export async function validateAntigravityRuntime(runtime, expectedVersion) {
|
|
479
530
|
const profileDirectory = await mkdtemp(join(tmpdir(), "openmaus-antigravity-verify-"));
|
|
480
531
|
let client;
|
|
532
|
+
let tempDirectory;
|
|
481
533
|
let failed = false;
|
|
482
534
|
let failure;
|
|
483
535
|
try {
|
|
536
|
+
// On Windows every verification unpacks into one shared folder under
|
|
537
|
+
// DATA_DIR/tmp/agy, swept below once the runtime is gone.
|
|
484
538
|
const profile = await prepareAntigravityProfile({
|
|
485
539
|
instanceId: `verify-${randomUUID()}`,
|
|
486
540
|
runtime,
|
|
487
541
|
profileDirectory,
|
|
488
|
-
|
|
489
|
-
// into TEMP. Forced shutdown skips its own cleanup. Keep verification's
|
|
490
|
-
// extraction inside the profile we already remove after confirmed close.
|
|
491
|
-
baseEnv: process.platform === "win32"
|
|
492
|
-
? { ...process.env, TEMP: profileDirectory, TMP: profileDirectory }
|
|
493
|
-
: undefined,
|
|
542
|
+
tempDirectory: antigravityTempDir(DATA_DIR, VERIFICATION_TEMP_KEY),
|
|
494
543
|
});
|
|
544
|
+
tempDirectory = profile.tempDirectory;
|
|
495
545
|
client = new AntigravityAcpClient(runtime, profile, profileDirectory);
|
|
496
546
|
const initialized = await client.initialize();
|
|
497
547
|
if (!isValidAntigravityInitializeResult(initialized, expectedVersion)) {
|
|
@@ -521,8 +571,16 @@ export async function validateAntigravityRuntime(runtime, expectedVersion) {
|
|
|
521
571
|
if (stopped) {
|
|
522
572
|
await rm(profileDirectory, { recursive: true, force: true, maxRetries: 4, retryDelay: 250 })
|
|
523
573
|
.catch((error) => {
|
|
524
|
-
console.warn(`antigravity: could not remove verification
|
|
574
|
+
console.warn(`antigravity: could not remove verification files ${profileDirectory}: ${error instanceof Error ? error.message : String(error)}`);
|
|
525
575
|
});
|
|
576
|
+
// The temp folder is shared, so only unpack folders whose process is
|
|
577
|
+
// gone go: this run's, and any an earlier run left when its runtime
|
|
578
|
+
// would not stop.
|
|
579
|
+
if (tempDirectory) {
|
|
580
|
+
await sweepAntigravityTemp(VERIFICATION_TEMP_KEY).catch((error) => {
|
|
581
|
+
console.warn(`antigravity: could not clear verification files: ${error instanceof Error ? error.message : String(error)}`);
|
|
582
|
+
});
|
|
583
|
+
}
|
|
526
584
|
}
|
|
527
585
|
if (failed)
|
|
528
586
|
throw failure;
|