@coreplane/switchboard 1.261.0 → 1.261.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/assets/deploy/cloudflare-memory/worker.ts +1 -1
- package/dist/assets/package-lock.json +3 -3
- package/dist/assets/package.json +1 -1
- package/dist/assets/source.json +3 -3
- package/dist/assets/src/core/coordinator/driver.ts +17 -5
- package/dist/assets/src/core/reviewVerdict.ts +33 -6
- package/dist/assets/src/core/ship/coordinator.ts +330 -72
- package/dist/assets/src/core/ship/renewal.ts +1 -1
- package/dist/cli.js +842 -510
- package/package.json +1 -1
|
@@ -2963,7 +2963,7 @@ export class RunHistoryDO extends DurableObject<Env> {
|
|
|
2963
2963
|
req.card ? JSON.stringify(req.card) : null,
|
|
2964
2964
|
req.system,
|
|
2965
2965
|
JSON.stringify(req.tools),
|
|
2966
|
-
JSON.stringify(req.state ?? {}),
|
|
2966
|
+
JSON.stringify({ ...existing!.state, ...(req.state ?? {}) }),
|
|
2967
2967
|
req.runId,
|
|
2968
2968
|
);
|
|
2969
2969
|
this.registerSession(req);
|
|
@@ -1,12 +1,12 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "switchboard",
|
|
3
|
-
"version": "1.261.
|
|
3
|
+
"version": "1.261.1",
|
|
4
4
|
"lockfileVersion": 3,
|
|
5
5
|
"requires": true,
|
|
6
6
|
"packages": {
|
|
7
7
|
"": {
|
|
8
8
|
"name": "switchboard",
|
|
9
|
-
"version": "1.261.
|
|
9
|
+
"version": "1.261.1",
|
|
10
10
|
"license": "Apache-2.0",
|
|
11
11
|
"workspaces": [
|
|
12
12
|
"web",
|
|
@@ -21060,7 +21060,7 @@
|
|
|
21060
21060
|
},
|
|
21061
21061
|
"packages/switchboard": {
|
|
21062
21062
|
"name": "@coreplane/switchboard",
|
|
21063
|
-
"version": "1.261.
|
|
21063
|
+
"version": "1.261.1",
|
|
21064
21064
|
"license": "Apache-2.0",
|
|
21065
21065
|
"dependencies": {
|
|
21066
21066
|
"@earendil-works/pi-ai": "0.85.1",
|
package/dist/assets/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "switchboard",
|
|
3
|
-
"version": "1.261.
|
|
3
|
+
"version": "1.261.1",
|
|
4
4
|
"private": true,
|
|
5
5
|
"description": "Mention it in Slack and an agent reviews the PR, ships the fix, or answers the question — on the model you choose, with its tools running where you decide.",
|
|
6
6
|
"license": "Apache-2.0",
|
package/dist/assets/source.json
CHANGED
|
@@ -510,8 +510,9 @@ function prCheckReturn(step: string, a: BotAnswer): Extract<StepReturn, { type:
|
|
|
510
510
|
},
|
|
511
511
|
at,
|
|
512
512
|
};
|
|
513
|
-
// A merged pull request is read whole or not at all:
|
|
514
|
-
//
|
|
513
|
+
// A merged pull request is read whole or not at all: its source head is
|
|
514
|
+
// retained for findings reconciliation, while the merge commit and time
|
|
515
|
+
// are what the unit's ending and report carry.
|
|
515
516
|
if (
|
|
516
517
|
ok === true &&
|
|
517
518
|
state === "merged" &&
|
|
@@ -527,6 +528,7 @@ function prCheckReturn(step: string, a: BotAnswer): Extract<StepReturn, { type:
|
|
|
527
528
|
state: "merged",
|
|
528
529
|
prNumber,
|
|
529
530
|
url,
|
|
531
|
+
...(typeof headSha === "string" ? { headSha } : {}),
|
|
530
532
|
sha,
|
|
531
533
|
mergedAt,
|
|
532
534
|
...(typeof mergedBy === "string" ? { mergedBy } : {}),
|
|
@@ -923,9 +925,19 @@ async function tellStepThrew(
|
|
|
923
925
|
: at.round.kind === "review" && /\/review\/(read|checks)\b/.test(at.step)
|
|
924
926
|
? `after round ${at.round.index}'s review verdict (\`${at.step}\`)`
|
|
925
927
|
: `in round ${at.round.index} (\`${at.step}\`)`;
|
|
926
|
-
const
|
|
927
|
-
|
|
928
|
-
|
|
928
|
+
const findingsReconcile = at.round?.kind === "findings" && /\/findings(?:\/a\d+)?\/pr-check$/.test(at.step);
|
|
929
|
+
const report = findingsReconcile
|
|
930
|
+
? [
|
|
931
|
+
`⚠️ Review did not restart because Switchboard could not read the pull request after the completed findings work: ${line}`,
|
|
932
|
+
pr !== undefined
|
|
933
|
+
? `Saved-work fact: the existing pull request is ${pr.url}, but its current branch and exact head were not verified.`
|
|
934
|
+
: "Saved-work fact: no open pull request or exact remote head was verified.",
|
|
935
|
+
"No review was started. Next action: retry ship when the pull request is readable so Switchboard can verify the exact head first.",
|
|
936
|
+
].join("\n\n")
|
|
937
|
+
: [
|
|
938
|
+
`⚠️ Switchboard failed ${where}: ${line}`,
|
|
939
|
+
"The unit remains resumable from its recorded branch and pull request facts. No continuation action was scheduled. Next action: start ship again after the failing operation is available.",
|
|
940
|
+
].join("\n\n");
|
|
929
941
|
const body = {
|
|
930
942
|
...tag,
|
|
931
943
|
ending: {
|
|
@@ -150,6 +150,10 @@ export interface ReviewVerdict {
|
|
|
150
150
|
export const LGTM_TOKEN = "LGTM:";
|
|
151
151
|
export const CHANGES_TOKEN = "Changes requested:";
|
|
152
152
|
export const NO_VERDICT_LINE = "No verdict submitted — not approving.";
|
|
153
|
+
/** Conservative ceiling below GitHub's 65,536-character review-body limit.
|
|
154
|
+
* Counted as Unicode code points so clipping never splits a surrogate pair. */
|
|
155
|
+
export const MAX_REVIEW_POST_CODE_POINTS = 65_000;
|
|
156
|
+
const REVIEW_TRUNCATION_NOTE = "_(review truncated to fit GitHub's review size limit)_";
|
|
153
157
|
|
|
154
158
|
/** Parse an arbitrary tool input into a verdict, or null when it is not one.
|
|
155
159
|
* `addressSeverity` is the level in force for the run (default `minor`): an
|
|
@@ -383,7 +387,11 @@ function verdictMarker(verdict: ReviewVerdict | undefined, target: ReviewBodyTar
|
|
|
383
387
|
* `approve`), a GitHub alert callout with the verdict word, the pinned head
|
|
384
388
|
* and the finding counts, the findings as a table (severity, id + title, the
|
|
385
389
|
* file linked at the head), the model's text folded under `Full review`, and
|
|
386
|
-
* the machine-readable marker last.
|
|
390
|
+
* the machine-readable marker last. Only the prose may be clipped to fit the
|
|
391
|
+
* platform: every fixed verdict section and the marker remain byte-for-byte.
|
|
392
|
+
* If those sections leave no room for a visible bounded review, the oversized
|
|
393
|
+
* body is returned so the publication boundary can refuse it instead of
|
|
394
|
+
* silently dropping the write-up. The prose decides nothing above the fold.
|
|
387
395
|
*/
|
|
388
396
|
export function buildReviewPostBody(
|
|
389
397
|
answer: string,
|
|
@@ -395,10 +403,10 @@ export function buildReviewPostBody(
|
|
|
395
403
|
...(target ? [`head \`${target.head.slice(0, 7)}\``] : []),
|
|
396
404
|
verdict ? findingCounts(verdict.findings) : "the run ended without a submit_verdict call",
|
|
397
405
|
];
|
|
398
|
-
const
|
|
406
|
+
const fixedParts: string[] = [verdictLine(verdict), `> [!${calloutKind(verdict)}]\n> ${facts.join(" · ")}`];
|
|
399
407
|
const findings = verdict?.findings ?? [];
|
|
400
408
|
if (findings.length > 0) {
|
|
401
|
-
|
|
409
|
+
fixedParts.push(
|
|
402
410
|
[
|
|
403
411
|
"| Severity | Finding | Where |",
|
|
404
412
|
"| --- | --- | --- |",
|
|
@@ -409,10 +417,29 @@ export function buildReviewPostBody(
|
|
|
409
417
|
].join("\n"),
|
|
410
418
|
);
|
|
411
419
|
}
|
|
420
|
+
|
|
421
|
+
const marker = verdictMarker(verdict, target);
|
|
422
|
+
const fixedBody = [...fixedParts, marker].join("\n\n");
|
|
412
423
|
const prose = answer.trim();
|
|
413
|
-
if (prose)
|
|
414
|
-
|
|
415
|
-
|
|
424
|
+
if (!prose) return fixedBody;
|
|
425
|
+
|
|
426
|
+
const details = (text: string, truncated: boolean): string =>
|
|
427
|
+
`<details>\n<summary>Full review</summary>\n\n${text}${truncated ? `${text ? "\n\n" : ""}${REVIEW_TRUNCATION_NOTE}` : ""}\n\n</details>`;
|
|
428
|
+
const render = (text: string, truncated: boolean): string =>
|
|
429
|
+
[...fixedParts, details(text, truncated), marker].join("\n\n");
|
|
430
|
+
const full = render(prose, false);
|
|
431
|
+
if ([...full].length <= MAX_REVIEW_POST_CODE_POINTS) return full;
|
|
432
|
+
|
|
433
|
+
const clippedShell = render("", true);
|
|
434
|
+
const proseSeparatorCodePoints = 2; // the blank line before the note when clipped prose is present
|
|
435
|
+
const proseBudget = MAX_REVIEW_POST_CODE_POINTS - [...clippedShell].length - proseSeparatorCodePoints;
|
|
436
|
+
if (proseBudget > 0) return render([...prose].slice(0, proseBudget).join(""), true);
|
|
437
|
+
if ([...clippedShell].length <= MAX_REVIEW_POST_CODE_POINTS) return clippedShell;
|
|
438
|
+
|
|
439
|
+
// A successful post must retain a visible write-up. Return the full body
|
|
440
|
+
// oversized when even the bounded shell cannot fit, so postReviewComment
|
|
441
|
+
// rejects before fetch and the review remains on the Slack-only path.
|
|
442
|
+
return full;
|
|
416
443
|
}
|
|
417
444
|
|
|
418
445
|
/**
|