@coreplane/switchboard 1.265.0 → 1.265.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.
|
@@ -1,12 +1,12 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "switchboard",
|
|
3
|
-
"version": "1.265.
|
|
3
|
+
"version": "1.265.1",
|
|
4
4
|
"lockfileVersion": 3,
|
|
5
5
|
"requires": true,
|
|
6
6
|
"packages": {
|
|
7
7
|
"": {
|
|
8
8
|
"name": "switchboard",
|
|
9
|
-
"version": "1.265.
|
|
9
|
+
"version": "1.265.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.265.
|
|
21063
|
+
"version": "1.265.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.265.
|
|
3
|
+
"version": "1.265.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
|
@@ -0,0 +1,173 @@
|
|
|
1
|
+
import type { ExistingPrPublicationBinding } from "./coordinator/contract.js";
|
|
2
|
+
import { COMMAND_CAP, TOOL_OUTPUT_CAP, type RunEvent } from "./runEvents.js";
|
|
3
|
+
|
|
4
|
+
export interface PublicationPushReceipt {
|
|
5
|
+
callId: string;
|
|
6
|
+
previousHeadSha: string;
|
|
7
|
+
repo: string;
|
|
8
|
+
pr: number;
|
|
9
|
+
owner: ExistingPrPublicationBinding["owner"];
|
|
10
|
+
}
|
|
11
|
+
|
|
12
|
+
type PushEvent = Extract<RunEvent, { type: "pushed_head" }>;
|
|
13
|
+
const fullSha = (s: string): boolean => /^[a-f0-9]{40}$/.test(s);
|
|
14
|
+
const refName = (s: string): boolean =>
|
|
15
|
+
/^[a-zA-Z0-9_/-]+(?:\.[a-zA-Z0-9_/-]+)*$/.test(s) && !s.includes("..") && !s.startsWith("-");
|
|
16
|
+
const shortRef = (s: string): string => s.replace(/^refs\/heads\//, "");
|
|
17
|
+
|
|
18
|
+
/** Deliberately not a shell interpreter. Only a standalone literal push can
|
|
19
|
+
* attest its result: pipelines, scripts, substitutions and compound commands
|
|
20
|
+
* cannot prove which operation produced stdout or the successful exit. */
|
|
21
|
+
export function leasedPushCommand(command: string): { ref: string; expectedHeadSha: string } | undefined {
|
|
22
|
+
if (command.length >= COMMAND_CAP) return;
|
|
23
|
+
const literal = command.replace(/[ \t]+2>&1[ \t]*$/, "");
|
|
24
|
+
if (!/^git push\s/.test(literal) || /[\n\r'"`$;|<>\\&]/.test(literal)) return;
|
|
25
|
+
const words = literal.trim().split(/\s+/).slice(2);
|
|
26
|
+
const leases = words.filter((w) => w.startsWith("--force-with-lease="));
|
|
27
|
+
if (leases.length !== 1) return;
|
|
28
|
+
const lease = /^--force-with-lease=refs\/heads\/([^:]+):([a-f0-9]{40})$/.exec(leases[0]!);
|
|
29
|
+
if (!lease || !refName(lease[1]!)) return;
|
|
30
|
+
const args = words.filter((w) => w !== leases[0] && w !== "-u" && w !== "--set-upstream");
|
|
31
|
+
if (args.length !== 2 || args[0] !== "origin") return;
|
|
32
|
+
const refs = args[1]!.split(":");
|
|
33
|
+
if (refs.length > 2 || shortRef(refs[0]!) !== lease[1] || shortRef(refs[1] ?? refs[0]!) !== lease[1]) return;
|
|
34
|
+
return { ref: lease[1]!, expectedHeadSha: lease[2]! };
|
|
35
|
+
}
|
|
36
|
+
|
|
37
|
+
/** A legacy record needs the complete pair, never its display summary, an
|
|
38
|
+
* assistant's prose or the remote head alone. More than one attempted push is
|
|
39
|
+
* intentionally ambiguous; historical reconciliation does not choose one. */
|
|
40
|
+
export function pairedPublicationPush(
|
|
41
|
+
events: readonly RunEvent[],
|
|
42
|
+
binding: ExistingPrPublicationBinding,
|
|
43
|
+
head: string,
|
|
44
|
+
callId?: string,
|
|
45
|
+
): PushEvent | undefined {
|
|
46
|
+
if (!fullSha(head) || !fullSha(binding.expectedHeadSha) || head === binding.expectedHeadSha) return;
|
|
47
|
+
const calls = events.filter(
|
|
48
|
+
(e): e is Extract<RunEvent, { type: "tool_call" }> =>
|
|
49
|
+
e.type === "tool_call" &&
|
|
50
|
+
e.tool === "bash" &&
|
|
51
|
+
(callId === undefined ? /\bgit\s+push\b/.test(e.command ?? e.summary) : e.callId === callId),
|
|
52
|
+
);
|
|
53
|
+
if (calls.length !== 1) return;
|
|
54
|
+
const call = calls[0]!;
|
|
55
|
+
if (
|
|
56
|
+
!call.callId ||
|
|
57
|
+
!call.command ||
|
|
58
|
+
events.filter((e) => e.type === "tool_call" && e.callId === call.callId).length !== 1
|
|
59
|
+
)
|
|
60
|
+
return;
|
|
61
|
+
const command = leasedPushCommand(call.command);
|
|
62
|
+
if (command?.ref !== binding.publicationRef || command.expectedHeadSha !== binding.expectedHeadSha) return;
|
|
63
|
+
const results = events.filter(
|
|
64
|
+
(e): e is Extract<RunEvent, { type: "tool_result" }> => e.type === "tool_result" && e.callId === call.callId,
|
|
65
|
+
);
|
|
66
|
+
if (results.length !== 1) return;
|
|
67
|
+
const result = results[0]!;
|
|
68
|
+
if (
|
|
69
|
+
events.indexOf(result) <= events.indexOf(call) ||
|
|
70
|
+
result.tool !== "bash" ||
|
|
71
|
+
!result.ok ||
|
|
72
|
+
result.exitCode !== 0 ||
|
|
73
|
+
result.cut ||
|
|
74
|
+
!result.output ||
|
|
75
|
+
result.output.length >= TOOL_OUTPUT_CAP
|
|
76
|
+
)
|
|
77
|
+
return;
|
|
78
|
+
let lines = result.output.trim().split(/\r?\n/);
|
|
79
|
+
const tracking = `branch '${binding.publicationRef}' set up to track 'origin/${binding.publicationRef}'.`;
|
|
80
|
+
if (
|
|
81
|
+
/(?:^|\s)(?:-u|--set-upstream)(?:\s|$)/.test(call.command) &&
|
|
82
|
+
lines.filter((line) => line === tracking).length === 1
|
|
83
|
+
)
|
|
84
|
+
lines = lines.filter((line) => line !== tracking);
|
|
85
|
+
if (lines.length !== 2) return;
|
|
86
|
+
const remote = /^To (?:https:\/\/github\.com\/|git@github\.com:|github\.com:)([^\s]+?)(?:\.git)?$/.exec(lines[0]!);
|
|
87
|
+
const update = /^\s*\+?\s*([a-f0-9]{7,40})(\.{2,3})([a-f0-9]{7,40})\s+(\S+) -> (\S+)( \(forced update\))?$/.exec(
|
|
88
|
+
lines[1]!,
|
|
89
|
+
);
|
|
90
|
+
if (
|
|
91
|
+
!remote ||
|
|
92
|
+
remote[1]!.toLowerCase() !== binding.repo.toLowerCase() ||
|
|
93
|
+
!update ||
|
|
94
|
+
!binding.expectedHeadSha.startsWith(update[1]!) ||
|
|
95
|
+
!head.startsWith(update[3]!) ||
|
|
96
|
+
update[1] === update[3] ||
|
|
97
|
+
shortRef(update[4]!) !== binding.publicationRef ||
|
|
98
|
+
shortRef(update[5]!) !== binding.publicationRef
|
|
99
|
+
)
|
|
100
|
+
return;
|
|
101
|
+
return {
|
|
102
|
+
type: "pushed_head",
|
|
103
|
+
ref: binding.publicationRef,
|
|
104
|
+
sha: head,
|
|
105
|
+
by: "push",
|
|
106
|
+
receipt: {
|
|
107
|
+
callId: call.callId,
|
|
108
|
+
previousHeadSha: binding.expectedHeadSha,
|
|
109
|
+
repo: binding.repo,
|
|
110
|
+
pr: binding.pr,
|
|
111
|
+
owner: { ...binding.owner },
|
|
112
|
+
},
|
|
113
|
+
};
|
|
114
|
+
}
|
|
115
|
+
|
|
116
|
+
/** State is stored JSON, not an unchecked cast back into publication authority. */
|
|
117
|
+
export function publicationReceiptsFromState(value: unknown): PushEvent[] {
|
|
118
|
+
if (!Array.isArray(value)) return [];
|
|
119
|
+
const valid = value.every((v) => {
|
|
120
|
+
if (typeof v !== "object" || v === null) return false;
|
|
121
|
+
const r = v.receipt;
|
|
122
|
+
return (
|
|
123
|
+
v.type === "pushed_head" &&
|
|
124
|
+
v.by === "push" &&
|
|
125
|
+
typeof v.ref === "string" &&
|
|
126
|
+
refName(v.ref) &&
|
|
127
|
+
typeof v.sha === "string" &&
|
|
128
|
+
fullSha(v.sha) &&
|
|
129
|
+
typeof r === "object" &&
|
|
130
|
+
r !== null &&
|
|
131
|
+
typeof r.callId === "string" &&
|
|
132
|
+
r.callId.length > 0 &&
|
|
133
|
+
typeof r.previousHeadSha === "string" &&
|
|
134
|
+
fullSha(r.previousHeadSha) &&
|
|
135
|
+
typeof r.repo === "string" &&
|
|
136
|
+
Number.isSafeInteger(r.pr) &&
|
|
137
|
+
r.pr > 0 &&
|
|
138
|
+
typeof r.owner === "object" &&
|
|
139
|
+
r.owner !== null &&
|
|
140
|
+
typeof r.owner.instanceId === "string" &&
|
|
141
|
+
typeof r.owner.unit === "string"
|
|
142
|
+
);
|
|
143
|
+
});
|
|
144
|
+
return valid ? ([...value] as PushEvent[]) : [];
|
|
145
|
+
}
|
|
146
|
+
|
|
147
|
+
/** Only the run's typed, contiguous leased transitions can move its restart
|
|
148
|
+
* fence. The caller still verifies the fresh PR and attached workspace. */
|
|
149
|
+
export function restoredPublicationHead(
|
|
150
|
+
events: readonly RunEvent[],
|
|
151
|
+
binding: ExistingPrPublicationBinding,
|
|
152
|
+
): string | undefined {
|
|
153
|
+
let head = binding.expectedHeadSha;
|
|
154
|
+
const seen = new Set<string>();
|
|
155
|
+
for (const e of events) {
|
|
156
|
+
if (e.type !== "pushed_head" || e.receipt === undefined) continue;
|
|
157
|
+
const r = e.receipt;
|
|
158
|
+
if (
|
|
159
|
+
e.ref !== binding.publicationRef ||
|
|
160
|
+
r.repo !== binding.repo ||
|
|
161
|
+
r.pr !== binding.pr ||
|
|
162
|
+
r.owner.instanceId !== binding.owner.instanceId ||
|
|
163
|
+
r.owner.unit !== binding.owner.unit ||
|
|
164
|
+
r.previousHeadSha !== head ||
|
|
165
|
+
!fullSha(e.sha) ||
|
|
166
|
+
seen.has(r.callId)
|
|
167
|
+
)
|
|
168
|
+
return;
|
|
169
|
+
seen.add(r.callId);
|
|
170
|
+
head = e.sha;
|
|
171
|
+
}
|
|
172
|
+
return head === binding.expectedHeadSha ? undefined : head;
|
|
173
|
+
}
|
|
@@ -944,12 +944,23 @@ export type RunEvent =
|
|
|
944
944
|
ref: string;
|
|
945
945
|
sha: string;
|
|
946
946
|
by: "push" | "salvage";
|
|
947
|
+
/** Exact leased tool result, retained before any post-step can refuse. */
|
|
948
|
+
receipt?: import("./publicationPush.js").PublicationPushReceipt;
|
|
947
949
|
/** No uncommitted or unpushed work at the push (record 0064): the fact
|
|
948
950
|
* the plane's soft stop reads. Absent where the measure was missing. */
|
|
949
951
|
clean?: boolean;
|
|
950
952
|
seq?: number;
|
|
951
953
|
at?: number;
|
|
952
954
|
}
|
|
955
|
+
/** The server gate admitted this exact leased push, not a child's claim. */
|
|
956
|
+
| {
|
|
957
|
+
type: "publication_push_authorized";
|
|
958
|
+
callId: string;
|
|
959
|
+
ref: string;
|
|
960
|
+
expectedHeadSha: string;
|
|
961
|
+
seq?: number;
|
|
962
|
+
at?: number;
|
|
963
|
+
}
|
|
953
964
|
/** The coordinator tag as a fact of the run (docs/reference/specs/run-history.md
|
|
954
965
|
* item 48a): the instance the run is a child of, the unit its idempotency
|
|
955
966
|
* key named, and the base branch its pull request targets — published by
|
package/dist/cli.js
CHANGED
|
@@ -36249,6 +36249,8 @@ function activityLine(e) {
|
|
|
36249
36249
|
case "pr_opened":
|
|
36250
36250
|
return "PR opened";
|
|
36251
36251
|
// published straight to the registry — never arrives here
|
|
36252
|
+
case "publication_push_authorized":
|
|
36253
|
+
return "push authorized";
|
|
36252
36254
|
case "pushed_head":
|
|
36253
36255
|
return `\u2B06 pushed ${e.ref} @ ${e.sha.slice(0, 7)}`;
|
|
36254
36256
|
case "coordinator_tag":
|
|
@@ -50261,6 +50263,11 @@ function reachFor(identity2) {
|
|
|
50261
50263
|
if (identity2 === "read") return READ_REACH;
|
|
50262
50264
|
return NONE_REACH;
|
|
50263
50265
|
}
|
|
50266
|
+
function publicationAttributionRefusal(rules, callId) {
|
|
50267
|
+
const pending2 = rules.publication?.attributingCallId;
|
|
50268
|
+
if (pending2 !== void 0 && pending2 !== callId)
|
|
50269
|
+
return "push attribution is still pending; no later tool may change the checkout until its result is recorded";
|
|
50270
|
+
}
|
|
50264
50271
|
function openCodeToolWord(action) {
|
|
50265
50272
|
const mapped = OPENCODE_ACTION_TO_TOOL_WORD[action];
|
|
50266
50273
|
if (mapped !== void 0) return mapped;
|
|
@@ -50464,6 +50471,98 @@ var init_toolRules = __esm({
|
|
|
50464
50471
|
}
|
|
50465
50472
|
});
|
|
50466
50473
|
|
|
50474
|
+
// ../../src/core/publicationPush.ts
|
|
50475
|
+
function leasedPushCommand(command2) {
|
|
50476
|
+
if (command2.length >= COMMAND_CAP) return;
|
|
50477
|
+
const literal = command2.replace(/[ \t]+2>&1[ \t]*$/, "");
|
|
50478
|
+
if (!/^git push\s/.test(literal) || /[\n\r'"`$;|<>\\&]/.test(literal)) return;
|
|
50479
|
+
const words = literal.trim().split(/\s+/).slice(2);
|
|
50480
|
+
const leases = words.filter((w) => w.startsWith("--force-with-lease="));
|
|
50481
|
+
if (leases.length !== 1) return;
|
|
50482
|
+
const lease = /^--force-with-lease=refs\/heads\/([^:]+):([a-f0-9]{40})$/.exec(leases[0]);
|
|
50483
|
+
if (!lease || !refName(lease[1])) return;
|
|
50484
|
+
const args = words.filter((w) => w !== leases[0] && w !== "-u" && w !== "--set-upstream");
|
|
50485
|
+
if (args.length !== 2 || args[0] !== "origin") return;
|
|
50486
|
+
const refs = args[1].split(":");
|
|
50487
|
+
if (refs.length > 2 || shortRef(refs[0]) !== lease[1] || shortRef(refs[1] ?? refs[0]) !== lease[1]) return;
|
|
50488
|
+
return { ref: lease[1], expectedHeadSha: lease[2] };
|
|
50489
|
+
}
|
|
50490
|
+
function pairedPublicationPush(events, binding, head, callId) {
|
|
50491
|
+
if (!fullSha(head) || !fullSha(binding.expectedHeadSha) || head === binding.expectedHeadSha) return;
|
|
50492
|
+
const calls = events.filter(
|
|
50493
|
+
(e) => e.type === "tool_call" && e.tool === "bash" && (callId === void 0 ? /\bgit\s+push\b/.test(e.command ?? e.summary) : e.callId === callId)
|
|
50494
|
+
);
|
|
50495
|
+
if (calls.length !== 1) return;
|
|
50496
|
+
const call2 = calls[0];
|
|
50497
|
+
if (!call2.callId || !call2.command || events.filter((e) => e.type === "tool_call" && e.callId === call2.callId).length !== 1)
|
|
50498
|
+
return;
|
|
50499
|
+
const command2 = leasedPushCommand(call2.command);
|
|
50500
|
+
if (command2?.ref !== binding.publicationRef || command2.expectedHeadSha !== binding.expectedHeadSha) return;
|
|
50501
|
+
const results = events.filter(
|
|
50502
|
+
(e) => e.type === "tool_result" && e.callId === call2.callId
|
|
50503
|
+
);
|
|
50504
|
+
if (results.length !== 1) return;
|
|
50505
|
+
const result = results[0];
|
|
50506
|
+
if (events.indexOf(result) <= events.indexOf(call2) || result.tool !== "bash" || !result.ok || result.exitCode !== 0 || result.cut || !result.output || result.output.length >= TOOL_OUTPUT_CAP)
|
|
50507
|
+
return;
|
|
50508
|
+
let lines = result.output.trim().split(/\r?\n/);
|
|
50509
|
+
const tracking = `branch '${binding.publicationRef}' set up to track 'origin/${binding.publicationRef}'.`;
|
|
50510
|
+
if (/(?:^|\s)(?:-u|--set-upstream)(?:\s|$)/.test(call2.command) && lines.filter((line4) => line4 === tracking).length === 1)
|
|
50511
|
+
lines = lines.filter((line4) => line4 !== tracking);
|
|
50512
|
+
if (lines.length !== 2) return;
|
|
50513
|
+
const remote = /^To (?:https:\/\/github\.com\/|git@github\.com:|github\.com:)([^\s]+?)(?:\.git)?$/.exec(lines[0]);
|
|
50514
|
+
const update = /^\s*\+?\s*([a-f0-9]{7,40})(\.{2,3})([a-f0-9]{7,40})\s+(\S+) -> (\S+)( \(forced update\))?$/.exec(
|
|
50515
|
+
lines[1]
|
|
50516
|
+
);
|
|
50517
|
+
if (!remote || remote[1].toLowerCase() !== binding.repo.toLowerCase() || !update || !binding.expectedHeadSha.startsWith(update[1]) || !head.startsWith(update[3]) || update[1] === update[3] || shortRef(update[4]) !== binding.publicationRef || shortRef(update[5]) !== binding.publicationRef)
|
|
50518
|
+
return;
|
|
50519
|
+
return {
|
|
50520
|
+
type: "pushed_head",
|
|
50521
|
+
ref: binding.publicationRef,
|
|
50522
|
+
sha: head,
|
|
50523
|
+
by: "push",
|
|
50524
|
+
receipt: {
|
|
50525
|
+
callId: call2.callId,
|
|
50526
|
+
previousHeadSha: binding.expectedHeadSha,
|
|
50527
|
+
repo: binding.repo,
|
|
50528
|
+
pr: binding.pr,
|
|
50529
|
+
owner: { ...binding.owner }
|
|
50530
|
+
}
|
|
50531
|
+
};
|
|
50532
|
+
}
|
|
50533
|
+
function publicationReceiptsFromState(value) {
|
|
50534
|
+
if (!Array.isArray(value)) return [];
|
|
50535
|
+
const valid = value.every((v) => {
|
|
50536
|
+
if (typeof v !== "object" || v === null) return false;
|
|
50537
|
+
const r = v.receipt;
|
|
50538
|
+
return v.type === "pushed_head" && v.by === "push" && typeof v.ref === "string" && refName(v.ref) && typeof v.sha === "string" && fullSha(v.sha) && typeof r === "object" && r !== null && typeof r.callId === "string" && r.callId.length > 0 && typeof r.previousHeadSha === "string" && fullSha(r.previousHeadSha) && typeof r.repo === "string" && Number.isSafeInteger(r.pr) && r.pr > 0 && typeof r.owner === "object" && r.owner !== null && typeof r.owner.instanceId === "string" && typeof r.owner.unit === "string";
|
|
50539
|
+
});
|
|
50540
|
+
return valid ? [...value] : [];
|
|
50541
|
+
}
|
|
50542
|
+
function restoredPublicationHead(events, binding) {
|
|
50543
|
+
let head = binding.expectedHeadSha;
|
|
50544
|
+
const seen = /* @__PURE__ */ new Set();
|
|
50545
|
+
for (const e of events) {
|
|
50546
|
+
if (e.type !== "pushed_head" || e.receipt === void 0) continue;
|
|
50547
|
+
const r = e.receipt;
|
|
50548
|
+
if (e.ref !== binding.publicationRef || r.repo !== binding.repo || r.pr !== binding.pr || r.owner.instanceId !== binding.owner.instanceId || r.owner.unit !== binding.owner.unit || r.previousHeadSha !== head || !fullSha(e.sha) || seen.has(r.callId))
|
|
50549
|
+
return;
|
|
50550
|
+
seen.add(r.callId);
|
|
50551
|
+
head = e.sha;
|
|
50552
|
+
}
|
|
50553
|
+
return head === binding.expectedHeadSha ? void 0 : head;
|
|
50554
|
+
}
|
|
50555
|
+
var fullSha, refName, shortRef;
|
|
50556
|
+
var init_publicationPush = __esm({
|
|
50557
|
+
"../../src/core/publicationPush.ts"() {
|
|
50558
|
+
"use strict";
|
|
50559
|
+
init_runEvents();
|
|
50560
|
+
fullSha = (s) => /^[a-f0-9]{40}$/.test(s);
|
|
50561
|
+
refName = (s) => /^[a-zA-Z0-9_/-]+(?:\.[a-zA-Z0-9_/-]+)*$/.test(s) && !s.includes("..") && !s.startsWith("-");
|
|
50562
|
+
shortRef = (s) => s.replace(/^refs\/heads\//, "");
|
|
50563
|
+
}
|
|
50564
|
+
});
|
|
50565
|
+
|
|
50467
50566
|
// ../../src/core/harness/pi/relay.ts
|
|
50468
50567
|
function stillRunningNote(tool) {
|
|
50469
50568
|
return `This ${tool} call is still running in the bot: the process that made it was relaunched while the call was in flight, and the call keeps running there rather than being re-run \u2014 do not run it again; its result is kept on the relay under the same call id.`;
|
|
@@ -50494,9 +50593,18 @@ function authorizeToolCall(harness, ask) {
|
|
|
50494
50593
|
return { allow: false, reason: reason2 };
|
|
50495
50594
|
};
|
|
50496
50595
|
if (blocked2 !== void 0) return refuse3(blocked2);
|
|
50596
|
+
const pending2 = publicationAttributionRefusal(harness.rules, ask.toolCallId);
|
|
50597
|
+
if (pending2 !== void 0) return refuse3(pending2);
|
|
50497
50598
|
if (harness.tools.some((t) => t.name === ask.tool)) return { allow: true };
|
|
50498
50599
|
const verdict = judgeToolCall(ask.tool, ask.input, harness.rules);
|
|
50499
|
-
if (verdict.verdict === "allowed")
|
|
50600
|
+
if (verdict.verdict === "allowed") {
|
|
50601
|
+
const authority = harness.rules.publication?.authority;
|
|
50602
|
+
const input = ask.input;
|
|
50603
|
+
const push = ask.tool === "bash" && typeof input?.command === "string" ? leasedPushCommand(input.command) : void 0;
|
|
50604
|
+
if (push !== void 0 && authority !== void 0 && !("blocked" in authority) && push.ref === authority.ref && push.expectedHeadSha === authority.expectedHeadSha)
|
|
50605
|
+
harness.emit({ type: "publication_push_authorized", callId: ask.toolCallId, ...push });
|
|
50606
|
+
return { allow: true };
|
|
50607
|
+
}
|
|
50500
50608
|
return refuse3(verdict.reason);
|
|
50501
50609
|
}
|
|
50502
50610
|
function answerCompaction(harness, ask) {
|
|
@@ -50563,6 +50671,7 @@ var init_relay = __esm({
|
|
|
50563
50671
|
init_runEvents();
|
|
50564
50672
|
init_compactionFallback();
|
|
50565
50673
|
init_toolRules();
|
|
50674
|
+
init_publicationPush();
|
|
50566
50675
|
HarnessRegistry = class {
|
|
50567
50676
|
live = /* @__PURE__ */ new Map();
|
|
50568
50677
|
/** The run drives a pi from here, its relayed calls kept beside it; the
|
|
@@ -52376,19 +52485,19 @@ var init_resume = __esm({
|
|
|
52376
52485
|
// ../../src/core/existingPrPublication.ts
|
|
52377
52486
|
function verifyExistingPrPublication(durable, local, remote) {
|
|
52378
52487
|
if (durable === void 0) return blocked("the durable existing-PR binding is missing");
|
|
52379
|
-
if (!
|
|
52488
|
+
if (!fullSha2(durable.expectedHeadSha)) return blocked("the durable expected head is not a full commit SHA");
|
|
52380
52489
|
if (durable.headRef !== durable.publicationRef)
|
|
52381
52490
|
return blocked("the durable head ref and owned publication ref do not agree");
|
|
52382
52491
|
if (local.repo !== durable.repo) return blocked("the resolved repository does not match the durable binding");
|
|
52383
52492
|
if (local.pr !== durable.pr) return blocked("the resolved pull request does not match the durable binding");
|
|
52384
52493
|
if (local.ref !== durable.headRef) return blocked("the resolved head ref does not match the durable binding");
|
|
52385
52494
|
if (local.baseRef !== durable.baseRef) return blocked("the resolved base ref does not match the durable binding");
|
|
52386
|
-
if (!
|
|
52495
|
+
if (!fullSha2(local.requestHeadSha)) return blocked("the request head is missing or not a full commit SHA");
|
|
52387
52496
|
if (local.requestHeadSha.toLowerCase() !== durable.expectedHeadSha.toLowerCase())
|
|
52388
52497
|
return blocked("the request head does not match the durable expected commit");
|
|
52389
52498
|
if (local.workspaceRef !== durable.publicationRef)
|
|
52390
52499
|
return blocked("the checked-out publication ref does not match the durable binding");
|
|
52391
|
-
if (!
|
|
52500
|
+
if (!fullSha2(local.workspaceHeadSha)) return blocked("the checked-out head is missing or not a full commit SHA");
|
|
52392
52501
|
if (local.workspaceHeadSha.toLowerCase() !== durable.expectedHeadSha.toLowerCase())
|
|
52393
52502
|
return blocked("the checked-out head does not match the durable expected commit");
|
|
52394
52503
|
if (!sameOwner(local.owner, durable.owner)) return blocked("the publication owner changed");
|
|
@@ -52398,7 +52507,7 @@ function verifyExistingPrPublication(durable, local, remote) {
|
|
|
52398
52507
|
if (remote.headBranchExists !== true) return blocked("the pull request head branch is missing or unknown");
|
|
52399
52508
|
if (remote.headRef !== durable.headRef) return blocked("the pull request head ref moved");
|
|
52400
52509
|
if (remote.baseRef !== durable.baseRef) return blocked("the pull request base ref moved");
|
|
52401
|
-
if (!
|
|
52510
|
+
if (!fullSha2(remote.headSha)) return blocked("the fresh pull-request head is missing or not a full commit SHA");
|
|
52402
52511
|
if (remote.headSha.toLowerCase() !== durable.expectedHeadSha.toLowerCase())
|
|
52403
52512
|
return blocked("the pull request head moved from the durable expected commit");
|
|
52404
52513
|
return {
|
|
@@ -52406,11 +52515,11 @@ function verifyExistingPrPublication(durable, local, remote) {
|
|
|
52406
52515
|
publication: { ref: durable.publicationRef, expectedHeadSha: durable.expectedHeadSha.toLowerCase() }
|
|
52407
52516
|
};
|
|
52408
52517
|
}
|
|
52409
|
-
var
|
|
52518
|
+
var fullSha2, sameOwner, blocked;
|
|
52410
52519
|
var init_existingPrPublication = __esm({
|
|
52411
52520
|
"../../src/core/existingPrPublication.ts"() {
|
|
52412
52521
|
"use strict";
|
|
52413
|
-
|
|
52522
|
+
fullSha2 = (value) => value !== void 0 && /^[0-9a-f]{40}$/i.test(value);
|
|
52414
52523
|
sameOwner = (left, right) => left?.instanceId === right.instanceId && left.unit === right.unit;
|
|
52415
52524
|
blocked = (reason2) => ({ ok: false, reason: reason2 });
|
|
52416
52525
|
}
|
|
@@ -53437,6 +53546,14 @@ async function runLoop(deps, ctx) {
|
|
|
53437
53546
|
let reviewHead = ctx.reviewHead;
|
|
53438
53547
|
let lastActivityAt = ctx.loopStartedAt;
|
|
53439
53548
|
const restored = resume?.row.state ?? {};
|
|
53549
|
+
const restoredReceipts = publicationReceiptsFromState(restored.publicationReceipts);
|
|
53550
|
+
const publicationReceipts = coordinator?.publication !== void 0 && restoredPublicationHead(restoredReceipts, coordinator.publication) !== void 0 ? restoredReceipts : [];
|
|
53551
|
+
const retainPublicationReceipts = () => {
|
|
53552
|
+
const history = registry.snapshot(run2.id, run2.token)?.events ?? [];
|
|
53553
|
+
const latest = publicationReceipts.at(-1);
|
|
53554
|
+
if (latest !== void 0 && !history.some((e) => e.type === "pushed_head" && e.ref === latest.ref))
|
|
53555
|
+
registry.publish(run2.id, latest);
|
|
53556
|
+
};
|
|
53440
53557
|
let checklist = typeof restored.checklist === "string" ? restored.checklist : void 0;
|
|
53441
53558
|
let lastActivity;
|
|
53442
53559
|
let inFlightTool;
|
|
@@ -53473,6 +53590,23 @@ async function runLoop(deps, ctx) {
|
|
|
53473
53590
|
let budgetEnded = false;
|
|
53474
53591
|
let recordedPushedBranch;
|
|
53475
53592
|
let modelBudgetEndsAt;
|
|
53593
|
+
const receiptAtToolResult = async (e) => {
|
|
53594
|
+
const binding2 = coordinator?.publication;
|
|
53595
|
+
const authority = existingPrPublicationFence?.authority;
|
|
53596
|
+
if (e.type !== "tool_result" || e.tool !== "bash" || !e.ok || e.exitCode !== 0 || e.cut || binding2 === void 0 || authority === void 0 || "blocked" in authority)
|
|
53597
|
+
return;
|
|
53598
|
+
const history = registry.snapshot(run2.id, run2.token)?.events ?? [];
|
|
53599
|
+
const gates = history.filter((event) => event.type === "publication_push_authorized" && event.callId === e.callId);
|
|
53600
|
+
if (gates.length !== 1 || gates[0]?.type !== "publication_push_authorized" || gates[0].ref !== authority.ref || gates[0].expectedHeadSha !== authority.expectedHeadSha)
|
|
53601
|
+
return;
|
|
53602
|
+
try {
|
|
53603
|
+
const head = (await executor.exec(`git rev-parse refs/heads/${authority.ref}`)).trim();
|
|
53604
|
+
const current = { ...binding2, expectedHeadSha: authority.expectedHeadSha };
|
|
53605
|
+
return pairedPublicationPush([...history, e], current, head, e.callId);
|
|
53606
|
+
} catch {
|
|
53607
|
+
return;
|
|
53608
|
+
}
|
|
53609
|
+
};
|
|
53476
53610
|
const publishEvent = (e, startsRunBudget) => {
|
|
53477
53611
|
registry.publish(run2.id, e);
|
|
53478
53612
|
if (isSpanRecord(e)) return;
|
|
@@ -53497,12 +53631,16 @@ async function runLoop(deps, ctx) {
|
|
|
53497
53631
|
card.update(currentFrame());
|
|
53498
53632
|
};
|
|
53499
53633
|
const onEvent = (e) => {
|
|
53634
|
+
if (e.type === "publication_push_authorized" && existingPrPublicationFence !== void 0)
|
|
53635
|
+
existingPrPublicationFence.attributingCallId = e.callId;
|
|
53500
53636
|
const startsRunBudget = e.type === "lease";
|
|
53501
53637
|
if (startsRunBudget) {
|
|
53502
53638
|
modelBudgetEndsAt = e.endsAt;
|
|
53503
53639
|
run2.control.startLease(() => e.endsAt - clock4());
|
|
53504
53640
|
}
|
|
53505
53641
|
void events.write(async () => {
|
|
53642
|
+
const receipt = await receiptAtToolResult(e);
|
|
53643
|
+
let receiptCommitted = false;
|
|
53506
53644
|
if ((e.type === "tool_call" || e.type === "tool_result") && typeof registry.commitLiveState === "function") {
|
|
53507
53645
|
const summary = registry.getById(run2.id);
|
|
53508
53646
|
if (summary?.liveState?.state === "working") {
|
|
@@ -53516,12 +53654,16 @@ async function runLoop(deps, ctx) {
|
|
|
53516
53654
|
state: "working",
|
|
53517
53655
|
bound,
|
|
53518
53656
|
detail: e.type === "tool_call" ? "running a tool" : "model turn",
|
|
53519
|
-
sourceEvents: [{ ...e, seq: sourceSeq }]
|
|
53657
|
+
sourceEvents: [{ ...e, seq: sourceSeq }, ...receipt ? [{ ...receipt, seq: sourceSeq + 1, at }] : []],
|
|
53658
|
+
...receipt ? { statePatch: { publicationReceipts: [...publicationReceipts, receipt] } } : {}
|
|
53520
53659
|
};
|
|
53521
53660
|
try {
|
|
53522
53661
|
if (ledgerRun?.tracked()) {
|
|
53523
53662
|
const committed = await ledgerRun.assignLiveState(assignment);
|
|
53524
|
-
if (committed.ok)
|
|
53663
|
+
if (committed.ok) {
|
|
53664
|
+
registry.commitLiveState(run2.id, committed);
|
|
53665
|
+
receiptCommitted = true;
|
|
53666
|
+
}
|
|
53525
53667
|
} else {
|
|
53526
53668
|
const local = assignRunLiveState(summary.liveState, summary.liveStateSeq ?? 0, assignment);
|
|
53527
53669
|
if (local.ok) registry.commitLiveState(run2.id, { ...local, liveStateSeq: sourceSeq });
|
|
@@ -53535,6 +53677,16 @@ async function runLoop(deps, ctx) {
|
|
|
53535
53677
|
}
|
|
53536
53678
|
}
|
|
53537
53679
|
publishEvent(e, startsRunBudget);
|
|
53680
|
+
if (receipt !== void 0) {
|
|
53681
|
+
if (receiptCommitted) {
|
|
53682
|
+
publicationReceipts.push(receipt);
|
|
53683
|
+
publishEvent(receipt, false);
|
|
53684
|
+
existingPrPublication = { ref: receipt.ref, expectedHeadSha: receipt.sha };
|
|
53685
|
+
if (existingPrPublicationFence !== void 0) existingPrPublicationFence.authority = existingPrPublication;
|
|
53686
|
+
} else blockExistingPrPublication("the successful push receipt could not be committed durably");
|
|
53687
|
+
}
|
|
53688
|
+
if (e.type === "tool_result" && existingPrPublicationFence !== void 0 && existingPrPublicationFence.attributingCallId === e.callId)
|
|
53689
|
+
delete existingPrPublicationFence.attributingCallId;
|
|
53538
53690
|
});
|
|
53539
53691
|
};
|
|
53540
53692
|
for (const s of mcpForRun?.servers ?? []) {
|
|
@@ -53686,6 +53838,8 @@ async function runLoop(deps, ctx) {
|
|
|
53686
53838
|
};
|
|
53687
53839
|
const preserveCodingChildWork = async (cue) => {
|
|
53688
53840
|
if (workSalvageAttempted || !isCodingPrRun || coordinator === void 0) return;
|
|
53841
|
+
await events.drain();
|
|
53842
|
+
retainPublicationReceipts();
|
|
53689
53843
|
if (!workspaceObserved) await observeWorkspaceNow();
|
|
53690
53844
|
const target = coordinatorBranch === void 0 ? { skipped: "the ending checkpoint was skipped: the unit's branch is unavailable" } : salvageTargetOf({
|
|
53691
53845
|
pushedBranch: pushes.branch(),
|
|
@@ -53839,6 +53993,40 @@ async function runLoop(deps, ctx) {
|
|
|
53839
53993
|
};
|
|
53840
53994
|
let workspaceBinding = workspaceBindingFor(round2.selection, profile.machine);
|
|
53841
53995
|
try {
|
|
53996
|
+
const prBase = repoCtx.baseRef ?? await coordinatorBase;
|
|
53997
|
+
if (coordinator?.publication !== void 0) {
|
|
53998
|
+
const original = coordinator.publication;
|
|
53999
|
+
const restoredHead = resume === void 0 ? void 0 : restoredPublicationHead(publicationReceipts, original);
|
|
54000
|
+
const publication = restoredHead === void 0 ? original : { ...original, expectedHeadSha: restoredHead };
|
|
54001
|
+
const fresh = await (deps.fetchPrFacts ?? fetchPullRequestFacts)({
|
|
54002
|
+
repo: publication.repo,
|
|
54003
|
+
number: publication.pr
|
|
54004
|
+
}).catch(() => void 0);
|
|
54005
|
+
const verified = verifyExistingPrPublication(
|
|
54006
|
+
publication,
|
|
54007
|
+
{
|
|
54008
|
+
repo: repoCtx.repo,
|
|
54009
|
+
pr: repoCtx.pr,
|
|
54010
|
+
ref: repoCtx.ref,
|
|
54011
|
+
baseRef: prBase,
|
|
54012
|
+
requestHeadSha: restoredHead ?? repoCtx.headSha,
|
|
54013
|
+
workspaceRef: binding?.ref,
|
|
54014
|
+
workspaceHeadSha: binding?.sha,
|
|
54015
|
+
owner: {
|
|
54016
|
+
instanceId: coordinator.parentInstanceId,
|
|
54017
|
+
unit: unitOfIdempotencyKey(coordinator.idempotencyKey) ?? ""
|
|
54018
|
+
}
|
|
54019
|
+
},
|
|
54020
|
+
fresh
|
|
54021
|
+
);
|
|
54022
|
+
existingPrPublication = verified.ok ? verified.publication : { blocked: verified.reason };
|
|
54023
|
+
existingPrPublicationFence = { authority: existingPrPublication };
|
|
54024
|
+
if (!verified.ok)
|
|
54025
|
+
shell.note(
|
|
54026
|
+
"quiet",
|
|
54027
|
+
`Existing-PR publication is blocked: ${verified.reason}. Work stays on the bound checkout; no alternate branch or pull request will be published.`
|
|
54028
|
+
);
|
|
54029
|
+
}
|
|
53842
54030
|
if (finish2) {
|
|
53843
54031
|
onEvent({
|
|
53844
54032
|
type: "run_note",
|
|
@@ -53904,38 +54092,6 @@ async function runLoop(deps, ctx) {
|
|
|
53904
54092
|
const providerCfg = deps.config.config.providers[providerName];
|
|
53905
54093
|
if (!providerCfg)
|
|
53906
54094
|
throw new Error(`the ${harness.name} harness found no provider named ${providerName} in the config`);
|
|
53907
|
-
const prBase = repoCtx.baseRef ?? await coordinatorBase;
|
|
53908
|
-
if (coordinator?.publication !== void 0) {
|
|
53909
|
-
const publication = coordinator.publication;
|
|
53910
|
-
const fresh = await (deps.fetchPrFacts ?? fetchPullRequestFacts)({
|
|
53911
|
-
repo: publication.repo,
|
|
53912
|
-
number: publication.pr
|
|
53913
|
-
}).catch(() => void 0);
|
|
53914
|
-
const verified = verifyExistingPrPublication(
|
|
53915
|
-
publication,
|
|
53916
|
-
{
|
|
53917
|
-
repo: repoCtx.repo,
|
|
53918
|
-
pr: repoCtx.pr,
|
|
53919
|
-
ref: repoCtx.ref,
|
|
53920
|
-
baseRef: prBase,
|
|
53921
|
-
requestHeadSha: repoCtx.headSha,
|
|
53922
|
-
workspaceRef: binding?.ref,
|
|
53923
|
-
workspaceHeadSha: binding?.sha,
|
|
53924
|
-
owner: {
|
|
53925
|
-
instanceId: coordinator.parentInstanceId,
|
|
53926
|
-
unit: unitOfIdempotencyKey(coordinator.idempotencyKey) ?? ""
|
|
53927
|
-
}
|
|
53928
|
-
},
|
|
53929
|
-
fresh
|
|
53930
|
-
);
|
|
53931
|
-
existingPrPublication = verified.ok ? verified.publication : { blocked: verified.reason };
|
|
53932
|
-
existingPrPublicationFence = { authority: existingPrPublication };
|
|
53933
|
-
if (!verified.ok)
|
|
53934
|
-
shell.note(
|
|
53935
|
-
"quiet",
|
|
53936
|
-
`Existing-PR publication is blocked: ${verified.reason}. Work stays on the bound checkout; no alternate branch or pull request will be published.`
|
|
53937
|
-
);
|
|
53938
|
-
}
|
|
53939
54095
|
const ownBranch = coordinator?.publication !== void 0 ? coordinator.publication.publicationRef : prBase !== void 0 ? binding?.ref ?? repoCtx.ref : void 0;
|
|
53940
54096
|
const protectedBranches = [
|
|
53941
54097
|
...new Set(
|
|
@@ -54196,6 +54352,8 @@ async function runLoop(deps, ctx) {
|
|
|
54196
54352
|
};
|
|
54197
54353
|
const endingNeedsPreservation = isCodingPrRun && coordinator !== void 0 && (budgetEnded || run2.control.requested !== void 0);
|
|
54198
54354
|
if (endingNeedsPreservation && run2.control.requested === "hard") await endHarness();
|
|
54355
|
+
await events.drain();
|
|
54356
|
+
retainPublicationReceipts();
|
|
54199
54357
|
if (isCodingPrRun && !tailSkipped()) await observeWorkspaceNow();
|
|
54200
54358
|
if (endingNeedsPreservation) await preserveCodingChildWork(budgetEnded ? "budget" : "ending");
|
|
54201
54359
|
else if (isCodingPrRun && coordinator !== void 0 && !tailSkipped()) await preserveCodingChildWork("completion");
|
|
@@ -54411,6 +54569,7 @@ async function runLoop(deps, ctx) {
|
|
|
54411
54569
|
} finally {
|
|
54412
54570
|
clearInterval(heartbeat);
|
|
54413
54571
|
await events.drain();
|
|
54572
|
+
retainPublicationReceipts();
|
|
54414
54573
|
const status3 = statusNow();
|
|
54415
54574
|
if (typeof registry.commitLiveState === "function") {
|
|
54416
54575
|
const commitBoundary = async (state, at2) => {
|
|
@@ -54574,6 +54733,7 @@ var init_runLoop = __esm({
|
|
|
54574
54733
|
init_githubPulls();
|
|
54575
54734
|
init_repoContext();
|
|
54576
54735
|
init_existingPrPublication();
|
|
54736
|
+
init_publicationPush();
|
|
54577
54737
|
init_prDescription();
|
|
54578
54738
|
init_handoff();
|
|
54579
54739
|
init_reviewVerdict();
|
|
@@ -60848,7 +61008,7 @@ function isBudgetRefusal(error) {
|
|
|
60848
61008
|
if (error.status !== void 0 && error.status !== 403) return false;
|
|
60849
61009
|
return TURN_BUDGET_REFUSAL.test(error.message);
|
|
60850
61010
|
}
|
|
60851
|
-
function judgeOpenCodeAsk(action, resources, rules, relayedToolNames) {
|
|
61011
|
+
function judgeOpenCodeAsk(action, resources, rules, relayedToolNames, callId) {
|
|
60852
61012
|
if (action === "doom_loop")
|
|
60853
61013
|
return {
|
|
60854
61014
|
reply: "reject",
|
|
@@ -60857,6 +61017,8 @@ function judgeOpenCodeAsk(action, resources, rules, relayedToolNames) {
|
|
|
60857
61017
|
};
|
|
60858
61018
|
const word = openCodeToolWord(action);
|
|
60859
61019
|
const tool = TOOL_NAME_WORD[action] ?? word;
|
|
61020
|
+
const pending2 = publicationAttributionRefusal(rules, callId);
|
|
61021
|
+
if (pending2 !== void 0) return { reply: "reject", message: pending2, tool };
|
|
60860
61022
|
if (relayedToolNames.has(action) || relayedToolNames.has(word)) return { reply: "once", tool: action };
|
|
60861
61023
|
const refuse3 = (reason2) => ({ reply: "reject", message: reason2, tool });
|
|
60862
61024
|
if (action === "external_directory") {
|
|
@@ -61677,6 +61839,7 @@ var init_bridge3 = __esm({
|
|
|
61677
61839
|
"use strict";
|
|
61678
61840
|
init_budgets();
|
|
61679
61841
|
init_runEvents();
|
|
61842
|
+
init_publicationPush();
|
|
61680
61843
|
init_contract4();
|
|
61681
61844
|
init_container();
|
|
61682
61845
|
init_reattach();
|
|
@@ -62544,9 +62707,14 @@ ${data.text}` : data.text;
|
|
|
62544
62707
|
request2.action,
|
|
62545
62708
|
Array.isArray(request2.resources) ? request2.resources : [],
|
|
62546
62709
|
this.deps.rules,
|
|
62547
|
-
this.deps.relayedToolNames
|
|
62710
|
+
this.deps.relayedToolNames,
|
|
62711
|
+
callId
|
|
62548
62712
|
);
|
|
62549
62713
|
if (this.answered.get(callId) !== "reject") this.answered.set(callId, verdict.reply);
|
|
62714
|
+
const authority = this.deps.rules.publication?.authority;
|
|
62715
|
+
const push = openCodeToolWord(request2.action) === "bash" && request2.resources?.length === 1 ? leasedPushCommand(request2.resources[0]) : void 0;
|
|
62716
|
+
if (this.answered.get(callId) === "once" && push !== void 0 && authority !== void 0 && !("blocked" in authority) && push.ref === authority.ref && push.expectedHeadSha === authority.expectedHeadSha)
|
|
62717
|
+
this.emit({ type: "publication_push_authorized", callId, ...push });
|
|
62550
62718
|
this.decidedReplies.set(request2.id, { reply: verdict.reply, callId, echoed: false });
|
|
62551
62719
|
const stepID = typeof request2.source?.messageID === "string" ? request2.source.messageID : void 0;
|
|
62552
62720
|
if (verdict.reply === "reject") {
|
|
@@ -74048,6 +74216,36 @@ function publicationStepPrefix(row2) {
|
|
|
74048
74216
|
}
|
|
74049
74217
|
return prefix;
|
|
74050
74218
|
}
|
|
74219
|
+
async function reconcileMissingFindingsPush(deps, instance, row2, runId3, head, siblings, after) {
|
|
74220
|
+
const binding = row2.publication;
|
|
74221
|
+
if (binding === void 0) return false;
|
|
74222
|
+
const read = await deps.runs.getRun(runId3, { include: "messages" });
|
|
74223
|
+
if (!read.ok) return false;
|
|
74224
|
+
const run2 = read.value;
|
|
74225
|
+
const complete2 = (r) => {
|
|
74226
|
+
const models = Object.values(r.usage?.byModel ?? {});
|
|
74227
|
+
return r.finished && r.persisted === true && !r.provisional && !r.restarting && r.truncated === false && r.events !== void 0 && r.eventCount === r.events.length && r.storedEventCount === r.events.length && r.events.every(
|
|
74228
|
+
(e) => e.type !== "tool_call" || e.tool !== "bash" || e.command !== void 0 && e.command.length < COMMAND_CAP && !e.command.includes("\u2026")
|
|
74229
|
+
) && !r.events.some((e) => e.type === "tool_result" && e.cut || e.type === "pushed_head") && models.length > 0 && models.every((m) => typeof m.usd === "number" && Number.isFinite(m.usd) && m.usd >= 0);
|
|
74230
|
+
};
|
|
74231
|
+
const tags = run2.events?.filter((e) => e.type === "coordinator_tag") ?? [];
|
|
74232
|
+
const tag = tags.length === 1 ? tags[0] : void 0;
|
|
74233
|
+
if (!complete2(run2) || run2.status !== "completed" || (run2.pushed?.length ?? 0) !== 0 || run2.headSha !== head || run2.parentInstanceId !== instance.id || run2.userId !== instance.userId || run2.repo?.toLowerCase() !== instance.repo.toLowerCase() || run2.threadKey !== (row2.threadKey ?? instance.threadKey) || run2.agent !== "coding" || run2.startedAt < after || run2.finishedAt === void 0 || run2.finishedAt < run2.startedAt || tag?.parentInstanceId !== instance.id || tag.unit !== row2.unit || tag.base !== instance.base || !samePublicationBinding(tag.publication, binding) || run2.pr !== void 0 && (run2.pr.number !== binding.pr || run2.pr.head !== void 0 && run2.pr.head !== row2.branch) || pairedPublicationPush(run2.events, binding, head) === void 0)
|
|
74234
|
+
return false;
|
|
74235
|
+
for (const sibling of siblings.filter(
|
|
74236
|
+
(r) => r.id !== runId3 && r.agent === "coding" && (r.finishedAt === void 0 || r.finishedAt >= after)
|
|
74237
|
+
)) {
|
|
74238
|
+
if (!sibling.finished || sibling.status !== "failed" && sibling.status !== "interrupted" || sibling.parentInstanceId !== instance.id || sibling.userId !== instance.userId || sibling.repo?.toLowerCase() !== instance.repo.toLowerCase() || sibling.threadKey !== run2.threadKey || sibling.startedAt < after || sibling.finishedAt === void 0 || sibling.finishedAt > run2.startedAt || (sibling.pushed?.length ?? 0) > 0)
|
|
74239
|
+
return false;
|
|
74240
|
+
const prior = await deps.runs.getRun(sibling.id, { include: "messages" });
|
|
74241
|
+
if (!prior.ok || !complete2(prior.value) || prior.value.events.some(
|
|
74242
|
+
(e) => e.type === "pushed_head" || e.type === "tool_call" && e.tool === "bash" && /\bgit\s+push\b/.test(e.command ?? e.summary)
|
|
74243
|
+
))
|
|
74244
|
+
return false;
|
|
74245
|
+
}
|
|
74246
|
+
const fresh = await deps.fetchPrFacts({ repo: instance.repo, number: binding.pr }).catch(() => void 0);
|
|
74247
|
+
return fresh?.state === "open" && fresh.sameRepoHead === true && fresh.headBranchExists === true && fresh.headRef === binding.headRef && fresh.baseRef === binding.baseRef && fresh.headSha === head && fresh.verifiedHead?.repo.toLowerCase() === instance.repo.toLowerCase() && fresh.verifiedHead.ref === binding.publicationRef && fresh.verifiedHead.sha === head;
|
|
74248
|
+
}
|
|
74051
74249
|
async function advanceFindingsPublication(deps, instance, row2, facts2, runId3) {
|
|
74052
74250
|
if (row2.publication === void 0 || facts2.headSha === row2.publication.expectedHeadSha) return row2;
|
|
74053
74251
|
const child = await deps.runs.getRun(runId3, { include: "messages" });
|
|
@@ -74055,6 +74253,7 @@ async function advanceFindingsPublication(deps, instance, row2, facts2, runId3)
|
|
|
74055
74253
|
const latestReview = [...row2.rounds].reverse().find((round2) => round2.agent === "review");
|
|
74056
74254
|
const prefix = row2.recovery !== void 0 ? `${row2.unit}/recovery` : publicationStepPrefix(row2);
|
|
74057
74255
|
const findingsPrefix = `${instance.id}:${prefix}/${latestReview?.index}/findings`;
|
|
74256
|
+
let reconciledPush = false;
|
|
74058
74257
|
if (row2.recovery === void 0) {
|
|
74059
74258
|
const tags = child.ok ? child.value.events?.filter((event) => event.type === "coordinator_tag") ?? [] : [];
|
|
74060
74259
|
const tag = tags.length === 1 ? tags[0] : void 0;
|
|
@@ -74073,8 +74272,18 @@ async function advanceFindingsPublication(deps, instance, row2, facts2, runId3)
|
|
|
74073
74272
|
(run2) => run2.id !== runId3 && !(run2.finished && (run2.status === "failed" || run2.status === "interrupted") && run2.agent === "coding" && run2.userId === instance.userId && run2.repo?.toLowerCase() === instance.repo.toLowerCase() && run2.threadKey === (row2.threadKey ?? instance.threadKey) && run2.startedAt >= latestReview.at && run2.finishedAt !== void 0 && run2.finishedAt >= run2.startedAt && run2.finishedAt <= child.value.startedAt && (run2.pushed?.length ?? 0) === 0)
|
|
74074
74273
|
))
|
|
74075
74274
|
throw new PublicationBindingRefusal("publication_facts_mismatch");
|
|
74275
|
+
if (child.ok && (child.value.pushed?.length ?? 0) === 0 && fullHead(facts2.headSha))
|
|
74276
|
+
reconciledPush = await reconcileMissingFindingsPush(
|
|
74277
|
+
deps,
|
|
74278
|
+
instance,
|
|
74279
|
+
row2,
|
|
74280
|
+
runId3,
|
|
74281
|
+
facts2.headSha,
|
|
74282
|
+
listing.runs,
|
|
74283
|
+
latestReview.at
|
|
74284
|
+
);
|
|
74076
74285
|
}
|
|
74077
|
-
if (!child.ok || child.value.finished !== true || child.value.status !== "completed" || child.value.agent !== "coding" || child.value.parentInstanceId !== instance.id || child.value.userId !== instance.userId || child.value.repo?.toLowerCase() !== instance.repo.toLowerCase() || child.value.threadKey !== (row2.threadKey ?? instance.threadKey) || (row2.recovery !== void 0 ? child.value.idempotencyKey?.startsWith(`${instance.id}:${row2.unit}/recovery/`) !== true || !/\/findings(?:\/a[1-9][0-9]*)?$/.test(child.value.idempotencyKey ?? "") : !isStepAttempt(child.value.idempotencyKey, findingsPrefix)) || !fullHead(facts2.headSha) || child.value.headSha !== facts2.headSha || child.value.pushed?.some((push) => push.ref === row2.branch && push.sha === facts2.headSha) !== true || facts2.state !== "open" || facts2.sameRepoHead !== true || facts2.headBranchExists !== true || facts2.headRef !== row2.publication.headRef || facts2.baseRef !== row2.publication.baseRef || facts2.verifiedHead?.repo.toLowerCase() !== instance.repo.toLowerCase() || facts2.verifiedHead.ref !== row2.publication.publicationRef || facts2.verifiedHead.sha !== facts2.headSha || row2.publication.repo.toLowerCase() !== instance.repo.toLowerCase() || row2.publication.pr !== row2.pr?.number || row2.publication.baseRef !== instance.base || row2.publication.headRef !== row2.branch || row2.publication.publicationRef !== row2.branch || !samePublicationOwner(row2.publication.owner, owner))
|
|
74286
|
+
if (!child.ok || child.value.finished !== true || child.value.status !== "completed" || child.value.agent !== "coding" || child.value.parentInstanceId !== instance.id || child.value.userId !== instance.userId || child.value.repo?.toLowerCase() !== instance.repo.toLowerCase() || child.value.threadKey !== (row2.threadKey ?? instance.threadKey) || (row2.recovery !== void 0 ? child.value.idempotencyKey?.startsWith(`${instance.id}:${row2.unit}/recovery/`) !== true || !/\/findings(?:\/a[1-9][0-9]*)?$/.test(child.value.idempotencyKey ?? "") : !isStepAttempt(child.value.idempotencyKey, findingsPrefix)) || !fullHead(facts2.headSha) || child.value.headSha !== facts2.headSha || !reconciledPush && child.value.pushed?.some((push) => push.ref === row2.branch && push.sha === facts2.headSha) !== true || facts2.state !== "open" || facts2.sameRepoHead !== true || facts2.headBranchExists !== true || facts2.headRef !== row2.publication.headRef || facts2.baseRef !== row2.publication.baseRef || facts2.verifiedHead?.repo.toLowerCase() !== instance.repo.toLowerCase() || facts2.verifiedHead.ref !== row2.publication.publicationRef || facts2.verifiedHead.sha !== facts2.headSha || row2.publication.repo.toLowerCase() !== instance.repo.toLowerCase() || row2.publication.pr !== row2.pr?.number || row2.publication.baseRef !== instance.base || row2.publication.headRef !== row2.branch || row2.publication.publicationRef !== row2.branch || !samePublicationOwner(row2.publication.owner, owner))
|
|
74078
74287
|
throw new PublicationBindingRefusal("publication_facts_mismatch");
|
|
74079
74288
|
try {
|
|
74080
74289
|
if (row2.recovery !== void 0 && deps.runnerOwnership?.claim(instance.repo, row2.publication.pr, owner) !== true)
|
|
@@ -74371,7 +74580,16 @@ async function recoverOriginalUnit(body, deps, caller) {
|
|
|
74371
74580
|
const checkedAttempts = attempts.filter((run2) => run2.idempotencyKey === `${instance.id}:${checkedStep}`);
|
|
74372
74581
|
const completed = checkedAttempts.length === 1 ? checkedAttempts[0] : void 0;
|
|
74373
74582
|
const reviewFinishedAt = review.finishedAt;
|
|
74374
|
-
|
|
74583
|
+
const reconciledPush = completed !== void 0 && headMoved && reviewFinishedAt !== void 0 && (completed.pushed?.length ?? 0) === 0 && await reconcileMissingFindingsPush(
|
|
74584
|
+
deps,
|
|
74585
|
+
instance,
|
|
74586
|
+
row2,
|
|
74587
|
+
completed.id,
|
|
74588
|
+
facts2.headSha,
|
|
74589
|
+
unitListing.runs,
|
|
74590
|
+
reviewFinishedAt
|
|
74591
|
+
);
|
|
74592
|
+
if (reviewFinishedAt === void 0 || completed === void 0 || !completed.finished || completed.status !== "completed" || completed.agent !== "coding" || completed.userId !== instance.userId || completed.repo?.toLowerCase() !== instance.repo.toLowerCase() || completed.threadKey !== unitThreadKey || completed.pr !== void 0 && (completed.pr.number !== pr.number || completed.pr.head !== void 0 && completed.pr.head !== row2.branch) || completed.headSha !== facts2.headSha || expectedHead !== reviewedHead && expectedHead !== facts2.headSha || completed.pushed?.some((push) => push.ref !== row2.branch) === true || headMoved && !reconciledPush && completed.pushed?.some((push) => push.ref === row2.branch && push.sha === facts2.headSha) !== true || completed.startedAt < reviewFinishedAt || completed.finishedAt === void 0 || completed.finishedAt < completed.startedAt || completed.finishedAt > row2.ending.at || // Inspect every attempt, not just runs matching the current PR head:
|
|
74375
74593
|
// any competing success or push remains ambiguous after a later push.
|
|
74376
74594
|
attempts.some(
|
|
74377
74595
|
(run2) => run2.id !== completed.id && !(run2.finished && (run2.status === "failed" || run2.status === "interrupted") && run2.agent === "coding" && run2.userId === instance.userId && run2.repo?.toLowerCase() === instance.repo.toLowerCase() && run2.threadKey === unitThreadKey && run2.startedAt >= reviewFinishedAt && run2.finishedAt !== void 0 && run2.finishedAt >= run2.startedAt && run2.finishedAt <= completed.startedAt && (run2.pushed?.length ?? 0) === 0)
|
|
@@ -76035,6 +76253,7 @@ var init_adminCoordinator = __esm({
|
|
|
76035
76253
|
"use strict";
|
|
76036
76254
|
init_budgets();
|
|
76037
76255
|
init_verbosity();
|
|
76256
|
+
init_publicationPush();
|
|
76038
76257
|
init_prDescription();
|
|
76039
76258
|
init_reply();
|
|
76040
76259
|
init_registry();
|
|
@@ -76049,6 +76268,7 @@ var init_adminCoordinator = __esm({
|
|
|
76049
76268
|
init_effort();
|
|
76050
76269
|
init_reviewVerdict();
|
|
76051
76270
|
init_runFriction();
|
|
76271
|
+
init_runEvents();
|
|
76052
76272
|
init_runRecord();
|
|
76053
76273
|
init_coordinator();
|
|
76054
76274
|
init_renewal();
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@coreplane/switchboard",
|
|
3
|
-
"version": "1.265.
|
|
3
|
+
"version": "1.265.1",
|
|
4
4
|
"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.",
|
|
5
5
|
"license": "Apache-2.0",
|
|
6
6
|
"homepage": "https://openswitchboard.dev",
|