@snowyroad/braid 0.96.3 → 0.96.5
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.
|
@@ -205,9 +205,9 @@ function newBeyondFile(list) {
|
|
|
205
205
|
flag: list.flag.filter((x) => !file.has(x))
|
|
206
206
|
};
|
|
207
207
|
}
|
|
208
|
-
function seg(n, singular,
|
|
208
|
+
function seg(n, singular, plural2) {
|
|
209
209
|
if (n === 0) return null;
|
|
210
|
-
return `+${n} ${n === 1 ? singular :
|
|
210
|
+
return `+${n} ${n === 1 ? singular : plural2}`;
|
|
211
211
|
}
|
|
212
212
|
function computePostureWarnings(flags, env, stored) {
|
|
213
213
|
if (stored === void 0) return [];
|
|
@@ -400,7 +400,7 @@ var InviteExpiredError = class extends Error {
|
|
|
400
400
|
}
|
|
401
401
|
};
|
|
402
402
|
async function redeemInvite(relayHttpUrl, code) {
|
|
403
|
-
const { boundedJsonFetch: boundedJsonFetch2 } = await import("./http-
|
|
403
|
+
const { boundedJsonFetch: boundedJsonFetch2 } = await import("./http-DZ3ZHOHX.js");
|
|
404
404
|
const r = await boundedJsonFetch2(`${relayHttpUrl.replace(/\/$/, "")}/invites/redeem`, {
|
|
405
405
|
method: "POST",
|
|
406
406
|
headers: { "content-type": "application/json" },
|
|
@@ -492,7 +492,7 @@ function validateReplacementToken(newToken, currentToken, nowMs = Date.now()) {
|
|
|
492
492
|
return { ok: true };
|
|
493
493
|
}
|
|
494
494
|
async function mintAccessToken(relayHttpUrl, agentKey, fetchFn = fetch, dpopProof) {
|
|
495
|
-
const { boundedJsonFetch: boundedJsonFetch2 } = await import("./http-
|
|
495
|
+
const { boundedJsonFetch: boundedJsonFetch2 } = await import("./http-DZ3ZHOHX.js");
|
|
496
496
|
const r = await boundedJsonFetch2(`${relayHttpUrl.replace(/\/$/, "")}/agents/token`, {
|
|
497
497
|
method: "POST",
|
|
498
498
|
headers: {
|
|
@@ -1770,6 +1770,44 @@ function dropVendorNotifications(input) {
|
|
|
1770
1770
|
});
|
|
1771
1771
|
}
|
|
1772
1772
|
|
|
1773
|
+
// src/acp/stderrNoise.ts
|
|
1774
|
+
var TEARDOWN_HEAD = /cancellation failed during teardown/;
|
|
1775
|
+
var CONTINUATION = /^(\s+at\s|\s*[{}]\s*$|\s+(telemetryMessage|errorClass|exitCode)\s*:)/;
|
|
1776
|
+
function createTeardownNoiseFilter(isStopping) {
|
|
1777
|
+
let pending = "";
|
|
1778
|
+
let dropping = false;
|
|
1779
|
+
function line(l) {
|
|
1780
|
+
if (dropping) {
|
|
1781
|
+
if (CONTINUATION.test(l)) return "";
|
|
1782
|
+
dropping = false;
|
|
1783
|
+
}
|
|
1784
|
+
if (isStopping() && TEARDOWN_HEAD.test(l)) {
|
|
1785
|
+
dropping = true;
|
|
1786
|
+
return "";
|
|
1787
|
+
}
|
|
1788
|
+
return l + "\n";
|
|
1789
|
+
}
|
|
1790
|
+
return {
|
|
1791
|
+
push(chunk) {
|
|
1792
|
+
pending += chunk;
|
|
1793
|
+
let out = "";
|
|
1794
|
+
let nl;
|
|
1795
|
+
while ((nl = pending.indexOf("\n")) !== -1) {
|
|
1796
|
+
out += line(pending.slice(0, nl));
|
|
1797
|
+
pending = pending.slice(nl + 1);
|
|
1798
|
+
}
|
|
1799
|
+
return out;
|
|
1800
|
+
},
|
|
1801
|
+
flush() {
|
|
1802
|
+
const rest = pending;
|
|
1803
|
+
pending = "";
|
|
1804
|
+
if (!rest) return "";
|
|
1805
|
+
if (dropping && CONTINUATION.test(rest)) return "";
|
|
1806
|
+
return rest;
|
|
1807
|
+
}
|
|
1808
|
+
};
|
|
1809
|
+
}
|
|
1810
|
+
|
|
1773
1811
|
// src/acp/client.ts
|
|
1774
1812
|
import { randomUUID } from "crypto";
|
|
1775
1813
|
|
|
@@ -2106,7 +2144,17 @@ var AcpClient = class {
|
|
|
2106
2144
|
detached: true
|
|
2107
2145
|
});
|
|
2108
2146
|
this.child = child;
|
|
2109
|
-
|
|
2147
|
+
const noise = createTeardownNoiseFilter(() => this.stopping);
|
|
2148
|
+
pipeSanitizedToTty(child.stderr, {
|
|
2149
|
+
write: (s) => {
|
|
2150
|
+
const kept = noise.push(s);
|
|
2151
|
+
if (kept) process.stderr.write(kept);
|
|
2152
|
+
}
|
|
2153
|
+
});
|
|
2154
|
+
child.on("close", () => {
|
|
2155
|
+
const rest = noise.flush();
|
|
2156
|
+
if (rest) process.stderr.write(rest);
|
|
2157
|
+
});
|
|
2110
2158
|
child.on("error", (err) => {
|
|
2111
2159
|
this.failPending(new Error(`ACP agent subprocess error: ${err.message}`));
|
|
2112
2160
|
});
|
|
@@ -2691,6 +2739,9 @@ function modelLaunchFor(agent, model) {
|
|
|
2691
2739
|
}
|
|
2692
2740
|
}
|
|
2693
2741
|
|
|
2742
|
+
// src/adapter.ts
|
|
2743
|
+
import { execFileSync as execFileSync2 } from "child_process";
|
|
2744
|
+
|
|
2694
2745
|
// src/scope/sandbox.ts
|
|
2695
2746
|
import { SandboxManager, SandboxRuntimeConfigSchema, installWindowsSandbox, getSrtWinPath } from "@anthropic-ai/sandbox-runtime";
|
|
2696
2747
|
import path4 from "path";
|
|
@@ -3434,6 +3485,137 @@ function classifyCatchUp(messages, agentName, nowMs, opts) {
|
|
|
3434
3485
|
return { context: inWindow, mentions: capped };
|
|
3435
3486
|
}
|
|
3436
3487
|
|
|
3488
|
+
// src/connStatus.ts
|
|
3489
|
+
var ESCALATE_FIRST_MS = 6e4;
|
|
3490
|
+
var ESCALATE_REPEAT_MS = 3e5;
|
|
3491
|
+
var CLOSE_TRY_AGAIN_LATER = 1013;
|
|
3492
|
+
function fmtDur(ms) {
|
|
3493
|
+
const s = Math.max(0, Math.floor(ms / 1e3));
|
|
3494
|
+
if (s < 60) return `${s}s`;
|
|
3495
|
+
const m = Math.floor(s / 60);
|
|
3496
|
+
if (m < 60) return `${m}m${String(s % 60).padStart(2, "0")}s`;
|
|
3497
|
+
return `${Math.floor(m / 60)}h${String(m % 60).padStart(2, "0")}m`;
|
|
3498
|
+
}
|
|
3499
|
+
function fmtClock(epochMs) {
|
|
3500
|
+
const d = new Date(epochMs);
|
|
3501
|
+
return `${String(d.getHours()).padStart(2, "0")}:${String(d.getMinutes()).padStart(2, "0")}`;
|
|
3502
|
+
}
|
|
3503
|
+
function plural(n, one, many) {
|
|
3504
|
+
return `${n} ${n === 1 ? one : many}`;
|
|
3505
|
+
}
|
|
3506
|
+
function createConnStatus(opts) {
|
|
3507
|
+
const now = opts.now ?? Date.now;
|
|
3508
|
+
const startedAt = now();
|
|
3509
|
+
let state = "starting";
|
|
3510
|
+
let connectedSince = null;
|
|
3511
|
+
let offlineSince = null;
|
|
3512
|
+
let attempts = 0;
|
|
3513
|
+
let reconnects = 0;
|
|
3514
|
+
let lastReconnectAt = null;
|
|
3515
|
+
let tokenExp = null;
|
|
3516
|
+
let lastCloseCode = null;
|
|
3517
|
+
let nextEscalateAt = null;
|
|
3518
|
+
let tryAgainLaterSaid = false;
|
|
3519
|
+
function noteDrop() {
|
|
3520
|
+
if (state !== "offline") {
|
|
3521
|
+
offlineSince = now();
|
|
3522
|
+
attempts = 0;
|
|
3523
|
+
nextEscalateAt = offlineSince + ESCALATE_FIRST_MS;
|
|
3524
|
+
}
|
|
3525
|
+
state = "offline";
|
|
3526
|
+
connectedSince = null;
|
|
3527
|
+
attempts++;
|
|
3528
|
+
}
|
|
3529
|
+
function closeEpisode(t) {
|
|
3530
|
+
if (offlineSince === null || attempts === 0) return null;
|
|
3531
|
+
const down = fmtDur(t - offlineSince);
|
|
3532
|
+
const suffix = attempts > 1 ? `${down}, ${plural(attempts, "attempt", "attempts")}` : down;
|
|
3533
|
+
offlineSince = null;
|
|
3534
|
+
nextEscalateAt = null;
|
|
3535
|
+
tryAgainLaterSaid = false;
|
|
3536
|
+
return suffix;
|
|
3537
|
+
}
|
|
3538
|
+
function adoptExp(exp) {
|
|
3539
|
+
if (exp !== void 0) tokenExp = exp;
|
|
3540
|
+
}
|
|
3541
|
+
function handle(e) {
|
|
3542
|
+
switch (e.kind) {
|
|
3543
|
+
case "connected": {
|
|
3544
|
+
const t = now();
|
|
3545
|
+
const hadRetries = offlineSince !== null && attempts > 0;
|
|
3546
|
+
const n = attempts;
|
|
3547
|
+
const since = offlineSince;
|
|
3548
|
+
const ep = closeEpisode(t);
|
|
3549
|
+
state = "connected";
|
|
3550
|
+
connectedSince = t;
|
|
3551
|
+
adoptExp(e.tokenExp);
|
|
3552
|
+
if (hadRetries && ep !== null && since !== null) {
|
|
3553
|
+
opts.log(`[braid] connected (after ${plural(n, "attempt", "attempts")}, ${fmtDur(t - since)})`);
|
|
3554
|
+
}
|
|
3555
|
+
break;
|
|
3556
|
+
}
|
|
3557
|
+
case "reconnected": {
|
|
3558
|
+
const t = now();
|
|
3559
|
+
const ep = closeEpisode(t);
|
|
3560
|
+
state = "connected";
|
|
3561
|
+
connectedSince = t;
|
|
3562
|
+
reconnects++;
|
|
3563
|
+
lastReconnectAt = t;
|
|
3564
|
+
adoptExp(e.tokenExp);
|
|
3565
|
+
opts.log(`[braid] reconnected${ep !== null ? ` (offline ${ep})` : ""}`);
|
|
3566
|
+
break;
|
|
3567
|
+
}
|
|
3568
|
+
case "disconnected":
|
|
3569
|
+
lastCloseCode = e.code;
|
|
3570
|
+
noteDrop();
|
|
3571
|
+
if (e.code === CLOSE_TRY_AGAIN_LATER) {
|
|
3572
|
+
if (!tryAgainLaterSaid) {
|
|
3573
|
+
tryAgainLaterSaid = true;
|
|
3574
|
+
opts.warn(
|
|
3575
|
+
`[braid] relay asked us to try again later${e.reason ? ` (${e.reason})` : ""}; reconnecting automatically`
|
|
3576
|
+
);
|
|
3577
|
+
}
|
|
3578
|
+
} else if (opts.verbose) {
|
|
3579
|
+
opts.log(`[braid] disconnected (close ${e.code})${e.reason ? `: ${e.reason}` : ""}`);
|
|
3580
|
+
}
|
|
3581
|
+
break;
|
|
3582
|
+
case "connectTimeout":
|
|
3583
|
+
noteDrop();
|
|
3584
|
+
if (opts.verbose) {
|
|
3585
|
+
opts.log(
|
|
3586
|
+
`[braid] connect attempt timed out after ${e.timeoutSecs}s (socket accepted but the relay never completed the handshake); retrying`
|
|
3587
|
+
);
|
|
3588
|
+
}
|
|
3589
|
+
break;
|
|
3590
|
+
case "remintStarted":
|
|
3591
|
+
if (opts.verbose) opts.log("[braid] access token rejected; re-minting from agent key");
|
|
3592
|
+
break;
|
|
3593
|
+
case "tokenRefreshed":
|
|
3594
|
+
adoptExp(e.tokenExp);
|
|
3595
|
+
if (opts.verbose) opts.log("[braid] token refreshed");
|
|
3596
|
+
break;
|
|
3597
|
+
case "tokenReminted":
|
|
3598
|
+
adoptExp(e.tokenExp);
|
|
3599
|
+
if (opts.verbose) opts.log("[braid] token proactively re-minted before expiry");
|
|
3600
|
+
break;
|
|
3601
|
+
}
|
|
3602
|
+
}
|
|
3603
|
+
function tick() {
|
|
3604
|
+
if (state !== "offline" || offlineSince === null || nextEscalateAt === null) return;
|
|
3605
|
+
const t = now();
|
|
3606
|
+
if (t >= nextEscalateAt) {
|
|
3607
|
+
opts.warn(
|
|
3608
|
+
`[braid] still reconnecting: offline ${fmtDur(t - offlineSince)} (${plural(attempts, "attempt", "attempts")})`
|
|
3609
|
+
);
|
|
3610
|
+
nextEscalateAt = t + ESCALATE_REPEAT_MS;
|
|
3611
|
+
}
|
|
3612
|
+
}
|
|
3613
|
+
function snapshot() {
|
|
3614
|
+
return { state, startedAt, connectedSince, offlineSince, attempts, reconnects, lastReconnectAt, tokenExp, lastCloseCode };
|
|
3615
|
+
}
|
|
3616
|
+
return { handle, tick, snapshot };
|
|
3617
|
+
}
|
|
3618
|
+
|
|
3437
3619
|
// src/cardIdentity.ts
|
|
3438
3620
|
import { createRemoteJWKSet, jwtVerify } from "jose";
|
|
3439
3621
|
|
|
@@ -4157,7 +4339,7 @@ var RelayClient = class {
|
|
|
4157
4339
|
this.fatalCb?.(code, reason);
|
|
4158
4340
|
return;
|
|
4159
4341
|
}
|
|
4160
|
-
if (!this.helloReceived) {
|
|
4342
|
+
if (!this.helloReceived && code !== CLOSE_TRY_AGAIN_LATER) {
|
|
4161
4343
|
this.preHelloFailures++;
|
|
4162
4344
|
if (this.preHelloFailures >= PRE_HELLO_HINT_AFTER && !this.handshakeHintShown) {
|
|
4163
4345
|
this.handshakeHintShown = true;
|
|
@@ -6101,8 +6283,11 @@ function resolvePathBinary(binary, hint) {
|
|
|
6101
6283
|
function launchSpecFor(agent) {
|
|
6102
6284
|
const cwd = process.cwd();
|
|
6103
6285
|
switch (agent) {
|
|
6104
|
-
case "claude-code":
|
|
6105
|
-
|
|
6286
|
+
case "claude-code": {
|
|
6287
|
+
const bin = resolveClaudeBinary();
|
|
6288
|
+
const env = bin.source === "bundled" ? {} : { env: { CLAUDE_CODE_EXECUTABLE: bin.path } };
|
|
6289
|
+
return { ...npxSpec(pinned("@agentclientprotocol/claude-agent-acp")), cwd, ...env };
|
|
6290
|
+
}
|
|
6106
6291
|
// codex: canonical adapter (BL-120, 2026-06-28). 1.x prefers CODEX_API_KEY,
|
|
6107
6292
|
// falling back to OPENAI_API_KEY; both pass through buildAcpEnv unchanged.
|
|
6108
6293
|
case "codex":
|
|
@@ -6200,6 +6385,35 @@ function resolveHandoffBinary(name, install) {
|
|
|
6200
6385
|
handoffBinaryAbs.set(name, found);
|
|
6201
6386
|
return found;
|
|
6202
6387
|
}
|
|
6388
|
+
var claudeBinaryCached;
|
|
6389
|
+
function claudeVersion(bin) {
|
|
6390
|
+
try {
|
|
6391
|
+
const out = execFileSync2(bin, ["--version"], { encoding: "utf8", timeout: 8e3, stdio: ["ignore", "pipe", "ignore"] });
|
|
6392
|
+
return out.trim().split("\n")[0] || null;
|
|
6393
|
+
} catch {
|
|
6394
|
+
return null;
|
|
6395
|
+
}
|
|
6396
|
+
}
|
|
6397
|
+
function resolveClaudeBinary(env = process.env, home = homedir5(), platform = process.platform) {
|
|
6398
|
+
if (claudeBinaryCached) return claudeBinaryCached;
|
|
6399
|
+
const explicit = env.CLAUDE_CODE_EXECUTABLE?.trim();
|
|
6400
|
+
let out;
|
|
6401
|
+
if (explicit) {
|
|
6402
|
+
out = { path: explicit, version: claudeVersion(explicit), source: "env" };
|
|
6403
|
+
} else {
|
|
6404
|
+
const found = findHandoffBinary("claude", env.PATH ?? "", home, platform);
|
|
6405
|
+
out = found ? { path: found, version: claudeVersion(found), source: "path" } : { path: "", version: null, source: "bundled" };
|
|
6406
|
+
}
|
|
6407
|
+
claudeBinaryCached = out;
|
|
6408
|
+
return out;
|
|
6409
|
+
}
|
|
6410
|
+
function describeClaudeBinary(b) {
|
|
6411
|
+
if (b.source === "bundled") {
|
|
6412
|
+
return "adapter-bundled copy (no `claude` on PATH or ~/.local/bin; install Claude Code so `claude update` keeps this current)";
|
|
6413
|
+
}
|
|
6414
|
+
const ver = b.version ? ` (${b.version})` : "";
|
|
6415
|
+
return b.source === "env" ? `${b.path}${ver} [CLAUDE_CODE_EXECUTABLE]` : `${b.path}${ver}, the one \`claude update\` maintains`;
|
|
6416
|
+
}
|
|
6203
6417
|
var HANDOFF_RESUME_SPECS = {
|
|
6204
6418
|
"claude-code": (sessionId, cwd) => ({ command: resolveHandoffBinary("claude", "Claude Code"), args: ["--resume", sessionId], cwd }),
|
|
6205
6419
|
codex: (sessionId, cwd) => ({ command: resolveHandoffBinary("codex", "the Codex CLI"), args: ["resume", sessionId], cwd }),
|
|
@@ -7396,7 +7610,7 @@ function buildVersion() {
|
|
|
7396
7610
|
return "";
|
|
7397
7611
|
}
|
|
7398
7612
|
}
|
|
7399
|
-
function startingLines(cfg) {
|
|
7613
|
+
function startingLines(cfg, extras = {}) {
|
|
7400
7614
|
const row = (label, value) => `${label.padEnd(8)} : ${value}`;
|
|
7401
7615
|
const anthropicCredential = cfg.agentMode !== "acp" || cfg.agent === "claude-code";
|
|
7402
7616
|
const catchUpHours = cfg.catchUpTtlMs % 36e5 === 0 ? `${cfg.catchUpTtlMs / 36e5}h` : `${Math.round(cfg.catchUpTtlMs / 6e4)}min`;
|
|
@@ -7408,6 +7622,9 @@ function startingLines(cfg) {
|
|
|
7408
7622
|
row("agent", sanitizeForTty(cfg.agentName)),
|
|
7409
7623
|
row("relay", sanitizeForTty(cfg.relayWsUrl)),
|
|
7410
7624
|
row("provider", cfg.agentMode === "acp" ? cfg.agent : "bundled SDK (generic mode)"),
|
|
7625
|
+
// Which Claude Code binary claude-code turns run (2026-09-21): the user's own, so
|
|
7626
|
+
// `claude update` is the fix for a client-version refusal, never a braid release.
|
|
7627
|
+
...extras.claudeBinary ? [row("claude", sanitizeForTty(extras.claudeBinary))] : [],
|
|
7411
7628
|
row("model", sanitizeForTty(redactConfig(cfg).model)),
|
|
7412
7629
|
row("fallback", cfg.fallbackModel ? sanitizeForTty(cfg.fallbackModel) : "(none \u2014 a rate limit is surfaced, not switched)"),
|
|
7413
7630
|
row("tools", cfg.toolMode),
|
|
@@ -7677,6 +7894,9 @@ export {
|
|
|
7677
7894
|
isAddressed,
|
|
7678
7895
|
isAddressedByReply,
|
|
7679
7896
|
classifyCatchUp,
|
|
7897
|
+
fmtDur,
|
|
7898
|
+
fmtClock,
|
|
7899
|
+
createConnStatus,
|
|
7680
7900
|
WS_MAX_PAYLOAD_BYTES,
|
|
7681
7901
|
RelayClient,
|
|
7682
7902
|
SourceBroker,
|
|
@@ -7685,6 +7905,8 @@ export {
|
|
|
7685
7905
|
scopedLaunch,
|
|
7686
7906
|
resetScope,
|
|
7687
7907
|
sandboxInstaller,
|
|
7908
|
+
resolveClaudeBinary,
|
|
7909
|
+
describeClaudeBinary,
|
|
7688
7910
|
interactiveHandoffSupported,
|
|
7689
7911
|
interactiveResumeSpecFor,
|
|
7690
7912
|
createAdapter,
|
package/dist/cli.js
CHANGED
|
@@ -29,11 +29,15 @@ import {
|
|
|
29
29
|
classifyFatal,
|
|
30
30
|
configDir,
|
|
31
31
|
createAdapter,
|
|
32
|
+
createConnStatus,
|
|
32
33
|
createEphemeralLisaAdapter,
|
|
33
34
|
createWorkspaceNoticeGate,
|
|
34
35
|
decodeInvite,
|
|
36
|
+
describeClaudeBinary,
|
|
35
37
|
distinctWorkspaceNames,
|
|
36
38
|
effectiveAnthropicCredential,
|
|
39
|
+
fmtClock,
|
|
40
|
+
fmtDur,
|
|
37
41
|
handoffCwdFor,
|
|
38
42
|
hasSubscriptionLogin,
|
|
39
43
|
initScopeRuntime,
|
|
@@ -59,6 +63,7 @@ import {
|
|
|
59
63
|
relayHostSegment,
|
|
60
64
|
resetScope,
|
|
61
65
|
resolveAgentSelection,
|
|
66
|
+
resolveClaudeBinary,
|
|
62
67
|
resolveConfig,
|
|
63
68
|
resolveIpcProfile,
|
|
64
69
|
resolveKnobs,
|
|
@@ -76,7 +81,7 @@ import {
|
|
|
76
81
|
updateAgentWorkspace,
|
|
77
82
|
validateRelayWsUrl,
|
|
78
83
|
wsToHttp
|
|
79
|
-
} from "./chunk-
|
|
84
|
+
} from "./chunk-DIUVFB7Z.js";
|
|
80
85
|
import {
|
|
81
86
|
clampUntrusted,
|
|
82
87
|
externalSourceLabel,
|
|
@@ -5712,127 +5717,6 @@ function installTee(outWriter, errWriter) {
|
|
|
5712
5717
|
// src/cli.ts
|
|
5713
5718
|
import { homedir as homedir6 } from "os";
|
|
5714
5719
|
|
|
5715
|
-
// src/connStatus.ts
|
|
5716
|
-
var ESCALATE_FIRST_MS = 6e4;
|
|
5717
|
-
var ESCALATE_REPEAT_MS = 3e5;
|
|
5718
|
-
function fmtDur(ms) {
|
|
5719
|
-
const s = Math.max(0, Math.floor(ms / 1e3));
|
|
5720
|
-
if (s < 60) return `${s}s`;
|
|
5721
|
-
const m = Math.floor(s / 60);
|
|
5722
|
-
if (m < 60) return `${m}m${String(s % 60).padStart(2, "0")}s`;
|
|
5723
|
-
return `${Math.floor(m / 60)}h${String(m % 60).padStart(2, "0")}m`;
|
|
5724
|
-
}
|
|
5725
|
-
function fmtClock(epochMs) {
|
|
5726
|
-
const d = new Date(epochMs);
|
|
5727
|
-
return `${String(d.getHours()).padStart(2, "0")}:${String(d.getMinutes()).padStart(2, "0")}`;
|
|
5728
|
-
}
|
|
5729
|
-
function plural(n, one, many) {
|
|
5730
|
-
return `${n} ${n === 1 ? one : many}`;
|
|
5731
|
-
}
|
|
5732
|
-
function createConnStatus(opts) {
|
|
5733
|
-
const now = opts.now ?? Date.now;
|
|
5734
|
-
const startedAt = now();
|
|
5735
|
-
let state = "starting";
|
|
5736
|
-
let connectedSince = null;
|
|
5737
|
-
let offlineSince = null;
|
|
5738
|
-
let attempts = 0;
|
|
5739
|
-
let reconnects = 0;
|
|
5740
|
-
let lastReconnectAt = null;
|
|
5741
|
-
let tokenExp = null;
|
|
5742
|
-
let lastCloseCode = null;
|
|
5743
|
-
let nextEscalateAt = null;
|
|
5744
|
-
function noteDrop() {
|
|
5745
|
-
if (state !== "offline") {
|
|
5746
|
-
offlineSince = now();
|
|
5747
|
-
attempts = 0;
|
|
5748
|
-
nextEscalateAt = offlineSince + ESCALATE_FIRST_MS;
|
|
5749
|
-
}
|
|
5750
|
-
state = "offline";
|
|
5751
|
-
connectedSince = null;
|
|
5752
|
-
attempts++;
|
|
5753
|
-
}
|
|
5754
|
-
function closeEpisode(t) {
|
|
5755
|
-
if (offlineSince === null || attempts === 0) return null;
|
|
5756
|
-
const down = fmtDur(t - offlineSince);
|
|
5757
|
-
const suffix = attempts > 1 ? `${down}, ${plural(attempts, "attempt", "attempts")}` : down;
|
|
5758
|
-
offlineSince = null;
|
|
5759
|
-
nextEscalateAt = null;
|
|
5760
|
-
return suffix;
|
|
5761
|
-
}
|
|
5762
|
-
function adoptExp(exp) {
|
|
5763
|
-
if (exp !== void 0) tokenExp = exp;
|
|
5764
|
-
}
|
|
5765
|
-
function handle(e) {
|
|
5766
|
-
switch (e.kind) {
|
|
5767
|
-
case "connected": {
|
|
5768
|
-
const t = now();
|
|
5769
|
-
const hadRetries = offlineSince !== null && attempts > 0;
|
|
5770
|
-
const n = attempts;
|
|
5771
|
-
const since = offlineSince;
|
|
5772
|
-
const ep = closeEpisode(t);
|
|
5773
|
-
state = "connected";
|
|
5774
|
-
connectedSince = t;
|
|
5775
|
-
adoptExp(e.tokenExp);
|
|
5776
|
-
if (hadRetries && ep !== null && since !== null) {
|
|
5777
|
-
opts.log(`[braid] connected (after ${plural(n, "attempt", "attempts")}, ${fmtDur(t - since)})`);
|
|
5778
|
-
}
|
|
5779
|
-
break;
|
|
5780
|
-
}
|
|
5781
|
-
case "reconnected": {
|
|
5782
|
-
const t = now();
|
|
5783
|
-
const ep = closeEpisode(t);
|
|
5784
|
-
state = "connected";
|
|
5785
|
-
connectedSince = t;
|
|
5786
|
-
reconnects++;
|
|
5787
|
-
lastReconnectAt = t;
|
|
5788
|
-
adoptExp(e.tokenExp);
|
|
5789
|
-
opts.log(`[braid] reconnected${ep !== null ? ` (offline ${ep})` : ""}`);
|
|
5790
|
-
break;
|
|
5791
|
-
}
|
|
5792
|
-
case "disconnected":
|
|
5793
|
-
lastCloseCode = e.code;
|
|
5794
|
-
noteDrop();
|
|
5795
|
-
if (opts.verbose) {
|
|
5796
|
-
opts.log(`[braid] disconnected (close ${e.code})${e.reason ? `: ${e.reason}` : ""}`);
|
|
5797
|
-
}
|
|
5798
|
-
break;
|
|
5799
|
-
case "connectTimeout":
|
|
5800
|
-
noteDrop();
|
|
5801
|
-
if (opts.verbose) {
|
|
5802
|
-
opts.log(
|
|
5803
|
-
`[braid] connect attempt timed out after ${e.timeoutSecs}s (socket accepted but the relay never completed the handshake); retrying`
|
|
5804
|
-
);
|
|
5805
|
-
}
|
|
5806
|
-
break;
|
|
5807
|
-
case "remintStarted":
|
|
5808
|
-
if (opts.verbose) opts.log("[braid] access token rejected; re-minting from agent key");
|
|
5809
|
-
break;
|
|
5810
|
-
case "tokenRefreshed":
|
|
5811
|
-
adoptExp(e.tokenExp);
|
|
5812
|
-
if (opts.verbose) opts.log("[braid] token refreshed");
|
|
5813
|
-
break;
|
|
5814
|
-
case "tokenReminted":
|
|
5815
|
-
adoptExp(e.tokenExp);
|
|
5816
|
-
if (opts.verbose) opts.log("[braid] token proactively re-minted before expiry");
|
|
5817
|
-
break;
|
|
5818
|
-
}
|
|
5819
|
-
}
|
|
5820
|
-
function tick() {
|
|
5821
|
-
if (state !== "offline" || offlineSince === null || nextEscalateAt === null) return;
|
|
5822
|
-
const t = now();
|
|
5823
|
-
if (t >= nextEscalateAt) {
|
|
5824
|
-
opts.warn(
|
|
5825
|
-
`[braid] still reconnecting: offline ${fmtDur(t - offlineSince)} (${plural(attempts, "attempt", "attempts")})`
|
|
5826
|
-
);
|
|
5827
|
-
nextEscalateAt = t + ESCALATE_REPEAT_MS;
|
|
5828
|
-
}
|
|
5829
|
-
}
|
|
5830
|
-
function snapshot() {
|
|
5831
|
-
return { state, startedAt, connectedSince, offlineSince, attempts, reconnects, lastReconnectAt, tokenExp, lastCloseCode };
|
|
5832
|
-
}
|
|
5833
|
-
return { handle, tick, snapshot };
|
|
5834
|
-
}
|
|
5835
|
-
|
|
5836
5720
|
// src/statusLine.ts
|
|
5837
5721
|
var ESC = String.fromCharCode(27);
|
|
5838
5722
|
var CLEAR = `\r${ESC}[2K`;
|
|
@@ -6329,7 +6213,9 @@ async function main() {
|
|
|
6329
6213
|
}
|
|
6330
6214
|
assertKeystorePerms(dir, file);
|
|
6331
6215
|
}
|
|
6332
|
-
console.log(banner("braid", "STARTING", startingLines(cfg
|
|
6216
|
+
console.log(banner("braid", "STARTING", startingLines(cfg, {
|
|
6217
|
+
...cfg.agentMode === "acp" && cfg.agent === "claude-code" ? { claudeBinary: describeClaudeBinary(resolveClaudeBinary()) } : {}
|
|
6218
|
+
})));
|
|
6333
6219
|
if (cfg.toolMode === "full") {
|
|
6334
6220
|
console.error(
|
|
6335
6221
|
"[braid] WARNING full tool access: remote channel content can drive local tool use on this machine"
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@snowyroad/braid",
|
|
3
|
-
"version": "0.96.
|
|
3
|
+
"version": "0.96.5",
|
|
4
4
|
"description": "Braid: connect your own coding agent (Claude Code, Codex, Gemini, Grok) to a Braid channel and collaborate with other agents and humans.",
|
|
5
5
|
"license": "SEE LICENSE IN LICENSE.md",
|
|
6
6
|
"author": "SnowyRoad",
|
package/service-shrinkwrap.json
CHANGED
|
@@ -1,12 +1,12 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@snowyroad/braid",
|
|
3
|
-
"version": "0.96.
|
|
3
|
+
"version": "0.96.5",
|
|
4
4
|
"lockfileVersion": 3,
|
|
5
5
|
"requires": true,
|
|
6
6
|
"packages": {
|
|
7
7
|
"": {
|
|
8
8
|
"name": "@snowyroad/braid",
|
|
9
|
-
"version": "0.96.
|
|
9
|
+
"version": "0.96.5",
|
|
10
10
|
"license": "SEE LICENSE IN LICENSE.md",
|
|
11
11
|
"dependencies": {
|
|
12
12
|
"@agentclientprotocol/sdk": "0.25.1",
|