usebeeline 0.1.30 → 0.1.33
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/usebeeline.mjs +159 -45
- package/package.json +1 -1
package/dist/usebeeline.mjs
CHANGED
|
@@ -16551,6 +16551,96 @@ function defaultSquireMcpClient(scope) {
|
|
|
16551
16551
|
return new StdioSquireMcpClient({ scope });
|
|
16552
16552
|
}
|
|
16553
16553
|
|
|
16554
|
+
// apps/body/dist/squire-share-renewals.js
|
|
16555
|
+
function shareResult(result) {
|
|
16556
|
+
const envelope = result;
|
|
16557
|
+
if (envelope?.isError || !Array.isArray(envelope?.content))
|
|
16558
|
+
throw new Error("Squire share renewal refused");
|
|
16559
|
+
const text2 = envelope.content.find((item) => item.type === "text")?.text;
|
|
16560
|
+
const body = JSON.parse(text2 ?? "{}");
|
|
16561
|
+
const expiresAt = typeof body.expires_at === "string" ? Date.parse(body.expires_at) : NaN;
|
|
16562
|
+
if (typeof body.url !== "string" || !/^https?:\/\/[^/]+\/s\/[A-Za-z0-9_-]{43}\/$/.test(body.url) || !Number.isFinite(expiresAt) || expiresAt <= Date.now())
|
|
16563
|
+
throw new Error("Squire share renewal returned an invalid lease");
|
|
16564
|
+
return { upstreamUrl: body.url, expiresAt };
|
|
16565
|
+
}
|
|
16566
|
+
var SquireShareRenewals = class {
|
|
16567
|
+
owns;
|
|
16568
|
+
persist;
|
|
16569
|
+
reaped;
|
|
16570
|
+
shares = /* @__PURE__ */ new Map();
|
|
16571
|
+
constructor(owns, persist, reaped) {
|
|
16572
|
+
this.owns = owns;
|
|
16573
|
+
this.persist = persist;
|
|
16574
|
+
this.reaped = reaped;
|
|
16575
|
+
}
|
|
16576
|
+
track(sessionId, client, result) {
|
|
16577
|
+
let lease;
|
|
16578
|
+
try {
|
|
16579
|
+
lease = shareResult(result);
|
|
16580
|
+
} catch {
|
|
16581
|
+
return;
|
|
16582
|
+
}
|
|
16583
|
+
this.stop(sessionId);
|
|
16584
|
+
const share = { sessionId, client, ...lease };
|
|
16585
|
+
this.shares.set(sessionId, share);
|
|
16586
|
+
this.schedule(share);
|
|
16587
|
+
}
|
|
16588
|
+
stop(sessionId) {
|
|
16589
|
+
const share = this.shares.get(sessionId);
|
|
16590
|
+
if (share?.timer)
|
|
16591
|
+
clearTimeout(share.timer);
|
|
16592
|
+
this.shares.delete(sessionId);
|
|
16593
|
+
}
|
|
16594
|
+
close() {
|
|
16595
|
+
for (const id of this.shares.keys())
|
|
16596
|
+
this.stop(id);
|
|
16597
|
+
}
|
|
16598
|
+
schedule(share, retry = false) {
|
|
16599
|
+
if (this.shares.get(share.sessionId) !== share)
|
|
16600
|
+
return;
|
|
16601
|
+
const remaining = share.expiresAt - Date.now();
|
|
16602
|
+
const margin = Math.min(4 * 6e4, Math.max(1, remaining / 2));
|
|
16603
|
+
const delay = retry ? 5e3 : Math.max(1, remaining - margin);
|
|
16604
|
+
share.timer = setTimeout(() => {
|
|
16605
|
+
void this.renew(share);
|
|
16606
|
+
}, delay);
|
|
16607
|
+
share.timer.unref?.();
|
|
16608
|
+
}
|
|
16609
|
+
async renew(share) {
|
|
16610
|
+
if (this.shares.get(share.sessionId) !== share)
|
|
16611
|
+
return;
|
|
16612
|
+
if (!this.owns(share.sessionId, share.client)) {
|
|
16613
|
+
this.stop(share.sessionId);
|
|
16614
|
+
this.reaped(share.sessionId);
|
|
16615
|
+
return;
|
|
16616
|
+
}
|
|
16617
|
+
try {
|
|
16618
|
+
const result = await share.client.requestMcp("tools/call", {
|
|
16619
|
+
name: "operate_share",
|
|
16620
|
+
arguments: { session_id: share.sessionId }
|
|
16621
|
+
});
|
|
16622
|
+
if (this.shares.get(share.sessionId) !== share)
|
|
16623
|
+
return;
|
|
16624
|
+
if (!this.owns(share.sessionId, share.client)) {
|
|
16625
|
+
this.stop(share.sessionId);
|
|
16626
|
+
this.reaped(share.sessionId);
|
|
16627
|
+
return;
|
|
16628
|
+
}
|
|
16629
|
+
const lease = shareResult(result);
|
|
16630
|
+
if (lease.upstreamUrl !== share.upstreamUrl || lease.expiresAt <= share.expiresAt)
|
|
16631
|
+
throw new Error("Squire did not renew the existing share");
|
|
16632
|
+
if (!await this.persist({ sessionId: share.sessionId, ...lease })) {
|
|
16633
|
+
this.stop(share.sessionId);
|
|
16634
|
+
return;
|
|
16635
|
+
}
|
|
16636
|
+
share.expiresAt = lease.expiresAt;
|
|
16637
|
+
this.schedule(share);
|
|
16638
|
+
} catch {
|
|
16639
|
+
this.schedule(share, true);
|
|
16640
|
+
}
|
|
16641
|
+
}
|
|
16642
|
+
};
|
|
16643
|
+
|
|
16554
16644
|
// apps/body/dist/squire-session-registry.js
|
|
16555
16645
|
var owners = /* @__PURE__ */ new Map();
|
|
16556
16646
|
function registerSquireSession(sessionId, owner) {
|
|
@@ -16659,6 +16749,7 @@ var SquireTaskRelay = class {
|
|
|
16659
16749
|
approvalRequestId;
|
|
16660
16750
|
closed = false;
|
|
16661
16751
|
callTail = Promise.resolve();
|
|
16752
|
+
shareRenewals;
|
|
16662
16753
|
constructor(agentId, roomId, contextFile, authorize, home = homedir4(), makeClient = (callbacks) => new StdioSquireMcpClient({
|
|
16663
16754
|
scope: { agentId, roomId },
|
|
16664
16755
|
home,
|
|
@@ -16669,7 +16760,7 @@ var SquireTaskRelay = class {
|
|
|
16669
16760
|
onNotification: callbacks.onNotification
|
|
16670
16761
|
}), onApprovalDecided = () => {
|
|
16671
16762
|
}, onSessionEnded = () => {
|
|
16672
|
-
}) {
|
|
16763
|
+
}, onShareRenewed = async () => false) {
|
|
16673
16764
|
this.agentId = agentId;
|
|
16674
16765
|
this.roomId = roomId;
|
|
16675
16766
|
this.contextFile = contextFile;
|
|
@@ -16677,10 +16768,13 @@ var SquireTaskRelay = class {
|
|
|
16677
16768
|
this.makeClient = makeClient;
|
|
16678
16769
|
this.onApprovalDecided = onApprovalDecided;
|
|
16679
16770
|
this.onSessionEnded = onSessionEnded;
|
|
16771
|
+
this.shareRenewals = new SquireShareRenewals((id, client) => !this.closed && squireSessionOwner(id)?.client === client, onShareRenewed, (id) => this.onSessionEnded(id));
|
|
16680
16772
|
}
|
|
16681
16773
|
ended(ids) {
|
|
16682
|
-
for (const id of ids)
|
|
16774
|
+
for (const id of ids) {
|
|
16775
|
+
this.shareRenewals.stop(id);
|
|
16683
16776
|
this.onSessionEnded(id);
|
|
16777
|
+
}
|
|
16684
16778
|
}
|
|
16685
16779
|
async listen() {
|
|
16686
16780
|
if (this.closed)
|
|
@@ -16774,6 +16868,7 @@ var SquireTaskRelay = class {
|
|
|
16774
16868
|
if (this.closed)
|
|
16775
16869
|
return;
|
|
16776
16870
|
this.closed = true;
|
|
16871
|
+
this.shareRenewals.close();
|
|
16777
16872
|
this.active = void 0;
|
|
16778
16873
|
this.approvalRequestId = void 0;
|
|
16779
16874
|
this.retire("helper-exit");
|
|
@@ -16985,6 +17080,8 @@ var SquireTaskRelay = class {
|
|
|
16985
17080
|
if (this.activeScheduleId)
|
|
16986
17081
|
for (const id of requestedIds)
|
|
16987
17082
|
tagSquireSessionSchedule(id, this.activeScheduleId);
|
|
17083
|
+
if (safeToolName === "operate_share" && requestedIds.length === 1)
|
|
17084
|
+
this.shareRenewals.track(requestedIds[0], callClient, result);
|
|
16988
17085
|
const approval = input.method === "tools/call" && squireApprovalFromMcp({ id: 1, method: input.method, params: input.params }, { id: 1, result });
|
|
16989
17086
|
if (approval) {
|
|
16990
17087
|
const ids = requestedIds.length ? requestedIds : [...task.sessions];
|
|
@@ -30453,7 +30550,7 @@ import { mkdir as mkdir13, rm as rm10, stat as stat8, writeFile as writeFile10 }
|
|
|
30453
30550
|
import { dirname as dirname13, resolve as resolve16 } from "node:path";
|
|
30454
30551
|
var UPDATE_CONVERGENCE_SLO_MS = 10 * 6e4;
|
|
30455
30552
|
var DEFAULT_UPDATE_INTERVAL_MS = 3e4;
|
|
30456
|
-
var UPDATE_DRAIN_DEADLINE_MS =
|
|
30553
|
+
var UPDATE_DRAIN_DEADLINE_MS = 5 * 6e4;
|
|
30457
30554
|
var MAX_HOST_RESTART_STAGGER_MS = 15e3;
|
|
30458
30555
|
function managedRestartStaggerMs(agentId, desiredRelease, drainDeadlineAt, now2 = Date.now()) {
|
|
30459
30556
|
const digest = createHash8("sha256").update(agentId).update("\0").update(desiredRelease).digest();
|
|
@@ -30655,8 +30752,8 @@ var ManagedUpdateHandoff = class _ManagedUpdateHandoff {
|
|
|
30655
30752
|
}
|
|
30656
30753
|
/**
|
|
30657
30754
|
* Resolve one handoff tick against the daemon's authoritative turn registry.
|
|
30658
|
-
* The coordinator keeps intake open
|
|
30659
|
-
*
|
|
30755
|
+
* The coordinator keeps intake open until a quiet gap or the bounded
|
|
30756
|
+
* drain deadline before activating a staged release.
|
|
30660
30757
|
*/
|
|
30661
30758
|
async restartRequest(readyToRestart) {
|
|
30662
30759
|
const activeDrift = await this.check();
|
|
@@ -30665,7 +30762,8 @@ var ManagedUpdateHandoff = class _ManagedUpdateHandoff {
|
|
|
30665
30762
|
const request = this.#restartRequest;
|
|
30666
30763
|
if (!request)
|
|
30667
30764
|
throw new Error("update drift was detected without an in-memory restart request");
|
|
30668
|
-
|
|
30765
|
+
const deadlineReached = this.#now() >= request.drainDeadlineAt;
|
|
30766
|
+
if (!readyToRestart(request, deadlineReached)) {
|
|
30669
30767
|
return { kind: "waiting", request };
|
|
30670
30768
|
}
|
|
30671
30769
|
if (this.#stagedReleaseId) {
|
|
@@ -30700,11 +30798,10 @@ var ManagedUpdateHandoff = class _ManagedUpdateHandoff {
|
|
|
30700
30798
|
});
|
|
30701
30799
|
}
|
|
30702
30800
|
this.#stagedReleaseId = void 0;
|
|
30703
|
-
this.#requested =
|
|
30704
|
-
this.#restartRequest =
|
|
30705
|
-
this.#armRestart(stagedReleaseId);
|
|
30801
|
+
this.#requested = true;
|
|
30802
|
+
this.#restartRequest = request;
|
|
30706
30803
|
}
|
|
30707
|
-
return { kind: "restart", request: this.#restartRequest ?? request };
|
|
30804
|
+
return { kind: "restart", mode: deadlineReached ? "interrupted" : "drained", request: this.#restartRequest ?? request };
|
|
30708
30805
|
}
|
|
30709
30806
|
};
|
|
30710
30807
|
async function coordinateManagedUpdateHandoff(update, readyToRestart, restart, waiting = async () => void 0) {
|
|
@@ -30715,7 +30812,7 @@ async function coordinateManagedUpdateHandoff(update, readyToRestart, restart, w
|
|
|
30715
30812
|
await waiting(next.request);
|
|
30716
30813
|
return "waiting-for-idle";
|
|
30717
30814
|
}
|
|
30718
|
-
await restart(next.request, "drained");
|
|
30815
|
+
await restart(next.request, next.mode ?? "drained");
|
|
30719
30816
|
return "restarting";
|
|
30720
30817
|
}
|
|
30721
30818
|
var UPDATE_DRAIN_WAIT_LOG_INTERVAL_MS = 6e4;
|
|
@@ -30750,7 +30847,7 @@ var ManagedUpdateDrain = class {
|
|
|
30750
30847
|
async #step() {
|
|
30751
30848
|
const { update, quiesce, activeTurnCount } = this.#options;
|
|
30752
30849
|
let armed;
|
|
30753
|
-
const progress = await coordinateManagedUpdateHandoff(update, () => activeTurnCount() === 0 && quiesce(), async (request, mode) => {
|
|
30850
|
+
const progress = await coordinateManagedUpdateHandoff(update, (_request, deadlineReached) => (activeTurnCount() === 0 || deadlineReached) && quiesce(deadlineReached), async (request, mode) => {
|
|
30754
30851
|
this.#disarm();
|
|
30755
30852
|
await this.#options.restart(request, mode);
|
|
30756
30853
|
this.#restarting = true;
|
|
@@ -30759,20 +30856,27 @@ var ManagedUpdateDrain = class {
|
|
|
30759
30856
|
await this.#options.waiting?.(request);
|
|
30760
30857
|
});
|
|
30761
30858
|
if (armed && !this.#waitLogTimer)
|
|
30762
|
-
this.#arm();
|
|
30859
|
+
this.#arm(armed);
|
|
30763
30860
|
return progress;
|
|
30764
30861
|
}
|
|
30765
|
-
#arm() {
|
|
30862
|
+
#arm(request) {
|
|
30766
30863
|
this.#logWaiting();
|
|
30767
|
-
this.#scheduleWaitLog();
|
|
30864
|
+
this.#scheduleWaitLog(request);
|
|
30768
30865
|
}
|
|
30769
|
-
#scheduleWaitLog() {
|
|
30866
|
+
#scheduleWaitLog(request) {
|
|
30770
30867
|
this.#waitLogTimer = this.#setTimer(() => {
|
|
30771
30868
|
this.#waitLogTimer = void 0;
|
|
30772
30869
|
if (this.#restarting)
|
|
30773
30870
|
return;
|
|
30774
|
-
void this.#resolve().catch((error) =>
|
|
30775
|
-
|
|
30871
|
+
void this.#resolve().catch((error) => {
|
|
30872
|
+
if (this.#options.failed)
|
|
30873
|
+
this.#options.failed(error);
|
|
30874
|
+
else
|
|
30875
|
+
this.#log(`[thin-core] update drain failed: ${String(error)}`);
|
|
30876
|
+
if (!this.#restarting && this.#waitLogTimer === void 0)
|
|
30877
|
+
this.#scheduleWaitLog(request);
|
|
30878
|
+
});
|
|
30879
|
+
}, Math.min(UPDATE_DRAIN_WAIT_LOG_INTERVAL_MS, updateWaitMs(this.#options, request)));
|
|
30776
30880
|
}
|
|
30777
30881
|
#logWaiting() {
|
|
30778
30882
|
this.#log(`[thin-core] update restart waiting: ${this.#options.activeTurnCount()} active turn(s); intake open; waiting for a quiet gap`);
|
|
@@ -30783,6 +30887,10 @@ var ManagedUpdateDrain = class {
|
|
|
30783
30887
|
this.#waitLogTimer = void 0;
|
|
30784
30888
|
}
|
|
30785
30889
|
};
|
|
30890
|
+
function updateWaitMs(options, request) {
|
|
30891
|
+
const remaining = request.drainDeadlineAt - (options.now ?? Date.now)();
|
|
30892
|
+
return remaining > 0 ? remaining : UPDATE_DRAIN_WAIT_LOG_INTERVAL_MS;
|
|
30893
|
+
}
|
|
30786
30894
|
function numberEnv(env, name, fallback) {
|
|
30787
30895
|
const value = Number(env[name]);
|
|
30788
30896
|
return Number.isFinite(value) && value >= 0 ? value : fallback;
|
|
@@ -31803,7 +31911,7 @@ var AcpRequestTimeoutError = class extends Error {
|
|
|
31803
31911
|
this.name = "AcpRequestTimeoutError";
|
|
31804
31912
|
}
|
|
31805
31913
|
};
|
|
31806
|
-
var TURN_BACKSTOP_MS =
|
|
31914
|
+
var TURN_BACKSTOP_MS = 45 * 6e4;
|
|
31807
31915
|
var AcpTurnBackstopError = class extends Error {
|
|
31808
31916
|
timeoutMs;
|
|
31809
31917
|
lastActivity;
|
|
@@ -32429,8 +32537,8 @@ ${String(error)}`);
|
|
|
32429
32537
|
/**
|
|
32430
32538
|
* Run one prompt turn. `timeoutMs` is an idle window, not a hard cap on
|
|
32431
32539
|
* turn length: every inbound ACP message for this session re-arms it.
|
|
32432
|
-
*
|
|
32433
|
-
*
|
|
32540
|
+
* Tool progress re-arms the same bound; an open tool never disables it.
|
|
32541
|
+
* A silent session is cancelled without stopping sibling sessions.
|
|
32434
32542
|
* `onChunk` is the ACP-boundary
|
|
32435
32543
|
* streaming hook: called for every incremental `agent_message_chunk`
|
|
32436
32544
|
* delta as it arrives, so a caller can project live text without waiting
|
|
@@ -32554,16 +32662,14 @@ ${String(error)}`);
|
|
|
32554
32662
|
}
|
|
32555
32663
|
}
|
|
32556
32664
|
/**
|
|
32557
|
-
*
|
|
32558
|
-
*
|
|
32665
|
+
* Bound silence even when a tool stays open: a missing terminal tool update
|
|
32666
|
+
* must not leave a claimed turn running indefinitely.
|
|
32559
32667
|
*/
|
|
32560
32668
|
resetPendingIdleTimeout(id) {
|
|
32561
32669
|
const p = this.pending.get(id);
|
|
32562
32670
|
if (!p || p.idleTimeoutMs === void 0)
|
|
32563
32671
|
return;
|
|
32564
32672
|
this.clearTimer(p);
|
|
32565
|
-
if (p.openToolCalls?.size)
|
|
32566
|
-
return;
|
|
32567
32673
|
const { idleTimeoutMs, reject, detail, sessionId, lastActivity } = p;
|
|
32568
32674
|
p.timer = setTimeout(() => {
|
|
32569
32675
|
this.pending.delete(id);
|
|
@@ -32601,13 +32707,6 @@ ${String(error)}`);
|
|
|
32601
32707
|
continue;
|
|
32602
32708
|
const update = params?.update;
|
|
32603
32709
|
pending.lastActivity = typeof update?.sessionUpdate === "string" ? update.sessionUpdate : method ?? `response:${response?.method ?? "request"}`;
|
|
32604
|
-
if (typeof update?.toolCallId === "string" && (update.sessionUpdate === "tool_call" || update.sessionUpdate === "tool_call_update")) {
|
|
32605
|
-
if (update.status === "pending" || update.status === "in_progress") {
|
|
32606
|
-
pending.openToolCalls?.add(update.toolCallId);
|
|
32607
|
-
} else if (update.status === "completed" || update.status === "failed") {
|
|
32608
|
-
pending.openToolCalls?.delete(update.toolCallId);
|
|
32609
|
-
}
|
|
32610
|
-
}
|
|
32611
32710
|
this.resetPendingIdleTimeout(requestId);
|
|
32612
32711
|
}
|
|
32613
32712
|
}
|
|
@@ -32791,7 +32890,7 @@ ${String(error)}`);
|
|
|
32791
32890
|
method,
|
|
32792
32891
|
...sessionId ? { sessionId } : {},
|
|
32793
32892
|
...detail ? { detail } : {},
|
|
32794
|
-
...backstop ? { idleTimeoutMs: timeoutMs, lastActivity: method
|
|
32893
|
+
...backstop ? { idleTimeoutMs: timeoutMs, lastActivity: method } : {}
|
|
32795
32894
|
});
|
|
32796
32895
|
if (backstop)
|
|
32797
32896
|
this.resetPendingIdleTimeout(id);
|
|
@@ -35962,8 +36061,11 @@ async function shareChromiumPage(input) {
|
|
|
35962
36061
|
if (message.method === "Page.frameNavigated" && message.params?.frame?.url) {
|
|
35963
36062
|
try {
|
|
35964
36063
|
const url = new URL(message.params.frame.url);
|
|
35965
|
-
if (
|
|
35966
|
-
|
|
36064
|
+
if (["http:", "https:"].includes(url.protocol) && relay.readyState === import_websocket.default.OPEN) {
|
|
36065
|
+
url.username = "";
|
|
36066
|
+
url.password = "";
|
|
36067
|
+
relay.send(JSON.stringify({ type: "share-event", name: "navigation", detail: url.href }));
|
|
36068
|
+
}
|
|
35967
36069
|
} catch {
|
|
35968
36070
|
}
|
|
35969
36071
|
return;
|
|
@@ -43234,7 +43336,7 @@ var MonolithRoomTurnLoop = class {
|
|
|
43234
43336
|
}, (sessionId) => {
|
|
43235
43337
|
options.api.execute("endComputerSession", { roomId: options.roomId, sessionId }).catch(() => {
|
|
43236
43338
|
});
|
|
43237
|
-
});
|
|
43339
|
+
}, async (lease) => (await options.api.execute("refreshComputerSession", { roomId: options.roomId, ...lease })).live);
|
|
43238
43340
|
this.options = { ...options, api: this.commandContext.bind(options.api) };
|
|
43239
43341
|
options.grantRunner?.register(options.roomId, {
|
|
43240
43342
|
workspaceId: options.workspaceId,
|
|
@@ -44277,6 +44379,8 @@ var MonolithRoomTurnLoop = class {
|
|
|
44277
44379
|
void finishTrace("cancelled").catch(() => void 0);
|
|
44278
44380
|
return;
|
|
44279
44381
|
}
|
|
44382
|
+
if (error instanceof AcpTurnBackstopError)
|
|
44383
|
+
await this.discardSession();
|
|
44280
44384
|
if (liveCornerOpened && error instanceof AcpTurnBackstopError) {
|
|
44281
44385
|
console.log(`[thin-core] monolith Room ${this.options.roomId} turn ${item.id}: turn_backstop after opening a corner; the work continues in the corner`);
|
|
44282
44386
|
this.options.onCornerOpened?.();
|
|
@@ -45493,7 +45597,7 @@ var MonolithCornerTurnLoop = class {
|
|
|
45493
45597
|
}, (sessionId) => {
|
|
45494
45598
|
options.api.execute("endComputerSession", { roomId: options.cornerId, sessionId }).catch(() => {
|
|
45495
45599
|
});
|
|
45496
|
-
});
|
|
45600
|
+
}, async (lease) => (await options.api.execute("refreshComputerSession", { roomId: options.cornerId, ...lease })).live);
|
|
45497
45601
|
this.options = { ...options, api: this.commandContext.bind(options.api) };
|
|
45498
45602
|
options.grantRunner?.register(options.cornerId, {
|
|
45499
45603
|
workspaceId: options.workspaceId,
|
|
@@ -46733,6 +46837,8 @@ ${continuation}` : assembled;
|
|
|
46733
46837
|
void finishTrace("cancelled").catch(() => void 0);
|
|
46734
46838
|
return;
|
|
46735
46839
|
}
|
|
46840
|
+
if (error instanceof AcpTurnBackstopError)
|
|
46841
|
+
await this.discardSession();
|
|
46736
46842
|
const reason = distillTurnFailureReason(error);
|
|
46737
46843
|
startTraceStats();
|
|
46738
46844
|
await api.execute("postAgentTurnReceipt", {
|
|
@@ -50042,12 +50148,22 @@ async function runMachineHelper(options = {}) {
|
|
|
50042
50148
|
env
|
|
50043
50149
|
});
|
|
50044
50150
|
}
|
|
50151
|
+
const recoverManagedRestart = (error) => {
|
|
50152
|
+
console.error("[helper] managed restart failed; serving again:", error);
|
|
50153
|
+
for (const agent of hosted.values())
|
|
50154
|
+
agent.lifecycle.resumeAfterFailedUpdate(() => agent.core.resumeServing());
|
|
50155
|
+
};
|
|
50045
50156
|
const updateDrain = update ? new ManagedUpdateDrain({
|
|
50046
50157
|
update,
|
|
50047
|
-
|
|
50158
|
+
failed: recoverManagedRestart,
|
|
50159
|
+
quiesce: (deadlineReached) => !forceUpdate?.active && lifecycle.serving && quiesceHostedAgents([...hosted.values()], deadlineReached),
|
|
50048
50160
|
activeTurnCount: () => [...hosted.values()].reduce((sum, agent) => sum + agent.core.activeTurnCount(), 0),
|
|
50049
|
-
restart: async ({ desiredRelease }) => {
|
|
50050
|
-
|
|
50161
|
+
restart: async ({ desiredRelease }, mode) => {
|
|
50162
|
+
if (mode === "interrupted") {
|
|
50163
|
+
for (const agent of hosted.values())
|
|
50164
|
+
journalInterruptedTurns(agent.runtimeDir, agent.core.interruptForServerMinimum());
|
|
50165
|
+
}
|
|
50166
|
+
stoppingStatus = `update pending, converging; loaded_release=${loadedRelease ?? "unknown"}; desired_release=${desiredRelease}; drain ${mode}; intake quiesced`;
|
|
50051
50167
|
await notifier.stopping(stoppingStatus).catch((error) => console.error("[helper] stopping notification failed:", error));
|
|
50052
50168
|
restartHelper("update");
|
|
50053
50169
|
},
|
|
@@ -50059,9 +50175,7 @@ async function runMachineHelper(options = {}) {
|
|
|
50059
50175
|
try {
|
|
50060
50176
|
await updateDrain?.tick();
|
|
50061
50177
|
} catch (error) {
|
|
50062
|
-
|
|
50063
|
-
for (const agent of hosted.values())
|
|
50064
|
-
agent.lifecycle.resumeAfterFailedUpdate(() => agent.core.resumeServing());
|
|
50178
|
+
recoverManagedRestart(error);
|
|
50065
50179
|
}
|
|
50066
50180
|
void sweepHostDisk();
|
|
50067
50181
|
};
|
|
@@ -50231,8 +50345,8 @@ async function runMachineHelper(options = {}) {
|
|
|
50231
50345
|
}
|
|
50232
50346
|
return finish(lifecycle.stopReason ?? "stopped");
|
|
50233
50347
|
}
|
|
50234
|
-
function quiesceHostedAgents(agents) {
|
|
50235
|
-
if (agents.some((agent) => agent.core.activeTurnCount() > 0))
|
|
50348
|
+
function quiesceHostedAgents(agents, deadlineReached = false) {
|
|
50349
|
+
if (!deadlineReached && agents.some((agent) => agent.core.activeTurnCount() > 0))
|
|
50236
50350
|
return false;
|
|
50237
50351
|
if (agents.some((agent) => !agent.lifecycle.serving && !(agent.lifecycle.state.kind === "quiescing" && agent.lifecycle.state.reason === "update")))
|
|
50238
50352
|
return false;
|