@botbuddy/cli 1.63.35 → 1.63.38
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.
|
Binary file
|
|
Binary file
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
|
-
"release_version": "1.63.
|
|
3
|
-
"source_commit": "
|
|
4
|
-
"source_sha256": "
|
|
2
|
+
"release_version": "1.63.38",
|
|
3
|
+
"source_commit": "d4f75d9b654171260fe0abb50b8d3c656b46959b",
|
|
4
|
+
"source_sha256": "3e988465d360302e88bfe7136d593b6d2942b818de190a2aea36fade6c8dae9f",
|
|
5
5
|
"target": "darwin-arm64",
|
|
6
|
-
"sha256": "
|
|
6
|
+
"sha256": "36a64ac8effd1df9fcdffe575fc220f3cbf5d20facbda4f4e9bf27e744cf8c5e"
|
|
7
7
|
}
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
|
-
"release_version": "1.63.
|
|
3
|
-
"source_commit": "
|
|
4
|
-
"source_sha256": "
|
|
2
|
+
"release_version": "1.63.38",
|
|
3
|
+
"source_commit": "d4f75d9b654171260fe0abb50b8d3c656b46959b",
|
|
4
|
+
"source_sha256": "3e988465d360302e88bfe7136d593b6d2942b818de190a2aea36fade6c8dae9f",
|
|
5
5
|
"target": "linux-x64",
|
|
6
|
-
"sha256": "
|
|
6
|
+
"sha256": "12ed409298d65b59e12c2cef901886aeea622d87130e1694081393b4f0b7e24e"
|
|
7
7
|
}
|
|
@@ -1,21 +1,21 @@
|
|
|
1
1
|
{
|
|
2
2
|
"schema_version": 1,
|
|
3
|
-
"release_version": "1.63.
|
|
4
|
-
"source_commit": "
|
|
5
|
-
"source_sha256": "
|
|
3
|
+
"release_version": "1.63.38",
|
|
4
|
+
"source_commit": "d4f75d9b654171260fe0abb50b8d3c656b46959b",
|
|
5
|
+
"source_sha256": "3e988465d360302e88bfe7136d593b6d2942b818de190a2aea36fade6c8dae9f",
|
|
6
6
|
"targets": {
|
|
7
7
|
"darwin-arm64": {
|
|
8
8
|
"goos": "darwin",
|
|
9
9
|
"goarch": "arm64",
|
|
10
10
|
"binary": "daemon/bin/darwin-arm64/botbuddy-daemon",
|
|
11
|
-
"sha256": "
|
|
11
|
+
"sha256": "36a64ac8effd1df9fcdffe575fc220f3cbf5d20facbda4f4e9bf27e744cf8c5e",
|
|
12
12
|
"mode": "0755"
|
|
13
13
|
},
|
|
14
14
|
"linux-x64": {
|
|
15
15
|
"goos": "linux",
|
|
16
16
|
"goarch": "amd64",
|
|
17
17
|
"binary": "daemon/bin/linux-x64/botbuddy-daemon",
|
|
18
|
-
"sha256": "
|
|
18
|
+
"sha256": "12ed409298d65b59e12c2cef901886aeea622d87130e1694081393b4f0b7e24e",
|
|
19
19
|
"mode": "0755"
|
|
20
20
|
}
|
|
21
21
|
}
|
package/package.json
CHANGED
package/src/wait.mjs
CHANGED
|
@@ -26,6 +26,7 @@ import { SETUP_BLOCK } from "./setup-block.mjs";
|
|
|
26
26
|
import { createWaitCheckpointStore, WaitCheckpointError } from "./wait-checkpoint.mjs";
|
|
27
27
|
import { realpath } from "node:fs/promises";
|
|
28
28
|
import { deepLinkNotice, safeWebUrl } from "./deep-link.mjs";
|
|
29
|
+
import { redactTelemetry } from "./telemetry-outbox.mjs";
|
|
29
30
|
import { assertWaitObserver, readWaitResult, waitCompletionState, waitObserverId, writeWaitReceipt } from "./wait-result.mjs";
|
|
30
31
|
|
|
31
32
|
// A protocol is deliberately distinct from package semver: compatible pinned
|
|
@@ -489,6 +490,25 @@ async function reserveClaimQueueBoundary(opts, conditions) {
|
|
|
489
490
|
}
|
|
490
491
|
}
|
|
491
492
|
|
|
493
|
+
// BOT-2855: the concrete next step for a typed registration refusal, or null when
|
|
494
|
+
// there is none beyond the relay's own detail. Built from the parsed conditions
|
|
495
|
+
// only (repo / PR number), never from a credential.
|
|
496
|
+
export function registerRefusalRecovery(errorCode, conditions) {
|
|
497
|
+
const release = Array.isArray(conditions) ? conditions.find((c) => c?.type === "staging-release") : null;
|
|
498
|
+
const repo = release?.params?.repo ?? "<owner/repo>";
|
|
499
|
+
const pr = release?.params?.pr ?? "<n>";
|
|
500
|
+
const arm = `bb wait 'staging-release:repo=${repo},pr=${pr}'`;
|
|
501
|
+
if (errorCode === "pr_not_merged") {
|
|
502
|
+
return "staging-release arms only after the PR merges. Before the merge, wait for it with "
|
|
503
|
+
+ `bb wait 'pr-state:repo=${repo},pr=${pr}' (or for its CI with bb wait 'ci:repo=${repo},scope=latest,pr=${pr}'), `
|
|
504
|
+
+ `then arm ${arm}`;
|
|
505
|
+
}
|
|
506
|
+
if (errorCode === "pr_merge_sha_unresolved") {
|
|
507
|
+
return `the merge commit is not visible yet; retry ${arm} in a minute`;
|
|
508
|
+
}
|
|
509
|
+
return null;
|
|
510
|
+
}
|
|
511
|
+
|
|
492
512
|
async function registerWait(opts, conditions, deadlineIso) {
|
|
493
513
|
// BOT-1649: a relay wait authenticates from the per-session `bb_sess_` token
|
|
494
514
|
// ($BOTBUDDY_AGENT_SESSION_TOKEN) — the relay derives agent, tenant, and session from it.
|
|
@@ -524,15 +544,34 @@ async function registerWait(opts, conditions, deadlineIso) {
|
|
|
524
544
|
},
|
|
525
545
|
body: JSON.stringify(registerBody),
|
|
526
546
|
});
|
|
547
|
+
// BOT-2855: read an error body ONCE. Each status branch below inspects it and may
|
|
548
|
+
// fall through to the generic `!res.ok` error; a second `res.json()` on the consumed
|
|
549
|
+
// body threw, the catch turned it into `{}`, and the relay's typed reason (e.g.
|
|
550
|
+
// `pr_not_merged`) collapsed to `register responded 400`. Redact any credential a
|
|
551
|
+
// relay detail might echo before it can reach stderr or the receipt.
|
|
552
|
+
let errorBody = null;
|
|
553
|
+
let errorBodyIsJson = false;
|
|
554
|
+
const readErrorBody = async () => {
|
|
555
|
+
if (errorBody) return errorBody;
|
|
556
|
+
const text = await res.text().catch(() => "");
|
|
557
|
+
try {
|
|
558
|
+
const parsed = JSON.parse(text);
|
|
559
|
+
errorBodyIsJson = Boolean(parsed) && typeof parsed === "object" && !Array.isArray(parsed);
|
|
560
|
+
errorBody = errorBodyIsJson ? redactTelemetry(parsed) : {};
|
|
561
|
+
} catch {
|
|
562
|
+
errorBody = {};
|
|
563
|
+
}
|
|
564
|
+
return errorBody;
|
|
565
|
+
};
|
|
527
566
|
if (res.status === 429) {
|
|
528
|
-
const body = await
|
|
567
|
+
const body = await readErrorBody();
|
|
529
568
|
const err = new Error(body.detail || "wait_session_cap_exceeded");
|
|
530
569
|
err.cap = true;
|
|
531
570
|
err.errorCode = body.error || "wait_session_cap_exceeded";
|
|
532
571
|
throw err;
|
|
533
572
|
}
|
|
534
573
|
if (res.status === 401) {
|
|
535
|
-
const body = await
|
|
574
|
+
const body = await readErrorBody();
|
|
536
575
|
const err = new Error(body.detail || body.message || body.error || "unauthorized agent key");
|
|
537
576
|
err.auth = true;
|
|
538
577
|
err.errorCode = body.error || "unauthorized";
|
|
@@ -548,7 +587,7 @@ async function registerWait(opts, conditions, deadlineIso) {
|
|
|
548
587
|
// A TRANSIENT backend 400 (e.g. `register_failed` from the RPC) is NOT
|
|
549
588
|
// caller-actionable — fall through to the existing live-only fallback so a valid
|
|
550
589
|
// wait still arms (Codex round-7 P2).
|
|
551
|
-
const body = await
|
|
590
|
+
const body = await readErrorBody();
|
|
552
591
|
const INVALID_CONDITION_CODES = new Set([
|
|
553
592
|
"no_signal_source",
|
|
554
593
|
"review_guard_registration_failed",
|
|
@@ -593,6 +632,9 @@ async function registerWait(opts, conditions, deadlineIso) {
|
|
|
593
632
|
// caller-actionable hard stops, not the untracked live-only fallback.
|
|
594
633
|
"chat_not_participant",
|
|
595
634
|
"chat_participant_ambiguous",
|
|
635
|
+
// BOT-2855: BOT-1458 AC-3 — staging-release arms only for a merged PR. Before the
|
|
636
|
+
// merge the relay refuses it by design; the caller must wait for the merge first.
|
|
637
|
+
"pr_not_merged",
|
|
596
638
|
// BOT-2318: command-run targets are authorization-bound to the registering
|
|
597
639
|
// tenant, agent and work session, and the condition cannot be composed.
|
|
598
640
|
// Both refusals require the caller to change its target/conditions; falling
|
|
@@ -609,7 +651,7 @@ async function registerWait(opts, conditions, deadlineIso) {
|
|
|
609
651
|
}
|
|
610
652
|
}
|
|
611
653
|
if (res.status === 404) {
|
|
612
|
-
const body = await
|
|
654
|
+
const body = await readErrorBody();
|
|
613
655
|
if (body.error === "linear_issue_not_found") {
|
|
614
656
|
const err = new Error(body.detail || body.error);
|
|
615
657
|
err.invalidCondition = true;
|
|
@@ -632,7 +674,7 @@ async function registerWait(opts, conditions, deadlineIso) {
|
|
|
632
674
|
}
|
|
633
675
|
}
|
|
634
676
|
if (res.status === 403) {
|
|
635
|
-
const body = await
|
|
677
|
+
const body = await readErrorBody();
|
|
636
678
|
if (new Set(["wait_tenant_unresolved", "wait_cross_tenant", "wait_tenant_mismatch", "wait_resume_identity_mismatch"]).has(body.error)) {
|
|
637
679
|
const err = new Error(body.detail || body.error);
|
|
638
680
|
err.invalidCondition = true;
|
|
@@ -655,7 +697,7 @@ async function registerWait(opts, conditions, deadlineIso) {
|
|
|
655
697
|
throw err;
|
|
656
698
|
}
|
|
657
699
|
if (res.status === 426) {
|
|
658
|
-
const body = await
|
|
700
|
+
const body = await readErrorBody();
|
|
659
701
|
const err = new Error(body.detail || "this wait client is no longer supported");
|
|
660
702
|
err.clientOutdated = true;
|
|
661
703
|
err.errorCode = body.error || "client_outdated";
|
|
@@ -669,7 +711,7 @@ async function registerWait(opts, conditions, deadlineIso) {
|
|
|
669
711
|
// unblocked wait would skip the server's initial evaluation and could resume a
|
|
670
712
|
// coding session on a false already_unblocked. Surface the typed receipt and a
|
|
671
713
|
// concrete exit code (5=backend/retryable, 4=invalid/not-found).
|
|
672
|
-
const body = await
|
|
714
|
+
const body = await readErrorBody();
|
|
673
715
|
if (body.error === "unblocked_projection_unresolved" || body.error === "unblocked_ticket_not_found") {
|
|
674
716
|
const err = new Error(body.detail || body.error);
|
|
675
717
|
err.hydrationRegister = true;
|
|
@@ -682,12 +724,20 @@ async function registerWait(opts, conditions, deadlineIso) {
|
|
|
682
724
|
}
|
|
683
725
|
}
|
|
684
726
|
if (!res.ok) {
|
|
685
|
-
const body = await
|
|
686
|
-
const
|
|
727
|
+
const body = await readErrorBody();
|
|
728
|
+
const typedCode = typeof body.error === "string" && body.error ? body.error : null;
|
|
729
|
+
const detail = [body.detail, body.message].find((v) => typeof v === "string" && v) ?? null;
|
|
730
|
+
const err = new Error(detail || typedCode ||
|
|
731
|
+
`register responded ${res.status}${errorBodyIsJson ? "" : " with a non-JSON body"}`);
|
|
732
|
+
// BOT-2855: keep the relay's typed reason for the caller and the receipt.
|
|
733
|
+
err.errorCode = typedCode || "register_failed";
|
|
734
|
+
err.httpStatus = res.status;
|
|
687
735
|
if (body.error === "initial_eval_failed") {
|
|
688
736
|
err.initialEval = true;
|
|
689
|
-
err.errorCode = body.error;
|
|
690
737
|
err.detail = body.detail || "initial evaluation failed";
|
|
738
|
+
} else if (body.error === "pr_merge_sha_unresolved") {
|
|
739
|
+
// Merged, but neither the projection nor GitHub returned the merge commit yet.
|
|
740
|
+
err.retryableRegister = true;
|
|
691
741
|
}
|
|
692
742
|
throw err;
|
|
693
743
|
}
|
|
@@ -2352,15 +2402,35 @@ export async function runWait(argv, { recoveryLocalWaitId = null } = {}) {
|
|
|
2352
2402
|
}
|
|
2353
2403
|
}
|
|
2354
2404
|
process.stderr.write(`bb-wait: invalid condition (${err.errorCode}) — ${err.message}\n`);
|
|
2405
|
+
const recovery = registerRefusalRecovery(err.errorCode, conditions);
|
|
2406
|
+
if (recovery) process.stderr.write(`bb-wait: next step: ${recovery}\n`);
|
|
2355
2407
|
await emitReceipt({
|
|
2356
2408
|
schema_version: 1,
|
|
2357
2409
|
outcome: "error",
|
|
2358
2410
|
error: err.errorCode,
|
|
2411
|
+
error_code: err.errorCode,
|
|
2359
2412
|
detail: String(err.message),
|
|
2360
2413
|
...(Array.isArray(err.candidates) && err.candidates.length > 0 ? { candidates: err.candidates } : {}),
|
|
2414
|
+
...(recovery ? { recovery } : {}),
|
|
2361
2415
|
});
|
|
2362
2416
|
process.exit(EXIT.INVALID);
|
|
2363
2417
|
}
|
|
2418
|
+
if (err && err.retryableRegister) {
|
|
2419
|
+
// BOT-2855: a merged PR whose merge commit is not visible yet — retryable.
|
|
2420
|
+
const recovery = registerRefusalRecovery(err.errorCode, conditions);
|
|
2421
|
+
process.stderr.write(`bb-wait: wait-session registration failed (${err.errorCode}) — ${err.message}\n`);
|
|
2422
|
+
if (recovery) process.stderr.write(`bb-wait: next step: ${recovery}\n`);
|
|
2423
|
+
await emitReceipt({
|
|
2424
|
+
schema_version: 1,
|
|
2425
|
+
outcome: "error",
|
|
2426
|
+
error: err.errorCode,
|
|
2427
|
+
error_code: err.errorCode,
|
|
2428
|
+
detail: String(err.message),
|
|
2429
|
+
http_status: err.httpStatus ?? null,
|
|
2430
|
+
...(recovery ? { recovery } : {}),
|
|
2431
|
+
});
|
|
2432
|
+
process.exit(EXIT.BACKEND);
|
|
2433
|
+
}
|
|
2364
2434
|
if (err && err.initialEval) {
|
|
2365
2435
|
process.stderr.write(`bb-wait: initial wait evaluation failed (${err.errorCode}) — ${err.detail}\n`);
|
|
2366
2436
|
await emitReceipt({ schema_version: 1, outcome: "error", error: err.errorCode, detail: String(err.detail) });
|
|
@@ -2375,9 +2445,19 @@ export async function runWait(argv, { recoveryLocalWaitId = null } = {}) {
|
|
|
2375
2445
|
// BOT-1338/1608: every non-timer wait is a relay-tracked, session-token wait.
|
|
2376
2446
|
// If registration did not succeed, neither the server nor this client has
|
|
2377
2447
|
// verified the session's tenant, so there is no safe untracked fallback.
|
|
2378
|
-
|
|
2448
|
+
// BOT-2855: name the relay's typed code (or register_failed when it sent none)
|
|
2449
|
+
// and its status, so the caller can act on it instead of a bare status line.
|
|
2450
|
+
const errorCode = (err && err.errorCode) || "register_failed";
|
|
2451
|
+
process.stderr.write(`bb-wait: wait-session registration failed (${errorCode}) — failing closed, not arming untracked: ${err && err.message || err}\n`);
|
|
2379
2452
|
await emitReceipt(withPrincipalReceipt(
|
|
2380
|
-
{
|
|
2453
|
+
{
|
|
2454
|
+
schema_version: 1,
|
|
2455
|
+
outcome: "error",
|
|
2456
|
+
error: "register_failed",
|
|
2457
|
+
error_code: errorCode,
|
|
2458
|
+
detail: String(err && err.message || err),
|
|
2459
|
+
...(err && Number.isInteger(err.httpStatus) ? { http_status: err.httpStatus } : {}),
|
|
2460
|
+
},
|
|
2381
2461
|
null,
|
|
2382
2462
|
{ sessionTenant: sessionTenant ?? null, agentId: null },
|
|
2383
2463
|
));
|