open-walnut 0.6.11 → 0.6.12
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/build-info.json +3 -3
- package/dist/cli-fast.js +4 -4
- package/dist/cli.js +52905 -52312
- package/dist/daemon-binaries/acp-daemon-core.cjs +9 -9
- package/dist/daemon-binaries/acp-worker.js +7 -7
- package/dist/daemon-binaries/acp-worker.js.version +1 -1
- package/dist/daemon-binaries/dtach-linux-x64 +0 -0
- package/dist/data/skills/walnut-rich-output/SKILL.md +2 -0
- package/dist/integrations/mail/index.js +2 -2
- package/dist/web/server.js +651 -58
- package/dist/web/static/assets/{OfficePreview-DBB8usOU.js → OfficePreview-BRP7aPKQ.js} +2 -2
- package/dist/web/static/assets/{docx-preview-DO-M-r7C.js → docx-preview-gD4iKJ2e.js} +1 -1
- package/dist/web/static/assets/{index-CDsfHTKk.js → index-B6JtEMq9.js} +1 -1
- package/dist/web/static/assets/{index-BH5SrTkh.js → index-B9wMLqkB.js} +1 -1
- package/dist/web/static/assets/{index-DrdLtoY7.js → index-BRHuQKnl.js} +1 -1
- package/dist/web/static/assets/{index-CJ8rBjS7.js → index-BVcrg2AK.js} +1 -1
- package/dist/web/static/assets/{index-Cf1FGt6z.js → index-CYk1zlE1.js} +1 -1
- package/dist/web/static/assets/{index-CwQ1J8zo.js → index-Cn8ACC-j.js} +423 -415
- package/dist/web/static/assets/{index-BiuGmgtx.js → index-CrRDEOok.js} +1 -1
- package/dist/web/static/assets/{index-CDJk2V31.js → index-D3pjiTcv.js} +1 -1
- package/dist/web/static/assets/{index-geB69Fz-.css → index-DEgRybbe.css} +1 -1
- package/dist/web/static/assets/{index-B0owO7hE.js → index-DZ_5YKaM.js} +1 -1
- package/dist/web/static/assets/{index-v_Hru-Ce.js → index-Dd57HhSU.js} +1 -1
- package/dist/web/static/assets/{index-ShZZkmTY.js → index-Din30GxM.js} +1 -1
- package/dist/web/static/assets/{index-Cr-efJVK.js → index-DxakwTTa.js} +1 -1
- package/dist/web/static/assets/{index-CCA160yl.js → index-DyOtvfYi.js} +1 -1
- package/dist/web/static/assets/{index-DfAlsi9y.js → index-QQY1_2Fb.js} +1 -1
- package/dist/web/static/assets/{index-2Y51Cotd.js → index-lCNb4QzD.js} +1 -1
- package/dist/web/static/assets/{index-D22ZkaJd.js → index-rSSibhKo.js} +1 -1
- package/dist/web/static/assets/{index-htULZnWT.js → index-tKpaCzaP.js} +1 -1
- package/dist/web/static/assets/{index-Cfz0y2sX.js → index-weeQuzTe.js} +1 -1
- package/dist/web/static/assets/{jszip.min-Cu9P_1RA.js → jszip.min-CSpmuVKF.js} +1 -1
- package/dist/web/static/assets/{pptx-preview.es-O80sMMc7.js → pptx-preview.es-5Knb-k-O.js} +1 -1
- package/dist/web/static/index.html +2 -2
- package/package.json +1 -1
package/dist/web/server.js
CHANGED
|
@@ -6020,6 +6020,7 @@ __export(git_sync_exports, {
|
|
|
6020
6020
|
isRepoAsync: () => isRepoAsync,
|
|
6021
6021
|
isSyncPaused: () => isSyncPaused,
|
|
6022
6022
|
localContainsRemoteTipForTest: () => localContainsRemoteTipForTest,
|
|
6023
|
+
measureRepoSize: () => measureRepoSize,
|
|
6023
6024
|
noteNetworkFailure: () => noteNetworkFailure,
|
|
6024
6025
|
noteNetworkSuccess: () => noteNetworkSuccess,
|
|
6025
6026
|
parseCloudRemoteUrl: () => parseCloudRemoteUrl,
|
|
@@ -6032,6 +6033,7 @@ __export(git_sync_exports, {
|
|
|
6032
6033
|
setCompactionInProgress: () => setCompactionInProgress,
|
|
6033
6034
|
setDiskPullOnly: () => setDiskPullOnly,
|
|
6034
6035
|
setRemote: () => setRemote,
|
|
6036
|
+
setRewriteAdoptedListener: () => setRewriteAdoptedListener,
|
|
6035
6037
|
stageAllArgs: () => stageAllArgs,
|
|
6036
6038
|
stageAllForCommit: () => stageAllForCommit,
|
|
6037
6039
|
sweepDeadFetchPacks: () => sweepDeadFetchPacks,
|
|
@@ -7080,6 +7082,10 @@ async function lwwMerge(branch, remoteBefore = null) {
|
|
|
7080
7082
|
reason: noMergeBase ? "no-merge-base" : "upstream-force-moved",
|
|
7081
7083
|
localOnlyCommitsParked: localOnly
|
|
7082
7084
|
});
|
|
7085
|
+
try {
|
|
7086
|
+
rewriteAdoptedListener?.();
|
|
7087
|
+
} catch {
|
|
7088
|
+
}
|
|
7083
7089
|
return { merged: true, conflicts: 0 };
|
|
7084
7090
|
}
|
|
7085
7091
|
let mergeError = null;
|
|
@@ -7311,6 +7317,9 @@ function checkRepoSize(repoDir = WALNUT_HOME) {
|
|
|
7311
7317
|
const now = Date.now();
|
|
7312
7318
|
if (now - lastRepoSizeCheck < REPO_SIZE_CHECK_INTERVAL_MS) return null;
|
|
7313
7319
|
lastRepoSizeCheck = now;
|
|
7320
|
+
return measureRepoSize(repoDir, now);
|
|
7321
|
+
}
|
|
7322
|
+
function measureRepoSize(repoDir = WALNUT_HOME, now = Date.now()) {
|
|
7314
7323
|
const before = measurePackDir(repoDir);
|
|
7315
7324
|
let sweptBytes = 0;
|
|
7316
7325
|
let debrisBytes = before.debrisBytes;
|
|
@@ -7325,6 +7334,9 @@ function checkRepoSize(repoDir = WALNUT_HOME) {
|
|
|
7325
7334
|
const warning2 = `data repo .git has grown to ${gbText(liveBytes)} of live packs (threshold 3GB)${debrisNote}: check for backup-* branches pinning old history, large tracked files, or tmp_pack_* debris; see open-walnut logs -s git`;
|
|
7326
7335
|
return { liveBytes, debrisBytes, sweptBytes, over, warning: warning2 };
|
|
7327
7336
|
}
|
|
7337
|
+
function setRewriteAdoptedListener(fn) {
|
|
7338
|
+
rewriteAdoptedListener = fn;
|
|
7339
|
+
}
|
|
7328
7340
|
function resetRepoSizeCheckForTest() {
|
|
7329
7341
|
lastRepoSizeCheck = 0;
|
|
7330
7342
|
}
|
|
@@ -7466,7 +7478,7 @@ function getSyncStatus() {
|
|
|
7466
7478
|
diskPullOnly
|
|
7467
7479
|
};
|
|
7468
7480
|
}
|
|
7469
|
-
var LOCAL_TIMEOUT, NETWORK_TIMEOUT, PULL_TIMEOUT, FETCH_TIMEOUT, FETCH_STREAK_FOR_RECOVERY, FETCH_RECOVERY_TIMEOUT, FETCH_DEBRIS_MIN_AGE_MS, KILL_GRACE_MS, compactionInProgress, SYNC_PAUSE_MARKER, lastPauseLogAt, NETWORK_GIT_RE, execAsync, credentialGuardCache, SYNC_PROBE_TTL_MS, gitAvailableProbe, repoProbe, remoteProbe, GITIGNORE_CONTENT, CRITICAL_IGNORE_FILES, CRITICAL_IGNORE_DIRS, CRITICAL_IGNORE_LINES, CRITICAL_IGNORES, EXTRA_IGNORE_PATTERNS, CLOUD_NEVER_STAGE_DIRS, MASS_DIRTY_THRESHOLD, MASS_DIRTY_TRACKED_FRACTION, TRACKED_COUNT_TTL_MS, trackedCountCache, FAILURE_STREAK_FOR_PULL_FIRST, RESURRECTION_TRIP_COUNT, RESURRECTION_LOG_DEPTH, guard, SAFE_MODE_RELOG_MS, lastSafeModeRefusalLog, diskPullOnly, PUSH_FAILURES_FOR_BUNDLE, pushFailureStreak, localContainsRemoteTipForTest, ORPHAN_SURGERY_MIN_AGE_MS, SURGERY_RELOG_MS, lastSurgeryWarnLog, MAX_MARKER_SCAN_FILES, syncInflight, pullInflight, REPO_SIZE_WARN_BYTES, REPO_SIZE_CHECK_INTERVAL_MS, REPO_SIZE_RECOVERY_KEY, lastRepoSizeCheck, gbText, lastSyncCache, LAST_SYNC_CACHE_MS;
|
|
7481
|
+
var LOCAL_TIMEOUT, NETWORK_TIMEOUT, PULL_TIMEOUT, FETCH_TIMEOUT, FETCH_STREAK_FOR_RECOVERY, FETCH_RECOVERY_TIMEOUT, FETCH_DEBRIS_MIN_AGE_MS, KILL_GRACE_MS, compactionInProgress, SYNC_PAUSE_MARKER, lastPauseLogAt, NETWORK_GIT_RE, execAsync, credentialGuardCache, SYNC_PROBE_TTL_MS, gitAvailableProbe, repoProbe, remoteProbe, GITIGNORE_CONTENT, CRITICAL_IGNORE_FILES, CRITICAL_IGNORE_DIRS, CRITICAL_IGNORE_LINES, CRITICAL_IGNORES, EXTRA_IGNORE_PATTERNS, CLOUD_NEVER_STAGE_DIRS, MASS_DIRTY_THRESHOLD, MASS_DIRTY_TRACKED_FRACTION, TRACKED_COUNT_TTL_MS, trackedCountCache, FAILURE_STREAK_FOR_PULL_FIRST, RESURRECTION_TRIP_COUNT, RESURRECTION_LOG_DEPTH, guard, SAFE_MODE_RELOG_MS, lastSafeModeRefusalLog, diskPullOnly, PUSH_FAILURES_FOR_BUNDLE, pushFailureStreak, localContainsRemoteTipForTest, ORPHAN_SURGERY_MIN_AGE_MS, SURGERY_RELOG_MS, lastSurgeryWarnLog, MAX_MARKER_SCAN_FILES, syncInflight, pullInflight, REPO_SIZE_WARN_BYTES, REPO_SIZE_CHECK_INTERVAL_MS, REPO_SIZE_RECOVERY_KEY, lastRepoSizeCheck, gbText, rewriteAdoptedListener, lastSyncCache, LAST_SYNC_CACHE_MS;
|
|
7470
7482
|
var init_git_sync = __esm({
|
|
7471
7483
|
"src/integrations/git-sync.ts"() {
|
|
7472
7484
|
"use strict";
|
|
@@ -7757,6 +7769,7 @@ sessions.json.*
|
|
|
7757
7769
|
REPO_SIZE_RECOVERY_KEY = "git:repo-size";
|
|
7758
7770
|
lastRepoSizeCheck = 0;
|
|
7759
7771
|
gbText = (bytes3) => `${(bytes3 / 1024 / 1024 / 1024).toFixed(1)}GB`;
|
|
7772
|
+
rewriteAdoptedListener = null;
|
|
7760
7773
|
lastSyncCache = null;
|
|
7761
7774
|
LAST_SYNC_CACHE_MS = 3e4;
|
|
7762
7775
|
}
|
|
@@ -9204,6 +9217,13 @@ function ensureSilenceSweep() {
|
|
|
9204
9217
|
silenceSweepTimer = setInterval(() => {
|
|
9205
9218
|
const now = Date.now();
|
|
9206
9219
|
for (const conn of bridges.values()) {
|
|
9220
|
+
if (conn.stillValid && !conn.validating) {
|
|
9221
|
+
conn.validating = true;
|
|
9222
|
+
void credentialStillValid(conn.stillValid).then((valid) => {
|
|
9223
|
+
conn.validating = false;
|
|
9224
|
+
if (!valid && bridges.get(conn.hostAlias) === conn) revokeBridge(conn);
|
|
9225
|
+
});
|
|
9226
|
+
}
|
|
9207
9227
|
if (now - conn.lastInbound > SILENCE_MS) {
|
|
9208
9228
|
log.ws.warn("bridge: host silent \u2014 dropping", {
|
|
9209
9229
|
hostAlias: conn.hostAlias,
|
|
@@ -9301,6 +9321,10 @@ function forwardJsonlLine(sessionId, line2) {
|
|
|
9301
9321
|
}
|
|
9302
9322
|
}
|
|
9303
9323
|
function handleFrame(conn, raw) {
|
|
9324
|
+
if (conn.scope === "status-only" && Buffer.byteLength(raw) > 65536) {
|
|
9325
|
+
revokeBridge(conn);
|
|
9326
|
+
return;
|
|
9327
|
+
}
|
|
9304
9328
|
conn.lastInbound = Date.now();
|
|
9305
9329
|
let msg;
|
|
9306
9330
|
try {
|
|
@@ -9308,6 +9332,7 @@ function handleFrame(conn, raw) {
|
|
|
9308
9332
|
} catch {
|
|
9309
9333
|
return;
|
|
9310
9334
|
}
|
|
9335
|
+
if (!msg || typeof msg !== "object" || Array.isArray(msg) || msg.ev !== void 0 && typeof msg.ev !== "string") return;
|
|
9311
9336
|
if (conn.hostAlias === PRIMARY_ALIAS && primaryBridgeActivityHandlers.size && Date.now() - primaryBridgeActivityAt >= 15e3) {
|
|
9312
9337
|
primaryBridgeActivityAt = Date.now();
|
|
9313
9338
|
for (const handler of primaryBridgeActivityHandlers) {
|
|
@@ -9318,6 +9343,10 @@ function handleFrame(conn, raw) {
|
|
|
9318
9343
|
}
|
|
9319
9344
|
}
|
|
9320
9345
|
}
|
|
9346
|
+
if (conn.scope === "status-only" && msg.ev && msg.ev !== "bridge-ping") {
|
|
9347
|
+
revokeBridge(conn);
|
|
9348
|
+
return;
|
|
9349
|
+
}
|
|
9321
9350
|
if (msg.ev === "chunk") {
|
|
9322
9351
|
const whole = conn.chunks.accept(msg);
|
|
9323
9352
|
if (whole !== null) handleFrame(conn, whole);
|
|
@@ -9408,7 +9437,57 @@ function addBridgeConnectedHandler(handler) {
|
|
|
9408
9437
|
hostBridgeConnectedHandlers.add(handler);
|
|
9409
9438
|
return () => hostBridgeConnectedHandlers.delete(handler);
|
|
9410
9439
|
}
|
|
9440
|
+
async function credentialStillValid(check) {
|
|
9441
|
+
let timer10;
|
|
9442
|
+
try {
|
|
9443
|
+
return await Promise.race([
|
|
9444
|
+
check(),
|
|
9445
|
+
new Promise((resolve) => {
|
|
9446
|
+
timer10 = setTimeout(() => resolve(false), 5e3);
|
|
9447
|
+
})
|
|
9448
|
+
]);
|
|
9449
|
+
} catch {
|
|
9450
|
+
return false;
|
|
9451
|
+
} finally {
|
|
9452
|
+
if (timer10) clearTimeout(timer10);
|
|
9453
|
+
}
|
|
9454
|
+
}
|
|
9455
|
+
function revokeBridge(conn) {
|
|
9456
|
+
conn.stats.initiator = "none";
|
|
9457
|
+
dropBridge(conn, "credential revoked");
|
|
9458
|
+
try {
|
|
9459
|
+
conn.ws.close(CLOSE_UNAUTHORIZED, "credential revoked");
|
|
9460
|
+
} catch {
|
|
9461
|
+
}
|
|
9462
|
+
}
|
|
9463
|
+
function watchBridgeRevocations() {
|
|
9464
|
+
stopRevokeWatch ??= onCredentialsRevoked((names2) => {
|
|
9465
|
+
const affected = [
|
|
9466
|
+
...[...preHello].filter(([, waiting3]) => names2.includes(waiting3.deviceName)).map(([ws, waiting3]) => ({ ws, check: waiting3.stillValid })),
|
|
9467
|
+
...[...bridges.values()].filter((conn) => names2.includes(conn.deviceName)).map((conn) => ({ ws: conn.ws, check: conn.stillValid }))
|
|
9468
|
+
];
|
|
9469
|
+
for (const { ws, check } of affected) {
|
|
9470
|
+
const closeRevoked = (valid) => {
|
|
9471
|
+
if (valid) return;
|
|
9472
|
+
const waiting3 = preHello.get(ws);
|
|
9473
|
+
if (waiting3) {
|
|
9474
|
+
clearTimeout(waiting3.timer);
|
|
9475
|
+
preHello.delete(ws);
|
|
9476
|
+
}
|
|
9477
|
+
const conn = findConn(ws);
|
|
9478
|
+
if (conn) revokeBridge(conn);
|
|
9479
|
+
else try {
|
|
9480
|
+
ws.close(CLOSE_UNAUTHORIZED, "credential revoked");
|
|
9481
|
+
} catch {
|
|
9482
|
+
}
|
|
9483
|
+
};
|
|
9484
|
+
if (check) void credentialStillValid(check).then(closeRevoked);
|
|
9485
|
+
else closeRevoked(false);
|
|
9486
|
+
}
|
|
9487
|
+
});
|
|
9488
|
+
}
|
|
9411
9489
|
function attachBridge(ws, deviceName, meta) {
|
|
9490
|
+
watchBridgeRevocations();
|
|
9412
9491
|
ensureSilenceSweep();
|
|
9413
9492
|
const stats3 = newBridgeWireStats(meta?.clientIp ?? null);
|
|
9414
9493
|
const helloTimer = setTimeout(() => {
|
|
@@ -9419,22 +9498,67 @@ function attachBridge(ws, deviceName, meta) {
|
|
|
9419
9498
|
} catch {
|
|
9420
9499
|
}
|
|
9421
9500
|
}, 1e4);
|
|
9422
|
-
preHello.set(ws, { deviceName, timer: helloTimer, stats: stats3 });
|
|
9501
|
+
preHello.set(ws, { deviceName, order: ++bridgeOrder, timer: helloTimer, stats: stats3, stillValid: meta?.stillValid, scope: meta?.scope, queued: [], queuedBytes: 0 });
|
|
9423
9502
|
ws.on("message", (data) => {
|
|
9424
9503
|
const raw = data.toString();
|
|
9425
9504
|
noteFrameIn(stats3, Buffer.isBuffer(data) ? data.length : Buffer.byteLength(raw));
|
|
9426
9505
|
const waiting3 = preHello.get(ws);
|
|
9427
9506
|
if (waiting3) {
|
|
9507
|
+
if (waiting3.scope === "status-only" && Buffer.byteLength(raw) > 65536) {
|
|
9508
|
+
clearTimeout(waiting3.timer);
|
|
9509
|
+
preHello.delete(ws);
|
|
9510
|
+
try {
|
|
9511
|
+
ws.close(1009, "status frame too large");
|
|
9512
|
+
} catch {
|
|
9513
|
+
}
|
|
9514
|
+
return;
|
|
9515
|
+
}
|
|
9516
|
+
if (waiting3.verifying) {
|
|
9517
|
+
waiting3.queuedBytes += Buffer.byteLength(raw);
|
|
9518
|
+
if (waiting3.queued.length >= 64 || waiting3.queuedBytes > 1048576) {
|
|
9519
|
+
clearTimeout(waiting3.timer);
|
|
9520
|
+
preHello.delete(ws);
|
|
9521
|
+
try {
|
|
9522
|
+
ws.close(1009, "hello buffer full");
|
|
9523
|
+
} catch {
|
|
9524
|
+
}
|
|
9525
|
+
} else {
|
|
9526
|
+
waiting3.queued.push(raw);
|
|
9527
|
+
}
|
|
9528
|
+
return;
|
|
9529
|
+
}
|
|
9428
9530
|
let msg;
|
|
9429
9531
|
try {
|
|
9430
9532
|
msg = JSON.parse(raw);
|
|
9431
9533
|
} catch {
|
|
9432
9534
|
return;
|
|
9433
9535
|
}
|
|
9536
|
+
if (!msg || typeof msg !== "object" || Array.isArray(msg)) return;
|
|
9434
9537
|
if (msg.ev !== "hello" || typeof msg.hostAlias !== "string") return;
|
|
9435
|
-
|
|
9436
|
-
|
|
9437
|
-
|
|
9538
|
+
const finishHello = (valid) => {
|
|
9539
|
+
if (preHello.get(ws) !== waiting3) return;
|
|
9540
|
+
clearTimeout(waiting3.timer);
|
|
9541
|
+
preHello.delete(ws);
|
|
9542
|
+
if (!valid || ws.readyState !== void 0 && ws.readyState !== 1) {
|
|
9543
|
+
try {
|
|
9544
|
+
ws.close(CLOSE_UNAUTHORIZED, "credential revoked");
|
|
9545
|
+
} catch {
|
|
9546
|
+
}
|
|
9547
|
+
return;
|
|
9548
|
+
}
|
|
9549
|
+
registerBridge(ws, waiting3.deviceName, msg, waiting3.stats, waiting3.stillValid, waiting3.order, waiting3.scope);
|
|
9550
|
+
const conn2 = findConn(ws);
|
|
9551
|
+
if (conn2) for (const queued of waiting3.queued) {
|
|
9552
|
+
if (findConn(ws) !== conn2) break;
|
|
9553
|
+
handleFrame(conn2, queued);
|
|
9554
|
+
}
|
|
9555
|
+
};
|
|
9556
|
+
if (waiting3.stillValid) {
|
|
9557
|
+
waiting3.verifying = true;
|
|
9558
|
+
void credentialStillValid(waiting3.stillValid).then(finishHello);
|
|
9559
|
+
} else {
|
|
9560
|
+
finishHello(true);
|
|
9561
|
+
}
|
|
9438
9562
|
return;
|
|
9439
9563
|
}
|
|
9440
9564
|
const conn = findConn(ws);
|
|
@@ -9470,12 +9594,12 @@ function tokenBoundAlias(deviceName) {
|
|
|
9470
9594
|
const alias = deviceName.startsWith("bridge-") ? deviceName.slice("bridge-".length) : deviceName;
|
|
9471
9595
|
return alias === "local" ? "__local__" : alias;
|
|
9472
9596
|
}
|
|
9473
|
-
function registerBridge(ws, deviceName, hello, stats3) {
|
|
9597
|
+
function registerBridge(ws, deviceName, hello, stats3, stillValid, order, scope) {
|
|
9474
9598
|
const hostAlias2 = hello.hostAlias;
|
|
9475
9599
|
if (typeof hello.connId === "string") stats3.connId = hello.connId.slice(0, 80);
|
|
9476
9600
|
stats3.hostAlias = hostAlias2.slice(0, 120);
|
|
9477
9601
|
const boundAlias = tokenBoundAlias(deviceName);
|
|
9478
|
-
if (hostAlias2 !== boundAlias) {
|
|
9602
|
+
if (hostAlias2 !== boundAlias || scope === "status-only" && hostAlias2 === PRIMARY_ALIAS) {
|
|
9479
9603
|
log.ws.warn("bridge: hello hostAlias does not match token identity \u2014 rejecting", {
|
|
9480
9604
|
claimedHostAlias: hostAlias2,
|
|
9481
9605
|
tokenAlias: boundAlias,
|
|
@@ -9489,6 +9613,14 @@ function registerBridge(ws, deviceName, hello, stats3) {
|
|
|
9489
9613
|
return;
|
|
9490
9614
|
}
|
|
9491
9615
|
const existing = bridges.get(hostAlias2);
|
|
9616
|
+
if (existing && existing.order > order) {
|
|
9617
|
+
stats3.initiator = "replaced";
|
|
9618
|
+
try {
|
|
9619
|
+
ws.close(CLOSE_REPLACED, "superseded");
|
|
9620
|
+
} catch {
|
|
9621
|
+
}
|
|
9622
|
+
return;
|
|
9623
|
+
}
|
|
9492
9624
|
if (existing) {
|
|
9493
9625
|
log.ws.info("bridge: replacing existing connection", { hostAlias: hostAlias2 });
|
|
9494
9626
|
existing.stats.initiator = "replaced";
|
|
@@ -9502,6 +9634,9 @@ function registerBridge(ws, deviceName, hello, stats3) {
|
|
|
9502
9634
|
ws,
|
|
9503
9635
|
hostAlias: hostAlias2,
|
|
9504
9636
|
deviceName,
|
|
9637
|
+
order,
|
|
9638
|
+
scope,
|
|
9639
|
+
stillValid,
|
|
9505
9640
|
version: hello.version,
|
|
9506
9641
|
instanceId: hello.instanceId,
|
|
9507
9642
|
since: Date.now(),
|
|
@@ -9524,11 +9659,11 @@ function registerBridge(ws, deviceName, hello, stats3) {
|
|
|
9524
9659
|
sids: Array.isArray(hello.sids) ? hello.sids.length : 0,
|
|
9525
9660
|
connId: stats3.connId
|
|
9526
9661
|
});
|
|
9527
|
-
if (hello.uplink === 1) {
|
|
9662
|
+
if (scope !== "status-only" && hello.uplink === 1) {
|
|
9528
9663
|
void bridgeRequest(hostAlias2, "bridge.peer", { chunkBytes: REPLICA_CHUNK_BYTES }).catch(() => {
|
|
9529
9664
|
});
|
|
9530
9665
|
}
|
|
9531
|
-
void reattachInterestedSessions(conn);
|
|
9666
|
+
if (scope !== "status-only") void reattachInterestedSessions(conn);
|
|
9532
9667
|
if (hostAlias2 === PRIMARY_ALIAS) {
|
|
9533
9668
|
for (const handler of [primaryBridgeConnectedHandler, ...primaryBridgeConnectedHandlers]) {
|
|
9534
9669
|
if (!handler) continue;
|
|
@@ -9646,8 +9781,8 @@ function waitForBridge(hostAlias2, timeoutMs2) {
|
|
|
9646
9781
|
function lastBridgeLossAt(hostAlias2) {
|
|
9647
9782
|
return bridgeLossAt.get(hostAlias2) ?? null;
|
|
9648
9783
|
}
|
|
9649
|
-
function bridgeHosts() {
|
|
9650
|
-
return [...bridges.values()].map((c8) => ({
|
|
9784
|
+
function bridgeHosts(opts2 = {}) {
|
|
9785
|
+
return [...bridges.values()].filter((c8) => !opts2.executionOnly || c8.scope !== "status-only").map((c8) => ({
|
|
9651
9786
|
hostAlias: c8.hostAlias,
|
|
9652
9787
|
since: c8.since,
|
|
9653
9788
|
version: c8.version
|
|
@@ -9656,6 +9791,11 @@ function bridgeHosts() {
|
|
|
9656
9791
|
function bridgeRequest(hostAlias2, cmd, params = {}, timeoutMs2 = DEFAULT_TIMEOUT_MS) {
|
|
9657
9792
|
const conn = bridges.get(hostAlias2);
|
|
9658
9793
|
if (!conn) return Promise.reject(new BridgeOfflineError(hostAlias2));
|
|
9794
|
+
if (conn.scope === "status-only") {
|
|
9795
|
+
if (cmd !== "ping" && cmd !== "status") return Promise.reject(new Error("Status-only bridge refuses this command"));
|
|
9796
|
+
const allowed = cmd === "ping" ? ["nonce", "ackSeq"] : ["nonce"];
|
|
9797
|
+
if (Object.keys(params).some((key) => !allowed.includes(key))) return Promise.reject(new Error("Status-only bridge refuses these parameters"));
|
|
9798
|
+
}
|
|
9659
9799
|
const id = ++conn.cmdCounter;
|
|
9660
9800
|
const frame = JSON.stringify({ id, cmd, ...params });
|
|
9661
9801
|
return new Promise((resolve, reject) => {
|
|
@@ -9722,8 +9862,10 @@ function closeAllBridges() {
|
|
|
9722
9862
|
clearInterval(silenceSweepTimer);
|
|
9723
9863
|
silenceSweepTimer = null;
|
|
9724
9864
|
}
|
|
9865
|
+
stopRevokeWatch?.();
|
|
9866
|
+
stopRevokeWatch = null;
|
|
9725
9867
|
}
|
|
9726
|
-
var PRIMARY_ALIAS, CLOSE_REPLACED, CLOSE_UNAUTHORIZED, DEFAULT_TIMEOUT_MS, SILENCE_MS, SILENCE_SWEEP_MS, bridges, preHello, hostInterest, bridgeWaiters, bridgeLossAt, silenceSweepTimer, lastCliState, mobileEventHandler, gatewayRequestHandler, primaryBridgeConnectedHandler, primaryBridgeConnectedHandlers, primaryBridgeActivityHandlers, primaryBridgeActivityAt, hostBridgeConnectedHandlers, BridgeOfflineError, BridgeAttachRefusedError;
|
|
9868
|
+
var PRIMARY_ALIAS, CLOSE_REPLACED, CLOSE_UNAUTHORIZED, DEFAULT_TIMEOUT_MS, SILENCE_MS, SILENCE_SWEEP_MS, bridges, preHello, bridgeOrder, hostInterest, bridgeWaiters, bridgeLossAt, silenceSweepTimer, lastCliState, mobileEventHandler, gatewayRequestHandler, primaryBridgeConnectedHandler, primaryBridgeConnectedHandlers, primaryBridgeActivityHandlers, primaryBridgeActivityAt, hostBridgeConnectedHandlers, stopRevokeWatch, BridgeOfflineError, BridgeAttachRefusedError;
|
|
9727
9869
|
var init_bridge_registry = __esm({
|
|
9728
9870
|
"src/web/ws/bridge-registry.ts"() {
|
|
9729
9871
|
"use strict";
|
|
@@ -9735,6 +9877,7 @@ var init_bridge_registry = __esm({
|
|
|
9735
9877
|
init_bridge_read_history();
|
|
9736
9878
|
init_bridge_wire();
|
|
9737
9879
|
init_event_loop_monitor();
|
|
9880
|
+
init_device_auth();
|
|
9738
9881
|
init_bridge_presence();
|
|
9739
9882
|
PRIMARY_ALIAS = "__local__";
|
|
9740
9883
|
CLOSE_REPLACED = 4e3;
|
|
@@ -9744,6 +9887,7 @@ var init_bridge_registry = __esm({
|
|
|
9744
9887
|
SILENCE_SWEEP_MS = 3e4;
|
|
9745
9888
|
bridges = /* @__PURE__ */ new Map();
|
|
9746
9889
|
preHello = /* @__PURE__ */ new Map();
|
|
9890
|
+
bridgeOrder = 0;
|
|
9747
9891
|
hostInterest = /* @__PURE__ */ new Map();
|
|
9748
9892
|
bridgeWaiters = /* @__PURE__ */ new Map();
|
|
9749
9893
|
bridgeLossAt = /* @__PURE__ */ new Map();
|
|
@@ -9756,6 +9900,7 @@ var init_bridge_registry = __esm({
|
|
|
9756
9900
|
primaryBridgeActivityHandlers = /* @__PURE__ */ new Set();
|
|
9757
9901
|
primaryBridgeActivityAt = 0;
|
|
9758
9902
|
hostBridgeConnectedHandlers = /* @__PURE__ */ new Set();
|
|
9903
|
+
stopRevokeWatch = null;
|
|
9759
9904
|
BridgeOfflineError = class extends Error {
|
|
9760
9905
|
constructor(hostAlias2) {
|
|
9761
9906
|
super(`No live bridge for host: ${hostAlias2}`);
|
|
@@ -11584,7 +11729,7 @@ async function rotateDevice(name, opts2) {
|
|
|
11584
11729
|
if (plan && (plan.push.length > 0 || plan.twins.length > 0)) trackRevokeWork(finishRevoke(plan, `device ${name} rotated`));
|
|
11585
11730
|
return { name: out.name, token: out.token, createdAt: out.createdAt, replaced: out.replaced };
|
|
11586
11731
|
}
|
|
11587
|
-
async function verifyDeviceToken(token) {
|
|
11732
|
+
async function verifyDeviceToken(token, opts2 = {}) {
|
|
11588
11733
|
if (!token || typeof token !== "string") return null;
|
|
11589
11734
|
const { devices, revoked } = await readRegistryAndRevoked();
|
|
11590
11735
|
const matched = recordForToken(devices, token);
|
|
@@ -11595,7 +11740,7 @@ async function verifyDeviceToken(token) {
|
|
|
11595
11740
|
}
|
|
11596
11741
|
const now = Date.now();
|
|
11597
11742
|
const lastWrite = lastUsedWriteAt.get(matched.name) ?? 0;
|
|
11598
|
-
if (now - lastWrite >= LAST_USED_WRITE_THROTTLE_MS) {
|
|
11743
|
+
if (opts2.touch !== false && now - lastWrite >= LAST_USED_WRITE_THROTTLE_MS) {
|
|
11599
11744
|
lastUsedWriteAt.set(matched.name, now);
|
|
11600
11745
|
const name = matched.name;
|
|
11601
11746
|
try {
|
|
@@ -11612,7 +11757,7 @@ async function verifyDeviceToken(token) {
|
|
|
11612
11757
|
});
|
|
11613
11758
|
}
|
|
11614
11759
|
}
|
|
11615
|
-
return matched.kind ? { name: matched.name, kind: matched.kind } : { name: matched.name };
|
|
11760
|
+
return matched.kind ? { name: matched.name, kind: matched.kind, ...matched.bridgeScope === "status-only" ? { bridgeScope: matched.bridgeScope } : {} } : { name: matched.name };
|
|
11616
11761
|
}
|
|
11617
11762
|
async function revokeWith(name, opts2, decide) {
|
|
11618
11763
|
const intent = { plan: null };
|
|
@@ -11792,7 +11937,7 @@ function requestIp(req) {
|
|
|
11792
11937
|
}
|
|
11793
11938
|
async function validateBearerCredential(token) {
|
|
11794
11939
|
const device = await verifyDeviceToken(token);
|
|
11795
|
-
if (device) return { name: device.name, kind: device.kind === "machine" ? "machine" : "device" };
|
|
11940
|
+
if (device) return { name: device.name, kind: device.kind === "machine" ? "machine" : "device", ...device.bridgeScope ? { bridgeScope: device.bridgeScope } : {} };
|
|
11796
11941
|
const keyName = await validateApiKey(token);
|
|
11797
11942
|
if (keyName) return { name: keyName, kind: "api_key" };
|
|
11798
11943
|
return null;
|
|
@@ -25710,6 +25855,16 @@ async function resumeForTrigger(home, sid, text, messageId, taskId) {
|
|
|
25710
25855
|
if (!current()) throw new Error('a stop request superseded this start');
|
|
25711
25856
|
return startSessionProcess({ cmd: 'start', sid: sid, args: args, cwd: spawn.cwd, message: text, resume: true, mode: spawn.mode, origin: { home: home, task: taskId } }, undefined, current);
|
|
25712
25857
|
});
|
|
25858
|
+
// The turn-start marker the server writes after its own resume start
|
|
25859
|
+
// (writeSyntheticUserEvent): the CLI never echoes its stdin, so without it
|
|
25860
|
+
// the stream shows no turn start and the fire never appears in the chat.
|
|
25861
|
+
try {
|
|
25862
|
+
var started = sessions.get(sid);
|
|
25863
|
+
if (started) appendUserMarkerLine(sid, started, text, messageId, false);
|
|
25864
|
+
else logMsg('warn', 'trigger: no turn-start marker for a host resume', { sid: sid, messageId: messageId });
|
|
25865
|
+
} catch (err) {
|
|
25866
|
+
logMsg('warn', 'trigger: no turn-start marker for a host resume', { sid: sid, messageId: messageId, error: err && err.message });
|
|
25867
|
+
}
|
|
25713
25868
|
logMsg('info', 'trigger: resumed a stopped session on this host', { sid: sid, taskId: taskId, messageId: messageId, mode: spawn.mode });
|
|
25714
25869
|
return { ok: true };
|
|
25715
25870
|
} catch (err) {
|
|
@@ -37916,6 +38071,9 @@ function refuse2(d8) {
|
|
|
37916
38071
|
throw new MachineCredentialRefused(d8.status, d8.code, d8.error);
|
|
37917
38072
|
}
|
|
37918
38073
|
async function mintMachineCredential(name, opts2) {
|
|
38074
|
+
if (opts2.bridgeScope === "status-only" && name === PRIMARY_MACHINE_CREDENTIAL) {
|
|
38075
|
+
throw new MachineCredentialRefused(400, "invalid_bridge_scope", "The primary credential cannot be status-only");
|
|
38076
|
+
}
|
|
37919
38077
|
const out = await updateAuth((auth) => {
|
|
37920
38078
|
const decision = machineCredentialDecision(auth, { by: opts2.by, name, action: opts2.replace ? "replace" : "mint", proof: opts2.proof });
|
|
37921
38079
|
if (!decision.ok) refuse2(decision);
|
|
@@ -37924,6 +38082,8 @@ async function mintMachineCredential(name, opts2) {
|
|
|
37924
38082
|
const me = applyDecision(auth, decision);
|
|
37925
38083
|
if (!me.tunnelDaemon) me.tunnelDaemon = { key: me.id };
|
|
37926
38084
|
const { record: record2, token } = newMachineRecord(name, me.id, existing ? existing.daemonKey : daemonKeyFor(me));
|
|
38085
|
+
const bridgeScope = opts2.bridgeScope ?? existing?.bridgeScope;
|
|
38086
|
+
if (bridgeScope) record2.bridgeScope = bridgeScope;
|
|
37927
38087
|
auth.devices = [...auth.devices.filter((d8) => d8.name !== name), record2];
|
|
37928
38088
|
return { persist: true, result: { name, token, createdAt: record2.createdAt, replaced: existing !== void 0, owner: me.name, recorded: decision.record?.via ?? null } };
|
|
37929
38089
|
});
|
|
@@ -38002,7 +38162,7 @@ __export(daemon_tunnel_exports, {
|
|
|
38002
38162
|
import { WebSocket as WebSocket3, WebSocketServer } from "ws";
|
|
38003
38163
|
function tunnelAdmission(cred, execEnabled2) {
|
|
38004
38164
|
if (!cred) return { ok: false, status: 401, refusal: "unauthorized", message: "a machine credential is required" };
|
|
38005
|
-
if (cred.kind !== "machine" || cred.name !== PRIMARY_MACHINE_DEVICE) {
|
|
38165
|
+
if (cred.kind !== "machine" || cred.name !== PRIMARY_MACHINE_DEVICE || cred.bridgeScope === "status-only") {
|
|
38006
38166
|
return { ok: false, status: 403, refusal: "not_primary", message: "only the paired primary may open the daemon tunnel" };
|
|
38007
38167
|
}
|
|
38008
38168
|
if (!execEnabled2) {
|
|
@@ -38412,7 +38572,14 @@ async function verifyCloudUpgrade(url, request2) {
|
|
|
38412
38572
|
return null;
|
|
38413
38573
|
}
|
|
38414
38574
|
log.ws.info("cloud ws upgrade authenticated", { name: cred.name, kind: cred.kind });
|
|
38415
|
-
return
|
|
38575
|
+
return {
|
|
38576
|
+
...cred,
|
|
38577
|
+
stillValid: async () => {
|
|
38578
|
+
const { verifyDeviceToken: verifyDeviceToken2 } = await Promise.resolve().then(() => (init_device_auth(), device_auth_exports));
|
|
38579
|
+
const current8 = await verifyDeviceToken2(token, { touch: false });
|
|
38580
|
+
return current8?.name === cred.name && current8.kind === "machine" && current8.bridgeScope === cred.bridgeScope;
|
|
38581
|
+
}
|
|
38582
|
+
};
|
|
38416
38583
|
}
|
|
38417
38584
|
function ignoreSocketError() {
|
|
38418
38585
|
}
|
|
@@ -38464,7 +38631,7 @@ function attachWss(server) {
|
|
|
38464
38631
|
}
|
|
38465
38632
|
const clientIp = bridgeClientIp2(request2.headers["x-forwarded-for"], request2.socket.remoteAddress);
|
|
38466
38633
|
wss.handleUpgrade(request2, socket, head2, (ws) => {
|
|
38467
|
-
attachBridge2(ws, cred.name, { clientIp });
|
|
38634
|
+
attachBridge2(ws, cred.name, { clientIp, stillValid: cred.stillValid, scope: cred.bridgeScope });
|
|
38468
38635
|
});
|
|
38469
38636
|
}).catch(() => socket.destroy());
|
|
38470
38637
|
return;
|
|
@@ -92926,10 +93093,10 @@ var init_output_mode = __esm({
|
|
|
92926
93093
|
init_types();
|
|
92927
93094
|
init_voice_reply();
|
|
92928
93095
|
OUTPUT_MODE_INSTRUCTION_MARKER = "[Rich output mode: ";
|
|
92929
|
-
RICH_OUTPUT_MODE_ON_INSTRUCTION = `${OUTPUT_MODE_INSTRUCTION_MARKER}ON] Keep writing markdown for what markdown does well (headings, lists, tables, code blocks), and mix raw HTML into the same reply (separated by a blank line) for what markdown cannot express: colour that carries meaning, diagrams (inline <svg>), side-by-side or card layout, and interactivity (<details>, CSS :checked). The client renders it natively while streaming. Set text and background colour together or neither: a custom background with inherited or muted text loses contrast (theme vars --fg, --fg-muted, --bg-secondary, --border always fit). For anything needing <script>, emit a \`\`\`html-app fenced block (rendered in a sandboxed iframe). A short answer needs none of this. Component recipes (steppers, SVG diagrams, animations, islands): \`walnut tools call skill_read '{"dirName":"walnut-rich-output"}'\`.`;
|
|
93096
|
+
RICH_OUTPUT_MODE_ON_INSTRUCTION = `${OUTPUT_MODE_INSTRUCTION_MARKER}ON] Keep writing markdown for what markdown does well (headings, lists, tables, code blocks), and mix raw HTML into the same reply (separated by a blank line) for what markdown cannot express: colour that carries meaning, diagrams (inline <svg>), side-by-side or card layout, and interactivity (<details>, CSS :checked). The client renders it natively while streaming. Write visual HTML directly in your reply, not in a file, screenshot, or HTML code fence, unless the user asks for one. Keep each widget contiguous, without internal blank lines. Set text and background colour together or neither: a custom background with inherited or muted text loses contrast (theme vars --fg, --fg-muted, --bg-secondary, --border always fit). For anything needing <script>, emit a \`\`\`html-app fenced block (rendered in a sandboxed iframe). A short answer needs none of this. Component recipes (steppers, SVG diagrams, animations, islands): \`walnut tools call skill_read '{"dirName":"walnut-rich-output"}'\`.`;
|
|
92930
93097
|
RICH_OUTPUT_MODE_OFF_INSTRUCTION = `${OUTPUT_MODE_INSTRUCTION_MARKER}OFF] Return to plain markdown replies.`;
|
|
92931
93098
|
OUTPUT_MODE_REMINDER_MARKER = "[Rich output mode is still on";
|
|
92932
|
-
RICH_OUTPUT_MODE_REMINDER = `${OUTPUT_MODE_REMINDER_MARKER}
|
|
93099
|
+
RICH_OUTPUT_MODE_REMINDER = `${OUTPUT_MODE_REMINDER_MARKER}: markdown first; for colour, diagrams, or layout, write raw HTML directly in this reply. No files, screenshots, or HTML code fences unless requested (html-app for scripts); no blank lines inside a widget.]`;
|
|
92933
93100
|
MODEL_NATIVE_OUTPUT_MODE = "markdown";
|
|
92934
93101
|
}
|
|
92935
93102
|
});
|
|
@@ -105471,6 +105638,7 @@ async function applySnapshot(sessionId, snapshot4, source2, canRecoverConnection
|
|
|
105471
105638
|
}
|
|
105472
105639
|
return { outcome: "shadow", diverged, projected };
|
|
105473
105640
|
}
|
|
105641
|
+
if (sameTurn()) sessionRunner2.findSessionByClaudeId(sessionId)?.setForegroundFromSnapshot?.(snapshot4, epochChanged);
|
|
105474
105642
|
let reconnectActivityCleared = false;
|
|
105475
105643
|
if (!recoveringConnection && record2.activity === "Reconnecting to remote host...") {
|
|
105476
105644
|
const cleared = await updateSessionRecordConditionally3(
|
|
@@ -107149,14 +107317,14 @@ async function terminateSession(sessionId, opts2 = {}) {
|
|
|
107149
107317
|
if (acpLive) {
|
|
107150
107318
|
log.session.info("session terminate: stopping ACP session", { sessionId });
|
|
107151
107319
|
await acpLive.gracefulStop();
|
|
107152
|
-
await updateSessionRecord2(sessionId, {
|
|
107320
|
+
const stopped2 = await updateSessionRecord2(sessionId, {
|
|
107153
107321
|
process_status: "stopped",
|
|
107154
107322
|
activity: void 0,
|
|
107155
107323
|
last_status_change: (/* @__PURE__ */ new Date()).toISOString(),
|
|
107156
107324
|
status_reason: "user_stopped",
|
|
107157
107325
|
status_changed_by: "user"
|
|
107158
|
-
}).catch(() =>
|
|
107159
|
-
});
|
|
107326
|
+
}).catch(() => null);
|
|
107327
|
+
if (stopped2) emitSessionStatusChanged2(stopped2, {}, ["*"], { source: "terminate" });
|
|
107160
107328
|
return { status: "terminated", sessionId, tookMs: Date.now() - startedAt };
|
|
107161
107329
|
}
|
|
107162
107330
|
if (!isAcpEngine(record2.engine)) {
|
|
@@ -119372,6 +119540,28 @@ function probeAllOverride(env3) {
|
|
|
119372
119540
|
function enginePathOverrideVar(engineId) {
|
|
119373
119541
|
return `WALNUT_${engineId.toUpperCase()}_PATH`;
|
|
119374
119542
|
}
|
|
119543
|
+
function resolveOverridePath(override2, cwd = process.cwd()) {
|
|
119544
|
+
const literal = path83.resolve(cwd, override2);
|
|
119545
|
+
const segments2 = literal.split(path83.sep);
|
|
119546
|
+
const starAt = segments2.findIndex((segment) => segment.includes("*"));
|
|
119547
|
+
if (starAt <= 0 || segments2.slice(starAt + 1).some((segment) => segment.includes("*"))) return literal;
|
|
119548
|
+
const parent = segments2.slice(0, starAt).join(path83.sep) || path83.sep;
|
|
119549
|
+
const [head2, tail4, ...more] = segments2[starAt].split("*");
|
|
119550
|
+
if (more.length > 0) return literal;
|
|
119551
|
+
let names2;
|
|
119552
|
+
try {
|
|
119553
|
+
names2 = fs56.readdirSync(parent);
|
|
119554
|
+
} catch {
|
|
119555
|
+
return literal;
|
|
119556
|
+
}
|
|
119557
|
+
const rest = segments2.slice(starAt + 1);
|
|
119558
|
+
const matches = names2.filter((name) => name.length >= head2.length + tail4.length && name.startsWith(head2) && name.endsWith(tail4)).sort((a8, b8) => b8.localeCompare(a8, void 0, { numeric: true }));
|
|
119559
|
+
for (const name of matches) {
|
|
119560
|
+
const candidate = path83.join(parent, name, ...rest);
|
|
119561
|
+
if (inspectExecutable(candidate).executable) return candidate;
|
|
119562
|
+
}
|
|
119563
|
+
return literal;
|
|
119564
|
+
}
|
|
119375
119565
|
function findEngineBinary(binaryName, options = {}) {
|
|
119376
119566
|
const env3 = options.env ?? process.env;
|
|
119377
119567
|
const cwd = options.cwd ?? process.cwd();
|
|
@@ -119511,7 +119701,7 @@ async function runProbe(caps, options) {
|
|
|
119511
119701
|
const override2 = env3[overrideVar];
|
|
119512
119702
|
let binary;
|
|
119513
119703
|
if (override2) {
|
|
119514
|
-
const verdict = inspectExecutable(
|
|
119704
|
+
const verdict = inspectExecutable(resolveOverridePath(override2, options.cwd ?? process.cwd()));
|
|
119515
119705
|
if (!verdict.executable) return { availability: unavailable(overrideReason(overrideVar, override2, verdict.rejection)) };
|
|
119516
119706
|
binary = verdict.executable;
|
|
119517
119707
|
} else {
|
|
@@ -127867,7 +128057,7 @@ var PRICING_VERSION, DEFAULT_PRICING;
|
|
|
127867
128057
|
var init_pricing = __esm({
|
|
127868
128058
|
"src/core/usage/pricing.ts"() {
|
|
127869
128059
|
"use strict";
|
|
127870
|
-
PRICING_VERSION = "2026-10-
|
|
128060
|
+
PRICING_VERSION = "2026-10-12";
|
|
127871
128061
|
DEFAULT_PRICING = [
|
|
127872
128062
|
// ── Claude models (Anthropic/Bedrock on-demand) ──
|
|
127873
128063
|
// Opus 5.5 (2026-09-22 list price). Must sit above 'claude-opus-5', which is a
|
|
@@ -127895,6 +128085,16 @@ var init_pricing = __esm({
|
|
|
127895
128085
|
{ pattern: "claude-haiku-4-5", input: 1, output: 5, cacheWrite: 1.25, cacheRead: 0.1 },
|
|
127896
128086
|
{ pattern: "claude-3-5-haiku", input: 0.8, output: 4, cacheWrite: 1, cacheRead: 0.08 },
|
|
127897
128087
|
{ pattern: "claude-3-haiku", input: 0.25, output: 1.25, cacheWrite: 0.3, cacheRead: 0.03 },
|
|
128088
|
+
// ── OpenAI GPT on Amazon Bedrock (Global CRIS) ──
|
|
128089
|
+
// GPT-6.1 Sol and GPT-6 Astra have a short and a long tier split at 272K
|
|
128090
|
+
// input tokens; these are the long tier, so an estimate errs high. Specific
|
|
128091
|
+
// ids sit above the shorter ones they contain ('gpt-6-sol' is not in
|
|
128092
|
+
// 'gpt-6.1-sol', but 'gpt-6' is in every GPT-6 id).
|
|
128093
|
+
{ pattern: "gpt-6.1-sol", input: 4, output: 15, cacheRead: 0.2 },
|
|
128094
|
+
{ pattern: "gpt-6-sol", input: 2, output: 10, cacheRead: 0.2 },
|
|
128095
|
+
{ pattern: "gpt-6-luna", input: 0.1, output: 0.5, cacheRead: 0.01 },
|
|
128096
|
+
{ pattern: "gpt-6-astra", input: 20, output: 75, cacheRead: 2 },
|
|
128097
|
+
{ pattern: "gpt-5.6", input: 8.8, output: 33, cacheRead: 0.88 },
|
|
127898
128098
|
// ── Zhipu GLM (docs.z.ai international USD pricing) ──
|
|
127899
128099
|
{ pattern: "glm-4.7-flashx", input: 0.07, output: 0.4, cacheRead: 0.01 },
|
|
127900
128100
|
{ pattern: "glm-4.7", input: 0.6, output: 2.2, cacheRead: 0.11 },
|
|
@@ -143658,8 +143858,8 @@ import path106 from "path";
|
|
|
143658
143858
|
import { fileURLToPath as fileURLToPath8 } from "url";
|
|
143659
143859
|
function getVersion() {
|
|
143660
143860
|
if (cachedVersion) return cachedVersion;
|
|
143661
|
-
if ("0.6.
|
|
143662
|
-
cachedVersion = "0.6.
|
|
143861
|
+
if ("0.6.12") {
|
|
143862
|
+
cachedVersion = "0.6.12";
|
|
143663
143863
|
versionKnown = true;
|
|
143664
143864
|
return cachedVersion;
|
|
143665
143865
|
}
|
|
@@ -168265,7 +168465,7 @@ async function getBackupLeader() {
|
|
|
168265
168465
|
instance3 = createBackupLeader({
|
|
168266
168466
|
now: () => Date.now(),
|
|
168267
168467
|
takeoverMs: takeoverMsFromEnv(),
|
|
168268
|
-
hosts: () => registry3.bridgeHosts().map((h8) => h8.hostAlias),
|
|
168468
|
+
hosts: () => registry3.bridgeHosts({ executionOnly: true }).map((h8) => h8.hostAlias),
|
|
168269
168469
|
request: (host, cmd, params, timeoutMs2) => registry3.bridgeRequest(host, cmd, params, timeoutMs2),
|
|
168270
168470
|
stateFile: path131.join(LEADER_STATE_DIR2, "companion.json")
|
|
168271
168471
|
});
|
|
@@ -199029,7 +199229,14 @@ function normalizeProjectedEvent(event) {
|
|
|
199029
199229
|
case "usage":
|
|
199030
199230
|
return [{
|
|
199031
199231
|
name: "session:usage-update",
|
|
199032
|
-
|
|
199232
|
+
// Spoken in the Claude event's vocabulary too, so the composer's
|
|
199233
|
+
// context badge reads an ACP session the same way.
|
|
199234
|
+
payload: {
|
|
199235
|
+
used: event.used,
|
|
199236
|
+
size: event.size,
|
|
199237
|
+
inputTokens: event.used,
|
|
199238
|
+
...event.size > 0 ? { contextWindow: event.size, contextPercent: Math.round(event.used / event.size * 100) } : {}
|
|
199239
|
+
}
|
|
199033
199240
|
}];
|
|
199034
199241
|
case "system":
|
|
199035
199242
|
return [{
|
|
@@ -199084,6 +199291,154 @@ var init_acp_stream_normalizer = __esm({
|
|
|
199084
199291
|
}
|
|
199085
199292
|
});
|
|
199086
199293
|
|
|
199294
|
+
// src/core/sessions/acp-turn-speed.ts
|
|
199295
|
+
var AcpTurnSpeedMeter;
|
|
199296
|
+
var init_acp_turn_speed = __esm({
|
|
199297
|
+
"src/core/sessions/acp-turn-speed.ts"() {
|
|
199298
|
+
"use strict";
|
|
199299
|
+
init_pricing();
|
|
199300
|
+
AcpTurnSpeedMeter = class {
|
|
199301
|
+
startedAt;
|
|
199302
|
+
firstChunkAt;
|
|
199303
|
+
model;
|
|
199304
|
+
open;
|
|
199305
|
+
/** Start of the call that was open last (the counted one at turn end). */
|
|
199306
|
+
lastCallStartedAt;
|
|
199307
|
+
lastCallFirstChunkAt;
|
|
199308
|
+
lastCallEndedAt;
|
|
199309
|
+
pendingTools = /* @__PURE__ */ new Set();
|
|
199310
|
+
calls = 0;
|
|
199311
|
+
usageUpdates = 0;
|
|
199312
|
+
partial = false;
|
|
199313
|
+
seenAnything = false;
|
|
199314
|
+
finalSnapshot;
|
|
199315
|
+
get active() {
|
|
199316
|
+
return this.finalSnapshot === void 0 && (this.startedAt !== void 0 || this.seenAnything);
|
|
199317
|
+
}
|
|
199318
|
+
start(ts, model) {
|
|
199319
|
+
this.startedAt = ts;
|
|
199320
|
+
this.firstChunkAt = void 0;
|
|
199321
|
+
this.model = model;
|
|
199322
|
+
this.openCall(ts);
|
|
199323
|
+
this.pendingTools.clear();
|
|
199324
|
+
this.calls = 0;
|
|
199325
|
+
this.usageUpdates = 0;
|
|
199326
|
+
this.partial = false;
|
|
199327
|
+
this.seenAnything = true;
|
|
199328
|
+
this.finalSnapshot = void 0;
|
|
199329
|
+
}
|
|
199330
|
+
/** Text or thought characters streamed by the model. */
|
|
199331
|
+
chunk(ts, chars2) {
|
|
199332
|
+
if (this.finalSnapshot || !(chars2 > 0)) return;
|
|
199333
|
+
this.attachIfNeeded(ts);
|
|
199334
|
+
if (this.firstChunkAt === void 0) this.firstChunkAt = ts;
|
|
199335
|
+
if (!this.open) this.openCall(ts);
|
|
199336
|
+
const open3 = this.open;
|
|
199337
|
+
if (open3.firstChunkAt === void 0) open3.firstChunkAt = ts;
|
|
199338
|
+
open3.chars += chars2;
|
|
199339
|
+
}
|
|
199340
|
+
/** The model asked for a tool: its call ended here. */
|
|
199341
|
+
toolCall(ts, toolCallId) {
|
|
199342
|
+
if (this.finalSnapshot) return;
|
|
199343
|
+
this.attachIfNeeded(ts);
|
|
199344
|
+
if (this.firstChunkAt === void 0) this.firstChunkAt = ts;
|
|
199345
|
+
this.closeCall(ts);
|
|
199346
|
+
this.pendingTools.add(toolCallId);
|
|
199347
|
+
}
|
|
199348
|
+
/** A tool finished; the next model call starts once none is pending. */
|
|
199349
|
+
toolDone(ts, toolCallId) {
|
|
199350
|
+
if (this.finalSnapshot) return;
|
|
199351
|
+
if (!this.pendingTools.delete(toolCallId)) return;
|
|
199352
|
+
if (this.pendingTools.size === 0 && !this.open) this.openCall(ts);
|
|
199353
|
+
}
|
|
199354
|
+
/** One usage_update per finished model call. */
|
|
199355
|
+
usage() {
|
|
199356
|
+
if (this.finalSnapshot) return;
|
|
199357
|
+
this.usageUpdates += 1;
|
|
199358
|
+
}
|
|
199359
|
+
snapshot(now) {
|
|
199360
|
+
if (this.finalSnapshot) return this.finalSnapshot;
|
|
199361
|
+
const open3 = this.open;
|
|
199362
|
+
const streaming = !!open3 && open3.chars > 0;
|
|
199363
|
+
return {
|
|
199364
|
+
...this.startedAt !== void 0 ? { startedAt: this.startedAt } : {},
|
|
199365
|
+
ttftMs: this.startedAt !== void 0 && this.firstChunkAt !== void 0 ? Math.max(0, this.firstChunkAt - this.startedAt) : null,
|
|
199366
|
+
generationMs: 0,
|
|
199367
|
+
outputTokens: 0,
|
|
199368
|
+
messages: this.calls,
|
|
199369
|
+
...this.model ? { model: this.model } : {},
|
|
199370
|
+
inFlight: streaming,
|
|
199371
|
+
inFlightChars: streaming ? open3.chars : 0,
|
|
199372
|
+
inFlightMs: streaming ? Math.max(0, now - open3.startedAt) : 0,
|
|
199373
|
+
inFlightVisibleMs: streaming && open3.firstChunkAt !== void 0 ? Math.max(0, now - open3.firstChunkAt) : 0,
|
|
199374
|
+
final: false,
|
|
199375
|
+
...this.partial ? { partial: true } : {}
|
|
199376
|
+
};
|
|
199377
|
+
}
|
|
199378
|
+
finish(ts, usage, interrupted) {
|
|
199379
|
+
if (this.finalSnapshot) return this.finalSnapshot;
|
|
199380
|
+
if (this.open) this.closeCall(ts);
|
|
199381
|
+
const live5 = this.snapshot(ts);
|
|
199382
|
+
const calls = Math.max(this.calls, this.usageUpdates);
|
|
199383
|
+
const output = usage?.outputTokens;
|
|
199384
|
+
const hiddenReasoning = (usage?.thoughtTokens ?? 0) > 0;
|
|
199385
|
+
const windowStart = hiddenReasoning ? this.lastCallStartedAt : this.lastCallFirstChunkAt ?? this.lastCallStartedAt;
|
|
199386
|
+
const lastCallMs = windowStart !== void 0 && this.lastCallEndedAt !== void 0 ? Math.max(0, this.lastCallEndedAt - windowStart) : 0;
|
|
199387
|
+
const counted = typeof output === "number";
|
|
199388
|
+
const cost = counted && this.model && findPricing(this.model) ? computeCost({
|
|
199389
|
+
model: this.model,
|
|
199390
|
+
input_tokens: usage?.inputTokens ?? 0,
|
|
199391
|
+
output_tokens: output,
|
|
199392
|
+
cache_creation_input_tokens: 0,
|
|
199393
|
+
cache_read_input_tokens: usage?.cachedReadTokens ?? 0
|
|
199394
|
+
}) : 0;
|
|
199395
|
+
this.finalSnapshot = {
|
|
199396
|
+
...live5,
|
|
199397
|
+
messages: calls,
|
|
199398
|
+
generationMs: counted ? lastCallMs : 0,
|
|
199399
|
+
outputTokens: counted ? output : 0,
|
|
199400
|
+
...counted ? { turnOutputTokens: output } : {},
|
|
199401
|
+
...counted && calls > 1 ? { tokenScope: "last-call" } : {},
|
|
199402
|
+
...typeof usage?.inputTokens === "number" ? { inputTokens: usage.inputTokens } : {},
|
|
199403
|
+
...typeof usage?.cachedReadTokens === "number" ? { cachedInputTokens: usage.cachedReadTokens } : {},
|
|
199404
|
+
...typeof usage?.thoughtTokens === "number" && usage.thoughtTokens > 0 ? { reasoningTokens: usage.thoughtTokens } : {},
|
|
199405
|
+
// One call's price is not the turn's: a several-call turn shows no cost.
|
|
199406
|
+
...cost > 0 && calls <= 1 ? { costUsd: cost, costEstimated: true } : {},
|
|
199407
|
+
...this.startedAt !== void 0 ? { durationMs: Math.max(0, ts - this.startedAt) } : {},
|
|
199408
|
+
inFlight: false,
|
|
199409
|
+
inFlightChars: 0,
|
|
199410
|
+
inFlightMs: 0,
|
|
199411
|
+
inFlightVisibleMs: 0,
|
|
199412
|
+
final: true,
|
|
199413
|
+
...interrupted ? { interrupted: true } : {},
|
|
199414
|
+
endedAt: ts
|
|
199415
|
+
};
|
|
199416
|
+
return this.finalSnapshot;
|
|
199417
|
+
}
|
|
199418
|
+
attachIfNeeded(ts) {
|
|
199419
|
+
if (this.startedAt !== void 0 || this.seenAnything) return;
|
|
199420
|
+
this.seenAnything = true;
|
|
199421
|
+
this.partial = true;
|
|
199422
|
+
this.openCall(ts);
|
|
199423
|
+
}
|
|
199424
|
+
openCall(ts) {
|
|
199425
|
+
this.open = { startedAt: ts, chars: 0 };
|
|
199426
|
+
this.lastCallStartedAt = ts;
|
|
199427
|
+
this.lastCallFirstChunkAt = void 0;
|
|
199428
|
+
this.lastCallEndedAt = void 0;
|
|
199429
|
+
}
|
|
199430
|
+
closeCall(ts) {
|
|
199431
|
+
if (!this.open) return;
|
|
199432
|
+
this.calls += 1;
|
|
199433
|
+
this.lastCallStartedAt = this.open.startedAt;
|
|
199434
|
+
this.lastCallFirstChunkAt = this.open.firstChunkAt;
|
|
199435
|
+
this.lastCallEndedAt = ts;
|
|
199436
|
+
this.open = void 0;
|
|
199437
|
+
}
|
|
199438
|
+
};
|
|
199439
|
+
}
|
|
199440
|
+
});
|
|
199441
|
+
|
|
199087
199442
|
// src/providers/acp-worker/protocol.ts
|
|
199088
199443
|
function resolveWalnutMcpServers(session2) {
|
|
199089
199444
|
if (session2?.acp_walnut_mcp !== true) return [];
|
|
@@ -199315,7 +199670,7 @@ function resolveSystemCodexPath(options = {}) {
|
|
|
199315
199670
|
});
|
|
199316
199671
|
}
|
|
199317
199672
|
function inspectExecutableCandidate(candidate, cwd) {
|
|
199318
|
-
const executable =
|
|
199673
|
+
const executable = resolveOverridePath(candidate, cwd);
|
|
199319
199674
|
const verdict = inspectExecutable(executable);
|
|
199320
199675
|
if (verdict.executable) return { executable: verdict.executable };
|
|
199321
199676
|
if (verdict.rejection === "npm_injected_path") return { failure: "override_forbidden" };
|
|
@@ -199509,6 +199864,7 @@ var init_acp_session = __esm({
|
|
|
199509
199864
|
init_daemon_connection();
|
|
199510
199865
|
init_acp_journal_projector();
|
|
199511
199866
|
init_acp_stream_normalizer();
|
|
199867
|
+
init_acp_turn_speed();
|
|
199512
199868
|
init_self_api_root();
|
|
199513
199869
|
init_protocol2();
|
|
199514
199870
|
init_session_tracker();
|
|
@@ -199583,6 +199939,11 @@ var init_acp_session = __esm({
|
|
|
199583
199939
|
_pendingPermissionCommit = Promise.resolve();
|
|
199584
199940
|
_selfReport = null;
|
|
199585
199941
|
normalizer;
|
|
199942
|
+
/** The composer's turn speed readout, measured from the journal (see acp-turn-speed.ts). */
|
|
199943
|
+
_speed = new AcpTurnSpeedMeter();
|
|
199944
|
+
_speedEmitAt = 0;
|
|
199945
|
+
/** Latest usage_update, written to the record at turn end (one write per turn). */
|
|
199946
|
+
_lastContext = null;
|
|
199586
199947
|
constructor(cfg) {
|
|
199587
199948
|
this.cfg = cfg;
|
|
199588
199949
|
this.taskId = cfg.taskId;
|
|
@@ -199777,6 +200138,7 @@ var init_acp_session = __esm({
|
|
|
199777
200138
|
}
|
|
199778
200139
|
}
|
|
199779
200140
|
}
|
|
200141
|
+
this.meterTurnSpeed(record2);
|
|
199780
200142
|
for (const { name, payload } of this.normalizer.normalize(record2)) {
|
|
199781
200143
|
const full = { ...payload, sessionId: this.trackingId(), taskId: this.taskId };
|
|
199782
200144
|
if (name === "session:permission-request") {
|
|
@@ -199812,6 +200174,78 @@ var init_acp_session = __esm({
|
|
|
199812
200174
|
this.commitTerminal(v3, record2);
|
|
199813
200175
|
}
|
|
199814
200176
|
}
|
|
200177
|
+
/** Feed one journal record to the turn speed meter; emit its readout. */
|
|
200178
|
+
meterTurnSpeed(record2) {
|
|
200179
|
+
if (record2.kind === "meta") {
|
|
200180
|
+
const event = record2.event;
|
|
200181
|
+
if (event.type === "turn-started") {
|
|
200182
|
+
this._speed.start(record2.ts, this.meterModel());
|
|
200183
|
+
this.emitTurnSpeed(record2.ts, false);
|
|
200184
|
+
} else if (event.type === "turn-ended" || event.type === "turn-interrupted") {
|
|
200185
|
+
if (!this._speed.active) return;
|
|
200186
|
+
const usage = event.type === "turn-ended" ? event.usage : void 0;
|
|
200187
|
+
const interrupted = event.type === "turn-interrupted" || event.type === "turn-ended" && event.stopReason === "cancelled";
|
|
200188
|
+
const speed = this._speed.finish(record2.ts, usage, interrupted);
|
|
200189
|
+
this.emitTurnSpeed(record2.ts, true);
|
|
200190
|
+
const sid = this._providerSessionId;
|
|
200191
|
+
const context = this._lastContext;
|
|
200192
|
+
if (sid) {
|
|
200193
|
+
void updateSessionRecord(sid, {
|
|
200194
|
+
lastTurnSpeed: speed,
|
|
200195
|
+
...context ? { contextTokens: context.used, ...context.size > 0 ? { modelMaxWindow: context.size } : {} } : {}
|
|
200196
|
+
}).catch(() => {
|
|
200197
|
+
});
|
|
200198
|
+
}
|
|
200199
|
+
}
|
|
200200
|
+
return;
|
|
200201
|
+
}
|
|
200202
|
+
if (record2.source !== void 0 && record2.source !== "live") return;
|
|
200203
|
+
const frame = record2.frame;
|
|
200204
|
+
if (frame?.method !== "session/update") return;
|
|
200205
|
+
const update3 = frame.params?.update;
|
|
200206
|
+
if (!update3) return;
|
|
200207
|
+
switch (update3.sessionUpdate) {
|
|
200208
|
+
case "agent_message_chunk":
|
|
200209
|
+
case "agent_thought_chunk": {
|
|
200210
|
+
const content = update3.content;
|
|
200211
|
+
const chars2 = content?.type === "text" && typeof content.text === "string" ? content.text.length : 0;
|
|
200212
|
+
this._speed.chunk(record2.ts, chars2);
|
|
200213
|
+
if (record2.ts - this._speedEmitAt >= 400) this.emitTurnSpeed(record2.ts, false);
|
|
200214
|
+
return;
|
|
200215
|
+
}
|
|
200216
|
+
case "tool_call":
|
|
200217
|
+
if (typeof update3.toolCallId === "string") this._speed.toolCall(record2.ts, update3.toolCallId);
|
|
200218
|
+
this.emitTurnSpeed(record2.ts, false);
|
|
200219
|
+
return;
|
|
200220
|
+
case "tool_call_update":
|
|
200221
|
+
if (typeof update3.toolCallId === "string" && (update3.status === "completed" || update3.status === "failed")) {
|
|
200222
|
+
this._speed.toolDone(record2.ts, update3.toolCallId);
|
|
200223
|
+
}
|
|
200224
|
+
return;
|
|
200225
|
+
case "usage_update":
|
|
200226
|
+
this._speed.usage();
|
|
200227
|
+
if (typeof update3.used === "number" && typeof update3.size === "number") {
|
|
200228
|
+
this._lastContext = { used: update3.used, size: update3.size };
|
|
200229
|
+
}
|
|
200230
|
+
return;
|
|
200231
|
+
}
|
|
200232
|
+
}
|
|
200233
|
+
/** The model the meter prices and names: the family id, without its effort. */
|
|
200234
|
+
meterModel() {
|
|
200235
|
+
const current8 = this._models.currentModelId;
|
|
200236
|
+
return current8 ? splitAcpModelId(current8).base : void 0;
|
|
200237
|
+
}
|
|
200238
|
+
emitTurnSpeed(now, final) {
|
|
200239
|
+
const sid = this._providerSessionId;
|
|
200240
|
+
if (!sid) return;
|
|
200241
|
+
if (!final && !this._speed.active) return;
|
|
200242
|
+
this._speedEmitAt = now;
|
|
200243
|
+
bus.emit(EventNames.SESSION_TURN_SPEED, {
|
|
200244
|
+
sessionId: sid,
|
|
200245
|
+
taskId: this.taskId,
|
|
200246
|
+
speed: this._speed.snapshot(now)
|
|
200247
|
+
}, ["web-ui"], { source: "session-runner" });
|
|
200248
|
+
}
|
|
199815
200249
|
/** Records/API key: providerSessionId once known, else runtimeId (pending phase). */
|
|
199816
200250
|
trackingId() {
|
|
199817
200251
|
return this._providerSessionId ?? this.runtimeId;
|
|
@@ -203975,6 +204409,8 @@ var init_claude_code_session = __esm({
|
|
|
203975
204409
|
/** THE authoritative set of background tasks (dynamic workflows / subagents), keyed by
|
|
203976
204410
|
* task_id, each carrying its latest status. "Is bg work in flight" is derived from this
|
|
203977
204411
|
* set (see hasActiveBackgroundWork) — there is no parallel scalar counter to desync. */
|
|
204412
|
+
_foregroundActive;
|
|
204413
|
+
_foregroundEvidenceV = 0;
|
|
203978
204414
|
_bgTasks = /* @__PURE__ */ new Map();
|
|
203979
204415
|
/** Task ids that have EVER appeared in a `background_tasks_changed` payload — proof of
|
|
203980
204416
|
* membership in the CLI's level universe. Only a task the CLI itself once listed may be
|
|
@@ -204117,6 +204553,7 @@ var init_claude_code_session = __esm({
|
|
|
204117
204553
|
* from the init line. */
|
|
204118
204554
|
_onTurnStartEdge(source2, persist5, sidHint) {
|
|
204119
204555
|
const sid = this.claudeSessionId ?? sidHint;
|
|
204556
|
+
this.setForegroundActive(true);
|
|
204120
204557
|
this._turnGen++;
|
|
204121
204558
|
if (source2 !== "state-running" || this._turnResultEmitted || this._turnEdgeAt === void 0) {
|
|
204122
204559
|
this._turnEdgeAt = Date.now();
|
|
@@ -204174,6 +204611,7 @@ var init_claude_code_session = __esm({
|
|
|
204174
204611
|
_completeTurnOnIdle() {
|
|
204175
204612
|
const sid = this.claudeSessionId;
|
|
204176
204613
|
if (!sid) return;
|
|
204614
|
+
this.setForegroundActive(false);
|
|
204177
204615
|
const interrupted = this.consumeInterruptRequest();
|
|
204178
204616
|
const outcome = this._deferredOutcome;
|
|
204179
204617
|
this._deferredOutcome = void 0;
|
|
@@ -204306,6 +204744,7 @@ var init_claude_code_session = __esm({
|
|
|
204306
204744
|
taskId: this.taskId,
|
|
204307
204745
|
workflowName: this._workflowName,
|
|
204308
204746
|
inFlight: this._runningBgCount(),
|
|
204747
|
+
foregroundActive: this._foregroundActive,
|
|
204309
204748
|
tasks: this.backgroundTasks,
|
|
204310
204749
|
phases: sortedPhases(this._workflowPhases),
|
|
204311
204750
|
agents: this.workflowAgents,
|
|
@@ -204342,6 +204781,12 @@ var init_claude_code_session = __esm({
|
|
|
204342
204781
|
});
|
|
204343
204782
|
return { stopped: true, status: row.status };
|
|
204344
204783
|
}
|
|
204784
|
+
setForegroundActive(active6) {
|
|
204785
|
+
if (this._currentEventV !== void 0) this._foregroundEvidenceV = Math.max(this._foregroundEvidenceV, this._currentEventV);
|
|
204786
|
+
if (this._foregroundActive === active6) return;
|
|
204787
|
+
this._foregroundActive = active6;
|
|
204788
|
+
if (this.claudeSessionId && this._bgTasks.size > 0) this._emitBackgroundTasksUpdate(this.claudeSessionId);
|
|
204789
|
+
}
|
|
204345
204790
|
/** Broadcast the current background-task set so the UI can render workflow progress. */
|
|
204346
204791
|
_emitBackgroundTasksUpdate(sessionId) {
|
|
204347
204792
|
bus.emit(EventNames.SESSION_BACKGROUND_TASKS, {
|
|
@@ -204349,6 +204794,7 @@ var init_claude_code_session = __esm({
|
|
|
204349
204794
|
taskId: this.taskId,
|
|
204350
204795
|
workflowName: this._workflowName,
|
|
204351
204796
|
inFlight: this._runningBgCount(),
|
|
204797
|
+
foregroundActive: this._foregroundActive,
|
|
204352
204798
|
tasks: this.backgroundTasks,
|
|
204353
204799
|
phases: sortedPhases(this._workflowPhases),
|
|
204354
204800
|
agents: this.workflowAgents,
|
|
@@ -204629,6 +205075,15 @@ var init_claude_code_session = __esm({
|
|
|
204629
205075
|
setProcessStatusFromReconciler(status2) {
|
|
204630
205076
|
this._processStatus = status2;
|
|
204631
205077
|
}
|
|
205078
|
+
setForegroundFromSnapshot(snapshot4, epochChanged = false) {
|
|
205079
|
+
if (epochChanged) this._foregroundEvidenceV = 0;
|
|
205080
|
+
if (snapshot4.v < this._foregroundEvidenceV) return;
|
|
205081
|
+
const active6 = snapshot4.lastResult ? false : snapshot4.turnActive ? true : void 0;
|
|
205082
|
+
this._foregroundEvidenceV = snapshot4.v;
|
|
205083
|
+
if (active6 === void 0 || active6 === this._foregroundActive) return;
|
|
205084
|
+
this._foregroundActive = active6;
|
|
205085
|
+
if (this.claudeSessionId && this._bgTasks.size > 0) this._emitBackgroundTasksUpdate(this.claudeSessionId);
|
|
205086
|
+
}
|
|
204632
205087
|
/**
|
|
204633
205088
|
* Epoch-reset the in-memory consumed watermark from the snapshot layer.
|
|
204634
205089
|
* The ONLY sanctioned backwards move (mirrors the record-side epoch
|
|
@@ -204853,6 +205308,8 @@ var init_claude_code_session = __esm({
|
|
|
204853
205308
|
}
|
|
204854
205309
|
this._active = true;
|
|
204855
205310
|
this._processStatus = initOnly ? "idle" : "running";
|
|
205311
|
+
this._foregroundActive = !initOnly;
|
|
205312
|
+
this._foregroundEvidenceV = 0;
|
|
204856
205313
|
this._exitCode = null;
|
|
204857
205314
|
this._exitStderr = void 0;
|
|
204858
205315
|
this.resultEmitted = false;
|
|
@@ -204884,6 +205341,7 @@ var init_claude_code_session = __esm({
|
|
|
204884
205341
|
this._bgSeenInLevel.clear();
|
|
204885
205342
|
this._deferredOutcome = void 0;
|
|
204886
205343
|
this._resetWorkflowState();
|
|
205344
|
+
if (this.claudeSessionId) this._emitBackgroundTasksUpdate(this.claudeSessionId);
|
|
204887
205345
|
this._teamActive = false;
|
|
204888
205346
|
this._cliSessionState = void 0;
|
|
204889
205347
|
this._transportGeneration++;
|
|
@@ -205262,12 +205720,16 @@ var init_claude_code_session = __esm({
|
|
|
205262
205720
|
status: status2,
|
|
205263
205721
|
toolUseId: foldToolUseId ?? prev?.toolUseId,
|
|
205264
205722
|
isBackgrounded: t3.isBackgrounded || prev?.isBackgrounded,
|
|
205723
|
+
taskType: t3.taskType ?? prev?.taskType,
|
|
205724
|
+
subagentType: t3.subagentType ?? prev?.subagentType,
|
|
205725
|
+
ownedBySubagent: t3.ownedBySubagent ?? prev?.ownedBySubagent,
|
|
205265
205726
|
// #870: the fold's level verdict rides along so a wedged hold stays
|
|
205266
205727
|
// un-wedged across an attach (gating skips endedPerLevel entries).
|
|
205267
205728
|
endedPerLevel: t3.endedPerLevel || prev?.endedPerLevel
|
|
205268
205729
|
});
|
|
205269
205730
|
if (t3.endedPerLevel) session2._bgSeenInLevel.add(taskId2);
|
|
205270
205731
|
}
|
|
205732
|
+
session2._foregroundActive = fold2.lastResult ? false : fold2.cliState === "running" ? true : void 0;
|
|
205271
205733
|
if (fold2.cliState != null) {
|
|
205272
205734
|
session2._sessionStateSeen = true;
|
|
205273
205735
|
session2._cliSessionState = fold2.cliState;
|
|
@@ -205529,6 +205991,8 @@ var init_claude_code_session = __esm({
|
|
|
205529
205991
|
const before = {
|
|
205530
205992
|
_processStatus: this._processStatus,
|
|
205531
205993
|
_activity: this._activity,
|
|
205994
|
+
_foregroundActive: this._foregroundActive,
|
|
205995
|
+
_foregroundEvidenceV: this._foregroundEvidenceV,
|
|
205532
205996
|
resultEmitted: this.resultEmitted,
|
|
205533
205997
|
_turnResultEmitted: this._turnResultEmitted,
|
|
205534
205998
|
_expectedTeardown: this._expectedTeardown,
|
|
@@ -205555,6 +206019,7 @@ var init_claude_code_session = __esm({
|
|
|
205555
206019
|
undoDispatch = () => {
|
|
205556
206020
|
if (this._transport === transport && this._turnGen === before._turnGen + 1 && this._lastJsonlEventTs === eventAt && transport.fileSize === offset) {
|
|
205557
206021
|
Object.assign(this, before);
|
|
206022
|
+
if (this.claudeSessionId) this._emitBackgroundTasksUpdate(this.claudeSessionId);
|
|
205558
206023
|
if (speedBefore.final) {
|
|
205559
206024
|
this._speed.restore(speedBefore);
|
|
205560
206025
|
this.emitTurnSpeed(true);
|
|
@@ -205567,6 +206032,8 @@ var init_claude_code_session = __esm({
|
|
|
205567
206032
|
this._activity = void 0;
|
|
205568
206033
|
this.resultEmitted = false;
|
|
205569
206034
|
this._turnResultEmitted = false;
|
|
206035
|
+
this._foregroundEvidenceV = Math.max(this._foregroundEvidenceV, transport.fileSize + 1);
|
|
206036
|
+
this.setForegroundActive(true);
|
|
205570
206037
|
this._expectedTeardown = false;
|
|
205571
206038
|
this._turnGen++;
|
|
205572
206039
|
this._turnEdgeAt = Date.now();
|
|
@@ -206841,6 +207308,7 @@ var init_claude_code_session = __esm({
|
|
|
206841
207308
|
this._deferredOutcome = void 0;
|
|
206842
207309
|
this._onTurnStartEdge("user-message", true);
|
|
206843
207310
|
}
|
|
207311
|
+
if ((event.type === "assistant" || event.type === "stream_event") && !event.parent_tool_use_id && this._isReplayedByOffset() !== true) this.setForegroundActive(true);
|
|
206844
207312
|
try {
|
|
206845
207313
|
switch (event.type) {
|
|
206846
207314
|
case "system": {
|
|
@@ -207182,6 +207650,7 @@ var init_claude_code_session = __esm({
|
|
|
207182
207650
|
}
|
|
207183
207651
|
const wasCompanionIdle = this._idleDebt > 0;
|
|
207184
207652
|
if (wasCompanionIdle) this._idleDebt--;
|
|
207653
|
+
else this.setForegroundActive(false);
|
|
207185
207654
|
if (this.hasActiveBackgroundWork()) {
|
|
207186
207655
|
log.session.info("idle while background work in flight \u2014 staying running, awaiting drain", {
|
|
207187
207656
|
sessionId: sid,
|
|
@@ -207245,6 +207714,7 @@ var init_claude_code_session = __esm({
|
|
|
207245
207714
|
// read it here too in case a future CLI stamps it at start. Never
|
|
207246
207715
|
// un-background: keep a previously-recorded true.
|
|
207247
207716
|
isBackgrounded: sys.is_backgrounded === true || prevStarted?.isBackgrounded,
|
|
207717
|
+
ownedBySubagent: typeof sys.owned_by_subagent === "boolean" ? sys.owned_by_subagent : prevStarted?.ownedBySubagent,
|
|
207248
207718
|
// Display-only. toolUseId: within ONE call the first non-empty wins (never
|
|
207249
207719
|
// clobber with undefined). startedAt is OUR clock at the first start we saw — a
|
|
207250
207720
|
// replayed task_started must not restart the row's elapsed timer.
|
|
@@ -207746,6 +208216,7 @@ var init_claude_code_session = __esm({
|
|
|
207746
208216
|
this._turnResultEmitted = true;
|
|
207747
208217
|
break;
|
|
207748
208218
|
}
|
|
208219
|
+
this.setForegroundActive(false);
|
|
207749
208220
|
const resultOrigin = event.origin;
|
|
207750
208221
|
const isTaskNotificationResult = resultOrigin?.kind === "task-notification";
|
|
207751
208222
|
if (isTaskNotificationResult) {
|
|
@@ -227968,6 +228439,9 @@ function foldSessionTail(content, baseOffset, opts2) {
|
|
|
227968
228439
|
fold2.bgTasks[taskId2] = {
|
|
227969
228440
|
status: !restarted && prev && BG_TERMINAL.has(prev.status) ? prev.status : "running",
|
|
227970
228441
|
toolUseId: toolUseId ?? prev?.toolUseId,
|
|
228442
|
+
taskType: typeof parsed.task_type === "string" ? parsed.task_type : prev?.taskType,
|
|
228443
|
+
subagentType: typeof parsed.subagent_type === "string" ? parsed.subagent_type : prev?.subagentType,
|
|
228444
|
+
ownedBySubagent: typeof parsed.owned_by_subagent === "boolean" ? parsed.owned_by_subagent : prev?.ownedBySubagent,
|
|
227971
228445
|
// A run_in_background Bash task carries is_backgrounded:true TOP-LEVEL
|
|
227972
228446
|
// on its task_started (verified live, local_bash 2026-08-28). Sticky,
|
|
227973
228447
|
// same as the task_updated patch path. Keep in parity with daemon-fold.
|
|
@@ -227977,6 +228451,11 @@ function foldSessionTail(content, baseOffset, opts2) {
|
|
|
227977
228451
|
const prev = fold2.bgTasks[taskId2];
|
|
227978
228452
|
const patch = parsed.patch;
|
|
227979
228453
|
fold2.bgTasks[taskId2] = {
|
|
228454
|
+
// Keep what task_started said (its type, its owner), but not a level
|
|
228455
|
+
// snapshot's endedPerLevel mark: the task's own event un-marks it, as
|
|
228456
|
+
// daemon-fold's task_updated does (gatingBgCount parity).
|
|
228457
|
+
...prev,
|
|
228458
|
+
endedPerLevel: void 0,
|
|
227980
228459
|
status: patch?.status ?? prev?.status ?? "running",
|
|
227981
228460
|
toolUseId: toolUseId ?? prev?.toolUseId,
|
|
227982
228461
|
// Sticky: is_backgrounded=true detaches the task from gating forever.
|
|
@@ -227985,6 +228464,8 @@ function foldSessionTail(content, baseOffset, opts2) {
|
|
|
227985
228464
|
} else if (taskId2 && subtype === "task_notification") {
|
|
227986
228465
|
const prev = fold2.bgTasks[taskId2];
|
|
227987
228466
|
fold2.bgTasks[taskId2] = {
|
|
228467
|
+
...prev,
|
|
228468
|
+
endedPerLevel: void 0,
|
|
227988
228469
|
status: parsed.status ?? "completed",
|
|
227989
228470
|
isBackgrounded: prev?.isBackgrounded,
|
|
227990
228471
|
toolUseId: toolUseId ?? prev?.toolUseId
|
|
@@ -246557,6 +247038,11 @@ var init_devices = __esm({
|
|
|
246557
247038
|
const existingMachine = CLOUD_MODE && req.body?.kind !== "machine" && (await listDeviceRecords()).some((d8) => d8.name === name && d8.kind === "machine");
|
|
246558
247039
|
const kind = req.body?.kind === "machine" || existingMachine ? "machine" : void 0;
|
|
246559
247040
|
const wantsCloud = req.body?.target === "cloud" && !CLOUD_MODE;
|
|
247041
|
+
const bridgeScope = req.body?.bridge_scope;
|
|
247042
|
+
if (bridgeScope !== void 0 && (bridgeScope !== "status-only" || kind !== "machine" || !CLOUD_MODE)) {
|
|
247043
|
+
res.status(400).json({ error: "bridge_scope requires a status-only machine credential on the companion" });
|
|
247044
|
+
return;
|
|
247045
|
+
}
|
|
246560
247046
|
const replace = req.body?.replace === true;
|
|
246561
247047
|
if (wantsCloud) {
|
|
246562
247048
|
if (!await cloudRelayAllowed(req, res, replace ? "rotate" : "create")) return;
|
|
@@ -246574,7 +247060,7 @@ var init_devices = __esm({
|
|
|
246574
247060
|
const target = CLOUD_MODE ? { origin: consoleOrigin, kind: "cloud" } : localTargets.find((t3) => t3.kind === req.body?.target) ?? localTargets[0];
|
|
246575
247061
|
const by = actorOf(req);
|
|
246576
247062
|
if (kind === "machine" && CLOUD_MODE) {
|
|
246577
|
-
const minted = await mintMachineCredential(name, { by, replace, proof: machineProof(req) });
|
|
247063
|
+
const minted = await mintMachineCredential(name, { by, replace, proof: machineProof(req), bridgeScope });
|
|
246578
247064
|
log.web.info("devices: machine credential created via console", { name, caller: callerDevice(req) });
|
|
246579
247065
|
res.status(201).json({ name, token: minted.token, pairingURI: buildPairingURI(name, minted.token, target?.origin), createdAt: minted.createdAt, target: target?.kind ?? null, server: target?.origin ?? null });
|
|
246580
247066
|
return;
|
|
@@ -264159,6 +264645,7 @@ var init_system = __esm({
|
|
|
264159
264645
|
|
|
264160
264646
|
// src/web/routes/hosts.ts
|
|
264161
264647
|
import { Router as Router70 } from "express";
|
|
264648
|
+
import { randomBytes as randomBytes6 } from "crypto";
|
|
264162
264649
|
async function cloudState(hostKey2) {
|
|
264163
264650
|
const { bridgeForHost: bridgeForHost2 } = await Promise.resolve().then(() => (init_bridge_registry(), bridge_registry_exports));
|
|
264164
264651
|
const connected2 = bridgeForHost2(hostKey2).connected;
|
|
@@ -264197,6 +264684,37 @@ var init_hosts = __esm({
|
|
|
264197
264684
|
next(err2);
|
|
264198
264685
|
}
|
|
264199
264686
|
});
|
|
264687
|
+
hostsRouter.get("/:host/status", async (req, res, next) => {
|
|
264688
|
+
const host = String(req.params.host ?? "");
|
|
264689
|
+
if (!/^[A-Za-z0-9_][A-Za-z0-9_.-]{0,79}$/.test(host)) {
|
|
264690
|
+
res.status(400).json({ error: "Invalid host alias" });
|
|
264691
|
+
return;
|
|
264692
|
+
}
|
|
264693
|
+
if (!CLOUD_MODE) {
|
|
264694
|
+
res.status(409).json({ error: "Outbound host status is served by the cloud companion" });
|
|
264695
|
+
return;
|
|
264696
|
+
}
|
|
264697
|
+
try {
|
|
264698
|
+
const { bridgeForHost: bridgeForHost2, bridgeRequest: bridgeRequest2 } = await Promise.resolve().then(() => (init_bridge_registry(), bridge_registry_exports));
|
|
264699
|
+
if (!bridgeForHost2(host).connected) {
|
|
264700
|
+
res.status(503).json({ host, connected: false, code: "bridge_offline" });
|
|
264701
|
+
return;
|
|
264702
|
+
}
|
|
264703
|
+
const nonce = randomBytes6(16).toString("hex");
|
|
264704
|
+
try {
|
|
264705
|
+
const reply2 = await bridgeRequest2(host, "ping", { nonce }, 5e3);
|
|
264706
|
+
if (reply2.ok !== true || reply2.pong !== true || reply2.nonce !== nonce) {
|
|
264707
|
+
res.status(503).json({ host, connected: false, code: "bridge_unconfirmed" });
|
|
264708
|
+
return;
|
|
264709
|
+
}
|
|
264710
|
+
res.json({ host, connected: true, transport: "outbound_bridge" });
|
|
264711
|
+
} catch {
|
|
264712
|
+
res.status(503).json({ host, connected: false, code: "bridge_unavailable" });
|
|
264713
|
+
}
|
|
264714
|
+
} catch (err2) {
|
|
264715
|
+
next(err2);
|
|
264716
|
+
}
|
|
264717
|
+
});
|
|
264200
264718
|
hostsRouter.post("/:host/connect", async (req, res, next) => {
|
|
264201
264719
|
try {
|
|
264202
264720
|
const host = typeof req.params.host === "string" ? req.params.host.trim() : "";
|
|
@@ -266314,6 +266832,7 @@ async function runJob2(state13, opts2) {
|
|
|
266314
266832
|
state13.error = void 0;
|
|
266315
266833
|
state13.steps[id] = { status: "running", startedAt: (/* @__PURE__ */ new Date()).toISOString() };
|
|
266316
266834
|
await persistIfCurrent();
|
|
266835
|
+
if (handle4.cancelled || superseded()) return;
|
|
266317
266836
|
emitProgress(state13);
|
|
266318
266837
|
const outcome = await runners[id](ctx);
|
|
266319
266838
|
if (handle4.cancelled || superseded()) return;
|
|
@@ -267597,7 +268116,7 @@ import fs133 from "fs";
|
|
|
267597
268116
|
import path237 from "path";
|
|
267598
268117
|
import { createRequire as createRequire3 } from "module";
|
|
267599
268118
|
import { spawn as spawn31 } from "child_process";
|
|
267600
|
-
import { randomBytes as
|
|
268119
|
+
import { randomBytes as randomBytes7 } from "crypto";
|
|
267601
268120
|
var RECORDING_STATE_FILE, require2, DEFAULT_CHUNK_MINUTES, DEFAULT_SAMPLE_RATE, DEFAULT_CHANNELS, DEFAULT_REFRESH_INTERVAL_SEC, WAV_FORMAT_PCM, AudioCaptureService, audioCaptureService;
|
|
267602
268121
|
var init_audio_capture = __esm({
|
|
267603
268122
|
"src/core/audio-capture.ts"() {
|
|
@@ -267818,7 +268337,7 @@ var init_audio_capture = __esm({
|
|
|
267818
268337
|
throw new Error("Audio capture not available. macOS 13+ with Screen Recording permission required.");
|
|
267819
268338
|
}
|
|
267820
268339
|
const { AudioCapture } = require2("screencapturekit-audio-capture");
|
|
267821
|
-
this.recordingId =
|
|
268340
|
+
this.recordingId = randomBytes7(6).toString("hex");
|
|
267822
268341
|
this.source = options.source;
|
|
267823
268342
|
this.mode = options.mode;
|
|
267824
268343
|
this.apps = options.apps ?? [];
|
|
@@ -286534,6 +287053,7 @@ __export(git_maintenance_exports, {
|
|
|
286534
287053
|
DEBRIS_MAX_AGE_MS: () => DEBRIS_MAX_AGE_MS,
|
|
286535
287054
|
FAILED_GC_BACKOFF_MS: () => FAILED_GC_BACKOFF_MS,
|
|
286536
287055
|
MAINTENANCE_INTERVAL_DAYS: () => MAINTENANCE_INTERVAL_DAYS,
|
|
287056
|
+
REWRITE_MAINTENANCE_DELAY_MS: () => REWRITE_MAINTENANCE_DELAY_MS,
|
|
286537
287057
|
SIZE_REGROWTH_RATIO: () => SIZE_REGROWTH_RATIO,
|
|
286538
287058
|
SIZE_TRIGGER_BYTES: () => SIZE_TRIGGER_BYTES,
|
|
286539
287059
|
expireCompactionBackups: () => expireCompactionBackups,
|
|
@@ -286542,7 +287062,9 @@ __export(git_maintenance_exports, {
|
|
|
286542
287062
|
maintenanceDue: () => maintenanceDue,
|
|
286543
287063
|
packDirBytes: () => packDirBytes,
|
|
286544
287064
|
readLastRun: () => readLastRun,
|
|
287065
|
+
requestMaintenancePass: () => requestMaintenancePass,
|
|
286545
287066
|
resolveGitDir: () => resolveGitDir,
|
|
287067
|
+
settleRepoSize: () => settleRepoSize,
|
|
286546
287068
|
staleCompactionBackups: () => staleCompactionBackups,
|
|
286547
287069
|
staleQuarantineDirs: () => staleQuarantineDirs,
|
|
286548
287070
|
startGitMaintenance: () => startGitMaintenance,
|
|
@@ -286695,7 +287217,7 @@ function staleCompactionBackups(gitDir, now = Date.now()) {
|
|
|
286695
287217
|
return d8 !== null && now - d8 > maxAgeMs;
|
|
286696
287218
|
}).sort();
|
|
286697
287219
|
}
|
|
286698
|
-
function maintenanceDue(gitDir, now = Date.now()) {
|
|
287220
|
+
function maintenanceDue(gitDir, now = Date.now(), opts2 = {}) {
|
|
286699
287221
|
const last2 = readLastRun(gitDir);
|
|
286700
287222
|
const pack = packDirBytes(gitDir);
|
|
286701
287223
|
const recentlyFailed = last2.failedAt !== void 0 && now - last2.failedAt < FAILED_GC_BACKOFF_MS;
|
|
@@ -286705,6 +287227,7 @@ function maintenanceDue(gitDir, now = Date.now()) {
|
|
|
286705
287227
|
}
|
|
286706
287228
|
if (staleQuarantineDirs(gitDir, now) >= DEBRIS_COUNT_TRIGGER) return "debris";
|
|
286707
287229
|
if (staleCompactionBackups(gitDir, now).length > 0 && !recentlyFailed) return "backup";
|
|
287230
|
+
if (opts2.afterRewrite && !recentlyFailed) return "rewrite";
|
|
286708
287231
|
if (now - last2.at >= MAINTENANCE_INTERVAL_DAYS * 864e5) return "interval";
|
|
286709
287232
|
return null;
|
|
286710
287233
|
}
|
|
@@ -286771,7 +287294,7 @@ async function maintainRepo(repoDir, opts2 = {}) {
|
|
|
286771
287294
|
packBytesBefore: 0,
|
|
286772
287295
|
packBytesAfter: 0
|
|
286773
287296
|
};
|
|
286774
|
-
const due2 = opts2.force ? "forced" : maintenanceDue(gitDir);
|
|
287297
|
+
const due2 = opts2.force ? "forced" : maintenanceDue(gitDir, Date.now(), { afterRewrite: opts2.afterRewrite });
|
|
286775
287298
|
if (!due2) return result;
|
|
286776
287299
|
result.reason = due2;
|
|
286777
287300
|
result.packBytesBefore = packDirBytes(gitDir);
|
|
@@ -286844,14 +287367,34 @@ function sweepDeployBundles(dir4 = os64.tmpdir(), now = Date.now()) {
|
|
|
286844
287367
|
}
|
|
286845
287368
|
return swept;
|
|
286846
287369
|
}
|
|
287370
|
+
function requestMaintenancePass() {
|
|
287371
|
+
return activePass ? activePass(false) : null;
|
|
287372
|
+
}
|
|
287373
|
+
async function settleRepoSize(verdict) {
|
|
287374
|
+
if (!verdict.over) return verdict;
|
|
287375
|
+
const pass = requestMaintenancePass();
|
|
287376
|
+
if (!pass) return verdict;
|
|
287377
|
+
const results = await pass;
|
|
287378
|
+
const worktree = results.find((r8) => r8.repo === WALNUT_HOME);
|
|
287379
|
+
if (!worktree?.ran) return verdict;
|
|
287380
|
+
return measureRepoSize(WALNUT_HOME);
|
|
287381
|
+
}
|
|
286847
287382
|
function startGitMaintenance(opts2 = {}) {
|
|
286848
287383
|
let stopped2 = false;
|
|
286849
287384
|
let timer10 = null;
|
|
287385
|
+
let rewriteTimer = null;
|
|
287386
|
+
let rewritePending = false;
|
|
287387
|
+
let inflight7 = null;
|
|
286850
287388
|
const pass = async (force = false) => {
|
|
286851
287389
|
const results = [];
|
|
287390
|
+
const afterRewrite = rewritePending;
|
|
287391
|
+
rewritePending = false;
|
|
286852
287392
|
try {
|
|
286853
|
-
|
|
287393
|
+
const worktree = await maintainRepo(WALNUT_HOME, { pauseSync: true, force, afterRewrite });
|
|
287394
|
+
results.push(worktree);
|
|
287395
|
+
if (afterRewrite && !worktree.ran) rewritePending = true;
|
|
286854
287396
|
} catch (err2) {
|
|
287397
|
+
if (afterRewrite) rewritePending = true;
|
|
286855
287398
|
log.git.warn("git-maintenance worktree pass failed", {
|
|
286856
287399
|
error: err2 instanceof Error ? err2.message : String(err2)
|
|
286857
287400
|
});
|
|
@@ -286874,9 +287417,32 @@ function startGitMaintenance(opts2 = {}) {
|
|
|
286874
287417
|
}
|
|
286875
287418
|
return results;
|
|
286876
287419
|
};
|
|
287420
|
+
const runPass = (force = false) => {
|
|
287421
|
+
if (inflight7 && force) return inflight7.then(() => runPass(true));
|
|
287422
|
+
if (inflight7) return inflight7;
|
|
287423
|
+
inflight7 = pass(force).finally(() => {
|
|
287424
|
+
inflight7 = null;
|
|
287425
|
+
});
|
|
287426
|
+
return inflight7;
|
|
287427
|
+
};
|
|
287428
|
+
activePass = runPass;
|
|
287429
|
+
const onRewriteAdopted = () => {
|
|
287430
|
+
if (stopped2) return;
|
|
287431
|
+
rewritePending = true;
|
|
287432
|
+
if (rewriteTimer) return;
|
|
287433
|
+
rewriteTimer = setTimeout(() => {
|
|
287434
|
+
rewriteTimer = null;
|
|
287435
|
+
void (async () => {
|
|
287436
|
+
if (inflight7) await inflight7;
|
|
287437
|
+
if (!stopped2 && rewritePending) await runPass();
|
|
287438
|
+
})();
|
|
287439
|
+
}, opts2.rewriteDelayMs ?? REWRITE_MAINTENANCE_DELAY_MS);
|
|
287440
|
+
rewriteTimer.unref?.();
|
|
287441
|
+
};
|
|
287442
|
+
setRewriteAdoptedListener(onRewriteAdopted);
|
|
286877
287443
|
const tick = async () => {
|
|
286878
287444
|
try {
|
|
286879
|
-
await
|
|
287445
|
+
await runPass();
|
|
286880
287446
|
} finally {
|
|
286881
287447
|
if (!stopped2) {
|
|
286882
287448
|
timer10 = setTimeout(() => {
|
|
@@ -286899,11 +287465,16 @@ function startGitMaintenance(opts2 = {}) {
|
|
|
286899
287465
|
stop() {
|
|
286900
287466
|
stopped2 = true;
|
|
286901
287467
|
if (timer10) clearTimeout(timer10);
|
|
287468
|
+
if (rewriteTimer) clearTimeout(rewriteTimer);
|
|
287469
|
+
if (activePass === runPass) {
|
|
287470
|
+
activePass = null;
|
|
287471
|
+
setRewriteAdoptedListener(null);
|
|
287472
|
+
}
|
|
286902
287473
|
},
|
|
286903
|
-
runOnce: (force = false) =>
|
|
287474
|
+
runOnce: (force = false) => runPass(force)
|
|
286904
287475
|
};
|
|
286905
287476
|
}
|
|
286906
|
-
var MAINTENANCE_INTERVAL_DAYS, SIZE_TRIGGER_BYTES, DEBRIS_MAX_AGE_MS, BACKUP_BRANCH_MAX_AGE_DAYS, COMPACTION_BACKUP_MAX_AGE_DAYS, SIZE_REGROWTH_RATIO, GC_TIMEOUT_MS, CHECK_INTERVAL_MS, LAST_RUN_FILE, FAILED_GC_BACKOFF_MS, DEBRIS_COUNT_TRIGGER, DEPLOY_DEBRIS_RE, DEPLOY_DEBRIS_MAX_AGE_MS;
|
|
287477
|
+
var MAINTENANCE_INTERVAL_DAYS, SIZE_TRIGGER_BYTES, DEBRIS_MAX_AGE_MS, BACKUP_BRANCH_MAX_AGE_DAYS, COMPACTION_BACKUP_MAX_AGE_DAYS, SIZE_REGROWTH_RATIO, GC_TIMEOUT_MS, CHECK_INTERVAL_MS, LAST_RUN_FILE, REWRITE_MAINTENANCE_DELAY_MS, FAILED_GC_BACKOFF_MS, DEBRIS_COUNT_TRIGGER, DEPLOY_DEBRIS_RE, DEPLOY_DEBRIS_MAX_AGE_MS, activePass;
|
|
286907
287478
|
var init_git_maintenance = __esm({
|
|
286908
287479
|
"src/integrations/git-maintenance.ts"() {
|
|
286909
287480
|
"use strict";
|
|
@@ -286919,10 +287490,12 @@ var init_git_maintenance = __esm({
|
|
|
286919
287490
|
GC_TIMEOUT_MS = 30 * 6e4;
|
|
286920
287491
|
CHECK_INTERVAL_MS = 24 * 60 * 60 * 1e3;
|
|
286921
287492
|
LAST_RUN_FILE = "walnut-last-maintenance";
|
|
287493
|
+
REWRITE_MAINTENANCE_DELAY_MS = 6e4;
|
|
286922
287494
|
FAILED_GC_BACKOFF_MS = 24 * 60 * 60 * 1e3;
|
|
286923
287495
|
DEBRIS_COUNT_TRIGGER = 10;
|
|
286924
287496
|
DEPLOY_DEBRIS_RE = /^(?:wn[-.].*\.bundle(?:\.\w+)?|walnut-[\w.-]*\.(?:bundle|tar\.gz|tgz)|deploy-seed[\w.-]*\.sh|wn\.bundle)$/;
|
|
286925
287497
|
DEPLOY_DEBRIS_MAX_AGE_MS = 24 * 60 * 60 * 1e3;
|
|
287498
|
+
activePass = null;
|
|
286926
287499
|
}
|
|
286927
287500
|
});
|
|
286928
287501
|
|
|
@@ -292805,17 +293378,27 @@ async function startServer(options = {}) {
|
|
|
292805
293378
|
recoveryKey: COMPACTION_RECOVERY_KEY
|
|
292806
293379
|
});
|
|
292807
293380
|
};
|
|
292808
|
-
const attemptCompaction = async (retriesLeft = 2) => {
|
|
293381
|
+
const attemptCompaction = async (retriesLeft = 2, deferralsLeft = 6) => {
|
|
292809
293382
|
if (!workerPath) {
|
|
292810
293383
|
reportCompactionFailure("compaction worker build is missing from dist/workers", "missing-worker");
|
|
292811
293384
|
return;
|
|
292812
293385
|
}
|
|
292813
|
-
const
|
|
293386
|
+
const gitSync = await Promise.resolve().then(() => (init_git_sync(), git_sync_exports));
|
|
293387
|
+
const { setCompactionInProgress: setCompactionInProgress2, waitForSyncSettled: waitForSyncSettled2, syncPauseReason: syncPauseReason2 } = gitSync;
|
|
292814
293388
|
const pausedBy = syncPauseReason2();
|
|
292815
293389
|
if (pausedBy) {
|
|
292816
293390
|
log.git.info("git compaction skipped \u2014 operator pause marker active", { reason: pausedBy });
|
|
292817
293391
|
return;
|
|
292818
293392
|
}
|
|
293393
|
+
if (gitSync.compactionInProgress) {
|
|
293394
|
+
if (deferralsLeft > 0) {
|
|
293395
|
+
log.git.info("git compaction deferred \u2014 git maintenance holds the repo", { deferralsLeft });
|
|
293396
|
+
setTimeout(() => {
|
|
293397
|
+
void attemptCompaction(retriesLeft, deferralsLeft - 1);
|
|
293398
|
+
}, 10 * 6e4).unref?.();
|
|
293399
|
+
}
|
|
293400
|
+
return;
|
|
293401
|
+
}
|
|
292819
293402
|
setCompactionInProgress2(true);
|
|
292820
293403
|
await waitForSyncSettled2();
|
|
292821
293404
|
const { utilityQosForkExec: utilityQosForkExec2 } = await Promise.resolve().then(() => (init_background_qos(), background_qos_exports));
|
|
@@ -294192,23 +294775,33 @@ function startGitAutoCommit() {
|
|
|
294192
294775
|
Promise.resolve().then(() => (init_task_outbox(), task_outbox_exports)).then(({ reconcileAfterPull: reconcileAfterPull2 }) => reconcileAfterPull2()).catch(() => {
|
|
294193
294776
|
});
|
|
294194
294777
|
}
|
|
294195
|
-
const
|
|
294196
|
-
if (
|
|
294197
|
-
if (
|
|
294198
|
-
log.git.warn("repo-size sentinel swept dead transfer debris", { sweptBytes:
|
|
294199
|
-
}
|
|
294200
|
-
if (size.over && size.warning) {
|
|
294201
|
-
log.git.warn(size.warning);
|
|
294202
|
-
void publishErrorNotification({
|
|
294203
|
-
title: "Data Repo Growing Too Large",
|
|
294204
|
-
body: size.warning,
|
|
294205
|
-
dedupScope: "git:repo-size",
|
|
294206
|
-
recoveryKey: REPO_SIZE_RECOVERY_KEY
|
|
294207
|
-
});
|
|
294208
|
-
}
|
|
294209
|
-
if (gitRecoveryTracker.observe(REPO_SIZE_RECOVERY_KEY, size.over)) {
|
|
294210
|
-
void publishRecovery([REPO_SIZE_RECOVERY_KEY]);
|
|
294778
|
+
const measured2 = checkRepoSize();
|
|
294779
|
+
if (measured2) {
|
|
294780
|
+
if (measured2.sweptBytes > 0) {
|
|
294781
|
+
log.git.warn("repo-size sentinel swept dead transfer debris", { sweptBytes: measured2.sweptBytes, liveBytes: measured2.liveBytes });
|
|
294211
294782
|
}
|
|
294783
|
+
void Promise.resolve().then(() => (init_git_maintenance(), git_maintenance_exports)).then(({ settleRepoSize: settleRepoSize2 }) => settleRepoSize2(measured2)).then((size) => {
|
|
294784
|
+
if (gitTickStopped) return;
|
|
294785
|
+
if (size.over && size.warning) {
|
|
294786
|
+
log.git.warn(size.warning);
|
|
294787
|
+
void publishErrorNotification({
|
|
294788
|
+
title: "Data Repo Growing Too Large",
|
|
294789
|
+
body: size.warning,
|
|
294790
|
+
dedupScope: "git:repo-size",
|
|
294791
|
+
recoveryKey: REPO_SIZE_RECOVERY_KEY
|
|
294792
|
+
});
|
|
294793
|
+
} else if (measured2.over) {
|
|
294794
|
+
log.git.info("repo-size sentinel: maintenance brought the data repo under the threshold", {
|
|
294795
|
+
liveBytesBefore: measured2.liveBytes,
|
|
294796
|
+
liveBytesAfter: size.liveBytes
|
|
294797
|
+
});
|
|
294798
|
+
}
|
|
294799
|
+
if (gitRecoveryTracker.observe(REPO_SIZE_RECOVERY_KEY, size.over)) {
|
|
294800
|
+
void publishRecovery([REPO_SIZE_RECOVERY_KEY]);
|
|
294801
|
+
}
|
|
294802
|
+
}).catch((err2) => {
|
|
294803
|
+
log.git.warn("repo-size sentinel failed", { error: String(err2) });
|
|
294804
|
+
});
|
|
294212
294805
|
}
|
|
294213
294806
|
const guardState = getSyncGuardState();
|
|
294214
294807
|
if (guardState.safeMode !== health2.safeMode) {
|